@typicalday/firegraph 0.8.0 → 0.10.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.
Files changed (72) hide show
  1. package/dist/backend-73p5Blx7.d.cts +97 -0
  2. package/dist/backend-BrqFkbid.d.ts +97 -0
  3. package/dist/backend.cjs +222 -0
  4. package/dist/backend.cjs.map +1 -0
  5. package/dist/backend.d.cts +122 -0
  6. package/dist/backend.d.ts +122 -0
  7. package/dist/backend.js +136 -0
  8. package/dist/backend.js.map +1 -0
  9. package/dist/{chunk-6OQW5OKO.js → chunk-5753Y42M.js} +12 -4
  10. package/dist/chunk-5753Y42M.js.map +1 -0
  11. package/dist/{chunk-YUXOALMR.js → chunk-LZOIQHYN.js} +69 -92
  12. package/dist/chunk-LZOIQHYN.js.map +1 -0
  13. package/dist/chunk-R7CRGYY4.js +94 -0
  14. package/dist/chunk-R7CRGYY4.js.map +1 -0
  15. package/dist/{chunk-KFA7G37W.js → chunk-SU4FNLC3.js} +32 -30
  16. package/dist/chunk-SU4FNLC3.js.map +1 -0
  17. package/dist/chunk-TYYPRVIE.js +57 -0
  18. package/dist/chunk-TYYPRVIE.js.map +1 -0
  19. package/dist/{do-sqlite.cjs → cloudflare/index.cjs} +1538 -1420
  20. package/dist/cloudflare/index.cjs.map +1 -0
  21. package/dist/cloudflare/index.d.cts +454 -0
  22. package/dist/cloudflare/index.d.ts +454 -0
  23. package/dist/cloudflare/index.js +822 -0
  24. package/dist/cloudflare/index.js.map +1 -0
  25. package/dist/codegen/index.d.cts +1 -1
  26. package/dist/codegen/index.d.ts +1 -1
  27. package/dist/editor/client/assets/index-Bq2bfzeY.js +411 -0
  28. package/dist/editor/client/index.html +1 -1
  29. package/dist/editor/server/index.mjs +6481 -6327
  30. package/dist/index.cjs +165 -44
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +14 -138
  33. package/dist/index.d.ts +14 -138
  34. package/dist/index.js +31 -22
  35. package/dist/index.js.map +1 -1
  36. package/dist/query-client/index.cjs +30 -28
  37. package/dist/query-client/index.cjs.map +1 -1
  38. package/dist/query-client/index.d.cts +2 -2
  39. package/dist/query-client/index.d.ts +2 -2
  40. package/dist/query-client/index.js +1 -1
  41. package/dist/react.cjs +0 -1
  42. package/dist/react.cjs.map +1 -1
  43. package/dist/react.js +0 -1
  44. package/dist/react.js.map +1 -1
  45. package/dist/scope-path-B1G3YiA7.d.cts +139 -0
  46. package/dist/scope-path-B1G3YiA7.d.ts +139 -0
  47. package/dist/{serialization-C6JNNOCS.js → serialization-ZZ7RSDRX.js} +2 -2
  48. package/dist/svelte.cjs +0 -2
  49. package/dist/svelte.cjs.map +1 -1
  50. package/dist/svelte.js +0 -2
  51. package/dist/svelte.js.map +1 -1
  52. package/dist/{types-BVtx9zLv.d.cts → types-DOemdlVA.d.cts} +20 -2
  53. package/dist/{types-BVtx9zLv.d.ts → types-DOemdlVA.d.ts} +20 -2
  54. package/package.json +39 -40
  55. package/dist/chunk-6OQW5OKO.js.map +0 -1
  56. package/dist/chunk-KFA7G37W.js.map +0 -1
  57. package/dist/chunk-WOAJRVHD.js +0 -699
  58. package/dist/chunk-WOAJRVHD.js.map +0 -1
  59. package/dist/chunk-YUXOALMR.js.map +0 -1
  60. package/dist/d1.cjs +0 -2416
  61. package/dist/d1.cjs.map +0 -1
  62. package/dist/d1.d.cts +0 -54
  63. package/dist/d1.d.ts +0 -54
  64. package/dist/d1.js +0 -75
  65. package/dist/d1.js.map +0 -1
  66. package/dist/do-sqlite.cjs.map +0 -1
  67. package/dist/do-sqlite.d.cts +0 -41
  68. package/dist/do-sqlite.d.ts +0 -41
  69. package/dist/do-sqlite.js +0 -78
  70. package/dist/do-sqlite.js.map +0 -1
  71. package/dist/editor/client/assets/index-tyFcX6qG.js +0 -411
  72. /package/dist/{serialization-C6JNNOCS.js.map → serialization-ZZ7RSDRX.js.map} +0 -0
