@typicalday/firegraph 0.12.0 → 0.13.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 (70) hide show
  1. package/README.md +317 -73
  2. package/dist/backend-DuvHGgK1.d.cts +1897 -0
  3. package/dist/backend-DuvHGgK1.d.ts +1897 -0
  4. package/dist/backend.cjs +222 -3
  5. package/dist/backend.cjs.map +1 -1
  6. package/dist/backend.d.cts +25 -5
  7. package/dist/backend.d.ts +25 -5
  8. package/dist/backend.js +197 -4
  9. package/dist/backend.js.map +1 -1
  10. package/dist/chunk-2DHMNTV6.js +16 -0
  11. package/dist/chunk-2DHMNTV6.js.map +1 -0
  12. package/dist/chunk-4MMQ5W74.js +288 -0
  13. package/dist/chunk-4MMQ5W74.js.map +1 -0
  14. package/dist/chunk-D4J7Z4FE.js +67 -0
  15. package/dist/chunk-D4J7Z4FE.js.map +1 -0
  16. package/dist/chunk-N5HFDWQX.js +23 -0
  17. package/dist/chunk-N5HFDWQX.js.map +1 -0
  18. package/dist/chunk-PAD7WFFU.js +573 -0
  19. package/dist/chunk-PAD7WFFU.js.map +1 -0
  20. package/dist/{chunk-AWW4MUJ5.js → chunk-TK64DNVK.js} +12 -1
  21. package/dist/chunk-TK64DNVK.js.map +1 -0
  22. package/dist/{chunk-HONQY4HF.js → chunk-WRTFC5NG.js} +362 -17
  23. package/dist/chunk-WRTFC5NG.js.map +1 -0
  24. package/dist/client-BKi3vk0Q.d.ts +34 -0
  25. package/dist/client-BrsaXtDV.d.cts +34 -0
  26. package/dist/cloudflare/index.cjs +930 -3
  27. package/dist/cloudflare/index.cjs.map +1 -1
  28. package/dist/cloudflare/index.d.cts +213 -12
  29. package/dist/cloudflare/index.d.ts +213 -12
  30. package/dist/cloudflare/index.js +562 -281
  31. package/dist/cloudflare/index.js.map +1 -1
  32. package/dist/codegen/index.d.cts +1 -1
  33. package/dist/codegen/index.d.ts +1 -1
  34. package/dist/errors-BRc3I_eH.d.cts +73 -0
  35. package/dist/errors-BRc3I_eH.d.ts +73 -0
  36. package/dist/firestore-enterprise/index.cjs +3877 -0
  37. package/dist/firestore-enterprise/index.cjs.map +1 -0
  38. package/dist/firestore-enterprise/index.d.cts +141 -0
  39. package/dist/firestore-enterprise/index.d.ts +141 -0
  40. package/dist/firestore-enterprise/index.js +985 -0
  41. package/dist/firestore-enterprise/index.js.map +1 -0
  42. package/dist/firestore-standard/index.cjs +3117 -0
  43. package/dist/firestore-standard/index.cjs.map +1 -0
  44. package/dist/firestore-standard/index.d.cts +49 -0
  45. package/dist/firestore-standard/index.d.ts +49 -0
  46. package/dist/firestore-standard/index.js +283 -0
  47. package/dist/firestore-standard/index.js.map +1 -0
  48. package/dist/index.cjs +590 -550
  49. package/dist/index.cjs.map +1 -1
  50. package/dist/index.d.cts +9 -37
  51. package/dist/index.d.ts +9 -37
  52. package/dist/index.js +178 -555
  53. package/dist/index.js.map +1 -1
  54. package/dist/{registry-Fi074zVa.d.ts → registry-Bc7h6WTM.d.cts} +1 -1
  55. package/dist/{registry-B1qsVL0E.d.cts → registry-C2KUPVZj.d.ts} +1 -1
  56. package/dist/{scope-path-B1G3YiA7.d.cts → scope-path-CROFZGr9.d.cts} +1 -56
  57. package/dist/{scope-path-B1G3YiA7.d.ts → scope-path-CROFZGr9.d.ts} +1 -56
  58. package/dist/sqlite/index.cjs +3631 -0
  59. package/dist/sqlite/index.cjs.map +1 -0
  60. package/dist/sqlite/index.d.cts +111 -0
  61. package/dist/sqlite/index.d.ts +111 -0
  62. package/dist/sqlite/index.js +1164 -0
  63. package/dist/sqlite/index.js.map +1 -0
  64. package/package.json +33 -3
  65. package/dist/backend-BsR0lnFL.d.ts +0 -200
  66. package/dist/backend-Ct-fLlkG.d.cts +0 -200
  67. package/dist/chunk-AWW4MUJ5.js.map +0 -1
  68. package/dist/chunk-HONQY4HF.js.map +0 -1
  69. package/dist/types-DxYLy8Ol.d.cts +0 -770
  70. package/dist/types-DxYLy8Ol.d.ts +0 -770
