@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.
- package/LICENSE +21 -0
- package/README.md +167 -0
- package/dist/chunk-32TESFMK.js +517 -0
- package/dist/chunk-32TESFMK.js.map +1 -0
- package/dist/codegen-cli.js +280 -0
- package/dist/codegen-cli.js.map +1 -0
- package/dist/index.d.ts +540 -0
- package/dist/index.js +428 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +32 -0
- package/dist/server.js +12 -0
- package/dist/server.js.map +1 -0
- package/package.json +45 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ModernRelay
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# @modernrelay/orbit-omnigraph
|
|
2
|
+
|
|
3
|
+
Optional Omnigraph data-source adapter for Orbit (spec Appendix B). Framework-agnostic:
|
|
4
|
+
depends only on the framework-free ingestion/error surface of `@modernrelay/orbit-core`
|
|
5
|
+
and the official TypeScript SDK `@modernrelay/omnigraph` (0.8.x) — never on React, and
|
|
6
|
+
core never depends on it.
|
|
7
|
+
|
|
8
|
+
## The v1 rule (B.10)
|
|
9
|
+
|
|
10
|
+
> **v1 loads graphs via `og.export()`; queries only ever resolve ids** (search, overlay
|
|
11
|
+
> selection — search itself arrives with S11). The query path never introduces nodes or
|
|
12
|
+
> edges into the graph.
|
|
13
|
+
|
|
14
|
+
Export is the only complete-graph read: it streams NDJSON with full identity — physical
|
|
15
|
+
edge ids included — so every v1 edge carries an export id and no synthesized-id scheme ever
|
|
16
|
+
mixes in (B.4/§5). What that buys, and what it defers (server-backed expansion, query-path
|
|
17
|
+
loading, authed blob fetch), is tabulated in spec B.10.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
// server.ts — the ONLY place authenticated construction lives (B.1)
|
|
23
|
+
import { createOmnigraphServerClient } from '@modernrelay/orbit-omnigraph/server';
|
|
24
|
+
|
|
25
|
+
const client = createOmnigraphServerClient({
|
|
26
|
+
baseUrl: 'http://127.0.0.1:8080',
|
|
27
|
+
token: process.env.OMNIGRAPH_TOKEN!, // secret material — server-side only
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
// anywhere (browser code passes a preconfigured safe client instead — see below)
|
|
33
|
+
import { createOmnigraphSource } from '@modernrelay/orbit-omnigraph';
|
|
34
|
+
import { createGraphInstance } from '@modernrelay/orbit-core';
|
|
35
|
+
|
|
36
|
+
const source = createOmnigraphSource({
|
|
37
|
+
client, // preconfigured SDK client — no token option here
|
|
38
|
+
graphId: 'demo',
|
|
39
|
+
branch: 'main', // default
|
|
40
|
+
// typeNames: ['Signal', 'Correlates'], // partial per-type load (wire: type_names)
|
|
41
|
+
// driftPolicy: 'reject', // default; see below
|
|
42
|
+
// maxPendingBytes: 64 * 1024 * 1024, // default whole-load atomic budget
|
|
43
|
+
onProgress: ({ lines, nodes, edges, bytes }) => console.log(lines, nodes, edges, bytes),
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const instance = createGraphInstance({ engine });
|
|
47
|
+
const result = await source.load(instance /*, abortSignal */);
|
|
48
|
+
// result: { sourceRevision, dataRef, counts, serverVersion, warnings }
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
`load()` drives one streamed export pass into a `purpose:'replace'` ingest session
|
|
52
|
+
(`datasetKey` = `og:<graphId>:<branch>`): edge lines arrive first (lexicographic table-key
|
|
53
|
+
order) and pass straight through — core's pending-endpoint index links them as nodes land
|
|
54
|
+
(§7.5). Every append is awaited, and the atomic load has a finite whole-export byte budget
|
|
55
|
+
(`maxPendingBytes`, 64 MiB by default); exceeding it aborts without publishing a partial
|
|
56
|
+
graph. Ids are namespaced through the B.3 codec, and B.6 wire encodings are normalized to
|
|
57
|
+
the query-path string forms. Byte progress and budgets use re-serialized UTF-8 NDJSON size,
|
|
58
|
+
including one line break per export row.
|
|
59
|
+
|
|
60
|
+
The `target` is structural (`IngestTarget`: `beginIngest` + `getRevisions` +
|
|
61
|
+
optional `getDiagnostics`) — deliberately decoupled from `GraphInstance`, which satisfies
|
|
62
|
+
it structurally; so does any recorder or headless pipeline.
|
|
63
|
+
|
|
64
|
+
## `sourceRevision` and drift (B.2)
|
|
65
|
+
|
|
66
|
+
The canonical `sourceRevision` is a hash of
|
|
67
|
+
`{ graphId, branch, headBefore, headAfter, schemaFingerprint }` — but `/export` is
|
|
68
|
+
branch-only (no snapshot isolation), so `headAfter` is only knowable after the stream,
|
|
69
|
+
while core's `beginIngest` requires `sourceRevision` up front for a replace session.
|
|
70
|
+
|
|
71
|
+
Resolution: under `'reject'` and `'retry-once'`, a session begins under the **provisional**
|
|
72
|
+
revision (the canonical hash with `headAfter := headBefore`), the stream appends into it,
|
|
73
|
+
and the head is re-read **before** `commit()`. `'accept-warn'` buffers the bounded export
|
|
74
|
+
until that second head is known, then opens one session under the canonical final revision:
|
|
75
|
+
|
|
76
|
+
- **equal heads** — provisional === final; commit cleanly. The steady-state path.
|
|
77
|
+
- **drift** (`driftPolicy`):
|
|
78
|
+
- `'reject'` (default) — abort the session and throw `OmnigraphDriftError`; the graph is
|
|
79
|
+
untouched. Right for durable/shareable sessions.
|
|
80
|
+
- `'accept-warn'` — commit under the canonical final revision; `dataRef` records
|
|
81
|
+
**both** heads and a warning is added to the result.
|
|
82
|
+
- `'retry-once'` — abort and restart the whole load once; a second drift rejects.
|
|
83
|
+
|
|
84
|
+
This is sound because a replace session is atomic and invisible until commit (§7.5):
|
|
85
|
+
nothing publishes under a revision the policy did not explicitly accept, and the decision
|
|
86
|
+
is never hidden — every accepted drift or retry appears in `result.warnings`.
|
|
87
|
+
|
|
88
|
+
A second identical load of a quiescent branch produces the same
|
|
89
|
+
`{datasetKey, sourceRevision}` and replays idempotently — core publishes nothing (§7.5).
|
|
90
|
+
|
|
91
|
+
## Search: stored-query `SearchService` (B.7 / §16.5)
|
|
92
|
+
|
|
93
|
+
`createOmnigraphSearchService` wires a stored server-side search query (BM25 / fuzzy /
|
|
94
|
+
vector / RRF with `order { score desc } limit K`) as a §16.5 `SearchService` — plug it into
|
|
95
|
+
core as `services.search` and the instance owns `RequestContext` creation, revision-keyed
|
|
96
|
+
caching, supersede cancellation, and stale-result admission. The service declares
|
|
97
|
+
`revisionDependencies: ['source']` and passes `ctx.signal` through SDK `CallOptions`, so a
|
|
98
|
+
superseded keystroke cancels the in-flight HTTP call.
|
|
99
|
+
|
|
100
|
+
```ts
|
|
101
|
+
import { createOmnigraphSearchService } from '@modernrelay/orbit-omnigraph';
|
|
102
|
+
|
|
103
|
+
const search = createOmnigraphSearchService({
|
|
104
|
+
client, // preconfigured SDK client — same B.1 rule as the loader
|
|
105
|
+
graphId: 'demo',
|
|
106
|
+
branch: 'main', // default
|
|
107
|
+
queryName: 'search-intel', // registry name; invoked as POST /queries/search-intel
|
|
108
|
+
// params: (q, limit) => ({ q, limit }), // default — reshape for your query's params
|
|
109
|
+
typeOf: { $s: 'Signal' }, // REQUIRED (B.3): column → node type for the id encoding
|
|
110
|
+
// labelColumn: '$s.title', // default: the first string-valued column per row
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
const instance = createGraphInstance({ engine, services: { search } });
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Row mapping (override wholesale with `mapRow: (row) => SearchResult | null`, `null` skips):
|
|
117
|
+
|
|
118
|
+
- **id** — Omnigraph ids are unique per type only and query rows carry no type
|
|
119
|
+
discriminator, so the adapter **requires** a caller-supplied column→node-type mapping
|
|
120
|
+
(`typeOf`): a record keyed by the bare-variable projection columns holding node structs
|
|
121
|
+
(`return { $s }` → `{ $s: 'Signal' }`; the first listed column present wins), or a
|
|
122
|
+
per-row function returning the type name (the row's first node-struct column supplies the
|
|
123
|
+
physical id). Every id is qualified via `encodeSourceId(nodeType, physicalId)` — it
|
|
124
|
+
round-trips `decodeSourceId` and matches export-loaded node ids exactly.
|
|
125
|
+
- **score** — a finite number in the row's `score` column (the score lane never doubles as
|
|
126
|
+
a label).
|
|
127
|
+
- **label** — `labelColumn`'s value when configured, else the first string-valued column in
|
|
128
|
+
row key order.
|
|
129
|
+
|
|
130
|
+
Trivial calls (empty query, non-positive limit) resolve `[]` without a network call, and
|
|
131
|
+
results are defensively capped at the requested limit. Errors follow B.9: SDK typed errors
|
|
132
|
+
rethrow as plain `omnigraph:`-prefixed `Error`s; aborts surface as `AbortError`.
|
|
133
|
+
|
|
134
|
+
> **v1 caveat (B.7):** the stored query searches the WHOLE branch server-side, so against a
|
|
135
|
+
> *partial* export load (`typeNames`) it can return ids outside the loaded set.
|
|
136
|
+
> `activateSearchResult` classifies those as `'not-loaded'` (§16.5) — the host may remedy
|
|
137
|
+
> that only by re-running export for the relevant types (query-path single-node fetch is
|
|
138
|
+
> exactly what B.10 defers). Constrain the stored query to the loaded types, or load the
|
|
139
|
+
> full graph, to avoid the mismatch. Discovery via `og.queries.list()` returns only the
|
|
140
|
+
> `mcp.expose == true` registry subset — invoking a known `queryName` works regardless.
|
|
141
|
+
|
|
142
|
+
## Server-only entry — do not import from the browser
|
|
143
|
+
|
|
144
|
+
`@modernrelay/orbit-omnigraph/server` (`createOmnigraphServerClient`) is the only
|
|
145
|
+
authenticated SDK construction and must stay server-side: `omnigraph-server` ships no CORS
|
|
146
|
+
configuration and uses static bearer tokens (secret material) (B.9). Browser deployments
|
|
147
|
+
pass a preconfigured safe same-origin/public client to `createOmnigraphSource` — the
|
|
148
|
+
browser entry deliberately has no `baseUrl`/`token` option — and typically route reads
|
|
149
|
+
through a proxy/BFF. A client-bundle exclusion gate (`scripts/pack-smoke.mjs`,
|
|
150
|
+
`treeshake:omnigraph-client`) asserts the server entry never reaches a client bundle.
|
|
151
|
+
|
|
152
|
+
## Error surface (B.9)
|
|
153
|
+
|
|
154
|
+
SDK typed errors (`NetworkError`, `ConflictError`, …) never cross this package's public
|
|
155
|
+
surface — failures rethrow as plain `Error`s with a stable `omnigraph:` message prefix.
|
|
156
|
+
Cancellation (`AbortSignal`) surfaces as an `AbortError`; the session is aborted and the
|
|
157
|
+
graph left untouched. SDK/server `major.minor` version mismatches (`og.health()` vs the
|
|
158
|
+
SDK's pinned `SERVER_VERSION`) surface as a result warning, never a hard failure (B.1).
|
|
159
|
+
|
|
160
|
+
## Also in the box
|
|
161
|
+
|
|
162
|
+
- `encodeSourceId`/`decodeSourceId`/`encodeSyntheticEdgeId` — the B.3/B.4 identity codec
|
|
163
|
+
every adapter path must use.
|
|
164
|
+
- `parsePgSchema`/`edgeEndpointTypes`/`schemaFingerprint`/`bigIntKeyWarnings` — the `.pg`
|
|
165
|
+
schema model (B.6): endpoint-type resolution, wire-type knowledge, revision fingerprint.
|
|
166
|
+
- `classifyExportLine`/`normalizeNode`/`normalizeEdge` — export-line normalization
|
|
167
|
+
(B.2/B.6).
|