@typicalday/firegraph 0.14.1 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +62 -20
- package/dist/backend-CE3pM9-T.d.ts +167 -0
- package/dist/{backend-DuvHGgK1.d.cts → backend-DNzv8KSR.d.cts} +34 -20
- package/dist/{backend-DuvHGgK1.d.ts → backend-DNzv8KSR.d.ts} +34 -20
- package/dist/backend-EjFfw9yO.d.cts +167 -0
- package/dist/backend.cjs.map +1 -1
- package/dist/backend.d.cts +2 -2
- package/dist/backend.d.ts +2 -2
- package/dist/backend.js +1 -1
- package/dist/chunk-5JBNLH5W.js +732 -0
- package/dist/chunk-5JBNLH5W.js.map +1 -0
- package/dist/{chunk-3AHHXMWX.js → chunk-6IO74NKD.js} +23 -44
- package/dist/chunk-6IO74NKD.js.map +1 -0
- package/dist/{chunk-DJI3VXXA.js → chunk-7IEZ6IYY.js} +2 -2
- package/dist/chunk-7IEZ6IYY.js.map +1 -0
- package/dist/chunk-NGAJCALM.js +34 -0
- package/dist/chunk-NGAJCALM.js.map +1 -0
- package/dist/chunk-NZVSLWNY.js +867 -0
- package/dist/chunk-NZVSLWNY.js.map +1 -0
- package/dist/{chunk-N5HFDWQX.js → chunk-PWIO46RT.js} +1 -1
- package/dist/{chunk-N5HFDWQX.js.map → chunk-PWIO46RT.js.map} +1 -1
- package/dist/{client-BKi3vk0Q.d.ts → client-CNAwJayO.d.ts} +1 -1
- package/dist/{client-BrsaXtDV.d.cts → client-CaXH5D5C.d.cts} +1 -1
- package/dist/{client-Bk2Cm6xv.d.cts → client-DoyEdJ5w.d.cts} +1 -1
- package/dist/{client-Bk2Cm6xv.d.ts → client-DoyEdJ5w.d.ts} +1 -1
- package/dist/cloudflare/index.cjs +159 -167
- package/dist/cloudflare/index.cjs.map +1 -1
- package/dist/cloudflare/index.d.cts +73 -70
- package/dist/cloudflare/index.d.ts +73 -70
- package/dist/cloudflare/index.js +54 -589
- package/dist/cloudflare/index.js.map +1 -1
- package/dist/codegen/index.d.cts +1 -1
- package/dist/codegen/index.d.ts +1 -1
- package/dist/firestore-enterprise/index.cjs +11 -9
- package/dist/firestore-enterprise/index.cjs.map +1 -1
- package/dist/firestore-enterprise/index.d.cts +3 -3
- package/dist/firestore-enterprise/index.d.ts +3 -3
- package/dist/firestore-enterprise/index.js +6 -4
- package/dist/firestore-enterprise/index.js.map +1 -1
- package/dist/firestore-standard/index.cjs +11 -9
- package/dist/firestore-standard/index.cjs.map +1 -1
- package/dist/firestore-standard/index.d.cts +3 -3
- package/dist/firestore-standard/index.d.ts +3 -3
- package/dist/firestore-standard/index.js +4 -3
- package/dist/firestore-standard/index.js.map +1 -1
- package/dist/index.cjs +11 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/query-client/index.d.cts +2 -2
- package/dist/query-client/index.d.ts +2 -2
- package/dist/{registry-C2KUPVZj.d.ts → registry-By1i-zge.d.ts} +2 -2
- package/dist/{registry-Bc7h6WTM.d.cts → registry-CNToyEra.d.cts} +2 -2
- package/dist/sqlite/index.cjs +599 -380
- package/dist/sqlite/index.cjs.map +1 -1
- package/dist/sqlite/index.d.cts +4 -110
- package/dist/sqlite/index.d.ts +4 -110
- package/dist/sqlite/index.js +7 -1144
- package/dist/sqlite/index.js.map +1 -1
- package/dist/sqlite/local.cjs +2262 -0
- package/dist/sqlite/local.cjs.map +1 -0
- package/dist/sqlite/local.d.cts +109 -0
- package/dist/sqlite/local.d.ts +109 -0
- package/dist/sqlite/local.js +546 -0
- package/dist/sqlite/local.js.map +1 -0
- package/package.json +15 -1
- package/dist/chunk-3AHHXMWX.js.map +0 -1
- package/dist/chunk-DJI3VXXA.js.map +0 -1
- package/dist/chunk-NNBSUOOF.js +0 -289
- package/dist/chunk-NNBSUOOF.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/json-schema.ts","../src/migration.ts","../src/scope.ts","../src/registry.ts","../src/sandbox.ts","../src/dynamic-registry.ts","../src/query.ts","../src/query-safety.ts","../src/batch.ts","../src/transaction.ts","../src/client.ts","../src/id.ts"],"sourcesContent":["/**\n * JSON Schema validation and introspection utilities.\n *\n * Uses `@cfworker/json-schema` for validation — a runtime-interpreter\n * JSON Schema validator that does not rely on `new Function()` and is\n * therefore compatible with Cloudflare Workers (which run V8 with\n * `--disallow-code-generation-from-strings`). Ajv was used here\n * previously, but its `ajv.compile(schema)` generates a validator via\n * the Function constructor and fails with \"Code generation from strings\n * disallowed for this context\" whenever firegraph's dynamic-registry\n * bootstrap or `reloadRegistry` runs inside a Worker.\n *\n * The introspection half (`jsonSchemaToFieldMeta`) is pure string/object\n * manipulation with no validator dependency.\n */\n\nimport { type OutputUnit, type Schema, Validator } from '@cfworker/json-schema';\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\n/** Cap on how many errors get joined into the human-readable message. */\nconst MAX_RENDERED_ERRORS = 20;\n\n/**\n * Compile a JSON Schema into a validation function.\n *\n * The returned function throws `ValidationError` if data is invalid. The\n * error's `details` is the `OutputUnit[]` array produced by\n * `@cfworker/json-schema` — consumers that previously inspected Ajv's\n * `ErrorObject[]` need to map to the cfworker shape\n * (`{ keyword, keywordLocation, instanceLocation, error }`).\n *\n * Draft 2020-12 is requested by default to match the library's richest\n * feature set; schemas that omit `$schema` still validate under it\n * since keyword semantics back-compat to draft-07 for the fields\n * firegraph actually uses.\n *\n * `shortCircuit` is explicitly disabled so `result.errors` contains\n * every violation, not just the first one — humans rely on the joined\n * error message to debug bad writes from the editor / chat UI. The\n * full array is preserved on `ValidationError.details`; only the\n * rendered message is capped at `MAX_RENDERED_ERRORS` lines so\n * pathological `oneOf`/`anyOf` schemas can't blow up log lines.\n *\n * Format keywords supported by `@cfworker/json-schema` (anything else\n * is silently passed through — see node_modules/@cfworker/json-schema/\n * src/format.ts):\n * `date`, `time`, `date-time`, `duration`,\n * `email`, `hostname`, `ipv4`, `ipv6`,\n * `uri`, `uri-reference`, `uri-template`, `url`,\n * `uuid`, `regex`,\n * `json-pointer`, `relative-json-pointer`, `json-pointer-uri-fragment`.\n */\nexport function compileSchema(schema: object, label?: string): (data: unknown) => void {\n // `object` is the public type used throughout `RegistryEntry.jsonSchema`\n // and the dynamic-client API; cfworker's `Schema` is structurally\n // `{ [k: string]: any }`, which a JSON Schema document always\n // satisfies at runtime. The cast is therefore safe in practice —\n // pass anything other than a plain JSON-Schema-shaped object and\n // `dereference()` inside the validator will throw at construction.\n const validator = new Validator(schema as Schema, '2020-12', false);\n return (data: unknown) => {\n const result = validator.validate(data);\n if (!result.valid) {\n const total = result.errors.length;\n const head = result.errors.slice(0, MAX_RENDERED_ERRORS).map(formatError).join('; ');\n const overflow = total > MAX_RENDERED_ERRORS ? ` (+${total - MAX_RENDERED_ERRORS} more)` : '';\n throw new ValidationError(\n `Data validation failed${label ? ' for ' + label : ''}: ${head}${overflow}`,\n result.errors,\n );\n }\n };\n}\n\n/**\n * Format a single cfworker `OutputUnit` into a human-readable line.\n *\n * cfworker's `instanceLocation` is a JSON-Pointer-as-URI-fragment\n * (`#`, `#/foo`, `#/foo/0/bar`); strip the leading `#` so the rendered\n * path looks like Ajv's `instancePath` (`/foo/0/bar`) and root errors\n * read as `/` rather than `#`. The `[keyword]` prefix is included so\n * messages stay actionable when `error` is terse (e.g. `not`, `enum`).\n */\nfunction formatError(err: OutputUnit): string {\n const path = err.instanceLocation.replace(/^#/, '') || '/';\n const keyword = err.keyword ? `[${err.keyword}] ` : '';\n const detail = err.error ? `: ${keyword}${err.error}` : '';\n return `${path}${detail}`;\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(migrations: MigrationStep[], label: string): 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(records.map((r) => migrateRecord(r, registry, globalWriteBack)));\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(path: string[], pi: number, pattern: string[], qi: number): 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 // Build aType → subgraph-topology index.\n //\n // For each source aType, collect edge entries whose `targetGraph` is set —\n // these are the aType's direct subgraph children. Dedupe by `targetGraph`\n // alone (not by axbType): the physical subgraph store is addressed by\n // (parentUid, targetGraph) and the cascade caller only cares about which\n // child subgraphs to tear down. Two distinct edge relations pointing into\n // the same `targetGraph` would otherwise produce duplicate destroy calls\n // on the same physical backend.\n const topologyIndex = new Map<string, ReadonlyArray<RegistryEntry>>();\n const topologyBuild = new Map<string, RegistryEntry[]>();\n const topologySeen = new Map<string, Set<string>>();\n for (const entry of entries) {\n if (!entry.targetGraph) continue;\n let seen = topologySeen.get(entry.aType);\n if (!seen) {\n seen = new Set();\n topologySeen.set(entry.aType, seen);\n }\n if (seen.has(entry.targetGraph)) continue;\n seen.add(entry.targetGraph);\n const existing = topologyBuild.get(entry.aType);\n if (existing) {\n existing.push(entry);\n } else {\n topologyBuild.set(entry.aType, [entry]);\n }\n }\n for (const [key, arr] of topologyBuild) {\n topologyIndex.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 getSubgraphTopology(aType: string): ReadonlyArray<RegistryEntry> {\n return topologyIndex.get(aType) ?? [];\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 getSubgraphTopology(aType: string): ReadonlyArray<RegistryEntry> {\n const baseResults = base.getSubgraphTopology(aType);\n const extResults = extension.getSubgraphTopology(aType);\n if (extResults.length === 0) return baseResults;\n if (baseResults.length === 0) return extResults;\n\n // Merge, base wins on `targetGraph` collision. Extension entries only\n // contribute new subgraph segments the base doesn't cover. Dedupe key\n // matches the physical DO address — (parentUid, targetGraph) — so two\n // different axbTypes pointing into the same segment collapse to one.\n const seen = new Set(baseResults.map((e) => e.targetGraph));\n const merged = [...baseResults];\n for (const entry of extResults) {\n if (!seen.has(entry.targetGraph)) {\n seen.add(entry.targetGraph);\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 indexes: entity.indexes,\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 indexes: entity.indexes,\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// non-Firestore bundles (e.g. the Cloudflare DO backend).\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 { 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 *\n * Memoized at module scope: `BOOTSTRAP_ENTRIES` is a `readonly` array\n * of module-level constants and `createRegistry` is pure over them, so\n * the resulting registry — including its compiled cfworker\n * `Validator`s — can be reused across every `GraphClientImpl`\n * constructor. This matters on Cloudflare Workers, where the dynamic\n * client constructor runs on every request that touches the\n * meta-registry path; without memoization we'd re-walk +\n * re-dereference these schemas per request.\n */\nlet _bootstrapRegistry: GraphRegistry | null = null;\nexport function createBootstrapRegistry(): GraphRegistry {\n if (_bootstrapRegistry) return _bootstrapRegistry;\n _bootstrapRegistry = createRegistry([...BOOTSTRAP_ENTRIES]);\n return _bootstrapRegistry;\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","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 type { BatchBackend, WritableRecord } from './internal/backend.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport { assertNoDeleteSentinels, flattenPatch } from './internal/write-plan.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 this.writeNode(aType, uid, data, 'merge');\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 this.writeEdge(aType, aUid, axbType, bType, bUid, data, 'merge');\n }\n\n async replaceNode(aType: string, uid: string, data: Record<string, unknown>): Promise<void> {\n this.writeNode(aType, uid, data, 'replace');\n }\n\n async replaceEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n this.writeEdge(aType, aUid, axbType, bType, bUid, data, 'replace');\n }\n\n private writeNode(\n aType: string,\n uid: string,\n data: Record<string, unknown>,\n mode: 'merge' | 'replace',\n ): void {\n assertNoDeleteSentinels(data, mode === 'replace' ? 'replaceNode' : 'putNode');\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, mode);\n }\n\n private writeEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n mode: 'merge' | 'replace',\n ): void {\n assertNoDeleteSentinels(data, mode === 'replace' ? 'replaceEdge' : 'putEdge');\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, mode);\n }\n\n async updateNode(uid: string, data: Record<string, unknown>): Promise<void> {\n const docId = computeNodeDocId(uid);\n this.backend.updateDoc(docId, { dataOps: flattenPatch(data) });\n }\n\n async updateEdge(\n aUid: string,\n axbType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n this.backend.updateDoc(docId, { dataOps: flattenPatch(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 { 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 { assertNoDeleteSentinels, flattenPatch } from './internal/write-plan.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 await this.writeNode(aType, uid, data, 'merge');\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 await this.writeEdge(aType, aUid, axbType, bType, bUid, data, 'merge');\n }\n\n async replaceNode(aType: string, uid: string, data: Record<string, unknown>): Promise<void> {\n await this.writeNode(aType, uid, data, 'replace');\n }\n\n async replaceEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n await this.writeEdge(aType, aUid, axbType, bType, bUid, data, 'replace');\n }\n\n private async writeNode(\n aType: string,\n uid: string,\n data: Record<string, unknown>,\n mode: 'merge' | 'replace',\n ): Promise<void> {\n assertNoDeleteSentinels(data, mode === 'replace' ? 'replaceNode' : 'putNode');\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, mode);\n }\n\n private async writeEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n mode: 'merge' | 'replace',\n ): Promise<void> {\n assertNoDeleteSentinels(data, mode === 'replace' ? 'replaceEdge' : 'putEdge');\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, mode);\n }\n\n async updateNode(uid: string, data: Record<string, unknown>): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.updateDoc(docId, { dataOps: flattenPatch(data) });\n }\n\n async updateEdge(\n aUid: string,\n axbType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n await this.backend.updateDoc(docId, { dataOps: flattenPatch(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 { BackendCapabilities, StorageBackend, WritableRecord } from './internal/backend.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport { assertNoDeleteSentinels, flattenPatch } from './internal/write-plan.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 AggregateResult,\n AggregateSpec,\n BulkOptions,\n BulkResult,\n BulkUpdatePatch,\n Capability,\n CascadeResult,\n CoreGraphClient,\n DefineTypeOptions,\n DynamicGraphClient,\n DynamicGraphMethods,\n DynamicRegistryConfig,\n EdgeTopology,\n EngineTraversalParams,\n EngineTraversalResult,\n ExpandParams,\n ExpandResult,\n FindEdgesParams,\n FindEdgesProjectedParams,\n FindNearestParams,\n FindNodesParams,\n FullTextSearchParams,\n GeoSearchParams,\n GraphBatch,\n GraphClient,\n GraphClientOptions,\n GraphReader,\n GraphRegistry,\n GraphTransaction,\n MigrationExecutor,\n MigrationFn,\n MigrationWriteBack,\n ProjectedRow,\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 CoreGraphClient, DynamicGraphMethods {\n readonly scanProtection: ScanProtection;\n\n /**\n * Capability set of the underlying backend. Mirrors `backend.capabilities`\n * verbatim so callers can portability-check (`client.capabilities.has(\n * 'query.join')`) without reaching for the backend handle. Static for the\n * lifetime of the client.\n */\n get capabilities(): BackendCapabilities {\n return this.backend.capabilities;\n }\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 * Snapshot of the currently-effective registry. Returns the merged view\n * used for domain-type validation and migration — in dynamic mode this is\n * `dynamicRegistry ?? staticRegistry ?? bootstrapRegistry`, so callers see\n * updates after `reloadRegistry()` without having to re-resolve anything.\n *\n * Exposed for backends that need topology access during bulk operations\n * (e.g. the Cloudflare DO backend's cross-DO cascade). Not part of the\n * public `GraphClient` surface.\n *\n * @internal\n */\n getRegistrySnapshot(): GraphRegistry | undefined {\n return this.getCombinedRegistry();\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 await this.writeNode(aType, uid, data, 'merge');\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 await this.writeEdge(aType, aUid, axbType, bType, bUid, data, 'merge');\n }\n\n async replaceNode(aType: string, uid: string, data: Record<string, unknown>): Promise<void> {\n await this.writeNode(aType, uid, data, 'replace');\n }\n\n async replaceEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n await this.writeEdge(aType, aUid, axbType, bType, bUid, data, 'replace');\n }\n\n private async writeNode(\n aType: string,\n uid: string,\n data: Record<string, unknown>,\n mode: 'merge' | 'replace',\n ): Promise<void> {\n assertNoDeleteSentinels(data, mode === 'replace' ? 'replaceNode' : 'putNode');\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, mode);\n }\n\n private async writeEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n mode: 'merge' | 'replace',\n ): Promise<void> {\n assertNoDeleteSentinels(data, mode === 'replace' ? 'replaceEdge' : 'putEdge');\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, mode);\n }\n\n async updateNode(uid: string, data: Record<string, unknown>): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.updateDoc(docId, { dataOps: flattenPatch(data) });\n }\n\n async updateEdge(\n aUid: string,\n axbType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n await this.backend.updateDoc(docId, { dataOps: flattenPatch(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 // Aggregate query (capability: query.aggregate)\n // ---------------------------------------------------------------------------\n\n async aggregate<A extends AggregateSpec>(\n params: FindEdgesParams & { aggregates: A },\n ): Promise<AggregateResult<A>> {\n if (!this.backend.aggregate) {\n throw new FiregraphError(\n 'aggregate() is not supported by the current storage backend.',\n 'UNSUPPORTED_OPERATION',\n );\n }\n\n // Allow zero-filter aggregates (e.g. count(*) over the whole collection).\n // findEdges-style buildEdgeQueryPlan rejects empty filter sets because a\n // bare findEdges with no identifying fields would be a full collection\n // scan; aggregate() is the legitimate use case for that shape.\n const hasAnyFilter =\n params.aType ||\n params.aUid ||\n params.axbType ||\n params.bType ||\n params.bUid ||\n (params.where && params.where.length > 0);\n\n if (!hasAnyFilter) {\n this.checkQuerySafety([], params.allowCollectionScan);\n const result = await this.backend.aggregate(params.aggregates, []);\n return result as AggregateResult<A>;\n }\n\n const plan = buildEdgeQueryPlan(params);\n if (plan.strategy === 'get') {\n throw new FiregraphError(\n 'aggregate() 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 result = await this.backend.aggregate(params.aggregates, plan.filters);\n return result as AggregateResult<A>;\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 // Server-side DML (capability: query.dml)\n // ---------------------------------------------------------------------------\n\n /**\n * Single-statement bulk DELETE. Translates `params` to a filter list via\n * `buildEdgeQueryPlan` (the same plan `findEdges` uses) and dispatches to\n * `backend.bulkDelete`. The fetch-then-delete loop in `bulkRemoveEdges`\n * is the cap-less fallback; this method is the fast path on backends\n * declaring `query.dml`.\n *\n * Scan-protection rules match `findEdges`: a query with no identifying\n * fields requires `allowCollectionScan: true` to pass. A bare-empty\n * filter set (no `aType`, `aUid`, etc., no `where`) is allowed at this\n * layer — shared SQLite bounds the blast radius via its leading `scope`\n * predicate — but the DO RPC backend rejects empty filters at the wire\n * boundary as defense-in-depth. To wipe a routed subgraph DO, use\n * `removeNodeCascade` on the parent node instead.\n */\n async bulkDelete(params: FindEdgesParams, options?: BulkOptions): Promise<BulkResult> {\n if (!this.backend.bulkDelete) {\n throw new FiregraphError(\n 'bulkDelete() is not supported by the current storage backend. ' +\n 'Fall back to bulkRemoveEdges() for backends without query.dml ' +\n '(e.g. Firestore Standard).',\n 'UNSUPPORTED_OPERATION',\n );\n }\n const filters = this.buildDmlFilters(params);\n return this.backend.bulkDelete(filters, options);\n }\n\n /**\n * Single-statement bulk UPDATE. Same translation path as `bulkDelete`,\n * but the patch is deep-merged into each matching row's `data` via the\n * shared `flattenPatch` pipeline. Identifying columns are immutable\n * through this path (see `BulkUpdatePatch` JSDoc).\n *\n * Empty-patch rejection happens inside the backend (`compileBulkUpdate`)\n * — a `data: {}` payload would only rewrite `updated_at`, which is\n * almost certainly a bug.\n */\n async bulkUpdate(\n params: FindEdgesParams,\n patch: BulkUpdatePatch,\n options?: BulkOptions,\n ): Promise<BulkResult> {\n if (!this.backend.bulkUpdate) {\n throw new FiregraphError(\n 'bulkUpdate() is not supported by the current storage backend.',\n 'UNSUPPORTED_OPERATION',\n );\n }\n const filters = this.buildDmlFilters(params);\n return this.backend.bulkUpdate(filters, patch, options);\n }\n\n // ---------------------------------------------------------------------------\n // Multi-source fan-out (capability: query.join)\n // ---------------------------------------------------------------------------\n\n /**\n * Fan out from `params.sources` over a single edge type in one round trip.\n * On backends without `query.join`, throws `UNSUPPORTED_OPERATION` — the\n * cap-less fallback is the per-source `findEdges` loop, which lives in\n * `traverse.ts` (the higher-level traversal walker) rather than here.\n *\n * `expand()` is intentionally edge-type-only — the source set is a flat\n * UID list and the hop matches one `axbType`. Multi-axbType expansions\n * become multiple `expand()` calls, one per relation.\n *\n * `params.sources.length === 0` short-circuits to an empty result. The\n * backend never sees the call. (`compileExpand` itself rejects empty\n * because `IN ()` is not valid SQL.)\n */\n async expand(params: ExpandParams): Promise<ExpandResult> {\n if (!this.backend.expand) {\n throw new FiregraphError(\n 'expand() is not supported by the current storage backend. ' +\n 'Backends without `query.join` can use createTraversal() instead — ' +\n 'the per-hop loop is functionally equivalent (just slower).',\n 'UNSUPPORTED_OPERATION',\n );\n }\n if (params.sources.length === 0) {\n return params.hydrate ? { edges: [], targets: [] } : { edges: [] };\n }\n return this.backend.expand(params);\n }\n\n // ---------------------------------------------------------------------------\n // Engine-level multi-hop traversal (capability: traversal.serverSide)\n // ---------------------------------------------------------------------------\n\n /**\n * Compile a multi-hop traversal spec into one server-side nested\n * Pipeline and dispatch a single round trip.\n *\n * Backends declaring `traversal.serverSide` (Firestore Enterprise\n * today) install this method; everywhere else, it throws\n * `UNSUPPORTED_OPERATION`. The capability gate matches the type-level\n * surface — `GraphClient<C>` only exposes `runEngineTraversal` when\n * `'traversal.serverSide' extends C`.\n *\n * Most callers should not invoke this method directly; the\n * `createTraversal(...).run()` builder routes through it\n * automatically when `engineTraversal: 'auto'` (the default) and\n * the spec is eligible per `firestore-traverse-compiler.ts`. Calling\n * directly is appropriate for benchmarking or for callers that have\n * already shaped their hop chain into the strict\n * `EngineTraversalParams` shape.\n *\n * `params.sources.length === 0` short-circuits to empty per-hop\n * arrays. The backend never sees the call.\n */\n async runEngineTraversal(params: EngineTraversalParams): Promise<EngineTraversalResult> {\n if (!this.backend.runEngineTraversal) {\n throw new FiregraphError(\n 'runEngineTraversal() is not supported by the current storage backend. ' +\n 'Backends without `traversal.serverSide` can use createTraversal() instead — ' +\n 'the per-hop loop is functionally equivalent for in-graph specs (different ' +\n 'round-trip profile).',\n 'UNSUPPORTED_OPERATION',\n );\n }\n if (params.sources.length === 0) {\n return {\n hops: params.hops.map(() => ({ edges: [], sourceCount: 0 })),\n totalReads: 0,\n };\n }\n return this.backend.runEngineTraversal(params);\n }\n\n // ---------------------------------------------------------------------------\n // Server-side projection (capability: query.select)\n // ---------------------------------------------------------------------------\n\n /**\n * Server-side projection — fetch only the requested fields from each\n * matching edge. The backend translates the call into a projecting query\n * (`SELECT json_extract(...)` on SQLite/DO, `Query.select(...)` on\n * Firestore Standard, classic projection on Enterprise) so the wire\n * payload is reduced to just the requested fields.\n *\n * Resolution rules for `select` (mirrored across all backends):\n *\n * - Built-in envelope fields (`aType`, `aUid`, `axbType`, `bType`,\n * `bUid`, `createdAt`, `updatedAt`, `v`) → resolve to the typed\n * column / Firestore field directly.\n * - `'data'` literal → returns the whole user payload.\n * - `'data.<x>'` → explicit nested path, returned at the same shape.\n * - bare name → rewritten to `data.<name>` (the canonical \"give me a\n * few keys out of the JSON payload\" shape).\n *\n * Empty `select: []` is rejected with `INVALID_QUERY`. Duplicate entries\n * are de-duped (first-occurrence order preserved); the result row carries\n * one slot per unique field.\n *\n * Migrations are *not* applied to the result. The caller asked for a\n * partial shape, and rehydrating it through the migration pipeline would\n * require synthesising every absent field — see\n * `StorageBackend.findEdgesProjected` for the rationale.\n *\n * Scan protection follows the `findEdges` rules: a query with no\n * identifying fields requires `allowCollectionScan: true` to pass. The\n * cap-less fallback would be `findEdges` + JS-side projection, but that\n * defeats the wire-payload reduction; backends without `query.select`\n * throw `UNSUPPORTED_OPERATION` rather than silently materialising full\n * rows.\n */\n async findEdgesProjected<F extends ReadonlyArray<string>>(\n params: FindEdgesProjectedParams<F>,\n ): Promise<Array<ProjectedRow<F>>> {\n if (!this.backend.findEdgesProjected) {\n throw new FiregraphError(\n 'findEdgesProjected() is not supported by the current storage backend. ' +\n 'There is no client-side fallback because the wire-payload reduction ' +\n 'is the entire point of the API — use findEdges() and project in JS ' +\n 'if the backend does not declare `query.select`.',\n 'UNSUPPORTED_OPERATION',\n );\n }\n if (params.select.length === 0) {\n throw new FiregraphError(\n 'findEdgesProjected() requires a non-empty `select` list.',\n 'INVALID_QUERY',\n );\n }\n\n // Reuse the same plan + scan-safety pipeline as `findEdges` so the\n // identifier-vs-where rules and `allowCollectionScan` semantics behave\n // identically. A GET-shape (all three identifiers, no `where`) is also\n // allowed here — projection over a single edge is a meaningful shape.\n // We translate it to the equivalent equality filter list because the\n // backend `findEdgesProjected` contract takes filters, not a docId.\n const plan = buildEdgeQueryPlan(params);\n let filters: QueryFilter[];\n let options: QueryOptions | undefined;\n if (plan.strategy === 'get') {\n // GET means `aUid`, `axbType`, `bUid` are all set and there are no\n // `where` clauses. Synthesize the equivalent equality filters so the\n // backend can issue a single projecting query whose WHERE clause\n // resolves to the same row the docId would have looked up.\n filters = [\n { field: 'aUid', op: '==', value: params.aUid! },\n { field: 'axbType', op: '==', value: params.axbType! },\n { field: 'bUid', op: '==', value: params.bUid! },\n ];\n if (params.aType) filters.push({ field: 'aType', op: '==', value: params.aType });\n if (params.bType) filters.push({ field: 'bType', op: '==', value: params.bType });\n options = undefined;\n } else {\n filters = plan.filters;\n options = plan.options;\n }\n this.checkQuerySafety(filters, params.allowCollectionScan);\n const rows = await this.backend.findEdgesProjected(params.select, filters, options);\n return rows as Array<ProjectedRow<F>>;\n }\n\n /**\n * Native vector / nearest-neighbour search (capability `search.vector`).\n *\n * Resolves to the top-K records by similarity, sorted nearest-first\n * (`EUCLIDEAN` / `COSINE`) or highest-first (`DOT_PRODUCT`). The wrapper\n * is intentionally thin: capability check, scan-protection, then forward\n * `params` verbatim to the backend. All field-path normalisation and\n * SDK-shape validation lives in the shared\n * `runFirestoreFindNearest` helper that both Firestore editions call —\n * keeping it there means the validation surface stays in lockstep with\n * the SDK call site, regardless of which backend is plugged in.\n *\n * Migrations are NOT applied. The vector index walked the raw stored\n * shape; rehydrating each row through the migration pipeline would\n * change the candidate set the index already chose. If you need\n * migrated shape, follow up with `getNode` / `findEdges` on the\n * returned UIDs — those paths apply migrations normally.\n *\n * Scan-protection mirrors `findEdges`: if no identifying filters\n * (`aType` / `axbType` / `bType`) and no `where` clauses are supplied,\n * the request must opt in via `allowCollectionScan: true`. The ANN\n * query still walks the candidate set the WHERE clause produces, so\n * an unfiltered nearest-neighbour search over a million-row collection\n * is the same scan trap as an unfiltered `findEdges`.\n *\n * Backends without `search.vector` throw `UNSUPPORTED_OPERATION` —\n * there is no client-side fallback because emulating ANN over the\n * generic backend surface (`findEdges` + JS-side cosine) doesn't scale\n * past trivial datasets and would give callers the wrong mental model\n * about cost.\n */\n async findNearest(params: FindNearestParams): Promise<StoredGraphRecord[]> {\n if (!this.backend.findNearest) {\n throw new FiregraphError(\n 'findNearest() is not supported by the current storage backend. ' +\n 'Vector search requires a backend that declares `search.vector` ' +\n '(currently Firestore Standard, Firestore Enterprise, and the ' +\n 'local better-sqlite3 backend). There is no client-side fallback ' +\n 'because emulating ANN on top of the generic backend surface does ' +\n 'not scale beyond toy datasets.',\n 'UNSUPPORTED_OPERATION',\n );\n }\n\n // Build the same filter list the helper passes to `applyFiltersToQuery`\n // so scan-protection sees exactly what the index will narrow on.\n // Identifiers come first (matching `buildVectorFilters` in the helper),\n // user-supplied `where` follows. We do NOT use `buildEdgeQueryPlan`\n // here — there is no GET-strategy notion for vector search; the\n // identifying-field filters are pure narrowing for the ANN walk.\n const filters: QueryFilter[] = [];\n if (params.aType) filters.push({ field: 'aType', op: '==', value: params.aType });\n if (params.axbType) filters.push({ field: 'axbType', op: '==', value: params.axbType });\n if (params.bType) filters.push({ field: 'bType', op: '==', value: params.bType });\n if (params.where) filters.push(...params.where);\n this.checkQuerySafety(filters, params.allowCollectionScan);\n\n return this.backend.findNearest(params);\n }\n\n /**\n * Native full-text search (capability `search.fullText`).\n *\n * Returns the top-N records by relevance, ordered by the search\n * index's score. Firestore Enterprise declares this capability (via\n * the Pipelines `search({ query: documentMatches(...) })` stage over\n * Enterprise's FTS index), as does the local better-sqlite3 backend\n * (`firegraph/sqlite-local`, via a trigger-synced FTS5 index ranked\n * by `bm25()`). Standard does not declare the cap (FTS is an\n * Enterprise-only product feature, not a typed-API gap); D1 and the\n * Cloudflare DO edition have no FTS trigger infrastructure. Backends\n * without `search.fullText` throw `UNSUPPORTED_OPERATION` from this\n * wrapper.\n *\n * Scan-protection mirrors `findNearest`: a search with no\n * identifying filters (`aType` / `axbType` / `bType`) walks every\n * row the index scored, so the request must opt in via\n * `allowCollectionScan: true`.\n *\n * Migrations are NOT applied. The FTS index walked the raw stored\n * shape; rehydrating each row through the migration pipeline would\n * change the candidate set the index already scored. If you need\n * migrated shape, follow up with `getNode` / `findEdges` on the\n * returned UIDs.\n */\n async fullTextSearch(params: FullTextSearchParams): Promise<StoredGraphRecord[]> {\n if (!this.backend.fullTextSearch) {\n throw new FiregraphError(\n 'fullTextSearch() is not supported by the current storage backend. ' +\n 'Full-text search requires a backend that declares `search.fullText` ' +\n '(currently Firestore Enterprise and the local better-sqlite3 ' +\n 'backend). There is no client-side fallback because emulating FTS over ' +\n 'the generic backend surface would not scale beyond toy datasets.',\n 'UNSUPPORTED_OPERATION',\n );\n }\n const filters: QueryFilter[] = [];\n if (params.aType) filters.push({ field: 'aType', op: '==', value: params.aType });\n if (params.axbType) filters.push({ field: 'axbType', op: '==', value: params.axbType });\n if (params.bType) filters.push({ field: 'bType', op: '==', value: params.bType });\n this.checkQuerySafety(filters, params.allowCollectionScan);\n return this.backend.fullTextSearch(params);\n }\n\n /**\n * Native geospatial distance search (capability `search.geo`).\n *\n * Returns rows whose `geoField` lies within `radiusMeters` of\n * `point`, ordered nearest-first by default. Only Firestore\n * Enterprise declares this capability — same Enterprise-only\n * gating as `fullTextSearch`. Backends without `search.geo` throw\n * `UNSUPPORTED_OPERATION` from this wrapper.\n *\n * Scan-protection mirrors `findNearest` and `fullTextSearch`.\n *\n * Migrations are NOT applied — same rationale as the other search\n * extensions.\n */\n async geoSearch(params: GeoSearchParams): Promise<StoredGraphRecord[]> {\n if (!this.backend.geoSearch) {\n throw new FiregraphError(\n 'geoSearch() is not supported by the current storage backend. ' +\n 'Geospatial search requires a backend that declares `search.geo` ' +\n '(currently Firestore Enterprise only — geo queries are an ' +\n 'Enterprise product feature). There is no client-side fallback ' +\n 'because emulating geo over the generic backend surface (haversine ' +\n 'over `findEdges`) would not scale beyond trivial datasets.',\n 'UNSUPPORTED_OPERATION',\n );\n }\n const filters: QueryFilter[] = [];\n if (params.aType) filters.push({ field: 'aType', op: '==', value: params.aType });\n if (params.axbType) filters.push({ field: 'axbType', op: '==', value: params.axbType });\n if (params.bType) filters.push({ field: 'bType', op: '==', value: params.bType });\n this.checkQuerySafety(filters, params.allowCollectionScan);\n return this.backend.geoSearch(params);\n }\n\n /**\n * Translate a `FindEdgesParams` into the `QueryFilter[]` shape the\n * backend `bulkDelete` / `bulkUpdate` methods expect. Mirrors the\n * `aggregate()` plan: a bare-empty params object becomes an empty\n * filter list (after a scan-protection check); a GET-shape (all three\n * identifiers) is rejected so we never silently turn a single-row\n * lookup into a server-side DML; otherwise we run `buildEdgeQueryPlan`\n * and surface its filters.\n */\n private buildDmlFilters(params: FindEdgesParams): QueryFilter[] {\n const hasAnyFilter =\n params.aType ||\n params.aUid ||\n params.axbType ||\n params.bType ||\n params.bUid ||\n (params.where && params.where.length > 0);\n\n if (!hasAnyFilter) {\n this.checkQuerySafety([], params.allowCollectionScan);\n return [];\n }\n\n const plan = buildEdgeQueryPlan(params);\n if (plan.strategy === 'get') {\n throw new FiregraphError(\n 'bulkDelete() / bulkUpdate() require a query, not a direct document lookup. ' +\n 'Use removeEdge() / updateEdge() for single-row operations, or omit one of ' +\n 'aUid/axbType/bUid to force a query strategy.',\n 'INVALID_QUERY',\n );\n }\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n return plan.filters;\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 a `StorageBackend`.\n *\n * Phase 3: the type parameter `C` is inferred from\n * `StorageBackend<C>.capabilities` and propagates to the returned\n * `GraphClient<C>`. Extension surfaces (aggregate, search, raw escape\n * hatches, …) are conditionally intersected — they exist on the returned\n * type only when `C` declares the matching capability. Calls into\n * undeclared extensions are TypeScript errors at the call site, not\n * runtime failures.\n *\n * The runtime delegate `GraphClientImpl` carries only the portable core\n * methods today; extension methods land in Phases 4–10 alongside their\n * backend implementations. Until then the type-level surface is ahead of\n * the runtime, but no backend declares any extension capability so the\n * narrowing is effectively a no-op for current callers.\n *\n * `createGraphClientFromBackend` is retained as a deprecated alias for\n * backward compatibility while the codebase migrates off the old name.\n */\nexport function createGraphClient<C extends Capability = Capability>(\n backend: StorageBackend<C>,\n options: GraphClientOptions & { registryMode: DynamicRegistryConfig },\n metaBackend?: StorageBackend,\n): DynamicGraphClient<C>;\nexport function createGraphClient<C extends Capability = Capability>(\n backend: StorageBackend<C>,\n options?: GraphClientOptions,\n metaBackend?: StorageBackend,\n): GraphClient<C>;\nexport function createGraphClient<C extends Capability = Capability>(\n backend: StorageBackend<C>,\n options?: GraphClientOptions,\n metaBackend?: StorageBackend,\n): GraphClient<C> | DynamicGraphClient<C> {\n // The double cast bridges the gap between the runtime delegate\n // (`GraphClientImpl`, which structurally implements\n // `CoreGraphClient & DynamicGraphMethods`) and the conditionally-\n // intersected return types `GraphClient<C>` / `DynamicGraphClient<C>`.\n // The implementation signature can't pick between the two overloads\n // without inspecting `options.registryMode` at the type level, which\n // requires conditional types over the `options` argument; the cast\n // collapses that ambiguity. Sound today because every `*Extension`\n // body is empty and `DynamicGraphMethods` is always present at runtime\n // (the validation routing inside `GraphClientImpl` no-ops the dynamic\n // methods when registryMode is absent).\n return new GraphClientImpl(backend, options, metaBackend) as unknown as\n | GraphClient<C>\n | DynamicGraphClient<C>;\n}\n\n/**\n * @deprecated Use `createGraphClient` instead. Kept temporarily so existing\n * callers (Cloudflare client, routing backend, tests) continue to compile\n * during the Phase 2 transition.\n */\nexport const createGraphClientFromBackend = createGraphClient;\n","import { nanoid } from 'nanoid';\n\nexport function generateId(): string {\n return nanoid();\n}\n"],"mappings":"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","names":["createHash","createHash","buildWritableNodeRecord","buildWritableEdgeRecord","buildWritableNodeRecord","buildWritableEdgeRecord","result"]}
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
NODE_RELATION,
|
|
6
6
|
computeEdgeDocId,
|
|
7
7
|
computeNodeDocId
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-NGAJCALM.js";
|
|
9
9
|
import {
|
|
10
10
|
FiregraphError,
|
|
11
11
|
assertSafePath,
|
|
@@ -596,4 +596,4 @@ export {
|
|
|
596
596
|
buildFirestoreUpdateArgs,
|
|
597
597
|
runFirestoreFindNearest
|
|
598
598
|
};
|
|
599
|
-
//# sourceMappingURL=chunk-
|
|
599
|
+
//# sourceMappingURL=chunk-7IEZ6IYY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/bulk.ts","../src/internal/firestore-aggregate.ts","../src/internal/firestore-classic-adapter.ts","../src/internal/firestore-classic-expand.ts","../src/internal/projection.ts","../src/internal/firestore-projection.ts","../src/internal/firestore-update.ts","../src/internal/firestore-vector.ts"],"sourcesContent":["import type { Firestore } from '@google-cloud/firestore';\n\nimport { computeEdgeDocId, computeNodeDocId } from './docid.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport type {\n BulkBatchError,\n BulkOptions,\n BulkResult,\n CascadeResult,\n FindEdgesParams,\n GraphReader,\n StoredGraphRecord,\n} from './types.js';\n\nconst MAX_BATCH_SIZE = 500;\nconst DEFAULT_MAX_RETRIES = 3;\nconst BASE_DELAY_MS = 200;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Splits an array into chunks of at most `size` elements.\n */\nfunction chunk<T>(arr: T[], size: number): T[][] {\n const chunks: T[][] = [];\n for (let i = 0; i < arr.length; i += size) {\n chunks.push(arr.slice(i, i + size));\n }\n return chunks;\n}\n\n/**\n * Deletes a list of document IDs in chunked Firestore batches with retries.\n */\nexport async function bulkDeleteDocIds(\n db: Firestore,\n collectionPath: string,\n docIds: string[],\n options?: BulkOptions,\n): Promise<BulkResult> {\n if (docIds.length === 0) {\n return { deleted: 0, batches: 0, errors: [] };\n }\n\n const batchSize = Math.min(options?.batchSize ?? MAX_BATCH_SIZE, MAX_BATCH_SIZE);\n const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;\n const onProgress = options?.onProgress;\n\n const chunks = chunk(docIds, batchSize);\n const errors: BulkBatchError[] = [];\n let deleted = 0;\n let completedBatches = 0;\n\n for (let i = 0; i < chunks.length; i++) {\n const ids = chunks[i];\n let committed = false;\n\n for (let attempt = 0; attempt <= maxRetries; attempt++) {\n try {\n const batch = db.batch();\n const collectionRef = db.collection(collectionPath);\n for (const id of ids) {\n batch.delete(collectionRef.doc(id));\n }\n await batch.commit();\n committed = true;\n deleted += ids.length;\n break;\n } catch (err) {\n if (attempt < maxRetries) {\n const delay = BASE_DELAY_MS * Math.pow(2, attempt);\n await sleep(delay);\n } else {\n errors.push({\n batchIndex: i,\n error: err instanceof Error ? err : new Error(String(err)),\n operationCount: ids.length,\n });\n }\n }\n }\n\n if (committed) {\n completedBatches++;\n }\n\n if (onProgress) {\n onProgress({\n completedBatches,\n totalBatches: chunks.length,\n deletedSoFar: deleted,\n });\n }\n }\n\n return { deleted, batches: completedBatches, errors };\n}\n\n/**\n * Finds all edges matching `params`, then deletes them in chunked batches.\n */\nexport async function bulkRemoveEdges(\n db: Firestore,\n collectionPath: string,\n reader: GraphReader,\n params: FindEdgesParams,\n options?: BulkOptions,\n): Promise<BulkResult> {\n // Override default query limit for bulk deletion — we need all matching edges.\n // limit: 0 bypasses DEFAULT_QUERY_LIMIT; an explicit user limit is preserved.\n // allowCollectionScan: true — bulk deletion inherently implies scanning.\n const effectiveParams =\n params.limit !== undefined\n ? { ...params, allowCollectionScan: params.allowCollectionScan ?? true }\n : { ...params, limit: 0, allowCollectionScan: params.allowCollectionScan ?? true };\n const edges = await reader.findEdges(effectiveParams);\n const docIds = edges.map((e) => computeEdgeDocId(e.aUid, e.axbType, e.bUid));\n return bulkDeleteDocIds(db, collectionPath, docIds, options);\n}\n\n/** Result from recursive subcollection deletion. */\ninterface SubcollectionDeleteResult {\n deleted: number;\n errors: BulkBatchError[];\n}\n\n/**\n * Recursively delete all documents in all subcollections under a given document.\n * Uses `listCollections()` (Admin SDK) to discover subcollections, then for each\n * subcollection: recurse into each document's subcollections first (depth-first),\n * then bulk delete all documents in the subcollection.\n *\n * The `onProgress` callback is intentionally NOT forwarded to subcollection\n * deletes to avoid confusing callers with interleaved progress from different\n * collection depths.\n */\nasync function deleteSubcollectionsRecursive(\n db: Firestore,\n collectionPath: string,\n docId: string,\n options?: BulkOptions,\n): Promise<SubcollectionDeleteResult> {\n const docRef = db.collection(collectionPath).doc(docId);\n const subcollections = await docRef.listCollections();\n\n if (subcollections.length === 0) return { deleted: 0, errors: [] };\n\n let totalDeleted = 0;\n const allErrors: BulkBatchError[] = [];\n\n // Strip onProgress for subcollection deletes — callers should only see\n // top-level progress, not interleaved reports from nested depths.\n const subOptions: BulkOptions | undefined = options\n ? { batchSize: options.batchSize, maxRetries: options.maxRetries }\n : undefined;\n\n for (const subCollRef of subcollections) {\n const subCollPath = subCollRef.path;\n // List all documents in this subcollection\n const snapshot = await subCollRef.select().get();\n const subDocIds = snapshot.docs.map((d) => d.id);\n\n // Depth-first: recurse into each document's subcollections\n for (const subDocId of subDocIds) {\n const subResult = await deleteSubcollectionsRecursive(db, subCollPath, subDocId, subOptions);\n totalDeleted += subResult.deleted;\n allErrors.push(...subResult.errors);\n }\n\n // Now delete all documents in this subcollection\n if (subDocIds.length > 0) {\n const result = await bulkDeleteDocIds(db, subCollPath, subDocIds, subOptions);\n totalDeleted += result.deleted;\n allErrors.push(...result.errors);\n }\n }\n\n return { deleted: totalDeleted, errors: allErrors };\n}\n\n/**\n * Deletes a node and all of its outgoing and incoming edges.\n *\n * Edges are deleted first in chunked batches, then the node document\n * is deleted in the final batch. This is NOT atomic across batches —\n * if a batch fails after retries, remaining batches still execute.\n *\n * By default, subcollections (subgraphs) under the node's document are\n * recursively deleted. Set `options.deleteSubcollections` to `false` to skip.\n */\nexport async function removeNodeCascade(\n db: Firestore,\n collectionPath: string,\n reader: GraphReader,\n uid: string,\n options?: BulkOptions,\n): Promise<CascadeResult> {\n // Find all edges touching this node (outgoing + incoming).\n // Filter out the node's own self-loop record (axbType === 'is').\n // These queries intentionally scan broadly — allowCollectionScan bypasses safety checks.\n // limit: 0 bypasses the DEFAULT_QUERY_LIMIT to ensure we find all edges.\n const [outgoingRaw, incomingRaw] = await Promise.all([\n reader.findEdges({ aUid: uid, allowCollectionScan: true, limit: 0 }),\n reader.findEdges({ bUid: uid, allowCollectionScan: true, limit: 0 }),\n ]);\n const outgoing = outgoingRaw.filter((e) => e.axbType !== NODE_RELATION);\n const incoming = incomingRaw.filter((e) => e.axbType !== NODE_RELATION);\n\n // Deduplicate: a self-referencing edge could appear in both lists.\n const edgeDocIdSet = new Set<string>();\n const allEdges: StoredGraphRecord[] = [];\n for (const edge of [...outgoing, ...incoming]) {\n const docId = computeEdgeDocId(edge.aUid, edge.axbType, edge.bUid);\n if (!edgeDocIdSet.has(docId)) {\n edgeDocIdSet.add(docId);\n allEdges.push(edge);\n }\n }\n\n // Delete subcollections (subgraphs) under this node's document (depth-first).\n const shouldDeleteSubcollections = options?.deleteSubcollections !== false;\n const nodeDocId = computeNodeDocId(uid);\n let subcollectionResult: SubcollectionDeleteResult = { deleted: 0, errors: [] };\n\n if (shouldDeleteSubcollections) {\n subcollectionResult = await deleteSubcollectionsRecursive(\n db,\n collectionPath,\n nodeDocId,\n options,\n );\n }\n\n // Build doc IDs: edges first, then the node last.\n const edgeDocIds = allEdges.map((e) => computeEdgeDocId(e.aUid, e.axbType, e.bUid));\n const allDocIds = [...edgeDocIds, nodeDocId];\n\n // Wrap the progress callback to track overall progress.\n const batchSize = Math.min(options?.batchSize ?? MAX_BATCH_SIZE, MAX_BATCH_SIZE);\n const result = await bulkDeleteDocIds(db, collectionPath, allDocIds, {\n ...options,\n batchSize,\n });\n\n // Determine if the node doc was in a failed batch.\n // The node is always in the last doc ID. If the last batch errored, node wasn't deleted.\n const totalChunks = Math.ceil(allDocIds.length / batchSize);\n const nodeChunkIndex = totalChunks - 1;\n const nodeDeleted = !result.errors.some((e) => e.batchIndex === nodeChunkIndex);\n\n // edgesDeleted counts only top-level edges (not subcollection docs).\n // deleted includes everything: top-level edges + node + subcollection docs.\n const topLevelEdgesDeleted = nodeDeleted ? result.deleted - 1 : result.deleted;\n\n return {\n deleted: result.deleted + subcollectionResult.deleted,\n batches: result.batches,\n errors: [...result.errors, ...subcollectionResult.errors],\n edgesDeleted: topLevelEdgesDeleted,\n nodeDeleted,\n };\n}\n","/**\n * Shared classic-API aggregate translation for both Firestore editions.\n *\n * Translates an `AggregateSpec` into a `Query.aggregate()` call using\n * `AggregateField.count() / sum() / average()`. The classic API does not\n * support `min` / `max` — those throw `UNSUPPORTED_AGGREGATE`. Backends\n * declaring `query.aggregate` are required to support at least count/sum/avg;\n * per-op limitations surface as runtime errors with a clear message.\n *\n * Both Standard and Enterprise editions can call `Query.aggregate()` directly\n * (the Enterprise pipeline `aggregate()` stage is a future optimisation that\n * would unlock min/max — Phase 11+). Until then both editions delegate to\n * this single helper, which keeps the per-edition backends thin and avoids\n * cross-subpath imports between them.\n *\n * Field paths follow the same dotted convention used elsewhere in firegraph\n * (`'data.price'`, `'data.profile.score'`). They are passed through to\n * Firestore unchanged — Firestore itself interprets `.` as a field-path\n * separator.\n */\n\nimport { AggregateField, type Query } from '@google-cloud/firestore';\n\nimport { FiregraphError } from '../errors.js';\nimport type { AggregateSpec, QueryFilter } from '../types.js';\n\n/**\n * Apply the firegraph filter list to a base Firestore query. Mirrors the\n * tiny `where()` loop used by the classic adapter; kept local so the\n * aggregate path doesn't depend on adapter internals it doesn't otherwise\n * need.\n */\nexport function applyFiltersToQuery(base: Query, filters: QueryFilter[]): Query {\n let q = base;\n for (const f of filters) {\n q = q.where(f.field, f.op, f.value);\n }\n return q;\n}\n\n/**\n * Run an aggregate query against a base Firestore `Query`. Returns the\n * resolved numeric result keyed by alias.\n *\n * `count` is the only op that ignores `field`; everything else requires it.\n * Missing fields throw `INVALID_QUERY` so the caller sees the bad spec\n * rather than a Firestore-side error from passing `undefined` into\n * `AggregateField.sum`.\n */\nexport async function runFirestoreAggregate(\n base: Query,\n spec: AggregateSpec,\n filters: QueryFilter[],\n { edition }: { edition: 'standard' | 'enterprise' },\n): Promise<Record<string, number>> {\n if (Object.keys(spec).length === 0) {\n throw new FiregraphError(\n 'aggregate() requires at least one aggregation in the `aggregates` map.',\n 'INVALID_QUERY',\n );\n }\n\n const filtered = applyFiltersToQuery(base, filters);\n // Firestore's `AggregateField` map values are heterogeneous — `count()`,\n // `sum(field)`, and `average(field)` each return distinct AggregateField\n // subtypes parameterized by their own input type. The library accepts a\n // record whose value union covers all three, so we type the local map\n // accordingly.\n type AnyAggField = ReturnType<typeof AggregateField.count>;\n const aggregations: Record<string, AnyAggField> = {};\n for (const [alias, { op, field }] of Object.entries(spec)) {\n if (op === 'count') {\n // Reject a stray `field` on count: counting rows never uses a column\n // expression, and silently ignoring would mask typos like\n // `{ n: { op: 'count', field: 'data.price' } }` (cribbed from a sum\n // spec). Better to surface the misuse than return a misleading row\n // count.\n if (field !== undefined) {\n throw new FiregraphError(\n `Aggregate '${alias}' op 'count' must not specify a field — ` +\n `count operates on rows, not a column expression.`,\n 'INVALID_QUERY',\n );\n }\n aggregations[alias] = AggregateField.count();\n continue;\n }\n if (!field) {\n throw new FiregraphError(\n `Aggregate '${alias}' op '${op}' requires a field.`,\n 'INVALID_QUERY',\n );\n }\n if (op === 'sum') {\n aggregations[alias] = AggregateField.sum(field) as unknown as AnyAggField;\n } else if (op === 'avg') {\n aggregations[alias] = AggregateField.average(field) as unknown as AnyAggField;\n } else {\n // Both editions currently route through the classic `Query.aggregate`\n // API, which exposes only count/sum/avg. Enterprise *could* expose\n // min/max via pipelines (deferred to a later phase). Until then, both\n // editions reject min/max identically — the message names the edition\n // so the diagnostic is accurate.\n const editionLabel = edition === 'enterprise' ? 'Firestore Enterprise' : 'Firestore Standard';\n throw new FiregraphError(\n `Aggregate op '${op}' is not supported on ${editionLabel}. ` +\n `Both Firestore editions support count/sum/avg via the classic Query API; ` +\n `min/max requires a backend with SQL aggregation (SQLite or DO).`,\n 'UNSUPPORTED_AGGREGATE',\n );\n }\n }\n\n const snap = await filtered.aggregate(aggregations).get();\n const data = snap.data() as Record<string, number | null>;\n const out: Record<string, number> = {};\n for (const alias of Object.keys(spec)) {\n const v = data[alias];\n // Firestore returns `null` for sum/avg over an empty set. Surface that\n // as `0` for sum (well-defined) and `NaN` for avg (avg of empty set is\n // mathematically undefined). `count` is never null.\n if (v === null || v === undefined) {\n const op = spec[alias].op;\n out[alias] = op === 'avg' ? Number.NaN : 0;\n } else {\n out[alias] = v;\n }\n }\n return out;\n}\n","import type { FieldPath, Firestore, Query, Transaction } from '@google-cloud/firestore';\n\nimport type { QueryFilter, QueryOptions, StoredGraphRecord } from '../types.js';\n\n/**\n * Variadic argument tuple for Firestore's\n * `update(field, value, …moreFieldsAndValues)` overload. Built by\n * `buildFirestoreUpdateArgs` (`./firestore-update.ts`); deep-merge ops use\n * `FieldPath` segments so exotic object keys address literal keys rather\n * than being reparsed as dotted paths.\n */\nexport type FirestoreUpdateArgs = [string | FieldPath, unknown, ...unknown[]];\n\nexport interface FirestoreAdapter {\n collectionPath: string;\n getDoc(docId: string): Promise<StoredGraphRecord | null>;\n setDoc(\n docId: string,\n data: Record<string, unknown>,\n options?: { merge?: boolean },\n ): Promise<void>;\n updateDoc(docId: string, args: FirestoreUpdateArgs): Promise<void>;\n deleteDoc(docId: string): Promise<void>;\n query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]>;\n}\n\nexport function createFirestoreAdapter(db: Firestore, collectionPath: string): FirestoreAdapter {\n const collectionRef = db.collection(collectionPath);\n\n return {\n collectionPath,\n\n async getDoc(docId: string): Promise<StoredGraphRecord | null> {\n const snap = await collectionRef.doc(docId).get();\n if (!snap.exists) return null;\n return snap.data() as StoredGraphRecord;\n },\n\n async setDoc(\n docId: string,\n data: Record<string, unknown>,\n options?: { merge?: boolean },\n ): Promise<void> {\n if (options?.merge) {\n await collectionRef.doc(docId).set(data, { merge: true });\n } else {\n await collectionRef.doc(docId).set(data);\n }\n },\n\n async updateDoc(docId: string, args: FirestoreUpdateArgs): Promise<void> {\n await collectionRef.doc(docId).update(...args);\n },\n\n async deleteDoc(docId: string): Promise<void> {\n await collectionRef.doc(docId).delete();\n },\n\n async query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]> {\n let q: Query = collectionRef;\n for (const f of filters) {\n q = q.where(f.field, f.op, f.value);\n }\n if (options?.orderBy) {\n q = q.orderBy(options.orderBy.field, options.orderBy.direction ?? 'asc');\n }\n if (options?.limit !== undefined) {\n q = q.limit(options.limit);\n }\n const snap = await q.get();\n return snap.docs.map((doc) => doc.data() as StoredGraphRecord);\n },\n };\n}\n\nexport interface TransactionAdapter {\n getDoc(docId: string): Promise<StoredGraphRecord | null>;\n setDoc(docId: string, data: Record<string, unknown>, options?: { merge?: boolean }): void;\n updateDoc(docId: string, args: FirestoreUpdateArgs): void;\n deleteDoc(docId: string): void;\n query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]>;\n}\n\nexport function createTransactionAdapter(\n db: Firestore,\n collectionPath: string,\n tx: Transaction,\n): TransactionAdapter {\n const collectionRef = db.collection(collectionPath);\n\n return {\n async getDoc(docId: string): Promise<StoredGraphRecord | null> {\n const snap = await tx.get(collectionRef.doc(docId));\n if (!snap.exists) return null;\n return snap.data() as StoredGraphRecord;\n },\n\n setDoc(docId: string, data: Record<string, unknown>, options?: { merge?: boolean }): void {\n if (options?.merge) {\n tx.set(collectionRef.doc(docId), data, { merge: true });\n } else {\n tx.set(collectionRef.doc(docId), data);\n }\n },\n\n updateDoc(docId: string, args: FirestoreUpdateArgs): void {\n tx.update(collectionRef.doc(docId), ...args);\n },\n\n deleteDoc(docId: string): void {\n tx.delete(collectionRef.doc(docId));\n },\n\n async query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]> {\n let q: Query = collectionRef;\n for (const f of filters) {\n q = q.where(f.field, f.op, f.value);\n }\n if (options?.orderBy) {\n q = q.orderBy(options.orderBy.field, options.orderBy.direction ?? 'asc');\n }\n if (options?.limit !== undefined) {\n q = q.limit(options.limit);\n }\n const snap = await tx.get(q);\n return snap.docs.map((doc) => doc.data() as StoredGraphRecord);\n },\n };\n}\n\nexport interface BatchAdapter {\n setDoc(docId: string, data: Record<string, unknown>, options?: { merge?: boolean }): void;\n updateDoc(docId: string, args: FirestoreUpdateArgs): void;\n deleteDoc(docId: string): void;\n commit(): Promise<void>;\n}\n\nexport function createBatchAdapter(db: Firestore, collectionPath: string): BatchAdapter {\n const collectionRef = db.collection(collectionPath);\n const batch = db.batch();\n\n return {\n setDoc(docId: string, data: Record<string, unknown>, options?: { merge?: boolean }): void {\n if (options?.merge) {\n batch.set(collectionRef.doc(docId), data, { merge: true });\n } else {\n batch.set(collectionRef.doc(docId), data);\n }\n },\n\n updateDoc(docId: string, args: FirestoreUpdateArgs): void {\n batch.update(collectionRef.doc(docId), ...args);\n },\n\n deleteDoc(docId: string): void {\n batch.delete(collectionRef.doc(docId));\n },\n\n async commit(): Promise<void> {\n await batch.commit();\n },\n };\n}\n","/**\n * Shared classic-API multi-source fan-out for Firestore.\n *\n * Used by:\n *\n * - `firestore-standard` (always — Standard has no Pipelines path).\n * - `firestore-enterprise` (only when `queryMode === 'classic'`, i.e.\n * under the emulator or when explicitly forced).\n *\n * Strategy: chunk `params.sources` into 30-element groups (the classic\n * `'in'` operator's documented cap), dispatch one `Query.where(field, 'in',\n * chunk)` per chunk in parallel via `Promise.all`, then concat. Optional\n * post-pass sort + total-limit cap so observable behaviour matches the\n * SQL backends' single-statement `WHERE … IN (?,?,…) ORDER BY … LIMIT N`.\n *\n * Why this is still a win over the per-source `findEdges` loop in\n * `traverse.ts`: that loop emits one query per source UID. With 100\n * sources it's 100 round trips. The chunked path is `ceil(100/30) = 4`\n * round trips. The Pipelines `equalAny` path (Enterprise) collapses all\n * 100 into one — see `firestore-expand.ts` — but Standard can't reach\n * that, so chunked classic is the best Standard can do.\n *\n * Hydration follows the same chunking strategy: a single classic query\n * per chunk that fetches every node row whose `aUid` is in the chunk\n * (nodes are stored as self-loops `(uid, 'is', uid)` so `aUid IN chunk`\n * picks them up). Document IDs would also work via per-UID `getDoc`,\n * but that's N round trips for N targets — chunking matches the\n * fan-out path's round-trip count.\n */\n\nimport { FiregraphError } from '../errors.js';\nimport type { ExpandParams, ExpandResult, QueryFilter, StoredGraphRecord } from '../types.js';\nimport { NODE_RELATION } from './constants.js';\nimport type { FirestoreAdapter } from './firestore-classic-adapter.js';\n\n/**\n * Maximum elements per Firestore classic `'in'` operator. Documented as 30\n * across the Node Admin SDK, Firestore web SDK, and the security-rules\n * runtime. Exceeding this throws `INVALID_ARGUMENT` on the wire.\n */\nexport const FIRESTORE_CLASSIC_IN_CHUNK_SIZE = 30;\n\n/**\n * Read a (possibly dotted) field from a record. Used for the post-concat\n * sort pass. Mirrors the dotted-path resolution in\n * `firestore-projection.ts` but read-only and without bare-name rewriting\n * — `ExpandParams.orderBy.field` is always a fully-qualified path.\n */\nfunction readField(record: StoredGraphRecord, path: string): unknown {\n if (!path.includes('.')) {\n return (record as unknown as Record<string, unknown>)[path];\n }\n let cursor: unknown = record;\n for (const seg of path.split('.')) {\n if (cursor === null || typeof cursor !== 'object') return undefined;\n cursor = (cursor as Record<string, unknown>)[seg];\n }\n return cursor;\n}\n\n/**\n * Compare two field values for a stable, total ordering. Mirrors\n * Firestore's classic-API ordering semantics: numbers numerically,\n * strings lexicographically, booleans (false < true), and nullish goes\n * first. Mixed-type comparisons fall back to `String(...)` so the sort is\n * defined for every input shape — the alternative would be the JS\n * default `<` semantics, which silently coerce and lose stability.\n */\nfunction compareFieldValues(a: unknown, b: unknown): number {\n if (a === b) return 0;\n if (a === undefined || a === null) return -1;\n if (b === undefined || b === null) return 1;\n const ta = typeof a;\n const tb = typeof b;\n if (ta === tb) {\n // `a` and `b` share a primitive type (number, string, boolean, etc.);\n // a direct `<` comparison is safe under the JS comparison rules. Cast\n // through `string` solely to satisfy TS's `unknown < unknown` ban —\n // the runtime behaviour is identical for any pair of same-type\n // primitives.\n return (a as string) < (b as string) ? -1 : 1;\n }\n // Mixed types — fall back to string comparison.\n const sa = String(a);\n const sb = String(b);\n return sa < sb ? -1 : sa > sb ? 1 : 0;\n}\n\n/**\n * Run a classic-API `expand()` against a Firestore collection via the\n * supplied `FirestoreAdapter`. Returns the same `ExpandResult` shape as\n * the SQL backends.\n *\n * Empty `params.sources` short-circuits to an empty result without\n * touching the adapter — the chunking pass would emit zero queries\n * anyway, but the early return makes the contract explicit and matches\n * the `client.expand` wrapper.\n *\n * Self-loop guard: when `params.axbType === NODE_RELATION`, edges where\n * `aUid === bUid` are node rows, not real hops. The SQL backends filter\n * those out via `aUid != bUid`; we mirror the same filter as a\n * post-process pass because Firestore classic queries don't support\n * column-vs-column predicates. The guard is defensive — `traverse.ts`\n * never sends `NODE_RELATION` as `axbType`, but a direct\n * `client.expand({ axbType: 'is' })` call would otherwise return the\n * source nodes themselves.\n */\nexport async function runFirestoreClassicExpand(\n adapter: FirestoreAdapter,\n params: ExpandParams,\n): Promise<ExpandResult> {\n if (params.sources.length === 0) {\n return params.hydrate ? { edges: [], targets: [] } : { edges: [] };\n }\n\n if (params.axbType.length === 0) {\n throw new FiregraphError('expand(): axbType must be a non-empty string.', 'INVALID_QUERY');\n }\n\n const direction = params.direction ?? 'forward';\n const sourceField = direction === 'forward' ? 'aUid' : 'bUid';\n\n const chunks = chunkUids(params.sources, FIRESTORE_CLASSIC_IN_CHUNK_SIZE);\n const totalLimit =\n params.limitPerSource !== undefined ? params.sources.length * params.limitPerSource : undefined;\n\n const buildFilters = (chunk: string[]): QueryFilter[] => {\n const filters: QueryFilter[] = [\n { field: 'axbType', op: '==', value: params.axbType },\n { field: sourceField, op: 'in', value: chunk },\n ];\n if (params.aType !== undefined) {\n filters.push({ field: 'aType', op: '==', value: params.aType });\n }\n if (params.bType !== undefined) {\n filters.push({ field: 'bType', op: '==', value: params.bType });\n }\n return filters;\n };\n\n const buildOptions = (chunk: string[]) => {\n const opts: { orderBy?: ExpandParams['orderBy']; limit?: number } = {};\n if (params.orderBy) opts.orderBy = params.orderBy;\n if (params.limitPerSource !== undefined) {\n // Per-chunk soft cap: chunk.length * limitPerSource. The post-concat\n // slice enforces the global cap.\n opts.limit = chunk.length * params.limitPerSource;\n }\n return opts;\n };\n\n const chunkResults = await Promise.all(\n chunks.map((chunk) => adapter.query(buildFilters(chunk), buildOptions(chunk))),\n );\n let edges: StoredGraphRecord[] = chunkResults.flat();\n\n // Self-loop filter when the caller targeted the node-relation. See JSDoc.\n if (params.axbType === NODE_RELATION) {\n edges = edges.filter((e) => e.aUid !== e.bUid);\n }\n\n // Cross-chunk ordering pass. Each chunk's result is already sorted (we\n // pushed `orderBy` into the per-chunk query), but concat'd order is not\n // globally sorted. Re-sort to honour the contract.\n if (params.orderBy) {\n const sortField = params.orderBy.field;\n const dir = params.orderBy.direction ?? 'asc';\n edges.sort((a, b) => {\n const cmp = compareFieldValues(readField(a, sortField), readField(b, sortField));\n return dir === 'asc' ? cmp : -cmp;\n });\n }\n if (totalLimit !== undefined && edges.length > totalLimit) {\n edges = edges.slice(0, totalLimit);\n }\n\n if (!params.hydrate) return { edges };\n\n // Hydration: fetch every target node by `aUid in chunk` against the\n // node-relation. Nodes are self-loops, so this picks up exactly one row\n // per UID. Chunked the same way as the fan-out queries above.\n const targetUids = edges.map((e) => (direction === 'forward' ? e.bUid : e.aUid));\n const uniqueTargets = [...new Set(targetUids)];\n if (uniqueTargets.length === 0) {\n return { edges, targets: [] };\n }\n const hydrateChunks = chunkUids(uniqueTargets, FIRESTORE_CLASSIC_IN_CHUNK_SIZE);\n const hydrateResults = await Promise.all(\n hydrateChunks.map((chunk) =>\n adapter.query([\n { field: 'axbType', op: '==', value: NODE_RELATION },\n { field: 'aUid', op: 'in', value: chunk },\n ]),\n ),\n );\n const byUid = new Map<string, StoredGraphRecord>();\n for (const row of hydrateResults.flat()) {\n // `bUid === aUid === uid` for node rows by construction.\n byUid.set(row.bUid, row);\n }\n const targets = targetUids.map((uid) => byUid.get(uid) ?? null);\n return { edges, targets };\n}\n\n/** Split a list into fixed-size chunks. Exported for the unit test. */\nexport function chunkUids(uids: readonly string[], chunkSize: number): string[][] {\n if (chunkSize <= 0) {\n throw new FiregraphError(\n `chunkUids: chunkSize must be positive (got ${chunkSize}).`,\n 'INVALID_QUERY',\n );\n }\n const out: string[][] = [];\n for (let i = 0; i < uids.length; i += chunkSize) {\n out.push(uids.slice(i, i + chunkSize) as string[]);\n }\n return out;\n}\n","/**\n * Shared helpers for the `query.select` projection contract.\n *\n * The SQLite-shaped backends share a `normalizeProjectionField`\n * implementation in `src/internal/sqlite-sql.ts` because it's entangled\n * with `FIELD_TO_COLUMN`. The Firestore-shaped backends (`firestore-standard`,\n * `firestore-enterprise`) plus the `RoutingStorageBackend` pass-through use\n * the helpers below to keep the projection contract consistent across the\n * three runtimes:\n *\n * - bare names → `data.<name>`\n * - `'data'` and `'data.*'` → as-is\n * - top-level envelope fields (`aType`, `aUid`, `axbType`, `bType`,\n * `bUid`, `createdAt`, `updatedAt`, `v`) → as-is\n *\n * The set diverges from `BUILTIN_FIELDS` in `internal/constants.ts` — that\n * one is the queryable-filter set and does not include `v`. Projection\n * accepts `v` because it is a top-level envelope field that the user may\n * want to read for diagnostics.\n */\n\nconst PROJECTION_BUILTIN_FIELDS: ReadonlySet<string> = new Set([\n 'aType',\n 'aUid',\n 'axbType',\n 'bType',\n 'bUid',\n 'createdAt',\n 'updatedAt',\n 'v',\n]);\n\n/**\n * Rewrite a caller-supplied projection field to the canonical form the\n * Firestore-shaped backends consume. See file header for the rules.\n */\nexport function normalizeFirestoreProjectionField(field: string): string {\n if (PROJECTION_BUILTIN_FIELDS.has(field)) return field;\n if (field === 'data' || field.startsWith('data.')) return field;\n return `data.${field}`;\n}\n\n/**\n * Read a (possibly dotted) path out of a partial document. Used by the\n * Firestore-shaped backends to translate `doc.data()` into the projected JS\n * shape: each row in the result is keyed by the *original* field as the\n * caller supplied it, and the value is whatever lives at the canonical\n * path inside the partial document Firestore returns.\n *\n * Missing path segments resolve to `null` (not `undefined`) to match the\n * SQLite-shaped backends, where `json_extract` returns SQL NULL for an\n * absent JSON path — the decoder surfaces that as `null`. Aligning here\n * means a consumer iterating over `Object.entries(row)` sees the same\n * shape across SQLite/DO/Firestore: every requested field is present in\n * the row object, and absent values are explicitly `null`. If we returned\n * `undefined`, Firestore rows would silently lose absent keys when\n * serialized through `JSON.stringify`, breaking that contract.\n */\nexport function readProjectionPath(\n obj: Record<string, unknown> | undefined | null,\n path: string,\n): unknown {\n if (obj === undefined || obj === null) return null;\n const raw = !path.includes('.')\n ? obj[path]\n : (() => {\n const parts = path.split('.');\n let cur: unknown = obj;\n for (const part of parts) {\n if (cur === undefined || cur === null) return undefined;\n if (typeof cur !== 'object') return undefined;\n cur = (cur as Record<string, unknown>)[part];\n }\n return cur;\n })();\n return raw === undefined ? null : raw;\n}\n","/**\n * Shared classic-API projection translation for both Firestore editions.\n *\n * Translates a `findEdgesProjected({ select })` call into a\n * `Query.select(...fieldPaths)` query and decodes the partial documents\n * Firestore returns. Both Standard and Enterprise editions delegate to this\n * single helper so the projection contract — bare-name normalization,\n * builtin / `data.*` resolution, dedup semantics, original-key preservation\n * — stays consistent across editions.\n *\n * Why the classic API on both editions: the Enterprise pipeline `select()`\n * stage is a future optimisation. Server-side projection's only deliverable\n * is the byte-savings on the wire, and the classic `Query.select(...)`\n * API already achieves that on both editions. When pipeline `select()`\n * lands on a future SDK release the wiring is additive — swap the\n * implementation behind this helper, callers don't change.\n *\n * Migrations are not applied to the result. The contract on\n * `StorageBackend.findEdgesProjected` documents the rationale: the caller\n * asked for a partial shape, and rehydrating it through the migration\n * pipeline would require synthesising every absent field.\n *\n * Absent fields surface as `null`, not `undefined` — this matches the\n * SQLite-shaped backends (where `json_extract` returns SQL NULL for an\n * absent JSON path) so the projected row shape is identical across\n * SQLite/DO/Firestore. See `readProjectionPath` for the normalisation.\n */\n\nimport type { Query } from '@google-cloud/firestore';\n\nimport { FiregraphError } from '../errors.js';\nimport type { QueryFilter, QueryOptions } from '../types.js';\nimport { normalizeFirestoreProjectionField, readProjectionPath } from './projection.js';\n\n/** Resolved projection field — original (caller-supplied) + canonical Firestore path. */\ninterface ResolvedProjectionField {\n original: string;\n canonical: string;\n}\n\n/**\n * Run a projecting query against a base Firestore `Query`. Returns the\n * decoded rows, each keyed by the *original* field name as the caller\n * supplied it.\n *\n * `select` is rejected when empty (matches the SQLite compiler — both\n * layers fail so a misuse caught by either surfaces a clean\n * `INVALID_QUERY`). Duplicate entries are de-duped while preserving\n * first-occurrence order.\n */\nexport async function runFirestoreFindEdgesProjected(\n base: Query,\n select: ReadonlyArray<string>,\n filters: QueryFilter[],\n options?: QueryOptions,\n): Promise<Array<Record<string, unknown>>> {\n if (select.length === 0) {\n throw new FiregraphError(\n 'findEdgesProjected requires a non-empty select list — ' +\n 'an empty projection has no representation distinct from `findEdges`.',\n 'INVALID_QUERY',\n );\n }\n\n const seen = new Set<string>();\n const fields: ResolvedProjectionField[] = [];\n for (const f of select) {\n if (!seen.has(f)) {\n seen.add(f);\n fields.push({ original: f, canonical: normalizeFirestoreProjectionField(f) });\n }\n }\n\n let q: Query = base;\n for (const f of filters) {\n q = q.where(f.field, f.op, f.value);\n }\n if (options?.orderBy) {\n q = q.orderBy(options.orderBy.field, options.orderBy.direction ?? 'asc');\n }\n if (options?.limit !== undefined) {\n q = q.limit(options.limit);\n }\n q = q.select(...fields.map((p) => p.canonical));\n\n const snap = await q.get();\n return snap.docs.map((doc) => {\n const data = doc.data() as Record<string, unknown>;\n const out: Record<string, unknown> = {};\n for (const { original, canonical } of fields) {\n out[original] = readProjectionPath(data, canonical);\n }\n return out;\n });\n}\n","/**\n * Shared `UpdatePayload` → Firestore `update()` argument builder used by both\n * Firestore editions (`firestore-standard` and `firestore-enterprise`).\n *\n * Firestore's `DocumentReference.update` has two overloads:\n * - `update(data: UpdateData)` — an object whose *string* keys are parsed\n * as dotted field paths (`'a.b'` → nested `a → b`).\n * - `update(field: string | FieldPath, value, …moreFieldsAndValues)` — a\n * variadic field/value list where a `FieldPath` addresses literal\n * segments with no dotted reparse.\n *\n * We build the **variadic** form and key every deep-merge op with\n * `new FieldPath('data', …op.path)`. Literal segments mean an object key can\n * be anything non-empty — digit-leading (`'4f9Kq_2bN'`), hyphenated,\n * containing dots/brackets/whitespace — and still address that exact key\n * rather than being split on `.` by Firestore's dotted-path parser. This is\n * the Firestore-side counterpart to the SQLite backend's quoted JSON-path\n * labels (`src/internal/sqlite-data-ops.ts`): both backends escape keys at\n * path-construction time instead of rejecting them.\n *\n * `replaceData` keeps the plain string `'data'` key — it overwrites the\n * whole map, so there is no nested path to escape; tagged Firestore types\n * from the migration sandbox are reconstructed here via\n * `deserializeFirestoreTypes`. `updatedAt` is always stamped; `v` is stamped\n * as a top-level field when provided.\n */\n\nimport type { Firestore } from '@google-cloud/firestore';\nimport { FieldPath, FieldValue } from '@google-cloud/firestore';\n\nimport { deserializeFirestoreTypes } from '../serialization.js';\nimport type { UpdatePayload } from './backend.js';\nimport { assertSafePath, assertUpdatePayloadExclusive } from './write-plan.js';\n\n/**\n * Variadic argument tuple for `DocumentReference.update(field, value, …)`.\n * Always at least one field/value pair (`updatedAt` is unconditionally\n * stamped), so the tuple is non-empty and matches the variadic overload\n * rather than the single-object overload.\n */\nexport type FirestoreUpdateArgs = [string | FieldPath, unknown, ...unknown[]];\n\nexport function buildFirestoreUpdateArgs(\n update: UpdatePayload,\n db: Firestore,\n): FirestoreUpdateArgs {\n assertUpdatePayloadExclusive(update);\n const args: unknown[] = [];\n\n if (update.replaceData) {\n args.push('data', deserializeFirestoreTypes(update.replaceData, db));\n } else if (update.dataOps) {\n for (const op of update.dataOps) {\n assertSafePath(op.path);\n args.push(new FieldPath('data', ...op.path), op.delete ? FieldValue.delete() : op.value);\n }\n }\n\n args.push('updatedAt', FieldValue.serverTimestamp());\n if (update.v !== undefined) {\n args.push('v', update.v);\n }\n\n return args as FirestoreUpdateArgs;\n}\n","/**\n * Shared classic-API vector / nearest-neighbour translation for both\n * Firestore editions.\n *\n * Translates a `findNearest({ ... })` call into a `Query.findNearest(opts)`\n * VectorQuery and decodes the snapshot. Standard and Enterprise both\n * delegate here so the field-path normalisation, identifying-filter\n * application, validation surface, and result shape are guaranteed\n * identical across editions.\n *\n * Why the classic API on both editions: the Enterprise pipeline\n * `findNearest` stage is a future optimisation. Vector search's\n * deliverable is \"top-K by similarity,\" and the classic\n * `Query.findNearest(...)` API already produces that on both editions\n * with identical index requirements. When pipeline `findNearest` becomes\n * preferable for some other reason (composing with other pipeline\n * stages), the wiring is additive — swap the implementation behind this\n * helper, callers don't change.\n *\n * Migrations are not applied to the result. The contract on\n * `StorageBackend.findNearest` documents the rationale: the vector\n * index walked the raw stored shape, and rehydrating through the\n * migration pipeline would change the candidate set the index already\n * chose.\n */\n\nimport type { FieldPath } from '@google-cloud/firestore';\nimport { type Query, type VectorValue } from '@google-cloud/firestore';\n\nimport { FiregraphError } from '../errors.js';\nimport type { FindNearestParams, QueryFilter, StoredGraphRecord } from '../types.js';\nimport { applyFiltersToQuery } from './firestore-aggregate.js';\n\n/**\n * Built-in envelope fields that must NOT be passed as `vectorField` or\n * `distanceResultField`. Vectors live inside `data`; the envelope is\n * reserved for firegraph metadata. Mirrors the projection /\n * filter-field contract.\n */\nconst ENVELOPE_FIELDS: ReadonlySet<string> = new Set([\n 'aType',\n 'aUid',\n 'axbType',\n 'bType',\n 'bUid',\n 'createdAt',\n 'updatedAt',\n 'v',\n]);\n\n/**\n * Normalise a caller-supplied vector / distance-result field path. Bare\n * names rewrite to `data.<name>`; `'data'` and `'data.*'` pass through;\n * envelope fields are rejected (they aren't vector-indexable, and the\n * SDK reserves them as `distanceResultField` targets).\n */\nexport function normalizeVectorFieldPath(label: string, field: string): string {\n if (ENVELOPE_FIELDS.has(field)) {\n throw new FiregraphError(\n `findNearest(): ${label} '${field}' is a built-in envelope field — ` +\n `vectors must live under \\`data.*\\`. Use a path like 'data.${field}' ` +\n `if you really meant a nested data field.`,\n 'INVALID_QUERY',\n );\n }\n if (field === 'data' || field.startsWith('data.')) return field;\n return `data.${field}`;\n}\n\n/**\n * Translate firegraph's identifying-filter shape (`aType`, `axbType`,\n * `bType`) plus the optional `where` array into a flat `QueryFilter[]`\n * that `applyFiltersToQuery` can consume. The client wrapper does\n * scan-protection on this same list before dispatching, so the order\n * (identifiers first, then user-supplied where) is the same one the\n * safety check saw.\n */\nexport function buildVectorFilters(params: FindNearestParams): QueryFilter[] {\n const filters: QueryFilter[] = [];\n if (params.aType) filters.push({ field: 'aType', op: '==', value: params.aType });\n if (params.axbType) filters.push({ field: 'axbType', op: '==', value: params.axbType });\n if (params.bType) filters.push({ field: 'bType', op: '==', value: params.bType });\n if (params.where) filters.push(...params.where);\n return filters;\n}\n\n/** Resolve a `queryVector` argument to a plain `number[]`. */\nfunction toNumberArray(qv: number[] | { toArray(): number[] }): number[] {\n if (Array.isArray(qv)) return qv;\n if (typeof (qv as { toArray?: unknown }).toArray === 'function') {\n return (qv as VectorValue).toArray();\n }\n throw new FiregraphError(\n 'findNearest(): queryVector must be a number[] or a Firestore VectorValue.',\n 'INVALID_QUERY',\n );\n}\n\n/**\n * Run a vector query against a base Firestore `Query`. Returns the\n * matching records as `StoredGraphRecord[]`, ordered by similarity (the\n * SDK's natural order — nearest-first for EUCLIDEAN/COSINE,\n * highest-first for DOT_PRODUCT).\n *\n * Validation surface (matches the `VectorExtension` JSDoc):\n *\n * - `vectorField` and `distanceResultField` (if set) must not be\n * envelope fields.\n * - `queryVector` must be a non-empty `number[]` / `VectorValue`.\n * - `limit` must be a positive integer ≤ 1000 (the SDK enforces 1000\n * on the wire; we mirror it client-side for a clearer error).\n */\nexport async function runFirestoreFindNearest(\n base: Query,\n params: FindNearestParams,\n): Promise<StoredGraphRecord[]> {\n const vec = toNumberArray(params.queryVector);\n if (vec.length === 0) {\n throw new FiregraphError(\n 'findNearest(): queryVector is empty — at least one dimension is required.',\n 'INVALID_QUERY',\n );\n }\n if (!Number.isInteger(params.limit) || params.limit <= 0 || params.limit > 1000) {\n throw new FiregraphError(\n `findNearest(): limit must be a positive integer ≤ 1000 (got ${params.limit}).`,\n 'INVALID_QUERY',\n );\n }\n\n const vectorField = normalizeVectorFieldPath('vectorField', params.vectorField);\n const distanceResultField =\n params.distanceResultField !== undefined\n ? normalizeVectorFieldPath('distanceResultField', params.distanceResultField)\n : undefined;\n\n const filtered = applyFiltersToQuery(base, buildVectorFilters(params));\n\n // Firestore's `findNearest({ vectorField, ... })` accepts a `string` or\n // `FieldPath` for both `vectorField` and `distanceResultField`. We pass\n // the dotted path verbatim — Firestore itself interprets `.` as the\n // field-path separator, matching the convention used everywhere else\n // in firegraph.\n const opts: {\n vectorField: string | FieldPath;\n queryVector: number[];\n limit: number;\n distanceMeasure: 'EUCLIDEAN' | 'COSINE' | 'DOT_PRODUCT';\n distanceThreshold?: number;\n distanceResultField?: string | FieldPath;\n } = {\n vectorField,\n queryVector: vec,\n limit: params.limit,\n distanceMeasure: params.distanceMeasure,\n };\n if (params.distanceThreshold !== undefined) opts.distanceThreshold = params.distanceThreshold;\n if (distanceResultField !== undefined) opts.distanceResultField = distanceResultField;\n\n // The classic `findNearest` returns a `VectorQuery` with its own `.get()`\n // that resolves to a `VectorQuerySnapshot`. The doc shape is identical\n // to a normal QueryDocumentSnapshot, so we can decode straight into\n // `StoredGraphRecord` like the regular `query()` adapter does.\n const snap = await filtered.findNearest(opts).get();\n return snap.docs.map((doc) => doc.data() as StoredGraphRecord);\n}\n"],"mappings":"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","names":["chunk"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// src/internal/constants.ts
|
|
2
|
+
var NODE_RELATION = "is";
|
|
3
|
+
var DEFAULT_QUERY_LIMIT = 500;
|
|
4
|
+
var BUILTIN_FIELDS = /* @__PURE__ */ new Set([
|
|
5
|
+
"aType",
|
|
6
|
+
"aUid",
|
|
7
|
+
"axbType",
|
|
8
|
+
"bType",
|
|
9
|
+
"bUid",
|
|
10
|
+
"createdAt",
|
|
11
|
+
"updatedAt"
|
|
12
|
+
]);
|
|
13
|
+
var SHARD_SEPARATOR = ":";
|
|
14
|
+
|
|
15
|
+
// src/docid.ts
|
|
16
|
+
import { createHash } from "crypto";
|
|
17
|
+
function computeNodeDocId(uid) {
|
|
18
|
+
return uid;
|
|
19
|
+
}
|
|
20
|
+
function computeEdgeDocId(aUid, axbType, bUid) {
|
|
21
|
+
const composite = `${aUid}${SHARD_SEPARATOR}${axbType}${SHARD_SEPARATOR}${bUid}`;
|
|
22
|
+
const hash = createHash("sha256").update(composite).digest("hex");
|
|
23
|
+
const shard = hash[0];
|
|
24
|
+
return `${shard}${SHARD_SEPARATOR}${aUid}${SHARD_SEPARATOR}${axbType}${SHARD_SEPARATOR}${bUid}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
NODE_RELATION,
|
|
29
|
+
DEFAULT_QUERY_LIMIT,
|
|
30
|
+
BUILTIN_FIELDS,
|
|
31
|
+
computeNodeDocId,
|
|
32
|
+
computeEdgeDocId
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=chunk-NGAJCALM.js.map
|