@@ -1,770 +0,0 @@
1
- import { WhereFilterOp, Timestamp, FieldValue } from '@google-cloud/firestore';
2
-
3
- /**
4
- * Firegraph Configuration — project-level config file support.
5
- *
6
- * Projects create a `firegraph.config.ts` (or `.js`/`.mjs`) in their root:
7
- *
8
- * @example
9
- * ```ts
10
- * import { defineConfig } from 'firegraph';
11
- *
12
- * export default defineConfig({
13
- * entities: './entities',
14
- * project: 'my-project',
15
- * collection: 'graph',
16
- * });
17
- * ```
18
- */
19
-
20
- /** Display contexts where views can appear. */
21
- type ViewContext = 'listing' | 'detail' | 'inline';
22
- /** View resolution configuration for a single entity type. */
23
- interface ViewResolverConfig {
24
- /** Default view name (e.g. 'card'). Falls back to 'json' if unset. */
25
- default?: string;
26
- /** View to use in NodeBrowser listing rows. */
27
- listing?: string;
28
- /** View to use on the NodeDetail page. */
29
- detail?: string;
30
- /** View to use for inline/embedded previews (edge rows, traversal). */
31
- inline?: string;
32
- }
33
- /** Declarative view defaults, keyed by entity type. */
34
- interface ViewDefaultsConfig {
35
- /** Node view defaults keyed by aType (e.g. 'user', 'task'). */
36
- nodes?: Record<string, ViewResolverConfig>;
37
- /** Edge view defaults keyed by axbType (e.g. 'hasDeparture'). */
38
- edges?: Record<string, ViewResolverConfig>;
39
- }
40
- /** Project-level firegraph configuration. */
41
- interface FiregraphConfig {
42
- /** Path to entities directory (per-entity folder convention). */
43
- entities?: string;
44
- /** GCP project ID. */
45
- project?: string;
46
- /** Firestore collection path (default: 'graph'). */
47
- collection?: string;
48
- /** Firestore emulator address (e.g. '127.0.0.1:8080'). */
49
- emulator?: string;
50
- /**
51
- * Query execution backend.
52
- *
53
- * - `'pipeline'` (default) — Uses Firestore Pipeline API. Requires Enterprise
54
- * Firestore. Enables indexless queries on `data.*` fields.
55
- * - `'standard'` — Uses standard Firestore `.where().get()` queries. Not
56
- * recommended for production. See README for risk details.
57
- *
58
- * When the emulator is active, always falls back to `'standard'`.
59
- */
60
- queryMode?: QueryMode;
61
- /**
62
- * AI chat configuration. Auto-detects `claude` CLI on PATH by default.
63
- * Set to `false` to disable chat even if claude is available.
64
- */
65
- chat?: false | {
66
- /** Claude model to use (default: 'sonnet'). */
67
- model?: string;
68
- /** Maximum concurrent claude processes (default: 2). */
69
- maxConcurrency?: number;
70
- };
71
- /** Editor-specific settings. */
72
- editor?: {
73
- /** Server port (default: 3883). */
74
- port?: number;
75
- /** Force read-only mode. */
76
- readonly?: boolean;
77
- };
78
- /** Declarative view defaults per entity type (overrides per-entity meta.json). */
79
- viewDefaults?: ViewDefaultsConfig;
80
- /**
81
- * Dynamic registry mode. When set, the editor loads type definitions
82
- * from Firestore meta-nodes in addition to filesystem entities.
83
- * Filesystem types take precedence on name conflicts.
84
- */
85
- registryMode?: DynamicRegistryConfig;
86
- }
87
- /**
88
- * Identity function providing type-checking and autocomplete for config files.
89
- *
90
- * @example
91
- * ```ts
92
- * import { defineConfig } from 'firegraph';
93
- * export default defineConfig({ entities: './entities' });
94
- * ```
95
- */
96
- declare function defineConfig(config: FiregraphConfig): FiregraphConfig;
97
- /**
98
- * Resolve which view to show for a given entity.
99
- *
100
- * 1. If `context` is provided and a context-specific default exists, use it.
101
- * 2. Falls back to `resolverConfig.default`.
102
- * 3. Ultimate fallback: `'json'`.
103
- *
104
- * Only returns view names that exist in `availableViewNames`.
105
- */
106
- declare function resolveView(resolverConfig: ViewResolverConfig | undefined, availableViewNames: string[], context?: ViewContext): string;
107
-
108
- /**
109
- * Backend-agnostic timestamp.
110
- *
111
- * Structurally compatible with `@google-cloud/firestore`'s `Timestamp` so
112
- * that records returned by either the Firestore or SQLite backend can be
113
- * consumed through the same `StoredGraphRecord` shape.
114
- *
115
- * Firestore's native `Timestamp` already satisfies this interface, so
116
- * existing Firestore consumers see no behavior change. The SQLite backend
117
- * returns instances of `GraphTimestampImpl` which also satisfies it.
118
- */
119
- interface GraphTimestamp {
120
- readonly seconds: number;
121
- readonly nanoseconds: number;
122
- toDate(): Date;
123
- toMillis(): number;
124
- }
125
-
126
- interface GraphRecord {
127
- aType: string;
128
- aUid: string;
129
- axbType: string;
130
- bType: string;
131
- bUid: string;
132
- data: Record<string, unknown>;
133
- createdAt: Timestamp | FieldValue;
134
- updatedAt: Timestamp | FieldValue;
135
- /** Schema version — set automatically when the registry entry has migrations. */
136
- v?: number;
137
- }
138
- interface StoredGraphRecord {
139
- aType: string;
140
- aUid: string;
141
- axbType: string;
142
- bType: string;
143
- bUid: string;
144
- data: Record<string, unknown>;
145
- /**
146
- * Backend-agnostic timestamp. Firestore returns its native `Timestamp`
147
- * (which structurally satisfies `GraphTimestamp`); the SQLite backends
148
- * return a `GraphTimestampImpl` instance.
149
- */
150
- createdAt: GraphTimestamp;
151
- updatedAt: GraphTimestamp;
152
- /** Schema version — set automatically when the registry entry has migrations. */
153
- v?: number;
154
- }
155
- interface WhereClause {
156
- field: string;
157
- op: '==' | '!=' | '<' | '<=' | '>' | '>=';
158
- value: unknown;
159
- }
160
- interface FindEdgesParams {
161
- aType?: string;
162
- aUid?: string;
163
- axbType?: string;
164
- bType?: string;
165
- bUid?: string;
166
- limit?: number;
167
- orderBy?: {
168
- field: string;
169
- direction?: 'asc' | 'desc';
170
- };
171
- where?: WhereClause[];
172
- /** Set to true to allow queries that may cause full collection scans. */
173
- allowCollectionScan?: boolean;
174
- }
175
- interface FindNodesParams {
176
- aType: string;
177
- limit?: number;
178
- orderBy?: {
179
- field: string;
180
- direction?: 'asc' | 'desc';
181
- };
182
- where?: WhereClause[];
183
- /** Set to true to allow queries that may cause full collection scans. */
184
- allowCollectionScan?: boolean;
185
- }
186
- interface QueryOptions {
187
- limit?: number;
188
- orderBy?: {
189
- field: string;
190
- direction?: 'asc' | 'desc';
191
- };
192
- }
193
- type QueryPlan = {
194
- strategy: 'get';
195
- docId: string;
196
- } | {
197
- strategy: 'query';
198
- filters: QueryFilter[];
199
- options?: QueryOptions;
200
- };
201
- interface QueryFilter {
202
- field: string;
203
- op: WhereFilterOp;
204
- value: unknown;
205
- }
206
- /**
207
- * An executable migration function that transforms data from one schema
208
- * version to the next. Can be synchronous or asynchronous.
209
- */
210
- type MigrationFn = (data: Record<string, unknown>) => Record<string, unknown> | Promise<Record<string, unknown>>;
211
- /**
212
- * A single migration step in a registry entry.
213
- * Transforms data from `fromVersion` to `toVersion`.
214
- */
215
- interface MigrationStep {
216
- fromVersion: number;
217
- toVersion: number;
218
- up: MigrationFn;
219
- }
220
- /**
221
- * A stored migration step for dynamic registry types.
222
- * The `up` field is a source code string that will be compiled at runtime.
223
- *
224
- * @example
225
- * ```ts
226
- * { fromVersion: 0, toVersion: 1, up: "(data) => ({ ...data, status: 'draft' })" }
227
- * ```
228
- */
229
- interface StoredMigrationStep {
230
- fromVersion: number;
231
- toVersion: number;
232
- up: string;
233
- }
234
- /**
235
- * Pluggable executor interface for compiling migration function source
236
- * strings into executable functions. Used for dynamic registry migrations.
237
- *
238
- * The default executor uses SES (Secure ECMAScript) Compartments with
239
- * JSON marshaling for isolation. Users can supply an alternative via
240
- * `GraphClientOptions.migrationSandbox`.
241
- */
242
- type MigrationExecutor = (source: string) => MigrationFn;
243
- /** Write-back mode for auto-migrated records. */
244
- type MigrationWriteBack = 'off' | 'eager' | 'background';
245
- /**
246
- * One field in a composite index. The string shorthand form defaults to
247
- * ascending order; use the object form when a field needs to be indexed
248
- * descending (e.g., pagination by `{ path: 'updatedAt', desc: true }`).
249
- */
250
- interface IndexFieldSpec {
251
- /**
252
- * Field path. Top-level firegraph fields (`aType`, `aUid`, `axbType`,
253
- * `bType`, `bUid`, `createdAt`, `updatedAt`, `v`) resolve to their
254
- * underlying columns. Dotted paths like `'data.status'` or
255
- * `'data.author.name'` index into the JSON data payload.
256
- *
257
- * Each dotted component must match `/^[A-Za-z_][A-Za-z0-9_-]*$/` — keys
258
- * with dots, quotes, brackets, spaces, or other syntax characters are
259
- * rejected at DDL build time. Indexes on exotic keys are not supported
260
- * because SQLite expression indexes must match the query compiler's
261
- * output verbatim, and inlining quoted path components into DDL would
262
- * desynchronize the two compilers. If you need to filter by an exotic
263
- * key, use `replaceNode` / `replaceEdge` writes rather than an indexed
264
- * field.
265
- */
266
- path: string;
267
- /** Descending order; defaults to ascending. */
268
- desc?: boolean;
269
- }
270
- /**
271
- * Declarative secondary index. Translators emit a `CREATE INDEX` statement
272
- * (SQLite) or a `FirestoreIndex` composite (Firestore) per spec.
273
- *
274
- * Composite indexes support the prefix of their `fields` list — a spec
275
- * `{ fields: ['aType', 'axbType'] }` also covers queries filtering on
276
- * `aType` alone.
277
- *
278
- * @example
279
- * ```ts
280
- * // Plain composite on top-level fields
281
- * { fields: ['aType', 'axbType'] }
282
- *
283
- * // Mixed string + object form; `updatedAt` descending
284
- * { fields: ['aType', 'aUid', 'axbType', { path: 'updatedAt', desc: true }] }
285
- *
286
- * // JSON data-field index (SQLite: expression index on json_extract)
287
- * { fields: ['aType', 'axbType', 'data.status'] }
288
- *
289
- * // Partial index (SQLite only — Firestore ignores the `where` clause)
290
- * { fields: ['aType'], where: "json_extract(data, '$.archived') = 0" }
291
- * ```
292
- */
293
- interface IndexSpec {
294
- /**
295
- * Ordered field list. String shorthand = ascending. Use `IndexFieldSpec`
296
- * form to mark individual fields descending.
297
- */
298
- fields: Array<string | IndexFieldSpec>;
299
- /**
300
- * Partial-index predicate. Applied verbatim after `WHERE` in the emitted
301
- * SQLite DDL. Ignored (with a one-time warning) by the Firestore
302
- * generator — Firestore composite indexes do not support predicates.
303
- */
304
- where?: string;
305
- }
306
- interface RegistryEntry {
307
- aType: string;
308
- axbType: string;
309
- bType: string;
310
- /** JSON Schema object for the data payload. */
311
- jsonSchema?: object;
312
- description?: string;
313
- inverseLabel?: string;
314
- /** Data field to use as the display title (e.g. 'name', 'date'). */
315
- titleField?: string;
316
- /** Data field to use as the display subtitle (e.g. 'status', 'difficulty'). */
317
- subtitleField?: string;
318
- /**
319
- * Scope patterns constraining where this type can exist.
320
- * Omit or leave empty to allow everywhere (backwards compatible).
321
- *
322
- * Patterns:
323
- * - `'root'` — top-level collection only
324
- * - `'agents'` — exact subgraph name match
325
- * - `'workflow/agents'` — exact path match
326
- * - `'*​/agents'` — `*` matches one segment
327
- * - `'**​/agents'` — `**` matches zero or more segments
328
- */
329
- allowedIn?: string[];
330
- /**
331
- * Subgraph name where cross-graph edges of this type live.
332
- *
333
- * When set, forward traversal queries the named subgraph under each
334
- * source node (e.g., `{collection}/{sourceUid}/{targetGraph}`) instead
335
- * of the current collection. The subgraph contains both the edge
336
- * documents and the target nodes they reference.
337
- *
338
- * Reverse traversal is unaffected — if you're already in the subgraph,
339
- * the edges are local.
340
- *
341
- * Only applies to edge entries (not node self-loop entries).
342
- * Must be a single segment (no `/`).
343
- *
344
- * @example
345
- * ```ts
346
- * { aType: 'task', axbType: 'assignedTo', bType: 'agent', targetGraph: 'workflow' }
347
- * // Forward traversal from task1: queries {collection}/task1/workflow
348
- * ```
349
- */
350
- targetGraph?: string;
351
- /**
352
- * Schema version for this type's data payload.
353
- * **Computed automatically** from `migrations` as `max(toVersion)`.
354
- * Do not set directly — provide migrations instead.
355
- */
356
- schemaVersion?: number;
357
- /**
358
- * Ordered list of migrations to transform data from older versions
359
- * to the current version. The schema version is derived as the highest
360
- * `toVersion` in this array.
361
- */
362
- migrations?: MigrationStep[];
363
- /**
364
- * Per-entry write-back override for auto-migrated records.
365
- * Takes precedence over `GraphClientOptions.migrationWriteBack`.
366
- * Omit to inherit the global setting.
367
- */
368
- migrationWriteBack?: MigrationWriteBack;
369
- /**
370
- * Secondary indexes tied to this triple. Each spec becomes a single
371
- * backend-native composite index scoped to rows matching
372
- * `(aType, axbType, bType)` — though the DDL does not currently restrict
373
- * by triple, so authors should think of these as globally-applied indexes
374
- * declared on the triple's behalf.
375
- *
376
- * Use this to accelerate `findNodes` / `findEdges` queries that filter
377
- * on `data.*` fields or compose with firegraph's top-level fields in ways
378
- * the default preset doesn't cover.
379
- */
380
- indexes?: IndexSpec[];
381
- }
382
- /** Topology declaration for an edge (from edge.json). */
383
- interface EdgeTopology {
384
- from: string | string[];
385
- to: string | string[];
386
- inverseLabel?: string;
387
- /**
388
- * Subgraph name where cross-graph edges of this type live.
389
- * See `RegistryEntry.targetGraph` for full documentation.
390
- */
391
- targetGraph?: string;
392
- }
393
- /** A discovered entity from the per-entity folder convention. */
394
- interface DiscoveredEntity {
395
- kind: 'node' | 'edge';
396
- name: string;
397
- /** Parsed JSON Schema for the data payload. */
398
- schema: object;
399
- /** Edge topology (only for edges). */
400
- topology?: EdgeTopology;
401
- description?: string;
402
- /** Data field to use as the display title (e.g. 'name', 'date'). */
403
- titleField?: string;
404
- /** Data field to use as the display subtitle (e.g. 'status', 'difficulty'). */
405
- subtitleField?: string;
406
- /** View defaults from meta.json. */
407
- viewDefaults?: ViewResolverConfig;
408
- /** Absolute path to views.ts if present. */
409
- viewsPath?: string;
410
- /** Sample data from sample.json. */
411
- sampleData?: Record<string, unknown>;
412
- /** Scope patterns constraining where this type can exist in subgraphs. */
413
- allowedIn?: string[];
414
- /** Subgraph name where cross-graph edges of this type live. */
415
- targetGraph?: string;
416
- /** Migration steps loaded from migrations.ts. */
417
- migrations?: MigrationStep[];
418
- /** Per-entity write-back override from meta.json. */
419
- migrationWriteBack?: MigrationWriteBack;
420
- /** Secondary indexes loaded from meta.json (`indexes` field). */
421
- indexes?: IndexSpec[];
422
- }
423
- /** Result of scanning an entities directory. */
424
- interface DiscoveryResult {
425
- nodes: Map<string, DiscoveredEntity>;
426
- edges: Map<string, DiscoveredEntity>;
427
- }
428
- /** Controls which Firestore query backend is used. */
429
- type QueryMode = 'pipeline' | 'standard';
430
- /**
431
- * Configuration for dynamic registry mode where type definitions
432
- * are stored as graph data (meta-nodes) rather than in code.
433
- */
434
- interface DynamicRegistryConfig {
435
- mode: 'dynamic';
436
- /**
437
- * Collection path for meta-type nodes (`nodeType`, `edgeType`).
438
- * Defaults to the main `collectionPath` if omitted.
439
- */
440
- collection?: string;
441
- }
442
- /** Options for defineNodeType / defineEdgeType beyond the core fields. */
443
- interface DefineTypeOptions {
444
- /** Data field to use as the display title (e.g. 'name', 'date'). */
445
- titleField?: string;
446
- /** Data field to use as the display subtitle (e.g. 'status', 'difficulty'). */
447
- subtitleField?: string;
448
- /** Mustache HTML template for rendering this type in the editor. */
449
- viewTemplate?: string;
450
- /** Scoped CSS for the view template (injected via Shadow DOM). */
451
- viewCss?: string;
452
- /** Scope patterns constraining where this type can exist in subgraphs. */
453
- allowedIn?: string[];
454
- /**
455
- * Migration steps. Accepts function objects (auto-serialized via .toString())
456
- * or strings (stored as-is). The schema version is derived as the highest
457
- * `toVersion` in this array.
458
- */
459
- migrations?: Array<{
460
- fromVersion: number;
461
- toVersion: number;
462
- up: MigrationFn | string;
463
- }>;
464
- /** Per-type write-back override for auto-migrated records. */
465
- migrationWriteBack?: MigrationWriteBack;
466
- }
467
- /** Data shape stored in a `nodeType` meta-node. */
468
- interface NodeTypeData {
469
- name: string;
470
- jsonSchema: object;
471
- description?: string;
472
- titleField?: string;
473
- subtitleField?: string;
474
- viewTemplate?: string;
475
- viewCss?: string;
476
- allowedIn?: string[];
477
- migrations?: StoredMigrationStep[];
478
- migrationWriteBack?: MigrationWriteBack;
479
- }
480
- /** Data shape stored in an `edgeType` meta-node. */
481
- interface EdgeTypeData {
482
- name: string;
483
- from: string | string[];
484
- to: string | string[];
485
- jsonSchema?: object;
486
- inverseLabel?: string;
487
- description?: string;
488
- titleField?: string;
489
- subtitleField?: string;
490
- viewTemplate?: string;
491
- viewCss?: string;
492
- allowedIn?: string[];
493
- targetGraph?: string;
494
- migrations?: StoredMigrationStep[];
495
- migrationWriteBack?: MigrationWriteBack;
496
- }
497
- type ScanProtection = 'error' | 'warn' | 'off';
498
- interface GraphClientOptions {
499
- /**
500
- * Static registry built from code/discovery.
501
- *
502
- * When provided alone, all writes are validated against this registry.
503
- *
504
- * When provided together with `registryMode`, operates in **merged mode**:
505
- * static entries take priority and cannot be overridden by dynamic
506
- * definitions. Dynamic definitions can only add new types. The merged
507
- * client is returned as a `DynamicGraphClient`.
508
- */
509
- registry?: GraphRegistry;
510
- /** Dynamic registry mode — type definitions stored as graph data. */
511
- registryMode?: DynamicRegistryConfig;
512
- /**
513
- * Query execution backend.
514
- *
515
- * - `'pipeline'` (default) — Uses Firestore Pipeline API. Requires Enterprise
516
- * Firestore. Enables indexless queries on `data.*` fields.
517
- * - `'standard'` — Uses standard Firestore `.where().get()` queries. Requires
518
- * composite indexes for `data.*` filters or risks full collection scans
519
- * (Enterprise) / query failures (Standard Firestore).
520
- *
521
- * When `FIRESTORE_EMULATOR_HOST` is set, the client auto-falls back to
522
- * `'standard'` regardless of this setting (emulator doesn't support pipelines).
523
- */
524
- queryMode?: QueryMode;
525
- /**
526
- * Controls query safety behavior for full collection scan prevention.
527
- *
528
- * - `'error'` (default) — Throws `QuerySafetyError` for queries that would
529
- * likely cause a full collection scan. Override per-query with
530
- * `allowCollectionScan: true`.
531
- * - `'warn'` — Logs a warning but executes the query.
532
- * - `'off'` — No scan protection.
533
- */
534
- scanProtection?: ScanProtection;
535
- /**
536
- * Global default for write-back of auto-migrated records on read.
537
- *
538
- * - `'off'` (default) — Migrated data is returned but NOT written back.
539
- * - `'eager'` — Migrated data is written back immediately after migration.
540
- * - `'background'` — Write-back happens asynchronously; errors are logged.
541
- *
542
- * Per-entry `migrationWriteBack` on `RegistryEntry` overrides this setting.
543
- */
544
- migrationWriteBack?: MigrationWriteBack;
545
- /**
546
- * Custom executor for compiling dynamic registry migration source strings.
547
- * Defaults to SES Compartments with JSON marshaling. Supply an
548
- * alternative for custom sandboxing.
549
- *
550
- * Only used for dynamic registry migrations — static registry migrations
551
- * are already in-memory functions and never go through this executor.
552
- */
553
- migrationSandbox?: MigrationExecutor;
554
- }
555
- interface GraphRegistry {
556
- validate(aType: string, axbType: string, bType: string, data: unknown, scopePath?: string): void;
557
- lookup(aType: string, axbType: string, bType: string): RegistryEntry | undefined;
558
- /** Return all entries matching the given axbType (edge relation name). */
559
- lookupByAxbType(axbType: string): ReadonlyArray<RegistryEntry>;
560
- /**
561
- * Return every edge entry originating from `aType` that has `targetGraph`
562
- * set — i.e. the direct subgraph children of nodes of this type.
563
- *
564
- * Used by backends that need to enumerate a node's subgraph DOs without
565
- * walking the graph. Each returned entry carries both `axbType` (the edge
566
- * label that introduces the subgraph) and `targetGraph` (the subgraph
567
- * segment name).
568
- *
569
- * Entries are deduplicated by `targetGraph` alone — the physical subgraph
570
- * store is addressed by `(parentUid, targetGraph)`, so multiple edge
571
- * relations (distinct `axbType` or `bType`) pointing into the same segment
572
- * collapse to a single representative entry. The first-declared entry
573
- * wins the collision. Callers only care about the subgraph name, not the
574
- * originating relation or target node type.
575
- */
576
- getSubgraphTopology(aType: string): ReadonlyArray<RegistryEntry>;
577
- entries(): ReadonlyArray<RegistryEntry>;
578
- }
579
- interface GraphReader {
580
- getNode(uid: string): Promise<StoredGraphRecord | null>;
581
- getEdge(aUid: string, axbType: string, bUid: string): Promise<StoredGraphRecord | null>;
582
- edgeExists(aUid: string, axbType: string, bUid: string): Promise<boolean>;
583
- findEdges(params: FindEdgesParams): Promise<StoredGraphRecord[]>;
584
- findNodes(params: FindNodesParams): Promise<StoredGraphRecord[]>;
585
- }
586
- interface GraphWriter {
587
- /**
588
- * Write a node, deep-merging into any existing record.
589
- *
590
- * Nested objects are merged recursively — sibling keys at any depth
591
- * survive. Arrays are terminal (replaced as a unit, not element-merged).
592
- * `undefined` values are omitted; `null` is preserved. To delete a field,
593
- * pass the `deleteField()` sentinel as its value.
594
- *
595
- * Use {@link replaceNode} when you want full-document replacement.
596
- */
597
- putNode(aType: string, uid: string, data: Record<string, unknown>): Promise<void>;
598
- /**
599
- * Write an edge, deep-merging into any existing record. See
600
- * {@link putNode} for the merge contract.
601
- */
602
- putEdge(aType: string, aUid: string, axbType: string, bType: string, bUid: string, data: Record<string, unknown>): Promise<void>;
603
- /**
604
- * Replace a node's `data` payload entirely. Any field absent from
605
- * `data` is dropped. Use sparingly — prefer {@link putNode} unless you
606
- * specifically need to drop unknown fields.
607
- */
608
- replaceNode(aType: string, uid: string, data: Record<string, unknown>): Promise<void>;
609
- /**
610
- * Replace an edge's `data` payload entirely. See {@link replaceNode}.
611
- */
612
- replaceEdge(aType: string, aUid: string, axbType: string, bType: string, bUid: string, data: Record<string, unknown>): Promise<void>;
613
- /**
614
- * Patch a node's `data` payload. Like {@link putNode} this is a deep
615
- * merge — nested objects are walked, only leaves are written. Use the
616
- * `deleteField()` sentinel to remove a field.
617
- */
618
- updateNode(uid: string, data: Record<string, unknown>): Promise<void>;
619
- /**
620
- * Patch an edge's `data` payload. See {@link updateNode}.
621
- */
622
- updateEdge(aUid: string, axbType: string, bUid: string, data: Record<string, unknown>): Promise<void>;
623
- removeNode(uid: string): Promise<void>;
624
- removeEdge(aUid: string, axbType: string, bUid: string): Promise<void>;
625
- }
626
- interface GraphClient extends GraphReader, GraphWriter {
627
- runTransaction<T>(fn: (tx: GraphTransaction) => Promise<T>): Promise<T>;
628
- batch(): GraphBatch;
629
- /** Delete a node and all its outgoing/incoming edges in chunked batches. */
630
- removeNodeCascade(uid: string, options?: BulkOptions): Promise<CascadeResult>;
631
- /** Find all edges matching `params` and delete them in chunked batches. */
632
- bulkRemoveEdges(params: FindEdgesParams, options?: BulkOptions): Promise<BulkResult>;
633
- /**
634
- * Create a scoped client for a Firestore subcollection under the given
635
- * parent node's document.
636
- *
637
- * The returned client shares a snapshot of the parent's registry at
638
- * the time of this call. If the parent is a `DynamicGraphClient` and
639
- * `reloadRegistry()` is called later, existing subgraph clients will
640
- * NOT see the updated types — create a new subgraph client after
641
- * reloading to pick up changes.
642
- *
643
- * @param parentNodeUid - UID of the parent node whose document owns the subcollection
644
- * @param name - Subcollection name (defaults to `'graph'`). Must not contain `/`.
645
- * @returns A `GraphClient` scoped to `{collectionPath}/{parentNodeUid}/{name}`
646
- */
647
- subgraph(parentNodeUid: string, name?: string): GraphClient;
648
- /**
649
- * Find edges across all subgraphs using a Firestore collection group query.
650
- *
651
- * Queries all collections with the given name (defaults to `'graph'`) across
652
- * the entire database. This is useful for cross-cutting reads that span
653
- * multiple subgraphs.
654
- *
655
- * **Requires** a Firestore collection group index for the query pattern.
656
- *
657
- * @param params - Edge filter parameters (same as `findEdges`)
658
- * @param collectionName - Collection name to query across (defaults to last segment of this client's collection path)
659
- */
660
- findEdgesGlobal(params: FindEdgesParams, collectionName?: string): Promise<StoredGraphRecord[]>;
661
- }
662
- interface DynamicGraphClient extends GraphClient {
663
- /** Define or update a node type in the dynamic registry. */
664
- defineNodeType(name: string, jsonSchema: object, description?: string, options?: DefineTypeOptions): Promise<void>;
665
- /** Define or update an edge type in the dynamic registry. */
666
- defineEdgeType(name: string, topology: EdgeTopology, jsonSchema?: object, description?: string, options?: DefineTypeOptions): Promise<void>;
667
- /** Reload the registry from meta-type nodes in the graph. */
668
- reloadRegistry(): Promise<void>;
669
- }
670
- interface GraphTransaction extends GraphReader, GraphWriter {
671
- }
672
- interface GraphBatch extends GraphWriter {
673
- commit(): Promise<void>;
674
- }
675
- interface HopDefinition {
676
- axbType: string;
677
- direction?: 'forward' | 'reverse';
678
- aType?: string;
679
- bType?: string;
680
- limit?: number;
681
- orderBy?: {
682
- field: string;
683
- direction?: 'asc' | 'desc';
684
- };
685
- filter?: (edge: StoredGraphRecord) => boolean;
686
- /**
687
- * Subgraph name to cross into for this hop (forward traversal only).
688
- *
689
- * When set, the traversal queries the named subgraph under each source node
690
- * instead of the current collection (`{collection}/{sourceUid}/{targetGraph}`).
691
- *
692
- * If omitted but the registry has a `targetGraph` for this `axbType`,
693
- * the registry value is used automatically.
694
- *
695
- * **Context tracking:** Once a hop crosses into a subgraph, subsequent
696
- * hops without `targetGraph` stay in that subgraph automatically. To
697
- * cross into a different subgraph, set `targetGraph` explicitly on the
698
- * next hop — explicit `targetGraph` always resolves relative to the
699
- * root client, not the current subgraph. To return to the root graph,
700
- * create a separate traversal from the root client.
701
- */
702
- targetGraph?: string;
703
- }
704
- interface TraversalOptions {
705
- maxReads?: number;
706
- concurrency?: number;
707
- returnIntermediates?: boolean;
708
- }
709
- interface HopResult {
710
- axbType: string;
711
- depth: number;
712
- edges: StoredGraphRecord[];
713
- sourceCount: number;
714
- truncated: boolean;
715
- }
716
- interface TraversalResult {
717
- nodes: StoredGraphRecord[];
718
- hops: HopResult[];
719
- totalReads: number;
720
- truncated: boolean;
721
- }
722
- interface TraversalBuilder {
723
- follow(axbType: string, options?: Omit<HopDefinition, 'axbType'>): TraversalBuilder;
724
- run(options?: TraversalOptions): Promise<TraversalResult>;
725
- }
726
- interface BulkOptions {
727
- /** Max operations per Firestore batch (default 500, Firestore hard limit). */
728
- batchSize?: number;
729
- /** Number of retry attempts per failed batch (default 3). */
730
- maxRetries?: number;
731
- /** Called after each batch commits. */
732
- onProgress?: (progress: BulkProgress) => void;
733
- /**
734
- * Recursively delete subcollections (subgraphs) under the node's document.
735
- * Defaults to `true` for `removeNodeCascade`.
736
- */
737
- deleteSubcollections?: boolean;
738
- }
739
- interface BulkProgress {
740
- /** Batches committed so far. */
741
- completedBatches: number;
742
- /** Total batches planned. */
743
- totalBatches: number;
744
- /** Total documents deleted so far. */
745
- deletedSoFar: number;
746
- }
747
- interface BulkResult {
748
- /** Total documents successfully deleted. */
749
- deleted: number;
750
- /** Number of batches committed. */
751
- batches: number;
752
- /** Errors from batches that failed after all retries. */
753
- errors: BulkBatchError[];
754
- }
755
- interface BulkBatchError {
756
- /** Zero-based index of the failed batch. */
757
- batchIndex: number;
758
- /** The underlying error. */
759
- error: Error;
760
- /** Number of operations in this batch that were not applied. */
761
- operationCount: number;
762
- }
763
- interface CascadeResult extends BulkResult {
764
- /** Number of edges deleted. */
765
- edgesDeleted: number;
766
- /** Whether the node itself was deleted. */
767
- nodeDeleted: boolean;
768
- }
769
-
770
- export { type ScanProtection as A, type BulkBatchError as B, type CascadeResult as C, type DynamicGraphClient as D, type EdgeTopology as E, type FindEdgesParams as F, type GraphClientOptions as G, type HopDefinition as H, type IndexSpec as I, type TraversalOptions as J, type TraversalResult as K, type ViewDefaultsConfig as L, type MigrationWriteBack as M, type NodeTypeData as N, type ViewResolverConfig as O, defineConfig as P, type QueryPlan as Q, type RegistryEntry as R, type StoredGraphRecord as S, type TraversalBuilder as T, resolveView as U, type ViewContext as V, type WhereClause as W, type GraphClient as a, type DiscoveryResult as b, type DynamicRegistryConfig as c, type MigrationStep as d, type GraphRegistry as e, type FindNodesParams as f, type QueryFilter as g, type GraphRecord as h, type MigrationExecutor as i, type MigrationFn as j, type StoredMigrationStep as k, type GraphReader as l, type BulkOptions as m, type BulkProgress as n, type BulkResult as o, type DefineTypeOptions as p, type DiscoveredEntity as q, type EdgeTypeData as r, type FiregraphConfig as s, type GraphBatch as t, type GraphTransaction as u, type GraphWriter as v, type HopResult as w, type IndexFieldSpec as x, type QueryMode as y, type QueryOptions as z };