@typicalday/firegraph 0.11.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/README.md +40 -15
  2. package/dist/backend-BsR0lnFL.d.ts +200 -0
  3. package/dist/backend-Ct-fLlkG.d.cts +200 -0
  4. package/dist/backend.cjs +143 -2
  5. package/dist/backend.cjs.map +1 -1
  6. package/dist/backend.d.cts +3 -3
  7. package/dist/backend.d.ts +3 -3
  8. package/dist/backend.js +13 -4
  9. package/dist/backend.js.map +1 -1
  10. package/dist/chunk-AWW4MUJ5.js +245 -0
  11. package/dist/chunk-AWW4MUJ5.js.map +1 -0
  12. package/dist/{chunk-5753Y42M.js → chunk-C2QMD7RY.js} +6 -10
  13. package/dist/chunk-C2QMD7RY.js.map +1 -0
  14. package/dist/chunk-EQJUUVFG.js +14 -0
  15. package/dist/chunk-EQJUUVFG.js.map +1 -0
  16. package/dist/{chunk-6SB34IPQ.js → chunk-HONQY4HF.js} +100 -28
  17. package/dist/chunk-HONQY4HF.js.map +1 -0
  18. package/dist/cloudflare/index.cjs +509 -102
  19. package/dist/cloudflare/index.cjs.map +1 -1
  20. package/dist/cloudflare/index.d.cts +45 -17
  21. package/dist/cloudflare/index.d.ts +45 -17
  22. package/dist/cloudflare/index.js +265 -74
  23. package/dist/cloudflare/index.js.map +1 -1
  24. package/dist/codegen/index.d.cts +1 -1
  25. package/dist/codegen/index.d.ts +1 -1
  26. package/dist/index.cjs +291 -47
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +59 -77
  29. package/dist/index.d.ts +59 -77
  30. package/dist/index.js +58 -28
  31. package/dist/index.js.map +1 -1
  32. package/dist/registry-B1qsVL0E.d.cts +64 -0
  33. package/dist/registry-Fi074zVa.d.ts +64 -0
  34. package/dist/{serialization-ZZ7RSDRX.js → serialization-OE2PFZMY.js} +6 -4
  35. package/dist/{types-BGWxcpI_.d.cts → types-DxYLy8Ol.d.cts} +36 -2
  36. package/dist/{types-BGWxcpI_.d.ts → types-DxYLy8Ol.d.ts} +36 -2
  37. package/package.json +8 -3
  38. package/dist/backend-U-MLShlg.d.ts +0 -97
  39. package/dist/backend-np4gEVhB.d.cts +0 -97
  40. package/dist/chunk-5753Y42M.js.map +0 -1
  41. package/dist/chunk-6SB34IPQ.js.map +0 -1
  42. package/dist/chunk-R7CRGYY4.js +0 -94
  43. package/dist/chunk-R7CRGYY4.js.map +0 -1
  44. /package/dist/{serialization-ZZ7RSDRX.js.map → serialization-OE2PFZMY.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/serialization.ts","../../src/cloudflare/index.ts","../../src/errors.ts","../../src/internal/constants.ts","../../src/timestamp.ts","../../src/default-indexes.ts","../../src/internal/sqlite-index-ddl.ts","../../src/cloudflare/schema.ts","../../src/cloudflare/sql.ts","../../src/cloudflare/backend.ts","../../src/docid.ts","../../src/batch.ts","../../src/dynamic-registry.ts","../../src/json-schema.ts","../../src/migration.ts","../../src/scope.ts","../../src/registry.ts","../../src/sandbox.ts","../../src/query.ts","../../src/query-safety.ts","../../src/transaction.ts","../../src/client.ts","../../src/cloudflare/client.ts","../../src/cloudflare/do.ts"],"sourcesContent":["/**\n * Firestore-aware serialization for the sandbox migration pipeline.\n *\n * Firestore documents can contain special types (Timestamp, GeoPoint,\n * VectorValue, DocumentReference) that don't survive plain JSON\n * round-tripping. This module provides tagged serialization: Firestore\n * types are wrapped in tagged plain objects before JSON marshaling and\n * reconstructed after.\n *\n * Only used by the `defaultExecutor` sandbox path. Static migrations\n * (in-memory functions) receive raw Firestore objects directly.\n */\n\nimport type { DocumentReference, Firestore } from '@google-cloud/firestore';\nimport { FieldValue, GeoPoint, Timestamp } from '@google-cloud/firestore';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n/** Sentinel key used to tag serialized Firestore types. */\nexport const SERIALIZATION_TAG = '__firegraph_ser__' as const;\n\n/** Known discriminator values for tagged types. */\nconst KNOWN_TYPES = new Set(['Timestamp', 'GeoPoint', 'VectorValue', 'DocumentReference']);\n\n// One-time warning for DocumentReference deserialization without db\nlet _docRefWarned = false;\n\n// ---------------------------------------------------------------------------\n// Type guard\n// ---------------------------------------------------------------------------\n\n/** Check if a value is a tagged serialized Firestore type. */\nexport function isTaggedValue(value: unknown): boolean {\n if (value === null || typeof value !== 'object') return false;\n const tag = (value as Record<string, unknown>)[SERIALIZATION_TAG];\n return typeof tag === 'string' && KNOWN_TYPES.has(tag);\n}\n\n// ---------------------------------------------------------------------------\n// Detection helpers\n// ---------------------------------------------------------------------------\n\nfunction isTimestamp(value: unknown): value is Timestamp {\n return value instanceof Timestamp;\n}\n\nfunction isGeoPoint(value: unknown): value is GeoPoint {\n return value instanceof GeoPoint;\n}\n\nfunction isDocumentReference(value: unknown): value is DocumentReference {\n // Duck-type check: DocumentReference has path (string) and firestore properties\n if (value === null || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.path === 'string' &&\n v.firestore !== undefined &&\n typeof v.id === 'string' &&\n v.constructor?.name === 'DocumentReference'\n );\n}\n\nfunction isVectorValue(value: unknown): boolean {\n if (value === null || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n return (\n v.constructor?.name === 'VectorValue' && Array.isArray((v as Record<string, unknown>)._values)\n );\n}\n\n// ---------------------------------------------------------------------------\n// Serialize\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively walk a data object and replace Firestore types with tagged\n * plain objects suitable for JSON serialization.\n *\n * Returns a new object tree — the input is never mutated.\n */\nexport function serializeFirestoreTypes(data: Record<string, unknown>): Record<string, unknown> {\n return serializeValue(data) as Record<string, unknown>;\n}\n\nfunction serializeValue(value: unknown): unknown {\n // Primitives\n if (value === null || value === undefined) return value;\n if (typeof value !== 'object') return value;\n\n // Firestore types (check before generic object/array)\n if (isTimestamp(value)) {\n return {\n [SERIALIZATION_TAG]: 'Timestamp',\n seconds: value.seconds,\n nanoseconds: value.nanoseconds,\n };\n }\n if (isGeoPoint(value)) {\n return {\n [SERIALIZATION_TAG]: 'GeoPoint',\n latitude: value.latitude,\n longitude: value.longitude,\n };\n }\n if (isDocumentReference(value)) {\n return { [SERIALIZATION_TAG]: 'DocumentReference', path: (value as DocumentReference).path };\n }\n if (isVectorValue(value)) {\n // Prefer toArray() (public API) over _values (private internal property)\n const v = value as Record<string, unknown>;\n const values =\n typeof v.toArray === 'function' ? (v.toArray as () => number[])() : (v._values as number[]);\n return { [SERIALIZATION_TAG]: 'VectorValue', values: [...values] };\n }\n\n // Arrays\n if (Array.isArray(value)) {\n return value.map(serializeValue);\n }\n\n // Plain objects — recurse\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n result[key] = serializeValue((value as Record<string, unknown>)[key]);\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Deserialize\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively walk a data object and reconstruct Firestore types from\n * tagged plain objects.\n *\n * @param data - The data to deserialize (typically from JSON.parse)\n * @param db - Optional Firestore instance for DocumentReference reconstruction.\n * If not provided, tagged DocumentReferences are left as-is with a one-time warning.\n *\n * Returns a new object tree — the input is never mutated.\n */\nexport function deserializeFirestoreTypes(\n data: Record<string, unknown>,\n db?: Firestore,\n): Record<string, unknown> {\n return deserializeValue(data, db) as Record<string, unknown>;\n}\n\nfunction deserializeValue(value: unknown, db?: Firestore): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value !== 'object') return value;\n\n // Short-circuit for values that are already real Firestore types.\n // This makes deserializeFirestoreTypes idempotent — safe to call on data\n // that has already been deserialized (e.g., write-back after defaultExecutor\n // already reconstructed types, or static migrations that return raw types).\n if (\n isTimestamp(value) ||\n isGeoPoint(value) ||\n isDocumentReference(value) ||\n isVectorValue(value)\n ) {\n return value;\n }\n\n // Arrays\n if (Array.isArray(value)) {\n return value.map((v) => deserializeValue(v, db));\n }\n\n const obj = value as Record<string, unknown>;\n\n // Check for tagged Firestore type\n if (isTaggedValue(obj)) {\n const tag = obj[SERIALIZATION_TAG] as string;\n\n switch (tag) {\n case 'Timestamp':\n // Validate expected fields before reconstruction\n if (typeof obj.seconds !== 'number' || typeof obj.nanoseconds !== 'number') return obj;\n return new Timestamp(obj.seconds, obj.nanoseconds);\n\n case 'GeoPoint':\n if (typeof obj.latitude !== 'number' || typeof obj.longitude !== 'number') return obj;\n return new GeoPoint(obj.latitude, obj.longitude);\n\n case 'VectorValue':\n if (!Array.isArray(obj.values)) return obj;\n return FieldValue.vector(obj.values as number[]);\n\n case 'DocumentReference':\n if (typeof obj.path !== 'string') return obj;\n if (db) {\n return db.doc(obj.path);\n }\n // No db available — leave as tagged object with one-time warning\n if (!_docRefWarned) {\n _docRefWarned = true;\n console.warn(\n '[firegraph] DocumentReference encountered during migration deserialization ' +\n 'but no Firestore instance available. The reference will remain as a tagged ' +\n 'object with its path. Enable write-back for full reconstruction.',\n );\n }\n return obj;\n\n default:\n // Unknown tag — leave as-is (forward compatibility)\n return obj;\n }\n }\n\n // Plain object — recurse\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n result[key] = deserializeValue(obj[key], db);\n }\n return result;\n}\n","/**\n * Public entry point for firegraph's Cloudflare-native backend.\n *\n * Everything re-exported here lives at `@typicalday/firegraph/cloudflare`.\n * The design philosophy — one Durable Object per subgraph, no scope column,\n * physical isolation — is documented in `do.ts` and `backend.ts`.\n *\n * ## Typical worker wiring\n *\n * ```ts\n * // worker.ts\n * export { FiregraphDO } from '@typicalday/firegraph/cloudflare';\n *\n * import { createDOClient } from '@typicalday/firegraph/cloudflare';\n * import { registry } from './registry.js';\n *\n * export default {\n * async fetch(_req: Request, env: { GRAPH: DurableObjectNamespace }) {\n * const client = createDOClient(env.GRAPH, 'main', { registry });\n * // … use client as a normal firegraph GraphClient …\n * },\n * };\n * ```\n *\n * ## Capability matrix\n *\n * | Feature | Status |\n * | ---------------------------- | -------------------------------------------------------- |\n * | CRUD (put/get/update/remove) | ✅ |\n * | Queries (`findEdges/Nodes`) | ✅ |\n * | Batches (atomic) | ✅ |\n * | Cascade (DO-local) | ✅ |\n * | Cross-subgraph cascade | ✅ via registry `getSubgraphTopology` fan-out |\n * | Bulk-remove-edges (DO-local) | ✅ |\n * | `.subgraph()` routing | ✅ (auto-provisioned via `namespace.idFromName`) |\n * | Static registry | ✅ (validation + migrations) |\n * | Dynamic registry | ✅ `registryMode: { mode: 'dynamic' }`; merged mode too |\n * | Interactive transactions | ❌ throws `UNSUPPORTED_OPERATION` — use `batch()` |\n * | `findEdgesGlobal()` | ❌ throws `UNSUPPORTED_OPERATION` — no cross-DO index |\n */\n\nexport type {\n DORPCBackendOptions,\n DurableObjectIdLike,\n FiregraphNamespace,\n FiregraphStub,\n} from './backend.js';\nexport { DORPCBackend } from './backend.js';\nexport type { DOClientOptions } from './client.js';\nexport { createDOClient, createSiblingClient } from './client.js';\nexport type {\n BatchOp,\n DOSqlCursor,\n DOSqlExecutor,\n DOStorage,\n DurableObjectStateLike,\n FiregraphDOOptions,\n} from './do.js';\nexport { FiregraphDO } from './do.js';\nexport type { BuildDOSchemaOptions } from './schema.js';\nexport { buildDOSchemaStatements } from './schema.js';\n","export class FiregraphError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n ) {\n super(message);\n this.name = 'FiregraphError';\n }\n}\n\nexport class NodeNotFoundError extends FiregraphError {\n constructor(uid: string) {\n super(`Node not found: ${uid}`, 'NODE_NOT_FOUND');\n this.name = 'NodeNotFoundError';\n }\n}\n\nexport class EdgeNotFoundError extends FiregraphError {\n constructor(aUid: string, axbType: string, bUid: string) {\n super(`Edge not found: ${aUid} -[${axbType}]-> ${bUid}`, 'EDGE_NOT_FOUND');\n this.name = 'EdgeNotFoundError';\n }\n}\n\nexport class ValidationError extends FiregraphError {\n constructor(\n message: string,\n public readonly details?: unknown,\n ) {\n super(message, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\nexport class RegistryViolationError extends FiregraphError {\n constructor(aType: string, axbType: string, bType: string) {\n super(`Unregistered triple: (${aType}) -[${axbType}]-> (${bType})`, 'REGISTRY_VIOLATION');\n this.name = 'RegistryViolationError';\n }\n}\n\nexport class InvalidQueryError extends FiregraphError {\n constructor(message: string) {\n super(message, 'INVALID_QUERY');\n this.name = 'InvalidQueryError';\n }\n}\n\nexport class TraversalError extends FiregraphError {\n constructor(message: string) {\n super(message, 'TRAVERSAL_ERROR');\n this.name = 'TraversalError';\n }\n}\n\nexport class DynamicRegistryError extends FiregraphError {\n constructor(message: string) {\n super(message, 'DYNAMIC_REGISTRY_ERROR');\n this.name = 'DynamicRegistryError';\n }\n}\n\nexport class QuerySafetyError extends FiregraphError {\n constructor(message: string) {\n super(message, 'QUERY_SAFETY');\n this.name = 'QuerySafetyError';\n }\n}\n\nexport class RegistryScopeError extends FiregraphError {\n constructor(\n aType: string,\n axbType: string,\n bType: string,\n scopePath: string,\n allowedIn: string[],\n ) {\n super(\n `Type (${aType}) -[${axbType}]-> (${bType}) is not allowed at scope \"${scopePath || 'root'}\". ` +\n `Allowed in: [${allowedIn.join(', ')}]`,\n 'REGISTRY_SCOPE',\n );\n this.name = 'RegistryScopeError';\n }\n}\n\nexport class MigrationError extends FiregraphError {\n constructor(message: string) {\n super(message, 'MIGRATION_ERROR');\n this.name = 'MigrationError';\n }\n}\n\n/**\n * Thrown when a caller tries to perform an operation that would require\n * atomicity across two physical storage backends — e.g. opening a routed\n * subgraph client from inside a transaction callback. Cross-backend\n * atomicity cannot be honoured by real-world storage engines (Firestore,\n * SQLite drivers over D1/DO/better-sqlite3, etc.), so firegraph surfaces\n * this as a typed error instead of silently confining the write to the\n * base backend.\n *\n * Normally `TransactionBackend` and `BatchBackend` don't expose `subgraph()`\n * at the type level, so this error is unreachable through well-typed code.\n * It exists as a public catchable type for app code that needs to tolerate\n * this case deliberately (e.g. dynamic code paths that bypass the type\n * system) and as future-proofing if the interface ever grows a way to\n * request a sub-scope inside a transaction.\n */\nexport class CrossBackendTransactionError extends FiregraphError {\n constructor(message: string) {\n super(message, 'CROSS_BACKEND_TRANSACTION');\n this.name = 'CrossBackendTransactionError';\n }\n}\n","export const NODE_RELATION = 'is';\n\n/**\n * Default result limit applied to findEdges/findNodes queries\n * when no explicit limit is provided. Prevents unbounded result sets\n * that could be expensive on Enterprise Firestore.\n */\nexport const DEFAULT_QUERY_LIMIT = 500;\n\n/**\n * Fields that are part of the firegraph record structure (not user data).\n * Used by the query planner and safety analysis to distinguish builtin\n * fields from data.* fields.\n */\nexport const BUILTIN_FIELDS = new Set([\n 'aType',\n 'aUid',\n 'axbType',\n 'bType',\n 'bUid',\n 'createdAt',\n 'updatedAt',\n]);\n\nexport const SHARD_ALGORITHM = 'sha256';\nexport const SHARD_SEPARATOR = ':';\nexport const SHARD_BUCKETS = 16;\n","/**\n * Backend-agnostic timestamp.\n *\n * Structurally compatible with `@google-cloud/firestore`'s `Timestamp` so\n * that records returned by either the Firestore or SQLite backend can be\n * consumed through the same `StoredGraphRecord` shape.\n *\n * Firestore's native `Timestamp` already satisfies this interface, so\n * existing Firestore consumers see no behavior change. The SQLite backend\n * returns instances of `GraphTimestampImpl` which also satisfies it.\n */\n\nexport interface GraphTimestamp {\n readonly seconds: number;\n readonly nanoseconds: number;\n toDate(): Date;\n toMillis(): number;\n}\n\n/**\n * Concrete `GraphTimestamp` implementation used by non-Firestore backends.\n * Mirrors the surface of Firestore's `Timestamp` enough for typical use.\n */\nexport class GraphTimestampImpl implements GraphTimestamp {\n constructor(\n public readonly seconds: number,\n public readonly nanoseconds: number,\n ) {}\n\n toDate(): Date {\n return new Date(this.toMillis());\n }\n\n toMillis(): number {\n return this.seconds * 1000 + Math.floor(this.nanoseconds / 1e6);\n }\n\n toJSON(): { seconds: number; nanoseconds: number } {\n return { seconds: this.seconds, nanoseconds: this.nanoseconds };\n }\n\n static fromMillis(ms: number): GraphTimestampImpl {\n const seconds = Math.floor(ms / 1000);\n const nanoseconds = (ms - seconds * 1000) * 1e6;\n return new GraphTimestampImpl(seconds, nanoseconds);\n }\n\n static now(): GraphTimestampImpl {\n return GraphTimestampImpl.fromMillis(Date.now());\n }\n}\n\n/**\n * Sentinel returned by `StorageBackend.serverTimestamp()` when the backend\n * has no native server-time concept and just wants a placeholder that the\n * adapter resolves to a concrete time at write commit. SQLite backends\n * substitute the wall-clock millis at the moment of `setDoc`/`updateDoc`.\n */\nexport const SERVER_TIMESTAMP_SENTINEL = Symbol.for('firegraph.serverTimestamp');\nexport type ServerTimestampSentinel = typeof SERVER_TIMESTAMP_SENTINEL;\n\nexport function isServerTimestampSentinel(value: unknown): value is ServerTimestampSentinel {\n return value === SERVER_TIMESTAMP_SENTINEL;\n}\n","/**\n * Default core index preset.\n *\n * This set covers the query patterns firegraph's query planner emits for\n * built-in operations — `findNodes`, `findEdges`, cascade delete, traversal,\n * and the DO/SQLite path compilers. Apps that need additional indexes\n * (descending timestamps, `data.*` filters, composite fields unique to\n * their query shapes) declare them on `RegistryEntry.indexes` or override\n * this preset wholesale via the backend-specific `coreIndexes` option —\n * `FiregraphDOOptions.coreIndexes` for the DO backend,\n * `BuildSchemaOptions.coreIndexes` for the legacy SQLite backend, and\n * `GenerateIndexOptions.coreIndexes` for the Firestore CLI generator.\n *\n * ## Ownership model\n *\n * This list is firegraph's *recommendation* — not non-negotiable policy.\n * Consumers can:\n *\n * 1. Accept the preset as-is (default).\n * 2. Extend it: `coreIndexes: [...DEFAULT_CORE_INDEXES, ...more]`.\n * 3. Replace it entirely with a tailored set.\n * 4. Disable it (`coreIndexes: []`) and take full responsibility for\n * index coverage — only do this if you're provisioning a complete\n * custom set.\n *\n * ## Per-backend emission\n *\n * The Firestore generator skips single-field entries (Firestore implicitly\n * indexes every field) and emits one composite index per multi-field spec.\n * The SQLite-flavored generators (DO, legacy) emit every spec as-is.\n *\n * ## Why these specific indexes\n *\n * - `aUid` / `bUid` — required for `_fgRemoveNodeCascade`, which scans by\n * each UID side independently. A composite `(aUid, axbType)` also\n * satisfies `aUid`-alone via leading-column prefix, but the single-field\n * form is cheaper for the common case.\n * - `aType` / `bType` — `findNodes({ aType })` and cross-type enumeration.\n * - `(aUid, axbType)` — forward edge lookup (`findEdges({ aUid, axbType })`)\n * and the `get` strategy fallback when only two of three triple fields\n * are present.\n * - `(axbType, bUid)` — reverse edge traversal.\n * - `(aType, axbType)` — type-scoped edge scans (e.g., `findEdges({ aType, axbType })`).\n * - `(axbType, bType)` — scope edges of one relation to a target type.\n */\n\nimport type { IndexSpec } from './types.js';\n\nexport const DEFAULT_CORE_INDEXES: ReadonlyArray<IndexSpec> = Object.freeze([\n { fields: ['aUid'] },\n { fields: ['bUid'] },\n { fields: ['aType'] },\n { fields: ['bType'] },\n { fields: ['aUid', 'axbType'] },\n { fields: ['axbType', 'bUid'] },\n { fields: ['aType', 'axbType'] },\n { fields: ['axbType', 'bType'] },\n]);\n","/**\n * Translator from `IndexSpec` to SQLite `CREATE INDEX` DDL.\n *\n * Shared between the DO SQLite backend (`src/cloudflare/schema.ts`) and the\n * legacy single-table SQLite backend (`src/internal/sqlite-schema.ts`). The\n * two backends differ only in:\n *\n * 1. Their field→column mapping (no `scope` column in the DO schema).\n * 2. Whether a fixed `scope` leading column is prepended to every index\n * (legacy backend only — DO rows are scoped by DO-instance identity).\n *\n * Both differences are handled via the `fieldToColumn` and `leadingColumns`\n * options; the rest of the emission logic is identical.\n *\n * ## JSON path expression indexes\n *\n * Data-field specs (`data.foo`, `data.nested.bar`) compile to\n * `json_extract(\"data\", '$.foo')` expression indexes. The JSON path\n * literal is inlined — not parametrized — so the SQLite query planner can\n * match the index against the expression emitted by the query compiler\n * (which also inlines the literal after this PR). Path components are\n * validated against a safe identifier pattern so inlining is not an\n * injection risk.\n *\n * ## Index naming\n *\n * Names are `{table}_idx_{hash}` where `hash` is a short FNV-1a of a\n * canonicalized spec. This keeps names stable across runs (so\n * `CREATE INDEX IF NOT EXISTS` is idempotent) and prevents collisions\n * between similar specs. The hash includes the field list, per-field\n * direction, and the `where` predicate.\n */\n\nimport { FiregraphError } from '../errors.js';\nimport type { IndexFieldSpec, IndexSpec } from '../types.js';\n\n/**\n * Valid SQLite identifier pattern — used for table and column names.\n * Mirrors the validation in `sqlite-schema.ts` / `cloudflare/schema.ts` so\n * this module doesn't need to import one over the other.\n */\nconst IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * Safe JSON path component. Must match `JSON_PATH_KEY_RE` in the SQLite\n * query compilers — an index is only useful if the query emits an\n * identical `json_extract` expression.\n */\nconst JSON_PATH_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;\n\nfunction quoteIdent(name: string): string {\n if (!IDENT_RE.test(name)) {\n throw new FiregraphError(\n `Invalid SQL identifier in index DDL: ${name}. Must match /^[A-Za-z_][A-Za-z0-9_]*$/.`,\n 'INVALID_INDEX',\n );\n }\n return `\"${name}\"`;\n}\n\n/**\n * FNV-1a 32-bit hash, returned as 8-char hex. Non-cryptographic;\n * used only to produce short, stable index names.\n */\nfunction fnv1a32(str: string): string {\n let h = 0x811c9dc5;\n for (let i = 0; i < str.length; i++) {\n h ^= str.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0).toString(16).padStart(8, '0');\n}\n\nfunction normalizeFields(\n fields: Array<string | IndexFieldSpec>,\n): Array<{ path: string; desc: boolean }> {\n return fields.map((f) => {\n if (typeof f === 'string') return { path: f, desc: false };\n if (!f.path || typeof f.path !== 'string') {\n throw new FiregraphError(\n `IndexSpec field must be a string or { path: string, desc?: boolean }; got ${JSON.stringify(f)}`,\n 'INVALID_INDEX',\n );\n }\n return { path: f.path, desc: !!f.desc };\n });\n}\n\nfunction specFingerprint(spec: IndexSpec, leadingColumns: string[]): string {\n // Canonical form: JSON of normalized fields + leading cols + where.\n // Leading columns are part of the fingerprint so the same spec under\n // two different backends gets distinct names (though in practice only\n // one backend compiles a given spec).\n const normalized = {\n lead: leadingColumns,\n fields: normalizeFields(spec.fields),\n where: spec.where ?? '',\n };\n return fnv1a32(JSON.stringify(normalized));\n}\n\n/**\n * Compile one field path to its SQLite column expression.\n *\n * - Firegraph top-level fields (`aType`, `createdAt`, …) → mapped column.\n * - `data.foo` / `data.foo.bar` → `json_extract(\"data\", '$.foo.bar')`.\n * - `data` alone → `json_extract(\"data\", '$')`.\n */\nfunction compileFieldExpr(path: string, fieldToColumn: Record<string, string>): string {\n const col = fieldToColumn[path];\n if (col) return quoteIdent(col);\n\n if (path === 'data') {\n return `json_extract(\"data\", '$')`;\n }\n if (path.startsWith('data.')) {\n const suffix = path.slice(5);\n const parts = suffix.split('.');\n for (const part of parts) {\n if (!JSON_PATH_KEY_RE.test(part)) {\n throw new FiregraphError(\n `IndexSpec data path \"${path}\" has invalid component \"${part}\". ` +\n `Each component must match /^[A-Za-z_][A-Za-z0-9_-]*$/.`,\n 'INVALID_INDEX',\n );\n }\n }\n // Inline the path literal (no parameter). Validated components above\n // are safe to embed — no quote or escape characters.\n return `json_extract(\"data\", '$.${suffix}')`;\n }\n\n throw new FiregraphError(\n `IndexSpec field \"${path}\" is not a known firegraph field. ` +\n `Use a top-level field (aType, aUid, axbType, bType, bUid, createdAt, updatedAt, v) ` +\n `or a dotted data path like 'data.status'.`,\n 'INVALID_INDEX',\n );\n}\n\nexport interface SqliteIndexDDLOptions {\n /** Target table. */\n table: string;\n /** Map from firegraph field name to SQLite column name. */\n fieldToColumn: Record<string, string>;\n /**\n * Columns prepended to every index's field list (leading ASC). Used by\n * the legacy shared-table SQLite backend to lead every index with\n * `scope`, matching the predicate its query compiler emits.\n *\n * Identifier names only — no JSON paths or expressions.\n */\n leadingColumns?: string[];\n}\n\n/**\n * Emit the `CREATE INDEX IF NOT EXISTS` DDL for one `IndexSpec`.\n *\n * Returns a single SQL string. Name is deterministic (same spec → same\n * name across runs), so re-running the bootstrap is idempotent.\n */\nexport function buildIndexDDL(spec: IndexSpec, options: SqliteIndexDDLOptions): string {\n const { table, fieldToColumn, leadingColumns = [] } = options;\n\n if (!spec.fields || spec.fields.length === 0) {\n throw new FiregraphError('IndexSpec.fields must be a non-empty array', 'INVALID_INDEX');\n }\n\n const normalized = normalizeFields(spec.fields);\n const hash = specFingerprint(spec, leadingColumns);\n const indexName = `${table}_idx_${hash}`;\n\n const cols: string[] = [];\n for (const col of leadingColumns) {\n cols.push(quoteIdent(col));\n }\n for (const f of normalized) {\n const expr = compileFieldExpr(f.path, fieldToColumn);\n cols.push(f.desc ? `${expr} DESC` : expr);\n }\n\n let ddl = `CREATE INDEX IF NOT EXISTS ${quoteIdent(indexName)} ON ${quoteIdent(table)}(${cols.join(', ')})`;\n\n if (spec.where) {\n // The predicate is inlined verbatim. It comes from library/app\n // configuration — never from user data — so we don't attempt to\n // parse, rewrite, or validate it. Callers authoring partial indexes\n // are responsible for writing a valid SQLite WHERE clause.\n ddl += ` WHERE ${spec.where}`;\n }\n\n return ddl;\n}\n\n/**\n * Deduplicate index specs by their deterministic fingerprint. Same spec\n * declared twice (e.g., by core preset + registry entry) collapses to a\n * single DDL statement.\n */\nexport function dedupeIndexSpecs(\n specs: ReadonlyArray<IndexSpec>,\n leadingColumns: string[] = [],\n): IndexSpec[] {\n const seen = new Set<string>();\n const out: IndexSpec[] = [];\n for (const spec of specs) {\n const fp = specFingerprint(spec, leadingColumns);\n if (seen.has(fp)) continue;\n seen.add(fp);\n out.push(spec);\n }\n return out;\n}\n","/**\n * Flat SQLite schema for a single firegraph DO.\n *\n * Each `FiregraphDO` instance owns its own SQLite database and holds exactly\n * one subgraph's triples. Subgraph isolation is physical (one DO per\n * subgraph), so there is no `scope` column — every row in this DO belongs to\n * the same logical scope. This is the Cloudflare-native design: the scope\n * discriminator used by the legacy shared-table SQLite backend\n * (`src/internal/sqlite-schema.ts`) does not exist here.\n *\n * Document IDs:\n * - Nodes: the UID itself\n * - Edges: `shard:aUid:axbType:bUid`\n *\n * Indexes come from two sources and are appended to the DDL list:\n *\n * 1. The core preset (`DEFAULT_CORE_INDEXES`), overridable per-DO via\n * `FiregraphDOOptions.coreIndexes`.\n * 2. Per-registry-entry `indexes` declared on `RegistryEntry` (from code or\n * `meta.json` via entity discovery).\n *\n * Both sets are deduplicated by canonical fingerprint, so declaring the same\n * composite twice (e.g., by preset + registry entry) collapses to one\n * `CREATE INDEX`.\n */\n\nimport { DEFAULT_CORE_INDEXES } from '../default-indexes.js';\nimport { buildIndexDDL, dedupeIndexSpecs } from '../internal/sqlite-index-ddl.js';\nimport type { GraphRegistry, IndexSpec } from '../types.js';\n\nexport const DO_COLUMNS = [\n 'doc_id',\n 'a_type',\n 'a_uid',\n 'axb_type',\n 'b_type',\n 'b_uid',\n 'data',\n 'v',\n 'created_at',\n 'updated_at',\n] as const;\n\nexport type DOColumn = (typeof DO_COLUMNS)[number];\n\n/**\n * Firegraph field name -> SQLite column name. Matches the legacy SQLite\n * backend's mapping so the query planner can compile filters identically.\n */\nexport const DO_FIELD_TO_COLUMN: Record<string, DOColumn> = {\n aType: 'a_type',\n aUid: 'a_uid',\n axbType: 'axb_type',\n bType: 'b_type',\n bUid: 'b_uid',\n v: 'v',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n};\n\nconst IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * Validate a SQLite identifier (table or column name). Identifier values in\n * this module come from config + constants, never from user data — but we\n * still fail fast if a caller passes a malformed table name, since the value\n * is interpolated directly into DDL.\n */\nexport function validateDOTableName(name: string): void {\n if (!IDENT_RE.test(name)) {\n throw new Error(`Invalid SQL identifier: ${name}. Must match /^[A-Za-z_][A-Za-z0-9_]*$/.`);\n }\n}\n\n/**\n * Double-quote a SQLite identifier, after validating it against the allowed\n * character set. Used in generated SQL to protect against keyword collisions.\n */\nexport function quoteDOIdent(name: string): string {\n validateDOTableName(name);\n return `\"${name}\"`;\n}\n\n/**\n * Options controlling DDL emission for `buildDOSchemaStatements`.\n */\nexport interface BuildDOSchemaOptions {\n /**\n * Replaces the built-in core preset. Defaults to `DEFAULT_CORE_INDEXES`.\n * Pass `[]` to disable core indexes entirely.\n */\n coreIndexes?: IndexSpec[];\n /**\n * Registry contributing per-triple `indexes` declarations. Entries with\n * no `indexes` field are ignored; the rest are flattened and deduplicated\n * against the core preset by canonical fingerprint.\n */\n registry?: GraphRegistry;\n}\n\n/**\n * DDL statements that create the firegraph table and its indexes. Returned\n * as separate statements because DO SQLite's `exec()` runs one statement per\n * call. Run via `FiregraphDO.ensureSchema()` on DO boot.\n *\n * The CREATE TABLE statement is always first; index statements follow in\n * deterministic order. Same specs across runs produce the same statements,\n * so `CREATE INDEX IF NOT EXISTS` is idempotent.\n */\nexport function buildDOSchemaStatements(\n table: string,\n options: BuildDOSchemaOptions = {},\n): string[] {\n const t = quoteDOIdent(table);\n const statements: string[] = [\n `CREATE TABLE IF NOT EXISTS ${t} (\n doc_id TEXT NOT NULL PRIMARY KEY,\n a_type TEXT NOT NULL,\n a_uid TEXT NOT NULL,\n axb_type TEXT NOT NULL,\n b_type TEXT NOT NULL,\n b_uid TEXT NOT NULL,\n data TEXT NOT NULL,\n v INTEGER,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n )`,\n ];\n\n const core = options.coreIndexes ?? [...DEFAULT_CORE_INDEXES];\n const fromRegistry = options.registry?.entries().flatMap((e) => e.indexes ?? []) ?? [];\n\n const deduped = dedupeIndexSpecs([...core, ...fromRegistry]);\n for (const spec of deduped) {\n statements.push(buildIndexDDL(spec, { table, fieldToColumn: DO_FIELD_TO_COLUMN }));\n }\n return statements;\n}\n","/**\n * SQL compilation for the DO SQLite backend.\n *\n * Every `FiregraphDO` instance owns one SQLite database holding exactly one\n * subgraph's triples — there is no `scope` column and no scope discriminator\n * on any statement. Contrast with `src/internal/sqlite-sql.ts`, which\n * carries a scope prefix on every read and write for the legacy shared-table\n * D1/DO SQLite backend.\n *\n * Filter compilation, JSON-path validation, and value binding mirror the\n * legacy module so the query planner (`src/query.ts`) emits the same\n * `QueryFilter[]` shape regardless of backend.\n */\n\nimport { FiregraphError } from '../errors.js';\nimport type { UpdatePayload, WritableRecord } from '../internal/backend.js';\nimport type { GraphTimestamp } from '../timestamp.js';\nimport { GraphTimestampImpl } from '../timestamp.js';\nimport type { QueryFilter, QueryOptions, StoredGraphRecord } from '../types.js';\nimport { DO_FIELD_TO_COLUMN, quoteDOIdent } from './schema.js';\n\n/**\n * Wire representation of a stored record across the DO RPC boundary.\n *\n * Durable Object RPC uses structured clone, which preserves plain data but\n * drops user-defined class prototypes — a `GraphTimestampImpl` from the DO\n * arrives at the client as a plain `{seconds, nanoseconds}` object without\n * its `toMillis()` / `toDate()` methods. To avoid silent `.toMillis is not a\n * function` crashes downstream, records returned from DO RPC carry the two\n * timestamps as plain millisecond numbers in `createdAtMs` / `updatedAtMs`;\n * the client-side backend rewraps them as `GraphTimestampImpl` via\n * `hydrateDORecord` before handing the record to the GraphClient.\n */\nexport interface DORecordWire {\n aType: string;\n aUid: string;\n axbType: string;\n bType: string;\n bUid: string;\n data: Record<string, unknown>;\n v?: number;\n createdAtMs: number;\n updatedAtMs: number;\n}\n\nexport interface CompiledStatement {\n sql: string;\n params: unknown[];\n}\n\n// ---------------------------------------------------------------------------\n// Filter compilation\n// ---------------------------------------------------------------------------\n\n/**\n * Translate a firegraph filter field to either a column reference or a\n * `json_extract(\"data\", '$.<path>')` expression. Built-in fields go\n * straight to their column; `data.<key>[.<key>…]` and bare `data` are\n * projected through `json_extract` with the JSON path **inlined as a\n * string literal** — not parametrized.\n *\n * Inlining matters: SQLite's query planner matches an expression index\n * (`CREATE INDEX … ON tbl(json_extract(\"data\", '$.status'))`) against\n * *textually identical* expressions in the WHERE clause. `json_extract(\n * \"data\", ?)` parametrizes the path and would never hit the index, even\n * though it evaluates to the same value. Inlining here makes the\n * expression literal in the SQL, which is what the index builder in\n * `sqlite-index-ddl.ts` also emits.\n *\n * Inlining is safe: each path component is validated against\n * `JSON_PATH_KEY_RE` (`/^[A-Za-z_][A-Za-z0-9_-]*$/`) before it reaches\n * this function — the pattern excludes every character SQLite would\n * treat as syntax (quote, backslash, dot, bracket, whitespace), so\n * string concatenation can't produce injection.\n */\nfunction compileFieldRef(field: string): { expr: string } {\n const column = DO_FIELD_TO_COLUMN[field];\n if (column) {\n return { expr: quoteDOIdent(column) };\n }\n if (field.startsWith('data.')) {\n const suffix = field.slice(5);\n for (const part of suffix.split('.')) {\n validateJsonPathKey(part);\n }\n return { expr: `json_extract(\"data\", '$.${suffix}')` };\n }\n if (field === 'data') {\n return { expr: `json_extract(\"data\", '$')` };\n }\n throw new FiregraphError(\n `DO SQLite backend cannot resolve filter field: ${field}`,\n 'INVALID_QUERY',\n );\n}\n\n/**\n * Firestore special types that don't survive `JSON.stringify`: they either\n * have non-enumerable accessors (`Timestamp.seconds`) or rely on class\n * identity the JSON representation drops. Detection is by `constructor.name`\n * to keep this module free of `@google-cloud/firestore` imports (bundle\n * pollution matters on Workers — see tests/unit/bundle-pollution.test.ts).\n */\nconst FIRESTORE_TYPE_NAMES = new Set([\n 'Timestamp',\n 'GeoPoint',\n 'VectorValue',\n 'DocumentReference',\n 'FieldValue',\n]);\n\nfunction isFirestoreSpecialType(value: object): string | null {\n const ctorName = (value as { constructor?: { name?: string } }).constructor?.name;\n if (ctorName && FIRESTORE_TYPE_NAMES.has(ctorName)) return ctorName;\n return null;\n}\n\n/**\n * Coerce a JS filter/update value into a SQLite-bindable primitive. Firestore\n * special types are rejected loudly because `JSON.stringify` would emit\n * garbage that silently fails to match any stored row. Callers should\n * project to a primitive (e.g. `ts.toMillis()`) before passing in.\n */\nfunction bindValue(value: unknown): unknown {\n if (value === null || value === undefined) return null;\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'bigint' ||\n typeof value === 'boolean'\n ) {\n return value;\n }\n if (value instanceof Date) return value.getTime();\n if (typeof value === 'object') {\n const firestoreType = isFirestoreSpecialType(value);\n if (firestoreType) {\n throw new FiregraphError(\n `DO SQLite backend cannot bind a Firestore ${firestoreType} value — JSON serialization ` +\n `would silently drop fields and the resulting bind would never match a stored row. ` +\n `Convert to a primitive (e.g. \\`ts.toMillis()\\` for Timestamp) before filtering or updating.`,\n 'INVALID_QUERY',\n );\n }\n return JSON.stringify(value);\n }\n return String(value);\n}\n\n/**\n * Identifiers accepted in `data.<key>` paths and `dataFields` keys.\n *\n * The allowed shape (`/^[A-Za-z_][A-Za-z0-9_-]*$/`) covers code-style\n * identifiers (camel, snake, kebab). Silently quoting exotic keys would\n * require symmetric quoting at every read/write call site; any drift\n * produces silent data corruption. Failing loudly at compile time is\n * safer — users with exotic keys can use `replaceData` instead of\n * `dataFields`.\n */\nconst JSON_PATH_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;\n\nfunction validateJsonPathKey(key: string): void {\n if (key.length === 0) {\n throw new FiregraphError(\n 'DO SQLite backend: empty JSON path component is not allowed',\n 'INVALID_QUERY',\n );\n }\n if (!JSON_PATH_KEY_RE.test(key)) {\n throw new FiregraphError(\n `DO SQLite backend: data field path component \"${key}\" is not a safe JSON-path identifier. ` +\n `Allowed pattern: /^[A-Za-z_][A-Za-z0-9_-]*$/. Use replaceData (full-data overwrite) ` +\n `for keys with reserved characters (whitespace, dots, brackets, quotes, etc.).`,\n 'INVALID_QUERY',\n );\n }\n}\n\nfunction compileFilter(filter: QueryFilter, params: unknown[]): string {\n const { expr } = compileFieldRef(filter.field);\n\n switch (filter.op) {\n case '==':\n params.push(bindValue(filter.value));\n return `${expr} = ?`;\n case '!=':\n params.push(bindValue(filter.value));\n return `${expr} != ?`;\n case '<':\n params.push(bindValue(filter.value));\n return `${expr} < ?`;\n case '<=':\n params.push(bindValue(filter.value));\n return `${expr} <= ?`;\n case '>':\n params.push(bindValue(filter.value));\n return `${expr} > ?`;\n case '>=':\n params.push(bindValue(filter.value));\n return `${expr} >= ?`;\n case 'in': {\n const values = asArray(filter.value, 'in');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `${expr} IN (${placeholders})`;\n }\n case 'not-in': {\n const values = asArray(filter.value, 'not-in');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `${expr} NOT IN (${placeholders})`;\n }\n case 'array-contains': {\n params.push(bindValue(filter.value));\n return `EXISTS (SELECT 1 FROM json_each(${expr}) WHERE value = ?)`;\n }\n case 'array-contains-any': {\n const values = asArray(filter.value, 'array-contains-any');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `EXISTS (SELECT 1 FROM json_each(${expr}) WHERE value IN (${placeholders}))`;\n }\n default:\n throw new FiregraphError(\n `DO SQLite backend does not support filter operator: ${String(filter.op)}`,\n 'INVALID_QUERY',\n );\n }\n}\n\nfunction asArray(value: unknown, op: string): unknown[] {\n if (!Array.isArray(value) || value.length === 0) {\n throw new FiregraphError(`Operator \"${op}\" requires a non-empty array value`, 'INVALID_QUERY');\n }\n return value;\n}\n\nfunction compileOrderBy(options: QueryOptions | undefined, _params: unknown[]): string {\n if (!options?.orderBy) return '';\n const { field, direction } = options.orderBy;\n const { expr } = compileFieldRef(field);\n const dir = direction === 'desc' ? 'DESC' : 'ASC';\n return ` ORDER BY ${expr} ${dir}`;\n}\n\nfunction compileLimit(options: QueryOptions | undefined, params: unknown[]): string {\n if (options?.limit === undefined) return '';\n params.push(options.limit);\n return ` LIMIT ?`;\n}\n\n// ---------------------------------------------------------------------------\n// Statement compilation\n// ---------------------------------------------------------------------------\n\n/**\n * SELECT rows matching `filters`. No scope predicate — every row in this\n * DO's database belongs to the same subgraph.\n */\nexport function compileDOSelect(\n table: string,\n filters: QueryFilter[],\n options?: QueryOptions,\n): CompiledStatement {\n const params: unknown[] = [];\n const conditions: string[] = [];\n\n for (const f of filters) {\n conditions.push(compileFilter(f, params));\n }\n\n const where = conditions.length > 0 ? ` WHERE ${conditions.join(' AND ')}` : '';\n let sql = `SELECT * FROM ${quoteDOIdent(table)}${where}`;\n sql += compileOrderBy(options, params);\n sql += compileLimit(options, params);\n\n return { sql, params };\n}\n\n/**\n * SELECT a single row by doc_id. `doc_id` is the PK so this is an O(1)\n * index lookup.\n */\nexport function compileDOSelectByDocId(table: string, docId: string): CompiledStatement {\n return {\n sql: `SELECT * FROM ${quoteDOIdent(table)} WHERE \"doc_id\" = ? LIMIT 1`,\n params: [docId],\n };\n}\n\n/**\n * INSERT OR REPLACE — `setDoc` semantics. Full row replacement keyed by\n * `doc_id`.\n */\nexport function compileDOSet(\n table: string,\n docId: string,\n record: WritableRecord,\n nowMillis: number,\n): CompiledStatement {\n const sql = `INSERT OR REPLACE INTO ${quoteDOIdent(table)} (\n doc_id, a_type, a_uid, axb_type, b_type, b_uid, data, v, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;\n const params: unknown[] = [\n docId,\n record.aType,\n record.aUid,\n record.axbType,\n record.bType,\n record.bUid,\n JSON.stringify(record.data ?? {}),\n record.v ?? null,\n nowMillis,\n nowMillis,\n ];\n return { sql, params };\n}\n\n/**\n * Compile an `UpdatePayload` into a single UPDATE statement. Same rules as\n * the legacy backend: `replaceData` overwrites the whole blob; `dataFields`\n * shallow-merges via `json_set(data, '$.k', v, …)`; `v` is stamped when\n * provided; `updated_at` is always stamped.\n */\nexport function compileDOUpdate(\n table: string,\n docId: string,\n update: UpdatePayload,\n nowMillis: number,\n): CompiledStatement {\n const setClauses: string[] = [];\n const params: unknown[] = [];\n\n if (update.replaceData) {\n setClauses.push(`\"data\" = ?`);\n params.push(JSON.stringify(update.replaceData));\n } else if (update.dataFields && Object.keys(update.dataFields).length > 0) {\n const entries = Object.entries(update.dataFields);\n const pathArgs = entries.map(() => `?, ?`).join(', ');\n setClauses.push(`\"data\" = json_set(COALESCE(\"data\", '{}'), ${pathArgs})`);\n for (const [k, v] of entries) {\n validateJsonPathKey(k);\n params.push(`$.${k}`);\n params.push(bindValue(v));\n }\n }\n\n if (update.v !== undefined) {\n setClauses.push(`\"v\" = ?`);\n params.push(update.v);\n }\n\n setClauses.push(`\"updated_at\" = ?`);\n params.push(nowMillis);\n\n params.push(docId);\n\n return {\n sql: `UPDATE ${quoteDOIdent(table)} SET ${setClauses.join(', ')} WHERE \"doc_id\" = ?`,\n params,\n };\n}\n\nexport function compileDODelete(table: string, docId: string): CompiledStatement {\n return {\n sql: `DELETE FROM ${quoteDOIdent(table)} WHERE \"doc_id\" = ?`,\n params: [docId],\n };\n}\n\n/**\n * DELETE every row in the table. Used when tearing down an entire subgraph\n * DO as part of cascade — the caller discovers the set of DOs to wipe via\n * registry topology (phase 2) and instructs each DO to clear itself.\n */\nexport function compileDODeleteAll(table: string): CompiledStatement {\n return {\n sql: `DELETE FROM ${quoteDOIdent(table)}`,\n params: [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Row -> record (wire-safe) and hydration\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a SQLite row into a `DORecordWire` — the wire-safe shape returned\n * across DO RPC. Timestamps stay as plain millisecond numbers here; the\n * client-side backend calls `hydrateDORecord` to rewrap them as\n * `GraphTimestampImpl` before surfacing the record to the GraphClient.\n *\n * Splitting serialization from hydration like this is what lets the DO\n * return values safely through structured clone without pretending its\n * output is a full `StoredGraphRecord`.\n */\nexport function rowToDORecord(row: Record<string, unknown>): DORecordWire {\n const dataString = row.data as string | null;\n const data = dataString ? (JSON.parse(dataString) as Record<string, unknown>) : {};\n\n const createdAtMs = toMillis(row.created_at);\n const updatedAtMs = toMillis(row.updated_at);\n\n const record: DORecordWire = {\n aType: row.a_type as string,\n aUid: row.a_uid as string,\n axbType: row.axb_type as string,\n bType: row.b_type as string,\n bUid: row.b_uid as string,\n data,\n createdAtMs,\n updatedAtMs,\n };\n\n if (row.v !== null && row.v !== undefined) {\n record.v = Number(row.v);\n }\n return record;\n}\n\n/**\n * Rewrap a `DORecordWire` as a full `StoredGraphRecord`, restoring\n * `GraphTimestampImpl` instances from the wire-level millisecond numbers.\n * Called by `DORPCBackend` on every record returned from a DO RPC.\n */\nexport function hydrateDORecord(wire: DORecordWire): StoredGraphRecord {\n const record: Record<string, unknown> = {\n aType: wire.aType,\n aUid: wire.aUid,\n axbType: wire.axbType,\n bType: wire.bType,\n bUid: wire.bUid,\n data: wire.data,\n createdAt: GraphTimestampImpl.fromMillis(wire.createdAtMs) as unknown as GraphTimestamp,\n updatedAt: GraphTimestampImpl.fromMillis(wire.updatedAtMs) as unknown as GraphTimestamp,\n };\n if (wire.v !== undefined) {\n record.v = wire.v;\n }\n return record as unknown as StoredGraphRecord;\n}\n\n/**\n * Coerce a timestamp column value to a plain millis number. The schema types\n * `created_at` / `updated_at` as `INTEGER NOT NULL` so the column should\n * always arrive as a number (or possibly a bigint on some SQLite bindings),\n * but a string row value from SQLite (e.g. BigInt.toString fallback) is also\n * accepted. Anything else indicates a corrupt row — throw loudly rather than\n * silently returning 0, which would quietly mask the bug on every read.\n */\nfunction toMillis(value: unknown): number {\n if (typeof value === 'number') return value;\n if (typeof value === 'bigint') return Number(value);\n if (typeof value === 'string') {\n const n = Number(value);\n if (Number.isFinite(n)) return n;\n }\n throw new FiregraphError(\n `DO SQLite row has non-numeric timestamp column: ${typeof value} (${String(value)})`,\n 'INVALID_QUERY',\n );\n}\n","/**\n * Client-side `StorageBackend` that forwards every operation to a\n * `FiregraphDO` instance over Durable Object RPC.\n *\n * One `DORPCBackend` corresponds to one DO — the root graph's DO, or a\n * subgraph's DO. `subgraph()` returns a new `DORPCBackend` bound to a\n * different DO, identified by deriving a new stable name from the chain of\n * parent UIDs and subgraph names. The library uses `namespace.idFromName()`\n * on that key, so two clients with the same key always reach the same DO.\n *\n * Key invariants:\n *\n * - There is no shared table and no `scope` column. Each DO owns its own\n * flat SQLite database; isolation is physical.\n * - Interactive transactions throw `UNSUPPORTED_OPERATION` — holding a\n * synchronous SQLite transaction across async RPC calls would block the\n * DO's single-threaded executor (see `transactionsUnsupported` below).\n * - `findEdgesGlobal` is deliberately left undefined on this class. The\n * `GraphClient` surfaces the generic \"not supported by current storage\n * backend\" error before running any query planning, which is both\n * accurate and sidesteps the misleading `QuerySafetyError` that would\n * otherwise fire for scan-unsafe calls. `createDOClient`'s docstring\n * explains the design rationale (no collection-group index across DOs).\n * - `removeNodeCascade` cascades across DOs: when a registry accessor is\n * wired, the backend walks `registry.getSubgraphTopology(aType)` for the\n * node being removed and destroys every descendant subgraph DO before\n * deleting the node itself. Pass `{ deleteSubcollections: false }` to\n * disable the cross-DO fan-out (parent node is still removed, child DOs\n * are left intact — matching the Firestore/SQLite backends' semantic).\n * Without an accessor (e.g. registry-less clients) it cascades within\n * the current DO only.\n */\n\nimport { FiregraphError } from '../errors.js';\nimport type {\n BatchBackend,\n StorageBackend,\n TransactionBackend,\n UpdatePayload,\n WritableRecord,\n} from '../internal/backend.js';\nimport { NODE_RELATION } from '../internal/constants.js';\nimport type {\n BulkOptions,\n BulkResult,\n CascadeResult,\n DynamicGraphClient,\n FindEdgesParams,\n GraphClient,\n GraphReader,\n GraphRegistry,\n QueryFilter,\n QueryOptions,\n StoredGraphRecord,\n} from '../types.js';\nimport type { BatchOp } from './do.js';\nimport type { DORecordWire } from './sql.js';\nimport { hydrateDORecord } from './sql.js';\n\n// ---------------------------------------------------------------------------\n// Minimal DO namespace / stub types\n//\n// We avoid importing `@cloudflare/workers-types` to keep this module usable\n// in any TypeScript consumer — users with workers-types installed get the\n// richer types via declaration merging at their call site.\n// ---------------------------------------------------------------------------\n\nexport interface DurableObjectIdLike {\n toString(): string;\n}\n\n/**\n * The RPC surface this backend calls on the DO stub. Every method matches a\n * `_fg…` method on `FiregraphDO`. Kept structurally typed so users can bring\n * their own subclass without the types having to know about it.\n *\n * Reads return `DORecordWire` (plain data — safe through DO structured\n * clone); the backend rewraps each record via `hydrateDORecord` before\n * handing it to the GraphClient, which expects `GraphTimestampImpl`\n * instances (not plain `{seconds, nanoseconds}` objects).\n */\nexport interface FiregraphStub {\n _fgGetDoc(docId: string): Promise<DORecordWire | null>;\n _fgQuery(filters: QueryFilter[], options?: QueryOptions): Promise<DORecordWire[]>;\n _fgSetDoc(docId: string, record: WritableRecord): Promise<void>;\n _fgUpdateDoc(docId: string, update: UpdatePayload): Promise<void>;\n _fgDeleteDoc(docId: string): Promise<void>;\n _fgBatch(ops: BatchOp[]): Promise<void>;\n _fgRemoveNodeCascade(uid: string): Promise<CascadeResult>;\n _fgBulkRemoveEdges(params: FindEdgesParams, options?: BulkOptions): Promise<BulkResult>;\n _fgDestroy(): Promise<void>;\n}\n\nexport interface FiregraphNamespace {\n idFromName(name: string): DurableObjectIdLike;\n get(id: DurableObjectIdLike): FiregraphStub;\n}\n\n// ---------------------------------------------------------------------------\n// Subgraph name validation\n//\n// The chain of subgraph segments becomes the input to `idFromName()`. Two\n// different paths must produce two different hashes, so segments can't\n// contain the `/` separator — otherwise `('A', 'x/y')` and `('A/x', 'y')`\n// would collide. The `GraphClient` already validates at the public API\n// layer; this is defense-in-depth for direct backend users (traversal,\n// cross-graph hops).\n// ---------------------------------------------------------------------------\n\nfunction validateSegment(value: string, label: string): void {\n if (!value || value.includes('/')) {\n throw new FiregraphError(\n `Invalid ${label} for subgraph: \"${value}\". Must be non-empty and not contain \"/\".`,\n 'INVALID_SUBGRAPH',\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Transaction backend — always throws\n// ---------------------------------------------------------------------------\n\n/**\n * Interactive transactions across DO RPC would require holding a synchronous\n * SQLite transaction open across multiple async RPC round-trips, which blocks\n * the DO's single-threaded executor for the duration. That's incompatible\n * with the runtime's fairness model, so this backend refuses `runTransaction`\n * outright. Callers should either restructure read-then-conditional-write\n * logic as an explicit `read → decide → batch` sequence, or use `batch()`\n * for atomic multi-write patterns.\n */\nfunction transactionsUnsupported(): FiregraphError {\n return new FiregraphError(\n 'Interactive transactions are not supported by the Cloudflare DO backend. ' +\n 'Use `batch()` for atomic multi-write commits, or restructure the read-then-conditional-write ' +\n 'as an explicit read → decide → batch sequence.',\n 'UNSUPPORTED_OPERATION',\n );\n}\n\n// ---------------------------------------------------------------------------\n// Batch backend — buffers locally, submits one RPC on commit\n// ---------------------------------------------------------------------------\n\nclass DORPCBatchBackend implements BatchBackend {\n private readonly ops: BatchOp[] = [];\n\n constructor(private readonly getStub: () => FiregraphStub) {}\n\n setDoc(docId: string, record: WritableRecord): void {\n this.ops.push({ kind: 'set', docId, record });\n }\n\n updateDoc(docId: string, update: UpdatePayload): void {\n this.ops.push({ kind: 'update', docId, update });\n }\n\n deleteDoc(docId: string): void {\n this.ops.push({ kind: 'delete', docId });\n }\n\n async commit(): Promise<void> {\n if (this.ops.length === 0) return;\n // Pass a shallow copy so that clearing the local buffer after commit\n // doesn't mutate what the stub received. Over real DO RPC the array is\n // already copied via structured clone; in-process tests and any future\n // in-memory stub see the copy explicitly.\n const ops = this.ops.slice();\n this.ops.length = 0;\n await this.getStub()._fgBatch(ops);\n }\n}\n\n// ---------------------------------------------------------------------------\n// StorageBackend implementation\n// ---------------------------------------------------------------------------\n\nexport interface DORPCBackendOptions {\n /** Scope path (names-only chain, used for `allowedIn`). Default: `''`. */\n scopePath?: string;\n /**\n * Opaque storage key used to derive the DO instance via\n * `namespace.idFromName(storageKey)`. Defaults to the root key passed to\n * `createDOClient` when the backend is first created.\n */\n storageKey: string;\n /**\n * Live registry accessor used by `removeNodeCascade` to consult the\n * subgraph topology and fan out `_fgDestroy` calls to child subgraph DOs.\n *\n * A function (not a snapshot) so that dynamic-registry clients see the\n * latest definitions after `reloadRegistry()`. Wired by `createDOClient`\n * via a forward reference to the constructed `GraphClient`. When\n * `undefined`, cross-DO cascade is disabled and `removeNodeCascade`\n * cascades within this DO only.\n * @internal\n */\n registryAccessor?: () => GraphRegistry | undefined;\n /**\n * Factory used by `createSiblingClient` to construct a peer `GraphClient`\n * that shares this client's namespace, registry, and other options but\n * targets a different root DO. Wired by `createDOClient`. Leaving it\n * `undefined` (e.g. when `DORPCBackend` is instantiated directly) disables\n * sibling-client construction — `createSiblingClient` will throw.\n *\n * The union return type mirrors `createDOClient`'s two overloads: dynamic\n * mode yields a `DynamicGraphClient`, everything else yields a plain\n * `GraphClient`. `createSiblingClient` narrows at the boundary via its\n * own overload signatures.\n * @internal\n */\n makeSiblingClient?: (siblingStorageKey: string) => GraphClient | DynamicGraphClient;\n}\n\nexport class DORPCBackend implements StorageBackend {\n readonly collectionPath = 'firegraph';\n readonly scopePath: string;\n /** @internal */\n readonly storageKey: string;\n /** @internal */\n readonly namespace: FiregraphNamespace;\n private readonly registryAccessor?: () => GraphRegistry | undefined;\n /** @internal — see `DORPCBackendOptions.makeSiblingClient` for the union-type rationale. */\n readonly makeSiblingClient?: (siblingStorageKey: string) => GraphClient | DynamicGraphClient;\n private cachedStub: FiregraphStub | null = null;\n\n constructor(namespace: FiregraphNamespace, options: DORPCBackendOptions) {\n this.namespace = namespace;\n this.scopePath = options.scopePath ?? '';\n this.storageKey = options.storageKey;\n this.registryAccessor = options.registryAccessor;\n this.makeSiblingClient = options.makeSiblingClient;\n }\n\n private get stub(): FiregraphStub {\n if (!this.cachedStub) {\n const id = this.namespace.idFromName(this.storageKey);\n this.cachedStub = this.namespace.get(id);\n }\n return this.cachedStub;\n }\n\n // --- Reads ---\n\n async getDoc(docId: string): Promise<StoredGraphRecord | null> {\n const wire = await this.stub._fgGetDoc(docId);\n return wire ? hydrateDORecord(wire) : null;\n }\n\n async query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]> {\n const wires = await this.stub._fgQuery(filters, options);\n return wires.map(hydrateDORecord);\n }\n\n // --- Writes ---\n\n async setDoc(docId: string, record: WritableRecord): Promise<void> {\n return this.stub._fgSetDoc(docId, record);\n }\n\n async updateDoc(docId: string, update: UpdatePayload): Promise<void> {\n return this.stub._fgUpdateDoc(docId, update);\n }\n\n async deleteDoc(docId: string): Promise<void> {\n return this.stub._fgDeleteDoc(docId);\n }\n\n // --- Transactions / batches ---\n\n async runTransaction<T>(_fn: (tx: TransactionBackend) => Promise<T>): Promise<T> {\n // Structurally surface the unsupported error — the tx argument passed to\n // `_fn` would throw on every call anyway, but we fail earlier so callers\n // don't discover the limitation mid-transaction.\n void _fn;\n throw transactionsUnsupported();\n }\n\n createBatch(): BatchBackend {\n return new DORPCBatchBackend(() => this.stub);\n }\n\n // --- Subgraphs ---\n\n subgraph(parentNodeUid: string, name: string): StorageBackend {\n validateSegment(parentNodeUid, 'parentNodeUid');\n validateSegment(name, 'subgraph name');\n const newStorageKey = `${this.storageKey}/${parentNodeUid}/${name}`;\n const newScopePath = this.scopePath ? `${this.scopePath}/${name}` : name;\n return new DORPCBackend(this.namespace, {\n scopePath: newScopePath,\n storageKey: newStorageKey,\n // Subgraph backends share the same live registry accessor so a cascade\n // invoked on a subgraph client still fans out correctly. The sibling\n // factory is also carried forward so `createSiblingClient` works from\n // any subgraph client in the chain.\n registryAccessor: this.registryAccessor,\n makeSiblingClient: this.makeSiblingClient,\n });\n }\n\n // --- Cascade & bulk ---\n\n async removeNodeCascade(\n uid: string,\n reader: GraphReader,\n options?: BulkOptions,\n ): Promise<CascadeResult> {\n // Cross-DO cascade. When a registry is wired and the caller wants\n // subcollections (the default, `deleteSubcollections !== false`), walk\n // the subgraph topology for this node's type and wipe every descendant\n // DO before deleting the node itself. This mirrors the Firestore and\n // SQLite backends, which honor the same flag to recurse into nested\n // subgraphs. Without an accessor we fall back to DO-local cascade only\n // — each DO owns its own scope, and registry-less clients have no way\n // to discover descendants.\n //\n // We need the node's aType to know what subgraphs to look for. That\n // means a `getNode` round-trip before touching any child DO; the reader\n // is the client that owns this backend, so the round-trip hits *this*\n // DO and stays cheap. If the node doesn't exist there's nothing to\n // cascade across — skip straight to the local cascade, which will\n // report `nodeDeleted: false`.\n const shouldDeleteSubgraphs = options?.deleteSubcollections !== false;\n const registry = this.registryAccessor?.();\n if (shouldDeleteSubgraphs && registry) {\n const node = await reader.getNode(uid);\n if (node) {\n const topology = registry.getSubgraphTopology(node.aType);\n for (const entry of topology) {\n // `getSubgraphTopology` only returns entries with a `targetGraph`.\n // The non-null assertion encodes that invariant — a missing value\n // here is a registry-construction bug, not a runtime data issue.\n const target = entry.targetGraph!;\n const childBackend = this.subgraph(uid, target) as DORPCBackend;\n await childBackend.destroyRecursively(registry);\n }\n }\n }\n return this.stub._fgRemoveNodeCascade(uid);\n }\n\n async bulkRemoveEdges(\n params: FindEdgesParams,\n _reader: GraphReader,\n options?: BulkOptions,\n ): Promise<BulkResult> {\n void _reader;\n return this.stub._fgBulkRemoveEdges(params, options);\n }\n\n // --- Cross-scope queries ---\n //\n // `findEdgesGlobal` is deliberately NOT defined on this class. The\n // GraphClient checks for its presence before running query planning and\n // throws `UNSUPPORTED_OPERATION` when absent, giving the caller an\n // immediate, accurate error. Defining the method with a throwing body\n // would only surface the same error AFTER `checkQuerySafety` had already\n // fired — and for scan-unsafe calls that results in a misleading\n // `QuerySafetyError` (\"add filters like aUid+axbType\") when no filter\n // combination would actually make the call work on this backend. See the\n // \"What's not supported\" section in `createDOClient` for the design\n // rationale (no collection-group index across DOs).\n\n // --- Destroy helpers ---\n\n /**\n * Wipe this DO's storage. The DO itself can't be deleted — its ID\n * persists forever — but its rows can be emptied, which is what the\n * cascade walk does on every descendant subgraph DO.\n *\n * Exposed on the concrete class (not `StorageBackend`) so generic\n * backend code doesn't reach for it.\n */\n async destroy(): Promise<void> {\n await this.stub._fgDestroy();\n }\n\n /**\n * Tear down every descendant subgraph DO, then wipe this DO's own rows.\n *\n * Invoked by cross-DO cascade: for each node in this DO we enumerate the\n * subgraph topology and recurse into child DOs depth-first before\n * wiping the current DO. The current DO's own rows are destroyed last so\n * that a partial failure mid-recursion leaves the caller's reader able\n * to discover what's still present.\n *\n * @internal\n */\n async destroyRecursively(registry: GraphRegistry): Promise<void> {\n // Enumerate every node (self-loop) in this DO. We only need nodes —\n // edges don't own subgraph children, only nodes do.\n const nodes = await this.query([{ field: 'axbType', op: '==', value: NODE_RELATION }]);\n for (const node of nodes) {\n const topology = registry.getSubgraphTopology(node.aType);\n for (const entry of topology) {\n // `getSubgraphTopology` only returns entries with a `targetGraph` —\n // see the matching assertion in `removeNodeCascade` above.\n const target = entry.targetGraph!;\n const childBackend = this.subgraph(node.aUid, target) as DORPCBackend;\n await childBackend.destroyRecursively(registry);\n }\n }\n await this.destroy();\n }\n}\n","import { createHash } from 'node:crypto';\n\nimport { SHARD_SEPARATOR } from './internal/constants.js';\n\nexport function computeNodeDocId(uid: string): string {\n return uid;\n}\n\nexport function computeEdgeDocId(aUid: string, axbType: string, bUid: string): string {\n const composite = `${aUid}${SHARD_SEPARATOR}${axbType}${SHARD_SEPARATOR}${bUid}`;\n const hash = createHash('sha256').update(composite).digest('hex');\n const shard = hash[0];\n return `${shard}${SHARD_SEPARATOR}${aUid}${SHARD_SEPARATOR}${axbType}${SHARD_SEPARATOR}${bUid}`;\n}\n","import { computeEdgeDocId, computeNodeDocId } from './docid.js';\nimport type { BatchBackend, WritableRecord } from './internal/backend.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport type { GraphBatch, GraphRegistry } from './types.js';\n\nfunction buildWritableNodeRecord(\n aType: string,\n uid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid: uid, axbType: NODE_RELATION, bType: aType, bUid: uid, data };\n}\n\nfunction buildWritableEdgeRecord(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid, axbType, bType, bUid, data };\n}\n\nexport class GraphBatchImpl implements GraphBatch {\n constructor(\n private readonly backend: BatchBackend,\n private readonly registry?: GraphRegistry,\n private readonly scopePath: string = '',\n ) {}\n\n async putNode(aType: string, uid: string, data: Record<string, unknown>): Promise<void> {\n if (this.registry) {\n this.registry.validate(aType, NODE_RELATION, aType, data, this.scopePath);\n }\n const docId = computeNodeDocId(uid);\n const record = buildWritableNodeRecord(aType, uid, data);\n if (this.registry) {\n const entry = this.registry.lookup(aType, NODE_RELATION, aType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n this.backend.setDoc(docId, record);\n }\n\n async putEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n if (this.registry) {\n this.registry.validate(aType, axbType, bType, data, this.scopePath);\n }\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = buildWritableEdgeRecord(aType, aUid, axbType, bType, bUid, data);\n if (this.registry) {\n const entry = this.registry.lookup(aType, axbType, bType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n this.backend.setDoc(docId, record);\n }\n\n async updateNode(uid: string, data: Record<string, unknown>): Promise<void> {\n const docId = computeNodeDocId(uid);\n this.backend.updateDoc(docId, { dataFields: data });\n }\n\n async removeNode(uid: string): Promise<void> {\n const docId = computeNodeDocId(uid);\n this.backend.deleteDoc(docId);\n }\n\n async removeEdge(aUid: string, axbType: string, bUid: string): Promise<void> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n this.backend.deleteDoc(docId);\n }\n\n async commit(): Promise<void> {\n await this.backend.commit();\n }\n}\n","import { createHash } from 'node:crypto';\n\nimport { NODE_RELATION } from './internal/constants.js';\nimport { createRegistry } from './registry.js';\nimport { compileMigrations, precompileSource } from './sandbox.js';\nimport type {\n EdgeTypeData,\n GraphReader,\n GraphRegistry,\n MigrationExecutor,\n NodeTypeData,\n RegistryEntry,\n} from './types.js';\n\n// ---------------------------------------------------------------------------\n// Meta-type constants\n// ---------------------------------------------------------------------------\n\n/** The aType used for node type definition meta-nodes. */\nexport const META_NODE_TYPE = 'nodeType';\n\n/** The aType used for edge type definition meta-nodes. */\nexport const META_EDGE_TYPE = 'edgeType';\n\n// ---------------------------------------------------------------------------\n// JSON Schemas for meta-type data payloads\n// ---------------------------------------------------------------------------\n\n/** JSON Schema for a single stored migration step. */\nconst STORED_MIGRATION_STEP_SCHEMA = {\n type: 'object',\n required: ['fromVersion', 'toVersion', 'up'],\n properties: {\n fromVersion: { type: 'integer', minimum: 0 },\n toVersion: { type: 'integer', minimum: 1 },\n up: { type: 'string', minLength: 1 },\n },\n additionalProperties: false,\n};\n\n/** JSON Schema for the `data` payload of a `nodeType` meta-node. */\nexport const NODE_TYPE_SCHEMA: object = {\n type: 'object',\n required: ['name', 'jsonSchema'],\n properties: {\n name: { type: 'string', minLength: 1 },\n jsonSchema: { type: 'object' },\n description: { type: 'string' },\n titleField: { type: 'string' },\n subtitleField: { type: 'string' },\n viewTemplate: { type: 'string' },\n viewCss: { type: 'string' },\n allowedIn: { type: 'array', items: { type: 'string', minLength: 1 } },\n schemaVersion: { type: 'integer', minimum: 0 },\n migrations: { type: 'array', items: STORED_MIGRATION_STEP_SCHEMA },\n migrationWriteBack: { type: 'string', enum: ['off', 'eager', 'background'] },\n },\n additionalProperties: false,\n};\n\n/** JSON Schema for the `data` payload of an `edgeType` meta-node. */\nexport const EDGE_TYPE_SCHEMA: object = {\n type: 'object',\n required: ['name', 'from', 'to'],\n properties: {\n name: { type: 'string', minLength: 1 },\n from: {\n oneOf: [\n { type: 'string', minLength: 1 },\n { type: 'array', items: { type: 'string', minLength: 1 }, minItems: 1 },\n ],\n },\n to: {\n oneOf: [\n { type: 'string', minLength: 1 },\n { type: 'array', items: { type: 'string', minLength: 1 }, minItems: 1 },\n ],\n },\n jsonSchema: { type: 'object' },\n inverseLabel: { type: 'string' },\n description: { type: 'string' },\n titleField: { type: 'string' },\n subtitleField: { type: 'string' },\n viewTemplate: { type: 'string' },\n viewCss: { type: 'string' },\n allowedIn: { type: 'array', items: { type: 'string', minLength: 1 } },\n targetGraph: { type: 'string', minLength: 1, pattern: '^[^/]+$' },\n schemaVersion: { type: 'integer', minimum: 0 },\n migrations: { type: 'array', items: STORED_MIGRATION_STEP_SCHEMA },\n migrationWriteBack: { type: 'string', enum: ['off', 'eager', 'background'] },\n },\n additionalProperties: false,\n};\n\n// ---------------------------------------------------------------------------\n// Bootstrap registry\n// ---------------------------------------------------------------------------\n\n/** Registry entries for the two meta-types (always present). */\nexport const BOOTSTRAP_ENTRIES: readonly RegistryEntry[] = [\n {\n aType: META_NODE_TYPE,\n axbType: NODE_RELATION,\n bType: META_NODE_TYPE,\n jsonSchema: NODE_TYPE_SCHEMA,\n description: 'Meta-type: defines a node type',\n },\n {\n aType: META_EDGE_TYPE,\n axbType: NODE_RELATION,\n bType: META_EDGE_TYPE,\n jsonSchema: EDGE_TYPE_SCHEMA,\n description: 'Meta-type: defines an edge type',\n },\n];\n\n/**\n * Build the bootstrap registry that validates meta-type writes.\n * This is always available, even before any dynamic types are loaded.\n */\nexport function createBootstrapRegistry(): GraphRegistry {\n return createRegistry([...BOOTSTRAP_ENTRIES]);\n}\n\n// ---------------------------------------------------------------------------\n// Deterministic UID generation\n// ---------------------------------------------------------------------------\n\n/**\n * Generate a deterministic UID for a meta-type definition.\n * This ensures that defining the same type name always targets the same\n * Firestore document, enabling upsert semantics.\n *\n * Format: 21-char base64url substring of SHA-256(`metaType:name`).\n */\nexport function generateDeterministicUid(metaType: string, name: string): string {\n const hash = createHash('sha256').update(`${metaType}:${name}`).digest('base64url');\n return hash.slice(0, 21);\n}\n\n// ---------------------------------------------------------------------------\n// createRegistryFromGraph\n// ---------------------------------------------------------------------------\n\n/**\n * Read meta-type nodes from the graph and compile them into a GraphRegistry.\n *\n * The returned registry includes both the dynamic entries AND the bootstrap\n * meta-type entries, so meta-type writes remain validateable after a reload.\n *\n * @param reader - A GraphReader pointed at the collection containing meta-nodes.\n * @param executor - Optional custom executor for compiling stored migration source strings.\n */\nexport async function createRegistryFromGraph(\n reader: GraphReader,\n executor?: MigrationExecutor,\n): Promise<GraphRegistry> {\n const [nodeTypes, edgeTypes] = await Promise.all([\n reader.findNodes({ aType: META_NODE_TYPE }),\n reader.findNodes({ aType: META_EDGE_TYPE }),\n ]);\n\n const entries: RegistryEntry[] = [...BOOTSTRAP_ENTRIES];\n\n // Eagerly pre-validate all migration sources in the sandbox before building\n // the registry. This ensures reloadRegistry() fails fast on invalid sources.\n const prevalidations: Promise<void>[] = [];\n for (const record of nodeTypes) {\n const data = record.data as unknown as NodeTypeData;\n if (data.migrations) {\n for (const m of data.migrations) {\n prevalidations.push(precompileSource(m.up, executor));\n }\n }\n }\n for (const record of edgeTypes) {\n const data = record.data as unknown as EdgeTypeData;\n if (data.migrations) {\n for (const m of data.migrations) {\n prevalidations.push(precompileSource(m.up, executor));\n }\n }\n }\n await Promise.all(prevalidations);\n\n // Convert nodeType records → self-loop RegistryEntries\n for (const record of nodeTypes) {\n const data = record.data as unknown as NodeTypeData;\n entries.push({\n aType: data.name,\n axbType: NODE_RELATION,\n bType: data.name,\n jsonSchema: data.jsonSchema,\n description: data.description,\n titleField: data.titleField,\n subtitleField: data.subtitleField,\n allowedIn: data.allowedIn,\n migrations: data.migrations ? compileMigrations(data.migrations, executor) : undefined,\n migrationWriteBack: data.migrationWriteBack,\n });\n }\n\n // Convert edgeType records → RegistryEntries (expand from/to arrays)\n for (const record of edgeTypes) {\n const data = record.data as unknown as EdgeTypeData;\n const fromTypes = Array.isArray(data.from) ? data.from : [data.from];\n const toTypes = Array.isArray(data.to) ? data.to : [data.to];\n\n const compiledMigrations = data.migrations\n ? compileMigrations(data.migrations, executor)\n : undefined;\n\n for (const aType of fromTypes) {\n for (const bType of toTypes) {\n entries.push({\n aType,\n axbType: data.name,\n bType,\n jsonSchema: data.jsonSchema,\n description: data.description,\n inverseLabel: data.inverseLabel,\n titleField: data.titleField,\n subtitleField: data.subtitleField,\n allowedIn: data.allowedIn,\n targetGraph: data.targetGraph,\n migrations: compiledMigrations,\n migrationWriteBack: data.migrationWriteBack,\n });\n }\n }\n }\n\n return createRegistry(entries);\n}\n","/**\n * JSON Schema validation and introspection utilities.\n *\n * Standard JSON Schema validation and introspection\n * processing. Uses ajv for validation and a recursive walker for converting\n * JSON Schema properties into FieldMeta objects for editor form generation.\n */\n\nimport Ajv from 'ajv';\nimport addFormats from 'ajv-formats';\n\nimport { ValidationError } from './errors.js';\n\n// ---------------------------------------------------------------------------\n// FieldMeta types (previously in editor/server/schema-introspect.ts)\n// ---------------------------------------------------------------------------\n\nexport interface FieldMeta {\n name: string;\n type: 'string' | 'number' | 'boolean' | 'enum' | 'array' | 'object' | 'unknown';\n required: boolean;\n description?: string;\n enumValues?: string[];\n minLength?: number;\n maxLength?: number;\n pattern?: string;\n min?: number;\n max?: number;\n isInt?: boolean;\n itemMeta?: FieldMeta;\n fields?: FieldMeta[];\n}\n\n// ---------------------------------------------------------------------------\n// Validation\n// ---------------------------------------------------------------------------\n\nconst ajv = new Ajv({ allErrors: true, strict: false });\naddFormats(ajv);\n\n/**\n * Compile a JSON Schema into a validation function.\n * The returned function throws `ValidationError` if data is invalid.\n */\nexport function compileSchema(schema: object, label?: string): (data: unknown) => void {\n const validate = ajv.compile(schema);\n return (data: unknown) => {\n if (!validate(data)) {\n const errors = validate.errors ?? [];\n const messages = errors\n .map((err) => `${err.instancePath || '/'}${err.message ? ': ' + err.message : ''}`)\n .join('; ');\n throw new ValidationError(\n `Data validation failed${label ? ' for ' + label : ''}: ${messages}`,\n errors,\n );\n }\n };\n}\n\n// ---------------------------------------------------------------------------\n// JSON Schema → FieldMeta introspection\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a JSON Schema (expected to be `type: \"object\"`) into `FieldMeta[]`\n * suitable for the editor's SchemaForm component.\n */\nexport function jsonSchemaToFieldMeta(schema: any): FieldMeta[] {\n if (!schema || schema.type !== 'object' || !schema.properties) return [];\n\n const requiredSet = new Set<string>(Array.isArray(schema.required) ? schema.required : []);\n\n return Object.entries(schema.properties).map(([name, prop]) =>\n propertyToFieldMeta(name, prop as any, requiredSet.has(name)),\n );\n}\n\n/**\n * Convert a single JSON Schema property into a `FieldMeta`.\n */\nfunction propertyToFieldMeta(name: string, prop: any, required: boolean): FieldMeta {\n if (!prop) return { name, type: 'unknown', required };\n\n // Handle enum (can appear with or without type)\n if (Array.isArray(prop.enum)) {\n return {\n name,\n type: 'enum',\n required,\n enumValues: prop.enum as string[],\n description: prop.description,\n };\n }\n\n // Handle oneOf/anyOf for nullable patterns like { oneOf: [{type:'string'}, {type:'null'}] }\n if (Array.isArray(prop.oneOf) || Array.isArray(prop.anyOf)) {\n const variants = (prop.oneOf ?? prop.anyOf) as any[];\n const nonNull = variants.filter((v: any) => v.type !== 'null');\n if (nonNull.length === 1) {\n // Nullable wrapper — unwrap and mark as optional\n return propertyToFieldMeta(name, nonNull[0], false);\n }\n return { name, type: 'unknown', required, description: prop.description };\n }\n\n const type = prop.type;\n\n if (type === 'string') {\n return {\n name,\n type: 'string',\n required,\n minLength: prop.minLength,\n maxLength: prop.maxLength,\n pattern: prop.pattern,\n description: prop.description,\n };\n }\n\n if (type === 'number' || type === 'integer') {\n return {\n name,\n type: 'number',\n required,\n min: prop.minimum,\n max: prop.maximum,\n isInt: type === 'integer' ? true : undefined,\n description: prop.description,\n };\n }\n\n if (type === 'boolean') {\n return { name, type: 'boolean', required, description: prop.description };\n }\n\n if (type === 'array') {\n const itemMeta = prop.items ? propertyToFieldMeta('item', prop.items, true) : undefined;\n return {\n name,\n type: 'array',\n required,\n itemMeta,\n description: prop.description,\n };\n }\n\n if (type === 'object') {\n return {\n name,\n type: 'object',\n required,\n fields: jsonSchemaToFieldMeta(prop),\n description: prop.description,\n };\n }\n\n return { name, type: 'unknown', required, description: prop.description };\n}\n","/**\n * Migration pipeline for auto-migrating records on read.\n *\n * When a record's `v` is behind the version derived from the registry\n * entry's migrations, the pipeline applies migration steps sequentially\n * to bring the data up to the current version.\n */\n\nimport { MigrationError } from './errors.js';\nimport type {\n GraphRegistry,\n MigrationStep,\n MigrationWriteBack,\n StoredGraphRecord,\n} from './types.js';\n\n/** Result of attempting to migrate a single record. */\nexport interface MigrationResult {\n record: StoredGraphRecord;\n migrated: boolean;\n /** Resolved write-back mode for this record (entry-level > global > 'off'). */\n writeBack: MigrationWriteBack;\n}\n\n/**\n * Apply a chain of migration steps to transform data from `currentVersion`\n * to `targetVersion`. Throws `MigrationError` if the chain is incomplete\n * or a migration function fails.\n *\n * Returns the migrated data payload only — the caller is responsible for\n * stamping `v` on the record envelope.\n */\nexport async function applyMigrationChain(\n data: Record<string, unknown>,\n currentVersion: number,\n targetVersion: number,\n migrations: MigrationStep[],\n): Promise<Record<string, unknown>> {\n const sorted = [...migrations].sort((a, b) => a.fromVersion - b.fromVersion);\n let result = { ...data };\n let version = currentVersion;\n\n for (const step of sorted) {\n if (step.fromVersion === version) {\n try {\n result = await step.up(result);\n } catch (err: unknown) {\n if (err instanceof MigrationError) throw err;\n throw new MigrationError(\n `Migration from v${step.fromVersion} to v${step.toVersion} failed: ${(err as Error).message}`,\n );\n }\n if (!result || typeof result !== 'object') {\n throw new MigrationError(\n `Migration from v${step.fromVersion} to v${step.toVersion} returned invalid data (expected object)`,\n );\n }\n version = step.toVersion;\n }\n }\n\n if (version !== targetVersion) {\n throw new MigrationError(\n `Incomplete migration chain: reached v${version} but target is v${targetVersion}`,\n );\n }\n\n return result;\n}\n\n/**\n * Validate that a migration chain forms a contiguous path from version 0\n * to the highest `toVersion`. Throws `MigrationError` if the chain has\n * gaps or duplicate `fromVersion` values.\n *\n * Called at registry construction time to catch incomplete chains early,\n * rather than at read time when a record is migrated.\n */\nexport function validateMigrationChain(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 { computeEdgeDocId } from './docid.js';\nimport { InvalidQueryError } from './errors.js';\nimport { BUILTIN_FIELDS, DEFAULT_QUERY_LIMIT, NODE_RELATION } from './internal/constants.js';\nimport type { FindEdgesParams, FindNodesParams, QueryFilter, QueryPlan } from './types.js';\n\nexport function buildEdgeQueryPlan(params: FindEdgesParams): QueryPlan {\n const { aType, aUid, axbType, bType, bUid, limit, orderBy } = params;\n\n if (aUid && axbType && bUid && !params.where?.length) {\n return { strategy: 'get', docId: computeEdgeDocId(aUid, axbType, bUid) };\n }\n\n const filters: QueryFilter[] = [];\n\n if (aType) filters.push({ field: 'aType', op: '==', value: aType });\n if (aUid) filters.push({ field: 'aUid', op: '==', value: aUid });\n if (axbType) filters.push({ field: 'axbType', op: '==', value: axbType });\n if (bType) filters.push({ field: 'bType', op: '==', value: bType });\n if (bUid) filters.push({ field: 'bUid', op: '==', value: bUid });\n\n if (params.where) {\n for (const clause of params.where) {\n const field = BUILTIN_FIELDS.has(clause.field)\n ? clause.field\n : clause.field.startsWith('data.')\n ? clause.field\n : `data.${clause.field}`;\n filters.push({ field, op: clause.op, value: clause.value });\n }\n }\n\n if (filters.length === 0) {\n throw new InvalidQueryError('findEdges requires at least one filter parameter');\n }\n\n // limit: undefined → apply DEFAULT_QUERY_LIMIT\n // limit: 0 → no limit (unlimited, used by internal bulk operations)\n // limit: N → use N\n const effectiveLimit = limit === undefined ? DEFAULT_QUERY_LIMIT : limit || undefined;\n return { strategy: 'query', filters, options: { limit: effectiveLimit, orderBy } };\n}\n\nexport function buildNodeQueryPlan(params: FindNodesParams): QueryPlan {\n const { aType, limit, orderBy } = params;\n\n const filters: QueryFilter[] = [\n { field: 'aType', op: '==', value: aType },\n { field: 'axbType', op: '==', value: NODE_RELATION },\n ];\n\n if (params.where) {\n for (const clause of params.where) {\n const field = BUILTIN_FIELDS.has(clause.field)\n ? clause.field\n : clause.field.startsWith('data.')\n ? clause.field\n : `data.${clause.field}`;\n filters.push({ field, op: clause.op, value: clause.value });\n }\n }\n\n const effectiveLimit = limit === undefined ? DEFAULT_QUERY_LIMIT : limit || undefined;\n return { strategy: 'query', filters, options: { limit: effectiveLimit, orderBy } };\n}\n","import { BUILTIN_FIELDS } from './internal/constants.js';\nimport type { QueryFilter } from './types.js';\n\n/**\n * Result of analyzing a query for collection scan risk.\n */\nexport interface QuerySafetyResult {\n /** Whether the query matches a known indexed pattern. */\n safe: boolean;\n /** Human-readable explanation when the query is unsafe. */\n reason?: string;\n}\n\n/**\n * Known composite index patterns that prevent full collection scans.\n * Each pattern is a set of field names that must ALL be present in the\n * query filters. Order within the set doesn't matter — what matters is\n * that the Firestore composite index covers the combination.\n *\n * These correspond to the indexes in firestore.indexes.json:\n * (aUid, axbType) — forward edge lookup\n * (axbType, bUid) — reverse edge lookup\n * (aType, axbType) — type-scoped queries + findNodes\n * (axbType, bType) — edge type + target type\n */\nconst SAFE_INDEX_PATTERNS: ReadonlyArray<ReadonlySet<string>> = [\n new Set(['aUid', 'axbType']),\n new Set(['axbType', 'bUid']),\n new Set(['aType', 'axbType']),\n new Set(['axbType', 'bType']),\n];\n\n/**\n * Analyzes a set of query filters to determine whether the query would\n * likely cause a full collection scan on Firestore Enterprise.\n *\n * A query is considered \"safe\" if the builtin fields present in the filters\n * match at least one known composite index pattern. Queries that only use\n * `data.*` fields without a safe base pattern are flagged as unsafe.\n */\nexport function analyzeQuerySafety(filters: QueryFilter[]): QuerySafetyResult {\n // Extract the set of builtin fields being filtered on (equality checks are\n // the primary index-usable operations, but we're generous here and count\n // any filter on a builtin field as potentially index-backed).\n const builtinFieldsPresent = new Set<string>();\n let hasDataFilters = false;\n\n for (const f of filters) {\n if (BUILTIN_FIELDS.has(f.field)) {\n builtinFieldsPresent.add(f.field);\n } else {\n // data.* or other non-builtin fields\n hasDataFilters = true;\n }\n }\n\n // Check if the builtin fields match any known safe index pattern.\n // A pattern is \"matched\" if all fields in the pattern are present in the query.\n for (const pattern of SAFE_INDEX_PATTERNS) {\n let matched = true;\n for (const field of pattern) {\n if (!builtinFieldsPresent.has(field)) {\n matched = false;\n break;\n }\n }\n if (matched) {\n // Even with data.* filters, the base index narrows the scan significantly.\n // The data.* filters are applied as post-filters on the index results.\n return { safe: true };\n }\n }\n\n // No safe pattern matched — build an explanation.\n const presentFields = [...builtinFieldsPresent];\n if (presentFields.length === 0 && hasDataFilters) {\n return {\n safe: false,\n reason:\n 'Query filters only use data.* fields with no builtin field constraints. ' +\n 'This requires a full collection scan. Add aType, aUid, axbType, bType, or bUid filters, ' +\n 'or set allowCollectionScan: true.',\n };\n }\n\n if (hasDataFilters) {\n return {\n safe: false,\n reason:\n `Query filters on [${presentFields.join(', ')}] do not match any indexed pattern. ` +\n 'data.* filters without an indexed base require a full collection scan. ' +\n `Safe patterns: (aUid + axbType), (axbType + bUid), (aType + axbType), (axbType + bType). ` +\n 'Set allowCollectionScan: true to override.',\n };\n }\n\n return {\n safe: false,\n reason:\n `Query filters on [${presentFields.join(', ')}] do not match any indexed pattern. ` +\n 'This may cause a full collection scan on Firestore Enterprise. ' +\n `Safe patterns: (aUid + axbType), (axbType + bUid), (aType + axbType), (axbType + bType). ` +\n 'Set allowCollectionScan: true to override.',\n };\n}\n","import { computeEdgeDocId, computeNodeDocId } from './docid.js';\nimport { QuerySafetyError } from './errors.js';\nimport type { TransactionBackend, WritableRecord } from './internal/backend.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport { migrateRecord, migrateRecords } from './migration.js';\nimport { buildEdgeQueryPlan, buildNodeQueryPlan } from './query.js';\nimport { analyzeQuerySafety } from './query-safety.js';\nimport type {\n FindEdgesParams,\n FindNodesParams,\n GraphRegistry,\n GraphTransaction,\n MigrationWriteBack,\n QueryFilter,\n ScanProtection,\n StoredGraphRecord,\n} from './types.js';\n\nfunction buildWritableNodeRecord(\n aType: string,\n uid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid: uid, axbType: NODE_RELATION, bType: aType, bUid: uid, data };\n}\n\nfunction buildWritableEdgeRecord(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid, axbType, bType, bUid, data };\n}\n\nexport class GraphTransactionImpl implements GraphTransaction {\n constructor(\n private readonly backend: TransactionBackend,\n private readonly registry?: GraphRegistry,\n private readonly scanProtection: ScanProtection = 'error',\n private readonly scopePath: string = '',\n private readonly globalWriteBack: MigrationWriteBack = 'off',\n ) {}\n\n async getNode(uid: string): Promise<StoredGraphRecord | null> {\n const docId = computeNodeDocId(uid);\n const record = await this.backend.getDoc(docId);\n if (!record || !this.registry) return record;\n const result = await migrateRecord(record, this.registry, this.globalWriteBack);\n if (result.migrated && result.writeBack !== 'off') {\n await this.backend.updateDoc(docId, {\n replaceData: result.record.data as Record<string, unknown>,\n v: result.record.v,\n });\n }\n return result.record;\n }\n\n async getEdge(aUid: string, axbType: string, bUid: string): Promise<StoredGraphRecord | null> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = await this.backend.getDoc(docId);\n if (!record || !this.registry) return record;\n const result = await migrateRecord(record, this.registry, this.globalWriteBack);\n if (result.migrated && result.writeBack !== 'off') {\n await this.backend.updateDoc(docId, {\n replaceData: result.record.data as Record<string, unknown>,\n v: result.record.v,\n });\n }\n return result.record;\n }\n\n async edgeExists(aUid: string, axbType: string, bUid: string): Promise<boolean> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = await this.backend.getDoc(docId);\n return record !== null;\n }\n\n private checkQuerySafety(filters: QueryFilter[], allowCollectionScan?: boolean): void {\n if (allowCollectionScan || this.scanProtection === 'off') return;\n\n const result = analyzeQuerySafety(filters);\n if (result.safe) return;\n\n if (this.scanProtection === 'error') {\n throw new QuerySafetyError(result.reason!);\n }\n\n console.warn(`[firegraph] Query safety warning: ${result.reason}`);\n }\n\n async findEdges(params: FindEdgesParams): Promise<StoredGraphRecord[]> {\n const plan = buildEdgeQueryPlan(params);\n let records: StoredGraphRecord[];\n if (plan.strategy === 'get') {\n const record = await this.backend.getDoc(plan.docId);\n records = record ? [record] : [];\n } else {\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n records = await this.backend.query(plan.filters, plan.options);\n }\n return this.applyMigrations(records);\n }\n\n async findNodes(params: FindNodesParams): Promise<StoredGraphRecord[]> {\n const plan = buildNodeQueryPlan(params);\n let records: StoredGraphRecord[];\n if (plan.strategy === 'get') {\n const record = await this.backend.getDoc(plan.docId);\n records = record ? [record] : [];\n } else {\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n records = await this.backend.query(plan.filters, plan.options);\n }\n return this.applyMigrations(records);\n }\n\n private async applyMigrations(records: StoredGraphRecord[]): Promise<StoredGraphRecord[]> {\n if (!this.registry || records.length === 0) return records;\n const results = await migrateRecords(records, this.registry, this.globalWriteBack);\n for (const result of results) {\n if (result.migrated && result.writeBack !== 'off') {\n const docId =\n result.record.axbType === NODE_RELATION\n ? computeNodeDocId(result.record.aUid)\n : computeEdgeDocId(result.record.aUid, result.record.axbType, result.record.bUid);\n await this.backend.updateDoc(docId, {\n replaceData: result.record.data as Record<string, unknown>,\n v: result.record.v,\n });\n }\n }\n return results.map((r) => r.record);\n }\n\n async putNode(aType: string, uid: string, data: Record<string, unknown>): Promise<void> {\n if (this.registry) {\n this.registry.validate(aType, NODE_RELATION, aType, data, this.scopePath);\n }\n const docId = computeNodeDocId(uid);\n const record = buildWritableNodeRecord(aType, uid, data);\n if (this.registry) {\n const entry = this.registry.lookup(aType, NODE_RELATION, aType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n await this.backend.setDoc(docId, record);\n }\n\n async putEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n if (this.registry) {\n this.registry.validate(aType, axbType, bType, data, this.scopePath);\n }\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = buildWritableEdgeRecord(aType, aUid, axbType, bType, bUid, data);\n if (this.registry) {\n const entry = this.registry.lookup(aType, axbType, bType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n await this.backend.setDoc(docId, record);\n }\n\n async updateNode(uid: string, data: Record<string, unknown>): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.updateDoc(docId, { dataFields: data });\n }\n\n async removeNode(uid: string): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.deleteDoc(docId);\n }\n\n async removeEdge(aUid: string, axbType: string, bUid: string): Promise<void> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n await this.backend.deleteDoc(docId);\n }\n}\n","import { GraphBatchImpl } from './batch.js';\nimport { computeEdgeDocId, computeNodeDocId } from './docid.js';\nimport {\n createBootstrapRegistry,\n createRegistryFromGraph,\n generateDeterministicUid,\n META_EDGE_TYPE,\n META_NODE_TYPE,\n} from './dynamic-registry.js';\nimport { DynamicRegistryError, FiregraphError, QuerySafetyError } from './errors.js';\nimport type { StorageBackend, WritableRecord } from './internal/backend.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport type { MigrationResult } from './migration.js';\nimport { migrateRecord, migrateRecords } from './migration.js';\nimport { buildEdgeQueryPlan, buildNodeQueryPlan } from './query.js';\nimport { analyzeQuerySafety } from './query-safety.js';\nimport { createMergedRegistry } from './registry.js';\nimport { precompileSource } from './sandbox.js';\nimport { GraphTransactionImpl } from './transaction.js';\nimport type {\n BulkOptions,\n BulkResult,\n CascadeResult,\n DefineTypeOptions,\n DynamicGraphClient,\n DynamicRegistryConfig,\n EdgeTopology,\n FindEdgesParams,\n FindNodesParams,\n GraphBatch,\n GraphClient,\n GraphClientOptions,\n GraphReader,\n GraphRegistry,\n GraphTransaction,\n MigrationExecutor,\n MigrationFn,\n MigrationWriteBack,\n QueryFilter,\n QueryOptions,\n ScanProtection,\n StoredGraphRecord,\n} from './types.js';\n\nconst RESERVED_TYPE_NAMES = new Set([META_NODE_TYPE, META_EDGE_TYPE]);\n\nfunction buildWritableNodeRecord(\n aType: string,\n uid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid: uid, axbType: NODE_RELATION, bType: aType, bUid: uid, data };\n}\n\nfunction buildWritableEdgeRecord(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid, axbType, bType, bUid, data };\n}\n\nexport class GraphClientImpl implements DynamicGraphClient {\n readonly scanProtection: ScanProtection;\n\n // Static mode\n private readonly staticRegistry?: GraphRegistry;\n\n // Dynamic mode\n private readonly dynamicConfig?: DynamicRegistryConfig;\n private readonly bootstrapRegistry?: GraphRegistry;\n private dynamicRegistry?: GraphRegistry;\n private readonly metaBackend?: StorageBackend;\n\n // Migration settings\n private readonly globalWriteBack: MigrationWriteBack;\n private readonly migrationSandbox?: MigrationExecutor;\n\n constructor(\n private readonly backend: StorageBackend,\n options?: GraphClientOptions,\n /** @internal Optional pre-built meta-backend (used by subgraph clones). */\n metaBackend?: StorageBackend,\n ) {\n this.globalWriteBack = options?.migrationWriteBack ?? 'off';\n this.migrationSandbox = options?.migrationSandbox;\n\n if (options?.registryMode) {\n this.dynamicConfig = options.registryMode;\n this.bootstrapRegistry = createBootstrapRegistry();\n if (options.registry) {\n this.staticRegistry = options.registry;\n }\n this.metaBackend = metaBackend;\n } else {\n this.staticRegistry = options?.registry;\n }\n\n this.scanProtection = options?.scanProtection ?? 'error';\n }\n\n // ---------------------------------------------------------------------------\n // Backend access (exposed for traversal helpers and subgraph cloning)\n // ---------------------------------------------------------------------------\n\n /** @internal */\n getBackend(): StorageBackend {\n return this.backend;\n }\n\n /**\n * 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 const registry = this.getRegistryForType(aType);\n if (registry) {\n registry.validate(aType, NODE_RELATION, aType, data, this.backend.scopePath);\n }\n const backend = this.getBackendForType(aType);\n const docId = computeNodeDocId(uid);\n const record = buildWritableNodeRecord(aType, uid, data);\n if (registry) {\n const entry = registry.lookup(aType, NODE_RELATION, aType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n await backend.setDoc(docId, record);\n }\n\n async putEdge(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n ): Promise<void> {\n const registry = this.getRegistryForType(aType);\n if (registry) {\n registry.validate(aType, axbType, bType, data, this.backend.scopePath);\n }\n const backend = this.getBackendForType(aType);\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n const record = buildWritableEdgeRecord(aType, aUid, axbType, bType, bUid, data);\n if (registry) {\n const entry = registry.lookup(aType, axbType, bType);\n if (entry?.schemaVersion && entry.schemaVersion > 0) {\n record.v = entry.schemaVersion;\n }\n }\n await backend.setDoc(docId, record);\n }\n\n async updateNode(uid: string, data: Record<string, unknown>): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.updateDoc(docId, { dataFields: data });\n }\n\n async removeNode(uid: string): Promise<void> {\n const docId = computeNodeDocId(uid);\n await this.backend.deleteDoc(docId);\n }\n\n async removeEdge(aUid: string, axbType: string, bUid: string): Promise<void> {\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n await this.backend.deleteDoc(docId);\n }\n\n // ---------------------------------------------------------------------------\n // Transactions & Batches\n // ---------------------------------------------------------------------------\n\n async runTransaction<T>(fn: (tx: GraphTransaction) => Promise<T>): Promise<T> {\n return this.backend.runTransaction(async (txBackend) => {\n const graphTx = new GraphTransactionImpl(\n txBackend,\n this.getCombinedRegistry(),\n this.scanProtection,\n this.backend.scopePath,\n this.globalWriteBack,\n );\n return fn(graphTx);\n });\n }\n\n batch(): GraphBatch {\n return new GraphBatchImpl(\n this.backend.createBatch(),\n this.getCombinedRegistry(),\n this.backend.scopePath,\n );\n }\n\n // ---------------------------------------------------------------------------\n // Subgraph\n // ---------------------------------------------------------------------------\n\n subgraph(parentNodeUid: string, name: string = 'graph'): GraphClient {\n if (!parentNodeUid || parentNodeUid.includes('/')) {\n throw new FiregraphError(\n `Invalid parentNodeUid for subgraph: \"${parentNodeUid}\". ` +\n 'Must be a non-empty string without \"/\".',\n 'INVALID_SUBGRAPH',\n );\n }\n if (name.includes('/')) {\n throw new FiregraphError(\n `Subgraph name must not contain \"/\": got \"${name}\". ` +\n 'Use chained .subgraph() calls for nested subgraphs.',\n 'INVALID_SUBGRAPH',\n );\n }\n\n const childBackend = this.backend.subgraph(parentNodeUid, name);\n\n return new GraphClientImpl(\n childBackend,\n {\n registry: this.getCombinedRegistry(),\n scanProtection: this.scanProtection,\n migrationWriteBack: this.globalWriteBack,\n migrationSandbox: this.migrationSandbox,\n },\n // Subgraphs do not have meta-backends; meta lives only at the root.\n );\n }\n\n // ---------------------------------------------------------------------------\n // Collection group query\n // ---------------------------------------------------------------------------\n\n async findEdgesGlobal(\n params: FindEdgesParams,\n collectionName?: string,\n ): Promise<StoredGraphRecord[]> {\n if (!this.backend.findEdgesGlobal) {\n throw new FiregraphError(\n 'findEdgesGlobal() is not supported by the current storage backend.',\n 'UNSUPPORTED_OPERATION',\n );\n }\n const plan = buildEdgeQueryPlan(params);\n if (plan.strategy === 'get') {\n throw new FiregraphError(\n 'findEdgesGlobal() requires a query, not a direct document lookup. ' +\n 'Omit one of aUid/axbType/bUid to force a query strategy.',\n 'INVALID_QUERY',\n );\n }\n this.checkQuerySafety(plan.filters, params.allowCollectionScan);\n const records = await this.backend.findEdgesGlobal(params, collectionName);\n return this.applyMigrations(records);\n }\n\n // ---------------------------------------------------------------------------\n // Bulk operations\n // ---------------------------------------------------------------------------\n\n async removeNodeCascade(uid: string, options?: BulkOptions): Promise<CascadeResult> {\n return this.backend.removeNodeCascade(uid, this, options);\n }\n\n async bulkRemoveEdges(params: FindEdgesParams, options?: BulkOptions): Promise<BulkResult> {\n return this.backend.bulkRemoveEdges(params, this, options);\n }\n\n // ---------------------------------------------------------------------------\n // Dynamic registry methods\n // ---------------------------------------------------------------------------\n\n async defineNodeType(\n name: string,\n jsonSchema: object,\n description?: string,\n options?: DefineTypeOptions,\n ): Promise<void> {\n if (!this.dynamicConfig) {\n throw new DynamicRegistryError(\n 'defineNodeType() is only available in dynamic registry mode. ' +\n 'Pass registryMode: { mode: \"dynamic\" } to createGraphClient().',\n );\n }\n\n if (RESERVED_TYPE_NAMES.has(name)) {\n throw new DynamicRegistryError(\n `Cannot define type \"${name}\": this name is reserved for the meta-registry.`,\n );\n }\n\n if (this.staticRegistry?.lookup(name, NODE_RELATION, name)) {\n throw new DynamicRegistryError(\n `Cannot define node type \"${name}\": already defined in the static registry.`,\n );\n }\n\n const uid = generateDeterministicUid(META_NODE_TYPE, name);\n const data: Record<string, unknown> = { name, jsonSchema };\n if (description !== undefined) data.description = description;\n if (options?.titleField !== undefined) data.titleField = options.titleField;\n if (options?.subtitleField !== undefined) data.subtitleField = options.subtitleField;\n if (options?.viewTemplate !== undefined) data.viewTemplate = options.viewTemplate;\n if (options?.viewCss !== undefined) data.viewCss = options.viewCss;\n if (options?.allowedIn !== undefined) data.allowedIn = options.allowedIn;\n if (options?.migrationWriteBack !== undefined)\n data.migrationWriteBack = options.migrationWriteBack;\n if (options?.migrations !== undefined) {\n data.migrations = await this.serializeMigrations(options.migrations);\n }\n\n await this.putNode(META_NODE_TYPE, uid, data);\n }\n\n async defineEdgeType(\n name: string,\n topology: EdgeTopology,\n jsonSchema?: object,\n description?: string,\n options?: DefineTypeOptions,\n ): Promise<void> {\n if (!this.dynamicConfig) {\n throw new DynamicRegistryError(\n 'defineEdgeType() is only available in dynamic registry mode. ' +\n 'Pass registryMode: { mode: \"dynamic\" } to createGraphClient().',\n );\n }\n\n if (RESERVED_TYPE_NAMES.has(name)) {\n throw new DynamicRegistryError(\n `Cannot define type \"${name}\": this name is reserved for the meta-registry.`,\n );\n }\n\n if (this.staticRegistry) {\n const fromTypes = Array.isArray(topology.from) ? topology.from : [topology.from];\n const toTypes = Array.isArray(topology.to) ? topology.to : [topology.to];\n for (const aType of fromTypes) {\n for (const bType of toTypes) {\n if (this.staticRegistry.lookup(aType, name, bType)) {\n throw new DynamicRegistryError(\n `Cannot define edge type \"${name}\" for (${aType}) -> (${bType}): already defined in the static registry.`,\n );\n }\n }\n }\n }\n\n const uid = generateDeterministicUid(META_EDGE_TYPE, name);\n const data: Record<string, unknown> = {\n name,\n from: topology.from,\n to: topology.to,\n };\n if (jsonSchema !== undefined) data.jsonSchema = jsonSchema;\n if (topology.inverseLabel !== undefined) data.inverseLabel = topology.inverseLabel;\n if (topology.targetGraph !== undefined) data.targetGraph = topology.targetGraph;\n if (description !== undefined) data.description = description;\n if (options?.titleField !== undefined) data.titleField = options.titleField;\n if (options?.subtitleField !== undefined) data.subtitleField = options.subtitleField;\n if (options?.viewTemplate !== undefined) data.viewTemplate = options.viewTemplate;\n if (options?.viewCss !== undefined) data.viewCss = options.viewCss;\n if (options?.allowedIn !== undefined) data.allowedIn = options.allowedIn;\n if (options?.migrationWriteBack !== undefined)\n data.migrationWriteBack = options.migrationWriteBack;\n if (options?.migrations !== undefined) {\n data.migrations = await this.serializeMigrations(options.migrations);\n }\n\n await this.putNode(META_EDGE_TYPE, uid, data);\n }\n\n async reloadRegistry(): Promise<void> {\n if (!this.dynamicConfig) {\n throw new DynamicRegistryError(\n 'reloadRegistry() is only available in dynamic registry mode. ' +\n 'Pass registryMode: { mode: \"dynamic\" } to createGraphClient().',\n );\n }\n\n const reader = this.createMetaReader();\n const dynamicOnly = await createRegistryFromGraph(reader, this.migrationSandbox);\n\n if (this.staticRegistry) {\n this.dynamicRegistry = createMergedRegistry(this.staticRegistry, dynamicOnly);\n } else {\n this.dynamicRegistry = dynamicOnly;\n }\n }\n\n private async serializeMigrations(\n migrations: Array<{ fromVersion: number; toVersion: number; up: MigrationFn | string }>,\n ): Promise<Array<{ fromVersion: number; toVersion: number; up: string }>> {\n const result = migrations.map((m) => {\n const source = typeof m.up === 'function' ? m.up.toString() : m.up;\n return { fromVersion: m.fromVersion, toVersion: m.toVersion, up: source };\n });\n await Promise.all(result.map((m) => precompileSource(m.up, this.migrationSandbox)));\n return result;\n }\n\n /**\n * Build a `GraphReader` over the meta-backend. If meta lives in the same\n * collection as the main backend, `this` is returned directly.\n */\n private createMetaReader(): GraphReader {\n if (!this.metaBackend) return this;\n\n const backend = this.metaBackend;\n\n const executeMetaQuery = (\n filters: QueryFilter[],\n options?: QueryOptions,\n ): Promise<StoredGraphRecord[]> => backend.query(filters, options);\n\n return {\n async getNode(uid: string): Promise<StoredGraphRecord | null> {\n return backend.getDoc(computeNodeDocId(uid));\n },\n async getEdge(\n aUid: string,\n axbType: string,\n bUid: string,\n ): Promise<StoredGraphRecord | null> {\n return backend.getDoc(computeEdgeDocId(aUid, axbType, bUid));\n },\n async edgeExists(aUid: string, axbType: string, bUid: string): Promise<boolean> {\n const record = await backend.getDoc(computeEdgeDocId(aUid, axbType, bUid));\n return record !== null;\n },\n async findEdges(params: FindEdgesParams): Promise<StoredGraphRecord[]> {\n const plan = buildEdgeQueryPlan(params);\n if (plan.strategy === 'get') {\n const record = await backend.getDoc(plan.docId);\n return record ? [record] : [];\n }\n return executeMetaQuery(plan.filters, plan.options);\n },\n async findNodes(params: FindNodesParams): Promise<StoredGraphRecord[]> {\n const plan = buildNodeQueryPlan(params);\n if (plan.strategy === 'get') {\n const record = await backend.getDoc(plan.docId);\n return record ? [record] : [];\n }\n return executeMetaQuery(plan.filters, plan.options);\n },\n };\n }\n}\n\n/**\n * Create a `GraphClient` backed by an arbitrary `StorageBackend`.\n *\n * Used by backend-specific factories (e.g. `createDOClient` in\n * `firegraph/cloudflare`) — most callers should use the higher-level\n * `createGraphClient(firestore, ...)` overload below for Firestore, or the\n * Cloudflare factory for DO-backed graphs.\n */\nexport function createGraphClientFromBackend(\n backend: StorageBackend,\n options?: GraphClientOptions,\n metaBackend?: StorageBackend,\n): GraphClient | DynamicGraphClient {\n return new GraphClientImpl(backend, options, metaBackend) as GraphClient | DynamicGraphClient;\n}\n","/**\n * `createDOClient` — the user-facing factory for the Cloudflare DO backend.\n *\n * Given a Durable Object namespace binding and a stable root key, returns a\n * `GraphClient` that speaks to a `FiregraphDO` instance. The root key is\n * hashed via `namespace.idFromName()` to derive the DO ID, so two clients\n * instantiated with the same key always reach the same DO — that's how we\n * achieve \"subgraphs are auto-provisioned\" without a separate allocation\n * step. Subsequent `.subgraph(uid, name)` calls derive child DO IDs from the\n * extended key chain (`${key}/${uid}/${name}`).\n *\n * ## What's supported\n *\n * - **Static registries.** Pass `registry` and every read/write validates\n * and migrates exactly like the Firestore/SQLite backends.\n * - **Dynamic registries.** Pass `registryMode: { mode: 'dynamic' }` and\n * call `defineNodeType` / `defineEdgeType` / `reloadRegistry` as with\n * any other backend. Meta-types live in the root DO by default; pass\n * `registryMode: { mode: 'dynamic', collection: 'meta-root' }` to put\n * them in a separately-addressed DO. Merged mode (static `registry`\n * plus `registryMode`) is also supported.\n * - **Cross-DO cascade.** `removeNodeCascade` consults the registry's\n * subgraph topology and wipes every descendant subgraph DO before\n * deleting the node. Requires a registry; without one it cascades\n * within the current DO only. Pass `{ deleteSubcollections: false }` to\n * keep the node removal but leave every descendant DO intact (mirrors\n * the Firestore/SQLite `bulk` option). In dynamic mode the accessor is\n * live, so cascading a node whose subgraph topology was just added via\n * `defineEdgeType` correctly fans out to the new descendants — **but\n * only after a `reloadRegistry()` call**. A cascade invoked between\n * `defineEdgeType` and `reloadRegistry` sees only the pre-define\n * topology and silently skips newly-declared subgraphs. This is the\n * same trade-off every dynamic-registry backend makes.\n * - **Static migrations** on registry entries (`migrations`,\n * `migrationWriteBack`, `migrationSandbox`) run in-process on the\n * Worker and don't cross the DO RPC boundary. The read-path migration\n * pipeline lives in `GraphClient`, not in `FiregraphDO`.\n *\n * ## Performance note on cascade\n *\n * Cross-DO cascade instantiates (via `namespace.get`) every declared child\n * subgraph DO even when it's empty — Durable Objects have no \"does this ID\n * exist\" primitive, and the cheapest way to tear a DO down is to issue one\n * RPC. For a node with N declared subgraph segments, expect N+1 RPCs per\n * cascade (one per child DO wipe, one for the parent). Topology width is\n * typically small and bounded by registry size, but keep it in mind for\n * wide fan-out designs.\n *\n * ## What's not supported\n *\n * - **Interactive transactions.** Would require pinning a SQLite\n * transaction open across async RPC calls — see `backend.ts` for the\n * rationale. Use `batch()` for atomic multi-write commits.\n * - **`findEdgesGlobal`.** Cross-DO collection-group queries don't map\n * onto \"one DO owns one subgraph's rows\" — each subgraph is a separate\n * DO with private SQLite and there's no namespace-wide catalog. The\n * method is intentionally undefined on the backend so `GraphClient`\n * surfaces a generic `UNSUPPORTED_OPERATION` error immediately, before\n * any query planning. Callers that need this should maintain an\n * application-level index DO or run an explicit traversal via\n * `client.subgraph(...)`.\n *\n * ## Binding example\n *\n * ```ts\n * // worker.ts\n * export { FiregraphDO } from '@typicalday/firegraph/cloudflare';\n *\n * export default {\n * async fetch(req: Request, env: Env) {\n * const client = createDOClient(env.GRAPH, 'main', { registry });\n * const project = await client.getNode('project', projectUid);\n * return Response.json(project);\n * },\n * };\n * ```\n *\n * ```toml\n * # wrangler.toml\n * [[durable_objects.bindings]]\n * name = \"GRAPH\"\n * class_name = \"FiregraphDO\"\n *\n * [[migrations]]\n * tag = \"v1\"\n * new_sqlite_classes = [\"FiregraphDO\"]\n * ```\n */\n\nimport type { GraphClientImpl } from '../client.js';\nimport { createGraphClientFromBackend } from '../client.js';\nimport { FiregraphError } from '../errors.js';\nimport type { StorageBackend } from '../internal/backend.js';\nimport type {\n DynamicGraphClient,\n DynamicRegistryConfig,\n GraphClient,\n GraphClientOptions,\n GraphRegistry,\n} from '../types.js';\nimport type { FiregraphNamespace } from './backend.js';\nimport { DORPCBackend } from './backend.js';\n\n/**\n * Options for `createDOClient`. Same shape as `GraphClientOptions`; the DO\n * backend does not expose a table label of its own — the DO owns its SQLite\n * schema (see `FiregraphDOOptions.table`, defaults to `'firegraph'`) and\n * that choice isn't surfaced through the client factory.\n */\nexport type DOClientOptions = GraphClientOptions;\n\n/**\n * Create a `GraphClient` backed by a `FiregraphDO` Durable Object.\n *\n * @param namespace The DO namespace binding (`env.GRAPH` in Worker code).\n * @param rootKey Stable name for the root graph's DO. The same value\n * always addresses the same DO — treat it as the graph's\n * identity. Subgraph DOs derive their names from this.\n * @param options Optional `GraphClientOptions` (registry, query mode,\n * `registryMode` for dynamic registries, etc.).\n * When `registryMode` is set the return type is\n * narrowed to `DynamicGraphClient`.\n */\nexport function createDOClient(\n namespace: FiregraphNamespace,\n rootKey: string,\n options: DOClientOptions & { registryMode: DynamicRegistryConfig },\n): DynamicGraphClient;\nexport function createDOClient(\n namespace: FiregraphNamespace,\n rootKey: string,\n options?: DOClientOptions,\n): GraphClient;\nexport function createDOClient(\n namespace: FiregraphNamespace,\n rootKey: string,\n options: DOClientOptions = {},\n): GraphClient | DynamicGraphClient {\n if (!rootKey || typeof rootKey !== 'string') {\n throw new FiregraphError(\n `createDOClient: rootKey must be a non-empty string, got ${JSON.stringify(rootKey)}.`,\n 'INVALID_ARGUMENT',\n );\n }\n if (rootKey.includes('/')) {\n // Subgraph chaining builds keys as `${rootKey}/${uid}/${name}`; a slash in\n // the root would make `${rootA}/uid/x` collide with `${rootB}/uid/x` if\n // `rootB === rootA + '/…'`. Keep the root a single opaque segment.\n throw new FiregraphError(\n `createDOClient: rootKey must not contain \"/\". Got: \"${rootKey}\".`,\n 'INVALID_ARGUMENT',\n );\n }\n\n // Forward-reference the client so the backend's registry accessor sees\n // whatever registry the client currently holds — including late updates\n // from `reloadRegistry()` in dynamic mode. The closure is invoked lazily\n // (only during `removeNodeCascade`) by design; any synchronous invocation\n // before `client` is assigned is a programming error in this module, not\n // a user-facing scenario — hence the throw rather than a silent\n // `undefined` return.\n let client: GraphClient | DynamicGraphClient | undefined;\n const registryAccessor = (): GraphRegistry | undefined => {\n if (!client) {\n throw new FiregraphError(\n 'createDOClient: registryAccessor fired before the client was assigned. ' +\n 'This indicates a programming error in the DO backend — the accessor must ' +\n 'only be invoked lazily from `removeNodeCascade`, never synchronously from ' +\n 'the `DORPCBackend` constructor.',\n 'INTERNAL',\n );\n }\n // `getRegistrySnapshot` is declared `@internal` on `GraphClientImpl`\n // and is not part of the public `GraphClient` interface. We cast\n // through `GraphClientImpl` so the call is type-checked; consumers\n // using a custom client wrapper would need to surface the same\n // accessor themselves.\n return (client as unknown as GraphClientImpl).getRegistrySnapshot();\n };\n\n // Factory used by `createSiblingClient` to spin up a peer root client\n // pointing at a different DO while reusing this call's namespace and\n // options. Carried on the backend so `createSiblingClient` can locate it\n // from any `GraphClient` (root or subgraph) without the caller having to\n // hold onto the original `createDOClient` arguments.\n //\n // We snapshot `options` (shallow copy) at capture time so that a later\n // mutation by the caller — e.g. setting `options.registry = newReg`\n // after construction — doesn't silently change sibling behaviour. Deep\n // cloning isn't needed: `GraphClientOptions` values are either primitives\n // or registries/functions the caller shouldn't mutate in place anyway.\n const siblingOptions: DOClientOptions = { ...options };\n const makeSiblingClient = (siblingRootKey: string): GraphClient | DynamicGraphClient =>\n createDOClient(namespace, siblingRootKey, siblingOptions);\n\n const backend = new DORPCBackend(namespace, {\n scopePath: '',\n storageKey: rootKey,\n registryAccessor,\n makeSiblingClient,\n });\n\n // Dynamic registry with an explicit meta-collection → spin up a second\n // DO for meta-types. By default (no `collection`, or `collection ===\n // rootKey`) meta-nodes live in the same DO as domain data; that's the\n // simpler bootstrap and avoids an extra DO when the caller doesn't need\n // the isolation.\n let metaBackend: StorageBackend | undefined;\n if (options.registryMode?.collection) {\n const metaKey = options.registryMode.collection;\n if (metaKey.includes('/')) {\n throw new FiregraphError(\n `createDOClient: registryMode.collection must not contain \"/\". Got: \"${metaKey}\".`,\n 'INVALID_ARGUMENT',\n );\n }\n if (metaKey !== rootKey) {\n metaBackend = new DORPCBackend(namespace, {\n scopePath: '',\n storageKey: metaKey,\n // Meta backend shares the accessor so its own `removeNodeCascade`\n // (unlikely, but safe) would also see the live registry. Sibling\n // factory is carried for consistency; there's no user-facing path\n // that creates a sibling from the meta backend, but it costs\n // nothing to keep the two backends in sync.\n registryAccessor,\n makeSiblingClient,\n });\n }\n }\n\n client = createGraphClientFromBackend(backend, options, metaBackend);\n return client;\n}\n\n/**\n * Construct a peer `GraphClient` that shares `client`'s DO namespace and\n * construction options but targets a different root DO (i.e. a different\n * root key — typically another tenant, workspace, or shard).\n *\n * This is the cheap, ergonomic way to talk to another root graph from\n * inside a single Worker request without re-plumbing `createDOClient`'s\n * arguments. The namespace binding plus the options snapshot captured at\n * the original `createDOClient` call (registry, query mode, migration\n * sandbox, `registryMode`, etc.) are inherited by the sibling.\n *\n * Works from any DO-backed client — root or subgraph. Passing a client\n * that wasn't produced by `createDOClient` (e.g. a Firestore-backed\n * client, or a `DORPCBackend` instantiated directly without the sibling\n * factory wired in) throws `UNSUPPORTED_OPERATION` with an explanation.\n *\n * ## Dynamic-registry caveat\n *\n * When the original client uses `registryMode: 'dynamic'`, siblings\n * inherit the *config* (so they're also dynamic clients) but NOT the\n * compiled runtime state. Meta-type nodes and `reloadRegistry()` results\n * are per-client; a sibling's `defineNodeType`/`defineEdgeType` calls go\n * to that sibling's own root DO, and its registry must be independently\n * populated and reloaded. If every tenant shares the same schema, pass a\n * static `registry` instead of dynamic mode — static registries ARE\n * inherited verbatim.\n *\n * @param client A client previously returned by `createDOClient`.\n * @param siblingRootKey Root key for the peer DO. Same validation rules\n * as `createDOClient`'s `rootKey`: non-empty,\n * no `/`.\n */\nexport function createSiblingClient(client: GraphClient, siblingRootKey: string): GraphClient;\nexport function createSiblingClient(\n client: DynamicGraphClient,\n siblingRootKey: string,\n): DynamicGraphClient;\nexport function createSiblingClient(\n client: GraphClient | DynamicGraphClient,\n siblingRootKey: string,\n): GraphClient | DynamicGraphClient {\n if (!siblingRootKey || typeof siblingRootKey !== 'string') {\n throw new FiregraphError(\n `createSiblingClient: siblingRootKey must be a non-empty string, got ${JSON.stringify(siblingRootKey)}.`,\n 'INVALID_ARGUMENT',\n );\n }\n if (siblingRootKey.includes('/')) {\n throw new FiregraphError(\n `createSiblingClient: siblingRootKey must not contain \"/\". Got: \"${siblingRootKey}\".`,\n 'INVALID_ARGUMENT',\n );\n }\n\n // `GraphClientImpl` exposes `getBackend()` as an `@internal` accessor.\n // Cast through it to read the backend; if the caller handed us a non-\n // firegraph client wrapper that's a programming error at their layer,\n // surfaced here as a clear error rather than an opaque property miss.\n //\n // We deliberately duck-type the backend (`typeof maker === 'function'`)\n // rather than using `instanceof DORPCBackend`: in monorepos with\n // duplicated `firegraph` copies a DO client built against copy A would\n // fail `instanceof` against copy B's class reference, even though every\n // other invariant holds. The duck-type check stays correct across module\n // boundaries — the only way `makeSiblingClient` exists on a backend is\n // if `createDOClient` wired it up.\n const impl = client as unknown as GraphClientImpl;\n const backend: StorageBackend | undefined =\n typeof impl.getBackend === 'function' ? impl.getBackend() : undefined;\n const maker =\n backend &&\n (\n backend as {\n makeSiblingClient?: (k: string) => GraphClient | DynamicGraphClient;\n }\n ).makeSiblingClient;\n if (typeof maker !== 'function') {\n throw new FiregraphError(\n 'createSiblingClient: the provided client is not backed by a DO client produced by `createDOClient`. ' +\n 'Sibling construction is only available for DO-backed clients.',\n 'UNSUPPORTED_OPERATION',\n );\n }\n\n return maker(siblingRootKey);\n}\n","/**\n * `FiregraphDO` — the Durable Object class that holds a single subgraph's\n * triples.\n *\n * The Cloudflare-native Firegraph design puts each subgraph in its own DO\n * instance: the root graph in one DO, `client.subgraph(uid, 'memories')` in\n * another, nested subgraphs in their own DOs, and so on. Each DO owns a\n * private flat SQLite database (`src/cloudflare/schema.ts`) — no `scope`\n * column, no shared table, no discriminator. The client routes to a\n * specific DO by hashing a stable name (`namespace.idFromName(storageKey)`);\n * on first RPC, the DO lazily materializes and runs the schema DDL.\n *\n * ## Using it in a Worker\n *\n * Bind the class in `wrangler.toml` and re-export it from the Worker entry:\n *\n * ```toml\n * [[durable_objects.bindings]]\n * name = \"GRAPH\"\n * class_name = \"FiregraphDO\"\n *\n * [[migrations]]\n * tag = \"v1\"\n * new_sqlite_classes = [\"FiregraphDO\"]\n * ```\n *\n * ```ts\n * // worker.ts\n * export { FiregraphDO } from '@typicalday/firegraph/cloudflare';\n * ```\n *\n * To add custom RPC methods, extend the class:\n *\n * ```ts\n * export class GraphDO extends FiregraphDO {\n * async myCustomRpc() { ... }\n * }\n * ```\n *\n * ## Why a plain class (not `extends DurableObject`)?\n *\n * Cloudflare accepts any class with the `(state, env)` constructor shape as\n * a DO class. Extending `DurableObject` from `cloudflare:workers` would pull\n * a runtime import into this module and prevent it from loading in Node\n * tests. The plain-class form keeps this file runtime-neutral — the only\n * Cloudflare thing we touch is `ctx.storage.sql`, typed via local minimal\n * interfaces.\n */\n\nimport { computeEdgeDocId, computeNodeDocId } from '../docid.js';\nimport { FiregraphError } from '../errors.js';\nimport type { UpdatePayload, WritableRecord } from '../internal/backend.js';\nimport { NODE_RELATION } from '../internal/constants.js';\nimport { buildEdgeQueryPlan } from '../query.js';\nimport type {\n BulkOptions,\n BulkResult,\n CascadeResult,\n FindEdgesParams,\n GraphRegistry,\n IndexSpec,\n QueryFilter,\n QueryOptions,\n} from '../types.js';\nimport { buildDOSchemaStatements, validateDOTableName } from './schema.js';\nimport type { CompiledStatement, DORecordWire } from './sql.js';\nimport {\n compileDODelete,\n compileDODeleteAll,\n compileDOSelect,\n compileDOSelectByDocId,\n compileDOSet,\n compileDOUpdate,\n rowToDORecord,\n} from './sql.js';\n\n// ---------------------------------------------------------------------------\n// Minimal DO runtime types — declared locally so this module doesn't depend\n// on `@cloudflare/workers-types`. Users importing the library get their own\n// `DurableObjectState` from workers-types; structurally the two shapes are\n// compatible.\n// ---------------------------------------------------------------------------\n\nexport interface DOSqlCursor<T> {\n toArray(): T[];\n}\n\nexport interface DOSqlExecutor {\n exec<T = Record<string, unknown>>(sql: string, ...params: unknown[]): DOSqlCursor<T>;\n}\n\nexport interface DOStorage {\n sql: DOSqlExecutor;\n transactionSync<T>(fn: () => T): T;\n}\n\nexport interface DurableObjectStateLike {\n readonly storage: DOStorage;\n blockConcurrencyWhile<T>(fn: () => Promise<T>): Promise<T>;\n}\n\n// ---------------------------------------------------------------------------\n// RPC wire shapes\n// ---------------------------------------------------------------------------\n\n/**\n * One op in a `batch()` RPC call. Discriminated by `kind` so the DO can\n * dispatch to the correct compiler.\n */\nexport type BatchOp =\n | { kind: 'set'; docId: string; record: WritableRecord }\n | { kind: 'update'; docId: string; update: UpdatePayload }\n | { kind: 'delete'; docId: string };\n\n/**\n * Options controlling `FiregraphDO` construction.\n */\nexport interface FiregraphDOOptions {\n /** Table name for firegraph triples. Default: `firegraph`. */\n table?: string;\n /** Run schema DDL on first boot. Default: `true`. */\n autoMigrate?: boolean;\n /**\n * Registry whose per-entry `indexes` get compiled into `CREATE INDEX`\n * statements during schema bootstrap. Supply the same registry you pass\n * to `createGraphClient` on the Worker side to keep DO and client in sync.\n */\n registry?: GraphRegistry;\n /**\n * Replaces the built-in core index preset\n * (`DEFAULT_CORE_INDEXES`). Supply this when the default set of\n * `(aUid, axbType)`, `(axbType, bUid)`, etc. composites doesn't fit your\n * query shapes — e.g., you want descending timestamps or a reduced set.\n * Entry-level `RegistryEntry.indexes` remain additive on top.\n *\n * Pass `[]` to disable core indexes entirely (advanced — only safe when\n * the provided `registry`'s entries cover every query shape your app\n * issues).\n */\n coreIndexes?: IndexSpec[];\n}\n\n// ---------------------------------------------------------------------------\n// FiregraphDO\n// ---------------------------------------------------------------------------\n\n/**\n * Default `FiregraphDO` options, used when a subclass calls `super(ctx, env)`\n * without passing options. Overridable in subclasses via constructor args.\n *\n * Only fields with a universal sensible default go here — optional index\n * and registry wiring is `undefined` by default and threaded through\n * `runSchema` as-is.\n */\nconst DEFAULT_OPTIONS: Required<Pick<FiregraphDOOptions, 'table' | 'autoMigrate'>> = {\n table: 'firegraph',\n autoMigrate: true,\n};\n\nexport class FiregraphDO {\n /** @internal — exposed for subclass access, not part of the public RPC. */\n protected readonly ctx: DurableObjectStateLike;\n /** @internal — exposed for subclass access; opaque to this class. */\n protected readonly env: unknown;\n /** @internal — table name used by every compiled statement. */\n protected readonly table: string;\n /** @internal — registry consulted by `runSchema` for per-entry indexes. */\n protected readonly registry?: GraphRegistry;\n /** @internal — overrides `DEFAULT_CORE_INDEXES` when set. */\n protected readonly coreIndexes?: IndexSpec[];\n\n constructor(ctx: DurableObjectStateLike, env: unknown, options: FiregraphDOOptions = {}) {\n this.ctx = ctx;\n this.env = env;\n const table = options.table ?? DEFAULT_OPTIONS.table;\n validateDOTableName(table);\n this.table = table;\n this.registry = options.registry;\n this.coreIndexes = options.coreIndexes;\n\n const autoMigrate = options.autoMigrate ?? DEFAULT_OPTIONS.autoMigrate;\n if (autoMigrate) {\n // `blockConcurrencyWhile` defers any incoming RPC until the schema is\n // in place. Without it a fast first caller could run a query against an\n // empty database before the CREATE TABLE lands.\n //\n // Fire-and-forget is safe: the DO runtime internally tracks and awaits\n // the returned promise, holding the RPC input queue until the schema\n // materializes. We don't need to `await` it from the constructor (which\n // can't be async anyway) — the next incoming RPC already waits.\n void this.ctx.blockConcurrencyWhile(async () => {\n this.runSchema();\n });\n }\n }\n\n // ---------------------------------------------------------------------------\n // RPC: reads\n //\n // Method names are prefixed `_fg` so user subclasses can add their own RPC\n // methods without name collisions. The client-side backend in\n // `src/cloudflare/backend.ts` calls these directly on the DO stub.\n // ---------------------------------------------------------------------------\n\n async _fgGetDoc(docId: string): Promise<DORecordWire | null> {\n const stmt = compileDOSelectByDocId(this.table, docId);\n const rows = this.execAll(stmt);\n return rows.length === 0 ? null : rowToDORecord(rows[0]);\n }\n\n async _fgQuery(filters: QueryFilter[], options?: QueryOptions): Promise<DORecordWire[]> {\n const stmt = compileDOSelect(this.table, filters, options);\n const rows = this.execAll(stmt);\n return rows.map(rowToDORecord);\n }\n\n // ---------------------------------------------------------------------------\n // RPC: writes\n // ---------------------------------------------------------------------------\n\n async _fgSetDoc(docId: string, record: WritableRecord): Promise<void> {\n const stmt = compileDOSet(this.table, docId, record, Date.now());\n this.execRun(stmt);\n }\n\n async _fgUpdateDoc(docId: string, update: UpdatePayload): Promise<void> {\n const stmt = compileDOUpdate(this.table, docId, update, Date.now());\n // RETURNING lets us surface NOT_FOUND at the client, matching Firestore's\n // `update()` semantics. SQLite ≥3.35 supports UPDATE … RETURNING and DO\n // SQLite is always recent enough.\n const sqlWithReturning = `${stmt.sql} RETURNING \"doc_id\"`;\n const rows = this.ctx.storage.sql\n .exec<Record<string, unknown>>(sqlWithReturning, ...stmt.params)\n .toArray();\n if (rows.length === 0) {\n throw new FiregraphError(`updateDoc: no document found for doc_id=${docId}`, 'NOT_FOUND');\n }\n }\n\n async _fgDeleteDoc(docId: string): Promise<void> {\n const stmt = compileDODelete(this.table, docId);\n this.execRun(stmt);\n }\n\n // ---------------------------------------------------------------------------\n // RPC: batch\n // ---------------------------------------------------------------------------\n\n /**\n * Execute a list of write ops atomically. DO SQLite's `transactionSync`\n * provides real atomicity — either every statement commits or none do.\n * No statement-count cap applies (contrast with D1's ~100-statement batch\n * limit), so the caller can submit as many ops as they like in one call.\n */\n async _fgBatch(ops: BatchOp[]): Promise<void> {\n if (ops.length === 0) return;\n const now = Date.now();\n const statements: CompiledStatement[] = ops.map((op) => {\n switch (op.kind) {\n case 'set':\n return compileDOSet(this.table, op.docId, op.record, now);\n case 'update':\n return compileDOUpdate(this.table, op.docId, op.update, now);\n case 'delete':\n return compileDODelete(this.table, op.docId);\n }\n });\n this.ctx.storage.transactionSync(() => {\n for (const stmt of statements) {\n this.ctx.storage.sql.exec(stmt.sql, ...stmt.params).toArray();\n }\n });\n }\n\n // ---------------------------------------------------------------------------\n // RPC: cascade + bulk (local DO only)\n //\n // These cascade *within this DO*. Subgraph DOs (nested under this node) are\n // not reachable from here — the client-side `DORPCBackend.removeNodeCascade`\n // consults the registry topology to discover descendant subgraph DOs and\n // fans out explicit `_fgDestroy` calls to each before invoking this method.\n // Without that topology the DO has no way to enumerate its children.\n // ---------------------------------------------------------------------------\n\n async _fgRemoveNodeCascade(uid: string): Promise<CascadeResult> {\n // Gather every edge whose aUid or bUid matches the node. The self-loop\n // (node record) is identified separately so we can report it distinctly\n // in `CascadeResult` — and so we don't falsely claim `nodeDeleted: true`\n // when the node never existed.\n const outgoingStmt = compileDOSelect(this.table, [{ field: 'aUid', op: '==', value: uid }]);\n const incomingStmt = compileDOSelect(this.table, [{ field: 'bUid', op: '==', value: uid }]);\n const outgoingRows = this.execAll(outgoingStmt);\n const incomingRows = this.execAll(incomingStmt);\n\n const seen = new Set<string>();\n const edgeDocIds: string[] = [];\n let nodeExists = false;\n for (const row of [...outgoingRows, ...incomingRows]) {\n const axbType = row.axb_type as string;\n const aUid = row.a_uid as string;\n const bUid = row.b_uid as string;\n if (axbType === NODE_RELATION && aUid === bUid) {\n nodeExists = true;\n continue;\n }\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n if (!seen.has(docId)) {\n seen.add(docId);\n edgeDocIds.push(docId);\n }\n }\n\n const statements: CompiledStatement[] = edgeDocIds.map((id) => compileDODelete(this.table, id));\n // Only queue the node delete if the self-loop was actually present; a\n // cascade on a nonexistent node returns `nodeDeleted: false` without\n // a wasted DELETE. Orphan edges still get cleaned up either way.\n if (nodeExists) {\n statements.push(compileDODelete(this.table, computeNodeDocId(uid)));\n }\n\n if (statements.length === 0) {\n return {\n deleted: 0,\n batches: 0,\n errors: [],\n edgesDeleted: 0,\n nodeDeleted: false,\n };\n }\n\n try {\n this.ctx.storage.transactionSync(() => {\n for (const stmt of statements) {\n this.ctx.storage.sql.exec(stmt.sql, ...stmt.params).toArray();\n }\n });\n return {\n deleted: statements.length,\n batches: 1,\n errors: [],\n edgesDeleted: edgeDocIds.length,\n nodeDeleted: nodeExists,\n };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return {\n deleted: 0,\n batches: 0,\n errors: [{ batchIndex: 0, error, operationCount: statements.length }],\n edgesDeleted: 0,\n nodeDeleted: false,\n };\n }\n }\n\n async _fgBulkRemoveEdges(params: FindEdgesParams, _options?: BulkOptions): Promise<BulkResult> {\n // Resolve the set of doc IDs to delete. For a fully-specified query\n // (`get` plan) the planner hands us the doc ID directly — we still\n // verify existence so the returned count reflects reality. For partial\n // queries we run the SELECT and collect every matching edge's doc ID.\n // `allowCollectionScan` / scan protection are deliberately ignored at\n // this layer — the client has already authorized the delete.\n const plan = buildEdgeQueryPlan(params);\n let docIds: string[];\n if (plan.strategy === 'get') {\n const existsStmt = compileDOSelectByDocId(this.table, plan.docId);\n const rows = this.execAll(existsStmt);\n docIds = rows.length > 0 ? [plan.docId] : [];\n } else {\n const selectStmt = compileDOSelect(this.table, plan.filters, plan.options);\n const rows = this.execAll(selectStmt);\n docIds = rows.map((row) =>\n computeEdgeDocId(row.a_uid as string, row.axb_type as string, row.b_uid as string),\n );\n }\n\n if (docIds.length === 0) {\n return { deleted: 0, batches: 0, errors: [] };\n }\n\n const deleteStmts = docIds.map((id) => compileDODelete(this.table, id));\n try {\n this.ctx.storage.transactionSync(() => {\n for (const stmt of deleteStmts) {\n this.ctx.storage.sql.exec(stmt.sql, ...stmt.params).toArray();\n }\n });\n return { deleted: deleteStmts.length, batches: 1, errors: [] };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return {\n deleted: 0,\n batches: 0,\n errors: [{ batchIndex: 0, error, operationCount: deleteStmts.length }],\n };\n }\n }\n\n // ---------------------------------------------------------------------------\n // RPC: admin\n // ---------------------------------------------------------------------------\n\n /**\n * Wipe every row. Called by the client when tearing down a subgraph DO as\n * part of cascade — the DO itself can't be destroyed (DO IDs persist\n * forever), but its storage can be emptied.\n */\n async _fgDestroy(): Promise<void> {\n const stmt = compileDODeleteAll(this.table);\n this.execRun(stmt);\n }\n\n // ---------------------------------------------------------------------------\n // Internals\n // ---------------------------------------------------------------------------\n\n protected runSchema(): void {\n const statements = buildDOSchemaStatements(this.table, {\n coreIndexes: this.coreIndexes,\n registry: this.registry,\n });\n for (const sql of statements) {\n this.ctx.storage.sql.exec(sql).toArray();\n }\n }\n\n private execAll(stmt: CompiledStatement): Record<string, unknown>[] {\n return this.ctx.storage.sql.exec<Record<string, unknown>>(stmt.sql, ...stmt.params).toArray();\n }\n\n private execRun(stmt: CompiledStatement): void {\n // DO SQL `exec` returns a cursor even for writes; consuming it via\n // `toArray()` forces execution and surfaces constraint errors\n // synchronously.\n this.ctx.storage.sql.exec(stmt.sql, ...stmt.params).toArray();\n }\n}\n"],"mappings":"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","names":["IDENT_RE","JSON_PATH_KEY_RE","import_node_crypto","Ajv","addFormats","import_node_crypto","serializeFirestoreTypes","deserializeFirestoreTypes","buildWritableNodeRecord","buildWritableEdgeRecord","buildWritableNodeRecord","buildWritableEdgeRecord"]}
1
+ {"version":3,"sources":["../../src/internal/serialization-tag.ts","../../src/serialization.ts","../../src/cloudflare/index.ts","../../src/errors.ts","../../src/internal/constants.ts","../../src/internal/sqlite-data-ops.ts","../../src/internal/sqlite-payload-guard.ts","../../src/internal/write-plan.ts","../../src/timestamp.ts","../../src/default-indexes.ts","../../src/internal/sqlite-index-ddl.ts","../../src/cloudflare/schema.ts","../../src/cloudflare/sql.ts","../../src/cloudflare/backend.ts","../../src/docid.ts","../../src/batch.ts","../../src/dynamic-registry.ts","../../src/json-schema.ts","../../src/migration.ts","../../src/scope.ts","../../src/registry.ts","../../src/sandbox.ts","../../src/query.ts","../../src/query-safety.ts","../../src/transaction.ts","../../src/client.ts","../../src/cloudflare/client.ts","../../src/cloudflare/do.ts","../../src/id.ts"],"sourcesContent":["/**\n * Firegraph serialization tag — split from `src/serialization.ts` so it can\n * be imported from Workers-facing code without dragging in\n * `@google-cloud/firestore`.\n *\n * The full serialization module (with Timestamp/GeoPoint round-tripping)\n * lives one folder up because the sandbox migration pipeline needs it; the\n * write-plan helper only needs to recognise tagged objects to keep them\n * terminal during patch flattening, so it imports just the tag from here.\n */\n\n/** Sentinel key used to tag serialized Firestore types. */\nexport const SERIALIZATION_TAG = '__firegraph_ser__' as const;\n\nconst KNOWN_TYPES = new Set(['Timestamp', 'GeoPoint', 'VectorValue', 'DocumentReference']);\n\n/** Check if a value is a tagged serialized Firestore type. */\nexport function isTaggedValue(value: unknown): boolean {\n if (value === null || typeof value !== 'object') return false;\n const tag = (value as Record<string, unknown>)[SERIALIZATION_TAG];\n return typeof tag === 'string' && KNOWN_TYPES.has(tag);\n}\n","/**\n * Firestore-aware serialization for the sandbox migration pipeline.\n *\n * Firestore documents can contain special types (Timestamp, GeoPoint,\n * VectorValue, DocumentReference) that don't survive plain JSON\n * round-tripping. This module provides tagged serialization: Firestore\n * types are wrapped in tagged plain objects before JSON marshaling and\n * reconstructed after.\n *\n * Only used by the `defaultExecutor` sandbox path. Static migrations\n * (in-memory functions) receive raw Firestore objects directly.\n */\n\nimport type { DocumentReference, Firestore } from '@google-cloud/firestore';\nimport { FieldValue, GeoPoint, Timestamp } from '@google-cloud/firestore';\n\n// ---------------------------------------------------------------------------\n// Constants\n// ---------------------------------------------------------------------------\n\n// SERIALIZATION_TAG and isTaggedValue live in `internal/serialization-tag.ts`\n// so Workers-facing code (e.g. `src/internal/write-plan.ts` and the\n// `firegraph/cloudflare` bundle) can recognise tagged values without\n// pulling in `@google-cloud/firestore`. Re-exported here so callers that\n// already import from `src/serialization.ts` keep working.\nexport { isTaggedValue, SERIALIZATION_TAG } from './internal/serialization-tag.js';\nimport { isTaggedValue, SERIALIZATION_TAG } from './internal/serialization-tag.js';\n\n// One-time warning for DocumentReference deserialization without db\nlet _docRefWarned = false;\n\n// ---------------------------------------------------------------------------\n// Detection helpers\n// ---------------------------------------------------------------------------\n\nfunction isTimestamp(value: unknown): value is Timestamp {\n return value instanceof Timestamp;\n}\n\nfunction isGeoPoint(value: unknown): value is GeoPoint {\n return value instanceof GeoPoint;\n}\n\nfunction isDocumentReference(value: unknown): value is DocumentReference {\n // Duck-type check: DocumentReference has path (string) and firestore properties\n if (value === null || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n return (\n typeof v.path === 'string' &&\n v.firestore !== undefined &&\n typeof v.id === 'string' &&\n v.constructor?.name === 'DocumentReference'\n );\n}\n\nfunction isVectorValue(value: unknown): boolean {\n if (value === null || typeof value !== 'object') return false;\n const v = value as Record<string, unknown>;\n return (\n v.constructor?.name === 'VectorValue' && Array.isArray((v as Record<string, unknown>)._values)\n );\n}\n\n// ---------------------------------------------------------------------------\n// Serialize\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively walk a data object and replace Firestore types with tagged\n * plain objects suitable for JSON serialization.\n *\n * Returns a new object tree — the input is never mutated.\n */\nexport function serializeFirestoreTypes(data: Record<string, unknown>): Record<string, unknown> {\n return serializeValue(data) as Record<string, unknown>;\n}\n\nfunction serializeValue(value: unknown): unknown {\n // Primitives\n if (value === null || value === undefined) return value;\n if (typeof value !== 'object') return value;\n\n // Firestore types (check before generic object/array)\n if (isTimestamp(value)) {\n return {\n [SERIALIZATION_TAG]: 'Timestamp',\n seconds: value.seconds,\n nanoseconds: value.nanoseconds,\n };\n }\n if (isGeoPoint(value)) {\n return {\n [SERIALIZATION_TAG]: 'GeoPoint',\n latitude: value.latitude,\n longitude: value.longitude,\n };\n }\n if (isDocumentReference(value)) {\n return { [SERIALIZATION_TAG]: 'DocumentReference', path: (value as DocumentReference).path };\n }\n if (isVectorValue(value)) {\n // Prefer toArray() (public API) over _values (private internal property)\n const v = value as Record<string, unknown>;\n const values =\n typeof v.toArray === 'function' ? (v.toArray as () => number[])() : (v._values as number[]);\n return { [SERIALIZATION_TAG]: 'VectorValue', values: [...values] };\n }\n\n // Arrays\n if (Array.isArray(value)) {\n return value.map(serializeValue);\n }\n\n // Plain objects — recurse\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(value as Record<string, unknown>)) {\n result[key] = serializeValue((value as Record<string, unknown>)[key]);\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Deserialize\n// ---------------------------------------------------------------------------\n\n/**\n * Recursively walk a data object and reconstruct Firestore types from\n * tagged plain objects.\n *\n * @param data - The data to deserialize (typically from JSON.parse)\n * @param db - Optional Firestore instance for DocumentReference reconstruction.\n * If not provided, tagged DocumentReferences are left as-is with a one-time warning.\n *\n * Returns a new object tree — the input is never mutated.\n */\nexport function deserializeFirestoreTypes(\n data: Record<string, unknown>,\n db?: Firestore,\n): Record<string, unknown> {\n return deserializeValue(data, db) as Record<string, unknown>;\n}\n\nfunction deserializeValue(value: unknown, db?: Firestore): unknown {\n if (value === null || value === undefined) return value;\n if (typeof value !== 'object') return value;\n\n // Short-circuit for values that are already real Firestore types.\n // This makes deserializeFirestoreTypes idempotent — safe to call on data\n // that has already been deserialized (e.g., write-back after defaultExecutor\n // already reconstructed types, or static migrations that return raw types).\n if (\n isTimestamp(value) ||\n isGeoPoint(value) ||\n isDocumentReference(value) ||\n isVectorValue(value)\n ) {\n return value;\n }\n\n // Arrays\n if (Array.isArray(value)) {\n return value.map((v) => deserializeValue(v, db));\n }\n\n const obj = value as Record<string, unknown>;\n\n // Check for tagged Firestore type\n if (isTaggedValue(obj)) {\n const tag = obj[SERIALIZATION_TAG] as string;\n\n switch (tag) {\n case 'Timestamp':\n // Validate expected fields before reconstruction\n if (typeof obj.seconds !== 'number' || typeof obj.nanoseconds !== 'number') return obj;\n return new Timestamp(obj.seconds, obj.nanoseconds);\n\n case 'GeoPoint':\n if (typeof obj.latitude !== 'number' || typeof obj.longitude !== 'number') return obj;\n return new GeoPoint(obj.latitude, obj.longitude);\n\n case 'VectorValue':\n if (!Array.isArray(obj.values)) return obj;\n return FieldValue.vector(obj.values as number[]);\n\n case 'DocumentReference':\n if (typeof obj.path !== 'string') return obj;\n if (db) {\n return db.doc(obj.path);\n }\n // No db available — leave as tagged object with one-time warning\n if (!_docRefWarned) {\n _docRefWarned = true;\n console.warn(\n '[firegraph] DocumentReference encountered during migration deserialization ' +\n 'but no Firestore instance available. The reference will remain as a tagged ' +\n 'object with its path. Enable write-back for full reconstruction.',\n );\n }\n return obj;\n\n default:\n // Unknown tag — leave as-is (forward compatibility)\n return obj;\n }\n }\n\n // Plain object — recurse\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(obj)) {\n result[key] = deserializeValue(obj[key], db);\n }\n return result;\n}\n","/**\n * Public entry point for firegraph's Cloudflare-native backend.\n *\n * Everything re-exported here lives at `@typicalday/firegraph/cloudflare`.\n * The design philosophy — one Durable Object per subgraph, no scope column,\n * physical isolation — is documented in `do.ts` and `backend.ts`.\n *\n * ## Typical worker wiring\n *\n * ```ts\n * // worker.ts\n * export { FiregraphDO } from '@typicalday/firegraph/cloudflare';\n *\n * import { createDOClient } from '@typicalday/firegraph/cloudflare';\n * import { registry } from './registry.js';\n *\n * export default {\n * async fetch(_req: Request, env: { GRAPH: DurableObjectNamespace }) {\n * const client = createDOClient(env.GRAPH, 'main', { registry });\n * // … use client as a normal firegraph GraphClient …\n * },\n * };\n * ```\n *\n * ## Capability matrix\n *\n * | Feature | Status |\n * | ---------------------------- | -------------------------------------------------------- |\n * | CRUD (put/get/update/remove) | ✅ |\n * | Queries (`findEdges/Nodes`) | ✅ |\n * | Batches (atomic) | ✅ |\n * | Cascade (DO-local) | ✅ |\n * | Cross-subgraph cascade | ✅ via registry `getSubgraphTopology` fan-out |\n * | Bulk-remove-edges (DO-local) | ✅ |\n * | `.subgraph()` routing | ✅ (auto-provisioned via `namespace.idFromName`) |\n * | Static registry | ✅ (validation + migrations) |\n * | Dynamic registry | ✅ `registryMode: { mode: 'dynamic' }`; merged mode too |\n * | Interactive transactions | ❌ throws `UNSUPPORTED_OPERATION` — use `batch()` |\n * | `findEdgesGlobal()` | ❌ throws `UNSUPPORTED_OPERATION` — no cross-DO index |\n */\n\nexport type {\n DORPCBackendOptions,\n DurableObjectIdLike,\n FiregraphNamespace,\n FiregraphStub,\n} from './backend.js';\nexport { DORPCBackend } from './backend.js';\nexport type { DOClientOptions } from './client.js';\nexport { createDOClient, createSiblingClient } from './client.js';\nexport type {\n BatchOp,\n DOSqlCursor,\n DOSqlExecutor,\n DOStorage,\n DurableObjectStateLike,\n FiregraphDOOptions,\n} from './do.js';\nexport { FiregraphDO } from './do.js';\nexport type { BuildDOSchemaOptions } from './schema.js';\nexport { buildDOSchemaStatements } from './schema.js';\n\n// Re-exports of Workers-safe utilities from the main entry. Importing them\n// from `firegraph/cloudflare` instead of `firegraph` lets workerd-bundled\n// callers stay clear of the Firestore module graph (which transitively\n// pulls `gcp-metadata` → `google-logging-utils` and crashes at module\n// load on workerd's `--disallow-code-generation-from-strings` runtime).\nexport { META_EDGE_TYPE, META_NODE_TYPE } from '../dynamic-registry.js';\nexport { generateId } from '../id.js';\nexport { deleteField } from '../internal/write-plan.js';\nexport { createMergedRegistry, createRegistry } from '../registry.js';\n","export class FiregraphError extends Error {\n constructor(\n message: string,\n public readonly code: string,\n ) {\n super(message);\n this.name = 'FiregraphError';\n }\n}\n\nexport class NodeNotFoundError extends FiregraphError {\n constructor(uid: string) {\n super(`Node not found: ${uid}`, 'NODE_NOT_FOUND');\n this.name = 'NodeNotFoundError';\n }\n}\n\nexport class EdgeNotFoundError extends FiregraphError {\n constructor(aUid: string, axbType: string, bUid: string) {\n super(`Edge not found: ${aUid} -[${axbType}]-> ${bUid}`, 'EDGE_NOT_FOUND');\n this.name = 'EdgeNotFoundError';\n }\n}\n\nexport class ValidationError extends FiregraphError {\n constructor(\n message: string,\n public readonly details?: unknown,\n ) {\n super(message, 'VALIDATION_ERROR');\n this.name = 'ValidationError';\n }\n}\n\nexport class RegistryViolationError extends FiregraphError {\n constructor(aType: string, axbType: string, bType: string) {\n super(`Unregistered triple: (${aType}) -[${axbType}]-> (${bType})`, 'REGISTRY_VIOLATION');\n this.name = 'RegistryViolationError';\n }\n}\n\nexport class InvalidQueryError extends FiregraphError {\n constructor(message: string) {\n super(message, 'INVALID_QUERY');\n this.name = 'InvalidQueryError';\n }\n}\n\nexport class TraversalError extends FiregraphError {\n constructor(message: string) {\n super(message, 'TRAVERSAL_ERROR');\n this.name = 'TraversalError';\n }\n}\n\nexport class DynamicRegistryError extends FiregraphError {\n constructor(message: string) {\n super(message, 'DYNAMIC_REGISTRY_ERROR');\n this.name = 'DynamicRegistryError';\n }\n}\n\nexport class QuerySafetyError extends FiregraphError {\n constructor(message: string) {\n super(message, 'QUERY_SAFETY');\n this.name = 'QuerySafetyError';\n }\n}\n\nexport class RegistryScopeError extends FiregraphError {\n constructor(\n aType: string,\n axbType: string,\n bType: string,\n scopePath: string,\n allowedIn: string[],\n ) {\n super(\n `Type (${aType}) -[${axbType}]-> (${bType}) is not allowed at scope \"${scopePath || 'root'}\". ` +\n `Allowed in: [${allowedIn.join(', ')}]`,\n 'REGISTRY_SCOPE',\n );\n this.name = 'RegistryScopeError';\n }\n}\n\nexport class MigrationError extends FiregraphError {\n constructor(message: string) {\n super(message, 'MIGRATION_ERROR');\n this.name = 'MigrationError';\n }\n}\n\n/**\n * Thrown when a caller tries to perform an operation that would require\n * atomicity across two physical storage backends — e.g. opening a routed\n * subgraph client from inside a transaction callback. Cross-backend\n * atomicity cannot be honoured by real-world storage engines (Firestore,\n * SQLite drivers over D1/DO/better-sqlite3, etc.), so firegraph surfaces\n * this as a typed error instead of silently confining the write to the\n * base backend.\n *\n * Normally `TransactionBackend` and `BatchBackend` don't expose `subgraph()`\n * at the type level, so this error is unreachable through well-typed code.\n * It exists as a public catchable type for app code that needs to tolerate\n * this case deliberately (e.g. dynamic code paths that bypass the type\n * system) and as future-proofing if the interface ever grows a way to\n * request a sub-scope inside a transaction.\n */\nexport class CrossBackendTransactionError extends FiregraphError {\n constructor(message: string) {\n super(message, 'CROSS_BACKEND_TRANSACTION');\n this.name = 'CrossBackendTransactionError';\n }\n}\n","export const NODE_RELATION = 'is';\n\n/**\n * Default result limit applied to findEdges/findNodes queries\n * when no explicit limit is provided. Prevents unbounded result sets\n * that could be expensive on Enterprise Firestore.\n */\nexport const DEFAULT_QUERY_LIMIT = 500;\n\n/**\n * Fields that are part of the firegraph record structure (not user data).\n * Used by the query planner and safety analysis to distinguish builtin\n * fields from data.* fields.\n */\nexport const BUILTIN_FIELDS = new Set([\n 'aType',\n 'aUid',\n 'axbType',\n 'bType',\n 'bUid',\n 'createdAt',\n 'updatedAt',\n]);\n\nexport const SHARD_ALGORITHM = 'sha256';\nexport const SHARD_SEPARATOR = ':';\nexport const SHARD_BUCKETS = 16;\n","/**\n * Shared `dataOps` SQL compilation helpers used by both SQLite-style backends\n * (`internal/sqlite-sql.ts` for the shared-table backend and `cloudflare/sql.ts`\n * for the per-DO backend).\n *\n * The two backends differ in identifier quoting and scope handling, but the\n * `data` column lives in JSON in both, the deep-merge / replace contract is\n * identical, and the `json_set` / `json_remove` expression they emit for a\n * `DataPathOp[]` is byte-for-byte the same. Lifting the helpers here keeps\n * that shape in one place — the comment in `cloudflare/sql.ts` used to read\n * \"keep them in sync\"; this module is what they keep in sync against.\n *\n * The helpers take a `backendLabel` parameter so error messages still\n * distinguish `\"SQLite backend\"` (shared-table) from `\"DO SQLite backend\"`\n * (per-Durable-Object). Identifier quoting is the caller's job — the helpers\n * here only emit JSON-path expressions against an opaque `base` argument,\n * never bare column names.\n */\n\nimport { FiregraphError } from '../errors.js';\nimport type { DataPathOp } from './write-plan.js';\n\n/**\n * Constructor names of Firestore special types that don't survive a plain\n * `JSON.stringify` round-trip — they have non-enumerable accessors (e.g.\n * `Timestamp.seconds`) or class identity that JSON loses. Detection is by\n * `constructor.name` to keep this module dependency-free (importing\n * `@google-cloud/firestore` here would pollute the Cloudflare Workers bundle —\n * see tests/unit/bundle-pollution.test.ts).\n */\nexport const FIRESTORE_TYPE_NAMES = new Set([\n 'Timestamp',\n 'GeoPoint',\n 'VectorValue',\n 'DocumentReference',\n 'FieldValue',\n]);\n\nexport function isFirestoreSpecialType(value: object): string | null {\n const ctorName = (value as { constructor?: { name?: string } }).constructor?.name;\n if (ctorName && FIRESTORE_TYPE_NAMES.has(ctorName)) return ctorName;\n return null;\n}\n\n/**\n * Identifiers accepted in `data.<key>` paths and `dataOps` path segments.\n * The pattern (`/^[A-Za-z_][A-Za-z0-9_-]*$/`) covers code-style identifiers\n * (camel, snake, kebab). Silently quoting exotic keys would require symmetric\n * quoting at every read/write call site; any drift produces silent data\n * corruption. Failing loudly at compile time is safer — users with exotic\n * keys can use `replaceNode` / `replaceEdge` (full-data overwrite) instead.\n */\nexport const JSON_PATH_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;\n\nexport function validateJsonPathKey(key: string, backendLabel: string): void {\n if (key.length === 0) {\n throw new FiregraphError(\n `${backendLabel}: empty JSON path component is not allowed`,\n 'INVALID_QUERY',\n );\n }\n if (!JSON_PATH_KEY_RE.test(key)) {\n throw new FiregraphError(\n `${backendLabel}: data field path component \"${key}\" is not a safe JSON-path identifier. ` +\n `Allowed pattern: /^[A-Za-z_][A-Za-z0-9_-]*$/. Use replaceNode/replaceEdge (full-data overwrite) ` +\n `for keys with reserved characters (whitespace, dots, brackets, quotes, etc.).`,\n 'INVALID_QUERY',\n );\n }\n}\n\n/**\n * Bind a value as a JSON-serializable string for `json(?)` placeholders in\n * the compiled `json_set` expression. `assertJsonSafePayload` already runs\n * eagerly at the write boundary, so the Firestore-special-type rejection\n * here is defense-in-depth — left in place per the team's preference for\n * symmetric guards across the SQLite compilers.\n */\nexport function jsonBind(value: unknown, backendLabel: string): string {\n if (value === undefined) return 'null';\n if (value !== null && typeof value === 'object') {\n const firestoreType = isFirestoreSpecialType(value);\n if (firestoreType) {\n throw new FiregraphError(\n `${backendLabel} cannot persist a Firestore ${firestoreType} value. ` +\n `Convert to a primitive before writing (e.g. \\`ts.toMillis()\\` for Timestamp).`,\n 'INVALID_ARGUMENT',\n );\n }\n }\n return JSON.stringify(value);\n}\n\n/**\n * Build the SQL expression that applies a list of `DataPathOp`s onto an\n * existing JSON column reference (e.g. `\"data\"` or `COALESCE(\"data\", '{}')`).\n *\n * Returns the full expression (already parenthesised where needed) and pushes\n * the bound parameters onto `params` in left-to-right order. Returns `null`\n * when there are no ops at all — the caller picks a fallback expression.\n *\n * Strategy:\n * 1. `json_remove(<base>, '$.a.b', '$.c', …)` strips delete-ops.\n * 2. `json_set(<#1>, '$.x.y', json(?), '$.z', json(?), …)` writes value-ops.\n * `json(?)` ensures non-string values bind as JSON (objects, arrays,\n * numbers, booleans, null).\n */\nexport function compileDataOpsExpr(\n ops: readonly DataPathOp[],\n base: string,\n params: unknown[],\n backendLabel: string,\n): string | null {\n if (ops.length === 0) return null;\n\n const deletes: DataPathOp[] = [];\n const sets: DataPathOp[] = [];\n for (const op of ops) (op.delete ? deletes : sets).push(op);\n\n let expr = base;\n\n if (deletes.length > 0) {\n const placeholders = deletes.map(() => '?').join(', ');\n expr = `json_remove(${expr}, ${placeholders})`;\n for (const op of deletes) {\n for (const seg of op.path) validateJsonPathKey(seg, backendLabel);\n params.push(`$.${op.path.join('.')}`);\n }\n }\n\n if (sets.length > 0) {\n const pieces = sets.map(() => '?, json(?)').join(', ');\n expr = `json_set(${expr}, ${pieces})`;\n for (const op of sets) {\n for (const seg of op.path) validateJsonPathKey(seg, backendLabel);\n params.push(`$.${op.path.join('.')}`);\n params.push(jsonBind(op.value, backendLabel));\n }\n }\n\n return expr;\n}\n","/**\n * Shared eager-validation helper for SQLite-style backends\n * (`internal/sqlite-sql.ts` and `cloudflare/sql.ts`).\n *\n * Both backends serialise `record.data` (and `update.replaceData`) as a raw\n * JSON blob via `JSON.stringify`. Two classes of value silently corrupt that\n * representation and the cross-backend contract:\n *\n * 1. **Firestore special types** (`Timestamp`, `GeoPoint`, `VectorValue`,\n * `DocumentReference`, `FieldValue`). They have non-enumerable accessors\n * or rely on class identity that JSON drops, so they round-trip as `{}`\n * or garbage. Callers must convert to primitives before writing.\n * 2. **`DELETE_FIELD` sentinel.** A `Symbol` is invisible to\n * `JSON.stringify`. If a caller embeds the sentinel in a `replaceNode`\n * payload or in a fresh-insert (no existing row), the field would\n * silently disappear instead of erroring loudly the way it does for the\n * `dataOps` path — so we reject it eagerly here.\n * 3. **Tagged serialization payloads** (`__firegraph_ser__`). These are the\n * sandbox migration boundary marshalling form. They are valid inside\n * Firestore (the Firestore backend re-hydrates them via\n * `deserializeFirestoreTypes`), but on SQLite they would persist as\n * opaque tagged objects that no downstream reader knows how to interpret.\n * Reject them at the boundary so the failure is loud.\n *\n * The Firestore backend does NOT call this — it accepts those types natively\n * and `deserializeFirestoreTypes` rebuilds tagged values into real Firestore\n * objects on its own write path.\n *\n * Detection avoids `instanceof` so this module stays free of\n * `@google-cloud/firestore`. Constructor-name + duck-type matches the\n * approach used by `bindValue`/`jsonBind` elsewhere in the SQLite compilers.\n */\n\nimport { FiregraphError } from '../errors.js';\nimport { SERIALIZATION_TAG } from './serialization-tag.js';\nimport { isDeleteSentinel } from './write-plan.js';\n\nconst FIRESTORE_TYPE_NAMES = new Set([\n 'Timestamp',\n 'GeoPoint',\n 'VectorValue',\n 'DocumentReference',\n 'FieldValue',\n]);\n\n/**\n * Walk `data` and throw on any value that the SQLite-style raw-JSON\n * persistence path can't faithfully serialise. `label` distinguishes the\n * caller in error messages (e.g. `'shared-table SQLite'` vs `'DO SQLite'`).\n *\n * Plain objects recurse. Arrays recurse element-wise. Primitives, `null`,\n * and `undefined` are accepted (mirroring how `flattenPatch` treats them\n * during the merge path).\n */\nexport function assertJsonSafePayload(data: unknown, label: string): void {\n walk(data, [], label);\n}\n\nfunction walk(node: unknown, path: readonly string[], label: string): void {\n if (node === null || node === undefined) return;\n if (isDeleteSentinel(node)) {\n throw new FiregraphError(\n `${label} backend cannot persist a deleteField() sentinel inside a ` +\n `full-data payload (replaceNode/replaceEdge or first-insert). The ` +\n `sentinel is only valid inside an updateNode/updateEdge dataOps patch. ` +\n `Path: ${formatPath(path)}.`,\n 'INVALID_ARGUMENT',\n );\n }\n const t = typeof node;\n if (t === 'symbol' || t === 'function') {\n throw new FiregraphError(\n `${label} backend cannot persist a value of type ${t}. ` +\n `JSON.stringify drops it silently. Path: ${formatPath(path)}.`,\n 'INVALID_ARGUMENT',\n );\n }\n if (t === 'bigint') {\n throw new FiregraphError(\n `${label} backend cannot persist a value of type bigint. ` +\n `JSON.stringify cannot serialize this type (throws TypeError). ` +\n `Path: ${formatPath(path)}.`,\n 'INVALID_ARGUMENT',\n );\n }\n if (t !== 'object') return;\n if (Array.isArray(node)) {\n for (let i = 0; i < node.length; i++) {\n walk(node[i], [...path, String(i)], label);\n }\n return;\n }\n // Reject any object carrying the firegraph serialization tag — both legit\n // tagged Firestore-type payloads (the migration-sandbox output that round-\n // trips through Firestore) and bogus user data that happens to put a\n // literal `__firegraph_ser__` key on a plain object. SQLite has no\n // Timestamp class to rebuild the tag into, and silently writing the\n // envelope would produce an unreadable column.\n const obj = node as Record<string, unknown>;\n if (Object.prototype.hasOwnProperty.call(obj, SERIALIZATION_TAG)) {\n const tagValue = obj[SERIALIZATION_TAG];\n throw new FiregraphError(\n `${label} backend cannot persist an object with a \\`${SERIALIZATION_TAG}\\` ` +\n `key (value: ${formatTagValue(tagValue)}). Recognised tags are valid only on ` +\n `the Firestore backend (migration-sandbox output); a literal ` +\n `\\`${SERIALIZATION_TAG}\\` field in user data is reserved and not allowed. ` +\n `Path: ${formatPath(path)}.`,\n 'INVALID_ARGUMENT',\n );\n }\n // Class instances: reject Firestore special types loudly; reject every\n // other class instance generically (Map, Set, Date are caller's\n // responsibility to convert — Date is allowed in filter binds via\n // `bindValue` but not as a stored payload value because JSON.stringify\n // produces a string, not a real Date).\n const proto = Object.getPrototypeOf(node);\n if (proto !== null && proto !== Object.prototype) {\n const ctor = (node as { constructor?: { name?: string } }).constructor;\n const ctorName = ctor && typeof ctor.name === 'string' ? ctor.name : '<anonymous>';\n if (FIRESTORE_TYPE_NAMES.has(ctorName)) {\n throw new FiregraphError(\n `${label} backend cannot persist a Firestore ${ctorName} value. ` +\n `Convert to a primitive before writing (e.g. \\`ts.toMillis()\\` for ` +\n `Timestamp, \\`{lat,lng}\\` for GeoPoint). Path: ${formatPath(path)}.`,\n 'INVALID_ARGUMENT',\n );\n }\n // Accept Date as an alias for its epoch-ms — it round-trips as an ISO\n // string via JSON.stringify, which the caller chose; not our place to\n // reject. Same for Buffer / typed arrays — they'll JSON-serialize as\n // best they can. Reject only opaque exotic instances that JSON drops.\n if (node instanceof Date) return;\n throw new FiregraphError(\n `${label} backend cannot persist a class instance of type ${ctorName}. ` +\n `Only plain objects, arrays, and primitives round-trip safely through ` +\n `JSON storage. Path: ${formatPath(path)}.`,\n 'INVALID_ARGUMENT',\n );\n }\n for (const key of Object.keys(obj)) {\n walk(obj[key], [...path, key], label);\n }\n}\n\nfunction formatPath(path: readonly string[]): string {\n return path.length === 0 ? '<root>' : path.map((p) => JSON.stringify(p)).join(' > ');\n}\n\nfunction formatTagValue(value: unknown): string {\n if (value === null) return 'null';\n if (value === undefined) return 'undefined';\n if (typeof value === 'string') return JSON.stringify(value);\n if (typeof value === 'number' || typeof value === 'boolean' || typeof value === 'bigint') {\n return String(value);\n }\n return typeof value;\n}\n","/**\n * Write-plan helper — flattens partial-update payloads into a list of\n * deep-path operations every backend can execute identically.\n *\n * Background: firegraph used to ship two write semantics that quietly\n * disagreed about depth.\n * - `putNode`/`putEdge` did a full document replace.\n * - `updateNode`/`updateEdge` did a one-level shallow merge: top-level\n * keys were preserved, but nested objects were replaced wholesale.\n *\n * Both behaviours dropped sibling keys silently. The 0.12 contract is that\n * `put*` and `update*` deep-merge by default (sibling keys at any depth\n * survive); `replace*` is the explicit escape hatch.\n *\n * `flattenPatch` walks a partial-update payload and emits one\n * {@link DataPathOp} per terminal value. Plain objects recurse; arrays,\n * primitives, Firestore special types, and tagged firegraph-serialization\n * objects are terminal (replaced as a unit). `undefined` values are\n * skipped; `null` is preserved as a real `null` write; the\n * {@link DELETE_FIELD} sentinel marks a field for removal.\n *\n * The output is deliberately backend-agnostic. Each backend translates ops\n * into its native dialect:\n * - Firestore: dotted field path → `data.a.b.c` for `update()`.\n * - SQLite / DO SQLite: `json_set(data, '$.a.b.c', ?)` /\n * `json_remove(data, '$.a.b.c')`.\n */\n\nimport { isTaggedValue, SERIALIZATION_TAG } from './serialization-tag.js';\n\n// ---------------------------------------------------------------------------\n// Public sentinel\n// ---------------------------------------------------------------------------\n\n/**\n * Sentinel returned by {@link deleteField}. Treated by all backends as\n * \"remove this field from the stored document\".\n *\n * Equivalent to Firestore's `FieldValue.delete()`, but works for SQLite\n * backends too. Use inside `updateNode`/`updateEdge` payloads.\n */\nexport const DELETE_FIELD: unique symbol = Symbol.for('firegraph.deleteField');\nexport type DeleteSentinel = typeof DELETE_FIELD;\n\n/**\n * Returns the firegraph delete sentinel. Place this anywhere in an\n * `updateNode`/`updateEdge` payload to remove the corresponding field.\n *\n * ```ts\n * await client.updateNode('tour', uid, {\n * attrs: { obsoleteFlag: deleteField() },\n * });\n * ```\n */\nexport function deleteField(): DeleteSentinel {\n return DELETE_FIELD;\n}\n\n/** Type guard for the delete sentinel. */\nexport function isDeleteSentinel(value: unknown): value is DeleteSentinel {\n return value === DELETE_FIELD;\n}\n\n// ---------------------------------------------------------------------------\n// Terminal-detection helpers\n// ---------------------------------------------------------------------------\n\nconst FIRESTORE_TERMINAL_CTOR = new Set([\n 'Timestamp',\n 'GeoPoint',\n 'VectorValue',\n 'DocumentReference',\n 'FieldValue',\n 'NumericIncrementTransform',\n 'ArrayUnionTransform',\n 'ArrayRemoveTransform',\n 'ServerTimestampTransform',\n 'DeleteTransform',\n]);\n\n/**\n * Should this value be written as a single terminal op (no recursion)?\n *\n * Plain JS objects (constructor === Object, or no prototype) are recursed.\n * Everything else — arrays, primitives, class instances, Firestore special\n * types, tagged serialization payloads — is terminal.\n */\nexport function isTerminalValue(value: unknown): boolean {\n if (value === null) return true;\n const t = typeof value;\n if (t !== 'object') return true;\n if (Array.isArray(value)) return true;\n // Tagged serialization payloads carry the SERIALIZATION_TAG sentinel and\n // should be persisted whole — never split into per-field ops.\n if (isTaggedValue(value)) return true;\n const proto = Object.getPrototypeOf(value);\n if (proto === null || proto === Object.prototype) return false;\n // Class instances — Firestore types or anything else exotic.\n const ctor = (value as { constructor?: { name?: string } }).constructor;\n if (ctor && typeof ctor.name === 'string' && FIRESTORE_TERMINAL_CTOR.has(ctor.name)) return true;\n // Unknown class instance: treat as terminal. Recursing into a class\n // instance is almost always wrong (Map, Set, Date, Buffer...).\n return true;\n}\n\n// ---------------------------------------------------------------------------\n// Core type\n// ---------------------------------------------------------------------------\n\n/**\n * Single terminal write operation produced by {@link flattenPatch}.\n *\n * `path` is a non-empty array of plain object keys. `value` is the value to\n * write; ignored when `delete` is `true`. Arrays / primitives / Firestore\n * special types appear here as whole terminal values.\n */\nexport interface DataPathOp {\n path: readonly string[];\n value: unknown;\n delete: boolean;\n}\n\n// ---------------------------------------------------------------------------\n// Path-segment validation\n// ---------------------------------------------------------------------------\n\n/**\n * Object keys that are safe to embed in SQLite `json_set`/`json_remove`\n * paths. The SQLite backend uses an allowlist regex too — keep these in\n * sync (see `JSON_PATH_KEY_RE` in `internal/sqlite-sql.ts` and\n * `cloudflare/sql.ts`).\n *\n * Allows: ASCII letters, digits, `_`, `-`. Must start with a letter or\n * underscore. This rejects keys containing dots, brackets, quotes, or\n * non-ASCII characters that could break path parsing or be used to\n * inject into the path expression.\n */\nconst SAFE_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;\n\n/**\n * Mutual-exclusion guard for {@link UpdatePayload}. The two branches of the\n * shape — `dataOps` (deep-merge) and `replaceData` (full replace) — are\n * structurally incompatible: combining them would tell the backend to\n * simultaneously merge AND wipe, and the three backends disagree on which\n * wins. This helper centralises the runtime check so all three backends\n * trip the same error.\n *\n * Imported as a runtime check from `firestore-backend`, `sqlite-sql`, and\n * `cloudflare/sql`. Backend authors implementing the public `StorageBackend`\n * contract should call it too.\n */\nexport function assertUpdatePayloadExclusive(update: {\n dataOps?: unknown;\n replaceData?: unknown;\n}): void {\n if (update.replaceData !== undefined && update.dataOps !== undefined) {\n throw new Error(\n 'firegraph: UpdatePayload cannot specify both `replaceData` and `dataOps`. ' +\n 'Use one or the other — `replaceData` is the migration-write-back form, ' +\n '`dataOps` is the standard partial-update form.',\n );\n }\n}\n\n/**\n * Reject `DELETE_FIELD` sentinels in payloads where field deletion isn't a\n * meaningful operation: full-document replace (`replaceNode`/`replaceEdge`)\n * and the merge-default put surface (`putNode`/`putEdge`).\n *\n * Why both:\n * - In **replace**, the entire `data` field is overwritten. A delete\n * sentinel in that payload either silently disappears (Firestore drops\n * the Symbol during `.set()` serialization) or produces an empty SQLite\n * `json_remove` no-op, depending on backend. Either way the caller's\n * intent — \"remove field X\" — is lost. Use `updateNode` instead.\n * - In **put** (merge mode), behaviour diverges across backends today:\n * SQLite's flattenPatch emits a real delete op, but Firestore's\n * `.set(..., {merge: true})` silently drops the Symbol. Until that's\n * fixed end-to-end, the safest contract is to reject sentinels at the\n * entry point and steer callers to `updateNode`.\n *\n * The walk mirrors `flattenPatch`: plain objects recurse, everything else\n * is terminal. Tagged serialization payloads short-circuit so we don't\n * recurse into the `__firegraph_ser__` envelope.\n */\nexport function assertNoDeleteSentinels(data: unknown, callerLabel: string): void {\n walkForDeleteSentinels(data, [], { kind: 'root' }, ({ path }) => {\n const where = path.length === 0 ? '<root>' : path.map((p) => JSON.stringify(p)).join(' > ');\n throw new Error(\n `firegraph: ${callerLabel} payload contains a deleteField() sentinel at ${where}. ` +\n `deleteField() is only valid inside updateNode/updateEdge — full-data ` +\n `writes (put*, replace*) cannot delete individual fields. Use updateNode ` +\n `with a deleteField() value, or omit the field from the replace payload.`,\n );\n });\n}\n\ntype SentinelParent = { kind: 'root' } | { kind: 'object' } | { kind: 'array'; index: number };\n\nfunction walkForDeleteSentinels(\n node: unknown,\n path: readonly string[],\n parent: SentinelParent,\n visit: (ctx: { path: readonly string[]; parent: SentinelParent }) => void,\n): void {\n if (node === null || node === undefined) return;\n if (isDeleteSentinel(node)) {\n visit({ path, parent });\n return;\n }\n if (typeof node !== 'object') return;\n if (isTaggedValue(node)) return;\n if (Array.isArray(node)) {\n for (let i = 0; i < node.length; i++) {\n walkForDeleteSentinels(node[i], [...path, String(i)], { kind: 'array', index: i }, visit);\n }\n return;\n }\n const proto = Object.getPrototypeOf(node);\n if (proto !== null && proto !== Object.prototype) return;\n const obj = node as Record<string, unknown>;\n for (const key of Object.keys(obj)) {\n walkForDeleteSentinels(obj[key], [...path, key], { kind: 'object' }, visit);\n }\n}\n\n/** Throws if any path segment in the patch is unsafe for SQLite paths. */\nexport function assertSafePath(path: readonly string[]): void {\n for (const seg of path) {\n if (!SAFE_KEY_RE.test(seg)) {\n throw new Error(\n `firegraph: unsafe object key ${JSON.stringify(seg)} at path ${path\n .map((p) => JSON.stringify(p))\n .join(' > ')}. Keys used inside update payloads must match ` +\n `/^[A-Za-z_][A-Za-z0-9_-]*$/ so they can be embedded safely in ` +\n `SQLite JSON paths.`,\n );\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// flattenPatch\n// ---------------------------------------------------------------------------\n\n/**\n * Flatten a partial-update payload into a list of terminal {@link DataPathOp}s.\n *\n * Rules:\n * - Plain objects (no prototype or `Object.prototype`) recurse — each\n * key becomes another path segment.\n * - Arrays are terminal: writing `{tags: ['a']}` overwrites the whole\n * `tags` array. Element-wise array merging is intentionally NOT\n * supported — it's almost never what callers actually want, and\n * Firestore `arrayUnion`/`arrayRemove` give precise semantics when\n * they are.\n * - `undefined` values are skipped (no op generated). Use\n * {@link deleteField} if you actually want to remove a field.\n * - `null` is preserved verbatim — emits a terminal op with `value: null`.\n * - {@link DELETE_FIELD} produces an op with `delete: true`.\n * - Firestore special types and tagged serialization payloads are terminal.\n * - Class instances are terminal.\n *\n * Throws if any object key on the recursion path is unsafe (see\n * {@link assertSafePath}).\n */\nexport function flattenPatch(data: Record<string, unknown>): DataPathOp[] {\n const ops: DataPathOp[] = [];\n walk(data, [], ops);\n return ops;\n}\n\nfunction assertNoDeleteSentinelsInArrayValue(\n arr: readonly unknown[],\n arrayPath: readonly string[],\n): void {\n walkForDeleteSentinels(arr, arrayPath, { kind: 'root' }, ({ parent }) => {\n const arrayPathStr =\n arrayPath.length === 0 ? '<root>' : arrayPath.map((p) => JSON.stringify(p)).join(' > ');\n if (parent.kind === 'array') {\n throw new Error(\n `firegraph: deleteField() sentinel at index ${parent.index} inside an array at ` +\n `path ${arrayPathStr}. Arrays are ` +\n `terminal in update payloads (replaced as a unit), so the sentinel ` +\n `would be silently dropped by JSON serialization. To remove the ` +\n `field entirely, pass deleteField() in place of the whole array.`,\n );\n }\n throw new Error(\n `firegraph: deleteField() sentinel inside an array element at ` +\n `path ${arrayPathStr}. ` +\n `Arrays are terminal in update payloads — the sentinel would ` +\n `be silently dropped by JSON serialization.`,\n );\n });\n}\n\nfunction walk(node: unknown, path: string[], out: DataPathOp[]): void {\n // Caller guarantees the root is a plain object; this branch only\n // matters for recursion.\n if (node === undefined) return;\n if (isDeleteSentinel(node)) {\n if (path.length === 0) {\n throw new Error('firegraph: deleteField() cannot be the entire update payload.');\n }\n assertSafePath(path);\n out.push({ path: [...path], value: undefined, delete: true });\n return;\n }\n if (isTerminalValue(node)) {\n if (path.length === 0) {\n // `null` / array / primitive at the root is illegal — patches must\n // describe per-key changes.\n throw new Error(\n 'firegraph: update payload must be a plain object. Got ' +\n (node === null ? 'null' : Array.isArray(node) ? 'array' : typeof node) +\n '.',\n );\n }\n // A DELETE_FIELD sentinel embedded inside an array (which is terminal\n // and replaced as a unit) would silently disappear: JSON.stringify drops\n // Symbols, and Firestore's serializer does likewise. Reject loudly so\n // the divergence between \"user wrote a delete\" and \"field stayed put\"\n // can't happen.\n if (Array.isArray(node)) {\n assertNoDeleteSentinelsInArrayValue(node, path);\n }\n assertSafePath(path);\n out.push({ path: [...path], value: node, delete: false });\n return;\n }\n // Plain object: recurse into its own enumerable keys.\n const obj = node as Record<string, unknown>;\n const keys = Object.keys(obj);\n if (keys.length === 0) {\n // Empty object at non-root: emit terminal op so an empty object can\n // be written explicitly when the caller really wants one. Skip at\n // the root — no-op patches should produce no ops.\n if (path.length > 0) {\n assertSafePath(path);\n out.push({ path: [...path], value: {}, delete: false });\n }\n return;\n }\n for (const key of keys) {\n if (key === SERIALIZATION_TAG) {\n const where = path.length === 0 ? '<root>' : path.map((p) => JSON.stringify(p)).join(' > ');\n throw new Error(\n `firegraph: update payload contains a literal \\`${SERIALIZATION_TAG}\\` key at ` +\n `${where}. That key is reserved for firegraph's serialization envelope and ` +\n `cannot appear on a plain object in user data. Use a different field name, ` +\n `or pass a recognized tagged value through replaceNode/replaceEdge instead.`,\n );\n }\n walk(obj[key], [...path, key], out);\n }\n}\n","/**\n * Backend-agnostic timestamp.\n *\n * Structurally compatible with `@google-cloud/firestore`'s `Timestamp` so\n * that records returned by either the Firestore or SQLite backend can be\n * consumed through the same `StoredGraphRecord` shape.\n *\n * Firestore's native `Timestamp` already satisfies this interface, so\n * existing Firestore consumers see no behavior change. The SQLite backend\n * returns instances of `GraphTimestampImpl` which also satisfies it.\n */\n\nexport interface GraphTimestamp {\n readonly seconds: number;\n readonly nanoseconds: number;\n toDate(): Date;\n toMillis(): number;\n}\n\n/**\n * Concrete `GraphTimestamp` implementation used by non-Firestore backends.\n * Mirrors the surface of Firestore's `Timestamp` enough for typical use.\n */\nexport class GraphTimestampImpl implements GraphTimestamp {\n constructor(\n public readonly seconds: number,\n public readonly nanoseconds: number,\n ) {}\n\n toDate(): Date {\n return new Date(this.toMillis());\n }\n\n toMillis(): number {\n return this.seconds * 1000 + Math.floor(this.nanoseconds / 1e6);\n }\n\n toJSON(): { seconds: number; nanoseconds: number } {\n return { seconds: this.seconds, nanoseconds: this.nanoseconds };\n }\n\n static fromMillis(ms: number): GraphTimestampImpl {\n const seconds = Math.floor(ms / 1000);\n const nanoseconds = (ms - seconds * 1000) * 1e6;\n return new GraphTimestampImpl(seconds, nanoseconds);\n }\n\n static now(): GraphTimestampImpl {\n return GraphTimestampImpl.fromMillis(Date.now());\n }\n}\n\n/**\n * Sentinel returned by `StorageBackend.serverTimestamp()` when the backend\n * has no native server-time concept and just wants a placeholder that the\n * adapter resolves to a concrete time at write commit. SQLite backends\n * substitute the wall-clock millis at the moment of `setDoc`/`updateDoc`.\n */\nexport const SERVER_TIMESTAMP_SENTINEL = Symbol.for('firegraph.serverTimestamp');\nexport type ServerTimestampSentinel = typeof SERVER_TIMESTAMP_SENTINEL;\n\nexport function isServerTimestampSentinel(value: unknown): value is ServerTimestampSentinel {\n return value === SERVER_TIMESTAMP_SENTINEL;\n}\n","/**\n * Default core index preset.\n *\n * This set covers the query patterns firegraph's query planner emits for\n * built-in operations — `findNodes`, `findEdges`, cascade delete, traversal,\n * and the DO/SQLite path compilers. Apps that need additional indexes\n * (descending timestamps, `data.*` filters, composite fields unique to\n * their query shapes) declare them on `RegistryEntry.indexes` or override\n * this preset wholesale via the backend-specific `coreIndexes` option —\n * `FiregraphDOOptions.coreIndexes` for the DO backend,\n * `BuildSchemaOptions.coreIndexes` for the legacy SQLite backend, and\n * `GenerateIndexOptions.coreIndexes` for the Firestore CLI generator.\n *\n * ## Ownership model\n *\n * This list is firegraph's *recommendation* — not non-negotiable policy.\n * Consumers can:\n *\n * 1. Accept the preset as-is (default).\n * 2. Extend it: `coreIndexes: [...DEFAULT_CORE_INDEXES, ...more]`.\n * 3. Replace it entirely with a tailored set.\n * 4. Disable it (`coreIndexes: []`) and take full responsibility for\n * index coverage — only do this if you're provisioning a complete\n * custom set.\n *\n * ## Per-backend emission\n *\n * The Firestore generator skips single-field entries (Firestore implicitly\n * indexes every field) and emits one composite index per multi-field spec.\n * The SQLite-flavored generators (DO, legacy) emit every spec as-is.\n *\n * ## Why these specific indexes\n *\n * - `aUid` / `bUid` — required for `_fgRemoveNodeCascade`, which scans by\n * each UID side independently. A composite `(aUid, axbType)` also\n * satisfies `aUid`-alone via leading-column prefix, but the single-field\n * form is cheaper for the common case.\n * - `aType` / `bType` — `findNodes({ aType })` and cross-type enumeration.\n * - `(aUid, axbType)` — forward edge lookup (`findEdges({ aUid, axbType })`)\n * and the `get` strategy fallback when only two of three triple fields\n * are present.\n * - `(axbType, bUid)` — reverse edge traversal.\n * - `(aType, axbType)` — type-scoped edge scans (e.g., `findEdges({ aType, axbType })`).\n * - `(axbType, bType)` — scope edges of one relation to a target type.\n */\n\nimport type { IndexSpec } from './types.js';\n\nexport const DEFAULT_CORE_INDEXES: ReadonlyArray<IndexSpec> = Object.freeze([\n { fields: ['aUid'] },\n { fields: ['bUid'] },\n { fields: ['aType'] },\n { fields: ['bType'] },\n { fields: ['aUid', 'axbType'] },\n { fields: ['axbType', 'bUid'] },\n { fields: ['aType', 'axbType'] },\n { fields: ['axbType', 'bType'] },\n]);\n","/**\n * Translator from `IndexSpec` to SQLite `CREATE INDEX` DDL.\n *\n * Shared between the DO SQLite backend (`src/cloudflare/schema.ts`) and the\n * legacy single-table SQLite backend (`src/internal/sqlite-schema.ts`). The\n * two backends differ only in:\n *\n * 1. Their field→column mapping (no `scope` column in the DO schema).\n * 2. Whether a fixed `scope` leading column is prepended to every index\n * (legacy backend only — DO rows are scoped by DO-instance identity).\n *\n * Both differences are handled via the `fieldToColumn` and `leadingColumns`\n * options; the rest of the emission logic is identical.\n *\n * ## JSON path expression indexes\n *\n * Data-field specs (`data.foo`, `data.nested.bar`) compile to\n * `json_extract(\"data\", '$.foo')` expression indexes. The JSON path\n * literal is inlined — not parametrized — so the SQLite query planner can\n * match the index against the expression emitted by the query compiler\n * (which also inlines the literal after this PR). Path components are\n * validated against a safe identifier pattern so inlining is not an\n * injection risk.\n *\n * ## Index naming\n *\n * Names are `{table}_idx_{hash}` where `hash` is a short FNV-1a of a\n * canonicalized spec. This keeps names stable across runs (so\n * `CREATE INDEX IF NOT EXISTS` is idempotent) and prevents collisions\n * between similar specs. The hash includes the field list, per-field\n * direction, and the `where` predicate.\n */\n\nimport { FiregraphError } from '../errors.js';\nimport type { IndexFieldSpec, IndexSpec } from '../types.js';\n\n/**\n * Valid SQLite identifier pattern — used for table and column names.\n * Mirrors the validation in `sqlite-schema.ts` / `cloudflare/schema.ts` so\n * this module doesn't need to import one over the other.\n */\nconst IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * Safe JSON path component. Must match `JSON_PATH_KEY_RE` in the SQLite\n * query compilers — an index is only useful if the query emits an\n * identical `json_extract` expression.\n */\nconst JSON_PATH_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;\n\nfunction quoteIdent(name: string): string {\n if (!IDENT_RE.test(name)) {\n throw new FiregraphError(\n `Invalid SQL identifier in index DDL: ${name}. Must match /^[A-Za-z_][A-Za-z0-9_]*$/.`,\n 'INVALID_INDEX',\n );\n }\n return `\"${name}\"`;\n}\n\n/**\n * FNV-1a 32-bit hash, returned as 8-char hex. Non-cryptographic;\n * used only to produce short, stable index names.\n */\nfunction fnv1a32(str: string): string {\n let h = 0x811c9dc5;\n for (let i = 0; i < str.length; i++) {\n h ^= str.charCodeAt(i);\n h = Math.imul(h, 0x01000193);\n }\n return (h >>> 0).toString(16).padStart(8, '0');\n}\n\nfunction normalizeFields(\n fields: Array<string | IndexFieldSpec>,\n): Array<{ path: string; desc: boolean }> {\n return fields.map((f) => {\n if (typeof f === 'string') return { path: f, desc: false };\n if (!f.path || typeof f.path !== 'string') {\n throw new FiregraphError(\n `IndexSpec field must be a string or { path: string, desc?: boolean }; got ${JSON.stringify(f)}`,\n 'INVALID_INDEX',\n );\n }\n return { path: f.path, desc: !!f.desc };\n });\n}\n\nfunction specFingerprint(spec: IndexSpec, leadingColumns: string[]): string {\n // Canonical form: JSON of normalized fields + leading cols + where.\n // Leading columns are part of the fingerprint so the same spec under\n // two different backends gets distinct names (though in practice only\n // one backend compiles a given spec).\n const normalized = {\n lead: leadingColumns,\n fields: normalizeFields(spec.fields),\n where: spec.where ?? '',\n };\n return fnv1a32(JSON.stringify(normalized));\n}\n\n/**\n * Compile one field path to its SQLite column expression.\n *\n * - Firegraph top-level fields (`aType`, `createdAt`, …) → mapped column.\n * - `data.foo` / `data.foo.bar` → `json_extract(\"data\", '$.foo.bar')`.\n * - `data` alone → `json_extract(\"data\", '$')`.\n */\nfunction compileFieldExpr(path: string, fieldToColumn: Record<string, string>): string {\n const col = fieldToColumn[path];\n if (col) return quoteIdent(col);\n\n if (path === 'data') {\n return `json_extract(\"data\", '$')`;\n }\n if (path.startsWith('data.')) {\n const suffix = path.slice(5);\n const parts = suffix.split('.');\n for (const part of parts) {\n if (!JSON_PATH_KEY_RE.test(part)) {\n throw new FiregraphError(\n `IndexSpec data path \"${path}\" has invalid component \"${part}\". ` +\n `Each component must match /^[A-Za-z_][A-Za-z0-9_-]*$/.`,\n 'INVALID_INDEX',\n );\n }\n }\n // Inline the path literal (no parameter). Validated components above\n // are safe to embed — no quote or escape characters.\n return `json_extract(\"data\", '$.${suffix}')`;\n }\n\n throw new FiregraphError(\n `IndexSpec field \"${path}\" is not a known firegraph field. ` +\n `Use a top-level field (aType, aUid, axbType, bType, bUid, createdAt, updatedAt, v) ` +\n `or a dotted data path like 'data.status'.`,\n 'INVALID_INDEX',\n );\n}\n\nexport interface SqliteIndexDDLOptions {\n /** Target table. */\n table: string;\n /** Map from firegraph field name to SQLite column name. */\n fieldToColumn: Record<string, string>;\n /**\n * Columns prepended to every index's field list (leading ASC). Used by\n * the legacy shared-table SQLite backend to lead every index with\n * `scope`, matching the predicate its query compiler emits.\n *\n * Identifier names only — no JSON paths or expressions.\n */\n leadingColumns?: string[];\n}\n\n/**\n * Emit the `CREATE INDEX IF NOT EXISTS` DDL for one `IndexSpec`.\n *\n * Returns a single SQL string. Name is deterministic (same spec → same\n * name across runs), so re-running the bootstrap is idempotent.\n */\nexport function buildIndexDDL(spec: IndexSpec, options: SqliteIndexDDLOptions): string {\n const { table, fieldToColumn, leadingColumns = [] } = options;\n\n if (!spec.fields || spec.fields.length === 0) {\n throw new FiregraphError('IndexSpec.fields must be a non-empty array', 'INVALID_INDEX');\n }\n\n const normalized = normalizeFields(spec.fields);\n const hash = specFingerprint(spec, leadingColumns);\n const indexName = `${table}_idx_${hash}`;\n\n const cols: string[] = [];\n for (const col of leadingColumns) {\n cols.push(quoteIdent(col));\n }\n for (const f of normalized) {\n const expr = compileFieldExpr(f.path, fieldToColumn);\n cols.push(f.desc ? `${expr} DESC` : expr);\n }\n\n let ddl = `CREATE INDEX IF NOT EXISTS ${quoteIdent(indexName)} ON ${quoteIdent(table)}(${cols.join(', ')})`;\n\n if (spec.where) {\n // The predicate is inlined verbatim. It comes from library/app\n // configuration — never from user data — so we don't attempt to\n // parse, rewrite, or validate it. Callers authoring partial indexes\n // are responsible for writing a valid SQLite WHERE clause.\n ddl += ` WHERE ${spec.where}`;\n }\n\n return ddl;\n}\n\n/**\n * Deduplicate index specs by their deterministic fingerprint. Same spec\n * declared twice (e.g., by core preset + registry entry) collapses to a\n * single DDL statement.\n */\nexport function dedupeIndexSpecs(\n specs: ReadonlyArray<IndexSpec>,\n leadingColumns: string[] = [],\n): IndexSpec[] {\n const seen = new Set<string>();\n const out: IndexSpec[] = [];\n for (const spec of specs) {\n const fp = specFingerprint(spec, leadingColumns);\n if (seen.has(fp)) continue;\n seen.add(fp);\n out.push(spec);\n }\n return out;\n}\n","/**\n * Flat SQLite schema for a single firegraph DO.\n *\n * Each `FiregraphDO` instance owns its own SQLite database and holds exactly\n * one subgraph's triples. Subgraph isolation is physical (one DO per\n * subgraph), so there is no `scope` column — every row in this DO belongs to\n * the same logical scope. This is the Cloudflare-native design: the scope\n * discriminator used by the legacy shared-table SQLite backend\n * (`src/internal/sqlite-schema.ts`) does not exist here.\n *\n * Document IDs:\n * - Nodes: the UID itself\n * - Edges: `shard:aUid:axbType:bUid`\n *\n * Indexes come from two sources and are appended to the DDL list:\n *\n * 1. The core preset (`DEFAULT_CORE_INDEXES`), overridable per-DO via\n * `FiregraphDOOptions.coreIndexes`.\n * 2. Per-registry-entry `indexes` declared on `RegistryEntry` (from code or\n * `meta.json` via entity discovery).\n *\n * Both sets are deduplicated by canonical fingerprint, so declaring the same\n * composite twice (e.g., by preset + registry entry) collapses to one\n * `CREATE INDEX`.\n */\n\nimport { DEFAULT_CORE_INDEXES } from '../default-indexes.js';\nimport { buildIndexDDL, dedupeIndexSpecs } from '../internal/sqlite-index-ddl.js';\nimport type { GraphRegistry, IndexSpec } from '../types.js';\n\nexport const DO_COLUMNS = [\n 'doc_id',\n 'a_type',\n 'a_uid',\n 'axb_type',\n 'b_type',\n 'b_uid',\n 'data',\n 'v',\n 'created_at',\n 'updated_at',\n] as const;\n\nexport type DOColumn = (typeof DO_COLUMNS)[number];\n\n/**\n * Firegraph field name -> SQLite column name. Matches the legacy SQLite\n * backend's mapping so the query planner can compile filters identically.\n */\nexport const DO_FIELD_TO_COLUMN: Record<string, DOColumn> = {\n aType: 'a_type',\n aUid: 'a_uid',\n axbType: 'axb_type',\n bType: 'b_type',\n bUid: 'b_uid',\n v: 'v',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n};\n\nconst IDENT_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;\n\n/**\n * Validate a SQLite identifier (table or column name). Identifier values in\n * this module come from config + constants, never from user data — but we\n * still fail fast if a caller passes a malformed table name, since the value\n * is interpolated directly into DDL.\n */\nexport function validateDOTableName(name: string): void {\n if (!IDENT_RE.test(name)) {\n throw new Error(`Invalid SQL identifier: ${name}. Must match /^[A-Za-z_][A-Za-z0-9_]*$/.`);\n }\n}\n\n/**\n * Double-quote a SQLite identifier, after validating it against the allowed\n * character set. Used in generated SQL to protect against keyword collisions.\n */\nexport function quoteDOIdent(name: string): string {\n validateDOTableName(name);\n return `\"${name}\"`;\n}\n\n/**\n * Options controlling DDL emission for `buildDOSchemaStatements`.\n */\nexport interface BuildDOSchemaOptions {\n /**\n * Replaces the built-in core preset. Defaults to `DEFAULT_CORE_INDEXES`.\n * Pass `[]` to disable core indexes entirely.\n */\n coreIndexes?: IndexSpec[];\n /**\n * Registry contributing per-triple `indexes` declarations. Entries with\n * no `indexes` field are ignored; the rest are flattened and deduplicated\n * against the core preset by canonical fingerprint.\n */\n registry?: GraphRegistry;\n}\n\n/**\n * DDL statements that create the firegraph table and its indexes. Returned\n * as separate statements because DO SQLite's `exec()` runs one statement per\n * call. Run via `FiregraphDO.ensureSchema()` on DO boot.\n *\n * The CREATE TABLE statement is always first; index statements follow in\n * deterministic order. Same specs across runs produce the same statements,\n * so `CREATE INDEX IF NOT EXISTS` is idempotent.\n */\nexport function buildDOSchemaStatements(\n table: string,\n options: BuildDOSchemaOptions = {},\n): string[] {\n const t = quoteDOIdent(table);\n const statements: string[] = [\n `CREATE TABLE IF NOT EXISTS ${t} (\n doc_id TEXT NOT NULL PRIMARY KEY,\n a_type TEXT NOT NULL,\n a_uid TEXT NOT NULL,\n axb_type TEXT NOT NULL,\n b_type TEXT NOT NULL,\n b_uid TEXT NOT NULL,\n data TEXT NOT NULL,\n v INTEGER,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL\n )`,\n ];\n\n const core = options.coreIndexes ?? [...DEFAULT_CORE_INDEXES];\n const fromRegistry = options.registry?.entries().flatMap((e) => e.indexes ?? []) ?? [];\n\n const deduped = dedupeIndexSpecs([...core, ...fromRegistry]);\n for (const spec of deduped) {\n statements.push(buildIndexDDL(spec, { table, fieldToColumn: DO_FIELD_TO_COLUMN }));\n }\n return statements;\n}\n","/**\n * SQL compilation for the DO SQLite backend.\n *\n * Every `FiregraphDO` instance owns one SQLite database holding exactly one\n * subgraph's triples — there is no `scope` column and no scope discriminator\n * on any statement. Contrast with `src/internal/sqlite-sql.ts`, which\n * carries a scope prefix on every read and write for the legacy shared-table\n * D1/DO SQLite backend.\n *\n * Filter compilation, JSON-path validation, and value binding mirror the\n * legacy module so the query planner (`src/query.ts`) emits the same\n * `QueryFilter[]` shape regardless of backend.\n */\n\nimport { FiregraphError } from '../errors.js';\nimport type { UpdatePayload, WritableRecord, WriteMode } from '../internal/backend.js';\nimport {\n compileDataOpsExpr,\n isFirestoreSpecialType,\n validateJsonPathKey,\n} from '../internal/sqlite-data-ops.js';\nimport { assertJsonSafePayload } from '../internal/sqlite-payload-guard.js';\nimport { assertUpdatePayloadExclusive, flattenPatch } from '../internal/write-plan.js';\nimport type { GraphTimestamp } from '../timestamp.js';\nimport { GraphTimestampImpl } from '../timestamp.js';\nimport type { QueryFilter, QueryOptions, StoredGraphRecord } from '../types.js';\nimport { DO_FIELD_TO_COLUMN, quoteDOIdent } from './schema.js';\n\nconst DO_BACKEND_LABEL = 'DO SQLite';\nconst DO_BACKEND_ERR_LABEL = 'DO SQLite backend';\n\n/**\n * Wire representation of a stored record across the DO RPC boundary.\n *\n * Durable Object RPC uses structured clone, which preserves plain data but\n * drops user-defined class prototypes — a `GraphTimestampImpl` from the DO\n * arrives at the client as a plain `{seconds, nanoseconds}` object without\n * its `toMillis()` / `toDate()` methods. To avoid silent `.toMillis is not a\n * function` crashes downstream, records returned from DO RPC carry the two\n * timestamps as plain millisecond numbers in `createdAtMs` / `updatedAtMs`;\n * the client-side backend rewraps them as `GraphTimestampImpl` via\n * `hydrateDORecord` before handing the record to the GraphClient.\n */\nexport interface DORecordWire {\n aType: string;\n aUid: string;\n axbType: string;\n bType: string;\n bUid: string;\n data: Record<string, unknown>;\n v?: number;\n createdAtMs: number;\n updatedAtMs: number;\n}\n\nexport interface CompiledStatement {\n sql: string;\n params: unknown[];\n}\n\n// ---------------------------------------------------------------------------\n// Filter compilation\n// ---------------------------------------------------------------------------\n\n/**\n * Translate a firegraph filter field to either a column reference or a\n * `json_extract(\"data\", '$.<path>')` expression. Built-in fields go\n * straight to their column; `data.<key>[.<key>…]` and bare `data` are\n * projected through `json_extract` with the JSON path **inlined as a\n * string literal** — not parametrized.\n *\n * Inlining matters: SQLite's query planner matches an expression index\n * (`CREATE INDEX … ON tbl(json_extract(\"data\", '$.status'))`) against\n * *textually identical* expressions in the WHERE clause. `json_extract(\n * \"data\", ?)` parametrizes the path and would never hit the index, even\n * though it evaluates to the same value. Inlining here makes the\n * expression literal in the SQL, which is what the index builder in\n * `sqlite-index-ddl.ts` also emits.\n *\n * Inlining is safe: each path component is validated against\n * `JSON_PATH_KEY_RE` (`/^[A-Za-z_][A-Za-z0-9_-]*$/`) before it reaches\n * this function — the pattern excludes every character SQLite would\n * treat as syntax (quote, backslash, dot, bracket, whitespace), so\n * string concatenation can't produce injection.\n */\nfunction compileFieldRef(field: string): { expr: string } {\n const column = DO_FIELD_TO_COLUMN[field];\n if (column) {\n return { expr: quoteDOIdent(column) };\n }\n if (field.startsWith('data.')) {\n const suffix = field.slice(5);\n for (const part of suffix.split('.')) {\n validateJsonPathKey(part, DO_BACKEND_ERR_LABEL);\n }\n return { expr: `json_extract(\"data\", '$.${suffix}')` };\n }\n if (field === 'data') {\n return { expr: `json_extract(\"data\", '$')` };\n }\n throw new FiregraphError(\n `DO SQLite backend cannot resolve filter field: ${field}`,\n 'INVALID_QUERY',\n );\n}\n\n/**\n * Coerce a JS filter/update value into a SQLite-bindable primitive. Firestore\n * special types are rejected loudly because `JSON.stringify` would emit\n * garbage that silently fails to match any stored row. Callers should\n * project to a primitive (e.g. `ts.toMillis()`) before passing in.\n */\nfunction bindValue(value: unknown): unknown {\n if (value === null || value === undefined) return null;\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'bigint' ||\n typeof value === 'boolean'\n ) {\n return value;\n }\n if (value instanceof Date) return value.getTime();\n if (typeof value === 'object') {\n const firestoreType = isFirestoreSpecialType(value);\n if (firestoreType) {\n throw new FiregraphError(\n `DO SQLite backend cannot bind a Firestore ${firestoreType} value — JSON serialization ` +\n `would silently drop fields and the resulting bind would never match a stored row. ` +\n `Convert to a primitive (e.g. \\`ts.toMillis()\\` for Timestamp) before filtering or updating.`,\n 'INVALID_QUERY',\n );\n }\n return JSON.stringify(value);\n }\n return String(value);\n}\n\nfunction compileFilter(filter: QueryFilter, params: unknown[]): string {\n const { expr } = compileFieldRef(filter.field);\n\n switch (filter.op) {\n case '==':\n params.push(bindValue(filter.value));\n return `${expr} = ?`;\n case '!=':\n params.push(bindValue(filter.value));\n return `${expr} != ?`;\n case '<':\n params.push(bindValue(filter.value));\n return `${expr} < ?`;\n case '<=':\n params.push(bindValue(filter.value));\n return `${expr} <= ?`;\n case '>':\n params.push(bindValue(filter.value));\n return `${expr} > ?`;\n case '>=':\n params.push(bindValue(filter.value));\n return `${expr} >= ?`;\n case 'in': {\n const values = asArray(filter.value, 'in');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `${expr} IN (${placeholders})`;\n }\n case 'not-in': {\n const values = asArray(filter.value, 'not-in');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `${expr} NOT IN (${placeholders})`;\n }\n case 'array-contains': {\n params.push(bindValue(filter.value));\n return `EXISTS (SELECT 1 FROM json_each(${expr}) WHERE value = ?)`;\n }\n case 'array-contains-any': {\n const values = asArray(filter.value, 'array-contains-any');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `EXISTS (SELECT 1 FROM json_each(${expr}) WHERE value IN (${placeholders}))`;\n }\n default:\n throw new FiregraphError(\n `DO SQLite backend does not support filter operator: ${String(filter.op)}`,\n 'INVALID_QUERY',\n );\n }\n}\n\nfunction asArray(value: unknown, op: string): unknown[] {\n if (!Array.isArray(value) || value.length === 0) {\n throw new FiregraphError(`Operator \"${op}\" requires a non-empty array value`, 'INVALID_QUERY');\n }\n return value;\n}\n\nfunction compileOrderBy(options: QueryOptions | undefined, _params: unknown[]): string {\n if (!options?.orderBy) return '';\n const { field, direction } = options.orderBy;\n const { expr } = compileFieldRef(field);\n const dir = direction === 'desc' ? 'DESC' : 'ASC';\n return ` ORDER BY ${expr} ${dir}`;\n}\n\nfunction compileLimit(options: QueryOptions | undefined, params: unknown[]): string {\n if (options?.limit === undefined) return '';\n params.push(options.limit);\n return ` LIMIT ?`;\n}\n\n// ---------------------------------------------------------------------------\n// Statement compilation\n// ---------------------------------------------------------------------------\n\n/**\n * SELECT rows matching `filters`. No scope predicate — every row in this\n * DO's database belongs to the same subgraph.\n */\nexport function compileDOSelect(\n table: string,\n filters: QueryFilter[],\n options?: QueryOptions,\n): CompiledStatement {\n const params: unknown[] = [];\n const conditions: string[] = [];\n\n for (const f of filters) {\n conditions.push(compileFilter(f, params));\n }\n\n const where = conditions.length > 0 ? ` WHERE ${conditions.join(' AND ')}` : '';\n let sql = `SELECT * FROM ${quoteDOIdent(table)}${where}`;\n sql += compileOrderBy(options, params);\n sql += compileLimit(options, params);\n\n return { sql, params };\n}\n\n/**\n * SELECT a single row by doc_id. `doc_id` is the PK so this is an O(1)\n * index lookup.\n */\nexport function compileDOSelectByDocId(table: string, docId: string): CompiledStatement {\n return {\n sql: `SELECT * FROM ${quoteDOIdent(table)} WHERE \"doc_id\" = ? LIMIT 1`,\n params: [docId],\n };\n}\n\n/**\n * Compile a `setDoc(record, mode)` call into a single statement.\n *\n * `mode === 'replace'` issues `INSERT OR REPLACE` (full row replacement).\n * `mode === 'merge'` issues `INSERT … ON CONFLICT(doc_id) DO UPDATE SET …`,\n * deep-merging the incoming `data` into the existing JSON via the chained\n * `json_set` / `json_remove` expression produced by `compileDODataOpsExpr`.\n * Sibling keys at every depth survive; arrays are terminal (replaced).\n *\n * `created_at` is re-stamped on every put for both modes (matches the\n * cross-backend contract today).\n */\nexport function compileDOSet(\n table: string,\n docId: string,\n record: WritableRecord,\n nowMillis: number,\n mode: WriteMode,\n): CompiledStatement {\n // See compileSet (sqlite-sql.ts) for rationale — eager validation so the\n // first-insert path can't silently corrupt Firestore special types or\n // drop a DELETE_FIELD sentinel that JSON.stringify would erase.\n assertJsonSafePayload(record.data, DO_BACKEND_LABEL);\n if (mode === 'replace') {\n const sql = `INSERT OR REPLACE INTO ${quoteDOIdent(table)} (\n doc_id, a_type, a_uid, axb_type, b_type, b_uid, data, v, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;\n const params: unknown[] = [\n docId,\n record.aType,\n record.aUid,\n record.axbType,\n record.bType,\n record.bUid,\n JSON.stringify(record.data ?? {}),\n record.v ?? null,\n nowMillis,\n nowMillis,\n ];\n return { sql, params };\n }\n\n const insertParams: unknown[] = [\n docId,\n record.aType,\n record.aUid,\n record.axbType,\n record.bType,\n record.bUid,\n JSON.stringify(record.data ?? {}),\n record.v ?? null,\n nowMillis,\n nowMillis,\n ];\n\n const ops = flattenPatch(record.data ?? {});\n const updateParams: unknown[] = [];\n const dataExpr =\n compileDataOpsExpr(ops, `COALESCE(\"data\", '{}')`, updateParams, DO_BACKEND_ERR_LABEL) ??\n `COALESCE(\"data\", '{}')`;\n\n // See compileSet (sqlite-sql.ts) — COALESCE preserves pre-existing `v`\n // when the incoming record has none, matching Firestore's merge semantics.\n const sql = `INSERT INTO ${quoteDOIdent(table)} (\n doc_id, a_type, a_uid, axb_type, b_type, b_uid, data, v, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ON CONFLICT(doc_id) DO UPDATE SET\n \"a_type\" = excluded.\"a_type\",\n \"a_uid\" = excluded.\"a_uid\",\n \"axb_type\" = excluded.\"axb_type\",\n \"b_type\" = excluded.\"b_type\",\n \"b_uid\" = excluded.\"b_uid\",\n \"data\" = ${dataExpr},\n \"v\" = COALESCE(excluded.\"v\", \"v\"),\n \"created_at\" = excluded.\"created_at\",\n \"updated_at\" = excluded.\"updated_at\"`;\n\n return { sql, params: [...insertParams, ...updateParams] };\n}\n\n/**\n * Compile an `UpdatePayload` into a single UPDATE statement.\n *\n * - `replaceData` overwrites the whole `data` JSON.\n * - `dataOps` applies a deep-path patch via chained `json_remove` /\n * `json_set` — sibling keys at every depth survive; arrays are terminal;\n * delete-ops use `json_remove`.\n * - `v` is set when provided.\n * - `updated_at` is always stamped.\n */\nexport function compileDOUpdate(\n table: string,\n docId: string,\n update: UpdatePayload,\n nowMillis: number,\n): CompiledStatement {\n assertUpdatePayloadExclusive(update);\n const setClauses: string[] = [];\n const params: unknown[] = [];\n\n if (update.replaceData) {\n assertJsonSafePayload(update.replaceData, DO_BACKEND_LABEL);\n setClauses.push(`\"data\" = ?`);\n params.push(JSON.stringify(update.replaceData));\n } else if (update.dataOps && update.dataOps.length > 0) {\n for (const op of update.dataOps) {\n if (!op.delete) assertJsonSafePayload(op.value, DO_BACKEND_LABEL);\n }\n const expr = compileDataOpsExpr(\n update.dataOps,\n `COALESCE(\"data\", '{}')`,\n params,\n DO_BACKEND_ERR_LABEL,\n );\n if (expr !== null) {\n setClauses.push(`\"data\" = ${expr}`);\n }\n }\n\n if (update.v !== undefined) {\n setClauses.push(`\"v\" = ?`);\n params.push(update.v);\n }\n\n setClauses.push(`\"updated_at\" = ?`);\n params.push(nowMillis);\n\n params.push(docId);\n\n return {\n sql: `UPDATE ${quoteDOIdent(table)} SET ${setClauses.join(', ')} WHERE \"doc_id\" = ?`,\n params,\n };\n}\n\nexport function compileDODelete(table: string, docId: string): CompiledStatement {\n return {\n sql: `DELETE FROM ${quoteDOIdent(table)} WHERE \"doc_id\" = ?`,\n params: [docId],\n };\n}\n\n/**\n * DELETE every row in the table. Used when tearing down an entire subgraph\n * DO as part of cascade — the caller discovers the set of DOs to wipe via\n * registry topology (phase 2) and instructs each DO to clear itself.\n */\nexport function compileDODeleteAll(table: string): CompiledStatement {\n return {\n sql: `DELETE FROM ${quoteDOIdent(table)}`,\n params: [],\n };\n}\n\n// ---------------------------------------------------------------------------\n// Row -> record (wire-safe) and hydration\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a SQLite row into a `DORecordWire` — the wire-safe shape returned\n * across DO RPC. Timestamps stay as plain millisecond numbers here; the\n * client-side backend calls `hydrateDORecord` to rewrap them as\n * `GraphTimestampImpl` before surfacing the record to the GraphClient.\n *\n * Splitting serialization from hydration like this is what lets the DO\n * return values safely through structured clone without pretending its\n * output is a full `StoredGraphRecord`.\n */\nexport function rowToDORecord(row: Record<string, unknown>): DORecordWire {\n const dataString = row.data as string | null;\n const data = dataString ? (JSON.parse(dataString) as Record<string, unknown>) : {};\n\n const createdAtMs = toMillis(row.created_at);\n const updatedAtMs = toMillis(row.updated_at);\n\n const record: DORecordWire = {\n aType: row.a_type as string,\n aUid: row.a_uid as string,\n axbType: row.axb_type as string,\n bType: row.b_type as string,\n bUid: row.b_uid as string,\n data,\n createdAtMs,\n updatedAtMs,\n };\n\n if (row.v !== null && row.v !== undefined) {\n record.v = Number(row.v);\n }\n return record;\n}\n\n/**\n * Rewrap a `DORecordWire` as a full `StoredGraphRecord`, restoring\n * `GraphTimestampImpl` instances from the wire-level millisecond numbers.\n * Called by `DORPCBackend` on every record returned from a DO RPC.\n */\nexport function hydrateDORecord(wire: DORecordWire): StoredGraphRecord {\n const record: Record<string, unknown> = {\n aType: wire.aType,\n aUid: wire.aUid,\n axbType: wire.axbType,\n bType: wire.bType,\n bUid: wire.bUid,\n data: wire.data,\n createdAt: GraphTimestampImpl.fromMillis(wire.createdAtMs) as unknown as GraphTimestamp,\n updatedAt: GraphTimestampImpl.fromMillis(wire.updatedAtMs) as unknown as GraphTimestamp,\n };\n if (wire.v !== undefined) {\n record.v = wire.v;\n }\n return record as unknown as StoredGraphRecord;\n}\n\n/**\n * Coerce a timestamp column value to a plain millis number. The schema types\n * `created_at` / `updated_at` as `INTEGER NOT NULL` so the column should\n * always arrive as a number (or possibly a bigint on some SQLite bindings),\n * but a string row value from SQLite (e.g. BigInt.toString fallback) is also\n * accepted. Anything else indicates a corrupt row — throw loudly rather than\n * silently returning 0, which would quietly mask the bug on every read.\n */\nfunction toMillis(value: unknown): number {\n if (typeof value === 'number') return value;\n if (typeof value === 'bigint') return Number(value);\n if (typeof value === 'string') {\n const n = Number(value);\n if (Number.isFinite(n)) return n;\n }\n throw new FiregraphError(\n `DO SQLite row has non-numeric timestamp column: ${typeof value} (${String(value)})`,\n 'INVALID_QUERY',\n );\n}\n","/**\n * Client-side `StorageBackend` that forwards every operation to a\n * `FiregraphDO` instance over Durable Object RPC.\n *\n * One `DORPCBackend` corresponds to one DO — the root graph's DO, or a\n * subgraph's DO. `subgraph()` returns a new `DORPCBackend` bound to a\n * different DO, identified by deriving a new stable name from the chain of\n * parent UIDs and subgraph names. The library uses `namespace.idFromName()`\n * on that key, so two clients with the same key always reach the same DO.\n *\n * Key invariants:\n *\n * - There is no shared table and no `scope` column. Each DO owns its own\n * flat SQLite database; isolation is physical.\n * - Interactive transactions throw `UNSUPPORTED_OPERATION` — holding a\n * synchronous SQLite transaction across async RPC calls would block the\n * DO's single-threaded executor (see `transactionsUnsupported` below).\n * - `findEdgesGlobal` is deliberately left undefined on this class. The\n * `GraphClient` surfaces the generic \"not supported by current storage\n * backend\" error before running any query planning, which is both\n * accurate and sidesteps the misleading `QuerySafetyError` that would\n * otherwise fire for scan-unsafe calls. `createDOClient`'s docstring\n * explains the design rationale (no collection-group index across DOs).\n * - `removeNodeCascade` cascades across DOs: when a registry accessor is\n * wired, the backend walks `registry.getSubgraphTopology(aType)` for the\n * node being removed and destroys every descendant subgraph DO before\n * deleting the node itself. Pass `{ deleteSubcollections: false }` to\n * disable the cross-DO fan-out (parent node is still removed, child DOs\n * are left intact — matching the Firestore/SQLite backends' semantic).\n * Without an accessor (e.g. registry-less clients) it cascades within\n * the current DO only.\n */\n\nimport { FiregraphError } from '../errors.js';\nimport type {\n BatchBackend,\n StorageBackend,\n TransactionBackend,\n UpdatePayload,\n WritableRecord,\n WriteMode,\n} from '../internal/backend.js';\nimport { NODE_RELATION } from '../internal/constants.js';\nimport type {\n BulkOptions,\n BulkResult,\n CascadeResult,\n DynamicGraphClient,\n FindEdgesParams,\n GraphClient,\n GraphReader,\n GraphRegistry,\n QueryFilter,\n QueryOptions,\n StoredGraphRecord,\n} from '../types.js';\nimport type { BatchOp } from './do.js';\nimport type { DORecordWire } from './sql.js';\nimport { hydrateDORecord } from './sql.js';\n\n// ---------------------------------------------------------------------------\n// Minimal DO namespace / stub types\n//\n// We avoid importing `@cloudflare/workers-types` to keep this module usable\n// in any TypeScript consumer — users with workers-types installed get the\n// richer types via declaration merging at their call site.\n// ---------------------------------------------------------------------------\n\nexport interface DurableObjectIdLike {\n toString(): string;\n}\n\n/**\n * The RPC surface this backend calls on the DO stub. Every method matches a\n * `_fg…` method on `FiregraphDO`. Kept structurally typed so users can bring\n * their own subclass without the types having to know about it.\n *\n * Reads return `DORecordWire` (plain data — safe through DO structured\n * clone); the backend rewraps each record via `hydrateDORecord` before\n * handing it to the GraphClient, which expects `GraphTimestampImpl`\n * instances (not plain `{seconds, nanoseconds}` objects).\n */\nexport interface FiregraphStub {\n _fgGetDoc(docId: string): Promise<DORecordWire | null>;\n _fgQuery(filters: QueryFilter[], options?: QueryOptions): Promise<DORecordWire[]>;\n _fgSetDoc(docId: string, record: WritableRecord, mode: WriteMode): Promise<void>;\n _fgUpdateDoc(docId: string, update: UpdatePayload): Promise<void>;\n _fgDeleteDoc(docId: string): Promise<void>;\n _fgBatch(ops: BatchOp[]): Promise<void>;\n _fgRemoveNodeCascade(uid: string): Promise<CascadeResult>;\n _fgBulkRemoveEdges(params: FindEdgesParams, options?: BulkOptions): Promise<BulkResult>;\n _fgDestroy(): Promise<void>;\n}\n\nexport interface FiregraphNamespace {\n idFromName(name: string): DurableObjectIdLike;\n get(id: DurableObjectIdLike): FiregraphStub;\n}\n\n// ---------------------------------------------------------------------------\n// Subgraph name validation\n//\n// The chain of subgraph segments becomes the input to `idFromName()`. Two\n// different paths must produce two different hashes, so segments can't\n// contain the `/` separator — otherwise `('A', 'x/y')` and `('A/x', 'y')`\n// would collide. The `GraphClient` already validates at the public API\n// layer; this is defense-in-depth for direct backend users (traversal,\n// cross-graph hops).\n// ---------------------------------------------------------------------------\n\nfunction validateSegment(value: string, label: string): void {\n if (!value || value.includes('/')) {\n throw new FiregraphError(\n `Invalid ${label} for subgraph: \"${value}\". Must be non-empty and not contain \"/\".`,\n 'INVALID_SUBGRAPH',\n );\n }\n}\n\n// ---------------------------------------------------------------------------\n// Transaction backend — always throws\n// ---------------------------------------------------------------------------\n\n/**\n * Interactive transactions across DO RPC would require holding a synchronous\n * SQLite transaction open across multiple async RPC round-trips, which blocks\n * the DO's single-threaded executor for the duration. That's incompatible\n * with the runtime's fairness model, so this backend refuses `runTransaction`\n * outright. Callers should either restructure read-then-conditional-write\n * logic as an explicit `read → decide → batch` sequence, or use `batch()`\n * for atomic multi-write patterns.\n */\nfunction transactionsUnsupported(): FiregraphError {\n return new FiregraphError(\n 'Interactive transactions are not supported by the Cloudflare DO backend. ' +\n 'Use `batch()` for atomic multi-write commits, or restructure the read-then-conditional-write ' +\n 'as an explicit read → decide → batch sequence.',\n 'UNSUPPORTED_OPERATION',\n );\n}\n\n// ---------------------------------------------------------------------------\n// Batch backend — buffers locally, submits one RPC on commit\n// ---------------------------------------------------------------------------\n\nclass DORPCBatchBackend implements BatchBackend {\n private readonly ops: BatchOp[] = [];\n\n constructor(private readonly getStub: () => FiregraphStub) {}\n\n setDoc(docId: string, record: WritableRecord, mode: WriteMode): void {\n this.ops.push({ kind: 'set', docId, record, mode });\n }\n\n updateDoc(docId: string, update: UpdatePayload): void {\n this.ops.push({ kind: 'update', docId, update });\n }\n\n deleteDoc(docId: string): void {\n this.ops.push({ kind: 'delete', docId });\n }\n\n async commit(): Promise<void> {\n if (this.ops.length === 0) return;\n // Pass a shallow copy so that clearing the local buffer after commit\n // doesn't mutate what the stub received. Over real DO RPC the array is\n // already copied via structured clone; in-process tests and any future\n // in-memory stub see the copy explicitly.\n const ops = this.ops.slice();\n this.ops.length = 0;\n await this.getStub()._fgBatch(ops);\n }\n}\n\n// ---------------------------------------------------------------------------\n// StorageBackend implementation\n// ---------------------------------------------------------------------------\n\nexport interface DORPCBackendOptions {\n /** Scope path (names-only chain, used for `allowedIn`). Default: `''`. */\n scopePath?: string;\n /**\n * Opaque storage key used to derive the DO instance via\n * `namespace.idFromName(storageKey)`. Defaults to the root key passed to\n * `createDOClient` when the backend is first created.\n */\n storageKey: string;\n /**\n * Live registry accessor used by `removeNodeCascade` to consult the\n * subgraph topology and fan out `_fgDestroy` calls to child subgraph DOs.\n *\n * A function (not a snapshot) so that dynamic-registry clients see the\n * latest definitions after `reloadRegistry()`. Wired by `createDOClient`\n * via a forward reference to the constructed `GraphClient`. When\n * `undefined`, cross-DO cascade is disabled and `removeNodeCascade`\n * cascades within this DO only.\n * @internal\n */\n registryAccessor?: () => GraphRegistry | undefined;\n /**\n * Factory used by `createSiblingClient` to construct a peer `GraphClient`\n * that shares this client's namespace, registry, and other options but\n * targets a different root DO. Wired by `createDOClient`. Leaving it\n * `undefined` (e.g. when `DORPCBackend` is instantiated directly) disables\n * sibling-client construction — `createSiblingClient` will throw.\n *\n * The union return type mirrors `createDOClient`'s two overloads: dynamic\n * mode yields a `DynamicGraphClient`, everything else yields a plain\n * `GraphClient`. `createSiblingClient` narrows at the boundary via its\n * own overload signatures.\n * @internal\n */\n makeSiblingClient?: (siblingStorageKey: string) => GraphClient | DynamicGraphClient;\n}\n\nexport class DORPCBackend implements StorageBackend {\n readonly collectionPath = 'firegraph';\n readonly scopePath: string;\n /** @internal */\n readonly storageKey: string;\n /** @internal */\n readonly namespace: FiregraphNamespace;\n private readonly registryAccessor?: () => GraphRegistry | undefined;\n /** @internal — see `DORPCBackendOptions.makeSiblingClient` for the union-type rationale. */\n readonly makeSiblingClient?: (siblingStorageKey: string) => GraphClient | DynamicGraphClient;\n private cachedStub: FiregraphStub | null = null;\n\n constructor(namespace: FiregraphNamespace, options: DORPCBackendOptions) {\n this.namespace = namespace;\n this.scopePath = options.scopePath ?? '';\n this.storageKey = options.storageKey;\n this.registryAccessor = options.registryAccessor;\n this.makeSiblingClient = options.makeSiblingClient;\n }\n\n private get stub(): FiregraphStub {\n if (!this.cachedStub) {\n const id = this.namespace.idFromName(this.storageKey);\n this.cachedStub = this.namespace.get(id);\n }\n return this.cachedStub;\n }\n\n // --- Reads ---\n\n async getDoc(docId: string): Promise<StoredGraphRecord | null> {\n const wire = await this.stub._fgGetDoc(docId);\n return wire ? hydrateDORecord(wire) : null;\n }\n\n async query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]> {\n const wires = await this.stub._fgQuery(filters, options);\n return wires.map(hydrateDORecord);\n }\n\n // --- Writes ---\n\n async setDoc(docId: string, record: WritableRecord, mode: WriteMode): Promise<void> {\n return this.stub._fgSetDoc(docId, record, mode);\n }\n\n async updateDoc(docId: string, update: UpdatePayload): Promise<void> {\n return this.stub._fgUpdateDoc(docId, update);\n }\n\n async deleteDoc(docId: string): Promise<void> {\n return this.stub._fgDeleteDoc(docId);\n }\n\n // --- Transactions / batches ---\n\n async runTransaction<T>(_fn: (tx: TransactionBackend) => Promise<T>): Promise<T> {\n // Structurally surface the unsupported error — the tx argument passed to\n // `_fn` would throw on every call anyway, but we fail earlier so callers\n // don't discover the limitation mid-transaction.\n void _fn;\n throw transactionsUnsupported();\n }\n\n createBatch(): BatchBackend {\n return new DORPCBatchBackend(() => this.stub);\n }\n\n // --- Subgraphs ---\n\n subgraph(parentNodeUid: string, name: string): StorageBackend {\n validateSegment(parentNodeUid, 'parentNodeUid');\n validateSegment(name, 'subgraph name');\n const newStorageKey = `${this.storageKey}/${parentNodeUid}/${name}`;\n const newScopePath = this.scopePath ? `${this.scopePath}/${name}` : name;\n return new DORPCBackend(this.namespace, {\n scopePath: newScopePath,\n storageKey: newStorageKey,\n // Subgraph backends share the same live registry accessor so a cascade\n // invoked on a subgraph client still fans out correctly. The sibling\n // factory is also carried forward so `createSiblingClient` works from\n // any subgraph client in the chain.\n registryAccessor: this.registryAccessor,\n makeSiblingClient: this.makeSiblingClient,\n });\n }\n\n // --- Cascade & bulk ---\n\n async removeNodeCascade(\n uid: string,\n reader: GraphReader,\n options?: BulkOptions,\n ): Promise<CascadeResult> {\n // Cross-DO cascade. When a registry is wired and the caller wants\n // subcollections (the default, `deleteSubcollections !== false`), walk\n // the subgraph topology for this node's type and wipe every descendant\n // DO before deleting the node itself. This mirrors the Firestore and\n // SQLite backends, which honor the same flag to recurse into nested\n // subgraphs. Without an accessor we fall back to DO-local cascade only\n // — each DO owns its own scope, and registry-less clients have no way\n // to discover descendants.\n //\n // We need the node's aType to know what subgraphs to look for. That\n // means a `getNode` round-trip before touching any child DO; the reader\n // is the client that owns this backend, so the round-trip hits *this*\n // DO and stays cheap. If the node doesn't exist there's nothing to\n // cascade across — skip straight to the local cascade, which will\n // report `nodeDeleted: false`.\n const shouldDeleteSubgraphs = options?.deleteSubcollections !== false;\n const registry = this.registryAccessor?.();\n if (shouldDeleteSubgraphs && registry) {\n const node = await reader.getNode(uid);\n if (node) {\n const topology = registry.getSubgraphTopology(node.aType);\n for (const entry of topology) {\n // `getSubgraphTopology` only returns entries with a `targetGraph`.\n // The non-null assertion encodes that invariant — a missing value\n // here is a registry-construction bug, not a runtime data issue.\n const target = entry.targetGraph!;\n const childBackend = this.subgraph(uid, target) as DORPCBackend;\n await childBackend.destroyRecursively(registry);\n }\n }\n }\n return this.stub._fgRemoveNodeCascade(uid);\n }\n\n async bulkRemoveEdges(\n params: FindEdgesParams,\n _reader: GraphReader,\n options?: BulkOptions,\n ): Promise<BulkResult> {\n void _reader;\n return this.stub._fgBulkRemoveEdges(params, options);\n }\n\n // --- Cross-scope queries ---\n //\n // `findEdgesGlobal` is deliberately NOT defined on this class. The\n // GraphClient checks for its presence before running query planning and\n // throws `UNSUPPORTED_OPERATION` when absent, giving the caller an\n // immediate, accurate error. Defining the method with a throwing body\n // would only surface the same error AFTER `checkQuerySafety` had already\n // fired — and for scan-unsafe calls that results in a misleading\n // `QuerySafetyError` (\"add filters like aUid+axbType\") when no filter\n // combination would actually make the call work on this backend. See the\n // \"What's not supported\" section in `createDOClient` for the design\n // rationale (no collection-group index across DOs).\n\n // --- Destroy helpers ---\n\n /**\n * Wipe this DO's storage. The DO itself can't be deleted — its ID\n * persists forever — but its rows can be emptied, which is what the\n * cascade walk does on every descendant subgraph DO.\n *\n * Exposed on the concrete class (not `StorageBackend`) so generic\n * backend code doesn't reach for it.\n */\n async destroy(): Promise<void> {\n await this.stub._fgDestroy();\n }\n\n /**\n * Tear down every descendant subgraph DO, then wipe this DO's own rows.\n *\n * Invoked by cross-DO cascade: for each node in this DO we enumerate the\n * subgraph topology and recurse into child DOs depth-first before\n * wiping the current DO. The current DO's own rows are destroyed last so\n * that a partial failure mid-recursion leaves the caller's reader able\n * to discover what's still present.\n *\n * @internal\n */\n async destroyRecursively(registry: GraphRegistry): Promise<void> {\n // Enumerate every node (self-loop) in this DO. We only need nodes —\n // edges don't own subgraph children, only nodes do.\n const nodes = await this.query([{ field: 'axbType', op: '==', value: NODE_RELATION }]);\n for (const node of nodes) {\n const topology = registry.getSubgraphTopology(node.aType);\n for (const entry of topology) {\n // `getSubgraphTopology` only returns entries with a `targetGraph` —\n // see the matching assertion in `removeNodeCascade` above.\n const target = entry.targetGraph!;\n const childBackend = this.subgraph(node.aUid, target) as DORPCBackend;\n await childBackend.destroyRecursively(registry);\n }\n }\n await this.destroy();\n }\n}\n","import { createHash } from 'node:crypto';\n\nimport { SHARD_SEPARATOR } from './internal/constants.js';\n\nexport function computeNodeDocId(uid: string): string {\n return uid;\n}\n\nexport function computeEdgeDocId(aUid: string, axbType: string, bUid: string): string {\n const composite = `${aUid}${SHARD_SEPARATOR}${axbType}${SHARD_SEPARATOR}${bUid}`;\n const hash = createHash('sha256').update(composite).digest('hex');\n const shard = hash[0];\n return `${shard}${SHARD_SEPARATOR}${aUid}${SHARD_SEPARATOR}${axbType}${SHARD_SEPARATOR}${bUid}`;\n}\n","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 { 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","/**\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 { computeEdgeDocId } from './docid.js';\nimport { InvalidQueryError } from './errors.js';\nimport { BUILTIN_FIELDS, DEFAULT_QUERY_LIMIT, NODE_RELATION } from './internal/constants.js';\nimport type { FindEdgesParams, FindNodesParams, QueryFilter, QueryPlan } from './types.js';\n\nexport function buildEdgeQueryPlan(params: FindEdgesParams): QueryPlan {\n const { aType, aUid, axbType, bType, bUid, limit, orderBy } = params;\n\n if (aUid && axbType && bUid && !params.where?.length) {\n return { strategy: 'get', docId: computeEdgeDocId(aUid, axbType, bUid) };\n }\n\n const filters: QueryFilter[] = [];\n\n if (aType) filters.push({ field: 'aType', op: '==', value: aType });\n if (aUid) filters.push({ field: 'aUid', op: '==', value: aUid });\n if (axbType) filters.push({ field: 'axbType', op: '==', value: axbType });\n if (bType) filters.push({ field: 'bType', op: '==', value: bType });\n if (bUid) filters.push({ field: 'bUid', op: '==', value: bUid });\n\n if (params.where) {\n for (const clause of params.where) {\n const field = BUILTIN_FIELDS.has(clause.field)\n ? clause.field\n : clause.field.startsWith('data.')\n ? clause.field\n : `data.${clause.field}`;\n filters.push({ field, op: clause.op, value: clause.value });\n }\n }\n\n if (filters.length === 0) {\n throw new InvalidQueryError('findEdges requires at least one filter parameter');\n }\n\n // limit: undefined → apply DEFAULT_QUERY_LIMIT\n // limit: 0 → no limit (unlimited, used by internal bulk operations)\n // limit: N → use N\n const effectiveLimit = limit === undefined ? DEFAULT_QUERY_LIMIT : limit || undefined;\n return { strategy: 'query', filters, options: { limit: effectiveLimit, orderBy } };\n}\n\nexport function buildNodeQueryPlan(params: FindNodesParams): QueryPlan {\n const { aType, limit, orderBy } = params;\n\n const filters: QueryFilter[] = [\n { field: 'aType', op: '==', value: aType },\n { field: 'axbType', op: '==', value: NODE_RELATION },\n ];\n\n if (params.where) {\n for (const clause of params.where) {\n const field = BUILTIN_FIELDS.has(clause.field)\n ? clause.field\n : clause.field.startsWith('data.')\n ? clause.field\n : `data.${clause.field}`;\n filters.push({ field, op: clause.op, value: clause.value });\n }\n }\n\n const effectiveLimit = limit === undefined ? DEFAULT_QUERY_LIMIT : limit || undefined;\n return { strategy: 'query', filters, options: { limit: effectiveLimit, orderBy } };\n}\n","import { BUILTIN_FIELDS } from './internal/constants.js';\nimport type { QueryFilter } from './types.js';\n\n/**\n * Result of analyzing a query for collection scan risk.\n */\nexport interface QuerySafetyResult {\n /** Whether the query matches a known indexed pattern. */\n safe: boolean;\n /** Human-readable explanation when the query is unsafe. */\n reason?: string;\n}\n\n/**\n * Known composite index patterns that prevent full collection scans.\n * Each pattern is a set of field names that must ALL be present in the\n * query filters. Order within the set doesn't matter — what matters is\n * that the Firestore composite index covers the combination.\n *\n * These correspond to the indexes in firestore.indexes.json:\n * (aUid, axbType) — forward edge lookup\n * (axbType, bUid) — reverse edge lookup\n * (aType, axbType) — type-scoped queries + findNodes\n * (axbType, bType) — edge type + target type\n */\nconst SAFE_INDEX_PATTERNS: ReadonlyArray<ReadonlySet<string>> = [\n new Set(['aUid', 'axbType']),\n new Set(['axbType', 'bUid']),\n new Set(['aType', 'axbType']),\n new Set(['axbType', 'bType']),\n];\n\n/**\n * Analyzes a set of query filters to determine whether the query would\n * likely cause a full collection scan on Firestore Enterprise.\n *\n * A query is considered \"safe\" if the builtin fields present in the filters\n * match at least one known composite index pattern. Queries that only use\n * `data.*` fields without a safe base pattern are flagged as unsafe.\n */\nexport function analyzeQuerySafety(filters: QueryFilter[]): QuerySafetyResult {\n // Extract the set of builtin fields being filtered on (equality checks are\n // the primary index-usable operations, but we're generous here and count\n // any filter on a builtin field as potentially index-backed).\n const builtinFieldsPresent = new Set<string>();\n let hasDataFilters = false;\n\n for (const f of filters) {\n if (BUILTIN_FIELDS.has(f.field)) {\n builtinFieldsPresent.add(f.field);\n } else {\n // data.* or other non-builtin fields\n hasDataFilters = true;\n }\n }\n\n // Check if the builtin fields match any known safe index pattern.\n // A pattern is \"matched\" if all fields in the pattern are present in the query.\n for (const pattern of SAFE_INDEX_PATTERNS) {\n let matched = true;\n for (const field of pattern) {\n if (!builtinFieldsPresent.has(field)) {\n matched = false;\n break;\n }\n }\n if (matched) {\n // Even with data.* filters, the base index narrows the scan significantly.\n // The data.* filters are applied as post-filters on the index results.\n return { safe: true };\n }\n }\n\n // No safe pattern matched — build an explanation.\n const presentFields = [...builtinFieldsPresent];\n if (presentFields.length === 0 && hasDataFilters) {\n return {\n safe: false,\n reason:\n 'Query filters only use data.* fields with no builtin field constraints. ' +\n 'This requires a full collection scan. Add aType, aUid, axbType, bType, or bUid filters, ' +\n 'or set allowCollectionScan: true.',\n };\n }\n\n if (hasDataFilters) {\n return {\n safe: false,\n reason:\n `Query filters on [${presentFields.join(', ')}] do not match any indexed pattern. ` +\n 'data.* filters without an indexed base require a full collection scan. ' +\n `Safe patterns: (aUid + axbType), (axbType + bUid), (aType + axbType), (axbType + bType). ` +\n 'Set allowCollectionScan: true to override.',\n };\n }\n\n return {\n safe: false,\n reason:\n `Query filters on [${presentFields.join(', ')}] do not match any indexed pattern. ` +\n 'This may cause a full collection scan on Firestore Enterprise. ' +\n `Safe patterns: (aUid + axbType), (axbType + bUid), (aType + axbType), (axbType + bType). ` +\n 'Set allowCollectionScan: true to override.',\n };\n}\n","import { computeEdgeDocId, computeNodeDocId } from './docid.js';\nimport { QuerySafetyError } from './errors.js';\nimport type { TransactionBackend, WritableRecord } from './internal/backend.js';\nimport { NODE_RELATION } from './internal/constants.js';\nimport { 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 { 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 BulkOptions,\n BulkResult,\n CascadeResult,\n DefineTypeOptions,\n DynamicGraphClient,\n DynamicRegistryConfig,\n EdgeTopology,\n FindEdgesParams,\n FindNodesParams,\n GraphBatch,\n GraphClient,\n GraphClientOptions,\n GraphReader,\n GraphRegistry,\n GraphTransaction,\n MigrationExecutor,\n MigrationFn,\n MigrationWriteBack,\n QueryFilter,\n QueryOptions,\n ScanProtection,\n StoredGraphRecord,\n} from './types.js';\n\nconst RESERVED_TYPE_NAMES = new Set([META_NODE_TYPE, META_EDGE_TYPE]);\n\nfunction buildWritableNodeRecord(\n aType: string,\n uid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid: uid, axbType: NODE_RELATION, bType: aType, bUid: uid, data };\n}\n\nfunction buildWritableEdgeRecord(\n aType: string,\n aUid: string,\n axbType: string,\n bType: string,\n bUid: string,\n data: Record<string, unknown>,\n): WritableRecord {\n return { aType, aUid, axbType, bType, bUid, data };\n}\n\nexport class GraphClientImpl implements DynamicGraphClient {\n readonly scanProtection: ScanProtection;\n\n // Static mode\n private readonly staticRegistry?: GraphRegistry;\n\n // Dynamic mode\n private readonly dynamicConfig?: DynamicRegistryConfig;\n private readonly bootstrapRegistry?: GraphRegistry;\n private dynamicRegistry?: GraphRegistry;\n private readonly metaBackend?: StorageBackend;\n\n // Migration settings\n private readonly globalWriteBack: MigrationWriteBack;\n private readonly migrationSandbox?: MigrationExecutor;\n\n constructor(\n private readonly backend: StorageBackend,\n options?: GraphClientOptions,\n /** @internal Optional pre-built meta-backend (used by subgraph clones). */\n metaBackend?: StorageBackend,\n ) {\n this.globalWriteBack = options?.migrationWriteBack ?? 'off';\n this.migrationSandbox = options?.migrationSandbox;\n\n if (options?.registryMode) {\n this.dynamicConfig = options.registryMode;\n this.bootstrapRegistry = createBootstrapRegistry();\n if (options.registry) {\n this.staticRegistry = options.registry;\n }\n this.metaBackend = metaBackend;\n } else {\n this.staticRegistry = options?.registry;\n }\n\n this.scanProtection = options?.scanProtection ?? 'error';\n }\n\n // ---------------------------------------------------------------------------\n // Backend access (exposed for traversal helpers and subgraph cloning)\n // ---------------------------------------------------------------------------\n\n /** @internal */\n getBackend(): StorageBackend {\n return this.backend;\n }\n\n /**\n * 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 // Bulk operations\n // ---------------------------------------------------------------------------\n\n async removeNodeCascade(uid: string, options?: BulkOptions): Promise<CascadeResult> {\n return this.backend.removeNodeCascade(uid, this, options);\n }\n\n async bulkRemoveEdges(params: FindEdgesParams, options?: BulkOptions): Promise<BulkResult> {\n return this.backend.bulkRemoveEdges(params, this, options);\n }\n\n // ---------------------------------------------------------------------------\n // Dynamic registry methods\n // ---------------------------------------------------------------------------\n\n async defineNodeType(\n name: string,\n jsonSchema: object,\n description?: string,\n options?: DefineTypeOptions,\n ): Promise<void> {\n if (!this.dynamicConfig) {\n throw new DynamicRegistryError(\n 'defineNodeType() is only available in dynamic registry mode. ' +\n 'Pass registryMode: { mode: \"dynamic\" } to createGraphClient().',\n );\n }\n\n if (RESERVED_TYPE_NAMES.has(name)) {\n throw new DynamicRegistryError(\n `Cannot define type \"${name}\": this name is reserved for the meta-registry.`,\n );\n }\n\n if (this.staticRegistry?.lookup(name, NODE_RELATION, name)) {\n throw new DynamicRegistryError(\n `Cannot define node type \"${name}\": already defined in the static registry.`,\n );\n }\n\n const uid = generateDeterministicUid(META_NODE_TYPE, name);\n const data: Record<string, unknown> = { name, jsonSchema };\n if (description !== undefined) data.description = description;\n if (options?.titleField !== undefined) data.titleField = options.titleField;\n if (options?.subtitleField !== undefined) data.subtitleField = options.subtitleField;\n if (options?.viewTemplate !== undefined) data.viewTemplate = options.viewTemplate;\n if (options?.viewCss !== undefined) data.viewCss = options.viewCss;\n if (options?.allowedIn !== undefined) data.allowedIn = options.allowedIn;\n if (options?.migrationWriteBack !== undefined)\n data.migrationWriteBack = options.migrationWriteBack;\n if (options?.migrations !== undefined) {\n data.migrations = await this.serializeMigrations(options.migrations);\n }\n\n await this.putNode(META_NODE_TYPE, uid, data);\n }\n\n async defineEdgeType(\n name: string,\n topology: EdgeTopology,\n jsonSchema?: object,\n description?: string,\n options?: DefineTypeOptions,\n ): Promise<void> {\n if (!this.dynamicConfig) {\n throw new DynamicRegistryError(\n 'defineEdgeType() is only available in dynamic registry mode. ' +\n 'Pass registryMode: { mode: \"dynamic\" } to createGraphClient().',\n );\n }\n\n if (RESERVED_TYPE_NAMES.has(name)) {\n throw new DynamicRegistryError(\n `Cannot define type \"${name}\": this name is reserved for the meta-registry.`,\n );\n }\n\n if (this.staticRegistry) {\n const fromTypes = Array.isArray(topology.from) ? topology.from : [topology.from];\n const toTypes = Array.isArray(topology.to) ? topology.to : [topology.to];\n for (const aType of fromTypes) {\n for (const bType of toTypes) {\n if (this.staticRegistry.lookup(aType, name, bType)) {\n throw new DynamicRegistryError(\n `Cannot define edge type \"${name}\" for (${aType}) -> (${bType}): already defined in the static registry.`,\n );\n }\n }\n }\n }\n\n const uid = generateDeterministicUid(META_EDGE_TYPE, name);\n const data: Record<string, unknown> = {\n name,\n from: topology.from,\n to: topology.to,\n };\n if (jsonSchema !== undefined) data.jsonSchema = jsonSchema;\n if (topology.inverseLabel !== undefined) data.inverseLabel = topology.inverseLabel;\n if (topology.targetGraph !== undefined) data.targetGraph = topology.targetGraph;\n if (description !== undefined) data.description = description;\n if (options?.titleField !== undefined) data.titleField = options.titleField;\n if (options?.subtitleField !== undefined) data.subtitleField = options.subtitleField;\n if (options?.viewTemplate !== undefined) data.viewTemplate = options.viewTemplate;\n if (options?.viewCss !== undefined) data.viewCss = options.viewCss;\n if (options?.allowedIn !== undefined) data.allowedIn = options.allowedIn;\n if (options?.migrationWriteBack !== undefined)\n data.migrationWriteBack = options.migrationWriteBack;\n if (options?.migrations !== undefined) {\n data.migrations = await this.serializeMigrations(options.migrations);\n }\n\n await this.putNode(META_EDGE_TYPE, uid, data);\n }\n\n async reloadRegistry(): Promise<void> {\n if (!this.dynamicConfig) {\n throw new DynamicRegistryError(\n 'reloadRegistry() is only available in dynamic registry mode. ' +\n 'Pass registryMode: { mode: \"dynamic\" } to createGraphClient().',\n );\n }\n\n const reader = this.createMetaReader();\n const dynamicOnly = await createRegistryFromGraph(reader, this.migrationSandbox);\n\n if (this.staticRegistry) {\n this.dynamicRegistry = createMergedRegistry(this.staticRegistry, dynamicOnly);\n } else {\n this.dynamicRegistry = dynamicOnly;\n }\n }\n\n private async serializeMigrations(\n migrations: Array<{ fromVersion: number; toVersion: number; up: MigrationFn | string }>,\n ): Promise<Array<{ fromVersion: number; toVersion: number; up: string }>> {\n const result = migrations.map((m) => {\n const source = typeof m.up === 'function' ? m.up.toString() : m.up;\n return { fromVersion: m.fromVersion, toVersion: m.toVersion, up: source };\n });\n await Promise.all(result.map((m) => precompileSource(m.up, this.migrationSandbox)));\n return result;\n }\n\n /**\n * Build a `GraphReader` over the meta-backend. If meta lives in the same\n * collection as the main backend, `this` is returned directly.\n */\n private createMetaReader(): GraphReader {\n if (!this.metaBackend) return this;\n\n const backend = this.metaBackend;\n\n const executeMetaQuery = (\n filters: QueryFilter[],\n options?: QueryOptions,\n ): Promise<StoredGraphRecord[]> => backend.query(filters, options);\n\n return {\n async getNode(uid: string): Promise<StoredGraphRecord | null> {\n return backend.getDoc(computeNodeDocId(uid));\n },\n async getEdge(\n aUid: string,\n axbType: string,\n bUid: string,\n ): Promise<StoredGraphRecord | null> {\n return backend.getDoc(computeEdgeDocId(aUid, axbType, bUid));\n },\n async edgeExists(aUid: string, axbType: string, bUid: string): Promise<boolean> {\n const record = await backend.getDoc(computeEdgeDocId(aUid, axbType, bUid));\n return record !== null;\n },\n async findEdges(params: FindEdgesParams): Promise<StoredGraphRecord[]> {\n const plan = buildEdgeQueryPlan(params);\n if (plan.strategy === 'get') {\n const record = await backend.getDoc(plan.docId);\n return record ? [record] : [];\n }\n return executeMetaQuery(plan.filters, plan.options);\n },\n async findNodes(params: FindNodesParams): Promise<StoredGraphRecord[]> {\n const plan = buildNodeQueryPlan(params);\n if (plan.strategy === 'get') {\n const record = await backend.getDoc(plan.docId);\n return record ? [record] : [];\n }\n return executeMetaQuery(plan.filters, plan.options);\n },\n };\n }\n}\n\n/**\n * Create a `GraphClient` backed by an arbitrary `StorageBackend`.\n *\n * Used by backend-specific factories (e.g. `createDOClient` in\n * `firegraph/cloudflare`) — most callers should use the higher-level\n * `createGraphClient(firestore, ...)` overload below for Firestore, or the\n * Cloudflare factory for DO-backed graphs.\n */\nexport function createGraphClientFromBackend(\n backend: StorageBackend,\n options?: GraphClientOptions,\n metaBackend?: StorageBackend,\n): GraphClient | DynamicGraphClient {\n return new GraphClientImpl(backend, options, metaBackend) as GraphClient | DynamicGraphClient;\n}\n","/**\n * `createDOClient` — the user-facing factory for the Cloudflare DO backend.\n *\n * Given a Durable Object namespace binding and a stable root key, returns a\n * `GraphClient` that speaks to a `FiregraphDO` instance. The root key is\n * hashed via `namespace.idFromName()` to derive the DO ID, so two clients\n * instantiated with the same key always reach the same DO — that's how we\n * achieve \"subgraphs are auto-provisioned\" without a separate allocation\n * step. Subsequent `.subgraph(uid, name)` calls derive child DO IDs from the\n * extended key chain (`${key}/${uid}/${name}`).\n *\n * ## What's supported\n *\n * - **Static registries.** Pass `registry` and every read/write validates\n * and migrates exactly like the Firestore/SQLite backends.\n * - **Dynamic registries.** Pass `registryMode: { mode: 'dynamic' }` and\n * call `defineNodeType` / `defineEdgeType` / `reloadRegistry` as with\n * any other backend. Meta-types live in the root DO by default; pass\n * `registryMode: { mode: 'dynamic', collection: 'meta-root' }` to put\n * them in a separately-addressed DO. Merged mode (static `registry`\n * plus `registryMode`) is also supported.\n * - **Cross-DO cascade.** `removeNodeCascade` consults the registry's\n * subgraph topology and wipes every descendant subgraph DO before\n * deleting the node. Requires a registry; without one it cascades\n * within the current DO only. Pass `{ deleteSubcollections: false }` to\n * keep the node removal but leave every descendant DO intact (mirrors\n * the Firestore/SQLite `bulk` option). In dynamic mode the accessor is\n * live, so cascading a node whose subgraph topology was just added via\n * `defineEdgeType` correctly fans out to the new descendants — **but\n * only after a `reloadRegistry()` call**. A cascade invoked between\n * `defineEdgeType` and `reloadRegistry` sees only the pre-define\n * topology and silently skips newly-declared subgraphs. This is the\n * same trade-off every dynamic-registry backend makes.\n * - **Static migrations** on registry entries (`migrations`,\n * `migrationWriteBack`, `migrationSandbox`) run in-process on the\n * Worker and don't cross the DO RPC boundary. The read-path migration\n * pipeline lives in `GraphClient`, not in `FiregraphDO`.\n *\n * ## Performance note on cascade\n *\n * Cross-DO cascade instantiates (via `namespace.get`) every declared child\n * subgraph DO even when it's empty — Durable Objects have no \"does this ID\n * exist\" primitive, and the cheapest way to tear a DO down is to issue one\n * RPC. For a node with N declared subgraph segments, expect N+1 RPCs per\n * cascade (one per child DO wipe, one for the parent). Topology width is\n * typically small and bounded by registry size, but keep it in mind for\n * wide fan-out designs.\n *\n * ## What's not supported\n *\n * - **Interactive transactions.** Would require pinning a SQLite\n * transaction open across async RPC calls — see `backend.ts` for the\n * rationale. Use `batch()` for atomic multi-write commits.\n * - **`findEdgesGlobal`.** Cross-DO collection-group queries don't map\n * onto \"one DO owns one subgraph's rows\" — each subgraph is a separate\n * DO with private SQLite and there's no namespace-wide catalog. The\n * method is intentionally undefined on the backend so `GraphClient`\n * surfaces a generic `UNSUPPORTED_OPERATION` error immediately, before\n * any query planning. Callers that need this should maintain an\n * application-level index DO or run an explicit traversal via\n * `client.subgraph(...)`.\n *\n * ## Binding example\n *\n * ```ts\n * // worker.ts\n * export { FiregraphDO } from '@typicalday/firegraph/cloudflare';\n *\n * export default {\n * async fetch(req: Request, env: Env) {\n * const client = createDOClient(env.GRAPH, 'main', { registry });\n * const project = await client.getNode('project', projectUid);\n * return Response.json(project);\n * },\n * };\n * ```\n *\n * ```toml\n * # wrangler.toml\n * [[durable_objects.bindings]]\n * name = \"GRAPH\"\n * class_name = \"FiregraphDO\"\n *\n * [[migrations]]\n * tag = \"v1\"\n * new_sqlite_classes = [\"FiregraphDO\"]\n * ```\n */\n\nimport type { GraphClientImpl } from '../client.js';\nimport { createGraphClientFromBackend } from '../client.js';\nimport { FiregraphError } from '../errors.js';\nimport type { StorageBackend } from '../internal/backend.js';\nimport type {\n DynamicGraphClient,\n DynamicRegistryConfig,\n GraphClient,\n GraphClientOptions,\n GraphRegistry,\n} from '../types.js';\nimport type { FiregraphNamespace } from './backend.js';\nimport { DORPCBackend } from './backend.js';\n\n/**\n * Options for `createDOClient`. Same shape as `GraphClientOptions`; the DO\n * backend does not expose a table label of its own — the DO owns its SQLite\n * schema (see `FiregraphDOOptions.table`, defaults to `'firegraph'`) and\n * that choice isn't surfaced through the client factory.\n */\nexport type DOClientOptions = GraphClientOptions;\n\n/**\n * Create a `GraphClient` backed by a `FiregraphDO` Durable Object.\n *\n * @param namespace The DO namespace binding (`env.GRAPH` in Worker code).\n * @param rootKey Stable name for the root graph's DO. The same value\n * always addresses the same DO — treat it as the graph's\n * identity. Subgraph DOs derive their names from this.\n * @param options Optional `GraphClientOptions` (registry, query mode,\n * `registryMode` for dynamic registries, etc.).\n * When `registryMode` is set the return type is\n * narrowed to `DynamicGraphClient`.\n */\nexport function createDOClient(\n namespace: FiregraphNamespace,\n rootKey: string,\n options: DOClientOptions & { registryMode: DynamicRegistryConfig },\n): DynamicGraphClient;\nexport function createDOClient(\n namespace: FiregraphNamespace,\n rootKey: string,\n options?: DOClientOptions,\n): GraphClient;\nexport function createDOClient(\n namespace: FiregraphNamespace,\n rootKey: string,\n options: DOClientOptions = {},\n): GraphClient | DynamicGraphClient {\n if (!rootKey || typeof rootKey !== 'string') {\n throw new FiregraphError(\n `createDOClient: rootKey must be a non-empty string, got ${JSON.stringify(rootKey)}.`,\n 'INVALID_ARGUMENT',\n );\n }\n if (rootKey.includes('/')) {\n // Subgraph chaining builds keys as `${rootKey}/${uid}/${name}`; a slash in\n // the root would make `${rootA}/uid/x` collide with `${rootB}/uid/x` if\n // `rootB === rootA + '/…'`. Keep the root a single opaque segment.\n throw new FiregraphError(\n `createDOClient: rootKey must not contain \"/\". Got: \"${rootKey}\".`,\n 'INVALID_ARGUMENT',\n );\n }\n\n // Forward-reference the client so the backend's registry accessor sees\n // whatever registry the client currently holds — including late updates\n // from `reloadRegistry()` in dynamic mode. The closure is invoked lazily\n // (only during `removeNodeCascade`) by design; any synchronous invocation\n // before `client` is assigned is a programming error in this module, not\n // a user-facing scenario — hence the throw rather than a silent\n // `undefined` return.\n let client: GraphClient | DynamicGraphClient | undefined;\n const registryAccessor = (): GraphRegistry | undefined => {\n if (!client) {\n throw new FiregraphError(\n 'createDOClient: registryAccessor fired before the client was assigned. ' +\n 'This indicates a programming error in the DO backend — the accessor must ' +\n 'only be invoked lazily from `removeNodeCascade`, never synchronously from ' +\n 'the `DORPCBackend` constructor.',\n 'INTERNAL',\n );\n }\n // `getRegistrySnapshot` is declared `@internal` on `GraphClientImpl`\n // and is not part of the public `GraphClient` interface. We cast\n // through `GraphClientImpl` so the call is type-checked; consumers\n // using a custom client wrapper would need to surface the same\n // accessor themselves.\n return (client as unknown as GraphClientImpl).getRegistrySnapshot();\n };\n\n // Factory used by `createSiblingClient` to spin up a peer root client\n // pointing at a different DO while reusing this call's namespace and\n // options. Carried on the backend so `createSiblingClient` can locate it\n // from any `GraphClient` (root or subgraph) without the caller having to\n // hold onto the original `createDOClient` arguments.\n //\n // We snapshot `options` (shallow copy) at capture time so that a later\n // mutation by the caller — e.g. setting `options.registry = newReg`\n // after construction — doesn't silently change sibling behaviour. Deep\n // cloning isn't needed: `GraphClientOptions` values are either primitives\n // or registries/functions the caller shouldn't mutate in place anyway.\n const siblingOptions: DOClientOptions = { ...options };\n const makeSiblingClient = (siblingRootKey: string): GraphClient | DynamicGraphClient =>\n createDOClient(namespace, siblingRootKey, siblingOptions);\n\n const backend = new DORPCBackend(namespace, {\n scopePath: '',\n storageKey: rootKey,\n registryAccessor,\n makeSiblingClient,\n });\n\n // Dynamic registry with an explicit meta-collection → spin up a second\n // DO for meta-types. By default (no `collection`, or `collection ===\n // rootKey`) meta-nodes live in the same DO as domain data; that's the\n // simpler bootstrap and avoids an extra DO when the caller doesn't need\n // the isolation.\n let metaBackend: StorageBackend | undefined;\n if (options.registryMode?.collection) {\n const metaKey = options.registryMode.collection;\n if (metaKey.includes('/')) {\n throw new FiregraphError(\n `createDOClient: registryMode.collection must not contain \"/\". Got: \"${metaKey}\".`,\n 'INVALID_ARGUMENT',\n );\n }\n if (metaKey !== rootKey) {\n metaBackend = new DORPCBackend(namespace, {\n scopePath: '',\n storageKey: metaKey,\n // Meta backend shares the accessor so its own `removeNodeCascade`\n // (unlikely, but safe) would also see the live registry. Sibling\n // factory is carried for consistency; there's no user-facing path\n // that creates a sibling from the meta backend, but it costs\n // nothing to keep the two backends in sync.\n registryAccessor,\n makeSiblingClient,\n });\n }\n }\n\n client = createGraphClientFromBackend(backend, options, metaBackend);\n return client;\n}\n\n/**\n * Construct a peer `GraphClient` that shares `client`'s DO namespace and\n * construction options but targets a different root DO (i.e. a different\n * root key — typically another tenant, workspace, or shard).\n *\n * This is the cheap, ergonomic way to talk to another root graph from\n * inside a single Worker request without re-plumbing `createDOClient`'s\n * arguments. The namespace binding plus the options snapshot captured at\n * the original `createDOClient` call (registry, query mode, migration\n * sandbox, `registryMode`, etc.) are inherited by the sibling.\n *\n * Works from any DO-backed client — root or subgraph. Passing a client\n * that wasn't produced by `createDOClient` (e.g. a Firestore-backed\n * client, or a `DORPCBackend` instantiated directly without the sibling\n * factory wired in) throws `UNSUPPORTED_OPERATION` with an explanation.\n *\n * ## Dynamic-registry caveat\n *\n * When the original client uses `registryMode: 'dynamic'`, siblings\n * inherit the *config* (so they're also dynamic clients) but NOT the\n * compiled runtime state. Meta-type nodes and `reloadRegistry()` results\n * are per-client; a sibling's `defineNodeType`/`defineEdgeType` calls go\n * to that sibling's own root DO, and its registry must be independently\n * populated and reloaded. If every tenant shares the same schema, pass a\n * static `registry` instead of dynamic mode — static registries ARE\n * inherited verbatim.\n *\n * @param client A client previously returned by `createDOClient`.\n * @param siblingRootKey Root key for the peer DO. Same validation rules\n * as `createDOClient`'s `rootKey`: non-empty,\n * no `/`.\n */\nexport function createSiblingClient(client: GraphClient, siblingRootKey: string): GraphClient;\nexport function createSiblingClient(\n client: DynamicGraphClient,\n siblingRootKey: string,\n): DynamicGraphClient;\nexport function createSiblingClient(\n client: GraphClient | DynamicGraphClient,\n siblingRootKey: string,\n): GraphClient | DynamicGraphClient {\n if (!siblingRootKey || typeof siblingRootKey !== 'string') {\n throw new FiregraphError(\n `createSiblingClient: siblingRootKey must be a non-empty string, got ${JSON.stringify(siblingRootKey)}.`,\n 'INVALID_ARGUMENT',\n );\n }\n if (siblingRootKey.includes('/')) {\n throw new FiregraphError(\n `createSiblingClient: siblingRootKey must not contain \"/\". Got: \"${siblingRootKey}\".`,\n 'INVALID_ARGUMENT',\n );\n }\n\n // `GraphClientImpl` exposes `getBackend()` as an `@internal` accessor.\n // Cast through it to read the backend; if the caller handed us a non-\n // firegraph client wrapper that's a programming error at their layer,\n // surfaced here as a clear error rather than an opaque property miss.\n //\n // We deliberately duck-type the backend (`typeof maker === 'function'`)\n // rather than using `instanceof DORPCBackend`: in monorepos with\n // duplicated `firegraph` copies a DO client built against copy A would\n // fail `instanceof` against copy B's class reference, even though every\n // other invariant holds. The duck-type check stays correct across module\n // boundaries — the only way `makeSiblingClient` exists on a backend is\n // if `createDOClient` wired it up.\n const impl = client as unknown as GraphClientImpl;\n const backend: StorageBackend | undefined =\n typeof impl.getBackend === 'function' ? impl.getBackend() : undefined;\n const maker =\n backend &&\n (\n backend as {\n makeSiblingClient?: (k: string) => GraphClient | DynamicGraphClient;\n }\n ).makeSiblingClient;\n if (typeof maker !== 'function') {\n throw new FiregraphError(\n 'createSiblingClient: the provided client is not backed by a DO client produced by `createDOClient`. ' +\n 'Sibling construction is only available for DO-backed clients.',\n 'UNSUPPORTED_OPERATION',\n );\n }\n\n return maker(siblingRootKey);\n}\n","/**\n * `FiregraphDO` — the Durable Object class that holds a single subgraph's\n * triples.\n *\n * The Cloudflare-native Firegraph design puts each subgraph in its own DO\n * instance: the root graph in one DO, `client.subgraph(uid, 'memories')` in\n * another, nested subgraphs in their own DOs, and so on. Each DO owns a\n * private flat SQLite database (`src/cloudflare/schema.ts`) — no `scope`\n * column, no shared table, no discriminator. The client routes to a\n * specific DO by hashing a stable name (`namespace.idFromName(storageKey)`);\n * on first RPC, the DO lazily materializes and runs the schema DDL.\n *\n * ## Using it in a Worker\n *\n * Bind the class in `wrangler.toml` and re-export it from the Worker entry:\n *\n * ```toml\n * [[durable_objects.bindings]]\n * name = \"GRAPH\"\n * class_name = \"FiregraphDO\"\n *\n * [[migrations]]\n * tag = \"v1\"\n * new_sqlite_classes = [\"FiregraphDO\"]\n * ```\n *\n * ```ts\n * // worker.ts\n * export { FiregraphDO } from '@typicalday/firegraph/cloudflare';\n * ```\n *\n * To add custom RPC methods, extend the class:\n *\n * ```ts\n * export class GraphDO extends FiregraphDO {\n * async myCustomRpc() { ... }\n * }\n * ```\n *\n * ## Why `extends DurableObject`?\n *\n * Cloudflare's modern Durable Objects RPC dispatcher only accepts arbitrary\n * method invocations on stubs whose backing class extends the special\n * `DurableObject` base from `cloudflare:workers`. Plain classes with the\n * `(state, env)` constructor shape still load and serve `fetch()`, but a\n * stub method call (`stub._fgGetDoc(...)`) on a plain-class DO throws:\n *\n * The receiving Durable Object does not support RPC, because its class\n * was not declared with `extends DurableObject`.\n *\n * `DORPCBackend` calls every operation as a stub method (see\n * `src/cloudflare/backend.ts`), so extending `DurableObject` is mandatory\n * for this library's design to work on production Workers.\n *\n * The `cloudflare:workers` import is virtual — only the workerd runtime\n * resolves it. For Node tests we route the import through a vitest alias\n * to a tiny stub class (`tests/__shims__/cloudflare-workers.ts`) that just\n * captures `ctx`/`env`. Tests instantiating `FiregraphDO` directly still\n * work; they just go through the stub instead of the real base class.\n */\n\n// `cloudflare:workers` is a virtual module — only the workerd runtime resolves\n// it. TypeScript needs to know the `DurableObject` base class shape at compile\n// time, which ships in `@cloudflare/workers-types`'s ambient `index.d.ts`.\n// Listing that package in `compilerOptions.types` would add 479KB of global\n// declarations to every source file's lookup scope (8min typecheck — see the\n// commit that added this file). Instead we pull it in once via the\n// triple-slash reference below so the cost is bounded to this one file.\n/// <reference types=\"@cloudflare/workers-types\" />\nimport { DurableObject } from 'cloudflare:workers';\n\nimport { computeEdgeDocId, computeNodeDocId } from '../docid.js';\nimport { FiregraphError } from '../errors.js';\nimport type { UpdatePayload, WritableRecord, WriteMode } from '../internal/backend.js';\nimport { NODE_RELATION } from '../internal/constants.js';\nimport { buildEdgeQueryPlan } from '../query.js';\nimport type {\n BulkOptions,\n BulkResult,\n CascadeResult,\n FindEdgesParams,\n GraphRegistry,\n IndexSpec,\n QueryFilter,\n QueryOptions,\n} from '../types.js';\nimport { buildDOSchemaStatements, validateDOTableName } from './schema.js';\nimport type { CompiledStatement, DORecordWire } from './sql.js';\nimport {\n compileDODelete,\n compileDODeleteAll,\n compileDOSelect,\n compileDOSelectByDocId,\n compileDOSet,\n compileDOUpdate,\n rowToDORecord,\n} from './sql.js';\n\n// ---------------------------------------------------------------------------\n// Minimal DO runtime types — declared locally so this module doesn't depend\n// on `@cloudflare/workers-types`. Users importing the library get their own\n// `DurableObjectState` from workers-types; structurally the two shapes are\n// compatible.\n// ---------------------------------------------------------------------------\n\nexport interface DOSqlCursor<T> {\n toArray(): T[];\n}\n\nexport interface DOSqlExecutor {\n exec<T = Record<string, unknown>>(sql: string, ...params: unknown[]): DOSqlCursor<T>;\n}\n\nexport interface DOStorage {\n sql: DOSqlExecutor;\n transactionSync<T>(fn: () => T): T;\n}\n\nexport interface DurableObjectStateLike {\n readonly storage: DOStorage;\n blockConcurrencyWhile<T>(fn: () => Promise<T>): Promise<T>;\n}\n\n// ---------------------------------------------------------------------------\n// RPC wire shapes\n// ---------------------------------------------------------------------------\n\n/**\n * One op in a `batch()` RPC call. Discriminated by `kind` so the DO can\n * dispatch to the correct compiler.\n */\nexport type BatchOp =\n | { kind: 'set'; docId: string; record: WritableRecord; mode: WriteMode }\n | { kind: 'update'; docId: string; update: UpdatePayload }\n | { kind: 'delete'; docId: string };\n\n/**\n * Options controlling `FiregraphDO` construction.\n */\nexport interface FiregraphDOOptions {\n /** Table name for firegraph triples. Default: `firegraph`. */\n table?: string;\n /** Run schema DDL on first boot. Default: `true`. */\n autoMigrate?: boolean;\n /**\n * Registry whose per-entry `indexes` get compiled into `CREATE INDEX`\n * statements during schema bootstrap. Supply the same registry you pass\n * to `createGraphClient` on the Worker side to keep DO and client in sync.\n */\n registry?: GraphRegistry;\n /**\n * Replaces the built-in core index preset\n * (`DEFAULT_CORE_INDEXES`). Supply this when the default set of\n * `(aUid, axbType)`, `(axbType, bUid)`, etc. composites doesn't fit your\n * query shapes — e.g., you want descending timestamps or a reduced set.\n * Entry-level `RegistryEntry.indexes` remain additive on top.\n *\n * Pass `[]` to disable core indexes entirely (advanced — only safe when\n * the provided `registry`'s entries cover every query shape your app\n * issues).\n */\n coreIndexes?: IndexSpec[];\n}\n\n// ---------------------------------------------------------------------------\n// FiregraphDO\n// ---------------------------------------------------------------------------\n\n/**\n * Default `FiregraphDO` options, used when a subclass calls `super(ctx, env)`\n * without passing options. Overridable in subclasses via constructor args.\n *\n * Only fields with a universal sensible default go here — optional index\n * and registry wiring is `undefined` by default and threaded through\n * `runSchema` as-is.\n */\nconst DEFAULT_OPTIONS: Required<Pick<FiregraphDOOptions, 'table' | 'autoMigrate'>> = {\n table: 'firegraph',\n autoMigrate: true,\n};\n\nexport class FiregraphDO extends DurableObject<unknown> {\n /**\n * @internal — locally-narrowed alias for `this.ctx`, used only by\n * FiregraphDO's own SQL helpers. Same runtime object as the inherited\n * `this.ctx`, but typed as `DurableObjectStateLike` (just `storage.sql`\n * / `transactionSync` / `blockConcurrencyWhile`) so internal calls\n * don't trip over workers-types' stricter\n * `SqlStorage.exec<T extends Record<string, SqlStorageValue>>`\n * constraint vs the `Record<string, unknown>` rows firegraph passes.\n *\n * **Subclasses should use `this.ctx`, not `this.state`.** `this.state`\n * deliberately exposes only the slice FiregraphDO needs internally;\n * subclasses that want `id`, `acceptWebSocket`, `setAlarm`, `getAlarm`,\n * `waitUntil`, `props`, etc. must reach for the inherited `this.ctx`\n * (the full workers-types `DurableObjectState`).\n */\n protected readonly state: DurableObjectStateLike;\n /** @internal — table name used by every compiled statement. */\n protected readonly table: string;\n /** @internal — registry consulted by `runSchema` for per-entry indexes. */\n protected readonly registry?: GraphRegistry;\n /** @internal — overrides `DEFAULT_CORE_INDEXES` when set. */\n protected readonly coreIndexes?: IndexSpec[];\n\n constructor(ctx: DurableObjectStateLike, env: unknown, options: FiregraphDOOptions = {}) {\n // The base `DurableObject` constructor expects the workers-types\n // `DurableObjectState`. Our public signature uses\n // `DurableObjectStateLike` (a structural subset) so consumers don't\n // need workers-types just to subclass. Cast via `unknown as\n // DurableObjectState` — narrower than `as never`: it still allows the\n // structurally-compatible value through, but if Cloudflare ever\n // tightens `DurableObjectState` (extra constructor param, new\n // required method) the type checker will surface the drift instead\n // of silently swallowing it.\n super(ctx as unknown as DurableObjectState, env);\n this.state = ctx;\n const table = options.table ?? DEFAULT_OPTIONS.table;\n validateDOTableName(table);\n this.table = table;\n this.registry = options.registry;\n this.coreIndexes = options.coreIndexes;\n\n const autoMigrate = options.autoMigrate ?? DEFAULT_OPTIONS.autoMigrate;\n if (autoMigrate) {\n // `blockConcurrencyWhile` defers any incoming RPC until the schema is\n // in place. Without it a fast first caller could run a query against an\n // empty database before the CREATE TABLE lands.\n //\n // Fire-and-forget is safe: the DO runtime internally tracks and awaits\n // the returned promise, holding the RPC input queue until the schema\n // materializes. We don't need to `await` it from the constructor (which\n // can't be async anyway) — the next incoming RPC already waits.\n void this.state.blockConcurrencyWhile(async () => {\n this.runSchema();\n });\n }\n }\n\n // ---------------------------------------------------------------------------\n // RPC: reads\n //\n // Method names are prefixed `_fg` so user subclasses can add their own RPC\n // methods without name collisions. The client-side backend in\n // `src/cloudflare/backend.ts` calls these directly on the DO stub.\n // ---------------------------------------------------------------------------\n\n async _fgGetDoc(docId: string): Promise<DORecordWire | null> {\n const stmt = compileDOSelectByDocId(this.table, docId);\n const rows = this.execAll(stmt);\n return rows.length === 0 ? null : rowToDORecord(rows[0]);\n }\n\n async _fgQuery(filters: QueryFilter[], options?: QueryOptions): Promise<DORecordWire[]> {\n const stmt = compileDOSelect(this.table, filters, options);\n const rows = this.execAll(stmt);\n return rows.map(rowToDORecord);\n }\n\n // ---------------------------------------------------------------------------\n // RPC: writes\n // ---------------------------------------------------------------------------\n\n async _fgSetDoc(docId: string, record: WritableRecord, mode: WriteMode): Promise<void> {\n const stmt = compileDOSet(this.table, docId, record, Date.now(), mode);\n this.execRun(stmt);\n }\n\n async _fgUpdateDoc(docId: string, update: UpdatePayload): Promise<void> {\n const stmt = compileDOUpdate(this.table, docId, update, Date.now());\n // RETURNING lets us surface NOT_FOUND at the client, matching Firestore's\n // `update()` semantics. SQLite ≥3.35 supports UPDATE … RETURNING and DO\n // SQLite is always recent enough.\n const sqlWithReturning = `${stmt.sql} RETURNING \"doc_id\"`;\n const rows = this.state.storage.sql\n .exec<Record<string, unknown>>(sqlWithReturning, ...stmt.params)\n .toArray();\n if (rows.length === 0) {\n throw new FiregraphError(`updateDoc: no document found for doc_id=${docId}`, 'NOT_FOUND');\n }\n }\n\n async _fgDeleteDoc(docId: string): Promise<void> {\n const stmt = compileDODelete(this.table, docId);\n this.execRun(stmt);\n }\n\n // ---------------------------------------------------------------------------\n // RPC: batch\n // ---------------------------------------------------------------------------\n\n /**\n * Execute a list of write ops atomically. DO SQLite's `transactionSync`\n * provides real atomicity — either every statement commits or none do.\n * No statement-count cap applies (contrast with D1's ~100-statement batch\n * limit), so the caller can submit as many ops as they like in one call.\n */\n async _fgBatch(ops: BatchOp[]): Promise<void> {\n if (ops.length === 0) return;\n const now = Date.now();\n const statements: CompiledStatement[] = ops.map((op) => {\n switch (op.kind) {\n case 'set':\n return compileDOSet(this.table, op.docId, op.record, now, op.mode);\n case 'update':\n return compileDOUpdate(this.table, op.docId, op.update, now);\n case 'delete':\n return compileDODelete(this.table, op.docId);\n }\n });\n this.state.storage.transactionSync(() => {\n for (const stmt of statements) {\n this.state.storage.sql.exec(stmt.sql, ...stmt.params).toArray();\n }\n });\n }\n\n // ---------------------------------------------------------------------------\n // RPC: cascade + bulk (local DO only)\n //\n // These cascade *within this DO*. Subgraph DOs (nested under this node) are\n // not reachable from here — the client-side `DORPCBackend.removeNodeCascade`\n // consults the registry topology to discover descendant subgraph DOs and\n // fans out explicit `_fgDestroy` calls to each before invoking this method.\n // Without that topology the DO has no way to enumerate its children.\n // ---------------------------------------------------------------------------\n\n async _fgRemoveNodeCascade(uid: string): Promise<CascadeResult> {\n // Gather every edge whose aUid or bUid matches the node. The self-loop\n // (node record) is identified separately so we can report it distinctly\n // in `CascadeResult` — and so we don't falsely claim `nodeDeleted: true`\n // when the node never existed.\n const outgoingStmt = compileDOSelect(this.table, [{ field: 'aUid', op: '==', value: uid }]);\n const incomingStmt = compileDOSelect(this.table, [{ field: 'bUid', op: '==', value: uid }]);\n const outgoingRows = this.execAll(outgoingStmt);\n const incomingRows = this.execAll(incomingStmt);\n\n const seen = new Set<string>();\n const edgeDocIds: string[] = [];\n let nodeExists = false;\n for (const row of [...outgoingRows, ...incomingRows]) {\n const axbType = row.axb_type as string;\n const aUid = row.a_uid as string;\n const bUid = row.b_uid as string;\n if (axbType === NODE_RELATION && aUid === bUid) {\n nodeExists = true;\n continue;\n }\n const docId = computeEdgeDocId(aUid, axbType, bUid);\n if (!seen.has(docId)) {\n seen.add(docId);\n edgeDocIds.push(docId);\n }\n }\n\n const statements: CompiledStatement[] = edgeDocIds.map((id) => compileDODelete(this.table, id));\n // Only queue the node delete if the self-loop was actually present; a\n // cascade on a nonexistent node returns `nodeDeleted: false` without\n // a wasted DELETE. Orphan edges still get cleaned up either way.\n if (nodeExists) {\n statements.push(compileDODelete(this.table, computeNodeDocId(uid)));\n }\n\n if (statements.length === 0) {\n return {\n deleted: 0,\n batches: 0,\n errors: [],\n edgesDeleted: 0,\n nodeDeleted: false,\n };\n }\n\n try {\n this.state.storage.transactionSync(() => {\n for (const stmt of statements) {\n this.state.storage.sql.exec(stmt.sql, ...stmt.params).toArray();\n }\n });\n return {\n deleted: statements.length,\n batches: 1,\n errors: [],\n edgesDeleted: edgeDocIds.length,\n nodeDeleted: nodeExists,\n };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return {\n deleted: 0,\n batches: 0,\n errors: [{ batchIndex: 0, error, operationCount: statements.length }],\n edgesDeleted: 0,\n nodeDeleted: false,\n };\n }\n }\n\n async _fgBulkRemoveEdges(params: FindEdgesParams, _options?: BulkOptions): Promise<BulkResult> {\n // Resolve the set of doc IDs to delete. For a fully-specified query\n // (`get` plan) the planner hands us the doc ID directly — we still\n // verify existence so the returned count reflects reality. For partial\n // queries we run the SELECT and collect every matching edge's doc ID.\n // `allowCollectionScan` / scan protection are deliberately ignored at\n // this layer — the client has already authorized the delete.\n const plan = buildEdgeQueryPlan(params);\n let docIds: string[];\n if (plan.strategy === 'get') {\n const existsStmt = compileDOSelectByDocId(this.table, plan.docId);\n const rows = this.execAll(existsStmt);\n docIds = rows.length > 0 ? [plan.docId] : [];\n } else {\n const selectStmt = compileDOSelect(this.table, plan.filters, plan.options);\n const rows = this.execAll(selectStmt);\n docIds = rows.map((row) =>\n computeEdgeDocId(row.a_uid as string, row.axb_type as string, row.b_uid as string),\n );\n }\n\n if (docIds.length === 0) {\n return { deleted: 0, batches: 0, errors: [] };\n }\n\n const deleteStmts = docIds.map((id) => compileDODelete(this.table, id));\n try {\n this.state.storage.transactionSync(() => {\n for (const stmt of deleteStmts) {\n this.state.storage.sql.exec(stmt.sql, ...stmt.params).toArray();\n }\n });\n return { deleted: deleteStmts.length, batches: 1, errors: [] };\n } catch (err) {\n const error = err instanceof Error ? err : new Error(String(err));\n return {\n deleted: 0,\n batches: 0,\n errors: [{ batchIndex: 0, error, operationCount: deleteStmts.length }],\n };\n }\n }\n\n // ---------------------------------------------------------------------------\n // RPC: admin\n // ---------------------------------------------------------------------------\n\n /**\n * Wipe every row. Called by the client when tearing down a subgraph DO as\n * part of cascade — the DO itself can't be destroyed (DO IDs persist\n * forever), but its storage can be emptied.\n */\n async _fgDestroy(): Promise<void> {\n const stmt = compileDODeleteAll(this.table);\n this.execRun(stmt);\n }\n\n // ---------------------------------------------------------------------------\n // Internals\n // ---------------------------------------------------------------------------\n\n protected runSchema(): void {\n const statements = buildDOSchemaStatements(this.table, {\n coreIndexes: this.coreIndexes,\n registry: this.registry,\n });\n for (const sql of statements) {\n this.state.storage.sql.exec(sql).toArray();\n }\n }\n\n private execAll(stmt: CompiledStatement): Record<string, unknown>[] {\n return this.state.storage.sql.exec<Record<string, unknown>>(stmt.sql, ...stmt.params).toArray();\n }\n\n private execRun(stmt: CompiledStatement): void {\n // DO SQL `exec` returns a cursor even for writes; consuming it via\n // `toArray()` forces execution and surfaces constraint errors\n // synchronously.\n this.state.storage.sql.exec(stmt.sql, ...stmt.params).toArray();\n }\n}\n","import { nanoid } from 'nanoid';\n\nexport function generateId(): string {\n return nanoid();\n}\n"],"mappings":"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","names":["FIRESTORE_TYPE_NAMES","walk","JSON_PATH_KEY_RE","IDENT_RE","sql","import_node_crypto","import_node_crypto","serializeFirestoreTypes","deserializeFirestoreTypes","buildWritableNodeRecord","buildWritableEdgeRecord","buildWritableNodeRecord","buildWritableEdgeRecord"]}