@rocicorp/zero 1.10.0-canary.23 → 1.10.0-canary.24
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/out/analyze-query/src/bin-analyze.js +8 -7
- package/out/analyze-query/src/bin-analyze.js.map +1 -1
- package/out/replicache/src/btree/node.d.ts.map +1 -1
- package/out/replicache/src/btree/node.js +3 -6
- package/out/replicache/src/btree/node.js.map +1 -1
- package/out/replicache/src/dag/gc.d.ts.map +1 -1
- package/out/replicache/src/dag/gc.js +6 -10
- package/out/replicache/src/dag/gc.js.map +1 -1
- package/out/replicache/src/kv/sqlite-store.d.ts.map +1 -1
- package/out/replicache/src/kv/sqlite-store.js +24 -2
- package/out/replicache/src/kv/sqlite-store.js.map +1 -1
- package/out/replicache/src/persist/persist.js +1 -1
- package/out/shared/src/arrays.d.ts +5 -0
- package/out/shared/src/arrays.d.ts.map +1 -1
- package/out/shared/src/arrays.js +8 -10
- package/out/shared/src/arrays.js.map +1 -1
- package/out/shared/src/map.d.ts +19 -2
- package/out/shared/src/map.d.ts.map +1 -1
- package/out/shared/src/map.js +18 -4
- package/out/shared/src/map.js.map +1 -1
- package/out/z2s/src/compiler.js +1 -1
- package/out/z2s/src/sql.d.ts.map +1 -1
- package/out/z2s/src/sql.js +2 -2
- package/out/z2s/src/sql.js.map +1 -1
- package/out/zero/package.js +1 -1
- package/out/zero/package.js.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.d.ts.map +1 -1
- package/out/zero-cache/src/auth/write-authorizer.js +1 -0
- package/out/zero-cache/src/auth/write-authorizer.js.map +1 -1
- package/out/zero-cache/src/config/zero-config.d.ts +15 -0
- package/out/zero-cache/src/config/zero-config.d.ts.map +1 -1
- package/out/zero-cache/src/config/zero-config.js +35 -0
- package/out/zero-cache/src/config/zero-config.js.map +1 -1
- package/out/zero-cache/src/db/lite-tables.d.ts.map +1 -1
- package/out/zero-cache/src/db/lite-tables.js +3 -4
- package/out/zero-cache/src/db/lite-tables.js.map +1 -1
- package/out/zero-cache/src/observability/metrics.d.ts.map +1 -1
- package/out/zero-cache/src/observability/metrics.js +2 -7
- package/out/zero-cache/src/observability/metrics.js.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.d.ts.map +1 -1
- package/out/zero-cache/src/server/inspector-delegate.js +2 -8
- package/out/zero-cache/src/server/inspector-delegate.js.map +1 -1
- package/out/zero-cache/src/server/syncer.d.ts.map +1 -1
- package/out/zero-cache/src/server/syncer.js +3 -1
- package/out/zero-cache/src/server/syncer.js.map +1 -1
- package/out/zero-cache/src/services/analyze.d.ts.map +1 -1
- package/out/zero-cache/src/services/analyze.js +8 -7
- package/out/zero-cache/src/services/analyze.js.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.d.ts +32 -32
- package/out/zero-cache/src/services/mutagen/pusher.d.ts.map +1 -1
- package/out/zero-cache/src/services/mutagen/pusher.js +3 -5
- package/out/zero-cache/src/services/mutagen/pusher.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js +15 -9
- package/out/zero-cache/src/services/view-syncer/pipeline-driver.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-covering.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/query-covering.js +2 -6
- package/out/zero-cache/src/services/view-syncer/query-covering.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts +95 -0
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.d.ts.map +1 -0
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js +155 -0
- package/out/zero-cache/src/services/view-syncer/snapshot-row-cache.js.map +1 -0
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts +38 -3
- package/out/zero-cache/src/services/view-syncer/snapshotter.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/snapshotter.js +110 -27
- package/out/zero-cache/src/services/view-syncer/snapshotter.js.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.d.ts.map +1 -1
- package/out/zero-cache/src/services/view-syncer/view-syncer.js +4 -13
- package/out/zero-cache/src/services/view-syncer/view-syncer.js.map +1 -1
- package/out/zero-cache/src/types/row-key.d.ts.map +1 -1
- package/out/zero-cache/src/types/row-key.js +5 -5
- package/out/zero-cache/src/types/row-key.js.map +1 -1
- package/out/zero-client/src/client/query-manager.d.ts.map +1 -1
- package/out/zero-client/src/client/query-manager.js +2 -5
- package/out/zero-client/src/client/query-manager.js.map +1 -1
- package/out/zero-client/src/client/version.js +1 -1
- package/out/zero-protocol/src/analyze-query-result.d.ts +5 -5
- package/out/zero-protocol/src/analyze-query-result.d.ts.map +1 -1
- package/out/zero-protocol/src/analyze-query-result.js +1 -1
- package/out/zero-protocol/src/analyze-query-result.js.map +1 -1
- package/out/zero-protocol/src/ast.d.ts.map +1 -1
- package/out/zero-protocol/src/ast.js +13 -14
- package/out/zero-protocol/src/ast.js.map +1 -1
- package/out/zero-protocol/src/down.d.ts +2 -2
- package/out/zero-protocol/src/inspect-down.d.ts +6 -6
- package/out/zero-protocol/src/protocol-version.d.ts.map +1 -1
- package/out/zero-protocol/src/protocol-version.js.map +1 -1
- package/out/zero-protocol/src/query-hash.d.ts.map +1 -1
- package/out/zero-protocol/src/query-hash.js +2 -5
- package/out/zero-protocol/src/query-hash.js.map +1 -1
- package/out/zql/src/builder/builder.d.ts +19 -0
- package/out/zql/src/builder/builder.d.ts.map +1 -1
- package/out/zql/src/builder/builder.js +28 -11
- package/out/zql/src/builder/builder.js.map +1 -1
- package/out/zql/src/builder/correlated-predicate-pushdown.d.ts +43 -0
- package/out/zql/src/builder/correlated-predicate-pushdown.d.ts.map +1 -0
- package/out/zql/src/builder/correlated-predicate-pushdown.js +161 -0
- package/out/zql/src/builder/correlated-predicate-pushdown.js.map +1 -0
- package/out/zql/src/ivm/connection-index.d.ts +68 -0
- package/out/zql/src/ivm/connection-index.d.ts.map +1 -0
- package/out/zql/src/ivm/connection-index.js +149 -0
- package/out/zql/src/ivm/connection-index.js.map +1 -0
- package/out/zql/src/ivm/exists.js +1 -1
- package/out/zql/src/ivm/filter-operators.d.ts +3 -2
- package/out/zql/src/ivm/filter-operators.d.ts.map +1 -1
- package/out/zql/src/ivm/filter-operators.js +19 -4
- package/out/zql/src/ivm/filter-operators.js.map +1 -1
- package/out/zql/src/ivm/memory-source.d.ts.map +1 -1
- package/out/zql/src/ivm/memory-source.js +17 -4
- package/out/zql/src/ivm/memory-source.js.map +1 -1
- package/out/zql/src/ivm/operator.d.ts +40 -0
- package/out/zql/src/ivm/operator.d.ts.map +1 -1
- package/out/zql/src/ivm/operator.js.map +1 -1
- package/out/zql/src/planner/planner-builder.d.ts +12 -3
- package/out/zql/src/planner/planner-builder.d.ts.map +1 -1
- package/out/zql/src/planner/planner-builder.js +40 -20
- package/out/zql/src/planner/planner-builder.js.map +1 -1
- package/out/zql/src/planner/planner-connection.d.ts +28 -2
- package/out/zql/src/planner/planner-connection.d.ts.map +1 -1
- package/out/zql/src/planner/planner-connection.js +108 -8
- package/out/zql/src/planner/planner-connection.js.map +1 -1
- package/out/zql/src/planner/planner-debug.d.ts +1 -1
- package/out/zql/src/planner/planner-debug.d.ts.map +1 -1
- package/out/zql/src/planner/planner-debug.js +3 -8
- package/out/zql/src/planner/planner-debug.js.map +1 -1
- package/out/zql/src/planner/planner-fan-out.d.ts +1 -0
- package/out/zql/src/planner/planner-fan-out.d.ts.map +1 -1
- package/out/zql/src/planner/planner-fan-out.js +3 -0
- package/out/zql/src/planner/planner-fan-out.js.map +1 -1
- package/out/zql/src/planner/planner-filter.d.ts +59 -0
- package/out/zql/src/planner/planner-filter.d.ts.map +1 -0
- package/out/zql/src/planner/planner-filter.js +116 -0
- package/out/zql/src/planner/planner-filter.js.map +1 -0
- package/out/zql/src/planner/planner-graph.d.ts +2 -0
- package/out/zql/src/planner/planner-graph.d.ts.map +1 -1
- package/out/zql/src/planner/planner-graph.js +2 -0
- package/out/zql/src/planner/planner-graph.js.map +1 -1
- package/out/zql/src/planner/planner-join.d.ts +2 -0
- package/out/zql/src/planner/planner-join.d.ts.map +1 -1
- package/out/zql/src/planner/planner-join.js +7 -0
- package/out/zql/src/planner/planner-join.js.map +1 -1
- package/out/zql/src/planner/planner-node.d.ts +2 -1
- package/out/zql/src/planner/planner-node.d.ts.map +1 -1
- package/out/zql/src/planner/planner-node.js.map +1 -1
- package/out/zql/src/planner/planner-source.d.ts +2 -2
- package/out/zql/src/planner/planner-source.d.ts.map +1 -1
- package/out/zql/src/planner/planner-source.js +2 -2
- package/out/zql/src/planner/planner-source.js.map +1 -1
- package/out/zql/src/query/query-impl.d.ts.map +1 -1
- package/out/zql/src/query/query-impl.js +6 -23
- package/out/zql/src/query/query-impl.js.map +1 -1
- package/out/zql/src/query/query-transitions.d.ts.map +1 -1
- package/out/zql/src/query/query-transitions.js +2 -5
- package/out/zql/src/query/query-transitions.js.map +1 -1
- package/out/zql/src/query/runnable-query-impl.d.ts.map +1 -1
- package/out/zql/src/query/runnable-query-impl.js +2 -10
- package/out/zql/src/query/runnable-query-impl.js.map +1 -1
- package/out/zqlite/src/query-builder.d.ts +1 -1
- package/out/zqlite/src/query-builder.d.ts.map +1 -1
- package/out/zqlite/src/query-builder.js +2 -1
- package/out/zqlite/src/query-builder.js.map +1 -1
- package/out/zqlite/src/query-delegate.d.ts.map +1 -1
- package/out/zqlite/src/query-delegate.js +5 -6
- package/out/zqlite/src/query-delegate.js.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.d.ts.map +1 -1
- package/out/zqlite/src/sqlite-cost-model.js +2 -30
- package/out/zqlite/src/sqlite-cost-model.js.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.d.ts.map +1 -1
- package/out/zqlite/src/sqlite-stat-fanout.js +3 -5
- package/out/zqlite/src/sqlite-stat-fanout.js.map +1 -1
- package/out/zqlite/src/table-source.d.ts +14 -2
- package/out/zqlite/src/table-source.d.ts.map +1 -1
- package/out/zqlite/src/table-source.js +25 -12
- package/out/zqlite/src/table-source.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sqlite-store.js","names":["#filename","#entry","#kind","#closed","#release","#preparedStatements","#pendingHasKeys","#pendingHasCallbacks","#scheduleLookup","#pendingGetKeys","#pendingGetCallbacks","#scheduled","#dbDelegate","#committed"],"sources":["../../../../../replicache/src/kv/sqlite-store.ts"],"sourcesContent":["import {RWLock} from '@rocicorp/lock';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {getOrInsertComputed} from '../../../shared/src/map.ts';\nimport {deepFreeze} from '../frozen-json.ts';\nimport type {Read, Store, Write} from './store.ts';\nimport {\n maybeTransactionIsClosedRejection,\n throwIfStoreClosed,\n transactionError,\n} from './throw-if-closed.ts';\nimport {deleteSentinel, WriteImplBase} from './write-impl-base.ts';\n\n/**\n * A SQLite prepared statement.\n *\n * `SQLiteStore` prepares one statement per SQL and shares it across all\n * concurrent readers, so implementations must make each call atomic with\n * respect to other callers of the same statement: `all()` must not let another\n * `exec()`/`all()` rebind or reset the underlying statement between executing\n * it and fetching its rows. Delegates whose native API splits execute and fetch\n * into separate round trips (e.g. expo-sqlite) must serialize per statement.\n */\nexport interface PreparedStatement {\n exec(params: string[]): Promise<void>;\n all(params: string[]): Promise<unknown[][]>;\n}\n\nexport interface SQLiteDatabase {\n /**\n * Close the database connection.\n */\n close(): void;\n\n /**\n * Destroy or delete the database (e.g. delete file).\n */\n destroy(): void;\n\n /**\n * Prepare a SQL string, returning a statement you can execute.\n * E.g. `const stmt = db.prepare(\"SELECT * FROM todos WHERE id=?\");`\n */\n prepare(sql: string): PreparedStatement;\n\n // for PRAGMA statements, schema creation and transaction control.\n execSync(sql: string): void;\n}\n\nexport type CreateSQLiteDatabase = (\n filename: string,\n opts?: SQLiteStoreOptions,\n) => SQLiteDatabase;\n\n/**\n * SQLite-based implementation of the Store interface using a configurable delegate.\n * Supports shared connections between multiple store instances with the same name,\n * providing efficient resource utilization and proper transaction isolation.\n * Uses parameterized queries for safety and performance.\n */\nexport class SQLiteStore implements Store {\n readonly #filename: string;\n readonly #entry: StoreEntry;\n readonly #kind: string;\n\n #closed = false;\n\n constructor(\n name: string,\n create: CreateSQLiteDatabase,\n opts?: SQLiteStoreOptions,\n kind = 'sqlite',\n ) {\n this.#filename = resolveFilename(name, opts);\n this.#entry = getOrCreateEntry(this.#filename, create, opts);\n this.#kind = kind;\n }\n\n get kind(): string {\n return this.#kind;\n }\n\n async read(): Promise<Read> {\n throwIfStoreClosed(this);\n\n const entry = this.#entry;\n const {db, lock, preparedStatements} = entry;\n const release = await lock.read();\n\n // Start shared read transaction if this is the first reader\n // This ensures consistent reads across all concurrent readers\n if (entry.activeReaders === 0) {\n db.execSync('BEGIN');\n }\n entry.activeReaders++;\n\n return new SQLiteStoreRead(() => {\n entry.activeReaders--;\n // Commit shared read transaction when last reader finishes\n if (entry.activeReaders === 0) {\n db.execSync('COMMIT');\n }\n release();\n }, preparedStatements);\n }\n\n async write(): Promise<Write> {\n throwIfStoreClosed(this);\n\n const {lock, db, preparedStatements} = this.#entry;\n const release = await lock.write();\n\n // At this point, RWLock guarantees no active readers\n // The last reader would have already committed the shared transaction\n\n db.execSync('BEGIN IMMEDIATE');\n\n return new SQLiteWrite(release, db, preparedStatements);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n\n const {lock, db} = this.#entry;\n // Wait for all readers and writers to finish.\n const writeRelease = await lock.write();\n\n // Handle reference counting for shared stores - only close database\n // when this is the last store instance using it\n decrementStoreRefCount(this.#filename, db);\n\n this.#closed = true;\n writeRelease();\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\nexport function safeFilename(name: string): string {\n return name.replace(/[^a-zA-Z0-9]/g, '_');\n}\n\nfunction resolveFilename(name: string, opts?: SQLiteStoreOptions): string {\n const safe = safeFilename(name);\n const dir = opts?.directory;\n return dir ? `${dir}/${safe}` : safe;\n}\n\nexport type PreparedStatements = {\n has: PreparedStatement;\n get: PreparedStatement;\n hasMany: PreparedStatement;\n getMany: PreparedStatement;\n put: PreparedStatement;\n del: PreparedStatement;\n /** Multi-row INSERT with the values bound as parameters, n rows wide. */\n putN: (n: number) => PreparedStatement;\n /** Multi-key DELETE with the keys bound as parameters, n keys wide. */\n delN: (n: number) => PreparedStatement;\n};\n\n/**\n * Widest batch we bind in one statement. SQLite's SQLITE_MAX_VARIABLE_NUMBER is\n * 32766, and a put costs two parameters per row, so this is far below the cap;\n * it exists to bound how many distinct statements we prepare and cache.\n */\nconst MAX_BATCH = 128;\n\n/** `repeatList('?', 3)` -> `'?,?,?'`. */\nfunction repeatList(item: string, n: number): string {\n return `${item},`.repeat(n).slice(0, -1);\n}\n\n/**\n * Prepares (and caches) a statement of each width on demand. Callers only ever\n * ask for powers of two, so the cache holds at most log2(MAX_BATCH)+1 entries\n * however many rows a commit turns out to have.\n */\nfunction batchStatements(\n delegate: SQLiteDatabase,\n sqlFor: (n: number) => string,\n): (n: number) => PreparedStatement {\n const cache = new Map<number, PreparedStatement>();\n return (n: number) =>\n getOrInsertComputed(cache, n, () => delegate.prepare(sqlFor(n)));\n}\n\n/**\n * Runs `items` through `getStatement` in power-of-two sized batches, so any\n * length is covered by a handful of cached statement widths.\n */\nasync function execInBatches<T>(\n items: readonly T[],\n getStatement: (n: number) => PreparedStatement,\n toParams: (item: T, out: string[]) => void,\n): Promise<void> {\n for (let i = 0; i < items.length; ) {\n const remaining = Math.min(MAX_BATCH, items.length - i);\n const n = 1 << (31 - Math.clz32(remaining));\n const params: string[] = [];\n for (let j = 0; j < n; j++) {\n toParams(items[i + j], params);\n }\n await getStatement(n).exec(params);\n i += n;\n }\n}\n\nexport interface SQLiteStoreOptions {\n // Common options\n busyTimeout?: number;\n journalMode?: 'WAL' | 'DELETE';\n synchronous?: 'NORMAL' | 'FULL';\n readUncommitted?: boolean;\n /** Directory in which to create the SQLite file. Defaults to the process CWD. */\n directory?: string | undefined;\n}\n\n/**\n * Common database setup logic shared between expo-sqlite and op-sqlite implementations.\n * Configures SQLite pragmas, creates the entry table, and prepares common statements.\n */\n\nexport function setupDatabase(\n delegate: SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): PreparedStatements {\n // Configure SQLite pragmas for optimal performance\n delegate.execSync(`PRAGMA busy_timeout = ${opts?.busyTimeout ?? 200}`);\n delegate.execSync(`PRAGMA journal_mode = '${opts?.journalMode ?? 'WAL'}'`);\n delegate.execSync(`PRAGMA synchronous = '${opts?.synchronous ?? 'NORMAL'}'`);\n delegate.execSync(\n `PRAGMA read_uncommitted = ${Boolean(opts?.readUncommitted)}`,\n );\n\n // Create the entry table\n delegate.execSync(`\n CREATE TABLE IF NOT EXISTS entry (\n key TEXT PRIMARY KEY,\n value TEXT NOT NULL\n ) WITHOUT ROWID\n `);\n\n // Prepare common statements\n return {\n has: delegate.prepare(`SELECT 1 FROM entry WHERE key = ? LIMIT 1`),\n get: delegate.prepare('SELECT value FROM entry WHERE key = ?'),\n hasMany: delegate.prepare(\n `SELECT key FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n getMany: delegate.prepare(\n `SELECT key, value FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n put: delegate.prepare(\n `INSERT OR REPLACE INTO entry (key, value)\n SELECT e.value->>0, e.value->1 FROM json_each(?) e`,\n ),\n del: delegate.prepare(\n `DELETE FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n putN: batchStatements(\n delegate,\n n =>\n `INSERT OR REPLACE INTO entry (key, value) VALUES ${repeatList('(?,?)', n)}`,\n ),\n delN: batchStatements(\n delegate,\n n => `DELETE FROM entry WHERE key IN (${repeatList('?', n)})`,\n ),\n };\n}\n\n// Callbacks are stored as striped pairs: [resolve, reject, resolve, reject, ...]\nconst CB_STRIDE = 2;\nconst CB_RESOLVE = 0;\nconst CB_REJECT = 1;\n\ntype GetResolve = (v: ReadonlyJSONValue | undefined) => void;\ntype HasResolve = (v: boolean) => void;\ntype Reject = (e: unknown) => void;\n\nfunction rejectAll(callbacks: unknown[], e: unknown): void {\n for (let i = CB_REJECT; i < callbacks.length; i += CB_STRIDE) {\n (callbacks[i] as Reject)(e);\n }\n}\n\nfunction parseRawValue(raw: string | undefined): ReadonlyJSONValue | undefined {\n return raw === undefined\n ? undefined\n : deepFreeze(JSON.parse(raw) as ReadonlyJSONValue);\n}\n\nfunction resolveGet(\n resolve: GetResolve,\n reject: Reject,\n raw: string | undefined,\n): void {\n try {\n resolve(parseRawValue(raw));\n } catch (e) {\n reject(e);\n }\n}\n\nasync function flushGets(\n keys: string[],\n callbacks: unknown[],\n ps: PreparedStatements,\n): Promise<void> {\n let rows: unknown[][];\n try {\n rows =\n keys.length === 1\n ? await ps.get.all([keys[0]])\n : await ps.getMany.all([JSON.stringify(keys)]);\n } catch (e) {\n rejectAll(callbacks, e);\n return;\n }\n if (keys.length === 1) {\n resolveGet(\n callbacks[CB_RESOLVE] as GetResolve,\n callbacks[CB_REJECT] as Reject,\n rows[0]?.[0] as string | undefined,\n );\n return;\n }\n const resultMap = new Map(rows as [string, string][]);\n for (let i = 0; i < keys.length; i++) {\n resolveGet(\n callbacks[i * CB_STRIDE + CB_RESOLVE] as GetResolve,\n callbacks[i * CB_STRIDE + CB_REJECT] as Reject,\n resultMap.get(keys[i]),\n );\n }\n}\n\nasync function flushHas(\n keys: string[],\n callbacks: unknown[],\n ps: PreparedStatements,\n): Promise<void> {\n let rows: unknown[][];\n try {\n rows =\n keys.length === 1\n ? await ps.has.all([keys[0]])\n : await ps.hasMany.all([JSON.stringify(keys)]);\n } catch (e) {\n rejectAll(callbacks, e);\n return;\n }\n if (keys.length === 1) {\n (callbacks[CB_RESOLVE] as HasResolve)(rows.length > 0);\n return;\n }\n const existingKeys = new Set(rows.map(row => row[0] as string));\n for (let i = 0; i < keys.length; i++) {\n (callbacks[i * CB_STRIDE + CB_RESOLVE] as HasResolve)(\n existingKeys.has(keys[i]),\n );\n }\n}\n\nexport class SQLiteStoreRead implements Read {\n readonly #release: () => void;\n readonly #preparedStatements: PreparedStatements;\n #closed = false;\n #pendingGetKeys: string[] = [];\n #pendingGetCallbacks: unknown[] = [];\n #pendingHasKeys: string[] = [];\n #pendingHasCallbacks: unknown[] = [];\n #scheduled = false;\n\n constructor(release: () => void, preparedStatements: PreparedStatements) {\n this.#release = release;\n this.#preparedStatements = preparedStatements;\n }\n\n has(key: string): Promise<boolean> {\n return (\n maybeTransactionIsClosedRejection(this) ??\n new Promise((resolve, reject) => {\n this.#pendingHasKeys.push(key);\n this.#pendingHasCallbacks.push(resolve, reject);\n this.#scheduleLookup();\n })\n );\n }\n\n get(key: string): Promise<ReadonlyJSONValue | undefined> {\n return (\n maybeTransactionIsClosedRejection(this) ??\n new Promise((resolve, reject) => {\n this.#pendingGetKeys.push(key);\n this.#pendingGetCallbacks.push(resolve, reject);\n this.#scheduleLookup();\n })\n );\n }\n\n #scheduleLookup(): void {\n if (!this.#scheduled) {\n this.#scheduled = true;\n queueMicrotask(() => {\n this.#scheduled = false;\n\n const ps = this.#preparedStatements;\n const getKeys = this.#pendingGetKeys;\n this.#pendingGetKeys = [];\n const getCallbacks = this.#pendingGetCallbacks;\n this.#pendingGetCallbacks = [];\n const hasKeys = this.#pendingHasKeys;\n this.#pendingHasKeys = [];\n const hasCallbacks = this.#pendingHasCallbacks;\n this.#pendingHasCallbacks = [];\n\n if (this.#closed) {\n const e = transactionError();\n rejectAll(getCallbacks, e);\n rejectAll(hasCallbacks, e);\n return;\n }\n\n if (getKeys.length > 0) {\n void flushGets(getKeys, getCallbacks, ps);\n }\n if (hasKeys.length > 0) {\n void flushHas(hasKeys, hasCallbacks, ps);\n }\n });\n }\n }\n\n release(): void {\n if (!this.#closed) {\n this.#closed = true;\n this.#release();\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\nexport class SQLiteWrite extends WriteImplBase implements Write {\n readonly #release: () => void;\n readonly #dbDelegate: SQLiteDatabase;\n readonly #preparedStatements: PreparedStatements;\n #committed = false;\n #closed = false;\n\n constructor(\n release: () => void,\n dbDelegate: SQLiteDatabase,\n preparedStatements: PreparedStatements,\n ) {\n super(new SQLiteStoreRead(() => undefined, preparedStatements));\n this.#release = release;\n this.#dbDelegate = dbDelegate;\n this.#preparedStatements = preparedStatements;\n }\n\n async commit(): Promise<void> {\n if (this.#closed) {\n throw transactionError();\n }\n\n const deleteKeys: string[] = [];\n for (const entry of this._pending) {\n if (entry[1] === deleteSentinel) {\n deleteKeys.push(entry[0]);\n this._pending.delete(entry[0]);\n }\n }\n\n // Bind real parameters rather than serializing the whole pending set into\n // one JSON document for json_each() to parse back out. Serializing it, and\n // pushing the resulting (often megabyte-scale) string across the native\n // bridge, measured as roughly a quarter of persist on device.\n //\n // Puts and deletes use different statements over disjoint keys (deletes\n // were removed from _pending above), so they overlap. The batches *within*\n // each must not: the power-of-two split reuses a width when a commit is\n // wide enough (300 rows -> 128, 128, 32, 8, 4), and running two of those\n // concurrently would have two callers on one prepared statement — the\n // rebind-during-execute hazard described in kv/expo-sqlite/store.ts, which\n // op-sqlite has no per-statement lock to absorb.\n const putP =\n this._pending.size > 0\n ? execInBatches(\n [...this._pending] as [string, ReadonlyJSONValue][],\n this.#preparedStatements.putN,\n ([key, value], out) => {\n out.push(key, JSON.stringify(value));\n },\n )\n : undefined;\n const delP =\n deleteKeys.length > 0\n ? execInBatches(\n deleteKeys,\n this.#preparedStatements.delN,\n (key, out) => {\n out.push(key);\n },\n )\n : undefined;\n\n if (putP) await putP;\n if (delP) await delP;\n\n this.#dbDelegate.execSync('COMMIT');\n this._pending.clear();\n this.#committed = true;\n }\n\n release(): void {\n if (!this.#closed) {\n this.#closed = true;\n super.release();\n let rollbackError: unknown;\n if (!this.#committed) {\n try {\n this.#dbDelegate.execSync('ROLLBACK');\n } catch (e) {\n rollbackError = e;\n }\n }\n this.#release();\n if (rollbackError !== undefined) {\n throw rollbackError;\n }\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\ntype StoreEntry = {\n readonly lock: RWLock;\n readonly db: SQLiteDatabase;\n refCount: number;\n activeReaders: number;\n preparedStatements: PreparedStatements;\n};\n\n// Global map to share database connections between multiple store instances with the same name\nconst stores = new Map<string, StoreEntry>();\n\n/**\n * Gets an existing store entry or creates a new one if it doesn't exist.\n * This implements the shared connection pattern where multiple stores with the same\n * name share the same database connection, lock, and delegate.\n */\nfunction getOrCreateEntry(\n filename: string,\n create: (filename: string, opts?: SQLiteStoreOptions) => SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): StoreEntry {\n const entry = stores.get(filename);\n\n if (entry) {\n entry.refCount++;\n return entry;\n }\n\n const dbDelegate = create(filename, opts);\n const preparedStatements = setupDatabase(dbDelegate, opts);\n\n const lock = new RWLock();\n\n const newEntry: StoreEntry = {\n lock,\n db: dbDelegate,\n refCount: 1,\n activeReaders: 0,\n preparedStatements,\n };\n stores.set(filename, newEntry);\n return newEntry;\n}\n\n/**\n * Decrements the reference count for a shared store and cleans up resources\n * when the last reference is released.\n */\n\nfunction decrementStoreRefCount(\n filename: string,\n dbDelegate: SQLiteDatabase,\n): void {\n const entry = stores.get(filename);\n if (entry) {\n entry.refCount--;\n if (entry.refCount <= 0) {\n dbDelegate.close();\n stores.delete(filename);\n }\n }\n}\nexport function clearAllNamedStoresForTesting(): void {\n for (const entry of stores.values()) {\n entry.db.close();\n }\n stores.clear();\n}\n\nexport function dropStore(\n name: string,\n createDelegate: (\n filename: string,\n opts?: SQLiteStoreOptions,\n ) => SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): Promise<void> {\n const filename = resolveFilename(name, opts);\n const entry = stores.get(filename);\n if (entry) {\n try {\n entry.db.close();\n } catch {\n // Ignore close errors\n }\n stores.delete(filename);\n }\n\n // Create a temporary delegate to handle database deletion\n const tempDelegate = createDelegate(filename, opts);\n try {\n // we close the db before destroying it - this\n // caused an issue with expo-sqlite since it requires this\n tempDelegate.close();\n } catch {\n // Ignore close errors\n }\n try {\n tempDelegate.destroy();\n } catch {\n // Destroy errors shouldn't be fatal; the file may already be gone or locked\n }\n\n return Promise.resolve();\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sqlite-store.js","names":["#filename","#entry","#kind","#closed","#release","#preparedStatements","#pendingHasKeys","#pendingHasCallbacks","#scheduleLookup","#pendingGetKeys","#pendingGetCallbacks","#scheduled","#dbDelegate","#committed"],"sources":["../../../../../replicache/src/kv/sqlite-store.ts"],"sourcesContent":["import {RWLock} from '@rocicorp/lock';\nimport type {ReadonlyJSONValue} from '../../../shared/src/json.ts';\nimport {getOrInsertComputed} from '../../../shared/src/map.ts';\nimport {deepFreeze} from '../frozen-json.ts';\nimport type {Read, Store, Write} from './store.ts';\nimport {\n maybeTransactionIsClosedRejection,\n throwIfStoreClosed,\n transactionError,\n} from './throw-if-closed.ts';\nimport {deleteSentinel, WriteImplBase} from './write-impl-base.ts';\n\n/**\n * A SQLite prepared statement.\n *\n * `SQLiteStore` prepares one statement per SQL and shares it across all\n * concurrent readers, so implementations must make each call atomic with\n * respect to other callers of the same statement: `all()` must not let another\n * `exec()`/`all()` rebind or reset the underlying statement between executing\n * it and fetching its rows. Delegates whose native API splits execute and fetch\n * into separate round trips (e.g. expo-sqlite) must serialize per statement.\n */\nexport interface PreparedStatement {\n exec(params: string[]): Promise<void>;\n all(params: string[]): Promise<unknown[][]>;\n}\n\nexport interface SQLiteDatabase {\n /**\n * Close the database connection.\n */\n close(): void;\n\n /**\n * Destroy or delete the database (e.g. delete file).\n */\n destroy(): void;\n\n /**\n * Prepare a SQL string, returning a statement you can execute.\n * E.g. `const stmt = db.prepare(\"SELECT * FROM todos WHERE id=?\");`\n */\n prepare(sql: string): PreparedStatement;\n\n // for PRAGMA statements, schema creation and transaction control.\n execSync(sql: string): void;\n}\n\nexport type CreateSQLiteDatabase = (\n filename: string,\n opts?: SQLiteStoreOptions,\n) => SQLiteDatabase;\n\n/**\n * SQLite-based implementation of the Store interface using a configurable delegate.\n * Supports shared connections between multiple store instances with the same name,\n * providing efficient resource utilization and proper transaction isolation.\n * Uses parameterized queries for safety and performance.\n */\nexport class SQLiteStore implements Store {\n readonly #filename: string;\n readonly #entry: StoreEntry;\n readonly #kind: string;\n\n #closed = false;\n\n constructor(\n name: string,\n create: CreateSQLiteDatabase,\n opts?: SQLiteStoreOptions,\n kind = 'sqlite',\n ) {\n this.#filename = resolveFilename(name, opts);\n this.#entry = getOrCreateEntry(this.#filename, create, opts);\n this.#kind = kind;\n }\n\n get kind(): string {\n return this.#kind;\n }\n\n async read(): Promise<Read> {\n throwIfStoreClosed(this);\n\n const entry = this.#entry;\n const {db, lock, preparedStatements} = entry;\n const release = await lock.read();\n\n // Start shared read transaction if this is the first reader\n // This ensures consistent reads across all concurrent readers\n if (entry.activeReaders === 0) {\n db.execSync('BEGIN');\n }\n entry.activeReaders++;\n\n return new SQLiteStoreRead(() => {\n entry.activeReaders--;\n // Commit shared read transaction when last reader finishes\n if (entry.activeReaders === 0) {\n db.execSync('COMMIT');\n }\n release();\n }, preparedStatements);\n }\n\n async write(): Promise<Write> {\n throwIfStoreClosed(this);\n\n const {lock, db, preparedStatements} = this.#entry;\n const release = await lock.write();\n\n // At this point, RWLock guarantees no active readers\n // The last reader would have already committed the shared transaction\n\n db.execSync('BEGIN IMMEDIATE');\n\n return new SQLiteWrite(release, db, preparedStatements);\n }\n\n async close(): Promise<void> {\n if (this.#closed) {\n return;\n }\n\n const {lock, db} = this.#entry;\n // Wait for all readers and writers to finish.\n const writeRelease = await lock.write();\n\n // Handle reference counting for shared stores - only close database\n // when this is the last store instance using it\n decrementStoreRefCount(this.#filename, db);\n\n this.#closed = true;\n writeRelease();\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\nexport function safeFilename(name: string): string {\n return name.replace(/[^a-zA-Z0-9]/g, '_');\n}\n\nfunction resolveFilename(name: string, opts?: SQLiteStoreOptions): string {\n const safe = safeFilename(name);\n const dir = opts?.directory;\n return dir ? `${dir}/${safe}` : safe;\n}\n\nexport type PreparedStatements = {\n has: PreparedStatement;\n get: PreparedStatement;\n hasMany: PreparedStatement;\n getMany: PreparedStatement;\n put: PreparedStatement;\n del: PreparedStatement;\n /** Multi-row INSERT with the values bound as parameters, n rows wide. */\n putN: (n: number) => PreparedStatement;\n /** Multi-key DELETE with the keys bound as parameters, n keys wide. */\n delN: (n: number) => PreparedStatement;\n};\n\n/**\n * Widest batch we bind in one statement. SQLite's SQLITE_MAX_VARIABLE_NUMBER is\n * 32766, and a put costs two parameters per row, so this is far below the cap;\n * it exists to bound how many distinct statements we prepare and cache.\n */\nconst MAX_BATCH = 128;\n\n/** `repeatList('?', 3)` -> `'?,?,?'`. */\nfunction repeatList(item: string, n: number): string {\n return `${item},`.repeat(n).slice(0, -1);\n}\n\n/**\n * Prepares (and caches) a statement of each width on demand. Callers only ever\n * ask for powers of two, so the cache holds at most log2(MAX_BATCH)+1 entries\n * however many rows a commit turns out to have.\n */\nfunction batchStatements(\n delegate: SQLiteDatabase,\n sqlFor: (n: number) => string,\n): (n: number) => PreparedStatement {\n const cache = new Map<number, PreparedStatement>();\n return (n: number) =>\n getOrInsertComputed(cache, n, () => delegate.prepare(sqlFor(n)));\n}\n\n/**\n * Runs `items` through `getStatement` in power-of-two sized batches, so any\n * length is covered by a handful of cached statement widths.\n */\nasync function execInBatches<T>(\n items: readonly T[],\n getStatement: (n: number) => PreparedStatement,\n toParams: (item: T, out: string[]) => void,\n): Promise<void> {\n for (let i = 0; i < items.length; ) {\n const remaining = Math.min(MAX_BATCH, items.length - i);\n const n = 1 << (31 - Math.clz32(remaining));\n const params: string[] = [];\n for (let j = 0; j < n; j++) {\n toParams(items[i + j], params);\n }\n await getStatement(n).exec(params);\n i += n;\n }\n}\n\nexport interface SQLiteStoreOptions {\n // Common options\n busyTimeout?: number;\n journalMode?: 'WAL' | 'DELETE';\n synchronous?: 'NORMAL' | 'FULL';\n readUncommitted?: boolean;\n /** Directory in which to create the SQLite file. Defaults to the process CWD. */\n directory?: string | undefined;\n}\n\n/**\n * Replicache's rows are B-tree chunks, which `BTreeWrite` targets at 8-16KB, so\n * most of them are larger than a page and spill into overflow pages at either\n * page size. 8192 does not avoid overflow; it halves the number of pages each\n * chunk is split across.\n *\n * Measured with `replicache-perf/rn`, 4096 vs 8192 + mmap, change in time:\n *\n * | device | persist 1024x10000 | startup read (expo / op) |\n * | ---------------------- | ------------------ | ------------------------ |\n * | iOS simulator | -6% | -12% / -14% |\n * | Android emulator, 2GB | -1% to -2% | -19% / -8% |\n * | Pixel 6 | ~0% | ~-40% / ~0% |\n *\n * Split by pragma on iOS, page_size carries the write win (~6%) and mmap most\n * of the read win. Neither regressed anything on any device.\n */\nconst PAGE_SIZE = 8192;\n\n/** 256MB, matching op-sqlite's own key-value store. */\nconst MMAP_SIZE = 268435456;\n\n/**\n * Common database setup logic shared between expo-sqlite and op-sqlite implementations.\n * Configures SQLite pragmas, creates the entry table, and prepares common statements.\n */\n\nexport function setupDatabase(\n delegate: SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): PreparedStatements {\n // Configure SQLite pragmas for optimal performance.\n //\n // page_size MUST come first. SQLite silently ignores it once the database\n // has content or a journal mode has been set — no error, no warning, the\n // pragma just does nothing and you are left on the 4096 default. Verified:\n // issuing it after `journal_mode = WAL`, or after CREATE TABLE, leaves\n // `PRAGMA page_size` reporting 4096. Do not reorder these.\n delegate.execSync(`PRAGMA page_size = ${PAGE_SIZE}`);\n delegate.execSync(`PRAGMA busy_timeout = ${opts?.busyTimeout ?? 200}`);\n delegate.execSync(`PRAGMA journal_mode = '${opts?.journalMode ?? 'WAL'}'`);\n delegate.execSync(`PRAGMA synchronous = '${opts?.synchronous ?? 'NORMAL'}'`);\n delegate.execSync(\n `PRAGMA read_uncommitted = ${Boolean(opts?.readUncommitted)}`,\n );\n // Reads served from the mmap window rather than the pager account for most\n // of the startup-read win measured in the PAGE_SIZE comment.\n delegate.execSync(`PRAGMA mmap_size = ${MMAP_SIZE}`);\n\n // Create the entry table.\n //\n // This is deliberately a rowid table, not `WITHOUT ROWID`. A `WITHOUT ROWID`\n // table stores whole rows in an index B-tree, which keeps at most ~1/4 of a\n // page inline, and SQLite recommends it only for rows under ~1/20 of a page.\n // Our rows are 8-16KB chunks. As a rowid table the key gets a small separate\n // index and the values live in the table B-tree. On a Pixel 6 (5 rounds, on\n // top of the pragmas above) that cut startup read by 24% on both expo and\n // op, expo startup scan by 10%, and persist 1024x10000 by 4-7%.\n //\n // `key` needs an explicit NOT NULL: in a rowid table a non-INTEGER primary key\n // is only a UNIQUE index, and SQLite (a bug kept for compatibility) lets it\n // hold NULLs, several of them. `WITHOUT ROWID` enforced this implicitly.\n //\n // `IF NOT EXISTS` leaves an existing database's table as it was created, so\n // stores created before this change stay `WITHOUT ROWID` until recreated.\n delegate.execSync(`\n CREATE TABLE IF NOT EXISTS entry (\n key TEXT PRIMARY KEY NOT NULL,\n value TEXT NOT NULL\n )\n `);\n\n // Prepare common statements\n return {\n has: delegate.prepare(`SELECT 1 FROM entry WHERE key = ? LIMIT 1`),\n get: delegate.prepare('SELECT value FROM entry WHERE key = ?'),\n hasMany: delegate.prepare(\n `SELECT key FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n getMany: delegate.prepare(\n `SELECT key, value FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n put: delegate.prepare(\n `INSERT OR REPLACE INTO entry (key, value)\n SELECT e.value->>0, e.value->1 FROM json_each(?) e`,\n ),\n del: delegate.prepare(\n `DELETE FROM entry WHERE key IN (SELECT value FROM json_each(?))`,\n ),\n putN: batchStatements(\n delegate,\n n =>\n `INSERT OR REPLACE INTO entry (key, value) VALUES ${repeatList('(?,?)', n)}`,\n ),\n delN: batchStatements(\n delegate,\n n => `DELETE FROM entry WHERE key IN (${repeatList('?', n)})`,\n ),\n };\n}\n\n// Callbacks are stored as striped pairs: [resolve, reject, resolve, reject, ...]\nconst CB_STRIDE = 2;\nconst CB_RESOLVE = 0;\nconst CB_REJECT = 1;\n\ntype GetResolve = (v: ReadonlyJSONValue | undefined) => void;\ntype HasResolve = (v: boolean) => void;\ntype Reject = (e: unknown) => void;\n\nfunction rejectAll(callbacks: unknown[], e: unknown): void {\n for (let i = CB_REJECT; i < callbacks.length; i += CB_STRIDE) {\n (callbacks[i] as Reject)(e);\n }\n}\n\nfunction parseRawValue(raw: string | undefined): ReadonlyJSONValue | undefined {\n return raw === undefined\n ? undefined\n : deepFreeze(JSON.parse(raw) as ReadonlyJSONValue);\n}\n\nfunction resolveGet(\n resolve: GetResolve,\n reject: Reject,\n raw: string | undefined,\n): void {\n try {\n resolve(parseRawValue(raw));\n } catch (e) {\n reject(e);\n }\n}\n\nasync function flushGets(\n keys: string[],\n callbacks: unknown[],\n ps: PreparedStatements,\n): Promise<void> {\n let rows: unknown[][];\n try {\n rows =\n keys.length === 1\n ? await ps.get.all([keys[0]])\n : await ps.getMany.all([JSON.stringify(keys)]);\n } catch (e) {\n rejectAll(callbacks, e);\n return;\n }\n if (keys.length === 1) {\n resolveGet(\n callbacks[CB_RESOLVE] as GetResolve,\n callbacks[CB_REJECT] as Reject,\n rows[0]?.[0] as string | undefined,\n );\n return;\n }\n const resultMap = new Map(rows as [string, string][]);\n for (let i = 0; i < keys.length; i++) {\n resolveGet(\n callbacks[i * CB_STRIDE + CB_RESOLVE] as GetResolve,\n callbacks[i * CB_STRIDE + CB_REJECT] as Reject,\n resultMap.get(keys[i]),\n );\n }\n}\n\nasync function flushHas(\n keys: string[],\n callbacks: unknown[],\n ps: PreparedStatements,\n): Promise<void> {\n let rows: unknown[][];\n try {\n rows =\n keys.length === 1\n ? await ps.has.all([keys[0]])\n : await ps.hasMany.all([JSON.stringify(keys)]);\n } catch (e) {\n rejectAll(callbacks, e);\n return;\n }\n if (keys.length === 1) {\n (callbacks[CB_RESOLVE] as HasResolve)(rows.length > 0);\n return;\n }\n const existingKeys = new Set(rows.map(row => row[0] as string));\n for (let i = 0; i < keys.length; i++) {\n (callbacks[i * CB_STRIDE + CB_RESOLVE] as HasResolve)(\n existingKeys.has(keys[i]),\n );\n }\n}\n\nexport class SQLiteStoreRead implements Read {\n readonly #release: () => void;\n readonly #preparedStatements: PreparedStatements;\n #closed = false;\n #pendingGetKeys: string[] = [];\n #pendingGetCallbacks: unknown[] = [];\n #pendingHasKeys: string[] = [];\n #pendingHasCallbacks: unknown[] = [];\n #scheduled = false;\n\n constructor(release: () => void, preparedStatements: PreparedStatements) {\n this.#release = release;\n this.#preparedStatements = preparedStatements;\n }\n\n has(key: string): Promise<boolean> {\n return (\n maybeTransactionIsClosedRejection(this) ??\n new Promise((resolve, reject) => {\n this.#pendingHasKeys.push(key);\n this.#pendingHasCallbacks.push(resolve, reject);\n this.#scheduleLookup();\n })\n );\n }\n\n get(key: string): Promise<ReadonlyJSONValue | undefined> {\n return (\n maybeTransactionIsClosedRejection(this) ??\n new Promise((resolve, reject) => {\n this.#pendingGetKeys.push(key);\n this.#pendingGetCallbacks.push(resolve, reject);\n this.#scheduleLookup();\n })\n );\n }\n\n #scheduleLookup(): void {\n if (!this.#scheduled) {\n this.#scheduled = true;\n queueMicrotask(() => {\n this.#scheduled = false;\n\n const ps = this.#preparedStatements;\n const getKeys = this.#pendingGetKeys;\n this.#pendingGetKeys = [];\n const getCallbacks = this.#pendingGetCallbacks;\n this.#pendingGetCallbacks = [];\n const hasKeys = this.#pendingHasKeys;\n this.#pendingHasKeys = [];\n const hasCallbacks = this.#pendingHasCallbacks;\n this.#pendingHasCallbacks = [];\n\n if (this.#closed) {\n const e = transactionError();\n rejectAll(getCallbacks, e);\n rejectAll(hasCallbacks, e);\n return;\n }\n\n if (getKeys.length > 0) {\n void flushGets(getKeys, getCallbacks, ps);\n }\n if (hasKeys.length > 0) {\n void flushHas(hasKeys, hasCallbacks, ps);\n }\n });\n }\n }\n\n release(): void {\n if (!this.#closed) {\n this.#closed = true;\n this.#release();\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\nexport class SQLiteWrite extends WriteImplBase implements Write {\n readonly #release: () => void;\n readonly #dbDelegate: SQLiteDatabase;\n readonly #preparedStatements: PreparedStatements;\n #committed = false;\n #closed = false;\n\n constructor(\n release: () => void,\n dbDelegate: SQLiteDatabase,\n preparedStatements: PreparedStatements,\n ) {\n super(new SQLiteStoreRead(() => undefined, preparedStatements));\n this.#release = release;\n this.#dbDelegate = dbDelegate;\n this.#preparedStatements = preparedStatements;\n }\n\n async commit(): Promise<void> {\n if (this.#closed) {\n throw transactionError();\n }\n\n const deleteKeys: string[] = [];\n for (const entry of this._pending) {\n if (entry[1] === deleteSentinel) {\n deleteKeys.push(entry[0]);\n this._pending.delete(entry[0]);\n }\n }\n\n // Bind real parameters rather than serializing the whole pending set into\n // one JSON document for json_each() to parse back out. Serializing it, and\n // pushing the resulting (often megabyte-scale) string across the native\n // bridge, measured as roughly a quarter of persist on device.\n //\n // Puts and deletes use different statements over disjoint keys (deletes\n // were removed from _pending above), so they overlap. The batches *within*\n // each must not: the power-of-two split reuses a width when a commit is\n // wide enough (300 rows -> 128, 128, 32, 8, 4), and running two of those\n // concurrently would have two callers on one prepared statement — the\n // rebind-during-execute hazard described in kv/expo-sqlite/store.ts, which\n // op-sqlite has no per-statement lock to absorb.\n const putP =\n this._pending.size > 0\n ? execInBatches(\n [...this._pending] as [string, ReadonlyJSONValue][],\n this.#preparedStatements.putN,\n ([key, value], out) => {\n out.push(key, JSON.stringify(value));\n },\n )\n : undefined;\n const delP =\n deleteKeys.length > 0\n ? execInBatches(\n deleteKeys,\n this.#preparedStatements.delN,\n (key, out) => {\n out.push(key);\n },\n )\n : undefined;\n\n if (putP) await putP;\n if (delP) await delP;\n\n this.#dbDelegate.execSync('COMMIT');\n this._pending.clear();\n this.#committed = true;\n }\n\n release(): void {\n if (!this.#closed) {\n this.#closed = true;\n super.release();\n let rollbackError: unknown;\n if (!this.#committed) {\n try {\n this.#dbDelegate.execSync('ROLLBACK');\n } catch (e) {\n rollbackError = e;\n }\n }\n this.#release();\n if (rollbackError !== undefined) {\n throw rollbackError;\n }\n }\n }\n\n get closed(): boolean {\n return this.#closed;\n }\n}\n\ntype StoreEntry = {\n readonly lock: RWLock;\n readonly db: SQLiteDatabase;\n refCount: number;\n activeReaders: number;\n preparedStatements: PreparedStatements;\n};\n\n// Global map to share database connections between multiple store instances with the same name\nconst stores = new Map<string, StoreEntry>();\n\n/**\n * Gets an existing store entry or creates a new one if it doesn't exist.\n * This implements the shared connection pattern where multiple stores with the same\n * name share the same database connection, lock, and delegate.\n */\nfunction getOrCreateEntry(\n filename: string,\n create: (filename: string, opts?: SQLiteStoreOptions) => SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): StoreEntry {\n const entry = stores.get(filename);\n\n if (entry) {\n entry.refCount++;\n return entry;\n }\n\n const dbDelegate = create(filename, opts);\n const preparedStatements = setupDatabase(dbDelegate, opts);\n\n const lock = new RWLock();\n\n const newEntry: StoreEntry = {\n lock,\n db: dbDelegate,\n refCount: 1,\n activeReaders: 0,\n preparedStatements,\n };\n stores.set(filename, newEntry);\n return newEntry;\n}\n\n/**\n * Decrements the reference count for a shared store and cleans up resources\n * when the last reference is released.\n */\n\nfunction decrementStoreRefCount(\n filename: string,\n dbDelegate: SQLiteDatabase,\n): void {\n const entry = stores.get(filename);\n if (entry) {\n entry.refCount--;\n if (entry.refCount <= 0) {\n dbDelegate.close();\n stores.delete(filename);\n }\n }\n}\nexport function clearAllNamedStoresForTesting(): void {\n for (const entry of stores.values()) {\n entry.db.close();\n }\n stores.clear();\n}\n\nexport function dropStore(\n name: string,\n createDelegate: (\n filename: string,\n opts?: SQLiteStoreOptions,\n ) => SQLiteDatabase,\n opts?: SQLiteStoreOptions,\n): Promise<void> {\n const filename = resolveFilename(name, opts);\n const entry = stores.get(filename);\n if (entry) {\n try {\n entry.db.close();\n } catch {\n // Ignore close errors\n }\n stores.delete(filename);\n }\n\n // Create a temporary delegate to handle database deletion\n const tempDelegate = createDelegate(filename, opts);\n try {\n // we close the db before destroying it - this\n // caused an issue with expo-sqlite since it requires this\n tempDelegate.close();\n } catch {\n // Ignore close errors\n }\n try {\n tempDelegate.destroy();\n } catch {\n // Destroy errors shouldn't be fatal; the file may already be gone or locked\n }\n\n return Promise.resolve();\n}\n"],"mappings":"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"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { assert } from "../../../shared/src/asserts.js";
|
|
2
|
+
import { getOrInsert, getOrInsertComputed } from "../../../shared/src/map.js";
|
|
2
3
|
import { DEFAULT_HEAD_NAME, assertSnapshotCommitDD31, baseSnapshotFromCommit, commitFromHash, commitFromHead, compareCookiesForSnapshots, localMutationsDD31, localMutationsGreaterThan } from "../db/commit.js";
|
|
3
4
|
import { withRead, withWrite } from "../with-transactions.js";
|
|
4
5
|
import { getClientGroup, setClientGroup } from "./client-groups.js";
|
|
5
6
|
import { assertClientV6, assertHasClientState, getClientGroupIDForClient, mustGetClient, setClient } from "./clients.js";
|
|
6
7
|
import { rebaseMutationAndPutCommit } from "../db/rebase.js";
|
|
7
|
-
import { getOrInsert, getOrInsertComputed } from "../../../shared/src/map.js";
|
|
8
8
|
import { GatherMemoryOnlyVisitor } from "./gather-mem-only-visitor.js";
|
|
9
9
|
//#region ../replicache/src/persist/persist.ts
|
|
10
10
|
/**
|
|
@@ -6,5 +6,10 @@ export declare function defined<T>(arr: (T | undefined)[]): T[];
|
|
|
6
6
|
export declare function areEqual<T>(arr1: readonly T[], arr2: readonly T[]): boolean;
|
|
7
7
|
export declare function zip<T1, T2>(a1: readonly T1[], a2: readonly T2[]): [T1, T2][];
|
|
8
8
|
export declare function last<T>(arr: T[]): T | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Creates an empty array. Pass this to `getOrInsertComputed` instead of an
|
|
11
|
+
* inline `() => []` so that a hit does not allocate a closure.
|
|
12
|
+
*/
|
|
13
|
+
export declare const newArray: <T>() => T[];
|
|
9
14
|
export declare function groupBy<T, K>(arr: readonly T[], keyFn: (el: T) => K): Map<K, T[]>;
|
|
10
15
|
//# sourceMappingURL=arrays.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arrays.d.ts","sourceRoot":"","sources":["../../../../shared/src/arrays.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"arrays.d.ts","sourceRoot":"","sources":["../../../../shared/src/arrays.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,OAAO,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC,EAAE,GAAG,CAAC,EAAE,CActD;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,SAAS,CAAC,EAAE,GAAG,OAAO,CAE3E;AAED,wBAAgB,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAO5E;AAED,wBAAgB,IAAI,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,CAAC,GAAG,SAAS,CAE/C;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,OAAK,CAAC,EAAQ,CAAC;AAEzC,wBAAgB,OAAO,CAAC,CAAC,EAAE,CAAC,EAC1B,GAAG,EAAE,SAAS,CAAC,EAAE,EACjB,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,GAClB,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAOb"}
|
package/out/shared/src/arrays.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assert } from "./asserts.js";
|
|
2
|
+
import { getOrInsertComputed } from "./map.js";
|
|
2
3
|
//#region ../shared/src/arrays.ts
|
|
3
4
|
function areEqual(arr1, arr2) {
|
|
4
5
|
return arr1.length === arr2.length && arr1.every((e, i) => e === arr2[i]);
|
|
@@ -12,20 +13,17 @@ function zip(a1, a2) {
|
|
|
12
13
|
function last(arr) {
|
|
13
14
|
return arr.at(-1);
|
|
14
15
|
}
|
|
16
|
+
/**
|
|
17
|
+
* Creates an empty array. Pass this to `getOrInsertComputed` instead of an
|
|
18
|
+
* inline `() => []` so that a hit does not allocate a closure.
|
|
19
|
+
*/
|
|
20
|
+
var newArray = () => [];
|
|
15
21
|
function groupBy(arr, keyFn) {
|
|
16
22
|
const groups = /* @__PURE__ */ new Map();
|
|
17
|
-
for (const el of arr)
|
|
18
|
-
const key = keyFn(el);
|
|
19
|
-
let group = groups.get(key);
|
|
20
|
-
if (group === void 0) {
|
|
21
|
-
group = [];
|
|
22
|
-
groups.set(key, group);
|
|
23
|
-
}
|
|
24
|
-
group.push(el);
|
|
25
|
-
}
|
|
23
|
+
for (const el of arr) getOrInsertComputed(groups, keyFn(el), newArray).push(el);
|
|
26
24
|
return groups;
|
|
27
25
|
}
|
|
28
26
|
//#endregion
|
|
29
|
-
export { areEqual, groupBy, last, zip };
|
|
27
|
+
export { areEqual, groupBy, last, newArray, zip };
|
|
30
28
|
|
|
31
29
|
//# sourceMappingURL=arrays.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"arrays.js","names":[],"sources":["../../../../shared/src/arrays.ts"],"sourcesContent":["import {assert} from './asserts.ts';\n\n/**\n * Returns `arr` as is if none of the elements are `undefined`.\n * Otherwise returns a new array with only defined elements in `arr`.\n */\nexport function defined<T>(arr: (T | undefined)[]): T[] {\n // avoid an array copy if possible\n let i = arr.findIndex(x => x === undefined);\n if (i < 0) {\n return arr as T[];\n }\n const defined: T[] = arr.slice(0, i) as T[];\n for (i++; i < arr.length; i++) {\n const x = arr[i];\n if (x !== undefined) {\n defined.push(x);\n }\n }\n return defined;\n}\n\nexport function areEqual<T>(arr1: readonly T[], arr2: readonly T[]): boolean {\n return arr1.length === arr2.length && arr1.every((e, i) => e === arr2[i]);\n}\n\nexport function zip<T1, T2>(a1: readonly T1[], a2: readonly T2[]): [T1, T2][] {\n assert(a1.length === a2.length, 'zip: arrays must have equal length');\n const result: [T1, T2][] = [];\n for (let i = 0; i < a1.length; i++) {\n result.push([a1[i], a2[i]]);\n }\n return result;\n}\n\nexport function last<T>(arr: T[]): T | undefined {\n return arr.at(-1);\n}\n\nexport function groupBy<T, K>(\n arr: readonly T[],\n keyFn: (el: T) => K,\n): Map<K, T[]> {\n const groups = new Map<K, T[]>();\n for (const el of arr) {\n const key = keyFn(el);\n
|
|
1
|
+
{"version":3,"file":"arrays.js","names":[],"sources":["../../../../shared/src/arrays.ts"],"sourcesContent":["import {assert} from './asserts.ts';\nimport {getOrInsertComputed} from './map.ts';\n\n/**\n * Returns `arr` as is if none of the elements are `undefined`.\n * Otherwise returns a new array with only defined elements in `arr`.\n */\nexport function defined<T>(arr: (T | undefined)[]): T[] {\n // avoid an array copy if possible\n let i = arr.findIndex(x => x === undefined);\n if (i < 0) {\n return arr as T[];\n }\n const defined: T[] = arr.slice(0, i) as T[];\n for (i++; i < arr.length; i++) {\n const x = arr[i];\n if (x !== undefined) {\n defined.push(x);\n }\n }\n return defined;\n}\n\nexport function areEqual<T>(arr1: readonly T[], arr2: readonly T[]): boolean {\n return arr1.length === arr2.length && arr1.every((e, i) => e === arr2[i]);\n}\n\nexport function zip<T1, T2>(a1: readonly T1[], a2: readonly T2[]): [T1, T2][] {\n assert(a1.length === a2.length, 'zip: arrays must have equal length');\n const result: [T1, T2][] = [];\n for (let i = 0; i < a1.length; i++) {\n result.push([a1[i], a2[i]]);\n }\n return result;\n}\n\nexport function last<T>(arr: T[]): T | undefined {\n return arr.at(-1);\n}\n\n/**\n * Creates an empty array. Pass this to `getOrInsertComputed` instead of an\n * inline `() => []` so that a hit does not allocate a closure.\n */\nexport const newArray = <T>(): T[] => [];\n\nexport function groupBy<T, K>(\n arr: readonly T[],\n keyFn: (el: T) => K,\n): Map<K, T[]> {\n const groups = new Map<K, T[]>();\n for (const el of arr) {\n const key = keyFn(el);\n getOrInsertComputed(groups, key, newArray).push(el);\n }\n return groups;\n}\n"],"mappings":";;;AAuBA,SAAgB,SAAY,MAAoB,MAA6B;CAC3E,OAAO,KAAK,WAAW,KAAK,UAAU,KAAK,OAAO,GAAG,MAAM,MAAM,KAAK,EAAE;AAC1E;AAEA,SAAgB,IAAY,IAAmB,IAA+B;CAC5E,OAAO,GAAG,WAAW,GAAG,QAAQ,oCAAoC;CACpE,MAAM,SAAqB,CAAC;CAC5B,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,QAAQ,KAC7B,OAAO,KAAK,CAAC,GAAG,IAAI,GAAG,EAAE,CAAC;CAE5B,OAAO;AACT;AAEA,SAAgB,KAAQ,KAAyB;CAC/C,OAAO,IAAI,GAAG,EAAE;AAClB;;;;;AAMA,IAAa,iBAAyB,CAAC;AAEvC,SAAgB,QACd,KACA,OACa;CACb,MAAM,yBAAS,IAAI,IAAY;CAC/B,KAAK,MAAM,MAAM,KAEf,oBAAoB,QADR,MAAM,EACU,GAAK,QAAQ,EAAE,KAAK,EAAE;CAEpD,OAAO;AACT"}
|
package/out/shared/src/map.d.ts
CHANGED
|
@@ -3,9 +3,26 @@
|
|
|
3
3
|
* is excluded because these helpers use `get() !== undefined` to detect absence.
|
|
4
4
|
*/
|
|
5
5
|
type Defined = {} | null;
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
/**
|
|
7
|
+
* A {@link Map} or a {@link WeakMap}. The ES2026 proposal adds `getOrInsert`
|
|
8
|
+
* and `getOrInsertComputed` to both.
|
|
9
|
+
*/
|
|
10
|
+
type MapOrWeakMap<K, V> = Map<K, V> | WeakMap<K & WeakKey, V>;
|
|
11
|
+
declare function getOrInsertNative<K, V extends Defined>(map: MapOrWeakMap<K, V>, key: K, defaultValue: NoInfer<V>): V;
|
|
12
|
+
declare function getOrInsertComputedNative<K, V extends Defined>(map: MapOrWeakMap<K, V>, key: K, compute: (key: K) => NoInfer<V>): V;
|
|
8
13
|
export declare const getOrInsert: typeof getOrInsertNative;
|
|
9
14
|
export declare const getOrInsertComputed: typeof getOrInsertComputedNative;
|
|
15
|
+
/**
|
|
16
|
+
* Creates an empty {@link Map}. Pass this to {@link getOrInsertComputed}
|
|
17
|
+
* instead of an inline `() => new Map()` so that a hit does not allocate a
|
|
18
|
+
* closure.
|
|
19
|
+
*/
|
|
20
|
+
export declare const newMap: <K, V>() => Map<K, V>;
|
|
21
|
+
/**
|
|
22
|
+
* Creates an empty {@link WeakMap}. Pass this to {@link getOrInsertComputed}
|
|
23
|
+
* instead of an inline `() => new WeakMap()` so that a hit does not allocate a
|
|
24
|
+
* closure.
|
|
25
|
+
*/
|
|
26
|
+
export declare const newWeakMap: <K extends WeakKey, V>() => WeakMap<K, V>;
|
|
10
27
|
export {};
|
|
11
28
|
//# sourceMappingURL=map.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../../../shared/src/map.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,KAAK,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"map.d.ts","sourceRoot":"","sources":["../../../../shared/src/map.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,KAAK,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC;AAEzB;;;GAGG;AACH,KAAK,YAAY,CAAC,CAAC,EAAE,CAAC,IAAI,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,CAAC,CAAC;AAwD9D,iBAAS,iBAAiB,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,EAC7C,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,EACvB,GAAG,EAAE,CAAC,EACN,YAAY,EAAE,OAAO,CAAC,CAAC,CAAC,GACvB,CAAC,CAEH;AAED,iBAAS,yBAAyB,CAAC,CAAC,EAAE,CAAC,SAAS,OAAO,EACrD,GAAG,EAAE,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,EACvB,GAAG,EAAE,CAAC,EACN,OAAO,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,GAC9B,CAAC,CAEH;AAED,eAAO,MAAM,WAAW,0BAED,CAAC;AAExB,eAAO,MAAM,mBAAmB,kCAED,CAAC;AAEhC;;;;GAIG;AACH,eAAO,MAAM,MAAM,GAAI,CAAC,EAAE,CAAC,OAAK,GAAG,CAAC,CAAC,EAAE,CAAC,CAAc,CAAC;AAEvD;;;;GAIG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,OAAO,EAAE,CAAC,OAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CACjD,CAAC"}
|
package/out/shared/src/map.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
//#region ../shared/src/map.ts
|
|
2
|
-
|
|
2
|
+
function hasNative(name) {
|
|
3
|
+
return typeof Map.prototype[name] === "function" && typeof WeakMap.prototype[name] === "function";
|
|
4
|
+
}
|
|
3
5
|
/**
|
|
4
6
|
* Returns the value for {@link key} in {@link map}. If no mapping exists,
|
|
5
7
|
* inserts {@link defaultValue} and returns it.
|
|
@@ -31,9 +33,21 @@ function getOrInsertNative(map, key, defaultValue) {
|
|
|
31
33
|
function getOrInsertComputedNative(map, key, compute) {
|
|
32
34
|
return map.getOrInsertComputed(key, compute);
|
|
33
35
|
}
|
|
34
|
-
var getOrInsert =
|
|
35
|
-
var getOrInsertComputed =
|
|
36
|
+
var getOrInsert = hasNative("getOrInsert") ? getOrInsertNative : getOrInsertPolyfill;
|
|
37
|
+
var getOrInsertComputed = hasNative("getOrInsertComputed") ? getOrInsertComputedNative : getOrInsertComputedPolyfill;
|
|
38
|
+
/**
|
|
39
|
+
* Creates an empty {@link Map}. Pass this to {@link getOrInsertComputed}
|
|
40
|
+
* instead of an inline `() => new Map()` so that a hit does not allocate a
|
|
41
|
+
* closure.
|
|
42
|
+
*/
|
|
43
|
+
var newMap = () => /* @__PURE__ */ new Map();
|
|
44
|
+
/**
|
|
45
|
+
* Creates an empty {@link WeakMap}. Pass this to {@link getOrInsertComputed}
|
|
46
|
+
* instead of an inline `() => new WeakMap()` so that a hit does not allocate a
|
|
47
|
+
* closure.
|
|
48
|
+
*/
|
|
49
|
+
var newWeakMap = () => /* @__PURE__ */ new WeakMap();
|
|
36
50
|
//#endregion
|
|
37
|
-
export { getOrInsert, getOrInsertComputed };
|
|
51
|
+
export { getOrInsert, getOrInsertComputed, newMap, newWeakMap };
|
|
38
52
|
|
|
39
53
|
//# sourceMappingURL=map.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"map.js","names":[],"sources":["../../../../shared/src/map.ts"],"sourcesContent":["/**\n * A value that {@link Map.get} can distinguish from a missing entry. `undefined`\n * is excluded because these helpers use `get() !== undefined` to detect absence.\n */\ntype Defined = {} | null;\n\
|
|
1
|
+
{"version":3,"file":"map.js","names":[],"sources":["../../../../shared/src/map.ts"],"sourcesContent":["/**\n * A value that {@link Map.get} can distinguish from a missing entry. `undefined`\n * is excluded because these helpers use `get() !== undefined` to detect absence.\n */\ntype Defined = {} | null;\n\n/**\n * A {@link Map} or a {@link WeakMap}. The ES2026 proposal adds `getOrInsert`\n * and `getOrInsertComputed` to both.\n */\ntype MapOrWeakMap<K, V> = Map<K, V> | WeakMap<K & WeakKey, V>;\n\nfunction hasNative(name: keyof MapES2026<unknown, Defined>): boolean {\n return (\n typeof (Map.prototype as unknown as MapES2026<unknown, Defined>)[name] ===\n 'function' &&\n typeof (WeakMap.prototype as unknown as MapES2026<WeakKey, Defined>)[\n name\n ] === 'function'\n );\n}\n\ninterface MapES2026<K, V> {\n getOrInsert(key: K, defaultValue: V): V;\n getOrInsertComputed(key: K, compute: (key: K) => V): V;\n}\n\n/**\n * Returns the value for {@link key} in {@link map}. If no mapping exists,\n * inserts {@link defaultValue} and returns it.\n *\n * Mirrors the ES2026 `Map.prototype.getOrInsert` proposal.\n */\nfunction getOrInsertPolyfill<K, V extends Defined>(\n map: MapOrWeakMap<K, V>,\n key: K,\n defaultValue: NoInfer<V>,\n): V {\n const existing = map.get(key as K & WeakKey);\n if (existing !== undefined) {\n return existing;\n }\n map.set(key as K & WeakKey, defaultValue);\n return defaultValue;\n}\n\n/**\n * Returns the value for {@link key} in {@link map}. If no mapping exists,\n * calls {@link compute} with the key, inserts the result, and returns it.\n *\n * Mirrors the ES2026 `Map.prototype.getOrInsertComputed` proposal.\n */\nfunction getOrInsertComputedPolyfill<K, V extends Defined>(\n map: MapOrWeakMap<K, V>,\n key: K,\n compute: (key: K) => NoInfer<V>,\n): V {\n const existing = map.get(key as K & WeakKey);\n if (existing !== undefined) {\n return existing;\n }\n const value = compute(key);\n map.set(key as K & WeakKey, value);\n return value;\n}\n\nfunction getOrInsertNative<K, V extends Defined>(\n map: MapOrWeakMap<K, V>,\n key: K,\n defaultValue: NoInfer<V>,\n): V {\n return (map as unknown as MapES2026<K, V>).getOrInsert(key, defaultValue);\n}\n\nfunction getOrInsertComputedNative<K, V extends Defined>(\n map: MapOrWeakMap<K, V>,\n key: K,\n compute: (key: K) => NoInfer<V>,\n): V {\n return (map as unknown as MapES2026<K, V>).getOrInsertComputed(key, compute);\n}\n\nexport const getOrInsert = hasNative('getOrInsert')\n ? getOrInsertNative\n : getOrInsertPolyfill;\n\nexport const getOrInsertComputed = hasNative('getOrInsertComputed')\n ? getOrInsertComputedNative\n : getOrInsertComputedPolyfill;\n\n/**\n * Creates an empty {@link Map}. Pass this to {@link getOrInsertComputed}\n * instead of an inline `() => new Map()` so that a hit does not allocate a\n * closure.\n */\nexport const newMap = <K, V>(): Map<K, V> => new Map();\n\n/**\n * Creates an empty {@link WeakMap}. Pass this to {@link getOrInsertComputed}\n * instead of an inline `() => new WeakMap()` so that a hit does not allocate a\n * closure.\n */\nexport const newWeakMap = <K extends WeakKey, V>(): WeakMap<K, V> =>\n new WeakMap();\n"],"mappings":";AAYA,SAAS,UAAU,MAAkD;CACnE,OACE,OAAQ,IAAI,UAAqD,UAC/D,cACF,OAAQ,QAAQ,UACd,UACI;AAEV;;;;;;;AAaA,SAAS,oBACP,KACA,KACA,cACG;CACH,MAAM,WAAW,IAAI,IAAI,GAAkB;CAC3C,IAAI,aAAa,KAAA,GACf,OAAO;CAET,IAAI,IAAI,KAAoB,YAAY;CACxC,OAAO;AACT;;;;;;;AAQA,SAAS,4BACP,KACA,KACA,SACG;CACH,MAAM,WAAW,IAAI,IAAI,GAAkB;CAC3C,IAAI,aAAa,KAAA,GACf,OAAO;CAET,MAAM,QAAQ,QAAQ,GAAG;CACzB,IAAI,IAAI,KAAoB,KAAK;CACjC,OAAO;AACT;AAEA,SAAS,kBACP,KACA,KACA,cACG;CACH,OAAQ,IAAmC,YAAY,KAAK,YAAY;AAC1E;AAEA,SAAS,0BACP,KACA,KACA,SACG;CACH,OAAQ,IAAmC,oBAAoB,KAAK,OAAO;AAC7E;AAEA,IAAa,cAAc,UAAU,aAAa,IAC9C,oBACA;AAEJ,IAAa,sBAAsB,UAAU,qBAAqB,IAC9D,4BACA;;;;;;AAOJ,IAAa,+BAAgC,IAAI,IAAI;;;;;;AAOrD,IAAa,mCACX,IAAI,QAAQ"}
|
package/out/z2s/src/compiler.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { assert, unreachable } from "../../shared/src/asserts.js";
|
|
2
2
|
import { hasOwn } from "../../shared/src/has-own.js";
|
|
3
3
|
import "../../shared/src/json.js";
|
|
4
|
+
import { last, zip } from "../../shared/src/arrays.js";
|
|
4
5
|
import { must } from "../../shared/src/must.js";
|
|
5
6
|
import { clientToServer } from "../../zero-schema/src/name-mapper.js";
|
|
6
7
|
import { completeOrdering } from "../../zql/src/query/complete-ordering.js";
|
|
7
|
-
import { last, zip } from "../../shared/src/arrays.js";
|
|
8
8
|
import { parse } from "../../shared/src/bigint-json.js";
|
|
9
9
|
import { sql, sqlConvertColumnArg, sqlConvertPluralLiteralArg, sqlConvertSingularLiteralArg } from "./sql.js";
|
|
10
10
|
//#region ../z2s/src/compiler.ts
|
package/out/z2s/src/sql.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../../../z2s/src/sql.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAwB,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AACpE,OAAO,GAAkB,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../../../z2s/src/sql.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAwB,QAAQ,EAAC,MAAM,gBAAgB,CAAC;AACpE,OAAO,GAAkB,MAAM,gBAAgB,CAAC;AAUhD,OAAO,KAAK,EAAC,kBAAkB,EAAC,MAAM,uCAAuC,CAAC;AAE9E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,QAAQ;UAmR9B,MAAM;YACJ,OAAO,EAAE;EAjRlB;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,QAAQ;UA8Q7C,MAAM;YACJ,OAAO,EAAE;EA5QlB;AAED,wBAAgB,YAAY,CAAC,GAAG,EAAE,QAAQ;UAyQlC,MAAM;YACJ,OAAO,EAAE;EAvQlB;AAID,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AACnE,MAAM,MAAM,iBAAiB,GAAG,OAAO,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;AAwB7D,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,IAAI,GACtC,QAAQ,CAQV;AAED,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,iBAAiB,EACvB,KAAK,EAAE,iBAAiB,EAAE,GACzB,QAAQ,CAQV;AAED,wBAAgB,mBAAmB,CACjC,kBAAkB,EAAE,kBAAkB,EACtC,KAAK,EAAE,OAAO,EACd,MAAM,EAAE,OAAO,EACf,YAAY,EAAE,OAAO,GACpB,QAAQ,CASV;AAwLD,OAAO,EAAC,GAAG,EAAC,CAAC"}
|
package/out/z2s/src/sql.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { assert, unreachable } from "../../shared/src/asserts.js";
|
|
2
|
+
import { getOrInsertComputed } from "../../shared/src/map.js";
|
|
2
3
|
import { isPgNativeStringType, isPgNumberType, isPgStringType, isPgTextRepresentedType } from "../../zero-cache/src/types/pg-data-type.js";
|
|
3
4
|
import { escapePostgresIdentifier } from "@databases/escape-identifier";
|
|
4
5
|
import sql, { SQLItemType } from "@databases/sql";
|
|
@@ -160,8 +161,7 @@ function formatFn(items, { escapeIdentifier, formatValue }) {
|
|
|
160
161
|
const names = item.names.length === 1 && typeof item.names[0] === "string" && item.names[0].includes(".") ? item.names[0].split(".") : item.names;
|
|
161
162
|
text += names.map((name) => {
|
|
162
163
|
if (typeof name === "string") return escapeIdentifier(name);
|
|
163
|
-
|
|
164
|
-
return escapeIdentifier(localIdentifiers.get(name));
|
|
164
|
+
return escapeIdentifier(getOrInsertComputed(localIdentifiers, name, () => `__local_${localIdentifiers.size}__`));
|
|
165
165
|
}).join(".");
|
|
166
166
|
break;
|
|
167
167
|
}
|
package/out/z2s/src/sql.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sql.js","names":["#seen","#size"],"sources":["../../../../z2s/src/sql.ts"],"sourcesContent":["import {\n escapePostgresIdentifier,\n escapeSQLiteIdentifier,\n} from '@databases/escape-identifier';\nimport type {FormatConfig, SQLItem, SQLQuery} from '@databases/sql';\nimport sql, {SQLItemType} from '@databases/sql';\nimport {assert, unreachable} from '../../shared/src/asserts.ts';\nimport {\n isPgNativeStringType,\n isPgNumberType,\n isPgStringType,\n isPgTextRepresentedType,\n} from '../../zero-cache/src/types/pg-data-type.ts';\nimport type {LiteralValue} from '../../zero-protocol/src/ast.ts';\nimport type {ServerColumnSchema} from '../../zero-types/src/server-schema.ts';\n\nexport function formatPg(sql: SQLQuery) {\n const format = new ReusingFormat(escapePostgresIdentifier);\n return sql.format((items: readonly SQLItem[]) => formatFn(items, format));\n}\n\nexport function formatPgInternalConvert(sql: SQLQuery) {\n const format = new SQLConvertFormat(escapePostgresIdentifier);\n return sql.format((items: readonly SQLItem[]) => formatFn(items, format));\n}\n\nexport function formatSqlite(sql: SQLQuery) {\n const format = new ReusingFormat(escapeSQLiteIdentifier);\n return sql.format((items: readonly SQLItem[]) => formatFn(items, format));\n}\n\nconst sqlConvert = Symbol('fromJson');\n\nexport type LiteralType = 'boolean' | 'number' | 'string' | 'null';\nexport type PluralLiteralType = Exclude<LiteralType, 'null'>;\n\ntype ColumnSqlConvertArg = {\n [sqlConvert]: 'column';\n type: string;\n value: unknown;\n plural: boolean;\n isEnum: boolean;\n isComparison: boolean;\n};\n\ntype SqlConvertArg =\n | ColumnSqlConvertArg\n | {\n [sqlConvert]: 'literal';\n type: LiteralType;\n value: LiteralValue;\n plural: boolean;\n };\n\nfunction isSqlConvert(value: unknown): value is SqlConvertArg {\n return value !== null && typeof value === 'object' && sqlConvert in value;\n}\n\nexport function sqlConvertSingularLiteralArg(\n value: string | boolean | number | null,\n): SQLQuery {\n const arg: SqlConvertArg = {\n [sqlConvert]: 'literal',\n type: value === null ? 'null' : (typeof value as LiteralType),\n value,\n plural: false,\n };\n return sql.value(arg);\n}\n\nexport function sqlConvertPluralLiteralArg(\n type: PluralLiteralType,\n value: PluralLiteralType[],\n): SQLQuery {\n const arg: SqlConvertArg = {\n [sqlConvert]: 'literal',\n type,\n value,\n plural: true,\n };\n return sql.value(arg);\n}\n\nexport function sqlConvertColumnArg(\n serverColumnSchema: ServerColumnSchema,\n value: unknown,\n plural: boolean,\n isComparison: boolean,\n): SQLQuery {\n return sql.value({\n [sqlConvert]: 'column',\n type: serverColumnSchema.type,\n isEnum: serverColumnSchema.isEnum,\n value,\n plural: plural || serverColumnSchema.isArray,\n isComparison,\n });\n}\n\nclass ReusingFormat implements FormatConfig {\n readonly #seen: Map<unknown, number> = new Map();\n readonly escapeIdentifier: (str: string) => string;\n\n constructor(escapeIdentifier: (str: string) => string) {\n this.escapeIdentifier = escapeIdentifier;\n }\n\n formatValue = (value: unknown) => {\n if (this.#seen.has(value)) {\n return {\n placeholder: `$${this.#seen.get(value)}`,\n value: PREVIOUSLY_SEEN_VALUE,\n };\n }\n this.#seen.set(value, this.#seen.size + 1);\n return {placeholder: `$${this.#seen.size}`, value};\n };\n}\n\nfunction stringify(arg: SqlConvertArg): string | null {\n if (arg.value === null) {\n return null;\n }\n if (arg.plural) {\n return JSON.stringify(arg.value);\n }\n if (arg[sqlConvert] === 'literal' && arg.type === 'string') {\n return arg.value as unknown as string;\n }\n if (\n arg[sqlConvert] === 'column' &&\n (arg.isEnum || isPgStringType(arg.type))\n ) {\n return arg.value as string;\n }\n return JSON.stringify(arg.value);\n}\n\nclass SQLConvertFormat implements FormatConfig {\n readonly #seen: Map<unknown, Map<string, number>> = new Map();\n #size = 0;\n readonly escapeIdentifier: (str: string) => string;\n\n constructor(escapeIdentifier: (str: string) => string) {\n this.escapeIdentifier = escapeIdentifier;\n }\n\n formatValue = (value: unknown) => {\n assert(isSqlConvert(value), 'JsonPackedFormat can only take JsonPackArgs.');\n const byType = this.#seen.get(value.value);\n if (byType?.has(value.type)) {\n return {\n placeholder: createPlaceholder(byType.get(value.type)!, value),\n value: PREVIOUSLY_SEEN_VALUE,\n };\n }\n this.#size++;\n if (byType) {\n byType.set(value.type, this.#size);\n } else {\n this.#seen.set(value.value, new Map([[value.type, this.#size]]));\n }\n return {\n placeholder: createPlaceholder(this.#size, value),\n value: stringify(value),\n };\n };\n}\n\nfunction createPlaceholder(index: number, arg: SqlConvertArg) {\n if (arg.type === 'null') {\n assert(arg.value === null, \"Args of type 'null' must have value null\");\n assert(!arg.plural, \"Args of type 'null' must not be plural\");\n return `$${index}`;\n }\n\n if (arg.value === null && arg.plural) {\n return `$${index}`;\n }\n\n if (arg[sqlConvert] === 'literal') {\n const {value} = arg;\n if (Array.isArray(value)) {\n const elType = pgTypeForLiteralType(arg.type);\n return formatPlural(index, `value::${elType}`);\n }\n return `$${index}::text::${pgTypeForLiteralType(arg.type)}`;\n }\n\n const common = formatCommonToSingularAndPlural(index, arg);\n return arg.plural ? formatPlural(index, common) : common;\n}\n\nfunction formatCommonToSingularAndPlural(\n index: number,\n arg: ColumnSqlConvertArg,\n) {\n // Ok, so what is with all the `::text` casts\n // before the final cast?\n // This is to force the statement to describe its arguments\n // as being text. Without the text cast the args are described as\n // being bool/json/numeric/whatever and the bindings try to coerce\n // the inputs to those types.\n const valuePlaceholder = arg.plural ? 'value' : `$${index}`;\n let atTimeZone = ` AT TIME ZONE 'UTC'`;\n switch (arg.type) {\n case 'timestamptz':\n // @ts-expect-error Fallthrough intended\n case 'timestamp with time zone':\n atTimeZone = '';\n // fallthrough\n\n case 'date':\n case 'timestamp':\n case 'timestamp without time zone':\n return `to_timestamp(${valuePlaceholder}::text::numeric / 1000.0)${atTimeZone}`;\n\n case 'timetz':\n // @ts-expect-error Fallthrough intended\n case 'time with time zone':\n atTimeZone = '';\n // fallthrough\n\n case 'time':\n case 'time without time zone':\n return `(${valuePlaceholder}::text::int * interval'1ms')::time${atTimeZone}`;\n\n // uuid: cast to native uuid type for proper comparison and index usage\n case 'uuid':\n return `${valuePlaceholder}::text::uuid`;\n }\n if (arg.isEnum) {\n return `${valuePlaceholder}::text::\"${arg.type}\"`;\n }\n if (isPgNativeStringType(arg.type)) {\n // For comparison cast to the general `text` type, not the\n // specific column type (i.e. `arg.type`), because we don't want to\n // force the value being compared to the size/max-size of the column\n // type before comparison.\n return arg.isComparison\n ? `${valuePlaceholder}::text`\n : `${valuePlaceholder}::text::${arg.type}`;\n }\n if (isPgTextRepresentedType(arg.type)) {\n return `${valuePlaceholder}::text::${arg.type}`;\n }\n if (isPgNumberType(arg.type)) {\n // For comparison cast to `double precision` which uses IEEE 754 (the same\n // representation as JavaScript numbers which will accurately\n // represent any number value from zql) not the specific column type\n // (i.e. `arg.type`), because we don't want to force the value being\n // compared to the range and precision of the column type before comparison.\n return arg.isComparison\n ? `${valuePlaceholder}::text::double precision`\n : `${valuePlaceholder}::text::${arg.type}`;\n }\n return `${valuePlaceholder}::text::${arg.type}`;\n}\n\nfunction formatPlural(index: number, select: string) {\n return `ARRAY(\n SELECT ${select} FROM jsonb_array_elements_text($${index}::text::jsonb)\n )`;\n}\n\nfunction pgTypeForLiteralType(type: Exclude<LiteralType, 'null'>) {\n switch (type) {\n case 'boolean':\n return 'boolean';\n case 'number':\n // `double precision` uses IEEE 754, the same representation as JavaScript\n // numbers, and so this will accurately represent any number value\n // from zql\n return 'double precision';\n case 'string':\n return 'text';\n default:\n unreachable(type);\n }\n}\n\nexport {sql};\n\nconst PREVIOUSLY_SEEN_VALUE = Symbol('PREVIOUSLY_SEEN_VALUE');\n\nconst whitespacePrefixRe = /^\\s*/;\n\nfunction formatFn(\n items: readonly SQLItem[],\n {escapeIdentifier, formatValue}: FormatConfig,\n): {\n text: string;\n values: unknown[];\n} {\n // Create an empty query object.\n let text = '';\n const values = [];\n\n const localIdentifiers = new Map<unknown, string>();\n\n for (const item of items) {\n switch (item.type) {\n // If this is just raw text, we add it directly to the query text.\n case SQLItemType.RAW: {\n text += item.text;\n break;\n }\n\n // If we got a value SQL item, add a placeholder and add the value to our\n // placeholder values array.\n case SQLItemType.VALUE: {\n const {placeholder, value} = formatValue(item.value, values.length);\n text += placeholder;\n if (value !== PREVIOUSLY_SEEN_VALUE) {\n values.push(value);\n }\n\n break;\n }\n\n // If we got an identifier type, escape the strings and get a local\n // identifier for non-string identifiers.\n case SQLItemType.IDENTIFIER: {\n // This is a specific addition for Zero as Zero\n // does not support dots in identifiers.\n // If a dot is found, we assume it is a namespace\n // and split the identifier into its parts.\n const names =\n item.names.length === 1 &&\n typeof item.names[0] === 'string' &&\n item.names[0].includes('.')\n ? item.names[0].split('.')\n : item.names;\n\n text += names\n .map((name): string => {\n if (typeof name === 'string') return escapeIdentifier(name);\n\n if (!localIdentifiers.has(name)) {\n localIdentifiers.set(name, `__local_${localIdentifiers.size}__`);\n }\n\n return escapeIdentifier(localIdentifiers.get(name)!);\n })\n .join('.');\n break;\n }\n }\n }\n\n if (text.trim()) {\n const lines = text.split('\\n');\n const min = Math.min(\n ...lines\n .filter(l => l.trim() !== '')\n .map(l => whitespacePrefixRe.exec(l)![0].length),\n );\n if (min) {\n text = lines.map(line => line.substr(min)).join('\\n');\n }\n }\n return {\n text: text.trim(),\n values,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"sql.js","names":["#seen","#size"],"sources":["../../../../z2s/src/sql.ts"],"sourcesContent":["import {\n escapePostgresIdentifier,\n escapeSQLiteIdentifier,\n} from '@databases/escape-identifier';\nimport type {FormatConfig, SQLItem, SQLQuery} from '@databases/sql';\nimport sql, {SQLItemType} from '@databases/sql';\nimport {assert, unreachable} from '../../shared/src/asserts.ts';\nimport {getOrInsertComputed} from '../../shared/src/map.ts';\nimport {\n isPgNativeStringType,\n isPgNumberType,\n isPgStringType,\n isPgTextRepresentedType,\n} from '../../zero-cache/src/types/pg-data-type.ts';\nimport type {LiteralValue} from '../../zero-protocol/src/ast.ts';\nimport type {ServerColumnSchema} from '../../zero-types/src/server-schema.ts';\n\nexport function formatPg(sql: SQLQuery) {\n const format = new ReusingFormat(escapePostgresIdentifier);\n return sql.format((items: readonly SQLItem[]) => formatFn(items, format));\n}\n\nexport function formatPgInternalConvert(sql: SQLQuery) {\n const format = new SQLConvertFormat(escapePostgresIdentifier);\n return sql.format((items: readonly SQLItem[]) => formatFn(items, format));\n}\n\nexport function formatSqlite(sql: SQLQuery) {\n const format = new ReusingFormat(escapeSQLiteIdentifier);\n return sql.format((items: readonly SQLItem[]) => formatFn(items, format));\n}\n\nconst sqlConvert = Symbol('fromJson');\n\nexport type LiteralType = 'boolean' | 'number' | 'string' | 'null';\nexport type PluralLiteralType = Exclude<LiteralType, 'null'>;\n\ntype ColumnSqlConvertArg = {\n [sqlConvert]: 'column';\n type: string;\n value: unknown;\n plural: boolean;\n isEnum: boolean;\n isComparison: boolean;\n};\n\ntype SqlConvertArg =\n | ColumnSqlConvertArg\n | {\n [sqlConvert]: 'literal';\n type: LiteralType;\n value: LiteralValue;\n plural: boolean;\n };\n\nfunction isSqlConvert(value: unknown): value is SqlConvertArg {\n return value !== null && typeof value === 'object' && sqlConvert in value;\n}\n\nexport function sqlConvertSingularLiteralArg(\n value: string | boolean | number | null,\n): SQLQuery {\n const arg: SqlConvertArg = {\n [sqlConvert]: 'literal',\n type: value === null ? 'null' : (typeof value as LiteralType),\n value,\n plural: false,\n };\n return sql.value(arg);\n}\n\nexport function sqlConvertPluralLiteralArg(\n type: PluralLiteralType,\n value: PluralLiteralType[],\n): SQLQuery {\n const arg: SqlConvertArg = {\n [sqlConvert]: 'literal',\n type,\n value,\n plural: true,\n };\n return sql.value(arg);\n}\n\nexport function sqlConvertColumnArg(\n serverColumnSchema: ServerColumnSchema,\n value: unknown,\n plural: boolean,\n isComparison: boolean,\n): SQLQuery {\n return sql.value({\n [sqlConvert]: 'column',\n type: serverColumnSchema.type,\n isEnum: serverColumnSchema.isEnum,\n value,\n plural: plural || serverColumnSchema.isArray,\n isComparison,\n });\n}\n\nclass ReusingFormat implements FormatConfig {\n readonly #seen: Map<unknown, number> = new Map();\n readonly escapeIdentifier: (str: string) => string;\n\n constructor(escapeIdentifier: (str: string) => string) {\n this.escapeIdentifier = escapeIdentifier;\n }\n\n formatValue = (value: unknown) => {\n if (this.#seen.has(value)) {\n return {\n placeholder: `$${this.#seen.get(value)}`,\n value: PREVIOUSLY_SEEN_VALUE,\n };\n }\n this.#seen.set(value, this.#seen.size + 1);\n return {placeholder: `$${this.#seen.size}`, value};\n };\n}\n\nfunction stringify(arg: SqlConvertArg): string | null {\n if (arg.value === null) {\n return null;\n }\n if (arg.plural) {\n return JSON.stringify(arg.value);\n }\n if (arg[sqlConvert] === 'literal' && arg.type === 'string') {\n return arg.value as unknown as string;\n }\n if (\n arg[sqlConvert] === 'column' &&\n (arg.isEnum || isPgStringType(arg.type))\n ) {\n return arg.value as string;\n }\n return JSON.stringify(arg.value);\n}\n\nclass SQLConvertFormat implements FormatConfig {\n readonly #seen: Map<unknown, Map<string, number>> = new Map();\n #size = 0;\n readonly escapeIdentifier: (str: string) => string;\n\n constructor(escapeIdentifier: (str: string) => string) {\n this.escapeIdentifier = escapeIdentifier;\n }\n\n formatValue = (value: unknown) => {\n assert(isSqlConvert(value), 'JsonPackedFormat can only take JsonPackArgs.');\n const byType = this.#seen.get(value.value);\n if (byType?.has(value.type)) {\n return {\n placeholder: createPlaceholder(byType.get(value.type)!, value),\n value: PREVIOUSLY_SEEN_VALUE,\n };\n }\n this.#size++;\n if (byType) {\n byType.set(value.type, this.#size);\n } else {\n this.#seen.set(value.value, new Map([[value.type, this.#size]]));\n }\n return {\n placeholder: createPlaceholder(this.#size, value),\n value: stringify(value),\n };\n };\n}\n\nfunction createPlaceholder(index: number, arg: SqlConvertArg) {\n if (arg.type === 'null') {\n assert(arg.value === null, \"Args of type 'null' must have value null\");\n assert(!arg.plural, \"Args of type 'null' must not be plural\");\n return `$${index}`;\n }\n\n if (arg.value === null && arg.plural) {\n return `$${index}`;\n }\n\n if (arg[sqlConvert] === 'literal') {\n const {value} = arg;\n if (Array.isArray(value)) {\n const elType = pgTypeForLiteralType(arg.type);\n return formatPlural(index, `value::${elType}`);\n }\n return `$${index}::text::${pgTypeForLiteralType(arg.type)}`;\n }\n\n const common = formatCommonToSingularAndPlural(index, arg);\n return arg.plural ? formatPlural(index, common) : common;\n}\n\nfunction formatCommonToSingularAndPlural(\n index: number,\n arg: ColumnSqlConvertArg,\n) {\n // Ok, so what is with all the `::text` casts\n // before the final cast?\n // This is to force the statement to describe its arguments\n // as being text. Without the text cast the args are described as\n // being bool/json/numeric/whatever and the bindings try to coerce\n // the inputs to those types.\n const valuePlaceholder = arg.plural ? 'value' : `$${index}`;\n let atTimeZone = ` AT TIME ZONE 'UTC'`;\n switch (arg.type) {\n case 'timestamptz':\n // @ts-expect-error Fallthrough intended\n case 'timestamp with time zone':\n atTimeZone = '';\n // fallthrough\n\n case 'date':\n case 'timestamp':\n case 'timestamp without time zone':\n return `to_timestamp(${valuePlaceholder}::text::numeric / 1000.0)${atTimeZone}`;\n\n case 'timetz':\n // @ts-expect-error Fallthrough intended\n case 'time with time zone':\n atTimeZone = '';\n // fallthrough\n\n case 'time':\n case 'time without time zone':\n return `(${valuePlaceholder}::text::int * interval'1ms')::time${atTimeZone}`;\n\n // uuid: cast to native uuid type for proper comparison and index usage\n case 'uuid':\n return `${valuePlaceholder}::text::uuid`;\n }\n if (arg.isEnum) {\n return `${valuePlaceholder}::text::\"${arg.type}\"`;\n }\n if (isPgNativeStringType(arg.type)) {\n // For comparison cast to the general `text` type, not the\n // specific column type (i.e. `arg.type`), because we don't want to\n // force the value being compared to the size/max-size of the column\n // type before comparison.\n return arg.isComparison\n ? `${valuePlaceholder}::text`\n : `${valuePlaceholder}::text::${arg.type}`;\n }\n if (isPgTextRepresentedType(arg.type)) {\n return `${valuePlaceholder}::text::${arg.type}`;\n }\n if (isPgNumberType(arg.type)) {\n // For comparison cast to `double precision` which uses IEEE 754 (the same\n // representation as JavaScript numbers which will accurately\n // represent any number value from zql) not the specific column type\n // (i.e. `arg.type`), because we don't want to force the value being\n // compared to the range and precision of the column type before comparison.\n return arg.isComparison\n ? `${valuePlaceholder}::text::double precision`\n : `${valuePlaceholder}::text::${arg.type}`;\n }\n return `${valuePlaceholder}::text::${arg.type}`;\n}\n\nfunction formatPlural(index: number, select: string) {\n return `ARRAY(\n SELECT ${select} FROM jsonb_array_elements_text($${index}::text::jsonb)\n )`;\n}\n\nfunction pgTypeForLiteralType(type: Exclude<LiteralType, 'null'>) {\n switch (type) {\n case 'boolean':\n return 'boolean';\n case 'number':\n // `double precision` uses IEEE 754, the same representation as JavaScript\n // numbers, and so this will accurately represent any number value\n // from zql\n return 'double precision';\n case 'string':\n return 'text';\n default:\n unreachable(type);\n }\n}\n\nexport {sql};\n\nconst PREVIOUSLY_SEEN_VALUE = Symbol('PREVIOUSLY_SEEN_VALUE');\n\nconst whitespacePrefixRe = /^\\s*/;\n\nfunction formatFn(\n items: readonly SQLItem[],\n {escapeIdentifier, formatValue}: FormatConfig,\n): {\n text: string;\n values: unknown[];\n} {\n // Create an empty query object.\n let text = '';\n const values = [];\n\n const localIdentifiers = new Map<unknown, string>();\n\n for (const item of items) {\n switch (item.type) {\n // If this is just raw text, we add it directly to the query text.\n case SQLItemType.RAW: {\n text += item.text;\n break;\n }\n\n // If we got a value SQL item, add a placeholder and add the value to our\n // placeholder values array.\n case SQLItemType.VALUE: {\n const {placeholder, value} = formatValue(item.value, values.length);\n text += placeholder;\n if (value !== PREVIOUSLY_SEEN_VALUE) {\n values.push(value);\n }\n\n break;\n }\n\n // If we got an identifier type, escape the strings and get a local\n // identifier for non-string identifiers.\n case SQLItemType.IDENTIFIER: {\n // This is a specific addition for Zero as Zero\n // does not support dots in identifiers.\n // If a dot is found, we assume it is a namespace\n // and split the identifier into its parts.\n const names =\n item.names.length === 1 &&\n typeof item.names[0] === 'string' &&\n item.names[0].includes('.')\n ? item.names[0].split('.')\n : item.names;\n\n text += names\n .map((name): string => {\n if (typeof name === 'string') return escapeIdentifier(name);\n\n return escapeIdentifier(\n getOrInsertComputed(\n localIdentifiers,\n name,\n () => `__local_${localIdentifiers.size}__`,\n ),\n );\n })\n .join('.');\n break;\n }\n }\n }\n\n if (text.trim()) {\n const lines = text.split('\\n');\n const min = Math.min(\n ...lines\n .filter(l => l.trim() !== '')\n .map(l => whitespacePrefixRe.exec(l)![0].length),\n );\n if (min) {\n text = lines.map(line => line.substr(min)).join('\\n');\n }\n }\n return {\n text: text.trim(),\n values,\n };\n}\n"],"mappings":"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"}
|
package/out/zero/package.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var package_default = {
|
|
2
2
|
name: "@rocicorp/zero",
|
|
3
|
-
version: "1.10.0-canary.
|
|
3
|
+
version: "1.10.0-canary.24",
|
|
4
4
|
description: "Zero is a web framework for serverless web development.",
|
|
5
5
|
homepage: "https://zero.rocicorp.dev",
|
|
6
6
|
bugs: { "url": "https://bugs.rocicorp.dev" },
|
package/out/zero/package.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"package.js","names":[],"sources":["../../package.json"],"sourcesContent":["{\n \"name\": \"@rocicorp/zero\",\n \"version\": \"1.10.0-canary.
|
|
1
|
+
{"version":3,"file":"package.js","names":[],"sources":["../../package.json"],"sourcesContent":["{\n \"name\": \"@rocicorp/zero\",\n \"version\": \"1.10.0-canary.24\",\n \"description\": \"Zero is a web framework for serverless web development.\",\n \"homepage\": \"https://zero.rocicorp.dev\",\n \"bugs\": {\n \"url\": \"https://bugs.rocicorp.dev\"\n },\n \"license\": \"Apache-2.0\",\n \"author\": \"Rocicorp, Inc.\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git+https://github.com/rocicorp/mono.git\",\n \"directory\": \"packages/zero\"\n },\n \"bin\": {\n \"analyze-query\": \"./out/zero/src/analyze-query.js\",\n \"ast-to-zql\": \"./out/zero/src/ast-to-zql.js\",\n \"transform-query\": \"./out/zero/src/transform-query.js\",\n \"zero-build-schema\": \"./out/zero/src/build-schema.js\",\n \"zero-cache\": \"./out/zero/src/cli.js\",\n \"zero-cache-dev\": \"./out/zero/src/zero-cache-dev.js\",\n \"zero-deploy-permissions\": \"./out/zero/src/deploy-permissions.js\",\n \"zero-out\": \"./out/zero/src/zero-out.js\"\n },\n \"files\": [\n \"out\",\n \"!*.tsbuildinfo\"\n ],\n \"type\": \"module\",\n \"main\": \"out/zero/src/zero.js\",\n \"module\": \"out/zero/src/zero.js\",\n \"types\": \"out/zero/src/zero.d.ts\",\n \"exports\": {\n \".\": {\n \"types\": \"./out/zero/src/zero.d.ts\",\n \"default\": \"./out/zero/src/zero.js\"\n },\n \"./analyze\": {\n \"types\": \"./out/zero/src/analyze.d.ts\",\n \"default\": \"./out/zero/src/analyze.js\"\n },\n \"./bindings\": {\n \"types\": \"./out/zero/src/bindings.d.ts\",\n \"default\": \"./out/zero/src/bindings.js\"\n },\n \"./change-protocol/v0\": {\n \"types\": \"./out/zero/src/change-protocol/v0.d.ts\",\n \"default\": \"./out/zero/src/change-protocol/v0.js\"\n },\n \"./expo-sqlite\": {\n \"types\": \"./out/zero/src/expo-sqlite.d.ts\",\n \"default\": \"./out/zero/src/expo-sqlite.js\"\n },\n \"./op-sqlite\": {\n \"types\": \"./out/zero/src/op-sqlite.d.ts\",\n \"default\": \"./out/zero/src/op-sqlite.js\"\n },\n \"./pg\": {\n \"types\": \"./out/zero/src/pg.d.ts\",\n \"default\": \"./out/zero/src/pg.js\"\n },\n \"./react\": {\n \"types\": \"./out/zero/src/react.d.ts\",\n \"default\": \"./out/zero/src/react.js\"\n },\n \"./react-native\": {\n \"types\": \"./out/zero/src/react-native.d.ts\",\n \"default\": \"./out/zero/src/react-native.js\"\n },\n \"./server\": {\n \"types\": \"./out/zero/src/server.d.ts\",\n \"default\": \"./out/zero/src/server.js\"\n },\n \"./server/adapters/drizzle\": {\n \"types\": \"./out/zero/src/adapters/drizzle.d.ts\",\n \"default\": \"./out/zero/src/adapters/drizzle.js\"\n },\n \"./server/adapters/kysely\": {\n \"types\": \"./out/zero/src/adapters/kysely.d.ts\",\n \"default\": \"./out/zero/src/adapters/kysely.js\"\n },\n \"./server/adapters/prisma\": {\n \"types\": \"./out/zero/src/adapters/prisma.d.ts\",\n \"default\": \"./out/zero/src/adapters/prisma.js\"\n },\n \"./server/adapters/pg\": {\n \"types\": \"./out/zero/src/adapters/pg.d.ts\",\n \"default\": \"./out/zero/src/adapters/pg.js\"\n },\n \"./server/adapters/postgresjs\": {\n \"types\": \"./out/zero/src/adapters/postgresjs.d.ts\",\n \"default\": \"./out/zero/src/adapters/postgresjs.js\"\n },\n \"./solid\": {\n \"types\": \"./out/zero/src/solid.d.ts\",\n \"default\": \"./out/zero/src/solid.js\"\n },\n \"./sqlite\": {\n \"types\": \"./out/zero/src/sqlite.d.ts\",\n \"default\": \"./out/zero/src/sqlite.js\"\n },\n \"./zqlite\": {\n \"types\": \"./out/zero/src/zqlite.d.ts\",\n \"default\": \"./out/zero/src/zqlite.js\"\n }\n },\n \"scripts\": {\n \"build\": \"node --experimental-strip-types --no-warnings tool/build.ts\",\n \"build:watch\": \"node --experimental-strip-types --no-warnings tool/build.ts --watch\",\n \"check-types\": \"tsc -p tsconfig.client.json && tsc -p tsconfig.server.json\",\n \"check-types:client:watch\": \"tsc -p tsconfig.client.json --watch\",\n \"check-types:server:watch\": \"tsc -p tsconfig.server.json --watch\",\n \"format\": \"oxfmt .\",\n \"check-format\": \"oxfmt --check .\",\n \"lint\": \"oxlint --quiet --config ../../oxlint.config.ts src/\",\n \"docs\": \"node --experimental-strip-types --no-warnings tool/generate-docs.ts\",\n \"docs:server\": \"node --watch --experimental-strip-types --no-warnings tool/generate-docs.ts --server\",\n \"fmt\": \"oxfmt .\",\n \"check-fmt\": \"oxfmt --check .\"\n },\n \"dependencies\": {\n \"@badrap/valita\": \"0.3.11\",\n \"@databases/escape-identifier\": \"^1.0.3\",\n \"@databases/sql\": \"^3.3.0\",\n \"@dotenvx/dotenvx\": \"^1.39.0\",\n \"@drdgvhbh/postgres-error-codes\": \"^0.0.6\",\n \"@fastify/cors\": \"^10.0.0\",\n \"@fastify/websocket\": \"^11.0.0\",\n \"@google-cloud/precise-date\": \"^4.0.0\",\n \"@opentelemetry/api\": \"^1.9.0\",\n \"@opentelemetry/api-logs\": \"^0.218.0\",\n \"@opentelemetry/auto-instrumentations-node\": \"^0.76.0\",\n \"@opentelemetry/exporter-metrics-otlp-http\": \"^0.218.0\",\n \"@opentelemetry/resources\": \"^2.7.1\",\n \"@opentelemetry/sdk-metrics\": \"^2.7.1\",\n \"@opentelemetry/sdk-node\": \"^0.218.0\",\n \"@opentelemetry/sdk-trace-node\": \"^2.7.1\",\n \"@opentelemetry/semantic-conventions\": \"^1.41.1\",\n \"@postgresql-typed/oids\": \"^0.2.0\",\n \"@rocicorp/lock\": \"^1.0.4\",\n \"@rocicorp/logger\": \"^6.1.0\",\n \"@rocicorp/resolver\": \"^1.0.2\",\n \"@rocicorp/zero-sqlite3\": \"^1.1.4\",\n \"@standard-schema/spec\": \"^1.0.0\",\n \"@types/basic-auth\": \"^1.1.8\",\n \"@types/ws\": \"^8.5.12\",\n \"basic-auth\": \"^2.0.1\",\n \"chalk-template\": \"^1.1.0\",\n \"chokidar\": \"^4.0.1\",\n \"cloudevents\": \"^10.0.0\",\n \"command-line-args\": \"^6.0.1\",\n \"command-line-usage\": \"^7.0.3\",\n \"compare-utf8\": \"^0.2.3\",\n \"defu\": \"^6.1.4\",\n \"eventemitter3\": \"^5.0.1\",\n \"fastify\": \"^5.0.0\",\n \"is-in-subnet\": \"^4.0.1\",\n \"jose\": \"^5.9.3\",\n \"json-custom-numbers\": \"^3.1.1\",\n \"kasi\": \"^1.1.0\",\n \"nanoid\": \"^5.1.2\",\n \"oxfmt\": \"^0.45.0\",\n \"parse-prometheus-text-format\": \"^1.1.1\",\n \"pg-format\": \"npm:pg-format-fix@^1.0.5\",\n \"postgres\": \"3.4.7\",\n \"semver\": \"^7.5.4\",\n \"url-pattern\": \"^1.0.3\",\n \"urlpattern-polyfill\": \"^10.1.0\",\n \"ws\": \"^8.18.1\"\n },\n \"devDependencies\": {\n \"@op-engineering/op-sqlite\": \">=15\",\n \"@vitest/runner\": \"^4.1.6\",\n \"analyze-query\": \"workspace:*\",\n \"ast-to-zql\": \"workspace:*\",\n \"expo-sqlite\": \">=15\",\n \"oxc-parser\": \"^0.129.0\",\n \"replicache\": \"workspace:*\",\n \"shared\": \"workspace:*\",\n \"syncpack\": \"^14.3.0\",\n \"typedoc\": \"^0.28.17\",\n \"typedoc-plugin-markdown\": \"^4.10.0\",\n \"typescript\": \"~6.0.2\",\n \"vite\": \"^8.0.13\",\n \"vitest\": \"^4.1.6\",\n \"zero-cache\": \"workspace:*\",\n \"zero-client\": \"workspace:*\",\n \"zero-pg\": \"workspace:*\",\n \"zero-react\": \"workspace:*\",\n \"zero-server\": \"workspace:*\",\n \"zero-solid\": \"workspace:*\",\n \"zqlite\": \"workspace:*\"\n },\n \"engines\": {\n \"node\": \">=22\"\n },\n \"packageManager\": \"pnpm@11.11.0\"\n}\n"],"mappings":""}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"write-authorizer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/auth/write-authorizer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,MAAM,CAAC;AAMrC,OAAO,KAAK,EACV,MAAM,EAIN,QAAQ,EACT,MAAM,wCAAwC,CAAC;AAsBhD,OAAO,KAAK,EAEV,eAAe,EAChB,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,2BAA2B,CAAC;AAMxD,OAAO,KAAK,EAAY,UAAU,EAAC,MAAM,0BAA0B,CAAC;AAapE,MAAM,WAAW,eAAe;IAC9B,cAAc,CACZ,QAAQ,EAAE,UAAU,GAAG,SAAS,EAChC,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,GAC/B,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,eAAe,CACb,QAAQ,EAAE,UAAU,GAAG,SAAS,EAChC,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,GAC/B,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,iBAAiB,IAAI,IAAI,CAAC;IAC1B,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;IAEzD;;;OAGG;IACH,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACzC;AAED,qBAAa,mBAAoB,YAAW,eAAe;;gBAgBvD,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,iBAAiB,EAAE,eAAe;
|
|
1
|
+
{"version":3,"file":"write-authorizer.d.ts","sourceRoot":"","sources":["../../../../../zero-cache/src/auth/write-authorizer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,MAAM,CAAC;AAMrC,OAAO,KAAK,EACV,MAAM,EAIN,QAAQ,EACT,MAAM,wCAAwC,CAAC;AAsBhD,OAAO,KAAK,EAEV,eAAe,EAChB,MAAM,yCAAyC,CAAC;AACjD,OAAO,KAAK,EAAC,QAAQ,EAAC,MAAM,2BAA2B,CAAC;AAMxD,OAAO,KAAK,EAAY,UAAU,EAAC,MAAM,0BAA0B,CAAC;AAapE,MAAM,WAAW,eAAe;IAC9B,cAAc,CACZ,QAAQ,EAAE,UAAU,GAAG,SAAS,EAChC,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,GAC/B,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,eAAe,CACb,QAAQ,EAAE,UAAU,GAAG,SAAS,EAChC,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,GAC/B,OAAO,CAAC,OAAO,CAAC,CAAC;IACpB,iBAAiB,IAAI,IAAI,CAAC;IAC1B,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE,CAAC;IAEzD;;;OAGG;IACH,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACzC;AAED,qBAAa,mBAAoB,YAAW,eAAe;;gBAgBvD,EAAE,EAAE,UAAU,EACd,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,QAAQ,EACjB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,iBAAiB,EAAE,eAAe;IA0BpC,iBAAiB;IAUjB,OAAO;IAID,cAAc,CAClB,QAAQ,EAAE,UAAU,GAAG,SAAS,EAChC,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE;IAsB5B,eAAe,CACnB,QAAQ,EAAE,UAAU,GAAG,SAAS,EAChC,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE;IA0ElC,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,QAAQ,CAAC,EAAE;IAuBxD,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,IAAI;CAkUxC"}
|
|
@@ -37,6 +37,7 @@ var WriteAuthorizerImpl = class {
|
|
|
37
37
|
this.#replica = replica;
|
|
38
38
|
this.#cgStorage = writeAuthzStorage.createClientGroupStorage(cgID);
|
|
39
39
|
this.#builderDelegate = {
|
|
40
|
+
disableCorrelatedPredicatePushdown: config.enableCorrelatedPredicatePushdown === false,
|
|
40
41
|
getSource: (name) => this.#getSource(name),
|
|
41
42
|
createStorage: () => this.#cgStorage.createStorage(),
|
|
42
43
|
decorateSourceInput: (input) => input,
|