@typicalday/firegraph 0.14.0 → 0.15.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 +23 -3
- package/dist/{backend-DuvHGgK1.d.cts → backend-BpYLdwCW.d.cts} +1 -1
- package/dist/{backend-DuvHGgK1.d.ts → backend-BpYLdwCW.d.ts} +1 -1
- package/dist/backend-CvImIwTY.d.cts +137 -0
- package/dist/backend-YH5HtawN.d.ts +137 -0
- package/dist/backend.cjs +2 -3
- package/dist/backend.cjs.map +1 -1
- package/dist/backend.d.cts +2 -2
- package/dist/backend.d.ts +2 -2
- package/dist/backend.js +1 -1
- package/dist/{chunk-WRTFC5NG.js → chunk-5HIRYV2S.js} +13 -36
- package/dist/chunk-5HIRYV2S.js.map +1 -0
- package/dist/{chunk-PAD7WFFU.js → chunk-7IEZ6IYY.js} +36 -10
- package/dist/chunk-7IEZ6IYY.js.map +1 -0
- package/dist/chunk-FODIMIWY.js +721 -0
- package/dist/chunk-FODIMIWY.js.map +1 -0
- package/dist/chunk-NGAJCALM.js +34 -0
- package/dist/chunk-NGAJCALM.js.map +1 -0
- package/dist/{chunk-TK64DNVK.js → chunk-SIHE4UY4.js} +3 -4
- package/dist/chunk-SIHE4UY4.js.map +1 -0
- package/dist/chunk-ULRDQ6HZ.js +862 -0
- package/dist/chunk-ULRDQ6HZ.js.map +1 -0
- package/dist/{client-BKi3vk0Q.d.ts → client-B5o39X79.d.ts} +1 -1
- package/dist/{client-BrsaXtDV.d.cts → client-BGHwxwPg.d.cts} +1 -1
- package/dist/{client-Bk2Cm6xv.d.cts → client-DoyEdJ5w.d.cts} +1 -1
- package/dist/{client-Bk2Cm6xv.d.ts → client-DoyEdJ5w.d.ts} +1 -1
- package/dist/cloudflare/index.cjs +155 -165
- package/dist/cloudflare/index.cjs.map +1 -1
- package/dist/cloudflare/index.d.cts +73 -70
- package/dist/cloudflare/index.d.ts +73 -70
- package/dist/cloudflare/index.js +54 -589
- package/dist/cloudflare/index.js.map +1 -1
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/firestore-enterprise/index.cjs +42 -40
- package/dist/firestore-enterprise/index.cjs.map +1 -1
- package/dist/firestore-enterprise/index.d.cts +3 -3
- package/dist/firestore-enterprise/index.d.ts +3 -3
- package/dist/firestore-enterprise/index.js +19 -35
- package/dist/firestore-enterprise/index.js.map +1 -1
- package/dist/firestore-standard/index.cjs +34 -37
- package/dist/firestore-standard/index.cjs.map +1 -1
- package/dist/firestore-standard/index.d.cts +3 -3
- package/dist/firestore-standard/index.d.ts +3 -3
- package/dist/firestore-standard/index.js +10 -34
- package/dist/firestore-standard/index.js.map +1 -1
- package/dist/index.cjs +2 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +7 -5
- package/dist/index.js.map +1 -1
- package/dist/query-client/index.d.cts +2 -2
- package/dist/query-client/index.d.ts +2 -2
- package/dist/{registry-Bc7h6WTM.d.cts → registry-BGh7Jqpb.d.cts} +2 -2
- package/dist/{registry-C2KUPVZj.d.ts → registry-tKTb5Kx1.d.ts} +2 -2
- package/dist/sqlite/index.cjs +585 -378
- package/dist/sqlite/index.cjs.map +1 -1
- package/dist/sqlite/index.d.cts +4 -110
- package/dist/sqlite/index.d.ts +4 -110
- package/dist/sqlite/index.js +7 -1144
- package/dist/sqlite/index.js.map +1 -1
- package/dist/sqlite/local.cjs +1835 -0
- package/dist/sqlite/local.cjs.map +1 -0
- package/dist/sqlite/local.d.cts +83 -0
- package/dist/sqlite/local.d.ts +83 -0
- package/dist/sqlite/local.js +121 -0
- package/dist/sqlite/local.js.map +1 -0
- package/package.json +15 -1
- package/dist/chunk-4MMQ5W74.js +0 -288
- package/dist/chunk-4MMQ5W74.js.map +0 -1
- package/dist/chunk-PAD7WFFU.js.map +0 -1
- package/dist/chunk-TK64DNVK.js.map +0 -1
- package/dist/chunk-WRTFC5NG.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bulk.ts","../src/internal/firestore-aggregate.ts","../src/internal/firestore-classic-adapter.ts","../src/internal/firestore-classic-expand.ts","../src/internal/projection.ts","../src/internal/firestore-projection.ts","../src/internal/firestore-update.ts","../src/internal/firestore-vector.ts"],"sourcesContent":["import type { Firestore } from '@google-cloud/firestore';\n\nimport { computeEdgeDocId, computeNodeDocId } from './docid.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport type {\n BulkBatchError,\n BulkOptions,\n BulkResult,\n CascadeResult,\n FindEdgesParams,\n GraphReader,\n StoredGraphRecord,\n} from './types.js';\n\nconst MAX_BATCH_SIZE = 500;\nconst DEFAULT_MAX_RETRIES = 3;\nconst BASE_DELAY_MS = 200;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Splits an array into chunks of at most `size` elements.\n */\nfunction chunk<T>(arr: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size));\n }\n return chunks;\n}\n\n/**\n * Deletes a list of document IDs in chunked Firestore batches with retries.\n */\nexport async function bulkDeleteDocIds(\n db: Firestore,\n collectionPath: string,\n docIds: string[],\n options?: BulkOptions,\n): Promise<BulkResult> {\n if (docIds.length === 0) {\n return { deleted: 0, batches: 0, errors: [] };\n }\n\n const batchSize = Math.min(options?.batchSize ?? MAX_BATCH_SIZE, MAX_BATCH_SIZE);\n const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;\n const onProgress = options?.onProgress;\n\n const chunks = chunk(docIds, batchSize);\n const errors: BulkBatchError[] = [];\n let deleted = 0;\n let completedBatches = 0;\n\n for (let i = 0; i < chunks.length; i++) {\n const ids = chunks[i];\n let committed = false;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const batch = db.batch();\n const collectionRef = db.collection(collectionPath);\n for (const id of ids) {\n batch.delete(collectionRef.doc(id));\n }\n await batch.commit();\n committed = true;\n deleted += ids.length;\n break;\n } catch (err) {\n if (attempt < maxRetries) {\n const delay = BASE_DELAY_MS * Math.pow(2, attempt);\n await sleep(delay);\n } else {\n errors.push({\n batchIndex: i,\n error: err instanceof Error ? err : new Error(String(err)),\n operationCount: ids.length,\n });\n }\n }\n }\n\n if (committed) {\n completedBatches++;\n }\n\n if (onProgress) {\n onProgress({\n completedBatches,\n totalBatches: chunks.length,\n deletedSoFar: deleted,\n });\n }\n }\n\n return { deleted, batches: completedBatches, errors };\n}\n\n/**\n * Finds all edges matching `params`, then deletes them in chunked batches.\n */\nexport async function bulkRemoveEdges(\n db: Firestore,\n collectionPath: string,\n reader: GraphReader,\n params: FindEdgesParams,\n options?: BulkOptions,\n): Promise<BulkResult> {\n // Override default query limit for bulk deletion — we need all matching edges.\n // limit: 0 bypasses DEFAULT_QUERY_LIMIT; an explicit user limit is preserved.\n // allowCollectionScan: true — bulk deletion inherently implies scanning.\n const effectiveParams =\n params.limit !== undefined\n ? { ...params, allowCollectionScan: params.allowCollectionScan ?? true }\n : { ...params, limit: 0, allowCollectionScan: params.allowCollectionScan ?? true };\n const edges = await reader.findEdges(effectiveParams);\n const docIds = edges.map((e) => computeEdgeDocId(e.aUid, e.axbType, e.bUid));\n return bulkDeleteDocIds(db, collectionPath, docIds, options);\n}\n\n/** Result from recursive subcollection deletion. */\ninterface SubcollectionDeleteResult {\n deleted: number;\n errors: BulkBatchError[];\n}\n\n/**\n * Recursively delete all documents in all subcollections under a given document.\n * Uses `listCollections()` (Admin SDK) to discover subcollections, then for each\n * subcollection: recurse into each document's subcollections first (depth-first),\n * then bulk delete all documents in the subcollection.\n *\n * The `onProgress` callback is intentionally NOT forwarded to subcollection\n * deletes to avoid confusing callers with interleaved progress from different\n * collection depths.\n */\nasync function deleteSubcollectionsRecursive(\n db: Firestore,\n collectionPath: string,\n docId: string,\n options?: BulkOptions,\n): Promise<SubcollectionDeleteResult> {\n const docRef = db.collection(collectionPath).doc(docId);\n const subcollections = await docRef.listCollections();\n\n if (subcollections.length === 0) return { deleted: 0, errors: [] };\n\n let totalDeleted = 0;\n const allErrors: BulkBatchError[] = [];\n\n // Strip onProgress for subcollection deletes — callers should only see\n // top-level progress, not interleaved reports from nested depths.\n const subOptions: BulkOptions | undefined = options\n ? { batchSize: options.batchSize, maxRetries: options.maxRetries }\n : undefined;\n\n for (const subCollRef of subcollections) {\n const subCollPath = subCollRef.path;\n // List all documents in this subcollection\n const snapshot = await subCollRef.select().get();\n const subDocIds = snapshot.docs.map((d) => d.id);\n\n // Depth-first: recurse into each document's subcollections\n for (const subDocId of subDocIds) {\n const subResult = await deleteSubcollectionsRecursive(db, subCollPath, subDocId, subOptions);\n totalDeleted += subResult.deleted;\n allErrors.push(...subResult.errors);\n }\n\n // Now delete all documents in this subcollection\n if (subDocIds.length > 0) {\n const result = await bulkDeleteDocIds(db, subCollPath, subDocIds, subOptions);\n totalDeleted += result.deleted;\n allErrors.push(...result.errors);\n }\n }\n\n return { deleted: totalDeleted, errors: allErrors };\n}\n\n/**\n * Deletes a node and all of its outgoing and incoming edges.\n *\n * Edges are deleted first in chunked batches, then the node document\n * is deleted in the final batch. This is NOT atomic across batches —\n * if a batch fails after retries, remaining batches still execute.\n *\n * By default, subcollections (subgraphs) under the node's document are\n * recursively deleted. Set `options.deleteSubcollections` to `false` to skip.\n */\nexport async function removeNodeCascade(\n db: Firestore,\n collectionPath: string,\n reader: GraphReader,\n uid: string,\n options?: BulkOptions,\n): Promise<CascadeResult> {\n // Find all edges touching this node (outgoing + incoming).\n // Filter out the node's own self-loop record (axbType === 'is').\n // These queries intentionally scan broadly — allowCollectionScan bypasses safety checks.\n // limit: 0 bypasses the DEFAULT_QUERY_LIMIT to ensure we find all edges.\n const [outgoingRaw, incomingRaw] = await Promise.all([\n reader.findEdges({ aUid: uid, allowCollectionScan: true, limit: 0 }),\n reader.findEdges({ bUid: uid, allowCollectionScan: true, limit: 0 }),\n ]);\n const outgoing = outgoingRaw.filter((e) => e.axbType !== NODE_RELATION);\n const incoming = incomingRaw.filter((e) => e.axbType !== NODE_RELATION);\n\n // Deduplicate: a self-referencing edge could appear in both lists.\n const edgeDocIdSet = new Set<string>();\n const allEdges: StoredGraphRecord[] = [];\n for (const edge of [...outgoing, ...incoming]) {\n const docId = computeEdgeDocId(edge.aUid, edge.axbType, edge.bUid);\n if (!edgeDocIdSet.has(docId)) {\n edgeDocIdSet.add(docId);\n allEdges.push(edge);\n }\n }\n\n // Delete subcollections (subgraphs) under this node's document (depth-first).\n const shouldDeleteSubcollections = options?.deleteSubcollections !== false;\n const nodeDocId = computeNodeDocId(uid);\n let subcollectionResult: SubcollectionDeleteResult = { deleted: 0, errors: [] };\n\n if (shouldDeleteSubcollections) {\n subcollectionResult = await deleteSubcollectionsRecursive(\n db,\n collectionPath,\n nodeDocId,\n options,\n );\n }\n\n // Build doc IDs: edges first, then the node last.\n const edgeDocIds = allEdges.map((e) => computeEdgeDocId(e.aUid, e.axbType, e.bUid));\n const allDocIds = [...edgeDocIds, nodeDocId];\n\n // Wrap the progress callback to track overall progress.\n const batchSize = Math.min(options?.batchSize ?? MAX_BATCH_SIZE, MAX_BATCH_SIZE);\n const result = await bulkDeleteDocIds(db, collectionPath, allDocIds, {\n ...options,\n batchSize,\n });\n\n // Determine if the node doc was in a failed batch.\n // The node is always in the last doc ID. If the last batch errored, node wasn't deleted.\n const totalChunks = Math.ceil(allDocIds.length / batchSize);\n const nodeChunkIndex = totalChunks - 1;\n const nodeDeleted = !result.errors.some((e) => e.batchIndex === nodeChunkIndex);\n\n // edgesDeleted counts only top-level edges (not subcollection docs).\n // deleted includes everything: top-level edges + node + subcollection docs.\n const topLevelEdgesDeleted = nodeDeleted ? result.deleted - 1 : result.deleted;\n\n return {\n deleted: result.deleted + subcollectionResult.deleted,\n batches: result.batches,\n errors: [...result.errors, ...subcollectionResult.errors],\n edgesDeleted: topLevelEdgesDeleted,\n nodeDeleted,\n };\n}\n","/**\n * Shared classic-API aggregate translation for both Firestore editions.\n *\n * Translates an `AggregateSpec` into a `Query.aggregate()` call using\n * `AggregateField.count() / sum() / average()`. The classic API does not\n * support `min` / `max` — those throw `UNSUPPORTED_AGGREGATE`. Backends\n * declaring `query.aggregate` are required to support at least count/sum/avg;\n * per-op limitations surface as runtime errors with a clear message.\n *\n * Both Standard and Enterprise editions can call `Query.aggregate()` directly\n * (the Enterprise pipeline `aggregate()` stage is a future optimisation that\n * would unlock min/max — Phase 11+). Until then both editions delegate to\n * this single helper, which keeps the per-edition backends thin and avoids\n * cross-subpath imports between them.\n *\n * Field paths follow the same dotted convention used elsewhere in firegraph\n * (`'data.price'`, `'data.profile.score'`). They are passed through to\n * Firestore unchanged — Firestore itself interprets `.` as a field-path\n * separator.\n */\n\nimport { AggregateField, type Query } from '@google-cloud/firestore';\n\nimport { FiregraphError } from '../errors.js';\nimport type { AggregateSpec, QueryFilter } from '../types.js';\n\n/**\n * Apply the firegraph filter list to a base Firestore query. Mirrors the\n * tiny `where()` loop used by the classic adapter; kept local so the\n * aggregate path doesn't depend on adapter internals it doesn't otherwise\n * need.\n */\nexport function applyFiltersToQuery(base: Query, filters: QueryFilter[]): Query {\n let q = base;\n for (const f of filters) {\n q = q.where(f.field, f.op, f.value);\n }\n return q;\n}\n\n/**\n * Run an aggregate query against a base Firestore `Query`. Returns the\n * resolved numeric result keyed by alias.\n *\n * `count` is the only op that ignores `field`; everything else requires it.\n * Missing fields throw `INVALID_QUERY` so the caller sees the bad spec\n * rather than a Firestore-side error from passing `undefined` into\n * `AggregateField.sum`.\n */\nexport async function runFirestoreAggregate(\n base: Query,\n spec: AggregateSpec,\n filters: QueryFilter[],\n { edition }: { edition: 'standard' | 'enterprise' },\n): Promise<Record<string, number>> {\n if (Object.keys(spec).length === 0) {\n throw new FiregraphError(\n 'aggregate() requires at least one aggregation in the `aggregates` map.',\n 'INVALID_QUERY',\n );\n }\n\n const filtered = applyFiltersToQuery(base, filters);\n // Firestore's `AggregateField` map values are heterogeneous — `count()`,\n // `sum(field)`, and `average(field)` each return distinct AggregateField\n // subtypes parameterized by their own input type. The library accepts a\n // record whose value union covers all three, so we type the local map\n // accordingly.\n type AnyAggField = ReturnType<typeof AggregateField.count>;\n const aggregations: Record<string, AnyAggField> = {};\n for (const [alias, { op, field }] of Object.entries(spec)) {\n if (op === 'count') {\n // Reject a stray `field` on count: counting rows never uses a column\n // expression, and silently ignoring would mask typos like\n // `{ n: { op: 'count', field: 'data.price' } }` (cribbed from a sum\n // spec). Better to surface the misuse than return a misleading row\n // count.\n if (field !== undefined) {\n throw new FiregraphError(\n `Aggregate '${alias}' op 'count' must not specify a field — ` +\n `count operates on rows, not a column expression.`,\n 'INVALID_QUERY',\n );\n }\n aggregations[alias] = AggregateField.count();\n continue;\n }\n if (!field) {\n throw new FiregraphError(\n `Aggregate '${alias}' op '${op}' requires a field.`,\n 'INVALID_QUERY',\n );\n }\n if (op === 'sum') {\n aggregations[alias] = AggregateField.sum(field) as unknown as AnyAggField;\n } else if (op === 'avg') {\n aggregations[alias] = AggregateField.average(field) as unknown as AnyAggField;\n } else {\n // Both editions currently route through the classic `Query.aggregate`\n // API, which exposes only count/sum/avg. Enterprise *could* expose\n // min/max via pipelines (deferred to a later phase). Until then, both\n // editions reject min/max identically — the message names the edition\n // so the diagnostic is accurate.\n const editionLabel = edition === 'enterprise' ? 'Firestore Enterprise' : 'Firestore Standard';\n throw new FiregraphError(\n `Aggregate op '${op}' is not supported on ${editionLabel}. ` +\n `Both Firestore editions support count/sum/avg via the classic Query API; ` +\n `min/max requires a backend with SQL aggregation (SQLite or DO).`,\n 'UNSUPPORTED_AGGREGATE',\n );\n }\n }\n\n const snap = await filtered.aggregate(aggregations).get();\n const data = snap.data() as Record<string, number | null>;\n const out: Record<string, number> = {};\n for (const alias of Object.keys(spec)) {\n const v = data[alias];\n // Firestore returns `null` for sum/avg over an empty set. Surface that\n // as `0` for sum (well-defined) and `NaN` for avg (avg of empty set is\n // mathematically undefined). `count` is never null.\n if (v === null || v === undefined) {\n const op = spec[alias].op;\n out[alias] = op === 'avg' ? Number.NaN : 0;\n } else {\n out[alias] = v;\n }\n }\n return out;\n}\n","import type { FieldPath, Firestore, Query, Transaction } from '@google-cloud/firestore';\n\nimport type { QueryFilter, QueryOptions, StoredGraphRecord } from '../types.js';\n\n/**\n * Variadic argument tuple for Firestore's\n * `update(field, value, …moreFieldsAndValues)` overload. Built by\n * `buildFirestoreUpdateArgs` (`./firestore-update.ts`); deep-merge ops use\n * `FieldPath` segments so exotic object keys address literal keys rather\n * than being reparsed as dotted paths.\n */\nexport type FirestoreUpdateArgs = [string | FieldPath, unknown, ...unknown[]];\n\nexport interface FirestoreAdapter {\n collectionPath: string;\n getDoc(docId: string): Promise<StoredGraphRecord | null>;\n setDoc(\n docId: string,\n data: Record<string, unknown>,\n options?: { merge?: boolean },\n ): Promise<void>;\n updateDoc(docId: string, args: FirestoreUpdateArgs): Promise<void>;\n deleteDoc(docId: string): Promise<void>;\n query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]>;\n}\n\nexport function createFirestoreAdapter(db: Firestore, collectionPath: string): FirestoreAdapter {\n const collectionRef = db.collection(collectionPath);\n\n return {\n collectionPath,\n\n async getDoc(docId: string): Promise<StoredGraphRecord | null> {\n const snap = await collectionRef.doc(docId).get();\n if (!snap.exists) return null;\n return snap.data() as StoredGraphRecord;\n },\n\n async setDoc(\n docId: string,\n data: Record<string, unknown>,\n options?: { merge?: boolean },\n ): Promise<void> {\n if (options?.merge) {\n await collectionRef.doc(docId).set(data, { merge: true });\n } else {\n await collectionRef.doc(docId).set(data);\n }\n },\n\n async updateDoc(docId: string, args: FirestoreUpdateArgs): Promise<void> {\n await collectionRef.doc(docId).update(...args);\n },\n\n async deleteDoc(docId: string): Promise<void> {\n await collectionRef.doc(docId).delete();\n },\n\n async query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]> {\n let q: Query = collectionRef;\n for (const f of filters) {\n q = q.where(f.field, f.op, f.value);\n }\n if (options?.orderBy) {\n q = q.orderBy(options.orderBy.field, options.orderBy.direction ?? 'asc');\n }\n if (options?.limit !== undefined) {\n q = q.limit(options.limit);\n }\n const snap = await q.get();\n return snap.docs.map((doc) => doc.data() as StoredGraphRecord);\n },\n };\n}\n\nexport interface TransactionAdapter {\n getDoc(docId: string): Promise<StoredGraphRecord | null>;\n setDoc(docId: string, data: Record<string, unknown>, options?: { merge?: boolean }): void;\n updateDoc(docId: string, args: FirestoreUpdateArgs): void;\n deleteDoc(docId: string): void;\n query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]>;\n}\n\nexport function createTransactionAdapter(\n db: Firestore,\n collectionPath: string,\n tx: Transaction,\n): TransactionAdapter {\n const collectionRef = db.collection(collectionPath);\n\n return {\n async getDoc(docId: string): Promise<StoredGraphRecord | null> {\n const snap = await tx.get(collectionRef.doc(docId));\n if (!snap.exists) return null;\n return snap.data() as StoredGraphRecord;\n },\n\n setDoc(docId: string, data: Record<string, unknown>, options?: { merge?: boolean }): void {\n if (options?.merge) {\n tx.set(collectionRef.doc(docId), data, { merge: true });\n } else {\n tx.set(collectionRef.doc(docId), data);\n }\n },\n\n updateDoc(docId: string, args: FirestoreUpdateArgs): void {\n tx.update(collectionRef.doc(docId), ...args);\n },\n\n deleteDoc(docId: string): void {\n tx.delete(collectionRef.doc(docId));\n },\n\n async query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]> {\n let q: Query = collectionRef;\n for (const f of filters) {\n q = q.where(f.field, f.op, f.value);\n }\n if (options?.orderBy) {\n q = q.orderBy(options.orderBy.field, options.orderBy.direction ?? 'asc');\n }\n if (options?.limit !== undefined) {\n q = q.limit(options.limit);\n }\n const snap = await tx.get(q);\n return snap.docs.map((doc) => doc.data() as StoredGraphRecord);\n },\n };\n}\n\nexport interface BatchAdapter {\n setDoc(docId: string, data: Record<string, unknown>, options?: { merge?: boolean }): void;\n updateDoc(docId: string, args: FirestoreUpdateArgs): void;\n deleteDoc(docId: string): void;\n commit(): Promise<void>;\n}\n\nexport function createBatchAdapter(db: Firestore, collectionPath: string): BatchAdapter {\n const collectionRef = db.collection(collectionPath);\n const batch = db.batch();\n\n return {\n setDoc(docId: string, data: Record<string, unknown>, options?: { merge?: boolean }): void {\n if (options?.merge) {\n batch.set(collectionRef.doc(docId), data, { merge: true });\n } else {\n batch.set(collectionRef.doc(docId), data);\n }\n },\n\n updateDoc(docId: string, args: FirestoreUpdateArgs): void {\n batch.update(collectionRef.doc(docId), ...args);\n },\n\n deleteDoc(docId: string): void {\n batch.delete(collectionRef.doc(docId));\n },\n\n async commit(): Promise<void> {\n await batch.commit();\n },\n };\n}\n","/**\n * Shared classic-API multi-source fan-out for Firestore.\n *\n * Used by:\n *\n * - `firestore-standard` (always — Standard has no Pipelines path).\n * - `firestore-enterprise` (only when `queryMode === 'classic'`, i.e.\n * under the emulator or when explicitly forced).\n *\n * Strategy: chunk `params.sources` into 30-element groups (the classic\n * `'in'` operator's documented cap), dispatch one `Query.where(field, 'in',\n * chunk)` per chunk in parallel via `Promise.all`, then concat. Optional\n * post-pass sort + total-limit cap so observable behaviour matches the\n * SQL backends' single-statement `WHERE … IN (?,?,…) ORDER BY … LIMIT N`.\n *\n * Why this is still a win over the per-source `findEdges` loop in\n * `traverse.ts`: that loop emits one query per source UID. With 100\n * sources it's 100 round trips. The chunked path is `ceil(100/30) = 4`\n * round trips. The Pipelines `equalAny` path (Enterprise) collapses all\n * 100 into one — see `firestore-expand.ts` — but Standard can't reach\n * that, so chunked classic is the best Standard can do.\n *\n * Hydration follows the same chunking strategy: a single classic query\n * per chunk that fetches every node row whose `aUid` is in the chunk\n * (nodes are stored as self-loops `(uid, 'is', uid)` so `aUid IN chunk`\n * picks them up). Document IDs would also work via per-UID `getDoc`,\n * but that's N round trips for N targets — chunking matches the\n * fan-out path's round-trip count.\n */\n\nimport { FiregraphError } from '../errors.js';\nimport type { ExpandParams, ExpandResult, QueryFilter, StoredGraphRecord } from '../types.js';\nimport { NODE_RELATION } from './constants.js';\nimport type { FirestoreAdapter } from './firestore-classic-adapter.js';\n\n/**\n * Maximum elements per Firestore classic `'in'` operator. Documented as 30\n * across the Node Admin SDK, Firestore web SDK, and the security-rules\n * runtime. Exceeding this throws `INVALID_ARGUMENT` on the wire.\n */\nexport const FIRESTORE_CLASSIC_IN_CHUNK_SIZE = 30;\n\n/**\n * Read a (possibly dotted) field from a record. Used for the post-concat\n * sort pass. Mirrors the dotted-path resolution in\n * `firestore-projection.ts` but read-only and without bare-name rewriting\n * — `ExpandParams.orderBy.field` is always a fully-qualified path.\n */\nfunction readField(record: StoredGraphRecord, path: string): unknown {\n if (!path.includes('.')) {\n return (record as unknown as Record<string, unknown>)[path];\n }\n let cursor: unknown = record;\n for (const seg of path.split('.')) {\n if (cursor === null || typeof cursor !== 'object') return undefined;\n cursor = (cursor as Record<string, unknown>)[seg];\n }\n return cursor;\n}\n\n/**\n * Compare two field values for a stable, total ordering. Mirrors\n * Firestore's classic-API ordering semantics: numbers numerically,\n * strings lexicographically, booleans (false < true), and nullish goes\n * first. Mixed-type comparisons fall back to `String(...)` so the sort is\n * defined for every input shape — the alternative would be the JS\n * default `<` semantics, which silently coerce and lose stability.\n */\nfunction compareFieldValues(a: unknown, b: unknown): number {\n if (a === b) return 0;\n if (a === undefined || a === null) return -1;\n if (b === undefined || b === null) return 1;\n const ta = typeof a;\n const tb = typeof b;\n if (ta === tb) {\n // `a` and `b` share a primitive type (number, string, boolean, etc.);\n // a direct `<` comparison is safe under the JS comparison rules. Cast\n // through `string` solely to satisfy TS's `unknown < unknown` ban —\n // the runtime behaviour is identical for any pair of same-type\n // primitives.\n return (a as string) < (b as string) ? -1 : 1;\n }\n // Mixed types — fall back to string comparison.\n const sa = String(a);\n const sb = String(b);\n return sa < sb ? -1 : sa > sb ? 1 : 0;\n}\n\n/**\n * Run a classic-API `expand()` against a Firestore collection via the\n * supplied `FirestoreAdapter`. Returns the same `ExpandResult` shape as\n * the SQL backends.\n *\n * Empty `params.sources` short-circuits to an empty result without\n * touching the adapter — the chunking pass would emit zero queries\n * anyway, but the early return makes the contract explicit and matches\n * the `client.expand` wrapper.\n *\n * Self-loop guard: when `params.axbType === NODE_RELATION`, edges where\n * `aUid === bUid` are node rows, not real hops. The SQL backends filter\n * those out via `aUid != bUid`; we mirror the same filter as a\n * post-process pass because Firestore classic queries don't support\n * column-vs-column predicates. The guard is defensive — `traverse.ts`\n * never sends `NODE_RELATION` as `axbType`, but a direct\n * `client.expand({ axbType: 'is' })` call would otherwise return the\n * source nodes themselves.\n */\nexport async function runFirestoreClassicExpand(\n adapter: FirestoreAdapter,\n params: ExpandParams,\n): Promise<ExpandResult> {\n if (params.sources.length === 0) {\n return params.hydrate ? { edges: [], targets: [] } : { edges: [] };\n }\n\n if (params.axbType.length === 0) {\n throw new FiregraphError('expand(): axbType must be a non-empty string.', 'INVALID_QUERY');\n }\n\n const direction = params.direction ?? 'forward';\n const sourceField = direction === 'forward' ? 'aUid' : 'bUid';\n\n const chunks = chunkUids(params.sources, FIRESTORE_CLASSIC_IN_CHUNK_SIZE);\n const totalLimit =\n params.limitPerSource !== undefined ? params.sources.length * params.limitPerSource : undefined;\n\n const buildFilters = (chunk: string[]): QueryFilter[] => {\n const filters: QueryFilter[] = [\n { field: 'axbType', op: '==', value: params.axbType },\n { field: sourceField, op: 'in', value: chunk },\n ];\n if (params.aType !== undefined) {\n filters.push({ field: 'aType', op: '==', value: params.aType });\n }\n if (params.bType !== undefined) {\n filters.push({ field: 'bType', op: '==', value: params.bType });\n }\n return filters;\n };\n\n const buildOptions = (chunk: string[]) => {\n const opts: { orderBy?: ExpandParams['orderBy']; limit?: number } = {};\n if (params.orderBy) opts.orderBy = params.orderBy;\n if (params.limitPerSource !== undefined) {\n // Per-chunk soft cap: chunk.length * limitPerSource. The post-concat\n // slice enforces the global cap.\n opts.limit = chunk.length * params.limitPerSource;\n }\n return opts;\n };\n\n const chunkResults = await Promise.all(\n chunks.map((chunk) => adapter.query(buildFilters(chunk), buildOptions(chunk))),\n );\n let edges: StoredGraphRecord[] = chunkResults.flat();\n\n // Self-loop filter when the caller targeted the node-relation. See JSDoc.\n if (params.axbType === NODE_RELATION) {\n edges = edges.filter((e) => e.aUid !== e.bUid);\n }\n\n // Cross-chunk ordering pass. Each chunk's result is already sorted (we\n // pushed `orderBy` into the per-chunk query), but concat'd order is not\n // globally sorted. Re-sort to honour the contract.\n if (params.orderBy) {\n const sortField = params.orderBy.field;\n const dir = params.orderBy.direction ?? 'asc';\n edges.sort((a, b) => {\n const cmp = compareFieldValues(readField(a, sortField), readField(b, sortField));\n return dir === 'asc' ? cmp : -cmp;\n });\n }\n if (totalLimit !== undefined && edges.length > totalLimit) {\n edges = edges.slice(0, totalLimit);\n }\n\n if (!params.hydrate) return { edges };\n\n // Hydration: fetch every target node by `aUid in chunk` against the\n // node-relation. Nodes are self-loops, so this picks up exactly one row\n // per UID. Chunked the same way as the fan-out queries above.\n const targetUids = edges.map((e) => (direction === 'forward' ? e.bUid : e.aUid));\n const uniqueTargets = [...new Set(targetUids)];\n if (uniqueTargets.length === 0) {\n return { edges, targets: [] };\n }\n const hydrateChunks = chunkUids(uniqueTargets, FIRESTORE_CLASSIC_IN_CHUNK_SIZE);\n const hydrateResults = await Promise.all(\n hydrateChunks.map((chunk) =>\n adapter.query([\n { field: 'axbType', op: '==', value: NODE_RELATION },\n { field: 'aUid', op: 'in', value: chunk },\n ]),\n ),\n );\n const byUid = new Map<string, StoredGraphRecord>();\n for (const row of hydrateResults.flat()) {\n // `bUid === aUid === uid` for node rows by construction.\n byUid.set(row.bUid, row);\n }\n const targets = targetUids.map((uid) => byUid.get(uid) ?? null);\n return { edges, targets };\n}\n\n/** Split a list into fixed-size chunks. Exported for the unit test. */\nexport function chunkUids(uids: readonly string[], chunkSize: number): string[][] {\n if (chunkSize <= 0) {\n throw new FiregraphError(\n `chunkUids: chunkSize must be positive (got ${chunkSize}).`,\n 'INVALID_QUERY',\n );\n }\n const out: string[][] = [];\n for (let i = 0; i < uids.length; i += chunkSize) {\n out.push(uids.slice(i, i + chunkSize) as string[]);\n }\n return out;\n}\n","/**\n * Shared helpers for the `query.select` projection contract.\n *\n * The SQLite-shaped backends share a `normalizeProjectionField`\n * implementation in `src/internal/sqlite-sql.ts` because it's entangled\n * with `FIELD_TO_COLUMN`. The Firestore-shaped backends (`firestore-standard`,\n * `firestore-enterprise`) plus the `RoutingStorageBackend` pass-through use\n * the helpers below to keep the projection contract consistent across the\n * three runtimes:\n *\n * - bare names → `data.<name>`\n * - `'data'` and `'data.*'` → as-is\n * - top-level envelope fields (`aType`, `aUid`, `axbType`, `bType`,\n * `bUid`, `createdAt`, `updatedAt`, `v`) → as-is\n *\n * The set diverges from `BUILTIN_FIELDS` in `internal/constants.ts` — that\n * one is the queryable-filter set and does not include `v`. Projection\n * accepts `v` because it is a top-level envelope field that the user may\n * want to read for diagnostics.\n */\n\nconst PROJECTION_BUILTIN_FIELDS: ReadonlySet<string> = new Set([\n 'aType',\n 'aUid',\n 'axbType',\n 'bType',\n 'bUid',\n 'createdAt',\n 'updatedAt',\n 'v',\n]);\n\n/**\n * Rewrite a caller-supplied projection field to the canonical form the\n * Firestore-shaped backends consume. See file header for the rules.\n */\nexport function normalizeFirestoreProjectionField(field: string): string {\n if (PROJECTION_BUILTIN_FIELDS.has(field)) return field;\n if (field === 'data' || field.startsWith('data.')) return field;\n return `data.${field}`;\n}\n\n/**\n * Read a (possibly dotted) path out of a partial document. Used by the\n * Firestore-shaped backends to translate `doc.data()` into the projected JS\n * shape: each row in the result is keyed by the *original* field as the\n * caller supplied it, and the value is whatever lives at the canonical\n * path inside the partial document Firestore returns.\n *\n * Missing path segments resolve to `null` (not `undefined`) to match the\n * SQLite-shaped backends, where `json_extract` returns SQL NULL for an\n * absent JSON path — the decoder surfaces that as `null`. Aligning here\n * means a consumer iterating over `Object.entries(row)` sees the same\n * shape across SQLite/DO/Firestore: every requested field is present in\n * the row object, and absent values are explicitly `null`. If we returned\n * `undefined`, Firestore rows would silently lose absent keys when\n * serialized through `JSON.stringify`, breaking that contract.\n */\nexport function readProjectionPath(\n obj: Record<string, unknown> | undefined | null,\n path: string,\n): unknown {\n if (obj === undefined || obj === null) return null;\n const raw = !path.includes('.')\n ? obj[path]\n : (() => {\n const parts = path.split('.');\n let cur: unknown = obj;\n for (const part of parts) {\n if (cur === undefined || cur === null) return undefined;\n if (typeof cur !== 'object') return undefined;\n cur = (cur as Record<string, unknown>)[part];\n }\n return cur;\n })();\n return raw === undefined ? null : raw;\n}\n","/**\n * Shared classic-API projection translation for both Firestore editions.\n *\n * Translates a `findEdgesProjected({ select })` call into a\n * `Query.select(...fieldPaths)` query and decodes the partial documents\n * Firestore returns. Both Standard and Enterprise editions delegate to this\n * single helper so the projection contract — bare-name normalization,\n * builtin / `data.*` resolution, dedup semantics, original-key preservation\n * — stays consistent across editions.\n *\n * Why the classic API on both editions: the Enterprise pipeline `select()`\n * stage is a future optimisation. Server-side projection's only deliverable\n * is the byte-savings on the wire, and the classic `Query.select(...)`\n * API already achieves that on both editions. When pipeline `select()`\n * lands on a future SDK release the wiring is additive — swap the\n * implementation behind this helper, callers don't change.\n *\n * Migrations are not applied to the result. The contract on\n * `StorageBackend.findEdgesProjected` documents the rationale: the caller\n * asked for a partial shape, and rehydrating it through the migration\n * pipeline would require synthesising every absent field.\n *\n * Absent fields surface as `null`, not `undefined` — this matches the\n * SQLite-shaped backends (where `json_extract` returns SQL NULL for an\n * absent JSON path) so the projected row shape is identical across\n * SQLite/DO/Firestore. See `readProjectionPath` for the normalisation.\n */\n\nimport type { Query } from '@google-cloud/firestore';\n\nimport { FiregraphError } from '../errors.js';\nimport type { QueryFilter, QueryOptions } from '../types.js';\nimport { normalizeFirestoreProjectionField, readProjectionPath } from './projection.js';\n\n/** Resolved projection field — original (caller-supplied) + canonical Firestore path. */\ninterface ResolvedProjectionField {\n original: string;\n canonical: string;\n}\n\n/**\n * Run a projecting query against a base Firestore `Query`. Returns the\n * decoded rows, each keyed by the *original* field name as the caller\n * supplied it.\n *\n * `select` is rejected when empty (matches the SQLite compiler — both\n * layers fail so a misuse caught by either surfaces a clean\n * `INVALID_QUERY`). Duplicate entries are de-duped while preserving\n * first-occurrence order.\n */\nexport async function runFirestoreFindEdgesProjected(\n base: Query,\n select: ReadonlyArray<string>,\n filters: QueryFilter[],\n options?: QueryOptions,\n): Promise<Array<Record<string, unknown>>> {\n if (select.length === 0) {\n throw new FiregraphError(\n 'findEdgesProjected requires a non-empty select list — ' +\n 'an empty projection has no representation distinct from `findEdges`.',\n 'INVALID_QUERY',\n );\n }\n\n const seen = new Set<string>();\n const fields: ResolvedProjectionField[] = [];\n for (const f of select) {\n if (!seen.has(f)) {\n seen.add(f);\n fields.push({ original: f, canonical: normalizeFirestoreProjectionField(f) });\n }\n }\n\n let q: Query = base;\n for (const f of filters) {\n q = q.where(f.field, f.op, f.value);\n }\n if (options?.orderBy) {\n q = q.orderBy(options.orderBy.field, options.orderBy.direction ?? 'asc');\n }\n if (options?.limit !== undefined) {\n q = q.limit(options.limit);\n }\n q = q.select(...fields.map((p) => p.canonical));\n\n const snap = await q.get();\n return snap.docs.map((doc) => {\n const data = doc.data() as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const { original, canonical } of fields) {\n out[original] = readProjectionPath(data, canonical);\n }\n return out;\n });\n}\n","/**\n * Shared `UpdatePayload` → Firestore `update()` argument builder used by both\n * Firestore editions (`firestore-standard` and `firestore-enterprise`).\n *\n * Firestore's `DocumentReference.update` has two overloads:\n * - `update(data: UpdateData)` — an object whose *string* keys are parsed\n * as dotted field paths (`'a.b'` → nested `a → b`).\n * - `update(field: string | FieldPath, value, …moreFieldsAndValues)` — a\n * variadic field/value list where a `FieldPath` addresses literal\n * segments with no dotted reparse.\n *\n * We build the **variadic** form and key every deep-merge op with\n * `new FieldPath('data', …op.path)`. Literal segments mean an object key can\n * be anything non-empty — digit-leading (`'4f9Kq_2bN'`), hyphenated,\n * containing dots/brackets/whitespace — and still address that exact key\n * rather than being split on `.` by Firestore's dotted-path parser. This is\n * the Firestore-side counterpart to the SQLite backend's quoted JSON-path\n * labels (`src/internal/sqlite-data-ops.ts`): both backends escape keys at\n * path-construction time instead of rejecting them.\n *\n * `replaceData` keeps the plain string `'data'` key — it overwrites the\n * whole map, so there is no nested path to escape; tagged Firestore types\n * from the migration sandbox are reconstructed here via\n * `deserializeFirestoreTypes`. `updatedAt` is always stamped; `v` is stamped\n * as a top-level field when provided.\n */\n\nimport type { Firestore } from '@google-cloud/firestore';\nimport { FieldPath, FieldValue } from '@google-cloud/firestore';\n\nimport { deserializeFirestoreTypes } from '../serialization.js';\nimport type { UpdatePayload } from './backend.js';\nimport { assertSafePath, assertUpdatePayloadExclusive } from './write-plan.js';\n\n/**\n * Variadic argument tuple for `DocumentReference.update(field, value, …)`.\n * Always at least one field/value pair (`updatedAt` is unconditionally\n * stamped), so the tuple is non-empty and matches the variadic overload\n * rather than the single-object overload.\n */\nexport type FirestoreUpdateArgs = [string | FieldPath, unknown, ...unknown[]];\n\nexport function buildFirestoreUpdateArgs(\n update: UpdatePayload,\n db: Firestore,\n): FirestoreUpdateArgs {\n assertUpdatePayloadExclusive(update);\n const args: unknown[] = [];\n\n if (update.replaceData) {\n args.push('data', deserializeFirestoreTypes(update.replaceData, db));\n } else if (update.dataOps) {\n for (const op of update.dataOps) {\n assertSafePath(op.path);\n args.push(new FieldPath('data', ...op.path), op.delete ? FieldValue.delete() : op.value);\n }\n }\n\n args.push('updatedAt', FieldValue.serverTimestamp());\n if (update.v !== undefined) {\n args.push('v', update.v);\n }\n\n return args as FirestoreUpdateArgs;\n}\n","/**\n * Shared classic-API vector / nearest-neighbour translation for both\n * Firestore editions.\n *\n * Translates a `findNearest({ ... })` call into a `Query.findNearest(opts)`\n * VectorQuery and decodes the snapshot. Standard and Enterprise both\n * delegate here so the field-path normalisation, identifying-filter\n * application, validation surface, and result shape are guaranteed\n * identical across editions.\n *\n * Why the classic API on both editions: the Enterprise pipeline\n * `findNearest` stage is a future optimisation. Vector search's\n * deliverable is \"top-K by similarity,\" and the classic\n * `Query.findNearest(...)` API already produces that on both editions\n * with identical index requirements. When pipeline `findNearest` becomes\n * preferable for some other reason (composing with other pipeline\n * stages), the wiring is additive — swap the implementation behind this\n * helper, callers don't change.\n *\n * Migrations are not applied to the result. The contract on\n * `StorageBackend.findNearest` documents the rationale: the vector\n * index walked the raw stored shape, and rehydrating through the\n * migration pipeline would change the candidate set the index already\n * chose.\n */\n\nimport type { FieldPath } from '@google-cloud/firestore';\nimport { type Query, type VectorValue } from '@google-cloud/firestore';\n\nimport { FiregraphError } from '../errors.js';\nimport type { FindNearestParams, QueryFilter, StoredGraphRecord } from '../types.js';\nimport { applyFiltersToQuery } from './firestore-aggregate.js';\n\n/**\n * Built-in envelope fields that must NOT be passed as `vectorField` or\n * `distanceResultField`. Vectors live inside `data`; the envelope is\n * reserved for firegraph metadata. Mirrors the projection /\n * filter-field contract.\n */\nconst ENVELOPE_FIELDS: ReadonlySet<string> = new Set([\n 'aType',\n 'aUid',\n 'axbType',\n 'bType',\n 'bUid',\n 'createdAt',\n 'updatedAt',\n 'v',\n]);\n\n/**\n * Normalise a caller-supplied vector / distance-result field path. Bare\n * names rewrite to `data.<name>`; `'data'` and `'data.*'` pass through;\n * envelope fields are rejected (they aren't vector-indexable, and the\n * SDK reserves them as `distanceResultField` targets).\n */\nexport function normalizeVectorFieldPath(label: string, field: string): string {\n if (ENVELOPE_FIELDS.has(field)) {\n throw new FiregraphError(\n `findNearest(): ${label} '${field}' is a built-in envelope field — ` +\n `vectors must live under \\`data.*\\`. Use a path like 'data.${field}' ` +\n `if you really meant a nested data field.`,\n 'INVALID_QUERY',\n );\n }\n if (field === 'data' || field.startsWith('data.')) return field;\n return `data.${field}`;\n}\n\n/**\n * Translate firegraph's identifying-filter shape (`aType`, `axbType`,\n * `bType`) plus the optional `where` array into a flat `QueryFilter[]`\n * that `applyFiltersToQuery` can consume. The client wrapper does\n * scan-protection on this same list before dispatching, so the order\n * (identifiers first, then user-supplied where) is the same one the\n * safety check saw.\n */\nexport function buildVectorFilters(params: FindNearestParams): QueryFilter[] {\n const filters: QueryFilter[] = [];\n if (params.aType) filters.push({ field: 'aType', op: '==', value: params.aType });\n if (params.axbType) filters.push({ field: 'axbType', op: '==', value: params.axbType });\n if (params.bType) filters.push({ field: 'bType', op: '==', value: params.bType });\n if (params.where) filters.push(...params.where);\n return filters;\n}\n\n/** Resolve a `queryVector` argument to a plain `number[]`. */\nfunction toNumberArray(qv: number[] | { toArray(): number[] }): number[] {\n if (Array.isArray(qv)) return qv;\n if (typeof (qv as { toArray?: unknown }).toArray === 'function') {\n return (qv as VectorValue).toArray();\n }\n throw new FiregraphError(\n 'findNearest(): queryVector must be a number[] or a Firestore VectorValue.',\n 'INVALID_QUERY',\n );\n}\n\n/**\n * Run a vector query against a base Firestore `Query`. Returns the\n * matching records as `StoredGraphRecord[]`, ordered by similarity (the\n * SDK's natural order — nearest-first for EUCLIDEAN/COSINE,\n * highest-first for DOT_PRODUCT).\n *\n * Validation surface (matches the `VectorExtension` JSDoc):\n *\n * - `vectorField` and `distanceResultField` (if set) must not be\n * envelope fields.\n * - `queryVector` must be a non-empty `number[]` / `VectorValue`.\n * - `limit` must be a positive integer ≤ 1000 (the SDK enforces 1000\n * on the wire; we mirror it client-side for a clearer error).\n */\nexport async function runFirestoreFindNearest(\n base: Query,\n params: FindNearestParams,\n): Promise<StoredGraphRecord[]> {\n const vec = toNumberArray(params.queryVector);\n if (vec.length === 0) {\n throw new FiregraphError(\n 'findNearest(): queryVector is empty — at least one dimension is required.',\n 'INVALID_QUERY',\n );\n }\n if (!Number.isInteger(params.limit) || params.limit <= 0 || params.limit > 1000) {\n throw new FiregraphError(\n `findNearest(): limit must be a positive integer ≤ 1000 (got ${params.limit}).`,\n 'INVALID_QUERY',\n );\n }\n\n const vectorField = normalizeVectorFieldPath('vectorField', params.vectorField);\n const distanceResultField =\n params.distanceResultField !== undefined\n ? normalizeVectorFieldPath('distanceResultField', params.distanceResultField)\n : undefined;\n\n const filtered = applyFiltersToQuery(base, buildVectorFilters(params));\n\n // Firestore's `findNearest({ vectorField, ... })` accepts a `string` or\n // `FieldPath` for both `vectorField` and `distanceResultField`. We pass\n // the dotted path verbatim — Firestore itself interprets `.` as the\n // field-path separator, matching the convention used everywhere else\n // in firegraph.\n const opts: {\n vectorField: string | FieldPath;\n queryVector: number[];\n limit: number;\n distanceMeasure: 'EUCLIDEAN' | 'COSINE' | 'DOT_PRODUCT';\n distanceThreshold?: number;\n distanceResultField?: string | FieldPath;\n } = {\n vectorField,\n queryVector: vec,\n limit: params.limit,\n distanceMeasure: params.distanceMeasure,\n };\n if (params.distanceThreshold !== undefined) opts.distanceThreshold = params.distanceThreshold;\n if (distanceResultField !== undefined) opts.distanceResultField = distanceResultField;\n\n // The classic `findNearest` returns a `VectorQuery` with its own `.get()`\n // that resolves to a `VectorQuerySnapshot`. The doc shape is identical\n // to a normal QueryDocumentSnapshot, so we can decode straight into\n // `StoredGraphRecord` like the regular `query()` adapter does.\n const snap = await filtered.findNearest(opts).get();\n return snap.docs.map((doc) => doc.data() as StoredGraphRecord);\n}\n"],"mappings":"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","names":["chunk"]}
|