@typicalday/firegraph 0.7.0 → 0.8.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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/internal/sqlite-schema.ts","../src/timestamp.ts","../src/internal/sqlite-sql.ts","../src/internal/sqlite-backend.ts"],"sourcesContent":["/**\n * SQLite schema for firegraph triples.\n *\n * Single-table design — both nodes (self-loops with `axbType = 'is'`) and\n * edges share one row. The `scope` column carries the materialized subgraph\n * path (parent UIDs interleaved with subgraph names), which preserves\n * Firestore's nested-subcollection semantics in a flat table.\n *\n * `data` is a JSON string. Built-in fields are projected to typed columns so\n * the query planner can use indexes without going through `json_extract`.\n */\n\nexport const SQLITE_COLUMNS = [\n 'doc_id',\n 'scope',\n 'a_type',\n 'a_uid',\n 'axb_type',\n 'b_type',\n 'b_uid',\n 'data',\n 'v',\n 'created_at',\n 'updated_at',\n] as const;\n\nexport type SqliteColumn = (typeof SQLITE_COLUMNS)[number];\n\n/**\n * Map firegraph field names (as they appear in `QueryFilter.field` and the\n * record envelope) to SQLite column names.\n */\nexport const FIELD_TO_COLUMN: Record<string, SqliteColumn> = {\n aType: 'a_type',\n aUid: 'a_uid',\n axbType: 'axb_type',\n bType: 'b_type',\n bUid: 'b_uid',\n v: 'v',\n createdAt: 'created_at',\n updatedAt: 'updated_at',\n};\n\n/**\n * Build the DDL statements that create the firegraph table and its indexes.\n * Returned as separate statements because some drivers (D1) require one\n * statement per `prepare()` call.\n */\nexport function buildSchemaStatements(table: string): string[] {\n const t = quoteIdent(table);\n return [\n `CREATE TABLE IF NOT EXISTS ${t} (\n doc_id TEXT NOT NULL,\n scope TEXT NOT NULL DEFAULT '',\n a_type TEXT NOT NULL,\n a_uid TEXT NOT NULL,\n axb_type TEXT NOT NULL,\n b_type TEXT NOT NULL,\n b_uid TEXT NOT NULL,\n data TEXT NOT NULL,\n v INTEGER,\n created_at INTEGER NOT NULL,\n updated_at INTEGER NOT NULL,\n PRIMARY KEY (scope, doc_id)\n )`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_a_uid`)} ON ${t}(scope, a_uid)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_b_uid`)} ON ${t}(scope, b_uid)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_axb_type_b_uid`)} ON ${t}(scope, axb_type, b_uid)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_a_type`)} ON ${t}(scope, a_type)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_scope_b_type`)} ON ${t}(scope, b_type)`,\n `CREATE INDEX IF NOT EXISTS ${quoteIdent(`${table}_idx_doc_id`)} ON ${t}(doc_id)`,\n ];\n}\n\n/**\n * Quote a SQL identifier with double quotes, escaping any embedded quotes.\n *\n * Identifier names (table, column, index) come from configuration and\n * static code in this module — never from user data — but quoting still\n * protects against accidental keyword collisions.\n */\nexport function quoteIdent(name: string): string {\n validateTableName(name);\n return `\"${name}\"`;\n}\n\n/**\n * Validate a SQLite identifier (table name, column name) against the\n * allowed character set. Exposed so factory functions can fail fast on\n * an invalid `options.table` rather than waiting until first SQL.\n */\nexport function validateTableName(name: string): void {\n if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name)) {\n throw new Error(`Invalid SQL identifier: ${name}. Must match /^[A-Za-z_][A-Za-z0-9_]*$/.`);\n }\n}\n","/**\n * 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 * Compile firegraph queries and writes into parameterized SQLite statements.\n *\n * The single-table SQLite schema mirrors the Firestore record envelope:\n * built-in fields (`aType`, `aUid`, etc.) are typed columns, and `data` is a\n * JSON string. `data.<key>` filter fields are translated to `json_extract`\n * expressions; built-in fields go straight to their column.\n */\n\nimport { FiregraphError } from '../errors.js';\nimport type { GraphTimestamp } from '../timestamp.js';\nimport { GraphTimestampImpl } from '../timestamp.js';\nimport type { QueryFilter, QueryOptions, StoredGraphRecord } from '../types.js';\nimport type { UpdatePayload, WritableRecord } from './backend.js';\nimport { FIELD_TO_COLUMN, quoteIdent } from './sqlite-schema.js';\n\nexport interface CompiledStatement {\n sql: string;\n params: unknown[];\n}\n\n/**\n * Translate a firegraph filter field to either a column reference or a\n * `json_extract(data, ?)` expression. The JSON path is returned separately\n * so it can be appended to the parameter list.\n */\nfunction compileFieldRef(field: string): { expr: string; pathParam?: string } {\n const column = FIELD_TO_COLUMN[field];\n if (column) {\n return { expr: quoteIdent(column) };\n }\n if (field.startsWith('data.')) {\n const key = field.slice(5);\n validateJsonPathKey(key);\n return { expr: 'json_extract(\"data\", ?)', pathParam: `$.${key}` };\n }\n if (field === 'data') {\n return { expr: 'json_extract(\"data\", ?)', pathParam: '$' };\n }\n throw new FiregraphError(`SQLite backend cannot resolve filter field: ${field}`, 'INVALID_QUERY');\n}\n\n/**\n * Constructor names of Firestore special types that don't survive a plain\n * JSON.stringify round-trip — they have non-enumerable accessors (e.g.\n * `Timestamp.seconds`) or class identity that JSON loses. Filtering by one\n * of these against SQLite would silently miss every row, so we fail loudly.\n *\n * Detection is by `constructor.name` + duck-type so this module stays\n * dependency-free (importing `@google-cloud/firestore` here would pollute\n * the Cloudflare Workers bundle — see tests/unit/bundle-pollution.test.ts).\n */\nconst FIRESTORE_TYPE_NAMES = new Set([\n 'Timestamp',\n 'GeoPoint',\n 'VectorValue',\n 'DocumentReference',\n 'FieldValue',\n]);\n\nfunction isFirestoreSpecialType(value: object): string | null {\n const ctorName = (value as { constructor?: { name?: string } }).constructor?.name;\n if (ctorName && FIRESTORE_TYPE_NAMES.has(ctorName)) return ctorName;\n return null;\n}\n\n/**\n * Coerce a JS filter value into a SQLite-bindable primitive. JS objects\n * (other than null) are JSON-stringified so they can match values stored\n * via `json_extract`.\n *\n * Firestore special types (Timestamp, GeoPoint, VectorValue,\n * DocumentReference, FieldValue) are rejected with `INVALID_QUERY` —\n * `JSON.stringify` would emit garbage (`{}` for Timestamp, etc.) that\n * silently fails to match anything. Convert to a primitive at the call site\n * (e.g. `ts.toMillis()` or `ts.toDate().toISOString()`) before passing in.\n */\nfunction bindValue(value: unknown): unknown {\n if (value === null || value === undefined) return null;\n if (\n typeof value === 'string' ||\n typeof value === 'number' ||\n typeof value === 'bigint' ||\n typeof value === 'boolean'\n ) {\n return value;\n }\n if (value instanceof Date) return value.getTime();\n if (typeof value === 'object') {\n const firestoreType = isFirestoreSpecialType(value);\n if (firestoreType) {\n throw new FiregraphError(\n `SQLite backend cannot bind a Firestore ${firestoreType} value — JSON serialization ` +\n `would silently drop fields and the resulting bind would never match a stored row. ` +\n `Convert to a primitive (e.g. \\`ts.toMillis()\\` for Timestamp) before filtering or ` +\n `updating.`,\n 'INVALID_QUERY',\n );\n }\n return JSON.stringify(value);\n }\n return String(value);\n}\n\n/**\n * Validate `data.<key>` paths and `dataFields` keys against the subset of\n * JSON keys that round-trip cleanly through SQLite's unquoted JSON-path\n * syntax (`$.<name>` style). The accepted shape — `[A-Za-z_][\\w-]*` —\n * covers the overwhelmingly common case of code-style identifiers (camel,\n * snake, kebab) without forcing the SQL emitter to quote/escape paths.\n *\n * Why not silently quote? Several characters (`.`, `[`, `]`, `\"`) are\n * structural in SQLite's JSON path even inside the string after `$.` —\n * silently quoting would mean the reads (`json_extract` for filters) and\n * writes (`json_set` for `dataFields`) need symmetric quoting at every\n * call site, and any drift produces silent data corruption. Failing loudly\n * at compile time is the safer trade-off; users with exotic keys can use\n * `replaceData` (full-blob overwrite) instead.\n */\nconst JSON_PATH_KEY_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;\n\nfunction validateJsonPathKey(key: string): void {\n if (key.length === 0) {\n throw new FiregraphError(\n 'SQLite backend: empty JSON path component is not allowed',\n 'INVALID_QUERY',\n );\n }\n if (!JSON_PATH_KEY_RE.test(key)) {\n throw new FiregraphError(\n `SQLite backend: data field path component \"${key}\" is not a safe JSON-path identifier. ` +\n `Allowed pattern: /^[A-Za-z_][A-Za-z0-9_-]*$/. Use replaceData (full-data overwrite) ` +\n `for keys with reserved characters (whitespace, dots, brackets, quotes, etc.).`,\n 'INVALID_QUERY',\n );\n }\n}\n\nfunction compileFilter(filter: QueryFilter, params: unknown[]): string {\n const { expr, pathParam } = compileFieldRef(filter.field);\n if (pathParam !== undefined) params.push(pathParam);\n\n switch (filter.op) {\n case '==':\n params.push(bindValue(filter.value));\n return `${expr} = ?`;\n case '!=':\n params.push(bindValue(filter.value));\n return `${expr} != ?`;\n case '<':\n params.push(bindValue(filter.value));\n return `${expr} < ?`;\n case '<=':\n params.push(bindValue(filter.value));\n return `${expr} <= ?`;\n case '>':\n params.push(bindValue(filter.value));\n return `${expr} > ?`;\n case '>=':\n params.push(bindValue(filter.value));\n return `${expr} >= ?`;\n case 'in': {\n const values = asArray(filter.value, 'in');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `${expr} IN (${placeholders})`;\n }\n case 'not-in': {\n const values = asArray(filter.value, 'not-in');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `${expr} NOT IN (${placeholders})`;\n }\n case 'array-contains': {\n params.push(bindValue(filter.value));\n return `EXISTS (SELECT 1 FROM json_each(${expr}) WHERE value = ?)`;\n }\n case 'array-contains-any': {\n const values = asArray(filter.value, 'array-contains-any');\n const placeholders = values.map(() => '?').join(', ');\n for (const v of values) params.push(bindValue(v));\n return `EXISTS (SELECT 1 FROM json_each(${expr}) WHERE value IN (${placeholders}))`;\n }\n default:\n throw new FiregraphError(\n `SQLite backend does not support filter operator: ${String(filter.op)}`,\n 'INVALID_QUERY',\n );\n }\n}\n\nfunction asArray(value: unknown, op: string): unknown[] {\n if (!Array.isArray(value) || value.length === 0) {\n throw new FiregraphError(`Operator \"${op}\" requires a non-empty array value`, 'INVALID_QUERY');\n }\n return value;\n}\n\nfunction compileOrderBy(options: QueryOptions | undefined, params: unknown[]): string {\n if (!options?.orderBy) return '';\n const { field, direction } = options.orderBy;\n const { expr, pathParam } = compileFieldRef(field);\n if (pathParam !== undefined) params.push(pathParam);\n const dir = direction === 'desc' ? 'DESC' : 'ASC';\n return ` ORDER BY ${expr} ${dir}`;\n}\n\nfunction compileLimit(options: QueryOptions | undefined, params: unknown[]): string {\n if (options?.limit === undefined) return '';\n params.push(options.limit);\n return ` LIMIT ?`;\n}\n\n/**\n * SELECT all rows matching `filters` within `scope`. The scope filter is\n * always added as the leading predicate so the `(scope, …)` indexes apply.\n */\nexport function compileSelect(\n table: string,\n scope: string,\n filters: QueryFilter[],\n options?: QueryOptions,\n): CompiledStatement {\n const params: unknown[] = [];\n const conditions: string[] = ['\"scope\" = ?'];\n params.push(scope);\n\n for (const f of filters) {\n conditions.push(compileFilter(f, params));\n }\n\n let sql = `SELECT * FROM ${quoteIdent(table)} WHERE ${conditions.join(' AND ')}`;\n // ORDER BY parameters must come after WHERE parameters in the bind list.\n const orderClause = compileOrderBy(options, params);\n sql += orderClause;\n sql += compileLimit(options, params);\n\n return { sql, params };\n}\n\n/**\n * Cross-scope SELECT — equivalent to Firestore's collection group query.\n * Used by `findEdgesGlobal`.\n *\n * `scopeNameFilter`, when present, narrows the result to rows whose scope's\n * last materialized-path segment equals the given subgraph name (or to the\n * root rows when the name equals the table name itself, matching Firestore's\n * `db.collectionGroup(tableName)` semantics).\n */\nexport function compileSelectGlobal(\n table: string,\n filters: QueryFilter[],\n options?: QueryOptions,\n scopeNameFilter?: { name: string; isRoot: boolean },\n): CompiledStatement {\n if (filters.length === 0) {\n throw new FiregraphError(\n 'compileSelectGlobal requires at least one filter — refusing to issue an unbounded SELECT.',\n 'INVALID_QUERY',\n );\n }\n\n const params: unknown[] = [];\n const conditions: string[] = [];\n\n if (scopeNameFilter) {\n if (scopeNameFilter.isRoot) {\n conditions.push(`\"scope\" = ?`);\n params.push('');\n } else {\n conditions.push(`\"scope\" LIKE ? ESCAPE '\\\\'`);\n params.push(`%/${escapeLike(scopeNameFilter.name)}`);\n }\n }\n\n for (const f of filters) {\n conditions.push(compileFilter(f, params));\n }\n\n const sql =\n `SELECT * FROM ${quoteIdent(table)} WHERE ${conditions.join(' AND ')}` +\n compileOrderBy(options, params) +\n compileLimit(options, params);\n return { sql, params };\n}\n\nexport function compileSelectByDocId(\n table: string,\n scope: string,\n docId: string,\n): CompiledStatement {\n return {\n sql: `SELECT * FROM ${quoteIdent(table)} WHERE \"scope\" = ? AND \"doc_id\" = ? LIMIT 1`,\n params: [scope, docId],\n };\n}\n\n/**\n * INSERT OR REPLACE — `setDoc` semantics: full replacement of the row\n * keyed by `(scope, doc_id)`.\n */\nexport function compileSet(\n table: string,\n scope: string,\n docId: string,\n record: WritableRecord,\n nowMillis: number,\n): CompiledStatement {\n const sql = `INSERT OR REPLACE INTO ${quoteIdent(table)} (\n doc_id, scope, a_type, a_uid, axb_type, b_type, b_uid, data, v, created_at, updated_at\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`;\n const params: unknown[] = [\n docId,\n scope,\n record.aType,\n record.aUid,\n record.axbType,\n record.bType,\n record.bUid,\n JSON.stringify(record.data ?? {}),\n record.v ?? null,\n nowMillis,\n nowMillis,\n ];\n return { sql, params };\n}\n\n/**\n * Compile an `UpdatePayload` into a single UPDATE statement.\n *\n * - `replaceData` overwrites the whole `data` JSON.\n * - `dataFields` shallow-merges fields via `json_set(data, '$.k', v, …)`.\n * - `v` is set when provided.\n * - `updated_at` is always stamped.\n */\nexport function compileUpdate(\n table: string,\n scope: string,\n docId: string,\n update: UpdatePayload,\n nowMillis: number,\n): CompiledStatement {\n const setClauses: string[] = [];\n const params: unknown[] = [];\n\n if (update.replaceData) {\n setClauses.push(`\"data\" = ?`);\n params.push(JSON.stringify(update.replaceData));\n } else if (update.dataFields && Object.keys(update.dataFields).length > 0) {\n // json_set(data, '$.k1', ?, '$.k2', ?, …)\n const entries = Object.entries(update.dataFields);\n const pathArgs = entries.map(() => `?, ?`).join(', ');\n setClauses.push(`\"data\" = json_set(COALESCE(\"data\", '{}'), ${pathArgs})`);\n for (const [k, v] of entries) {\n validateJsonPathKey(k);\n params.push(`$.${k}`);\n params.push(bindValue(v));\n }\n }\n\n if (update.v !== undefined) {\n setClauses.push(`\"v\" = ?`);\n params.push(update.v);\n }\n\n setClauses.push(`\"updated_at\" = ?`);\n params.push(nowMillis);\n\n // WHERE params come last\n params.push(scope, docId);\n\n return {\n sql: `UPDATE ${quoteIdent(table)} SET ${setClauses.join(', ')} WHERE \"scope\" = ? AND \"doc_id\" = ?`,\n params,\n };\n}\n\nexport function compileDelete(table: string, scope: string, docId: string): CompiledStatement {\n return {\n sql: `DELETE FROM ${quoteIdent(table)} WHERE \"scope\" = ? AND \"doc_id\" = ?`,\n params: [scope, docId],\n };\n}\n\n/**\n * Delete every row whose scope starts with `scopePrefix` followed by '/'.\n * Used by cascade delete to wipe all subgraphs nested under a node.\n *\n * The trailing '/' guard prevents `'a'` from matching `'ab'`. The exact-\n * match case (`scope = scopePrefix`) is intentionally NOT included: the\n * prefix passed in is always `<storageScope>/<uid>` (or just `<uid>` at\n * root), which is an odd-segment count, while every stored row's scope is\n * `''` (root) or an even-segment `<uid>/<name>[/…]` pair sequence — so\n * `scope = scopePrefix` can never match a real row.\n */\nexport function compileDeleteScopePrefix(table: string, scopePrefix: string): CompiledStatement {\n // SQLite LIKE escape: prefix could contain '%' or '_'. Escape with '\\\\'.\n const escaped = escapeLike(scopePrefix);\n return {\n sql: `DELETE FROM ${quoteIdent(table)} WHERE \"scope\" LIKE ? ESCAPE '\\\\'`,\n params: [`${escaped}/%`],\n };\n}\n\n/**\n * Count rows that `compileDeleteScopePrefix` would delete. Used by cascade\n * to report an accurate total in `CascadeResult.deleted` — the prefix-\n * delete is a single SQL statement and the executor surface doesn't expose\n * per-row counts. One extra index lookup per cascade is cheap relative to\n * the delete itself.\n */\nexport function compileCountScopePrefix(table: string, scopePrefix: string): CompiledStatement {\n const escaped = escapeLike(scopePrefix);\n return {\n sql: `SELECT COUNT(*) AS n FROM ${quoteIdent(table)} WHERE \"scope\" LIKE ? ESCAPE '\\\\'`,\n params: [`${escaped}/%`],\n };\n}\n\nfunction escapeLike(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/%/g, '\\\\%').replace(/_/g, '\\\\_');\n}\n\n/**\n * Convert a row returned by a SQLite driver into a `StoredGraphRecord`.\n * `created_at`/`updated_at` are wrapped in `GraphTimestampImpl` so they\n * present the same surface as Firestore's `Timestamp`.\n */\nexport function rowToRecord(row: Record<string, unknown>): StoredGraphRecord {\n const dataString = row.data as string | null;\n const data = dataString ? (JSON.parse(dataString) as Record<string, unknown>) : {};\n\n const createdMs = toMillis(row.created_at);\n const updatedMs = toMillis(row.updated_at);\n\n const record: Record<string, unknown> = {\n aType: row.a_type as string,\n aUid: row.a_uid as string,\n axbType: row.axb_type as string,\n bType: row.b_type as string,\n bUid: row.b_uid as string,\n data,\n createdAt: GraphTimestampImpl.fromMillis(createdMs) as unknown as GraphTimestamp,\n updatedAt: GraphTimestampImpl.fromMillis(updatedMs) as unknown as GraphTimestamp,\n };\n\n if (row.v !== null && row.v !== undefined) {\n record.v = Number(row.v);\n }\n return record as unknown as StoredGraphRecord;\n}\n\nfunction toMillis(value: unknown): number {\n if (typeof value === 'number') return value;\n if (typeof value === 'bigint') return Number(value);\n if (typeof value === 'string') return Number(value);\n return 0;\n}\n","/**\n * SQLite implementation of `StorageBackend`.\n *\n * Uses a single table keyed by `(scope, doc_id)`. Subgraphs are encoded in\n * the `scope` column as a materialized path of interleaved parent UIDs and\n * subgraph names — `''` at the root, `'<uid>/<name>'` one level down,\n * `'<uid1>/<name1>/<uid2>/<name2>'` two levels down, and so on. Cascade\n * delete uses a single `DELETE … WHERE scope LIKE 'prefix/%'` instead of\n * walking subcollections.\n */\n\nimport { computeEdgeDocId, computeNodeDocId } from '../docid.js';\nimport { FiregraphError } from '../errors.js';\nimport { buildEdgeQueryPlan } from '../query.js';\nimport type {\n BulkBatchError,\n BulkOptions,\n BulkResult,\n CascadeResult,\n FindEdgesParams,\n GraphReader,\n QueryFilter,\n QueryOptions,\n StoredGraphRecord,\n} from '../types.js';\nimport type {\n BatchBackend,\n StorageBackend,\n TransactionBackend,\n UpdatePayload,\n WritableRecord,\n} from './backend.js';\nimport { NODE_RELATION } from './constants.js';\nimport type { SqliteExecutor, SqliteTxExecutor } from './sqlite-executor.js';\nimport type { CompiledStatement } from './sqlite-sql.js';\nimport {\n compileCountScopePrefix,\n compileDelete,\n compileDeleteScopePrefix,\n compileSelect,\n compileSelectByDocId,\n compileSelectGlobal,\n compileSet,\n compileUpdate,\n rowToRecord,\n} from './sqlite-sql.js';\n\nexport interface SqliteBackendOptions {\n /** Logical scope path (chained subgraph names) — used for `allowedIn` matching. */\n scopePath?: string;\n /** Internal storage scope (interleaved parent-uid/name path). */\n storageScope?: string;\n}\n\n/**\n * Default per-chunk retry budget for bulk/cascade operations. Mirrors the\n * Firestore bulk path (`src/bulk.ts`) so behaviour is consistent across\n * backends. Callers override via `BulkOptions.maxRetries`.\n */\nconst DEFAULT_MAX_RETRIES = 3;\nconst BASE_RETRY_DELAY_MS = 200;\n/**\n * Upper bound for the exponential backoff between chunk retries. Without\n * this cap, `maxRetries: 10` would push the final wait past 100s; legitimate\n * transient errors recover well within a few seconds, and longer waits just\n * delay the surfacing of permanent failures.\n */\nconst MAX_RETRY_DELAY_MS = 5000;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n/**\n * Return the smaller of two optional positive numbers, treating `undefined`\n * as \"no cap.\" Used to combine caller-supplied `BulkOptions.batchSize` with\n * the driver's own `maxBatchSize` so the more restrictive cap wins.\n */\nfunction minDefined(a: number | undefined, b: number | undefined): number | undefined {\n if (a === undefined) return b;\n if (b === undefined) return a;\n return Math.min(a, b);\n}\n\n/**\n * Split `statements` into chunks that respect both a per-batch statement\n * count cap (`maxStatements`) and a per-batch total bound-parameter cap\n * (`maxParams`). When neither cap is provided the entire list is returned\n * as a single chunk (preserves cross-batch atomicity for drivers like\n * DO SQLite that have no caps).\n *\n * Single-statement edge case: if a single statement's parameter count\n * already exceeds `maxParams`, it's emitted as its own chunk anyway. The\n * driver will reject it, which is the correct behavior — silently\n * dropping it would be worse.\n */\nfunction chunkStatements<T extends { params: unknown[] }>(\n statements: T[],\n maxStatements: number | undefined,\n maxParams: number | undefined,\n): T[][] {\n const stmtCap =\n maxStatements && maxStatements > 0 && Number.isFinite(maxStatements)\n ? Math.floor(maxStatements)\n : Infinity;\n const paramCap =\n maxParams && maxParams > 0 && Number.isFinite(maxParams) ? Math.floor(maxParams) : Infinity;\n\n if (stmtCap === Infinity && paramCap === Infinity) {\n return [statements];\n }\n\n const chunks: T[][] = [];\n let current: T[] = [];\n let currentParamCount = 0;\n for (const stmt of statements) {\n const stmtParams = stmt.params.length;\n const wouldExceedStmt = current.length + 1 > stmtCap;\n const wouldExceedParam = currentParamCount + stmtParams > paramCap;\n if (current.length > 0 && (wouldExceedStmt || wouldExceedParam)) {\n chunks.push(current);\n current = [];\n currentParamCount = 0;\n }\n current.push(stmt);\n currentParamCount += stmtParams;\n }\n if (current.length > 0) chunks.push(current);\n return chunks;\n}\n\nclass SqliteTransactionBackendImpl implements TransactionBackend {\n constructor(\n private readonly tx: SqliteTxExecutor,\n private readonly tableName: string,\n private readonly storageScope: string,\n ) {}\n\n async getDoc(docId: string): Promise<StoredGraphRecord | null> {\n const stmt = compileSelectByDocId(this.tableName, this.storageScope, docId);\n const rows = await this.tx.all(stmt.sql, stmt.params);\n return rows.length === 0 ? null : rowToRecord(rows[0]);\n }\n\n async query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]> {\n const stmt = compileSelect(this.tableName, this.storageScope, filters, options);\n const rows = await this.tx.all(stmt.sql, stmt.params);\n return rows.map(rowToRecord);\n }\n\n async setDoc(docId: string, record: WritableRecord): Promise<void> {\n const stmt = compileSet(this.tableName, this.storageScope, docId, record, Date.now());\n await this.tx.run(stmt.sql, stmt.params);\n }\n\n async updateDoc(docId: string, update: UpdatePayload): Promise<void> {\n const stmt = compileUpdate(this.tableName, this.storageScope, docId, update, Date.now());\n // RETURNING + `all()` for parity with Firestore — see SqliteBackendImpl.updateDoc.\n const sqlWithReturning = `${stmt.sql} RETURNING \"doc_id\"`;\n const rows = await this.tx.all(sqlWithReturning, stmt.params);\n if (rows.length === 0) {\n throw new FiregraphError(\n `updateDoc: no document found for doc_id=${docId} (scope=${this.storageScope})`,\n 'NOT_FOUND',\n );\n }\n }\n\n async deleteDoc(docId: string): Promise<void> {\n const stmt = compileDelete(this.tableName, this.storageScope, docId);\n await this.tx.run(stmt.sql, stmt.params);\n }\n}\n\nclass SqliteBatchBackendImpl implements BatchBackend {\n private readonly statements: CompiledStatement[] = [];\n\n constructor(\n private readonly executor: SqliteExecutor,\n private readonly tableName: string,\n private readonly storageScope: string,\n ) {}\n\n setDoc(docId: string, record: WritableRecord): void {\n this.statements.push(compileSet(this.tableName, this.storageScope, docId, record, Date.now()));\n }\n\n updateDoc(docId: string, update: UpdatePayload): void {\n this.statements.push(\n compileUpdate(this.tableName, this.storageScope, docId, update, Date.now()),\n );\n }\n\n deleteDoc(docId: string): void {\n this.statements.push(compileDelete(this.tableName, this.storageScope, docId));\n }\n\n async commit(): Promise<void> {\n if (this.statements.length === 0) return;\n await this.executor.batch(this.statements);\n this.statements.length = 0;\n }\n}\n\nclass SqliteBackendImpl implements StorageBackend {\n /** Logical table name (returned through `collectionPath` for parity with Firestore). */\n readonly collectionPath: string;\n readonly scopePath: string;\n /** Materialized storage scope (interleaved parent UIDs + subgraph names). */\n private readonly storageScope: string;\n\n constructor(\n private readonly executor: SqliteExecutor,\n tableName: string,\n storageScope: string,\n scopePath: string,\n ) {\n this.collectionPath = tableName;\n this.storageScope = storageScope;\n this.scopePath = scopePath;\n }\n\n // --- Reads ---\n\n async getDoc(docId: string): Promise<StoredGraphRecord | null> {\n const stmt = compileSelectByDocId(this.collectionPath, this.storageScope, docId);\n const rows = await this.executor.all(stmt.sql, stmt.params);\n return rows.length === 0 ? null : rowToRecord(rows[0]);\n }\n\n async query(filters: QueryFilter[], options?: QueryOptions): Promise<StoredGraphRecord[]> {\n const stmt = compileSelect(this.collectionPath, this.storageScope, filters, options);\n const rows = await this.executor.all(stmt.sql, stmt.params);\n return rows.map(rowToRecord);\n }\n\n // --- Writes ---\n\n async setDoc(docId: string, record: WritableRecord): Promise<void> {\n const stmt = compileSet(this.collectionPath, this.storageScope, docId, record, Date.now());\n await this.executor.run(stmt.sql, stmt.params);\n }\n\n async updateDoc(docId: string, update: UpdatePayload): Promise<void> {\n const stmt = compileUpdate(this.collectionPath, this.storageScope, docId, update, Date.now());\n // Use RETURNING + `all()` so missing rows surface as an error, matching\n // Firestore's `update()` semantics (NOT_FOUND when the doc doesn't exist).\n // SQLite ≥3.35 supports UPDATE … RETURNING; better-sqlite3, D1, and DO\n // SQLite all run on a recent enough engine.\n const sqlWithReturning = `${stmt.sql} RETURNING \"doc_id\"`;\n const rows = await this.executor.all(sqlWithReturning, stmt.params);\n if (rows.length === 0) {\n throw new FiregraphError(\n `updateDoc: no document found for doc_id=${docId} (scope=${this.storageScope})`,\n 'NOT_FOUND',\n );\n }\n }\n\n async deleteDoc(docId: string): Promise<void> {\n const stmt = compileDelete(this.collectionPath, this.storageScope, docId);\n await this.executor.run(stmt.sql, stmt.params);\n }\n\n // --- Transactions / Batches ---\n\n async runTransaction<T>(fn: (tx: TransactionBackend) => Promise<T>): Promise<T> {\n if (!this.executor.transaction) {\n throw new FiregraphError(\n 'Interactive transactions are not supported by this SQLite driver. ' +\n 'D1 in particular has no read-then-conditional-write transactions; ' +\n 'use a Durable Object SQLite client instead, or rewrite the code path ' +\n 'as a batch().',\n 'UNSUPPORTED_OPERATION',\n );\n }\n return this.executor.transaction(async (tx) => {\n const txBackend = new SqliteTransactionBackendImpl(\n tx,\n this.collectionPath,\n this.storageScope,\n );\n return fn(txBackend);\n });\n }\n\n createBatch(): BatchBackend {\n return new SqliteBatchBackendImpl(this.executor, this.collectionPath, this.storageScope);\n }\n\n // --- Subgraphs ---\n\n subgraph(parentNodeUid: string, name: string): StorageBackend {\n // Defense-in-depth: the public `GraphClient.subgraph()` also validates,\n // but backend users (traversal, cross-graph hops, custom integrations)\n // reach this method directly. A bad UID or a name containing '/' would\n // corrupt the materialized-path scope encoding — reject loudly.\n if (!parentNodeUid || parentNodeUid.includes('/')) {\n throw new FiregraphError(\n `Invalid parentNodeUid for subgraph: \"${parentNodeUid}\". ` +\n 'Must be a non-empty string without \"/\".',\n 'INVALID_SUBGRAPH',\n );\n }\n if (!name || name.includes('/')) {\n throw new FiregraphError(\n `Subgraph name must not contain \"/\" and must be non-empty: got \"${name}\". ` +\n 'Use chained .subgraph() calls for nested subgraphs.',\n 'INVALID_SUBGRAPH',\n );\n }\n const newStorageScope = this.storageScope\n ? `${this.storageScope}/${parentNodeUid}/${name}`\n : `${parentNodeUid}/${name}`;\n const newScope = this.scopePath ? `${this.scopePath}/${name}` : name;\n return new SqliteBackendImpl(this.executor, this.collectionPath, newStorageScope, newScope);\n }\n\n // --- Cascade & bulk ---\n\n async removeNodeCascade(\n uid: string,\n reader: GraphReader,\n options?: BulkOptions,\n ): Promise<CascadeResult> {\n // Collect all edges touching the node in the current scope (excluding self-loop).\n const [outgoingRaw, incomingRaw] = await Promise.all([\n reader.findEdges({ aUid: uid, allowCollectionScan: true, limit: 0 }),\n reader.findEdges({ bUid: uid, allowCollectionScan: true, limit: 0 }),\n ]);\n\n const seen = new Set<string>();\n const edgeDocIds: string[] = [];\n for (const edge of [...outgoingRaw, ...incomingRaw]) {\n if (edge.axbType === NODE_RELATION) continue;\n const docId = computeEdgeDocId(edge.aUid, edge.axbType, edge.bUid);\n if (!seen.has(docId)) {\n seen.add(docId);\n edgeDocIds.push(docId);\n }\n }\n\n const nodeDocId = computeNodeDocId(uid);\n const shouldDeleteSubgraphs = options?.deleteSubcollections !== false;\n\n // Pre-count subgraph rows so the returned `deleted` total reflects the\n // actual number of records removed by the prefix-delete (which is a\n // single statement, but may match many rows). One extra index lookup is\n // cheap relative to the cascade itself.\n let subgraphRowCount = 0;\n if (shouldDeleteSubgraphs) {\n const prefix = this.storageScope ? `${this.storageScope}/${uid}` : uid;\n const countStmt = compileCountScopePrefix(this.collectionPath, prefix);\n const countRows = await this.executor.all(countStmt.sql, countStmt.params);\n const first = countRows[0] as Record<string, unknown> | undefined;\n const n = first?.n;\n subgraphRowCount = typeof n === 'bigint' ? Number(n) : Number(n ?? 0);\n }\n\n // Build the full statement list. Order: edges → node → prefix-delete.\n // When the executor's `batch()` is fully atomic (DO SQLite uses\n // `transactionSync`) the chunking loop below collapses to a single batch\n // and the operation is atomic. When the executor caps batches (D1, ~100\n // statements) we lose cross-batch atomicity, but `removeNodeCascade` is\n // idempotent so a caller can retry after a partial failure.\n const writeStatements: CompiledStatement[] = edgeDocIds.map((id) =>\n compileDelete(this.collectionPath, this.storageScope, id),\n );\n writeStatements.push(compileDelete(this.collectionPath, this.storageScope, nodeDocId));\n if (shouldDeleteSubgraphs) {\n const prefix = this.storageScope ? `${this.storageScope}/${uid}` : uid;\n writeStatements.push(compileDeleteScopePrefix(this.collectionPath, prefix));\n }\n\n const {\n deleted: stmtDeleted,\n batches,\n errors,\n } = await this.executeChunkedBatches(writeStatements, options);\n\n // `nodeDeleted` / `edgesDeleted` reflect best-effort completion: a\n // chunk failure leaves us unable to know which sub-batch contained the\n // node-row delete, so we conservatively flag both as incomplete when\n // any batch fails. The caller can retry — cascade is idempotent.\n const allOk = errors.length === 0;\n const edgesDeleted = allOk ? edgeDocIds.length : 0;\n const nodeDeleted = allOk;\n\n // `stmtDeleted` counts committed *statements*. Replace the prefix-\n // delete's per-statement contribution (1) with the pre-computed row\n // count so callers see a true row total. Only credit subgraph rows when\n // every chunk succeeded — partial failure means we can't be sure the\n // chunk containing the prefix-delete actually committed.\n const prefixStatementContribution = shouldDeleteSubgraphs && allOk ? 1 : 0;\n const deleted = stmtDeleted - prefixStatementContribution + (allOk ? subgraphRowCount : 0);\n\n return { deleted, batches, errors, edgesDeleted, nodeDeleted };\n }\n\n async bulkRemoveEdges(\n params: FindEdgesParams,\n reader: GraphReader,\n options?: BulkOptions,\n ): Promise<BulkResult> {\n const effectiveParams =\n params.limit !== undefined\n ? { ...params, allowCollectionScan: params.allowCollectionScan ?? true }\n : { ...params, limit: 0, allowCollectionScan: params.allowCollectionScan ?? true };\n const edges = await reader.findEdges(effectiveParams);\n const docIds = edges.map((e) => computeEdgeDocId(e.aUid, e.axbType, e.bUid));\n\n if (docIds.length === 0) {\n return { deleted: 0, batches: 0, errors: [] };\n }\n\n const statements = docIds.map((id) =>\n compileDelete(this.collectionPath, this.storageScope, id),\n );\n\n return this.executeChunkedBatches(statements, options);\n }\n\n /**\n * Submit `statements` to the executor as one or more `batch()` calls,\n * chunking by `executor.maxBatchSize` (e.g. D1's ~100-statement cap).\n * Drivers that don't advertise a cap submit everything in one batch,\n * preserving cross-batch atomicity.\n *\n * Each chunk is retried with exponential backoff up to `maxRetries`\n * (default 3) before being recorded in `errors`. The loop continues past\n * a permanently failed chunk so the caller still gets partial progress\n * visibility — to halt on first failure, set `maxRetries: 0` and check\n * `result.errors.length` after the call.\n *\n * Returns `BulkResult`-shaped fields. `deleted` reflects only the\n * statement count of *successfully committed* batches — a prefix-delete\n * statement contributes 1 to that total even though it may match many\n * rows; `removeNodeCascade` patches that up with a pre-counted row total.\n *\n * **Atomicity caveat (D1):** when chunking kicks in, atomicity is lost\n * across chunk boundaries — one chunk may commit while a later one fails.\n * `removeNodeCascade` is idempotent (deleting the same docs again is a\n * no-op) so a caller can simply retry on partial failure. `bulkRemoveEdges`\n * is also idempotent for the same reason. DO SQLite leaves `maxBatchSize`\n * unset, so everything funnels through one atomic `transactionSync` and\n * this caveat does not apply.\n */\n private async executeChunkedBatches(\n statements: CompiledStatement[],\n options?: BulkOptions,\n ): Promise<{ deleted: number; batches: number; errors: BulkBatchError[] }> {\n if (statements.length === 0) {\n return { deleted: 0, batches: 0, errors: [] };\n }\n const maxRetries = options?.maxRetries ?? DEFAULT_MAX_RETRIES;\n\n // Split `statements` into chunks up front. Chunking honors the smallest\n // of: caller-supplied `batchSize` (used by callers who want progress\n // granularity), the driver's statement-count cap (`maxBatchSize`, D1 ≈\n // 100), and the driver's total bound-parameter cap (`maxBatchParams`,\n // D1 ≈ 1000). Most cascade/bulk statements are 2-param DELETEs so the\n // param cap rarely triggers, but we respect it defensively. Drivers with\n // no declared caps and no caller cap submit everything in one batch (DO\n // SQLite's atomic `transactionSync`).\n const callerBatchSize = options?.batchSize;\n const stmtCap = minDefined(callerBatchSize, this.executor.maxBatchSize);\n const chunks = chunkStatements(statements, stmtCap, this.executor.maxBatchParams);\n\n const errors: BulkBatchError[] = [];\n let deleted = 0;\n let batches = 0;\n const totalBatches = chunks.length;\n\n const driverParamCap = this.executor.maxBatchParams;\n\n for (let batchIndex = 0; batchIndex < chunks.length; batchIndex++) {\n const chunk = chunks[batchIndex];\n\n // A chunk that's a single statement whose param count already exceeds\n // the driver's per-batch param cap will be rejected on every attempt —\n // retrying just adds latency before surfacing the failure. `chunkStatements`\n // intentionally emits such statements as their own chunk (failing loudly\n // beats silently dropping); fast-fail here closes the loop.\n const isUnretriableOversize =\n chunk.length === 1 &&\n driverParamCap !== undefined &&\n chunk[0].params.length > driverParamCap;\n\n let committed = false;\n let lastError: Error | null = null;\n const effectiveRetries = isUnretriableOversize ? 0 : maxRetries;\n for (let attempt = 0; attempt <= effectiveRetries; attempt++) {\n try {\n await this.executor.batch(chunk);\n committed = true;\n break;\n } catch (err) {\n lastError = err instanceof Error ? err : new Error(String(err));\n if (attempt < effectiveRetries) {\n const delay = Math.min(BASE_RETRY_DELAY_MS * Math.pow(2, attempt), MAX_RETRY_DELAY_MS);\n await sleep(delay);\n }\n }\n }\n\n if (committed) {\n deleted += chunk.length;\n batches += 1;\n } else if (lastError) {\n errors.push({\n batchIndex,\n error: lastError,\n operationCount: chunk.length,\n });\n }\n\n if (options?.onProgress) {\n options.onProgress({\n completedBatches: batches,\n totalBatches,\n deletedSoFar: deleted,\n });\n }\n }\n\n return { deleted, batches, errors };\n }\n\n // --- Cross-scope (collection group) ---\n\n async findEdgesGlobal(\n params: FindEdgesParams,\n collectionName?: string,\n ): Promise<StoredGraphRecord[]> {\n const plan = buildEdgeQueryPlan(params);\n if (plan.strategy === 'get') {\n throw new FiregraphError(\n 'findEdgesGlobal() requires a query, not a direct document lookup. ' +\n 'Omit one of aUid/axbType/bUid to force a query strategy.',\n 'INVALID_QUERY',\n );\n }\n // Mirror Firestore's `collectionGroup(name)` semantics over the\n // materialized-scope SQLite layout: when `collectionName` matches the\n // table name (the implicit root default), filter to root rows; otherwise\n // filter to rows whose scope's last segment equals the requested name.\n const name = collectionName ?? this.collectionPath;\n const scopeNameFilter = {\n name,\n isRoot: name === this.collectionPath,\n };\n const stmt = compileSelectGlobal(\n this.collectionPath,\n plan.filters,\n plan.options,\n scopeNameFilter,\n );\n const rows = await this.executor.all(stmt.sql, stmt.params);\n return rows.map(rowToRecord);\n }\n}\n\n/**\n * Create a SQLite-backed `StorageBackend`.\n *\n * `tableName` is the single table that holds every triple. The driver must\n * have already created the table and indexes via `buildSchemaStatements()`\n * (the public driver factories — `createD1GraphClient` and\n * `createDOSqliteGraphClient` — handle this automatically).\n */\nexport function createSqliteBackend(\n executor: SqliteExecutor,\n tableName: string,\n options: SqliteBackendOptions = {},\n): StorageBackend {\n const storageScope = options.storageScope ?? '';\n const scopePath = options.scopePath ?? '';\n return new SqliteBackendImpl(executor, tableName, storageScope, scopePath);\n}\n"],"mappings":"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","names":[]}