@typicalday/firegraph 0.9.0 → 0.11.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 (60) hide show
  1. package/README.md +93 -90
  2. package/bin/firegraph.mjs +21 -7
  3. package/dist/backend-U-MLShlg.d.ts +97 -0
  4. package/dist/backend-np4gEVhB.d.cts +97 -0
  5. package/dist/backend.cjs.map +1 -1
  6. package/dist/backend.d.cts +7 -6
  7. package/dist/backend.d.ts +7 -6
  8. package/dist/backend.js +1 -1
  9. package/dist/backend.js.map +1 -1
  10. package/dist/{chunk-EVUM6ORB.js → chunk-6SB34IPQ.js} +76 -8
  11. package/dist/chunk-6SB34IPQ.js.map +1 -0
  12. package/dist/{chunk-SU4FNLC3.js → chunk-EEKWRX5E.js} +1 -1
  13. package/dist/{chunk-SU4FNLC3.js.map → chunk-EEKWRX5E.js.map} +1 -1
  14. package/dist/{chunk-YLGXLEUE.js → chunk-GJVVRTQT.js} +5 -14
  15. package/dist/chunk-GJVVRTQT.js.map +1 -0
  16. package/dist/{chunk-GLOVWKQH.js → chunk-R7CRGYY4.js} +1 -1
  17. package/dist/{chunk-GLOVWKQH.js.map → chunk-R7CRGYY4.js.map} +1 -1
  18. package/dist/{do-sqlite.cjs → cloudflare/index.cjs} +1659 -1422
  19. package/dist/cloudflare/index.cjs.map +1 -0
  20. package/dist/cloudflare/index.d.cts +529 -0
  21. package/dist/cloudflare/index.d.ts +529 -0
  22. package/dist/cloudflare/index.js +934 -0
  23. package/dist/cloudflare/index.js.map +1 -0
  24. package/dist/codegen/index.cjs +4 -13
  25. package/dist/codegen/index.cjs.map +1 -1
  26. package/dist/codegen/index.d.cts +1 -1
  27. package/dist/codegen/index.d.ts +1 -1
  28. package/dist/codegen/index.js +1 -1
  29. package/dist/index.cjs +144 -132
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +116 -27
  32. package/dist/index.d.ts +116 -27
  33. package/dist/index.js +77 -123
  34. package/dist/index.js.map +1 -1
  35. package/dist/query-client/index.cjs.map +1 -1
  36. package/dist/query-client/index.js +1 -1
  37. package/dist/{scope-path-BtajqNK5.d.ts → scope-path-B1G3YiA7.d.cts} +5 -100
  38. package/dist/{scope-path-D2mNENJ-.d.cts → scope-path-B1G3YiA7.d.ts} +5 -100
  39. package/dist/{types-DfWVTsMn.d.ts → types-BGWxcpI_.d.cts} +92 -1
  40. package/dist/{types-DfWVTsMn.d.cts → types-BGWxcpI_.d.ts} +92 -1
  41. package/package.json +13 -17
  42. package/dist/chunk-EVUM6ORB.js.map +0 -1
  43. package/dist/chunk-SZ6W4VAS.js +0 -701
  44. package/dist/chunk-SZ6W4VAS.js.map +0 -1
  45. package/dist/chunk-YLGXLEUE.js.map +0 -1
  46. package/dist/d1.cjs +0 -2421
  47. package/dist/d1.cjs.map +0 -1
  48. package/dist/d1.d.cts +0 -54
  49. package/dist/d1.d.ts +0 -54
  50. package/dist/d1.js +0 -76
  51. package/dist/d1.js.map +0 -1
  52. package/dist/do-sqlite.cjs.map +0 -1
  53. package/dist/do-sqlite.d.cts +0 -41
  54. package/dist/do-sqlite.d.ts +0 -41
  55. package/dist/do-sqlite.js +0 -79
  56. package/dist/do-sqlite.js.map +0 -1
  57. package/dist/editor/client/assets/index-Bq2bfzeY.js +0 -411
  58. package/dist/editor/client/assets/index-CJ4m_EOL.css +0 -1
  59. package/dist/editor/client/index.html +0 -16
  60. package/dist/editor/server/index.mjs +0 -51511
package/dist/index.d.cts CHANGED
@@ -1,8 +1,9 @@
1
- import { S as StorageBackend, F as FiregraphError } from './scope-path-D2mNENJ-.cjs';
2
- export { C as CrossBackendTransactionError, D as DynamicRegistryError, E as EdgeNotFoundError, I as InvalidQueryError, M as MigrationError, N as NodeNotFoundError, Q as QuerySafetyError, R as RegistryScopeError, c as RegistryViolationError, a as StorageScopeSegment, d as TraversalError, V as ValidationError, b as appendStorageScope, i as isAncestorScopeUid, p as parseStorageScope, r as resolveAncestorScope } from './scope-path-D2mNENJ-.cjs';
3
- import { G as GraphClientOptions, b as GraphClient, a as DynamicGraphClient, c as DiscoveryResult, R as RegistryEntry, d as GraphRegistry, e as GraphReader, M as MigrationExecutor, D as DynamicRegistryConfig, S as StoredGraphRecord, f as MigrationWriteBack, g as MigrationStep, F as FindEdgesParams, Q as QueryPlan, h as FindNodesParams, i as QueryFilter, j as GraphRecord, k as MigrationFn, l as StoredMigrationStep, T as TraversalBuilder } from './types-DfWVTsMn.cjs';
4
- export { B as BulkBatchError, m as BulkOptions, n as BulkProgress, o as BulkResult, C as CascadeResult, p as DefineTypeOptions, q as DiscoveredEntity, E as EdgeTopology, r as EdgeTypeData, s as FiregraphConfig, t as GraphBatch, u as GraphTransaction, v as GraphWriter, H as HopDefinition, w as HopResult, N as NodeTypeData, x as QueryMode, y as QueryOptions, z as ScanProtection, A as TraversalOptions, I as TraversalResult, V as ViewContext, J as ViewDefaultsConfig, K as ViewResolverConfig, W as WhereClause, L as defineConfig, O as resolveView } from './types-DfWVTsMn.cjs';
1
+ import { S as StorageBackend } from './backend-np4gEVhB.cjs';
2
+ import { G as GraphClientOptions, a as GraphClient, D as DynamicGraphClient, I as IndexSpec, b as DiscoveryResult, R as RegistryEntry, c as GraphRegistry, d as GraphReader, M as MigrationExecutor, e as DynamicRegistryConfig, S as StoredGraphRecord, f as MigrationWriteBack, g as MigrationStep, F as FindEdgesParams, Q as QueryPlan, h as FindNodesParams, i as QueryFilter, j as GraphRecord, k as MigrationFn, l as StoredMigrationStep, T as TraversalBuilder } from './types-BGWxcpI_.cjs';
3
+ export { B as BulkBatchError, m as BulkOptions, n as BulkProgress, o as BulkResult, C as CascadeResult, p as DefineTypeOptions, q as DiscoveredEntity, E as EdgeTopology, r as EdgeTypeData, s as FiregraphConfig, t as GraphBatch, u as GraphTransaction, v as GraphWriter, H as HopDefinition, w as HopResult, x as IndexFieldSpec, N as NodeTypeData, y as QueryMode, z as QueryOptions, A as ScanProtection, J as TraversalOptions, K as TraversalResult, V as ViewContext, L as ViewDefaultsConfig, O as ViewResolverConfig, W as WhereClause, P as defineConfig, U as resolveView } from './types-BGWxcpI_.cjs';
5
4
  export { CodegenOptions, generateTypes } from './codegen/index.cjs';
