@typicalday/firegraph 0.2.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.
- package/README.md +253 -6
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/editor/server/index.mjs +63 -4
- package/dist/{index-wSlVH5Nv.d.cts → index-CQkofEC_.d.cts} +61 -0
- package/dist/{index-wSlVH5Nv.d.ts → index-CQkofEC_.d.ts} +61 -0
- package/dist/index.cjs +201 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +70 -6
- package/dist/index.d.ts +70 -6
- package/dist/index.js +199 -25
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Firestore } from '@google-cloud/firestore';
|
|
2
|
-
import { G as GraphClientOptions, D as DynamicRegistryConfig, a as DynamicGraphClient, b as GraphClient, R as RegistryEntry, c as DiscoveryResult, d as GraphRegistry, e as GraphReader, f as GraphRecord, F as FindEdgesParams, Q as QueryPlan, g as FindNodesParams, T as TraversalBuilder, h as QueryFilter } from './index-
|
|
3
|
-
export { B as BulkBatchError, i as BulkOptions, j as BulkProgress, k as BulkResult, C as CascadeResult, l as CodegenOptions, m as DefineTypeOptions, n as DiscoveredEntity, E as EdgeTopology, o as EdgeTypeData, p as FiregraphConfig, q as GraphBatch, r as GraphTransaction, s as GraphWriter, H as HopDefinition, t as HopResult, N as NodeTypeData, u as QueryMode, v as QueryOptions, S as ScanProtection, w as StoredGraphRecord, x as TraversalOptions, y as TraversalResult, V as ViewContext, z as ViewDefaultsConfig, A as ViewResolverConfig, W as WhereClause, I as defineConfig, J as generateTypes, K as resolveView } from './index-
|
|
2
|
+
import { G as GraphClientOptions, D as DynamicRegistryConfig, a as DynamicGraphClient, b as GraphClient, R as RegistryEntry, c as DiscoveryResult, d as GraphRegistry, e as GraphReader, f as GraphRecord, F as FindEdgesParams, Q as QueryPlan, g as FindNodesParams, T as TraversalBuilder, h as QueryFilter } from './index-CQkofEC_.cjs';
|
|
3
|
+
export { B as BulkBatchError, i as BulkOptions, j as BulkProgress, k as BulkResult, C as CascadeResult, l as CodegenOptions, m as DefineTypeOptions, n as DiscoveredEntity, E as EdgeTopology, o as EdgeTypeData, p as FiregraphConfig, q as GraphBatch, r as GraphTransaction, s as GraphWriter, H as HopDefinition, t as HopResult, N as NodeTypeData, u as QueryMode, v as QueryOptions, S as ScanProtection, w as StoredGraphRecord, x as TraversalOptions, y as TraversalResult, V as ViewContext, z as ViewDefaultsConfig, A as ViewResolverConfig, W as WhereClause, I as defineConfig, J as generateTypes, K as resolveView } from './index-CQkofEC_.cjs';
|
|
4
4
|
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';
|
|
5
5
|
export { Q as QueryClient, a as QueryClientError, b as QueryClientErrorCode, c as QueryClientOptions } from './client-Bk2Cm6xv.cjs';
|
|
6
6
|
|
|
@@ -71,7 +71,18 @@ declare function buildEdgeRecord(aType: string, aUid: string, axbType: string, b
|
|
|
71
71
|
declare function buildEdgeQueryPlan(params: FindEdgesParams): QueryPlan;
|
|
72
72
|
declare function buildNodeQueryPlan(params: FindNodesParams): QueryPlan;
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Create a traversal builder for multi-hop graph traversal.
|
|
76
|
+
*
|
|
77
|
+
* Accepts either a `GraphReader` (backwards compatible) or a `GraphClient`.
|
|
78
|
+
* When a `GraphClient` is provided, cross-graph traversal via `targetGraph`
|
|
79
|
+
* is supported — the traversal can follow edges into subgraphs.
|
|
80
|
+
*
|
|
81
|
+
* @param reader - A `GraphClient` or `GraphReader` to execute queries against
|
|
82
|
+
* @param startUid - UID of the starting node
|
|
83
|
+
* @param registry - Optional registry for automatic `targetGraph` resolution
|
|
84
|
+
*/
|
|
85
|
+
declare function createTraversal(reader: GraphClient | GraphReader, startUid: string, registry?: GraphRegistry): TraversalBuilder;
|
|
75
86
|
|
|
76
87
|
declare class FiregraphError extends Error {
|
|
77
88
|
readonly code: string;
|
|
@@ -187,6 +198,52 @@ declare function matchScope(scopePath: string, pattern: string): boolean;
|
|
|
187
198
|
*/
|
|
188
199
|
declare function matchScopeAny(scopePath: string, patterns: string[]): boolean;
|
|
189
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Cross-graph edge resolution utilities.
|
|
203
|
+
*
|
|
204
|
+
* Provides path-scanning resolution for determining whether an edge's source
|
|
205
|
+
* (aUid) is an ancestor node by checking if the UID appears in the Firestore
|
|
206
|
+
* collection path.
|
|
207
|
+
*
|
|
208
|
+
* Firestore paths have a rigid alternating structure:
|
|
209
|
+
* collection / docId / collection / docId / collection
|
|
210
|
+
*
|
|
211
|
+
* Given a path like `graph/A/workspace/B/context`, segments at even indices
|
|
212
|
+
* are collection names and odd indices are document IDs. When we find a UID
|
|
213
|
+
* at an odd index, the collection containing that document is the path up to
|
|
214
|
+
* (and including) the preceding even-index segment.
|
|
215
|
+
*/
|
|
216
|
+
/**
|
|
217
|
+
* Parse a Firestore collection path and determine the collection path
|
|
218
|
+
* where a given UID's document lives, if that UID is an ancestor in the path.
|
|
219
|
+
*
|
|
220
|
+
* @param collectionPath - The full Firestore collection path of the current client
|
|
221
|
+
* @param uid - The UID to search for in the path
|
|
222
|
+
* @returns The collection path containing the UID, or `null` if not found in the path
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* // Path: graph/A/workspace/B/context
|
|
227
|
+
* resolveAncestorCollection('graph/A/workspace/B/context', 'A')
|
|
228
|
+
* // → 'graph'
|
|
229
|
+
*
|
|
230
|
+
* resolveAncestorCollection('graph/A/workspace/B/context', 'B')
|
|
231
|
+
* // → 'graph/A/workspace'
|
|
232
|
+
*
|
|
233
|
+
* resolveAncestorCollection('graph/A/workspace/B/context', 'unknown')
|
|
234
|
+
* // → null
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
declare function resolveAncestorCollection(collectionPath: string, uid: string): string | null;
|
|
238
|
+
/**
|
|
239
|
+
* Check whether a UID belongs to an ancestor node by scanning the collection path.
|
|
240
|
+
*
|
|
241
|
+
* @param collectionPath - The full Firestore collection path of the current client
|
|
242
|
+
* @param uid - The UID to check
|
|
243
|
+
* @returns `true` if the UID appears as a document segment in the path
|
|
244
|
+
*/
|
|
245
|
+
declare function isAncestorUid(collectionPath: string, uid: string): boolean;
|
|
246
|
+
|
|
190
247
|
/**
|
|
191
248
|
* JSON Schema validation and introspection utilities.
|
|
192
249
|
*
|
|
@@ -226,7 +283,7 @@ interface FirestoreIndexField {
|
|
|
226
283
|
}
|
|
227
284
|
interface FirestoreIndex {
|
|
228
285
|
collectionGroup: string;
|
|
229
|
-
queryScope: 'COLLECTION';
|
|
286
|
+
queryScope: 'COLLECTION' | 'COLLECTION_GROUP';
|
|
230
287
|
fields: FirestoreIndexField[];
|
|
231
288
|
}
|
|
232
289
|
interface FirestoreIndexConfig {
|
|
@@ -241,10 +298,17 @@ interface FirestoreIndexConfig {
|
|
|
241
298
|
* patterns on node data fields:
|
|
242
299
|
* (aType, axbType, data.{field})
|
|
243
300
|
*
|
|
301
|
+
* When registry entries with `targetGraph` are provided, also generates
|
|
302
|
+
* collection group indexes for `findEdgesGlobal()` queries. The collection
|
|
303
|
+
* group name defaults to `'graph'` (the standard subgraph name) but can be
|
|
304
|
+
* overridden per `targetGraph` value.
|
|
305
|
+
*
|
|
244
306
|
* @param collection - Firestore collection name (e.g. 'graph')
|
|
245
307
|
* @param entities - Optional discovery result for per-entity data field indexes
|
|
308
|
+
* @param registryEntries - Optional registry entries; when any have `targetGraph`,
|
|
309
|
+
* collection group indexes are generated for the distinct subgraph names
|
|
246
310
|
*/
|
|
247
|
-
declare function generateIndexConfig(collection: string, entities?: DiscoveryResult): FirestoreIndexConfig;
|
|
311
|
+
declare function generateIndexConfig(collection: string, entities?: DiscoveryResult, registryEntries?: ReadonlyArray<RegistryEntry>): FirestoreIndexConfig;
|
|
248
312
|
|
|
249
313
|
/**
|
|
250
314
|
* Result of analyzing a query for collection scan risk.
|
|
@@ -272,4 +336,4 @@ declare function analyzeQuerySafety(filters: QueryFilter[]): QuerySafetyResult;
|
|
|
272
336
|
*/
|
|
273
337
|
declare const DEFAULT_QUERY_LIMIT = 500;
|
|
274
338
|
|
|
275
|
-
export { BOOTSTRAP_ENTRIES, DEFAULT_QUERY_LIMIT, type DiscoverResult, DiscoveryError, DiscoveryResult, type DiscoveryWarning, DynamicGraphClient, DynamicRegistryConfig, DynamicRegistryError, EDGE_TYPE_SCHEMA, EdgeNotFoundError, type FieldMeta, FindEdgesParams, FindNodesParams, FiregraphError, type FirestoreIndex, type FirestoreIndexConfig, type FirestoreIndexField, GraphClient, GraphClientOptions, GraphReader, GraphRecord, GraphRegistry, InvalidQueryError, META_EDGE_TYPE, META_NODE_TYPE, NODE_TYPE_SCHEMA, NodeNotFoundError, QueryFilter, QueryPlan, QuerySafetyError, type QuerySafetyResult, RegistryEntry, RegistryScopeError, RegistryViolationError, TraversalBuilder, TraversalError, ValidationError, analyzeQuerySafety, buildEdgeQueryPlan, buildEdgeRecord, buildNodeQueryPlan, buildNodeRecord, compileSchema, computeEdgeDocId, computeNodeDocId, createBootstrapRegistry, createGraphClient, createRegistry, createRegistryFromGraph, createTraversal, discoverEntities, generateDeterministicUid, generateId, generateIndexConfig, jsonSchemaToFieldMeta, matchScope, matchScopeAny };
|
|
339
|
+
export { BOOTSTRAP_ENTRIES, DEFAULT_QUERY_LIMIT, type DiscoverResult, DiscoveryError, DiscoveryResult, type DiscoveryWarning, DynamicGraphClient, DynamicRegistryConfig, DynamicRegistryError, EDGE_TYPE_SCHEMA, EdgeNotFoundError, type FieldMeta, FindEdgesParams, FindNodesParams, FiregraphError, type FirestoreIndex, type FirestoreIndexConfig, type FirestoreIndexField, GraphClient, GraphClientOptions, GraphReader, GraphRecord, GraphRegistry, InvalidQueryError, META_EDGE_TYPE, META_NODE_TYPE, NODE_TYPE_SCHEMA, NodeNotFoundError, QueryFilter, QueryPlan, QuerySafetyError, type QuerySafetyResult, RegistryEntry, RegistryScopeError, RegistryViolationError, TraversalBuilder, TraversalError, ValidationError, analyzeQuerySafety, buildEdgeQueryPlan, buildEdgeRecord, buildNodeQueryPlan, buildNodeRecord, compileSchema, computeEdgeDocId, computeNodeDocId, createBootstrapRegistry, createGraphClient, createRegistry, createRegistryFromGraph, createTraversal, discoverEntities, generateDeterministicUid, generateId, generateIndexConfig, isAncestorUid, jsonSchemaToFieldMeta, matchScope, matchScopeAny, resolveAncestorCollection };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Firestore } from '@google-cloud/firestore';
|
|
2
|
-
import { G as GraphClientOptions, D as DynamicRegistryConfig, a as DynamicGraphClient, b as GraphClient, R as RegistryEntry, c as DiscoveryResult, d as GraphRegistry, e as GraphReader, f as GraphRecord, F as FindEdgesParams, Q as QueryPlan, g as FindNodesParams, T as TraversalBuilder, h as QueryFilter } from './index-
|
|
3
|
-
export { B as BulkBatchError, i as BulkOptions, j as BulkProgress, k as BulkResult, C as CascadeResult, l as CodegenOptions, m as DefineTypeOptions, n as DiscoveredEntity, E as EdgeTopology, o as EdgeTypeData, p as FiregraphConfig, q as GraphBatch, r as GraphTransaction, s as GraphWriter, H as HopDefinition, t as HopResult, N as NodeTypeData, u as QueryMode, v as QueryOptions, S as ScanProtection, w as StoredGraphRecord, x as TraversalOptions, y as TraversalResult, V as ViewContext, z as ViewDefaultsConfig, A as ViewResolverConfig, W as WhereClause, I as defineConfig, J as generateTypes, K as resolveView } from './index-
|
|
2
|
+
import { G as GraphClientOptions, D as DynamicRegistryConfig, a as DynamicGraphClient, b as GraphClient, R as RegistryEntry, c as DiscoveryResult, d as GraphRegistry, e as GraphReader, f as GraphRecord, F as FindEdgesParams, Q as QueryPlan, g as FindNodesParams, T as TraversalBuilder, h as QueryFilter } from './index-CQkofEC_.js';
|
|
3
|
+
export { B as BulkBatchError, i as BulkOptions, j as BulkProgress, k as BulkResult, C as CascadeResult, l as CodegenOptions, m as DefineTypeOptions, n as DiscoveredEntity, E as EdgeTopology, o as EdgeTypeData, p as FiregraphConfig, q as GraphBatch, r as GraphTransaction, s as GraphWriter, H as HopDefinition, t as HopResult, N as NodeTypeData, u as QueryMode, v as QueryOptions, S as ScanProtection, w as StoredGraphRecord, x as TraversalOptions, y as TraversalResult, V as ViewContext, z as ViewDefaultsConfig, A as ViewResolverConfig, W as WhereClause, I as defineConfig, J as generateTypes, K as resolveView } from './index-CQkofEC_.js';
|
|
4
4
|
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';
|
|
5
5
|
export { Q as QueryClient, a as QueryClientError, b as QueryClientErrorCode, c as QueryClientOptions } from './client-Bk2Cm6xv.js';
|
|
6
6
|
|
|
@@ -71,7 +71,18 @@ declare function buildEdgeRecord(aType: string, aUid: string, axbType: string, b
|
|
|
71
71
|
declare function buildEdgeQueryPlan(params: FindEdgesParams): QueryPlan;
|
|
72
72
|
declare function buildNodeQueryPlan(params: FindNodesParams): QueryPlan;
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Create a traversal builder for multi-hop graph traversal.
|
|
76
|
+
*
|
|
77
|
+
* Accepts either a `GraphReader` (backwards compatible) or a `GraphClient`.
|
|
78
|
+
* When a `GraphClient` is provided, cross-graph traversal via `targetGraph`
|
|
79
|
+
* is supported — the traversal can follow edges into subgraphs.
|
|
80
|
+
*
|
|
81
|
+
* @param reader - A `GraphClient` or `GraphReader` to execute queries against
|
|
82
|
+
* @param startUid - UID of the starting node
|
|
83
|
+
* @param registry - Optional registry for automatic `targetGraph` resolution
|
|
84
|
+
*/
|
|
85
|
+
declare function createTraversal(reader: GraphClient | GraphReader, startUid: string, registry?: GraphRegistry): TraversalBuilder;
|
|
75
86
|
|
|
76
87
|
declare class FiregraphError extends Error {
|
|
77
88
|
readonly code: string;
|
|
@@ -187,6 +198,52 @@ declare function matchScope(scopePath: string, pattern: string): boolean;
|
|
|
187
198
|
*/
|
|
188
199
|
declare function matchScopeAny(scopePath: string, patterns: string[]): boolean;
|
|
189
200
|
|
|
201
|
+
/**
|
|
202
|
+
* Cross-graph edge resolution utilities.
|
|
203
|
+
*
|
|
204
|
+
* Provides path-scanning resolution for determining whether an edge's source
|
|
205
|
+
* (aUid) is an ancestor node by checking if the UID appears in the Firestore
|
|
206
|
+
* collection path.
|
|
207
|
+
*
|
|
208
|
+
* Firestore paths have a rigid alternating structure:
|
|
209
|
+
* collection / docId / collection / docId / collection
|
|
210
|
+
*
|
|
211
|
+
* Given a path like `graph/A/workspace/B/context`, segments at even indices
|
|
212
|
+
* are collection names and odd indices are document IDs. When we find a UID
|
|
213
|
+
* at an odd index, the collection containing that document is the path up to
|
|
214
|
+
* (and including) the preceding even-index segment.
|
|
215
|
+
*/
|
|
216
|
+
/**
|
|
217
|
+
* Parse a Firestore collection path and determine the collection path
|
|
218
|
+
* where a given UID's document lives, if that UID is an ancestor in the path.
|
|
219
|
+
*
|
|
220
|
+
* @param collectionPath - The full Firestore collection path of the current client
|
|
221
|
+
* @param uid - The UID to search for in the path
|
|
222
|
+
* @returns The collection path containing the UID, or `null` if not found in the path
|
|
223
|
+
*
|
|
224
|
+
* @example
|
|
225
|
+
* ```ts
|
|
226
|
+
* // Path: graph/A/workspace/B/context
|
|
227
|
+
* resolveAncestorCollection('graph/A/workspace/B/context', 'A')
|
|
228
|
+
* // → 'graph'
|
|
229
|
+
*
|
|
230
|
+
* resolveAncestorCollection('graph/A/workspace/B/context', 'B')
|
|
231
|
+
* // → 'graph/A/workspace'
|
|
232
|
+
*
|
|
233
|
+
* resolveAncestorCollection('graph/A/workspace/B/context', 'unknown')
|
|
234
|
+
* // → null
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
declare function resolveAncestorCollection(collectionPath: string, uid: string): string | null;
|
|
238
|
+
/**
|
|
239
|
+
* Check whether a UID belongs to an ancestor node by scanning the collection path.
|
|
240
|
+
*
|
|
241
|
+
* @param collectionPath - The full Firestore collection path of the current client
|
|
242
|
+
* @param uid - The UID to check
|
|
243
|
+
* @returns `true` if the UID appears as a document segment in the path
|
|
244
|
+
*/
|
|
245
|
+
declare function isAncestorUid(collectionPath: string, uid: string): boolean;
|
|
246
|
+
|
|
190
247
|
/**
|
|
191
248
|
* JSON Schema validation and introspection utilities.
|
|
192
249
|
*
|
|
@@ -226,7 +283,7 @@ interface FirestoreIndexField {
|
|
|
226
283
|
}
|
|
227
284
|
interface FirestoreIndex {
|
|
228
285
|
collectionGroup: string;
|
|
229
|
-
queryScope: 'COLLECTION';
|
|
286
|
+
queryScope: 'COLLECTION' | 'COLLECTION_GROUP';
|
|
230
287
|
fields: FirestoreIndexField[];
|
|
231
288
|
}
|
|
232
289
|
interface FirestoreIndexConfig {
|
|
@@ -241,10 +298,17 @@ interface FirestoreIndexConfig {
|
|
|
241
298
|
* patterns on node data fields:
|
|
242
299
|
* (aType, axbType, data.{field})
|
|
243
300
|
*
|
|
301
|
+
* When registry entries with `targetGraph` are provided, also generates
|
|
302
|
+
* collection group indexes for `findEdgesGlobal()` queries. The collection
|
|
303
|
+
* group name defaults to `'graph'` (the standard subgraph name) but can be
|
|
304
|
+
* overridden per `targetGraph` value.
|
|
305
|
+
*
|
|
244
306
|
* @param collection - Firestore collection name (e.g. 'graph')
|
|
245
307
|
* @param entities - Optional discovery result for per-entity data field indexes
|
|
308
|
+
* @param registryEntries - Optional registry entries; when any have `targetGraph`,
|
|
309
|
+
* collection group indexes are generated for the distinct subgraph names
|
|
246
310
|
*/
|
|
247
|
-
declare function generateIndexConfig(collection: string, entities?: DiscoveryResult): FirestoreIndexConfig;
|
|
311
|
+
declare function generateIndexConfig(collection: string, entities?: DiscoveryResult, registryEntries?: ReadonlyArray<RegistryEntry>): FirestoreIndexConfig;
|
|
248
312
|
|
|
249
313
|
/**
|
|
250
314
|
* Result of analyzing a query for collection scan risk.
|
|
@@ -272,4 +336,4 @@ declare function analyzeQuerySafety(filters: QueryFilter[]): QuerySafetyResult;
|
|
|
272
336
|
*/
|
|
273
337
|
declare const DEFAULT_QUERY_LIMIT = 500;
|
|
274
338
|
|
|
275
|
-
export { BOOTSTRAP_ENTRIES, DEFAULT_QUERY_LIMIT, type DiscoverResult, DiscoveryError, DiscoveryResult, type DiscoveryWarning, DynamicGraphClient, DynamicRegistryConfig, DynamicRegistryError, EDGE_TYPE_SCHEMA, EdgeNotFoundError, type FieldMeta, FindEdgesParams, FindNodesParams, FiregraphError, type FirestoreIndex, type FirestoreIndexConfig, type FirestoreIndexField, GraphClient, GraphClientOptions, GraphReader, GraphRecord, GraphRegistry, InvalidQueryError, META_EDGE_TYPE, META_NODE_TYPE, NODE_TYPE_SCHEMA, NodeNotFoundError, QueryFilter, QueryPlan, QuerySafetyError, type QuerySafetyResult, RegistryEntry, RegistryScopeError, RegistryViolationError, TraversalBuilder, TraversalError, ValidationError, analyzeQuerySafety, buildEdgeQueryPlan, buildEdgeRecord, buildNodeQueryPlan, buildNodeRecord, compileSchema, computeEdgeDocId, computeNodeDocId, createBootstrapRegistry, createGraphClient, createRegistry, createRegistryFromGraph, createTraversal, discoverEntities, generateDeterministicUid, generateId, generateIndexConfig, jsonSchemaToFieldMeta, matchScope, matchScopeAny };
|
|
339
|
+
export { BOOTSTRAP_ENTRIES, DEFAULT_QUERY_LIMIT, type DiscoverResult, DiscoveryError, DiscoveryResult, type DiscoveryWarning, DynamicGraphClient, DynamicRegistryConfig, DynamicRegistryError, EDGE_TYPE_SCHEMA, EdgeNotFoundError, type FieldMeta, FindEdgesParams, FindNodesParams, FiregraphError, type FirestoreIndex, type FirestoreIndexConfig, type FirestoreIndexField, GraphClient, GraphClientOptions, GraphReader, GraphRecord, GraphRegistry, InvalidQueryError, META_EDGE_TYPE, META_NODE_TYPE, NODE_TYPE_SCHEMA, NodeNotFoundError, QueryFilter, QueryPlan, QuerySafetyError, type QuerySafetyResult, RegistryEntry, RegistryScopeError, RegistryViolationError, TraversalBuilder, TraversalError, ValidationError, analyzeQuerySafety, buildEdgeQueryPlan, buildEdgeRecord, buildNodeQueryPlan, buildNodeRecord, compileSchema, computeEdgeDocId, computeNodeDocId, createBootstrapRegistry, createGraphClient, createRegistry, createRegistryFromGraph, createTraversal, discoverEntities, generateDeterministicUid, generateId, generateIndexConfig, isAncestorUid, jsonSchemaToFieldMeta, matchScope, matchScopeAny, resolveAncestorCollection };
|
package/dist/index.js
CHANGED
|
@@ -783,14 +783,35 @@ function createRegistry(input) {
|
|
|
783
783
|
}
|
|
784
784
|
const entryList = Object.freeze([...entries]);
|
|
785
785
|
for (const entry of entries) {
|
|
786
|
+
if (entry.targetGraph && entry.targetGraph.includes("/")) {
|
|
787
|
+
throw new ValidationError(
|
|
788
|
+
`Entry (${entry.aType}) -[${entry.axbType}]-> (${entry.bType}) has invalid targetGraph "${entry.targetGraph}" \u2014 must be a single segment (no "/")`
|
|
789
|
+
);
|
|
790
|
+
}
|
|
786
791
|
const key = tripleKey(entry.aType, entry.axbType, entry.bType);
|
|
787
792
|
const validator = entry.jsonSchema ? compileSchema(entry.jsonSchema, `(${entry.aType}) -[${entry.axbType}]-> (${entry.bType})`) : void 0;
|
|
788
793
|
map.set(key, { entry, validate: validator });
|
|
789
794
|
}
|
|
795
|
+
const axbIndex = /* @__PURE__ */ new Map();
|
|
796
|
+
const axbBuild = /* @__PURE__ */ new Map();
|
|
797
|
+
for (const entry of entries) {
|
|
798
|
+
const existing = axbBuild.get(entry.axbType);
|
|
799
|
+
if (existing) {
|
|
800
|
+
existing.push(entry);
|
|
801
|
+
} else {
|
|
802
|
+
axbBuild.set(entry.axbType, [entry]);
|
|
803
|
+
}
|
|
804
|
+
}
|
|
805
|
+
for (const [key, arr] of axbBuild) {
|
|
806
|
+
axbIndex.set(key, Object.freeze(arr));
|
|
807
|
+
}
|
|
790
808
|
return {
|
|
791
809
|
lookup(aType, axbType, bType) {
|
|
792
810
|
return map.get(tripleKey(aType, axbType, bType))?.entry;
|
|
793
811
|
},
|
|
812
|
+
lookupByAxbType(axbType) {
|
|
813
|
+
return axbIndex.get(axbType) ?? [];
|
|
814
|
+
},
|
|
794
815
|
validate(aType, axbType, bType, data, scopePath) {
|
|
795
816
|
const rec = map.get(tripleKey(aType, axbType, bType));
|
|
796
817
|
if (!rec) {
|
|
@@ -837,6 +858,12 @@ function discoveryToEntries(discovery) {
|
|
|
837
858
|
if (!topology) continue;
|
|
838
859
|
const fromTypes = Array.isArray(topology.from) ? topology.from : [topology.from];
|
|
839
860
|
const toTypes = Array.isArray(topology.to) ? topology.to : [topology.to];
|
|
861
|
+
const resolvedTargetGraph = entity.targetGraph ?? topology.targetGraph;
|
|
862
|
+
if (resolvedTargetGraph && resolvedTargetGraph.includes("/")) {
|
|
863
|
+
throw new ValidationError(
|
|
864
|
+
`Edge "${axbType}" has invalid targetGraph "${resolvedTargetGraph}" \u2014 must be a single segment (no "/")`
|
|
865
|
+
);
|
|
866
|
+
}
|
|
840
867
|
for (const aType of fromTypes) {
|
|
841
868
|
for (const bType of toTypes) {
|
|
842
869
|
entries.push({
|
|
@@ -848,7 +875,8 @@ function discoveryToEntries(discovery) {
|
|
|
848
875
|
inverseLabel: topology.inverseLabel,
|
|
849
876
|
titleField: entity.titleField,
|
|
850
877
|
subtitleField: entity.subtitleField,
|
|
851
|
-
allowedIn: entity.allowedIn
|
|
878
|
+
allowedIn: entity.allowedIn,
|
|
879
|
+
targetGraph: resolvedTargetGraph
|
|
852
880
|
});
|
|
853
881
|
}
|
|
854
882
|
}
|
|
@@ -898,7 +926,8 @@ var EDGE_TYPE_SCHEMA = {
|
|
|
898
926
|
subtitleField: { type: "string" },
|
|
899
927
|
viewTemplate: { type: "string" },
|
|
900
928
|
viewCss: { type: "string" },
|
|
901
|
-
allowedIn: { type: "array", items: { type: "string", minLength: 1 } }
|
|
929
|
+
allowedIn: { type: "array", items: { type: "string", minLength: 1 } },
|
|
930
|
+
targetGraph: { type: "string", minLength: 1, pattern: "^[^/]+$" }
|
|
902
931
|
},
|
|
903
932
|
additionalProperties: false
|
|
904
933
|
};
|
|
@@ -959,7 +988,8 @@ async function createRegistryFromGraph(reader) {
|
|
|
959
988
|
inverseLabel: data.inverseLabel,
|
|
960
989
|
titleField: data.titleField,
|
|
961
990
|
subtitleField: data.subtitleField,
|
|
962
|
-
allowedIn: data.allowedIn
|
|
991
|
+
allowedIn: data.allowedIn,
|
|
992
|
+
targetGraph: data.targetGraph
|
|
963
993
|
});
|
|
964
994
|
}
|
|
965
995
|
}
|
|
@@ -1213,6 +1243,33 @@ var GraphClientImpl = class _GraphClientImpl {
|
|
|
1213
1243
|
);
|
|
1214
1244
|
}
|
|
1215
1245
|
// ---------------------------------------------------------------------------
|
|
1246
|
+
// Collection group query
|
|
1247
|
+
// ---------------------------------------------------------------------------
|
|
1248
|
+
async findEdgesGlobal(params, collectionName) {
|
|
1249
|
+
const name = collectionName ?? this.adapter.collectionPath.split("/").pop();
|
|
1250
|
+
const plan = buildEdgeQueryPlan(params);
|
|
1251
|
+
if (plan.strategy === "get") {
|
|
1252
|
+
throw new FiregraphError(
|
|
1253
|
+
"findEdgesGlobal() requires a query, not a direct document lookup. Omit one of aUid/axbType/bUid to force a query strategy.",
|
|
1254
|
+
"INVALID_QUERY"
|
|
1255
|
+
);
|
|
1256
|
+
}
|
|
1257
|
+
this.checkQuerySafety(plan.filters, params.allowCollectionScan);
|
|
1258
|
+
const collectionGroupRef = this.db.collectionGroup(name);
|
|
1259
|
+
let q = collectionGroupRef;
|
|
1260
|
+
for (const f of plan.filters) {
|
|
1261
|
+
q = q.where(f.field, f.op, f.value);
|
|
1262
|
+
}
|
|
1263
|
+
if (plan.options?.orderBy) {
|
|
1264
|
+
q = q.orderBy(plan.options.orderBy.field, plan.options.orderBy.direction ?? "asc");
|
|
1265
|
+
}
|
|
1266
|
+
if (plan.options?.limit !== void 0) {
|
|
1267
|
+
q = q.limit(plan.options.limit);
|
|
1268
|
+
}
|
|
1269
|
+
const snap = await q.get();
|
|
1270
|
+
return snap.docs.map((doc) => doc.data());
|
|
1271
|
+
}
|
|
1272
|
+
// ---------------------------------------------------------------------------
|
|
1216
1273
|
// Bulk operations
|
|
1217
1274
|
// ---------------------------------------------------------------------------
|
|
1218
1275
|
async removeNodeCascade(uid, options) {
|
|
@@ -1264,6 +1321,7 @@ var GraphClientImpl = class _GraphClientImpl {
|
|
|
1264
1321
|
};
|
|
1265
1322
|
if (jsonSchema !== void 0) data.jsonSchema = jsonSchema;
|
|
1266
1323
|
if (topology.inverseLabel !== void 0) data.inverseLabel = topology.inverseLabel;
|
|
1324
|
+
if (topology.targetGraph !== void 0) data.targetGraph = topology.targetGraph;
|
|
1267
1325
|
if (description !== void 0) data.description = description;
|
|
1268
1326
|
if (options?.titleField !== void 0) data.titleField = options.titleField;
|
|
1269
1327
|
if (options?.subtitleField !== void 0) data.subtitleField = options.subtitleField;
|
|
@@ -1338,6 +1396,10 @@ function generateId() {
|
|
|
1338
1396
|
var DEFAULT_LIMIT = 10;
|
|
1339
1397
|
var DEFAULT_MAX_READS = 100;
|
|
1340
1398
|
var DEFAULT_CONCURRENCY = 5;
|
|
1399
|
+
var _crossGraphWarned = false;
|
|
1400
|
+
function isGraphClient(reader) {
|
|
1401
|
+
return "subgraph" in reader && typeof reader.subgraph === "function";
|
|
1402
|
+
}
|
|
1341
1403
|
var Semaphore = class {
|
|
1342
1404
|
constructor(slots) {
|
|
1343
1405
|
this.slots = slots;
|
|
@@ -1363,9 +1425,10 @@ var Semaphore = class {
|
|
|
1363
1425
|
}
|
|
1364
1426
|
};
|
|
1365
1427
|
var TraversalBuilderImpl = class {
|
|
1366
|
-
constructor(reader, startUid) {
|
|
1428
|
+
constructor(reader, startUid, registry) {
|
|
1367
1429
|
this.reader = reader;
|
|
1368
1430
|
this.startUid = startUid;
|
|
1431
|
+
this.registry = registry;
|
|
1369
1432
|
}
|
|
1370
1433
|
hops = [];
|
|
1371
1434
|
follow(axbType, options) {
|
|
@@ -1382,11 +1445,13 @@ var TraversalBuilderImpl = class {
|
|
|
1382
1445
|
const semaphore = new Semaphore(concurrency);
|
|
1383
1446
|
let totalReads = 0;
|
|
1384
1447
|
let truncated = false;
|
|
1385
|
-
let
|
|
1448
|
+
let sources = [
|
|
1449
|
+
{ uid: this.startUid, reader: this.reader }
|
|
1450
|
+
];
|
|
1386
1451
|
const hopResults = [];
|
|
1387
1452
|
for (let depth = 0; depth < this.hops.length; depth++) {
|
|
1388
1453
|
const hop = this.hops[depth];
|
|
1389
|
-
if (
|
|
1454
|
+
if (sources.length === 0) {
|
|
1390
1455
|
hopResults.push({
|
|
1391
1456
|
axbType: hop.axbType,
|
|
1392
1457
|
depth,
|
|
@@ -1397,9 +1462,12 @@ var TraversalBuilderImpl = class {
|
|
|
1397
1462
|
continue;
|
|
1398
1463
|
}
|
|
1399
1464
|
const hopEdges = [];
|
|
1400
|
-
const sourceCount =
|
|
1465
|
+
const sourceCount = sources.length;
|
|
1401
1466
|
let hopTruncated = false;
|
|
1402
|
-
const
|
|
1467
|
+
const resolvedTargetGraph = this.resolveTargetGraph(hop);
|
|
1468
|
+
const direction = hop.direction ?? "forward";
|
|
1469
|
+
const isCrossGraph = direction === "forward" && !!resolvedTargetGraph;
|
|
1470
|
+
const tasks = sources.map(({ uid, reader: sourceReader }) => async () => {
|
|
1403
1471
|
if (totalReads >= maxReads) {
|
|
1404
1472
|
hopTruncated = true;
|
|
1405
1473
|
return;
|
|
@@ -1411,19 +1479,18 @@ var TraversalBuilderImpl = class {
|
|
|
1411
1479
|
return;
|
|
1412
1480
|
}
|
|
1413
1481
|
totalReads++;
|
|
1414
|
-
const direction2 = hop.direction ?? "forward";
|
|
1415
1482
|
const params = { axbType: hop.axbType };
|
|
1416
|
-
if (
|
|
1483
|
+
if (direction === "forward") {
|
|
1417
1484
|
params.aUid = uid;
|
|
1418
1485
|
if (hop.bType) params.bType = hop.bType;
|
|
1419
1486
|
} else {
|
|
1420
1487
|
params.bUid = uid;
|
|
1421
1488
|
if (hop.aType) params.aType = hop.aType;
|
|
1422
1489
|
}
|
|
1423
|
-
if (
|
|
1490
|
+
if (direction === "forward" && hop.aType) {
|
|
1424
1491
|
params.aType = hop.aType;
|
|
1425
1492
|
}
|
|
1426
|
-
if (
|
|
1493
|
+
if (direction === "reverse" && hop.bType) {
|
|
1427
1494
|
params.bType = hop.bType;
|
|
1428
1495
|
}
|
|
1429
1496
|
if (hop.orderBy) params.orderBy = hop.orderBy;
|
|
@@ -1433,31 +1500,58 @@ var TraversalBuilderImpl = class {
|
|
|
1433
1500
|
} else {
|
|
1434
1501
|
params.limit = limit;
|
|
1435
1502
|
}
|
|
1436
|
-
let
|
|
1503
|
+
let hopReader;
|
|
1504
|
+
let nextReader;
|
|
1505
|
+
if (isCrossGraph) {
|
|
1506
|
+
if (isGraphClient(this.reader)) {
|
|
1507
|
+
hopReader = this.reader.subgraph(uid, resolvedTargetGraph);
|
|
1508
|
+
nextReader = hopReader;
|
|
1509
|
+
} else {
|
|
1510
|
+
hopReader = sourceReader;
|
|
1511
|
+
nextReader = sourceReader;
|
|
1512
|
+
if (!_crossGraphWarned) {
|
|
1513
|
+
_crossGraphWarned = true;
|
|
1514
|
+
console.warn(
|
|
1515
|
+
`[firegraph] Traversal hop "${hop.axbType}" has targetGraph "${resolvedTargetGraph}" but the reader does not support subgraph(). Cross-graph hop will query the current collection instead. Pass a GraphClient to createTraversal() to enable cross-graph traversal.`
|
|
1516
|
+
);
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
} else {
|
|
1520
|
+
hopReader = sourceReader;
|
|
1521
|
+
nextReader = sourceReader;
|
|
1522
|
+
}
|
|
1523
|
+
let edges2 = await hopReader.findEdges(params);
|
|
1437
1524
|
if (hop.filter) {
|
|
1438
|
-
|
|
1439
|
-
|
|
1525
|
+
edges2 = edges2.filter(hop.filter);
|
|
1526
|
+
edges2 = edges2.slice(0, limit);
|
|
1527
|
+
}
|
|
1528
|
+
for (const edge of edges2) {
|
|
1529
|
+
hopEdges.push({ edge, reader: nextReader });
|
|
1440
1530
|
}
|
|
1441
|
-
hopEdges.push(...edges);
|
|
1442
1531
|
} finally {
|
|
1443
1532
|
semaphore.release();
|
|
1444
1533
|
}
|
|
1445
1534
|
});
|
|
1446
1535
|
await Promise.all(tasks.map((task) => task()));
|
|
1536
|
+
const edges = hopEdges.map((h) => h.edge);
|
|
1447
1537
|
hopResults.push({
|
|
1448
1538
|
axbType: hop.axbType,
|
|
1449
1539
|
depth,
|
|
1450
|
-
edges: returnIntermediates ? [...
|
|
1540
|
+
edges: returnIntermediates ? [...edges] : edges,
|
|
1451
1541
|
sourceCount,
|
|
1452
1542
|
truncated: hopTruncated
|
|
1453
1543
|
});
|
|
1454
1544
|
if (hopTruncated) {
|
|
1455
1545
|
truncated = true;
|
|
1456
1546
|
}
|
|
1457
|
-
const
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1547
|
+
const seen = /* @__PURE__ */ new Map();
|
|
1548
|
+
for (const { edge, reader: edgeReader } of hopEdges) {
|
|
1549
|
+
const nextUid = direction === "forward" ? edge.bUid : edge.aUid;
|
|
1550
|
+
if (!seen.has(nextUid)) {
|
|
1551
|
+
seen.set(nextUid, edgeReader);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
sources = [...seen.entries()].map(([uid, reader]) => ({ uid, reader }));
|
|
1461
1555
|
}
|
|
1462
1556
|
const lastHop = hopResults[hopResults.length - 1];
|
|
1463
1557
|
return {
|
|
@@ -1467,9 +1561,25 @@ var TraversalBuilderImpl = class {
|
|
|
1467
1561
|
truncated
|
|
1468
1562
|
};
|
|
1469
1563
|
}
|
|
1564
|
+
/**
|
|
1565
|
+
* Resolve the targetGraph for a hop. Priority:
|
|
1566
|
+
* 1. Explicit `hop.targetGraph` (user override)
|
|
1567
|
+
* 2. Registry `targetGraph` for the axbType (if registry available)
|
|
1568
|
+
* 3. undefined (no cross-graph)
|
|
1569
|
+
*/
|
|
1570
|
+
resolveTargetGraph(hop) {
|
|
1571
|
+
if (hop.targetGraph) return hop.targetGraph;
|
|
1572
|
+
if (this.registry) {
|
|
1573
|
+
const entries = this.registry.lookupByAxbType(hop.axbType);
|
|
1574
|
+
for (const entry of entries) {
|
|
1575
|
+
if (entry.targetGraph) return entry.targetGraph;
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
return void 0;
|
|
1579
|
+
}
|
|
1470
1580
|
};
|
|
1471
|
-
function createTraversal(reader, startUid) {
|
|
1472
|
-
return new TraversalBuilderImpl(reader, startUid);
|
|
1581
|
+
function createTraversal(reader, startUid, registry) {
|
|
1582
|
+
return new TraversalBuilderImpl(reader, startUid, registry);
|
|
1473
1583
|
}
|
|
1474
1584
|
|
|
1475
1585
|
// src/views.ts
|
|
@@ -1717,7 +1827,8 @@ function loadEdgeEntity(dir, name) {
|
|
|
1717
1827
|
viewDefaults: meta?.viewDefaults,
|
|
1718
1828
|
viewsPath,
|
|
1719
1829
|
sampleData,
|
|
1720
|
-
allowedIn: meta?.allowedIn
|
|
1830
|
+
allowedIn: meta?.allowedIn,
|
|
1831
|
+
targetGraph: topology.targetGraph ?? meta?.targetGraph
|
|
1721
1832
|
};
|
|
1722
1833
|
}
|
|
1723
1834
|
function getSubdirectories(dir) {
|
|
@@ -1760,6 +1871,20 @@ function discoverEntities(entitiesDir) {
|
|
|
1760
1871
|
};
|
|
1761
1872
|
}
|
|
1762
1873
|
|
|
1874
|
+
// src/cross-graph.ts
|
|
1875
|
+
function resolveAncestorCollection(collectionPath, uid) {
|
|
1876
|
+
const segments = collectionPath.split("/");
|
|
1877
|
+
for (let i = 1; i < segments.length; i += 2) {
|
|
1878
|
+
if (segments[i] === uid) {
|
|
1879
|
+
return segments.slice(0, i).join("/");
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
return null;
|
|
1883
|
+
}
|
|
1884
|
+
function isAncestorUid(collectionPath, uid) {
|
|
1885
|
+
return resolveAncestorCollection(collectionPath, uid) !== null;
|
|
1886
|
+
}
|
|
1887
|
+
|
|
1763
1888
|
// src/indexes.ts
|
|
1764
1889
|
function baseIndexes(collection) {
|
|
1765
1890
|
return [
|
|
@@ -1802,7 +1927,43 @@ function extractSchemaFields(schema) {
|
|
|
1802
1927
|
if (s.type !== "object" || !s.properties) return [];
|
|
1803
1928
|
return Object.keys(s.properties);
|
|
1804
1929
|
}
|
|
1805
|
-
function
|
|
1930
|
+
function collectionGroupIndexes(collectionName) {
|
|
1931
|
+
return [
|
|
1932
|
+
{
|
|
1933
|
+
collectionGroup: collectionName,
|
|
1934
|
+
queryScope: "COLLECTION_GROUP",
|
|
1935
|
+
fields: [
|
|
1936
|
+
{ fieldPath: "aUid", order: "ASCENDING" },
|
|
1937
|
+
{ fieldPath: "axbType", order: "ASCENDING" }
|
|
1938
|
+
]
|
|
1939
|
+
},
|
|
1940
|
+
{
|
|
1941
|
+
collectionGroup: collectionName,
|
|
1942
|
+
queryScope: "COLLECTION_GROUP",
|
|
1943
|
+
fields: [
|
|
1944
|
+
{ fieldPath: "axbType", order: "ASCENDING" },
|
|
1945
|
+
{ fieldPath: "bUid", order: "ASCENDING" }
|
|
1946
|
+
]
|
|
1947
|
+
},
|
|
1948
|
+
{
|
|
1949
|
+
collectionGroup: collectionName,
|
|
1950
|
+
queryScope: "COLLECTION_GROUP",
|
|
1951
|
+
fields: [
|
|
1952
|
+
{ fieldPath: "aType", order: "ASCENDING" },
|
|
1953
|
+
{ fieldPath: "axbType", order: "ASCENDING" }
|
|
1954
|
+
]
|
|
1955
|
+
},
|
|
1956
|
+
{
|
|
1957
|
+
collectionGroup: collectionName,
|
|
1958
|
+
queryScope: "COLLECTION_GROUP",
|
|
1959
|
+
fields: [
|
|
1960
|
+
{ fieldPath: "axbType", order: "ASCENDING" },
|
|
1961
|
+
{ fieldPath: "bType", order: "ASCENDING" }
|
|
1962
|
+
]
|
|
1963
|
+
}
|
|
1964
|
+
];
|
|
1965
|
+
}
|
|
1966
|
+
function generateIndexConfig(collection, entities, registryEntries) {
|
|
1806
1967
|
const indexes = baseIndexes(collection);
|
|
1807
1968
|
if (entities) {
|
|
1808
1969
|
for (const [, entity] of entities.nodes) {
|
|
@@ -1834,6 +1995,17 @@ function generateIndexConfig(collection, entities) {
|
|
|
1834
1995
|
}
|
|
1835
1996
|
}
|
|
1836
1997
|
}
|
|
1998
|
+
if (registryEntries) {
|
|
1999
|
+
const targetGraphNames = /* @__PURE__ */ new Set();
|
|
2000
|
+
for (const entry of registryEntries) {
|
|
2001
|
+
if (entry.targetGraph) {
|
|
2002
|
+
targetGraphNames.add(entry.targetGraph);
|
|
2003
|
+
}
|
|
2004
|
+
}
|
|
2005
|
+
for (const name of targetGraphNames) {
|
|
2006
|
+
indexes.push(...collectionGroupIndexes(name));
|
|
2007
|
+
}
|
|
2008
|
+
}
|
|
1837
2009
|
return { indexes, fieldOverrides: [] };
|
|
1838
2010
|
}
|
|
1839
2011
|
export {
|
|
@@ -1876,9 +2048,11 @@ export {
|
|
|
1876
2048
|
generateId,
|
|
1877
2049
|
generateIndexConfig,
|
|
1878
2050
|
generateTypes,
|
|
2051
|
+
isAncestorUid,
|
|
1879
2052
|
jsonSchemaToFieldMeta,
|
|
1880
2053
|
matchScope,
|
|
1881
2054
|
matchScopeAny,
|
|
2055
|
+
resolveAncestorCollection,
|
|
1882
2056
|
resolveView
|
|
1883
2057
|
};
|
|
1884
2058
|
//# sourceMappingURL=index.js.map
|