@@ -0,0 +1,454 @@
1
+ import { W as WritableRecord, U as UpdatePayload, S as StorageBackend, T as TransactionBackend, B as BatchBackend } from '../backend-BrqFkbid.js';
2
+ import { i as QueryFilter, y as QueryOptions, C as CascadeResult, F as FindEdgesParams, m as BulkOptions, o as BulkResult, a as GraphClient, D as DynamicGraphClient, c as GraphRegistry, S as StoredGraphRecord, d as GraphReader, G as GraphClientOptions, e as DynamicRegistryConfig } from '../types-DOemdlVA.js';
3
+ import '@google-cloud/firestore';
4
+
5
+ /**
6
+ * SQL compilation for the DO SQLite backend.
7
+ *
8
+ * Every `FiregraphDO` instance owns one SQLite database holding exactly one
9
+ * subgraph's triples — there is no `scope` column and no scope discriminator
10
+ * on any statement. Contrast with `src/internal/sqlite-sql.ts`, which
11
+ * carries a scope prefix on every read and write for the legacy shared-table
12
+ * D1/DO SQLite backend.
13
+ *
14
+ * Filter compilation, JSON-path validation, and value binding mirror the
15
+ * legacy module so the query planner (`src/query.ts`) emits the same
16
+ * `QueryFilter[]` shape regardless of backend.
17
+ */
18
+
19
+ /**
20
+ * Wire representation of a stored record across the DO RPC boundary.
21
+ *
22
+ * Durable Object RPC uses structured clone, which preserves plain data but
23
+ * drops user-defined class prototypes — a `GraphTimestampImpl` from the DO
24
+ * arrives at the client as a plain `{seconds, nanoseconds}` object without
25
+ * its `toMillis()` / `toDate()` methods. To avoid silent `.toMillis is not a
26
+ * function` crashes downstream, records returned from DO RPC carry the two
27
+ * timestamps as plain millisecond numbers in `createdAtMs` / `updatedAtMs`;
28
+ * the client-side backend rewraps them as `GraphTimestampImpl` via
29
+ * `hydrateDORecord` before handing the record to the GraphClient.
30
+ */
31
+ interface DORecordWire {
32
+ aType: string;
33
+ aUid: string;
34
+ axbType: string;
35
+ bType: string;
36
+ bUid: string;
37
+ data: Record<string, unknown>;
38
+ v?: number;
39
+ createdAtMs: number;
40
+ updatedAtMs: number;
41
+ }
42
+
43
+ /**
44
+ * `FiregraphDO` — the Durable Object class that holds a single subgraph's
45
+ * triples.
46
+ *
47
+ * The Cloudflare-native Firegraph design puts each subgraph in its own DO
48
+ * instance: the root graph in one DO, `client.subgraph(uid, 'memories')` in
49
+ * another, nested subgraphs in their own DOs, and so on. Each DO owns a
50
+ * private flat SQLite database (`src/cloudflare/schema.ts`) — no `scope`
51
+ * column, no shared table, no discriminator. The client routes to a
52
+ * specific DO by hashing a stable name (`namespace.idFromName(storageKey)`);
53
+ * on first RPC, the DO lazily materializes and runs the schema DDL.
54
+ *
55
+ * ## Using it in a Worker
56
+ *
57
+ * Bind the class in `wrangler.toml` and re-export it from the Worker entry:
58
+ *
59
+ * ```toml
60
+ * [[durable_objects.bindings]]
61
+ * name = "GRAPH"
62
+ * class_name = "FiregraphDO"
63
+ *
64
+ * [[migrations]]
65
+ * tag = "v1"
66
+ * new_sqlite_classes = ["FiregraphDO"]
67
+ * ```
68
+ *
69
+ * ```ts
70
+ * // worker.ts
71
+ * export { FiregraphDO } from '@typicalday/firegraph/cloudflare';
72
+ * ```
73
+ *
74
+ * To add custom RPC methods, extend the class:
75
+ *
76
+ * ```ts
77
+ * export class GraphDO extends FiregraphDO {
78
+ * async myCustomRpc() { ... }
79
+ * }
80
+ * ```
81
+ *
82
+ * ## Why a plain class (not `extends DurableObject`)?
83
+ *
84
+ * Cloudflare accepts any class with the `(state, env)` constructor shape as
85
+ * a DO class. Extending `DurableObject` from `cloudflare:workers` would pull
86
+ * a runtime import into this module and prevent it from loading in Node
87
+ * tests. The plain-class form keeps this file runtime-neutral — the only
88
+ * Cloudflare thing we touch is `ctx.storage.sql`, typed via local minimal
89
+ * interfaces.
90
+ */
91
+
92
+ interface DOSqlCursor<T> {
93
+ toArray(): T[];
94
+ }
95
+ interface DOSqlExecutor {
96
+ exec<T = Record<string, unknown>>(sql: string, ...params: unknown[]): DOSqlCursor<T>;
97
+ }
98
+ interface DOStorage {
99
+ sql: DOSqlExecutor;
100
+ transactionSync<T>(fn: () => T): T;
101
+ }
102
+ interface DurableObjectStateLike {
103
+ readonly storage: DOStorage;
104
+ blockConcurrencyWhile<T>(fn: () => Promise<T>): Promise<T>;
105
+ }
106
+ /**
107
+ * One op in a `batch()` RPC call. Discriminated by `kind` so the DO can
108
+ * dispatch to the correct compiler.
109
+ */
110
+ type BatchOp = {
111
+ kind: 'set';
112
+ docId: string;
113
+ record: WritableRecord;
114
+ } | {
115
+ kind: 'update';
116
+ docId: string;
117
+ update: UpdatePayload;
118
+ } | {
119
+ kind: 'delete';
120
+ docId: string;
121
+ };
122
+ /**
123
+ * Options controlling `FiregraphDO` construction.
124
+ */
125
+ interface FiregraphDOOptions {
126
+ /** Table name for firegraph triples. Default: `firegraph`. */
127
+ table?: string;
128
+ /** Run schema DDL on first boot. Default: `true`. */
129
+ autoMigrate?: boolean;
130
+ }
131
+ declare class FiregraphDO {
132
+ /** @internal — exposed for subclass access, not part of the public RPC. */
133
+ protected readonly ctx: DurableObjectStateLike;
134
+ /** @internal — exposed for subclass access; opaque to this class. */
135
+ protected readonly env: unknown;
136
+ /** @internal — table name used by every compiled statement. */
137
+ protected readonly table: string;
138
+ constructor(ctx: DurableObjectStateLike, env: unknown, options?: FiregraphDOOptions);
139
+ _fgGetDoc(docId: string): Promise<DORecordWire | null>;
140
+ _fgQuery(filters: QueryFilter[], options?: QueryOptions): Promise<DORecordWire[]>;
141
+ _fgSetDoc(docId: string, record: WritableRecord): Promise<void>;
142
+ _fgUpdateDoc(docId: string, update: UpdatePayload): Promise<void>;
143
+ _fgDeleteDoc(docId: string): Promise<void>;
144
+ /**
145
+ * Execute a list of write ops atomically. DO SQLite's `transactionSync`
146
+ * provides real atomicity — either every statement commits or none do.
147
+ * No statement-count cap applies (contrast with D1's ~100-statement batch
148
+ * limit), so the caller can submit as many ops as they like in one call.
149
+ */
150
+ _fgBatch(ops: BatchOp[]): Promise<void>;
151
+ _fgRemoveNodeCascade(uid: string): Promise<CascadeResult>;
152
+ _fgBulkRemoveEdges(params: FindEdgesParams, _options?: BulkOptions): Promise<BulkResult>;
153
+ /**
154
+ * Wipe every row. Called by the client when tearing down a subgraph DO as
155
+ * part of cascade — the DO itself can't be destroyed (DO IDs persist
156
+ * forever), but its storage can be emptied.
157
+ */
158
+ _fgDestroy(): Promise<void>;
159
+ protected runSchema(): void;
160
+ private execAll;
161
+ private execRun;
162
+ }
163
+
164
+ /**
165
+ * Client-side `StorageBackend` that forwards every operation to a
166
+ * `FiregraphDO` instance over Durable Object RPC.
167
+ *
168
+ * One `DORPCBackend` corresponds to one DO — the root graph's DO, or a
169
+ * subgraph's DO. `subgraph()` returns a new `DORPCBackend` bound to a
170
+ * different DO, identified by deriving a new stable name from the chain of
171
+ * parent UIDs and subgraph names. The library uses `namespace.idFromName()`
172
+ * on that key, so two clients with the same key always reach the same DO.
173
+ *
174
+ * Key invariants:
175
+ *
176
+ * - There is no shared table and no `scope` column. Each DO owns its own
177
+ * flat SQLite database; isolation is physical.
178
+ * - Interactive transactions throw `UNSUPPORTED_OPERATION` — holding a
179
+ * synchronous SQLite transaction across async RPC calls would block the
180
+ * DO's single-threaded executor (see `transactionsUnsupported` below).
181
+ * - `findEdgesGlobal` is deliberately left undefined on this class. The
182
+ * `GraphClient` surfaces the generic "not supported by current storage
183
+ * backend" error before running any query planning, which is both
184
+ * accurate and sidesteps the misleading `QuerySafetyError` that would
185
+ * otherwise fire for scan-unsafe calls. `createDOClient`'s docstring
186
+ * explains the design rationale (no collection-group index across DOs).
187
+ * - `removeNodeCascade` cascades across DOs: when a registry accessor is
188
+ * wired, the backend walks `registry.getSubgraphTopology(aType)` for the
189
+ * node being removed and destroys every descendant subgraph DO before
190
+ * deleting the node itself. Pass `{ deleteSubcollections: false }` to
191
+ * disable the cross-DO fan-out (parent node is still removed, child DOs
192
+ * are left intact — matching the Firestore/SQLite backends' semantic).
193
+ * Without an accessor (e.g. registry-less clients) it cascades within
194
+ * the current DO only.
195
+ */
196
+
197
+ interface DurableObjectIdLike {
198
+ toString(): string;
199
+ }
200
+ /**
201
+ * The RPC surface this backend calls on the DO stub. Every method matches a
202
+ * `_fg…` method on `FiregraphDO`. Kept structurally typed so users can bring
203
+ * their own subclass without the types having to know about it.
204
+ *
205
+ * Reads return `DORecordWire` (plain data — safe through DO structured
206
+ * clone); the backend rewraps each record via `hydrateDORecord` before
207
+ * handing it to the GraphClient, which expects `GraphTimestampImpl`
208
+ * instances (not plain `{seconds, nanoseconds}` objects).
209
+ */
210
+ interface FiregraphStub {
211
+ _fgGetDoc(docId: string): Promise<DORecordWire | null>;
212
+ _fgQuery(filters: QueryFilter[], options?: QueryOptions): Promise<DORecordWire[]>;
213
+ _fgSetDoc(docId: string, record: WritableRecord): Promise<void>;
214
+ _fgUpdateDoc(docId: string, update: UpdatePayload): Promise<void>;
215
+ _fgDeleteDoc(docId: string): Promise<void>;
216
+ _fgBatch(ops: BatchOp[]): Promise<void>;
217
+ _fgRemoveNodeCascade(uid: string): Promise<CascadeResult>;
218
+ _fgBulkRemoveEdges(params: FindEdgesParams, options?: BulkOptions): Promise<BulkResult>;
219
+ _fgDestroy(): Promise<void>;
220
+ }
221
+ interface FiregraphNamespace {
222
+ idFromName(name: string): DurableObjectIdLike;
223
+ get(id: DurableObjectIdLike): FiregraphStub;
224
+ }
225
+ interface DORPCBackendOptions {
226
+ /** Scope path (names-only chain, used for `allowedIn`). Default: `''`. */
227
+ scopePath?: string;
228
+ /**
229
+ * Opaque storage key used to derive the DO instance via
230
+ * `namespace.idFromName(storageKey)`. Defaults to the root key passed to
231
+ * `createDOClient` when the backend is first created.
232
+ */
233
+ storageKey: string;
234
+ /**
235
+ * Live registry accessor used by `removeNodeCascade` to consult the
236
+ * subgraph topology and fan out `_fgDestroy` calls to child subgraph DOs.
237
+ *
238
+ * A function (not a snapshot) so that dynamic-registry clients see the
239
+ * latest definitions after `reloadRegistry()`. Wired by `createDOClient`
240
+ * via a forward reference to the constructed `GraphClient`. When
241
+ * `undefined`, cross-DO cascade is disabled and `removeNodeCascade`
242
+ * cascades within this DO only.
243
+ * @internal
244
+ */
245
+ registryAccessor?: () => GraphRegistry | undefined;
246
+ /**
247
+ * Factory used by `createSiblingClient` to construct a peer `GraphClient`
248
+ * that shares this client's namespace, registry, and other options but
249
+ * targets a different root DO. Wired by `createDOClient`. Leaving it
250
+ * `undefined` (e.g. when `DORPCBackend` is instantiated directly) disables
251
+ * sibling-client construction — `createSiblingClient` will throw.
252
+ *
253
+ * The union return type mirrors `createDOClient`'s two overloads: dynamic
254
+ * mode yields a `DynamicGraphClient`, everything else yields a plain
255
+ * `GraphClient`. `createSiblingClient` narrows at the boundary via its
256
+ * own overload signatures.
257
+ * @internal
258
+ */
259
+ makeSiblingClient?: (siblingStorageKey: string) => GraphClient | DynamicGraphClient;
260
+ }
261
+ declare class DORPCBackend implements StorageBackend {
262
+ readonly collectionPath = "firegraph";
263
+ readonly scopePath: string;
264
+ /** @internal */
265
+ readonly storageKey: string;
266
+ /** @internal */
267
+ readonly namespace: FiregraphNamespace;
268
+ private readonly registryAccessor?;
269
+ /** @internal — see `DORPCBackendOptions.makeSiblingClient` for the union-type rationale. */
270
+ readonly makeSiblingClient?: (siblingStorageKey: string) => GraphClient | DynamicGraphClient;
271
+ private cachedStub;
272
+ constructor(namespace: FiregraphNamespace, options: DORPCBackendOptions);
273
+ private get stub();
274
+ getDoc(docId: string): Promise<StoredGraphRecord | null>;
275
+ query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]>;
276
+ setDoc(docId: string, record: WritableRecord): Promise<void>;
277
+ updateDoc(docId: string, update: UpdatePayload): Promise<void>;
278
+ deleteDoc(docId: string): Promise<void>;
279
+ runTransaction<T>(_fn: (tx: TransactionBackend) => Promise<T>): Promise<T>;
280
+ createBatch(): BatchBackend;
281
+ subgraph(parentNodeUid: string, name: string): StorageBackend;
282
+ removeNodeCascade(uid: string, reader: GraphReader, options?: BulkOptions): Promise<CascadeResult>;
283
+ bulkRemoveEdges(params: FindEdgesParams, _reader: GraphReader, options?: BulkOptions): Promise<BulkResult>;
284
+ /**
285
+ * Wipe this DO's storage. The DO itself can't be deleted — its ID
286
+ * persists forever — but its rows can be emptied, which is what the
287
+ * cascade walk does on every descendant subgraph DO.
288
+ *
289
+ * Exposed on the concrete class (not `StorageBackend`) so generic
290
+ * backend code doesn't reach for it.
291
+ */
292
+ destroy(): Promise<void>;
293
+ /**
294
+ * Tear down every descendant subgraph DO, then wipe this DO's own rows.
295
+ *
296
+ * Invoked by cross-DO cascade: for each node in this DO we enumerate the
297
+ * subgraph topology and recurse into child DOs depth-first before
298
+ * wiping the current DO. The current DO's own rows are destroyed last so
299
+ * that a partial failure mid-recursion leaves the caller's reader able
300
+ * to discover what's still present.
301
+ *
302
+ * @internal
303
+ */
304
+ destroyRecursively(registry: GraphRegistry): Promise<void>;
305
+ }
306
+
307
+ /**
308
+ * `createDOClient` — the user-facing factory for the Cloudflare DO backend.
309
+ *
310
+ * Given a Durable Object namespace binding and a stable root key, returns a
311
+ * `GraphClient` that speaks to a `FiregraphDO` instance. The root key is
312
+ * hashed via `namespace.idFromName()` to derive the DO ID, so two clients
313
+ * instantiated with the same key always reach the same DO — that's how we
314
+ * achieve "subgraphs are auto-provisioned" without a separate allocation
315
+ * step. Subsequent `.subgraph(uid, name)` calls derive child DO IDs from the
316
+ * extended key chain (`${key}/${uid}/${name}`).
317
+ *
318
+ * ## What's supported
319
+ *
320
+ * - **Static registries.** Pass `registry` and every read/write validates
321
+ * and migrates exactly like the Firestore/SQLite backends.
322
+ * - **Dynamic registries.** Pass `registryMode: { mode: 'dynamic' }` and
323
+ * call `defineNodeType` / `defineEdgeType` / `reloadRegistry` as with
324
+ * any other backend. Meta-types live in the root DO by default; pass
325
+ * `registryMode: { mode: 'dynamic', collection: 'meta-root' }` to put
326
+ * them in a separately-addressed DO. Merged mode (static `registry`
327
+ * plus `registryMode`) is also supported.
328
+ * - **Cross-DO cascade.** `removeNodeCascade` consults the registry's
329
+ * subgraph topology and wipes every descendant subgraph DO before
330
+ * deleting the node. Requires a registry; without one it cascades
331
+ * within the current DO only. Pass `{ deleteSubcollections: false }` to
332
+ * keep the node removal but leave every descendant DO intact (mirrors
333
+ * the Firestore/SQLite `bulk` option). In dynamic mode the accessor is
334
+ * live, so cascading a node whose subgraph topology was just added via
335
+ * `defineEdgeType` correctly fans out to the new descendants — **but
336
+ * only after a `reloadRegistry()` call**. A cascade invoked between
337
+ * `defineEdgeType` and `reloadRegistry` sees only the pre-define
338
+ * topology and silently skips newly-declared subgraphs. This is the
339
+ * same trade-off every dynamic-registry backend makes.
340
+ * - **Static migrations** on registry entries (`migrations`,
341
+ * `migrationWriteBack`, `migrationSandbox`) run in-process on the
342
+ * Worker and don't cross the DO RPC boundary. The read-path migration
343
+ * pipeline lives in `GraphClient`, not in `FiregraphDO`.
344
+ *
345
+ * ## Performance note on cascade
346
+ *
347
+ * Cross-DO cascade instantiates (via `namespace.get`) every declared child
348
+ * subgraph DO even when it's empty — Durable Objects have no "does this ID
349
+ * exist" primitive, and the cheapest way to tear a DO down is to issue one
350
+ * RPC. For a node with N declared subgraph segments, expect N+1 RPCs per
351
+ * cascade (one per child DO wipe, one for the parent). Topology width is
352
+ * typically small and bounded by registry size, but keep it in mind for
353
+ * wide fan-out designs.
354
+ *
355
+ * ## What's not supported
356
+ *
357
+ * - **Interactive transactions.** Would require pinning a SQLite
358
+ * transaction open across async RPC calls — see `backend.ts` for the
359
+ * rationale. Use `batch()` for atomic multi-write commits.
360
+ * - **`findEdgesGlobal`.** Cross-DO collection-group queries don't map
361
+ * onto "one DO owns one subgraph's rows" — each subgraph is a separate
362
+ * DO with private SQLite and there's no namespace-wide catalog. The
363
+ * method is intentionally undefined on the backend so `GraphClient`
364
+ * surfaces a generic `UNSUPPORTED_OPERATION` error immediately, before
365
+ * any query planning. Callers that need this should maintain an
366
+ * application-level index DO or run an explicit traversal via
367
+ * `client.subgraph(...)`.
368
+ *
369
+ * ## Binding example
370
+ *
371
+ * ```ts
372
+ * // worker.ts
373
+ * export { FiregraphDO } from '@typicalday/firegraph/cloudflare';
374
+ *
375
+ * export default {
376
+ * async fetch(req: Request, env: Env) {
377
+ * const client = createDOClient(env.GRAPH, 'main', { registry });
378
+ * const project = await client.getNode('project', projectUid);
379
+ * return Response.json(project);
380
+ * },
381
+ * };
382
+ * ```
383
+ *
384
+ * ```toml
385
+ * # wrangler.toml
386
+ * [[durable_objects.bindings]]
387
+ * name = "GRAPH"
388
+ * class_name = "FiregraphDO"
389
+ *
390
+ * [[migrations]]
391
+ * tag = "v1"
392
+ * new_sqlite_classes = ["FiregraphDO"]
393
+ * ```
394
+ */
395
+
396
+ /**
397
+ * Options for `createDOClient`. Same shape as `GraphClientOptions`; the DO
398
+ * backend does not expose a table label of its own — the DO owns its SQLite
399
+ * schema (see `FiregraphDOOptions.table`, defaults to `'firegraph'`) and
400
+ * that choice isn't surfaced through the client factory.
401
+ */
402
+ type DOClientOptions = GraphClientOptions;
403
+ /**
404
+ * Create a `GraphClient` backed by a `FiregraphDO` Durable Object.
405
+ *
406
+ * @param namespace The DO namespace binding (`env.GRAPH` in Worker code).
407
+ * @param rootKey Stable name for the root graph's DO. The same value
408
+ * always addresses the same DO — treat it as the graph's
409
+ * identity. Subgraph DOs derive their names from this.
410
+ * @param options Optional `GraphClientOptions` (registry, query mode,
411
+ * `registryMode` for dynamic registries, etc.).
412
+ * When `registryMode` is set the return type is
413
+ * narrowed to `DynamicGraphClient`.
414
+ */
415
+ declare function createDOClient(namespace: FiregraphNamespace, rootKey: string, options: DOClientOptions & {
416
+ registryMode: DynamicRegistryConfig;
417
+ }): DynamicGraphClient;
418
+ declare function createDOClient(namespace: FiregraphNamespace, rootKey: string, options?: DOClientOptions): GraphClient;
419
+ /**
420
+ * Construct a peer `GraphClient` that shares `client`'s DO namespace and
421
+ * construction options but targets a different root DO (i.e. a different
422
+ * root key — typically another tenant, workspace, or shard).
423
+ *
424
+ * This is the cheap, ergonomic way to talk to another root graph from
425
+ * inside a single Worker request without re-plumbing `createDOClient`'s
426
+ * arguments. The namespace binding plus the options snapshot captured at
427
+ * the original `createDOClient` call (registry, query mode, migration
428
+ * sandbox, `registryMode`, etc.) are inherited by the sibling.
429
+ *
430
+ * Works from any DO-backed client — root or subgraph. Passing a client
431
+ * that wasn't produced by `createDOClient` (e.g. a Firestore-backed
432
+ * client, or a `DORPCBackend` instantiated directly without the sibling
433
+ * factory wired in) throws `UNSUPPORTED_OPERATION` with an explanation.
434
+ *
435
+ * ## Dynamic-registry caveat
436
+ *
437
+ * When the original client uses `registryMode: 'dynamic'`, siblings
438
+ * inherit the *config* (so they're also dynamic clients) but NOT the
439
+ * compiled runtime state. Meta-type nodes and `reloadRegistry()` results
440
+ * are per-client; a sibling's `defineNodeType`/`defineEdgeType` calls go
441
+ * to that sibling's own root DO, and its registry must be independently
442
+ * populated and reloaded. If every tenant shares the same schema, pass a
443
+ * static `registry` instead of dynamic mode — static registries ARE
444
+ * inherited verbatim.
445
+ *
446
+ * @param client A client previously returned by `createDOClient`.
447
+ * @param siblingRootKey Root key for the peer DO. Same validation rules
448
+ * as `createDOClient`'s `rootKey`: non-empty,
449
+ * no `/`.
450
+ */
451
+ declare function createSiblingClient(client: GraphClient, siblingRootKey: string): GraphClient;
452
+ declare function createSiblingClient(client: DynamicGraphClient, siblingRootKey: string): DynamicGraphClient;
453
+
454
+ export { type BatchOp, type DOClientOptions, DORPCBackend, type DORPCBackendOptions, type DOSqlCursor, type DOSqlExecutor, type DOStorage, type DurableObjectIdLike, type DurableObjectStateLike, FiregraphDO, type FiregraphDOOptions, type FiregraphNamespace, type FiregraphStub, createDOClient, createSiblingClient };