@typicalday/firegraph 0.1.0 → 0.3.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.
@@ -141,9 +141,19 @@ interface FindEdgesParams {
141
141
  direction?: 'asc' | 'desc';
142
142
  };
143
143
  where?: WhereClause[];
144
+ /** Set to true to allow queries that may cause full collection scans. */
145
+ allowCollectionScan?: boolean;
144
146
  }
145
147
  interface FindNodesParams {
146
148
  aType: string;
149
+ limit?: number;
150
+ orderBy?: {
151
+ field: string;
152
+ direction?: 'asc' | 'desc';
153
+ };
154
+ where?: WhereClause[];
155
+ /** Set to true to allow queries that may cause full collection scans. */
156
+ allowCollectionScan?: boolean;
147
157
  }
148
158
  interface QueryOptions {
149
159
  limit?: number;
@@ -177,12 +187,50 @@ interface RegistryEntry {
177
187
  titleField?: string;
178
188
  /** Data field to use as the display subtitle (e.g. 'status', 'difficulty'). */
179
189
  subtitleField?: string;
190
+ /**
191
+ * Scope patterns constraining where this type can exist.
192
+ * Omit or leave empty to allow everywhere (backwards compatible).
193
+ *
194
+ * Patterns:
195
+ * - `'root'` — top-level collection only
196
+ * - `'agents'` — exact subgraph name match
197
+ * - `'workflow/agents'` — exact path match
198
+ * - `'*​/agents'` — `*` matches one segment
199
+ * - `'**​/agents'` — `**` matches zero or more segments
200
+ */
201
+ allowedIn?: string[];
202
+ /**
203
+ * Subgraph name where cross-graph edges of this type live.
204
+ *
205
+ * When set, forward traversal queries the named subgraph under each
206
+ * source node (e.g., `{collection}/{sourceUid}/{targetGraph}`) instead
207
+ * of the current collection. The subgraph contains both the edge
208
+ * documents and the target nodes they reference.
209
+ *
210
+ * Reverse traversal is unaffected — if you're already in the subgraph,
211
+ * the edges are local.
212
+ *
213
+ * Only applies to edge entries (not node self-loop entries).
214
+ * Must be a single segment (no `/`).
215
+ *
216
+ * @example
217
+ * ```ts
218
+ * { aType: 'task', axbType: 'assignedTo', bType: 'agent', targetGraph: 'workflow' }
219
+ * // Forward traversal from task1: queries {collection}/task1/workflow
220
+ * ```
221
+ */
222
+ targetGraph?: string;
180
223
  }
181
224
  /** Topology declaration for an edge (from edge.json). */
182
225
  interface EdgeTopology {
183
226
  from: string | string[];
184
227
  to: string | string[];
185
228
  inverseLabel?: string;
229
+ /**
230
+ * Subgraph name where cross-graph edges of this type live.
231
+ * See `RegistryEntry.targetGraph` for full documentation.
232
+ */
233
+ targetGraph?: string;
186
234
  }
187
235
  /** A discovered entity from the per-entity folder convention. */
188
236
  interface DiscoveredEntity {
@@ -203,6 +251,10 @@ interface DiscoveredEntity {
203
251
  viewsPath?: string;
204
252
  /** Sample data from sample.json. */
205
253
  sampleData?: Record<string, unknown>;
254
+ /** Scope patterns constraining where this type can exist in subgraphs. */
255
+ allowedIn?: string[];
256
+ /** Subgraph name where cross-graph edges of this type live. */
257
+ targetGraph?: string;
206
258
  }
207
259
  /** Result of scanning an entities directory. */
208
260
  interface DiscoveryResult {
@@ -233,6 +285,8 @@ interface DefineTypeOptions {
233
285
  viewTemplate?: string;
234
286
  /** Scoped CSS for the view template (injected via Shadow DOM). */
235
287
  viewCss?: string;
288
+ /** Scope patterns constraining where this type can exist in subgraphs. */
289
+ allowedIn?: string[];
236
290
  }
237
291
  /** Data shape stored in a `nodeType` meta-node. */
238
292
  interface NodeTypeData {
@@ -243,6 +297,7 @@ interface NodeTypeData {
243
297
  subtitleField?: string;
244
298
  viewTemplate?: string;
245
299
  viewCss?: string;
300
+ allowedIn?: string[];
246
301
  }
247
302
  /** Data shape stored in an `edgeType` meta-node. */
248
303
  interface EdgeTypeData {
@@ -256,7 +311,10 @@ interface EdgeTypeData {
256
311
  subtitleField?: string;
257
312
  viewTemplate?: string;
258
313
  viewCss?: string;
314
+ allowedIn?: string[];
315
+ targetGraph?: string;
259
316
  }
317
+ type ScanProtection = 'error' | 'warn' | 'off';
260
318
  interface GraphClientOptions {
261
319
  /** Static registry built from code/discovery. Ignored if registryMode is set. */
262
320
  registry?: GraphRegistry;
@@ -275,10 +333,22 @@ interface GraphClientOptions {
275
333
  * `'standard'` regardless of this setting (emulator doesn't support pipelines).
276
334
  */
277
335
  queryMode?: QueryMode;
336
+ /**
337
+ * Controls query safety behavior for full collection scan prevention.
338
+ *
339
+ * - `'error'` (default) — Throws `QuerySafetyError` for queries that would
340
+ * likely cause a full collection scan. Override per-query with
341
+ * `allowCollectionScan: true`.
342
+ * - `'warn'` — Logs a warning but executes the query.
343
+ * - `'off'` — No scan protection.
344
+ */
345
+ scanProtection?: ScanProtection;
278
346
  }
279
347
  interface GraphRegistry {
280
- validate(aType: string, axbType: string, bType: string, data: unknown): void;
348
+ validate(aType: string, axbType: string, bType: string, data: unknown, scopePath?: string): void;
281
349
  lookup(aType: string, axbType: string, bType: string): RegistryEntry | undefined;
350
+ /** Return all entries matching the given axbType (edge relation name). */
351
+ lookupByAxbType(axbType: string): ReadonlyArray<RegistryEntry>;
282
352
  entries(): ReadonlyArray<RegistryEntry>;
283
353
  }
284
354
  interface GraphReader {
@@ -302,6 +372,34 @@ interface GraphClient extends GraphReader, GraphWriter {
302
372
  removeNodeCascade(uid: string, options?: BulkOptions): Promise<CascadeResult>;
303
373
  /** Find all edges matching `params` and delete them in chunked batches. */
304
374
  bulkRemoveEdges(params: FindEdgesParams, options?: BulkOptions): Promise<BulkResult>;
375
+ /**
376
+ * Create a scoped client for a Firestore subcollection under the given
377
+ * parent node's document.
378
+ *
379
+ * The returned client shares a snapshot of the parent's registry at
380
+ * the time of this call. If the parent is a `DynamicGraphClient` and
381
+ * `reloadRegistry()` is called later, existing subgraph clients will
382
+ * NOT see the updated types — create a new subgraph client after
383
+ * reloading to pick up changes.
384
+ *
385
+ * @param parentNodeUid - UID of the parent node whose document owns the subcollection
386
+ * @param name - Subcollection name (defaults to `'graph'`). Must not contain `/`.
387
+ * @returns A `GraphClient` scoped to `{collectionPath}/{parentNodeUid}/{name}`
388
+ */
389
+ subgraph(parentNodeUid: string, name?: string): GraphClient;
390
+ /**
391
+ * Find edges across all subgraphs using a Firestore collection group query.
392
+ *
393
+ * Queries all collections with the given name (defaults to `'graph'`) across
394
+ * the entire database. This is useful for cross-cutting reads that span
395
+ * multiple subgraphs.
396
+ *
397
+ * **Requires** a Firestore collection group index for the query pattern.
398
+ *
399
+ * @param params - Edge filter parameters (same as `findEdges`)
400
+ * @param collectionName - Collection name to query across (defaults to last segment of this client's collection path)
401
+ */
402
+ findEdgesGlobal(params: FindEdgesParams, collectionName?: string): Promise<StoredGraphRecord[]>;
305
403
  }
306
404
  interface DynamicGraphClient extends GraphClient {
307
405
  /** Define or update a node type in the dynamic registry. */
@@ -327,6 +425,23 @@ interface HopDefinition {
327
425
  direction?: 'asc' | 'desc';
328
426
  };
329
427
  filter?: (edge: StoredGraphRecord) => boolean;
428
+ /**
429
+ * Subgraph name to cross into for this hop (forward traversal only).
430
+ *
431
+ * When set, the traversal queries the named subgraph under each source node
432
+ * instead of the current collection (`{collection}/{sourceUid}/{targetGraph}`).
433
+ *
434
+ * If omitted but the registry has a `targetGraph` for this `axbType`,
435
+ * the registry value is used automatically.
436
+ *
437
+ * **Context tracking:** Once a hop crosses into a subgraph, subsequent
438
+ * hops without `targetGraph` stay in that subgraph automatically. To
439
+ * cross into a different subgraph, set `targetGraph` explicitly on the
440
+ * next hop — explicit `targetGraph` always resolves relative to the
441
+ * root client, not the current subgraph. To return to the root graph,
442
+ * create a separate traversal from the root client.
443
+ */
444
+ targetGraph?: string;
330
445
  }
331
446
  interface TraversalOptions {
332
447
  maxReads?: number;
@@ -357,6 +472,11 @@ interface BulkOptions {
357
472
  maxRetries?: number;
358
473
  /** Called after each batch commits. */
359
474
  onProgress?: (progress: BulkProgress) => void;
475
+ /**
476
+ * Recursively delete subcollections (subgraphs) under the node's document.
477
+ * Defaults to `true` for `removeNodeCascade`.
478
+ */
479
+ deleteSubcollections?: boolean;
360
480
  }
361
481
  interface BulkProgress {
362
482
  /** Batches committed so far. */
@@ -411,4 +531,4 @@ interface CodegenOptions {
411
531
  */
412
532
  declare function generateTypes(discovery: DiscoveryResult, options?: CodegenOptions): Promise<string>;
413
533
 
414
- export { defineConfig as A, type BulkBatchError as B, type CascadeResult as C, type DynamicRegistryConfig as D, type EdgeTopology as E, type FindEdgesParams as F, type GraphClientOptions as G, type HopDefinition as H, generateTypes as I, resolveView as J, type NodeTypeData as N, type QueryPlan as Q, type RegistryEntry as R, type StoredGraphRecord as S, type TraversalBuilder as T, type ViewContext as V, type WhereClause as W, type DynamicGraphClient as a, type GraphClient as b, type DiscoveryResult as c, type GraphRegistry as d, type GraphReader as e, type GraphRecord as f, type FindNodesParams as g, type BulkOptions as h, type BulkProgress as i, type BulkResult as j, type CodegenOptions as k, type DefineTypeOptions as l, type DiscoveredEntity as m, type EdgeTypeData as n, type FiregraphConfig as o, type GraphBatch as p, type GraphTransaction as q, type GraphWriter as r, type HopResult as s, type QueryFilter as t, type QueryMode as u, type QueryOptions as v, type TraversalOptions as w, type TraversalResult as x, type ViewDefaultsConfig as y, type ViewResolverConfig as z };
534
+ export { type ViewResolverConfig as A, type BulkBatchError as B, type CascadeResult as C, type DynamicRegistryConfig as D, type EdgeTopology as E, type FindEdgesParams as F, type GraphClientOptions as G, type HopDefinition as H, defineConfig as I, generateTypes as J, resolveView as K, type NodeTypeData as N, type QueryPlan as Q, type RegistryEntry as R, type ScanProtection as S, type TraversalBuilder as T, type ViewContext as V, type WhereClause as W, type DynamicGraphClient as a, type GraphClient as b, type DiscoveryResult as c, type GraphRegistry as d, type GraphReader as e, type GraphRecord as f, type FindNodesParams as g, type QueryFilter as h, type BulkOptions as i, type BulkProgress as j, type BulkResult as k, type CodegenOptions as l, type DefineTypeOptions as m, type DiscoveredEntity as n, type EdgeTypeData as o, type FiregraphConfig as p, type GraphBatch as q, type GraphTransaction as r, type GraphWriter as s, type HopResult as t, type QueryMode as u, type QueryOptions as v, type StoredGraphRecord as w, type TraversalOptions as x, type TraversalResult as y, type ViewDefaultsConfig as z };
@@ -141,9 +141,19 @@ interface FindEdgesParams {
141
141
  direction?: 'asc' | 'desc';
142
142
  };
143
143
  where?: WhereClause[];
144
+ /** Set to true to allow queries that may cause full collection scans. */
145
+ allowCollectionScan?: boolean;
144
146
  }
145
147
  interface FindNodesParams {
146
148
  aType: string;
149
+ limit?: number;
150
+ orderBy?: {
151
+ field: string;
152
+ direction?: 'asc' | 'desc';
153
+ };
154
+ where?: WhereClause[];
155
+ /** Set to true to allow queries that may cause full collection scans. */
156
+ allowCollectionScan?: boolean;
147
157
  }
148
158
  interface QueryOptions {
149
159
  limit?: number;
@@ -177,12 +187,50 @@ interface RegistryEntry {
177
187
  titleField?: string;
178
188
  /** Data field to use as the display subtitle (e.g. 'status', 'difficulty'). */
179
189
  subtitleField?: string;
190
+ /**
191
+ * Scope patterns constraining where this type can exist.
192
+ * Omit or leave empty to allow everywhere (backwards compatible).
193
+ *
194
+ * Patterns:
195
+ * - `'root'` — top-level collection only
196
+ * - `'agents'` — exact subgraph name match
197
+ * - `'workflow/agents'` — exact path match
198
+ * - `'*​/agents'` — `*` matches one segment
199
+ * - `'**​/agents'` — `**` matches zero or more segments
200
+ */
201
+ allowedIn?: string[];
202
+ /**
203
+ * Subgraph name where cross-graph edges of this type live.
204
+ *
205
+ * When set, forward traversal queries the named subgraph under each
206
+ * source node (e.g., `{collection}/{sourceUid}/{targetGraph}`) instead
207
+ * of the current collection. The subgraph contains both the edge
208
+ * documents and the target nodes they reference.
209
+ *
210
+ * Reverse traversal is unaffected — if you're already in the subgraph,
211
+ * the edges are local.
212
+ *
213
+ * Only applies to edge entries (not node self-loop entries).
214
+ * Must be a single segment (no `/`).
215
+ *
216
+ * @example
217
+ * ```ts
218
+ * { aType: 'task', axbType: 'assignedTo', bType: 'agent', targetGraph: 'workflow' }
219
+ * // Forward traversal from task1: queries {collection}/task1/workflow
220
+ * ```
221
+ */
222
+ targetGraph?: string;
180
223
  }
181
224
  /** Topology declaration for an edge (from edge.json). */
182
225
  interface EdgeTopology {
183
226
  from: string | string[];
184
227
  to: string | string[];
185
228
  inverseLabel?: string;
229
+ /**
230
+ * Subgraph name where cross-graph edges of this type live.
231
+ * See `RegistryEntry.targetGraph` for full documentation.
232
+ */
233
+ targetGraph?: string;
186
234
  }
187
235
  /** A discovered entity from the per-entity folder convention. */
188
236
  interface DiscoveredEntity {
@@ -203,6 +251,10 @@ interface DiscoveredEntity {
203
251
  viewsPath?: string;
204
252
  /** Sample data from sample.json. */
205
253
  sampleData?: Record<string, unknown>;
254
+ /** Scope patterns constraining where this type can exist in subgraphs. */
255
+ allowedIn?: string[];
256
+ /** Subgraph name where cross-graph edges of this type live. */
257
+ targetGraph?: string;
206
258
  }
207
259
  /** Result of scanning an entities directory. */
208
260
  interface DiscoveryResult {
@@ -233,6 +285,8 @@ interface DefineTypeOptions {
233
285
  viewTemplate?: string;
234
286
  /** Scoped CSS for the view template (injected via Shadow DOM). */
235
287
  viewCss?: string;
288
+ /** Scope patterns constraining where this type can exist in subgraphs. */
289
+ allowedIn?: string[];
236
290
  }
237
291
  /** Data shape stored in a `nodeType` meta-node. */
238
292
  interface NodeTypeData {
@@ -243,6 +297,7 @@ interface NodeTypeData {
243
297
  subtitleField?: string;
244
298
  viewTemplate?: string;
245
299
  viewCss?: string;
300
+ allowedIn?: string[];
246
301
  }
247
302
  /** Data shape stored in an `edgeType` meta-node. */
248
303
  interface EdgeTypeData {
@@ -256,7 +311,10 @@ interface EdgeTypeData {
256
311
  subtitleField?: string;
257
312
  viewTemplate?: string;
258
313
  viewCss?: string;
314
+ allowedIn?: string[];
315
+ targetGraph?: string;
259
316
  }
317
+ type ScanProtection = 'error' | 'warn' | 'off';
260
318
  interface GraphClientOptions {
261
319
  /** Static registry built from code/discovery. Ignored if registryMode is set. */
262
320
  registry?: GraphRegistry;
@@ -275,10 +333,22 @@ interface GraphClientOptions {
275
333
  * `'standard'` regardless of this setting (emulator doesn't support pipelines).
276
334
  */
277
335
  queryMode?: QueryMode;
336
+ /**
337
+ * Controls query safety behavior for full collection scan prevention.
338
+ *
339
+ * - `'error'` (default) — Throws `QuerySafetyError` for queries that would
340
+ * likely cause a full collection scan. Override per-query with
341
+ * `allowCollectionScan: true`.
342
+ * - `'warn'` — Logs a warning but executes the query.
343
+ * - `'off'` — No scan protection.
344
+ */
345
+ scanProtection?: ScanProtection;
278
346
  }
279
347
  interface GraphRegistry {
280
- validate(aType: string, axbType: string, bType: string, data: unknown): void;
348
+ validate(aType: string, axbType: string, bType: string, data: unknown, scopePath?: string): void;
281
349
  lookup(aType: string, axbType: string, bType: string): RegistryEntry | undefined;
350
+ /** Return all entries matching the given axbType (edge relation name). */
351
+ lookupByAxbType(axbType: string): ReadonlyArray<RegistryEntry>;
282
352
  entries(): ReadonlyArray<RegistryEntry>;
283
353
  }
284
354
  interface GraphReader {
@@ -302,6 +372,34 @@ interface GraphClient extends GraphReader, GraphWriter {
302
372
  removeNodeCascade(uid: string, options?: BulkOptions): Promise<CascadeResult>;
303
373
  /** Find all edges matching `params` and delete them in chunked batches. */
304
374
  bulkRemoveEdges(params: FindEdgesParams, options?: BulkOptions): Promise<BulkResult>;
375
+ /**
376
+ * Create a scoped client for a Firestore subcollection under the given
377
+ * parent node's document.
378
+ *
379
+ * The returned client shares a snapshot of the parent's registry at
380
+ * the time of this call. If the parent is a `DynamicGraphClient` and
381
+ * `reloadRegistry()` is called later, existing subgraph clients will
382
+ * NOT see the updated types — create a new subgraph client after
383
+ * reloading to pick up changes.
384
+ *
385
+ * @param parentNodeUid - UID of the parent node whose document owns the subcollection
386
+ * @param name - Subcollection name (defaults to `'graph'`). Must not contain `/`.
387
+ * @returns A `GraphClient` scoped to `{collectionPath}/{parentNodeUid}/{name}`
388
+ */
389
+ subgraph(parentNodeUid: string, name?: string): GraphClient;
390
+ /**
391
+ * Find edges across all subgraphs using a Firestore collection group query.
392
+ *
393
+ * Queries all collections with the given name (defaults to `'graph'`) across
394
+ * the entire database. This is useful for cross-cutting reads that span
395
+ * multiple subgraphs.
396
+ *
397
+ * **Requires** a Firestore collection group index for the query pattern.
398
+ *
399
+ * @param params - Edge filter parameters (same as `findEdges`)
400
+ * @param collectionName - Collection name to query across (defaults to last segment of this client's collection path)
401
+ */
402
+ findEdgesGlobal(params: FindEdgesParams, collectionName?: string): Promise<StoredGraphRecord[]>;
305
403
  }
306
404
  interface DynamicGraphClient extends GraphClient {
307
405
  /** Define or update a node type in the dynamic registry. */
@@ -327,6 +425,23 @@ interface HopDefinition {
327
425
  direction?: 'asc' | 'desc';
328
426
  };
329
427
  filter?: (edge: StoredGraphRecord) => boolean;
428
+ /**
429
+ * Subgraph name to cross into for this hop (forward traversal only).
430
+ *
431
+ * When set, the traversal queries the named subgraph under each source node
432
+ * instead of the current collection (`{collection}/{sourceUid}/{targetGraph}`).
433
+ *
434
+ * If omitted but the registry has a `targetGraph` for this `axbType`,
435
+ * the registry value is used automatically.
436
+ *
437
+ * **Context tracking:** Once a hop crosses into a subgraph, subsequent
438
+ * hops without `targetGraph` stay in that subgraph automatically. To
439
+ * cross into a different subgraph, set `targetGraph` explicitly on the
440
+ * next hop — explicit `targetGraph` always resolves relative to the
441
+ * root client, not the current subgraph. To return to the root graph,
442
+ * create a separate traversal from the root client.
443
+ */
444
+ targetGraph?: string;
330
445
  }
331
446
  interface TraversalOptions {
332
447
  maxReads?: number;
@@ -357,6 +472,11 @@ interface BulkOptions {
357
472
  maxRetries?: number;
358
473
  /** Called after each batch commits. */
359
474
  onProgress?: (progress: BulkProgress) => void;
475
+ /**
476
+ * Recursively delete subcollections (subgraphs) under the node's document.
477
+ * Defaults to `true` for `removeNodeCascade`.
478
+ */
479
+ deleteSubcollections?: boolean;
360
480
  }
361
481
  interface BulkProgress {
362
482
  /** Batches committed so far. */
@@ -411,4 +531,4 @@ interface CodegenOptions {
411
531
  */
412
532
  declare function generateTypes(discovery: DiscoveryResult, options?: CodegenOptions): Promise<string>;
413
533
 
414
- export { defineConfig as A, type BulkBatchError as B, type CascadeResult as C, type DynamicRegistryConfig as D, type EdgeTopology as E, type FindEdgesParams as F, type GraphClientOptions as G, type HopDefinition as H, generateTypes as I, resolveView as J, type NodeTypeData as N, type QueryPlan as Q, type RegistryEntry as R, type StoredGraphRecord as S, type TraversalBuilder as T, type ViewContext as V, type WhereClause as W, type DynamicGraphClient as a, type GraphClient as b, type DiscoveryResult as c, type GraphRegistry as d, type GraphReader as e, type GraphRecord as f, type FindNodesParams as g, type BulkOptions as h, type BulkProgress as i, type BulkResult as j, type CodegenOptions as k, type DefineTypeOptions as l, type DiscoveredEntity as m, type EdgeTypeData as n, type FiregraphConfig as o, type GraphBatch as p, type GraphTransaction as q, type GraphWriter as r, type HopResult as s, type QueryFilter as t, type QueryMode as u, type QueryOptions as v, type TraversalOptions as w, type TraversalResult as x, type ViewDefaultsConfig as y, type ViewResolverConfig as z };
534
+ export { type ViewResolverConfig as A, type BulkBatchError as B, type CascadeResult as C, type DynamicRegistryConfig as D, type EdgeTopology as E, type FindEdgesParams as F, type GraphClientOptions as G, type HopDefinition as H, defineConfig as I, generateTypes as J, resolveView as K, type NodeTypeData as N, type QueryPlan as Q, type RegistryEntry as R, type ScanProtection as S, type TraversalBuilder as T, type ViewContext as V, type WhereClause as W, type DynamicGraphClient as a, type GraphClient as b, type DiscoveryResult as c, type GraphRegistry as d, type GraphReader as e, type GraphRecord as f, type FindNodesParams as g, type QueryFilter as h, type BulkOptions as i, type BulkProgress as j, type BulkResult as k, type CodegenOptions as l, type DefineTypeOptions as m, type DiscoveredEntity as n, type EdgeTypeData as o, type FiregraphConfig as p, type GraphBatch as q, type GraphTransaction as r, type GraphWriter as s, type HopResult as t, type QueryMode as u, type QueryOptions as v, type StoredGraphRecord as w, type TraversalOptions as x, type TraversalResult as y, type ViewDefaultsConfig as z };