@typicalday/firegraph 0.8.0 → 0.9.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/dist/backend.cjs +222 -0
- package/dist/backend.cjs.map +1 -0
- package/dist/backend.d.cts +121 -0
- package/dist/backend.d.ts +121 -0
- package/dist/backend.js +136 -0
- package/dist/backend.js.map +1 -0
- package/dist/{chunk-6OQW5OKO.js → chunk-5753Y42M.js} +12 -4
- package/dist/chunk-5753Y42M.js.map +1 -0
- package/dist/{chunk-YUXOALMR.js → chunk-EVUM6ORB.js} +14 -92
- package/dist/chunk-EVUM6ORB.js.map +1 -0
- package/dist/chunk-GLOVWKQH.js +94 -0
- package/dist/chunk-GLOVWKQH.js.map +1 -0
- package/dist/{chunk-KFA7G37W.js → chunk-SU4FNLC3.js} +32 -30
- package/dist/chunk-SU4FNLC3.js.map +1 -0
- package/dist/{chunk-WOAJRVHD.js → chunk-SZ6W4VAS.js} +5 -3
- package/dist/{chunk-WOAJRVHD.js.map → chunk-SZ6W4VAS.js.map} +1 -1
- package/dist/chunk-TYYPRVIE.js +57 -0
- package/dist/chunk-TYYPRVIE.js.map +1 -0
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/d1.cjs +11 -6
- package/dist/d1.cjs.map +1 -1
- package/dist/d1.d.cts +1 -1
- package/dist/d1.d.ts +1 -1
- package/dist/d1.js +3 -2
- package/dist/d1.js.map +1 -1
- package/dist/do-sqlite.cjs +11 -6
- package/dist/do-sqlite.cjs.map +1 -1
- package/dist/do-sqlite.d.cts +1 -1
- package/dist/do-sqlite.d.ts +1 -1
- package/dist/do-sqlite.js +3 -2
- package/dist/do-sqlite.js.map +1 -1
- package/dist/editor/client/assets/index-Bq2bfzeY.js +411 -0
- package/dist/editor/client/index.html +1 -1
- package/dist/editor/server/index.mjs +6432 -6333
- package/dist/index.cjs +110 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -133
- package/dist/index.d.ts +5 -133
- package/dist/index.js +36 -27
- package/dist/index.js.map +1 -1
- package/dist/query-client/index.cjs +30 -28
- package/dist/query-client/index.cjs.map +1 -1
- package/dist/query-client/index.d.cts +2 -2
- package/dist/query-client/index.d.ts +2 -2
- package/dist/query-client/index.js +1 -1
- package/dist/react.cjs +0 -1
- package/dist/react.cjs.map +1 -1
- package/dist/react.js +0 -1
- package/dist/react.js.map +1 -1
- package/dist/scope-path-BtajqNK5.d.ts +234 -0
- package/dist/scope-path-D2mNENJ-.d.cts +234 -0
- package/dist/{serialization-C6JNNOCS.js → serialization-ZZ7RSDRX.js} +2 -2
- package/dist/svelte.cjs +0 -2
- package/dist/svelte.cjs.map +1 -1
- package/dist/svelte.js +0 -2
- package/dist/svelte.js.map +1 -1
- package/dist/{types-BVtx9zLv.d.cts → types-DfWVTsMn.d.cts} +3 -2
- package/dist/{types-BVtx9zLv.d.ts → types-DfWVTsMn.d.ts} +3 -2
- package/package.json +12 -1
- package/dist/chunk-6OQW5OKO.js.map +0 -1
- package/dist/chunk-KFA7G37W.js.map +0 -1
- package/dist/chunk-YUXOALMR.js.map +0 -1
- package/dist/editor/client/assets/index-tyFcX6qG.js +0 -411
- /package/dist/{serialization-C6JNNOCS.js.map → serialization-ZZ7RSDRX.js.map} +0 -0
package/dist/d1.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/serialization.ts","../src/d1.ts","../src/docid.ts","../src/internal/constants.ts","../src/batch.ts","../src/dynamic-registry.ts","../src/errors.ts","../src/json-schema.ts","../src/migration.ts","../src/scope.ts","../src/registry.ts","../src/sandbox.ts","../src/query.ts","../src/query-safety.ts","../src/transaction.ts","../src/client.ts","../src/timestamp.ts","../src/internal/sqlite-schema.ts","../src/internal/sqlite-sql.ts","../src/internal/sqlite-backend.ts"],"sourcesContent":["/**\n * Firestore-aware serialization for the sandbox migration pipeline.\n *\n * Firestore documents can contain special types (Timestamp, GeoPoint,\n * VectorValue, DocumentReference) that don't survive plain JSON\n * round-tripping. This module provides tagged serialization: Firestore\n * types are wrapped in tagged plain objects before JSON marshaling and\n * reconstructed after.\n *\n * Only used by the `defaultExecutor` sandbox path. Static migrations\n * (in-memory functions) receive raw Firestore objects directly.\n */\n\nimport { Timestamp, GeoPoint, FieldValue } from '@google-cloud/firestore';\nimport type { Firestore, DocumentReference } from '@google-cloud/firestore';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Sentinel key used to tag serialized Firestore types. */\nexport const SERIALIZATION_TAG = '__firegraph_ser__' as const;\n\n/** Known discriminator values for tagged types. */\nconst KNOWN_TYPES = new Set(['Timestamp', 'GeoPoint', 'VectorValue', 'DocumentReference']);\n\n// One-time warning for DocumentReference deserialization without db\nlet _docRefWarned = false;\n\n// ---------------------------------------------------------------------------\n// Type guard\n// ---------------------------------------------------------------------------\n\n/** Check if a value is a tagged serialized Firestore type. */\nexport function isTaggedValue(value: unknown): boolean {\n if (value === null || typeof value !== 'object') return false;\n const tag = (value as Record<string, unknown>)[SERIALIZATION_TAG];\n return typeof tag === 'string' && KNOWN_TYPES.has(tag);\n}\n\n// ---------------------------------------------------------------------------\n// Detection helpers\n// ---------------------------------------------------------------------------\n\nfunction isTimestamp(value: unknown): value is Timestamp {\n return value instanceof Timestamp;\n}\n\nfunction isGeoPoint(value: unknown): value is GeoPoint {\n return value instanceof GeoPoint;\n}\n\nfunction isDocumentReference(value: unknown): value is DocumentReference {\n // Duck-type check: DocumentReference has path (string) and firestore properties\n if (value === null || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.path === 'string' &&\n v.firestore !== undefined &&\n typeof v.id === 'string' &&\n v.constructor?.name === 'DocumentReference'\n );\n}\n\nfunction isVectorValue(value: unknown): boolean {\n if (value === null || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n return (\n v.constructor?.name === 'VectorValue' &&\n Array.isArray((v as Record<string, unknown>)._values)\n );\n}\n\n// ---------------------------------------------------------------------------\n// Serialize\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively walk a data object and replace Firestore types with tagged\n * plain objects suitable for JSON serialization.\n *\n * Returns a new object tree — the input is never mutated.\n */\nexport function serializeFirestoreTypes(\n data: Record<string, unknown>,\n): Record<string, unknown> {\n return serializeValue(data) as Record<string, unknown>;\n}\n\nfunction serializeValue(value: unknown): unknown {\n // Primitives\n if (value === null || value === undefined) return value;\n if (typeof value !== 'object') return value;\n\n // Firestore types (check before generic object/array)\n if (isTimestamp(value)) {\n return { [SERIALIZATION_TAG]: 'Timestamp', seconds: value.seconds, nanoseconds: value.nanoseconds };\n }\n if (isGeoPoint(value)) {\n return { [SERIALIZATION_TAG]: 'GeoPoint', latitude: value.latitude, longitude: value.longitude };\n }\n if (isDocumentReference(value)) {\n return { [SERIALIZATION_TAG]: 'DocumentReference', path: (value as DocumentReference).path };\n }\n if (isVectorValue(value)) {\n // Prefer toArray() (public API) over _values (private internal property)\n const v = value as Record<string, unknown>;\n const values = typeof v.toArray === 'function'\n ? (v.toArray as () => number[])()\n : (v._values as number[]);\n return { [SERIALIZATION_TAG]: 'VectorValue', values: [...values] };\n }\n\n // Arrays\n if (Array.isArray(value)) {\n return value.map(serializeValue);\n }\n\n // Plain objects — recurse\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n result[key] = serializeValue((value as Record<string, unknown>)[key]);\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Deserialize\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively walk a data object and reconstruct Firestore types from\n * tagged plain objects.\n *\n * @param data - The data to deserialize (typically from JSON.parse)\n * @param db - Optional Firestore instance for DocumentReference reconstruction.\n * If not provided, tagged DocumentReferences are left as-is with a one-time warning.\n *\n * Returns a new object tree — the input is never mutated.\n */\nexport function deserializeFirestoreTypes(\n data: Record<string, unknown>,\n db?: Firestore,\n): Record<string, unknown> {\n return deserializeValue(data, db) as Record<string, unknown>;\n}\n\nfunction deserializeValue(value: unknown, db?: Firestore): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value !== 'object') return value;\n\n // Short-circuit for values that are already real Firestore types.\n // This makes deserializeFirestoreTypes idempotent — safe to call on data\n // that has already been deserialized (e.g., write-back after defaultExecutor\n // already reconstructed types, or static migrations that return raw types).\n if (isTimestamp(value) || isGeoPoint(value) || isDocumentReference(value) || isVectorValue(value)) {\n return value;\n }\n\n // Arrays\n if (Array.isArray(value)) {\n return value.map((v) => deserializeValue(v, db));\n }\n\n const obj = value as Record<string, unknown>;\n\n // Check for tagged Firestore type\n if (isTaggedValue(obj)) {\n const tag = obj[SERIALIZATION_TAG] as string;\n\n switch (tag) {\n case 'Timestamp':\n // Validate expected fields before reconstruction\n if (typeof obj.seconds !== 'number' || typeof obj.nanoseconds !== 'number') return obj;\n return new Timestamp(obj.seconds, obj.nanoseconds);\n\n case 'GeoPoint':\n if (typeof obj.latitude !== 'number' || typeof obj.longitude !== 'number') return obj;\n return new GeoPoint(obj.latitude, obj.longitude);\n\n case 'VectorValue':\n if (!Array.isArray(obj.values)) return obj;\n return FieldValue.vector(obj.values as number[]);\n\n case 'DocumentReference':\n if (typeof obj.path !== 'string') return obj;\n if (db) {\n return db.doc(obj.path);\n }\n // No db available — leave as tagged object with one-time warning\n if (!_docRefWarned) {\n _docRefWarned = true;\n console.warn(\n '[firegraph] DocumentReference encountered during migration deserialization ' +\n 'but no Firestore instance available. The reference will remain as a tagged ' +\n 'object with its path. Enable write-back for full reconstruction.',\n );\n }\n return obj;\n\n default:\n // Unknown tag — leave as-is (forward compatibility)\n return obj;\n }\n }\n\n // Plain object — recurse\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n result[key] = deserializeValue(obj[key], db);\n }\n return result;\n}\n","/**\n * Cloudflare D1 driver for firegraph.\n *\n * D1 is async, prepared-statement-based, and supports atomic multi-statement\n * batches via `db.batch()`. D1 has no interactive transactions — reads and\n * conditional writes cannot be interleaved, so `GraphClient.runTransaction()`\n * will throw `UNSUPPORTED_OPERATION` on this driver. Use `GraphClient.batch()`\n * or migrate to Durable Object SQLite for interactive transactions.\n *\n * **Bulk-delete atomicity:** `db.batch()` is atomic *within* one batch but D1\n * caps batches at ~100 statements / 1000 bound parameters. The shared SQLite\n * backend chunks `removeNodeCascade` and `bulkRemoveEdges` automatically; each\n * chunk retries with exponential backoff (`BulkOptions.maxRetries`, default 3).\n * Cross-chunk atomicity is *not* guaranteed — a hub node with thousands of\n * edges may have some chunks commit and others fail. Both operations are\n * idempotent (re-deleting an already-deleted row is a no-op), so callers can\n * safely retry on partial failure. Inspect `result.errors` to detect it.\n */\n\nimport { createGraphClientFromBackend } from './client.js';\nimport { createSqliteBackend } from './internal/sqlite-backend.js';\nimport type { SqliteExecutor } from './internal/sqlite-executor.js';\nimport { buildSchemaStatements, validateTableName } from './internal/sqlite-schema.js';\nimport type {\n DynamicGraphClient,\n DynamicRegistryConfig,\n GraphClient,\n GraphClientOptions,\n} from './types.js';\n\n/**\n * Subset of the Cloudflare D1 Database interface that firegraph depends on.\n * Typed against the official `@cloudflare/workers-types` shape without\n * importing it, so this module has no runtime dependency on the Workers SDK.\n */\nexport interface D1Database {\n prepare(sql: string): D1PreparedStatement;\n batch(statements: D1PreparedStatement[]): Promise<unknown[]>;\n exec(sql: string): Promise<unknown>;\n}\n\nexport interface D1PreparedStatement {\n bind(...values: unknown[]): D1PreparedStatement;\n all<T = Record<string, unknown>>(): Promise<{ results?: T[] }>;\n run(): Promise<unknown>;\n}\n\nexport interface D1ClientOptions extends GraphClientOptions {\n /** Table name for firegraph triples (default: `firegraph`). */\n table?: string;\n /**\n * Run `CREATE TABLE IF NOT EXISTS …` statements on first use.\n * Default: `true`. Disable if you manage schema via a migration tool.\n */\n autoMigrate?: boolean;\n}\n\nclass D1Executor implements SqliteExecutor {\n /**\n * D1 caps `db.batch()` at roughly 100 statements (and ~1000 bound\n * parameters across them). The SqliteBackend uses this hint to chunk\n * large cascade/bulk delete operations so a hub node with thousands of\n * edges doesn't trigger a hard rejection.\n */\n readonly maxBatchSize = 100;\n /**\n * D1's secondary cap: total bound parameters across the batch. Cascade\n * deletes are 2 params each (well under the limit) but the chunker\n * respects this defensively if a future statement type pushes through\n * `executeChunkedBatches` with a higher per-statement param count.\n */\n readonly maxBatchParams = 1000;\n\n constructor(private readonly db: D1Database) {}\n\n async all(sql: string, params: unknown[]): Promise<Record<string, unknown>[]> {\n const stmt = this.db.prepare(sql).bind(...params);\n const result = await stmt.all<Record<string, unknown>>();\n return result.results ?? [];\n }\n\n async run(sql: string, params: unknown[]): Promise<void> {\n await this.db\n .prepare(sql)\n .bind(...params)\n .run();\n }\n\n async batch(statements: ReadonlyArray<{ sql: string; params: unknown[] }>): Promise<void> {\n if (statements.length === 0) return;\n const prepared = statements.map((s) => this.db.prepare(s.sql).bind(...s.params));\n await this.db.batch(prepared);\n }\n\n // No `transaction` — D1 has no interactive transactions.\n}\n\nasync function ensureSchema(db: D1Database, table: string): Promise<void> {\n const statements = buildSchemaStatements(table);\n for (const sql of statements) {\n await db.prepare(sql).run();\n }\n}\n\nexport function createD1GraphClient(\n db: D1Database,\n options: D1ClientOptions & { registryMode: DynamicRegistryConfig },\n): Promise<DynamicGraphClient>;\nexport function createD1GraphClient(\n db: D1Database,\n options?: D1ClientOptions,\n): Promise<GraphClient>;\nexport async function createD1GraphClient(\n db: D1Database,\n options: D1ClientOptions = {},\n): Promise<GraphClient | DynamicGraphClient> {\n const table = options.table ?? 'firegraph';\n validateTableName(table);\n if (options.autoMigrate !== false) {\n await ensureSchema(db, table);\n }\n\n const executor = new D1Executor(db);\n const backend = createSqliteBackend(executor, table);\n\n const { table: _t, autoMigrate: _m, ...clientOptions } = options;\n void _t;\n void _m;\n\n // If a separate meta-collection is requested, create a second backend for it.\n let metaBackend;\n if (\n clientOptions.registryMode &&\n typeof clientOptions.registryMode === 'object' &&\n clientOptions.registryMode.collection &&\n clientOptions.registryMode.collection !== table\n ) {\n const metaTable = clientOptions.registryMode.collection;\n validateTableName(metaTable);\n if (options.autoMigrate !== false) {\n await ensureSchema(db, metaTable);\n }\n metaBackend = createSqliteBackend(executor, metaTable);\n }\n\n return createGraphClientFromBackend(backend, clientOptions, metaBackend);\n}\n","import { createHash } from 'node:crypto';\nimport { SHARD_SEPARATOR } from './internal/constants.js';\n\nexport function computeNodeDocId(uid: string): string {\n return uid;\n}\n\nexport function computeEdgeDocId(aUid: string, axbType: string, bUid: string): string {\n const composite = `${aUid}${SHARD_SEPARATOR}${axbType}${SHARD_SEPARATOR}${bUid}`;\n const hash = createHash('sha256').update(composite).digest('hex');\n const shard = hash[0];\n return `${shard}${SHARD_SEPARATOR}${aUid}${SHARD_SEPARATOR}${axbType}${SHARD_SEPARATOR}${bUid}`;\n}\n","export const NODE_RELATION = 'is';\n\n/**\n * Default result limit applied to findEdges/findNodes queries\n * when no explicit limit is provided. Prevents unbounded result sets\n * that could be expensive on Enterprise Firestore.\n */\nexport const DEFAULT_QUERY_LIMIT = 500;\n\n/**\n * Fields that are part of the firegraph record structure (not user data).\n * Used by the query planner and safety analysis to distinguish builtin\n * fields from data.* fields.\n */\nexport const BUILTIN_FIELDS = new Set([\n 'aType', 'aUid', 'axbType', 'bType', 'bUid', 'createdAt', 'updatedAt',\n]);\n\nexport const SHARD_ALGORITHM = 'sha256';\nexport const SHARD_SEPARATOR = ':';\nexport const SHARD_BUCKETS = 16;\n","import { computeEdgeDocId, computeNodeDocId } from './docid.js';\nimport type { BatchBackend, WritableRecord } from './internal/backend.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport type { GraphBatch, GraphRegistry } from './types.js';\n\nfunction buildWritableNodeRecord(\n aType: string,\n uid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid: uid, axbType: NODE_RELATION, bType: aType, bUid: uid, data };\n}\n\nfunction buildWritableEdgeRecord(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid, axbType, bType, bUid, data };\n}\n\nexport class GraphBatchImpl implements GraphBatch {\n constructor(\n private readonly backend: BatchBackend,\n private readonly registry?: GraphRegistry,\n private readonly scopePath: string = '',\n ) {}\n\n async putNode(aType: string, uid: string, data: Record<string, unknown>): Promise<void> {\n if (this.registry) {\n this.registry.validate(aType, NODE_RELATION, aType, data, this.scopePath);\n }\n const docId = computeNodeDocId(uid);\n const record = buildWritableNodeRecord(aType, uid, data);\n if (this.registry) {\n const entry = this.registry.lookup(aType, NODE_RELATION, aType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n this.backend.setDoc(docId, record);\n }\n\n async putEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n if (this.registry) {\n this.registry.validate(aType, axbType, bType, data, this.scopePath);\n }\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = buildWritableEdgeRecord(aType, aUid, axbType, bType, bUid, data);\n if (this.registry) {\n const entry = this.registry.lookup(aType, axbType, bType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n this.backend.setDoc(docId, record);\n }\n\n async updateNode(uid: string, data: Record<string, unknown>): Promise<void> {\n const docId = computeNodeDocId(uid);\n this.backend.updateDoc(docId, { dataFields: data });\n }\n\n async removeNode(uid: string): Promise<void> {\n const docId = computeNodeDocId(uid);\n this.backend.deleteDoc(docId);\n }\n\n async removeEdge(aUid: string, axbType: string, bUid: string): Promise<void> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n this.backend.deleteDoc(docId);\n }\n\n async commit(): Promise<void> {\n await this.backend.commit();\n }\n}\n","import { createHash } from 'node:crypto';\nimport { createRegistry } from './registry.js';\nimport { compileMigrations, precompileSource } from './sandbox.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport type {\n GraphReader,\n GraphRegistry,\n MigrationExecutor,\n RegistryEntry,\n NodeTypeData,\n EdgeTypeData,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Meta-type constants\n// ---------------------------------------------------------------------------\n\n/** The aType used for node type definition meta-nodes. */\nexport const META_NODE_TYPE = 'nodeType';\n\n/** The aType used for edge type definition meta-nodes. */\nexport const META_EDGE_TYPE = 'edgeType';\n\n// ---------------------------------------------------------------------------\n// JSON Schemas for meta-type data payloads\n// ---------------------------------------------------------------------------\n\n/** JSON Schema for a single stored migration step. */\nconst STORED_MIGRATION_STEP_SCHEMA = {\n type: 'object',\n required: ['fromVersion', 'toVersion', 'up'],\n properties: {\n fromVersion: { type: 'integer', minimum: 0 },\n toVersion: { type: 'integer', minimum: 1 },\n up: { type: 'string', minLength: 1 },\n },\n additionalProperties: false,\n};\n\n/** JSON Schema for the `data` payload of a `nodeType` meta-node. */\nexport const NODE_TYPE_SCHEMA: object = {\n type: 'object',\n required: ['name', 'jsonSchema'],\n properties: {\n name: { type: 'string', minLength: 1 },\n jsonSchema: { type: 'object' },\n description: { type: 'string' },\n titleField: { type: 'string' },\n subtitleField: { type: 'string' },\n viewTemplate: { type: 'string' },\n viewCss: { type: 'string' },\n allowedIn: { type: 'array', items: { type: 'string', minLength: 1 } },\n schemaVersion: { type: 'integer', minimum: 0 },\n migrations: { type: 'array', items: STORED_MIGRATION_STEP_SCHEMA },\n migrationWriteBack: { type: 'string', enum: ['off', 'eager', 'background'] },\n },\n additionalProperties: false,\n};\n\n/** JSON Schema for the `data` payload of an `edgeType` meta-node. */\nexport const EDGE_TYPE_SCHEMA: object = {\n type: 'object',\n required: ['name', 'from', 'to'],\n properties: {\n name: { type: 'string', minLength: 1 },\n from: {\n oneOf: [\n { type: 'string', minLength: 1 },\n { type: 'array', items: { type: 'string', minLength: 1 }, minItems: 1 },\n ],\n },\n to: {\n oneOf: [\n { type: 'string', minLength: 1 },\n { type: 'array', items: { type: 'string', minLength: 1 }, minItems: 1 },\n ],\n },\n jsonSchema: { type: 'object' },\n inverseLabel: { type: 'string' },\n description: { type: 'string' },\n titleField: { type: 'string' },\n subtitleField: { type: 'string' },\n viewTemplate: { type: 'string' },\n viewCss: { type: 'string' },\n allowedIn: { type: 'array', items: { type: 'string', minLength: 1 } },\n targetGraph: { type: 'string', minLength: 1, pattern: '^[^/]+$' },\n schemaVersion: { type: 'integer', minimum: 0 },\n migrations: { type: 'array', items: STORED_MIGRATION_STEP_SCHEMA },\n migrationWriteBack: { type: 'string', enum: ['off', 'eager', 'background'] },\n },\n additionalProperties: false,\n};\n\n// ---------------------------------------------------------------------------\n// Bootstrap registry\n// ---------------------------------------------------------------------------\n\n/** Registry entries for the two meta-types (always present). */\nexport const BOOTSTRAP_ENTRIES: readonly RegistryEntry[] = [\n {\n aType: META_NODE_TYPE,\n axbType: NODE_RELATION,\n bType: META_NODE_TYPE,\n jsonSchema: NODE_TYPE_SCHEMA,\n description: 'Meta-type: defines a node type',\n },\n {\n aType: META_EDGE_TYPE,\n axbType: NODE_RELATION,\n bType: META_EDGE_TYPE,\n jsonSchema: EDGE_TYPE_SCHEMA,\n description: 'Meta-type: defines an edge type',\n },\n];\n\n/**\n * Build the bootstrap registry that validates meta-type writes.\n * This is always available, even before any dynamic types are loaded.\n */\nexport function createBootstrapRegistry(): GraphRegistry {\n return createRegistry([...BOOTSTRAP_ENTRIES]);\n}\n\n// ---------------------------------------------------------------------------\n// Deterministic UID generation\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a deterministic UID for a meta-type definition.\n * This ensures that defining the same type name always targets the same\n * Firestore document, enabling upsert semantics.\n *\n * Format: 21-char base64url substring of SHA-256(`metaType:name`).\n */\nexport function generateDeterministicUid(metaType: string, name: string): string {\n const hash = createHash('sha256')\n .update(`${metaType}:${name}`)\n .digest('base64url');\n return hash.slice(0, 21);\n}\n\n// ---------------------------------------------------------------------------\n// createRegistryFromGraph\n// ---------------------------------------------------------------------------\n\n/**\n * Read meta-type nodes from the graph and compile them into a GraphRegistry.\n *\n * The returned registry includes both the dynamic entries AND the bootstrap\n * meta-type entries, so meta-type writes remain validateable after a reload.\n *\n * @param reader - A GraphReader pointed at the collection containing meta-nodes.\n * @param executor - Optional custom executor for compiling stored migration source strings.\n */\nexport async function createRegistryFromGraph(\n reader: GraphReader,\n executor?: MigrationExecutor,\n): Promise<GraphRegistry> {\n const [nodeTypes, edgeTypes] = await Promise.all([\n reader.findNodes({ aType: META_NODE_TYPE }),\n reader.findNodes({ aType: META_EDGE_TYPE }),\n ]);\n\n const entries: RegistryEntry[] = [...BOOTSTRAP_ENTRIES];\n\n // Eagerly pre-validate all migration sources in the sandbox before building\n // the registry. This ensures reloadRegistry() fails fast on invalid sources.\n const prevalidations: Promise<void>[] = [];\n for (const record of nodeTypes) {\n const data = record.data as unknown as NodeTypeData;\n if (data.migrations) {\n for (const m of data.migrations) {\n prevalidations.push(precompileSource(m.up, executor));\n }\n }\n }\n for (const record of edgeTypes) {\n const data = record.data as unknown as EdgeTypeData;\n if (data.migrations) {\n for (const m of data.migrations) {\n prevalidations.push(precompileSource(m.up, executor));\n }\n }\n }\n await Promise.all(prevalidations);\n\n // Convert nodeType records → self-loop RegistryEntries\n for (const record of nodeTypes) {\n const data = record.data as unknown as NodeTypeData;\n entries.push({\n aType: data.name,\n axbType: NODE_RELATION,\n bType: data.name,\n jsonSchema: data.jsonSchema,\n description: data.description,\n titleField: data.titleField,\n subtitleField: data.subtitleField,\n allowedIn: data.allowedIn,\n migrations: data.migrations ? compileMigrations(data.migrations, executor) : undefined,\n migrationWriteBack: data.migrationWriteBack,\n });\n }\n\n // Convert edgeType records → RegistryEntries (expand from/to arrays)\n for (const record of edgeTypes) {\n const data = record.data as unknown as EdgeTypeData;\n const fromTypes = Array.isArray(data.from) ? data.from : [data.from];\n const toTypes = Array.isArray(data.to) ? data.to : [data.to];\n\n const compiledMigrations = data.migrations\n ? compileMigrations(data.migrations, executor)\n : undefined;\n\n for (const aType of fromTypes) {\n for (const bType of toTypes) {\n entries.push({\n aType,\n axbType: data.name,\n bType,\n jsonSchema: data.jsonSchema,\n description: data.description,\n inverseLabel: data.inverseLabel,\n titleField: data.titleField,\n subtitleField: data.subtitleField,\n allowedIn: data.allowedIn,\n targetGraph: data.targetGraph,\n migrations: compiledMigrations,\n migrationWriteBack: data.migrationWriteBack,\n });\n }\n }\n }\n\n return createRegistry(entries);\n}\n","export class FiregraphError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n ) {\n super(message);\n this.name = 'FiregraphError';\n }\n}\n\nexport class NodeNotFoundError extends FiregraphError {\n constructor(uid: string) {\n super(`Node not found: ${uid}`, 'NODE_NOT_FOUND');\n this.name = 'NodeNotFoundError';\n }\n}\n\nexport class EdgeNotFoundError extends FiregraphError {\n constructor(aUid: string, axbType: string, bUid: string) {\n super(`Edge not found: ${aUid} -[${axbType}]-> ${bUid}`, 'EDGE_NOT_FOUND');\n this.name = 'EdgeNotFoundError';\n }\n}\n\nexport class ValidationError extends FiregraphError {\n constructor(\n message: string,\n public readonly details?: unknown,\n ) {\n super(message, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\nexport class RegistryViolationError extends FiregraphError {\n constructor(aType: string, axbType: string, bType: string) {\n super(\n `Unregistered triple: (${aType}) -[${axbType}]-> (${bType})`,\n 'REGISTRY_VIOLATION',\n );\n this.name = 'RegistryViolationError';\n }\n}\n\nexport class InvalidQueryError extends FiregraphError {\n constructor(message: string) {\n super(message, 'INVALID_QUERY');\n this.name = 'InvalidQueryError';\n }\n}\n\nexport class TraversalError extends FiregraphError {\n constructor(message: string) {\n super(message, 'TRAVERSAL_ERROR');\n this.name = 'TraversalError';\n }\n}\n\nexport class DynamicRegistryError extends FiregraphError {\n constructor(message: string) {\n super(message, 'DYNAMIC_REGISTRY_ERROR');\n this.name = 'DynamicRegistryError';\n }\n}\n\nexport class QuerySafetyError extends FiregraphError {\n constructor(message: string) {\n super(message, 'QUERY_SAFETY');\n this.name = 'QuerySafetyError';\n }\n}\n\nexport class RegistryScopeError extends FiregraphError {\n constructor(aType: string, axbType: string, bType: string, scopePath: string, allowedIn: string[]) {\n super(\n `Type (${aType}) -[${axbType}]-> (${bType}) is not allowed at scope \"${scopePath || 'root'}\". ` +\n `Allowed in: [${allowedIn.join(', ')}]`,\n 'REGISTRY_SCOPE',\n );\n this.name = 'RegistryScopeError';\n }\n}\n\nexport class MigrationError extends FiregraphError {\n constructor(message: string) {\n super(message, 'MIGRATION_ERROR');\n this.name = 'MigrationError';\n }\n}\n","/**\n * JSON Schema validation and introspection utilities.\n *\n * Standard JSON Schema validation and introspection\n * processing. Uses ajv for validation and a recursive walker for converting\n * JSON Schema properties into FieldMeta objects for editor form generation.\n */\n\nimport Ajv from 'ajv';\nimport addFormats from 'ajv-formats';\nimport { ValidationError } from './errors.js';\n\n// ---------------------------------------------------------------------------\n// FieldMeta types (previously in editor/server/schema-introspect.ts)\n// ---------------------------------------------------------------------------\n\nexport interface FieldMeta {\n name: string;\n type: 'string' | 'number' | 'boolean' | 'enum' | 'array' | 'object' | 'unknown';\n required: boolean;\n description?: string;\n enumValues?: string[];\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n min?: number;\n max?: number;\n isInt?: boolean;\n itemMeta?: FieldMeta;\n fields?: FieldMeta[];\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nconst ajv = new Ajv({ allErrors: true, strict: false });\naddFormats(ajv);\n\n/**\n * Compile a JSON Schema into a validation function.\n * The returned function throws `ValidationError` if data is invalid.\n */\nexport function compileSchema(\n schema: object,\n label?: string,\n): (data: unknown) => void {\n const validate = ajv.compile(schema);\n return (data: unknown) => {\n if (!validate(data)) {\n const errors = validate.errors ?? [];\n const messages = errors\n .map((err) => `${err.instancePath || '/'}${err.message ? ': ' + err.message : ''}`)\n .join('; ');\n throw new ValidationError(\n `Data validation failed${label ? ' for ' + label : ''}: ${messages}`,\n errors,\n );\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// JSON Schema → FieldMeta introspection\n// ---------------------------------------------------------------------------\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\n/**\n * Convert a JSON Schema (expected to be `type: \"object\"`) into `FieldMeta[]`\n * suitable for the editor's SchemaForm component.\n */\nexport function jsonSchemaToFieldMeta(schema: any): FieldMeta[] {\n if (!schema || schema.type !== 'object' || !schema.properties) return [];\n\n const requiredSet = new Set<string>(\n Array.isArray(schema.required) ? schema.required : [],\n );\n\n return Object.entries(schema.properties).map(([name, prop]) =>\n propertyToFieldMeta(name, prop as any, requiredSet.has(name)),\n );\n}\n\n/**\n * Convert a single JSON Schema property into a `FieldMeta`.\n */\nfunction propertyToFieldMeta(\n name: string,\n prop: any,\n required: boolean,\n): FieldMeta {\n if (!prop) return { name, type: 'unknown', required };\n\n // Handle enum (can appear with or without type)\n if (Array.isArray(prop.enum)) {\n return {\n name,\n type: 'enum',\n required,\n enumValues: prop.enum as string[],\n description: prop.description,\n };\n }\n\n // Handle oneOf/anyOf for nullable patterns like { oneOf: [{type:'string'}, {type:'null'}] }\n if (Array.isArray(prop.oneOf) || Array.isArray(prop.anyOf)) {\n const variants = (prop.oneOf ?? prop.anyOf) as any[];\n const nonNull = variants.filter((v: any) => v.type !== 'null');\n if (nonNull.length === 1) {\n // Nullable wrapper — unwrap and mark as optional\n return propertyToFieldMeta(name, nonNull[0], false);\n }\n return { name, type: 'unknown', required, description: prop.description };\n }\n\n const type = prop.type;\n\n if (type === 'string') {\n return {\n name,\n type: 'string',\n required,\n minLength: prop.minLength,\n maxLength: prop.maxLength,\n pattern: prop.pattern,\n description: prop.description,\n };\n }\n\n if (type === 'number' || type === 'integer') {\n return {\n name,\n type: 'number',\n required,\n min: prop.minimum,\n max: prop.maximum,\n isInt: type === 'integer' ? true : undefined,\n description: prop.description,\n };\n }\n\n if (type === 'boolean') {\n return { name, type: 'boolean', required, description: prop.description };\n }\n\n if (type === 'array') {\n const itemMeta = prop.items\n ? propertyToFieldMeta('item', prop.items, true)\n : undefined;\n return {\n name,\n type: 'array',\n required,\n itemMeta,\n description: prop.description,\n };\n }\n\n if (type === 'object') {\n return {\n name,\n type: 'object',\n required,\n fields: jsonSchemaToFieldMeta(prop),\n description: prop.description,\n };\n }\n\n return { name, type: 'unknown', required, description: prop.description };\n}\n\n/* eslint-enable @typescript-eslint/no-explicit-any */\n","/**\n * Migration pipeline for auto-migrating records on read.\n *\n * When a record's `v` is behind the version derived from the registry\n * entry's migrations, the pipeline applies migration steps sequentially\n * to bring the data up to the current version.\n */\n\nimport { MigrationError } from './errors.js';\nimport type {\n GraphRegistry,\n MigrationStep,\n MigrationWriteBack,\n StoredGraphRecord,\n} from './types.js';\n\n/** Result of attempting to migrate a single record. */\nexport interface MigrationResult {\n record: StoredGraphRecord;\n migrated: boolean;\n /** Resolved write-back mode for this record (entry-level > global > 'off'). */\n writeBack: MigrationWriteBack;\n}\n\n/**\n * Apply a chain of migration steps to transform data from `currentVersion`\n * to `targetVersion`. Throws `MigrationError` if the chain is incomplete\n * or a migration function fails.\n *\n * Returns the migrated data payload only — the caller is responsible for\n * stamping `v` on the record envelope.\n */\nexport async function applyMigrationChain(\n data: Record<string, unknown>,\n currentVersion: number,\n targetVersion: number,\n migrations: MigrationStep[],\n): Promise<Record<string, unknown>> {\n const sorted = [...migrations].sort((a, b) => a.fromVersion - b.fromVersion);\n let result = { ...data };\n let version = currentVersion;\n\n for (const step of sorted) {\n if (step.fromVersion === version) {\n try {\n result = await step.up(result);\n } catch (err: unknown) {\n if (err instanceof MigrationError) throw err;\n throw new MigrationError(\n `Migration from v${step.fromVersion} to v${step.toVersion} failed: ${(err as Error).message}`,\n );\n }\n if (!result || typeof result !== 'object') {\n throw new MigrationError(\n `Migration from v${step.fromVersion} to v${step.toVersion} returned invalid data (expected object)`,\n );\n }\n version = step.toVersion;\n }\n }\n\n if (version !== targetVersion) {\n throw new MigrationError(\n `Incomplete migration chain: reached v${version} but target is v${targetVersion}`,\n );\n }\n\n return result;\n}\n\n/**\n * Validate that a migration chain forms a contiguous path from version 0\n * to the highest `toVersion`. Throws `MigrationError` if the chain has\n * gaps or duplicate `fromVersion` values.\n *\n * Called at registry construction time to catch incomplete chains early,\n * rather than at read time when a record is migrated.\n */\nexport function validateMigrationChain(\n migrations: MigrationStep[],\n label: string,\n): void {\n if (migrations.length === 0) return;\n\n // Validate individual steps\n const seen = new Set<number>();\n for (const step of migrations) {\n if (step.toVersion <= step.fromVersion) {\n throw new MigrationError(\n `${label}: migration step has toVersion (${step.toVersion}) <= fromVersion (${step.fromVersion})`,\n );\n }\n if (seen.has(step.fromVersion)) {\n throw new MigrationError(\n `${label}: duplicate migration step for fromVersion ${step.fromVersion}`,\n );\n }\n seen.add(step.fromVersion);\n }\n\n const sorted = [...migrations].sort((a, b) => a.fromVersion - b.fromVersion);\n const targetVersion = Math.max(...migrations.map((m) => m.toVersion));\n let version = 0;\n\n for (const step of sorted) {\n if (step.fromVersion === version) {\n version = step.toVersion;\n } else if (step.fromVersion > version) {\n throw new MigrationError(\n `${label}: migration chain has a gap — no step covers v${version} → v${step.fromVersion}`,\n );\n }\n }\n\n if (version !== targetVersion) {\n throw new MigrationError(\n `${label}: migration chain does not reach v${targetVersion} (stuck at v${version})`,\n );\n }\n}\n\n/**\n * Attempt to migrate a single record based on its registry entry.\n *\n * Returns the original record unchanged if no migration is needed\n * (no schema version, already at current version, or no migrations defined).\n */\nexport async function migrateRecord(\n record: StoredGraphRecord,\n registry: GraphRegistry,\n globalWriteBack: MigrationWriteBack = 'off',\n): Promise<MigrationResult> {\n const entry = registry.lookup(record.aType, record.axbType, record.bType);\n\n if (!entry?.migrations?.length || !entry.schemaVersion) {\n return { record, migrated: false, writeBack: 'off' };\n }\n\n const currentVersion = record.v ?? 0;\n\n if (currentVersion >= entry.schemaVersion) {\n return { record, migrated: false, writeBack: 'off' };\n }\n\n const migratedData = await applyMigrationChain(\n record.data,\n currentVersion,\n entry.schemaVersion,\n entry.migrations,\n );\n\n // Two-tier resolution: entry-level > global > 'off'\n const writeBack = entry.migrationWriteBack ?? globalWriteBack ?? 'off';\n\n return {\n record: { ...record, data: migratedData, v: entry.schemaVersion },\n migrated: true,\n writeBack,\n };\n}\n\n/**\n * Migrate an array of records, returning all results.\n * If any single migration fails, the entire call rejects — a broken\n * migration function is a bug that should surface immediately.\n */\nexport async function migrateRecords(\n records: StoredGraphRecord[],\n registry: GraphRegistry,\n globalWriteBack: MigrationWriteBack = 'off',\n): Promise<MigrationResult[]> {\n return Promise.all(\n records.map((r) => migrateRecord(r, registry, globalWriteBack)),\n );\n}\n","/**\n * Scope path matching for subgraph-level registry constraints.\n *\n * Scope paths are slash-separated names derived from the chain of\n * `subgraph()` calls (e.g., `'agents'`, `'agents/memories'`).\n * The root graph has an empty scope path (`''`).\n *\n * Patterns:\n * - `'root'` — matches only the root graph (empty scope path)\n * - `'agents'` — matches exactly `'agents'`\n * - `'agents/memories'` — matches exactly `'agents/memories'`\n * - `'*/agents'` — `*` matches one segment: `'foo/agents'` but not `'a/b/agents'`\n * - `'**/memories'` — `**` matches zero or more segments\n * - `'**'` — matches everything including root\n */\n\n/**\n * Test whether a scope path matches a single pattern.\n *\n * @param scopePath - The current scope path (empty string for root)\n * @param pattern - The pattern to match against\n */\nexport function matchScope(scopePath: string, pattern: string): boolean {\n // Special case: 'root' matches only the root graph\n if (pattern === 'root') return scopePath === '';\n\n // Special case: '**' matches everything\n if (pattern === '**') return true;\n\n const pathSegments = scopePath === '' ? [] : scopePath.split('/');\n const patternSegments = pattern.split('/');\n\n return matchSegments(pathSegments, 0, patternSegments, 0);\n}\n\n/**\n * Test whether a scope path matches any pattern in a list.\n * Returns `true` if the list is empty or undefined (allowed everywhere).\n *\n * @param scopePath - The current scope path (empty string for root)\n * @param patterns - Array of patterns to match against\n */\nexport function matchScopeAny(scopePath: string, patterns: string[]): boolean {\n if (!patterns || patterns.length === 0) return true;\n return patterns.some((p) => matchScope(scopePath, p));\n}\n\n/**\n * Recursive segment matcher with support for `*` (one segment) and\n * `**` (zero or more segments).\n */\nfunction matchSegments(\n path: string[],\n pi: number,\n pattern: string[],\n qi: number,\n): boolean {\n // Both exhausted — match\n if (pi === path.length && qi === pattern.length) return true;\n\n // Pattern exhausted but path remains — no match\n if (qi === pattern.length) return false;\n\n const seg = pattern[qi];\n\n if (seg === '**') {\n // '**' at the end of pattern — matches everything remaining\n if (qi === pattern.length - 1) return true;\n\n // Try consuming 0, 1, 2, ... path segments\n for (let skip = 0; skip <= path.length - pi; skip++) {\n if (matchSegments(path, pi + skip, pattern, qi + 1)) return true;\n }\n return false;\n }\n\n // Path exhausted but pattern has non-** segments remaining — no match\n if (pi === path.length) return false;\n\n if (seg === '*') {\n // '*' matches exactly one segment\n return matchSegments(path, pi + 1, pattern, qi + 1);\n }\n\n // Literal match\n if (path[pi] === seg) {\n return matchSegments(path, pi + 1, pattern, qi + 1);\n }\n\n return false;\n}\n","import { RegistryViolationError, RegistryScopeError, ValidationError } from './errors.js';\nimport { compileSchema } from './json-schema.js';\nimport { validateMigrationChain } from './migration.js';\nimport { matchScopeAny } from './scope.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport type { GraphRegistry, RegistryEntry, DiscoveryResult } from './types.js';\n\nfunction tripleKey(aType: string, axbType: string, bType: string): string {\n return `${aType}:${axbType}:${bType}`;\n}\n\nfunction tripleKeyFor(e: RegistryEntry): string {\n return tripleKey(e.aType, e.axbType, e.bType);\n}\n\n/**\n * Build a registry from either explicit entries or a DiscoveryResult.\n *\n * @example\n * ```ts\n * // From explicit entries (programmatic)\n * const registry = createRegistry([\n * { aType: 'user', axbType: 'is', bType: 'user', jsonSchema: userSchema },\n * { aType: 'user', axbType: 'follows', bType: 'user', jsonSchema: followsSchema },\n * ]);\n *\n * // From discovery result (folder convention)\n * const discovered = await discoverEntities('./entities');\n * const registry = createRegistry(discovered);\n * ```\n */\nexport function createRegistry(\n input: RegistryEntry[] | DiscoveryResult,\n): GraphRegistry {\n const map = new Map<string, { entry: RegistryEntry; validate?: (data: unknown) => void }>();\n\n let entries: RegistryEntry[];\n\n if (Array.isArray(input)) {\n entries = input;\n } else {\n entries = discoveryToEntries(input);\n }\n\n const entryList: ReadonlyArray<RegistryEntry> = Object.freeze([...entries]);\n\n for (const entry of entries) {\n if (entry.targetGraph && entry.targetGraph.includes('/')) {\n throw new ValidationError(\n `Entry (${entry.aType}) -[${entry.axbType}]-> (${entry.bType}) has invalid targetGraph \"${entry.targetGraph}\" — must be a single segment (no \"/\")`,\n );\n }\n if (entry.migrations?.length) {\n const label = `Entry (${entry.aType}) -[${entry.axbType}]-> (${entry.bType})`;\n validateMigrationChain(entry.migrations, label);\n // Derive schemaVersion from migrations — single source of truth\n entry.schemaVersion = Math.max(...entry.migrations.map((m) => m.toVersion));\n } else {\n // No migrations → no versioning (ignore any user-supplied schemaVersion)\n entry.schemaVersion = undefined;\n }\n const key = tripleKey(entry.aType, entry.axbType, entry.bType);\n const validator = entry.jsonSchema\n ? compileSchema(entry.jsonSchema, `(${entry.aType}) -[${entry.axbType}]-> (${entry.bType})`)\n : undefined;\n map.set(key, { entry, validate: validator });\n }\n\n // Build axbType index for lookupByAxbType\n const axbIndex = new Map<string, ReadonlyArray<RegistryEntry>>();\n const axbBuild = new Map<string, RegistryEntry[]>();\n for (const entry of entries) {\n const existing = axbBuild.get(entry.axbType);\n if (existing) {\n existing.push(entry);\n } else {\n axbBuild.set(entry.axbType, [entry]);\n }\n }\n for (const [key, arr] of axbBuild) {\n axbIndex.set(key, Object.freeze(arr));\n }\n\n return {\n lookup(aType: string, axbType: string, bType: string): RegistryEntry | undefined {\n return map.get(tripleKey(aType, axbType, bType))?.entry;\n },\n\n lookupByAxbType(axbType: string): ReadonlyArray<RegistryEntry> {\n return axbIndex.get(axbType) ?? [];\n },\n\n validate(aType: string, axbType: string, bType: string, data: unknown, scopePath?: string): void {\n const rec = map.get(tripleKey(aType, axbType, bType));\n\n if (!rec) {\n throw new RegistryViolationError(aType, axbType, bType);\n }\n\n // Scope validation: check allowedIn patterns when a scope context is provided\n if (scopePath !== undefined && rec.entry.allowedIn && rec.entry.allowedIn.length > 0) {\n if (!matchScopeAny(scopePath, rec.entry.allowedIn)) {\n throw new RegistryScopeError(aType, axbType, bType, scopePath, rec.entry.allowedIn);\n }\n }\n\n if (rec.validate) {\n try {\n rec.validate(data);\n } catch (err: unknown) {\n if (err instanceof ValidationError) throw err;\n throw new ValidationError(\n `Data validation failed for (${aType}) -[${axbType}]-> (${bType})`,\n err,\n );\n }\n }\n },\n\n entries(): ReadonlyArray<RegistryEntry> {\n return entryList;\n },\n };\n}\n\n/**\n * Create a merged registry where `base` entries take priority and `extension`\n * entries fill in gaps. Lookups and validation check `base` first; only if the\n * triple is not found there does the merged registry fall through to\n * `extension`.\n *\n * The `entries()` method returns a deduplicated list (base wins on collision).\n * The `lookupByAxbType()` method merges results from both registries,\n * deduplicating by triple key with base entries winning.\n */\nexport function createMergedRegistry(\n base: GraphRegistry,\n extension: GraphRegistry,\n): GraphRegistry {\n // Build a set of triple keys from the base registry for fast collision checks.\n const baseKeys = new Set(base.entries().map(tripleKeyFor));\n\n return {\n lookup(aType: string, axbType: string, bType: string): RegistryEntry | undefined {\n return base.lookup(aType, axbType, bType) ?? extension.lookup(aType, axbType, bType);\n },\n\n lookupByAxbType(axbType: string): ReadonlyArray<RegistryEntry> {\n const baseResults = base.lookupByAxbType(axbType);\n const extResults = extension.lookupByAxbType(axbType);\n if (extResults.length === 0) return baseResults;\n if (baseResults.length === 0) return extResults;\n\n // Merge, base wins on triple-key collision\n const seen = new Set(baseResults.map(tripleKeyFor));\n const merged = [...baseResults];\n for (const entry of extResults) {\n if (!seen.has(tripleKeyFor(entry))) {\n merged.push(entry);\n }\n }\n return Object.freeze(merged);\n },\n\n validate(aType: string, axbType: string, bType: string, data: unknown, scopePath?: string): void {\n if (baseKeys.has(tripleKey(aType, axbType, bType))) {\n return base.validate(aType, axbType, bType, data, scopePath);\n }\n // Falls through to extension (which throws RegistryViolationError if not found)\n return extension.validate(aType, axbType, bType, data, scopePath);\n },\n\n entries(): ReadonlyArray<RegistryEntry> {\n const extEntries = extension.entries();\n if (extEntries.length === 0) return base.entries();\n\n const merged = [...base.entries()];\n for (const entry of extEntries) {\n if (!baseKeys.has(tripleKeyFor(entry))) {\n merged.push(entry);\n }\n }\n return Object.freeze(merged);\n },\n };\n}\n\n/**\n * Convert a DiscoveryResult into flat RegistryEntry[].\n * Nodes become self-loop triples `(name, 'is', name)`.\n * Edges expand `from`/`to` arrays into one triple per combination.\n */\nfunction discoveryToEntries(discovery: DiscoveryResult): RegistryEntry[] {\n const entries: RegistryEntry[] = [];\n\n // Nodes → self-loop triples\n for (const [name, entity] of discovery.nodes) {\n entries.push({\n aType: name,\n axbType: NODE_RELATION,\n bType: name,\n jsonSchema: entity.schema,\n description: entity.description,\n titleField: entity.titleField,\n subtitleField: entity.subtitleField,\n allowedIn: entity.allowedIn,\n migrations: entity.migrations,\n migrationWriteBack: entity.migrationWriteBack,\n });\n }\n\n // Edges → expand from/to into one triple per combination\n for (const [axbType, entity] of discovery.edges) {\n const topology = entity.topology;\n if (!topology) continue;\n\n const fromTypes = Array.isArray(topology.from) ? topology.from : [topology.from];\n const toTypes = Array.isArray(topology.to) ? topology.to : [topology.to];\n\n const resolvedTargetGraph = entity.targetGraph ?? topology.targetGraph;\n if (resolvedTargetGraph && resolvedTargetGraph.includes('/')) {\n throw new ValidationError(\n `Edge \"${axbType}\" has invalid targetGraph \"${resolvedTargetGraph}\" — must be a single segment (no \"/\")`,\n );\n }\n\n for (const aType of fromTypes) {\n for (const bType of toTypes) {\n entries.push({\n aType,\n axbType,\n bType,\n jsonSchema: entity.schema,\n description: entity.description,\n inverseLabel: topology.inverseLabel,\n titleField: entity.titleField,\n subtitleField: entity.subtitleField,\n allowedIn: entity.allowedIn,\n targetGraph: resolvedTargetGraph,\n migrations: entity.migrations,\n migrationWriteBack: entity.migrationWriteBack,\n });\n }\n }\n }\n\n return entries;\n}\n","/**\n * Sandbox module for compiling dynamic registry migration source strings\n * into executable functions.\n *\n * Uses a dedicated worker thread with SES (Secure ECMAScript) Compartments\n * for isolation. SES `lockdown()` and `Compartment` evaluation run in the\n * worker thread so that the host process's intrinsics remain unaffected.\n *\n * Each migration function runs in a hardened compartment with no ambient\n * authority — no access to `process`, `require`, `fetch`, `setTimeout`,\n * or any other host-provided globals. Data crosses the compartment boundary\n * as JSON strings to prevent prototype chain escapes.\n *\n * Static registry migrations are already in-memory functions and never\n * go through this module.\n */\n\nimport { createHash } from 'node:crypto';\nimport type { Worker } from 'node:worker_threads';\n\nimport { MigrationError } from './errors.js';\nimport type * as SerializationModule from './serialization.js';\nimport type {\n MigrationExecutor,\n MigrationFn,\n MigrationStep,\n StoredMigrationStep,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Sandbox worker — SES lockdown and Compartment evaluation run in a\n// dedicated worker thread so that lockdown() does not affect the host\n// process's intrinsics. The worker is spawned lazily on first use.\n// ---------------------------------------------------------------------------\n\nlet _worker: Worker | null = null;\nlet _requestId = 0;\nconst _pending = new Map<\n number,\n {\n resolve: (value: unknown) => void;\n reject: (reason: Error) => void;\n }\n>();\n\n/**\n * Inline worker source evaluated as CJS in a dedicated worker thread.\n * Contains all SES setup, compilation, and execution logic.\n *\n * **Why inline?** Using `new Worker(code, { eval: true })` avoids\n * ESM/CJS file resolution issues when the library is consumed from\n * different module formats or bundlers.\n */\nconst WORKER_SOURCE = [\n `'use strict';`,\n `var _wt = require('node:worker_threads');`,\n `var _mod = require('node:module');`,\n `var _crypto = require('node:crypto');`,\n `var parentPort = _wt.parentPort;`,\n `var workerData = _wt.workerData;`,\n ``,\n `// Load SES using the parent module's resolution context`,\n `var esmRequire = _mod.createRequire(workerData.parentUrl);`,\n `esmRequire('ses');`,\n ``,\n `lockdown({`,\n ` errorTaming: 'unsafe',`,\n ` consoleTaming: 'unsafe',`,\n ` evalTaming: 'safe-eval',`,\n ` overrideTaming: 'moderate',`,\n ` stackFiltering: 'verbose'`,\n `});`,\n ``,\n `// Defense-in-depth: verify lockdown() actually hardened JSON.`,\n `if (!Object.isFrozen(JSON)) {`,\n ` throw new Error('SES lockdown failed: JSON is not frozen');`,\n `}`,\n ``,\n `var cache = new Map();`,\n ``,\n `function hashSource(s) {`,\n ` return _crypto.createHash('sha256').update(s).digest('hex');`,\n `}`,\n ``,\n `function buildWrapper(source) {`,\n ` return '(function() {' +`,\n ` ' var fn = (' + source + ');\\\\n' +`,\n ` ' if (typeof fn !== \"function\") return null;\\\\n' +`,\n ` ' return function(jsonIn) {\\\\n' +`,\n ` ' var data = JSON.parse(jsonIn);\\\\n' +`,\n ` ' var result = fn(data);\\\\n' +`,\n ` ' if (result !== null && typeof result === \"object\" && typeof result.then === \"function\") {\\\\n' +`,\n ` ' return result.then(function(r) { return JSON.stringify(r); });\\\\n' +`,\n ` ' }\\\\n' +`,\n ` ' return JSON.stringify(result);\\\\n' +`,\n ` ' };\\\\n' +`,\n ` '})()';`,\n `}`,\n ``,\n `function compileSource(source) {`,\n ` var key = hashSource(source);`,\n ` var cached = cache.get(key);`,\n ` if (cached) return cached;`,\n ``,\n ` var compartmentFn;`,\n ` try {`,\n ` var c = new Compartment({ JSON: JSON });`,\n ` compartmentFn = c.evaluate(buildWrapper(source));`,\n ` } catch (err) {`,\n ` throw new Error('Failed to compile migration source: ' + (err.message || String(err)));`,\n ` }`,\n ``,\n ` if (typeof compartmentFn !== 'function') {`,\n ` throw new Error('Migration source did not produce a function: ' + source.slice(0, 80));`,\n ` }`,\n ``,\n ` cache.set(key, compartmentFn);`,\n ` return compartmentFn;`,\n `}`,\n ``,\n `parentPort.on('message', function(msg) {`,\n ` var id = msg.id;`,\n ` try {`,\n ` if (msg.type === 'compile') {`,\n ` compileSource(msg.source);`,\n ` parentPort.postMessage({ id: id, type: 'compiled' });`,\n ` return;`,\n ` }`,\n ` if (msg.type === 'execute') {`,\n ` var fn = compileSource(msg.source);`,\n ` var raw;`,\n ` try {`,\n ` raw = fn(msg.jsonData);`,\n ` } catch (err) {`,\n ` parentPort.postMessage({ id: id, type: 'error', message: 'Migration function threw: ' + (err.message || String(err)) });`,\n ` return;`,\n ` }`,\n ` if (raw !== null && typeof raw === 'object' && typeof raw.then === 'function') {`,\n ` raw.then(`,\n ` function(jsonResult) {`,\n ` if (jsonResult === undefined || jsonResult === null) {`,\n ` parentPort.postMessage({ id: id, type: 'error', message: 'Migration returned a non-JSON-serializable value' });`,\n ` } else {`,\n ` parentPort.postMessage({ id: id, type: 'result', jsonResult: jsonResult });`,\n ` }`,\n ` },`,\n ` function(err) {`,\n ` parentPort.postMessage({ id: id, type: 'error', message: 'Async migration function threw: ' + (err.message || String(err)) });`,\n ` }`,\n ` );`,\n ` return;`,\n ` }`,\n ` if (raw === undefined || raw === null) {`,\n ` parentPort.postMessage({ id: id, type: 'error', message: 'Migration returned a non-JSON-serializable value' });`,\n ` } else {`,\n ` parentPort.postMessage({ id: id, type: 'result', jsonResult: raw });`,\n ` }`,\n ` }`,\n ` } catch (err) {`,\n ` parentPort.postMessage({ id: id, type: 'error', message: err.message || String(err) });`,\n ` }`,\n `});`,\n].join('\\n');\n\n// ---------------------------------------------------------------------------\n// Worker lifecycle management\n// ---------------------------------------------------------------------------\n\ninterface WorkerResponse {\n id: number;\n type: string;\n message?: string;\n jsonResult?: string;\n}\n\n// `node:worker_threads` is loaded lazily so this module can be imported in\n// runtimes without it (Cloudflare Workers, browsers). Only callers that\n// actually exercise the default migration sandbox will trigger the import.\nlet _WorkerCtor: (new (source: string, opts: Record<string, unknown>) => Worker) | null = null;\n\nasync function loadWorkerCtor(): Promise<NonNullable<typeof _WorkerCtor>> {\n if (_WorkerCtor) return _WorkerCtor;\n const wt = await import('node:worker_threads');\n _WorkerCtor = wt.Worker as unknown as NonNullable<typeof _WorkerCtor>;\n return _WorkerCtor;\n}\n\nasync function ensureWorker(): Promise<Worker> {\n if (_worker) return _worker;\n\n const Ctor = await loadWorkerCtor();\n _worker = new Ctor(WORKER_SOURCE, {\n eval: true,\n workerData: { parentUrl: import.meta.url },\n });\n\n // Don't let the worker prevent process exit\n _worker.unref();\n\n _worker.on('message', (msg: WorkerResponse) => {\n if (msg.id === undefined) return;\n const pending = _pending.get(msg.id);\n if (!pending) return;\n _pending.delete(msg.id);\n\n if (msg.type === 'error') {\n pending.reject(new MigrationError(msg.message ?? 'Unknown sandbox error'));\n } else {\n pending.resolve(msg);\n }\n });\n\n _worker.on('error', (err: Error) => {\n // Worker crashed — reject all pending requests and allow respawn\n for (const [, p] of _pending) {\n p.reject(new MigrationError(`Sandbox worker error: ${err.message}`));\n }\n _pending.clear();\n _worker = null;\n });\n\n _worker.on('exit', (code: number) => {\n // Always reject pending requests — a worker exiting while requests\n // are in-flight is always an error from the caller's perspective,\n // even if the exit code is 0 (e.g., graceful termination).\n if (_pending.size > 0) {\n for (const [, p] of _pending) {\n p.reject(new MigrationError(`Sandbox worker exited with code ${code}`));\n }\n _pending.clear();\n }\n _worker = null;\n });\n\n return _worker;\n}\n\nasync function sendToWorker(msg: Record<string, unknown>): Promise<WorkerResponse> {\n const worker = await ensureWorker();\n if (_requestId >= Number.MAX_SAFE_INTEGER) _requestId = 0;\n const id = ++_requestId;\n return new Promise<WorkerResponse>((resolve, reject) => {\n _pending.set(id, { resolve: resolve as (v: unknown) => void, reject });\n worker.postMessage({ ...msg, id });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Compiled function cache (keyed by executor → SHA-256 hash of source string)\n// ---------------------------------------------------------------------------\n\n// Two-level cache: outer key is the executor reference (WeakMap so that\n// short-lived executors and their caches can be garbage collected), inner\n// key is the SHA-256 hash of the source string. This prevents cache\n// poisoning when different clients use different sandbox executors in\n// the same process.\nconst compiledCache = new WeakMap<MigrationExecutor, Map<string, MigrationFn>>();\n\nfunction getExecutorCache(executor: MigrationExecutor): Map<string, MigrationFn> {\n let cache = compiledCache.get(executor);\n if (!cache) {\n cache = new Map();\n compiledCache.set(executor, cache);\n }\n return cache;\n}\n\nfunction hashSource(source: string): string {\n return createHash('sha256').update(source).digest('hex');\n}\n\n// ---------------------------------------------------------------------------\n// Lazy serialization loader. Pulls `@google-cloud/firestore` only when the\n// default executor actually runs a migration — keeps Firestore out of\n// SQLite-only bundles (D1, DO-SQLite).\n// ---------------------------------------------------------------------------\n\nlet _serializationModule: typeof SerializationModule | null = null;\n\nasync function loadSerialization(): Promise<typeof SerializationModule> {\n if (_serializationModule) return _serializationModule;\n _serializationModule = await import('./serialization.js');\n return _serializationModule;\n}\n\n// ---------------------------------------------------------------------------\n// Default executor\n// ---------------------------------------------------------------------------\n\n/**\n * Default executor using a worker-thread SES Compartment with JSON marshaling.\n *\n * Migration source is compiled and executed inside an isolated SES\n * Compartment running in a dedicated worker thread. The worker calls\n * `lockdown()` in its own V8 isolate, leaving the host process's\n * intrinsics completely unaffected.\n *\n * Data crosses the compartment boundary as JSON strings, preventing\n * prototype chain escapes. The compartment receives only `JSON` as an\n * endowment for parsing/stringifying data.\n *\n * The returned `MigrationFn` always returns a `Promise` (communication\n * with the worker is inherently async via `postMessage`).\n */\nexport function defaultExecutor(source: string): MigrationFn {\n // Worker is spawned lazily on first execution via `sendToWorker`.\n // Eager spawning here would force a top-level `node:worker_threads`\n // load and break Cloudflare Workers / browser callers that never\n // exercise the default sandbox.\n\n // Return a MigrationFn that delegates to the worker thread.\n // Compilation + execution happen in the worker's SES Compartment.\n return (async (data: Record<string, unknown>) => {\n const { serializeFirestoreTypes, deserializeFirestoreTypes } = await loadSerialization();\n const jsonData = JSON.stringify(serializeFirestoreTypes(data));\n const response = await sendToWorker({ type: 'execute', source, jsonData });\n if (response.jsonResult === undefined || response.jsonResult === null) {\n throw new MigrationError('Migration returned a non-JSON-serializable value');\n }\n try {\n return deserializeFirestoreTypes(JSON.parse(response.jsonResult));\n } catch {\n throw new MigrationError('Migration returned a non-JSON-serializable value');\n }\n }) as MigrationFn;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Eagerly validate a migration source string by compiling it in the\n * sandbox worker (or via a custom executor) without executing it.\n *\n * Use this to catch syntax errors at define-time or reload-time rather\n * than at first migration execution.\n *\n * @throws {MigrationError} If the source is syntactically invalid or\n * does not produce a function.\n */\nexport async function precompileSource(\n source: string,\n executor?: MigrationExecutor,\n): Promise<void> {\n if (executor && executor !== defaultExecutor) {\n // Custom executors validate synchronously the old way\n try {\n executor(source);\n } catch (err: unknown) {\n if (err instanceof MigrationError) throw err;\n throw new MigrationError(`Failed to compile migration source: ${(err as Error).message}`);\n }\n return;\n }\n\n // Default executor: send a compile-only message to the worker\n await sendToWorker({ type: 'compile', source });\n}\n\n/**\n * Compile a stored migration source string into an executable function.\n * Results are cached by SHA-256 hash of the source string so repeated\n * reads never re-parse the same migration.\n *\n * **Important:** When using the default executor, this function does NOT\n * validate the source synchronously — validation is deferred to the\n * worker thread at execution time. Callers that need eager validation\n * (e.g., `defineNodeType`, `reloadRegistry`) should call\n * `precompileSource()` before or alongside `compileMigrationFn()`.\n */\nexport function compileMigrationFn(\n source: string,\n executor: MigrationExecutor = defaultExecutor,\n): MigrationFn {\n const cache = getExecutorCache(executor);\n const key = hashSource(source);\n const cached = cache.get(key);\n if (cached) return cached;\n\n try {\n const fn = executor(source);\n cache.set(key, fn);\n return fn;\n } catch (err: unknown) {\n if (err instanceof MigrationError) throw err;\n throw new MigrationError(`Failed to compile migration source: ${(err as Error).message}`);\n }\n}\n\n/**\n * Batch compile stored migration steps into executable MigrationStep[].\n *\n * With the default executor, source validation is deferred to execution\n * time. Use `precompileSource()` to validate eagerly — see\n * `createRegistryFromGraph()` for the recommended pattern.\n */\nexport function compileMigrations(\n stored: StoredMigrationStep[],\n executor?: MigrationExecutor,\n): MigrationStep[] {\n return stored.map((step) => ({\n fromVersion: step.fromVersion,\n toVersion: step.toVersion,\n up: compileMigrationFn(step.up, executor),\n }));\n}\n\n/**\n * Terminate the sandbox worker thread. The worker will be respawned\n * on the next `defaultExecutor` call.\n *\n * Primarily useful for test cleanup to avoid vitest hanging on\n * unfinished worker threads.\n */\nexport async function destroySandboxWorker(): Promise<void> {\n if (!_worker) return;\n const w = _worker;\n _worker = null;\n // Reject any remaining pending requests\n for (const [, p] of _pending) {\n p.reject(new MigrationError('Sandbox worker terminated'));\n }\n _pending.clear();\n await w.terminate();\n}\n","import { NODE_RELATION, DEFAULT_QUERY_LIMIT, BUILTIN_FIELDS } from './internal/constants.js';\nimport { computeEdgeDocId } from './docid.js';\nimport { InvalidQueryError } from './errors.js';\nimport type { FindEdgesParams, FindNodesParams, QueryPlan, QueryFilter } from './types.js';\n\nexport function buildEdgeQueryPlan(params: FindEdgesParams): QueryPlan {\n const { aType, aUid, axbType, bType, bUid, limit, orderBy } = params;\n\n if (aUid && axbType && bUid && !params.where?.length) {\n return { strategy: 'get', docId: computeEdgeDocId(aUid, axbType, bUid) };\n }\n\n const filters: QueryFilter[] = [];\n\n if (aType) filters.push({ field: 'aType', op: '==', value: aType });\n if (aUid) filters.push({ field: 'aUid', op: '==', value: aUid });\n if (axbType) filters.push({ field: 'axbType', op: '==', value: axbType });\n if (bType) filters.push({ field: 'bType', op: '==', value: bType });\n if (bUid) filters.push({ field: 'bUid', op: '==', value: bUid });\n\n if (params.where) {\n for (const clause of params.where) {\n const field = BUILTIN_FIELDS.has(clause.field) ? clause.field\n : clause.field.startsWith('data.') ? clause.field : `data.${clause.field}`;\n filters.push({ field, op: clause.op, value: clause.value });\n }\n }\n\n if (filters.length === 0) {\n throw new InvalidQueryError('findEdges requires at least one filter parameter');\n }\n\n // limit: undefined → apply DEFAULT_QUERY_LIMIT\n // limit: 0 → no limit (unlimited, used by internal bulk operations)\n // limit: N → use N\n const effectiveLimit = limit === undefined ? DEFAULT_QUERY_LIMIT : (limit || undefined);\n return { strategy: 'query', filters, options: { limit: effectiveLimit, orderBy } };\n}\n\nexport function buildNodeQueryPlan(params: FindNodesParams): QueryPlan {\n const { aType, limit, orderBy } = params;\n\n const filters: QueryFilter[] = [\n { field: 'aType', op: '==', value: aType },\n { field: 'axbType', op: '==', value: NODE_RELATION },\n ];\n\n if (params.where) {\n for (const clause of params.where) {\n const field = BUILTIN_FIELDS.has(clause.field) ? clause.field\n : clause.field.startsWith('data.') ? clause.field : `data.${clause.field}`;\n filters.push({ field, op: clause.op, value: clause.value });\n }\n }\n\n const effectiveLimit = limit === undefined ? DEFAULT_QUERY_LIMIT : (limit || undefined);\n return { strategy: 'query', filters, options: { limit: effectiveLimit, orderBy } };\n}\n","import { BUILTIN_FIELDS } from './internal/constants.js';\nimport type { QueryFilter } from './types.js';\n\n/**\n * Result of analyzing a query for collection scan risk.\n */\nexport interface QuerySafetyResult {\n /** Whether the query matches a known indexed pattern. */\n safe: boolean;\n /** Human-readable explanation when the query is unsafe. */\n reason?: string;\n}\n\n/**\n * Known composite index patterns that prevent full collection scans.\n * Each pattern is a set of field names that must ALL be present in the\n * query filters. Order within the set doesn't matter — what matters is\n * that the Firestore composite index covers the combination.\n *\n * These correspond to the indexes in firestore.indexes.json:\n * (aUid, axbType) — forward edge lookup\n * (axbType, bUid) — reverse edge lookup\n * (aType, axbType) — type-scoped queries + findNodes\n * (axbType, bType) — edge type + target type\n */\nconst SAFE_INDEX_PATTERNS: ReadonlyArray<ReadonlySet<string>> = [\n new Set(['aUid', 'axbType']),\n new Set(['axbType', 'bUid']),\n new Set(['aType', 'axbType']),\n new Set(['axbType', 'bType']),\n];\n\n/**\n * Analyzes a set of query filters to determine whether the query would\n * likely cause a full collection scan on Firestore Enterprise.\n *\n * A query is considered \"safe\" if the builtin fields present in the filters\n * match at least one known composite index pattern. Queries that only use\n * `data.*` fields without a safe base pattern are flagged as unsafe.\n */\nexport function analyzeQuerySafety(filters: QueryFilter[]): QuerySafetyResult {\n // Extract the set of builtin fields being filtered on (equality checks are\n // the primary index-usable operations, but we're generous here and count\n // any filter on a builtin field as potentially index-backed).\n const builtinFieldsPresent = new Set<string>();\n let hasDataFilters = false;\n\n for (const f of filters) {\n if (BUILTIN_FIELDS.has(f.field)) {\n builtinFieldsPresent.add(f.field);\n } else {\n // data.* or other non-builtin fields\n hasDataFilters = true;\n }\n }\n\n // Check if the builtin fields match any known safe index pattern.\n // A pattern is \"matched\" if all fields in the pattern are present in the query.\n for (const pattern of SAFE_INDEX_PATTERNS) {\n let matched = true;\n for (const field of pattern) {\n if (!builtinFieldsPresent.has(field)) {\n matched = false;\n break;\n }\n }\n if (matched) {\n // Even with data.* filters, the base index narrows the scan significantly.\n // The data.* filters are applied as post-filters on the index results.\n return { safe: true };\n }\n }\n\n // No safe pattern matched — build an explanation.\n const presentFields = [...builtinFieldsPresent];\n if (presentFields.length === 0 && hasDataFilters) {\n return {\n safe: false,\n reason:\n 'Query filters only use data.* fields with no builtin field constraints. ' +\n 'This requires a full collection scan. Add aType, aUid, axbType, bType, or bUid filters, ' +\n 'or set allowCollectionScan: true.',\n };\n }\n\n if (hasDataFilters) {\n return {\n safe: false,\n reason:\n `Query filters on [${presentFields.join(', ')}] do not match any indexed pattern. ` +\n 'data.* filters without an indexed base require a full collection scan. ' +\n `Safe patterns: (aUid + axbType), (axbType + bUid), (aType + axbType), (axbType + bType). ` +\n 'Set allowCollectionScan: true to override.',\n };\n }\n\n return {\n safe: false,\n reason:\n `Query filters on [${presentFields.join(', ')}] do not match any indexed pattern. ` +\n 'This may cause a full collection scan on Firestore Enterprise. ' +\n `Safe patterns: (aUid + axbType), (axbType + bUid), (aType + axbType), (axbType + bType). ` +\n 'Set allowCollectionScan: true to override.',\n };\n}\n","import { computeEdgeDocId, computeNodeDocId } from './docid.js';\nimport { QuerySafetyError } from './errors.js';\nimport type { TransactionBackend, WritableRecord } from './internal/backend.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport { migrateRecord, migrateRecords } from './migration.js';\nimport { buildEdgeQueryPlan, buildNodeQueryPlan } from './query.js';\nimport { analyzeQuerySafety } from './query-safety.js';\nimport type {\n FindEdgesParams,\n FindNodesParams,\n GraphRegistry,\n GraphTransaction,\n MigrationWriteBack,\n QueryFilter,\n ScanProtection,\n StoredGraphRecord,\n} from './types.js';\n\nfunction buildWritableNodeRecord(\n aType: string,\n uid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid: uid, axbType: NODE_RELATION, bType: aType, bUid: uid, data };\n}\n\nfunction buildWritableEdgeRecord(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid, axbType, bType, bUid, data };\n}\n\nexport class GraphTransactionImpl implements GraphTransaction {\n constructor(\n private readonly backend: TransactionBackend,\n private readonly registry?: GraphRegistry,\n private readonly scanProtection: ScanProtection = 'error',\n private readonly scopePath: string = '',\n private readonly globalWriteBack: MigrationWriteBack = 'off',\n ) {}\n\n async getNode(uid: string): Promise<StoredGraphRecord | null> {\n const docId = computeNodeDocId(uid);\n const record = await this.backend.getDoc(docId);\n if (!record || !this.registry) return record;\n const result = await migrateRecord(record, this.registry, this.globalWriteBack);\n if (result.migrated && result.writeBack !== 'off') {\n await this.backend.updateDoc(docId, {\n replaceData: result.record.data as Record<string, unknown>,\n v: result.record.v,\n });\n }\n return result.record;\n }\n\n async getEdge(aUid: string, axbType: string, bUid: string): Promise<StoredGraphRecord | null> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = await this.backend.getDoc(docId);\n if (!record || !this.registry) return record;\n const result = await migrateRecord(record, this.registry, this.globalWriteBack);\n if (result.migrated && result.writeBack !== 'off') {\n await this.backend.updateDoc(docId, {\n replaceData: result.record.data as Record<string, unknown>,\n v: result.record.v,\n });\n }\n return result.record;\n }\n\n async edgeExists(aUid: string, axbType: string, bUid: string): Promise<boolean> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = await this.backend.getDoc(docId);\n return record !== null;\n }\n\n private checkQuerySafety(filters: QueryFilter[], allowCollectionScan?: boolean): void {\n if (allowCollectionScan || this.scanProtection === 'off') return;\n\n const result = analyzeQuerySafety(filters);\n if (result.safe) return;\n\n if (this.scanProtection === 'error') {\n throw new QuerySafetyError(result.reason!);\n }\n\n console.warn(`[firegraph] Query safety warning: ${result.reason}`);\n }\n\n async findEdges(params: FindEdgesParams): Promise<StoredGraphRecord[]> {\n const plan = buildEdgeQueryPlan(params);\n let records: StoredGraphRecord[];\n if (plan.strategy === 'get') {\n const record = await this.backend.getDoc(plan.docId);\n records = record ? [record] : [];\n } else {\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n records = await this.backend.query(plan.filters, plan.options);\n }\n return this.applyMigrations(records);\n }\n\n async findNodes(params: FindNodesParams): Promise<StoredGraphRecord[]> {\n const plan = buildNodeQueryPlan(params);\n let records: StoredGraphRecord[];\n if (plan.strategy === 'get') {\n const record = await this.backend.getDoc(plan.docId);\n records = record ? [record] : [];\n } else {\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n records = await this.backend.query(plan.filters, plan.options);\n }\n return this.applyMigrations(records);\n }\n\n private async applyMigrations(records: StoredGraphRecord[]): Promise<StoredGraphRecord[]> {\n if (!this.registry || records.length === 0) return records;\n const results = await migrateRecords(records, this.registry, this.globalWriteBack);\n for (const result of results) {\n if (result.migrated && result.writeBack !== 'off') {\n const docId =\n result.record.axbType === NODE_RELATION\n ? computeNodeDocId(result.record.aUid)\n : computeEdgeDocId(result.record.aUid, result.record.axbType, result.record.bUid);\n await this.backend.updateDoc(docId, {\n replaceData: result.record.data as Record<string, unknown>,\n v: result.record.v,\n });\n }\n }\n return results.map((r) => r.record);\n }\n\n async putNode(aType: string, uid: string, data: Record<string, unknown>): Promise<void> {\n if (this.registry) {\n this.registry.validate(aType, NODE_RELATION, aType, data, this.scopePath);\n }\n const docId = computeNodeDocId(uid);\n const record = buildWritableNodeRecord(aType, uid, data);\n if (this.registry) {\n const entry = this.registry.lookup(aType, NODE_RELATION, aType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n await this.backend.setDoc(docId, record);\n }\n\n async putEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n if (this.registry) {\n this.registry.validate(aType, axbType, bType, data, this.scopePath);\n }\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = buildWritableEdgeRecord(aType, aUid, axbType, bType, bUid, data);\n if (this.registry) {\n const entry = this.registry.lookup(aType, axbType, bType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n await this.backend.setDoc(docId, record);\n }\n\n async updateNode(uid: string, data: Record<string, unknown>): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.updateDoc(docId, { dataFields: data });\n }\n\n async removeNode(uid: string): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.deleteDoc(docId);\n }\n\n async removeEdge(aUid: string, axbType: string, bUid: string): Promise<void> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n await this.backend.deleteDoc(docId);\n }\n}\n","import { GraphBatchImpl } from './batch.js';\nimport { computeEdgeDocId, computeNodeDocId } from './docid.js';\nimport {\n createBootstrapRegistry,\n createRegistryFromGraph,\n generateDeterministicUid,\n META_EDGE_TYPE,\n META_NODE_TYPE,\n} from './dynamic-registry.js';\nimport { DynamicRegistryError, FiregraphError, QuerySafetyError } from './errors.js';\nimport type { StorageBackend, WritableRecord } from './internal/backend.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport type { MigrationResult } from './migration.js';\nimport { migrateRecord, migrateRecords } from './migration.js';\nimport { buildEdgeQueryPlan, buildNodeQueryPlan } from './query.js';\nimport { analyzeQuerySafety } from './query-safety.js';\nimport { createMergedRegistry } from './registry.js';\nimport { precompileSource } from './sandbox.js';\nimport { GraphTransactionImpl } from './transaction.js';\nimport type {\n BulkOptions,\n BulkResult,\n CascadeResult,\n DefineTypeOptions,\n DynamicGraphClient,\n DynamicRegistryConfig,\n EdgeTopology,\n FindEdgesParams,\n FindNodesParams,\n GraphBatch,\n GraphClient,\n GraphClientOptions,\n GraphReader,\n GraphRegistry,\n GraphTransaction,\n MigrationExecutor,\n MigrationFn,\n MigrationWriteBack,\n QueryFilter,\n QueryOptions,\n ScanProtection,\n StoredGraphRecord,\n} from './types.js';\n\nconst RESERVED_TYPE_NAMES = new Set([META_NODE_TYPE, META_EDGE_TYPE]);\n\nfunction buildWritableNodeRecord(\n aType: string,\n uid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid: uid, axbType: NODE_RELATION, bType: aType, bUid: uid, data };\n}\n\nfunction buildWritableEdgeRecord(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid, axbType, bType, bUid, data };\n}\n\nexport class GraphClientImpl implements DynamicGraphClient {\n readonly scanProtection: ScanProtection;\n\n // Static mode\n private readonly staticRegistry?: GraphRegistry;\n\n // Dynamic mode\n private readonly dynamicConfig?: DynamicRegistryConfig;\n private readonly bootstrapRegistry?: GraphRegistry;\n private dynamicRegistry?: GraphRegistry;\n private readonly metaBackend?: StorageBackend;\n\n // Migration settings\n private readonly globalWriteBack: MigrationWriteBack;\n private readonly migrationSandbox?: MigrationExecutor;\n\n constructor(\n private readonly backend: StorageBackend,\n options?: GraphClientOptions,\n /** @internal Optional pre-built meta-backend (used by subgraph clones). */\n metaBackend?: StorageBackend,\n ) {\n this.globalWriteBack = options?.migrationWriteBack ?? 'off';\n this.migrationSandbox = options?.migrationSandbox;\n\n if (options?.registryMode) {\n this.dynamicConfig = options.registryMode;\n this.bootstrapRegistry = createBootstrapRegistry();\n if (options.registry) {\n this.staticRegistry = options.registry;\n }\n this.metaBackend = metaBackend;\n } else {\n this.staticRegistry = options?.registry;\n }\n\n this.scanProtection = options?.scanProtection ?? 'error';\n }\n\n // ---------------------------------------------------------------------------\n // Backend access (exposed for traversal helpers and subgraph cloning)\n // ---------------------------------------------------------------------------\n\n /** @internal */\n getBackend(): StorageBackend {\n return this.backend;\n }\n\n // ---------------------------------------------------------------------------\n // Registry routing\n // ---------------------------------------------------------------------------\n\n private getRegistryForType(aType: string): GraphRegistry | undefined {\n if (!this.dynamicConfig) return this.staticRegistry;\n\n if (aType === META_NODE_TYPE || aType === META_EDGE_TYPE) {\n return this.bootstrapRegistry;\n }\n\n return this.dynamicRegistry ?? this.staticRegistry ?? this.bootstrapRegistry;\n }\n\n private getBackendForType(aType: string): StorageBackend {\n if (this.metaBackend && (aType === META_NODE_TYPE || aType === META_EDGE_TYPE)) {\n return this.metaBackend;\n }\n return this.backend;\n }\n\n private getCombinedRegistry(): GraphRegistry | undefined {\n if (!this.dynamicConfig) return this.staticRegistry;\n return this.dynamicRegistry ?? this.staticRegistry ?? this.bootstrapRegistry;\n }\n\n // ---------------------------------------------------------------------------\n // Query safety\n // ---------------------------------------------------------------------------\n\n private checkQuerySafety(filters: QueryFilter[], allowCollectionScan?: boolean): void {\n if (allowCollectionScan || this.scanProtection === 'off') return;\n\n const result = analyzeQuerySafety(filters);\n if (result.safe) return;\n\n if (this.scanProtection === 'error') {\n throw new QuerySafetyError(result.reason!);\n }\n\n console.warn(`[firegraph] Query safety warning: ${result.reason}`);\n }\n\n // ---------------------------------------------------------------------------\n // Migration helpers\n // ---------------------------------------------------------------------------\n\n private async applyMigration(\n record: StoredGraphRecord,\n docId: string,\n ): Promise<StoredGraphRecord> {\n const registry = this.getCombinedRegistry();\n if (!registry) return record;\n\n const result = await migrateRecord(record, registry, this.globalWriteBack);\n if (result.migrated) {\n this.handleWriteBack(result, docId);\n }\n return result.record;\n }\n\n private async applyMigrations(records: StoredGraphRecord[]): Promise<StoredGraphRecord[]> {\n const registry = this.getCombinedRegistry();\n if (!registry || records.length === 0) return records;\n\n const results = await migrateRecords(records, registry, this.globalWriteBack);\n for (const result of results) {\n if (result.migrated) {\n const docId =\n result.record.axbType === NODE_RELATION\n ? computeNodeDocId(result.record.aUid)\n : computeEdgeDocId(result.record.aUid, result.record.axbType, result.record.bUid);\n this.handleWriteBack(result, docId);\n }\n }\n return results.map((r) => r.record);\n }\n\n /**\n * Fire-and-forget write-back for a migrated record. Both `'eager'` and\n * `'background'` are non-blocking; the difference is the log level on\n * failure. For synchronous write-back, use a transaction — see\n * `GraphTransactionImpl`.\n */\n private handleWriteBack(result: MigrationResult, docId: string): void {\n if (result.writeBack === 'off') return;\n\n const doWriteBack = async () => {\n try {\n await this.backend.updateDoc(docId, {\n replaceData: result.record.data as Record<string, unknown>,\n v: result.record.v,\n });\n } catch (err: unknown) {\n const msg = `[firegraph] Migration write-back failed for ${docId}: ${(err as Error).message}`;\n if (result.writeBack === 'eager') {\n console.error(msg);\n } else {\n console.warn(msg);\n }\n }\n };\n\n void doWriteBack();\n }\n\n // ---------------------------------------------------------------------------\n // GraphReader\n // ---------------------------------------------------------------------------\n\n async getNode(uid: string): Promise<StoredGraphRecord | null> {\n const docId = computeNodeDocId(uid);\n const record = await this.backend.getDoc(docId);\n if (!record) return null;\n return this.applyMigration(record, docId);\n }\n\n async getEdge(aUid: string, axbType: string, bUid: string): Promise<StoredGraphRecord | null> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = await this.backend.getDoc(docId);\n if (!record) return null;\n return this.applyMigration(record, docId);\n }\n\n async edgeExists(aUid: string, axbType: string, bUid: string): Promise<boolean> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = await this.backend.getDoc(docId);\n return record !== null;\n }\n\n async findEdges(params: FindEdgesParams): Promise<StoredGraphRecord[]> {\n const plan = buildEdgeQueryPlan(params);\n let records: StoredGraphRecord[];\n if (plan.strategy === 'get') {\n const record = await this.backend.getDoc(plan.docId);\n records = record ? [record] : [];\n } else {\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n records = await this.backend.query(plan.filters, plan.options);\n }\n return this.applyMigrations(records);\n }\n\n async findNodes(params: FindNodesParams): Promise<StoredGraphRecord[]> {\n const plan = buildNodeQueryPlan(params);\n let records: StoredGraphRecord[];\n if (plan.strategy === 'get') {\n const record = await this.backend.getDoc(plan.docId);\n records = record ? [record] : [];\n } else {\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n records = await this.backend.query(plan.filters, plan.options);\n }\n return this.applyMigrations(records);\n }\n\n // ---------------------------------------------------------------------------\n // GraphWriter\n // ---------------------------------------------------------------------------\n\n async putNode(aType: string, uid: string, data: Record<string, unknown>): Promise<void> {\n const registry = this.getRegistryForType(aType);\n if (registry) {\n registry.validate(aType, NODE_RELATION, aType, data, this.backend.scopePath);\n }\n const backend = this.getBackendForType(aType);\n const docId = computeNodeDocId(uid);\n const record = buildWritableNodeRecord(aType, uid, data);\n if (registry) {\n const entry = registry.lookup(aType, NODE_RELATION, aType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n await backend.setDoc(docId, record);\n }\n\n async putEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n const registry = this.getRegistryForType(aType);\n if (registry) {\n registry.validate(aType, axbType, bType, data, this.backend.scopePath);\n }\n const backend = this.getBackendForType(aType);\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = buildWritableEdgeRecord(aType, aUid, axbType, bType, bUid, data);\n if (registry) {\n const entry = registry.lookup(aType, axbType, bType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n await backend.setDoc(docId, record);\n }\n\n async updateNode(uid: string, data: Record<string, unknown>): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.updateDoc(docId, { dataFields: data });\n }\n\n async removeNode(uid: string): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.deleteDoc(docId);\n }\n\n async removeEdge(aUid: string, axbType: string, bUid: string): Promise<void> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n await this.backend.deleteDoc(docId);\n }\n\n // ---------------------------------------------------------------------------\n // Transactions & Batches\n // ---------------------------------------------------------------------------\n\n async runTransaction<T>(fn: (tx: GraphTransaction) => Promise<T>): Promise<T> {\n return this.backend.runTransaction(async (txBackend) => {\n const graphTx = new GraphTransactionImpl(\n txBackend,\n this.getCombinedRegistry(),\n this.scanProtection,\n this.backend.scopePath,\n this.globalWriteBack,\n );\n return fn(graphTx);\n });\n }\n\n batch(): GraphBatch {\n return new GraphBatchImpl(\n this.backend.createBatch(),\n this.getCombinedRegistry(),\n this.backend.scopePath,\n );\n }\n\n // ---------------------------------------------------------------------------\n // Subgraph\n // ---------------------------------------------------------------------------\n\n subgraph(parentNodeUid: string, name: string = 'graph'): GraphClient {\n if (!parentNodeUid || parentNodeUid.includes('/')) {\n throw new FiregraphError(\n `Invalid parentNodeUid for subgraph: \"${parentNodeUid}\". ` +\n 'Must be a non-empty string without \"/\".',\n 'INVALID_SUBGRAPH',\n );\n }\n if (name.includes('/')) {\n throw new FiregraphError(\n `Subgraph name must not contain \"/\": got \"${name}\". ` +\n 'Use chained .subgraph() calls for nested subgraphs.',\n 'INVALID_SUBGRAPH',\n );\n }\n\n const childBackend = this.backend.subgraph(parentNodeUid, name);\n\n return new GraphClientImpl(\n childBackend,\n {\n registry: this.getCombinedRegistry(),\n scanProtection: this.scanProtection,\n migrationWriteBack: this.globalWriteBack,\n migrationSandbox: this.migrationSandbox,\n },\n // Subgraphs do not have meta-backends; meta lives only at the root.\n );\n }\n\n // ---------------------------------------------------------------------------\n // Collection group query\n // ---------------------------------------------------------------------------\n\n async findEdgesGlobal(\n params: FindEdgesParams,\n collectionName?: string,\n ): Promise<StoredGraphRecord[]> {\n if (!this.backend.findEdgesGlobal) {\n throw new FiregraphError(\n 'findEdgesGlobal() is not supported by the current storage backend.',\n 'UNSUPPORTED_OPERATION',\n );\n }\n const plan = buildEdgeQueryPlan(params);\n if (plan.strategy === 'get') {\n throw new FiregraphError(\n 'findEdgesGlobal() requires a query, not a direct document lookup. ' +\n 'Omit one of aUid/axbType/bUid to force a query strategy.',\n 'INVALID_QUERY',\n );\n }\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n const records = await this.backend.findEdgesGlobal(params, collectionName);\n return this.applyMigrations(records);\n }\n\n // ---------------------------------------------------------------------------\n // Bulk operations\n // ---------------------------------------------------------------------------\n\n async removeNodeCascade(uid: string, options?: BulkOptions): Promise<CascadeResult> {\n return this.backend.removeNodeCascade(uid, this, options);\n }\n\n async bulkRemoveEdges(params: FindEdgesParams, options?: BulkOptions): Promise<BulkResult> {\n return this.backend.bulkRemoveEdges(params, this, options);\n }\n\n // ---------------------------------------------------------------------------\n // Dynamic registry methods\n // ---------------------------------------------------------------------------\n\n async defineNodeType(\n name: string,\n jsonSchema: object,\n description?: string,\n options?: DefineTypeOptions,\n ): Promise<void> {\n if (!this.dynamicConfig) {\n throw new DynamicRegistryError(\n 'defineNodeType() is only available in dynamic registry mode. ' +\n 'Pass registryMode: { mode: \"dynamic\" } to createGraphClient().',\n );\n }\n\n if (RESERVED_TYPE_NAMES.has(name)) {\n throw new DynamicRegistryError(\n `Cannot define type \"${name}\": this name is reserved for the meta-registry.`,\n );\n }\n\n if (this.staticRegistry?.lookup(name, NODE_RELATION, name)) {\n throw new DynamicRegistryError(\n `Cannot define node type \"${name}\": already defined in the static registry.`,\n );\n }\n\n const uid = generateDeterministicUid(META_NODE_TYPE, name);\n const data: Record<string, unknown> = { name, jsonSchema };\n if (description !== undefined) data.description = description;\n if (options?.titleField !== undefined) data.titleField = options.titleField;\n if (options?.subtitleField !== undefined) data.subtitleField = options.subtitleField;\n if (options?.viewTemplate !== undefined) data.viewTemplate = options.viewTemplate;\n if (options?.viewCss !== undefined) data.viewCss = options.viewCss;\n if (options?.allowedIn !== undefined) data.allowedIn = options.allowedIn;\n if (options?.migrationWriteBack !== undefined)\n data.migrationWriteBack = options.migrationWriteBack;\n if (options?.migrations !== undefined) {\n data.migrations = await this.serializeMigrations(options.migrations);\n }\n\n await this.putNode(META_NODE_TYPE, uid, data);\n }\n\n async defineEdgeType(\n name: string,\n topology: EdgeTopology,\n jsonSchema?: object,\n description?: string,\n options?: DefineTypeOptions,\n ): Promise<void> {\n if (!this.dynamicConfig) {\n throw new DynamicRegistryError(\n 'defineEdgeType() is only available in dynamic registry mode. ' +\n 'Pass registryMode: { mode: \"dynamic\" } to createGraphClient().',\n );\n }\n\n if (RESERVED_TYPE_NAMES.has(name)) {\n throw new DynamicRegistryError(\n `Cannot define type \"${name}\": this name is reserved for the meta-registry.`,\n );\n }\n\n if (this.staticRegistry) {\n const fromTypes = Array.isArray(topology.from) ? topology.from : [topology.from];\n const toTypes = Array.isArray(topology.to) ? topology.to : [topology.to];\n for (const aType of fromTypes) {\n for (const bType of toTypes) {\n if (this.staticRegistry.lookup(aType, name, bType)) {\n throw new DynamicRegistryError(\n `Cannot define edge type \"${name}\" for (${aType}) -> (${bType}): already defined in the static registry.`,\n );\n }\n }\n }\n }\n\n const uid = generateDeterministicUid(META_EDGE_TYPE, name);\n const data: Record<string, unknown> = {\n name,\n from: topology.from,\n to: topology.to,\n };\n if (jsonSchema !== undefined) data.jsonSchema = jsonSchema;\n if (topology.inverseLabel !== undefined) data.inverseLabel = topology.inverseLabel;\n if (topology.targetGraph !== undefined) data.targetGraph = topology.targetGraph;\n if (description !== undefined) data.description = description;\n if (options?.titleField !== undefined) data.titleField = options.titleField;\n if (options?.subtitleField !== undefined) data.subtitleField = options.subtitleField;\n if (options?.viewTemplate !== undefined) data.viewTemplate = options.viewTemplate;\n if (options?.viewCss !== undefined) data.viewCss = options.viewCss;\n if (options?.allowedIn !== undefined) data.allowedIn = options.allowedIn;\n if (options?.migrationWriteBack !== undefined)\n data.migrationWriteBack = options.migrationWriteBack;\n if (options?.migrations !== undefined) {\n data.migrations = await this.serializeMigrations(options.migrations);\n }\n\n await this.putNode(META_EDGE_TYPE, uid, data);\n }\n\n async reloadRegistry(): Promise<void> {\n if (!this.dynamicConfig) {\n throw new DynamicRegistryError(\n 'reloadRegistry() is only available in dynamic registry mode. ' +\n 'Pass registryMode: { mode: \"dynamic\" } to createGraphClient().',\n );\n }\n\n const reader = this.createMetaReader();\n const dynamicOnly = await createRegistryFromGraph(reader, this.migrationSandbox);\n\n if (this.staticRegistry) {\n this.dynamicRegistry = createMergedRegistry(this.staticRegistry, dynamicOnly);\n } else {\n this.dynamicRegistry = dynamicOnly;\n }\n }\n\n private async serializeMigrations(\n migrations: Array<{ fromVersion: number; toVersion: number; up: MigrationFn | string }>,\n ): Promise<Array<{ fromVersion: number; toVersion: number; up: string }>> {\n const result = migrations.map((m) => {\n const source = typeof m.up === 'function' ? m.up.toString() : m.up;\n return { fromVersion: m.fromVersion, toVersion: m.toVersion, up: source };\n });\n await Promise.all(result.map((m) => precompileSource(m.up, this.migrationSandbox)));\n return result;\n }\n\n /**\n * Build a `GraphReader` over the meta-backend. If meta lives in the same\n * collection as the main backend, `this` is returned directly.\n */\n private createMetaReader(): GraphReader {\n if (!this.metaBackend) return this;\n\n const backend = this.metaBackend;\n\n const executeMetaQuery = (\n filters: QueryFilter[],\n options?: QueryOptions,\n ): Promise<StoredGraphRecord[]> => backend.query(filters, options);\n\n return {\n async getNode(uid: string): Promise<StoredGraphRecord | null> {\n return backend.getDoc(computeNodeDocId(uid));\n },\n async getEdge(\n aUid: string,\n axbType: string,\n bUid: string,\n ): Promise<StoredGraphRecord | null> {\n return backend.getDoc(computeEdgeDocId(aUid, axbType, bUid));\n },\n async edgeExists(aUid: string, axbType: string, bUid: string): Promise<boolean> {\n const record = await backend.getDoc(computeEdgeDocId(aUid, axbType, bUid));\n return record !== null;\n },\n async findEdges(params: FindEdgesParams): Promise<StoredGraphRecord[]> {\n const plan = buildEdgeQueryPlan(params);\n if (plan.strategy === 'get') {\n const record = await backend.getDoc(plan.docId);\n return record ? [record] : [];\n }\n return executeMetaQuery(plan.filters, plan.options);\n },\n async findNodes(params: FindNodesParams): Promise<StoredGraphRecord[]> {\n const plan = buildNodeQueryPlan(params);\n if (plan.strategy === 'get') {\n const record = await backend.getDoc(plan.docId);\n return record ? [record] : [];\n }\n return executeMetaQuery(plan.filters, plan.options);\n },\n };\n }\n}\n\n/**\n * Create a `GraphClient` backed by an arbitrary `StorageBackend`.\n *\n * Used by backend-specific factories (D1, DO-SQLite, etc.) — most callers\n * should use the higher-level `createGraphClient(firestore, ...)` overload\n * below or import the equivalent from `firegraph/d1` / `firegraph/do-sqlite`.\n */\nexport function createGraphClientFromBackend(\n backend: StorageBackend,\n options?: GraphClientOptions,\n metaBackend?: StorageBackend,\n): GraphClient | DynamicGraphClient {\n return new GraphClientImpl(backend, options, metaBackend) as GraphClient | DynamicGraphClient;\n}\n","/**\n * Backend-agnostic timestamp.\n *\n * Structurally compatible with `@google-cloud/firestore`'s `Timestamp` so\n * that records returned by either the Firestore or SQLite backend can be\n * consumed through the same `StoredGraphRecord` shape.\n *\n * Firestore's native `Timestamp` already satisfies this interface, so\n * existing Firestore consumers see no behavior change. The SQLite backend\n * returns instances of `GraphTimestampImpl` which also satisfies it.\n */\n\nexport interface GraphTimestamp {\n readonly seconds: number;\n readonly nanoseconds: number;\n toDate(): Date;\n toMillis(): number;\n}\n\n/**\n * Concrete `GraphTimestamp` implementation used by non-Firestore backends.\n * Mirrors the surface of Firestore's `Timestamp` enough for typical use.\n */\nexport class GraphTimestampImpl implements GraphTimestamp {\n constructor(\n public readonly seconds: number,\n public readonly nanoseconds: number,\n ) {}\n\n toDate(): Date {\n return new Date(this.toMillis());\n }\n\n toMillis(): number {\n return this.seconds * 1000 + Math.floor(this.nanoseconds / 1e6);\n }\n\n toJSON(): { seconds: number; nanoseconds: number } {\n return { seconds: this.seconds, nanoseconds: this.nanoseconds };\n }\n\n static fromMillis(ms: number): GraphTimestampImpl {\n const seconds = Math.floor(ms / 1000);\n const nanoseconds = (ms - seconds * 1000) * 1e6;\n return new GraphTimestampImpl(seconds, nanoseconds);\n }\n\n static now(): GraphTimestampImpl {\n return GraphTimestampImpl.fromMillis(Date.now());\n }\n}\n\n/**\n * Sentinel returned by `StorageBackend.serverTimestamp()` when the backend\n * has no native server-time concept and just wants a placeholder that the\n * adapter resolves to a concrete time at write commit. SQLite backends\n * substitute the wall-clock millis at the moment of `setDoc`/`updateDoc`.\n */\nexport const SERVER_TIMESTAMP_SENTINEL = Symbol.for('firegraph.serverTimestamp');\nexport type ServerTimestampSentinel = typeof SERVER_TIMESTAMP_SENTINEL;\n\nexport function isServerTimestampSentinel(value: unknown): value is ServerTimestampSentinel {\n return value === SERVER_TIMESTAMP_SENTINEL;\n}\n","/**\n * SQLite schema for firegraph triples.\n *\n * Single-table design — both nodes (self-loops with `axbType = 'is'`) and\n * edges share one row. The `scope` column carries the materialized subgraph\n * path (parent UIDs interleaved with subgraph names), which preserves\n * Firestore's nested-subcollection semantics in a flat table.\n *\n * `data` is a JSON string. Built-in fields are projected to typed columns so\n * the query planner can use indexes without going through `json_extract`.\n */\n\nexport const SQLITE_COLUMNS = [\n 'doc_id',\n 'scope',\n 'a_type',\n 'a_uid',\n 'axb_type',\n 'b_type',\n 'b_uid',\n 'data',\n 'v',\n 'created_at',\n 'updated_at',\n] as const;\n\nexport type SqliteColumn = (typeof SQLITE_COLUMNS)[number];\n\n/**\n * Map firegraph field names (as they appear in `QueryFilter.field` and the\n * record envelope) to SQLite column names.\n */\nexport const FIELD_TO_COLUMN: Record<string, SqliteColumn> = {\n aType: 'a_type',\n aUid: 'a_uid',\n axbType: 'axb_type',\n bType: 'b_type',\n bUid: 'b_uid',\n v: 'v',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n};\n\n/**\n * Build the DDL statements that create the firegraph table and its indexes.\n * Returned as separate statements because some drivers (D1) require one\n * statement per `prepare()` call.\n */\nexport function buildSchemaStatements(table: string): string[] {\n const t = quoteIdent(table);\n return [\n `CREATE TABLE IF NOT EXISTS ${t} (\n doc_id TEXT NOT NULL,\n scope TEXT NOT NULL DEFAULT '',\n a_type TEXT NOT NULL,\n a_uid TEXT NOT NULL,\n axb_type TEXT NOT NULL,\n b_type TEXT NOT NULL,\n b_uid TEXT NOT NULL,\n data TEXT NOT NULL,\n v INTEGER,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (scope, doc_id)\n )`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_a_uid`)} ON ${t}(scope, a_uid)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_b_uid`)} ON ${t}(scope, b_uid)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_axb_type_b_uid`)} ON ${t}(scope, axb_type, b_uid)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_a_type`)} ON ${t}(scope, a_type)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_b_type`)} ON ${t}(scope, b_type)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_doc_id`)} ON ${t}(doc_id)`,\n ];\n}\n\n/**\n * Quote a SQL identifier with double quotes, escaping any embedded quotes.\n *\n * Identifier names (table, column, index) come from configuration and\n * static code in this module — never from user data — but quoting still\n * protects against accidental keyword collisions.\n */\nexport function quoteIdent(name: string): string {\n validateTableName(name);\n return `\"${name}\"`;\n}\n\n/**\n * Validate a SQLite identifier (table name, column name) against the\n * allowed character set. Exposed so factory functions can fail fast on\n * an invalid `options.table` rather than waiting until first SQL.\n */\nexport function validateTableName(name: string): void {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {\n throw new Error(`Invalid SQL identifier: ${name}. Must match /^[A-Za-z_][A-Za-z0-9_]*$/.`);\n }\n}\n","/**\n * Compile firegraph queries and writes into parameterized SQLite statements.\n *\n * The single-table SQLite schema mirrors the Firestore record envelope:\n * built-in fields (`aType`, `aUid`, etc.) are typed columns, and `data` is a\n * JSON string. `data.<key>` filter fields are translated to `json_extract`\n * expressions; built-in fields go straight to their column.\n */\n\nimport { FiregraphError } from '../errors.js';\nimport type { GraphTimestamp } from '../timestamp.js';\nimport { GraphTimestampImpl } from '../timestamp.js';\nimport type { QueryFilter, QueryOptions, StoredGraphRecord } from '../types.js';\nimport type { UpdatePayload, WritableRecord } from './backend.js';\nimport { FIELD_TO_COLUMN, quoteIdent } from './sqlite-schema.js';\n\nexport interface CompiledStatement {\n sql: string;\n params: unknown[];\n}\n\n/**\n * Translate a firegraph filter field to either a column reference or a\n * `json_extract(data, ?)` expression. The JSON path is returned separately\n * so it can be appended to the parameter list.\n */\nfunction compileFieldRef(field: string): { expr: string; pathParam?: string } {\n const column = FIELD_TO_COLUMN[field];\n if (column) {\n return { expr: quoteIdent(column) };\n }\n if (field.startsWith('data.')) {\n const key = field.slice(5);\n validateJsonPathKey(key);\n return { expr: 'json_extract(\"data\", ?)', pathParam: `$.${key}` };\n }\n if (field === 'data') {\n return { expr: 'json_extract(\"data\", ?)', pathParam: '$' };\n }\n throw new FiregraphError(`SQLite backend cannot resolve filter field: ${field}`, 'INVALID_QUERY');\n}\n\n/**\n * Constructor names of Firestore special types that don't survive a plain\n * JSON.stringify round-trip — they have non-enumerable accessors (e.g.\n * `Timestamp.seconds`) or class identity that JSON loses. Filtering by one\n * of these against SQLite would silently miss every row, so we fail loudly.\n *\n * Detection is by `constructor.name` + duck-type so this module stays\n * dependency-free (importing `@google-cloud/firestore` here would pollute\n * the Cloudflare Workers bundle — see tests/unit/bundle-pollution.test.ts).\n */\nconst FIRESTORE_TYPE_NAMES = new Set([\n 'Timestamp',\n 'GeoPoint',\n 'VectorValue',\n 'DocumentReference',\n 'FieldValue',\n]);\n\nfunction isFirestoreSpecialType(value: object): string | null {\n const ctorName = (value as { constructor?: { name?: string } }).constructor?.name;\n if (ctorName && FIRESTORE_TYPE_NAMES.has(ctorName)) return ctorName;\n return null;\n}\n\n/**\n * Coerce a JS filter value into a SQLite-bindable primitive. JS objects\n * (other than null) are JSON-stringified so they can match values stored\n * via `json_extract`.\n *\n * Firestore special types (Timestamp, GeoPoint, VectorValue,\n * DocumentReference, FieldValue) are rejected with `INVALID_QUERY` —\n * `JSON.stringify` would emit garbage (`{}` for Timestamp, etc.) that\n * silently fails to match anything. Convert to a primitive at the call site\n * (e.g. `ts.toMillis()` or `ts.toDate().toISOString()`) before passing in.\n */\nfunction bindValue(value: unknown): unknown {\n if (value === null || value === undefined) return null;\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'bigint' ||\n typeof value === 'boolean'\n ) {\n return value;\n }\n if (value instanceof Date) return value.getTime();\n if (typeof value === 'object') {\n const firestoreType = isFirestoreSpecialType(value);\n if (firestoreType) {\n throw new FiregraphError(\n `SQLite backend cannot bind a Firestore ${firestoreType} value — JSON serialization ` +\n `would silently drop fields and the resulting bind would never match a stored row. ` +\n `Convert to a primitive (e.g. \\`ts.toMillis()\\` for Timestamp) before filtering or ` +\n `updating.`,\n 'INVALID_QUERY',\n );\n }\n return JSON.stringify(value);\n }\n return String(value);\n}\n\n/**\n * Validate `data.<key>` paths and `dataFields` keys against the subset of\n * JSON keys that round-trip cleanly through SQLite's unquoted JSON-path\n * syntax (`$.<name>` style). The accepted shape — `[A-Za-z_][\\w-]*` —\n * covers the overwhelmingly common case of code-style identifiers (camel,\n * snake, kebab) without forcing the SQL emitter to quote/escape paths.\n *\n * Why not silently quote? Several characters (`.`, `[`, `]`, `\"`) are\n * structural in SQLite's JSON path even inside the string after `$.` —\n * silently quoting would mean the reads (`json_extract` for filters) and\n * writes (`json_set` for `dataFields`) need symmetric quoting at every\n * call site, and any drift produces silent data corruption. Failing loudly\n * at compile time is the safer trade-off; users with exotic keys can use\n * `replaceData` (full-blob overwrite) instead.\n */\nconst JSON_PATH_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;\n\nfunction validateJsonPathKey(key: string): void {\n if (key.length === 0) {\n throw new FiregraphError(\n 'SQLite backend: empty JSON path component is not allowed',\n 'INVALID_QUERY',\n );\n }\n if (!JSON_PATH_KEY_RE.test(key)) {\n throw new FiregraphError(\n `SQLite backend: data field path component \"${key}\" is not a safe JSON-path identifier. ` +\n `Allowed pattern: /^[A-Za-z_][A-Za-z0-9_-]*$/. Use replaceData (full-data overwrite) ` +\n `for keys with reserved characters (whitespace, dots, brackets, quotes, etc.).`,\n 'INVALID_QUERY',\n );\n }\n}\n\nfunction compileFilter(filter: QueryFilter, params: unknown[]): string {\n const { expr, pathParam } = compileFieldRef(filter.field);\n if (pathParam !== undefined) params.push(pathParam);\n\n switch (filter.op) {\n case '==':\n params.push(bindValue(filter.value));\n return `${expr} = ?`;\n case '!=':\n params.push(bindValue(filter.value));\n return `${expr} != ?`;\n case '<':\n params.push(bindValue(filter.value));\n return `${expr} < ?`;\n case '<=':\n params.push(bindValue(filter.value));\n return `${expr} <= ?`;\n case '>':\n params.push(bindValue(filter.value));\n return `${expr} > ?`;\n case '>=':\n params.push(bindValue(filter.value));\n return `${expr} >= ?`;\n case 'in': {\n const values = asArray(filter.value, 'in');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `${expr} IN (${placeholders})`;\n }\n case 'not-in': {\n const values = asArray(filter.value, 'not-in');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `${expr} NOT IN (${placeholders})`;\n }\n case 'array-contains': {\n params.push(bindValue(filter.value));\n return `EXISTS (SELECT 1 FROM json_each(${expr}) WHERE value = ?)`;\n }\n case 'array-contains-any': {\n const values = asArray(filter.value, 'array-contains-any');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `EXISTS (SELECT 1 FROM json_each(${expr}) WHERE value IN (${placeholders}))`;\n }\n default:\n throw new FiregraphError(\n `SQLite backend does not support filter operator: ${String(filter.op)}`,\n 'INVALID_QUERY',\n );\n }\n}\n\nfunction asArray(value: unknown, op: string): unknown[] {\n if (!Array.isArray(value) || value.length === 0) {\n throw new FiregraphError(`Operator \"${op}\" requires a non-empty array value`, 'INVALID_QUERY');\n }\n return value;\n}\n\nfunction compileOrderBy(options: QueryOptions | undefined, params: unknown[]): string {\n if (!options?.orderBy) return '';\n const { field, direction } = options.orderBy;\n const { expr, pathParam } = compileFieldRef(field);\n if (pathParam !== undefined) params.push(pathParam);\n const dir = direction === 'desc' ? 'DESC' : 'ASC';\n return ` ORDER BY ${expr} ${dir}`;\n}\n\nfunction compileLimit(options: QueryOptions | undefined, params: unknown[]): string {\n if (options?.limit === undefined) return '';\n params.push(options.limit);\n return ` LIMIT ?`;\n}\n\n/**\n * SELECT all rows matching `filters` within `scope`. The scope filter is\n * always added as the leading predicate so the `(scope, …)` indexes apply.\n */\nexport function compileSelect(\n table: string,\n scope: string,\n filters: QueryFilter[],\n options?: QueryOptions,\n): CompiledStatement {\n const params: unknown[] = [];\n const conditions: string[] = ['\"scope\" = ?'];\n params.push(scope);\n\n for (const f of filters) {\n conditions.push(compileFilter(f, params));\n }\n\n let sql = `SELECT * FROM ${quoteIdent(table)} WHERE ${conditions.join(' AND ')}`;\n // ORDER BY parameters must come after WHERE parameters in the bind list.\n const orderClause = compileOrderBy(options, params);\n sql += orderClause;\n sql += compileLimit(options, params);\n\n return { sql, params };\n}\n\n/**\n * Cross-scope SELECT — equivalent to Firestore's collection group query.\n * Used by `findEdgesGlobal`.\n *\n * `scopeNameFilter`, when present, narrows the result to rows whose scope's\n * last materialized-path segment equals the given subgraph name (or to the\n * root rows when the name equals the table name itself, matching Firestore's\n * `db.collectionGroup(tableName)` semantics).\n */\nexport function compileSelectGlobal(\n table: string,\n filters: QueryFilter[],\n options?: QueryOptions,\n scopeNameFilter?: { name: string; isRoot: boolean },\n): CompiledStatement {\n if (filters.length === 0) {\n throw new FiregraphError(\n 'compileSelectGlobal requires at least one filter — refusing to issue an unbounded SELECT.',\n 'INVALID_QUERY',\n );\n }\n\n const params: unknown[] = [];\n const conditions: string[] = [];\n\n if (scopeNameFilter) {\n if (scopeNameFilter.isRoot) {\n conditions.push(`\"scope\" = ?`);\n params.push('');\n } else {\n conditions.push(`\"scope\" LIKE ? ESCAPE '\\\\'`);\n params.push(`%/${escapeLike(scopeNameFilter.name)}`);\n }\n }\n\n for (const f of filters) {\n conditions.push(compileFilter(f, params));\n }\n\n const sql =\n `SELECT * FROM ${quoteIdent(table)} WHERE ${conditions.join(' AND ')}` +\n compileOrderBy(options, params) +\n compileLimit(options, params);\n return { sql, params };\n}\n\nexport function compileSelectByDocId(\n table: string,\n scope: string,\n docId: string,\n): CompiledStatement {\n return {\n sql: `SELECT * FROM ${quoteIdent(table)} WHERE \"scope\" = ? AND \"doc_id\" = ? LIMIT 1`,\n params: [scope, docId],\n };\n}\n\n/**\n * INSERT OR REPLACE — `setDoc` semantics: full replacement of the row\n * keyed by `(scope, doc_id)`.\n */\nexport function compileSet(\n table: string,\n scope: string,\n docId: string,\n record: WritableRecord,\n nowMillis: number,\n): CompiledStatement {\n const sql = `INSERT OR REPLACE INTO ${quoteIdent(table)} (\n doc_id, scope, a_type, a_uid, axb_type, b_type, b_uid, data, v, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;\n const params: unknown[] = [\n docId,\n scope,\n record.aType,\n record.aUid,\n record.axbType,\n record.bType,\n record.bUid,\n JSON.stringify(record.data ?? {}),\n record.v ?? null,\n nowMillis,\n nowMillis,\n ];\n return { sql, params };\n}\n\n/**\n * Compile an `UpdatePayload` into a single UPDATE statement.\n *\n * - `replaceData` overwrites the whole `data` JSON.\n * - `dataFields` shallow-merges fields via `json_set(data, '$.k', v, …)`.\n * - `v` is set when provided.\n * - `updated_at` is always stamped.\n */\nexport function compileUpdate(\n table: string,\n scope: string,\n docId: string,\n update: UpdatePayload,\n nowMillis: number,\n): CompiledStatement {\n const setClauses: string[] = [];\n const params: unknown[] = [];\n\n if (update.replaceData) {\n setClauses.push(`\"data\" = ?`);\n params.push(JSON.stringify(update.replaceData));\n } else if (update.dataFields && Object.keys(update.dataFields).length > 0) {\n // json_set(data, '$.k1', ?, '$.k2', ?, …)\n const entries = Object.entries(update.dataFields);\n const pathArgs = entries.map(() => `?, ?`).join(', ');\n setClauses.push(`\"data\" = json_set(COALESCE(\"data\", '{}'), ${pathArgs})`);\n for (const [k, v] of entries) {\n validateJsonPathKey(k);\n params.push(`$.${k}`);\n params.push(bindValue(v));\n }\n }\n\n if (update.v !== undefined) {\n setClauses.push(`\"v\" = ?`);\n params.push(update.v);\n }\n\n setClauses.push(`\"updated_at\" = ?`);\n params.push(nowMillis);\n\n // WHERE params come last\n params.push(scope, docId);\n\n return {\n sql: `UPDATE ${quoteIdent(table)} SET ${setClauses.join(', ')} WHERE \"scope\" = ? AND \"doc_id\" = ?`,\n params,\n };\n}\n\nexport function compileDelete(table: string, scope: string, docId: string): CompiledStatement {\n return {\n sql: `DELETE FROM ${quoteIdent(table)} WHERE \"scope\" = ? AND \"doc_id\" = ?`,\n params: [scope, docId],\n };\n}\n\n/**\n * Delete every row whose scope starts with `scopePrefix` followed by '/'.\n * Used by cascade delete to wipe all subgraphs nested under a node.\n *\n * The trailing '/' guard prevents `'a'` from matching `'ab'`. The exact-\n * match case (`scope = scopePrefix`) is intentionally NOT included: the\n * prefix passed in is always `<storageScope>/<uid>` (or just `<uid>` at\n * root), which is an odd-segment count, while every stored row's scope is\n * `''` (root) or an even-segment `<uid>/<name>[/…]` pair sequence — so\n * `scope = scopePrefix` can never match a real row.\n */\nexport function compileDeleteScopePrefix(table: string, scopePrefix: string): CompiledStatement {\n // SQLite LIKE escape: prefix could contain '%' or '_'. Escape with '\\\\'.\n const escaped = escapeLike(scopePrefix);\n return {\n sql: `DELETE FROM ${quoteIdent(table)} WHERE \"scope\" LIKE ? ESCAPE '\\\\'`,\n params: [`${escaped}/%`],\n };\n}\n\n/**\n * Count rows that `compileDeleteScopePrefix` would delete. Used by cascade\n * to report an accurate total in `CascadeResult.deleted` — the prefix-\n * delete is a single SQL statement and the executor surface doesn't expose\n * per-row counts. One extra index lookup per cascade is cheap relative to\n * the delete itself.\n */\nexport function compileCountScopePrefix(table: string, scopePrefix: string): CompiledStatement {\n const escaped = escapeLike(scopePrefix);\n return {\n sql: `SELECT COUNT(*) AS n FROM ${quoteIdent(table)} WHERE \"scope\" LIKE ? ESCAPE '\\\\'`,\n params: [`${escaped}/%`],\n };\n}\n\nfunction escapeLike(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/%/g, '\\\\%').replace(/_/g, '\\\\_');\n}\n\n/**\n * Convert a row returned by a SQLite driver into a `StoredGraphRecord`.\n * `created_at`/`updated_at` are wrapped in `GraphTimestampImpl` so they\n * present the same surface as Firestore's `Timestamp`.\n */\nexport function rowToRecord(row: Record<string, unknown>): StoredGraphRecord {\n const dataString = row.data as string | null;\n const data = dataString ? (JSON.parse(dataString) as Record<string, unknown>) : {};\n\n const createdMs = toMillis(row.created_at);\n const updatedMs = toMillis(row.updated_at);\n\n const record: Record<string, unknown> = {\n aType: row.a_type as string,\n aUid: row.a_uid as string,\n axbType: row.axb_type as string,\n bType: row.b_type as string,\n bUid: row.b_uid as string,\n data,\n createdAt: GraphTimestampImpl.fromMillis(createdMs) as unknown as GraphTimestamp,\n updatedAt: GraphTimestampImpl.fromMillis(updatedMs) as unknown as GraphTimestamp,\n };\n\n if (row.v !== null && row.v !== undefined) {\n record.v = Number(row.v);\n }\n return record as unknown as StoredGraphRecord;\n}\n\nfunction toMillis(value: unknown): number {\n if (typeof value === 'number') return value;\n if (typeof value === 'bigint') return Number(value);\n if (typeof value === 'string') return Number(value);\n return 0;\n}\n","/**\n * SQLite implementation of `StorageBackend`.\n *\n * Uses a single table keyed by `(scope, doc_id)`. Subgraphs are encoded in\n * the `scope` column as a materialized path of interleaved parent UIDs and\n * subgraph names — `''` at the root, `'<uid>/<name>'` one level down,\n * `'<uid1>/<name1>/<uid2>/<name2>'` two levels down, and so on. Cascade\n * delete uses a single `DELETE … WHERE scope LIKE 'prefix/%'` instead of\n * walking subcollections.\n */\n\nimport { computeEdgeDocId, computeNodeDocId } from '../docid.js';\nimport { FiregraphError } from '../errors.js';\nimport { buildEdgeQueryPlan } from '../query.js';\nimport type {\n BulkBatchError,\n BulkOptions,\n BulkResult,\n CascadeResult,\n FindEdgesParams,\n GraphReader,\n QueryFilter,\n QueryOptions,\n StoredGraphRecord,\n} from '../types.js';\nimport type {\n BatchBackend,\n StorageBackend,\n TransactionBackend,\n UpdatePayload,\n WritableRecord,\n} from './backend.js';\nimport { NODE_RELATION } from './constants.js';\nimport type { SqliteExecutor, SqliteTxExecutor } from './sqlite-executor.js';\nimport type { CompiledStatement } from './sqlite-sql.js';\nimport {\n compileCountScopePrefix,\n compileDelete,\n compileDeleteScopePrefix,\n compileSelect,\n compileSelectByDocId,\n compileSelectGlobal,\n compileSet,\n compileUpdate,\n rowToRecord,\n} from './sqlite-sql.js';\n\nexport interface SqliteBackendOptions {\n /** Logical scope path (chained subgraph names) — used for `allowedIn` matching. */\n scopePath?: string;\n /** Internal storage scope (interleaved parent-uid/name path). */\n storageScope?: string;\n}\n\n/**\n * Default per-chunk retry budget for bulk/cascade operations. Mirrors the\n * Firestore bulk path (`src/bulk.ts`) so behaviour is consistent across\n * backends. Callers override via `BulkOptions.maxRetries`.\n */\nconst DEFAULT_MAX_RETRIES = 3;\nconst BASE_RETRY_DELAY_MS = 200;\n/**\n * Upper bound for the exponential backoff between chunk retries. Without\n * this cap, `maxRetries: 10` would push the final wait past 100s; legitimate\n * transient errors recover well within a few seconds, and longer waits just\n * delay the surfacing of permanent failures.\n */\nconst MAX_RETRY_DELAY_MS = 5000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Return the smaller of two optional positive numbers, treating `undefined`\n * as \"no cap.\" Used to combine caller-supplied `BulkOptions.batchSize` with\n * the driver's own `maxBatchSize` so the more restrictive cap wins.\n */\nfunction minDefined(a: number | undefined, b: number | undefined): number | undefined {\n if (a === undefined) return b;\n if (b === undefined) return a;\n return Math.min(a, b);\n}\n\n/**\n * Split `statements` into chunks that respect both a per-batch statement\n * count cap (`maxStatements`) and a per-batch total bound-parameter cap\n * (`maxParams`). When neither cap is provided the entire list is returned\n * as a single chunk (preserves cross-batch atomicity for drivers like\n * DO SQLite that have no caps).\n *\n * Single-statement edge case: if a single statement's parameter count\n * already exceeds `maxParams`, it's emitted as its own chunk anyway. The\n * driver will reject it, which is the correct behavior — silently\n * dropping it would be worse.\n */\nfunction chunkStatements<T extends { params: unknown[] }>(\n statements: T[],\n maxStatements: number | undefined,\n maxParams: number | undefined,\n): T[][] {\n const stmtCap =\n maxStatements && maxStatements > 0 && Number.isFinite(maxStatements)\n ? Math.floor(maxStatements)\n : Infinity;\n const paramCap =\n maxParams && maxParams > 0 && Number.isFinite(maxParams) ? Math.floor(maxParams) : Infinity;\n\n if (stmtCap === Infinity && paramCap === Infinity) {\n return [statements];\n }\n\n const chunks: T[][] = [];\n let current: T[] = [];\n let currentParamCount = 0;\n for (const stmt of statements) {\n const stmtParams = stmt.params.length;\n const wouldExceedStmt = current.length + 1 > stmtCap;\n const wouldExceedParam = currentParamCount + stmtParams > paramCap;\n if (current.length > 0 && (wouldExceedStmt || wouldExceedParam)) {\n chunks.push(current);\n current = [];\n currentParamCount = 0;\n }\n current.push(stmt);\n currentParamCount += stmtParams;\n }\n if (current.length > 0) chunks.push(current);\n return chunks;\n}\n\nclass SqliteTransactionBackendImpl implements TransactionBackend {\n constructor(\n private readonly tx: SqliteTxExecutor,\n private readonly tableName: string,\n private readonly storageScope: string,\n ) {}\n\n async getDoc(docId: string): Promise<StoredGraphRecord | null> {\n const stmt = compileSelectByDocId(this.tableName, this.storageScope, docId);\n const rows = await this.tx.all(stmt.sql, stmt.params);\n return rows.length === 0 ? null : rowToRecord(rows[0]);\n }\n\n async query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]> {\n const stmt = compileSelect(this.tableName, this.storageScope, filters, options);\n const rows = await this.tx.all(stmt.sql, stmt.params);\n return rows.map(rowToRecord);\n }\n\n async setDoc(docId: string, record: WritableRecord): Promise<void> {\n const stmt = compileSet(this.tableName, this.storageScope, docId, record, Date.now());\n await this.tx.run(stmt.sql, stmt.params);\n }\n\n async updateDoc(docId: string, update: UpdatePayload): Promise<void> {\n const stmt = compileUpdate(this.tableName, this.storageScope, docId, update, Date.now());\n // RETURNING + `all()` for parity with Firestore — see SqliteBackendImpl.updateDoc.\n const sqlWithReturning = `${stmt.sql} RETURNING \"doc_id\"`;\n const rows = await this.tx.all(sqlWithReturning, stmt.params);\n if (rows.length === 0) {\n throw new FiregraphError(\n `updateDoc: no document found for doc_id=${docId} (scope=${this.storageScope})`,\n 'NOT_FOUND',\n );\n }\n }\n\n async deleteDoc(docId: string): Promise<void> {\n const stmt = compileDelete(this.tableName, this.storageScope, docId);\n await this.tx.run(stmt.sql, stmt.params);\n }\n}\n\nclass SqliteBatchBackendImpl implements BatchBackend {\n private readonly statements: CompiledStatement[] = [];\n\n constructor(\n private readonly executor: SqliteExecutor,\n private readonly tableName: string,\n private readonly storageScope: string,\n ) {}\n\n setDoc(docId: string, record: WritableRecord): void {\n this.statements.push(compileSet(this.tableName, this.storageScope, docId, record, Date.now()));\n }\n\n updateDoc(docId: string, update: UpdatePayload): void {\n this.statements.push(\n compileUpdate(this.tableName, this.storageScope, docId, update, Date.now()),\n );\n }\n\n deleteDoc(docId: string): void {\n this.statements.push(compileDelete(this.tableName, this.storageScope, docId));\n }\n\n async commit(): Promise<void> {\n if (this.statements.length === 0) return;\n await this.executor.batch(this.statements);\n this.statements.length = 0;\n }\n}\n\nclass SqliteBackendImpl implements StorageBackend {\n /** Logical table name (returned through `collectionPath` for parity with Firestore). */\n readonly collectionPath: string;\n readonly scopePath: string;\n /** Materialized storage scope (interleaved parent UIDs + subgraph names). */\n private readonly storageScope: string;\n\n constructor(\n private readonly executor: SqliteExecutor,\n tableName: string,\n storageScope: string,\n scopePath: string,\n ) {\n this.collectionPath = tableName;\n this.storageScope = storageScope;\n this.scopePath = scopePath;\n }\n\n // --- Reads ---\n\n async getDoc(docId: string): Promise<StoredGraphRecord | null> {\n const stmt = compileSelectByDocId(this.collectionPath, this.storageScope, docId);\n const rows = await this.executor.all(stmt.sql, stmt.params);\n return rows.length === 0 ? null : rowToRecord(rows[0]);\n }\n\n async query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]> {\n const stmt = compileSelect(this.collectionPath, this.storageScope, filters, options);\n const rows = await this.executor.all(stmt.sql, stmt.params);\n return rows.map(rowToRecord);\n }\n\n // --- Writes ---\n\n async setDoc(docId: string, record: WritableRecord): Promise<void> {\n const stmt = compileSet(this.collectionPath, this.storageScope, docId, record, Date.now());\n await this.executor.run(stmt.sql, stmt.params);\n }\n\n async updateDoc(docId: string, update: UpdatePayload): Promise<void> {\n const stmt = compileUpdate(this.collectionPath, this.storageScope, docId, update, Date.now());\n // Use RETURNING + `all()` so missing rows surface as an error, matching\n // Firestore's `update()` semantics (NOT_FOUND when the doc doesn't exist).\n // SQLite ≥3.35 supports UPDATE … RETURNING; better-sqlite3, D1, and DO\n // SQLite all run on a recent enough engine.\n const sqlWithReturning = `${stmt.sql} RETURNING \"doc_id\"`;\n const rows = await this.executor.all(sqlWithReturning, stmt.params);\n if (rows.length === 0) {\n throw new FiregraphError(\n `updateDoc: no document found for doc_id=${docId} (scope=${this.storageScope})`,\n 'NOT_FOUND',\n );\n }\n }\n\n async deleteDoc(docId: string): Promise<void> {\n const stmt = compileDelete(this.collectionPath, this.storageScope, docId);\n await this.executor.run(stmt.sql, stmt.params);\n }\n\n // --- Transactions / Batches ---\n\n async runTransaction<T>(fn: (tx: TransactionBackend) => Promise<T>): Promise<T> {\n if (!this.executor.transaction) {\n throw new FiregraphError(\n 'Interactive transactions are not supported by this SQLite driver. ' +\n 'D1 in particular has no read-then-conditional-write transactions; ' +\n 'use a Durable Object SQLite client instead, or rewrite the code path ' +\n 'as a batch().',\n 'UNSUPPORTED_OPERATION',\n );\n }\n return this.executor.transaction(async (tx) => {\n const txBackend = new SqliteTransactionBackendImpl(\n tx,\n this.collectionPath,\n this.storageScope,\n );\n return fn(txBackend);\n });\n }\n\n createBatch(): BatchBackend {\n return new SqliteBatchBackendImpl(this.executor, this.collectionPath, this.storageScope);\n }\n\n // --- Subgraphs ---\n\n subgraph(parentNodeUid: string, name: string): StorageBackend {\n // Defense-in-depth: the public `GraphClient.subgraph()` also validates,\n // but backend users (traversal, cross-graph hops, custom integrations)\n // reach this method directly. A bad UID or a name containing '/' would\n // corrupt the materialized-path scope encoding — reject loudly.\n if (!parentNodeUid || parentNodeUid.includes('/')) {\n throw new FiregraphError(\n `Invalid parentNodeUid for subgraph: \"${parentNodeUid}\". ` +\n 'Must be a non-empty string without \"/\".',\n 'INVALID_SUBGRAPH',\n );\n }\n if (!name || name.includes('/')) {\n throw new FiregraphError(\n `Subgraph name must not contain \"/\" and must be non-empty: got \"${name}\". ` +\n 'Use chained .subgraph() calls for nested subgraphs.',\n 'INVALID_SUBGRAPH',\n );\n }\n const newStorageScope = this.storageScope\n ? `${this.storageScope}/${parentNodeUid}/${name}`\n : `${parentNodeUid}/${name}`;\n const newScope = this.scopePath ? `${this.scopePath}/${name}` : name;\n return new SqliteBackendImpl(this.executor, this.collectionPath, newStorageScope, newScope);\n }\n\n // --- Cascade & bulk ---\n\n async removeNodeCascade(\n uid: string,\n reader: GraphReader,\n options?: BulkOptions,\n ): Promise<CascadeResult> {\n // Collect all edges touching the node in the current scope (excluding self-loop).\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\n const seen = new Set<string>();\n const edgeDocIds: string[] = [];\n for (const edge of [...outgoingRaw, ...incomingRaw]) {\n if (edge.axbType === NODE_RELATION) continue;\n const docId = computeEdgeDocId(edge.aUid, edge.axbType, edge.bUid);\n if (!seen.has(docId)) {\n seen.add(docId);\n edgeDocIds.push(docId);\n }\n }\n\n const nodeDocId = computeNodeDocId(uid);\n const shouldDeleteSubgraphs = options?.deleteSubcollections !== false;\n\n // Pre-count subgraph rows so the returned `deleted` total reflects the\n // actual number of records removed by the prefix-delete (which is a\n // single statement, but may match many rows). One extra index lookup is\n // cheap relative to the cascade itself.\n let subgraphRowCount = 0;\n if (shouldDeleteSubgraphs) {\n const prefix = this.storageScope ? `${this.storageScope}/${uid}` : uid;\n const countStmt = compileCountScopePrefix(this.collectionPath, prefix);\n const countRows = await this.executor.all(countStmt.sql, countStmt.params);\n const first = countRows[0] as Record<string, unknown> | undefined;\n const n = first?.n;\n subgraphRowCount = typeof n === 'bigint' ? Number(n) : Number(n ?? 0);\n }\n\n // Build the full statement list. Order: edges → node → prefix-delete.\n // When the executor's `batch()` is fully atomic (DO SQLite uses\n // `transactionSync`) the chunking loop below collapses to a single batch\n // and the operation is atomic. When the executor caps batches (D1, ~100\n // statements) we lose cross-batch atomicity, but `removeNodeCascade` is\n // idempotent so a caller can retry after a partial failure.\n const writeStatements: CompiledStatement[] = edgeDocIds.map((id) =>\n compileDelete(this.collectionPath, this.storageScope, id),\n );\n writeStatements.push(compileDelete(this.collectionPath, this.storageScope, nodeDocId));\n if (shouldDeleteSubgraphs) {\n const prefix = this.storageScope ? `${this.storageScope}/${uid}` : uid;\n writeStatements.push(compileDeleteScopePrefix(this.collectionPath, prefix));\n }\n\n const {\n deleted: stmtDeleted,\n batches,\n errors,\n } = await this.executeChunkedBatches(writeStatements, options);\n\n // `nodeDeleted` / `edgesDeleted` reflect best-effort completion: a\n // chunk failure leaves us unable to know which sub-batch contained the\n // node-row delete, so we conservatively flag both as incomplete when\n // any batch fails. The caller can retry — cascade is idempotent.\n const allOk = errors.length === 0;\n const edgesDeleted = allOk ? edgeDocIds.length : 0;\n const nodeDeleted = allOk;\n\n // `stmtDeleted` counts committed *statements*. Replace the prefix-\n // delete's per-statement contribution (1) with the pre-computed row\n // count so callers see a true row total. Only credit subgraph rows when\n // every chunk succeeded — partial failure means we can't be sure the\n // chunk containing the prefix-delete actually committed.\n const prefixStatementContribution = shouldDeleteSubgraphs && allOk ? 1 : 0;\n const deleted = stmtDeleted - prefixStatementContribution + (allOk ? subgraphRowCount : 0);\n\n return { deleted, batches, errors, edgesDeleted, nodeDeleted };\n }\n\n async bulkRemoveEdges(\n params: FindEdgesParams,\n reader: GraphReader,\n options?: BulkOptions,\n ): Promise<BulkResult> {\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\n if (docIds.length === 0) {\n return { deleted: 0, batches: 0, errors: [] };\n }\n\n const statements = docIds.map((id) =>\n compileDelete(this.collectionPath, this.storageScope, id),\n );\n\n return this.executeChunkedBatches(statements, options);\n }\n\n /**\n * Submit `statements` to the executor as one or more `batch()` calls,\n * chunking by `executor.maxBatchSize` (e.g. D1's ~100-statement cap).\n * Drivers that don't advertise a cap submit everything in one batch,\n * preserving cross-batch atomicity.\n *\n * Each chunk is retried with exponential backoff up to `maxRetries`\n * (default 3) before being recorded in `errors`. The loop continues past\n * a permanently failed chunk so the caller still gets partial progress\n * visibility — to halt on first failure, set `maxRetries: 0` and check\n * `result.errors.length` after the call.\n *\n * Returns `BulkResult`-shaped fields. `deleted` reflects only the\n * statement count of *successfully committed* batches — a prefix-delete\n * statement contributes 1 to that total even though it may match many\n * rows; `removeNodeCascade` patches that up with a pre-counted row total.\n *\n * **Atomicity caveat (D1):** when chunking kicks in, atomicity is lost\n * across chunk boundaries — one chunk may commit while a later one fails.\n * `removeNodeCascade` is idempotent (deleting the same docs again is a\n * no-op) so a caller can simply retry on partial failure. `bulkRemoveEdges`\n * is also idempotent for the same reason. DO SQLite leaves `maxBatchSize`\n * unset, so everything funnels through one atomic `transactionSync` and\n * this caveat does not apply.\n */\n private async executeChunkedBatches(\n statements: CompiledStatement[],\n options?: BulkOptions,\n ): Promise<{ deleted: number; batches: number; errors: BulkBatchError[] }> {\n if (statements.length === 0) {\n return { deleted: 0, batches: 0, errors: [] };\n }\n const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;\n\n // Split `statements` into chunks up front. Chunking honors the smallest\n // of: caller-supplied `batchSize` (used by callers who want progress\n // granularity), the driver's statement-count cap (`maxBatchSize`, D1 ≈\n // 100), and the driver's total bound-parameter cap (`maxBatchParams`,\n // D1 ≈ 1000). Most cascade/bulk statements are 2-param DELETEs so the\n // param cap rarely triggers, but we respect it defensively. Drivers with\n // no declared caps and no caller cap submit everything in one batch (DO\n // SQLite's atomic `transactionSync`).\n const callerBatchSize = options?.batchSize;\n const stmtCap = minDefined(callerBatchSize, this.executor.maxBatchSize);\n const chunks = chunkStatements(statements, stmtCap, this.executor.maxBatchParams);\n\n const errors: BulkBatchError[] = [];\n let deleted = 0;\n let batches = 0;\n const totalBatches = chunks.length;\n\n const driverParamCap = this.executor.maxBatchParams;\n\n for (let batchIndex = 0; batchIndex < chunks.length; batchIndex++) {\n const chunk = chunks[batchIndex];\n\n // A chunk that's a single statement whose param count already exceeds\n // the driver's per-batch param cap will be rejected on every attempt —\n // retrying just adds latency before surfacing the failure. `chunkStatements`\n // intentionally emits such statements as their own chunk (failing loudly\n // beats silently dropping); fast-fail here closes the loop.\n const isUnretriableOversize =\n chunk.length === 1 &&\n driverParamCap !== undefined &&\n chunk[0].params.length > driverParamCap;\n\n let committed = false;\n let lastError: Error | null = null;\n const effectiveRetries = isUnretriableOversize ? 0 : maxRetries;\n for (let attempt = 0; attempt <= effectiveRetries; attempt++) {\n try {\n await this.executor.batch(chunk);\n committed = true;\n break;\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n if (attempt < effectiveRetries) {\n const delay = Math.min(BASE_RETRY_DELAY_MS * Math.pow(2, attempt), MAX_RETRY_DELAY_MS);\n await sleep(delay);\n }\n }\n }\n\n if (committed) {\n deleted += chunk.length;\n batches += 1;\n } else if (lastError) {\n errors.push({\n batchIndex,\n error: lastError,\n operationCount: chunk.length,\n });\n }\n\n if (options?.onProgress) {\n options.onProgress({\n completedBatches: batches,\n totalBatches,\n deletedSoFar: deleted,\n });\n }\n }\n\n return { deleted, batches, errors };\n }\n\n // --- Cross-scope (collection group) ---\n\n async findEdgesGlobal(\n params: FindEdgesParams,\n collectionName?: string,\n ): Promise<StoredGraphRecord[]> {\n const plan = buildEdgeQueryPlan(params);\n if (plan.strategy === 'get') {\n throw new FiregraphError(\n 'findEdgesGlobal() requires a query, not a direct document lookup. ' +\n 'Omit one of aUid/axbType/bUid to force a query strategy.',\n 'INVALID_QUERY',\n );\n }\n // Mirror Firestore's `collectionGroup(name)` semantics over the\n // materialized-scope SQLite layout: when `collectionName` matches the\n // table name (the implicit root default), filter to root rows; otherwise\n // filter to rows whose scope's last segment equals the requested name.\n const name = collectionName ?? this.collectionPath;\n const scopeNameFilter = {\n name,\n isRoot: name === this.collectionPath,\n };\n const stmt = compileSelectGlobal(\n this.collectionPath,\n plan.filters,\n plan.options,\n scopeNameFilter,\n );\n const rows = await this.executor.all(stmt.sql, stmt.params);\n return rows.map(rowToRecord);\n }\n}\n\n/**\n * Create a SQLite-backed `StorageBackend`.\n *\n * `tableName` is the single table that holds every triple. The driver must\n * have already created the table and indexes via `buildSchemaStatements()`\n * (the public driver factories — `createD1GraphClient` and\n * `createDOSqliteGraphClient` — handle this automatically).\n */\nexport function createSqliteBackend(\n executor: SqliteExecutor,\n tableName: string,\n options: SqliteBackendOptions = {},\n): StorageBackend {\n const storageScope = options.storageScope ?? '';\n const scopePath = options.scopePath ?? '';\n return new SqliteBackendImpl(executor, tableName, storageScope, scopePath);\n}\n"],"mappings":"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","names":["import_node_crypto","Ajv","addFormats","import_node_crypto","serializeFirestoreTypes","deserializeFirestoreTypes","buildWritableNodeRecord","buildWritableEdgeRecord","buildWritableNodeRecord","buildWritableEdgeRecord"]}
|
|
1
|
+
{"version":3,"sources":["../src/serialization.ts","../src/d1.ts","../src/docid.ts","../src/internal/constants.ts","../src/batch.ts","../src/dynamic-registry.ts","../src/errors.ts","../src/json-schema.ts","../src/migration.ts","../src/scope.ts","../src/registry.ts","../src/sandbox.ts","../src/query.ts","../src/query-safety.ts","../src/transaction.ts","../src/client.ts","../src/timestamp.ts","../src/internal/sqlite-schema.ts","../src/internal/sqlite-sql.ts","../src/internal/sqlite-backend.ts"],"sourcesContent":["/**\n * Firestore-aware serialization for the sandbox migration pipeline.\n *\n * Firestore documents can contain special types (Timestamp, GeoPoint,\n * VectorValue, DocumentReference) that don't survive plain JSON\n * round-tripping. This module provides tagged serialization: Firestore\n * types are wrapped in tagged plain objects before JSON marshaling and\n * reconstructed after.\n *\n * Only used by the `defaultExecutor` sandbox path. Static migrations\n * (in-memory functions) receive raw Firestore objects directly.\n */\n\nimport type { DocumentReference, Firestore } from '@google-cloud/firestore';\nimport { FieldValue, GeoPoint, Timestamp } from '@google-cloud/firestore';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Sentinel key used to tag serialized Firestore types. */\nexport const SERIALIZATION_TAG = '__firegraph_ser__' as const;\n\n/** Known discriminator values for tagged types. */\nconst KNOWN_TYPES = new Set(['Timestamp', 'GeoPoint', 'VectorValue', 'DocumentReference']);\n\n// One-time warning for DocumentReference deserialization without db\nlet _docRefWarned = false;\n\n// ---------------------------------------------------------------------------\n// Type guard\n// ---------------------------------------------------------------------------\n\n/** Check if a value is a tagged serialized Firestore type. */\nexport function isTaggedValue(value: unknown): boolean {\n if (value === null || typeof value !== 'object') return false;\n const tag = (value as Record<string, unknown>)[SERIALIZATION_TAG];\n return typeof tag === 'string' && KNOWN_TYPES.has(tag);\n}\n\n// ---------------------------------------------------------------------------\n// Detection helpers\n// ---------------------------------------------------------------------------\n\nfunction isTimestamp(value: unknown): value is Timestamp {\n return value instanceof Timestamp;\n}\n\nfunction isGeoPoint(value: unknown): value is GeoPoint {\n return value instanceof GeoPoint;\n}\n\nfunction isDocumentReference(value: unknown): value is DocumentReference {\n // Duck-type check: DocumentReference has path (string) and firestore properties\n if (value === null || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.path === 'string' &&\n v.firestore !== undefined &&\n typeof v.id === 'string' &&\n v.constructor?.name === 'DocumentReference'\n );\n}\n\nfunction isVectorValue(value: unknown): boolean {\n if (value === null || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n return (\n v.constructor?.name === 'VectorValue' && Array.isArray((v as Record<string, unknown>)._values)\n );\n}\n\n// ---------------------------------------------------------------------------\n// Serialize\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively walk a data object and replace Firestore types with tagged\n * plain objects suitable for JSON serialization.\n *\n * Returns a new object tree — the input is never mutated.\n */\nexport function serializeFirestoreTypes(data: Record<string, unknown>): Record<string, unknown> {\n return serializeValue(data) as Record<string, unknown>;\n}\n\nfunction serializeValue(value: unknown): unknown {\n // Primitives\n if (value === null || value === undefined) return value;\n if (typeof value !== 'object') return value;\n\n // Firestore types (check before generic object/array)\n if (isTimestamp(value)) {\n return {\n [SERIALIZATION_TAG]: 'Timestamp',\n seconds: value.seconds,\n nanoseconds: value.nanoseconds,\n };\n }\n if (isGeoPoint(value)) {\n return {\n [SERIALIZATION_TAG]: 'GeoPoint',\n latitude: value.latitude,\n longitude: value.longitude,\n };\n }\n if (isDocumentReference(value)) {\n return { [SERIALIZATION_TAG]: 'DocumentReference', path: (value as DocumentReference).path };\n }\n if (isVectorValue(value)) {\n // Prefer toArray() (public API) over _values (private internal property)\n const v = value as Record<string, unknown>;\n const values =\n typeof v.toArray === 'function' ? (v.toArray as () => number[])() : (v._values as number[]);\n return { [SERIALIZATION_TAG]: 'VectorValue', values: [...values] };\n }\n\n // Arrays\n if (Array.isArray(value)) {\n return value.map(serializeValue);\n }\n\n // Plain objects — recurse\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n result[key] = serializeValue((value as Record<string, unknown>)[key]);\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Deserialize\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively walk a data object and reconstruct Firestore types from\n * tagged plain objects.\n *\n * @param data - The data to deserialize (typically from JSON.parse)\n * @param db - Optional Firestore instance for DocumentReference reconstruction.\n * If not provided, tagged DocumentReferences are left as-is with a one-time warning.\n *\n * Returns a new object tree — the input is never mutated.\n */\nexport function deserializeFirestoreTypes(\n data: Record<string, unknown>,\n db?: Firestore,\n): Record<string, unknown> {\n return deserializeValue(data, db) as Record<string, unknown>;\n}\n\nfunction deserializeValue(value: unknown, db?: Firestore): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value !== 'object') return value;\n\n // Short-circuit for values that are already real Firestore types.\n // This makes deserializeFirestoreTypes idempotent — safe to call on data\n // that has already been deserialized (e.g., write-back after defaultExecutor\n // already reconstructed types, or static migrations that return raw types).\n if (\n isTimestamp(value) ||\n isGeoPoint(value) ||\n isDocumentReference(value) ||\n isVectorValue(value)\n ) {\n return value;\n }\n\n // Arrays\n if (Array.isArray(value)) {\n return value.map((v) => deserializeValue(v, db));\n }\n\n const obj = value as Record<string, unknown>;\n\n // Check for tagged Firestore type\n if (isTaggedValue(obj)) {\n const tag = obj[SERIALIZATION_TAG] as string;\n\n switch (tag) {\n case 'Timestamp':\n // Validate expected fields before reconstruction\n if (typeof obj.seconds !== 'number' || typeof obj.nanoseconds !== 'number') return obj;\n return new Timestamp(obj.seconds, obj.nanoseconds);\n\n case 'GeoPoint':\n if (typeof obj.latitude !== 'number' || typeof obj.longitude !== 'number') return obj;\n return new GeoPoint(obj.latitude, obj.longitude);\n\n case 'VectorValue':\n if (!Array.isArray(obj.values)) return obj;\n return FieldValue.vector(obj.values as number[]);\n\n case 'DocumentReference':\n if (typeof obj.path !== 'string') return obj;\n if (db) {\n return db.doc(obj.path);\n }\n // No db available — leave as tagged object with one-time warning\n if (!_docRefWarned) {\n _docRefWarned = true;\n console.warn(\n '[firegraph] DocumentReference encountered during migration deserialization ' +\n 'but no Firestore instance available. The reference will remain as a tagged ' +\n 'object with its path. Enable write-back for full reconstruction.',\n );\n }\n return obj;\n\n default:\n // Unknown tag — leave as-is (forward compatibility)\n return obj;\n }\n }\n\n // Plain object — recurse\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n result[key] = deserializeValue(obj[key], db);\n }\n return result;\n}\n","/**\n * Cloudflare D1 driver for firegraph.\n *\n * D1 is async, prepared-statement-based, and supports atomic multi-statement\n * batches via `db.batch()`. D1 has no interactive transactions — reads and\n * conditional writes cannot be interleaved, so `GraphClient.runTransaction()`\n * will throw `UNSUPPORTED_OPERATION` on this driver. Use `GraphClient.batch()`\n * or migrate to Durable Object SQLite for interactive transactions.\n *\n * **Bulk-delete atomicity:** `db.batch()` is atomic *within* one batch but D1\n * caps batches at ~100 statements / 1000 bound parameters. The shared SQLite\n * backend chunks `removeNodeCascade` and `bulkRemoveEdges` automatically; each\n * chunk retries with exponential backoff (`BulkOptions.maxRetries`, default 3).\n * Cross-chunk atomicity is *not* guaranteed — a hub node with thousands of\n * edges may have some chunks commit and others fail. Both operations are\n * idempotent (re-deleting an already-deleted row is a no-op), so callers can\n * safely retry on partial failure. Inspect `result.errors` to detect it.\n */\n\nimport { createGraphClientFromBackend } from './client.js';\nimport { createSqliteBackend } from './internal/sqlite-backend.js';\nimport type { SqliteExecutor } from './internal/sqlite-executor.js';\nimport { buildSchemaStatements, validateTableName } from './internal/sqlite-schema.js';\nimport type {\n DynamicGraphClient,\n DynamicRegistryConfig,\n GraphClient,\n GraphClientOptions,\n} from './types.js';\n\n/**\n * Subset of the Cloudflare D1 Database interface that firegraph depends on.\n * Typed against the official `@cloudflare/workers-types` shape without\n * importing it, so this module has no runtime dependency on the Workers SDK.\n */\nexport interface D1Database {\n prepare(sql: string): D1PreparedStatement;\n batch(statements: D1PreparedStatement[]): Promise<unknown[]>;\n exec(sql: string): Promise<unknown>;\n}\n\nexport interface D1PreparedStatement {\n bind(...values: unknown[]): D1PreparedStatement;\n all<T = Record<string, unknown>>(): Promise<{ results?: T[] }>;\n run(): Promise<unknown>;\n}\n\nexport interface D1ClientOptions extends GraphClientOptions {\n /** Table name for firegraph triples (default: `firegraph`). */\n table?: string;\n /**\n * Run `CREATE TABLE IF NOT EXISTS …` statements on first use.\n * Default: `true`. Disable if you manage schema via a migration tool.\n */\n autoMigrate?: boolean;\n}\n\nclass D1Executor implements SqliteExecutor {\n /**\n * D1 caps `db.batch()` at roughly 100 statements (and ~1000 bound\n * parameters across them). The SqliteBackend uses this hint to chunk\n * large cascade/bulk delete operations so a hub node with thousands of\n * edges doesn't trigger a hard rejection.\n */\n readonly maxBatchSize = 100;\n /**\n * D1's secondary cap: total bound parameters across the batch. Cascade\n * deletes are 2 params each (well under the limit) but the chunker\n * respects this defensively if a future statement type pushes through\n * `executeChunkedBatches` with a higher per-statement param count.\n */\n readonly maxBatchParams = 1000;\n\n constructor(private readonly db: D1Database) {}\n\n async all(sql: string, params: unknown[]): Promise<Record<string, unknown>[]> {\n const stmt = this.db.prepare(sql).bind(...params);\n const result = await stmt.all<Record<string, unknown>>();\n return result.results ?? [];\n }\n\n async run(sql: string, params: unknown[]): Promise<void> {\n await this.db\n .prepare(sql)\n .bind(...params)\n .run();\n }\n\n async batch(statements: ReadonlyArray<{ sql: string; params: unknown[] }>): Promise<void> {\n if (statements.length === 0) return;\n const prepared = statements.map((s) => this.db.prepare(s.sql).bind(...s.params));\n await this.db.batch(prepared);\n }\n\n // No `transaction` — D1 has no interactive transactions.\n}\n\nasync function ensureSchema(db: D1Database, table: string): Promise<void> {\n const statements = buildSchemaStatements(table);\n for (const sql of statements) {\n await db.prepare(sql).run();\n }\n}\n\nexport function createD1GraphClient(\n db: D1Database,\n options: D1ClientOptions & { registryMode: DynamicRegistryConfig },\n): Promise<DynamicGraphClient>;\nexport function createD1GraphClient(\n db: D1Database,\n options?: D1ClientOptions,\n): Promise<GraphClient>;\nexport async function createD1GraphClient(\n db: D1Database,\n options: D1ClientOptions = {},\n): Promise<GraphClient | DynamicGraphClient> {\n const table = options.table ?? 'firegraph';\n validateTableName(table);\n if (options.autoMigrate !== false) {\n await ensureSchema(db, table);\n }\n\n const executor = new D1Executor(db);\n const backend = createSqliteBackend(executor, table);\n\n const { table: _t, autoMigrate: _m, ...clientOptions } = options;\n void _t;\n void _m;\n\n // If a separate meta-collection is requested, create a second backend for it.\n let metaBackend;\n if (\n clientOptions.registryMode &&\n typeof clientOptions.registryMode === 'object' &&\n clientOptions.registryMode.collection &&\n clientOptions.registryMode.collection !== table\n ) {\n const metaTable = clientOptions.registryMode.collection;\n validateTableName(metaTable);\n if (options.autoMigrate !== false) {\n await ensureSchema(db, metaTable);\n }\n metaBackend = createSqliteBackend(executor, metaTable);\n }\n\n return createGraphClientFromBackend(backend, clientOptions, metaBackend);\n}\n","import { createHash } from 'node:crypto';\n\nimport { SHARD_SEPARATOR } from './internal/constants.js';\n\nexport function computeNodeDocId(uid: string): string {\n return uid;\n}\n\nexport function computeEdgeDocId(aUid: string, axbType: string, bUid: string): string {\n const composite = `${aUid}${SHARD_SEPARATOR}${axbType}${SHARD_SEPARATOR}${bUid}`;\n const hash = createHash('sha256').update(composite).digest('hex');\n const shard = hash[0];\n return `${shard}${SHARD_SEPARATOR}${aUid}${SHARD_SEPARATOR}${axbType}${SHARD_SEPARATOR}${bUid}`;\n}\n","export const NODE_RELATION = 'is';\n\n/**\n * Default result limit applied to findEdges/findNodes queries\n * when no explicit limit is provided. Prevents unbounded result sets\n * that could be expensive on Enterprise Firestore.\n */\nexport const DEFAULT_QUERY_LIMIT = 500;\n\n/**\n * Fields that are part of the firegraph record structure (not user data).\n * Used by the query planner and safety analysis to distinguish builtin\n * fields from data.* fields.\n */\nexport const BUILTIN_FIELDS = new Set([\n 'aType', 'aUid', 'axbType', 'bType', 'bUid', 'createdAt', 'updatedAt',\n]);\n\nexport const SHARD_ALGORITHM = 'sha256';\nexport const SHARD_SEPARATOR = ':';\nexport const SHARD_BUCKETS = 16;\n","import { computeEdgeDocId, computeNodeDocId } from './docid.js';\nimport type { BatchBackend, WritableRecord } from './internal/backend.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport type { GraphBatch, GraphRegistry } from './types.js';\n\nfunction buildWritableNodeRecord(\n aType: string,\n uid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid: uid, axbType: NODE_RELATION, bType: aType, bUid: uid, data };\n}\n\nfunction buildWritableEdgeRecord(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid, axbType, bType, bUid, data };\n}\n\nexport class GraphBatchImpl implements GraphBatch {\n constructor(\n private readonly backend: BatchBackend,\n private readonly registry?: GraphRegistry,\n private readonly scopePath: string = '',\n ) {}\n\n async putNode(aType: string, uid: string, data: Record<string, unknown>): Promise<void> {\n if (this.registry) {\n this.registry.validate(aType, NODE_RELATION, aType, data, this.scopePath);\n }\n const docId = computeNodeDocId(uid);\n const record = buildWritableNodeRecord(aType, uid, data);\n if (this.registry) {\n const entry = this.registry.lookup(aType, NODE_RELATION, aType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n this.backend.setDoc(docId, record);\n }\n\n async putEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n if (this.registry) {\n this.registry.validate(aType, axbType, bType, data, this.scopePath);\n }\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = buildWritableEdgeRecord(aType, aUid, axbType, bType, bUid, data);\n if (this.registry) {\n const entry = this.registry.lookup(aType, axbType, bType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n this.backend.setDoc(docId, record);\n }\n\n async updateNode(uid: string, data: Record<string, unknown>): Promise<void> {\n const docId = computeNodeDocId(uid);\n this.backend.updateDoc(docId, { dataFields: data });\n }\n\n async removeNode(uid: string): Promise<void> {\n const docId = computeNodeDocId(uid);\n this.backend.deleteDoc(docId);\n }\n\n async removeEdge(aUid: string, axbType: string, bUid: string): Promise<void> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n this.backend.deleteDoc(docId);\n }\n\n async commit(): Promise<void> {\n await this.backend.commit();\n }\n}\n","import { createHash } from 'node:crypto';\n\nimport { NODE_RELATION } from './internal/constants.js';\nimport { createRegistry } from './registry.js';\nimport { compileMigrations, precompileSource } from './sandbox.js';\nimport type {\n EdgeTypeData,\n GraphReader,\n GraphRegistry,\n MigrationExecutor,\n NodeTypeData,\n RegistryEntry,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Meta-type constants\n// ---------------------------------------------------------------------------\n\n/** The aType used for node type definition meta-nodes. */\nexport const META_NODE_TYPE = 'nodeType';\n\n/** The aType used for edge type definition meta-nodes. */\nexport const META_EDGE_TYPE = 'edgeType';\n\n// ---------------------------------------------------------------------------\n// JSON Schemas for meta-type data payloads\n// ---------------------------------------------------------------------------\n\n/** JSON Schema for a single stored migration step. */\nconst STORED_MIGRATION_STEP_SCHEMA = {\n type: 'object',\n required: ['fromVersion', 'toVersion', 'up'],\n properties: {\n fromVersion: { type: 'integer', minimum: 0 },\n toVersion: { type: 'integer', minimum: 1 },\n up: { type: 'string', minLength: 1 },\n },\n additionalProperties: false,\n};\n\n/** JSON Schema for the `data` payload of a `nodeType` meta-node. */\nexport const NODE_TYPE_SCHEMA: object = {\n type: 'object',\n required: ['name', 'jsonSchema'],\n properties: {\n name: { type: 'string', minLength: 1 },\n jsonSchema: { type: 'object' },\n description: { type: 'string' },\n titleField: { type: 'string' },\n subtitleField: { type: 'string' },\n viewTemplate: { type: 'string' },\n viewCss: { type: 'string' },\n allowedIn: { type: 'array', items: { type: 'string', minLength: 1 } },\n schemaVersion: { type: 'integer', minimum: 0 },\n migrations: { type: 'array', items: STORED_MIGRATION_STEP_SCHEMA },\n migrationWriteBack: { type: 'string', enum: ['off', 'eager', 'background'] },\n },\n additionalProperties: false,\n};\n\n/** JSON Schema for the `data` payload of an `edgeType` meta-node. */\nexport const EDGE_TYPE_SCHEMA: object = {\n type: 'object',\n required: ['name', 'from', 'to'],\n properties: {\n name: { type: 'string', minLength: 1 },\n from: {\n oneOf: [\n { type: 'string', minLength: 1 },\n { type: 'array', items: { type: 'string', minLength: 1 }, minItems: 1 },\n ],\n },\n to: {\n oneOf: [\n { type: 'string', minLength: 1 },\n { type: 'array', items: { type: 'string', minLength: 1 }, minItems: 1 },\n ],\n },\n jsonSchema: { type: 'object' },\n inverseLabel: { type: 'string' },\n description: { type: 'string' },\n titleField: { type: 'string' },\n subtitleField: { type: 'string' },\n viewTemplate: { type: 'string' },\n viewCss: { type: 'string' },\n allowedIn: { type: 'array', items: { type: 'string', minLength: 1 } },\n targetGraph: { type: 'string', minLength: 1, pattern: '^[^/]+$' },\n schemaVersion: { type: 'integer', minimum: 0 },\n migrations: { type: 'array', items: STORED_MIGRATION_STEP_SCHEMA },\n migrationWriteBack: { type: 'string', enum: ['off', 'eager', 'background'] },\n },\n additionalProperties: false,\n};\n\n// ---------------------------------------------------------------------------\n// Bootstrap registry\n// ---------------------------------------------------------------------------\n\n/** Registry entries for the two meta-types (always present). */\nexport const BOOTSTRAP_ENTRIES: readonly RegistryEntry[] = [\n {\n aType: META_NODE_TYPE,\n axbType: NODE_RELATION,\n bType: META_NODE_TYPE,\n jsonSchema: NODE_TYPE_SCHEMA,\n description: 'Meta-type: defines a node type',\n },\n {\n aType: META_EDGE_TYPE,\n axbType: NODE_RELATION,\n bType: META_EDGE_TYPE,\n jsonSchema: EDGE_TYPE_SCHEMA,\n description: 'Meta-type: defines an edge type',\n },\n];\n\n/**\n * Build the bootstrap registry that validates meta-type writes.\n * This is always available, even before any dynamic types are loaded.\n */\nexport function createBootstrapRegistry(): GraphRegistry {\n return createRegistry([...BOOTSTRAP_ENTRIES]);\n}\n\n// ---------------------------------------------------------------------------\n// Deterministic UID generation\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a deterministic UID for a meta-type definition.\n * This ensures that defining the same type name always targets the same\n * Firestore document, enabling upsert semantics.\n *\n * Format: 21-char base64url substring of SHA-256(`metaType:name`).\n */\nexport function generateDeterministicUid(metaType: string, name: string): string {\n const hash = createHash('sha256').update(`${metaType}:${name}`).digest('base64url');\n return hash.slice(0, 21);\n}\n\n// ---------------------------------------------------------------------------\n// createRegistryFromGraph\n// ---------------------------------------------------------------------------\n\n/**\n * Read meta-type nodes from the graph and compile them into a GraphRegistry.\n *\n * The returned registry includes both the dynamic entries AND the bootstrap\n * meta-type entries, so meta-type writes remain validateable after a reload.\n *\n * @param reader - A GraphReader pointed at the collection containing meta-nodes.\n * @param executor - Optional custom executor for compiling stored migration source strings.\n */\nexport async function createRegistryFromGraph(\n reader: GraphReader,\n executor?: MigrationExecutor,\n): Promise<GraphRegistry> {\n const [nodeTypes, edgeTypes] = await Promise.all([\n reader.findNodes({ aType: META_NODE_TYPE }),\n reader.findNodes({ aType: META_EDGE_TYPE }),\n ]);\n\n const entries: RegistryEntry[] = [...BOOTSTRAP_ENTRIES];\n\n // Eagerly pre-validate all migration sources in the sandbox before building\n // the registry. This ensures reloadRegistry() fails fast on invalid sources.\n const prevalidations: Promise<void>[] = [];\n for (const record of nodeTypes) {\n const data = record.data as unknown as NodeTypeData;\n if (data.migrations) {\n for (const m of data.migrations) {\n prevalidations.push(precompileSource(m.up, executor));\n }\n }\n }\n for (const record of edgeTypes) {\n const data = record.data as unknown as EdgeTypeData;\n if (data.migrations) {\n for (const m of data.migrations) {\n prevalidations.push(precompileSource(m.up, executor));\n }\n }\n }\n await Promise.all(prevalidations);\n\n // Convert nodeType records → self-loop RegistryEntries\n for (const record of nodeTypes) {\n const data = record.data as unknown as NodeTypeData;\n entries.push({\n aType: data.name,\n axbType: NODE_RELATION,\n bType: data.name,\n jsonSchema: data.jsonSchema,\n description: data.description,\n titleField: data.titleField,\n subtitleField: data.subtitleField,\n allowedIn: data.allowedIn,\n migrations: data.migrations ? compileMigrations(data.migrations, executor) : undefined,\n migrationWriteBack: data.migrationWriteBack,\n });\n }\n\n // Convert edgeType records → RegistryEntries (expand from/to arrays)\n for (const record of edgeTypes) {\n const data = record.data as unknown as EdgeTypeData;\n const fromTypes = Array.isArray(data.from) ? data.from : [data.from];\n const toTypes = Array.isArray(data.to) ? data.to : [data.to];\n\n const compiledMigrations = data.migrations\n ? compileMigrations(data.migrations, executor)\n : undefined;\n\n for (const aType of fromTypes) {\n for (const bType of toTypes) {\n entries.push({\n aType,\n axbType: data.name,\n bType,\n jsonSchema: data.jsonSchema,\n description: data.description,\n inverseLabel: data.inverseLabel,\n titleField: data.titleField,\n subtitleField: data.subtitleField,\n allowedIn: data.allowedIn,\n targetGraph: data.targetGraph,\n migrations: compiledMigrations,\n migrationWriteBack: data.migrationWriteBack,\n });\n }\n }\n }\n\n return createRegistry(entries);\n}\n","export class FiregraphError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n ) {\n super(message);\n this.name = 'FiregraphError';\n }\n}\n\nexport class NodeNotFoundError extends FiregraphError {\n constructor(uid: string) {\n super(`Node not found: ${uid}`, 'NODE_NOT_FOUND');\n this.name = 'NodeNotFoundError';\n }\n}\n\nexport class EdgeNotFoundError extends FiregraphError {\n constructor(aUid: string, axbType: string, bUid: string) {\n super(`Edge not found: ${aUid} -[${axbType}]-> ${bUid}`, 'EDGE_NOT_FOUND');\n this.name = 'EdgeNotFoundError';\n }\n}\n\nexport class ValidationError extends FiregraphError {\n constructor(\n message: string,\n public readonly details?: unknown,\n ) {\n super(message, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\nexport class RegistryViolationError extends FiregraphError {\n constructor(aType: string, axbType: string, bType: string) {\n super(`Unregistered triple: (${aType}) -[${axbType}]-> (${bType})`, 'REGISTRY_VIOLATION');\n this.name = 'RegistryViolationError';\n }\n}\n\nexport class InvalidQueryError extends FiregraphError {\n constructor(message: string) {\n super(message, 'INVALID_QUERY');\n this.name = 'InvalidQueryError';\n }\n}\n\nexport class TraversalError extends FiregraphError {\n constructor(message: string) {\n super(message, 'TRAVERSAL_ERROR');\n this.name = 'TraversalError';\n }\n}\n\nexport class DynamicRegistryError extends FiregraphError {\n constructor(message: string) {\n super(message, 'DYNAMIC_REGISTRY_ERROR');\n this.name = 'DynamicRegistryError';\n }\n}\n\nexport class QuerySafetyError extends FiregraphError {\n constructor(message: string) {\n super(message, 'QUERY_SAFETY');\n this.name = 'QuerySafetyError';\n }\n}\n\nexport class RegistryScopeError extends FiregraphError {\n constructor(\n aType: string,\n axbType: string,\n bType: string,\n scopePath: string,\n allowedIn: string[],\n ) {\n super(\n `Type (${aType}) -[${axbType}]-> (${bType}) is not allowed at scope \"${scopePath || 'root'}\". ` +\n `Allowed in: [${allowedIn.join(', ')}]`,\n 'REGISTRY_SCOPE',\n );\n this.name = 'RegistryScopeError';\n }\n}\n\nexport class MigrationError extends FiregraphError {\n constructor(message: string) {\n super(message, 'MIGRATION_ERROR');\n this.name = 'MigrationError';\n }\n}\n\n/**\n * Thrown when a caller tries to perform an operation that would require\n * atomicity across two physical storage backends — e.g. opening a routed\n * subgraph client from inside a transaction callback. Cross-backend\n * atomicity cannot be honoured by any of the underlying drivers (D1, DO\n * SQLite, Firestore), so firegraph surfaces this as a typed error instead\n * of silently confining the write to the base backend.\n *\n * Normally `TransactionBackend` and `BatchBackend` don't expose `subgraph()`\n * at the type level, so this error is unreachable through well-typed code.\n * It exists as a public catchable type for app code that needs to tolerate\n * this case deliberately (e.g. dynamic code paths that bypass the type\n * system) and as future-proofing if the interface ever grows a way to\n * request a sub-scope inside a transaction.\n */\nexport class CrossBackendTransactionError extends FiregraphError {\n constructor(message: string) {\n super(message, 'CROSS_BACKEND_TRANSACTION');\n this.name = 'CrossBackendTransactionError';\n }\n}\n","/**\n * JSON Schema validation and introspection utilities.\n *\n * Standard JSON Schema validation and introspection\n * processing. Uses ajv for validation and a recursive walker for converting\n * JSON Schema properties into FieldMeta objects for editor form generation.\n */\n\nimport Ajv from 'ajv';\nimport addFormats from 'ajv-formats';\n\nimport { ValidationError } from './errors.js';\n\n// ---------------------------------------------------------------------------\n// FieldMeta types (previously in editor/server/schema-introspect.ts)\n// ---------------------------------------------------------------------------\n\nexport interface FieldMeta {\n name: string;\n type: 'string' | 'number' | 'boolean' | 'enum' | 'array' | 'object' | 'unknown';\n required: boolean;\n description?: string;\n enumValues?: string[];\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n min?: number;\n max?: number;\n isInt?: boolean;\n itemMeta?: FieldMeta;\n fields?: FieldMeta[];\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nconst ajv = new Ajv({ allErrors: true, strict: false });\naddFormats(ajv);\n\n/**\n * Compile a JSON Schema into a validation function.\n * The returned function throws `ValidationError` if data is invalid.\n */\nexport function compileSchema(schema: object, label?: string): (data: unknown) => void {\n const validate = ajv.compile(schema);\n return (data: unknown) => {\n if (!validate(data)) {\n const errors = validate.errors ?? [];\n const messages = errors\n .map((err) => `${err.instancePath || '/'}${err.message ? ': ' + err.message : ''}`)\n .join('; ');\n throw new ValidationError(\n `Data validation failed${label ? ' for ' + label : ''}: ${messages}`,\n errors,\n );\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// JSON Schema → FieldMeta introspection\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a JSON Schema (expected to be `type: \"object\"`) into `FieldMeta[]`\n * suitable for the editor's SchemaForm component.\n */\nexport function jsonSchemaToFieldMeta(schema: any): FieldMeta[] {\n if (!schema || schema.type !== 'object' || !schema.properties) return [];\n\n const requiredSet = new Set<string>(Array.isArray(schema.required) ? schema.required : []);\n\n return Object.entries(schema.properties).map(([name, prop]) =>\n propertyToFieldMeta(name, prop as any, requiredSet.has(name)),\n );\n}\n\n/**\n * Convert a single JSON Schema property into a `FieldMeta`.\n */\nfunction propertyToFieldMeta(name: string, prop: any, required: boolean): FieldMeta {\n if (!prop) return { name, type: 'unknown', required };\n\n // Handle enum (can appear with or without type)\n if (Array.isArray(prop.enum)) {\n return {\n name,\n type: 'enum',\n required,\n enumValues: prop.enum as string[],\n description: prop.description,\n };\n }\n\n // Handle oneOf/anyOf for nullable patterns like { oneOf: [{type:'string'}, {type:'null'}] }\n if (Array.isArray(prop.oneOf) || Array.isArray(prop.anyOf)) {\n const variants = (prop.oneOf ?? prop.anyOf) as any[];\n const nonNull = variants.filter((v: any) => v.type !== 'null');\n if (nonNull.length === 1) {\n // Nullable wrapper — unwrap and mark as optional\n return propertyToFieldMeta(name, nonNull[0], false);\n }\n return { name, type: 'unknown', required, description: prop.description };\n }\n\n const type = prop.type;\n\n if (type === 'string') {\n return {\n name,\n type: 'string',\n required,\n minLength: prop.minLength,\n maxLength: prop.maxLength,\n pattern: prop.pattern,\n description: prop.description,\n };\n }\n\n if (type === 'number' || type === 'integer') {\n return {\n name,\n type: 'number',\n required,\n min: prop.minimum,\n max: prop.maximum,\n isInt: type === 'integer' ? true : undefined,\n description: prop.description,\n };\n }\n\n if (type === 'boolean') {\n return { name, type: 'boolean', required, description: prop.description };\n }\n\n if (type === 'array') {\n const itemMeta = prop.items ? propertyToFieldMeta('item', prop.items, true) : undefined;\n return {\n name,\n type: 'array',\n required,\n itemMeta,\n description: prop.description,\n };\n }\n\n if (type === 'object') {\n return {\n name,\n type: 'object',\n required,\n fields: jsonSchemaToFieldMeta(prop),\n description: prop.description,\n };\n }\n\n return { name, type: 'unknown', required, description: prop.description };\n}\n","/**\n * Migration pipeline for auto-migrating records on read.\n *\n * When a record's `v` is behind the version derived from the registry\n * entry's migrations, the pipeline applies migration steps sequentially\n * to bring the data up to the current version.\n */\n\nimport { MigrationError } from './errors.js';\nimport type {\n GraphRegistry,\n MigrationStep,\n MigrationWriteBack,\n StoredGraphRecord,\n} from './types.js';\n\n/** Result of attempting to migrate a single record. */\nexport interface MigrationResult {\n record: StoredGraphRecord;\n migrated: boolean;\n /** Resolved write-back mode for this record (entry-level > global > 'off'). */\n writeBack: MigrationWriteBack;\n}\n\n/**\n * Apply a chain of migration steps to transform data from `currentVersion`\n * to `targetVersion`. Throws `MigrationError` if the chain is incomplete\n * or a migration function fails.\n *\n * Returns the migrated data payload only — the caller is responsible for\n * stamping `v` on the record envelope.\n */\nexport async function applyMigrationChain(\n data: Record<string, unknown>,\n currentVersion: number,\n targetVersion: number,\n migrations: MigrationStep[],\n): Promise<Record<string, unknown>> {\n const sorted = [...migrations].sort((a, b) => a.fromVersion - b.fromVersion);\n let result = { ...data };\n let version = currentVersion;\n\n for (const step of sorted) {\n if (step.fromVersion === version) {\n try {\n result = await step.up(result);\n } catch (err: unknown) {\n if (err instanceof MigrationError) throw err;\n throw new MigrationError(\n `Migration from v${step.fromVersion} to v${step.toVersion} failed: ${(err as Error).message}`,\n );\n }\n if (!result || typeof result !== 'object') {\n throw new MigrationError(\n `Migration from v${step.fromVersion} to v${step.toVersion} returned invalid data (expected object)`,\n );\n }\n version = step.toVersion;\n }\n }\n\n if (version !== targetVersion) {\n throw new MigrationError(\n `Incomplete migration chain: reached v${version} but target is v${targetVersion}`,\n );\n }\n\n return result;\n}\n\n/**\n * Validate that a migration chain forms a contiguous path from version 0\n * to the highest `toVersion`. Throws `MigrationError` if the chain has\n * gaps or duplicate `fromVersion` values.\n *\n * Called at registry construction time to catch incomplete chains early,\n * rather than at read time when a record is migrated.\n */\nexport function validateMigrationChain(\n migrations: MigrationStep[],\n label: string,\n): void {\n if (migrations.length === 0) return;\n\n // Validate individual steps\n const seen = new Set<number>();\n for (const step of migrations) {\n if (step.toVersion <= step.fromVersion) {\n throw new MigrationError(\n `${label}: migration step has toVersion (${step.toVersion}) <= fromVersion (${step.fromVersion})`,\n );\n }\n if (seen.has(step.fromVersion)) {\n throw new MigrationError(\n `${label}: duplicate migration step for fromVersion ${step.fromVersion}`,\n );\n }\n seen.add(step.fromVersion);\n }\n\n const sorted = [...migrations].sort((a, b) => a.fromVersion - b.fromVersion);\n const targetVersion = Math.max(...migrations.map((m) => m.toVersion));\n let version = 0;\n\n for (const step of sorted) {\n if (step.fromVersion === version) {\n version = step.toVersion;\n } else if (step.fromVersion > version) {\n throw new MigrationError(\n `${label}: migration chain has a gap — no step covers v${version} → v${step.fromVersion}`,\n );\n }\n }\n\n if (version !== targetVersion) {\n throw new MigrationError(\n `${label}: migration chain does not reach v${targetVersion} (stuck at v${version})`,\n );\n }\n}\n\n/**\n * Attempt to migrate a single record based on its registry entry.\n *\n * Returns the original record unchanged if no migration is needed\n * (no schema version, already at current version, or no migrations defined).\n */\nexport async function migrateRecord(\n record: StoredGraphRecord,\n registry: GraphRegistry,\n globalWriteBack: MigrationWriteBack = 'off',\n): Promise<MigrationResult> {\n const entry = registry.lookup(record.aType, record.axbType, record.bType);\n\n if (!entry?.migrations?.length || !entry.schemaVersion) {\n return { record, migrated: false, writeBack: 'off' };\n }\n\n const currentVersion = record.v ?? 0;\n\n if (currentVersion >= entry.schemaVersion) {\n return { record, migrated: false, writeBack: 'off' };\n }\n\n const migratedData = await applyMigrationChain(\n record.data,\n currentVersion,\n entry.schemaVersion,\n entry.migrations,\n );\n\n // Two-tier resolution: entry-level > global > 'off'\n const writeBack = entry.migrationWriteBack ?? globalWriteBack ?? 'off';\n\n return {\n record: { ...record, data: migratedData, v: entry.schemaVersion },\n migrated: true,\n writeBack,\n };\n}\n\n/**\n * Migrate an array of records, returning all results.\n * If any single migration fails, the entire call rejects — a broken\n * migration function is a bug that should surface immediately.\n */\nexport async function migrateRecords(\n records: StoredGraphRecord[],\n registry: GraphRegistry,\n globalWriteBack: MigrationWriteBack = 'off',\n): Promise<MigrationResult[]> {\n return Promise.all(\n records.map((r) => migrateRecord(r, registry, globalWriteBack)),\n );\n}\n","/**\n * Scope path matching for subgraph-level registry constraints.\n *\n * Scope paths are slash-separated names derived from the chain of\n * `subgraph()` calls (e.g., `'agents'`, `'agents/memories'`).\n * The root graph has an empty scope path (`''`).\n *\n * Patterns:\n * - `'root'` — matches only the root graph (empty scope path)\n * - `'agents'` — matches exactly `'agents'`\n * - `'agents/memories'` — matches exactly `'agents/memories'`\n * - `'*/agents'` — `*` matches one segment: `'foo/agents'` but not `'a/b/agents'`\n * - `'**/memories'` — `**` matches zero or more segments\n * - `'**'` — matches everything including root\n */\n\n/**\n * Test whether a scope path matches a single pattern.\n *\n * @param scopePath - The current scope path (empty string for root)\n * @param pattern - The pattern to match against\n */\nexport function matchScope(scopePath: string, pattern: string): boolean {\n // Special case: 'root' matches only the root graph\n if (pattern === 'root') return scopePath === '';\n\n // Special case: '**' matches everything\n if (pattern === '**') return true;\n\n const pathSegments = scopePath === '' ? [] : scopePath.split('/');\n const patternSegments = pattern.split('/');\n\n return matchSegments(pathSegments, 0, patternSegments, 0);\n}\n\n/**\n * Test whether a scope path matches any pattern in a list.\n * Returns `true` if the list is empty or undefined (allowed everywhere).\n *\n * @param scopePath - The current scope path (empty string for root)\n * @param patterns - Array of patterns to match against\n */\nexport function matchScopeAny(scopePath: string, patterns: string[]): boolean {\n if (!patterns || patterns.length === 0) return true;\n return patterns.some((p) => matchScope(scopePath, p));\n}\n\n/**\n * Recursive segment matcher with support for `*` (one segment) and\n * `**` (zero or more segments).\n */\nfunction matchSegments(\n path: string[],\n pi: number,\n pattern: string[],\n qi: number,\n): boolean {\n // Both exhausted — match\n if (pi === path.length && qi === pattern.length) return true;\n\n // Pattern exhausted but path remains — no match\n if (qi === pattern.length) return false;\n\n const seg = pattern[qi];\n\n if (seg === '**') {\n // '**' at the end of pattern — matches everything remaining\n if (qi === pattern.length - 1) return true;\n\n // Try consuming 0, 1, 2, ... path segments\n for (let skip = 0; skip <= path.length - pi; skip++) {\n if (matchSegments(path, pi + skip, pattern, qi + 1)) return true;\n }\n return false;\n }\n\n // Path exhausted but pattern has non-** segments remaining — no match\n if (pi === path.length) return false;\n\n if (seg === '*') {\n // '*' matches exactly one segment\n return matchSegments(path, pi + 1, pattern, qi + 1);\n }\n\n // Literal match\n if (path[pi] === seg) {\n return matchSegments(path, pi + 1, pattern, qi + 1);\n }\n\n return false;\n}\n","import { RegistryScopeError, RegistryViolationError, ValidationError } from './errors.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport { compileSchema } from './json-schema.js';\nimport { validateMigrationChain } from './migration.js';\nimport { matchScopeAny } from './scope.js';\nimport type { DiscoveryResult, GraphRegistry, RegistryEntry } from './types.js';\n\nfunction tripleKey(aType: string, axbType: string, bType: string): string {\n return `${aType}:${axbType}:${bType}`;\n}\n\nfunction tripleKeyFor(e: RegistryEntry): string {\n return tripleKey(e.aType, e.axbType, e.bType);\n}\n\n/**\n * Build a registry from either explicit entries or a DiscoveryResult.\n *\n * @example\n * ```ts\n * // From explicit entries (programmatic)\n * const registry = createRegistry([\n * { aType: 'user', axbType: 'is', bType: 'user', jsonSchema: userSchema },\n * { aType: 'user', axbType: 'follows', bType: 'user', jsonSchema: followsSchema },\n * ]);\n *\n * // From discovery result (folder convention)\n * const discovered = await discoverEntities('./entities');\n * const registry = createRegistry(discovered);\n * ```\n */\nexport function createRegistry(input: RegistryEntry[] | DiscoveryResult): GraphRegistry {\n const map = new Map<string, { entry: RegistryEntry; validate?: (data: unknown) => void }>();\n\n let entries: RegistryEntry[];\n\n if (Array.isArray(input)) {\n entries = input;\n } else {\n entries = discoveryToEntries(input);\n }\n\n const entryList: ReadonlyArray<RegistryEntry> = Object.freeze([...entries]);\n\n for (const entry of entries) {\n if (entry.targetGraph && entry.targetGraph.includes('/')) {\n throw new ValidationError(\n `Entry (${entry.aType}) -[${entry.axbType}]-> (${entry.bType}) has invalid targetGraph \"${entry.targetGraph}\" — must be a single segment (no \"/\")`,\n );\n }\n if (entry.migrations?.length) {\n const label = `Entry (${entry.aType}) -[${entry.axbType}]-> (${entry.bType})`;\n validateMigrationChain(entry.migrations, label);\n // Derive schemaVersion from migrations — single source of truth\n entry.schemaVersion = Math.max(...entry.migrations.map((m) => m.toVersion));\n } else {\n // No migrations → no versioning (ignore any user-supplied schemaVersion)\n entry.schemaVersion = undefined;\n }\n const key = tripleKey(entry.aType, entry.axbType, entry.bType);\n const validator = entry.jsonSchema\n ? compileSchema(entry.jsonSchema, `(${entry.aType}) -[${entry.axbType}]-> (${entry.bType})`)\n : undefined;\n map.set(key, { entry, validate: validator });\n }\n\n // Build axbType index for lookupByAxbType\n const axbIndex = new Map<string, ReadonlyArray<RegistryEntry>>();\n const axbBuild = new Map<string, RegistryEntry[]>();\n for (const entry of entries) {\n const existing = axbBuild.get(entry.axbType);\n if (existing) {\n existing.push(entry);\n } else {\n axbBuild.set(entry.axbType, [entry]);\n }\n }\n for (const [key, arr] of axbBuild) {\n axbIndex.set(key, Object.freeze(arr));\n }\n\n return {\n lookup(aType: string, axbType: string, bType: string): RegistryEntry | undefined {\n return map.get(tripleKey(aType, axbType, bType))?.entry;\n },\n\n lookupByAxbType(axbType: string): ReadonlyArray<RegistryEntry> {\n return axbIndex.get(axbType) ?? [];\n },\n\n validate(\n aType: string,\n axbType: string,\n bType: string,\n data: unknown,\n scopePath?: string,\n ): void {\n const rec = map.get(tripleKey(aType, axbType, bType));\n\n if (!rec) {\n throw new RegistryViolationError(aType, axbType, bType);\n }\n\n // Scope validation: check allowedIn patterns when a scope context is provided\n if (scopePath !== undefined && rec.entry.allowedIn && rec.entry.allowedIn.length > 0) {\n if (!matchScopeAny(scopePath, rec.entry.allowedIn)) {\n throw new RegistryScopeError(aType, axbType, bType, scopePath, rec.entry.allowedIn);\n }\n }\n\n if (rec.validate) {\n try {\n rec.validate(data);\n } catch (err: unknown) {\n if (err instanceof ValidationError) throw err;\n throw new ValidationError(\n `Data validation failed for (${aType}) -[${axbType}]-> (${bType})`,\n err,\n );\n }\n }\n },\n\n entries(): ReadonlyArray<RegistryEntry> {\n return entryList;\n },\n };\n}\n\n/**\n * Create a merged registry where `base` entries take priority and `extension`\n * entries fill in gaps. Lookups and validation check `base` first; only if the\n * triple is not found there does the merged registry fall through to\n * `extension`.\n *\n * The `entries()` method returns a deduplicated list (base wins on collision).\n * The `lookupByAxbType()` method merges results from both registries,\n * deduplicating by triple key with base entries winning.\n */\nexport function createMergedRegistry(base: GraphRegistry, extension: GraphRegistry): GraphRegistry {\n // Build a set of triple keys from the base registry for fast collision checks.\n const baseKeys = new Set(base.entries().map(tripleKeyFor));\n\n return {\n lookup(aType: string, axbType: string, bType: string): RegistryEntry | undefined {\n return base.lookup(aType, axbType, bType) ?? extension.lookup(aType, axbType, bType);\n },\n\n lookupByAxbType(axbType: string): ReadonlyArray<RegistryEntry> {\n const baseResults = base.lookupByAxbType(axbType);\n const extResults = extension.lookupByAxbType(axbType);\n if (extResults.length === 0) return baseResults;\n if (baseResults.length === 0) return extResults;\n\n // Merge, base wins on triple-key collision\n const seen = new Set(baseResults.map(tripleKeyFor));\n const merged = [...baseResults];\n for (const entry of extResults) {\n if (!seen.has(tripleKeyFor(entry))) {\n merged.push(entry);\n }\n }\n return Object.freeze(merged);\n },\n\n validate(\n aType: string,\n axbType: string,\n bType: string,\n data: unknown,\n scopePath?: string,\n ): void {\n if (baseKeys.has(tripleKey(aType, axbType, bType))) {\n return base.validate(aType, axbType, bType, data, scopePath);\n }\n // Falls through to extension (which throws RegistryViolationError if not found)\n return extension.validate(aType, axbType, bType, data, scopePath);\n },\n\n entries(): ReadonlyArray<RegistryEntry> {\n const extEntries = extension.entries();\n if (extEntries.length === 0) return base.entries();\n\n const merged = [...base.entries()];\n for (const entry of extEntries) {\n if (!baseKeys.has(tripleKeyFor(entry))) {\n merged.push(entry);\n }\n }\n return Object.freeze(merged);\n },\n };\n}\n\n/**\n * Convert a DiscoveryResult into flat RegistryEntry[].\n * Nodes become self-loop triples `(name, 'is', name)`.\n * Edges expand `from`/`to` arrays into one triple per combination.\n */\nfunction discoveryToEntries(discovery: DiscoveryResult): RegistryEntry[] {\n const entries: RegistryEntry[] = [];\n\n // Nodes → self-loop triples\n for (const [name, entity] of discovery.nodes) {\n entries.push({\n aType: name,\n axbType: NODE_RELATION,\n bType: name,\n jsonSchema: entity.schema,\n description: entity.description,\n titleField: entity.titleField,\n subtitleField: entity.subtitleField,\n allowedIn: entity.allowedIn,\n migrations: entity.migrations,\n migrationWriteBack: entity.migrationWriteBack,\n });\n }\n\n // Edges → expand from/to into one triple per combination\n for (const [axbType, entity] of discovery.edges) {\n const topology = entity.topology;\n if (!topology) continue;\n\n const fromTypes = Array.isArray(topology.from) ? topology.from : [topology.from];\n const toTypes = Array.isArray(topology.to) ? topology.to : [topology.to];\n\n const resolvedTargetGraph = entity.targetGraph ?? topology.targetGraph;\n if (resolvedTargetGraph && resolvedTargetGraph.includes('/')) {\n throw new ValidationError(\n `Edge \"${axbType}\" has invalid targetGraph \"${resolvedTargetGraph}\" — must be a single segment (no \"/\")`,\n );\n }\n\n for (const aType of fromTypes) {\n for (const bType of toTypes) {\n entries.push({\n aType,\n axbType,\n bType,\n jsonSchema: entity.schema,\n description: entity.description,\n inverseLabel: topology.inverseLabel,\n titleField: entity.titleField,\n subtitleField: entity.subtitleField,\n allowedIn: entity.allowedIn,\n targetGraph: resolvedTargetGraph,\n migrations: entity.migrations,\n migrationWriteBack: entity.migrationWriteBack,\n });\n }\n }\n }\n\n return entries;\n}\n","/**\n * Sandbox module for compiling dynamic registry migration source strings\n * into executable functions.\n *\n * Uses a dedicated worker thread with SES (Secure ECMAScript) Compartments\n * for isolation. SES `lockdown()` and `Compartment` evaluation run in the\n * worker thread so that the host process's intrinsics remain unaffected.\n *\n * Each migration function runs in a hardened compartment with no ambient\n * authority — no access to `process`, `require`, `fetch`, `setTimeout`,\n * or any other host-provided globals. Data crosses the compartment boundary\n * as JSON strings to prevent prototype chain escapes.\n *\n * Static registry migrations are already in-memory functions and never\n * go through this module.\n */\n\nimport { createHash } from 'node:crypto';\nimport type { Worker } from 'node:worker_threads';\n\nimport { MigrationError } from './errors.js';\nimport type * as SerializationModule from './serialization.js';\nimport type {\n MigrationExecutor,\n MigrationFn,\n MigrationStep,\n StoredMigrationStep,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Sandbox worker — SES lockdown and Compartment evaluation run in a\n// dedicated worker thread so that lockdown() does not affect the host\n// process's intrinsics. The worker is spawned lazily on first use.\n// ---------------------------------------------------------------------------\n\nlet _worker: Worker | null = null;\nlet _requestId = 0;\nconst _pending = new Map<\n number,\n {\n resolve: (value: unknown) => void;\n reject: (reason: Error) => void;\n }\n>();\n\n/**\n * Inline worker source evaluated as CJS in a dedicated worker thread.\n * Contains all SES setup, compilation, and execution logic.\n *\n * **Why inline?** Using `new Worker(code, { eval: true })` avoids\n * ESM/CJS file resolution issues when the library is consumed from\n * different module formats or bundlers.\n */\nconst WORKER_SOURCE = [\n `'use strict';`,\n `var _wt = require('node:worker_threads');`,\n `var _mod = require('node:module');`,\n `var _crypto = require('node:crypto');`,\n `var parentPort = _wt.parentPort;`,\n `var workerData = _wt.workerData;`,\n ``,\n `// Load SES using the parent module's resolution context`,\n `var esmRequire = _mod.createRequire(workerData.parentUrl);`,\n `esmRequire('ses');`,\n ``,\n `lockdown({`,\n ` errorTaming: 'unsafe',`,\n ` consoleTaming: 'unsafe',`,\n ` evalTaming: 'safe-eval',`,\n ` overrideTaming: 'moderate',`,\n ` stackFiltering: 'verbose'`,\n `});`,\n ``,\n `// Defense-in-depth: verify lockdown() actually hardened JSON.`,\n `if (!Object.isFrozen(JSON)) {`,\n ` throw new Error('SES lockdown failed: JSON is not frozen');`,\n `}`,\n ``,\n `var cache = new Map();`,\n ``,\n `function hashSource(s) {`,\n ` return _crypto.createHash('sha256').update(s).digest('hex');`,\n `}`,\n ``,\n `function buildWrapper(source) {`,\n ` return '(function() {' +`,\n ` ' var fn = (' + source + ');\\\\n' +`,\n ` ' if (typeof fn !== \"function\") return null;\\\\n' +`,\n ` ' return function(jsonIn) {\\\\n' +`,\n ` ' var data = JSON.parse(jsonIn);\\\\n' +`,\n ` ' var result = fn(data);\\\\n' +`,\n ` ' if (result !== null && typeof result === \"object\" && typeof result.then === \"function\") {\\\\n' +`,\n ` ' return result.then(function(r) { return JSON.stringify(r); });\\\\n' +`,\n ` ' }\\\\n' +`,\n ` ' return JSON.stringify(result);\\\\n' +`,\n ` ' };\\\\n' +`,\n ` '})()';`,\n `}`,\n ``,\n `function compileSource(source) {`,\n ` var key = hashSource(source);`,\n ` var cached = cache.get(key);`,\n ` if (cached) return cached;`,\n ``,\n ` var compartmentFn;`,\n ` try {`,\n ` var c = new Compartment({ JSON: JSON });`,\n ` compartmentFn = c.evaluate(buildWrapper(source));`,\n ` } catch (err) {`,\n ` throw new Error('Failed to compile migration source: ' + (err.message || String(err)));`,\n ` }`,\n ``,\n ` if (typeof compartmentFn !== 'function') {`,\n ` throw new Error('Migration source did not produce a function: ' + source.slice(0, 80));`,\n ` }`,\n ``,\n ` cache.set(key, compartmentFn);`,\n ` return compartmentFn;`,\n `}`,\n ``,\n `parentPort.on('message', function(msg) {`,\n ` var id = msg.id;`,\n ` try {`,\n ` if (msg.type === 'compile') {`,\n ` compileSource(msg.source);`,\n ` parentPort.postMessage({ id: id, type: 'compiled' });`,\n ` return;`,\n ` }`,\n ` if (msg.type === 'execute') {`,\n ` var fn = compileSource(msg.source);`,\n ` var raw;`,\n ` try {`,\n ` raw = fn(msg.jsonData);`,\n ` } catch (err) {`,\n ` parentPort.postMessage({ id: id, type: 'error', message: 'Migration function threw: ' + (err.message || String(err)) });`,\n ` return;`,\n ` }`,\n ` if (raw !== null && typeof raw === 'object' && typeof raw.then === 'function') {`,\n ` raw.then(`,\n ` function(jsonResult) {`,\n ` if (jsonResult === undefined || jsonResult === null) {`,\n ` parentPort.postMessage({ id: id, type: 'error', message: 'Migration returned a non-JSON-serializable value' });`,\n ` } else {`,\n ` parentPort.postMessage({ id: id, type: 'result', jsonResult: jsonResult });`,\n ` }`,\n ` },`,\n ` function(err) {`,\n ` parentPort.postMessage({ id: id, type: 'error', message: 'Async migration function threw: ' + (err.message || String(err)) });`,\n ` }`,\n ` );`,\n ` return;`,\n ` }`,\n ` if (raw === undefined || raw === null) {`,\n ` parentPort.postMessage({ id: id, type: 'error', message: 'Migration returned a non-JSON-serializable value' });`,\n ` } else {`,\n ` parentPort.postMessage({ id: id, type: 'result', jsonResult: raw });`,\n ` }`,\n ` }`,\n ` } catch (err) {`,\n ` parentPort.postMessage({ id: id, type: 'error', message: err.message || String(err) });`,\n ` }`,\n `});`,\n].join('\\n');\n\n// ---------------------------------------------------------------------------\n// Worker lifecycle management\n// ---------------------------------------------------------------------------\n\ninterface WorkerResponse {\n id: number;\n type: string;\n message?: string;\n jsonResult?: string;\n}\n\n// `node:worker_threads` is loaded lazily so this module can be imported in\n// runtimes without it (Cloudflare Workers, browsers). Only callers that\n// actually exercise the default migration sandbox will trigger the import.\nlet _WorkerCtor: (new (source: string, opts: Record<string, unknown>) => Worker) | null = null;\n\nasync function loadWorkerCtor(): Promise<NonNullable<typeof _WorkerCtor>> {\n if (_WorkerCtor) return _WorkerCtor;\n const wt = await import('node:worker_threads');\n _WorkerCtor = wt.Worker as unknown as NonNullable<typeof _WorkerCtor>;\n return _WorkerCtor;\n}\n\nasync function ensureWorker(): Promise<Worker> {\n if (_worker) return _worker;\n\n const Ctor = await loadWorkerCtor();\n _worker = new Ctor(WORKER_SOURCE, {\n eval: true,\n workerData: { parentUrl: import.meta.url },\n });\n\n // Don't let the worker prevent process exit\n _worker.unref();\n\n _worker.on('message', (msg: WorkerResponse) => {\n if (msg.id === undefined) return;\n const pending = _pending.get(msg.id);\n if (!pending) return;\n _pending.delete(msg.id);\n\n if (msg.type === 'error') {\n pending.reject(new MigrationError(msg.message ?? 'Unknown sandbox error'));\n } else {\n pending.resolve(msg);\n }\n });\n\n _worker.on('error', (err: Error) => {\n // Worker crashed — reject all pending requests and allow respawn\n for (const [, p] of _pending) {\n p.reject(new MigrationError(`Sandbox worker error: ${err.message}`));\n }\n _pending.clear();\n _worker = null;\n });\n\n _worker.on('exit', (code: number) => {\n // Always reject pending requests — a worker exiting while requests\n // are in-flight is always an error from the caller's perspective,\n // even if the exit code is 0 (e.g., graceful termination).\n if (_pending.size > 0) {\n for (const [, p] of _pending) {\n p.reject(new MigrationError(`Sandbox worker exited with code ${code}`));\n }\n _pending.clear();\n }\n _worker = null;\n });\n\n return _worker;\n}\n\nasync function sendToWorker(msg: Record<string, unknown>): Promise<WorkerResponse> {\n const worker = await ensureWorker();\n if (_requestId >= Number.MAX_SAFE_INTEGER) _requestId = 0;\n const id = ++_requestId;\n return new Promise<WorkerResponse>((resolve, reject) => {\n _pending.set(id, { resolve: resolve as (v: unknown) => void, reject });\n worker.postMessage({ ...msg, id });\n });\n}\n\n// ---------------------------------------------------------------------------\n// Compiled function cache (keyed by executor → SHA-256 hash of source string)\n// ---------------------------------------------------------------------------\n\n// Two-level cache: outer key is the executor reference (WeakMap so that\n// short-lived executors and their caches can be garbage collected), inner\n// key is the SHA-256 hash of the source string. This prevents cache\n// poisoning when different clients use different sandbox executors in\n// the same process.\nconst compiledCache = new WeakMap<MigrationExecutor, Map<string, MigrationFn>>();\n\nfunction getExecutorCache(executor: MigrationExecutor): Map<string, MigrationFn> {\n let cache = compiledCache.get(executor);\n if (!cache) {\n cache = new Map();\n compiledCache.set(executor, cache);\n }\n return cache;\n}\n\nfunction hashSource(source: string): string {\n return createHash('sha256').update(source).digest('hex');\n}\n\n// ---------------------------------------------------------------------------\n// Lazy serialization loader. Pulls `@google-cloud/firestore` only when the\n// default executor actually runs a migration — keeps Firestore out of\n// SQLite-only bundles (D1, DO-SQLite).\n// ---------------------------------------------------------------------------\n\nlet _serializationModule: typeof SerializationModule | null = null;\n\nasync function loadSerialization(): Promise<typeof SerializationModule> {\n if (_serializationModule) return _serializationModule;\n _serializationModule = await import('./serialization.js');\n return _serializationModule;\n}\n\n// ---------------------------------------------------------------------------\n// Default executor\n// ---------------------------------------------------------------------------\n\n/**\n * Default executor using a worker-thread SES Compartment with JSON marshaling.\n *\n * Migration source is compiled and executed inside an isolated SES\n * Compartment running in a dedicated worker thread. The worker calls\n * `lockdown()` in its own V8 isolate, leaving the host process's\n * intrinsics completely unaffected.\n *\n * Data crosses the compartment boundary as JSON strings, preventing\n * prototype chain escapes. The compartment receives only `JSON` as an\n * endowment for parsing/stringifying data.\n *\n * The returned `MigrationFn` always returns a `Promise` (communication\n * with the worker is inherently async via `postMessage`).\n */\nexport function defaultExecutor(source: string): MigrationFn {\n // Worker is spawned lazily on first execution via `sendToWorker`.\n // Eager spawning here would force a top-level `node:worker_threads`\n // load and break Cloudflare Workers / browser callers that never\n // exercise the default sandbox.\n\n // Return a MigrationFn that delegates to the worker thread.\n // Compilation + execution happen in the worker's SES Compartment.\n return (async (data: Record<string, unknown>) => {\n const { serializeFirestoreTypes, deserializeFirestoreTypes } = await loadSerialization();\n const jsonData = JSON.stringify(serializeFirestoreTypes(data));\n const response = await sendToWorker({ type: 'execute', source, jsonData });\n if (response.jsonResult === undefined || response.jsonResult === null) {\n throw new MigrationError('Migration returned a non-JSON-serializable value');\n }\n try {\n return deserializeFirestoreTypes(JSON.parse(response.jsonResult));\n } catch {\n throw new MigrationError('Migration returned a non-JSON-serializable value');\n }\n }) as MigrationFn;\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Eagerly validate a migration source string by compiling it in the\n * sandbox worker (or via a custom executor) without executing it.\n *\n * Use this to catch syntax errors at define-time or reload-time rather\n * than at first migration execution.\n *\n * @throws {MigrationError} If the source is syntactically invalid or\n * does not produce a function.\n */\nexport async function precompileSource(\n source: string,\n executor?: MigrationExecutor,\n): Promise<void> {\n if (executor && executor !== defaultExecutor) {\n // Custom executors validate synchronously the old way\n try {\n executor(source);\n } catch (err: unknown) {\n if (err instanceof MigrationError) throw err;\n throw new MigrationError(`Failed to compile migration source: ${(err as Error).message}`);\n }\n return;\n }\n\n // Default executor: send a compile-only message to the worker\n await sendToWorker({ type: 'compile', source });\n}\n\n/**\n * Compile a stored migration source string into an executable function.\n * Results are cached by SHA-256 hash of the source string so repeated\n * reads never re-parse the same migration.\n *\n * **Important:** When using the default executor, this function does NOT\n * validate the source synchronously — validation is deferred to the\n * worker thread at execution time. Callers that need eager validation\n * (e.g., `defineNodeType`, `reloadRegistry`) should call\n * `precompileSource()` before or alongside `compileMigrationFn()`.\n */\nexport function compileMigrationFn(\n source: string,\n executor: MigrationExecutor = defaultExecutor,\n): MigrationFn {\n const cache = getExecutorCache(executor);\n const key = hashSource(source);\n const cached = cache.get(key);\n if (cached) return cached;\n\n try {\n const fn = executor(source);\n cache.set(key, fn);\n return fn;\n } catch (err: unknown) {\n if (err instanceof MigrationError) throw err;\n throw new MigrationError(`Failed to compile migration source: ${(err as Error).message}`);\n }\n}\n\n/**\n * Batch compile stored migration steps into executable MigrationStep[].\n *\n * With the default executor, source validation is deferred to execution\n * time. Use `precompileSource()` to validate eagerly — see\n * `createRegistryFromGraph()` for the recommended pattern.\n */\nexport function compileMigrations(\n stored: StoredMigrationStep[],\n executor?: MigrationExecutor,\n): MigrationStep[] {\n return stored.map((step) => ({\n fromVersion: step.fromVersion,\n toVersion: step.toVersion,\n up: compileMigrationFn(step.up, executor),\n }));\n}\n\n/**\n * Terminate the sandbox worker thread. The worker will be respawned\n * on the next `defaultExecutor` call.\n *\n * Primarily useful for test cleanup to avoid vitest hanging on\n * unfinished worker threads.\n */\nexport async function destroySandboxWorker(): Promise<void> {\n if (!_worker) return;\n const w = _worker;\n _worker = null;\n // Reject any remaining pending requests\n for (const [, p] of _pending) {\n p.reject(new MigrationError('Sandbox worker terminated'));\n }\n _pending.clear();\n await w.terminate();\n}\n","import { computeEdgeDocId } from './docid.js';\nimport { InvalidQueryError } from './errors.js';\nimport { BUILTIN_FIELDS, DEFAULT_QUERY_LIMIT, NODE_RELATION } from './internal/constants.js';\nimport type { FindEdgesParams, FindNodesParams, QueryFilter, QueryPlan } from './types.js';\n\nexport function buildEdgeQueryPlan(params: FindEdgesParams): QueryPlan {\n const { aType, aUid, axbType, bType, bUid, limit, orderBy } = params;\n\n if (aUid && axbType && bUid && !params.where?.length) {\n return { strategy: 'get', docId: computeEdgeDocId(aUid, axbType, bUid) };\n }\n\n const filters: QueryFilter[] = [];\n\n if (aType) filters.push({ field: 'aType', op: '==', value: aType });\n if (aUid) filters.push({ field: 'aUid', op: '==', value: aUid });\n if (axbType) filters.push({ field: 'axbType', op: '==', value: axbType });\n if (bType) filters.push({ field: 'bType', op: '==', value: bType });\n if (bUid) filters.push({ field: 'bUid', op: '==', value: bUid });\n\n if (params.where) {\n for (const clause of params.where) {\n const field = BUILTIN_FIELDS.has(clause.field)\n ? clause.field\n : clause.field.startsWith('data.')\n ? clause.field\n : `data.${clause.field}`;\n filters.push({ field, op: clause.op, value: clause.value });\n }\n }\n\n if (filters.length === 0) {\n throw new InvalidQueryError('findEdges requires at least one filter parameter');\n }\n\n // limit: undefined → apply DEFAULT_QUERY_LIMIT\n // limit: 0 → no limit (unlimited, used by internal bulk operations)\n // limit: N → use N\n const effectiveLimit = limit === undefined ? DEFAULT_QUERY_LIMIT : limit || undefined;\n return { strategy: 'query', filters, options: { limit: effectiveLimit, orderBy } };\n}\n\nexport function buildNodeQueryPlan(params: FindNodesParams): QueryPlan {\n const { aType, limit, orderBy } = params;\n\n const filters: QueryFilter[] = [\n { field: 'aType', op: '==', value: aType },\n { field: 'axbType', op: '==', value: NODE_RELATION },\n ];\n\n if (params.where) {\n for (const clause of params.where) {\n const field = BUILTIN_FIELDS.has(clause.field)\n ? clause.field\n : clause.field.startsWith('data.')\n ? clause.field\n : `data.${clause.field}`;\n filters.push({ field, op: clause.op, value: clause.value });\n }\n }\n\n const effectiveLimit = limit === undefined ? DEFAULT_QUERY_LIMIT : limit || undefined;\n return { strategy: 'query', filters, options: { limit: effectiveLimit, orderBy } };\n}\n","import { BUILTIN_FIELDS } from './internal/constants.js';\nimport type { QueryFilter } from './types.js';\n\n/**\n * Result of analyzing a query for collection scan risk.\n */\nexport interface QuerySafetyResult {\n /** Whether the query matches a known indexed pattern. */\n safe: boolean;\n /** Human-readable explanation when the query is unsafe. */\n reason?: string;\n}\n\n/**\n * Known composite index patterns that prevent full collection scans.\n * Each pattern is a set of field names that must ALL be present in the\n * query filters. Order within the set doesn't matter — what matters is\n * that the Firestore composite index covers the combination.\n *\n * These correspond to the indexes in firestore.indexes.json:\n * (aUid, axbType) — forward edge lookup\n * (axbType, bUid) — reverse edge lookup\n * (aType, axbType) — type-scoped queries + findNodes\n * (axbType, bType) — edge type + target type\n */\nconst SAFE_INDEX_PATTERNS: ReadonlyArray<ReadonlySet<string>> = [\n new Set(['aUid', 'axbType']),\n new Set(['axbType', 'bUid']),\n new Set(['aType', 'axbType']),\n new Set(['axbType', 'bType']),\n];\n\n/**\n * Analyzes a set of query filters to determine whether the query would\n * likely cause a full collection scan on Firestore Enterprise.\n *\n * A query is considered \"safe\" if the builtin fields present in the filters\n * match at least one known composite index pattern. Queries that only use\n * `data.*` fields without a safe base pattern are flagged as unsafe.\n */\nexport function analyzeQuerySafety(filters: QueryFilter[]): QuerySafetyResult {\n // Extract the set of builtin fields being filtered on (equality checks are\n // the primary index-usable operations, but we're generous here and count\n // any filter on a builtin field as potentially index-backed).\n const builtinFieldsPresent = new Set<string>();\n let hasDataFilters = false;\n\n for (const f of filters) {\n if (BUILTIN_FIELDS.has(f.field)) {\n builtinFieldsPresent.add(f.field);\n } else {\n // data.* or other non-builtin fields\n hasDataFilters = true;\n }\n }\n\n // Check if the builtin fields match any known safe index pattern.\n // A pattern is \"matched\" if all fields in the pattern are present in the query.\n for (const pattern of SAFE_INDEX_PATTERNS) {\n let matched = true;\n for (const field of pattern) {\n if (!builtinFieldsPresent.has(field)) {\n matched = false;\n break;\n }\n }\n if (matched) {\n // Even with data.* filters, the base index narrows the scan significantly.\n // The data.* filters are applied as post-filters on the index results.\n return { safe: true };\n }\n }\n\n // No safe pattern matched — build an explanation.\n const presentFields = [...builtinFieldsPresent];\n if (presentFields.length === 0 && hasDataFilters) {\n return {\n safe: false,\n reason:\n 'Query filters only use data.* fields with no builtin field constraints. ' +\n 'This requires a full collection scan. Add aType, aUid, axbType, bType, or bUid filters, ' +\n 'or set allowCollectionScan: true.',\n };\n }\n\n if (hasDataFilters) {\n return {\n safe: false,\n reason:\n `Query filters on [${presentFields.join(', ')}] do not match any indexed pattern. ` +\n 'data.* filters without an indexed base require a full collection scan. ' +\n `Safe patterns: (aUid + axbType), (axbType + bUid), (aType + axbType), (axbType + bType). ` +\n 'Set allowCollectionScan: true to override.',\n };\n }\n\n return {\n safe: false,\n reason:\n `Query filters on [${presentFields.join(', ')}] do not match any indexed pattern. ` +\n 'This may cause a full collection scan on Firestore Enterprise. ' +\n `Safe patterns: (aUid + axbType), (axbType + bUid), (aType + axbType), (axbType + bType). ` +\n 'Set allowCollectionScan: true to override.',\n };\n}\n","import { computeEdgeDocId, computeNodeDocId } from './docid.js';\nimport { QuerySafetyError } from './errors.js';\nimport type { TransactionBackend, WritableRecord } from './internal/backend.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport { migrateRecord, migrateRecords } from './migration.js';\nimport { buildEdgeQueryPlan, buildNodeQueryPlan } from './query.js';\nimport { analyzeQuerySafety } from './query-safety.js';\nimport type {\n FindEdgesParams,\n FindNodesParams,\n GraphRegistry,\n GraphTransaction,\n MigrationWriteBack,\n QueryFilter,\n ScanProtection,\n StoredGraphRecord,\n} from './types.js';\n\nfunction buildWritableNodeRecord(\n aType: string,\n uid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid: uid, axbType: NODE_RELATION, bType: aType, bUid: uid, data };\n}\n\nfunction buildWritableEdgeRecord(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid, axbType, bType, bUid, data };\n}\n\nexport class GraphTransactionImpl implements GraphTransaction {\n constructor(\n private readonly backend: TransactionBackend,\n private readonly registry?: GraphRegistry,\n private readonly scanProtection: ScanProtection = 'error',\n private readonly scopePath: string = '',\n private readonly globalWriteBack: MigrationWriteBack = 'off',\n ) {}\n\n async getNode(uid: string): Promise<StoredGraphRecord | null> {\n const docId = computeNodeDocId(uid);\n const record = await this.backend.getDoc(docId);\n if (!record || !this.registry) return record;\n const result = await migrateRecord(record, this.registry, this.globalWriteBack);\n if (result.migrated && result.writeBack !== 'off') {\n await this.backend.updateDoc(docId, {\n replaceData: result.record.data as Record<string, unknown>,\n v: result.record.v,\n });\n }\n return result.record;\n }\n\n async getEdge(aUid: string, axbType: string, bUid: string): Promise<StoredGraphRecord | null> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = await this.backend.getDoc(docId);\n if (!record || !this.registry) return record;\n const result = await migrateRecord(record, this.registry, this.globalWriteBack);\n if (result.migrated && result.writeBack !== 'off') {\n await this.backend.updateDoc(docId, {\n replaceData: result.record.data as Record<string, unknown>,\n v: result.record.v,\n });\n }\n return result.record;\n }\n\n async edgeExists(aUid: string, axbType: string, bUid: string): Promise<boolean> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = await this.backend.getDoc(docId);\n return record !== null;\n }\n\n private checkQuerySafety(filters: QueryFilter[], allowCollectionScan?: boolean): void {\n if (allowCollectionScan || this.scanProtection === 'off') return;\n\n const result = analyzeQuerySafety(filters);\n if (result.safe) return;\n\n if (this.scanProtection === 'error') {\n throw new QuerySafetyError(result.reason!);\n }\n\n console.warn(`[firegraph] Query safety warning: ${result.reason}`);\n }\n\n async findEdges(params: FindEdgesParams): Promise<StoredGraphRecord[]> {\n const plan = buildEdgeQueryPlan(params);\n let records: StoredGraphRecord[];\n if (plan.strategy === 'get') {\n const record = await this.backend.getDoc(plan.docId);\n records = record ? [record] : [];\n } else {\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n records = await this.backend.query(plan.filters, plan.options);\n }\n return this.applyMigrations(records);\n }\n\n async findNodes(params: FindNodesParams): Promise<StoredGraphRecord[]> {\n const plan = buildNodeQueryPlan(params);\n let records: StoredGraphRecord[];\n if (plan.strategy === 'get') {\n const record = await this.backend.getDoc(plan.docId);\n records = record ? [record] : [];\n } else {\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n records = await this.backend.query(plan.filters, plan.options);\n }\n return this.applyMigrations(records);\n }\n\n private async applyMigrations(records: StoredGraphRecord[]): Promise<StoredGraphRecord[]> {\n if (!this.registry || records.length === 0) return records;\n const results = await migrateRecords(records, this.registry, this.globalWriteBack);\n for (const result of results) {\n if (result.migrated && result.writeBack !== 'off') {\n const docId =\n result.record.axbType === NODE_RELATION\n ? computeNodeDocId(result.record.aUid)\n : computeEdgeDocId(result.record.aUid, result.record.axbType, result.record.bUid);\n await this.backend.updateDoc(docId, {\n replaceData: result.record.data as Record<string, unknown>,\n v: result.record.v,\n });\n }\n }\n return results.map((r) => r.record);\n }\n\n async putNode(aType: string, uid: string, data: Record<string, unknown>): Promise<void> {\n if (this.registry) {\n this.registry.validate(aType, NODE_RELATION, aType, data, this.scopePath);\n }\n const docId = computeNodeDocId(uid);\n const record = buildWritableNodeRecord(aType, uid, data);\n if (this.registry) {\n const entry = this.registry.lookup(aType, NODE_RELATION, aType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n await this.backend.setDoc(docId, record);\n }\n\n async putEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n if (this.registry) {\n this.registry.validate(aType, axbType, bType, data, this.scopePath);\n }\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = buildWritableEdgeRecord(aType, aUid, axbType, bType, bUid, data);\n if (this.registry) {\n const entry = this.registry.lookup(aType, axbType, bType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n await this.backend.setDoc(docId, record);\n }\n\n async updateNode(uid: string, data: Record<string, unknown>): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.updateDoc(docId, { dataFields: data });\n }\n\n async removeNode(uid: string): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.deleteDoc(docId);\n }\n\n async removeEdge(aUid: string, axbType: string, bUid: string): Promise<void> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n await this.backend.deleteDoc(docId);\n }\n}\n","import { GraphBatchImpl } from './batch.js';\nimport { computeEdgeDocId, computeNodeDocId } from './docid.js';\nimport {\n createBootstrapRegistry,\n createRegistryFromGraph,\n generateDeterministicUid,\n META_EDGE_TYPE,\n META_NODE_TYPE,\n} from './dynamic-registry.js';\nimport { DynamicRegistryError, FiregraphError, QuerySafetyError } from './errors.js';\nimport type { StorageBackend, WritableRecord } from './internal/backend.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport type { MigrationResult } from './migration.js';\nimport { migrateRecord, migrateRecords } from './migration.js';\nimport { buildEdgeQueryPlan, buildNodeQueryPlan } from './query.js';\nimport { analyzeQuerySafety } from './query-safety.js';\nimport { createMergedRegistry } from './registry.js';\nimport { precompileSource } from './sandbox.js';\nimport { GraphTransactionImpl } from './transaction.js';\nimport type {\n BulkOptions,\n BulkResult,\n CascadeResult,\n DefineTypeOptions,\n DynamicGraphClient,\n DynamicRegistryConfig,\n EdgeTopology,\n FindEdgesParams,\n FindNodesParams,\n GraphBatch,\n GraphClient,\n GraphClientOptions,\n GraphReader,\n GraphRegistry,\n GraphTransaction,\n MigrationExecutor,\n MigrationFn,\n MigrationWriteBack,\n QueryFilter,\n QueryOptions,\n ScanProtection,\n StoredGraphRecord,\n} from './types.js';\n\nconst RESERVED_TYPE_NAMES = new Set([META_NODE_TYPE, META_EDGE_TYPE]);\n\nfunction buildWritableNodeRecord(\n aType: string,\n uid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid: uid, axbType: NODE_RELATION, bType: aType, bUid: uid, data };\n}\n\nfunction buildWritableEdgeRecord(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid, axbType, bType, bUid, data };\n}\n\nexport class GraphClientImpl implements DynamicGraphClient {\n readonly scanProtection: ScanProtection;\n\n // Static mode\n private readonly staticRegistry?: GraphRegistry;\n\n // Dynamic mode\n private readonly dynamicConfig?: DynamicRegistryConfig;\n private readonly bootstrapRegistry?: GraphRegistry;\n private dynamicRegistry?: GraphRegistry;\n private readonly metaBackend?: StorageBackend;\n\n // Migration settings\n private readonly globalWriteBack: MigrationWriteBack;\n private readonly migrationSandbox?: MigrationExecutor;\n\n constructor(\n private readonly backend: StorageBackend,\n options?: GraphClientOptions,\n /** @internal Optional pre-built meta-backend (used by subgraph clones). */\n metaBackend?: StorageBackend,\n ) {\n this.globalWriteBack = options?.migrationWriteBack ?? 'off';\n this.migrationSandbox = options?.migrationSandbox;\n\n if (options?.registryMode) {\n this.dynamicConfig = options.registryMode;\n this.bootstrapRegistry = createBootstrapRegistry();\n if (options.registry) {\n this.staticRegistry = options.registry;\n }\n this.metaBackend = metaBackend;\n } else {\n this.staticRegistry = options?.registry;\n }\n\n this.scanProtection = options?.scanProtection ?? 'error';\n }\n\n // ---------------------------------------------------------------------------\n // Backend access (exposed for traversal helpers and subgraph cloning)\n // ---------------------------------------------------------------------------\n\n /** @internal */\n getBackend(): StorageBackend {\n return this.backend;\n }\n\n // ---------------------------------------------------------------------------\n // Registry routing\n // ---------------------------------------------------------------------------\n\n private getRegistryForType(aType: string): GraphRegistry | undefined {\n if (!this.dynamicConfig) return this.staticRegistry;\n\n if (aType === META_NODE_TYPE || aType === META_EDGE_TYPE) {\n return this.bootstrapRegistry;\n }\n\n return this.dynamicRegistry ?? this.staticRegistry ?? this.bootstrapRegistry;\n }\n\n private getBackendForType(aType: string): StorageBackend {\n if (this.metaBackend && (aType === META_NODE_TYPE || aType === META_EDGE_TYPE)) {\n return this.metaBackend;\n }\n return this.backend;\n }\n\n private getCombinedRegistry(): GraphRegistry | undefined {\n if (!this.dynamicConfig) return this.staticRegistry;\n return this.dynamicRegistry ?? this.staticRegistry ?? this.bootstrapRegistry;\n }\n\n // ---------------------------------------------------------------------------\n // Query safety\n // ---------------------------------------------------------------------------\n\n private checkQuerySafety(filters: QueryFilter[], allowCollectionScan?: boolean): void {\n if (allowCollectionScan || this.scanProtection === 'off') return;\n\n const result = analyzeQuerySafety(filters);\n if (result.safe) return;\n\n if (this.scanProtection === 'error') {\n throw new QuerySafetyError(result.reason!);\n }\n\n console.warn(`[firegraph] Query safety warning: ${result.reason}`);\n }\n\n // ---------------------------------------------------------------------------\n // Migration helpers\n // ---------------------------------------------------------------------------\n\n private async applyMigration(\n record: StoredGraphRecord,\n docId: string,\n ): Promise<StoredGraphRecord> {\n const registry = this.getCombinedRegistry();\n if (!registry) return record;\n\n const result = await migrateRecord(record, registry, this.globalWriteBack);\n if (result.migrated) {\n this.handleWriteBack(result, docId);\n }\n return result.record;\n }\n\n private async applyMigrations(records: StoredGraphRecord[]): Promise<StoredGraphRecord[]> {\n const registry = this.getCombinedRegistry();\n if (!registry || records.length === 0) return records;\n\n const results = await migrateRecords(records, registry, this.globalWriteBack);\n for (const result of results) {\n if (result.migrated) {\n const docId =\n result.record.axbType === NODE_RELATION\n ? computeNodeDocId(result.record.aUid)\n : computeEdgeDocId(result.record.aUid, result.record.axbType, result.record.bUid);\n this.handleWriteBack(result, docId);\n }\n }\n return results.map((r) => r.record);\n }\n\n /**\n * Fire-and-forget write-back for a migrated record. Both `'eager'` and\n * `'background'` are non-blocking; the difference is the log level on\n * failure. For synchronous write-back, use a transaction — see\n * `GraphTransactionImpl`.\n */\n private handleWriteBack(result: MigrationResult, docId: string): void {\n if (result.writeBack === 'off') return;\n\n const doWriteBack = async () => {\n try {\n await this.backend.updateDoc(docId, {\n replaceData: result.record.data as Record<string, unknown>,\n v: result.record.v,\n });\n } catch (err: unknown) {\n const msg = `[firegraph] Migration write-back failed for ${docId}: ${(err as Error).message}`;\n if (result.writeBack === 'eager') {\n console.error(msg);\n } else {\n console.warn(msg);\n }\n }\n };\n\n void doWriteBack();\n }\n\n // ---------------------------------------------------------------------------\n // GraphReader\n // ---------------------------------------------------------------------------\n\n async getNode(uid: string): Promise<StoredGraphRecord | null> {\n const docId = computeNodeDocId(uid);\n const record = await this.backend.getDoc(docId);\n if (!record) return null;\n return this.applyMigration(record, docId);\n }\n\n async getEdge(aUid: string, axbType: string, bUid: string): Promise<StoredGraphRecord | null> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = await this.backend.getDoc(docId);\n if (!record) return null;\n return this.applyMigration(record, docId);\n }\n\n async edgeExists(aUid: string, axbType: string, bUid: string): Promise<boolean> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = await this.backend.getDoc(docId);\n return record !== null;\n }\n\n async findEdges(params: FindEdgesParams): Promise<StoredGraphRecord[]> {\n const plan = buildEdgeQueryPlan(params);\n let records: StoredGraphRecord[];\n if (plan.strategy === 'get') {\n const record = await this.backend.getDoc(plan.docId);\n records = record ? [record] : [];\n } else {\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n records = await this.backend.query(plan.filters, plan.options);\n }\n return this.applyMigrations(records);\n }\n\n async findNodes(params: FindNodesParams): Promise<StoredGraphRecord[]> {\n const plan = buildNodeQueryPlan(params);\n let records: StoredGraphRecord[];\n if (plan.strategy === 'get') {\n const record = await this.backend.getDoc(plan.docId);\n records = record ? [record] : [];\n } else {\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n records = await this.backend.query(plan.filters, plan.options);\n }\n return this.applyMigrations(records);\n }\n\n // ---------------------------------------------------------------------------\n // GraphWriter\n // ---------------------------------------------------------------------------\n\n async putNode(aType: string, uid: string, data: Record<string, unknown>): Promise<void> {\n const registry = this.getRegistryForType(aType);\n if (registry) {\n registry.validate(aType, NODE_RELATION, aType, data, this.backend.scopePath);\n }\n const backend = this.getBackendForType(aType);\n const docId = computeNodeDocId(uid);\n const record = buildWritableNodeRecord(aType, uid, data);\n if (registry) {\n const entry = registry.lookup(aType, NODE_RELATION, aType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n await backend.setDoc(docId, record);\n }\n\n async putEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n const registry = this.getRegistryForType(aType);\n if (registry) {\n registry.validate(aType, axbType, bType, data, this.backend.scopePath);\n }\n const backend = this.getBackendForType(aType);\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = buildWritableEdgeRecord(aType, aUid, axbType, bType, bUid, data);\n if (registry) {\n const entry = registry.lookup(aType, axbType, bType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n await backend.setDoc(docId, record);\n }\n\n async updateNode(uid: string, data: Record<string, unknown>): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.updateDoc(docId, { dataFields: data });\n }\n\n async removeNode(uid: string): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.deleteDoc(docId);\n }\n\n async removeEdge(aUid: string, axbType: string, bUid: string): Promise<void> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n await this.backend.deleteDoc(docId);\n }\n\n // ---------------------------------------------------------------------------\n // Transactions & Batches\n // ---------------------------------------------------------------------------\n\n async runTransaction<T>(fn: (tx: GraphTransaction) => Promise<T>): Promise<T> {\n return this.backend.runTransaction(async (txBackend) => {\n const graphTx = new GraphTransactionImpl(\n txBackend,\n this.getCombinedRegistry(),\n this.scanProtection,\n this.backend.scopePath,\n this.globalWriteBack,\n );\n return fn(graphTx);\n });\n }\n\n batch(): GraphBatch {\n return new GraphBatchImpl(\n this.backend.createBatch(),\n this.getCombinedRegistry(),\n this.backend.scopePath,\n );\n }\n\n // ---------------------------------------------------------------------------\n // Subgraph\n // ---------------------------------------------------------------------------\n\n subgraph(parentNodeUid: string, name: string = 'graph'): GraphClient {\n if (!parentNodeUid || parentNodeUid.includes('/')) {\n throw new FiregraphError(\n `Invalid parentNodeUid for subgraph: \"${parentNodeUid}\". ` +\n 'Must be a non-empty string without \"/\".',\n 'INVALID_SUBGRAPH',\n );\n }\n if (name.includes('/')) {\n throw new FiregraphError(\n `Subgraph name must not contain \"/\": got \"${name}\". ` +\n 'Use chained .subgraph() calls for nested subgraphs.',\n 'INVALID_SUBGRAPH',\n );\n }\n\n const childBackend = this.backend.subgraph(parentNodeUid, name);\n\n return new GraphClientImpl(\n childBackend,\n {\n registry: this.getCombinedRegistry(),\n scanProtection: this.scanProtection,\n migrationWriteBack: this.globalWriteBack,\n migrationSandbox: this.migrationSandbox,\n },\n // Subgraphs do not have meta-backends; meta lives only at the root.\n );\n }\n\n // ---------------------------------------------------------------------------\n // Collection group query\n // ---------------------------------------------------------------------------\n\n async findEdgesGlobal(\n params: FindEdgesParams,\n collectionName?: string,\n ): Promise<StoredGraphRecord[]> {\n if (!this.backend.findEdgesGlobal) {\n throw new FiregraphError(\n 'findEdgesGlobal() is not supported by the current storage backend.',\n 'UNSUPPORTED_OPERATION',\n );\n }\n const plan = buildEdgeQueryPlan(params);\n if (plan.strategy === 'get') {\n throw new FiregraphError(\n 'findEdgesGlobal() requires a query, not a direct document lookup. ' +\n 'Omit one of aUid/axbType/bUid to force a query strategy.',\n 'INVALID_QUERY',\n );\n }\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n const records = await this.backend.findEdgesGlobal(params, collectionName);\n return this.applyMigrations(records);\n }\n\n // ---------------------------------------------------------------------------\n // Bulk operations\n // ---------------------------------------------------------------------------\n\n async removeNodeCascade(uid: string, options?: BulkOptions): Promise<CascadeResult> {\n return this.backend.removeNodeCascade(uid, this, options);\n }\n\n async bulkRemoveEdges(params: FindEdgesParams, options?: BulkOptions): Promise<BulkResult> {\n return this.backend.bulkRemoveEdges(params, this, options);\n }\n\n // ---------------------------------------------------------------------------\n // Dynamic registry methods\n // ---------------------------------------------------------------------------\n\n async defineNodeType(\n name: string,\n jsonSchema: object,\n description?: string,\n options?: DefineTypeOptions,\n ): Promise<void> {\n if (!this.dynamicConfig) {\n throw new DynamicRegistryError(\n 'defineNodeType() is only available in dynamic registry mode. ' +\n 'Pass registryMode: { mode: \"dynamic\" } to createGraphClient().',\n );\n }\n\n if (RESERVED_TYPE_NAMES.has(name)) {\n throw new DynamicRegistryError(\n `Cannot define type \"${name}\": this name is reserved for the meta-registry.`,\n );\n }\n\n if (this.staticRegistry?.lookup(name, NODE_RELATION, name)) {\n throw new DynamicRegistryError(\n `Cannot define node type \"${name}\": already defined in the static registry.`,\n );\n }\n\n const uid = generateDeterministicUid(META_NODE_TYPE, name);\n const data: Record<string, unknown> = { name, jsonSchema };\n if (description !== undefined) data.description = description;\n if (options?.titleField !== undefined) data.titleField = options.titleField;\n if (options?.subtitleField !== undefined) data.subtitleField = options.subtitleField;\n if (options?.viewTemplate !== undefined) data.viewTemplate = options.viewTemplate;\n if (options?.viewCss !== undefined) data.viewCss = options.viewCss;\n if (options?.allowedIn !== undefined) data.allowedIn = options.allowedIn;\n if (options?.migrationWriteBack !== undefined)\n data.migrationWriteBack = options.migrationWriteBack;\n if (options?.migrations !== undefined) {\n data.migrations = await this.serializeMigrations(options.migrations);\n }\n\n await this.putNode(META_NODE_TYPE, uid, data);\n }\n\n async defineEdgeType(\n name: string,\n topology: EdgeTopology,\n jsonSchema?: object,\n description?: string,\n options?: DefineTypeOptions,\n ): Promise<void> {\n if (!this.dynamicConfig) {\n throw new DynamicRegistryError(\n 'defineEdgeType() is only available in dynamic registry mode. ' +\n 'Pass registryMode: { mode: \"dynamic\" } to createGraphClient().',\n );\n }\n\n if (RESERVED_TYPE_NAMES.has(name)) {\n throw new DynamicRegistryError(\n `Cannot define type \"${name}\": this name is reserved for the meta-registry.`,\n );\n }\n\n if (this.staticRegistry) {\n const fromTypes = Array.isArray(topology.from) ? topology.from : [topology.from];\n const toTypes = Array.isArray(topology.to) ? topology.to : [topology.to];\n for (const aType of fromTypes) {\n for (const bType of toTypes) {\n if (this.staticRegistry.lookup(aType, name, bType)) {\n throw new DynamicRegistryError(\n `Cannot define edge type \"${name}\" for (${aType}) -> (${bType}): already defined in the static registry.`,\n );\n }\n }\n }\n }\n\n const uid = generateDeterministicUid(META_EDGE_TYPE, name);\n const data: Record<string, unknown> = {\n name,\n from: topology.from,\n to: topology.to,\n };\n if (jsonSchema !== undefined) data.jsonSchema = jsonSchema;\n if (topology.inverseLabel !== undefined) data.inverseLabel = topology.inverseLabel;\n if (topology.targetGraph !== undefined) data.targetGraph = topology.targetGraph;\n if (description !== undefined) data.description = description;\n if (options?.titleField !== undefined) data.titleField = options.titleField;\n if (options?.subtitleField !== undefined) data.subtitleField = options.subtitleField;\n if (options?.viewTemplate !== undefined) data.viewTemplate = options.viewTemplate;\n if (options?.viewCss !== undefined) data.viewCss = options.viewCss;\n if (options?.allowedIn !== undefined) data.allowedIn = options.allowedIn;\n if (options?.migrationWriteBack !== undefined)\n data.migrationWriteBack = options.migrationWriteBack;\n if (options?.migrations !== undefined) {\n data.migrations = await this.serializeMigrations(options.migrations);\n }\n\n await this.putNode(META_EDGE_TYPE, uid, data);\n }\n\n async reloadRegistry(): Promise<void> {\n if (!this.dynamicConfig) {\n throw new DynamicRegistryError(\n 'reloadRegistry() is only available in dynamic registry mode. ' +\n 'Pass registryMode: { mode: \"dynamic\" } to createGraphClient().',\n );\n }\n\n const reader = this.createMetaReader();\n const dynamicOnly = await createRegistryFromGraph(reader, this.migrationSandbox);\n\n if (this.staticRegistry) {\n this.dynamicRegistry = createMergedRegistry(this.staticRegistry, dynamicOnly);\n } else {\n this.dynamicRegistry = dynamicOnly;\n }\n }\n\n private async serializeMigrations(\n migrations: Array<{ fromVersion: number; toVersion: number; up: MigrationFn | string }>,\n ): Promise<Array<{ fromVersion: number; toVersion: number; up: string }>> {\n const result = migrations.map((m) => {\n const source = typeof m.up === 'function' ? m.up.toString() : m.up;\n return { fromVersion: m.fromVersion, toVersion: m.toVersion, up: source };\n });\n await Promise.all(result.map((m) => precompileSource(m.up, this.migrationSandbox)));\n return result;\n }\n\n /**\n * Build a `GraphReader` over the meta-backend. If meta lives in the same\n * collection as the main backend, `this` is returned directly.\n */\n private createMetaReader(): GraphReader {\n if (!this.metaBackend) return this;\n\n const backend = this.metaBackend;\n\n const executeMetaQuery = (\n filters: QueryFilter[],\n options?: QueryOptions,\n ): Promise<StoredGraphRecord[]> => backend.query(filters, options);\n\n return {\n async getNode(uid: string): Promise<StoredGraphRecord | null> {\n return backend.getDoc(computeNodeDocId(uid));\n },\n async getEdge(\n aUid: string,\n axbType: string,\n bUid: string,\n ): Promise<StoredGraphRecord | null> {\n return backend.getDoc(computeEdgeDocId(aUid, axbType, bUid));\n },\n async edgeExists(aUid: string, axbType: string, bUid: string): Promise<boolean> {\n const record = await backend.getDoc(computeEdgeDocId(aUid, axbType, bUid));\n return record !== null;\n },\n async findEdges(params: FindEdgesParams): Promise<StoredGraphRecord[]> {\n const plan = buildEdgeQueryPlan(params);\n if (plan.strategy === 'get') {\n const record = await backend.getDoc(plan.docId);\n return record ? [record] : [];\n }\n return executeMetaQuery(plan.filters, plan.options);\n },\n async findNodes(params: FindNodesParams): Promise<StoredGraphRecord[]> {\n const plan = buildNodeQueryPlan(params);\n if (plan.strategy === 'get') {\n const record = await backend.getDoc(plan.docId);\n return record ? [record] : [];\n }\n return executeMetaQuery(plan.filters, plan.options);\n },\n };\n }\n}\n\n/**\n * Create a `GraphClient` backed by an arbitrary `StorageBackend`.\n *\n * Used by backend-specific factories (D1, DO-SQLite, etc.) — most callers\n * should use the higher-level `createGraphClient(firestore, ...)` overload\n * below or import the equivalent from `firegraph/d1` / `firegraph/do-sqlite`.\n */\nexport function createGraphClientFromBackend(\n backend: StorageBackend,\n options?: GraphClientOptions,\n metaBackend?: StorageBackend,\n): GraphClient | DynamicGraphClient {\n return new GraphClientImpl(backend, options, metaBackend) as GraphClient | DynamicGraphClient;\n}\n","/**\n * Backend-agnostic timestamp.\n *\n * Structurally compatible with `@google-cloud/firestore`'s `Timestamp` so\n * that records returned by either the Firestore or SQLite backend can be\n * consumed through the same `StoredGraphRecord` shape.\n *\n * Firestore's native `Timestamp` already satisfies this interface, so\n * existing Firestore consumers see no behavior change. The SQLite backend\n * returns instances of `GraphTimestampImpl` which also satisfies it.\n */\n\nexport interface GraphTimestamp {\n readonly seconds: number;\n readonly nanoseconds: number;\n toDate(): Date;\n toMillis(): number;\n}\n\n/**\n * Concrete `GraphTimestamp` implementation used by non-Firestore backends.\n * Mirrors the surface of Firestore's `Timestamp` enough for typical use.\n */\nexport class GraphTimestampImpl implements GraphTimestamp {\n constructor(\n public readonly seconds: number,\n public readonly nanoseconds: number,\n ) {}\n\n toDate(): Date {\n return new Date(this.toMillis());\n }\n\n toMillis(): number {\n return this.seconds * 1000 + Math.floor(this.nanoseconds / 1e6);\n }\n\n toJSON(): { seconds: number; nanoseconds: number } {\n return { seconds: this.seconds, nanoseconds: this.nanoseconds };\n }\n\n static fromMillis(ms: number): GraphTimestampImpl {\n const seconds = Math.floor(ms / 1000);\n const nanoseconds = (ms - seconds * 1000) * 1e6;\n return new GraphTimestampImpl(seconds, nanoseconds);\n }\n\n static now(): GraphTimestampImpl {\n return GraphTimestampImpl.fromMillis(Date.now());\n }\n}\n\n/**\n * Sentinel returned by `StorageBackend.serverTimestamp()` when the backend\n * has no native server-time concept and just wants a placeholder that the\n * adapter resolves to a concrete time at write commit. SQLite backends\n * substitute the wall-clock millis at the moment of `setDoc`/`updateDoc`.\n */\nexport const SERVER_TIMESTAMP_SENTINEL = Symbol.for('firegraph.serverTimestamp');\nexport type ServerTimestampSentinel = typeof SERVER_TIMESTAMP_SENTINEL;\n\nexport function isServerTimestampSentinel(value: unknown): value is ServerTimestampSentinel {\n return value === SERVER_TIMESTAMP_SENTINEL;\n}\n","/**\n * SQLite schema for firegraph triples.\n *\n * Single-table design — both nodes (self-loops with `axbType = 'is'`) and\n * edges share one row. The `scope` column carries the materialized subgraph\n * path (parent UIDs interleaved with subgraph names), which preserves\n * Firestore's nested-subcollection semantics in a flat table.\n *\n * `data` is a JSON string. Built-in fields are projected to typed columns so\n * the query planner can use indexes without going through `json_extract`.\n */\n\nexport const SQLITE_COLUMNS = [\n 'doc_id',\n 'scope',\n 'a_type',\n 'a_uid',\n 'axb_type',\n 'b_type',\n 'b_uid',\n 'data',\n 'v',\n 'created_at',\n 'updated_at',\n] as const;\n\nexport type SqliteColumn = (typeof SQLITE_COLUMNS)[number];\n\n/**\n * Map firegraph field names (as they appear in `QueryFilter.field` and the\n * record envelope) to SQLite column names.\n */\nexport const FIELD_TO_COLUMN: Record<string, SqliteColumn> = {\n aType: 'a_type',\n aUid: 'a_uid',\n axbType: 'axb_type',\n bType: 'b_type',\n bUid: 'b_uid',\n v: 'v',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n};\n\n/**\n * Build the DDL statements that create the firegraph table and its indexes.\n * Returned as separate statements because some drivers (D1) require one\n * statement per `prepare()` call.\n */\nexport function buildSchemaStatements(table: string): string[] {\n const t = quoteIdent(table);\n return [\n `CREATE TABLE IF NOT EXISTS ${t} (\n doc_id TEXT NOT NULL,\n scope TEXT NOT NULL DEFAULT '',\n a_type TEXT NOT NULL,\n a_uid TEXT NOT NULL,\n axb_type TEXT NOT NULL,\n b_type TEXT NOT NULL,\n b_uid TEXT NOT NULL,\n data TEXT NOT NULL,\n v INTEGER,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (scope, doc_id)\n )`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_a_uid`)} ON ${t}(scope, a_uid)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_b_uid`)} ON ${t}(scope, b_uid)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_axb_type_b_uid`)} ON ${t}(scope, axb_type, b_uid)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_a_type`)} ON ${t}(scope, a_type)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_b_type`)} ON ${t}(scope, b_type)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_doc_id`)} ON ${t}(doc_id)`,\n ];\n}\n\n/**\n * Quote a SQL identifier with double quotes, escaping any embedded quotes.\n *\n * Identifier names (table, column, index) come from configuration and\n * static code in this module — never from user data — but quoting still\n * protects against accidental keyword collisions.\n */\nexport function quoteIdent(name: string): string {\n validateTableName(name);\n return `\"${name}\"`;\n}\n\n/**\n * Validate a SQLite identifier (table name, column name) against the\n * allowed character set. Exposed so factory functions can fail fast on\n * an invalid `options.table` rather than waiting until first SQL.\n */\nexport function validateTableName(name: string): void {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {\n throw new Error(`Invalid SQL identifier: ${name}. Must match /^[A-Za-z_][A-Za-z0-9_]*$/.`);\n }\n}\n","/**\n * Compile firegraph queries and writes into parameterized SQLite statements.\n *\n * The single-table SQLite schema mirrors the Firestore record envelope:\n * built-in fields (`aType`, `aUid`, etc.) are typed columns, and `data` is a\n * JSON string. `data.<key>` filter fields are translated to `json_extract`\n * expressions; built-in fields go straight to their column.\n */\n\nimport { FiregraphError } from '../errors.js';\nimport type { GraphTimestamp } from '../timestamp.js';\nimport { GraphTimestampImpl } from '../timestamp.js';\nimport type { QueryFilter, QueryOptions, StoredGraphRecord } from '../types.js';\nimport type { UpdatePayload, WritableRecord } from './backend.js';\nimport { FIELD_TO_COLUMN, quoteIdent } from './sqlite-schema.js';\n\nexport interface CompiledStatement {\n sql: string;\n params: unknown[];\n}\n\n/**\n * Translate a firegraph filter field to either a column reference or a\n * `json_extract(data, ?)` expression. The JSON path is returned separately\n * so it can be appended to the parameter list.\n */\nfunction compileFieldRef(field: string): { expr: string; pathParam?: string } {\n const column = FIELD_TO_COLUMN[field];\n if (column) {\n return { expr: quoteIdent(column) };\n }\n if (field.startsWith('data.')) {\n const key = field.slice(5);\n validateJsonPathKey(key);\n return { expr: 'json_extract(\"data\", ?)', pathParam: `$.${key}` };\n }\n if (field === 'data') {\n return { expr: 'json_extract(\"data\", ?)', pathParam: '$' };\n }\n throw new FiregraphError(`SQLite backend cannot resolve filter field: ${field}`, 'INVALID_QUERY');\n}\n\n/**\n * Constructor names of Firestore special types that don't survive a plain\n * JSON.stringify round-trip — they have non-enumerable accessors (e.g.\n * `Timestamp.seconds`) or class identity that JSON loses. Filtering by one\n * of these against SQLite would silently miss every row, so we fail loudly.\n *\n * Detection is by `constructor.name` + duck-type so this module stays\n * dependency-free (importing `@google-cloud/firestore` here would pollute\n * the Cloudflare Workers bundle — see tests/unit/bundle-pollution.test.ts).\n */\nconst FIRESTORE_TYPE_NAMES = new Set([\n 'Timestamp',\n 'GeoPoint',\n 'VectorValue',\n 'DocumentReference',\n 'FieldValue',\n]);\n\nfunction isFirestoreSpecialType(value: object): string | null {\n const ctorName = (value as { constructor?: { name?: string } }).constructor?.name;\n if (ctorName && FIRESTORE_TYPE_NAMES.has(ctorName)) return ctorName;\n return null;\n}\n\n/**\n * Coerce a JS filter value into a SQLite-bindable primitive. JS objects\n * (other than null) are JSON-stringified so they can match values stored\n * via `json_extract`.\n *\n * Firestore special types (Timestamp, GeoPoint, VectorValue,\n * DocumentReference, FieldValue) are rejected with `INVALID_QUERY` —\n * `JSON.stringify` would emit garbage (`{}` for Timestamp, etc.) that\n * silently fails to match anything. Convert to a primitive at the call site\n * (e.g. `ts.toMillis()` or `ts.toDate().toISOString()`) before passing in.\n */\nfunction bindValue(value: unknown): unknown {\n if (value === null || value === undefined) return null;\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'bigint' ||\n typeof value === 'boolean'\n ) {\n return value;\n }\n if (value instanceof Date) return value.getTime();\n if (typeof value === 'object') {\n const firestoreType = isFirestoreSpecialType(value);\n if (firestoreType) {\n throw new FiregraphError(\n `SQLite backend cannot bind a Firestore ${firestoreType} value — JSON serialization ` +\n `would silently drop fields and the resulting bind would never match a stored row. ` +\n `Convert to a primitive (e.g. \\`ts.toMillis()\\` for Timestamp) before filtering or ` +\n `updating.`,\n 'INVALID_QUERY',\n );\n }\n return JSON.stringify(value);\n }\n return String(value);\n}\n\n/**\n * Validate `data.<key>` paths and `dataFields` keys against the subset of\n * JSON keys that round-trip cleanly through SQLite's unquoted JSON-path\n * syntax (`$.<name>` style). The accepted shape — `[A-Za-z_][\\w-]*` —\n * covers the overwhelmingly common case of code-style identifiers (camel,\n * snake, kebab) without forcing the SQL emitter to quote/escape paths.\n *\n * Why not silently quote? Several characters (`.`, `[`, `]`, `\"`) are\n * structural in SQLite's JSON path even inside the string after `$.` —\n * silently quoting would mean the reads (`json_extract` for filters) and\n * writes (`json_set` for `dataFields`) need symmetric quoting at every\n * call site, and any drift produces silent data corruption. Failing loudly\n * at compile time is the safer trade-off; users with exotic keys can use\n * `replaceData` (full-blob overwrite) instead.\n */\nconst JSON_PATH_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;\n\nfunction validateJsonPathKey(key: string): void {\n if (key.length === 0) {\n throw new FiregraphError(\n 'SQLite backend: empty JSON path component is not allowed',\n 'INVALID_QUERY',\n );\n }\n if (!JSON_PATH_KEY_RE.test(key)) {\n throw new FiregraphError(\n `SQLite backend: data field path component \"${key}\" is not a safe JSON-path identifier. ` +\n `Allowed pattern: /^[A-Za-z_][A-Za-z0-9_-]*$/. Use replaceData (full-data overwrite) ` +\n `for keys with reserved characters (whitespace, dots, brackets, quotes, etc.).`,\n 'INVALID_QUERY',\n );\n }\n}\n\nfunction compileFilter(filter: QueryFilter, params: unknown[]): string {\n const { expr, pathParam } = compileFieldRef(filter.field);\n if (pathParam !== undefined) params.push(pathParam);\n\n switch (filter.op) {\n case '==':\n params.push(bindValue(filter.value));\n return `${expr} = ?`;\n case '!=':\n params.push(bindValue(filter.value));\n return `${expr} != ?`;\n case '<':\n params.push(bindValue(filter.value));\n return `${expr} < ?`;\n case '<=':\n params.push(bindValue(filter.value));\n return `${expr} <= ?`;\n case '>':\n params.push(bindValue(filter.value));\n return `${expr} > ?`;\n case '>=':\n params.push(bindValue(filter.value));\n return `${expr} >= ?`;\n case 'in': {\n const values = asArray(filter.value, 'in');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `${expr} IN (${placeholders})`;\n }\n case 'not-in': {\n const values = asArray(filter.value, 'not-in');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `${expr} NOT IN (${placeholders})`;\n }\n case 'array-contains': {\n params.push(bindValue(filter.value));\n return `EXISTS (SELECT 1 FROM json_each(${expr}) WHERE value = ?)`;\n }\n case 'array-contains-any': {\n const values = asArray(filter.value, 'array-contains-any');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `EXISTS (SELECT 1 FROM json_each(${expr}) WHERE value IN (${placeholders}))`;\n }\n default:\n throw new FiregraphError(\n `SQLite backend does not support filter operator: ${String(filter.op)}`,\n 'INVALID_QUERY',\n );\n }\n}\n\nfunction asArray(value: unknown, op: string): unknown[] {\n if (!Array.isArray(value) || value.length === 0) {\n throw new FiregraphError(`Operator \"${op}\" requires a non-empty array value`, 'INVALID_QUERY');\n }\n return value;\n}\n\nfunction compileOrderBy(options: QueryOptions | undefined, params: unknown[]): string {\n if (!options?.orderBy) return '';\n const { field, direction } = options.orderBy;\n const { expr, pathParam } = compileFieldRef(field);\n if (pathParam !== undefined) params.push(pathParam);\n const dir = direction === 'desc' ? 'DESC' : 'ASC';\n return ` ORDER BY ${expr} ${dir}`;\n}\n\nfunction compileLimit(options: QueryOptions | undefined, params: unknown[]): string {\n if (options?.limit === undefined) return '';\n params.push(options.limit);\n return ` LIMIT ?`;\n}\n\n/**\n * SELECT all rows matching `filters` within `scope`. The scope filter is\n * always added as the leading predicate so the `(scope, …)` indexes apply.\n */\nexport function compileSelect(\n table: string,\n scope: string,\n filters: QueryFilter[],\n options?: QueryOptions,\n): CompiledStatement {\n const params: unknown[] = [];\n const conditions: string[] = ['\"scope\" = ?'];\n params.push(scope);\n\n for (const f of filters) {\n conditions.push(compileFilter(f, params));\n }\n\n let sql = `SELECT * FROM ${quoteIdent(table)} WHERE ${conditions.join(' AND ')}`;\n // ORDER BY parameters must come after WHERE parameters in the bind list.\n const orderClause = compileOrderBy(options, params);\n sql += orderClause;\n sql += compileLimit(options, params);\n\n return { sql, params };\n}\n\n/**\n * Cross-scope SELECT — equivalent to Firestore's collection group query.\n * Used by `findEdgesGlobal`.\n *\n * `scopeNameFilter`, when present, narrows the result to rows whose scope's\n * last materialized-path segment equals the given subgraph name (or to the\n * root rows when the name equals the table name itself, matching Firestore's\n * `db.collectionGroup(tableName)` semantics).\n */\nexport function compileSelectGlobal(\n table: string,\n filters: QueryFilter[],\n options?: QueryOptions,\n scopeNameFilter?: { name: string; isRoot: boolean },\n): CompiledStatement {\n if (filters.length === 0) {\n throw new FiregraphError(\n 'compileSelectGlobal requires at least one filter — refusing to issue an unbounded SELECT.',\n 'INVALID_QUERY',\n );\n }\n\n const params: unknown[] = [];\n const conditions: string[] = [];\n\n if (scopeNameFilter) {\n if (scopeNameFilter.isRoot) {\n conditions.push(`\"scope\" = ?`);\n params.push('');\n } else {\n conditions.push(`\"scope\" LIKE ? ESCAPE '\\\\'`);\n params.push(`%/${escapeLike(scopeNameFilter.name)}`);\n }\n }\n\n for (const f of filters) {\n conditions.push(compileFilter(f, params));\n }\n\n const sql =\n `SELECT * FROM ${quoteIdent(table)} WHERE ${conditions.join(' AND ')}` +\n compileOrderBy(options, params) +\n compileLimit(options, params);\n return { sql, params };\n}\n\nexport function compileSelectByDocId(\n table: string,\n scope: string,\n docId: string,\n): CompiledStatement {\n return {\n sql: `SELECT * FROM ${quoteIdent(table)} WHERE \"scope\" = ? AND \"doc_id\" = ? LIMIT 1`,\n params: [scope, docId],\n };\n}\n\n/**\n * INSERT OR REPLACE — `setDoc` semantics: full replacement of the row\n * keyed by `(scope, doc_id)`.\n */\nexport function compileSet(\n table: string,\n scope: string,\n docId: string,\n record: WritableRecord,\n nowMillis: number,\n): CompiledStatement {\n const sql = `INSERT OR REPLACE INTO ${quoteIdent(table)} (\n doc_id, scope, a_type, a_uid, axb_type, b_type, b_uid, data, v, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;\n const params: unknown[] = [\n docId,\n scope,\n record.aType,\n record.aUid,\n record.axbType,\n record.bType,\n record.bUid,\n JSON.stringify(record.data ?? {}),\n record.v ?? null,\n nowMillis,\n nowMillis,\n ];\n return { sql, params };\n}\n\n/**\n * Compile an `UpdatePayload` into a single UPDATE statement.\n *\n * - `replaceData` overwrites the whole `data` JSON.\n * - `dataFields` shallow-merges fields via `json_set(data, '$.k', v, …)`.\n * - `v` is set when provided.\n * - `updated_at` is always stamped.\n */\nexport function compileUpdate(\n table: string,\n scope: string,\n docId: string,\n update: UpdatePayload,\n nowMillis: number,\n): CompiledStatement {\n const setClauses: string[] = [];\n const params: unknown[] = [];\n\n if (update.replaceData) {\n setClauses.push(`\"data\" = ?`);\n params.push(JSON.stringify(update.replaceData));\n } else if (update.dataFields && Object.keys(update.dataFields).length > 0) {\n // json_set(data, '$.k1', ?, '$.k2', ?, …)\n const entries = Object.entries(update.dataFields);\n const pathArgs = entries.map(() => `?, ?`).join(', ');\n setClauses.push(`\"data\" = json_set(COALESCE(\"data\", '{}'), ${pathArgs})`);\n for (const [k, v] of entries) {\n validateJsonPathKey(k);\n params.push(`$.${k}`);\n params.push(bindValue(v));\n }\n }\n\n if (update.v !== undefined) {\n setClauses.push(`\"v\" = ?`);\n params.push(update.v);\n }\n\n setClauses.push(`\"updated_at\" = ?`);\n params.push(nowMillis);\n\n // WHERE params come last\n params.push(scope, docId);\n\n return {\n sql: `UPDATE ${quoteIdent(table)} SET ${setClauses.join(', ')} WHERE \"scope\" = ? AND \"doc_id\" = ?`,\n params,\n };\n}\n\nexport function compileDelete(table: string, scope: string, docId: string): CompiledStatement {\n return {\n sql: `DELETE FROM ${quoteIdent(table)} WHERE \"scope\" = ? AND \"doc_id\" = ?`,\n params: [scope, docId],\n };\n}\n\n/**\n * Delete every row whose scope starts with `scopePrefix` followed by '/'.\n * Used by cascade delete to wipe all subgraphs nested under a node.\n *\n * The trailing '/' guard prevents `'a'` from matching `'ab'`. The exact-\n * match case (`scope = scopePrefix`) is intentionally NOT included: the\n * prefix passed in is always `<storageScope>/<uid>` (or just `<uid>` at\n * root), which is an odd-segment count, while every stored row's scope is\n * `''` (root) or an even-segment `<uid>/<name>[/…]` pair sequence — so\n * `scope = scopePrefix` can never match a real row.\n */\nexport function compileDeleteScopePrefix(table: string, scopePrefix: string): CompiledStatement {\n // SQLite LIKE escape: prefix could contain '%' or '_'. Escape with '\\\\'.\n const escaped = escapeLike(scopePrefix);\n return {\n sql: `DELETE FROM ${quoteIdent(table)} WHERE \"scope\" LIKE ? ESCAPE '\\\\'`,\n params: [`${escaped}/%`],\n };\n}\n\n/**\n * Count rows that `compileDeleteScopePrefix` would delete. Used by cascade\n * to report an accurate total in `CascadeResult.deleted` — the prefix-\n * delete is a single SQL statement and the executor surface doesn't expose\n * per-row counts. One extra index lookup per cascade is cheap relative to\n * the delete itself.\n */\nexport function compileCountScopePrefix(table: string, scopePrefix: string): CompiledStatement {\n const escaped = escapeLike(scopePrefix);\n return {\n sql: `SELECT COUNT(*) AS n FROM ${quoteIdent(table)} WHERE \"scope\" LIKE ? ESCAPE '\\\\'`,\n params: [`${escaped}/%`],\n };\n}\n\nfunction escapeLike(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/%/g, '\\\\%').replace(/_/g, '\\\\_');\n}\n\n/**\n * Convert a row returned by a SQLite driver into a `StoredGraphRecord`.\n * `created_at`/`updated_at` are wrapped in `GraphTimestampImpl` so they\n * present the same surface as Firestore's `Timestamp`.\n */\nexport function rowToRecord(row: Record<string, unknown>): StoredGraphRecord {\n const dataString = row.data as string | null;\n const data = dataString ? (JSON.parse(dataString) as Record<string, unknown>) : {};\n\n const createdMs = toMillis(row.created_at);\n const updatedMs = toMillis(row.updated_at);\n\n const record: Record<string, unknown> = {\n aType: row.a_type as string,\n aUid: row.a_uid as string,\n axbType: row.axb_type as string,\n bType: row.b_type as string,\n bUid: row.b_uid as string,\n data,\n createdAt: GraphTimestampImpl.fromMillis(createdMs) as unknown as GraphTimestamp,\n updatedAt: GraphTimestampImpl.fromMillis(updatedMs) as unknown as GraphTimestamp,\n };\n\n if (row.v !== null && row.v !== undefined) {\n record.v = Number(row.v);\n }\n return record as unknown as StoredGraphRecord;\n}\n\nfunction toMillis(value: unknown): number {\n if (typeof value === 'number') return value;\n if (typeof value === 'bigint') return Number(value);\n if (typeof value === 'string') return Number(value);\n return 0;\n}\n","/**\n * SQLite implementation of `StorageBackend`.\n *\n * Uses a single table keyed by `(scope, doc_id)`. Subgraphs are encoded in\n * the `scope` column as a materialized path of interleaved parent UIDs and\n * subgraph names — `''` at the root, `'<uid>/<name>'` one level down,\n * `'<uid1>/<name1>/<uid2>/<name2>'` two levels down, and so on. Cascade\n * delete uses a single `DELETE … WHERE scope LIKE 'prefix/%'` instead of\n * walking subcollections.\n */\n\nimport { computeEdgeDocId, computeNodeDocId } from '../docid.js';\nimport { FiregraphError } from '../errors.js';\nimport { buildEdgeQueryPlan } from '../query.js';\nimport type {\n BulkBatchError,\n BulkOptions,\n BulkResult,\n CascadeResult,\n FindEdgesParams,\n GraphReader,\n QueryFilter,\n QueryOptions,\n StoredGraphRecord,\n} from '../types.js';\nimport type {\n BatchBackend,\n StorageBackend,\n TransactionBackend,\n UpdatePayload,\n WritableRecord,\n} from './backend.js';\nimport { NODE_RELATION } from './constants.js';\nimport type { SqliteExecutor, SqliteTxExecutor } from './sqlite-executor.js';\nimport type { CompiledStatement } from './sqlite-sql.js';\nimport {\n compileCountScopePrefix,\n compileDelete,\n compileDeleteScopePrefix,\n compileSelect,\n compileSelectByDocId,\n compileSelectGlobal,\n compileSet,\n compileUpdate,\n rowToRecord,\n} from './sqlite-sql.js';\n\nexport interface SqliteBackendOptions {\n /** Logical scope path (chained subgraph names) — used for `allowedIn` matching. */\n scopePath?: string;\n /** Internal storage scope (interleaved parent-uid/name path). */\n storageScope?: string;\n}\n\n/**\n * Default per-chunk retry budget for bulk/cascade operations. Mirrors the\n * Firestore bulk path (`src/bulk.ts`) so behaviour is consistent across\n * backends. Callers override via `BulkOptions.maxRetries`.\n */\nconst DEFAULT_MAX_RETRIES = 3;\nconst BASE_RETRY_DELAY_MS = 200;\n/**\n * Upper bound for the exponential backoff between chunk retries. Without\n * this cap, `maxRetries: 10` would push the final wait past 100s; legitimate\n * transient errors recover well within a few seconds, and longer waits just\n * delay the surfacing of permanent failures.\n */\nconst MAX_RETRY_DELAY_MS = 5000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Return the smaller of two optional positive numbers, treating `undefined`\n * as \"no cap.\" Used to combine caller-supplied `BulkOptions.batchSize` with\n * the driver's own `maxBatchSize` so the more restrictive cap wins.\n */\nfunction minDefined(a: number | undefined, b: number | undefined): number | undefined {\n if (a === undefined) return b;\n if (b === undefined) return a;\n return Math.min(a, b);\n}\n\n/**\n * Split `statements` into chunks that respect both a per-batch statement\n * count cap (`maxStatements`) and a per-batch total bound-parameter cap\n * (`maxParams`). When neither cap is provided the entire list is returned\n * as a single chunk (preserves cross-batch atomicity for drivers like\n * DO SQLite that have no caps).\n *\n * Single-statement edge case: if a single statement's parameter count\n * already exceeds `maxParams`, it's emitted as its own chunk anyway. The\n * driver will reject it, which is the correct behavior — silently\n * dropping it would be worse.\n */\nfunction chunkStatements<T extends { params: unknown[] }>(\n statements: T[],\n maxStatements: number | undefined,\n maxParams: number | undefined,\n): T[][] {\n const stmtCap =\n maxStatements && maxStatements > 0 && Number.isFinite(maxStatements)\n ? Math.floor(maxStatements)\n : Infinity;\n const paramCap =\n maxParams && maxParams > 0 && Number.isFinite(maxParams) ? Math.floor(maxParams) : Infinity;\n\n if (stmtCap === Infinity && paramCap === Infinity) {\n return [statements];\n }\n\n const chunks: T[][] = [];\n let current: T[] = [];\n let currentParamCount = 0;\n for (const stmt of statements) {\n const stmtParams = stmt.params.length;\n const wouldExceedStmt = current.length + 1 > stmtCap;\n const wouldExceedParam = currentParamCount + stmtParams > paramCap;\n if (current.length > 0 && (wouldExceedStmt || wouldExceedParam)) {\n chunks.push(current);\n current = [];\n currentParamCount = 0;\n }\n current.push(stmt);\n currentParamCount += stmtParams;\n }\n if (current.length > 0) chunks.push(current);\n return chunks;\n}\n\nclass SqliteTransactionBackendImpl implements TransactionBackend {\n constructor(\n private readonly tx: SqliteTxExecutor,\n private readonly tableName: string,\n private readonly storageScope: string,\n ) {}\n\n async getDoc(docId: string): Promise<StoredGraphRecord | null> {\n const stmt = compileSelectByDocId(this.tableName, this.storageScope, docId);\n const rows = await this.tx.all(stmt.sql, stmt.params);\n return rows.length === 0 ? null : rowToRecord(rows[0]);\n }\n\n async query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]> {\n const stmt = compileSelect(this.tableName, this.storageScope, filters, options);\n const rows = await this.tx.all(stmt.sql, stmt.params);\n return rows.map(rowToRecord);\n }\n\n async setDoc(docId: string, record: WritableRecord): Promise<void> {\n const stmt = compileSet(this.tableName, this.storageScope, docId, record, Date.now());\n await this.tx.run(stmt.sql, stmt.params);\n }\n\n async updateDoc(docId: string, update: UpdatePayload): Promise<void> {\n const stmt = compileUpdate(this.tableName, this.storageScope, docId, update, Date.now());\n // RETURNING + `all()` for parity with Firestore — see SqliteBackendImpl.updateDoc.\n const sqlWithReturning = `${stmt.sql} RETURNING \"doc_id\"`;\n const rows = await this.tx.all(sqlWithReturning, stmt.params);\n if (rows.length === 0) {\n throw new FiregraphError(\n `updateDoc: no document found for doc_id=${docId} (scope=${this.storageScope})`,\n 'NOT_FOUND',\n );\n }\n }\n\n async deleteDoc(docId: string): Promise<void> {\n const stmt = compileDelete(this.tableName, this.storageScope, docId);\n await this.tx.run(stmt.sql, stmt.params);\n }\n}\n\nclass SqliteBatchBackendImpl implements BatchBackend {\n private readonly statements: CompiledStatement[] = [];\n\n constructor(\n private readonly executor: SqliteExecutor,\n private readonly tableName: string,\n private readonly storageScope: string,\n ) {}\n\n setDoc(docId: string, record: WritableRecord): void {\n this.statements.push(compileSet(this.tableName, this.storageScope, docId, record, Date.now()));\n }\n\n updateDoc(docId: string, update: UpdatePayload): void {\n this.statements.push(\n compileUpdate(this.tableName, this.storageScope, docId, update, Date.now()),\n );\n }\n\n deleteDoc(docId: string): void {\n this.statements.push(compileDelete(this.tableName, this.storageScope, docId));\n }\n\n async commit(): Promise<void> {\n if (this.statements.length === 0) return;\n await this.executor.batch(this.statements);\n this.statements.length = 0;\n }\n}\n\nclass SqliteBackendImpl implements StorageBackend {\n /** Logical table name (returned through `collectionPath` for parity with Firestore). */\n readonly collectionPath: string;\n readonly scopePath: string;\n /** Materialized storage scope (interleaved parent UIDs + subgraph names). */\n private readonly storageScope: string;\n\n constructor(\n private readonly executor: SqliteExecutor,\n tableName: string,\n storageScope: string,\n scopePath: string,\n ) {\n this.collectionPath = tableName;\n this.storageScope = storageScope;\n this.scopePath = scopePath;\n }\n\n // --- Reads ---\n\n async getDoc(docId: string): Promise<StoredGraphRecord | null> {\n const stmt = compileSelectByDocId(this.collectionPath, this.storageScope, docId);\n const rows = await this.executor.all(stmt.sql, stmt.params);\n return rows.length === 0 ? null : rowToRecord(rows[0]);\n }\n\n async query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]> {\n const stmt = compileSelect(this.collectionPath, this.storageScope, filters, options);\n const rows = await this.executor.all(stmt.sql, stmt.params);\n return rows.map(rowToRecord);\n }\n\n // --- Writes ---\n\n async setDoc(docId: string, record: WritableRecord): Promise<void> {\n const stmt = compileSet(this.collectionPath, this.storageScope, docId, record, Date.now());\n await this.executor.run(stmt.sql, stmt.params);\n }\n\n async updateDoc(docId: string, update: UpdatePayload): Promise<void> {\n const stmt = compileUpdate(this.collectionPath, this.storageScope, docId, update, Date.now());\n // Use RETURNING + `all()` so missing rows surface as an error, matching\n // Firestore's `update()` semantics (NOT_FOUND when the doc doesn't exist).\n // SQLite ≥3.35 supports UPDATE … RETURNING; better-sqlite3, D1, and DO\n // SQLite all run on a recent enough engine.\n const sqlWithReturning = `${stmt.sql} RETURNING \"doc_id\"`;\n const rows = await this.executor.all(sqlWithReturning, stmt.params);\n if (rows.length === 0) {\n throw new FiregraphError(\n `updateDoc: no document found for doc_id=${docId} (scope=${this.storageScope})`,\n 'NOT_FOUND',\n );\n }\n }\n\n async deleteDoc(docId: string): Promise<void> {\n const stmt = compileDelete(this.collectionPath, this.storageScope, docId);\n await this.executor.run(stmt.sql, stmt.params);\n }\n\n // --- Transactions / Batches ---\n\n async runTransaction<T>(fn: (tx: TransactionBackend) => Promise<T>): Promise<T> {\n if (!this.executor.transaction) {\n throw new FiregraphError(\n 'Interactive transactions are not supported by this SQLite driver. ' +\n 'D1 in particular has no read-then-conditional-write transactions; ' +\n 'use a Durable Object SQLite client instead, or rewrite the code path ' +\n 'as a batch().',\n 'UNSUPPORTED_OPERATION',\n );\n }\n return this.executor.transaction(async (tx) => {\n const txBackend = new SqliteTransactionBackendImpl(\n tx,\n this.collectionPath,\n this.storageScope,\n );\n return fn(txBackend);\n });\n }\n\n createBatch(): BatchBackend {\n return new SqliteBatchBackendImpl(this.executor, this.collectionPath, this.storageScope);\n }\n\n // --- Subgraphs ---\n\n subgraph(parentNodeUid: string, name: string): StorageBackend {\n // Defense-in-depth: the public `GraphClient.subgraph()` also validates,\n // but backend users (traversal, cross-graph hops, custom integrations)\n // reach this method directly. A bad UID or a name containing '/' would\n // corrupt the materialized-path scope encoding — reject loudly.\n if (!parentNodeUid || parentNodeUid.includes('/')) {\n throw new FiregraphError(\n `Invalid parentNodeUid for subgraph: \"${parentNodeUid}\". ` +\n 'Must be a non-empty string without \"/\".',\n 'INVALID_SUBGRAPH',\n );\n }\n if (!name || name.includes('/')) {\n throw new FiregraphError(\n `Subgraph name must not contain \"/\" and must be non-empty: got \"${name}\". ` +\n 'Use chained .subgraph() calls for nested subgraphs.',\n 'INVALID_SUBGRAPH',\n );\n }\n const newStorageScope = this.storageScope\n ? `${this.storageScope}/${parentNodeUid}/${name}`\n : `${parentNodeUid}/${name}`;\n const newScope = this.scopePath ? `${this.scopePath}/${name}` : name;\n return new SqliteBackendImpl(this.executor, this.collectionPath, newStorageScope, newScope);\n }\n\n // --- Cascade & bulk ---\n\n async removeNodeCascade(\n uid: string,\n reader: GraphReader,\n options?: BulkOptions,\n ): Promise<CascadeResult> {\n // Collect all edges touching the node in the current scope (excluding self-loop).\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\n const seen = new Set<string>();\n const edgeDocIds: string[] = [];\n for (const edge of [...outgoingRaw, ...incomingRaw]) {\n if (edge.axbType === NODE_RELATION) continue;\n const docId = computeEdgeDocId(edge.aUid, edge.axbType, edge.bUid);\n if (!seen.has(docId)) {\n seen.add(docId);\n edgeDocIds.push(docId);\n }\n }\n\n const nodeDocId = computeNodeDocId(uid);\n const shouldDeleteSubgraphs = options?.deleteSubcollections !== false;\n\n // Pre-count subgraph rows so the returned `deleted` total reflects the\n // actual number of records removed by the prefix-delete (which is a\n // single statement, but may match many rows). One extra index lookup is\n // cheap relative to the cascade itself.\n let subgraphRowCount = 0;\n if (shouldDeleteSubgraphs) {\n const prefix = this.storageScope ? `${this.storageScope}/${uid}` : uid;\n const countStmt = compileCountScopePrefix(this.collectionPath, prefix);\n const countRows = await this.executor.all(countStmt.sql, countStmt.params);\n const first = countRows[0] as Record<string, unknown> | undefined;\n const n = first?.n;\n subgraphRowCount = typeof n === 'bigint' ? Number(n) : Number(n ?? 0);\n }\n\n // Build the full statement list. Order: edges → node → prefix-delete.\n // When the executor's `batch()` is fully atomic (DO SQLite uses\n // `transactionSync`) the chunking loop below collapses to a single batch\n // and the operation is atomic. When the executor caps batches (D1, ~100\n // statements) we lose cross-batch atomicity, but `removeNodeCascade` is\n // idempotent so a caller can retry after a partial failure.\n const writeStatements: CompiledStatement[] = edgeDocIds.map((id) =>\n compileDelete(this.collectionPath, this.storageScope, id),\n );\n writeStatements.push(compileDelete(this.collectionPath, this.storageScope, nodeDocId));\n if (shouldDeleteSubgraphs) {\n const prefix = this.storageScope ? `${this.storageScope}/${uid}` : uid;\n writeStatements.push(compileDeleteScopePrefix(this.collectionPath, prefix));\n }\n\n const {\n deleted: stmtDeleted,\n batches,\n errors,\n } = await this.executeChunkedBatches(writeStatements, options);\n\n // `nodeDeleted` / `edgesDeleted` reflect best-effort completion: a\n // chunk failure leaves us unable to know which sub-batch contained the\n // node-row delete, so we conservatively flag both as incomplete when\n // any batch fails. The caller can retry — cascade is idempotent.\n const allOk = errors.length === 0;\n const edgesDeleted = allOk ? edgeDocIds.length : 0;\n const nodeDeleted = allOk;\n\n // `stmtDeleted` counts committed *statements*. Replace the prefix-\n // delete's per-statement contribution (1) with the pre-computed row\n // count so callers see a true row total. Only credit subgraph rows when\n // every chunk succeeded — partial failure means we can't be sure the\n // chunk containing the prefix-delete actually committed.\n const prefixStatementContribution = shouldDeleteSubgraphs && allOk ? 1 : 0;\n const deleted = stmtDeleted - prefixStatementContribution + (allOk ? subgraphRowCount : 0);\n\n return { deleted, batches, errors, edgesDeleted, nodeDeleted };\n }\n\n async bulkRemoveEdges(\n params: FindEdgesParams,\n reader: GraphReader,\n options?: BulkOptions,\n ): Promise<BulkResult> {\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\n if (docIds.length === 0) {\n return { deleted: 0, batches: 0, errors: [] };\n }\n\n const statements = docIds.map((id) =>\n compileDelete(this.collectionPath, this.storageScope, id),\n );\n\n return this.executeChunkedBatches(statements, options);\n }\n\n /**\n * Submit `statements` to the executor as one or more `batch()` calls,\n * chunking by `executor.maxBatchSize` (e.g. D1's ~100-statement cap).\n * Drivers that don't advertise a cap submit everything in one batch,\n * preserving cross-batch atomicity.\n *\n * Each chunk is retried with exponential backoff up to `maxRetries`\n * (default 3) before being recorded in `errors`. The loop continues past\n * a permanently failed chunk so the caller still gets partial progress\n * visibility — to halt on first failure, set `maxRetries: 0` and check\n * `result.errors.length` after the call.\n *\n * Returns `BulkResult`-shaped fields. `deleted` reflects only the\n * statement count of *successfully committed* batches — a prefix-delete\n * statement contributes 1 to that total even though it may match many\n * rows; `removeNodeCascade` patches that up with a pre-counted row total.\n *\n * **Atomicity caveat (D1):** when chunking kicks in, atomicity is lost\n * across chunk boundaries — one chunk may commit while a later one fails.\n * `removeNodeCascade` is idempotent (deleting the same docs again is a\n * no-op) so a caller can simply retry on partial failure. `bulkRemoveEdges`\n * is also idempotent for the same reason. DO SQLite leaves `maxBatchSize`\n * unset, so everything funnels through one atomic `transactionSync` and\n * this caveat does not apply.\n */\n private async executeChunkedBatches(\n statements: CompiledStatement[],\n options?: BulkOptions,\n ): Promise<{ deleted: number; batches: number; errors: BulkBatchError[] }> {\n if (statements.length === 0) {\n return { deleted: 0, batches: 0, errors: [] };\n }\n const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;\n\n // Split `statements` into chunks up front. Chunking honors the smallest\n // of: caller-supplied `batchSize` (used by callers who want progress\n // granularity), the driver's statement-count cap (`maxBatchSize`, D1 ≈\n // 100), and the driver's total bound-parameter cap (`maxBatchParams`,\n // D1 ≈ 1000). Most cascade/bulk statements are 2-param DELETEs so the\n // param cap rarely triggers, but we respect it defensively. Drivers with\n // no declared caps and no caller cap submit everything in one batch (DO\n // SQLite's atomic `transactionSync`).\n const callerBatchSize = options?.batchSize;\n const stmtCap = minDefined(callerBatchSize, this.executor.maxBatchSize);\n const chunks = chunkStatements(statements, stmtCap, this.executor.maxBatchParams);\n\n const errors: BulkBatchError[] = [];\n let deleted = 0;\n let batches = 0;\n const totalBatches = chunks.length;\n\n const driverParamCap = this.executor.maxBatchParams;\n\n for (let batchIndex = 0; batchIndex < chunks.length; batchIndex++) {\n const chunk = chunks[batchIndex];\n\n // A chunk that's a single statement whose param count already exceeds\n // the driver's per-batch param cap will be rejected on every attempt —\n // retrying just adds latency before surfacing the failure. `chunkStatements`\n // intentionally emits such statements as their own chunk (failing loudly\n // beats silently dropping); fast-fail here closes the loop.\n const isUnretriableOversize =\n chunk.length === 1 &&\n driverParamCap !== undefined &&\n chunk[0].params.length > driverParamCap;\n\n let committed = false;\n let lastError: Error | null = null;\n const effectiveRetries = isUnretriableOversize ? 0 : maxRetries;\n for (let attempt = 0; attempt <= effectiveRetries; attempt++) {\n try {\n await this.executor.batch(chunk);\n committed = true;\n break;\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n if (attempt < effectiveRetries) {\n const delay = Math.min(BASE_RETRY_DELAY_MS * Math.pow(2, attempt), MAX_RETRY_DELAY_MS);\n await sleep(delay);\n }\n }\n }\n\n if (committed) {\n deleted += chunk.length;\n batches += 1;\n } else if (lastError) {\n errors.push({\n batchIndex,\n error: lastError,\n operationCount: chunk.length,\n });\n }\n\n if (options?.onProgress) {\n options.onProgress({\n completedBatches: batches,\n totalBatches,\n deletedSoFar: deleted,\n });\n }\n }\n\n return { deleted, batches, errors };\n }\n\n // --- Cross-scope (collection group) ---\n\n async findEdgesGlobal(\n params: FindEdgesParams,\n collectionName?: string,\n ): Promise<StoredGraphRecord[]> {\n const plan = buildEdgeQueryPlan(params);\n if (plan.strategy === 'get') {\n throw new FiregraphError(\n 'findEdgesGlobal() requires a query, not a direct document lookup. ' +\n 'Omit one of aUid/axbType/bUid to force a query strategy.',\n 'INVALID_QUERY',\n );\n }\n // Mirror Firestore's `collectionGroup(name)` semantics over the\n // materialized-scope SQLite layout: when `collectionName` matches the\n // table name (the implicit root default), filter to root rows; otherwise\n // filter to rows whose scope's last segment equals the requested name.\n const name = collectionName ?? this.collectionPath;\n const scopeNameFilter = {\n name,\n isRoot: name === this.collectionPath,\n };\n const stmt = compileSelectGlobal(\n this.collectionPath,\n plan.filters,\n plan.options,\n scopeNameFilter,\n );\n const rows = await this.executor.all(stmt.sql, stmt.params);\n return rows.map(rowToRecord);\n }\n}\n\n/**\n * Create a SQLite-backed `StorageBackend`.\n *\n * `tableName` is the single table that holds every triple. The driver must\n * have already created the table and indexes via `buildSchemaStatements()`\n * (the public driver factories — `createD1GraphClient` and\n * `createDOSqliteGraphClient` — handle this automatically).\n */\nexport function createSqliteBackend(\n executor: SqliteExecutor,\n tableName: string,\n options: SqliteBackendOptions = {},\n): StorageBackend {\n const storageScope = options.storageScope ?? '';\n const scopePath = options.scopePath ?? '';\n return new SqliteBackendImpl(executor, tableName, storageScope, scopePath);\n}\n"],"mappings":"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","names":["import_node_crypto","Ajv","addFormats","import_node_crypto","serializeFirestoreTypes","deserializeFirestoreTypes","buildWritableNodeRecord","buildWritableEdgeRecord","buildWritableNodeRecord","buildWritableEdgeRecord"]}
|