@supalive/core 1.1.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -0
- package/dist/db-ClCicssQ.js +28 -0
- package/dist/db-ClCicssQ.js.map +1 -0
- package/dist/{index-DouKwYL6.d.ts → index-DJYvJYR4.d.ts} +214 -9
- package/dist/index-DJYvJYR4.d.ts.map +1 -0
- package/dist/{mysql-Bxurg_PH.d.ts → mysql-53W9lRh9.d.ts} +8 -3
- package/dist/mysql-53W9lRh9.d.ts.map +1 -0
- package/dist/{mysql-bvnjYWvZ.js → mysql-C0mysDQ_.js} +57 -25
- package/dist/mysql-C0mysDQ_.js.map +1 -0
- package/dist/{overlap-checker-Ct_HP_iB.js → overlap-checker-ClJC0nxW.js} +52 -4
- package/dist/overlap-checker-ClJC0nxW.js.map +1 -0
- package/dist/{postgres-Ck-xt72U.d.ts → postgres-7d6R9P7w.d.ts} +8 -3
- package/dist/postgres-7d6R9P7w.d.ts.map +1 -0
- package/dist/{postgres-B8u1YN8X.js → postgres-SvdXfb0w.js} +55 -22
- package/dist/postgres-SvdXfb0w.js.map +1 -0
- package/dist/{procedure-DX87tyef.js → procedure-Ze1GE-N9.js} +33 -2
- package/dist/procedure-Ze1GE-N9.js.map +1 -0
- package/dist/{query-Bh0RNxDz.js → query-CrbJ_W0W.js} +21 -1
- package/dist/query-CrbJ_W0W.js.map +1 -0
- package/dist/{realtime_db-DOCgqbwi.js → realtime_db-BtwBABDY.js} +76 -51
- package/dist/realtime_db-BtwBABDY.js.map +1 -0
- package/dist/{router-DlTYWpop.js → router-CiWYeWUR.js} +8 -2
- package/dist/router-CiWYeWUR.js.map +1 -0
- package/dist/src/client/index.d.ts +1 -1
- package/dist/src/client/index.js +44 -5
- package/dist/src/client/index.js.map +1 -1
- package/dist/src/exports/mysql.d.ts +1 -1
- package/dist/src/exports/mysql.js +1 -1
- package/dist/src/exports/postgres.d.ts +1 -1
- package/dist/src/exports/postgres.js +1 -1
- package/dist/src/exports/procedure.d.ts +2 -2
- package/dist/src/exports/procedure.js +4 -4
- package/dist/src/exports/schema-sql.d.ts +36 -2
- package/dist/src/exports/schema-sql.d.ts.map +1 -1
- package/dist/src/exports/schema-sql.js +74 -19
- package/dist/src/exports/schema-sql.js.map +1 -1
- package/dist/src/exports/server.d.ts +65 -17
- package/dist/src/exports/server.d.ts.map +1 -1
- package/dist/src/exports/server.js +724 -63
- package/dist/src/exports/server.js.map +1 -1
- package/dist/src/exports/subscription-manager-worker-entry.js +8 -5
- package/dist/src/exports/subscription-manager-worker-entry.js.map +1 -1
- package/dist/src/exports/types.d.ts +3 -3
- package/dist/src/exports/types.js +1 -1
- package/dist/{subscription-worker-CqoWp6zB.js → subscription-worker-B2XtjFWY.js} +307 -69
- package/dist/subscription-worker-B2XtjFWY.js.map +1 -0
- package/dist/{types_db-DXj_l9n-.js → types_db-CTRm_awX.js} +7 -2
- package/dist/types_db-CTRm_awX.js.map +1 -0
- package/dist/types_server-wW3ZgE7l.d.ts +420 -0
- package/dist/types_server-wW3ZgE7l.d.ts.map +1 -0
- package/package.json +5 -4
- package/dist/index-CaWMDqq0.d.ts +0 -1754
- package/dist/index-CaWMDqq0.d.ts.map +0 -1
- package/dist/index-DouKwYL6.d.ts.map +0 -1
- package/dist/mysql-Bxurg_PH.d.ts.map +0 -1
- package/dist/mysql-Cxd4Uq5p.d.ts +0 -104
- package/dist/mysql-Cxd4Uq5p.d.ts.map +0 -1
- package/dist/mysql-bvnjYWvZ.js.map +0 -1
- package/dist/overlap-checker-Ct_HP_iB.js.map +0 -1
- package/dist/postgres-B8u1YN8X.js.map +0 -1
- package/dist/postgres-CfT_aDx3.d.ts +0 -108
- package/dist/postgres-CfT_aDx3.d.ts.map +0 -1
- package/dist/postgres-Ck-xt72U.d.ts.map +0 -1
- package/dist/procedure-DX87tyef.js.map +0 -1
- package/dist/query-Bh0RNxDz.js.map +0 -1
- package/dist/realtime_db-DOCgqbwi.js.map +0 -1
- package/dist/router-DlTYWpop.js.map +0 -1
- package/dist/src/exports/codegen.d.ts +0 -38
- package/dist/src/exports/codegen.d.ts.map +0 -1
- package/dist/src/exports/codegen.js +0 -134
- package/dist/src/exports/codegen.js.map +0 -1
- package/dist/subscription-worker-CqoWp6zB.js.map +0 -1
- package/dist/subscription-worker-PtmJWEj1.js +0 -511
- package/dist/subscription-worker-PtmJWEj1.js.map +0 -1
- package/dist/subscription-worker-pahX0zxg.js +0 -511
- package/dist/subscription-worker-pahX0zxg.js.map +0 -1
- package/dist/types_db-DXj_l9n-.js.map +0 -1
- package/dist/types_server-BW1ys_SK.d.ts +0 -216
- package/dist/types_server-BW1ys_SK.d.ts.map +0 -1
- package/dist/types_server-Dpsi0wpG.d.ts +0 -216
- package/dist/types_server-Dpsi0wpG.d.ts.map +0 -1
- package/dist/types_server-DzrIccyO.d.ts +0 -216
- package/dist/types_server-DzrIccyO.d.ts.map +0 -1
- package/dist/types_server-TFo6SWDH.d.ts +0 -216
- package/dist/types_server-TFo6SWDH.d.ts.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres-SvdXfb0w.js","names":[],"sources":["../src/db/postgres.ts"],"sourcesContent":["import { Pool, PoolConfig, PoolClient } from \"pg\";\nimport { Database, PooledClient, DbQueryResult, DbType, SqlBuilder, TxDatabase, PreparedQuery, PendingPointEntry, PreparedQueries, LazyCommitTsParam, CoreInitLockCtx, CORE_INIT_LOCK_KEY } from \"./db\";\nimport type { CommitLogEntry, ReadEntry, WriteEntry } from \"./types_db\";\nimport { normalizeIdToBytes, OccConflictError, normalizeToBytes } from \"./types_db\";\nimport { utf8Encode } from \"./bytes\";\n\n// pg's bytea encoder checks `Buffer.isBuffer`; a plain Uint8Array falls through\n// to its scalar path and corrupts the bytes. Wrap as a zero-copy Buffer view.\nfunction toBufferView(bytes: Uint8Array): Buffer {\n if (Buffer.isBuffer(bytes)) return bytes;\n return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n}\nimport { checkLogsAffectReadSet, mapReadSetToRaw } from \"../server/overlap-checker\";\nimport { supaliveStringify } from \"../helper\";\nimport { CacheLayer } from \"./cache\";\nimport { Logger } from \"pino\";\n\nexport class PgDatabase implements Database {\n constructor(\n logger: Logger,\n config: PoolConfig,\n pool: Pool | undefined = undefined,\n ) {\n this.logger = logger;\n this.config = config;\n if (pool) {\n this.pool = pool;\n } else {\n this.pool = new Pool(config);\n }\n }\n\n private logger: Logger;\n public config: PoolConfig;\n private pool: Pool;\n readonly sqlBuilder: SqlBuilder = pgSql;\n readonly dbType: DbType = \"postgres\";\n // Lazy per-table column-type cache. The apply path consults this for\n // unnest carriers/casts in batched writes; we never re-query pg_attribute\n // for a table we've already seen unless the cache is explicitly\n // invalidated (e.g. after a migration). Shared with tx clients via\n // adoptRegistries so a single startup load services all transactions.\n public columnTypes: Map<string, Map<string, string>> = new Map();\n // Compiled-SQL cache keyed by (op, table, sorted column signature).\n // Mirrors columnTypes — invalidated together when types change.\n protected sqlCache: Map<string, string> = new Map();\n\n /**\n * Fetches actual column types from Postgres for `table` and returns a\n * map of DB column name → PG type string (as produced by `format_type`,\n * e.g. `bigint`, `integer`, `character varying(32)`, `text[]`, `jsonb`).\n * No caching here — see {@link getColumnTypes} for the cached path.\n */\n async loadColumnTypesFromPg(table: string): Promise<Map<string, string>> {\n const result = await this.query<{ column_name: string; pg_type: string }>(\n `SELECT a.attname AS column_name, format_type(a.atttypid, a.atttypmod) AS pg_type\n FROM pg_attribute a\n WHERE a.attrelid = $1::regclass AND a.attnum > 0 AND NOT a.attisdropped\n ORDER BY a.attnum`,\n [table],\n );\n // PG folds unquoted identifiers to lowercase on the server side, so\n // pg_attribute.attname comes back lowercase. JS-side column keys\n // (`entry.data`) often arrive in camelCase from the schema layer; we\n // lookup via lowercase to bridge the two cases without making callers\n // worry about it.\n const out = new Map<string, string>();\n for (const row of result.rows) out.set(row.column_name.toLowerCase(), row.pg_type);\n return out;\n }\n\n /**\n * Synchronous column-type lookup. The apply path calls this on every\n * batched write, so it can't await — populate the cache via\n * {@link bootstrapColumnTypes} at server start. Throws if `table` is\n * missing (programmer error / bootstrap skipped).\n */\n getColumnTypes(table: string): Map<string, string> | null {\n const hit = this.columnTypes.get(table);\n if (!hit) {\n this.logger.warn({ table }, \"PgDatabase: no cached column types fallback to non-batched writes\");\n return null;\n }\n return hit;\n }\n\n /**\n * Eagerly load column types into the in-memory cache. With a Redis\n * client supplied, attempts the cache key first and falls through to\n * Postgres on miss (writing the result back to Redis). This is the\n * \"one server queries PG, the rest read from Redis\" pattern for cold\n * starts in a multi-instance deploy. Without Redis, always queries PG.\n *\n * Pass `tables` to limit the load to a specific list; otherwise every\n * user table in the connected database is loaded.\n *\n * The Redis cache key should encode whatever invalidates types in your\n * environment (typically a schema version / deploy version). The\n * subscriber in supalive-server.ts is expected to publish a reload\n * signal after migrations and invoke {@link invalidateColumnTypes}.\n */\n async bootstrapColumnTypes(opts: {\n redis?: CacheLayer;\n cacheKey: string;\n tables?: string[];\n }): Promise<void> {\n this.invalidateColumnTypes();\n\n const { redis, cacheKey, tables } = opts;\n\n // Redis hit path: deserialize the full table → column → pgType map\n // and populate in one shot, skipping any pg_attribute round-trips.\n if (redis) {\n const cached = await redis.getCachedTableColumnTypes(cacheKey);\n if (cached) {\n for (const [table, cols] of Object.entries(cached)) {\n this.columnTypes.set(table, new Map(Object.entries(cols)));\n }\n return;\n }\n }\n\n // Cache miss (or no Redis): query Postgres for the listed tables\n // (or enumerate all user tables) and populate.\n const names = tables ?? await this.listUserTables();\n for (const t of names) {\n if (this.columnTypes.has(t)) continue;\n\n const fresh = await this.loadColumnTypesFromPg(t);\n this.columnTypes.set(t, fresh);\n }\n\n // Write back to Redis so subsequent server instances skip the PG hop.\n if (redis) {\n const dump: Record<string, Record<string, string>> = {};\n for (const [table, cols] of this.columnTypes) {\n dump[table] = Object.fromEntries([...cols.entries()].sort());\n }\n await redis.setCachedTableColumnsTypes(cacheKey, dump);\n }\n }\n\n /**\n * Invalidate cached types (and dependent SQL) for `table`, or the entire\n * cache if `table` is omitted. The Redis-pubsub reload signal in\n * supalive-server.ts is expected to call this on schema changes.\n */\n private invalidateColumnTypes(table?: string): void {\n if (table) {\n this.columnTypes.delete(table);\n for (const k of [...this.sqlCache.keys()]) {\n if (k.includes(`:${table}:`) || k === `d:${table}`) this.sqlCache.delete(k);\n }\n } else {\n this.columnTypes.clear();\n this.sqlCache.clear();\n }\n }\n\n /** Enumerate the user-defined tables in the connected database — used\n * by bootstrapColumnTypes() when no explicit list is provided. */\n private async listUserTables(): Promise<string[]> {\n const res = await this.query<{ relname: string }>(\n `SELECT c.relname FROM pg_class c\n JOIN pg_namespace n ON n.oid = c.relnamespace\n WHERE c.relkind = 'r' AND n.nspname = ANY (current_schemas(false))\n AND c.relname NOT IN ('commit_logs','metadata')`,\n );\n return res.rows.map((r) => r.relname);\n }\n\n async getCoreVersion(): Promise<bigint> {\n const result = await this.query<{ meta_value: string | number | bigint }>(\n \"SELECT meta_value FROM metadata WHERE meta_key = 'core_version'\",\n );\n return BigInt(result.rows[0]?.meta_value ?? 0);\n }\n\n // Runtime core bootstrap applied per version step. All schema (tables,\n // indexes, the global_commit_ts sequence) lives in the generated schema and\n // is migrated ahead of runtime (see coreTablesSql); this covers data seeding\n // and any case where a schema change must be made directly at runtime.\n coreMigrationStatements(toVersion: bigint): string[] {\n if (toVersion === 1n) {\n return [\n `INSERT INTO metadata (meta_key, meta_value) VALUES ('latest_committed_ts', 0) ON CONFLICT (meta_key) DO NOTHING;`,\n `INSERT INTO metadata (meta_key, meta_value) VALUES ('min_retained_ts', 0) ON CONFLICT (meta_key) DO NOTHING;`,\n ];\n }\n return [];\n }\n\n async withCoreInitLock(fn: (ctx: CoreInitLockCtx) => Promise<void>): Promise<void> {\n const client = await this.pool.connect();\n try {\n await client.query(\"BEGIN\");\n // Transaction-level advisory lock: auto-released on COMMIT/ROLLBACK, so\n // no leak even if fn throws. Serializes core init across processes.\n await client.query(\"SELECT pg_advisory_xact_lock($1)\", [CORE_INIT_LOCK_KEY]);\n\n const ctx: CoreInitLockCtx = {\n exec: async (sql, params = []) => {\n const r = await client.query(sql, params);\n return { rows: r.rows, rowCount: r.rowCount };\n },\n getVersion: async () => {\n const r = await client.query<{ meta_value: string | number | bigint }>(\n \"SELECT meta_value FROM metadata WHERE meta_key = 'core_version'\",\n );\n return BigInt(r.rows[0]?.meta_value ?? 0);\n },\n setVersion: async (version) => {\n await client.query(\n `INSERT INTO metadata (meta_key, meta_value) VALUES ('core_version', $1)\n ON CONFLICT (meta_key) DO UPDATE SET meta_value = EXCLUDED.meta_value`,\n [version.toString()],\n );\n },\n };\n\n await fn(ctx);\n await client.query(\"COMMIT\");\n } catch (err) {\n await client.query(\"ROLLBACK\").catch(() => { });\n throw translatePgError(err);\n } finally {\n client.release();\n }\n }\n\n async query<T = any>(sql: string, params: unknown[] = []): Promise<DbQueryResult<T>> {\n const client = await this.pool.connect();\n try {\n const result = await client.query(sql, params);\n return {\n rows: result.rows as T[],\n rowCount: result.rowCount,\n };\n } catch (err) {\n throw translatePgError(err);\n } finally {\n client.release();\n }\n }\n\n async getClient(): Promise<PooledClient> {\n return await this.pool.connect() as PooledClient;\n }\n\n async getTransactionClient(): Promise<TxDatabase> {\n const client = await this.pool.connect();\n const tx = new PgTxDatabase(this.logger, this.config, this.pool, client);\n // Tx clients are short-lived. Share the parent's caches by reference so\n // (a) types loaded once at startup are visible inside transactions, and\n // (b) the SQL cache grows across the process rather than per-tx.\n tx.adoptCaches(this.columnTypes, this.sqlCache);\n return tx;\n }\n\n /** Internal: replace this instance's caches with shared references from\n * a parent. Used by PgDatabase.getTransactionClient. */\n adoptCaches(\n columnTypes: Map<string, Map<string, string>>,\n sqlCache: Map<string, string>,\n ): void {\n this.columnTypes = columnTypes;\n this.sqlCache = sqlCache;\n }\n\n async getLatestSnapshotTimestamp(): Promise<bigint> {\n const result = await this.query<{ meta_value: string | number | bigint }>(\"SELECT meta_value FROM metadata WHERE meta_key = 'latest_committed_ts'\");\n return BigInt(result.rows[0]?.meta_value ?? 0);\n }\n\n async updateLatestSnapshotTimestamp(commitTs: bigint): Promise<void> {\n await this.query(\n \"UPDATE metadata SET meta_value = $1 WHERE meta_key = 'latest_committed_ts' AND meta_value < $1\",\n [commitTs],\n );\n }\n\n async getMinSnapshotTimestamp(): Promise<bigint> {\n const result = await this.query<{ meta_value: string | number | bigint }>(\n \"SELECT meta_value FROM metadata WHERE meta_key = 'min_retained_ts'\",\n );\n return BigInt(result.rows[0]?.meta_value ?? 0);\n }\n\n async updateMinSnapshotTimestamp(ts: bigint): Promise<void> {\n await this.query(\n \"UPDATE metadata SET meta_value = $1 WHERE meta_key = 'min_retained_ts' AND meta_value < $1\",\n [ts],\n );\n }\n\n async getNextTimestamp(): Promise<bigint> {\n const result = await this.query<{ nextval: string | number | bigint }>(\n \"SELECT nextval('global_commit_ts') as nextval\"\n );\n return BigInt(result.rows[0]?.nextval ?? \"0\");\n }\n\n // Runs on the pool, not the tx client — intentionally. This keeps the\n // transaction window short, but it means `prevData` is not snapshot-\n // consistent with the eventual commitTs (a peer may have moved the row\n // between beginTs and this SELECT). That's safe because:\n // 1. The per-row CAS in pgApplyWrites is the real snapshot fence. If a\n // peer committed in our staleness window, the CAS sees a moved\n // commit_ts and throws OccConflictError → retry.\n // 2. The only window where CAS passes with \"stale\" prevData is when\n // the current row at our SELECT moment matches `expectedCommitTs`;\n // by construction nobody committed between our captured ts and now,\n // so prevData IS consistent with the ts we're CAS-ing against.\n // 3. The downstream consumer (overlap-checker.checkRowWriteAffectsRange)\n // is conservative on missing/odd prevData — \"unknown\" verdicts still\n // invalidate, so the worst case here is a spurious invalidation,\n // never a missed one.\n // Locked in by postgres.attach-prev-data.test.ts.\n async attachPrevDataToWriteSet(writeSet: WriteEntry[], readSet: ReadEntry[]): Promise<void> {\n // Index prior point reads by (table, id) so we can carry the\n // read-time commit_ts onto matching writes. Iterate in reverse so the\n // last read wins (handles handlers that read the same row twice).\n const readTsByKey = new Map<string, bigint | null>();\n for (let i = readSet.length - 1; i >= 0; i--) {\n const r = readSet[i];\n if (r.kind !== \"point\" || r.expectedCommitTs === undefined) continue;\n\n const key = `${r.table} ${r.id}`;\n if (!readTsByKey.has(key)) readTsByKey.set(key, r.expectedCommitTs);\n }\n\n const byTable = new Map<string, WriteEntry[]>();\n for (const entry of writeSet) {\n if (entry.op !== \"update\" && entry.op !== \"delete\") continue;\n\n const bucket = byTable.get(entry.table);\n if (bucket) bucket.push(entry);\n else byTable.set(entry.table, [entry]);\n }\n if (byTable.size === 0) return;\n\n for (const [table, entries] of byTable) {\n const ids = entries.map((e) => e.id);\n const placeholders = ids.map((_, i) => `$${i + 1}`).join(\", \");\n // No FOR UPDATE — the per-row CAS on the subsequent UPDATE/DELETE is\n // the fence. If a peer mutated the row since our read, the CAS will\n // see a stale expected commit_ts and throw OCC.\n const sql = `SELECT * FROM ${table} WHERE id IN (${placeholders})`;\n const result = await this.query<Record<string, unknown>>(sql, ids);\n\n const byId = new Map<string, Record<string, unknown>>();\n for (const row of result.rows) {\n byId.set(String(row.id), row);\n }\n for (const entry of entries) {\n const row = byId.get(entry.id);\n const key = `${entry.table} ${entry.id}`;\n const priorReadTs = readTsByKey.get(key);\n\n if (row) {\n entry.prevData = row;\n // Prefer the read-time ts so read-then-update patterns get\n // beginTs-level isolation; fall back to current row ts for\n // blind writes (no prior read).\n entry.expectedCommitTs = priorReadTs !== undefined\n ? priorReadTs\n : BigInt(row.commit_ts as string | number | bigint);\n } else if (priorReadTs !== undefined) {\n // Row gone now, but we had read it earlier: carry the read ts —\n // the CAS will then mismatch (row deleted) and throw OCC.\n entry.expectedCommitTs = priorReadTs;\n }\n // else: neither a prior read nor a current row → leave undefined\n // so CAS misses and surfaces OCC.\n }\n }\n }\n\n // Prepare BEFORE the conflict scan: each write is a CAS against the row's\n // commit_ts, so any concurrent peer that touched the row produces a\n // 0-row-affected UPDATE/DELETE → OccConflictError. Inserts surface as\n // PK violations, also translated to OCC.\n async prepareCommitWrites(\n readSet: ReadEntry[],\n writeSet: WriteEntry[],\n commitTs: LazyCommitTsParam,\n ): Promise<PreparedQueries> {\n\n const result: PreparedQuery[] = [];\n\n await this.pgPrepareBatchWrites(result, writeSet, commitTs);\n await this.pgPrepareCommitLogs(result, writeSet, commitTs);\n\n const hasRangeRead = readSet.some(r => r.kind === \"range\");\n if (!hasRangeRead) {\n const validateReads = this.pgPrepareValidatePointReadsByCommitTs(readSet, writeSet);\n return { commits: result, validateReadPoints: validateReads, hasRangeRead: false };\n }\n\n // has atleast one range read so use scan, prepare data\n const tableIds = [...new Set(readSet.map(r => r.tableId))];\n const rawReadSet = mapReadSetToRaw(readSet);\n\n return { commits: result, hasRangeRead: true, tableIds: tableIds, rawReadSet: rawReadSet };\n };\n\n private async pgPrepareBatchWrites(\n result: PreparedQuery[],\n writeSet: WriteEntry[],\n commitTs: LazyCommitTsParam,\n ) {\n let i = 0;\n let counter = 0;\n while (i < writeSet.length) {\n counter++;\n const { table, op } = writeSet[i];\n let j = i + 1;\n while (j < writeSet.length && writeSet[j].table === table && writeSet[j].op === op) j++;\n\n const batch = writeSet.slice(i, j);\n if (op === \"insert\") await this.pgPrepareInsertBatch(result, table, batch, commitTs);\n else if (op === \"update\") await this.pgPrepareUpdateBatch(result, table, batch, commitTs);\n else await this.pgPrepareDeleteBatch(result, table, batch);\n i = j;\n\n if (counter % 50 == 0)\n await new Promise<void>((resolve) => setImmediate(resolve));\n }\n }\n\n // N=1 INSERT fast-path: identical SQL shape to the original per-row\n // code, no unnest overhead. PG infers param types from the column\n // context directly, so we don't need pg_attribute either.\n private pgPrepareInsertSingle(\n result: PreparedQuery[],\n table: string,\n entry: WriteEntry,\n commitTs: LazyCommitTsParam,\n ) {\n const data = { ...(entry.data ?? {}), commit_ts: commitTs } as Record<string, unknown>;\n const cols = Object.keys(data);\n const placeholders = cols.map((_, i) => `$${i + 1}`);\n result.push({\n sql: `INSERT INTO ${table} (${cols.join(\", \")}) VALUES (${placeholders.join(\", \")})`,\n params: Object.values(data),\n operation: \"insertSingle\",\n table: table,\n entryId: entry.id,\n })\n }\n\n private async pgPrepareInsertBatch(\n result: PreparedQuery[],\n table: string,\n entries: WriteEntry[],\n commitTs: LazyCommitTsParam,\n ) {\n if (entries.length === 0) return;\n\n const types = this.getColumnTypes(table);\n if (!types) {\n this.logger.warn({ table }, \"No column types found, falling back to non-batched writes for insert\");\n let counter = 0;\n for (const entry of entries) {\n counter++;\n this.pgPrepareInsertSingle(result, table, entry, commitTs);\n\n if (counter % 50 == 0) {\n await new Promise<void>((resolve) => setImmediate(resolve));\n }\n }\n return;\n }\n\n // Sub-bucket by sorted column signature: writes with different optional\n // columns can't share a statement.\n const groups = new Map<string, { cols: string[]; rows: WriteEntry[] }>();\n for (const entry of entries) {\n const cols = [...Object.keys(entry.data ?? {}), 'commit_ts'].sort();\n const key = cols.join(\"|\");\n let g = groups.get(key);\n if (!g) { g = { cols, rows: [] }; groups.set(key, g); }\n g.rows.push(entry);\n }\n\n let counter = 0;\n for (const { cols, rows } of groups.values()) {\n counter++;\n\n const sql = this.buildOrGetInsertSql(table, types, cols);\n // Params layout: one carrier array per column, each of length N.\n const params: unknown[][] = cols.map(() => new Array(rows.length));\n for (let r = 0; r < rows.length; r++) {\n const data = { ...(rows[r].data ?? {}), commit_ts: commitTs } as Record<string, unknown>;\n for (let c = 0; c < cols.length; c++) {\n const pgType = types.get(cols[c].toLowerCase()) ?? \"text\";\n params[c][r] = encodeForCarrier(data[cols[c]], pgType);\n }\n }\n\n if (counter % 50 == 0) {\n await new Promise<void>((resolve) => setImmediate(resolve));\n }\n\n result.push({\n sql: sql,\n params: params,\n operation: \"insertBatch\",\n table: table,\n })\n }\n }\n\n // N=1 UPDATE fast-path: same shape as the original per-row CAS.\n private pgPrepareUpdateSingle(\n result: PreparedQuery[],\n table: string,\n entry: WriteEntry,\n commitTs: LazyCommitTsParam,\n ) {\n const data = { ...(entry.data ?? {}), commit_ts: commitTs } as Record<string, unknown>;\n const cols = Object.keys(data);\n const sets = cols.map((col, i) => `${col} = $${i + 1}`);\n const values = Object.values(data);\n const idP = `$${values.length + 1}`;\n const tsP = `$${values.length + 2}`;\n const expected = entry.expectedCommitTs ?? -1n;\n\n result.push({\n sql: `UPDATE ${table} SET ${sets.join(\", \")} WHERE id = ${idP} AND commit_ts = ${tsP}`,\n params: [...values, entry.id, expected],\n operation: \"updateSingle\",\n table: table,\n entryId: entry.id,\n expectedTs: expected,\n })\n }\n\n private async pgPrepareUpdateBatch(\n result: PreparedQuery[],\n table: string,\n entries: WriteEntry[],\n commitTs: LazyCommitTsParam,\n ) {\n if (entries.length === 0) return;\n\n const types = this.getColumnTypes(table);\n if (!types) {\n this.logger.warn({ table }, \"No column types found, falling back to non-batched writes for update\");\n\n let counter = 0;\n for (const entry of entries) {\n counter++;\n this.pgPrepareUpdateSingle(result, table, entry, commitTs);\n\n if (counter % 50 == 0) {\n await new Promise<void>((resolve) => setImmediate(resolve));\n }\n }\n return;\n }\n\n const groups = new Map<string, { cols: string[]; rows: WriteEntry[] }>();\n for (const entry of entries) {\n const cols = [...Object.keys(entry.data ?? {}), 'commit_ts'].sort();\n const key = cols.join(\"|\");\n let g = groups.get(key);\n if (!g) { g = { cols, rows: [] }; groups.set(key, g); }\n g.rows.push(entry);\n }\n\n let counter = 0;\n for (const { cols, rows } of groups.values()) {\n counter++;\n\n const sql = this.buildOrGetUpdateSql(table, types, cols);\n const idType = types.get(\"id\") ?? \"text\";\n const idArr: unknown[] = new Array(rows.length);\n const expArr: unknown[] = new Array(rows.length);\n const colArrs: unknown[][] = cols.map(() => new Array(rows.length));\n for (let r = 0; r < rows.length; r++) {\n const entry = rows[r];\n const data = { ...(entry.data ?? {}), commit_ts: commitTs } as Record<string, unknown>;\n const expected = entry.expectedCommitTs ?? -1n;\n idArr[r] = encodeForCarrier(entry.id, idType);\n expArr[r] = encodeForCarrier(expected, \"bigint\");\n for (let c = 0; c < cols.length; c++) {\n const pgType = types.get(cols[c].toLowerCase()) ?? \"text\";\n colArrs[c][r] = encodeForCarrier(data[cols[c]], pgType);\n }\n }\n\n if (counter % 50 == 0) {\n await new Promise<void>((resolve) => setImmediate(resolve));\n }\n\n const params: unknown[][] = [idArr, expArr, ...colArrs];\n result.push({\n sql: sql,\n params: params,\n operation: \"updateBatch\",\n table: table,\n entries: rows,\n })\n }\n }\n\n // N=1 DELETE fast-path: original per-row CAS shape.\n private pgPrepareDeleteSingle(\n result: PreparedQuery[],\n table: string,\n entry: WriteEntry,\n ) {\n const expected = entry.expectedCommitTs ?? -1n;\n result.push({\n sql: `DELETE FROM ${table} WHERE id = $1 AND commit_ts = $2`,\n params: [entry.id, expected],\n operation: \"deleteSingle\",\n table: table,\n entryId: entry.id,\n expectedTs: expected,\n });\n }\n\n private async pgPrepareDeleteBatch(\n result: PreparedQuery[],\n table: string,\n entries: WriteEntry[],\n ) {\n if (entries.length === 0) return;\n\n const types = this.getColumnTypes(table);\n if (!types) {\n this.logger.warn({ table }, \"No column types found, falling back to non-batched writes for delete\");\n\n let counter = 0;\n for (const entry of entries) {\n counter++;\n this.pgPrepareDeleteSingle(result, table, entry);\n\n if (counter % 50 == 0) {\n await new Promise<void>((resolve) => setImmediate(resolve));\n }\n }\n return;\n }\n\n const sql = this.buildOrGetDeleteSql(table, types);\n const idType = types.get(\"id\") ?? \"text\";\n const idArr: unknown[] = new Array(entries.length);\n const expArr: unknown[] = new Array(entries.length);\n\n let counter = 0;\n for (let i = 0; i < entries.length; i++) {\n counter++;\n const entry = entries[i];\n const expected = entry.expectedCommitTs ?? -1n;\n idArr[i] = encodeForCarrier(entry.id, idType);\n expArr[i] = encodeForCarrier(expected, \"bigint\");\n\n if (counter % 50 == 0) {\n await new Promise<void>((resolve) => setImmediate(resolve));\n }\n }\n result.push({\n sql: sql,\n params: [idArr, expArr],\n operation: \"deleteBatch\",\n table: table,\n entries: entries,\n });\n }\n\n private buildOrGetInsertSql(table: string, types: Map<string, string>, cols: string[]): string {\n const key = `i:${table}:${cols.join(\",\")}`;\n let sql = this.sqlCache.get(key);\n if (sql) return sql;\n\n const placeholders = cols.map((c, i) => {\n const pgType = types.get(c.toLowerCase()) ?? \"text\";\n return `$${i + 1}::${pgCarrierType(pgType)}`;\n });\n const aliases = cols.map((_, i) => `c${i}`);\n const selectExprs = cols.map((c, i) => {\n const pgType = types.get(c.toLowerCase()) ?? \"text\";\n return pgSelectExpr(aliases[i], pgType);\n });\n sql = `INSERT INTO ${table} (${cols.join(\", \")}) ` +\n `SELECT ${selectExprs.join(\", \")} ` +\n `FROM unnest(${placeholders.join(\", \")}) AS u(${aliases.join(\", \")})`;\n this.sqlCache.set(key, sql);\n return sql;\n }\n\n private buildOrGetUpdateSql(table: string, types: Map<string, string>, cols: string[]): string {\n const key = `u:${table}:${cols.join(\",\")}`;\n let sql = this.sqlCache.get(key);\n if (sql) return sql;\n\n const idType = types.get(\"id\") ?? \"text\";\n const idCarrier = `$1::${pgCarrierType(idType)}`;\n const expCarrier = `$2::bigint[]`;\n const dataCarriers = cols.map((c, i) => {\n const pgType = types.get(c.toLowerCase()) ?? \"text\";\n return `$${i + 3}::${pgCarrierType(pgType)}`;\n });\n const aliases = [\"id_c\", \"exp_c\", ...cols.map((_, i) => `c${i}`)];\n const setClause = cols\n .map((c, i) => {\n const pgType = types.get(c.toLowerCase()) ?? \"text\";\n return `${c} = ${pgSelectExpr(`u.${aliases[i + 2]}`, pgType)}`;\n })\n .join(\", \");\n sql =\n `UPDATE ${table} AS t SET ${setClause} ` +\n `FROM unnest(${idCarrier}, ${expCarrier}, ${dataCarriers.join(\", \")}) AS u(${aliases.join(\", \")}) ` +\n `WHERE t.id = u.id_c AND t.commit_ts = u.exp_c ` +\n `RETURNING t.id AS id`;\n this.sqlCache.set(key, sql);\n return sql;\n }\n\n private buildOrGetDeleteSql(table: string, types: Map<string, string>): string {\n const key = `d:${table}`;\n let sql = this.sqlCache.get(key);\n if (sql) return sql;\n const idType = types.get(\"id\") ?? \"text\";\n sql =\n `DELETE FROM ${table} AS t ` +\n `USING unnest($1::${pgCarrierType(idType)}, $2::bigint[]) AS u(id_c, exp_c) ` +\n `WHERE t.id = u.id_c AND t.commit_ts = u.exp_c ` +\n `RETURNING t.id AS id`;\n this.sqlCache.set(key, sql);\n return sql;\n }\n\n private async pgPrepareCommitLogs(\n result: PreparedQuery[],\n writeEntries: WriteEntry[],\n commitTs: LazyCommitTsParam,\n ) {\n if (writeEntries.length === 0) return;\n\n // unnest with column-typed arrays: 4 placeholders regardless of N\n // (vs 4N for multi-row VALUES). All four columns have fixed driver-side\n // types so no per-table metadata lookup is needed.\n const ids: Buffer[] = new Array(writeEntries.length);\n const tss: LazyCommitTsParam[] = new Array(writeEntries.length);\n const tableIds: Buffer[] = new Array(writeEntries.length);\n const datas: Buffer[] = new Array(writeEntries.length);\n\n let counter = 0;\n for (let i = 0; i < writeEntries.length; i++) {\n counter++;\n\n const entry = writeEntries[i];\n ids[i] = toBufferView(normalizeIdToBytes(entry.id));\n tss[i] = commitTs;\n tableIds[i] = toBufferView(entry.tableId);\n datas[i] = toBufferView(utf8Encode(supaliveStringify(entry)));\n\n if (counter % 50 == 0) {\n await new Promise<void>((resolve) => setImmediate(resolve));\n }\n }\n\n result.push({\n sql: `INSERT INTO commit_logs (id, ts, table_id, data) ` +\n `SELECT * FROM unnest($1::bytea[], $2::bigint[], $3::bytea[], $4::bytea[])`,\n params: [ids, tss, tableIds, datas],\n table: 'commit_logs',\n operation: 'commit_logs',\n })\n }\n\n /**\n * Point-only OCC check. For each genuine point read, compare the row's\n * current commit_ts against the value captured at read time. A `null`\n * expectedCommitTs means \"row didn't exist when we read\"; any row found\n * now is a conflict. Reads whose row is being written in this same txn\n * are skipped — the CAS UPDATE/DELETE already validated those.\n */\n private pgPrepareValidatePointReadsByCommitTs(\n readSet: ReadEntry[],\n writeSet: WriteEntry[],\n ): PreparedQuery[] {\n const result: PreparedQuery[] = [];\n\n // Skip reads whose row is being mutated in this same tx — CAS already\n // fenced them. We dedupe per (table, id) so duplicate reads of the same\n // row don't cause extra fetches.\n const writeKeys = new Set<string>();\n for (const w of writeSet) {\n writeKeys.add(`${w.table} ${w.id}`);\n }\n\n const byTable = new Map<string, PendingPointEntry[]>();\n const seen = new Set<string>();\n\n for (const entry of readSet) {\n if (entry.kind !== \"point\") continue;\n\n const key = `${entry.table} ${entry.id}`;\n if (writeKeys.has(key) || seen.has(key)) continue;\n\n seen.add(key);\n // expectedCommitTs === undefined → no commit_ts captured at read time\n // (synthetic or pre-migration). Skip — we cannot CAS-validate it. This\n // path should only fire when callers construct PointReads externally.\n if (entry.expectedCommitTs === undefined) continue;\n\n const bucket = byTable.get(entry.table);\n if (bucket) bucket.push({ entry, key });\n else byTable.set(entry.table, [{ entry, key }]);\n }\n\n for (const [table, pendings] of byTable) {\n const ids = pendings.map((p) => p.entry.id);\n const placeholders = ids.map((_, i) => `$${i + 1}`).join(\", \");\n\n result.push({\n sql: `SELECT id, commit_ts FROM ${table} WHERE id IN (${placeholders})`,\n params: ids,\n table: table,\n operation: \"pointReadValidation\",\n pendingsEntry: pendings,\n });\n }\n\n return result;\n }\n\n async commitWrites(\n beginTs: bigint,\n queries: PreparedQueries,\n commitTs: bigint,\n ): Promise<{ success: boolean }> {\n await this.pgApplyWrites(queries.commits);\n\n // Fast path: when the readSet has no range reads, we don't need a\n // commit-log scan at all — point reads validate via per-row commit_ts,\n // and writes already self-validated via CAS above. This is the dominant\n // shape (most mutations target known ids) and eliminates the heaviest\n // OCC cost in the previous design.\n if (!queries.hasRangeRead) {\n if ((queries.validateReadPoints?.length ?? 0) > 0) {\n await this.validatePointReadsByCommitTs(queries.validateReadPoints ?? []);\n }\n return { success: true };\n }\n\n // Slow path: at least one range read present. Fall back to the\n // commit-log scan so phantom inserts and predicate transitions-in are\n // still caught. Point reads in the same set are covered by the same\n // scan, so no separate validation is needed.\n const logs = await this.getCommitLogsSinceTs(beginTs, commitTs, queries.tableIds ?? []);\n const hasConflict = checkLogsAffectReadSet(logs, queries.rawReadSet ?? []);\n if (hasConflict) {\n throw new OccConflictError(\n `Conflict detected: overlapping commit logs since ${beginTs}`,\n { logs }\n );\n }\n\n return { success: true };\n };\n\n // Walks the writeSet in order and coalesces consecutive entries with the\n // same (table, op) into one batched statement. Order is preserved across\n // batch boundaries so replace-patterns (delete-then-insert of the same\n // id) behave identically to the per-row code path. Single-row batches\n // take a direct INSERT/UPDATE/DELETE fast-path to avoid the unnest+join\n // overhead, which matters under high contention.\n private async pgApplyWrites(result: PreparedQuery[]): Promise<void> {\n for (const q of result) {\n switch (q.operation) {\n case 'insertSingle': {\n try {\n await this.query(q.sql, q.params);\n } catch (err) {\n if (err && typeof err === \"object\" && (err as { code?: string }).code === \"23505\") {\n throw new OccConflictError(\n `Conflict: insert into ${q.table}/${q.entryId} hit duplicate key`,\n err,\n );\n }\n throw err;\n }\n break;\n }\n case 'insertBatch': {\n try {\n await this.query(q.sql, q.params);\n } catch (err) {\n if (err && typeof err === \"object\" && (err as { code?: string }).code === \"23505\") {\n const detail = (err as { detail?: string }).detail;\n const idMatch = detail?.match(/Key \\([^)]+\\)=\\(([^)]+)\\)/);\n const failedId = idMatch?.[1];\n throw new OccConflictError(\n failedId !== undefined\n ? `Conflict: insert into ${q.table}/${failedId} hit duplicate key`\n : `Conflict: batch insert into ${q.table} hit duplicate key`,\n err,\n );\n }\n throw err;\n }\n break;\n }\n case 'updateSingle': {\n const result = await this.query(q.sql, q.params);\n if ((result.rowCount ?? 0) === 0) {\n throw new OccConflictError(\n `Conflict: CAS update of ${q.table}/${q.entryId} (expected commit_ts=${q.expectedTs}) matched no rows`,\n { table: q.table, id: q.entryId, expectedTs: q.expectedTs },\n );\n }\n break;\n }\n case 'updateBatch': {\n const result = await this.query<{ id: string }>(q.sql, q.params);\n\n const returned = new Set<string>();\n for (const r of result.rows) {\n returned.add(r.id);\n }\n for (const entry of q.entries!) {\n if (!returned.has(entry.id)) {\n const expected = entry.expectedCommitTs ?? -1n;\n throw new OccConflictError(\n `Conflict: CAS update of ${q.table}/${entry.id} (expected commit_ts=${expected}) matched no rows`,\n { table: q.table, id: q.entryId, expectedTs: q.expectedTs },\n );\n }\n }\n break;\n }\n case 'deleteSingle': {\n const result = await this.query(q.sql, q.params);\n if ((result.rowCount ?? 0) === 0) {\n throw new OccConflictError(\n `Conflict: CAS delete of ${q.table}/${q.entryId} (expected commit_ts=${q.expectedTs}) matched no rows`,\n { table: q.table, id: q.entryId, expectedTs: q.expectedTs },\n );\n }\n break;\n }\n case 'deleteBatch': {\n const result = await this.query<{ id: string }>(q.sql, q.params);\n const returned = new Set<string>();\n for (const r of result.rows) returned.add(r.id);\n for (const entry of q.entries!) {\n if (!returned.has(entry.id)) {\n const expected = entry.expectedCommitTs ?? -1n;\n throw new OccConflictError(\n `Conflict: CAS delete of ${q.table}/${entry.id} (expected commit_ts=${expected}) matched no rows`,\n { table: q.table, id: entry.id, expected },\n );\n }\n }\n break;\n }\n case 'commit_logs': {\n await this.query(q.sql, q.params);\n break;\n }\n default:\n throw new Error(\"Unhandled Postgres Operation: \" + q.operation);\n\n }\n }\n }\n\n /**\n * Point-only OCC check. For each genuine point read, compare the row's\n * current commit_ts against the value captured at read time. A `null`\n * expectedCommitTs means \"row didn't exist when we read\"; any row found\n * now is a conflict. Reads whose row is being written in this same txn\n * are skipped — the CAS UPDATE/DELETE already validated those.\n */\n private async validatePointReadsByCommitTs(\n queries: PreparedQuery[],\n ): Promise<void> {\n\n for (const q of queries) {\n const result = await this.query<{ id: string; commit_ts: string | number | bigint }>(q.sql, q.params);\n\n const currentById = new Map<string, bigint>();\n for (const row of result.rows) {\n currentById.set(row.id, BigInt(row.commit_ts));\n }\n\n for (const { entry } of (q.pendingsEntry ?? [])) {\n const current = currentById.get(entry.id);\n const expected = entry.expectedCommitTs;\n\n if (expected === null) {\n // We read \"no row\"; conflict if a row now exists.\n if (current !== undefined) {\n throw new OccConflictError(\n `Conflict: row ${entry.table}/${entry.id} was inserted concurrently`,\n { table: entry.table, id: entry.id }\n );\n }\n } else {\n if (current === undefined) {\n throw new OccConflictError(\n `Conflict: row ${entry.table}/${entry.id} was deleted concurrently`,\n { table: entry.table, id: entry.id, expected }\n );\n }\n if (current !== expected) {\n throw new OccConflictError(\n `Conflict: row ${entry.table}/${entry.id} changed from ts=${expected} to ts=${current}`,\n { table: entry.table, id: entry.id, expected, current }\n );\n }\n }\n }\n }\n }\n\n async pruneCommitLogsBefore(ts: bigint): Promise<bigint> {\n const result = await this.query(\n `DELETE FROM commit_logs WHERE ts < $1`,\n [ts]\n );\n return BigInt(result.rowCount ?? 0);\n }\n\n async getMinCommitLogTs(): Promise<bigint | null> {\n const result = await this.query<{ min: string | number | bigint | null }>(\n `SELECT min(ts) AS min FROM commit_logs`,\n );\n const min = result.rows[0]?.min;\n return min === null || min === undefined ? null : BigInt(min);\n }\n\n async getCommitLogsBetweenTs(beginTs: bigint, endTs: bigint, tableIds: Uint8Array[]): Promise<CommitLogEntry[]> {\n if (tableIds.length === 0) return [];\n\n const placeholders = tableIds.map((_, i) => `$${i + 3}`).join(\", \");\n const sql = `SELECT id, ts, table_id, data FROM commit_logs WHERE ts >= $1 AND ts < $2 AND table_id IN (${placeholders})`;\n\n const result = await this.query<{ id: unknown; ts: string | number | bigint; table_id: unknown; data: unknown }>(\n sql,\n [beginTs, endTs, ...tableIds.map(toBufferView)]\n );\n\n return result.rows.map((row) => ({\n id: normalizeToBytes(row.id),\n ts: BigInt(row.ts),\n tableId: normalizeToBytes(row.table_id),\n data: normalizeToBytes(row.data),\n }));\n }\n\n async getCommitLogsSinceTs(sinceTs: bigint, excludeTs: bigint, tableIds: Uint8Array[]): Promise<CommitLogEntry[]> {\n if (tableIds.length === 0) return [];\n\n const placeholders = tableIds.map((_, i) => `$${i + 3}`).join(\", \");\n const sql = `SELECT id, ts, table_id, data FROM commit_logs WHERE ts >= $1 AND ts <> $2 AND table_id IN (${placeholders})`;\n\n const result = await this.query<{ id: unknown; ts: string | number | bigint; table_id: unknown; data: unknown }>(\n sql,\n [sinceTs, excludeTs, ...tableIds.map(toBufferView)]\n );\n\n return result.rows.map((row) => ({\n id: normalizeToBytes(row.id),\n ts: BigInt(row.ts),\n tableId: normalizeToBytes(row.table_id),\n data: normalizeToBytes(row.data),\n }));\n }\n\n async close(): Promise<void> {\n await this.pool.end();\n }\n}\n\nexport class PgTxDatabase extends PgDatabase {\n constructor(logger: Logger, config: PoolConfig, pool: Pool, client: PoolClient) {\n super(logger, config, pool);\n this.client = client;\n }\n\n private client: PoolClient;\n\n override async query<T = any>(sql: string, params: unknown[] = []): Promise<DbQueryResult<T>> {\n try {\n const result = await this.client.query(sql, params);\n return {\n rows: result.rows as T[],\n rowCount: result.rowCount,\n };\n } catch (err) {\n throw translatePgError(err);\n }\n }\n\n async begin(): Promise<void> {\n await this.client.query(\"BEGIN\");\n }\n\n async commit(): Promise<void> {\n await this.client.query(\"COMMIT\");\n }\n\n async rollback(): Promise<void> {\n await this.client.query(\"ROLLBACK\");\n }\n\n release(): void {\n this.client.release();\n }\n}\n\n/**\n * Returns the unnest carrier array type for a column of `pgType`.\n * Scalars ride as their own array type so PG infers cleanly without an\n * extra cast on the SELECT/SET side. Array and jsonb columns can't ride\n * as their own array type (ragged 2D arrays aren't permitted, and the\n * driver can't tag jsonb sub-elements correctly inside a typed array),\n * so they get the universal `text[]` carrier with an explicit cast in\n * the SELECT/SET clause. Bytea rides natively as `bytea[]`.\n */\nfunction pgCarrierType(pgType: string): string {\n const base = pgType.trim().toLowerCase();\n if (base === \"bytea\") return \"bytea[]\";\n if (base === \"jsonb\" || base === \"json\") return \"text[]\";\n if (base.endsWith(\"[]\")) return \"text[]\";\n return `${pgType}[]`;\n}\n\n/** SELECT/SET expression for an unnest alias of a column with `pgType`.\n * When the carrier matches the column type (scalar / bytea), no cast is\n * needed and the alias passes through. When the carrier is text[] (jsonb\n * or array column), we cast back to the declared type. */\nfunction pgSelectExpr(alias: string, pgType: string): string {\n const base = pgType.trim().toLowerCase();\n if (base === \"jsonb\" || base === \"json\" || base.endsWith(\"[]\")) {\n return `(${alias})::${pgType}`;\n }\n return alias;\n}\n\n/**\n * Encodes a JS runtime value for transport via the chosen unnest carrier.\n * For scalar columns the pg driver handles type coercion directly (bigint,\n * number, boolean, Date, etc. all serialize cleanly). For jsonb columns we\n * stringify; for array columns we build a PG array literal. For bytea\n * columns we forward the bytes.\n *\n * Codecs declared by the upstream schema layer run before this point, so\n * `value` is already in the driver-side shape for its declared column.\n */\nfunction encodeForCarrier(value: unknown, pgTypeBase: string): unknown {\n if (value === null || value === undefined) return null;\n if (pgTypeBase === \"bytea\") {\n return toBufferView(normalizeToBytes(value));\n }\n if (pgTypeBase === \"jsonb\" || pgTypeBase === \"json\") {\n return typeof value === \"string\" ? value : supaliveStringify(value);\n }\n if (pgTypeBase.endsWith(\"[]\")) {\n if (!Array.isArray(value)) {\n throw new Error(`Expected array for column of type ${pgTypeBase}, got ${typeof value}`);\n }\n return pgArrayLiteral(value);\n }\n return value;\n}\n\n/** Serializes a JS array to a Postgres array literal `{a,b,c}`. Strings\n * are double-quoted with backslash/quote escaping; primitives stringify\n * directly; null becomes the literal NULL. */\nfunction pgArrayLiteral(arr: unknown[]): string {\n const parts: string[] = new Array(arr.length);\n for (let i = 0; i < arr.length; i++) {\n const v = arr[i];\n if (v === null || v === undefined) {\n parts[i] = \"NULL\";\n } else if (typeof v === \"string\") {\n parts[i] = `\"${v.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n } else if (typeof v === \"number\" || typeof v === \"bigint\" || typeof v === \"boolean\") {\n parts[i] = String(v);\n } else {\n const s = supaliveStringify(v);\n parts[i] = `\"${s.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n }\n }\n return `{${parts.join(\",\")}}`;\n}\n\n/**\n * Postgres deadlock_detected (40P01) is transient and semantically equivalent\n * to an OCC conflict from the mutation runner's perspective: re-execute the\n * handler with a fresh snapshot. Wrapping here lets realtime_db.ts stay\n * driver-agnostic — its retry loop already handles OccConflictError.\n */\nfunction translatePgError(err: unknown): unknown {\n if (err && typeof err === \"object\" && (err as { code?: string }).code === \"40P01\") {\n return new OccConflictError(\"deadlock detected\", err);\n }\n return err;\n}\n\n// PG: when a sub-path is set, scope the target via `jsonb_path_query_first`\n// (returns the value at the path, or NULL → falsy in WHERE) before the\n// containment / array-membership check. Cast bound text to jsonb so the\n// same code path works for both `json` and `jsonb` columns.\nfunction pgScopedTarget(col: string, pathIndex: number | undefined): string {\n return pathIndex === undefined\n ? col\n : `jsonb_path_query_first(${col}::jsonb, $${pathIndex}::jsonpath)`;\n}\n\nexport const pgSql: SqlBuilder = {\n placeholder: (i) => `$${i}`,\n like: (col, i) => `${col} LIKE $${i}`,\n ilike: (col, i) => `${col} ILIKE $${i}`,\n icontains: (col, i) => `${col} ILIKE $${i}`,\n jsonContains: (col, i, opts) =>\n `${pgScopedTarget(col, opts?.pathIndex)} @> $${i}::jsonb`,\n jsonContainedBy: (col, i, opts) =>\n `${pgScopedTarget(col, opts?.pathIndex)} <@ $${i}::jsonb`,\n jsonHasKey: (col, i, opts) => {\n if (opts?.pathCount && opts.pathCount > 0) {\n const joiner = opts.mode === \"all\" ? \" AND \" : \" OR \";\n const parts: string[] = [];\n for (let k = 0; k < opts.pathCount; k++) {\n parts.push(`jsonb_path_exists(${col}, $${i + k}::jsonpath)`);\n }\n return `(${parts.join(joiner)})`;\n }\n return opts?.raw\n ? `jsonb_path_exists(${col}, $${i}::jsonpath)`\n : `${col} ? $${i}`;\n },\n jsonArrayContains: (col, i, opts) =>\n `${pgScopedTarget(col, opts?.pathIndex)} @> jsonb_build_array($${i}::jsonb)`,\n};\n"],"mappings":"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"}
|
|
@@ -118,7 +118,38 @@ function createActionBuilder() {
|
|
|
118
118
|
};
|
|
119
119
|
};
|
|
120
120
|
}
|
|
121
|
+
/**
|
|
122
|
+
* Create a job builder with a pre-defined context type. A job is a
|
|
123
|
+
* server-only procedure invoked by the scheduler over HTTP — either on its
|
|
124
|
+
* declared `cron` schedule or as a precise one-shot enqueued via the
|
|
125
|
+
* server's job API. Jobs run like actions (full db, non-transactional) and
|
|
126
|
+
* are always internal, so they are never reachable from a client.
|
|
127
|
+
*
|
|
128
|
+
* @example
|
|
129
|
+
* const job = createJobBuilder<ServerContext>();
|
|
130
|
+
*
|
|
131
|
+
* export const cleanupOtps = job({
|
|
132
|
+
* cron: "0 * * * *", // hourly
|
|
133
|
+
* args: z.object({}),
|
|
134
|
+
* handler: async (ctx) => {
|
|
135
|
+
* await ctx.db.mutation(async (db) => { ... });
|
|
136
|
+
* return { ok: true };
|
|
137
|
+
* },
|
|
138
|
+
* });
|
|
139
|
+
*/
|
|
140
|
+
function createJobBuilder() {
|
|
141
|
+
return function job(config) {
|
|
142
|
+
return {
|
|
143
|
+
_type: "procedure",
|
|
144
|
+
procedureType: "job",
|
|
145
|
+
inputSchema: config.args,
|
|
146
|
+
fn: config.handler,
|
|
147
|
+
internal: true,
|
|
148
|
+
cron: config.cron
|
|
149
|
+
};
|
|
150
|
+
};
|
|
151
|
+
}
|
|
121
152
|
//#endregion
|
|
122
|
-
export {
|
|
153
|
+
export { createQueryBuilder as i, createJobBuilder as n, createMutationBuilder as r, createActionBuilder as t };
|
|
123
154
|
|
|
124
|
-
//# sourceMappingURL=procedure-
|
|
155
|
+
//# sourceMappingURL=procedure-Ze1GE-N9.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"procedure-Ze1GE-N9.js","names":[],"sources":["../src/router/procedure.ts"],"sourcesContent":["import type { ZodType } from \"zod\";\nimport type { DbReader, DbWriter } from \"../db/context\";\nimport type { SupaliveDb } from \"../db/realtime_db\";\n\n// ─── Context Types ───────────────────────────────────────────────────────────\n\n/**\n * Query context passed to query handlers.\n * Contains database reader and user-defined server context.\n */\nexport interface QueryCtx<TContext = unknown> {\n /** Database reader for queries */\n db: DbReader;\n /** User-defined server context (auth, requestId, etc.) */\n serverCtx?: TContext;\n}\n\n/**\n * Mutation context passed to mutation handlers.\n * Contains database writer and user-defined server context.\n */\nexport interface MutationCtx<TContext = unknown> {\n /** Database writer for mutations (includes insert/update/delete) */\n db: DbWriter;\n /** User-defined server context (auth, requestId, etc.) */\n serverCtx?: TContext;\n}\n\n/**\n * Action context passed to action handlers.\n * Contains the full SupaliveDb for both queries and mutations, plus user-defined server context.\n */\nexport interface ActionCtx<TContext = unknown> {\n /** Full database interface for queries and mutations */\n db: SupaliveDb;\n /** User-defined server context (auth, requestId, etc.) */\n serverCtx?: TContext;\n}\n\n/**\n * Job context passed to job handlers. Structurally identical to\n * {@link ActionCtx}: a job is a server-only procedure that runs\n * non-transactionally against the full {@link SupaliveDb} and may perform\n * external work, but it is triggered by the scheduler over HTTP (a cron tick\n * or a precise one-shot) rather than by a connected client. `serverCtx` is\n * the system context built by the server's `jobContext` factory.\n */\nexport interface JobCtx<TContext = unknown> {\n /** Full database interface for queries and mutations */\n db: SupaliveDb;\n /** System server context built by the server for scheduler-triggered runs */\n serverCtx?: TContext;\n}\n\n/**\n * Context handed to `caller.<proc>.runQuery` / `.runMutation` when invoking one\n * procedure from inside another. Pass the caller handler's own `ctx` — its `db`\n * carries the parent's live transaction (a {@link DbReader}/{@link DbWriter}) or,\n * inside an action, the full {@link SupaliveDb}. The caller uses this to decide\n * whether the nested call joins the parent's snapshot (queries) or runs as an\n * independent sub-transaction (mutations).\n */\nexport interface ParentCtx<TContext = unknown> {\n db: DbReader | DbWriter | SupaliveDb;\n serverCtx?: TContext | undefined;\n}\n\n// ─── Procedure Types ─────────────────────────────────────────────────────────\n\nexport type QueryFn<TInput, TResult, TContext = unknown> = (\n ctx: QueryCtx<TContext>,\n input: TInput,\n) => Promise<TResult>;\n\nexport type MutationFn<TInput, TResult, TContext = unknown> = (\n ctx: MutationCtx<TContext>,\n input: TInput,\n) => Promise<TResult>;\n\nexport type ActionFn<TInput, TResult, TContext = unknown> = (\n ctx: ActionCtx<TContext>,\n input: TInput,\n) => Promise<TResult>;\n\nexport type JobFn<TInput, TResult, TContext = unknown> = (\n ctx: JobCtx<TContext>,\n input: TInput,\n) => Promise<TResult>;\n\n/**\n * Per-procedure override for the cache/subscription segmentation key.\n *\n * omitted | undefined → fall back to `config.getUserId(serverCtx)` (default)\n * false → no identity in hash; cache/sub shared across all users\n * string → a static identity literal (e.g. \"public\" or a tenant id)\n * function → compute from serverCtx + input (sync)\n *\n * When the result of a query is identical regardless of who calls it, set\n * this to a literal (or `false`) so a single cache entry serves everyone.\n *\n * The function form intentionally takes `serverCtx` (not the full `QueryCtx`)\n * because identity is resolved before any DB read is issued.\n */\nexport type QueryIdentitySpec<TInput, TContext> =\n | false\n | string\n | ((serverCtx: TContext, input: TInput) => string | null | undefined);\n\nexport interface BaseProcedure<\n TInput, TResult,\n TType extends \"query\" | \"mutation\" | \"action\" | \"job\",\n TContext = unknown,\n TInternal extends boolean = boolean\n> {\n readonly _type: \"procedure\";\n readonly procedureType: TType;\n readonly inputSchema: ZodType<TInput>;\n readonly fn: QueryFn<TInput, TResult, TContext> | MutationFn<TInput, TResult, TContext> | ActionFn<TInput, TResult, TContext> | JobFn<TInput, TResult, TContext>;\n readonly internal: TInternal;\n}\n\nexport interface QueryProcedure<\n TInput, TResult,\n TContext = unknown,\n TInternal extends boolean = boolean\n> extends BaseProcedure<TInput, TResult, \"query\", TContext, TInternal> {\n readonly procedureType: \"query\";\n readonly fn: QueryFn<TInput, TResult, TContext>;\n readonly queryIdentity?: QueryIdentitySpec<TInput, TContext>;\n}\n\nexport interface MutationProcedure<\n TInput, TResult,\n TContext = unknown,\n TInternal extends boolean = boolean\n> extends BaseProcedure<TInput, TResult, \"mutation\", TContext, TInternal> {\n readonly procedureType: \"mutation\";\n readonly fn: MutationFn<TInput, TResult, TContext>;\n}\n\nexport interface ActionProcedure<\n TInput, TResult,\n TContext = unknown,\n TInternal extends boolean = boolean\n> extends BaseProcedure<TInput, TResult, \"action\", TContext, TInternal> {\n readonly procedureType: \"action\";\n readonly fn: ActionFn<TInput, TResult, TContext>;\n}\n\n/**\n * A scheduler-triggered, server-only procedure. Runs like an action (full\n * db, non-transactional, may do external work) but is dispatched by the\n * server's HTTP job endpoint on a cron tick or a precise one-shot rather\n * than over the client WebSocket. Always {@link internal}: true, so it is\n * never reachable via `call`/`subscribe`.\n */\nexport interface JobProcedure<\n TInput, TResult,\n TContext = unknown,\n> extends BaseProcedure<TInput, TResult, \"job\", TContext, true> {\n readonly procedureType: \"job\";\n readonly fn: JobFn<TInput, TResult, TContext>;\n /**\n * Cron expression for a recurring job (e.g. `\"0 3 * * *\"`). Declared crons\n * are synced to the scheduler at server startup. Omit for a job that is\n * only ever invoked as a precise one-shot via the scheduler API.\n */\n readonly cron?: string;\n}\n\nexport type AnyProcedure<TContext = unknown> =\n | QueryProcedure<any, any, TContext>\n | MutationProcedure<any, any, TContext>\n | ActionProcedure<any, any, TContext>\n | JobProcedure<any, any, TContext>;\n\n// ─── Procedure Configuration Types ───────────────────────────────────────────\n\nexport interface QueryConfig<TInput, TResult, TContext = unknown> {\n /** Zod schema for input validation */\n args: ZodType<TInput>;\n /** The query handler function */\n handler: QueryFn<TInput, TResult, TContext>;\n /** Mark as internal (server-only). Default: false */\n internal?: boolean;\n /**\n * Override the cache/subscription segmentation key for this procedure.\n * See {@link QueryIdentitySpec}. Omit to keep the default (per-user) behavior.\n */\n queryIdentity?: QueryIdentitySpec<TInput, TContext>;\n}\n\nexport interface MutationConfig<TInput, TResult, TContext = unknown> {\n /** Zod schema for input validation */\n args: ZodType<TInput>;\n /** The mutation handler function */\n handler: MutationFn<TInput, TResult, TContext>;\n /** Mark as internal (server-only). Default: false */\n internal?: boolean;\n}\n\nexport interface ActionConfig<TInput, TResult, TContext = unknown> {\n /** Zod schema for input validation */\n args: ZodType<TInput>;\n /** The action handler function */\n handler: ActionFn<TInput, TResult, TContext>;\n /** Mark as internal (server-only). Default: false */\n internal?: boolean;\n}\n\nexport interface JobConfig<TInput, TResult, TContext = unknown> {\n /** Zod schema for the job payload */\n args: ZodType<TInput>;\n /** The job handler function */\n handler: JobFn<TInput, TResult, TContext>;\n /**\n * Cron expression for a recurring schedule (e.g. `\"0 3 * * *\"`). Omit for a\n * job that is only invoked as a precise one-shot.\n */\n cron?: string;\n}\n\n/** Extract input type from a procedure */\nexport type InputOf<T> = T extends BaseProcedure<infer I, any, any, any> ? I : never;\n\n/** Extract output type from a procedure */\nexport type OutputOf<T> = T extends BaseProcedure<any, infer O, any, any> ? O : never;\n\n/** Extract procedure type (query/mutation) */\nexport type TypeOf<T> = T extends BaseProcedure<any, any, infer Type, any> ? Type : never;\n\n/** Extract server context type from a procedure */\nexport type ContextOf<T> = T extends BaseProcedure<any, any, any, infer C> ? C : never;\n\n/**\n * Create a query builder with a pre-defined context type.\n * This allows you to define the context type once and have it inferred\n * automatically in all your query handlers.\n * \n * @example\n * // Define your server context\n * interface ServerContext {\n * auth: { userId: string };\n * requestId: string;\n * }\n * \n * // Create a typed query builder\n * const query = createQueryBuilder<ServerContext>();\n * \n * // Use it - context type is automatically inferred!\n * const getUser = query({\n * args: z.object({ id: z.string() }),\n * handler: async (ctx, { id }) => {\n * // ctx.db for database queries\n * const user = await ctx.db.query(UsersSchema).find(id);\n * // ctx.context for server context\n * console.log(ctx.context.requestId);\n * return user;\n * }\n * });\n */\nexport function createQueryBuilder<TContext = unknown>() {\n return function query<TInput, TResult, const TInternal extends boolean = false>(\n config: QueryConfig<TInput, TResult, TContext> & { internal?: TInternal }\n ): QueryProcedure<TInput, TResult, TContext, TInternal> {\n return {\n _type: \"procedure\",\n procedureType: \"query\",\n inputSchema: config.args,\n fn: config.handler,\n internal: (config.internal ?? false) as TInternal,\n queryIdentity: config.queryIdentity,\n };\n };\n}\n\n// ─── Mutation Builder Factory ────────────────────────────────────────────────\n\n/**\n * Create a mutation builder with a pre-defined context type.\n * This allows you to define the context type once and have it inferred\n * automatically in all your mutation handlers.\n * \n * @example\n * // Define your server context\n * interface ServerContext {\n * auth: { userId: string };\n * requestId: string;\n * }\n * \n * // Create a typed mutation builder\n * const mutation = createMutationBuilder<ServerContext>();\n * \n * // Use it - context type is automatically inferred!\n * const createUser = mutation({\n * args: z.object({ name: z.string() }),\n * handler: async (ctx, { name }) => {\n * // ctx.db for mutations\n * await ctx.db.insert(UsersSchema, id, { name });\n * // ctx.context for server context\n * console.log(ctx.context.requestId);\n * return { success: true };\n * }\n * });\n */\nexport function createMutationBuilder<TContext = unknown>() {\n return function mutation<TInput, TResult, const TInternal extends boolean = false>(\n config: MutationConfig<TInput, TResult, TContext> & { internal?: TInternal }\n ): MutationProcedure<TInput, TResult, TContext, TInternal> {\n return {\n _type: \"procedure\",\n procedureType: \"mutation\",\n inputSchema: config.args,\n fn: config.handler,\n internal: (config.internal ?? false) as TInternal,\n };\n };\n}\n\n// ─── Action Builder Factory ──────────────────────────────────────────────────\n\n/**\n * Create an action builder with a pre-defined context type.\n * Actions have access to the full SupaliveDb for both queries and mutations.\n *\n * @example\n * // Define your server context\n * interface ServerContext {\n * auth: { userId: string };\n * requestId: string;\n * }\n *\n * // Create a typed action builder\n * const action = createActionBuilder<ServerContext>();\n *\n * // Use it - context type is automatically inferred!\n * const processOrder = action({\n * args: z.object({ orderId: z.string() }),\n * handler: async (ctx, { orderId }) => {\n * // ctx.db for full database access\n * const order = await ctx.db.query(async (db) => {\n * return db.query(OrdersSchema).find(orderId);\n * });\n * await ctx.db.mutation(async (db) => {\n * await db.update(OrdersSchema, orderId, { status: \"processed\" });\n * });\n * // ctx.context for server context\n * console.log(ctx.context.requestId);\n * return { success: true };\n * }\n * });\n */\nexport function createActionBuilder<TContext = unknown>() {\n return function action<TInput, TResult, const TInternal extends boolean = false>(\n config: ActionConfig<TInput, TResult, TContext> & { internal?: TInternal }\n ): ActionProcedure<TInput, TResult, TContext, TInternal> {\n return {\n _type: \"procedure\",\n procedureType: \"action\",\n inputSchema: config.args,\n fn: config.handler,\n internal: (config.internal ?? false) as TInternal,\n };\n };\n}\n\n// ─── Job Builder Factory ─────────────────────────────────────────────────────\n\n/**\n * Create a job builder with a pre-defined context type. A job is a\n * server-only procedure invoked by the scheduler over HTTP — either on its\n * declared `cron` schedule or as a precise one-shot enqueued via the\n * server's job API. Jobs run like actions (full db, non-transactional) and\n * are always internal, so they are never reachable from a client.\n *\n * @example\n * const job = createJobBuilder<ServerContext>();\n *\n * export const cleanupOtps = job({\n * cron: \"0 * * * *\", // hourly\n * args: z.object({}),\n * handler: async (ctx) => {\n * await ctx.db.mutation(async (db) => { ... });\n * return { ok: true };\n * },\n * });\n */\nexport function createJobBuilder<TContext = unknown>() {\n return function job<TInput, TResult>(\n config: JobConfig<TInput, TResult, TContext>\n ): JobProcedure<TInput, TResult, TContext> {\n return {\n _type: \"procedure\",\n procedureType: \"job\",\n inputSchema: config.args,\n fn: config.handler,\n internal: true,\n cron: config.cron,\n };\n };\n}"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqQA,SAAgB,qBAAyC;CACvD,OAAO,SAAS,MACd,QACsD;EACtD,OAAO;GACL,OAAO;GACP,eAAe;GACf,aAAa,OAAO;GACpB,IAAI,OAAO;GACX,UAAW,OAAO,YAAY;GAC9B,eAAe,OAAO;EACxB;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+BA,SAAgB,wBAA4C;CAC1D,OAAO,SAAS,SACd,QACyD;EACzD,OAAO;GACL,OAAO;GACP,eAAe;GACf,aAAa,OAAO;GACpB,IAAI,OAAO;GACX,UAAW,OAAO,YAAY;EAChC;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,sBAA0C;CACxD,OAAO,SAAS,OACd,QACuD;EACvD,OAAO;GACL,OAAO;GACP,eAAe;GACf,aAAa,OAAO;GACpB,IAAI,OAAO;GACX,UAAW,OAAO,YAAY;EAChC;CACF;AACF;;;;;;;;;;;;;;;;;;;;AAuBA,SAAgB,mBAAuC;CACrD,OAAO,SAAS,IACd,QACyC;EACzC,OAAO;GACL,OAAO;GACP,eAAe;GACf,aAAa,OAAO;GACpB,IAAI,OAAO;GACX,UAAU;GACV,MAAM,OAAO;EACf;CACF;AACF"}
|
|
@@ -367,6 +367,17 @@ var FilterBuilder = class FilterBuilder {
|
|
|
367
367
|
this.add(this.getColumnName(key), "between", this.encode(key, lo), this.encode(key, hi));
|
|
368
368
|
return this;
|
|
369
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* Set membership: `key` equals any of `values` (SQL `IN`). Each value is
|
|
372
|
+
* codec-encoded like `eq`. An empty `values` matches no rows. Bound as one
|
|
373
|
+
* parameter per element, so keep the list within the driver's parameter
|
|
374
|
+
* limit (chunk very large sets at the call site).
|
|
375
|
+
*/
|
|
376
|
+
in(key, values) {
|
|
377
|
+
const col = this.getColumnName(key);
|
|
378
|
+
this.add(col, "in", values.map((v) => this.encode(key, v)));
|
|
379
|
+
return this;
|
|
380
|
+
}
|
|
370
381
|
isNull(key) {
|
|
371
382
|
this.add(this.getColumnName(key), "isNull", null);
|
|
372
383
|
return this;
|
|
@@ -427,6 +438,14 @@ function buildLeafSql(leaf, params, sql) {
|
|
|
427
438
|
params.push(leaf.value2);
|
|
428
439
|
return `${col} BETWEEN ${p1} AND ${sql.placeholder(params.length)}`;
|
|
429
440
|
}
|
|
441
|
+
case "in": {
|
|
442
|
+
const values = leaf.value ?? [];
|
|
443
|
+
if (values.length === 0) return "1 = 0";
|
|
444
|
+
return `${col} IN (${values.map((v) => {
|
|
445
|
+
params.push(v);
|
|
446
|
+
return sql.placeholder(params.length);
|
|
447
|
+
}).join(", ")})`;
|
|
448
|
+
}
|
|
430
449
|
case "like":
|
|
431
450
|
params.push(leaf.value);
|
|
432
451
|
return `${col} LIKE ${sql.placeholder(params.length)}`;
|
|
@@ -512,6 +531,7 @@ function matchesLeaf(row, leaf) {
|
|
|
512
531
|
case "<": return val < leaf.value;
|
|
513
532
|
case "<=": return val <= leaf.value;
|
|
514
533
|
case "between": return val >= leaf.value && val <= leaf.value2;
|
|
534
|
+
case "in": return Array.isArray(leaf.value) && leaf.value.includes(val);
|
|
515
535
|
case "like":
|
|
516
536
|
case "contains":
|
|
517
537
|
case "ilike":
|
|
@@ -670,4 +690,4 @@ function jsonPathExists(val, path) {
|
|
|
670
690
|
//#endregion
|
|
671
691
|
export { jsonPathExtract as a, jsonPathExists as i, buildPredicateSql as n, matchesPredicate as o, jsonContains as r, QueryBuilder as t };
|
|
672
692
|
|
|
673
|
-
//# sourceMappingURL=query-
|
|
693
|
+
//# sourceMappingURL=query-CrbJ_W0W.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-CrbJ_W0W.js","names":["formatComment","z"],"sources":["../src/db/sqlcommenter.ts","../src/db/query.ts"],"sourcesContent":["export function comment(input: string | Record<string, string>): string {\n if (!input) return \"\";\n if (typeof input === \"string\") return `/*${input}*/`;\n const entries = Object.entries(input).filter(([_, v]) => v != null);\n if (entries.length === 0) return \"\";\n const pairs = entries\n .map(([k, v]) => `${encodeURIComponent(k)}='${encodeURIComponent(v)}'`)\n .sort();\n return `/*${pairs.join(\",\")}*/`;\n}\n","import { z, ZodRawShape } from \"zod\";\nimport { Predicate, LeafPredicate, CompareOperator, QuerySpec, RangeRead } from \"./types_db\";\nimport {\n ColumnCodec, ComputedCodec, ComputedDataType, ComputedField, ComputedFieldConfig,\n InferSchema, Model, OverrideField, Prettify, SchemaDefinition\n} from \"./schema\";\nimport { TxContext } from \"./context\";\nimport { normalizeCodec } from \"./codec\";\nimport { SqlBuilder } from \"./db\";\nimport { bytesEqual } from \"./bytes\";\nimport { comment as formatComment } from \"./sqlcommenter\";\n\n// Decoded value type of a schema field (codec-applied), narrowed to the\n// caller's key. `K` is not constrained to `keyof InferSchema<S>` so callers\n// can pass a key constrained to a subset (e.g. StringFieldKeys) without\n// TypeScript widening the resulting value type.\ntype FieldValue<S extends SchemaDefinition<string, ZodRawShape>, K> =\n K extends keyof InferSchema<S> ? InferSchema<S>[K] : never;\n\ntype OrderableValue = number | bigint | string | Date;\n\n// Keys whose decoded value is string-like (covers `string`, string literal\n// unions like enums, and `string | null | undefined`).\ntype StringFieldKeys<S extends SchemaDefinition<string, ZodRawShape>> = {\n [K in keyof InferSchema<S>]: NonNullable<InferSchema<S>[K]> extends string ? K : never;\n}[keyof InferSchema<S>];\n\n// Keys whose decoded value supports SQL ordering (gt/gte/lt/lte/between).\ntype OrderableFieldKeys<S extends SchemaDefinition<string, ZodRawShape>> = {\n [K in keyof InferSchema<S>]: NonNullable<InferSchema<S>[K]> extends OrderableValue ? K : never;\n}[keyof InferSchema<S>];\n\n// Keys whose decoded value is shaped like a JSON/JSONB column (object/array/unknown).\n// Used to constrain jsonContains / jsonContainedBy / jsonHasKey / jsonArrayContains\n// so they can't be invoked on plain scalar fields.\ntype JsonFieldKeys<S extends SchemaDefinition<string, ZodRawShape>> = {\n [K in keyof InferSchema<S>]:\n NonNullable<InferSchema<S>[K]> extends OrderableValue ? never :\n NonNullable<InferSchema<S>[K]> extends boolean ? never :\n NonNullable<InferSchema<S>[K]> extends (object | unknown[]) ? K :\n unknown extends NonNullable<InferSchema<S>[K]> ? K :\n never;\n}[keyof InferSchema<S>];\n\nexport interface JsonOpOptions {\n /**\n * Skip client-side preparation of the value (no JSON-stringify, no key\n * escaping). The caller is responsible for the exact form the dialect\n * expects: a JSON path string for jsonHasKey, a JSON-encoded string for\n * the contains family.\n */\n raw?: true;\n}\n\nexport interface JsonContainsOptions extends JsonOpOptions {\n /**\n * Sub-path into the target JSON to scope the check to. `string` is a\n * single literal top-level key; `string[]` is a nested literal-key path\n * compiled to `$.\"a\".\"b\"`. Mutually exclusive with `pathRaw`.\n */\n path?: string | string[];\n /**\n * Raw jsonpath expression used verbatim by the dialect (PG `jsonpath`,\n * MySQL JSON path). Mutually exclusive with `path`.\n */\n pathRaw?: string;\n}\n\nexport interface JsonHasKeyMultiOptions extends JsonOpOptions {\n /** Required for the multi-path overload — chooses any vs every. */\n mode: \"one\" | \"all\";\n}\n\n// Escape and join a key path into a JSON path expression usable by both\n// PG (`jsonb_path_exists`) and MySQL (`JSON_CONTAINS_PATH`):\n// [\"a\", \"b.c\"] → `$.\"a\".\"b.c\"`\nfunction jsonKeysToPath(keys: string[]): string {\n let out = \"$\";\n for (const k of keys) {\n out += `.\"${k.replace(/\"/g, '\\\\\"')}\"`;\n }\n return out;\n}\n\n// Resolve the `path` / `pathRaw` option pair down to a single jsonpath\n// expression. Returns a string in `$.\"...\"` form for structured `path`,\n// or the caller's `pathRaw` verbatim. Returns `undefined` when neither is\n// set. Throws when both are supplied.\nfunction resolveJsonPath(opts: JsonContainsOptions | undefined): string | undefined {\n if (opts?.path !== undefined && opts?.pathRaw !== undefined) {\n throw new Error(\"jsonContains: pass either `path` or `pathRaw`, not both\");\n }\n if (opts?.pathRaw !== undefined) return opts.pathRaw;\n if (opts?.path !== undefined) {\n const arr = Array.isArray(opts.path) ? opts.path : [opts.path];\n return jsonKeysToPath(arr);\n }\n return undefined;\n}\n\nexport class QueryBuilder<S extends SchemaDefinition<string, ZodRawShape>> {\n constructor(\n protected ctx: TxContext,\n protected model: Model\n ) { }\n\n protected getColumnName(key: string): string {\n return this.model.columns[key] ?? key;\n }\n\n select(): SelectQueryBuilder<S, keyof InferSchema<S>>;\n select<K extends keyof InferSchema<S>>(...columns: K[]): SelectQueryBuilder<S, K>;\n select<K extends keyof InferSchema<S>>(...columns: K[]): SelectQueryBuilder<S, K> {\n const selectedProps = (columns.length > 0 ? columns : Object.keys(this.model.columns)) as K[];\n return new SelectQueryBuilder<S, K>(this.ctx, this.model, [], null, null, null, selectedProps);\n }\n}\n\nexport type IdAndCommitTs = { id: string; commitTs: bigint };\n\n// Row type returned by `.get()`, `.find()`, `.first()`. Selected schema keys\n// plus the always-included id/commitTs and any computed fields, flattened so\n// the IDE renders a plain object literal instead of an intersection chain.\ntype SelectRow<\n S extends SchemaDefinition<string, ZodRawShape>,\n K extends keyof InferSchema<S>,\n C extends Record<string, unknown>,\n> = Prettify<Pick<InferSchema<S>, Extract<K | \"id\" | \"commitTs\", keyof InferSchema<S>>> & C>;\n\nclass SelectQueryBuilder<\n S extends SchemaDefinition<string, ZodRawShape>,\n K extends keyof InferSchema<S> = never,\n C extends Record<string, unknown> = {},\n> {\n private filters: ((fb: FilterBuilder<S>) => void)[] = [];\n private orderByClause: string | null = null;\n private limitVal: number | null = null;\n private offsetVal: number | null = null;\n private selectedProps: (keyof InferSchema<S>)[] = [];\n private computedFields: ComputedField[] = [];\n private commentStr: string = \"\";\n\n constructor(\n private ctx: TxContext,\n private model: Model,\n filters: ((fb: FilterBuilder<S>) => void)[] = [],\n orderByClause: string | null = null,\n limitVal: number | null = null,\n offsetVal: number | null = null,\n selectedProps: (keyof InferSchema<S>)[] = [],\n computedFields: ComputedField[] = [],\n commentStr: string = \"\",\n ) {\n this.filters = filters;\n this.orderByClause = orderByClause;\n this.limitVal = limitVal;\n this.offsetVal = offsetVal;\n this.selectedProps = selectedProps;\n this.computedFields = computedFields;\n this.commentStr = commentStr;\n }\n\n private getColumnName(key: string): string {\n return this.model.columns[key] ?? key;\n }\n\n private getPropertyName(column: string): string {\n return this.model.reverseColumns[column] ?? this.computedFields.find(f => f.alias === column)?.field ?? column;\n }\n\n private mapRowToSchema(row: Record<string, any>): Record<string, any> {\n const result: Record<string, any> = {};\n for (const [col, value] of Object.entries(row)) {\n const prop = this.getPropertyName(col)\n const codec = this.model.codecs[prop] ?? this.computedFields.find((f) => f.alias === col)?.codec;\n result[prop] = codec?.decode && value !== undefined && value !== null ? codec.decode(value) : value;\n }\n return result;\n }\n\n where(filterFn: (fb: FilterBuilder<S>) => void): SelectQueryBuilder<S, K, C> {\n this.filters.push(filterFn);\n return this;\n }\n\n orderBy(\n column: keyof InferSchema<S> | keyof C,\n direction: \"asc\" | \"desc\" = \"asc\",\n ): SelectQueryBuilder<S, K, C> {\n this.orderByClause = `${this.getColumnName(column as string)} ${direction}`;\n return this\n }\n\n limit(n: number): SelectQueryBuilder<S, K, C> {\n this.limitVal = n;\n return this;\n }\n\n offset(n: number): SelectQueryBuilder<S, K, C> {\n this.offsetVal = n;\n return this;\n }\n computed<\n TField extends string,\n TZodType extends z.ZodTypeAny | undefined = undefined,\n TDecode extends (value: any) => any = (value: unknown) => unknown,\n >(\n config: ComputedFieldConfig<TField, ReturnType<TDecode>, Parameters<TDecode>[0], TZodType> & {\n codec: {\n decode: TDecode;\n encode?: (value: ReturnType<TDecode>) => Parameters<TDecode>[0];\n };\n }\n ): SelectQueryBuilder<S, K | TField, OverrideField<C, TField, ComputedDataType<ComputedCodec<ReturnType<TDecode>, Parameters<TDecode>[0]>, TZodType>>>;\n computed<\n TField extends string,\n TZodType extends z.ZodTypeAny | undefined = undefined,\n >(\n config: ComputedFieldConfig<TField, unknown, unknown, TZodType> & {\n codec?: undefined;\n }\n ): SelectQueryBuilder<S, K | TField, OverrideField<C, TField, ComputedDataType<undefined, TZodType>>>;\n computed<\n TField extends string,\n TZodType extends z.ZodTypeAny | undefined = undefined,\n TCodec extends ComputedCodec<any, any> | undefined = undefined,\n >(\n config: ComputedFieldConfig<TField, unknown, unknown, TZodType> & {\n codec?: TCodec;\n }\n ): SelectQueryBuilder<S, K | TField, OverrideField<C, TField, ComputedDataType<TCodec, TZodType>>> {\n const computed: ComputedField = {\n field: config.field,\n sql: config.sql,\n alias: config.alias ?? config.field,\n codec: normalizeCodec(config.codec, config.type),\n };\n this.computedFields.push(computed);\n return new SelectQueryBuilder<S, K | TField, OverrideField<C, TField, ComputedDataType<TCodec, TZodType>>>(\n this.ctx, this.model, this.filters, this.orderByClause,\n this.limitVal, this.offsetVal, [...this.selectedProps, config.field], this.computedFields,\n this.commentStr,\n );\n }\n\n comment(input: string | Record<string, string>): SelectQueryBuilder<S, K, C> {\n this.commentStr = formatComment(input);\n return this;\n }\n\n async first(): Promise<SelectRow<S, K, C> | null> {\n const rows = await this.limit(1).get();\n return (rows[0] ?? null) as any;\n }\n\n async exists(): Promise<boolean> {\n return await this.count() > 0;\n }\n\n private static defaultCountType = z.coerce.number();\n\n async count(): Promise<number>;\n async count<T extends z.ZodType>(parser: T): Promise<z.output<T>>;\n async count<T extends z.ZodType>(parser?: T): Promise<z.output<T>> {\n const predicate = this.buildPredicate() ?? { kind: \"leaf\" as const, column: \"1\", operator: \"=\" as const, value: \"1\" };\n const table = this.model.table;\n\n // Build query spec for read tracking (without totalTs)\n const querySpec: RangeRead = {\n kind: \"range\",\n table: table,\n tableId: this.model.tableId,\n predicate: predicate,\n columns: [\"count\"],\n orderBy: null,\n limit: null,\n offset: null,\n };\n\n // Execute query\n const params: unknown[] = [];\n const whereSql = buildPredicateSql(predicate, params, this.ctx.db.sqlBuilder);\n\n const sql = `SELECT COUNT(*) as count FROM ${table} WHERE (${whereSql})${this.commentStr ? \" \" + this.commentStr : \"\"}`;\n const result = await this.ctx.db.query(sql, params);\n const raw = result.rows[0]?.count ?? \"0\";\n\n // Record read with totalTs for OCC validation\n this.ctx.internalGetReadSet().push({\n ...querySpec,\n tableId: this.model.tableId,\n });\n\n if (parser) {\n return parser.parse(raw) as z.output<T>;\n } else {\n return SelectQueryBuilder.defaultCountType.parse(raw) as z.output<T>;\n }\n }\n\n async find(id: string): Promise<SelectRow<S, K, C> | null> {\n if (this.filters.length > 0) {\n throw new Error(`Cannot use find() after where() - find() is meant for primary key lookups without additional filters`);\n }\n\n const table = this.model.table;\n const tableId = this.model.tableId;\n\n // Check pending writes first\n const pendingWrite = this.ctx.internalGetWriteSet().find(w => w.id === id && bytesEqual(w.tableId, tableId));\n\n if (pendingWrite) {\n if (pendingWrite.op === \"delete\") {\n // Don't cache pending writes, just return\n return null;\n }\n const row = this.mapRowToSchema({ ...pendingWrite.data, id, commit_ts: this.ctx.beginTs });\n return row as SelectRow<S, K, C>;\n }\n\n const computedCols = this.computedFields.map(f => `${f.sql} as ${f.alias}`);\n const selectPropsWithComputed = this.selectedProps.slice(0, this.selectedProps.length - this.computedFields.length);\n const selectCols = selectPropsWithComputed.length > 0\n ? [\"id\", ...selectPropsWithComputed.map(p => this.getColumnName(p as string)), ...computedCols, \"commit_ts\"].join(\", \")\n : \"*\";\n let sql = `SELECT ${selectCols} FROM ${table} WHERE id = ${this.ctx.db.sqlBuilder.placeholder(1)}`;\n if (this.commentStr) sql += ` ${this.commentStr}`;\n const result = await this.ctx.db.query(sql, [id]);\n\n const row = result.rows[0] ?? null;\n\n // Capture commit_ts as the per-row OCC fingerprint. `null` means\n // \"no row at read time\" — a later concurrent insert with this id\n // becomes a conflict at commit time.\n const expectedCommitTs: bigint | null = row ? BigInt(row.commit_ts) : null;\n this.ctx.internalGetReadSet().push({\n kind: \"point\",\n table,\n tableId,\n id,\n expectedCommitTs,\n });\n\n const mappedRow = row ? this.mapRowToSchema(row) as SelectRow<S, K, C> : null;\n\n return mappedRow;\n }\n\n async get(): Promise<SelectRow<S, K, C>[]> {\n const predicate = this.buildPredicate() ?? { kind: \"leaf\" as const, column: \"1\", operator: \"=\" as const, value: \"1\" };\n const table = this.model.table;\n const tableId = this.model.tableId;\n\n // Build query spec for read tracking (without totalTs)\n const querySpec: RangeRead = {\n kind: \"range\",\n table: table,\n tableId: tableId,\n predicate: predicate,\n columns: this.selectedProps as string[],\n orderBy: this.orderByClause,\n limit: this.limitVal,\n offset: this.offsetVal,\n };\n\n // Execute query\n const params: unknown[] = [];\n const whereSql = buildPredicateSql(predicate, params, this.ctx.db.sqlBuilder);\n\n const computedCols = this.computedFields.map(f => `${f.sql} as ${f.alias}`);\n const selectPropsWithComputed = this.selectedProps.slice(0, this.selectedProps.length - this.computedFields.length);\n const selectColsNames = selectPropsWithComputed.length > 0\n ? [\"id\", ...selectPropsWithComputed.map(p => this.getColumnName(p as string)), ...computedCols, \"commit_ts\"]\n : \"*\";\n const selectCols = Array.from(new Set(selectColsNames).values()).join(\", \")\n let sql = `SELECT ${selectCols} FROM ${table} WHERE (${whereSql})`;\n\n if (this.orderByClause) {\n sql += ` ORDER BY ${this.orderByClause}`;\n }\n if (this.limitVal !== null) {\n sql += ` LIMIT ${this.limitVal}`;\n }\n if (this.offsetVal !== null) {\n sql += ` OFFSET ${this.offsetVal}`;\n }\n if (this.commentStr) sql += ` ${this.commentStr}`;\n\n const result = await this.ctx.db.query(sql, params);\n\n // Record read with totalTs for OCC validation\n this.ctx.internalGetReadSet().push({\n ...querySpec,\n tableId: tableId,\n });\n\n const rows = result.rows.map(row => this.mapRowToSchema(row)) as SelectRow<S, K, C>[];\n\n return rows;\n }\n\n private buildPredicate(): Predicate | null {\n if (this.filters.length === 0) return null;\n\n const predicates: Predicate[] = [];\n const fb = new FilterBuilder<S>(this.model.columns, this.model.codecs);\n\n for (const filterFn of this.filters) {\n filterFn(fb);\n if (fb.predicates.length > 0) {\n const hasOr = fb.predicates.some(p => p.kind === \"or\");\n if (hasOr) {\n predicates.push(...fb.predicates);\n } else {\n predicates.push({ kind: \"and\", children: fb.predicates });\n }\n fb.predicates = [];\n }\n }\n\n if (predicates.length === 0) return null;\n if (predicates.length === 1) return predicates[0] ?? null;\n return { kind: \"and\", children: predicates };\n }\n}\n\n// ─── Filter Builder ──────────────────────────────────────────────────────────────\n\nclass FilterBuilder<S extends SchemaDefinition<string, ZodRawShape>> {\n predicates: Predicate[] = [];\n\n constructor(\n private columns: Record<string, string>,\n private codecs: Record<string, ColumnCodec<unknown, unknown>>\n ) { }\n\n private add(column: string, operator: CompareOperator, value: unknown, value2?: unknown, raw?: boolean): void {\n const leaf: LeafPredicate = { kind: \"leaf\", column, operator: operator as any, value, value2 };\n if (raw) leaf.raw = true;\n this.predicates.push(leaf);\n }\n\n private addJsonContains(operator: CompareOperator, key: string, value: unknown, opts: JsonContainsOptions | undefined): void {\n const col = this.getColumnName(key);\n const leaf: LeafPredicate = {\n kind: \"leaf\",\n column: col,\n operator: operator as any,\n value: this.encodeJson(value, opts?.raw),\n };\n if (opts?.raw) leaf.raw = true;\n const path = resolveJsonPath(opts);\n if (path !== undefined) leaf.path = path;\n this.predicates.push(leaf);\n }\n\n private getColumnName(key: string): string {\n return this.columns[key] ?? key;\n }\n\n private encode(key: string, value: unknown): unknown {\n const codec = this.codecs[key];\n if (!codec?.encode || value === undefined || value === null) return value;\n return codec.encode(value);\n }\n\n private encodeJson(value: unknown, raw: boolean | undefined): unknown {\n if (value === null || value === undefined) return value;\n // raw=true: caller has already produced the dialect-native form\n // (typically a JSON-encoded string or a path expression). Pass through.\n if (raw) return value;\n // Otherwise serialize the JS value to a JSON literal so the SQL\n // emitter can hand a single text param to the driver.\n return JSON.stringify(value);\n }\n\n eq<K extends keyof InferSchema<S>>(key: K, value: FieldValue<S, K>): FilterBuilder<S> {\n this.add(this.getColumnName(key as string), \"=\", this.encode(key as string, value));\n return this;\n }\n\n neq<K extends keyof InferSchema<S>>(key: K, value: FieldValue<S, K>): FilterBuilder<S> {\n this.add(this.getColumnName(key as string), \"!=\", this.encode(key as string, value));\n return this;\n }\n\n gt<K extends OrderableFieldKeys<S>>(key: K, value: FieldValue<S, K>): FilterBuilder<S> {\n this.add(this.getColumnName(key as string), \">\", this.encode(key as string, value));\n return this;\n }\n\n gte<K extends OrderableFieldKeys<S>>(key: K, value: FieldValue<S, K>): FilterBuilder<S> {\n this.add(this.getColumnName(key as string), \">=\", this.encode(key as string, value));\n return this;\n }\n\n lt<K extends OrderableFieldKeys<S>>(key: K, value: FieldValue<S, K>): FilterBuilder<S> {\n this.add(this.getColumnName(key as string), \"<\", this.encode(key as string, value));\n return this;\n }\n\n lte<K extends OrderableFieldKeys<S>>(key: K, value: FieldValue<S, K>): FilterBuilder<S> {\n this.add(this.getColumnName(key as string), \"<=\", this.encode(key as string, value));\n return this;\n }\n\n like<K extends StringFieldKeys<S>>(key: K, pattern: string): FilterBuilder<S> {\n this.add(this.getColumnName(key as string), \"like\", this.encode(key as string, pattern));\n return this;\n }\n\n ilike<K extends StringFieldKeys<S>>(key: K, pattern: string): FilterBuilder<S> {\n this.add(this.getColumnName(key as string), \"ilike\", this.encode(key as string, pattern));\n return this;\n }\n\n contains<K extends StringFieldKeys<S>>(key: K, value: string): FilterBuilder<S> {\n this.add(this.getColumnName(key as string), \"contains\", this.encode(key as string, value));\n return this;\n }\n\n icontains<K extends StringFieldKeys<S>>(key: K, value: string): FilterBuilder<S> {\n this.add(this.getColumnName(key as string), \"icontains\", this.encode(key as string, value));\n return this;\n }\n\n /**\n * JSON containment. Targets a JSON/JSONB column and asserts the document\n * contains the given candidate (object-key subset for objects,\n * element-subset for arrays — see dialect-specific docs for edge cases).\n * Pass `{ raw: true }` if `value` is already a JSON-encoded string.\n * Pass `{ path: ... }` to scope the check to a sub-document of the\n * target (jsonb_path_query_first on PG, JSON_CONTAINS path arg on MySQL).\n */\n jsonContains<K extends JsonFieldKeys<S>>(key: K, value: unknown, opts?: JsonContainsOptions): FilterBuilder<S> {\n this.addJsonContains(\"jsonContains\", key as string, value, opts);\n return this;\n }\n\n /** Inverse of {@link jsonContains}: column is contained in the given value. */\n jsonContainedBy<K extends JsonFieldKeys<S>>(key: K, value: unknown, opts?: JsonContainsOptions): FilterBuilder<S> {\n this.addJsonContains(\"jsonContainedBy\", key as string, value, opts);\n return this;\n }\n\n /**\n * Key/path existence check on a JSON column.\n *\n * - String form (top-level key): `jsonHasKey(\"meta\", \"color\")`. On PG\n * also matches when the column is a string array and `value` is one of\n * its elements (PG `?` semantics); MySQL stays strict to object keys.\n * - Array form (nested path): `jsonHasKey(\"meta\", [\"a\", \"b\"])` compiles\n * to a path expression `$.\"a\".\"b\"` on both dialects.\n * - Raw form: `jsonHasKey(\"meta\", \"$.foo[*]\", { raw: true })` passes the\n * string through as a dialect path expression (PG `jsonb_path_exists`\n * jsonpath, MySQL `JSON_CONTAINS_PATH`). Caller owns the syntax.\n * - Multi-path form: `jsonHasKey(\"meta\", [[\"a\",\"b\"], \"color\"], { mode: \"all\" })`\n * compiles each entry to a jsonpath and emits PG OR/AND of\n * `jsonb_path_exists` or MySQL `JSON_CONTAINS_PATH('all', ...)`.\n */\n jsonHasKey<K extends JsonFieldKeys<S>>(key: K, value: string): FilterBuilder<S>;\n jsonHasKey<K extends JsonFieldKeys<S>>(key: K, value: string[]): FilterBuilder<S>;\n jsonHasKey<K extends JsonFieldKeys<S>>(key: K, value: string, opts: { raw: true }): FilterBuilder<S>;\n jsonHasKey<K extends JsonFieldKeys<S>>(key: K, paths: (string | string[])[], opts: JsonHasKeyMultiOptions): FilterBuilder<S>;\n jsonHasKey<K extends JsonFieldKeys<S>>(key: K, paths: string[], opts: JsonHasKeyMultiOptions & { raw: true }): FilterBuilder<S>;\n jsonHasKey<K extends JsonFieldKeys<S>>(\n key: K,\n value: string | string[] | (string | string[])[],\n opts?: JsonOpOptions & { mode?: \"one\" | \"all\" }\n ): FilterBuilder<S> {\n const col = this.getColumnName(key as string);\n // Multi-path mode: array of (string|string[]) entries with explicit mode.\n if (opts?.mode && Array.isArray(value)) {\n const paths = (value as (string | string[])[]).map((p) =>\n opts.raw && typeof p === \"string\" ? p : jsonKeysToPath(Array.isArray(p) ? p : [p])\n );\n const leaf: LeafPredicate = {\n kind: \"leaf\",\n column: col,\n operator: \"jsonHasKey\",\n value: paths,\n mode: opts.mode,\n };\n this.predicates.push(leaf);\n return this;\n }\n // Single-key/path mode.\n if (Array.isArray(value)) {\n this.add(col, \"jsonHasKey\", jsonKeysToPath(value as string[]), undefined, true);\n } else if (opts?.raw) {\n this.add(col, \"jsonHasKey\", value, undefined, true);\n } else {\n this.add(col, \"jsonHasKey\", value, undefined, false);\n }\n return this;\n }\n\n /**\n * Array element containment: target column is a JSON array; the given\n * `value` (any JSON-encodable type) must appear as one of its elements.\n * Pass `{ raw: true }` if `value` is already a JSON-encoded string.\n * Pass `{ path: ... }` to scope the check to a sub-array of the column.\n */\n jsonArrayContains<K extends JsonFieldKeys<S>>(key: K, value: unknown, opts?: JsonContainsOptions): FilterBuilder<S> {\n this.addJsonContains(\"jsonArrayContains\", key as string, value, opts);\n return this;\n }\n\n between<K extends OrderableFieldKeys<S>>(key: K, lo: FieldValue<S, K>, hi: FieldValue<S, K>): FilterBuilder<S> {\n this.add(this.getColumnName(key as string), \"between\", this.encode(key as string, lo), this.encode(key as string, hi));\n return this;\n }\n\n /**\n * Set membership: `key` equals any of `values` (SQL `IN`). Each value is\n * codec-encoded like `eq`. An empty `values` matches no rows. Bound as one\n * parameter per element, so keep the list within the driver's parameter\n * limit (chunk very large sets at the call site).\n */\n in<K extends keyof InferSchema<S>>(key: K, values: readonly FieldValue<S, K>[]): FilterBuilder<S> {\n const col = this.getColumnName(key as string);\n this.add(col, \"in\", values.map((v) => this.encode(key as string, v)));\n return this;\n }\n\n isNull<K extends keyof InferSchema<S>>(key: K): FilterBuilder<S> {\n this.add(this.getColumnName(key as string), \"isNull\", null);\n return this;\n }\n\n notNull<K extends keyof InferSchema<S>>(key: K): FilterBuilder<S> {\n this.add(this.getColumnName(key as string), \"isNotNull\", null);\n return this;\n }\n\n /**\n * Compose an OR clause. Each callback fills a fresh sub-FilterBuilder\n * with predicates that are AND-ed together within that branch; the\n * branches themselves are OR-ed. Example:\n * f.or([\n * b => b.eq(\"tag\", \"a\"),\n * b => b.eq(\"tag\", \"b\").gt(\"qty\", 3), // (tag=b AND qty>3)\n * ])\n * produces (tag=a) OR (tag=b AND qty>3).\n */\n or(branches: ((fb: FilterBuilder<S>) => void)[]): FilterBuilder<S> {\n const children: Predicate[] = [];\n for (const fn of branches) {\n const sub = new FilterBuilder<S>(this.columns, this.codecs);\n fn(sub);\n if (sub.predicates.length === 1) {\n children.push(sub.predicates[0]);\n } else if (sub.predicates.length > 1) {\n children.push({ kind: \"and\", children: sub.predicates });\n }\n }\n if (children.length > 0) {\n this.predicates.push({ kind: \"or\", children });\n }\n return this;\n }\n}\n\n\nexport function buildPredicateSql(predicate: Predicate, params: unknown[], sql: SqlBuilder): string {\n switch (predicate.kind) {\n case \"and\": {\n const parts = predicate.children.map((c) => buildPredicateSql(c, params, sql));\n return `(${parts.join(\" AND \")})`;\n }\n case \"or\": {\n const parts = predicate.children.map((c) => buildPredicateSql(c, params, sql));\n return `(${parts.join(\" OR \")})`;\n }\n case \"leaf\": {\n return buildLeafSql(predicate, params, sql);\n }\n }\n}\n\nfunction buildLeafSql(leaf: LeafPredicate, params: unknown[], sql: SqlBuilder): string {\n const col = leaf.column;\n switch (leaf.operator) {\n case \"=\": case \"!=\": case \">\": case \">=\": case \"<\": case \"<=\": {\n params.push(leaf.value);\n return `${col} ${leaf.operator} ${sql.placeholder(params.length)}`;\n }\n case \"between\": {\n params.push(leaf.value);\n const p1 = sql.placeholder(params.length);\n params.push(leaf.value2);\n const p2 = sql.placeholder(params.length);\n return `${col} BETWEEN ${p1} AND ${p2}`;\n }\n case \"in\": {\n const values = (leaf.value ?? []) as unknown[];\n // `col IN ()` is invalid SQL; an empty set matches nothing.\n if (values.length === 0) return \"1 = 0\";\n const placeholders = values.map((v) => {\n params.push(v);\n return sql.placeholder(params.length);\n });\n return `${col} IN (${placeholders.join(\", \")})`;\n }\n case \"like\": {\n params.push(leaf.value);\n return `${col} LIKE ${sql.placeholder(params.length)}`;\n }\n case \"ilike\": {\n params.push(leaf.value);\n return sql.ilike(col, params.length);\n }\n case \"contains\": {\n params.push(`%${leaf.value}%`);\n return `${col} LIKE ${sql.placeholder(params.length)}`;\n }\n case \"icontains\": {\n params.push(`%${leaf.value}%`);\n return sql.icontains(col, params.length);\n }\n case \"jsonContains\": {\n params.push(leaf.value);\n const valIdx = params.length;\n let pathIndex: number | undefined;\n if (leaf.path !== undefined) {\n params.push(leaf.path);\n pathIndex = params.length;\n }\n return sql.jsonContains(col, valIdx, { raw: leaf.raw, pathIndex });\n }\n case \"jsonContainedBy\": {\n params.push(leaf.value);\n const valIdx = params.length;\n let pathIndex: number | undefined;\n if (leaf.path !== undefined) {\n params.push(leaf.path);\n pathIndex = params.length;\n }\n return sql.jsonContainedBy(col, valIdx, { raw: leaf.raw, pathIndex });\n }\n case \"jsonHasKey\": {\n if (leaf.mode && Array.isArray(leaf.value)) {\n const paths = leaf.value as string[];\n const startIdx = params.length + 1;\n for (const p of paths) params.push(p);\n return sql.jsonHasKey(col, startIdx, {\n raw: true,\n mode: leaf.mode,\n pathCount: paths.length,\n });\n }\n params.push(leaf.value);\n return sql.jsonHasKey(col, params.length, { raw: leaf.raw });\n }\n case \"jsonArrayContains\": {\n params.push(leaf.value);\n const valIdx = params.length;\n let pathIndex: number | undefined;\n if (leaf.path !== undefined) {\n params.push(leaf.path);\n pathIndex = params.length;\n }\n return sql.jsonArrayContains(col, valIdx, { raw: leaf.raw, pathIndex });\n }\n case \"isNull\": return `${col} IS NULL`;\n case \"isNotNull\": return `${col} IS NOT NULL`;\n }\n}\n\nexport function matchesPredicate(row: Record<string, unknown>, predicate: Predicate): boolean {\n switch (predicate.kind) {\n case \"and\": return predicate.children.every((c) => matchesPredicate(row, c));\n case \"or\": return predicate.children.some((c) => matchesPredicate(row, c));\n case \"leaf\": return matchesLeaf(row, predicate);\n }\n}\n\nfunction matchesLeaf(row: Record<string, unknown>, leaf: LeafPredicate): boolean {\n const val = row[leaf.column];\n\n switch (leaf.operator) {\n case \"=\": return val === leaf.value;\n case \"!=\": return val !== leaf.value;\n case \">\": return (val as number) > (leaf.value as number);\n case \">=\": return (val as number) >= (leaf.value as number);\n case \"<\": return (val as number) < (leaf.value as number);\n case \"<=\": return (val as number) <= (leaf.value as number);\n\n case \"between\":\n return (val as number) >= (leaf.value as number) &&\n (val as number) <= (leaf.value2 as number);\n\n case \"in\":\n return Array.isArray(leaf.value) && (leaf.value as unknown[]).includes(val);\n\n case \"like\":\n case \"contains\":\n case \"ilike\":\n case \"icontains\": {\n if (typeof val !== \"string\" || typeof leaf.value !== \"string\") return false;\n const haystack = (leaf.operator === \"ilike\" || leaf.operator === \"icontains\")\n ? (val as string).toLowerCase()\n : val as string;\n const raw = leaf.value as string;\n const needle = (leaf.operator === \"ilike\" || leaf.operator === \"icontains\")\n ? raw.toLowerCase()\n : raw;\n const pattern = (leaf.operator === \"contains\" || leaf.operator === \"icontains\")\n ? `%${needle}%`\n : needle;\n const regex = new RegExp(\n \"^\" + pattern.replace(/%/g, \".*\").replace(/_/g, \".\") + \"$\"\n );\n return regex.test(haystack);\n }\n\n case \"jsonContains\": {\n const candidate = decodeJsonLeafValue(leaf);\n const target = leaf.path !== undefined ? jsonPathExtract(val, leaf.path) : val;\n if (target === undefined) return false;\n return jsonContains(target, candidate);\n }\n\n case \"jsonContainedBy\": {\n const candidate = decodeJsonLeafValue(leaf);\n const target = leaf.path !== undefined ? jsonPathExtract(val, leaf.path) : val;\n if (target === undefined) return false;\n return jsonContains(candidate, target);\n }\n\n case \"jsonHasKey\": {\n if (leaf.mode && Array.isArray(leaf.value)) {\n const paths = leaf.value as string[];\n if (paths.length === 0) return false;\n return leaf.mode === \"all\"\n ? paths.every((p) => jsonPathExists(val, p))\n : paths.some((p) => jsonPathExists(val, p));\n }\n if (leaf.raw) {\n return jsonPathExists(val, leaf.value as string);\n }\n const key = leaf.value as string;\n if (val === null || typeof val !== \"object\") return false;\n if (Array.isArray(val)) return (val as unknown[]).includes(key);\n return key in (val as object);\n }\n\n case \"jsonArrayContains\": {\n const candidate = decodeJsonLeafValue(leaf);\n const target = leaf.path !== undefined ? jsonPathExtract(val, leaf.path) : val;\n if (!Array.isArray(target)) return false;\n return (target as unknown[]).some((el) => jsonContains(el, candidate));\n }\n\n case \"isNull\":\n return val === null;\n\n case \"isNotNull\":\n return val !== null;\n\n default:\n return false;\n }\n}\n\n// When raw=true the leaf.value is a JSON-encoded string; decode for in-memory\n// comparison. Otherwise the value is the original JS value as passed.\nfunction decodeJsonLeafValue(leaf: LeafPredicate): unknown {\n if (!leaf.raw) return leaf.value;\n if (typeof leaf.value !== \"string\") return leaf.value;\n try { return JSON.parse(leaf.value); } catch { return leaf.value; }\n}\n\n// PG `@>` / MySQL `JSON_CONTAINS` semantics. Recursive containment:\n// - scalars: strict equality\n// - object: every candidate key matches target's value recursively\n// - array: every candidate element is contained in some target element\n// - scalar candidate vs array target: candidate equals some element\nexport function jsonContains(target: unknown, candidate: unknown): boolean {\n if (candidate === target) return true;\n if (target === null || candidate === null) return target === candidate;\n if (target === undefined || candidate === undefined) return false;\n\n const tArr = Array.isArray(target);\n const cArr = Array.isArray(candidate);\n const tObj = !tArr && typeof target === \"object\";\n const cObj = !cArr && typeof candidate === \"object\";\n\n // Scalar candidate in array target.\n if (!cArr && !cObj && tArr) {\n return (target as unknown[]).some((el) => jsonContains(el, candidate));\n }\n // Both arrays: every candidate element matches some target element.\n if (cArr && tArr) {\n const t = target as unknown[];\n return (candidate as unknown[]).every((c) => t.some((el) => jsonContains(el, c)));\n }\n // Both objects.\n if (cObj && tObj) {\n const t = target as Record<string, unknown>;\n const c = candidate as Record<string, unknown>;\n for (const k of Object.keys(c)) {\n if (!(k in t)) return false;\n if (!jsonContains(t[k], c[k])) return false;\n }\n return true;\n }\n return false;\n}\n\n// Walk the JSON path and return the value at it, or `undefined` if any\n// segment is missing or unresolvable. Same syntax subset as\n// {@link jsonPathExists}.\nexport function jsonPathExtract(val: unknown, path: string): unknown {\n let p = path;\n if (p.startsWith(\"$\")) p = p.slice(1);\n let cur: unknown = val;\n while (p.length > 0) {\n if (p.startsWith('.\"')) {\n let end = 2;\n while (end < p.length) {\n if (p[end] === \"\\\\\" && end + 1 < p.length) { end += 2; continue; }\n if (p[end] === '\"') break;\n end++;\n }\n if (end >= p.length) return undefined;\n const key = p.slice(2, end).replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n if (cur === null || typeof cur !== \"object\" || Array.isArray(cur)) return undefined;\n if (!(key in (cur as object))) return undefined;\n cur = (cur as Record<string, unknown>)[key];\n p = p.slice(end + 1);\n } else if (p.startsWith(\".\")) {\n const m = p.slice(1).match(/^([^.[]+)/);\n if (!m) return undefined;\n const key = m[1];\n if (cur === null || typeof cur !== \"object\" || Array.isArray(cur)) return undefined;\n if (!(key in (cur as object))) return undefined;\n cur = (cur as Record<string, unknown>)[key];\n p = p.slice(1 + key.length);\n } else if (p.startsWith(\"[\")) {\n const end = p.indexOf(\"]\");\n if (end === -1) return undefined;\n const idx = parseInt(p.slice(1, end), 10);\n if (!Array.isArray(cur) || idx < 0 || idx >= (cur as unknown[]).length) return undefined;\n cur = (cur as unknown[])[idx];\n p = p.slice(end + 1);\n } else {\n return undefined;\n }\n }\n return cur;\n}\n\n// Best-effort JSON path evaluator for the in-memory predicate match.\n// Supports the path syntax this module emits (`$.\"key\".\"nested\"`) plus the\n// common shorthands `$.key` and `$.key[0]`. Anything more exotic (filters,\n// wildcards) returns false — DB will still evaluate it correctly server-side.\nexport function jsonPathExists(val: unknown, path: string): boolean {\n let p = path;\n if (p.startsWith(\"$\")) p = p.slice(1);\n let cur: unknown = val;\n while (p.length > 0) {\n if (p.startsWith('.\"')) {\n let end = 2;\n while (end < p.length) {\n if (p[end] === \"\\\\\" && end + 1 < p.length) { end += 2; continue; }\n if (p[end] === '\"') break;\n end++;\n }\n if (end >= p.length) return false;\n const key = p.slice(2, end).replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, \"\\\\\");\n if (cur === null || typeof cur !== \"object\" || Array.isArray(cur)) return false;\n if (!(key in (cur as object))) return false;\n cur = (cur as Record<string, unknown>)[key];\n p = p.slice(end + 1);\n } else if (p.startsWith(\".\")) {\n const m = p.slice(1).match(/^([^.[]+)/);\n if (!m) return false;\n const key = m[1];\n if (cur === null || typeof cur !== \"object\" || Array.isArray(cur)) return false;\n if (!(key in (cur as object))) return false;\n cur = (cur as Record<string, unknown>)[key];\n p = p.slice(1 + key.length);\n } else if (p.startsWith(\"[\")) {\n const end = p.indexOf(\"]\");\n if (end === -1) return false;\n const idx = parseInt(p.slice(1, end), 10);\n if (!Array.isArray(cur) || idx < 0 || idx >= (cur as unknown[]).length) return false;\n cur = (cur as unknown[])[idx];\n p = p.slice(end + 1);\n } else {\n return false;\n }\n }\n return cur !== undefined;\n}\n"],"mappings":"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"}
|
|
@@ -1,30 +1,47 @@
|
|
|
1
1
|
import { n as normalizeCodec, r as unwrapZodType } from "./codec-BHAmGEIQ.js";
|
|
2
|
-
import { t as QueryBuilder } from "./query-
|
|
3
|
-
import {
|
|
2
|
+
import { t as QueryBuilder } from "./query-CrbJ_W0W.js";
|
|
3
|
+
import { r as LazyCommitTsParam } from "./db-ClCicssQ.js";
|
|
4
|
+
import { l as OccConflictError } from "./types_db-CTRm_awX.js";
|
|
4
5
|
import { z as z$1 } from "zod";
|
|
5
6
|
import { ExponentialBackoff, handleWhen, retry } from "cockatiel";
|
|
6
|
-
//#region src/db/db.ts
|
|
7
|
-
var LazyCommitTsParam = class LazyCommitTsParam {
|
|
8
|
-
value;
|
|
9
|
-
constructor(value) {
|
|
10
|
-
this.value = value;
|
|
11
|
-
}
|
|
12
|
-
toPostgres(prepareValue) {
|
|
13
|
-
return prepareValue(this.value);
|
|
14
|
-
}
|
|
15
|
-
toString() {
|
|
16
|
-
return this.value.toString();
|
|
17
|
-
}
|
|
18
|
-
validate() {
|
|
19
|
-
const p = new LazyCommitTsParam(void 0);
|
|
20
|
-
const hasNotObjConstructor = p.constructor !== Object;
|
|
21
|
-
const hasNotToJSON = typeof p.toJSON !== "function";
|
|
22
|
-
console.log("constructor must not object", hasNotObjConstructor);
|
|
23
|
-
console.log("should not have toJSON", hasNotToJSON);
|
|
24
|
-
}
|
|
25
|
-
};
|
|
26
|
-
//#endregion
|
|
27
7
|
//#region src/db/context.ts
|
|
8
|
+
/**
|
|
9
|
+
* Materialize a schema's runtime {@link Model}: column-name mapping, per-column
|
|
10
|
+
* coarse type tags, and the codec used to (de)serialize each column.
|
|
11
|
+
*
|
|
12
|
+
* Codecs are resolved via {@link normalizeCodec}, which supplies a sensible
|
|
13
|
+
* default per Zod type (e.g. a {@link dateCodec} for `z.date()`, so `Date`
|
|
14
|
+
* columns round-trip automatically) while letting a user-supplied codec in
|
|
15
|
+
* `schema.columnsOptions.codecs` override it — partially or fully.
|
|
16
|
+
*/
|
|
17
|
+
function buildModel(schema) {
|
|
18
|
+
const shape = schema.schema.shape;
|
|
19
|
+
const customColumns = schema.columns;
|
|
20
|
+
const columns = {};
|
|
21
|
+
const types = {};
|
|
22
|
+
const codecs = {};
|
|
23
|
+
const schemaCodecs = schema.columnsOptions.codecs ?? {};
|
|
24
|
+
for (const key of Object.keys(shape)) {
|
|
25
|
+
columns[key] = customColumns?.[key] ?? key;
|
|
26
|
+
const field = shape[key];
|
|
27
|
+
const baseField = unwrapZodType(field);
|
|
28
|
+
if (baseField instanceof z$1.ZodNumber) types[key] = "integer";
|
|
29
|
+
else if (baseField instanceof z$1.ZodString) types[key] = "string";
|
|
30
|
+
else if (baseField instanceof z$1.ZodBoolean) types[key] = "boolean";
|
|
31
|
+
else if (baseField instanceof z$1.ZodDate) types[key] = "date";
|
|
32
|
+
else if (baseField instanceof z$1.ZodArray) types[key] = "array";
|
|
33
|
+
else types[key] = "json";
|
|
34
|
+
codecs[key] = normalizeCodec(schemaCodecs[key], baseField);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
table: schema.table,
|
|
38
|
+
tableId: schema.tableId,
|
|
39
|
+
columns,
|
|
40
|
+
reverseColumns: Object.fromEntries(Object.entries(columns).map(([k, v]) => [v, k])),
|
|
41
|
+
types,
|
|
42
|
+
codecs
|
|
43
|
+
};
|
|
44
|
+
}
|
|
28
45
|
var TxContext = class {
|
|
29
46
|
beginTs;
|
|
30
47
|
db;
|
|
@@ -34,31 +51,7 @@ var TxContext = class {
|
|
|
34
51
|
getOrCreateModel(schema) {
|
|
35
52
|
const existing = this.models.get(schema.table);
|
|
36
53
|
if (existing) return existing;
|
|
37
|
-
const
|
|
38
|
-
const customColumns = schema.columns;
|
|
39
|
-
const columns = {};
|
|
40
|
-
const types = {};
|
|
41
|
-
const codecs = {};
|
|
42
|
-
const schemaCodecs = schema.columnsOptions.codecs ?? {};
|
|
43
|
-
for (const key of Object.keys(shape)) {
|
|
44
|
-
columns[key] = customColumns?.[key] ?? key;
|
|
45
|
-
const field = shape[key];
|
|
46
|
-
const baseField = unwrapZodType(field);
|
|
47
|
-
if (baseField instanceof z$1.ZodNumber) types[key] = "integer";
|
|
48
|
-
else if (baseField instanceof z$1.ZodString) types[key] = "string";
|
|
49
|
-
else if (baseField instanceof z$1.ZodBoolean) types[key] = "boolean";
|
|
50
|
-
else if (baseField instanceof z$1.ZodArray) types[key] = "array";
|
|
51
|
-
else types[key] = "json";
|
|
52
|
-
codecs[key] = normalizeCodec(schemaCodecs[key], baseField);
|
|
53
|
-
}
|
|
54
|
-
const model = {
|
|
55
|
-
table: schema.table,
|
|
56
|
-
tableId: schema.tableId,
|
|
57
|
-
columns,
|
|
58
|
-
reverseColumns: Object.fromEntries(Object.entries(columns).map(([k, v]) => [v, k])),
|
|
59
|
-
types,
|
|
60
|
-
codecs
|
|
61
|
-
};
|
|
54
|
+
const model = buildModel(schema);
|
|
62
55
|
this.models.set(schema.table, model);
|
|
63
56
|
return model;
|
|
64
57
|
}
|
|
@@ -72,6 +65,15 @@ var TxContext = class {
|
|
|
72
65
|
internalGetWriteSet() {
|
|
73
66
|
return this.writeSet;
|
|
74
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Merge a parent procedure's reads into this context's readSet. Used when a
|
|
70
|
+
* `runMutation` sub-transaction inherits the reads of the procedure that
|
|
71
|
+
* invoked it, so the sub-tx's OCC validation also fences on those rows.
|
|
72
|
+
* Entries are shallow-cloned so the two readSets don't alias.
|
|
73
|
+
*/
|
|
74
|
+
appendInternalReadSet(parentEntries) {
|
|
75
|
+
this.readSet.push(...parentEntries.map((e) => ({ ...e })));
|
|
76
|
+
}
|
|
75
77
|
findPendingWrite(table, id) {
|
|
76
78
|
return this.writeSet.find((w) => w.table === table && w.id === id);
|
|
77
79
|
}
|
|
@@ -192,11 +194,31 @@ var SupaliveDb = class SupaliveDb {
|
|
|
192
194
|
return new SupaliveDb(config.db);
|
|
193
195
|
}
|
|
194
196
|
onAfterCommit;
|
|
197
|
+
/**
|
|
198
|
+
* Register a hook fired once after every successful `mutation()` commit
|
|
199
|
+
* (never on an OCC conflict/rollback), with the committed writeSet and its
|
|
200
|
+
* commitTs. The server uses this to drive subscription invalidation for ALL
|
|
201
|
+
* mutation sources — client WS calls, actions, jobs, and the internal caller —
|
|
202
|
+
* so a server-side write pushes live updates just like a client write does.
|
|
203
|
+
* Non-server consumers (migration tooling, tests) leave it unset (no-op).
|
|
204
|
+
*/
|
|
205
|
+
setOnAfterCommit(cb) {
|
|
206
|
+
this.onAfterCommit = cb;
|
|
207
|
+
}
|
|
195
208
|
async query(fn) {
|
|
196
209
|
const beginTs = await this.impl.getLatestSnapshotTimestamp();
|
|
197
210
|
return await fn(new DbReader(this.impl, beginTs));
|
|
198
211
|
}
|
|
199
|
-
|
|
212
|
+
/**
|
|
213
|
+
* Compute a query once at the latest snapshot, returning the result together
|
|
214
|
+
* with the snapshot `ts` and the tracked `readSet`. This is a plain one-shot
|
|
215
|
+
* evaluation — it registers NO live subscription; the `readSet`/`ts` are
|
|
216
|
+
* returned purely so the caller (e.g. the one-shot query cache) can store
|
|
217
|
+
* freshness metadata. Use this for cached one-shot query calls (WS `call`,
|
|
218
|
+
* HTTP RPC). `liveQuery` is the same computation under the subscription path's
|
|
219
|
+
* name and simply delegates here.
|
|
220
|
+
*/
|
|
221
|
+
async queryInternal(fn) {
|
|
200
222
|
const beginTs = await this.impl.getLatestSnapshotTimestamp();
|
|
201
223
|
const ctx = new DbReader(this.impl, beginTs);
|
|
202
224
|
return {
|
|
@@ -205,8 +227,11 @@ var SupaliveDb = class SupaliveDb {
|
|
|
205
227
|
readSet: ctx.internalGetReadSet()
|
|
206
228
|
};
|
|
207
229
|
}
|
|
230
|
+
async liveQuery(fn) {
|
|
231
|
+
return this.queryInternal(fn);
|
|
232
|
+
}
|
|
208
233
|
async mutation(fn, retryPolicy = occRetryPolicy) {
|
|
209
|
-
return await retryPolicy.execute(async () => {
|
|
234
|
+
return await retryPolicy.execute(async (p) => {
|
|
210
235
|
let db;
|
|
211
236
|
try {
|
|
212
237
|
const beginTs = await this.impl.getLatestSnapshotTimestamp() + 1n;
|
|
@@ -244,4 +269,4 @@ const sleep = (ms) => new Promise((res) => setTimeout(res, ms));
|
|
|
244
269
|
//#endregion
|
|
245
270
|
export { TxContext as a, DbWriter as i, sleep as n, DbReader as r, SupaliveDb as t };
|
|
246
271
|
|
|
247
|
-
//# sourceMappingURL=realtime_db-
|
|
272
|
+
//# sourceMappingURL=realtime_db-BtwBABDY.js.map
|