5
+ import { F as FiregraphError } from './scope-path-B1G3YiA7.cjs';
6
+ export { C as CrossBackendTransactionError, D as DynamicRegistryError, E as EdgeNotFoundError, I as InvalidQueryError, M as MigrationError, N as NodeNotFoundError, Q as QuerySafetyError, R as RegistryScopeError, b as RegistryViolationError, S as StorageScopeSegment, T as TraversalError, V as ValidationError, a as appendStorageScope, i as isAncestorScopeUid, p as parseStorageScope, r as resolveAncestorScope } from './scope-path-B1G3YiA7.cjs';
6
7
  import { Firestore } from '@google-cloud/firestore';
7
8
  export { Q as QueryClient, a as QueryClientError, b as QueryClientErrorCode, c as QueryClientOptions } from './client-Bk2Cm6xv.cjs';
8
9
  export { E as EntityViewConfig, a as EntityViewMeta, V as ViewComponentClass, b as ViewMeta, c as ViewRegistry, d as ViewRegistryInput, e as defineViews } from './views-DL60k0cf.cjs';
@@ -10,9 +11,10 @@ export { E as EntityViewConfig, a as EntityViewMeta, V as ViewComponentClass, b
10
11
  /**
11
12
  * Create a `GraphClient` backed by an arbitrary `StorageBackend`.
12
13
  *
13
- * Used by backend-specific factories (D1, DO-SQLite, etc.) most callers
14
- * should use the higher-level `createGraphClient(firestore, ...)` overload
15
- * below or import the equivalent from `firegraph/d1` / `firegraph/do-sqlite`.
14
+ * Used by backend-specific factories (e.g. `createDOClient` in
15
+ * `firegraph/cloudflare`) — most callers should use the higher-level
16
+ * `createGraphClient(firestore, ...)` overload below for Firestore, or the
17
+ * Cloudflare factory for DO-backed graphs.
16
18
  */
17
19
  declare function createGraphClientFromBackend(backend: StorageBackend, options?: GraphClientOptions, metaBackend?: StorageBackend): GraphClient | DynamicGraphClient;
18
20
 
@@ -62,6 +64,54 @@ declare function resolveAncestorCollection(collectionPath: string, uid: string):
62
64
  */
63
65
  declare function isAncestorUid(collectionPath: string, uid: string): boolean;
64
66
 
67
+ /**
68
+ * Default core index preset.
69
+ *
70
+ * This set covers the query patterns firegraph's query planner emits for
71
+ * built-in operations — `findNodes`, `findEdges`, cascade delete, traversal,
72
+ * and the DO/SQLite path compilers. Apps that need additional indexes
73
+ * (descending timestamps, `data.*` filters, composite fields unique to
74
+ * their query shapes) declare them on `RegistryEntry.indexes` or override
75
+ * this preset wholesale via the backend-specific `coreIndexes` option —
76
+ * `FiregraphDOOptions.coreIndexes` for the DO backend,
77
+ * `BuildSchemaOptions.coreIndexes` for the legacy SQLite backend, and
78
+ * `GenerateIndexOptions.coreIndexes` for the Firestore CLI generator.
79
+ *
80
+ * ## Ownership model
81
+ *
82
+ * This list is firegraph's *recommendation* — not non-negotiable policy.
83
+ * Consumers can:
84
+ *
85
+ * 1. Accept the preset as-is (default).
86
+ * 2. Extend it: `coreIndexes: [...DEFAULT_CORE_INDEXES, ...more]`.
87
+ * 3. Replace it entirely with a tailored set.
88
+ * 4. Disable it (`coreIndexes: []`) and take full responsibility for
89
+ * index coverage — only do this if you're provisioning a complete
90
+ * custom set.
91
+ *
92
+ * ## Per-backend emission
93
+ *
94
+ * The Firestore generator skips single-field entries (Firestore implicitly
95
+ * indexes every field) and emits one composite index per multi-field spec.
96
+ * The SQLite-flavored generators (DO, legacy) emit every spec as-is.
97
+ *
98
+ * ## Why these specific indexes
99
+ *
100
+ * - `aUid` / `bUid` — required for `_fgRemoveNodeCascade`, which scans by
101
+ * each UID side independently. A composite `(aUid, axbType)` also
102
+ * satisfies `aUid`-alone via leading-column prefix, but the single-field
103
+ * form is cheaper for the common case.
104
+ * - `aType` / `bType` — `findNodes({ aType })` and cross-type enumeration.
105
+ * - `(aUid, axbType)` — forward edge lookup (`findEdges({ aUid, axbType })`)
106
+ * and the `get` strategy fallback when only two of three triple fields
107
+ * are present.
108
+ * - `(axbType, bUid)` — reverse edge traversal.
109
+ * - `(aType, axbType)` — type-scoped edge scans (e.g., `findEdges({ aType, axbType })`).
110
+ * - `(axbType, bType)` — scope edges of one relation to a target type.
111
+ */
112
+
113
+ declare const DEFAULT_CORE_INDEXES: ReadonlyArray<IndexSpec>;
114
+
65
115
  /**
66
116
  * Entity Discovery — convention-based auto-discovery of entities from
67
117
  * a per-entity folder structure.
@@ -153,8 +203,10 @@ declare function createRegistryFromGraph(reader: GraphReader, executor?: Migrati
153
203
  * Firestore-specific client factory.
154
204
  *
155
205
  * Kept in its own module so that bundlers don't pull
156
- * `@google-cloud/firestore` into SQLite-only entry points
157
- * (`firegraph/d1`, `firegraph/do-sqlite`).
206
+ * `@google-cloud/firestore` into non-Firestore entry points — most
207
+ * importantly the Cloudflare DO backend (`firegraph/cloudflare`) and the
208
+ * routing primitive (`firegraph/backend`), both of which must load cleanly
209
+ * in a Workers environment where the Node Firestore SDK is unavailable.
158
210
  */
159
211
 
160
212
  declare function createGraphClient(db: Firestore, collectionPath: string, options: GraphClientOptions & {
@@ -164,6 +216,33 @@ declare function createGraphClient(db: Firestore, collectionPath: string, option
164
216
 
165
217
  declare function generateId(): string;
166
218
 
219
+ /**
220
+ * Firestore composite index generator.
221
+ *
222
+ * Translates firegraph's declarative `IndexSpec[]` (core preset plus per-entry
223
+ * registry indexes) into the `firestore.indexes.json` shape consumed by
224
+ * `firebase deploy --only firestore:indexes`.
225
+ *
226
+ * ## What Firestore needs
227
+ *
228
+ * Firestore auto-indexes every top-level field (including `data.*`) for
229
+ * single-field equality queries — we only need to emit *composite* indexes
230
+ * here. That means:
231
+ *
232
+ * 1. Single-field specs are dropped (Firestore already covers them).
233
+ * 2. Composite specs (two or more fields) get one `FirestoreIndex`.
234
+ * 3. Specs with `where` are dropped with a warning — Firestore composite
235
+ * indexes do not support partial predicates.
236
+ * 4. When a registry entry has `targetGraph` set, every composite is also
237
+ * emitted with `queryScope: 'COLLECTION_GROUP'` under the targetGraph
238
+ * name, so `findEdgesGlobal()` queries across subgraphs can hit an
239
+ * index.
240
+ *
241
+ * The SQLite-flavored backends (DO, legacy) consume the same `IndexSpec[]`
242
+ * via `src/internal/sqlite-index-ddl.ts` but emit every spec (single fields
243
+ * included) as `CREATE INDEX` DDL.
244
+ */
245
+
167
246
  interface FirestoreIndexField {
168
247
  fieldPath: string;
169
248
  order: 'ASCENDING' | 'DESCENDING';
@@ -177,25 +256,35 @@ interface FirestoreIndexConfig {
177
256
  indexes: FirestoreIndex[];
178
257
  fieldOverrides: unknown[];
179
258
  }
259
+ interface GenerateIndexOptions {
260
+ /**
261
+ * Replaces firegraph's built-in core preset. Defaults to
262
+ * `DEFAULT_CORE_INDEXES`. Pass `[]` to disable core indexes entirely.
263
+ */
264
+ coreIndexes?: IndexSpec[];
265
+ /**
266
+ * Registry entries supplying per-triple `indexes`. Entries without
267
+ * `indexes` contribute no composites; entries with `targetGraph` also
268
+ * trigger `COLLECTION_GROUP` mirrors under each distinct targetGraph
269
+ * segment name.
270
+ */
271
+ registryEntries?: ReadonlyArray<RegistryEntry>;
272
+ /**
273
+ * Entity discovery result. Convenience for callers that have a
274
+ * `DiscoveryResult` but not a built registry — treated as if every
275
+ * discovered entity were expanded to its registry entries carrying just
276
+ * `indexes` + `targetGraph`. Mutually usable with `registryEntries`
277
+ * (both are concatenated and deduplicated at the spec level).
278
+ */
279
+ entities?: DiscoveryResult;
280
+ }
180
281
  /**
181
- * Generates a Firestore index configuration for a firegraph collection.
182
- *
183
- * Always includes the 4 base composite indexes. If an entity discovery result
184
- * is provided, generates additional data-field indexes for common query
185
- * patterns on node data fields:
186
- * (aType, axbType, data.{field})
187
- *
188
- * When registry entries with `targetGraph` are provided, also generates
189
- * collection group indexes for `findEdgesGlobal()` queries. The collection
190
- * group name defaults to `'graph'` (the standard subgraph name) but can be
191
- * overridden per `targetGraph` value.
192
- *
193
- * @param collection - Firestore collection name (e.g. 'graph')
194
- * @param entities - Optional discovery result for per-entity data field indexes
195
- * @param registryEntries - Optional registry entries; when any have `targetGraph`,
196
- * collection group indexes are generated for the distinct subgraph names
282
+ * Build a Firestore index configuration from firegraph's declarative index
283
+ * specs. Deduplicates by field list + scope before emitting. Single-field
284
+ * specs are dropped; partial-index specs (`where` set) are dropped with a
285
+ * one-time warning.
197
286
  */
198
- declare function generateIndexConfig(collection: string, entities?: DiscoveryResult, registryEntries?: ReadonlyArray<RegistryEntry>): FirestoreIndexConfig;
287
+ declare function generateIndexConfig(collection: string, options?: GenerateIndexOptions): FirestoreIndexConfig;
199
288
 
200
289
  /**
201
290
  * Default result limit applied to findEdges/findNodes queries
@@ -491,4 +580,4 @@ declare function deserializeFirestoreTypes(data: Record<string, unknown>, db?: F
491
580
  */
492
581
  declare function createTraversal(reader: GraphClient | GraphReader, startUid: string, registry?: GraphRegistry): TraversalBuilder;
493
582
 
494
- export { BOOTSTRAP_ENTRIES, DEFAULT_QUERY_LIMIT, type DiscoverResult, DiscoveryError, DiscoveryResult, type DiscoveryWarning, DynamicGraphClient, DynamicRegistryConfig, EDGE_TYPE_SCHEMA, type FieldMeta, FindEdgesParams, FindNodesParams, FiregraphError, type FirestoreIndex, type FirestoreIndexConfig, type FirestoreIndexField, GraphClient, GraphClientOptions, GraphReader, GraphRecord, GraphRegistry, META_EDGE_TYPE, META_NODE_TYPE, MigrationExecutor, MigrationFn, type MigrationResult, MigrationStep, MigrationWriteBack, NODE_TYPE_SCHEMA, QueryFilter, QueryPlan, type QuerySafetyResult, RegistryEntry, SERIALIZATION_TAG, StoredGraphRecord, StoredMigrationStep, TraversalBuilder, analyzeQuerySafety, applyMigrationChain, buildEdgeQueryPlan, buildEdgeRecord, buildNodeQueryPlan, buildNodeRecord, compileMigrationFn, compileMigrations, compileSchema, computeEdgeDocId, computeNodeDocId, createBootstrapRegistry, createGraphClient, createGraphClientFromBackend, createMergedRegistry, createRegistry, createRegistryFromGraph, createTraversal, defaultExecutor, deserializeFirestoreTypes, destroySandboxWorker, discoverEntities, generateDeterministicUid, generateId, generateIndexConfig, isAncestorUid, isTaggedValue, jsonSchemaToFieldMeta, matchScope, matchScopeAny, migrateRecord, migrateRecords, precompileSource, resolveAncestorCollection, serializeFirestoreTypes, validateMigrationChain };
583
+ export { BOOTSTRAP_ENTRIES, DEFAULT_CORE_INDEXES, DEFAULT_QUERY_LIMIT, type DiscoverResult, DiscoveryError, DiscoveryResult, type DiscoveryWarning, DynamicGraphClient, DynamicRegistryConfig, EDGE_TYPE_SCHEMA, type FieldMeta, FindEdgesParams, FindNodesParams, FiregraphError, type FirestoreIndex, type FirestoreIndexConfig, type FirestoreIndexField, type GenerateIndexOptions, GraphClient, GraphClientOptions, GraphReader, GraphRecord, GraphRegistry, IndexSpec, META_EDGE_TYPE, META_NODE_TYPE, MigrationExecutor, MigrationFn, type MigrationResult, MigrationStep, MigrationWriteBack, NODE_TYPE_SCHEMA, QueryFilter, QueryPlan, type QuerySafetyResult, RegistryEntry, SERIALIZATION_TAG, StoredGraphRecord, StoredMigrationStep, TraversalBuilder, analyzeQuerySafety, applyMigrationChain, buildEdgeQueryPlan, buildEdgeRecord, buildNodeQueryPlan, buildNodeRecord, compileMigrationFn, compileMigrations, compileSchema, computeEdgeDocId, computeNodeDocId, createBootstrapRegistry, createGraphClient, createGraphClientFromBackend, createMergedRegistry, createRegistry, createRegistryFromGraph, createTraversal, defaultExecutor, deserializeFirestoreTypes, destroySandboxWorker, discoverEntities, generateDeterministicUid, generateId, generateIndexConfig, isAncestorUid, isTaggedValue, jsonSchemaToFieldMeta, matchScope, matchScopeAny, migrateRecord, migrateRecords, precompileSource, resolveAncestorCollection, serializeFirestoreTypes, validateMigrationChain };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { S as StorageBackend, F as FiregraphError } from './scope-path-BtajqNK5.js';
2
- export { C as CrossBackendTransactionError, D as DynamicRegistryError, E as EdgeNotFoundError, I as InvalidQueryError, M as MigrationError, N as NodeNotFoundError, Q as QuerySafetyError, R as RegistryScopeError, c as RegistryViolationError, a as StorageScopeSegment, d as TraversalError, V as ValidationError, b as appendStorageScope, i as isAncestorScopeUid, p as parseStorageScope, r as resolveAncestorScope } from './scope-path-BtajqNK5.js';
3
- import { G as GraphClientOptions, b as GraphClient, a as DynamicGraphClient, c as DiscoveryResult, R as RegistryEntry, d as GraphRegistry, e as GraphReader, M as MigrationExecutor, D as DynamicRegistryConfig, S as StoredGraphRecord, f as MigrationWriteBack, g as MigrationStep, F as FindEdgesParams, Q as QueryPlan, h as FindNodesParams, i as QueryFilter, j as GraphRecord, k as MigrationFn, l as StoredMigrationStep, T as TraversalBuilder } from './types-DfWVTsMn.js';
4
- export { B as BulkBatchError, m as BulkOptions, n as BulkProgress, o as BulkResult, C as CascadeResult, p as DefineTypeOptions, q as DiscoveredEntity, E as EdgeTopology, r as EdgeTypeData, s as FiregraphConfig, t as GraphBatch, u as GraphTransaction, v as GraphWriter, H as HopDefinition, w as HopResult, N as NodeTypeData, x as QueryMode, y as QueryOptions, z as ScanProtection, A as TraversalOptions, I as TraversalResult, V as ViewContext, J as ViewDefaultsConfig, K as ViewResolverConfig, W as WhereClause, L as defineConfig, O as resolveView } from './types-DfWVTsMn.js';
1
+ import { S as StorageBackend } from './backend-U-MLShlg.js';
2
+ import { G as GraphClientOptions, a as GraphClient, D as DynamicGraphClient, I as IndexSpec, b as DiscoveryResult, R as RegistryEntry, c as GraphRegistry, d as GraphReader, M as MigrationExecutor, e as DynamicRegistryConfig, S as StoredGraphRecord, f as MigrationWriteBack, g as MigrationStep, F as FindEdgesParams, Q as QueryPlan, h as FindNodesParams, i as QueryFilter, j as GraphRecord, k as MigrationFn, l as StoredMigrationStep, T as TraversalBuilder } from './types-BGWxcpI_.js';
3
+ export { B as BulkBatchError, m as BulkOptions, n as BulkProgress, o as BulkResult, C as CascadeResult, p as DefineTypeOptions, q as DiscoveredEntity, E as EdgeTopology, r as EdgeTypeData, s as FiregraphConfig, t as GraphBatch, u as GraphTransaction, v as GraphWriter, H as HopDefinition, w as HopResult, x as IndexFieldSpec, N as NodeTypeData, y as QueryMode, z as QueryOptions, A as ScanProtection, J as TraversalOptions, K as TraversalResult, V as ViewContext, L as ViewDefaultsConfig, O as ViewResolverConfig, W as WhereClause, P as defineConfig, U as resolveView } from './types-BGWxcpI_.js';
5
4
  export { CodegenOptions, generateTypes } from './codegen/index.js';
5
+ import { F as FiregraphError } from './scope-path-B1G3YiA7.js';
6
+ export { C as CrossBackendTransactionError, D as DynamicRegistryError, E as EdgeNotFoundError, I as InvalidQueryError, M as MigrationError, N as NodeNotFoundError, Q as QuerySafetyError, R as RegistryScopeError, b as RegistryViolationError, S as StorageScopeSegment, T as TraversalError, V as ValidationError, a as appendStorageScope, i as isAncestorScopeUid, p as parseStorageScope, r as resolveAncestorScope } from './scope-path-B1G3YiA7.js';
6
7
  import { Firestore } from '@google-cloud/firestore';
7
8
  export { Q as QueryClient, a as QueryClientError, b as QueryClientErrorCode, c as QueryClientOptions } from './client-Bk2Cm6xv.js';
8
9
  export { E as EntityViewConfig, a as EntityViewMeta, V as ViewComponentClass, b as ViewMeta, c as ViewRegistry, d as ViewRegistryInput, e as defineViews } from './views-DL60k0cf.js';
@@ -10,9 +11,10 @@ export { E as EntityViewConfig, a as EntityViewMeta, V as ViewComponentClass, b
10
11
  /**
11
12
  * Create a `GraphClient` backed by an arbitrary `StorageBackend`.
12
13
  *
13
- * Used by backend-specific factories (D1, DO-SQLite, etc.) most callers
14
- * should use the higher-level `createGraphClient(firestore, ...)` overload
15
- * below or import the equivalent from `firegraph/d1` / `firegraph/do-sqlite`.
14
+ * Used by backend-specific factories (e.g. `createDOClient` in
15
+ * `firegraph/cloudflare`) — most callers should use the higher-level
16
+ * `createGraphClient(firestore, ...)` overload below for Firestore, or the
17
+ * Cloudflare factory for DO-backed graphs.
16
18
  */
17
19
  declare function createGraphClientFromBackend(backend: StorageBackend, options?: GraphClientOptions, metaBackend?: StorageBackend): GraphClient | DynamicGraphClient;
18
20
 
@@ -62,6 +64,54 @@ declare function resolveAncestorCollection(collectionPath: string, uid: string):
62
64
  */
63
65
  declare function isAncestorUid(collectionPath: string, uid: string): boolean;
64
66
 
67
+ /**
68
+ * Default core index preset.
69
+ *
70
+ * This set covers the query patterns firegraph's query planner emits for
71
+ * built-in operations — `findNodes`, `findEdges`, cascade delete, traversal,
72
+ * and the DO/SQLite path compilers. Apps that need additional indexes
73
+ * (descending timestamps, `data.*` filters, composite fields unique to
74
+ * their query shapes) declare them on `RegistryEntry.indexes` or override
75
+ * this preset wholesale via the backend-specific `coreIndexes` option —
76
+ * `FiregraphDOOptions.coreIndexes` for the DO backend,
77
+ * `BuildSchemaOptions.coreIndexes` for the legacy SQLite backend, and
78
+ * `GenerateIndexOptions.coreIndexes` for the Firestore CLI generator.
79
+ *
80
+ * ## Ownership model
81
+ *
82
+ * This list is firegraph's *recommendation* — not non-negotiable policy.
83
+ * Consumers can:
84
+ *
85
+ * 1. Accept the preset as-is (default).
86
+ * 2. Extend it: `coreIndexes: [...DEFAULT_CORE_INDEXES, ...more]`.
87
+ * 3. Replace it entirely with a tailored set.
88
+ * 4. Disable it (`coreIndexes: []`) and take full responsibility for
89
+ * index coverage — only do this if you're provisioning a complete
90
+ * custom set.
91
+ *
92
+ * ## Per-backend emission
93
+ *
94
+ * The Firestore generator skips single-field entries (Firestore implicitly
95
+ * indexes every field) and emits one composite index per multi-field spec.
96
+ * The SQLite-flavored generators (DO, legacy) emit every spec as-is.
97
+ *
98
+ * ## Why these specific indexes
99
+ *
100
+ * - `aUid` / `bUid` — required for `_fgRemoveNodeCascade`, which scans by
101
+ * each UID side independently. A composite `(aUid, axbType)` also
102
+ * satisfies `aUid`-alone via leading-column prefix, but the single-field
103
+ * form is cheaper for the common case.
104
+ * - `aType` / `bType` — `findNodes({ aType })` and cross-type enumeration.
105
+ * - `(aUid, axbType)` — forward edge lookup (`findEdges({ aUid, axbType })`)
106
+ * and the `get` strategy fallback when only two of three triple fields
107
+ * are present.
108
+ * - `(axbType, bUid)` — reverse edge traversal.
109
+ * - `(aType, axbType)` — type-scoped edge scans (e.g., `findEdges({ aType, axbType })`).
110
+ * - `(axbType, bType)` — scope edges of one relation to a target type.
111
+ */
112
+
113
+ declare const DEFAULT_CORE_INDEXES: ReadonlyArray<IndexSpec>;
114
+
65
115
  /**
66
116
  * Entity Discovery — convention-based auto-discovery of entities from
67
117
  * a per-entity folder structure.
@@ -153,8 +203,10 @@ declare function createRegistryFromGraph(reader: GraphReader, executor?: Migrati
153
203
  * Firestore-specific client factory.
154
204
  *
155
205
  * Kept in its own module so that bundlers don't pull
156
- * `@google-cloud/firestore` into SQLite-only entry points
157
- * (`firegraph/d1`, `firegraph/do-sqlite`).
206
+ * `@google-cloud/firestore` into non-Firestore entry points — most
207
+ * importantly the Cloudflare DO backend (`firegraph/cloudflare`) and the
208
+ * routing primitive (`firegraph/backend`), both of which must load cleanly
209
+ * in a Workers environment where the Node Firestore SDK is unavailable.
158
210
  */
159
211
 
160
212
  declare function createGraphClient(db: Firestore, collectionPath: string, options: GraphClientOptions & {
@@ -164,6 +216,33 @@ declare function createGraphClient(db: Firestore, collectionPath: string, option
164
216
 
165
217
  declare function generateId(): string;
166
218
 
219
+ /**
220
+ * Firestore composite index generator.
221
+ *
222
+ * Translates firegraph's declarative `IndexSpec[]` (core preset plus per-entry
223
+ * registry indexes) into the `firestore.indexes.json` shape consumed by
224
+ * `firebase deploy --only firestore:indexes`.
225
+ *
226
+ * ## What Firestore needs
227
+ *
228
+ * Firestore auto-indexes every top-level field (including `data.*`) for
229
+ * single-field equality queries — we only need to emit *composite* indexes
230
+ * here. That means:
231
+ *
232
+ * 1. Single-field specs are dropped (Firestore already covers them).
233
+ * 2. Composite specs (two or more fields) get one `FirestoreIndex`.
234
+ * 3. Specs with `where` are dropped with a warning — Firestore composite
235
+ * indexes do not support partial predicates.
236
+ * 4. When a registry entry has `targetGraph` set, every composite is also
237
+ * emitted with `queryScope: 'COLLECTION_GROUP'` under the targetGraph
238
+ * name, so `findEdgesGlobal()` queries across subgraphs can hit an
239
+ * index.
240
+ *
241
+ * The SQLite-flavored backends (DO, legacy) consume the same `IndexSpec[]`
242
+ * via `src/internal/sqlite-index-ddl.ts` but emit every spec (single fields
243
+ * included) as `CREATE INDEX` DDL.
244
+ */
245
+
167
246
  interface FirestoreIndexField {
168
247
  fieldPath: string;
169
248
  order: 'ASCENDING' | 'DESCENDING';
@@ -177,25 +256,35 @@ interface FirestoreIndexConfig {
177
256
  indexes: FirestoreIndex[];
178
257
  fieldOverrides: unknown[];
179
258
  }
259
+ interface GenerateIndexOptions {
260
+ /**
261
+ * Replaces firegraph's built-in core preset. Defaults to
262
+ * `DEFAULT_CORE_INDEXES`. Pass `[]` to disable core indexes entirely.
263
+ */
264
+ coreIndexes?: IndexSpec[];
265
+ /**
266
+ * Registry entries supplying per-triple `indexes`. Entries without
267
+ * `indexes` contribute no composites; entries with `targetGraph` also
268
+ * trigger `COLLECTION_GROUP` mirrors under each distinct targetGraph
269
+ * segment name.
270
+ */
271
+ registryEntries?: ReadonlyArray<RegistryEntry>;
272
+ /**
273
+ * Entity discovery result. Convenience for callers that have a
274
+ * `DiscoveryResult` but not a built registry — treated as if every
275
+ * discovered entity were expanded to its registry entries carrying just
276
+ * `indexes` + `targetGraph`. Mutually usable with `registryEntries`
277
+ * (both are concatenated and deduplicated at the spec level).
278
+ */
279
+ entities?: DiscoveryResult;
280
+ }
180
281
  /**
181
- * Generates a Firestore index configuration for a firegraph collection.
182
- *
183
- * Always includes the 4 base composite indexes. If an entity discovery result
184
- * is provided, generates additional data-field indexes for common query
185
- * patterns on node data fields:
186
- * (aType, axbType, data.{field})
187
- *
188
- * When registry entries with `targetGraph` are provided, also generates
189
- * collection group indexes for `findEdgesGlobal()` queries. The collection
190
- * group name defaults to `'graph'` (the standard subgraph name) but can be
191
- * overridden per `targetGraph` value.
192
- *
193
- * @param collection - Firestore collection name (e.g. 'graph')
194
- * @param entities - Optional discovery result for per-entity data field indexes
195
- * @param registryEntries - Optional registry entries; when any have `targetGraph`,
196
- * collection group indexes are generated for the distinct subgraph names
282
+ * Build a Firestore index configuration from firegraph's declarative index
283
+ * specs. Deduplicates by field list + scope before emitting. Single-field
284
+ * specs are dropped; partial-index specs (`where` set) are dropped with a
285
+ * one-time warning.
197
286
  */
198
- declare function generateIndexConfig(collection: string, entities?: DiscoveryResult, registryEntries?: ReadonlyArray<RegistryEntry>): FirestoreIndexConfig;
287
+ declare function generateIndexConfig(collection: string, options?: GenerateIndexOptions): FirestoreIndexConfig;
199
288
 
200
289
  /**
201
290
  * Default result limit applied to findEdges/findNodes queries
@@ -491,4 +580,4 @@ declare function deserializeFirestoreTypes(data: Record<string, unknown>, db?: F
491
580
  */
492
581
  declare function createTraversal(reader: GraphClient | GraphReader, startUid: string, registry?: GraphRegistry): TraversalBuilder;
493
582
 
494
- export { BOOTSTRAP_ENTRIES, DEFAULT_QUERY_LIMIT, type DiscoverResult, DiscoveryError, DiscoveryResult, type DiscoveryWarning, DynamicGraphClient, DynamicRegistryConfig, EDGE_TYPE_SCHEMA, type FieldMeta, FindEdgesParams, FindNodesParams, FiregraphError, type FirestoreIndex, type FirestoreIndexConfig, type FirestoreIndexField, GraphClient, GraphClientOptions, GraphReader, GraphRecord, GraphRegistry, META_EDGE_TYPE, META_NODE_TYPE, MigrationExecutor, MigrationFn, type MigrationResult, MigrationStep, MigrationWriteBack, NODE_TYPE_SCHEMA, QueryFilter, QueryPlan, type QuerySafetyResult, RegistryEntry, SERIALIZATION_TAG, StoredGraphRecord, StoredMigrationStep, TraversalBuilder, analyzeQuerySafety, applyMigrationChain, buildEdgeQueryPlan, buildEdgeRecord, buildNodeQueryPlan, buildNodeRecord, compileMigrationFn, compileMigrations, compileSchema, computeEdgeDocId, computeNodeDocId, createBootstrapRegistry, createGraphClient, createGraphClientFromBackend, createMergedRegistry, createRegistry, createRegistryFromGraph, createTraversal, defaultExecutor, deserializeFirestoreTypes, destroySandboxWorker, discoverEntities, generateDeterministicUid, generateId, generateIndexConfig, isAncestorUid, isTaggedValue, jsonSchemaToFieldMeta, matchScope, matchScopeAny, migrateRecord, migrateRecords, precompileSource, resolveAncestorCollection, serializeFirestoreTypes, validateMigrationChain };
583
+ export { BOOTSTRAP_ENTRIES, DEFAULT_CORE_INDEXES, DEFAULT_QUERY_LIMIT, type DiscoverResult, DiscoveryError, DiscoveryResult, type DiscoveryWarning, DynamicGraphClient, DynamicRegistryConfig, EDGE_TYPE_SCHEMA, type FieldMeta, FindEdgesParams, FindNodesParams, FiregraphError, type FirestoreIndex, type FirestoreIndexConfig, type FirestoreIndexField, type GenerateIndexOptions, GraphClient, GraphClientOptions, GraphReader, GraphRecord, GraphRegistry, IndexSpec, META_EDGE_TYPE, META_NODE_TYPE, MigrationExecutor, MigrationFn, type MigrationResult, MigrationStep, MigrationWriteBack, NODE_TYPE_SCHEMA, QueryFilter, QueryPlan, type QuerySafetyResult, RegistryEntry, SERIALIZATION_TAG, StoredGraphRecord, StoredMigrationStep, TraversalBuilder, analyzeQuerySafety, applyMigrationChain, buildEdgeQueryPlan, buildEdgeRecord, buildNodeQueryPlan, buildNodeRecord, compileMigrationFn, compileMigrations, compileSchema, computeEdgeDocId, computeNodeDocId, createBootstrapRegistry, createGraphClient, createGraphClientFromBackend, createMergedRegistry, createRegistry, createRegistryFromGraph, createTraversal, defaultExecutor, deserializeFirestoreTypes, destroySandboxWorker, discoverEntities, generateDeterministicUid, generateId, generateIndexConfig, isAncestorUid, isTaggedValue, jsonSchemaToFieldMeta, matchScope, matchScopeAny, migrateRecord, migrateRecords, precompileSource, resolveAncestorCollection, serializeFirestoreTypes, validateMigrationChain };
package/dist/index.js CHANGED
@@ -1,9 +1,3 @@
1
- import {
2
- SERIALIZATION_TAG,
3
- deserializeFirestoreTypes,
4
- isTaggedValue,
5
- serializeFirestoreTypes
6
- } from "./chunk-5753Y42M.js";
7
1
  import {
8
2
  appendStorageScope,
9
3
  isAncestorScopeUid,
@@ -12,6 +6,7 @@ import {
12
6
  } from "./chunk-TYYPRVIE.js";
13
7
  import {
14
8
  BOOTSTRAP_ENTRIES,
9
+ DEFAULT_CORE_INDEXES,
15
10
  DEFAULT_QUERY_LIMIT,
16
11
  EDGE_TYPE_SCHEMA,
17
12
  GraphClientImpl,
@@ -43,7 +38,7 @@ import {
43
38
  migrateRecords,
44
39
  precompileSource,
45
40
  validateMigrationChain
46
- } from "./chunk-EVUM6ORB.js";
41
+ } from "./chunk-6SB34IPQ.js";
47
42
  import {
48
43
  CrossBackendTransactionError,
49
44
  DynamicRegistryError,
@@ -57,14 +52,20 @@ import {
57
52
  RegistryViolationError,
58
53
  TraversalError,
59
54
  ValidationError
60
- } from "./chunk-GLOVWKQH.js";
55
+ } from "./chunk-R7CRGYY4.js";
61
56
  import {
62
57
  generateTypes
63
- } from "./chunk-YLGXLEUE.js";
58
+ } from "./chunk-GJVVRTQT.js";
64
59
  import {
65
60
  QueryClient,
66
61
  QueryClientError
67
- } from "./chunk-SU4FNLC3.js";
62
+ } from "./chunk-EEKWRX5E.js";
63
+ import {
64
+ SERIALIZATION_TAG,
65
+ deserializeFirestoreTypes,
66
+ isTaggedValue,
67
+ serializeFirestoreTypes
68
+ } from "./chunk-5753Y42M.js";
68
69
 
69
70
  // src/config.ts
70
71
  function defineConfig(config) {
@@ -219,7 +220,8 @@ function loadNodeEntity(dir, name) {
219
220
  sampleData,
220
221
  allowedIn: meta?.allowedIn,
221
222
  migrations,
222
- migrationWriteBack: meta?.migrationWriteBack
223
+ migrationWriteBack: meta?.migrationWriteBack,
224
+ indexes: meta?.indexes
223
225
  };
224
226
  }
225
227
  function loadEdgeEntity(dir, name) {
@@ -256,7 +258,8 @@ function loadEdgeEntity(dir, name) {
256
258
  allowedIn: meta?.allowedIn,
257
259
  targetGraph: topology.targetGraph ?? meta?.targetGraph,
258
260
  migrations,
259
- migrationWriteBack: meta?.migrationWriteBack
261
+ migrationWriteBack: meta?.migrationWriteBack,
262
+ indexes: meta?.indexes
260
263
  };
261
264
  }
262
265
  function getSubdirectories(dir) {
@@ -783,124 +786,74 @@ function generateId() {
783
786
  }
784
787
 
785
788
  // src/indexes.ts
786
- function baseIndexes(collection) {
787
- return [
788
- {
789
- collectionGroup: collection,
790
- queryScope: "COLLECTION",
791
- fields: [
792
- { fieldPath: "aUid", order: "ASCENDING" },
793
- { fieldPath: "axbType", order: "ASCENDING" }
794
- ]
795
- },
796
- {
797
- collectionGroup: collection,
798
- queryScope: "COLLECTION",
799
- fields: [
800
- { fieldPath: "axbType", order: "ASCENDING" },
801
- { fieldPath: "bUid", order: "ASCENDING" }
802
- ]
803
- },
804
- {
805
- collectionGroup: collection,
806
- queryScope: "COLLECTION",
807
- fields: [
808
- { fieldPath: "aType", order: "ASCENDING" },
809
- { fieldPath: "axbType", order: "ASCENDING" }
810
- ]
811
- },
812
- {
813
- collectionGroup: collection,
814
- queryScope: "COLLECTION",
815
- fields: [
816
- { fieldPath: "axbType", order: "ASCENDING" },
817
- { fieldPath: "bType", order: "ASCENDING" }
818
- ]
819
- }
820
- ];
789
+ function normalizeField(f) {
790
+ return typeof f === "string" ? { path: f, desc: false } : { path: f.path, desc: !!f.desc };
821
791
  }
822
- function extractSchemaFields(schema) {
823
- const s = schema;
824
- if (s.type !== "object" || !s.properties) return [];
825
- return Object.keys(s.properties);
792
+ function specFingerprint(spec, scope) {
793
+ const normalized = spec.fields.map(normalizeField);
794
+ return `${scope}::${JSON.stringify(normalized)}`;
826
795
  }
827
- function collectionGroupIndexes(collectionName) {
828
- return [
829
- {
830
- collectionGroup: collectionName,
831
- queryScope: "COLLECTION_GROUP",
832
- fields: [
833
- { fieldPath: "aUid", order: "ASCENDING" },
834
- { fieldPath: "axbType", order: "ASCENDING" }
835
- ]
836
- },
837
- {
838
- collectionGroup: collectionName,
839
- queryScope: "COLLECTION_GROUP",
840
- fields: [
841
- { fieldPath: "axbType", order: "ASCENDING" },
842
- { fieldPath: "bUid", order: "ASCENDING" }
843
- ]
844
- },
845
- {
846
- collectionGroup: collectionName,
847
- queryScope: "COLLECTION_GROUP",
848
- fields: [
849
- { fieldPath: "aType", order: "ASCENDING" },
850
- { fieldPath: "axbType", order: "ASCENDING" }
851
- ]
852
- },
853
- {
854
- collectionGroup: collectionName,
855
- queryScope: "COLLECTION_GROUP",
856
- fields: [
857
- { fieldPath: "axbType", order: "ASCENDING" },
858
- { fieldPath: "bType", order: "ASCENDING" }
859
- ]
860
- }
861
- ];
796
+ function toFirestoreFields(spec) {
797
+ return spec.fields.map((f) => {
798
+ const n = normalizeField(f);
799
+ return {
800
+ fieldPath: n.path,
801
+ order: n.desc ? "DESCENDING" : "ASCENDING"
802
+ };
803
+ });
862
804
  }
863
- function generateIndexConfig(collection, entities, registryEntries) {
864
- const indexes = baseIndexes(collection);
865
- if (entities) {
866
- for (const [, entity] of entities.nodes) {
867
- const fields = extractSchemaFields(entity.schema);
868
- for (const field of fields) {
869
- indexes.push({
870
- collectionGroup: collection,
871
- queryScope: "COLLECTION",
872
- fields: [
873
- { fieldPath: "aType", order: "ASCENDING" },
874
- { fieldPath: "axbType", order: "ASCENDING" },
875
- { fieldPath: `data.${field}`, order: "ASCENDING" }
876
- ]
877
- });
878
- }
879
- }
880
- for (const [, entity] of entities.edges) {
881
- const fields = extractSchemaFields(entity.schema);
882
- for (const field of fields) {
883
- indexes.push({
884
- collectionGroup: collection,
885
- queryScope: "COLLECTION",
886
- fields: [
887
- { fieldPath: "aUid", order: "ASCENDING" },
888
- { fieldPath: "axbType", order: "ASCENDING" },
889
- { fieldPath: `data.${field}`, order: "ASCENDING" }
890
- ]
891
- });
892
- }
805
+ var warnedOnPartialIndex = false;
806
+ function generateIndexConfig(collection, options = {}) {
807
+ const core = options.coreIndexes ?? [...DEFAULT_CORE_INDEXES];
808
+ const fromEntries = (options.registryEntries ?? []).flatMap((e) => {
809
+ if (!e.indexes) return [];
810
+ return e.indexes;
811
+ });
812
+ const targetGraphNames = /* @__PURE__ */ new Set();
813
+ for (const entry of options.registryEntries ?? []) {
814
+ if (entry.targetGraph) targetGraphNames.add(entry.targetGraph);
815
+ }
816
+ if (options.entities) {
817
+ for (const [, entity] of options.entities.edges) {
818
+ const tg = entity.targetGraph ?? entity.topology?.targetGraph;
819
+ if (tg) targetGraphNames.add(tg);
893
820
  }
894
821
  }
895
- if (registryEntries) {
896
- const targetGraphNames = /* @__PURE__ */ new Set();
897
- for (const entry of registryEntries) {
898
- if (entry.targetGraph) {
899
- targetGraphNames.add(entry.targetGraph);
822
+ const allSpecs = [...core, ...fromEntries];
823
+ const seen = /* @__PURE__ */ new Set();
824
+ const indexes = [];
825
+ for (const spec of allSpecs) {
826
+ if (!spec.fields || spec.fields.length < 2) {
827
+ continue;
828
+ }
829
+ if (spec.where) {
830
+ if (!warnedOnPartialIndex) {
831
+ warnedOnPartialIndex = true;
832
+ console.warn(
833
+ "firegraph: IndexSpec.where is ignored by the Firestore generator \u2014 Firestore composite indexes do not support predicates. The SQLite backends will still honor `where`."
834
+ );
900
835
  }
836
+ continue;
837
+ }
838
+ const fields = toFirestoreFields(spec);
839
+ const colKey = specFingerprint(spec, `col:${collection}`);
840
+ if (!seen.has(colKey)) {
841
+ seen.add(colKey);
842
+ indexes.push({
843
+ collectionGroup: collection,
844
+ queryScope: "COLLECTION",
845
+ fields
846
+ });
901
847
  }
902
- for (const name of targetGraphNames) {
903
- indexes.push(...collectionGroupIndexes(name));
848
+ for (const tg of targetGraphNames) {
849
+ const cgKey = specFingerprint(spec, `cg:${tg}`);
850
+ if (seen.has(cgKey)) continue;
851
+ seen.add(cgKey);
852
+ indexes.push({
853
+ collectionGroup: tg,
854
+ queryScope: "COLLECTION_GROUP",
855
+ fields
856
+ });
904
857
  }
905
858
  }
906
859
  return { indexes, fieldOverrides: [] };
@@ -1226,6 +1179,7 @@ function defineViews(input) {
1226
1179
  export {
1227
1180
  BOOTSTRAP_ENTRIES,
1228
1181
  CrossBackendTransactionError,
1182
+ DEFAULT_CORE_INDEXES,
1229
1183
  DEFAULT_QUERY_LIMIT,
1230
1184
  DiscoveryError,
1231
1185
  DynamicRegistryError,