@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.
- package/dist/backend-73p5Blx7.d.cts +97 -0
- package/dist/backend-BrqFkbid.d.ts +97 -0
- package/dist/backend.cjs +222 -0
- package/dist/backend.cjs.map +1 -0
- package/dist/backend.d.cts +122 -0
- package/dist/backend.d.ts +122 -0
- package/dist/backend.js +136 -0
- package/dist/backend.js.map +1 -0
- package/dist/{chunk-6OQW5OKO.js → chunk-5753Y42M.js} +12 -4
- package/dist/chunk-5753Y42M.js.map +1 -0
- package/dist/{chunk-YUXOALMR.js → chunk-LZOIQHYN.js} +69 -92
- package/dist/chunk-LZOIQHYN.js.map +1 -0
- package/dist/chunk-R7CRGYY4.js +94 -0
- package/dist/chunk-R7CRGYY4.js.map +1 -0
- package/dist/{chunk-KFA7G37W.js → chunk-SU4FNLC3.js} +32 -30
- package/dist/chunk-SU4FNLC3.js.map +1 -0
- package/dist/chunk-TYYPRVIE.js +57 -0
- package/dist/chunk-TYYPRVIE.js.map +1 -0
- package/dist/{do-sqlite.cjs → cloudflare/index.cjs} +1538 -1420
- package/dist/cloudflare/index.cjs.map +1 -0
- package/dist/cloudflare/index.d.cts +454 -0
- package/dist/cloudflare/index.d.ts +454 -0
- package/dist/cloudflare/index.js +822 -0
- package/dist/cloudflare/index.js.map +1 -0
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/editor/client/assets/index-Bq2bfzeY.js +411 -0
- package/dist/editor/client/index.html +1 -1
- package/dist/editor/server/index.mjs +6481 -6327
- package/dist/index.cjs +165 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -138
- package/dist/index.d.ts +14 -138
- package/dist/index.js +31 -22
- package/dist/index.js.map +1 -1
- package/dist/query-client/index.cjs +30 -28
- package/dist/query-client/index.cjs.map +1 -1
- package/dist/query-client/index.d.cts +2 -2
- package/dist/query-client/index.d.ts +2 -2
- package/dist/query-client/index.js +1 -1
- package/dist/react.cjs +0 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +0 -1
- package/dist/react.js.map +1 -1
- package/dist/scope-path-B1G3YiA7.d.cts +139 -0
- package/dist/scope-path-B1G3YiA7.d.ts +139 -0
- package/dist/{serialization-C6JNNOCS.js → serialization-ZZ7RSDRX.js} +2 -2
- package/dist/svelte.cjs +0 -2
- package/dist/svelte.cjs.map +1 -1
- package/dist/svelte.js +0 -2
- package/dist/svelte.js.map +1 -1
- package/dist/{types-BVtx9zLv.d.cts → types-DOemdlVA.d.cts} +20 -2
- package/dist/{types-BVtx9zLv.d.ts → types-DOemdlVA.d.ts} +20 -2
- package/package.json +39 -40
- package/dist/chunk-6OQW5OKO.js.map +0 -1
- package/dist/chunk-KFA7G37W.js.map +0 -1
- package/dist/chunk-WOAJRVHD.js +0 -699
- package/dist/chunk-WOAJRVHD.js.map +0 -1
- package/dist/chunk-YUXOALMR.js.map +0 -1
- package/dist/d1.cjs +0 -2416
- package/dist/d1.cjs.map +0 -1
- package/dist/d1.d.cts +0 -54
- package/dist/d1.d.ts +0 -54
- package/dist/d1.js +0 -75
- package/dist/d1.js.map +0 -1
- package/dist/do-sqlite.cjs.map +0 -1
- package/dist/do-sqlite.d.cts +0 -41
- package/dist/do-sqlite.d.ts +0 -41
- package/dist/do-sqlite.js +0 -78
- package/dist/do-sqlite.js.map +0 -1
- package/dist/editor/client/assets/index-tyFcX6qG.js +0 -411
- /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-73p5Blx7.cjs';
|
|
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.cjs';
|
|
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 };
|