@korajs/store 1.0.0-beta.12 → 1.0.0-beta.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -37
- package/dist/adapters/better-sqlite3.cjs +90 -4
- package/dist/adapters/better-sqlite3.cjs.map +1 -1
- package/dist/adapters/better-sqlite3.d.cts +21 -12
- package/dist/adapters/better-sqlite3.d.ts +21 -12
- package/dist/adapters/better-sqlite3.js +69 -5
- package/dist/adapters/better-sqlite3.js.map +1 -1
- package/dist/adapters/indexeddb.cjs +1316 -610
- package/dist/adapters/indexeddb.cjs.map +1 -1
- package/dist/adapters/indexeddb.d.cts +29 -3
- package/dist/adapters/indexeddb.d.ts +29 -3
- package/dist/adapters/indexeddb.js +247 -221
- package/dist/adapters/indexeddb.js.map +1 -1
- package/dist/adapters/sqlite-wasm-worker.cjs +856 -63
- package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -1
- package/dist/adapters/sqlite-wasm-worker.js +529 -63
- package/dist/adapters/sqlite-wasm-worker.js.map +1 -1
- package/dist/adapters/sqlite-wasm.cjs +1032 -344
- package/dist/adapters/sqlite-wasm.cjs.map +1 -1
- package/dist/adapters/sqlite-wasm.d.cts +239 -18
- package/dist/adapters/sqlite-wasm.d.ts +239 -18
- package/dist/adapters/sqlite-wasm.js +111 -5
- package/dist/adapters/sqlite-wasm.js.map +1 -1
- package/dist/backup-IHKVMUAM.js +25 -0
- package/dist/chunk-5AQSGMJV.js +5471 -0
- package/dist/chunk-5AQSGMJV.js.map +1 -0
- package/dist/chunk-5JMGMPUE.js +591 -0
- package/dist/chunk-5JMGMPUE.js.map +1 -0
- package/dist/{chunk-CYO3YKFL.js → chunk-5ZRRF6R2.js} +2 -2
- package/dist/chunk-A6GFH5IG.js +36 -0
- package/dist/chunk-A6GFH5IG.js.map +1 -0
- package/dist/chunk-BILSA6XZ.js +608 -0
- package/dist/chunk-BILSA6XZ.js.map +1 -0
- package/dist/chunk-BQSIL6TT.js +1095 -0
- package/dist/chunk-BQSIL6TT.js.map +1 -0
- package/dist/{chunk-GSJ7EEUC.js → chunk-J5AGXF3D.js} +58 -9
- package/dist/chunk-J5AGXF3D.js.map +1 -0
- package/dist/chunk-KK3GBONL.js +112 -0
- package/dist/chunk-KK3GBONL.js.map +1 -0
- package/dist/chunk-KTVNANUH.js +876 -0
- package/dist/chunk-KTVNANUH.js.map +1 -0
- package/dist/chunk-QW4UU7NG.js +1003 -0
- package/dist/chunk-QW4UU7NG.js.map +1 -0
- package/dist/chunk-SRHDJF5B.js +186 -0
- package/dist/chunk-SRHDJF5B.js.map +1 -0
- package/dist/chunk-UC2LGFPV.js +810 -0
- package/dist/chunk-UC2LGFPV.js.map +1 -0
- package/dist/{export-audit-ODGQTQIK.js → export-audit-J2QXRHD2.js} +3 -3
- package/dist/index.cjs +9533 -4246
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1143 -110
- package/dist/index.d.ts +1143 -110
- package/dist/index.js +298 -3191
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +581 -264
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +15 -85
- package/dist/internal.d.ts +15 -85
- package/dist/internal.js +10 -8
- package/dist/restore-MNDPR557.js +299 -0
- package/dist/restore-MNDPR557.js.map +1 -0
- package/dist/sql-builder-D0tC3sv9.d.cts +615 -0
- package/dist/sql-builder-D5UMzHEo.d.ts +615 -0
- package/dist/sqlite-wasm-channel-DVX93Uiq.d.cts +141 -0
- package/dist/sqlite-wasm-channel-DVX93Uiq.d.ts +141 -0
- package/dist/sqlite-wasm-channel-QCCNBYCU.js +12 -0
- package/dist/store-HNGX4KTT.js +16 -0
- package/dist/store-HNGX4KTT.js.map +1 -0
- package/dist/{types-BslDOTk0.d.ts → types-Dh-xyUjP.d.cts} +99 -26
- package/dist/{types-BslDOTk0.d.cts → types-Dh-xyUjP.d.ts} +99 -26
- package/package.json +3 -2
- package/dist/backup-5S7K2Z35.js +0 -14
- package/dist/chunk-GSJ7EEUC.js.map +0 -1
- package/dist/chunk-HS6DBMST.js +0 -383
- package/dist/chunk-HS6DBMST.js.map +0 -1
- package/dist/chunk-RJIEKEN4.js +0 -751
- package/dist/chunk-RJIEKEN4.js.map +0 -1
- package/dist/chunk-TUR7RGEP.js +0 -84
- package/dist/chunk-TUR7RGEP.js.map +0 -1
- package/dist/chunk-VDBJQ75K.js +0 -78
- package/dist/chunk-VDBJQ75K.js.map +0 -1
- package/dist/chunk-YT7MRFCP.js +0 -687
- package/dist/chunk-YT7MRFCP.js.map +0 -1
- package/dist/sqlite-wasm-channel-GGHA2BXU.js +0 -12
- package/dist/sqlite-wasm-channel-L6o3bWQK.d.cts +0 -92
- package/dist/sqlite-wasm-channel-L6o3bWQK.d.ts +0 -92
- package/dist/types-BOO9EPt1.d.ts +0 -170
- package/dist/types-C_GDavG-.d.cts +0 -170
- /package/dist/{backup-5S7K2Z35.js.map → backup-IHKVMUAM.js.map} +0 -0
- /package/dist/{chunk-CYO3YKFL.js.map → chunk-5ZRRF6R2.js.map} +0 -0
- /package/dist/{export-audit-ODGQTQIK.js.map → export-audit-J2QXRHD2.js.map} +0 -0
- /package/dist/{sqlite-wasm-channel-GGHA2BXU.js.map → sqlite-wasm-channel-QCCNBYCU.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/sqlite-wasm-worker-core.ts","../../src/adapters/sqlite-wasm-worker.ts"],"sourcesContent":["/// <reference lib=\"webworker\" />\n/**\n * Reusable SQLite WASM core, decoupled from any specific worker global.\n *\n * The dedicated worker ({@link file://./sqlite-wasm-worker.ts}) wires one core to\n * `self.onmessage`/`self.postMessage`.\n *\n * The WASM module and the OPFS SyncAccessHandle pool are cached at module scope\n * and shared across every core in the same worker: the pool holds\n * multiple database files keyed by filename, so installing it once and opening\n * each database within it avoids the single-writer conflict that installing the\n * pool twice in one scope would cause.\n *\n * This file cannot be unit-tested in Node (no WASM/OPFS); it is exercised through\n * the browser E2E suite.\n */\n\nimport type { WorkerRequest, WorkerResponse } from './sqlite-wasm-channel'\n\ninterface SqliteDb {\n\texec(opts: {\n\t\tsql: string\n\t\tbind?: unknown[]\n\t\treturnValue?: string\n\t\trowMode?: string\n\t\tcallback?: (row: Record<string, unknown>) => void\n\t}): void\n\tclose(): void\n\tdeserialize?: (data: Uint8Array) => void\n}\n\ninterface OpfsPool {\n\tOpfsSAHPoolDb: new (filename: string) => SqliteDb\n}\n\ninterface Sqlite3Api {\n\too1: { DB: new (opts: { filename: string }) => SqliteDb }\n\tinstallOpfsSAHPoolVfs?: (opts: { name: string }) => Promise<OpfsPool>\n\tcapi?: { sqlite3_deserialize?: unknown }\n}\n\n/** OPFS SAH pool name. Shared across all databases on the origin (the pool holds\n * multiple db files keyed by filename), so it must NOT be namespaced per database\n * or existing persisted data would be orphaned. */\nconst OPFS_POOL_NAME = 'kora-opfs'\n\n/** Headless browsers and some profiles hang on OPFS VFS install; fall back to memory. */\nconst OPFS_INIT_TIMEOUT_MS = 10_000\n\nasync function withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined\n\ttry {\n\t\treturn await Promise.race([\n\t\t\tpromise,\n\t\t\tnew Promise<T>((_, reject) => {\n\t\t\t\ttimer = setTimeout(\n\t\t\t\t\t() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)),\n\t\t\t\t\ttimeoutMs,\n\t\t\t\t)\n\t\t\t}),\n\t\t])\n\t} finally {\n\t\tif (timer !== undefined) clearTimeout(timer)\n\t}\n}\n\nfunction opfsDatabaseFilename(dbName: string): string {\n\tconst base = dbName.replace(/[^a-zA-Z0-9._-]/g, '_')\n\treturn base.endsWith('.db') ? base : `${base}.db`\n}\n\n/**\n * Classify why the OPFS SyncAccessHandle pool could not be installed, so the\n * fallback to in-memory is explainable rather than silent.\n */\nfunction classifyOpfsFailure(error: unknown): 'lock-conflict' | 'timeout' | 'unsupported' {\n\tconst message = error instanceof Error ? error.message : String(error)\n\tif (/timed out|timeout/i.test(message)) {\n\t\treturn 'timeout'\n\t}\n\tif (\n\t\t/lock|already|in use|in-use|NoModificationAllowed|acquire|SyncAccessHandle|being created/i.test(\n\t\t\tmessage,\n\t\t)\n\t) {\n\t\treturn 'lock-conflict'\n\t}\n\treturn 'unsupported'\n}\n\n// Scope-global caches shared by every core in this worker.\nlet sqlite3Promise: Promise<Sqlite3Api> | null = null\nlet opfsPoolPromise: Promise<OpfsPool | null> | null = null\nlet opfsFallbackReason: 'lock-conflict' | 'timeout' | 'unsupported' | undefined\n\nasync function loadSqlite3(): Promise<Sqlite3Api> {\n\tconst sqlite3InitModule = (await import('@sqlite.org/sqlite-wasm')).default\n\t// In production builds, Vite hashes asset filenames (e.g. sqlite3-[hash].wasm).\n\t// The sqlite3 module's default locateFile resolves the unhashed name, causing a\n\t// 404. The worker/host entry sets __KORA_SQLITE_WASM_URL via a `?url` import so\n\t// we can override locateFile with the correct hashed URL.\n\tconst wasmUrl = (globalThis as Record<string, unknown>).__KORA_SQLITE_WASM_URL as\n\t\t| string\n\t\t| undefined\n\tconst initOptions = wasmUrl\n\t\t? {\n\t\t\t\tlocateFile: (file: string): string => (file.endsWith('.wasm') ? wasmUrl : file),\n\t\t\t}\n\t\t: undefined\n\tconst initFn = sqlite3InitModule as unknown as (\n\t\topts?: Record<string, unknown>,\n\t) => Promise<unknown>\n\treturn (await withTimeout(initFn(initOptions), 60_000, 'SQLite3 module init')) as Sqlite3Api\n}\n\nfunction getSqlite3(): Promise<Sqlite3Api> {\n\tif (!sqlite3Promise) {\n\t\tsqlite3Promise = loadSqlite3()\n\t}\n\treturn sqlite3Promise\n}\n\nasync function installOpfsPool(sqlite3: Sqlite3Api): Promise<OpfsPool | null> {\n\tif (!sqlite3.installOpfsSAHPoolVfs) {\n\t\topfsFallbackReason = 'unsupported'\n\t\treturn null\n\t}\n\ttry {\n\t\treturn await withTimeout(\n\t\t\tsqlite3.installOpfsSAHPoolVfs({ name: OPFS_POOL_NAME }),\n\t\t\tOPFS_INIT_TIMEOUT_MS,\n\t\t\t'OPFS VFS install',\n\t\t)\n\t} catch (error) {\n\t\topfsFallbackReason = classifyOpfsFailure(error)\n\t\treturn null\n\t}\n}\n\nfunction getOpfsPool(sqlite3: Sqlite3Api): Promise<OpfsPool | null> {\n\tif (!opfsPoolPromise) {\n\t\topfsPoolPromise = installOpfsPool(sqlite3)\n\t}\n\treturn opfsPoolPromise\n}\n\n/** Handle to a single database, dispatching the worker protocol for it. */\nexport interface SqliteWasmCore {\n\thandle(request: WorkerRequest): Promise<WorkerResponse>\n}\n\n/**\n * Creates a core bound to one database. `handle` resolves with the response for\n * each request; the caller routes it back through the dedicated worker's\n * `postMessage`.\n */\nexport function createSqliteWasmCore(): SqliteWasmCore {\n\tlet db: SqliteDb | null = null\n\tlet sqlite3Api: Sqlite3Api | null = null\n\tlet persistent = false\n\n\tasync function open(\n\t\tid: number,\n\t\tddlStatements: string[],\n\t\tdbName?: string,\n\t): Promise<WorkerResponse> {\n\t\ttry {\n\t\t\t// Re-run idempotent DDL on the existing handle rather than opening the\n\t\t\t// database a second time.\n\t\t\tif (db) {\n\t\t\t\tapplyDdl(db, ddlStatements)\n\t\t\t\treturn { id, type: 'success', data: buildOpenData() }\n\t\t\t}\n\n\t\t\tconst sqlite3 = await getSqlite3()\n\t\t\tsqlite3Api = sqlite3\n\t\t\tconst pool = await getOpfsPool(sqlite3)\n\n\t\t\tif (pool) {\n\t\t\t\tdb = new pool.OpfsSAHPoolDb(opfsDatabaseFilename(dbName ?? 'kora-db'))\n\t\t\t\tpersistent = true\n\t\t\t} else {\n\t\t\t\tdb = new sqlite3.oo1.DB({ filename: ':memory:' })\n\t\t\t\tpersistent = false\n\t\t\t}\n\n\t\t\tdb.exec({ sql: 'PRAGMA journal_mode = WAL' })\n\t\t\tdb.exec({ sql: 'PRAGMA foreign_keys = ON' })\n\n\t\t\tapplyDdl(db, ddlStatements)\n\t\t\treturn { id, type: 'success', data: buildOpenData() }\n\t\t} catch (error) {\n\t\t\treturn { id, type: 'error', message: (error as Error).message, code: 'INIT_ERROR' }\n\t\t}\n\t}\n\n\tfunction buildOpenData(): { persistent: boolean; fallbackReason?: string } {\n\t\treturn {\n\t\t\tpersistent,\n\t\t\t...(persistent ? {} : { fallbackReason: opfsFallbackReason ?? 'unsupported' }),\n\t\t}\n\t}\n\n\tfunction applyDdl(target: SqliteDb, ddlStatements: string[]): void {\n\t\tfor (const sql of ddlStatements) {\n\t\t\tif (sql.startsWith('--kora:safe-alter')) {\n\t\t\t\ttry {\n\t\t\t\t\ttarget.exec({ sql: sql.replace('--kora:safe-alter\\n', '') })\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst msg = (error as Error).message || ''\n\t\t\t\t\tif (!msg.includes('duplicate column name')) {\n\t\t\t\t\t\tthrow error\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttarget.exec({ sql })\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction execute(id: number, sql: string, params?: unknown[]): WorkerResponse {\n\t\tif (!db) {\n\t\t\treturn { id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' }\n\t\t}\n\t\ttry {\n\t\t\tdb.exec({ sql, bind: params })\n\t\t\treturn { id, type: 'success' }\n\t\t} catch (error) {\n\t\t\treturn { id, type: 'error', message: (error as Error).message, code: 'EXEC_ERROR' }\n\t\t}\n\t}\n\n\tfunction query(id: number, sql: string, params?: unknown[]): WorkerResponse {\n\t\tif (!db) {\n\t\t\treturn { id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' }\n\t\t}\n\t\ttry {\n\t\t\tconst rows: Record<string, unknown>[] = []\n\t\t\tdb.exec({\n\t\t\t\tsql,\n\t\t\t\tbind: params,\n\t\t\t\trowMode: 'object',\n\t\t\t\tcallback: (row: Record<string, unknown>) => {\n\t\t\t\t\trows.push({ ...row })\n\t\t\t\t},\n\t\t\t})\n\t\t\treturn { id, type: 'success', data: rows }\n\t\t} catch (error) {\n\t\t\treturn { id, type: 'error', message: (error as Error).message, code: 'QUERY_ERROR' }\n\t\t}\n\t}\n\n\tfunction close(id: number): WorkerResponse {\n\t\tif (db) {\n\t\t\tdb.close()\n\t\t\tdb = null\n\t\t}\n\t\treturn { id, type: 'success' }\n\t}\n\n\tfunction migrate(id: number, statements: string[]): WorkerResponse {\n\t\tif (!db) {\n\t\t\treturn { id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' }\n\t\t}\n\t\ttry {\n\t\t\tfor (const sql of statements) {\n\t\t\t\tdb.exec({ sql })\n\t\t\t}\n\t\t\treturn { id, type: 'success' }\n\t\t} catch (error) {\n\t\t\treturn { id, type: 'error', message: (error as Error).message, code: 'MIGRATE_ERROR' }\n\t\t}\n\t}\n\n\tfunction importData(id: number, data: Uint8Array): WorkerResponse {\n\t\tif (!db) {\n\t\t\treturn { id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' }\n\t\t}\n\t\tconst dbWithDeserialize = db as SqliteDb & { deserialize?: (bytes: Uint8Array) => void }\n\t\tif (typeof dbWithDeserialize.deserialize === 'function') {\n\t\t\ttry {\n\t\t\t\tdbWithDeserialize.deserialize(data)\n\t\t\t\treturn { id, type: 'success' }\n\t\t\t} catch (error) {\n\t\t\t\treturn { id, type: 'error', message: (error as Error).message, code: 'IMPORT_ERROR' }\n\t\t\t}\n\t\t}\n\t\tif (!sqlite3Api || typeof sqlite3Api.capi?.sqlite3_deserialize === 'undefined') {\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\ttype: 'error',\n\t\t\t\tmessage: 'Import not supported in this SQLite WASM runtime',\n\t\t\t\tcode: 'IMPORT_NOT_SUPPORTED',\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tid,\n\t\t\ttype: 'error',\n\t\t\tmessage:\n\t\t\t\t'Import requires runtime-specific deserialize wiring and is unavailable in this worker build',\n\t\t\tcode: 'IMPORT_NOT_SUPPORTED',\n\t\t}\n\t}\n\n\tasync function handle(request: WorkerRequest): Promise<WorkerResponse> {\n\t\ttry {\n\t\t\tswitch (request.type) {\n\t\t\t\tcase 'open':\n\t\t\t\t\treturn await open(request.id, request.ddlStatements, request.dbName)\n\t\t\t\tcase 'close':\n\t\t\t\t\treturn close(request.id)\n\t\t\t\tcase 'execute':\n\t\t\t\t\treturn execute(request.id, request.sql, request.params)\n\t\t\t\tcase 'query':\n\t\t\t\t\treturn query(request.id, request.sql, request.params)\n\t\t\t\tcase 'begin':\n\t\t\t\t\treturn execute(request.id, 'BEGIN')\n\t\t\t\tcase 'commit':\n\t\t\t\t\treturn execute(request.id, 'COMMIT')\n\t\t\t\tcase 'rollback':\n\t\t\t\t\treturn execute(request.id, 'ROLLBACK')\n\t\t\t\tcase 'migrate':\n\t\t\t\t\treturn migrate(request.id, request.statements)\n\t\t\t\tcase 'import':\n\t\t\t\t\treturn importData(request.id, request.data)\n\t\t\t\tcase 'export':\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\tmessage: 'Export not yet supported in browser worker',\n\t\t\t\t\t\tcode: 'EXPORT_NOT_SUPPORTED',\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: (request as WorkerRequest).id,\n\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\tmessage: 'Unknown request type',\n\t\t\t\t\t\tcode: 'UNKNOWN_REQUEST',\n\t\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tid: request.id,\n\t\t\t\ttype: 'error',\n\t\t\t\tmessage: (error as Error).message,\n\t\t\t\tcode: 'WORKER_ERROR',\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { handle }\n}\n","/// <reference lib=\"webworker\" />\n/**\n * Web Worker script for running SQLite WASM in a dedicated worker.\n *\n * This file is intended to run inside a Web Worker in browsers. It wires one\n * {@link createSqliteWasmCore} to the worker's message channel.\n *\n * This script cannot be tested in Node.js; it is validated in E2E browser tests.\n */\n\nimport type { WorkerRequest } from './sqlite-wasm-channel'\nimport { createSqliteWasmCore } from './sqlite-wasm-worker-core'\n\ndeclare const self: DedicatedWorkerGlobalScope\n\nconst core = createSqliteWasmCore()\n\nself.onmessage = (event: MessageEvent<WorkerRequest>): void => {\n\tvoid core.handle(event.data).then((response) => {\n\t\tself.postMessage(response)\n\t})\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/opfs-names.ts","../../src/errors.ts","../../src/multi-tab/tab-storage.ts","../../src/migrations/schema-ceiling.ts","../../src/adapters/opfs-pool-ownership.ts","../../src/adapters/sqlite-wasm-loader.ts","../../src/adapters/sqlite-wasm-worker-core.ts","../../src/adapters/sqlite-wasm-worker.ts"],"sourcesContent":["/**\n * Names shared by the main thread and the SQLite worker for OPFS pools, Web Locks\n * and database files. Pure functions, so ownership rules are testable in Node.\n *\n * Ownership model (W8a, NEW-STORE-5): one OPFS SyncAccessHandle pool per\n * database. The tab leader lock (`kora-leader-<db>`) already guarantees one\n * leader tab per database, so a pool per database means two different databases\n * on one origin (per-user databases, two Kora apps) never contend for one pool.\n */\n\n/** Pool name every database used before W8a (beta.12 and earlier): one per origin. */\nexport const LEGACY_OPFS_POOL_NAME = 'kora-opfs'\n\n/** Prefix of the per-database pool names. */\nexport const OPFS_POOL_PREFIX = 'kora-opfs-'\n\n/**\n * Longest readable database slug kept in a pool name. Pool names become OPFS\n * directory names (`.<pool>`) and VFS names, so they stay short and portable.\n */\nconst MAX_POOL_SLUG_LENGTH = 48\n\n/**\n * 32-bit FNV-1a hash as 8 hex digits. Used only to keep pool names unique when\n * a database name had to be sanitized or truncated; not a security primitive.\n */\nexport function fnv1a32Hex(value: string): string {\n\tlet hash = 0x811c9dc5\n\tfor (let i = 0; i < value.length; i += 1) {\n\t\thash ^= value.charCodeAt(i)\n\t\thash = Math.imul(hash, 0x01000193)\n\t}\n\treturn (hash >>> 0).toString(16).padStart(8, '0')\n}\n\n/**\n * The OPFS SAH pool that owns `dbName`. Database names that are already safe\n * map to `kora-opfs-<dbName>`; anything sanitized or truncated gets a hash suffix\n * so two distinct names can never share a pool.\n *\n * @example opfsPoolNameFor('school__user_alice') === 'kora-opfs-school__user_alice'\n */\nexport function opfsPoolNameFor(dbName: string): string {\n\tconst sanitized = dbName.replace(/[^a-zA-Z0-9_-]/g, '_')\n\tconst slug = sanitized.slice(0, MAX_POOL_SLUG_LENGTH)\n\tif (slug === dbName) {\n\t\treturn `${OPFS_POOL_PREFIX}${slug}`\n\t}\n\treturn `${OPFS_POOL_PREFIX}${slug}-${fnv1a32Hex(dbName)}`\n}\n\n/** OPFS directory sqlite-wasm uses for a pool (its default is `.` + pool name). */\nexport function opfsPoolDirectory(poolName: string): string {\n\treturn `.${poolName}`\n}\n\n/**\n * Web Lock held by the worker that owns a pool, for the pool's entire life. A\n * new owner (a promoted tab, a reopened database) waits on it instead of racing\n * the previous worker for the file handles.\n */\nexport function opfsPoolLockName(poolName: string): string {\n\treturn `kora-opfs-pool:${poolName}`\n}\n\n/** Tab leader lock for a database (one leader tab per database per origin). */\nexport function leaderLockName(dbName: string): string {\n\treturn `kora-leader-${dbName}`\n}\n\n/** BroadcastChannel carrying follower RPC, heartbeats and pings for a database. */\nexport function storageChannelName(dbName: string): string {\n\treturn `kora-storage-${dbName}`\n}\n\n/** File name of a database inside its pool (unchanged from beta.12 for migration). */\nexport function opfsDatabaseFilename(dbName: string): string {\n\tconst base = dbName.replace(/[^a-zA-Z0-9._-]/g, '_')\n\treturn base.endsWith('.db') ? base : `${base}.db`\n}\n\n/** Absolute path under which the SAH pool tracks a database file. */\nexport function opfsPoolPath(dbName: string): string {\n\treturn `/${opfsDatabaseFilename(dbName)}`\n}\n","import { KoraError } from '@korajs/core'\n\n/**\n * Thrown when a query is invalid (bad field names, invalid operators, etc.).\n */\nexport class QueryError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'QUERY_ERROR', context)\n\t\tthis.name = 'QueryError'\n\t}\n}\n\n/**\n * Thrown when a record is not found by ID (findById, update, delete on missing record).\n */\nexport class RecordNotFoundError extends KoraError {\n\tconstructor(collection: string, recordId: string) {\n\t\tsuper(`Record \"${recordId}\" not found in collection \"${collection}\"`, 'RECORD_NOT_FOUND', {\n\t\t\tcollection,\n\t\t\trecordId,\n\t\t})\n\t\tthis.name = 'RecordNotFoundError'\n\t}\n}\n\n/**\n * Thrown inside `applyRemoteOperation` when the row's version state changed\n * between the caller's snapshot read and the guarded write (a concurrent local\n * mutation landed in the window). The transaction is rolled back with nothing\n * written; the caller re-reads fresh state, recomputes its merge, and retries.\n * This is the optimistic-concurrency guard that keeps merge-engine results\n * (richtext, add-wins arrays, constraint resolutions) from clobbering newer\n * local edits they never saw.\n */\nexport class OptimisticLockError extends KoraError {\n\tconstructor(collection: string, recordId: string) {\n\t\tsuper(\n\t\t\t`Row version changed while merging record \"${recordId}\" in \"${collection}\"; retry with fresh state`,\n\t\t\t'OPTIMISTIC_LOCK',\n\t\t\t{ collection, recordId },\n\t\t)\n\t\tthis.name = 'OptimisticLockError'\n\t}\n}\n\n/**\n * Thrown when a storage adapter operation fails.\n */\nexport class AdapterError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(message, 'ADAPTER_ERROR', context)\n\t\tthis.name = 'AdapterError'\n\t}\n}\n\n/**\n * Thrown when a write reaches a storage adapter that could not obtain durable\n * storage (it opened, or was promoted to leader, on a non-persistent in-memory\n * database). Kora refuses such writes instead of accepting data that would be\n * lost on reload. Reads still work.\n *\n * Fix: close other tabs or apps using the same origin's OPFS storage and reopen,\n * or opt in explicitly with `allowNonDurable: true` if in-memory storage is\n * acceptable for this app.\n */\nexport class StorageDurabilityError extends KoraError {\n\tconstructor(dbName: string, phase: 'open' | 'promotion', reason: string) {\n\t\tsuper(\n\t\t\t`Database \"${dbName}\" has no durable storage (${reason} during ${phase}); writes are refused so they are not silently lost on reload. Close other tabs or apps using this origin's storage and reload, or set allowNonDurable: true to accept in-memory storage.`,\n\t\t\t'STORAGE_DURABILITY_LOST',\n\t\t\t{ dbName, phase, reason },\n\t\t)\n\t\tthis.name = 'StorageDurabilityError'\n\t}\n}\n\n/**\n * Thrown when the store opens a database a NEWER build of the app already migrated: its\n * stored schema version is above this code's `schema.version` (RT-109). Running older\n * code against it would write operations of the old schema into the new one, or fail\n * part-way, so the store refuses before changing anything and emits `store:schema-ahead`.\n *\n * Fix: run the newer build (reload while online, or accept the app's update prompt).\n * Never lower `schema.version` in a deployed app.\n */\nexport class SchemaVersionAheadError extends KoraError {\n\tconstructor(\n\t\tpublic readonly dbName: string,\n\t\tpublic readonly storedVersion: number,\n\t\tpublic readonly codeVersion: number,\n\t) {\n\t\tsuper(\n\t\t\t`Database \"${dbName}\" is at schema version ${storedVersion}, newer than this build's schema version ${codeVersion}: a newer build of the app already migrated it. It was not opened and nothing in it changed. Run the newer build (reload while online, or accept the update); never lower schema.version in a deployed app.`,\n\t\t\t'SCHEMA_VERSION_AHEAD',\n\t\t\t{ dbName, storedVersion, codeVersion },\n\t\t)\n\t\tthis.name = 'SchemaVersionAheadError'\n\t}\n}\n\n/**\n * Thrown when an operation is attempted on a store that has not been opened.\n */\nexport class StoreNotOpenError extends KoraError {\n\tconstructor() {\n\t\tsuper('Store is not open. Call store.open() before performing operations.', 'STORE_NOT_OPEN')\n\t\tthis.name = 'StoreNotOpenError'\n\t}\n}\n\n/**\n * Thrown when the Web Worker fails to initialize (WASM load failure, OPFS unavailable, etc.).\n */\nexport class WorkerInitError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(`Worker initialization failed: ${message}`, 'WORKER_INIT_ERROR', context)\n\t\tthis.name = 'WorkerInitError'\n\t}\n}\n\n/**\n * Thrown when the Web Worker does not respond within the configured timeout.\n */\nexport class WorkerTimeoutError extends KoraError {\n\tconstructor(operation: string, timeoutMs: number) {\n\t\tsuper(\n\t\t\t`Worker did not respond within ${timeoutMs}ms for operation \"${operation}\"`,\n\t\t\t'WORKER_TIMEOUT',\n\t\t\t{ operation, timeoutMs },\n\t\t)\n\t\tthis.name = 'WorkerTimeoutError'\n\t}\n}\n\n/**\n * Thrown when a follower tab cannot reach a live leader tab over the multi-tab\n * broadcast channel (the leader tab was closed, crashed, or is unresponsive).\n * Distinct from {@link WorkerTimeoutError}: this fails fast on a confirmed-absent\n * leader instead of waiting out the full RPC timeout.\n */\nexport class NoLeaderError extends KoraError {\n\tconstructor(\n\t\toperation: string,\n\t\tmessage = `No live leader tab is answering multi-tab storage RPC for operation \"${operation}\"`,\n\t\tcode = 'NO_LEADER',\n\t\tcontext: Record<string, unknown> = {},\n\t) {\n\t\tsuper(message, code, { operation, ...context })\n\t\tthis.name = 'NoLeaderError'\n\t}\n}\n\n/**\n * Thrown to a follower tab's pending storage requests when the leader tab that\n * was answering stopped sending heartbeats (hung, frozen, or suspended). The\n * request may or may not have been applied: retry it with the same `requestId`\n * (in the error context) and the leader de-duplicates it. Retriable.\n */\nexport class LeaderUnresponsiveError extends NoLeaderError {\n\tconstructor(operation: string, silentMs: number, requestId: string) {\n\t\tsuper(\n\t\t\toperation,\n\t\t\t`The leader tab stopped responding (no heartbeat for ${silentMs}ms) during \"${operation}\". The request may or may not have been applied; retry it with the same requestId and the leader de-duplicates it.`,\n\t\t\t'LEADER_UNRESPONSIVE',\n\t\t\t{ silentMs, requestId, retriable: true },\n\t\t)\n\t\tthis.name = 'LeaderUnresponsiveError'\n\t}\n}\n\n/**\n * Thrown to pending storage requests when their bridge was torn down, typically\n * because this tab was just promoted to leader or the leader handed over.\n * Retriable: the next attempt goes to the new leader.\n */\nexport class BridgeTerminatedError extends KoraError {\n\tconstructor(operation: string, reason: string) {\n\t\tsuper(\n\t\t\t`Storage request \"${operation}\" was interrupted (${reason}); retry it against the new storage leader.`,\n\t\t\t'BRIDGE_TERMINATED',\n\t\t\t{ operation, reason, retriable: true },\n\t\t)\n\t\tthis.name = 'BridgeTerminatedError'\n\t}\n}\n\n/** Thrown when a storage request is cancelled through its `AbortSignal`. */\nexport class RequestAbortedError extends KoraError {\n\tconstructor(operation: string, requestId: string) {\n\t\tsuper(\n\t\t\t`Storage request \"${operation}\" was aborted by the caller. A write may already have reached the leader; retry with the same requestId to stay idempotent.`,\n\t\t\t'REQUEST_ABORTED',\n\t\t\t{ operation, requestId },\n\t\t)\n\t\tthis.name = 'RequestAbortedError'\n\t}\n}\n\n/**\n * Thrown by `deleteDatabase()` when the database is open in some tab or worker\n * on this origin. Close it everywhere (or call `app.close()`) and retry.\n */\nexport class StorageInUseError extends KoraError {\n\tconstructor(dbName: string) {\n\t\tsuper(\n\t\t\t`Database \"${dbName}\" is open in a tab on this origin; close it before deleting it.`,\n\t\t\t'STORAGE_IN_USE',\n\t\t\t{ dbName },\n\t\t)\n\t\tthis.name = 'StorageInUseError'\n\t}\n}\n\n/**\n * Thrown by `deleteDatabase()` when the database still holds operations that\n * never reached the server. Sync first, or pass `{ force: true }` to discard them.\n */\nexport class UnsyncedDataError extends KoraError {\n\tconstructor(dbName: string) {\n\t\tsuper(\n\t\t\t`Database \"${dbName}\" has unsynced operations; deleting it would lose them. Sync first, or pass { force: true } to discard them deliberately.`,\n\t\t\t'UNSYNCED_DATA',\n\t\t\t{ dbName },\n\t\t)\n\t\tthis.name = 'UnsyncedDataError'\n\t}\n}\n\n/**\n * Thrown when a database's data lives in a storage backend this runtime cannot\n * read (for example it was written to OPFS, and OPFS is unavailable now). Kora\n * refuses to start an empty, disjoint copy rather than hide the existing data.\n */\nexport class StorageBackendMismatchError extends KoraError {\n\tconstructor(dbName: string, recorded: string, requested: string, reason: string) {\n\t\tsuper(\n\t\t\t`Database \"${dbName}\" is stored in ${recorded}, but this session can only use ${requested} (${reason}). Kora will not start a separate empty copy; reload in a browser profile where ${recorded} is available.`,\n\t\t\t'STORAGE_BACKEND_MISMATCH',\n\t\t\t{ dbName, recorded, requested, reason },\n\t\t)\n\t\tthis.name = 'StorageBackendMismatchError'\n\t}\n}\n\n/**\n * Thrown when IndexedDB persistence operations fail (serialize/deserialize).\n */\nexport class PersistenceError extends KoraError {\n\tconstructor(message: string, context?: Record<string, unknown>) {\n\t\tsuper(`Persistence error: ${message}`, 'PERSISTENCE_ERROR', context)\n\t\tthis.name = 'PersistenceError'\n\t}\n}\n\n/**\n * Thrown by a local write while the store's pinned node id (an auth device id) belongs\n * to another user than the one signed in (F9): a browser two users shared. The write\n * could only be stored under the owner's node and would later upload as the owner's.\n */\nexport class NodeOwnedByAnotherUserError extends KoraError {\n\tconstructor(nodeId: string, principal: string | null) {\n\t\tsuper(\n\t\t\t\"This device belongs to another account, so changes made while this account is signed in here cannot be saved: they would be uploaded as the device owner's. Sign in as the device's owner, or use this account on its own device.\",\n\t\t\t'NODE_OWNED_BY_ANOTHER_USER',\n\t\t\t{ nodeId, principal },\n\t\t)\n\t\tthis.name = 'NodeOwnedByAnotherUserError'\n\t}\n}\n","/**\n * Multi-tab SQLite storage coordination via `navigator.locks` and `BroadcastChannel`.\n *\n * One tab holds the exclusive `kora-leader-${dbName}` lock and owns the SQLite\n * worker. Other tabs send worker RPC over a named broadcast channel. In browsers\n * the leader's WORKER answers that channel itself ({@link startWorkerRpcRelay}),\n * so a hung leader main thread never blocks other tabs, and it pushes heartbeats\n * (also from inside long statements) that followers use to detect a hung or\n * frozen leader (NEW-STORE-9).\n */\n\nimport { leaderLockName, storageChannelName } from '../adapters/opfs-names'\nimport type {\n\tWorkerBridge,\n\tWorkerRequest,\n\tWorkerResponse,\n\tWorkerSendOptions,\n} from '../adapters/sqlite-wasm-channel'\nimport {\n\tBridgeTerminatedError,\n\tLeaderUnresponsiveError,\n\tNoLeaderError,\n\tRequestAbortedError,\n\tWorkerTimeoutError,\n} from '../errors'\n\nconst RPC_REQUEST = 'kora-worker-request'\nconst RPC_RESPONSE = 'kora-worker-response'\nconst CLIENT_LEAVE = 'kora-client-leave'\nconst LEADER_PING = 'kora-leader-ping'\nconst LEADER_PONG = 'kora-leader-pong'\nconst LEADER_HEARTBEAT = 'kora-leader-heartbeat'\n\n/** Default interval between leader heartbeats and follower watchdog ticks. */\nexport const DEFAULT_HEARTBEAT_MS = 1000\n/** Missed heartbeat intervals after which a follower gives up on a leader it heard. */\nconst MISSED_BEATS_UNRESPONSIVE = 3\n/** Silent intervals after which a follower that never heard a leader gives up. */\nconst MISSED_BEATS_ABSENT = 2\n/** Default idle budget for a client that owns an open transaction span. */\nconst DEFAULT_TRANSACTION_IDLE_TIMEOUT_MS = 10_000\n/** Completed follower responses remembered for de-duplicating retried requests. */\nconst DEDUP_CAPACITY = 512\n\ninterface RpcRequestMessage {\n\ttype: typeof RPC_REQUEST\n\trequestId: string\n\tclientId: string\n\trequest: WorkerRequest\n}\n\ninterface RpcResponseMessage {\n\ttype: typeof RPC_RESPONSE\n\trequestId: string\n\tresponse: WorkerResponse\n}\n\ninterface ClientLeaveMessage {\n\ttype: typeof CLIENT_LEAVE\n\tclientId: string\n}\n\ninterface HeartbeatMessage {\n\ttype: typeof LEADER_HEARTBEAT\n\tepoch: string\n\tsource: 'main' | 'worker'\n}\n\ntype ChannelMessage =\n\t| RpcRequestMessage\n\t| RpcResponseMessage\n\t| ClientLeaveMessage\n\t| HeartbeatMessage\n\t| { type: typeof LEADER_PING }\n\t| { type: typeof LEADER_PONG }\n\ninterface ReclaimingWorkerBridge extends WorkerBridge {\n\treclaimClient(clientId: string, reason: string): void\n}\n\nexport type TabStorageRole = 'leader' | 'follower'\n\nexport interface AcquireTabStorageOptions {\n\t/**\n\t * Invoked when a follower is promoted to leader because the previous leader\n\t * released the lock (its tab closed, crashed, or suspended itself on freeze).\n\t * The adapter uses this to rebuild its bridge as a leader. Never fires for a\n\t * tab that started as leader.\n\t */\n\tonPromote?: () => void\n}\n\nexport interface TabStorageSession {\n\trole: TabStorageRole\n\tchannelName: string\n\t/** Leader only: release the navigator lock when closing the database. */\n\treleaseLock?: () => Promise<void>\n\t/** Leader only: stop the broadcast RPC relay or heartbeat. */\n\tstopRelay?: () => void\n\t/**\n\t * Follower only: cancels the queued lock request (or, once promoted, releases\n\t * the lock this tab won). Present so callers can tear the session down.\n\t */\n\tcancelPromotionWatch?: () => void\n}\n\n/**\n * Returns whether multi-tab coordination APIs exist in this runtime.\n */\nexport function isMultiTabStorageSupported(): boolean {\n\treturn (\n\t\ttypeof globalThis !== 'undefined' &&\n\t\ttypeof BroadcastChannel !== 'undefined' &&\n\t\ttypeof navigator !== 'undefined' &&\n\t\ttypeof navigator.locks?.request === 'function'\n\t)\n}\n\n/**\n * Resolve leader vs follower for a database name.\n * Without lock APIs, every instance is treated as leader (single-tab / Node).\n *\n * A follower additionally queues a blocking request for the same lock. When the\n * current leader releases it (its tab closed, crashed, or suspended on freeze),\n * the browser grants the lock to this follower and\n * {@link AcquireTabStorageOptions.onPromote} fires so the adapter can rebuild\n * itself as the new leader. Kora never uses the Web Locks `steal` option: a\n * frozen leader's worker would still hold the OPFS file handles.\n */\nexport async function acquireTabStorageSession(\n\tdbName: string,\n\toptions?: AcquireTabStorageOptions,\n): Promise<TabStorageSession> {\n\tconst channelName = storageChannelName(dbName)\n\tconst lockName = leaderLockName(dbName)\n\n\tif (!isMultiTabStorageSupported()) {\n\t\treturn { role: 'leader', channelName }\n\t}\n\n\treturn new Promise<TabStorageSession>((resolve) => {\n\t\tlet releaseHeld: (() => void) | undefined\n\n\t\tvoid navigator.locks.request(lockName, { mode: 'exclusive', ifAvailable: true }, (lock) => {\n\t\t\tif (lock === null) {\n\t\t\t\tresolve(startFollowerSession(channelName, lockName, options))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tresolve({\n\t\t\t\trole: 'leader',\n\t\t\t\tchannelName,\n\t\t\t\treleaseLock: async () => {\n\t\t\t\t\treleaseHeld?.()\n\t\t\t\t},\n\t\t\t})\n\n\t\t\treturn new Promise<void>((release) => {\n\t\t\t\treleaseHeld = release\n\t\t\t})\n\t\t})\n\t})\n}\n\n/**\n * Builds a follower session and starts watching for promotion. The follower holds\n * a queued lock request; when it is finally granted (old leader gone), it becomes\n * the leader for the rest of its lifetime and notifies via `onPromote`.\n */\nfunction startFollowerSession(\n\tchannelName: string,\n\tlockName: string,\n\toptions?: AcquireTabStorageOptions,\n): TabStorageSession {\n\tlet promoted = false\n\tlet releasePromotedLock: (() => void) | undefined\n\tconst abort = new AbortController()\n\n\tvoid navigator.locks\n\t\t.request(lockName, { mode: 'exclusive', signal: abort.signal }, () => {\n\t\t\t// Reaching here means the previous leader released the lock and this tab\n\t\t\t// won it. Hold it (never-resolving promise) so this tab is now the leader.\n\t\t\tpromoted = true\n\t\t\toptions?.onPromote?.()\n\t\t\treturn new Promise<void>((release) => {\n\t\t\t\treleasePromotedLock = release\n\t\t\t})\n\t\t})\n\t\t.catch(() => {\n\t\t\t// AbortError when the tab closes before promotion. Nothing to do.\n\t\t})\n\n\treturn {\n\t\trole: 'follower',\n\t\tchannelName,\n\t\tcancelPromotionWatch: () => {\n\t\t\tif (promoted) {\n\t\t\t\treleasePromotedLock?.()\n\t\t\t} else {\n\t\t\t\tabort.abort()\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Answers follower RPC with a bounded response cache keyed by request id, so a\n * follower that retries a request it lost track of (leader looked unresponsive,\n * caller aborted) gets the original response instead of a second application.\n */\nfunction createRequestDeduper(): {\n\trun(requestId: string, start: () => Promise<WorkerResponse>): Promise<WorkerResponse> | null\n} {\n\tconst inflight = new Map<string, Promise<WorkerResponse>>()\n\tconst completed = new Map<string, WorkerResponse>()\n\treturn {\n\t\trun(requestId, start) {\n\t\t\tconst done = completed.get(requestId)\n\t\t\tif (done) return Promise.resolve(done)\n\t\t\t// Already running: the original response is broadcast when it lands.\n\t\t\tif (inflight.has(requestId)) return null\n\t\t\tconst promise = start().then((response) => {\n\t\t\t\tinflight.delete(requestId)\n\t\t\t\tcompleted.set(requestId, response)\n\t\t\t\tif (completed.size > DEDUP_CAPACITY) {\n\t\t\t\t\tconst oldest = completed.keys().next().value\n\t\t\t\t\tif (oldest !== undefined) completed.delete(oldest)\n\t\t\t\t}\n\t\t\t\treturn response\n\t\t\t})\n\t\t\tinflight.set(requestId, promise)\n\t\t\treturn promise\n\t\t},\n\t}\n}\n\n/** Options for the leader-side relays. */\nexport interface LeaderRelayOptions {\n\t/** Heartbeat interval; 0 disables heartbeats. */\n\theartbeatMs?: number\n}\n\n/** Handle returned by {@link startWorkerRpcRelay}. */\nexport interface WorkerRpcRelay {\n\t/** Push a heartbeat now (called from inside long statements). */\n\tbeat(): void\n\tstop(): void\n}\n\nfunction serveChannel(\n\tchannel: BroadcastChannel,\n\tbridge: WorkerBridge,\n\tsource: 'main' | 'worker',\n\theartbeatMs: number,\n): WorkerRpcRelay {\n\tconst epoch = createClientId()\n\tconst dedup = createRequestDeduper()\n\tconst beat = (): void => {\n\t\tconst message: HeartbeatMessage = { type: LEADER_HEARTBEAT, epoch, source }\n\t\tchannel.postMessage(message)\n\t}\n\tconst respond = (requestId: string, response: WorkerResponse): void => {\n\t\tconst msg: RpcResponseMessage = { type: RPC_RESPONSE, requestId, response }\n\t\tchannel.postMessage(msg)\n\t}\n\n\tconst onMessage = (event: MessageEvent<ChannelMessage>): void => {\n\t\tconst data = event.data\n\t\t// Answer liveness probes immediately, without touching the database.\n\t\tif (data?.type === LEADER_PING) {\n\t\t\tchannel.postMessage({ type: LEADER_PONG })\n\t\t\treturn\n\t\t}\n\t\tif (data?.type === CLIENT_LEAVE) {\n\t\t\tif (hasReclaimClient(bridge)) {\n\t\t\t\tbridge.reclaimClient(data.clientId, 'client-left')\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif (data?.type !== RPC_REQUEST) {\n\t\t\treturn\n\t\t}\n\t\tif (\n\t\t\tdata.request.type === 'close' ||\n\t\t\tdata.request.type === 'destroy' ||\n\t\t\tdata.request.type === 'serve'\n\t\t) {\n\t\t\t// Only the owning tab may close or remove the database; a follower\n\t\t\t// leaving just stops sending requests.\n\t\t\trespond(data.requestId, { id: data.request.id, type: 'success' })\n\t\t\treturn\n\t\t}\n\n\t\tconst pending = dedup.run(data.requestId, () =>\n\t\t\tbridge.send(data.request, data.clientId).catch(\n\t\t\t\t(error: unknown): WorkerResponse => ({\n\t\t\t\t\tid: data.request.id,\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tmessage: error instanceof Error ? error.message : 'Worker RPC failed',\n\t\t\t\t\tcode: 'LEADER_RPC_ERROR',\n\t\t\t\t}),\n\t\t\t),\n\t\t)\n\t\tvoid pending?.then((response) => respond(data.requestId, response))\n\t}\n\n\tchannel.addEventListener('message', onMessage)\n\tconst interval = heartbeatMs > 0 ? setInterval(beat, heartbeatMs) : undefined\n\tif (heartbeatMs > 0) beat()\n\treturn {\n\t\tbeat,\n\t\tstop: () => {\n\t\t\tif (interval !== undefined) clearInterval(interval)\n\t\t\tchannel.removeEventListener('message', onMessage)\n\t\t\tchannel.close()\n\t\t},\n\t}\n}\n\n/**\n * Leader main thread: forward follower RPC to the worker bridge. Used where the\n * worker cannot host the relay itself (Node tests, custom bridges). Heartbeats\n * are off by default here; pass `heartbeatMs` to enable them.\n */\nexport function startLeaderRpcRelay(\n\tchannelName: string,\n\tbridge: WorkerBridge,\n\toptions: LeaderRelayOptions = {},\n): () => void {\n\treturn serveChannel(new BroadcastChannel(channelName), bridge, 'main', options.heartbeatMs ?? 0)\n\t\t.stop\n}\n\n/**\n * Leader WORKER: answer follower RPC, pings and heartbeats from inside the\n * dedicated SQLite worker, through the worker's transaction serializer. The\n * leader tab's main thread is not on this path, so a hung or busy main thread\n * does not stall other tabs (NEW-STORE-9).\n */\nexport function startWorkerRpcRelay(\n\tchannelName: string,\n\tbridge: WorkerBridge,\n\toptions: LeaderRelayOptions = {},\n): WorkerRpcRelay {\n\treturn serveChannel(\n\t\tnew BroadcastChannel(channelName),\n\t\tbridge,\n\t\t'worker',\n\t\toptions.heartbeatMs ?? DEFAULT_HEARTBEAT_MS,\n\t)\n}\n\ninterface PendingFollowerRequest {\n\ttype: string\n\tresolve: (r: WorkerResponse) => void\n\treject: (e: Error) => void\n}\n\n/**\n * Follower tab: proxy {@link WorkerBridge} over BroadcastChannel to the leader.\n *\n * A single watchdog runs while requests are pending. Any message from the leader\n * (heartbeat, pong, response) is a sign of life; when the leader goes quiet the\n * watchdog pings it, and after {@link MISSED_BEATS_UNRESPONSIVE} silent intervals\n * it fails every pending request with a typed, retriable\n * {@link LeaderUnresponsiveError} (or {@link NoLeaderError} if no leader was\n * heard at all) instead of waiting out the full RPC timeout.\n */\nexport class FollowerBroadcastBridge implements WorkerBridge {\n\tprivate readonly channel: BroadcastChannel\n\tprivate readonly clientId = createClientId()\n\tprivate readonly onPageHide = (): void => {\n\t\tthis.leaveLeader()\n\t}\n\tprivate readonly pending = new Map<string, PendingFollowerRequest>()\n\tprivate readonly timeoutMs: number\n\tprivate readonly heartbeatMs: number\n\tprivate terminated = false\n\tprivate lastHeardAt = 0\n\tprivate heardSinceWatch = false\n\tprivate watchdog: ReturnType<typeof setInterval> | undefined\n\n\t/**\n\t * @param channelName - Storage channel of the database\n\t * @param timeoutMs - Hard ceiling per request (default 30s)\n\t * @param heartbeatMs - Expected leader heartbeat interval, also the watchdog tick\n\t */\n\tconstructor(channelName: string, timeoutMs = 30000, heartbeatMs = DEFAULT_HEARTBEAT_MS) {\n\t\tthis.timeoutMs = timeoutMs\n\t\tthis.heartbeatMs = Math.max(10, Math.min(heartbeatMs, timeoutMs))\n\t\tthis.channel = new BroadcastChannel(channelName)\n\t\tif (typeof addEventListener === 'function') {\n\t\t\taddEventListener('pagehide', this.onPageHide)\n\t\t}\n\t\tthis.channel.addEventListener('message', (event: MessageEvent<ChannelMessage>) => {\n\t\t\tconst data = event.data\n\t\t\tif (\n\t\t\t\tdata?.type === LEADER_HEARTBEAT ||\n\t\t\t\tdata?.type === LEADER_PONG ||\n\t\t\t\tdata?.type === RPC_RESPONSE\n\t\t\t) {\n\t\t\t\tthis.markHeard()\n\t\t\t}\n\t\t\tif (data?.type !== RPC_RESPONSE) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst entry = this.pending.get(data.requestId)\n\t\t\tif (entry) {\n\t\t\t\tthis.pending.delete(data.requestId)\n\t\t\t\tentry.resolve(data.response)\n\t\t\t}\n\t\t})\n\t}\n\n\t/**\n\t * Readiness handshake: resolves `true` as soon as a live leader answers a ping,\n\t * or `false` if none answers within the budget. The adapter calls this before\n\t * its first RPC so a follower created before a leader relay is live retries the\n\t * handshake instead of firing into the void.\n\t */\n\tasync waitForLeader(timeoutMs = 3000, attempts = 3): Promise<boolean> {\n\t\tconst perAttempt = Math.max(50, Math.floor(timeoutMs / Math.max(1, attempts)))\n\t\tfor (let attempt = 0; attempt < attempts; attempt += 1) {\n\t\t\tif (this.terminated) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tif (await this.pingLeader(perAttempt)) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\n\t/** Sends one liveness ping and resolves whether a leader answered in time. */\n\tprivate pingLeader(timeoutMs: number): Promise<boolean> {\n\t\tif (this.terminated) {\n\t\t\treturn Promise.resolve(false)\n\t\t}\n\t\treturn new Promise<boolean>((resolve) => {\n\t\t\tconst onPong = (event: MessageEvent<{ type: string }>): void => {\n\t\t\t\tif (event.data?.type === LEADER_PONG) {\n\t\t\t\t\tcleanup()\n\t\t\t\t\tresolve(true)\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst cleanup = (): void => {\n\t\t\t\tclearTimeout(timer)\n\t\t\t\tthis.channel.removeEventListener('message', onPong)\n\t\t\t}\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tcleanup()\n\t\t\t\tresolve(false)\n\t\t\t}, timeoutMs)\n\t\t\tthis.channel.addEventListener('message', onPong)\n\t\t\tthis.channel.postMessage({ type: LEADER_PING })\n\t\t})\n\t}\n\n\tasync send(\n\t\trequest: WorkerRequest,\n\t\t_clientId?: string,\n\t\toptions: WorkerSendOptions = {},\n\t): Promise<WorkerResponse> {\n\t\tif (this.terminated) {\n\t\t\treturn {\n\t\t\t\tid: request.id,\n\t\t\t\ttype: 'error',\n\t\t\t\tmessage: 'Follower bridge terminated',\n\t\t\t\tcode: 'BRIDGE_TERMINATED',\n\t\t\t}\n\t\t}\n\n\t\tconst requestId = request.requestId ?? createClientId()\n\t\tconst signal = options.signal\n\t\tif (signal?.aborted) {\n\t\t\tthrow new RequestAbortedError(request.type, requestId)\n\t\t}\n\t\tconst msg: RpcRequestMessage = {\n\t\t\ttype: RPC_REQUEST,\n\t\t\trequestId,\n\t\t\tclientId: this.clientId,\n\t\t\trequest: { ...request, requestId },\n\t\t}\n\t\tconst timeoutMs = options.timeoutMs ?? this.timeoutMs\n\n\t\treturn new Promise<WorkerResponse>((resolve, reject) => {\n\t\t\tlet timer: ReturnType<typeof setTimeout> | undefined\n\t\t\tconst onAbort = (): void => {\n\t\t\t\tsettle(() => reject(new RequestAbortedError(request.type, requestId)))\n\t\t\t}\n\t\t\tconst settle = (fn: () => void): void => {\n\t\t\t\tif (timer !== undefined) clearTimeout(timer)\n\t\t\t\tsignal?.removeEventListener('abort', onAbort)\n\t\t\t\tthis.pending.delete(requestId)\n\t\t\t\tthis.stopWatchdogIfIdle()\n\t\t\t\tfn()\n\t\t\t}\n\t\t\tif (Number.isFinite(timeoutMs)) {\n\t\t\t\ttimer = setTimeout(() => {\n\t\t\t\t\tsettle(() => reject(new WorkerTimeoutError(`follower-rpc:${request.type}`, timeoutMs)))\n\t\t\t\t}, timeoutMs)\n\t\t\t}\n\t\t\tsignal?.addEventListener('abort', onAbort, { once: true })\n\n\t\t\tthis.pending.set(requestId, {\n\t\t\t\ttype: request.type,\n\t\t\t\tresolve: (response) => settle(() => resolve(response)),\n\t\t\t\treject: (error) => settle(() => reject(error)),\n\t\t\t})\n\t\t\tthis.startWatchdog()\n\t\t\tthis.channel.postMessage(msg)\n\t\t})\n\t}\n\n\tterminate(): void {\n\t\tif (this.terminated) {\n\t\t\treturn\n\t\t}\n\t\tthis.terminated = true\n\t\tthis.leaveLeader()\n\t\tif (typeof removeEventListener === 'function') {\n\t\t\tremoveEventListener('pagehide', this.onPageHide)\n\t\t}\n\t\tthis.channel.close()\n\t\tfor (const [, entry] of [...this.pending]) {\n\t\t\tentry.reject(new BridgeTerminatedError(entry.type, 'follower bridge terminated'))\n\t\t}\n\t\tthis.pending.clear()\n\t\tthis.stopWatchdog()\n\t}\n\n\tprivate markHeard(): void {\n\t\tthis.lastHeardAt = Date.now()\n\t\tthis.heardSinceWatch = true\n\t}\n\n\tprivate startWatchdog(): void {\n\t\tif (this.watchdog !== undefined) return\n\t\t// A recent beat means a leader is known to exist; otherwise the silence\n\t\t// clock starts now.\n\t\tconst now = Date.now()\n\t\tthis.heardSinceWatch = now - this.lastHeardAt < this.heartbeatMs * 2\n\t\tif (!this.heardSinceWatch) this.lastHeardAt = now\n\t\tthis.watchdog = setInterval(() => this.checkLiveness(), this.heartbeatMs)\n\t}\n\n\tprivate stopWatchdogIfIdle(): void {\n\t\tif (this.pending.size === 0) this.stopWatchdog()\n\t}\n\n\tprivate stopWatchdog(): void {\n\t\tif (this.watchdog !== undefined) {\n\t\t\tclearInterval(this.watchdog)\n\t\t\tthis.watchdog = undefined\n\t\t}\n\t}\n\n\tprivate checkLiveness(): void {\n\t\tif (this.pending.size === 0) {\n\t\t\tthis.stopWatchdog()\n\t\t\treturn\n\t\t}\n\t\tconst silentMs = Date.now() - this.lastHeardAt\n\t\tconst heard = this.heardSinceWatch\n\t\tconst limit = this.heartbeatMs * (heard ? MISSED_BEATS_UNRESPONSIVE : MISSED_BEATS_ABSENT)\n\t\tif (silentMs >= limit) {\n\t\t\tfor (const [requestId, entry] of [...this.pending]) {\n\t\t\t\tentry.reject(\n\t\t\t\t\theard\n\t\t\t\t\t\t? new LeaderUnresponsiveError(`follower-rpc:${entry.type}`, silentMs, requestId)\n\t\t\t\t\t\t: new NoLeaderError(`follower-rpc:${entry.type}`),\n\t\t\t\t)\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tif (silentMs >= this.heartbeatMs) {\n\t\t\t// Pull a sign of life from leaders that do not push heartbeats.\n\t\t\tthis.channel.postMessage({ type: LEADER_PING })\n\t\t}\n\t}\n\n\tprivate leaveLeader(): void {\n\t\ttry {\n\t\t\tconst message: ClientLeaveMessage = {\n\t\t\t\ttype: CLIENT_LEAVE,\n\t\t\t\tclientId: this.clientId,\n\t\t\t}\n\t\t\tthis.channel.postMessage(message)\n\t\t} catch {\n\t\t\t// Best effort: the serializer's idle rollback is the guaranteed backstop.\n\t\t}\n\t}\n}\n\ninterface QueuedWorkerRequest {\n\tclientId: string\n\trequest: WorkerRequest\n\tresolve: (response: WorkerResponse) => void\n\treject: (error: Error) => void\n}\n\n/**\n * Serializes one SQLite worker across the leader tab and all follower tabs.\n *\n * SQLite transactions are represented as multiple worker messages\n * (`begin`, one or more reads/writes, then `commit`/`rollback`). Per-tab mutexes\n * cannot protect that span because follower messages converge at the leader.\n * This bridge promotes the worker boundary into the serialization point, so no\n * other client can interleave while a client owns an active transaction.\n */\nexport class TransactionSerializingWorkerBridge implements WorkerBridge {\n\tprivate readonly inner: WorkerBridge\n\tprivate readonly leaderClientId = createClientId()\n\tprivate readonly transactionIdleTimeoutMs: number\n\tprivate queue: QueuedWorkerRequest[] = []\n\tprivate activeTransactionClient: string | null = null\n\tprivate abortedClients = new Set<string>()\n\tprivate transactionIdleTimer: ReturnType<typeof setTimeout> | null = null\n\tprivate reclaiming = false\n\tprivate processing = false\n\tprivate terminated = false\n\n\tconstructor(inner: WorkerBridge, transactionIdleTimeoutMs = DEFAULT_TRANSACTION_IDLE_TIMEOUT_MS) {\n\t\tthis.inner = inner\n\t\tthis.transactionIdleTimeoutMs = transactionIdleTimeoutMs\n\t}\n\n\tsend(request: WorkerRequest, clientId = this.leaderClientId): Promise<WorkerResponse> {\n\t\tif (this.terminated) {\n\t\t\treturn Promise.resolve({\n\t\t\t\tid: request.id,\n\t\t\t\ttype: 'error',\n\t\t\t\tmessage: 'Worker has been terminated',\n\t\t\t\tcode: 'WORKER_TERMINATED',\n\t\t\t})\n\t\t}\n\t\tif (this.abortedClients.has(clientId)) {\n\t\t\tif (request.type === 'begin') {\n\t\t\t\tthis.abortedClients.delete(clientId)\n\t\t\t} else {\n\t\t\t\treturn Promise.resolve({\n\t\t\t\t\tid: request.id,\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tmessage: 'Previous transaction was aborted because the client stopped sending requests.',\n\t\t\t\t\tcode: 'TRANSACTION_ABORTED',\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\treturn new Promise<WorkerResponse>((resolve, reject) => {\n\t\t\tthis.queue.push({ clientId, request, resolve, reject })\n\t\t\tvoid this.processQueue()\n\t\t})\n\t}\n\n\tterminate(): void {\n\t\tif (this.terminated) {\n\t\t\treturn\n\t\t}\n\t\tthis.terminated = true\n\t\tthis.clearTransactionIdleTimer()\n\t\tthis.inner.terminate()\n\t\tconst pending = this.queue.splice(0)\n\t\tfor (const entry of pending) {\n\t\t\tentry.reject(new BridgeTerminatedError(entry.request.type, 'worker terminated'))\n\t\t}\n\t}\n\n\treclaimClient(clientId: string, reason: string): void {\n\t\tif (this.terminated) {\n\t\t\treturn\n\t\t}\n\t\tvoid this.reclaimClientNow(clientId, reason)\n\t}\n\n\tprivate async processQueue(): Promise<void> {\n\t\tif (this.processing || this.reclaiming) {\n\t\t\treturn\n\t\t}\n\t\tthis.processing = true\n\t\ttry {\n\t\t\twhile (!this.terminated) {\n\t\t\t\tconst index = this.nextRunnableIndex()\n\t\t\t\tif (index === -1) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tconst [entry] = this.queue.splice(index, 1)\n\t\t\t\tif (!entry) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await this.inner.send(entry.request)\n\t\t\t\t\tthis.recordTransactionState(entry, response)\n\t\t\t\t\tentry.resolve(response)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tthis.resetFailedTransaction(entry)\n\t\t\t\t\tentry.reject(error instanceof Error ? error : new Error(String(error)))\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.processing = false\n\t\t\tif (!this.terminated && !this.reclaiming && this.nextRunnableIndex() !== -1) {\n\t\t\t\tvoid this.processQueue()\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate nextRunnableIndex(): number {\n\t\tif (this.queue.length === 0) {\n\t\t\treturn -1\n\t\t}\n\t\tif (this.activeTransactionClient === null) {\n\t\t\treturn 0\n\t\t}\n\t\treturn this.queue.findIndex((entry) => entry.clientId === this.activeTransactionClient)\n\t}\n\n\tprivate recordTransactionState(entry: QueuedWorkerRequest, response: WorkerResponse): void {\n\t\tif (response.type === 'error') {\n\t\t\tthis.resetFailedTransaction(entry)\n\t\t\treturn\n\t\t}\n\t\tif (entry.request.type === 'begin') {\n\t\t\tthis.activeTransactionClient = entry.clientId\n\t\t\tthis.armTransactionIdleTimer(entry.clientId)\n\t\t\treturn\n\t\t}\n\t\tif (this.activeTransactionClient === entry.clientId) {\n\t\t\tthis.armTransactionIdleTimer(entry.clientId)\n\t\t}\n\t\tif (\n\t\t\tthis.activeTransactionClient === entry.clientId &&\n\t\t\t(entry.request.type === 'commit' || entry.request.type === 'rollback')\n\t\t) {\n\t\t\tthis.activeTransactionClient = null\n\t\t\tthis.clearTransactionIdleTimer()\n\t\t}\n\t}\n\n\tprivate resetFailedTransaction(entry: QueuedWorkerRequest): void {\n\t\tif (this.activeTransactionClient === entry.clientId) {\n\t\t\tthis.activeTransactionClient = null\n\t\t\tthis.clearTransactionIdleTimer()\n\t\t}\n\t}\n\n\tprivate armTransactionIdleTimer(clientId: string): void {\n\t\tthis.clearTransactionIdleTimer()\n\t\tthis.transactionIdleTimer = setTimeout(() => {\n\t\t\tvoid this.reclaimClientNow(clientId, 'transaction-idle-timeout')\n\t\t}, this.transactionIdleTimeoutMs)\n\t}\n\n\tprivate clearTransactionIdleTimer(): void {\n\t\tif (this.transactionIdleTimer) {\n\t\t\tclearTimeout(this.transactionIdleTimer)\n\t\t\tthis.transactionIdleTimer = null\n\t\t}\n\t}\n\n\tprivate async reclaimClientNow(clientId: string, reason: string): Promise<void> {\n\t\tif (this.reclaiming || this.activeTransactionClient !== clientId) {\n\t\t\treturn\n\t\t}\n\t\tthis.reclaiming = true\n\t\tthis.clearTransactionIdleTimer()\n\t\tthis.activeTransactionClient = null\n\t\tthis.abortedClients.add(clientId)\n\t\tthis.rejectQueuedClientRequests(clientId, reason)\n\n\t\ttry {\n\t\t\tconst response = await this.inner.send({ id: 0, type: 'rollback' })\n\t\t\tif (response.type === 'error') {\n\t\t\t\t// If there was no active SQLite transaction left, the important state\n\t\t\t\t// is already reclaimed in JS. The next request will establish a fresh\n\t\t\t\t// transaction if needed.\n\t\t\t}\n\t\t} catch {\n\t\t\t// Keep the queue moving. The inner worker will surface any unrecoverable\n\t\t\t// state through subsequent requests.\n\t\t} finally {\n\t\t\tthis.reclaiming = false\n\t\t\tif (!this.terminated && this.nextRunnableIndex() !== -1) {\n\t\t\t\tvoid this.processQueue()\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate rejectQueuedClientRequests(clientId: string, reason: string): void {\n\t\tconst keep: QueuedWorkerRequest[] = []\n\t\tfor (const entry of this.queue) {\n\t\t\tif (entry.clientId === clientId) {\n\t\t\t\tentry.resolve({\n\t\t\t\t\tid: entry.request.id,\n\t\t\t\t\ttype: 'error',\n\t\t\t\t\tmessage: `Transaction client was reclaimed: ${reason}`,\n\t\t\t\t\tcode: 'TRANSACTION_ABORTED',\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tkeep.push(entry)\n\t\t\t}\n\t\t}\n\t\tthis.queue = keep\n\t}\n}\n\nfunction hasReclaimClient(bridge: WorkerBridge): bridge is ReclaimingWorkerBridge {\n\treturn typeof (bridge as Partial<ReclaimingWorkerBridge>).reclaimClient === 'function'\n}\n\nfunction createClientId(): string {\n\tif (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n\t\treturn crypto.randomUUID()\n\t}\n\treturn `${Date.now()}-${Math.random().toString(36).slice(2)}`\n}\n","/**\n * The schema-version ceiling (RT-109): a build never runs against a database a newer\n * build already migrated.\n *\n * `generateFullDDL` emits `--kora:schema-ceiling <version>` right after `_kora_meta`, before\n * any other DDL. It is a SQL comment; Kora's DDL executors (the native adapter, the SQLite\n * WASM worker, the test bridge) stop there with {@link schemaAheadMessage} when the\n * database's stored schema version is newer, so not even this build's additive DDL touches\n * it. The store then maps the failure to `SchemaVersionAheadError` and checks again after\n * every open (adapters that restore data after their DDL, such as the IndexedDB fallback).\n *\n * No imports: the SQLite WASM worker bundles this file.\n */\n\nconst DIRECTIVE = /^--kora:schema-ceiling (\\d+)$/\nconst AHEAD = /KORA_SCHEMA_VERSION_AHEAD stored=(\\d+) code=(\\d+)/\n\n/** The version a `--kora:schema-ceiling <version>` DDL statement names, or null. */\nexport function parseSchemaCeiling(sql: string): number | null {\n\tconst match = DIRECTIVE.exec(sql.trim())\n\treturn match ? Number(match[1]) : null\n}\n\n/** The stored version from a `_kora_meta` row read, or 0 when none is stored. */\nexport function storedSchemaVersion(rows: ReadonlyArray<{ value: unknown }>): number {\n\tconst value = Number(rows[0]?.value ?? 0)\n\treturn Number.isFinite(value) ? value : 0\n}\n\n/** The message a DDL executor throws when the database is ahead (parsed back by the store). */\nexport function schemaAheadMessage(stored: number, code: number): string {\n\treturn `KORA_SCHEMA_VERSION_AHEAD stored=${stored} code=${code}: the database was migrated by a newer build (schema version ${stored}); this build's schema version is ${code}.`\n}\n\n/** The versions in an error raised by {@link schemaAheadMessage}, wherever it was wrapped. */\nexport function parseSchemaAhead(error: unknown): { stored: number; code: number } | null {\n\tconst seen = new Set<unknown>()\n\tlet current: unknown = error\n\twhile (current !== null && current !== undefined && !seen.has(current)) {\n\t\tseen.add(current)\n\t\tconst text =\n\t\t\tcurrent instanceof Error ? current.message : typeof current === 'string' ? current : ''\n\t\tconst match = AHEAD.exec(text)\n\t\tif (match) return { stored: Number(match[1]), code: Number(match[2]) }\n\t\tcurrent = current instanceof Error ? (current as { cause?: unknown }).cause : null\n\t}\n\treturn null\n}\n\n/** Query the stored schema version through a synchronous runner (DDL executors). */\nexport const STORED_SCHEMA_VERSION_SQL = \"SELECT value FROM _kora_meta WHERE key = 'schema_version'\"\n","/// <reference lib=\"webworker\" />\n/**\n * Worker-side OPFS pool ownership (W8a, NEW-STORE-5/7/8/10).\n *\n * Durable storage on the web is a protocol between tabs, workers and the\n * SyncAccessHandle (SAH) pool, not a single open call:\n *\n * - One pool per database ({@link opfsPoolNameFor}); the tab leader lock already\n * makes one tab the owner of each database.\n * - The worker that installs a pool holds the Web Lock named after it for the\n * pool's whole life. Termination of that worker releases both the lock and the\n * file handles, so the next owner waits on the lock instead of racing.\n * - The lock can be released a few milliseconds before the browser finishes\n * closing the old worker's handles (measured ~90ms). Before installing, the\n * new owner therefore probes every pool file for a free handle and waits.\n * This matters for correctness, not just speed: when sqlite-wasm's install\n * fails half-way it calls `removeVfs()`, which recursively deletes the pool\n * directory's files that are not locked. Installing only once every handle is\n * free keeps that path unreachable.\n * - Contention with a live holder is a wait plus a reported blocking state,\n * never a fallback to non-durable storage.\n *\n * This file runs inside the dedicated SQLite worker; it is exercised by the\n * real-browser suite (tests/repro/browser), not by Node unit tests.\n */\n\nimport { LEGACY_OPFS_POOL_NAME, opfsPoolDirectory, opfsPoolLockName } from './opfs-names'\n\n/** The subset of sqlite-wasm's oo1.DB the worker uses. */\nexport interface SqliteDb {\n\texec(opts: {\n\t\tsql: string\n\t\tbind?: unknown[]\n\t\treturnValue?: string\n\t\trowMode?: string\n\t\tcallback?: (row: Record<string, unknown>) => void\n\t}): unknown\n\tclose(): void\n\tpointer?: number\n\tdeserialize?: (data: Uint8Array) => void\n}\n\n/** The subset of sqlite-wasm's OpfsSAHPoolUtil the worker uses. */\nexport interface OpfsPool {\n\tOpfsSAHPoolDb: new (filename: string) => SqliteDb\n\tgetFileCount(): number\n\tgetFileNames(): string[]\n\tgetCapacity(): number\n\treserveMinimumCapacity(min: number): Promise<number>\n\texportFile(name: string): Uint8Array\n\timportDb(name: string, bytes: Uint8Array): number | Promise<number>\n\tunlink(name: string): boolean\n\tremoveVfs(): Promise<boolean>\n\tpauseVfs(): unknown\n\tunpauseVfs(): Promise<unknown>\n\tisPaused(): boolean\n}\n\n/** The subset of the sqlite3 namespace the worker uses. */\nexport interface Sqlite3Api {\n\too1: { DB: new (opts: { filename: string }) => SqliteDb }\n\tinstallOpfsSAHPoolVfs?: (opts: {\n\t\tname: string\n\t\tforceReinitIfPreviouslyFailed?: boolean\n\t}) => Promise<OpfsPool>\n\tcapi?: {\n\t\tsqlite3_deserialize?: unknown\n\t\tsqlite3_progress_handler?: (\n\t\t\tdb: number,\n\t\t\tnOps: number,\n\t\t\thandler: (() => number) | 0,\n\t\t\tctx: number,\n\t\t) => void\n\t}\n}\n\n/** Reports the start and end of a wait on another holder. */\nexport interface BlockingReporter {\n\tblocked(): void\n\tunblocked(waitedMs: number): void\n}\n\n/** Free slots kept beyond the database being opened (other journals, imports). */\nconst OPFS_POOL_HEADROOM = 2\n\n/** How long a wait stays silent before it is reported as a blocking state. */\nconst BLOCKED_REPORT_AFTER_MS = 750\n\n/** Install attempts after the handle probe said \"free\" (covers the release race). */\nconst INSTALL_RACE_ATTEMPTS = 20\n\n/** Headless browsers and some profiles hang on OPFS VFS install. */\nconst OPFS_INIT_TIMEOUT_MS = 10_000\n\nconst OPAQUE_DIR_NAME = '.opaque'\n\n/** Why a durable pool could not be obtained. Contention is never a reason: it waits. */\nexport class PoolUnavailableError extends Error {\n\tconstructor(\n\t\tmessage: string,\n\t\treadonly reason: 'unsupported' | 'timeout' | 'lock-conflict',\n\t) {\n\t\tsuper(message)\n\t\tthis.name = 'PoolUnavailableError'\n\t}\n}\n\n/** True when this worker has every API the SAH pool needs. */\nexport function opfsSahSupported(sqlite3: Sqlite3Api): boolean {\n\tconst g = globalThis as Record<string, unknown>\n\tconst fileHandle = g.FileSystemFileHandle as { prototype?: Record<string, unknown> } | undefined\n\treturn (\n\t\ttypeof sqlite3.installOpfsSAHPoolVfs === 'function' &&\n\t\ttypeof g.FileSystemDirectoryHandle !== 'undefined' &&\n\t\ttypeof fileHandle?.prototype?.createSyncAccessHandle === 'function' &&\n\t\ttypeof navigator !== 'undefined' &&\n\t\ttypeof navigator.storage?.getDirectory === 'function'\n\t)\n}\n\nfunction sleep(ms: number): Promise<void> {\n\treturn new Promise((resolve) => setTimeout(resolve, ms))\n}\n\nasync function withTimeout<T>(promise: Promise<T>, timeoutMs: number, label: string): Promise<T> {\n\tlet timer: ReturnType<typeof setTimeout> | undefined\n\ttry {\n\t\treturn await Promise.race([\n\t\t\tpromise,\n\t\t\tnew Promise<T>((_, reject) => {\n\t\t\t\ttimer = setTimeout(\n\t\t\t\t\t() =>\n\t\t\t\t\t\treject(new PoolUnavailableError(`${label} timed out after ${timeoutMs}ms`, 'timeout')),\n\t\t\t\t\ttimeoutMs,\n\t\t\t\t)\n\t\t\t}),\n\t\t])\n\t} finally {\n\t\tif (timer !== undefined) clearTimeout(timer)\n\t}\n}\n\n/**\n * Acquire an exclusive Web Lock and hold it until the returned release function\n * is called (or this worker terminates). Waits for a live holder, reporting the\n * wait once it lasts long enough to matter. Without the Web Locks API (very old\n * engines that also lack the SAH pool) it returns a no-op release.\n */\nexport async function holdExclusiveLock(\n\tname: string,\n\treporter?: BlockingReporter,\n): Promise<() => void> {\n\tconst locks = typeof navigator !== 'undefined' ? navigator.locks : undefined\n\tif (!locks || typeof locks.request !== 'function') {\n\t\treturn () => {}\n\t}\n\tconst grab = (ifAvailable: boolean): Promise<(() => void) | null> =>\n\t\tnew Promise<(() => void) | null>((resolve, reject) => {\n\t\t\tlocks\n\t\t\t\t.request(name, { mode: 'exclusive', ifAvailable }, (lock) => {\n\t\t\t\t\tif (lock === null) {\n\t\t\t\t\t\tresolve(null)\n\t\t\t\t\t\treturn undefined\n\t\t\t\t\t}\n\t\t\t\t\treturn new Promise<void>((release) => {\n\t\t\t\t\t\tresolve(() => release())\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t\t.catch(reject)\n\t\t})\n\n\tconst immediate = await grab(true)\n\tif (immediate) return immediate\n\tconst start = Date.now()\n\treporter?.blocked()\n\tconst release = await grab(false)\n\tif (!release) {\n\t\tthrow new PoolUnavailableError(`Web Lock \"${name}\" was not granted`, 'lock-conflict')\n\t}\n\treporter?.unblocked(Date.now() - start)\n\treturn release\n}\n\ninterface DirHandle {\n\tgetDirectoryHandle(name: string, options?: { create?: boolean }): Promise<DirHandle>\n\tremoveEntry(name: string, options?: { recursive?: boolean }): Promise<void>\n\tentries(): AsyncIterable<[string, { kind: string }]>\n}\n\ninterface FileHandleWithSah {\n\tkind: 'file'\n\tcreateSyncAccessHandle(): Promise<{ close(): void }>\n}\n\nasync function opfsRoot(): Promise<DirHandle> {\n\treturn (await navigator.storage.getDirectory()) as unknown as DirHandle\n}\n\nasync function childDirectory(parent: DirHandle, name: string): Promise<DirHandle | null> {\n\ttry {\n\t\treturn await parent.getDirectoryHandle(name)\n\t} catch {\n\t\treturn null\n\t}\n}\n\n/** Whether a pool's directory exists in OPFS (it may still be empty). */\nexport async function poolDirectoryExists(poolName: string): Promise<boolean> {\n\treturn (await childDirectory(await opfsRoot(), opfsPoolDirectory(poolName))) !== null\n}\n\nfunction isHandleBusy(error: unknown): boolean {\n\tconst name = (error as { name?: string } | null)?.name\n\treturn name === 'NoModificationAllowedError' || name === 'InvalidStateError'\n}\n\n/**\n * True when some file of the pool still has an open SyncAccessHandle elsewhere\n * (a live owner, or a terminated owner whose handles are still being released).\n */\nasync function poolHandlesBusy(poolName: string): Promise<boolean> {\n\tconst dir = await childDirectory(await opfsRoot(), opfsPoolDirectory(poolName))\n\tif (!dir) return false\n\tconst opaque = await childDirectory(dir, OPAQUE_DIR_NAME)\n\tif (!opaque) return false\n\tfor await (const [, handle] of opaque.entries()) {\n\t\tif (handle.kind !== 'file') continue\n\t\ttry {\n\t\t\tconst access = await (handle as unknown as FileHandleWithSah).createSyncAccessHandle()\n\t\t\taccess.close()\n\t\t} catch (error) {\n\t\t\tif (isHandleBusy(error)) return true\n\t\t\tthrow error\n\t\t}\n\t}\n\treturn false\n}\n\n/**\n * Wait until every file in the pool can be opened by this worker. Waits as long\n * as a holder exists (a beta.12 tab that never takes the pool lock, or a raw\n * user of the pool), reporting the blocking state; never gives up and falls back.\n */\nasync function waitForPoolHandles(poolName: string, reporter?: BlockingReporter): Promise<void> {\n\tconst start = Date.now()\n\tlet reported = false\n\tlet delay = 15\n\twhile (await poolHandlesBusy(poolName)) {\n\t\tif (!reported && Date.now() - start >= BLOCKED_REPORT_AFTER_MS) {\n\t\t\treported = true\n\t\t\treporter?.blocked()\n\t\t}\n\t\tawait sleep(delay)\n\t\tdelay = Math.min(delay * 2, 500)\n\t}\n\tif (reported) reporter?.unblocked(Date.now() - start)\n}\n\n/**\n * Install (or resume) a pool this worker already holds the pool lock for. The\n * bounded retry with `forceReinitIfPreviouslyFailed` covers only the residual\n * handle-release window; sqlite-wasm otherwise caches a failed install forever.\n */\nexport async function installPool(\n\tsqlite3: Sqlite3Api,\n\tpoolName: string,\n\treporter?: BlockingReporter,\n): Promise<OpfsPool> {\n\tconst install = sqlite3.installOpfsSAHPoolVfs\n\tif (!install || !opfsSahSupported(sqlite3)) {\n\t\tthrow new PoolUnavailableError('OPFS SyncAccessHandle pool is not supported', 'unsupported')\n\t}\n\tlet lastError: unknown = null\n\tfor (let attempt = 0; attempt < INSTALL_RACE_ATTEMPTS; attempt += 1) {\n\t\tawait waitForPoolHandles(poolName, reporter)\n\t\ttry {\n\t\t\tconst pool = await withTimeout(\n\t\t\t\tinstall({ name: poolName, forceReinitIfPreviouslyFailed: true }),\n\t\t\t\tOPFS_INIT_TIMEOUT_MS,\n\t\t\t\t'OPFS VFS install',\n\t\t\t)\n\t\t\tif (pool.isPaused()) {\n\t\t\t\tawait pool.unpauseVfs()\n\t\t\t}\n\t\t\treturn pool\n\t\t} catch (error) {\n\t\t\tif (error instanceof PoolUnavailableError) throw error\n\t\t\tlastError = error\n\t\t\tif (!isHandleBusy(error)) break\n\t\t\tawait sleep(Math.min(25 * 2 ** attempt, 250))\n\t\t}\n\t}\n\tconst message = lastError instanceof Error ? lastError.message : String(lastError)\n\tif (isHandleBusy(lastError)) {\n\t\tthrow new PoolUnavailableError(\n\t\t\t`OPFS pool \"${poolName}\" stayed busy: ${message}`,\n\t\t\t'lock-conflict',\n\t\t)\n\t}\n\tthrow new PoolUnavailableError(\n\t\t`OPFS pool \"${poolName}\" could not be installed: ${message}`,\n\t\t'unsupported',\n\t)\n}\n\n/**\n * Grow the pool to fit the database about to be opened plus its journal before\n * opening it (NEW-STORE-7). Slots are empty pre-allocated files, so this is\n * cheap; files are never evicted.\n */\nexport async function ensurePoolCapacity(pool: OpfsPool): Promise<void> {\n\tawait pool.reserveMinimumCapacity(pool.getFileCount() + 2 + OPFS_POOL_HEADROOM)\n}\n\n/** Runs `PRAGMA quick_check` and throws unless the database reports `ok`. */\nexport function assertDatabaseHealthy(db: SqliteDb, label: string): void {\n\tconst rows = db.exec({\n\t\tsql: 'PRAGMA quick_check',\n\t\treturnValue: 'resultRows',\n\t\trowMode: 'array',\n\t}) as unknown[][]\n\tconst verdict = rows?.[0]?.[0]\n\tif (verdict !== 'ok') {\n\t\tthrow new Error(`${label} failed integrity check: ${String(verdict)}`)\n\t}\n}\n\n/** Release the pool's handles (database files must already be closed). */\nexport function pausePool(pool: OpfsPool): void {\n\tif (!pool.isPaused()) {\n\t\tpool.pauseVfs()\n\t}\n}\n\n/**\n * Move a database file from the origin-wide beta.12 pool (`kora-opfs`) into its\n * own pool, the first time the database opens under W8a.\n *\n * - Runs only when the legacy pool directory exists. Holders of the legacy pool\n * (another migrating worker, or a beta.12 tab still open) are waited for, never\n * raced: a beta.12 tab keeps the new tab in the reported blocking state.\n * - Opening the legacy file first lets SQLite roll back a hot journal, so the\n * exported image is consistent. The imported copy must pass `quick_check`\n * before the legacy copy is removed.\n * - The legacy pool is kept while it still holds other databases and removed\n * once its last one has moved.\n *\n * @returns true when this call moved the database.\n */\nexport async function migrateFromLegacyPool(\n\tsqlite3: Sqlite3Api,\n\ttarget: OpfsPool,\n\tpoolPath: string,\n\treporter?: BlockingReporter,\n): Promise<boolean> {\n\tif (!(await poolDirectoryExists(LEGACY_OPFS_POOL_NAME))) {\n\t\treturn false\n\t}\n\tconst releaseLegacyLock = await holdExclusiveLock(\n\t\topfsPoolLockName(LEGACY_OPFS_POOL_NAME),\n\t\treporter,\n\t)\n\ttry {\n\t\t// Another worker may have finished the last migration while we waited.\n\t\tif (!(await poolDirectoryExists(LEGACY_OPFS_POOL_NAME))) {\n\t\t\treturn false\n\t\t}\n\t\tconst legacy = await installPool(sqlite3, LEGACY_OPFS_POOL_NAME, reporter)\n\t\ttry {\n\t\t\tif (!legacy.getFileNames().includes(poolPath)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tconst legacyDb = new legacy.OpfsSAHPoolDb(poolPath)\n\t\t\ttry {\n\t\t\t\tassertDatabaseHealthy(legacyDb, `Legacy database ${poolPath}`)\n\t\t\t} finally {\n\t\t\t\tlegacyDb.close()\n\t\t\t}\n\t\t\tconst bytes = legacy.exportFile(poolPath)\n\t\t\tif (bytes.byteLength === 0) {\n\t\t\t\t// An empty file left by a failed beta.12 open holds no data.\n\t\t\t\tlegacy.unlink(poolPath)\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tawait ensurePoolCapacity(target)\n\t\t\tawait target.importDb(poolPath, bytes)\n\t\t\tlet migratedDb: SqliteDb | null = null\n\t\t\ttry {\n\t\t\t\tmigratedDb = new target.OpfsSAHPoolDb(poolPath)\n\t\t\t\tassertDatabaseHealthy(migratedDb, `Migrated database ${poolPath}`)\n\t\t\t} catch (error) {\n\t\t\t\tmigratedDb?.close()\n\t\t\t\tmigratedDb = null\n\t\t\t\t// Keep the legacy copy as the source of truth; drop the bad import.\n\t\t\t\ttarget.unlink(poolPath)\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\tmigratedDb.close()\n\t\t\tlegacy.unlink(poolPath)\n\t\t\tlegacy.unlink(`${poolPath}-journal`)\n\t\t\treturn true\n\t\t} finally {\n\t\t\tif (legacy.getFileCount() === 0) {\n\t\t\t\tawait legacy.removeVfs()\n\t\t\t} else {\n\t\t\t\tpausePool(legacy)\n\t\t\t}\n\t\t}\n\t} finally {\n\t\treleaseLegacyLock()\n\t}\n}\n","import { WorkerInitError } from '../errors'\n\n/** How long SQLite WASM may take to initialize before the open fails. */\nexport const SQLITE_INIT_TIMEOUT_MS = 60_000\n\n/** Subscribes to unhandled promise rejections; returns the unsubscribe function. */\nexport type UnhandledRejectionSource = (listener: (reason: unknown) => void) => () => void\n\n/** Options for {@link loadSqliteModule}. */\nexport interface LoadSqliteModuleOptions {\n\t/** Options passed to the module's init function (Emscripten module arguments). */\n\tinitOptions?: Record<string, unknown>\n\t/** Where unhandled rejections are observed. Defaults to the global scope's event. */\n\tunhandledRejections?: UnhandledRejectionSource\n\t/** Init timeout in milliseconds. Defaults to {@link SQLITE_INIT_TIMEOUT_MS}. */\n\ttimeoutMs?: number\n}\n\n/**\n * Initialize the SQLite WASM module, failing fast when the `.wasm` cannot be fetched\n * or compiled (F15).\n *\n * The sqlite-wasm build installs its own `instantiateWasm` hook, which fetches and\n * compiles the binary but drops the resulting promise: when the fetch fails (the\n * device is offline before the binary was ever cached) or the response is not WASM\n * (a 404 page), that rejection is unhandled and the module's init promise never\n * settles. Waiting for the init timeout then holds the app for a full minute. While\n * init runs, an unhandled fetch or WebAssembly error is therefore taken as the init's\n * failure and rejects at once.\n *\n * @param init - The module's init function (`@sqlite.org/sqlite-wasm`'s default export)\n * @param options - Init options, the rejection source (tests) and the timeout\n * @returns The initialized module\n * @throws {WorkerInitError} When the binary cannot be loaded, or init times out\n */\nexport async function loadSqliteModule<T>(\n\tinit: (options?: Record<string, unknown>) => Promise<unknown>,\n\toptions: LoadSqliteModuleOptions = {},\n): Promise<T> {\n\tconst timeoutMs = options.timeoutMs ?? SQLITE_INIT_TIMEOUT_MS\n\tconst source = options.unhandledRejections ?? globalUnhandledRejections\n\tlet timer: ReturnType<typeof setTimeout> | undefined\n\tlet unsubscribe: (() => void) | undefined\n\ttry {\n\t\treturn (await Promise.race([\n\t\t\tinit(options.initOptions),\n\t\t\tnew Promise<never>((_, reject) => {\n\t\t\t\tunsubscribe = source((reason) => {\n\t\t\t\t\tif (!isWasmLoadFailure(reason)) return\n\t\t\t\t\treject(\n\t\t\t\t\t\tnew WorkerInitError(\n\t\t\t\t\t\t\t`could not load the SQLite WebAssembly binary (sqlite3.wasm): ${describe(reason)}. The device may be offline before the app was cached, or the file is not served (check the URL and the application/wasm MIME type).`,\n\t\t\t\t\t\t\t{ cause: describe(reason) },\n\t\t\t\t\t\t),\n\t\t\t\t\t)\n\t\t\t\t})\n\t\t\t\ttimer = setTimeout(\n\t\t\t\t\t() =>\n\t\t\t\t\t\treject(\n\t\t\t\t\t\t\tnew WorkerInitError(`SQLite3 module init timed out after ${String(timeoutMs)}ms`),\n\t\t\t\t\t\t),\n\t\t\t\t\ttimeoutMs,\n\t\t\t\t)\n\t\t\t}),\n\t\t])) as T\n\t} finally {\n\t\tif (timer !== undefined) clearTimeout(timer)\n\t\tunsubscribe?.()\n\t}\n}\n\n/**\n * True for the errors a failed `.wasm` fetch or compile produces: `TypeError` from\n * `fetch` or `instantiateStreaming` (network failure, wrong MIME type) and the\n * WebAssembly compile, link and runtime errors.\n */\nfunction isWasmLoadFailure(reason: unknown): boolean {\n\tif (reason instanceof TypeError) return true\n\tconst wasm = (globalThis as { WebAssembly?: Record<string, unknown> }).WebAssembly\n\tif (!wasm) return false\n\tfor (const name of ['CompileError', 'LinkError', 'RuntimeError']) {\n\t\tconst ctor = wasm[name]\n\t\tif (typeof ctor === 'function' && reason instanceof (ctor as new () => Error)) return true\n\t}\n\treturn false\n}\n\nfunction describe(reason: unknown): string {\n\treturn reason instanceof Error ? reason.message : String(reason)\n}\n\n/** Unhandled rejections of the current global scope (a worker or a window). */\nconst globalUnhandledRejections: UnhandledRejectionSource = (listener) => {\n\tconst scope = globalThis as {\n\t\taddEventListener?: (type: string, handler: (event: { reason?: unknown }) => void) => void\n\t\tremoveEventListener?: (type: string, handler: (event: { reason?: unknown }) => void) => void\n\t}\n\tif (typeof scope.addEventListener !== 'function') return () => {}\n\tconst handler = (event: { reason?: unknown }): void => listener(event.reason)\n\tscope.addEventListener('unhandledrejection', handler)\n\treturn () => scope.removeEventListener?.('unhandledrejection', handler)\n}\n","/// <reference lib=\"webworker\" />\n/**\n * Reusable SQLite WASM core, decoupled from any specific worker global.\n *\n * The dedicated worker ({@link file://./sqlite-wasm-worker.ts}) wires one core to\n * `self.onmessage`/`self.postMessage`. Each worker serves exactly one database\n * and owns that database's OPFS SyncAccessHandle pool (one pool per database,\n * W8a). Pool ownership, the pool Web Lock and the beta.12 legacy-pool migration\n * live in {@link file://./opfs-pool-ownership.ts}.\n *\n * This file cannot be unit-tested in Node (no WASM/OPFS); it is exercised through\n * the real-browser suite.\n */\n\nimport {\n\tSTORED_SCHEMA_VERSION_SQL,\n\tparseSchemaCeiling,\n\tschemaAheadMessage,\n\tstoredSchemaVersion,\n} from '../migrations/schema-ceiling'\nimport { opfsPoolLockName, opfsPoolNameFor, opfsPoolPath } from './opfs-names'\nimport {\n\ttype BlockingReporter,\n\ttype OpfsPool,\n\tPoolUnavailableError,\n\ttype Sqlite3Api,\n\ttype SqliteDb,\n\tensurePoolCapacity,\n\tholdExclusiveLock,\n\tinstallPool,\n\tmigrateFromLegacyPool,\n\topfsSahSupported,\n\tpausePool,\n} from './opfs-pool-ownership'\nimport type { WorkerRequest, WorkerResponse, WorkerStatusEvent } from './sqlite-wasm-channel'\nimport { loadSqliteModule } from './sqlite-wasm-loader'\n\n/** Minimum gap between progress heartbeats sent from inside a long statement. */\nconst PROGRESS_HEARTBEAT_MS = 500\n/** SQLite VM instructions between progress-handler callbacks. */\nconst PROGRESS_HANDLER_OPS = 20_000\n\nlet sqlite3Promise: Promise<Sqlite3Api> | null = null\n\nasync function loadSqlite3(): Promise<Sqlite3Api> {\n\tconst sqlite3InitModule = (await import('@sqlite.org/sqlite-wasm')).default\n\t// In production builds, Vite hashes asset filenames (e.g. sqlite3-[hash].wasm).\n\t// The sqlite3 module's default locateFile resolves the unhashed name, causing a\n\t// 404. The worker/host entry sets __KORA_SQLITE_WASM_URL via a `?url` import so\n\t// we can override locateFile with the correct hashed URL.\n\tconst wasmUrl = (globalThis as Record<string, unknown>).__KORA_SQLITE_WASM_URL as\n\t\t| string\n\t\t| undefined\n\tconst initOptions = wasmUrl\n\t\t? {\n\t\t\t\tlocateFile: (file: string): string => (file.endsWith('.wasm') ? wasmUrl : file),\n\t\t\t}\n\t\t: undefined\n\tconst initFn = sqlite3InitModule as unknown as (\n\t\topts?: Record<string, unknown>,\n\t) => Promise<unknown>\n\t// Fails fast when the .wasm cannot be fetched or compiled, instead of waiting out\n\t// the init timeout (F15).\n\treturn loadSqliteModule<Sqlite3Api>(initFn, initOptions ? { initOptions } : {})\n}\n\nfunction getSqlite3(): Promise<Sqlite3Api> {\n\tif (!sqlite3Promise) {\n\t\tconst loading = loadSqlite3()\n\t\tsqlite3Promise = loading\n\t\t// A failed load is not kept: the next open in this worker tries again (F15).\n\t\tloading.catch(() => {\n\t\t\tif (sqlite3Promise === loading) sqlite3Promise = null\n\t\t})\n\t}\n\treturn sqlite3Promise\n}\n\n/** Options for {@link createSqliteWasmCore}. */\nexport interface SqliteWasmCoreOptions {\n\t/** Posts unsolicited status (blocking state) to the tab that owns this worker. */\n\tpostEvent?: (event: WorkerStatusEvent) => void\n\t/**\n\t * Called from inside long-running statements (sqlite progress handler) so the\n\t * worker can keep announcing liveness to follower tabs while it is busy.\n\t */\n\tonProgress?: () => void\n}\n\n/** Handle to a single database, dispatching the worker protocol for it. */\nexport interface SqliteWasmCore {\n\thandle(request: WorkerRequest): Promise<WorkerResponse>\n}\n\n/**\n * Creates a core bound to one database. `handle` resolves with the response for\n * each request; the caller routes it back through the dedicated worker's\n * `postMessage`.\n */\nexport function createSqliteWasmCore(options: SqliteWasmCoreOptions = {}): SqliteWasmCore {\n\tlet db: SqliteDb | null = null\n\tlet sqlite3Api: Sqlite3Api | null = null\n\tlet persistent = false\n\tlet fallbackReason: 'timeout' | 'unsupported' | 'lock-conflict' | undefined\n\tlet pool: OpfsPool | null = null\n\tlet poolName: string | null = null\n\tlet releasePoolLock: (() => void) | null = null\n\tlet openInfo: { created: boolean; migratedFromLegacy: boolean } = {\n\t\tcreated: false,\n\t\tmigratedFromLegacy: false,\n\t}\n\tlet lastProgressBeat = 0\n\n\tfunction reporterFor(resource: 'pool' | 'legacy-pool', name: string): BlockingReporter {\n\t\treturn {\n\t\t\tblocked: () => options.postEvent?.({ kind: 'storage-blocked', resource, poolName: name }),\n\t\t\tunblocked: (waitedMs) =>\n\t\t\t\toptions.postEvent?.({ kind: 'storage-unblocked', resource, poolName: name, waitedMs }),\n\t\t}\n\t}\n\n\t/** Close the database and give the pool back: handles first, then the lock. */\n\tasync function releaseStorage(mode: 'pause' | 'remove'): Promise<void> {\n\t\tif (db) {\n\t\t\tdetachProgressHandler(db)\n\t\t\ttry {\n\t\t\t\tdb.close()\n\t\t\t} finally {\n\t\t\t\tdb = null\n\t\t\t}\n\t\t}\n\t\tif (pool) {\n\t\t\tif (mode === 'remove') {\n\t\t\t\tawait pool.removeVfs()\n\t\t\t} else {\n\t\t\t\tpausePool(pool)\n\t\t\t}\n\t\t\tpool = null\n\t\t}\n\t\treleasePoolLock?.()\n\t\treleasePoolLock = null\n\t}\n\n\tfunction attachProgressHandler(target: SqliteDb): void {\n\t\tconst register = sqlite3Api?.capi?.sqlite3_progress_handler\n\t\tif (!options.onProgress || typeof register !== 'function' || !target.pointer) return\n\t\tregister(\n\t\t\ttarget.pointer,\n\t\t\tPROGRESS_HANDLER_OPS,\n\t\t\t() => {\n\t\t\t\tconst now = Date.now()\n\t\t\t\tif (now - lastProgressBeat >= PROGRESS_HEARTBEAT_MS) {\n\t\t\t\t\tlastProgressBeat = now\n\t\t\t\t\toptions.onProgress?.()\n\t\t\t\t}\n\t\t\t\treturn 0\n\t\t\t},\n\t\t\t0,\n\t\t)\n\t}\n\n\tfunction detachProgressHandler(target: SqliteDb): void {\n\t\tconst register = sqlite3Api?.capi?.sqlite3_progress_handler\n\t\tif (typeof register !== 'function' || !target.pointer) return\n\t\ttry {\n\t\t\tregister(target.pointer, 0, 0, 0)\n\t\t} catch {\n\t\t\t// Closing the database releases the handler binding anyway.\n\t\t}\n\t}\n\n\tasync function openOpfs(\n\t\tsqlite3: Sqlite3Api,\n\t\tdbName: string,\n\t\tmustExist: boolean,\n\t): Promise<WorkerResponse | null> {\n\t\tconst name = opfsPoolNameFor(dbName)\n\t\tconst path = opfsPoolPath(dbName)\n\t\tpoolName = name\n\t\treleasePoolLock = await holdExclusiveLock(opfsPoolLockName(name), reporterFor('pool', name))\n\t\tpool = await installPool(sqlite3, name, reporterFor('pool', name))\n\n\t\tlet migratedFromLegacy = false\n\t\tif (!pool.getFileNames().includes(path)) {\n\t\t\tmigratedFromLegacy = await migrateFromLegacyPool(\n\t\t\t\tsqlite3,\n\t\t\t\tpool,\n\t\t\t\tpath,\n\t\t\t\treporterFor('legacy-pool', name),\n\t\t\t)\n\t\t}\n\t\tconst existedBefore = pool.getFileNames().includes(path)\n\t\tif (!existedBefore && mustExist) {\n\t\t\t// Do not leave behind the empty pool this probe just created.\n\t\t\tawait releaseStorage(pool.getFileCount() === 0 ? 'remove' : 'pause')\n\t\t\treturn { id: 0, type: 'error', message: `Database file ${path} not found`, code: 'NOT_FOUND' }\n\t\t}\n\t\tawait ensurePoolCapacity(pool)\n\t\ttry {\n\t\t\tdb = new pool.OpfsSAHPoolDb(path)\n\t\t} catch (error) {\n\t\t\tif (!existedBefore) pool.unlink(path)\n\t\t\tthrow error\n\t\t}\n\t\tpersistent = true\n\t\topenInfo = { created: !existedBefore, migratedFromLegacy }\n\t\treturn null\n\t}\n\n\tasync function open(\n\t\tid: number,\n\t\tddlStatements: string[],\n\t\tdbName: string,\n\t\tstorage: 'opfs' | 'memory',\n\t\tmustExist: boolean,\n\t): Promise<WorkerResponse> {\n\t\t// Re-run idempotent DDL on the existing handle rather than opening the\n\t\t// database a second time (a follower's open lands here on the leader).\n\t\tif (db) {\n\t\t\ttry {\n\t\t\t\tapplyDdl(db, ddlStatements)\n\t\t\t\treturn { id, type: 'success', data: buildOpenData() }\n\t\t\t} catch (error) {\n\t\t\t\treturn { id, type: 'error', message: (error as Error).message, code: 'INIT_ERROR' }\n\t\t\t}\n\t\t}\n\n\t\tlet createdPath: string | null = null\n\t\ttry {\n\t\t\tconst sqlite3 = await getSqlite3()\n\t\t\tsqlite3Api = sqlite3\n\t\t\tif (storage === 'opfs' && opfsSahSupported(sqlite3)) {\n\t\t\t\ttry {\n\t\t\t\t\tconst early = await openOpfs(sqlite3, dbName, mustExist)\n\t\t\t\t\tif (early) return { ...early, id }\n\t\t\t\t\tif (openInfo.created) createdPath = opfsPoolPath(dbName)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (!(error instanceof PoolUnavailableError) || error.reason === 'lock-conflict') {\n\t\t\t\t\t\tthrow error\n\t\t\t\t\t}\n\t\t\t\t\t// Only an unsupported or hung OPFS selects memory here; the adapter\n\t\t\t\t\t// then reports it (and refuses writes unless the app opted in).\n\t\t\t\t\tawait releaseStorage('pause')\n\t\t\t\t\tfallbackReason = error.reason\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tfallbackReason = 'unsupported'\n\t\t\t}\n\t\t\tif (!db) {\n\t\t\t\tdb = new sqlite3.oo1.DB({ filename: ':memory:' })\n\t\t\t\tpersistent = false\n\t\t\t}\n\n\t\t\t// NEW-STORE-11: no `PRAGMA journal_mode = WAL` here. WAL needs the VFS's\n\t\t\t// shared-memory methods, which opfs-sahpool does not implement, so the pragma\n\t\t\t// was a silent no-op (the mode stayed `delete`). The OPFS database uses\n\t\t\t// SQLite's default rollback journal (DELETE), the in-memory fallback uses\n\t\t\t// `memory`; the actual mode is reported in the open result (`journalMode`).\n\t\t\tdb.exec({ sql: 'PRAGMA foreign_keys = ON' })\n\t\t\tapplyDdl(db, ddlStatements)\n\t\t\tattachProgressHandler(db)\n\t\t\treturn { id, type: 'success', data: buildOpenData() }\n\t\t} catch (error) {\n\t\t\t// A failed open removes only the file this open created (never existing\n\t\t\t// or migrated data) and gives the pool and its lock back (NEW-STORE-8).\n\t\t\tconst currentPool = pool\n\t\t\tif (db) {\n\t\t\t\ttry {\n\t\t\t\t\tdb.close()\n\t\t\t\t} catch {\n\t\t\t\t\t// Already failing; the original error is what matters.\n\t\t\t\t}\n\t\t\t\tdb = null\n\t\t\t}\n\t\t\tif (createdPath && currentPool) {\n\t\t\t\ttry {\n\t\t\t\t\tcurrentPool.unlink(createdPath)\n\t\t\t\t\tcurrentPool.unlink(`${createdPath}-journal`)\n\t\t\t\t} catch {\n\t\t\t\t\t// Best effort: an empty leftover slot is harmless.\n\t\t\t\t}\n\t\t\t}\n\t\t\ttry {\n\t\t\t\tawait releaseStorage('pause')\n\t\t\t} catch {\n\t\t\t\t// The worker is about to be terminated by the adapter anyway.\n\t\t\t}\n\t\t\tconst reason = error instanceof PoolUnavailableError ? error.reason : undefined\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\ttype: 'error',\n\t\t\t\tmessage: (error as Error).message,\n\t\t\t\tcode: 'INIT_ERROR',\n\t\t\t\tcontext: {\n\t\t\t\t\t...(reason ? { reason } : {}),\n\t\t\t\t\t...(createdPath ? { removedCreatedFile: createdPath } : {}),\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction readJournalMode(): string | undefined {\n\t\tif (!db) return undefined\n\t\ttry {\n\t\t\tlet mode: string | undefined\n\t\t\tdb.exec({\n\t\t\t\tsql: 'PRAGMA journal_mode',\n\t\t\t\trowMode: 'object',\n\t\t\t\tcallback: (row: Record<string, unknown>) => {\n\t\t\t\t\tif (typeof row.journal_mode === 'string') mode = row.journal_mode\n\t\t\t\t},\n\t\t\t})\n\t\t\treturn mode\n\t\t} catch {\n\t\t\t// Diagnostic only: an unreadable mode must not fail the open.\n\t\t\treturn undefined\n\t\t}\n\t}\n\n\tfunction buildOpenData(): Record<string, unknown> {\n\t\tconst journalMode = readJournalMode()\n\t\treturn {\n\t\t\t...(journalMode ? { journalMode } : {}),\n\t\t\tpersistent,\n\t\t\t...(persistent ? {} : { fallbackReason: fallbackReason ?? 'unsupported' }),\n\t\t\t...(poolName && persistent ? { poolName } : {}),\n\t\t\tcreated: openInfo.created,\n\t\t\tmigratedFromLegacy: openInfo.migratedFromLegacy,\n\t\t}\n\t}\n\n\tfunction applyDdl(target: SqliteDb, ddlStatements: string[]): void {\n\t\tfor (const sql of ddlStatements) {\n\t\t\tconst ceiling = parseSchemaCeiling(sql)\n\t\t\tif (ceiling !== null) {\n\t\t\t\t// A database a newer build migrated gets none of this build's DDL (RT-109).\n\t\t\t\tconst rows: Array<{ value: unknown }> = []\n\t\t\t\ttarget.exec({\n\t\t\t\t\tsql: STORED_SCHEMA_VERSION_SQL,\n\t\t\t\t\trowMode: 'object',\n\t\t\t\t\tcallback: (row: Record<string, unknown>) => {\n\t\t\t\t\t\trows.push({ value: row.value })\n\t\t\t\t\t},\n\t\t\t\t})\n\t\t\t\tconst stored = storedSchemaVersion(rows)\n\t\t\t\tif (stored > ceiling) throw new Error(schemaAheadMessage(stored, ceiling))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (sql.startsWith('--kora:safe-alter')) {\n\t\t\t\ttry {\n\t\t\t\t\ttarget.exec({ sql: sql.replace('--kora:safe-alter\\n', '') })\n\t\t\t\t} catch (error) {\n\t\t\t\t\tconst msg = (error as Error).message || ''\n\t\t\t\t\tif (!msg.includes('duplicate column name')) {\n\t\t\t\t\t\tthrow error\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\ttarget.exec({ sql })\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction execute(id: number, sql: string, params?: unknown[]): WorkerResponse {\n\t\tif (!db) {\n\t\t\treturn { id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' }\n\t\t}\n\t\ttry {\n\t\t\tdb.exec({ sql, bind: params })\n\t\t\treturn { id, type: 'success' }\n\t\t} catch (error) {\n\t\t\treturn { id, type: 'error', message: (error as Error).message, code: 'EXEC_ERROR' }\n\t\t}\n\t}\n\n\tfunction query(id: number, sql: string, params?: unknown[]): WorkerResponse {\n\t\tif (!db) {\n\t\t\treturn { id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' }\n\t\t}\n\t\ttry {\n\t\t\tconst rows: Record<string, unknown>[] = []\n\t\t\tdb.exec({\n\t\t\t\tsql,\n\t\t\t\tbind: params,\n\t\t\t\trowMode: 'object',\n\t\t\t\tcallback: (row: Record<string, unknown>) => {\n\t\t\t\t\trows.push({ ...row })\n\t\t\t\t},\n\t\t\t})\n\t\t\treturn { id, type: 'success', data: rows }\n\t\t} catch (error) {\n\t\t\treturn { id, type: 'error', message: (error as Error).message, code: 'QUERY_ERROR' }\n\t\t}\n\t}\n\n\tasync function close(id: number, mode: 'pause' | 'remove'): Promise<WorkerResponse> {\n\t\ttry {\n\t\t\tawait releaseStorage(mode)\n\t\t\treturn { id, type: 'success' }\n\t\t} catch (error) {\n\t\t\treturn { id, type: 'error', message: (error as Error).message, code: 'CLOSE_ERROR' }\n\t\t}\n\t}\n\n\tfunction migrate(id: number, statements: string[]): WorkerResponse {\n\t\tif (!db) {\n\t\t\treturn { id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' }\n\t\t}\n\t\ttry {\n\t\t\tfor (const sql of statements) {\n\t\t\t\tdb.exec({ sql })\n\t\t\t}\n\t\t\treturn { id, type: 'success' }\n\t\t} catch (error) {\n\t\t\treturn { id, type: 'error', message: (error as Error).message, code: 'MIGRATE_ERROR' }\n\t\t}\n\t}\n\n\tfunction importData(id: number, data: Uint8Array): WorkerResponse {\n\t\tif (!db) {\n\t\t\treturn { id, type: 'error', message: 'Database is not open', code: 'DB_NOT_OPEN' }\n\t\t}\n\t\tif (typeof db.deserialize === 'function') {\n\t\t\ttry {\n\t\t\t\tdb.deserialize(data)\n\t\t\t\treturn { id, type: 'success' }\n\t\t\t} catch (error) {\n\t\t\t\treturn { id, type: 'error', message: (error as Error).message, code: 'IMPORT_ERROR' }\n\t\t\t}\n\t\t}\n\t\tif (!sqlite3Api || typeof sqlite3Api.capi?.sqlite3_deserialize === 'undefined') {\n\t\t\treturn {\n\t\t\t\tid,\n\t\t\t\ttype: 'error',\n\t\t\t\tmessage: 'Import not supported in this SQLite WASM runtime',\n\t\t\t\tcode: 'IMPORT_NOT_SUPPORTED',\n\t\t\t}\n\t\t}\n\t\treturn {\n\t\t\tid,\n\t\t\ttype: 'error',\n\t\t\tmessage:\n\t\t\t\t'Import requires runtime-specific deserialize wiring and is unavailable in this worker build',\n\t\t\tcode: 'IMPORT_NOT_SUPPORTED',\n\t\t}\n\t}\n\n\tasync function handle(request: WorkerRequest): Promise<WorkerResponse> {\n\t\ttry {\n\t\t\tswitch (request.type) {\n\t\t\t\tcase 'open':\n\t\t\t\t\treturn await open(\n\t\t\t\t\t\trequest.id,\n\t\t\t\t\t\trequest.ddlStatements,\n\t\t\t\t\t\trequest.dbName ?? 'kora-db',\n\t\t\t\t\t\trequest.storage ?? 'opfs',\n\t\t\t\t\t\trequest.mustExist === true,\n\t\t\t\t\t)\n\t\t\t\tcase 'close':\n\t\t\t\t\treturn await close(request.id, 'pause')\n\t\t\t\tcase 'destroy':\n\t\t\t\t\treturn await close(request.id, 'remove')\n\t\t\t\tcase 'execute':\n\t\t\t\t\treturn execute(request.id, request.sql, request.params)\n\t\t\t\tcase 'query':\n\t\t\t\t\treturn query(request.id, request.sql, request.params)\n\t\t\t\tcase 'begin':\n\t\t\t\t\treturn execute(request.id, 'BEGIN')\n\t\t\t\tcase 'commit':\n\t\t\t\t\treturn execute(request.id, 'COMMIT')\n\t\t\t\tcase 'rollback':\n\t\t\t\t\treturn execute(request.id, 'ROLLBACK')\n\t\t\t\tcase 'migrate':\n\t\t\t\t\treturn migrate(request.id, request.statements)\n\t\t\t\tcase 'import':\n\t\t\t\t\treturn importData(request.id, request.data)\n\t\t\t\tcase 'export':\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: request.id,\n\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\tmessage: 'Export not yet supported in browser worker',\n\t\t\t\t\t\tcode: 'EXPORT_NOT_SUPPORTED',\n\t\t\t\t\t}\n\t\t\t\tcase 'serve':\n\t\t\t\t\t// Handled by the worker entry, which owns the BroadcastChannel.\n\t\t\t\t\treturn { id: request.id, type: 'success' }\n\t\t\t\tdefault:\n\t\t\t\t\treturn {\n\t\t\t\t\t\tid: (request as WorkerRequest).id,\n\t\t\t\t\t\ttype: 'error',\n\t\t\t\t\t\tmessage: 'Unknown request type',\n\t\t\t\t\t\tcode: 'UNKNOWN_REQUEST',\n\t\t\t\t\t}\n\t\t\t}\n\t\t} catch (error) {\n\t\t\treturn {\n\t\t\t\tid: request.id,\n\t\t\t\ttype: 'error',\n\t\t\t\tmessage: (error as Error).message,\n\t\t\t\tcode: 'WORKER_ERROR',\n\t\t\t}\n\t\t}\n\t}\n\n\treturn { handle }\n}\n","/// <reference lib=\"webworker\" />\n/**\n * Web Worker script for running SQLite WASM in a dedicated worker.\n *\n * One worker serves one database. Requests from the owning (leader) tab arrive\n * by postMessage; requests from follower tabs arrive on the database's\n * BroadcastChannel once the leader sends `serve`. Both go through one\n * {@link TransactionSerializingWorkerBridge}, so transaction spans from different\n * tabs never interleave, and the leader tab's main thread is not on the\n * follower path at all.\n *\n * This script cannot be tested in Node.js; it is validated in browser suites.\n */\n\nimport { TransactionSerializingWorkerBridge, startWorkerRpcRelay } from '../multi-tab/tab-storage'\nimport type { WorkerRpcRelay } from '../multi-tab/tab-storage'\nimport type {\n\tWorkerEventMessage,\n\tWorkerInboundMessage,\n\tWorkerResponse,\n} from './sqlite-wasm-channel'\nimport { createSqliteWasmCore } from './sqlite-wasm-worker-core'\n\ndeclare const self: DedicatedWorkerGlobalScope\n\nlet relay: WorkerRpcRelay | null = null\n\nconst core = createSqliteWasmCore({\n\tpostEvent: (event) => {\n\t\tconst message: WorkerEventMessage = { id: -1, type: 'event', event }\n\t\tself.postMessage(message)\n\t},\n\t// Keep followers' watchdogs fed while a long statement blocks this worker.\n\tonProgress: () => relay?.beat(),\n})\n\nconst serializer = new TransactionSerializingWorkerBridge({\n\tsend: (request) => core.handle(request),\n\tterminate: () => {},\n})\n\nself.onmessage = (event: MessageEvent<WorkerInboundMessage>): void => {\n\tconst { clientId, ...request } = event.data\n\tif (request.type === 'serve') {\n\t\tif (!relay) {\n\t\t\trelay = startWorkerRpcRelay(request.channelName, serializer)\n\t\t}\n\t\tconst response: WorkerResponse = { id: request.id, type: 'success' }\n\t\tself.postMessage(response)\n\t\treturn\n\t}\n\tvoid serializer.send(request, clientId).then(\n\t\t(response) => {\n\t\t\tif (request.type === 'close' || request.type === 'destroy') {\n\t\t\t\t// The pool is released; stop answering followers for this database.\n\t\t\t\trelay?.stop()\n\t\t\t\trelay = null\n\t\t\t}\n\t\t\tself.postMessage(response)\n\t\t},\n\t\t(error: unknown) => {\n\t\t\tconst response: WorkerResponse = {\n\t\t\t\tid: request.id,\n\t\t\t\ttype: 'error',\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\tcode: 'WORKER_ERROR',\n\t\t\t}\n\t\t\tself.postMessage(response)\n\t\t},\n\t)\n}\n"],"mappings":"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","names":["release"]}
|