@korajs/store 1.0.0-beta.13 → 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.
Files changed (53) hide show
  1. package/dist/adapters/better-sqlite3.cjs.map +1 -1
  2. package/dist/adapters/better-sqlite3.js +1 -1
  3. package/dist/adapters/indexeddb.cjs.map +1 -1
  4. package/dist/adapters/indexeddb.js +4 -4
  5. package/dist/adapters/sqlite-wasm-worker.cjs +67 -15
  6. package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -1
  7. package/dist/adapters/sqlite-wasm-worker.js +65 -17
  8. package/dist/adapters/sqlite-wasm-worker.js.map +1 -1
  9. package/dist/adapters/sqlite-wasm.cjs.map +1 -1
  10. package/dist/adapters/sqlite-wasm.js +5 -5
  11. package/dist/{backup-4NUPDEMB.js → backup-IHKVMUAM.js} +5 -5
  12. package/dist/{chunk-Y5LRH7KI.js → chunk-5AQSGMJV.js} +151 -21
  13. package/dist/chunk-5AQSGMJV.js.map +1 -0
  14. package/dist/{chunk-W5F2LWG6.js → chunk-5JMGMPUE.js} +2 -2
  15. package/dist/{chunk-HZTDBLDB.js → chunk-BILSA6XZ.js} +4 -3
  16. package/dist/{chunk-HZTDBLDB.js.map → chunk-BILSA6XZ.js.map} +1 -1
  17. package/dist/{chunk-QDUSJMFV.js → chunk-BQSIL6TT.js} +5 -5
  18. package/dist/{chunk-MCQKEHOD.js → chunk-KK3GBONL.js} +2 -2
  19. package/dist/{chunk-32EDKWMI.js → chunk-KTVNANUH.js} +2 -2
  20. package/dist/{chunk-SJP2HTYZ.js → chunk-QW4UU7NG.js} +2 -2
  21. package/dist/{chunk-DUKTWVZW.js → chunk-SRHDJF5B.js} +13 -2
  22. package/dist/{chunk-DUKTWVZW.js.map → chunk-SRHDJF5B.js.map} +1 -1
  23. package/dist/{chunk-P4V375FY.js → chunk-UC2LGFPV.js} +4 -4
  24. package/dist/index.cjs +203 -47
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +96 -6
  27. package/dist/index.d.ts +96 -6
  28. package/dist/index.js +55 -39
  29. package/dist/index.js.map +1 -1
  30. package/dist/internal.cjs +1 -0
  31. package/dist/internal.cjs.map +1 -1
  32. package/dist/internal.d.cts +2 -2
  33. package/dist/internal.d.ts +2 -2
  34. package/dist/internal.js +3 -3
  35. package/dist/{restore-FR7XIKT2.js → restore-MNDPR557.js} +4 -4
  36. package/dist/{sql-builder-C4u55jKW.d.cts → sql-builder-D0tC3sv9.d.cts} +8 -1
  37. package/dist/{sql-builder-CGckdBDL.d.ts → sql-builder-D5UMzHEo.d.ts} +8 -1
  38. package/dist/sqlite-wasm-channel-QCCNBYCU.js +12 -0
  39. package/dist/store-HNGX4KTT.js +16 -0
  40. package/package.json +2 -2
  41. package/dist/chunk-Y5LRH7KI.js.map +0 -1
  42. package/dist/sqlite-wasm-channel-MLHLKEED.js +0 -12
  43. package/dist/store-IMWKZLWB.js +0 -16
  44. /package/dist/{backup-4NUPDEMB.js.map → backup-IHKVMUAM.js.map} +0 -0
  45. /package/dist/{chunk-W5F2LWG6.js.map → chunk-5JMGMPUE.js.map} +0 -0
  46. /package/dist/{chunk-QDUSJMFV.js.map → chunk-BQSIL6TT.js.map} +0 -0
  47. /package/dist/{chunk-MCQKEHOD.js.map → chunk-KK3GBONL.js.map} +0 -0
  48. /package/dist/{chunk-32EDKWMI.js.map → chunk-KTVNANUH.js.map} +0 -0
  49. /package/dist/{chunk-SJP2HTYZ.js.map → chunk-QW4UU7NG.js.map} +0 -0
  50. /package/dist/{chunk-P4V375FY.js.map → chunk-UC2LGFPV.js.map} +0 -0
  51. /package/dist/{restore-FR7XIKT2.js.map → restore-MNDPR557.js.map} +0 -0
  52. /package/dist/{sqlite-wasm-channel-MLHLKEED.js.map → sqlite-wasm-channel-QCCNBYCU.js.map} +0 -0
  53. /package/dist/{store-IMWKZLWB.js.map → store-HNGX4KTT.js.map} +0 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/errors.ts","../../src/adapters/mutex.ts","../../src/adapters/sqlite-wasm-channel.ts","../../src/adapters/indexeddb.ts","../../src/adapters/indexeddb-adapter.ts","../../src/migrations/schema-ceiling.ts","../../src/adapters/database-dump.ts","../../src/adapters/indexeddb-persistence-scheduler.ts","../../src/adapters/sqlite-wasm-adapter.ts","../../src/adapters/opfs-names.ts","../../src/multi-tab/tab-storage.ts","../../src/adapters/sqlite-wasm-persistence.ts","../../src/adapters/storage-manifest.ts"],"sourcesContent":["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 * Async mutex for serializing access to a resource across `await` boundaries.\n *\n * This exists because an adapter's `transaction()` is async (its callback may\n * await), but the underlying database transaction (BEGIN…COMMIT) is not\n * reentrant: starting a second transaction while one is open is an error. When\n * a client applies a relayed remote operation while a local write is in flight,\n * the two `transaction()` calls interleave at their `await` points, and without\n * serialization the second `BEGIN` collides with the first, silently dropping\n * the operation. Serializing every transaction through this mutex guarantees at\n * most one is active at a time.\n */\nexport class Mutex {\n\tprivate locked = false\n\tprivate readonly waiters: Array<() => void> = []\n\n\t/**\n\t * Acquire the mutex. Resolves with a release function once the lock is held.\n\t * If the mutex is already held, the caller waits (FIFO) until it is released.\n\t */\n\tasync acquire(): Promise<() => void> {\n\t\tif (!this.locked) {\n\t\t\tthis.locked = true\n\t\t\treturn this.createRelease()\n\t\t}\n\n\t\treturn new Promise<() => void>((resolve) => {\n\t\t\tthis.waiters.push(() => {\n\t\t\t\tresolve(this.createRelease())\n\t\t\t})\n\t\t})\n\t}\n\n\tprivate createRelease(): () => void {\n\t\tlet released = false\n\t\treturn () => {\n\t\t\tif (released) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\treleased = true\n\t\t\tconst next = this.waiters.shift()\n\t\t\tif (next) {\n\t\t\t\tnext()\n\t\t\t} else {\n\t\t\t\tthis.locked = false\n\t\t\t}\n\t\t}\n\t}\n}\n","/// <reference lib=\"dom\" />\nimport { BridgeTerminatedError, RequestAbortedError, WorkerTimeoutError } from '../errors'\n\n// === Message Protocol ===\n\n/** Fields every request carries besides its type. */\ninterface RequestMeta {\n\t/** Correlates a worker response with its request on one postMessage channel. */\n\tid: number\n\t/**\n\t * Stable id for de-duplication. A follower retrying a request (after a\n\t * `LeaderUnresponsiveError`, say) sends the same id and the leader worker\n\t * answers from its response cache instead of applying it twice.\n\t */\n\trequestId?: string\n}\n\n/**\n * Request message sent from the main thread (or a follower tab) to the SQLite\n * WASM worker.\n */\nexport type WorkerRequest =\n\t| (RequestMeta & {\n\t\t\ttype: 'open'\n\t\t\tddlStatements: string[]\n\t\t\tdbName?: string\n\t\t\t/**\n\t\t\t * `memory` opens an in-memory database without touching OPFS (the\n\t\t\t * IndexedDB adapter persists snapshots itself). Defaults to `opfs`.\n\t\t\t */\n\t\t\tstorage?: 'opfs' | 'memory'\n\t\t\t/** Fail with `NOT_FOUND` instead of creating the database file. */\n\t\t\tmustExist?: boolean\n\t })\n\t| (RequestMeta & { type: 'close' })\n\t| (RequestMeta & { type: 'destroy' })\n\t| (RequestMeta & { type: 'execute'; sql: string; params?: unknown[] })\n\t| (RequestMeta & { type: 'query'; sql: string; params?: unknown[] })\n\t| (RequestMeta & { type: 'begin' })\n\t| (RequestMeta & { type: 'commit' })\n\t| (RequestMeta & { type: 'rollback' })\n\t| (RequestMeta & { type: 'migrate'; from: number; to: number; statements: string[] })\n\t| (RequestMeta & { type: 'export' })\n\t| (RequestMeta & { type: 'import'; data: Uint8Array })\n\t| (RequestMeta & {\n\t\t\t/**\n\t\t\t * Leader worker only: answer follower RPC, pings and heartbeats on this\n\t\t\t * BroadcastChannel directly, so a hung leader main thread never blocks\n\t\t\t * other tabs (NEW-STORE-9).\n\t\t\t */\n\t\t\ttype: 'serve'\n\t\t\tchannelName: string\n\t })\n\n/**\n * Response message sent from the worker back to the main thread.\n * Matches the request `id` for correlation.\n */\nexport type WorkerResponse =\n\t| { id: number; type: 'success'; data?: unknown }\n\t| { id: number; type: 'error'; message: string; code: string; context?: Record<string, unknown> }\n\n/**\n * Unsolicited status the worker reports while it works on a request, such as\n * waiting for another holder of the OPFS pool (a blocking state the app shows).\n */\nexport type WorkerStatusEvent =\n\t| {\n\t\t\tkind: 'storage-blocked'\n\t\t\tresource: 'pool' | 'legacy-pool'\n\t\t\tpoolName: string\n\t }\n\t| {\n\t\t\tkind: 'storage-unblocked'\n\t\t\tresource: 'pool' | 'legacy-pool'\n\t\t\tpoolName: string\n\t\t\twaitedMs: number\n\t }\n\n/** Envelope for {@link WorkerStatusEvent}s on the worker's postMessage channel. */\nexport interface WorkerEventMessage {\n\tid: -1\n\ttype: 'event'\n\tevent: WorkerStatusEvent\n}\n\n/** Message the main thread posts to a dedicated worker. */\nexport type WorkerInboundMessage = WorkerRequest & { clientId?: string }\n\n/** Per-request options accepted by bridges. */\nexport interface WorkerSendOptions {\n\t/** Cancels the wait for the response (the worker may still apply the request). */\n\tsignal?: AbortSignal\n\t/** Overrides the bridge timeout for this request; `Infinity` disables it. */\n\ttimeoutMs?: number\n}\n\n// === WorkerBridge Interface ===\n\n/**\n * Abstraction over the communication channel with the SQLite WASM worker.\n * In browsers, this is backed by a real Web Worker via MessagePort.\n * In Node.js tests, this is backed by better-sqlite3 via MockWorkerBridge.\n */\nexport interface WorkerBridge {\n\t/** Send a request to the worker and wait for a response. */\n\tsend(\n\t\trequest: WorkerRequest,\n\t\tclientId?: string,\n\t\toptions?: WorkerSendOptions,\n\t): Promise<WorkerResponse>\n\n\t/** Terminate the worker. Safe to call multiple times. */\n\tterminate(): void\n}\n\n// === Mutex ===\n\n// The Mutex now lives in its own module so non-browser adapters (better-sqlite3)\n// can serialize transactions without importing browser-worker code.\nexport { Mutex } from './mutex'\n\n// === WebWorkerBridge ===\n\n/** Options for {@link WebWorkerBridge}. */\nexport interface WebWorkerBridgeOptions {\n\t/** Receives status events (blocking state) the worker reports. */\n\tonEvent?: (event: WorkerStatusEvent) => void\n}\n\n/**\n * WorkerBridge implementation for browser environments.\n * Communicates with an actual Web Worker running SQLite WASM.\n */\nexport class WebWorkerBridge implements WorkerBridge {\n\tprivate worker: Worker\n\tprivate pending = new Map<\n\t\tnumber,\n\t\t{ resolve: (r: WorkerResponse) => void; reject: (e: Error) => void; type: string }\n\t>()\n\tprivate nextId = 1\n\tprivate terminated = false\n\tprivate timeoutMs: number\n\n\t/**\n\t * @param workerUrl - URL to the sqlite-wasm-worker script\n\t * @param timeoutMs - Timeout for worker responses in milliseconds (default: 30000)\n\t * @param options - Status event listener\n\t */\n\tconstructor(workerUrl: string | URL, timeoutMs = 30000, options: WebWorkerBridgeOptions = {}) {\n\t\tthis.timeoutMs = timeoutMs\n\t\tthis.worker = new Worker(workerUrl, { type: 'module' })\n\t\tthis.worker.onmessage = (event: MessageEvent<WorkerResponse | WorkerEventMessage>) => {\n\t\t\tconst message = event.data\n\t\t\tif (message.type === 'event') {\n\t\t\t\toptions.onEvent?.(message.event)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst entry = this.pending.get(message.id)\n\t\t\tif (entry) {\n\t\t\t\tthis.pending.delete(message.id)\n\t\t\t\tentry.resolve(message)\n\t\t\t}\n\t\t}\n\t\tthis.worker.onerror = (event) => {\n\t\t\t// Reject all pending requests on worker error\n\t\t\tconst error = new Error(`Worker error: ${event.message}`)\n\t\t\tfor (const [id, entry] of this.pending) {\n\t\t\t\tthis.pending.delete(id)\n\t\t\t\tentry.reject(error)\n\t\t\t}\n\t\t}\n\t}\n\n\t/** True once {@link terminate} ran. */\n\tisTerminated(): boolean {\n\t\treturn this.terminated\n\t}\n\n\t/** Post a request without awaiting a response (fire-and-forget control messages). */\n\tpost(request: WorkerRequest, clientId?: string): void {\n\t\tif (this.terminated) return\n\t\tconst message: WorkerInboundMessage = { ...request, id: this.nextId++, clientId }\n\t\tthis.worker.postMessage(message)\n\t}\n\n\tasync send(\n\t\trequest: WorkerRequest,\n\t\tclientId?: 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: 'Worker has been terminated',\n\t\t\t\tcode: 'WORKER_TERMINATED',\n\t\t\t}\n\t\t}\n\n\t\tconst id = this.nextId++\n\t\tconst message: WorkerInboundMessage = { ...request, id, clientId }\n\t\tconst timeoutMs = options.timeoutMs ?? this.timeoutMs\n\t\tconst signal = options.signal\n\t\tif (signal?.aborted) {\n\t\t\tthrow new RequestAbortedError(request.type, request.requestId ?? String(id))\n\t\t}\n\n\t\treturn new Promise<WorkerResponse>((resolve, reject) => {\n\t\t\tconst timer = Number.isFinite(timeoutMs)\n\t\t\t\t? setTimeout(() => {\n\t\t\t\t\t\tthis.pending.delete(id)\n\t\t\t\t\t\tcleanup()\n\t\t\t\t\t\treject(new WorkerTimeoutError(message.type, timeoutMs))\n\t\t\t\t\t}, timeoutMs)\n\t\t\t\t: undefined\n\t\t\tconst onAbort = (): void => {\n\t\t\t\tthis.pending.delete(id)\n\t\t\t\tcleanup()\n\t\t\t\treject(new RequestAbortedError(request.type, request.requestId ?? String(id)))\n\t\t\t}\n\t\t\tconst cleanup = (): void => {\n\t\t\t\tif (timer !== undefined) clearTimeout(timer)\n\t\t\t\tsignal?.removeEventListener('abort', onAbort)\n\t\t\t}\n\t\t\tsignal?.addEventListener('abort', onAbort, { once: true })\n\n\t\t\tthis.pending.set(id, {\n\t\t\t\ttype: request.type,\n\t\t\t\tresolve: (response) => {\n\t\t\t\t\tcleanup()\n\t\t\t\t\tresolve(response)\n\t\t\t\t},\n\t\t\t\treject: (error) => {\n\t\t\t\t\tcleanup()\n\t\t\t\t\treject(error)\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tthis.worker.postMessage(message)\n\t\t})\n\t}\n\n\tterminate(): void {\n\t\tif (this.terminated) return\n\t\tthis.terminated = true\n\t\t// Terminating the worker releases its OPFS handles and the pool Web Lock it\n\t\t// holds; callers release the tab leader lock only after this (NEW-STORE-10).\n\t\tthis.worker.terminate()\n\t\tfor (const [id, entry] of this.pending) {\n\t\t\tthis.pending.delete(id)\n\t\t\tentry.reject(new BridgeTerminatedError(entry.type, 'worker terminated'))\n\t\t}\n\t}\n}\n","// Entry point for @korajs/store/indexeddb\nexport { IndexedDbAdapter } from './indexeddb-adapter'\nexport type { IndexedDbAdapterOptions } from './indexeddb-adapter'\n","import type { KoraEventEmitter, SchemaDefinition } from '@korajs/core'\nimport {\n\tAdapterError,\n\tPersistenceError,\n\tSchemaVersionAheadError,\n\tStorageBackendMismatchError,\n} from '../errors'\nimport { STORED_SCHEMA_VERSION_SQL, storedSchemaVersion } from '../migrations/schema-ceiling'\nimport type { MigrationPlan, StorageAdapter, StorageOpenState, Transaction } from '../types'\nimport { type DatabaseDump, exportDump, restoreDumpStatements } from './database-dump'\nimport { IndexedDbPersistenceScheduler } from './indexeddb-persistence-scheduler'\nimport { SqliteWasmAdapter } from './sqlite-wasm-adapter'\nimport type { WorkerBridge } from './sqlite-wasm-channel'\nimport {\n\tdeleteSnapshotFromIndexedDB,\n\tisIndexedDbQuotaError,\n\tloadDumpFromIndexedDB,\n\tloadFromIndexedDB,\n\tsaveDumpToIndexedDB,\n\tsaveToIndexedDB,\n} from './sqlite-wasm-persistence'\nimport { isManifestAvailable, readManifestRecord, recordDatabase } from './storage-manifest'\n\n/**\n * Options for creating an IndexedDbAdapter.\n */\nexport interface IndexedDbAdapterOptions {\n\t/**\n\t * Database name used as the IndexedDB key for persistence.\n\t * Defaults to 'kora-db'.\n\t */\n\tdbName?: string\n\n\t/**\n\t * Injected WorkerBridge for testing. If omitted, a WebWorkerBridge is created\n\t * in browser environments.\n\t */\n\tbridge?: WorkerBridge\n\n\t/**\n\t * URL to the sqlite-wasm-worker script. Required in browsers if no bridge is provided.\n\t */\n\tworkerUrl?: string | URL\n\n\t/** Timeout for worker / follower RPC responses. Defaults to 30000ms. */\n\tworkerResponseTimeoutMs?: number\n\n\t/**\n\t * Debounce interval (ms) before writing snapshots to IndexedDB. Defaults to 500.\n\t */\n\tpersistenceDebounceMs?: number\n\n\t/**\n\t * When set, persistence failures and quota errors are emitted on this emitter.\n\t */\n\temitter?: KoraEventEmitter\n}\n\n/**\n * IndexedDB-backed adapter that uses SQLite WASM in-memory and serializes\n * the entire database to IndexedDB after mutations (coalesced/debounced).\n *\n * This is the fallback adapter for browsers where OPFS is not available.\n * It provides the same SQL interface as SqliteWasmAdapter, but persists by\n * serializing the full SQLite database to a single IndexedDB blob.\n *\n * @example\n * ```typescript\n * const adapter = new IndexedDbAdapter({ workerUrl: '/sqlite-wasm-worker.js' })\n * ```\n */\nexport class IndexedDbAdapter implements StorageAdapter {\n\tprivate inner: SqliteWasmAdapter\n\tprivate readonly dbName: string\n\tprivate readonly emitter: KoraEventEmitter | undefined\n\tprivate readonly scheduler: IndexedDbPersistenceScheduler\n\tprivate storageOpenState: StorageOpenState | null = null\n\tprivate readonly options: IndexedDbAdapterOptions\n\t/** True in browsers (a real worker), where the backend choice is recorded. */\n\tprivate readonly usesRealWorker: boolean\n\n\tconstructor(options: IndexedDbAdapterOptions = {}) {\n\t\tthis.options = options\n\t\tthis.dbName = options.dbName ?? 'kora-db'\n\t\tthis.emitter = options.emitter\n\t\tthis.usesRealWorker = !options.bridge && options.workerUrl !== undefined\n\t\tthis.inner = new SqliteWasmAdapter({\n\t\t\tbridge: options.bridge,\n\t\t\tworkerUrl: options.workerUrl,\n\t\t\tdbName: this.dbName,\n\t\t\tworkerResponseTimeoutMs: options.workerResponseTimeoutMs,\n\t\t\t// The inner SQLite database is in memory by design: this adapter makes it\n\t\t\t// durable by persisting snapshots to IndexedDB, so the inner adapter must\n\t\t\t// not refuse writes as non-durable, and must never touch OPFS (which would\n\t\t\t// create a second, disjoint copy of the data).\n\t\t\tallowNonDurable: true,\n\t\t\tstorage: 'memory',\n\t\t\tskipBackendRecord: true,\n\t\t\t// A browser leader restores the snapshot into its own fresh worker database\n\t\t\t// before serving other tabs (first open and promotion, STORE-6).\n\t\t\tonLeaderWorkerOpened: (tx) => this.restoreDumpIfFresh(tx),\n\t\t})\n\t\tthis.scheduler = new IndexedDbPersistenceScheduler({\n\t\t\tdebounceMs: options.persistenceDebounceMs,\n\t\t\tflush: () => this.writeSnapshot(),\n\t\t\tonError: (error) => this.handlePersistenceError(error),\n\t\t})\n\t}\n\n\tasync open(schema: SchemaDefinition): Promise<void> {\n\t\t// The backend choice is recorded per database. If OPFS holds the\n\t\t// authoritative copy, carry it over explicitly before this session starts,\n\t\t// so OPFS and IndexedDB never hold two disjoint copies (NEW-STORE-5).\n\t\tconst tracksBackend = this.usesRealWorker && isManifestAvailable()\n\t\tconst recorded = tracksBackend ? await readManifestRecord(this.dbName).catch(() => null) : null\n\t\tconst carried = recorded?.backend === 'opfs' ? await this.readOpfsCopy(schema) : null\n\n\t\tawait this.inner.open(schema)\n\t\tthis.storageOpenState = { persistent: true, mode: 'indexeddb' }\n\n\t\tif (carried && this.inner.isLeader()) {\n\t\t\t// The fresh worker database is marked restored, then receives the OPFS copy.\n\t\t\tawait this.inner.transaction(async (tx) => {\n\t\t\t\tawait markRestored(tx)\n\t\t\t})\n\t\t\tawait this.applyDump(carried)\n\t\t\tawait this.scheduler.flushNow()\n\t\t\tawait recordDatabase({ dbName: this.dbName, backend: 'indexeddb' })\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:storage-migrated',\n\t\t\t\tdbName: this.dbName,\n\t\t\t\tfrom: 'opfs',\n\t\t\t\tto: 'indexeddb',\n\t\t\t\tmessage: `Database \"${this.dbName}\" moved from OPFS into IndexedDB because OPFS is not usable in this session.`,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\t// Only the storage leader restores, and only into a database no snapshot was\n\t\t// restored into yet: a follower tab (or a second adapter on the same worker)\n\t\t// opening must never rewrite the leader's live database (STORE-6).\n\t\tif (this.inner.isLeader()) {\n\t\t\tawait this.restoreIfFresh()\n\t\t}\n\t\t// A snapshot a newer build migrated (RT-109): refuse it before anything writes, and\n\t\t// without the snapshot a close() would write (the in-memory copy ran this build's\n\t\t// DDL before the restore).\n\t\tconst stored = storedSchemaVersion(\n\t\t\tawait this.inner.query<{ value: unknown }>(STORED_SCHEMA_VERSION_SQL),\n\t\t)\n\t\tif (stored > schema.version) {\n\t\t\tthis.scheduler.dispose()\n\t\t\tawait this.inner.close().catch(() => undefined)\n\t\t\tthrow new SchemaVersionAheadError(this.dbName, stored, schema.version)\n\t\t}\n\t\tif (tracksBackend) {\n\t\t\tawait recordDatabase({ dbName: this.dbName, backend: 'indexeddb' }).catch((error: unknown) =>\n\t\t\t\tconsole.warn(`[kora] Could not record the storage backend of \"${this.dbName}\":`, error),\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Read the OPFS copy of this database through a short-lived SQLite WASM\n\t * adapter (as leader, or as a follower of a tab that has it open). Refuses\n\t * loudly when OPFS cannot be read, instead of starting an empty copy.\n\t */\n\tprivate async readOpfsCopy(schema: SchemaDefinition): Promise<DatabaseDump> {\n\t\tconst reader = new SqliteWasmAdapter({\n\t\t\tdbName: this.dbName,\n\t\t\tworkerUrl: this.options.workerUrl,\n\t\t\tworkerResponseTimeoutMs: this.options.workerResponseTimeoutMs,\n\t\t\tskipBackendRecord: true,\n\t\t\treleaseOnFreeze: false,\n\t\t})\n\t\ttry {\n\t\t\tawait reader.open(schema)\n\t\t\tif (reader.getStorageOpenState()?.persistent !== true) {\n\t\t\t\tthrow new StorageBackendMismatchError(\n\t\t\t\t\tthis.dbName,\n\t\t\t\t\t'OPFS',\n\t\t\t\t\t'IndexedDB',\n\t\t\t\t\t`OPFS reported ${reader.getStorageOpenState()?.fallbackReason ?? 'no durable storage'}`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tlet dump: DatabaseDump = { tables: [] }\n\t\t\tawait reader.transaction(async (tx) => {\n\t\t\t\tdump = await exportDump((sql, params) => tx.query(sql, params))\n\t\t\t})\n\t\t\treturn dump\n\t\t} catch (error) {\n\t\t\tif (error instanceof StorageBackendMismatchError) throw error\n\t\t\tthrow new StorageBackendMismatchError(\n\t\t\t\tthis.dbName,\n\t\t\t\t'OPFS',\n\t\t\t\t'IndexedDB',\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t)\n\t\t} finally {\n\t\t\tawait reader.close().catch(() => undefined)\n\t\t}\n\t}\n\n\tprivate async applyDump(dump: DatabaseDump): Promise<void> {\n\t\tawait this.inner.transaction(async (tx) => {\n\t\t\tfor (const statement of restoreDumpStatements(dump)) {\n\t\t\t\tawait tx.execute(statement.sql, statement.params)\n\t\t\t}\n\t\t})\n\t}\n\n\tasync close(): Promise<void> {\n\t\tawait this.scheduler.flushNow()\n\t\tthis.scheduler.dispose()\n\t\tawait this.inner.close()\n\t}\n\n\tasync execute(sql: string, params?: unknown[]): Promise<void> {\n\t\tawait this.inner.execute(sql, params)\n\t\tthis.scheduler.schedule()\n\t}\n\n\tasync query<T>(sql: string, params?: unknown[]): Promise<T[]> {\n\t\treturn this.inner.query<T>(sql, params)\n\t}\n\n\tasync transaction(fn: (tx: Transaction) => Promise<void>): Promise<void> {\n\t\tawait this.inner.transaction(fn)\n\t\tthis.scheduler.schedule()\n\t}\n\n\tasync migrate(from: number, to: number, migration: MigrationPlan): Promise<void> {\n\t\tawait this.inner.migrate(from, to, migration)\n\t\tthis.scheduler.schedule()\n\t}\n\n\tgetStorageOpenState(): StorageOpenState | null {\n\t\treturn this.storageOpenState\n\t}\n\n\t/**\n\t * Force an immediate snapshot write to IndexedDB (skips debounce).\n\t * Useful before tab unload or in tests.\n\t */\n\tasync flushPersistence(): Promise<void> {\n\t\tawait this.scheduler.flushNow()\n\t}\n\n\t/**\n\t * Durability barrier (RT-35): resolves once every write committed before the call is\n\t * in a snapshot persisted to IndexedDB, and rejects when the snapshot cannot be\n\t * written. Sync calls it before an operation leaves the device, so the server never\n\t * holds an operation this device could lose on reload.\n\t */\n\tasync ensureDurable(): Promise<void> {\n\t\tawait this.scheduler.flushBarrier()\n\t}\n\n\tprivate async writeSnapshot(): Promise<void> {\n\t\tconst dump = await this.exportDump()\n\t\tawait saveDumpToIndexedDB(this.dbName, dump)\n\n\t\ttry {\n\t\t\tconst data = await this.inner.exportDatabase()\n\t\t\tawait saveToIndexedDB(this.dbName, data)\n\t\t} catch (error) {\n\t\t\tif (!isUnsupportedWorkerExport(error)) {\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\tawait deleteSnapshotFromIndexedDB(this.dbName)\n\t\t}\n\t}\n\n\tprivate handlePersistenceError(error: unknown): void {\n\t\tconst message = error instanceof Error ? error.message : 'IndexedDB persistence failed'\n\t\tconst code = error instanceof PersistenceError ? error.code : 'PERSISTENCE_FAILED'\n\t\tconst quotaExceeded = isIndexedDbQuotaError(error)\n\n\t\tif (quotaExceeded) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:quota-exceeded',\n\t\t\t\tdbName: this.dbName,\n\t\t\t\tmessage,\n\t\t\t})\n\t\t}\n\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'store:persistence-error',\n\t\t\tdbName: this.dbName,\n\t\t\tmessage,\n\t\t\tcode: quotaExceeded ? 'QUOTA_EXCEEDED' : code,\n\t\t})\n\t}\n\n\t/**\n\t * Restore the persisted snapshot into the worker database unless one was restored\n\t * into it already (this adapter or another one sharing the worker). The binary\n\t * snapshot (Node / test workers that support export) is imported whole; the JSON dump\n\t * (browsers) is restored in one transaction.\n\t */\n\tprivate async restoreIfFresh(): Promise<void> {\n\t\tconst persisted = await loadFromIndexedDB(this.dbName)\n\t\tif (persisted) {\n\t\t\tconst fresh = { value: false }\n\t\t\tawait this.inner.transaction(async (tx) => {\n\t\t\t\tfresh.value = !(await isRestored(tx))\n\t\t\t})\n\t\t\tif (!fresh.value) return\n\t\t\ttry {\n\t\t\t\tawait this.inner.importDatabase(persisted)\n\t\t\t\tawait this.inner.transaction(markRestored)\n\t\t\t\treturn\n\t\t\t} catch {\n\t\t\t\t// Fall through to the JSON dump.\n\t\t\t}\n\t\t}\n\t\tawait this.inner.transaction((tx) => this.restoreDumpIfFresh(tx))\n\t}\n\n\t/** In `tx`: restore the JSON dump when no snapshot was restored into this database. */\n\tprivate async restoreDumpIfFresh(tx: Transaction): Promise<void> {\n\t\tif (await isRestored(tx)) return\n\t\tawait markRestored(tx)\n\t\tconst dump = await loadDumpFromIndexedDB<DatabaseDump>(this.dbName)\n\t\tif (!dump) return\n\t\tfor (const statement of restoreDumpStatements(dump)) {\n\t\t\tawait tx.execute(statement.sql, statement.params)\n\t\t}\n\t}\n\n\t/** A consistent snapshot: every table is read inside one transaction (STORE-7). */\n\tprivate async exportDump(): Promise<DatabaseDump> {\n\t\tlet dump: DatabaseDump = { tables: [] }\n\t\tawait this.inner.transaction(async (tx) => {\n\t\t\tdump = await exportDump((sql, params) => tx.query(sql, params))\n\t\t})\n\t\treturn dump\n\t}\n}\n\n/**\n * Connection-local marker (a TEMP table: never part of the database file or of a dump)\n * recording that a snapshot was restored into this worker database (STORE-6).\n */\nconst RESTORED_MARKER = '_kora_idb_restored'\n\nasync function isRestored(tx: Transaction): Promise<boolean> {\n\tconst rows = await tx.query<{ name: string }>(\n\t\t\"SELECT name FROM sqlite_temp_master WHERE type = 'table' AND name = ?\",\n\t\t[RESTORED_MARKER],\n\t)\n\treturn rows.length > 0\n}\n\nasync function markRestored(tx: Transaction): Promise<void> {\n\tawait tx.execute(`CREATE TEMP TABLE IF NOT EXISTS ${RESTORED_MARKER} (restored INTEGER)`)\n}\n\nfunction isUnsupportedWorkerExport(error: unknown): boolean {\n\treturn error instanceof AdapterError && error.context?.code === 'EXPORT_NOT_SUPPORTED'\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","import { quoteIdent } from '@korajs/core'\nimport { PersistenceError } from '../errors'\n\n/**\n * Logical copy of every user table of a SQLite database. Used to move a\n * database between storage backends (OPFS <-> IndexedDB) explicitly, and as the\n * IndexedDB adapter's durable snapshot format.\n */\nexport interface DatabaseDump {\n\ttables: Array<{\n\t\tname: string\n\t\t/**\n\t\t * The table's `CREATE TABLE` statement (from `sqlite_master`), so a restore can\n\t\t * recreate a table the target database does not have yet with its constraints.\n\t\t * Absent in dumps written before it was recorded.\n\t\t */\n\t\tsql?: string\n\t\tcolumns: string[]\n\t\trows: Array<Record<string, unknown>>\n\t}>\n}\n\n/** A parameterized SQL statement. */\nexport interface DumpStatement {\n\tsql: string\n\tparams?: unknown[]\n}\n\ntype QueryFn = <T>(sql: string, params?: unknown[]) => Promise<T[]>\n\n/** Throws unless `identifier` is a plain SQL identifier (dump data is untrusted). */\nexport function ensureSafeIdentifier(identifier: string): string {\n\tif (!/^[a-zA-Z0-9_]+$/.test(identifier)) {\n\t\tthrow new PersistenceError(`Unsafe SQL identifier: ${identifier}`, {\n\t\t\tcode: 'UNSAFE_IDENTIFIER',\n\t\t\tidentifier,\n\t\t})\n\t}\n\treturn identifier\n}\n\n/**\n * Read every user table through `query`. Pass a transaction's `query` to get a\n * consistent snapshot.\n */\nexport async function exportDump(query: QueryFn): Promise<DatabaseDump> {\n\tconst tableRows = await query<{ name: string; sql: string | null }>(\n\t\t\"SELECT name, sql FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\",\n\t)\n\tconst tables: DatabaseDump['tables'] = []\n\tfor (const tableRow of tableRows) {\n\t\tconst tableName = ensureSafeIdentifier(tableRow.name)\n\t\tconst columns = await query<{ name: string }>(`PRAGMA table_info(${quoteIdent(tableName)})`)\n\t\tconst rows = await query<Record<string, unknown>>(`SELECT * FROM ${quoteIdent(tableName)}`)\n\t\ttables.push({\n\t\t\tname: tableName,\n\t\t\t...(typeof tableRow.sql === 'string' ? { sql: tableRow.sql } : {}),\n\t\t\tcolumns: columns.map((column) => column.name),\n\t\t\trows,\n\t\t})\n\t}\n\treturn { tables }\n}\n\n/**\n * Statements that replace the contents of each dumped table with the dump's\n * rows. Run them inside one transaction so a failure leaves the target as it was.\n *\n * A table the target does not have yet (the store creates some of its bookkeeping\n * tables after the schema DDL) is created first from the dump's recorded `CREATE TABLE`\n * statement, constraints included. A dump written before statements were recorded\n * creates it untyped, so its rows are still restored.\n *\n * @param createMissingTables - Kept for callers that restore into scratch databases\n * with no schema; missing tables are always created now.\n */\nexport function restoreDumpStatements(\n\tdump: DatabaseDump,\n\t_createMissingTables = true,\n): DumpStatement[] {\n\tconst statements: DumpStatement[] = []\n\tfor (const table of dump.tables) {\n\t\tconst name = quoteIdent(ensureSafeIdentifier(table.name))\n\t\tconst create = createTableIfNotExists(table.name, table.sql)\n\t\tif (create) {\n\t\t\tstatements.push({ sql: create })\n\t\t} else {\n\t\t\tconst columns = table.columns.map((column) => quoteIdent(ensureSafeIdentifier(column)))\n\t\t\tif (columns.length === 0) continue\n\t\t\tstatements.push({ sql: `CREATE TABLE IF NOT EXISTS ${name} (${columns.join(', ')})` })\n\t\t}\n\t\tstatements.push({ sql: `DELETE FROM ${name}` })\n\t\tfor (const row of table.rows) {\n\t\t\tconst columns = table.columns.filter((column) =>\n\t\t\t\tObject.prototype.hasOwnProperty.call(row, column),\n\t\t\t)\n\t\t\tif (columns.length === 0) continue\n\t\t\tconst quotedColumns = columns\n\t\t\t\t.map((column) => quoteIdent(ensureSafeIdentifier(column)))\n\t\t\t\t.join(', ')\n\t\t\tstatements.push({\n\t\t\t\tsql: `INSERT INTO ${name} (${quotedColumns}) VALUES (${columns.map(() => '?').join(', ')})`,\n\t\t\t\tparams: columns.map((column) => row[column]),\n\t\t\t})\n\t\t}\n\t}\n\treturn statements\n}\n\n/**\n * The recorded `CREATE TABLE` statement made idempotent, or null when there is none or\n * it is not a single plain `CREATE TABLE` of exactly this table (dump data is untrusted).\n */\nfunction createTableIfNotExists(tableName: string, sql: string | undefined): string | null {\n\tif (typeof sql !== 'string' || sql.includes(';')) return null\n\tconst match = /^\\s*CREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(\"?)([A-Za-z0-9_]+)\\1\\s*\\(/i.exec(\n\t\tsql,\n\t)\n\tif (!match || match[2] !== tableName) return null\n\treturn `CREATE TABLE IF NOT EXISTS ${quoteIdent(tableName)} ${sql.slice(match[0].length - 1)}`\n}\n","/// <reference lib=\"dom\" />\n\nconst DEFAULT_DEBOUNCE_MS = 500\n\nexport interface IndexedDbPersistenceSchedulerOptions {\n\t/** Debounce interval before writing a snapshot. Defaults to 500ms. */\n\tdebounceMs?: number\n\t/** Persist the in-memory database to IndexedDB. */\n\tflush: () => Promise<void>\n\t/** Called when persistence fails (mutation already committed in memory). */\n\tonError?: (error: unknown) => void\n}\n\n/**\n * Coalesces IndexedDB snapshot writes: debounces rapid mutations and flushes\n * immediately on tab hide (`visibilitychange`) or explicit {@link flushNow}.\n */\nexport class IndexedDbPersistenceScheduler {\n\tprivate readonly debounceMs: number\n\tprivate readonly flush: () => Promise<void>\n\tprivate readonly onError: ((error: unknown) => void) | undefined\n\tprivate timer: ReturnType<typeof setTimeout> | null = null\n\tprivate inFlight: Promise<void> | null = null\n\tprivate disposed = false\n\t/**\n\t * Mutation generations (RT-35): `scheduled` counts schedule() calls (one per committed\n\t * write), `persisted` is the generation the last SUCCESSFUL snapshot covered. A\n\t * snapshot covers every generation scheduled before it started.\n\t */\n\tprivate scheduledGeneration = 0\n\tprivate persistedGeneration = 0\n\t/** Error of the last snapshot attempt, cleared by the next success. */\n\tprivate lastFlushError: unknown = null\n\tprivate readonly onVisibilityChange: () => void\n\n\tconstructor(options: IndexedDbPersistenceSchedulerOptions) {\n\t\tthis.debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS\n\t\tthis.flush = options.flush\n\t\tthis.onError = options.onError\n\t\tthis.onVisibilityChange = () => {\n\t\t\tif (typeof document !== 'undefined' && document.visibilityState === 'hidden') {\n\t\t\t\tvoid this.flushNow()\n\t\t\t}\n\t\t}\n\t\tif (typeof document !== 'undefined') {\n\t\t\tdocument.addEventListener('visibilitychange', this.onVisibilityChange)\n\t\t}\n\t}\n\n\t/** Schedule a debounced snapshot write. */\n\tschedule(): void {\n\t\tif (this.disposed) return\n\t\tthis.scheduledGeneration++\n\t\tif (this.debounceMs <= 0) {\n\t\t\tvoid this.flushNow()\n\t\t\treturn\n\t\t}\n\t\tif (this.timer !== null) {\n\t\t\tclearTimeout(this.timer)\n\t\t}\n\t\tthis.timer = setTimeout(() => {\n\t\t\tthis.timer = null\n\t\t\tvoid this.flushNow()\n\t\t}, this.debounceMs)\n\t}\n\n\t/**\n\t * Cancel any pending debounce and persist immediately. Resolves only when a snapshot\n\t * taken after every write scheduled so far was written (STORE-7): a snapshot already in\n\t * flight may predate the latest writes, so the call waits for it and then writes again\n\t * while anything is still dirty. A failed snapshot ends the loop (the failure is\n\t * reported through `onError`; {@link flushBarrier} is the variant that rejects).\n\t */\n\tasync flushNow(): Promise<void> {\n\t\tif (this.disposed) return\n\t\tif (this.timer !== null) {\n\t\t\tclearTimeout(this.timer)\n\t\t\tthis.timer = null\n\t\t}\n\t\t// With nothing in flight, \"persist immediately\" writes one snapshot even when no\n\t\t// write was scheduled (callers use it to force the first snapshot).\n\t\tlet mustRun = this.inFlight === null\n\t\tfor (;;) {\n\t\t\tif (this.inFlight) {\n\t\t\t\tawait this.inFlight\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!mustRun && this.persistedGeneration >= this.scheduledGeneration) return\n\t\t\tmustRun = false\n\t\t\tthis.inFlight = this.runFlush()\n\t\t\ttry {\n\t\t\t\tawait this.inFlight\n\t\t\t} finally {\n\t\t\t\tthis.inFlight = null\n\t\t\t}\n\t\t\tif (this.lastFlushError !== null) return\n\t\t}\n\t}\n\n\t/** Whether a write was scheduled that no successful snapshot covers yet. */\n\tisDirty(): boolean {\n\t\treturn this.persistedGeneration < this.scheduledGeneration\n\t}\n\n\t/**\n\t * Durability barrier (RT-35): resolve once every write scheduled before this call is\n\t * in a snapshot persisted to IndexedDB. A snapshot already in flight may predate the\n\t * latest write, so the barrier waits for it and then writes a fresh one when needed.\n\t * Rejects when the snapshot cannot be written: the caller must treat the writes as\n\t * not durable (for example, not upload them yet).\n\t */\n\tasync flushBarrier(): Promise<void> {\n\t\tconst target = this.scheduledGeneration\n\t\tif (this.persistedGeneration >= target) return\n\t\tif (this.disposed) {\n\t\t\tthrow new Error('IndexedDB persistence was disposed before the writes were persisted')\n\t\t}\n\t\tif (this.timer !== null) {\n\t\t\tclearTimeout(this.timer)\n\t\t\tthis.timer = null\n\t\t}\n\t\t// At most two rounds: one that may have started before `target`, then one after.\n\t\tfor (let round = 0; round < 3 && this.persistedGeneration < target; round++) {\n\t\t\tif (this.inFlight) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.inFlight\n\t\t\t\t} catch {\n\t\t\t\t\t// runFlush never rejects; the error is in lastFlushError.\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tthis.inFlight = this.runFlush()\n\t\t\ttry {\n\t\t\t\tawait this.inFlight\n\t\t\t} finally {\n\t\t\t\tthis.inFlight = null\n\t\t\t}\n\t\t\tif (this.persistedGeneration < target) break\n\t\t}\n\t\tif (this.persistedGeneration < target) {\n\t\t\tthrow this.lastFlushError instanceof Error\n\t\t\t\t? this.lastFlushError\n\t\t\t\t: new Error('IndexedDB snapshot was not persisted')\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.disposed = true\n\t\tif (this.timer !== null) {\n\t\t\tclearTimeout(this.timer)\n\t\t\tthis.timer = null\n\t\t}\n\t\tif (typeof document !== 'undefined') {\n\t\t\tdocument.removeEventListener('visibilitychange', this.onVisibilityChange)\n\t\t}\n\t}\n\n\tprivate async runFlush(): Promise<void> {\n\t\tconst generation = this.scheduledGeneration\n\t\ttry {\n\t\t\tawait this.flush()\n\t\t\tthis.persistedGeneration = Math.max(this.persistedGeneration, generation)\n\t\t\tthis.lastFlushError = null\n\t\t} catch (error) {\n\t\t\tthis.lastFlushError = error\n\t\t\tthis.onError?.(error)\n\t\t}\n\t}\n}\n","import { generateFullDDL } from '@korajs/core'\nimport type { KoraEventEmitter, SchemaDefinition } from '@korajs/core'\nimport {\n\tAdapterError,\n\tBridgeTerminatedError,\n\tStorageDurabilityError,\n\tStoreNotOpenError,\n} from '../errors'\nimport {\n\tFollowerBroadcastBridge,\n\ttype TabStorageSession,\n\tacquireTabStorageSession,\n} from '../multi-tab/tab-storage'\nimport type {\n\tMigrationPlan,\n\tStorageAdapter,\n\tStorageFallbackReason,\n\tStorageOpenState,\n\tTransaction,\n} from '../types'\nimport { restoreDumpStatements } from './database-dump'\nimport type { DatabaseDump } from './database-dump'\nimport { Mutex } from './sqlite-wasm-channel'\nimport type {\n\tWebWorkerBridge,\n\tWorkerBridge,\n\tWorkerRequest,\n\tWorkerResponse,\n\tWorkerSendOptions,\n\tWorkerStatusEvent,\n} from './sqlite-wasm-channel'\nimport { deleteFromIndexedDB, loadDumpFromIndexedDB } from './sqlite-wasm-persistence'\nimport { isManifestAvailable, readManifestRecord, recordDatabase } from './storage-manifest'\n\ntype WorkerSuccessResponse = Extract<WorkerResponse, { type: 'success' }>\nlet warnedSharedWorkerDeprecated = false\n\n/** Per-request options for {@link SqliteWasmAdapter.execute} and `query`. */\nexport interface StorageRequestOptions {\n\t/**\n\t * Stop waiting for the response. The leader may still apply a write that\n\t * already reached it; retry with the same `requestId` to stay idempotent.\n\t */\n\tsignal?: AbortSignal\n\t/**\n\t * Stable id for this request. A follower tab's retry with the same id is\n\t * answered from the leader's response cache instead of being applied twice.\n\t */\n\trequestId?: string\n}\n\n/**\n * Options for creating a SqliteWasmAdapter.\n */\nexport interface SqliteWasmAdapterOptions {\n\t/**\n\t * Injected WorkerBridge for testing. If omitted, a WebWorkerBridge is created\n\t * in browser environments.\n\t */\n\tbridge?: WorkerBridge\n\n\t/**\n\t * Database name for persistence. Each database gets its own OPFS pool.\n\t */\n\tdbName?: string\n\n\t/**\n\t * URL to the sqlite-wasm-worker script. Required in browsers if no bridge is provided.\n\t */\n\tworkerUrl?: string | URL\n\n\t/** Timeout for worker / follower RPC responses. Defaults to 30000ms. */\n\tworkerResponseTimeoutMs?: number\n\n\t/**\n\t * @deprecated SharedWorker-hosted SQLite cannot use OPFS SyncAccessHandle and\n\t * is never durable. Kora ignores this option and uses the dedicated-worker\n\t * leader/follower path for durable multi-tab storage.\n\t */\n\tsharedWorkerUrl?: string | URL\n\n\t/**\n\t * When false, the adapter records a non-persistent open without emitting\n\t * `store:opfs-unavailable`. `createApp()` uses this while it promotes the store\n\t * to durable IndexedDB; direct adapter users keep the diagnostic by default.\n\t */\n\temitNonPersistentDiagnostic?: boolean\n\n\t/**\n\t * Accept writes when durable OPFS storage could not be obtained and the\n\t * database runs in memory. Defaults to false: a non-durable open or promotion\n\t * emits the blocking `store:durability-lost` event and every write is refused\n\t * with {@link StorageDurabilityError}, so data is never accepted and then lost\n\t * on reload. Set true only for apps that can tolerate losing local writes.\n\t */\n\tallowNonDurable?: boolean\n\n\t/**\n\t * For `createApp()`: skip the durability check at open only, because the caller\n\t * inspects {@link SqliteWasmAdapter.getStorageOpenState} right after open and\n\t * switches to a durable backend itself. A later leader promotion that loses\n\t * durable storage is still enforced.\n\t */\n\tdeferOpenDurabilityCheck?: boolean\n\n\t/**\n\t * Release storage ownership when the page is frozen or hidden into the\n\t * back/forward cache (`freeze` / `pagehide`), so a visible tab can take over\n\t * instead of waiting on a tab that cannot run; ownership is re-acquired on\n\t * `resume` / `pageshow`. Defaults to true.\n\t */\n\treleaseOnFreeze?: boolean\n\n\t/**\n\t * When set, storage diagnostics are emitted here: `store:opfs-unavailable`,\n\t * `store:durability-lost`, `store:storage-blocked` (another holder has the\n\t * storage; the open waits), `store:storage-migrated` and\n\t * `store:db-name-collision`.\n\t */\n\temitter?: KoraEventEmitter\n\n\t/**\n\t * @internal Used by the IndexedDB adapter: open an in-memory database that\n\t * never touches OPFS (the IndexedDB adapter persists snapshots itself).\n\t */\n\tstorage?: 'opfs' | 'memory'\n\n\t/**\n\t * @internal Do not read or write the storage manifest (the IndexedDB adapter's\n\t * inner database and its one-shot OPFS reader manage the manifest themselves).\n\t */\n\tskipBackendRecord?: boolean\n\n\t/**\n\t * @internal Used by the IndexedDB adapter (STORE-6): runs in one transaction on this\n\t * tab's OWN worker right after it opened the database as storage leader (first open\n\t * or promotion), before the worker serves any other tab. The IndexedDB adapter\n\t * restores its persisted snapshot here, so a snapshot is only ever restored into a\n\t * freshly created worker database and never over a leader's live one. Throwing fails\n\t * the open (or the promotion, which then refuses writes).\n\t */\n\tonLeaderWorkerOpened?: (tx: Transaction) => Promise<void>\n}\n\ninterface Gate {\n\tpromise: Promise<void>\n\topen(): void\n}\n\nfunction createGate(): Gate {\n\tlet open: () => void = () => {}\n\tconst promise = new Promise<void>((resolve) => {\n\t\topen = resolve\n\t})\n\treturn { promise, open }\n}\n\n/**\n * SQLite WASM adapter that communicates with a SQLite instance through a WorkerBridge.\n *\n * In browsers, one tab per database (the leader) runs a dedicated worker that\n * owns the database's OPFS pool; other tabs reach that worker over a\n * BroadcastChannel. In Node.js tests, the bridge is backed by MockWorkerBridge\n * wrapping better-sqlite3.\n *\n * Storage is durable or loud, never silently in memory (W8a): another holder of\n * the storage makes the open wait (reported with `store:storage-blocked`); only a\n * runtime without OPFS SAH support gets a non-durable database, which refuses\n * writes unless the app opted in with `allowNonDurable`.\n *\n * @example\n * ```typescript\n * // Browser usage\n * const adapter = new SqliteWasmAdapter({ workerUrl: '/sqlite-wasm-worker.js' })\n *\n * // Test usage with MockWorkerBridge\n * import { MockWorkerBridge } from './sqlite-wasm-mock-bridge'\n * const adapter = new SqliteWasmAdapter({ bridge: new MockWorkerBridge() })\n * ```\n */\nexport class SqliteWasmAdapter implements StorageAdapter {\n\tprivate bridge: WorkerBridge | null = null\n\t/** The dedicated worker this tab runs while it is the database's leader. */\n\tprivate ownWorker: WebWorkerBridge | null = null\n\tprivate opened = false\n\tprivate readonly mutex = new Mutex()\n\tprivate readonly injectedBridge: WorkerBridge | undefined\n\tprivate readonly workerUrl: string | URL | undefined\n\tprivate readonly sharedWorkerUrl: string | URL | undefined\n\tprivate readonly workerResponseTimeoutMs: number\n\tprivate readonly dbName: string\n\tprivate readonly emitter: KoraEventEmitter | undefined\n\tprivate tabSession: TabStorageSession | null = null\n\tprivate readonly emitNonPersistentDiagnostic: boolean\n\tprivate storageOpenState: StorageOpenState | null = null\n\t/** Retained so a follower promoted to leader can re-open its own worker. */\n\tprivate schema: SchemaDefinition | null = null\n\tprivate promoting = false\n\tprivate closing = false\n\tprivate suspended = false\n\t/** Requests wait here while leadership changes hands (promotion, resume). */\n\tprivate gate: Gate | null = null\n\tprivate removeLifecycleListeners: (() => void) | null = null\n\tprivate readonly allowNonDurable: boolean\n\tprivate readonly deferOpenDurabilityCheck: boolean\n\tprivate readonly releaseOnFreeze: boolean\n\tprivate readonly storage: 'opfs' | 'memory'\n\tprivate readonly skipBackendRecord: boolean\n\t/** Set when this adapter lost (or never had) durable storage; writes are refused. */\n\tprivate durabilityLoss: { phase: 'open' | 'promotion'; reason: string } | null = null\n\tprivate readonly onLeaderWorkerOpened: ((tx: Transaction) => Promise<void>) | undefined\n\n\tconstructor(options: SqliteWasmAdapterOptions = {}) {\n\t\tthis.injectedBridge = options.bridge\n\t\tthis.workerUrl = options.workerUrl\n\t\tthis.sharedWorkerUrl = options.sharedWorkerUrl\n\t\tthis.workerResponseTimeoutMs = options.workerResponseTimeoutMs ?? 30_000\n\t\tthis.dbName = options.dbName ?? 'kora-db'\n\t\tthis.emitter = options.emitter\n\t\tthis.emitNonPersistentDiagnostic = options.emitNonPersistentDiagnostic ?? true\n\t\tthis.allowNonDurable = options.allowNonDurable ?? false\n\t\tthis.deferOpenDurabilityCheck = options.deferOpenDurabilityCheck ?? false\n\t\tthis.releaseOnFreeze = options.releaseOnFreeze ?? true\n\t\tthis.storage = options.storage ?? 'opfs'\n\t\tthis.skipBackendRecord = options.skipBackendRecord ?? false\n\t\tthis.onLeaderWorkerOpened = options.onLeaderWorkerOpened\n\t}\n\n\tasync open(schema: SchemaDefinition): Promise<void> {\n\t\tif (this.opened) return\n\n\t\tconst ddlStatements = generateFullDDL(schema)\n\t\tif (this.injectedBridge) {\n\t\t\tthis.bridge = this.injectedBridge\n\t\t\tconst response = await this.openCurrentBridge(ddlStatements)\n\t\t\tthis.reportStorageMode(response.data)\n\t\t\tthis.enforceDurability('open')\n\t\t\tthis.opened = true\n\t\t\treturn\n\t\t}\n\t\tif (this.workerUrl) {\n\t\t\tthis.warnSharedWorkerDeprecated()\n\t\t\tthis.schema = schema\n\t\t\ttry {\n\t\t\t\tconst response = await this.attachAndOpen(ddlStatements)\n\t\t\t\tthis.reportStorageMode(response.data)\n\t\t\t\tthis.enforceDurability('open')\n\t\t\t\tawait this.afterLeaderOpen(response.data)\n\t\t\t\tthis.opened = true\n\t\t\t} catch (error) {\n\t\t\t\t// A failed open must not keep the worker, the OPFS pool or the leader\n\t\t\t\t// lock for the rest of the page's life (NEW-STORE-8).\n\t\t\t\tawait this.teardown()\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tthrow new AdapterError(\n\t\t\t'SqliteWasmAdapter requires either a bridge (for testing) or a workerUrl (for browsers). ' +\n\t\t\t\t'Pass { bridge: new MockWorkerBridge() } for tests, or { workerUrl: \"/worker.js\" } for browsers.',\n\t\t)\n\t}\n\n\t/**\n\t * Join the database as leader (own worker) or follower (RPC to the leader's\n\t * worker), then open it. The open waits, without a timeout, while another\n\t * holder still has the storage; the worker reports that as a blocking state.\n\t */\n\tprivate async attachAndOpen(ddlStatements: string[]): Promise<WorkerSuccessResponse> {\n\t\tconst workerUrl = this.workerUrl\n\t\tif (!workerUrl) {\n\t\t\tthrow new AdapterError('Durable SQLite WASM storage requires workerUrl.')\n\t\t}\n\t\tconst session = await acquireTabStorageSession(this.dbName, {\n\t\t\tonPromote: () => {\n\t\t\t\tvoid this.promoteToLeader()\n\t\t\t},\n\t\t})\n\t\tthis.tabSession = session\n\n\t\tif (session.role === 'leader') {\n\t\t\tconst { WebWorkerBridge } = await import('./sqlite-wasm-channel')\n\t\t\tconst worker = new WebWorkerBridge(workerUrl, this.workerResponseTimeoutMs, {\n\t\t\t\tonEvent: (event) => this.onWorkerEvent(event),\n\t\t\t})\n\t\t\tthis.ownWorker = worker\n\t\t\tthis.bridge = worker\n\t\t\tconst response = await this.openLeaderWorker(worker, ddlStatements)\n\t\t\tthis.installLifecycleListeners()\n\t\t\treturn response\n\t\t}\n\n\t\tconst followerBridge = new FollowerBroadcastBridge(\n\t\t\tsession.channelName,\n\t\t\tthis.workerResponseTimeoutMs,\n\t\t)\n\t\tthis.bridge = followerBridge\n\t\t// Another runtime on this origin already owns this database name. That is\n\t\t// expected for multiple tabs of the same app (they share one leader),\n\t\t// but a bug if these are logically separate apps, so surface it so the\n\t\t// developer can give them distinct store names.\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'store:db-name-collision',\n\t\t\tdbName: this.dbName,\n\t\t\tmessage: `Another runtime on this origin is already using database \"${this.dbName}\"; this runtime is sharing it as a follower. If these are separate apps, give each a distinct store name.`,\n\t\t})\n\t\t// Readiness handshake: give the leader relay a moment to answer before the\n\t\t// first RPC, so a follower opened during a leader's startup race retries the\n\t\t// handshake instead of firing into the void.\n\t\tawait followerBridge.waitForLeader()\n\t\tthis.installLifecycleListeners()\n\t\treturn this.checkedOpen(followerBridge, ddlStatements)\n\t}\n\n\t/** Open the database in this tab's own worker and start serving followers. */\n\tprivate async openLeaderWorker(\n\t\tworker: WebWorkerBridge,\n\t\tddlStatements: string[],\n\t): Promise<WorkerSuccessResponse> {\n\t\tconst hook = this.onLeaderWorkerOpened\n\t\tif (!hook) {\n\t\t\tconst pending = this.checkedOpen(worker, ddlStatements)\n\t\t\t// Posted after the open so follower requests queue behind it in the worker.\n\t\t\tworker.post({ id: 0, type: 'serve', channelName: this.tabSession?.channelName ?? '' })\n\t\t\treturn pending\n\t\t}\n\t\tconst response = await this.checkedOpen(worker, ddlStatements)\n\t\t// The hook (snapshot restore, STORE-6) runs before the worker serves other tabs,\n\t\t// so no follower ever reads or writes the database before it holds its data.\n\t\tawait runWorkerTransaction(worker, hook)\n\t\tworker.post({ id: 0, type: 'serve', channelName: this.tabSession?.channelName ?? '' })\n\t\treturn response\n\t}\n\n\t/**\n\t * Whether this runtime owns the database's worker: the storage leader in a browser,\n\t * or any adapter with an injected bridge (tests, Node). A follower tab relays every\n\t * request to the leader's worker (STORE-6).\n\t */\n\tisLeader(): boolean {\n\t\tif (this.injectedBridge) return true\n\t\treturn this.tabSession?.role === 'leader'\n\t}\n\n\tprivate async checkedOpen(\n\t\tbridge: WorkerBridge,\n\t\tddlStatements: string[],\n\t): Promise<WorkerSuccessResponse> {\n\t\tconst response = await bridge.send(\n\t\t\t{\n\t\t\t\tid: 0,\n\t\t\t\ttype: 'open',\n\t\t\t\tddlStatements,\n\t\t\t\tdbName: this.dbName,\n\t\t\t\t...(this.storage === 'memory' ? { storage: 'memory' as const } : {}),\n\t\t\t},\n\t\t\tundefined,\n\t\t\t{ timeoutMs: Number.POSITIVE_INFINITY },\n\t\t)\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`Failed to open database: ${response.message}`, {\n\t\t\t\tcode: response.code,\n\t\t\t\tdbName: this.dbName,\n\t\t\t\t...(response.context ?? {}),\n\t\t\t})\n\t\t}\n\t\treturn response\n\t}\n\n\tprivate async openCurrentBridge(ddlStatements: string[]): Promise<WorkerSuccessResponse> {\n\t\tconst bridge = this.bridge\n\t\tif (!bridge) throw new StoreNotOpenError()\n\t\treturn this.checkedOpen(bridge, ddlStatements)\n\t}\n\n\tprivate onWorkerEvent(event: WorkerStatusEvent): void {\n\t\tif (!this.emitter) return\n\t\tconst holder =\n\t\t\tevent.resource === 'legacy-pool'\n\t\t\t\t? 'the shared OPFS pool used by older Kora versions (a tab running an older version of this app may still be open)'\n\t\t\t\t: 'another tab or worker that is still releasing it'\n\t\tif (event.kind === 'storage-blocked') {\n\t\t\tthis.emitter.emit({\n\t\t\t\ttype: 'store:storage-blocked',\n\t\t\t\tdbName: this.dbName,\n\t\t\t\tresource: event.resource,\n\t\t\t\tstate: 'waiting',\n\t\t\t\tmessage: `Database \"${this.dbName}\" is waiting for ${holder}. Close other tabs of this app if this persists; Kora will not fall back to non-durable storage.`,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tthis.emitter.emit({\n\t\t\ttype: 'store:storage-blocked',\n\t\t\tdbName: this.dbName,\n\t\t\tresource: event.resource,\n\t\t\tstate: 'resolved',\n\t\t\twaitedMs: event.waitedMs,\n\t\t\tmessage: `Database \"${this.dbName}\" obtained its storage after waiting ${event.waitedMs}ms.`,\n\t\t})\n\t}\n\n\t/**\n\t * Leader-only bookkeeping after a durable open: report a legacy-pool\n\t * migration, move an IndexedDB-held copy into OPFS explicitly (never two\n\t * disjoint copies), and record OPFS as this database's backend.\n\t */\n\tprivate async afterLeaderOpen(data: unknown): Promise<void> {\n\t\tif (!this.ownWorker || this.skipBackendRecord || this.storageOpenState?.persistent !== true) {\n\t\t\treturn\n\t\t}\n\t\tconst info = (typeof data === 'object' && data !== null ? data : {}) as {\n\t\t\tmigratedFromLegacy?: boolean\n\t\t}\n\t\tif (info.migratedFromLegacy === true) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:storage-migrated',\n\t\t\t\tdbName: this.dbName,\n\t\t\t\tfrom: 'legacy-opfs-pool',\n\t\t\t\tto: 'opfs',\n\t\t\t\tmessage: `Database \"${this.dbName}\" moved from the shared pre-1.0 OPFS pool into its own pool.`,\n\t\t\t})\n\t\t}\n\t\tif (!isManifestAvailable()) return\n\n\t\tlet recordedBackend: string | null = null\n\t\ttry {\n\t\t\trecordedBackend = (await readManifestRecord(this.dbName))?.backend ?? null\n\t\t} catch (error) {\n\t\t\tconsole.warn(`[kora] Could not read the storage manifest for \"${this.dbName}\":`, error)\n\t\t}\n\t\tif (recordedBackend === 'indexeddb') {\n\t\t\t// The authoritative copy is in IndexedDB (an earlier session ran without\n\t\t\t// OPFS). Move it here before anything reads or writes; a failure fails\n\t\t\t// the open instead of showing the older OPFS copy.\n\t\t\tconst dump = await loadDumpFromIndexedDB<DatabaseDump>(this.dbName)\n\t\t\tif (dump) {\n\t\t\t\tawait this.applyDumpThroughWorker(this.ownWorker, dump)\n\t\t\t}\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:storage-migrated',\n\t\t\t\tdbName: this.dbName,\n\t\t\t\tfrom: 'indexeddb',\n\t\t\t\tto: 'opfs',\n\t\t\t\tmessage: `Database \"${this.dbName}\" moved from IndexedDB into OPFS.`,\n\t\t\t})\n\t\t}\n\t\ttry {\n\t\t\tawait recordDatabase({\n\t\t\t\tdbName: this.dbName,\n\t\t\t\tbackend: 'opfs',\n\t\t\t\t...(this.storageOpenState.poolName ? { poolName: this.storageOpenState.poolName } : {}),\n\t\t\t})\n\t\t\tif (recordedBackend === 'indexeddb') {\n\t\t\t\tawait deleteFromIndexedDB(this.dbName)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.warn(`[kora] Could not update the storage manifest for \"${this.dbName}\":`, error)\n\t\t}\n\t}\n\n\tprivate async applyDumpThroughWorker(worker: WorkerBridge, dump: DatabaseDump): Promise<void> {\n\t\tconst check = async (request: WorkerRequest, what: string): Promise<void> => {\n\t\t\tconst response = await worker.send(request)\n\t\t\tif (response.type === 'error') {\n\t\t\t\tthrow new AdapterError(`${what} failed: ${response.message}`, { dbName: this.dbName })\n\t\t\t}\n\t\t}\n\t\tawait check({ id: 0, type: 'begin' }, 'BEGIN IndexedDB import')\n\t\ttry {\n\t\t\tfor (const statement of restoreDumpStatements(dump)) {\n\t\t\t\tawait check(\n\t\t\t\t\t{ id: 0, type: 'execute', sql: statement.sql, params: statement.params },\n\t\t\t\t\t'IndexedDB import',\n\t\t\t\t)\n\t\t\t}\n\t\t\tawait check({ id: 0, type: 'commit' }, 'COMMIT IndexedDB import')\n\t\t} catch (error) {\n\t\t\tawait worker.send({ id: 0, type: 'rollback' }).catch(() => undefined)\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Surface a `store:opfs-unavailable` diagnostic when the worker reported that\n\t * persistence silently degraded to an in-memory database, so a data-loss\n\t * condition is observable instead of failing silently. A persistent open (or a\n\t * bridge that does not report a mode, e.g. the Node mock) emits nothing.\n\t */\n\tprivate reportStorageMode(data: unknown): void {\n\t\tthis.storageOpenState = parseStorageOpenState(data)\n\t\tif (!this.emitNonPersistentDiagnostic || !this.emitter || !this.storageOpenState) {\n\t\t\treturn\n\t\t}\n\t\tif (this.storageOpenState.persistent === false && this.storage === 'opfs') {\n\t\t\tconst reason = this.storageOpenState.fallbackReason ?? 'unsupported'\n\t\t\tthis.emitter.emit({\n\t\t\t\ttype: 'store:opfs-unavailable',\n\t\t\t\tdbName: this.dbName,\n\t\t\t\treason,\n\t\t\t\tmessage: `OPFS persistence is unavailable (${reason}) for database \"${this.dbName}\"; the store is running in memory and data will not survive a reload.`,\n\t\t\t})\n\t\t}\n\t}\n\n\tgetStorageOpenState(): StorageOpenState | null {\n\t\treturn this.storageOpenState\n\t}\n\n\t/**\n\t * Durable or loud, never silent (NEW-STORE-6). When the open or a promotion\n\t * ended on non-persistent storage and the app did not opt into that, emit the\n\t * blocking `store:durability-lost` event and refuse every later write. A\n\t * bridge that reports no mode (the Node mock) is not judged.\n\t */\n\tprivate enforceDurability(\n\t\tphase: 'open' | 'promotion',\n\t\tfailure?: { reason: 'open-failed'; message: string },\n\t): void {\n\t\tif (this.allowNonDurable) return\n\t\tif (phase === 'open' && this.deferOpenDurabilityCheck && !failure) return\n\t\tlet reason: 'lock-conflict' | 'timeout' | 'unsupported' | 'open-failed'\n\t\tif (failure) {\n\t\t\treason = failure.reason\n\t\t} else if (this.storageOpenState?.persistent === false) {\n\t\t\treason = this.storageOpenState.fallbackReason ?? 'unsupported'\n\t\t} else {\n\t\t\tthis.durabilityLoss = null\n\t\t\treturn\n\t\t}\n\t\tthis.durabilityLoss = { phase, reason }\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'store:durability-lost',\n\t\t\tdbName: this.dbName,\n\t\t\tphase,\n\t\t\treason,\n\t\t\tmessage:\n\t\t\t\t`Database \"${this.dbName}\" has no durable storage (${reason} during ${phase}); ` +\n\t\t\t\t`writes are refused until the app reloads with durable storage.${failure ? ` ${failure.message}` : ''}`,\n\t\t})\n\t}\n\n\tprivate guardDurableWrite(): void {\n\t\tif (this.durabilityLoss) {\n\t\t\tthrow new StorageDurabilityError(\n\t\t\t\tthis.dbName,\n\t\t\t\tthis.durabilityLoss.phase,\n\t\t\t\tthis.durabilityLoss.reason,\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate warnSharedWorkerDeprecated(): void {\n\t\tif (!this.sharedWorkerUrl || warnedSharedWorkerDeprecated) {\n\t\t\treturn\n\t\t}\n\t\twarnedSharedWorkerDeprecated = true\n\t\tconsole.warn(\n\t\t\t'[kora] sharedWorkerUrl is deprecated and ignored: SharedWorker-hosted SQLite ' +\n\t\t\t\t'cannot use OPFS and is never durable. Kora uses the durable dedicated-worker ' +\n\t\t\t\t'leader/follower path for multi-tab storage.',\n\t\t)\n\t}\n\n\t/**\n\t * Close the database. A leader closes the database file and releases the OPFS\n\t * pool inside its worker, terminates the worker, and only then releases the\n\t * tab leader lock, so the next leader never races a still-running worker for\n\t * the file handles (NEW-STORE-10). A follower just leaves.\n\t */\n\tasync close(): Promise<void> {\n\t\tif (!this.bridge && !this.tabSession) return\n\t\tthis.closing = true\n\t\ttry {\n\t\t\tif (this.injectedBridge && this.bridge) {\n\t\t\t\tawait this.bridge.send({ id: 0, type: 'close' })\n\t\t\t} else if (this.ownWorker && !this.ownWorker.isTerminated()) {\n\t\t\t\tconst response = await this.ownWorker\n\t\t\t\t\t.send({ id: 0, type: 'close' }, undefined, { timeoutMs: 5000 })\n\t\t\t\t\t.catch((error: unknown) => ({\n\t\t\t\t\t\tid: 0,\n\t\t\t\t\t\ttype: 'error' as const,\n\t\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\tcode: 'CLOSE_FAILED',\n\t\t\t\t\t}))\n\t\t\t\tif (response.type === 'error') {\n\t\t\t\t\t// Terminating the worker below still releases the pool.\n\t\t\t\t\tconsole.warn(`[kora] Closing \"${this.dbName}\" in its worker failed:`, response.message)\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tawait this.teardown()\n\t\t\tthis.closing = false\n\t\t}\n\t}\n\n\t/**\n\t * Release everything this adapter holds, in ownership order: lifecycle\n\t * listeners, then the worker (which holds the OPFS pool and its Web Lock),\n\t * then the tab leader lock or queued promotion request.\n\t */\n\tprivate async teardown(): Promise<void> {\n\t\tthis.removeLifecycleListeners?.()\n\t\tthis.removeLifecycleListeners = null\n\t\tconst session = this.tabSession\n\t\tthis.tabSession = null\n\t\tsession?.stopRelay?.()\n\t\tthis.bridge?.terminate()\n\t\tthis.bridge = null\n\t\tthis.ownWorker = null\n\t\tif (session) {\n\t\t\tif (session.releaseLock) {\n\t\t\t\tawait session.releaseLock()\n\t\t\t}\n\t\t\tsession.cancelPromotionWatch?.()\n\t\t}\n\t\tthis.opened = false\n\t\tthis.suspended = false\n\t\tconst gate = this.gate\n\t\tthis.gate = null\n\t\tgate?.open()\n\t}\n\n\t/**\n\t * Promotes a follower to leader after the previous leader released the storage\n\t * lock (its tab closed, crashed, or suspended on freeze). The new worker waits\n\t * on the pool's Web Lock and handles, so it never races the previous worker.\n\t * Requests issued during the hand-off wait for it; requests that were in\n\t * flight to the old leader fail with a retriable `BridgeTerminatedError`.\n\t */\n\tprivate async promoteToLeader(): Promise<void> {\n\t\tif (\n\t\t\tthis.promoting ||\n\t\t\tthis.closing ||\n\t\t\t!this.opened ||\n\t\t\tthis.workerUrl === undefined ||\n\t\t\tthis.schema === null\n\t\t) {\n\t\t\treturn\n\t\t}\n\t\tthis.promoting = true\n\t\tconst gate = createGate()\n\t\tthis.gate = gate\n\n\t\ttry {\n\t\t\tconst { WebWorkerBridge } = await import('./sqlite-wasm-channel')\n\t\t\tif (this.closing || !this.opened) return\n\t\t\tconst worker = new WebWorkerBridge(this.workerUrl, this.workerResponseTimeoutMs, {\n\t\t\t\tonEvent: (event) => this.onWorkerEvent(event),\n\t\t\t})\n\t\t\tconst previousBridge = this.bridge\n\t\t\tthis.bridge = worker\n\t\t\tthis.ownWorker = worker\n\t\t\tif (this.tabSession) {\n\t\t\t\tthis.tabSession.role = 'leader'\n\t\t\t}\n\t\t\tpreviousBridge?.terminate()\n\n\t\t\t// Re-open against our own worker. DDL is idempotent, and the OPFS data the\n\t\t\t// old leader persisted is readable once its worker is gone.\n\t\t\tconst ddlStatements = generateFullDDL(this.schema)\n\t\t\tlet response: WorkerSuccessResponse\n\t\t\ttry {\n\t\t\t\tresponse = await this.openLeaderWorker(worker, ddlStatements)\n\t\t\t} catch (error) {\n\t\t\t\t// The promoted worker could not obtain durable storage. Never let it\n\t\t\t\t// run silently in memory.\n\t\t\t\tthis.storageOpenState = { persistent: false, mode: 'memory' }\n\t\t\t\tthis.enforceDurability('promotion', {\n\t\t\t\t\treason: 'open-failed',\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.storageOpenState = parseStorageOpenState(response.data) ?? this.storageOpenState\n\t\t\tthis.enforceDurability('promotion')\n\t\t\ttry {\n\t\t\t\tawait this.afterLeaderOpen(response.data)\n\t\t\t} catch (error) {\n\t\t\t\tthis.enforceDurability('promotion', {\n\t\t\t\t\treason: 'open-failed',\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t})\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.promoting = false\n\t\t\tif (this.gate === gate) this.gate = null\n\t\t\tgate.open()\n\t\t}\n\t}\n\n\t/**\n\t * Page Lifecycle: a frozen or bfcached page cannot run, so it must not keep\n\t * other tabs waiting on its storage. Leaders terminate their worker (freeing\n\t * the OPFS pool) and then release the leader lock, so a visible tab is\n\t * promoted; followers withdraw their queued lock request so a frozen tab is\n\t * never granted leadership it cannot use. Never uses Web Locks `steal`.\n\t */\n\tprivate installLifecycleListeners(): void {\n\t\tif (!this.releaseOnFreeze || this.removeLifecycleListeners) return\n\t\tif (typeof addEventListener !== 'function' || typeof document === 'undefined') return\n\t\tconst onFreeze = (): void => this.suspend()\n\t\tconst onPageHide = (): void => this.suspend()\n\t\tconst onResume = (): void => {\n\t\t\tvoid this.resume()\n\t\t}\n\t\tconst onPageShow = (event: Event): void => {\n\t\t\tif ((event as PageTransitionEvent).persisted) void this.resume()\n\t\t}\n\t\tdocument.addEventListener('freeze', onFreeze)\n\t\tdocument.addEventListener('resume', onResume)\n\t\taddEventListener('pagehide', onPageHide)\n\t\taddEventListener('pageshow', onPageShow)\n\t\tthis.removeLifecycleListeners = () => {\n\t\t\tdocument.removeEventListener('freeze', onFreeze)\n\t\t\tdocument.removeEventListener('resume', onResume)\n\t\t\tremoveEventListener('pagehide', onPageHide)\n\t\t\tremoveEventListener('pageshow', onPageShow)\n\t\t}\n\t}\n\n\t/** Synchronous by necessity: a `freeze` handler gets no further turns. */\n\tprivate suspend(): void {\n\t\tif (!this.opened || this.suspended || this.closing || this.promoting) return\n\t\tthis.suspended = true\n\t\tthis.gate = createGate()\n\t\tconst session = this.tabSession\n\t\tthis.tabSession = null\n\t\t// Worker (pool holder) first, then the leader lock (NEW-STORE-10).\n\t\tthis.bridge?.terminate()\n\t\tthis.bridge = null\n\t\tthis.ownWorker = null\n\t\tif (session) {\n\t\t\tvoid session.releaseLock?.()\n\t\t\tsession.cancelPromotionWatch?.()\n\t\t}\n\t}\n\n\tprivate async resume(): Promise<void> {\n\t\tif (!this.suspended || this.closing || this.schema === null) return\n\t\tconst gate = this.gate\n\t\ttry {\n\t\t\tconst response = await this.attachAndOpen(generateFullDDL(this.schema))\n\t\t\tthis.reportStorageMode(response.data)\n\t\t\tthis.enforceDurability('promotion')\n\t\t\tawait this.afterLeaderOpen(response.data)\n\t\t} catch (error) {\n\t\t\tthis.enforceDurability('promotion', {\n\t\t\t\treason: 'open-failed',\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t} finally {\n\t\t\tthis.suspended = false\n\t\t\tif (this.gate === gate) this.gate = null\n\t\t\tgate?.open()\n\t\t}\n\t}\n\n\t/**\n\t * Non-transactional write. Takes the same mutex as {@link transaction}, so it\n\t * never lands inside this tab's open transaction and is never rolled back\n\t * with it (STORE-8). Inside a transaction callback, use the `tx` handle.\n\t */\n\tasync execute(sql: string, params?: unknown[], options?: StorageRequestOptions): Promise<void> {\n\t\tthis.guardOpen()\n\t\tthis.guardDurableWrite()\n\t\tconst release = await this.mutex.acquire()\n\t\ttry {\n\t\t\tconst response = await this.sendRequest(\n\t\t\t\t{\n\t\t\t\t\tid: 0,\n\t\t\t\t\ttype: 'execute',\n\t\t\t\t\tsql,\n\t\t\t\t\tparams,\n\t\t\t\t\t...(options?.requestId ? { requestId: options.requestId } : {}),\n\t\t\t\t},\n\t\t\t\toptions?.signal ? { signal: options.signal } : undefined,\n\t\t\t)\n\t\t\tif (response.type === 'error') {\n\t\t\t\tthrow new AdapterError(`Execute failed: ${response.message}`, { sql, params })\n\t\t\t}\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\t/**\n\t * Non-transactional read. Waits for this tab's open transaction to finish, so\n\t * it never observes uncommitted (possibly rolled-back) rows (STORE-8).\n\t */\n\tasync query<T>(sql: string, params?: unknown[], options?: StorageRequestOptions): Promise<T[]> {\n\t\tthis.guardOpen()\n\t\tconst release = await this.mutex.acquire()\n\t\ttry {\n\t\t\tconst response = await this.sendRequest(\n\t\t\t\t{\n\t\t\t\t\tid: 0,\n\t\t\t\t\ttype: 'query',\n\t\t\t\t\tsql,\n\t\t\t\t\tparams,\n\t\t\t\t\t...(options?.requestId ? { requestId: options.requestId } : {}),\n\t\t\t\t},\n\t\t\t\toptions?.signal ? { signal: options.signal } : undefined,\n\t\t\t)\n\t\t\tif (response.type === 'error') {\n\t\t\t\tthrow new AdapterError(`Query failed: ${response.message}`, { sql, params })\n\t\t\t}\n\t\t\treturn (response.data as T[]) ?? []\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\tasync transaction(fn: (tx: Transaction) => Promise<void>): Promise<void> {\n\t\tthis.guardOpen()\n\t\tthis.guardDurableWrite()\n\n\t\tconst release = await this.mutex.acquire()\n\t\tlet txBridge: WorkerBridge | null = null\n\t\t// Every statement of the span must reach the worker that ran BEGIN. If\n\t\t// leadership changed hands mid-transaction, the old worker rolled it back\n\t\t// (or is gone); sending the rest to the new worker would autocommit a\n\t\t// partial transaction, so fail with a retriable error instead.\n\t\tconst sendInSpan = async (request: WorkerRequest): Promise<WorkerResponse> => {\n\t\t\tif (txBridge === null || this.bridge !== txBridge) {\n\t\t\t\tthrow new BridgeTerminatedError(request.type, 'storage leader changed during transaction')\n\t\t\t}\n\t\t\treturn txBridge.send(request)\n\t\t}\n\t\ttry {\n\t\t\twhile (this.gate) {\n\t\t\t\tawait this.gate.promise\n\t\t\t}\n\t\t\ttxBridge = this.bridge\n\t\t\tif (!txBridge) throw new StoreNotOpenError()\n\t\t\tconst begin = await sendInSpan({ id: 0, type: 'begin' })\n\t\t\tif (begin.type === 'error') {\n\t\t\t\tthrow new AdapterError(`BEGIN transaction failed: ${begin.message}`)\n\t\t\t}\n\n\t\t\tconst tx: Transaction = {\n\t\t\t\texecute: async (sql: string, params?: unknown[]): Promise<void> => {\n\t\t\t\t\tconst response = await sendInSpan({ id: 0, type: 'execute', sql, params })\n\t\t\t\t\tif (response.type === 'error') {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction execute failed: ${response.message}`, {\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tquery: async <T>(sql: string, params?: unknown[]): Promise<T[]> => {\n\t\t\t\t\tconst response = await sendInSpan({ id: 0, type: 'query', sql, params })\n\t\t\t\t\tif (response.type === 'error') {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction query failed: ${response.message}`, { sql, params })\n\t\t\t\t\t}\n\t\t\t\t\treturn (response.data as T[]) ?? []\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tawait fn(tx)\n\t\t\tconst commit = await sendInSpan({ id: 0, type: 'commit' })\n\t\t\tif (commit.type === 'error') {\n\t\t\t\tthrow new AdapterError(`COMMIT transaction failed: ${commit.message}`)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Attempt rollback on the worker that ran BEGIN, but don't mask the\n\t\t\t// original error.\n\t\t\ttry {\n\t\t\t\tif (txBridge && this.bridge === txBridge) {\n\t\t\t\t\tawait txBridge.send({ id: 0, type: 'rollback' })\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Rollback failure is secondary to the original error\n\t\t\t}\n\t\t\tthrow error\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\tasync migrate(from: number, to: number, migration: MigrationPlan): Promise<void> {\n\t\tthis.guardOpen()\n\t\tthis.guardDurableWrite()\n\n\t\tconst release = await this.mutex.acquire()\n\t\ttry {\n\t\t\tawait this.sendChecked({ id: 0, type: 'begin' }, 'BEGIN migration')\n\n\t\t\tfor (const sql of migration.statements) {\n\t\t\t\tconst response = await this.sendRequest({ id: 0, type: 'execute', sql })\n\t\t\t\tif (response.type === 'error') {\n\t\t\t\t\tthrow new AdapterError(`Migration from v${from} to v${to} failed: ${response.message}`, {\n\t\t\t\t\t\tfrom,\n\t\t\t\t\t\tto,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait this.sendChecked({ id: 0, type: 'commit' }, 'COMMIT migration')\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tawait this.sendRequest({ id: 0, type: 'rollback' })\n\t\t\t} catch {\n\t\t\t\t// Rollback failure is secondary\n\t\t\t}\n\t\t\tif (error instanceof AdapterError) throw error\n\t\t\tthrow new AdapterError(\n\t\t\t\t`Migration from v${from} to v${to} failed: ${(error as Error).message}`,\n\t\t\t\t{ from, to },\n\t\t\t)\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\t/**\n\t * Export the database as a Uint8Array (for IndexedDB persistence).\n\t * Only available when the database is open.\n\t */\n\tasync exportDatabase(): Promise<Uint8Array> {\n\t\tthis.guardOpen()\n\t\tconst release = await this.mutex.acquire()\n\t\ttry {\n\t\t\tconst response = await this.sendRequest({ id: 0, type: 'export' })\n\t\t\tif (response.type === 'error') {\n\t\t\t\tthrow new AdapterError(`Export failed: ${response.message}`, {\n\t\t\t\t\tcode: response.code,\n\t\t\t\t\tcontext: response.context,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn response.data as Uint8Array\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\t/**\n\t * Import a serialized database snapshot.\n\t */\n\tasync importDatabase(data: Uint8Array): Promise<void> {\n\t\tthis.guardOpen()\n\t\tthis.guardDurableWrite()\n\t\tconst release = await this.mutex.acquire()\n\t\ttry {\n\t\t\tconst response = await this.sendRequest({ id: 0, type: 'import', data })\n\t\t\tif (response.type === 'error') {\n\t\t\t\tthrow new AdapterError(`Import failed: ${response.message}`)\n\t\t\t}\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\tprivate guardOpen(): void {\n\t\tif (!this.opened || (!this.bridge && !this.gate)) {\n\t\t\tthrow new StoreNotOpenError()\n\t\t}\n\t}\n\n\tprivate async sendRequest(\n\t\trequest: WorkerRequest,\n\t\toptions?: WorkerSendOptions,\n\t): Promise<WorkerResponse> {\n\t\t// While leadership changes hands, wait for the new bridge instead of\n\t\t// sending into a torn-down one.\n\t\twhile (this.gate) {\n\t\t\tawait this.gate.promise\n\t\t}\n\t\tconst bridge = this.bridge\n\t\tif (!bridge) {\n\t\t\tthrow new StoreNotOpenError()\n\t\t}\n\t\treturn bridge.send(request, undefined, options)\n\t}\n\n\tprivate async sendChecked(request: WorkerRequest, description: string): Promise<void> {\n\t\tconst response = await this.sendRequest(request)\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`${description} failed: ${response.message}`)\n\t\t}\n\t}\n}\n\n/**\n * Run `fn` in one transaction sent straight to `worker`, bypassing the adapter's mutex\n * and leadership gate (the caller is the leader opening its own worker).\n */\nasync function runWorkerTransaction(\n\tworker: WorkerBridge,\n\tfn: (tx: Transaction) => Promise<void>,\n): Promise<void> {\n\tconst send = async (request: WorkerRequest, what: string): Promise<WorkerResponse> => {\n\t\tconst response = await worker.send(request)\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`${what} failed: ${response.message}`, {\n\t\t\t\tcode: response.code,\n\t\t\t\t...(request.type === 'execute' || request.type === 'query' ? { sql: request.sql } : {}),\n\t\t\t})\n\t\t}\n\t\treturn response\n\t}\n\tawait send({ id: 0, type: 'begin' }, 'BEGIN')\n\ttry {\n\t\tawait fn({\n\t\t\texecute: async (sql, params) => {\n\t\t\t\tawait send({ id: 0, type: 'execute', sql, params }, 'Leader open execute')\n\t\t\t},\n\t\t\tquery: async <T>(sql: string, params?: unknown[]): Promise<T[]> => {\n\t\t\t\tconst response = await send({ id: 0, type: 'query', sql, params }, 'Leader open query')\n\t\t\t\treturn ((response as WorkerSuccessResponse).data as T[]) ?? []\n\t\t\t},\n\t\t})\n\t\tawait send({ id: 0, type: 'commit' }, 'COMMIT')\n\t} catch (error) {\n\t\tawait worker.send({ id: 0, type: 'rollback' }).catch(() => undefined)\n\t\tthrow error\n\t}\n}\n\nfunction parseStorageOpenState(data: unknown): StorageOpenState | null {\n\tif (typeof data !== 'object' || data === null) {\n\t\treturn null\n\t}\n\tconst mode = data as {\n\t\tpersistent?: boolean\n\t\tfallbackReason?: StorageFallbackReason\n\t\tpoolName?: string\n\t\tjournalMode?: string\n\t}\n\tif (typeof mode.persistent !== 'boolean') {\n\t\treturn null\n\t}\n\treturn {\n\t\tpersistent: mode.persistent,\n\t\tmode: mode.persistent ? 'opfs' : 'memory',\n\t\t...(mode.fallbackReason ? { fallbackReason: mode.fallbackReason } : {}),\n\t\t...(typeof mode.poolName === 'string' ? { poolName: mode.poolName } : {}),\n\t\t...(typeof mode.journalMode === 'string' ? { journalMode: mode.journalMode } : {}),\n\t}\n}\n","/**\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","/**\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","/// <reference lib=\"dom\" />\nimport { PersistenceError } from '../errors'\n\nconst IDB_DATABASE_NAME = 'kora-persistence'\nconst IDB_STORE_NAME = 'databases'\nconst IDB_VERSION = 1\n\nconst DUMP_SUFFIX = '::dump'\n\n/**\n * Returns true when an IndexedDB error indicates storage quota was exceeded.\n */\nexport function isIndexedDbQuotaError(error: unknown): boolean {\n\tif (error instanceof DOMException && error.name === 'QuotaExceededError') {\n\t\treturn true\n\t}\n\tif (error instanceof PersistenceError && error.context?.quotaExceeded === true) {\n\t\treturn true\n\t}\n\tif (error instanceof Error && /quota/i.test(error.message)) {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunction rejectWithIdbError(\n\treject: (reason: unknown) => void,\n\tmessage: string,\n\tcontext: Record<string, unknown>,\n\tevent: Event,\n): void {\n\tconst domError = (event.target as IDBRequest | IDBTransaction | undefined)?.error\n\tif (domError && isIndexedDbQuotaError(domError)) {\n\t\treject(\n\t\t\tnew PersistenceError(message, {\n\t\t\t\t...context,\n\t\t\t\tquotaExceeded: true,\n\t\t\t\tcause: domError.message,\n\t\t\t}),\n\t\t)\n\t\treturn\n\t}\n\treject(new PersistenceError(message, context))\n}\n\n/**\n * Open the IndexedDB database used for SQLite persistence.\n * Creates the object store on first access.\n */\nfunction openIdb(): Promise<IDBDatabase> {\n\treturn new Promise<IDBDatabase>((resolve, reject) => {\n\t\tconst request = indexedDB.open(IDB_DATABASE_NAME, IDB_VERSION)\n\t\trequest.onupgradeneeded = () => {\n\t\t\tconst db = request.result\n\t\t\tif (!db.objectStoreNames.contains(IDB_STORE_NAME)) {\n\t\t\t\tdb.createObjectStore(IDB_STORE_NAME)\n\t\t\t}\n\t\t}\n\t\trequest.onsuccess = () => resolve(request.result)\n\t\trequest.onerror = () =>\n\t\t\treject(\n\t\t\t\tnew PersistenceError(\n\t\t\t\t\t`Failed to open IndexedDB: ${request.error?.message ?? 'unknown error'}`,\n\t\t\t\t\t{ database: IDB_DATABASE_NAME },\n\t\t\t\t),\n\t\t\t)\n\t})\n}\n\n/**\n * Save a serialized SQLite database to IndexedDB.\n *\n * @param dbName - Key under which to store the data\n * @param data - Serialized database as Uint8Array\n */\nexport async function saveToIndexedDB(dbName: string, data: Uint8Array): Promise<void> {\n\tconst idb = await openIdb()\n\ttry {\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst tx = idb.transaction(IDB_STORE_NAME, 'readwrite')\n\t\t\tconst store = tx.objectStore(IDB_STORE_NAME)\n\t\t\tstore.put(data, dbName)\n\t\t\ttx.oncomplete = () => resolve()\n\t\t\ttx.onerror = (event) =>\n\t\t\t\trejectWithIdbError(\n\t\t\t\t\treject,\n\t\t\t\t\t`Failed to save database \"${dbName}\" to IndexedDB`,\n\t\t\t\t\t{ dbName },\n\t\t\t\t\tevent,\n\t\t\t\t)\n\t\t})\n\t} finally {\n\t\tidb.close()\n\t}\n}\n\n/**\n * Save a logical SQL dump payload to IndexedDB for import-fallback restore.\n */\nexport async function saveDumpToIndexedDB(dbName: string, dump: unknown): Promise<void> {\n\tconst idb = await openIdb()\n\ttry {\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst tx = idb.transaction(IDB_STORE_NAME, 'readwrite')\n\t\t\tconst store = tx.objectStore(IDB_STORE_NAME)\n\t\t\tstore.put(dump, `${dbName}${DUMP_SUFFIX}`)\n\t\t\ttx.oncomplete = () => resolve()\n\t\t\ttx.onerror = (event) =>\n\t\t\t\trejectWithIdbError(\n\t\t\t\t\treject,\n\t\t\t\t\t`Failed to save dump for database \"${dbName}\"`,\n\t\t\t\t\t{ dbName },\n\t\t\t\t\tevent,\n\t\t\t\t)\n\t\t})\n\t} finally {\n\t\tidb.close()\n\t}\n}\n\n/**\n * Load a serialized SQLite database from IndexedDB.\n *\n * @param dbName - Key under which the data was stored\n * @returns The serialized database, or null if not found\n */\nexport async function loadFromIndexedDB(dbName: string): Promise<Uint8Array | null> {\n\tconst idb = await openIdb()\n\ttry {\n\t\treturn await new Promise<Uint8Array | null>((resolve, reject) => {\n\t\t\tconst tx = idb.transaction(IDB_STORE_NAME, 'readonly')\n\t\t\tconst store = tx.objectStore(IDB_STORE_NAME)\n\t\t\tconst request = store.get(dbName)\n\t\t\trequest.onsuccess = () => {\n\t\t\t\tconst result = request.result\n\t\t\t\tif (result instanceof Uint8Array) {\n\t\t\t\t\tresolve(result)\n\t\t\t\t} else if (result) {\n\t\t\t\t\t// Handle ArrayBuffer or other typed array forms\n\t\t\t\t\tresolve(new Uint8Array(result as ArrayBuffer))\n\t\t\t\t} else {\n\t\t\t\t\tresolve(null)\n\t\t\t\t}\n\t\t\t}\n\t\t\trequest.onerror = () =>\n\t\t\t\treject(\n\t\t\t\t\tnew PersistenceError(`Failed to load database \"${dbName}\" from IndexedDB`, { dbName }),\n\t\t\t\t)\n\t\t})\n\t} finally {\n\t\tidb.close()\n\t}\n}\n\n/**\n * Load a logical SQL dump payload from IndexedDB.\n */\nexport async function loadDumpFromIndexedDB<T>(dbName: string): Promise<T | null> {\n\tconst idb = await openIdb()\n\ttry {\n\t\treturn await new Promise<T | null>((resolve, reject) => {\n\t\t\tconst tx = idb.transaction(IDB_STORE_NAME, 'readonly')\n\t\t\tconst store = tx.objectStore(IDB_STORE_NAME)\n\t\t\tconst request = store.get(`${dbName}${DUMP_SUFFIX}`)\n\t\t\trequest.onsuccess = () => {\n\t\t\t\tresolve((request.result as T | undefined) ?? null)\n\t\t\t}\n\t\t\trequest.onerror = () =>\n\t\t\t\treject(\n\t\t\t\t\tnew PersistenceError(`Failed to load dump for database \"${dbName}\" from IndexedDB`, {\n\t\t\t\t\t\tdbName,\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t})\n\t} finally {\n\t\tidb.close()\n\t}\n}\n\n/**\n * Delete only the binary SQLite database snapshot from IndexedDB.\n *\n * The logical dump is stored under a separate key and may be the authoritative\n * durable fallback when a browser worker cannot export a binary SQLite image.\n */\nexport async function deleteSnapshotFromIndexedDB(dbName: string): Promise<void> {\n\tconst idb = await openIdb()\n\ttry {\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst tx = idb.transaction(IDB_STORE_NAME, 'readwrite')\n\t\t\tconst store = tx.objectStore(IDB_STORE_NAME)\n\t\t\tstore.delete(dbName)\n\t\t\ttx.oncomplete = () => resolve()\n\t\t\ttx.onerror = () =>\n\t\t\t\treject(\n\t\t\t\t\tnew PersistenceError(`Failed to delete database snapshot \"${dbName}\" from IndexedDB`, {\n\t\t\t\t\t\tdbName,\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t})\n\t} finally {\n\t\tidb.close()\n\t}\n}\n\n/**\n * Delete a serialized SQLite database from IndexedDB.\n *\n * @param dbName - Key to delete\n */\nexport async function deleteFromIndexedDB(dbName: string): Promise<void> {\n\tconst idb = await openIdb()\n\ttry {\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst tx = idb.transaction(IDB_STORE_NAME, 'readwrite')\n\t\t\tconst store = tx.objectStore(IDB_STORE_NAME)\n\t\t\tstore.delete(dbName)\n\t\t\tstore.delete(`${dbName}${DUMP_SUFFIX}`)\n\t\t\ttx.oncomplete = () => resolve()\n\t\t\ttx.onerror = () =>\n\t\t\t\treject(\n\t\t\t\t\tnew PersistenceError(`Failed to delete database \"${dbName}\" from IndexedDB`, { dbName }),\n\t\t\t\t)\n\t\t})\n\t} finally {\n\t\tidb.close()\n\t}\n}\n","/// <reference lib=\"dom\" />\n/**\n * Kora-owned manifest of the local databases this origin created (W8a).\n *\n * One record per database name, kept in its own IndexedDB database so it is\n * readable from any tab without opening the OPFS pools. It records which backend\n * holds the database's data, which is how Kora keeps OPFS and IndexedDB from\n * ever holding two disjoint copies: a backend switch is a recorded, explicit\n * migration, never a silent fresh start (NEW-STORE-5, LMS-5c).\n *\n * The manifest is advisory for listing and authoritative for the backend\n * choice. Nothing is ever evicted automatically; deletion is the explicit\n * `deleteDatabase()` API.\n */\n\nconst MANIFEST_DB_NAME = 'kora-manifest'\nconst MANIFEST_STORE = 'databases'\nconst MANIFEST_VERSION = 1\n\n/** Where a database's data lives. */\nexport type LocalDatabaseBackend = 'opfs' | 'indexeddb'\n\n/** One manifest entry, as returned by `listDatabases()`. */\nexport interface LocalDatabaseRecord {\n\t/** The store name (`store.name`, plus the per-user suffix if namespaced). */\n\tdbName: string\n\t/** The backend that holds the authoritative copy. */\n\tbackend: LocalDatabaseBackend\n\t/** OPFS pool holding the file (opfs backend only). */\n\tpoolName?: string\n\t/** Wall-clock milliseconds; informational only, never used for ordering. */\n\tcreatedAt: number\n\t/** Wall-clock milliseconds of the last durable open; informational only. */\n\tlastOpenedAt: number\n}\n\n/** True when this runtime can keep a manifest (browsers; not Node tests). */\nexport function isManifestAvailable(): boolean {\n\treturn typeof indexedDB !== 'undefined'\n}\n\nfunction openManifest(): Promise<IDBDatabase> {\n\treturn new Promise<IDBDatabase>((resolve, reject) => {\n\t\tconst request = indexedDB.open(MANIFEST_DB_NAME, MANIFEST_VERSION)\n\t\trequest.onupgradeneeded = () => {\n\t\t\tconst db = request.result\n\t\t\tif (!db.objectStoreNames.contains(MANIFEST_STORE)) {\n\t\t\t\tdb.createObjectStore(MANIFEST_STORE, { keyPath: 'dbName' })\n\t\t\t}\n\t\t}\n\t\trequest.onsuccess = () => resolve(request.result)\n\t\trequest.onerror = () =>\n\t\t\treject(request.error ?? new Error('Failed to open the Kora storage manifest'))\n\t})\n}\n\nasync function withStore<T>(\n\tmode: IDBTransactionMode,\n\trun: (store: IDBObjectStore) => IDBRequest<T> | null,\n): Promise<T | undefined> {\n\tconst db = await openManifest()\n\ttry {\n\t\treturn await new Promise<T | undefined>((resolve, reject) => {\n\t\t\tconst tx = db.transaction(MANIFEST_STORE, mode)\n\t\t\tconst request = run(tx.objectStore(MANIFEST_STORE))\n\t\t\tlet result: T | undefined\n\t\t\tif (request) {\n\t\t\t\trequest.onsuccess = () => {\n\t\t\t\t\tresult = request.result\n\t\t\t\t}\n\t\t\t}\n\t\t\ttx.oncomplete = () => resolve(result)\n\t\t\ttx.onerror = () => reject(tx.error ?? new Error('Kora storage manifest transaction failed'))\n\t\t\ttx.onabort = () => reject(tx.error ?? new Error('Kora storage manifest transaction aborted'))\n\t\t})\n\t} finally {\n\t\tdb.close()\n\t}\n}\n\nfunction isRecord(value: unknown): value is LocalDatabaseRecord {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst record = value as Partial<LocalDatabaseRecord>\n\treturn (\n\t\ttypeof record.dbName === 'string' &&\n\t\t(record.backend === 'opfs' || record.backend === 'indexeddb')\n\t)\n}\n\n/** Read the manifest entry for a database, or null when Kora never recorded it. */\nexport async function readManifestRecord(dbName: string): Promise<LocalDatabaseRecord | null> {\n\tif (!isManifestAvailable()) return null\n\tconst value = await withStore<unknown>('readonly', (store) => store.get(dbName))\n\treturn isRecord(value) ? value : null\n}\n\n/** Every database Kora recorded on this origin, sorted by name. */\nexport async function listManifestRecords(): Promise<LocalDatabaseRecord[]> {\n\tif (!isManifestAvailable()) return []\n\tconst values = await withStore<unknown[]>('readonly', (store) => store.getAll())\n\treturn (values ?? []).filter(isRecord).sort((a, b) => (a.dbName < b.dbName ? -1 : 1))\n}\n\n/**\n * Record (or refresh) which backend holds a database after a durable open,\n * keeping the original creation time.\n */\nexport async function recordDatabase(entry: {\n\tdbName: string\n\tbackend: LocalDatabaseBackend\n\tpoolName?: string\n}): Promise<void> {\n\tif (!isManifestAvailable()) return\n\tconst existing = await readManifestRecord(entry.dbName)\n\tconst now = Date.now()\n\tconst record: LocalDatabaseRecord = {\n\t\tdbName: entry.dbName,\n\t\tbackend: entry.backend,\n\t\t...(entry.poolName ? { poolName: entry.poolName } : {}),\n\t\tcreatedAt: existing?.createdAt ?? now,\n\t\tlastOpenedAt: now,\n\t}\n\tawait withStore('readwrite', (store) => store.put(record))\n}\n\n/** Remove a database's manifest entry (after its data was deleted). */\nexport async function deleteManifestRecord(dbName: string): Promise<void> {\n\tif (!isManifestAvailable()) return\n\tawait withStore('readwrite', (store) => store.delete(dbName))\n}\n"],"mappings":"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","names":["import_core","import_core","WebWorkerBridge"]}
1
+ {"version":3,"sources":["../../src/errors.ts","../../src/adapters/mutex.ts","../../src/adapters/sqlite-wasm-channel.ts","../../src/adapters/indexeddb.ts","../../src/adapters/indexeddb-adapter.ts","../../src/migrations/schema-ceiling.ts","../../src/adapters/database-dump.ts","../../src/adapters/indexeddb-persistence-scheduler.ts","../../src/adapters/sqlite-wasm-adapter.ts","../../src/adapters/opfs-names.ts","../../src/multi-tab/tab-storage.ts","../../src/adapters/sqlite-wasm-persistence.ts","../../src/adapters/storage-manifest.ts"],"sourcesContent":["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 * Async mutex for serializing access to a resource across `await` boundaries.\n *\n * This exists because an adapter's `transaction()` is async (its callback may\n * await), but the underlying database transaction (BEGIN…COMMIT) is not\n * reentrant: starting a second transaction while one is open is an error. When\n * a client applies a relayed remote operation while a local write is in flight,\n * the two `transaction()` calls interleave at their `await` points, and without\n * serialization the second `BEGIN` collides with the first, silently dropping\n * the operation. Serializing every transaction through this mutex guarantees at\n * most one is active at a time.\n */\nexport class Mutex {\n\tprivate locked = false\n\tprivate readonly waiters: Array<() => void> = []\n\n\t/**\n\t * Acquire the mutex. Resolves with a release function once the lock is held.\n\t * If the mutex is already held, the caller waits (FIFO) until it is released.\n\t */\n\tasync acquire(): Promise<() => void> {\n\t\tif (!this.locked) {\n\t\t\tthis.locked = true\n\t\t\treturn this.createRelease()\n\t\t}\n\n\t\treturn new Promise<() => void>((resolve) => {\n\t\t\tthis.waiters.push(() => {\n\t\t\t\tresolve(this.createRelease())\n\t\t\t})\n\t\t})\n\t}\n\n\tprivate createRelease(): () => void {\n\t\tlet released = false\n\t\treturn () => {\n\t\t\tif (released) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\treleased = true\n\t\t\tconst next = this.waiters.shift()\n\t\t\tif (next) {\n\t\t\t\tnext()\n\t\t\t} else {\n\t\t\t\tthis.locked = false\n\t\t\t}\n\t\t}\n\t}\n}\n","/// <reference lib=\"dom\" />\nimport { BridgeTerminatedError, RequestAbortedError, WorkerTimeoutError } from '../errors'\n\n// === Message Protocol ===\n\n/** Fields every request carries besides its type. */\ninterface RequestMeta {\n\t/** Correlates a worker response with its request on one postMessage channel. */\n\tid: number\n\t/**\n\t * Stable id for de-duplication. A follower retrying a request (after a\n\t * `LeaderUnresponsiveError`, say) sends the same id and the leader worker\n\t * answers from its response cache instead of applying it twice.\n\t */\n\trequestId?: string\n}\n\n/**\n * Request message sent from the main thread (or a follower tab) to the SQLite\n * WASM worker.\n */\nexport type WorkerRequest =\n\t| (RequestMeta & {\n\t\t\ttype: 'open'\n\t\t\tddlStatements: string[]\n\t\t\tdbName?: string\n\t\t\t/**\n\t\t\t * `memory` opens an in-memory database without touching OPFS (the\n\t\t\t * IndexedDB adapter persists snapshots itself). Defaults to `opfs`.\n\t\t\t */\n\t\t\tstorage?: 'opfs' | 'memory'\n\t\t\t/** Fail with `NOT_FOUND` instead of creating the database file. */\n\t\t\tmustExist?: boolean\n\t })\n\t| (RequestMeta & { type: 'close' })\n\t| (RequestMeta & { type: 'destroy' })\n\t| (RequestMeta & { type: 'execute'; sql: string; params?: unknown[] })\n\t| (RequestMeta & { type: 'query'; sql: string; params?: unknown[] })\n\t| (RequestMeta & { type: 'begin' })\n\t| (RequestMeta & { type: 'commit' })\n\t| (RequestMeta & { type: 'rollback' })\n\t| (RequestMeta & { type: 'migrate'; from: number; to: number; statements: string[] })\n\t| (RequestMeta & { type: 'export' })\n\t| (RequestMeta & { type: 'import'; data: Uint8Array })\n\t| (RequestMeta & {\n\t\t\t/**\n\t\t\t * Leader worker only: answer follower RPC, pings and heartbeats on this\n\t\t\t * BroadcastChannel directly, so a hung leader main thread never blocks\n\t\t\t * other tabs (NEW-STORE-9).\n\t\t\t */\n\t\t\ttype: 'serve'\n\t\t\tchannelName: string\n\t })\n\n/**\n * Response message sent from the worker back to the main thread.\n * Matches the request `id` for correlation.\n */\nexport type WorkerResponse =\n\t| { id: number; type: 'success'; data?: unknown }\n\t| { id: number; type: 'error'; message: string; code: string; context?: Record<string, unknown> }\n\n/**\n * Unsolicited status the worker reports while it works on a request, such as\n * waiting for another holder of the OPFS pool (a blocking state the app shows).\n */\nexport type WorkerStatusEvent =\n\t| {\n\t\t\tkind: 'storage-blocked'\n\t\t\tresource: 'pool' | 'legacy-pool'\n\t\t\tpoolName: string\n\t }\n\t| {\n\t\t\tkind: 'storage-unblocked'\n\t\t\tresource: 'pool' | 'legacy-pool'\n\t\t\tpoolName: string\n\t\t\twaitedMs: number\n\t }\n\n/** Envelope for {@link WorkerStatusEvent}s on the worker's postMessage channel. */\nexport interface WorkerEventMessage {\n\tid: -1\n\ttype: 'event'\n\tevent: WorkerStatusEvent\n}\n\n/** Message the main thread posts to a dedicated worker. */\nexport type WorkerInboundMessage = WorkerRequest & { clientId?: string }\n\n/** Per-request options accepted by bridges. */\nexport interface WorkerSendOptions {\n\t/** Cancels the wait for the response (the worker may still apply the request). */\n\tsignal?: AbortSignal\n\t/** Overrides the bridge timeout for this request; `Infinity` disables it. */\n\ttimeoutMs?: number\n}\n\n// === WorkerBridge Interface ===\n\n/**\n * Abstraction over the communication channel with the SQLite WASM worker.\n * In browsers, this is backed by a real Web Worker via MessagePort.\n * In Node.js tests, this is backed by better-sqlite3 via MockWorkerBridge.\n */\nexport interface WorkerBridge {\n\t/** Send a request to the worker and wait for a response. */\n\tsend(\n\t\trequest: WorkerRequest,\n\t\tclientId?: string,\n\t\toptions?: WorkerSendOptions,\n\t): Promise<WorkerResponse>\n\n\t/** Terminate the worker. Safe to call multiple times. */\n\tterminate(): void\n}\n\n// === Mutex ===\n\n// The Mutex now lives in its own module so non-browser adapters (better-sqlite3)\n// can serialize transactions without importing browser-worker code.\nexport { Mutex } from './mutex'\n\n// === WebWorkerBridge ===\n\n/** Options for {@link WebWorkerBridge}. */\nexport interface WebWorkerBridgeOptions {\n\t/** Receives status events (blocking state) the worker reports. */\n\tonEvent?: (event: WorkerStatusEvent) => void\n}\n\n/**\n * WorkerBridge implementation for browser environments.\n * Communicates with an actual Web Worker running SQLite WASM.\n */\nexport class WebWorkerBridge implements WorkerBridge {\n\tprivate worker: Worker\n\tprivate pending = new Map<\n\t\tnumber,\n\t\t{ resolve: (r: WorkerResponse) => void; reject: (e: Error) => void; type: string }\n\t>()\n\tprivate nextId = 1\n\tprivate terminated = false\n\tprivate timeoutMs: number\n\n\t/**\n\t * @param workerUrl - URL to the sqlite-wasm-worker script\n\t * @param timeoutMs - Timeout for worker responses in milliseconds (default: 30000)\n\t * @param options - Status event listener\n\t */\n\tconstructor(workerUrl: string | URL, timeoutMs = 30000, options: WebWorkerBridgeOptions = {}) {\n\t\tthis.timeoutMs = timeoutMs\n\t\tthis.worker = new Worker(workerUrl, { type: 'module' })\n\t\tthis.worker.onmessage = (event: MessageEvent<WorkerResponse | WorkerEventMessage>) => {\n\t\t\tconst message = event.data\n\t\t\tif (message.type === 'event') {\n\t\t\t\toptions.onEvent?.(message.event)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst entry = this.pending.get(message.id)\n\t\t\tif (entry) {\n\t\t\t\tthis.pending.delete(message.id)\n\t\t\t\tentry.resolve(message)\n\t\t\t}\n\t\t}\n\t\tthis.worker.onerror = (event) => {\n\t\t\t// Reject all pending requests on worker error\n\t\t\tconst error = new Error(`Worker error: ${event.message}`)\n\t\t\tfor (const [id, entry] of this.pending) {\n\t\t\t\tthis.pending.delete(id)\n\t\t\t\tentry.reject(error)\n\t\t\t}\n\t\t}\n\t}\n\n\t/** True once {@link terminate} ran. */\n\tisTerminated(): boolean {\n\t\treturn this.terminated\n\t}\n\n\t/** Post a request without awaiting a response (fire-and-forget control messages). */\n\tpost(request: WorkerRequest, clientId?: string): void {\n\t\tif (this.terminated) return\n\t\tconst message: WorkerInboundMessage = { ...request, id: this.nextId++, clientId }\n\t\tthis.worker.postMessage(message)\n\t}\n\n\tasync send(\n\t\trequest: WorkerRequest,\n\t\tclientId?: 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: 'Worker has been terminated',\n\t\t\t\tcode: 'WORKER_TERMINATED',\n\t\t\t}\n\t\t}\n\n\t\tconst id = this.nextId++\n\t\tconst message: WorkerInboundMessage = { ...request, id, clientId }\n\t\tconst timeoutMs = options.timeoutMs ?? this.timeoutMs\n\t\tconst signal = options.signal\n\t\tif (signal?.aborted) {\n\t\t\tthrow new RequestAbortedError(request.type, request.requestId ?? String(id))\n\t\t}\n\n\t\treturn new Promise<WorkerResponse>((resolve, reject) => {\n\t\t\tconst timer = Number.isFinite(timeoutMs)\n\t\t\t\t? setTimeout(() => {\n\t\t\t\t\t\tthis.pending.delete(id)\n\t\t\t\t\t\tcleanup()\n\t\t\t\t\t\treject(new WorkerTimeoutError(message.type, timeoutMs))\n\t\t\t\t\t}, timeoutMs)\n\t\t\t\t: undefined\n\t\t\tconst onAbort = (): void => {\n\t\t\t\tthis.pending.delete(id)\n\t\t\t\tcleanup()\n\t\t\t\treject(new RequestAbortedError(request.type, request.requestId ?? String(id)))\n\t\t\t}\n\t\t\tconst cleanup = (): void => {\n\t\t\t\tif (timer !== undefined) clearTimeout(timer)\n\t\t\t\tsignal?.removeEventListener('abort', onAbort)\n\t\t\t}\n\t\t\tsignal?.addEventListener('abort', onAbort, { once: true })\n\n\t\t\tthis.pending.set(id, {\n\t\t\t\ttype: request.type,\n\t\t\t\tresolve: (response) => {\n\t\t\t\t\tcleanup()\n\t\t\t\t\tresolve(response)\n\t\t\t\t},\n\t\t\t\treject: (error) => {\n\t\t\t\t\tcleanup()\n\t\t\t\t\treject(error)\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tthis.worker.postMessage(message)\n\t\t})\n\t}\n\n\tterminate(): void {\n\t\tif (this.terminated) return\n\t\tthis.terminated = true\n\t\t// Terminating the worker releases its OPFS handles and the pool Web Lock it\n\t\t// holds; callers release the tab leader lock only after this (NEW-STORE-10).\n\t\tthis.worker.terminate()\n\t\tfor (const [id, entry] of this.pending) {\n\t\t\tthis.pending.delete(id)\n\t\t\tentry.reject(new BridgeTerminatedError(entry.type, 'worker terminated'))\n\t\t}\n\t}\n}\n","// Entry point for @korajs/store/indexeddb\nexport { IndexedDbAdapter } from './indexeddb-adapter'\nexport type { IndexedDbAdapterOptions } from './indexeddb-adapter'\n","import type { KoraEventEmitter, SchemaDefinition } from '@korajs/core'\nimport {\n\tAdapterError,\n\tPersistenceError,\n\tSchemaVersionAheadError,\n\tStorageBackendMismatchError,\n} from '../errors'\nimport { STORED_SCHEMA_VERSION_SQL, storedSchemaVersion } from '../migrations/schema-ceiling'\nimport type { MigrationPlan, StorageAdapter, StorageOpenState, Transaction } from '../types'\nimport { type DatabaseDump, exportDump, restoreDumpStatements } from './database-dump'\nimport { IndexedDbPersistenceScheduler } from './indexeddb-persistence-scheduler'\nimport { SqliteWasmAdapter } from './sqlite-wasm-adapter'\nimport type { WorkerBridge } from './sqlite-wasm-channel'\nimport {\n\tdeleteSnapshotFromIndexedDB,\n\tisIndexedDbQuotaError,\n\tloadDumpFromIndexedDB,\n\tloadFromIndexedDB,\n\tsaveDumpToIndexedDB,\n\tsaveToIndexedDB,\n} from './sqlite-wasm-persistence'\nimport { isManifestAvailable, readManifestRecord, recordDatabase } from './storage-manifest'\n\n/**\n * Options for creating an IndexedDbAdapter.\n */\nexport interface IndexedDbAdapterOptions {\n\t/**\n\t * Database name used as the IndexedDB key for persistence.\n\t * Defaults to 'kora-db'.\n\t */\n\tdbName?: string\n\n\t/**\n\t * Injected WorkerBridge for testing. If omitted, a WebWorkerBridge is created\n\t * in browser environments.\n\t */\n\tbridge?: WorkerBridge\n\n\t/**\n\t * URL to the sqlite-wasm-worker script. Required in browsers if no bridge is provided.\n\t */\n\tworkerUrl?: string | URL\n\n\t/** Timeout for worker / follower RPC responses. Defaults to 30000ms. */\n\tworkerResponseTimeoutMs?: number\n\n\t/**\n\t * Debounce interval (ms) before writing snapshots to IndexedDB. Defaults to 500.\n\t */\n\tpersistenceDebounceMs?: number\n\n\t/**\n\t * When set, persistence failures and quota errors are emitted on this emitter.\n\t */\n\temitter?: KoraEventEmitter\n}\n\n/**\n * IndexedDB-backed adapter that uses SQLite WASM in-memory and serializes\n * the entire database to IndexedDB after mutations (coalesced/debounced).\n *\n * This is the fallback adapter for browsers where OPFS is not available.\n * It provides the same SQL interface as SqliteWasmAdapter, but persists by\n * serializing the full SQLite database to a single IndexedDB blob.\n *\n * @example\n * ```typescript\n * const adapter = new IndexedDbAdapter({ workerUrl: '/sqlite-wasm-worker.js' })\n * ```\n */\nexport class IndexedDbAdapter implements StorageAdapter {\n\tprivate inner: SqliteWasmAdapter\n\tprivate readonly dbName: string\n\tprivate readonly emitter: KoraEventEmitter | undefined\n\tprivate readonly scheduler: IndexedDbPersistenceScheduler\n\tprivate storageOpenState: StorageOpenState | null = null\n\tprivate readonly options: IndexedDbAdapterOptions\n\t/** True in browsers (a real worker), where the backend choice is recorded. */\n\tprivate readonly usesRealWorker: boolean\n\n\tconstructor(options: IndexedDbAdapterOptions = {}) {\n\t\tthis.options = options\n\t\tthis.dbName = options.dbName ?? 'kora-db'\n\t\tthis.emitter = options.emitter\n\t\tthis.usesRealWorker = !options.bridge && options.workerUrl !== undefined\n\t\tthis.inner = new SqliteWasmAdapter({\n\t\t\tbridge: options.bridge,\n\t\t\tworkerUrl: options.workerUrl,\n\t\t\tdbName: this.dbName,\n\t\t\tworkerResponseTimeoutMs: options.workerResponseTimeoutMs,\n\t\t\t// The inner SQLite database is in memory by design: this adapter makes it\n\t\t\t// durable by persisting snapshots to IndexedDB, so the inner adapter must\n\t\t\t// not refuse writes as non-durable, and must never touch OPFS (which would\n\t\t\t// create a second, disjoint copy of the data).\n\t\t\tallowNonDurable: true,\n\t\t\tstorage: 'memory',\n\t\t\tskipBackendRecord: true,\n\t\t\t// A browser leader restores the snapshot into its own fresh worker database\n\t\t\t// before serving other tabs (first open and promotion, STORE-6).\n\t\t\tonLeaderWorkerOpened: (tx) => this.restoreDumpIfFresh(tx),\n\t\t})\n\t\tthis.scheduler = new IndexedDbPersistenceScheduler({\n\t\t\tdebounceMs: options.persistenceDebounceMs,\n\t\t\tflush: () => this.writeSnapshot(),\n\t\t\tonError: (error) => this.handlePersistenceError(error),\n\t\t})\n\t}\n\n\tasync open(schema: SchemaDefinition): Promise<void> {\n\t\t// The backend choice is recorded per database. If OPFS holds the\n\t\t// authoritative copy, carry it over explicitly before this session starts,\n\t\t// so OPFS and IndexedDB never hold two disjoint copies (NEW-STORE-5).\n\t\tconst tracksBackend = this.usesRealWorker && isManifestAvailable()\n\t\tconst recorded = tracksBackend ? await readManifestRecord(this.dbName).catch(() => null) : null\n\t\tconst carried = recorded?.backend === 'opfs' ? await this.readOpfsCopy(schema) : null\n\n\t\tawait this.inner.open(schema)\n\t\tthis.storageOpenState = { persistent: true, mode: 'indexeddb' }\n\n\t\tif (carried && this.inner.isLeader()) {\n\t\t\t// The fresh worker database is marked restored, then receives the OPFS copy.\n\t\t\tawait this.inner.transaction(async (tx) => {\n\t\t\t\tawait markRestored(tx)\n\t\t\t})\n\t\t\tawait this.applyDump(carried)\n\t\t\tawait this.scheduler.flushNow()\n\t\t\tawait recordDatabase({ dbName: this.dbName, backend: 'indexeddb' })\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:storage-migrated',\n\t\t\t\tdbName: this.dbName,\n\t\t\t\tfrom: 'opfs',\n\t\t\t\tto: 'indexeddb',\n\t\t\t\tmessage: `Database \"${this.dbName}\" moved from OPFS into IndexedDB because OPFS is not usable in this session.`,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\n\t\t// Only the storage leader restores, and only into a database no snapshot was\n\t\t// restored into yet: a follower tab (or a second adapter on the same worker)\n\t\t// opening must never rewrite the leader's live database (STORE-6).\n\t\tif (this.inner.isLeader()) {\n\t\t\tawait this.restoreIfFresh()\n\t\t}\n\t\t// A snapshot a newer build migrated (RT-109): refuse it before anything writes, and\n\t\t// without the snapshot a close() would write (the in-memory copy ran this build's\n\t\t// DDL before the restore).\n\t\tconst stored = storedSchemaVersion(\n\t\t\tawait this.inner.query<{ value: unknown }>(STORED_SCHEMA_VERSION_SQL),\n\t\t)\n\t\tif (stored > schema.version) {\n\t\t\tthis.scheduler.dispose()\n\t\t\tawait this.inner.close().catch(() => undefined)\n\t\t\tthrow new SchemaVersionAheadError(this.dbName, stored, schema.version)\n\t\t}\n\t\tif (tracksBackend) {\n\t\t\tawait recordDatabase({ dbName: this.dbName, backend: 'indexeddb' }).catch((error: unknown) =>\n\t\t\t\tconsole.warn(`[kora] Could not record the storage backend of \"${this.dbName}\":`, error),\n\t\t\t)\n\t\t}\n\t}\n\n\t/**\n\t * Read the OPFS copy of this database through a short-lived SQLite WASM\n\t * adapter (as leader, or as a follower of a tab that has it open). Refuses\n\t * loudly when OPFS cannot be read, instead of starting an empty copy.\n\t */\n\tprivate async readOpfsCopy(schema: SchemaDefinition): Promise<DatabaseDump> {\n\t\tconst reader = new SqliteWasmAdapter({\n\t\t\tdbName: this.dbName,\n\t\t\tworkerUrl: this.options.workerUrl,\n\t\t\tworkerResponseTimeoutMs: this.options.workerResponseTimeoutMs,\n\t\t\tskipBackendRecord: true,\n\t\t\treleaseOnFreeze: false,\n\t\t})\n\t\ttry {\n\t\t\tawait reader.open(schema)\n\t\t\tif (reader.getStorageOpenState()?.persistent !== true) {\n\t\t\t\tthrow new StorageBackendMismatchError(\n\t\t\t\t\tthis.dbName,\n\t\t\t\t\t'OPFS',\n\t\t\t\t\t'IndexedDB',\n\t\t\t\t\t`OPFS reported ${reader.getStorageOpenState()?.fallbackReason ?? 'no durable storage'}`,\n\t\t\t\t)\n\t\t\t}\n\t\t\tlet dump: DatabaseDump = { tables: [] }\n\t\t\tawait reader.transaction(async (tx) => {\n\t\t\t\tdump = await exportDump((sql, params) => tx.query(sql, params))\n\t\t\t})\n\t\t\treturn dump\n\t\t} catch (error) {\n\t\t\tif (error instanceof StorageBackendMismatchError) throw error\n\t\t\tthrow new StorageBackendMismatchError(\n\t\t\t\tthis.dbName,\n\t\t\t\t'OPFS',\n\t\t\t\t'IndexedDB',\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t)\n\t\t} finally {\n\t\t\tawait reader.close().catch(() => undefined)\n\t\t}\n\t}\n\n\tprivate async applyDump(dump: DatabaseDump): Promise<void> {\n\t\tawait this.inner.transaction(async (tx) => {\n\t\t\tfor (const statement of restoreDumpStatements(dump)) {\n\t\t\t\tawait tx.execute(statement.sql, statement.params)\n\t\t\t}\n\t\t})\n\t}\n\n\tasync close(): Promise<void> {\n\t\tawait this.scheduler.flushNow()\n\t\tthis.scheduler.dispose()\n\t\tawait this.inner.close()\n\t}\n\n\tasync execute(sql: string, params?: unknown[]): Promise<void> {\n\t\tawait this.inner.execute(sql, params)\n\t\tthis.scheduler.schedule()\n\t}\n\n\tasync query<T>(sql: string, params?: unknown[]): Promise<T[]> {\n\t\treturn this.inner.query<T>(sql, params)\n\t}\n\n\tasync transaction(fn: (tx: Transaction) => Promise<void>): Promise<void> {\n\t\tawait this.inner.transaction(fn)\n\t\tthis.scheduler.schedule()\n\t}\n\n\tasync migrate(from: number, to: number, migration: MigrationPlan): Promise<void> {\n\t\tawait this.inner.migrate(from, to, migration)\n\t\tthis.scheduler.schedule()\n\t}\n\n\tgetStorageOpenState(): StorageOpenState | null {\n\t\treturn this.storageOpenState\n\t}\n\n\t/**\n\t * Force an immediate snapshot write to IndexedDB (skips debounce).\n\t * Useful before tab unload or in tests.\n\t */\n\tasync flushPersistence(): Promise<void> {\n\t\tawait this.scheduler.flushNow()\n\t}\n\n\t/**\n\t * Durability barrier (RT-35): resolves once every write committed before the call is\n\t * in a snapshot persisted to IndexedDB, and rejects when the snapshot cannot be\n\t * written. Sync calls it before an operation leaves the device, so the server never\n\t * holds an operation this device could lose on reload.\n\t */\n\tasync ensureDurable(): Promise<void> {\n\t\tawait this.scheduler.flushBarrier()\n\t}\n\n\tprivate async writeSnapshot(): Promise<void> {\n\t\tconst dump = await this.exportDump()\n\t\tawait saveDumpToIndexedDB(this.dbName, dump)\n\n\t\ttry {\n\t\t\tconst data = await this.inner.exportDatabase()\n\t\t\tawait saveToIndexedDB(this.dbName, data)\n\t\t} catch (error) {\n\t\t\tif (!isUnsupportedWorkerExport(error)) {\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\tawait deleteSnapshotFromIndexedDB(this.dbName)\n\t\t}\n\t}\n\n\tprivate handlePersistenceError(error: unknown): void {\n\t\tconst message = error instanceof Error ? error.message : 'IndexedDB persistence failed'\n\t\tconst code = error instanceof PersistenceError ? error.code : 'PERSISTENCE_FAILED'\n\t\tconst quotaExceeded = isIndexedDbQuotaError(error)\n\n\t\tif (quotaExceeded) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:quota-exceeded',\n\t\t\t\tdbName: this.dbName,\n\t\t\t\tmessage,\n\t\t\t})\n\t\t}\n\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'store:persistence-error',\n\t\t\tdbName: this.dbName,\n\t\t\tmessage,\n\t\t\tcode: quotaExceeded ? 'QUOTA_EXCEEDED' : code,\n\t\t})\n\t}\n\n\t/**\n\t * Restore the persisted snapshot into the worker database unless one was restored\n\t * into it already (this adapter or another one sharing the worker). The binary\n\t * snapshot (Node / test workers that support export) is imported whole; the JSON dump\n\t * (browsers) is restored in one transaction.\n\t */\n\tprivate async restoreIfFresh(): Promise<void> {\n\t\tconst persisted = await loadFromIndexedDB(this.dbName)\n\t\tif (persisted) {\n\t\t\tconst fresh = { value: false }\n\t\t\tawait this.inner.transaction(async (tx) => {\n\t\t\t\tfresh.value = !(await isRestored(tx))\n\t\t\t})\n\t\t\tif (!fresh.value) return\n\t\t\ttry {\n\t\t\t\tawait this.inner.importDatabase(persisted)\n\t\t\t\tawait this.inner.transaction(markRestored)\n\t\t\t\treturn\n\t\t\t} catch {\n\t\t\t\t// Fall through to the JSON dump.\n\t\t\t}\n\t\t}\n\t\tawait this.inner.transaction((tx) => this.restoreDumpIfFresh(tx))\n\t}\n\n\t/** In `tx`: restore the JSON dump when no snapshot was restored into this database. */\n\tprivate async restoreDumpIfFresh(tx: Transaction): Promise<void> {\n\t\tif (await isRestored(tx)) return\n\t\tawait markRestored(tx)\n\t\tconst dump = await loadDumpFromIndexedDB<DatabaseDump>(this.dbName)\n\t\tif (!dump) return\n\t\tfor (const statement of restoreDumpStatements(dump)) {\n\t\t\tawait tx.execute(statement.sql, statement.params)\n\t\t}\n\t}\n\n\t/** A consistent snapshot: every table is read inside one transaction (STORE-7). */\n\tprivate async exportDump(): Promise<DatabaseDump> {\n\t\tlet dump: DatabaseDump = { tables: [] }\n\t\tawait this.inner.transaction(async (tx) => {\n\t\t\tdump = await exportDump((sql, params) => tx.query(sql, params))\n\t\t})\n\t\treturn dump\n\t}\n}\n\n/**\n * Connection-local marker (a TEMP table: never part of the database file or of a dump)\n * recording that a snapshot was restored into this worker database (STORE-6).\n */\nconst RESTORED_MARKER = '_kora_idb_restored'\n\nasync function isRestored(tx: Transaction): Promise<boolean> {\n\tconst rows = await tx.query<{ name: string }>(\n\t\t\"SELECT name FROM sqlite_temp_master WHERE type = 'table' AND name = ?\",\n\t\t[RESTORED_MARKER],\n\t)\n\treturn rows.length > 0\n}\n\nasync function markRestored(tx: Transaction): Promise<void> {\n\tawait tx.execute(`CREATE TEMP TABLE IF NOT EXISTS ${RESTORED_MARKER} (restored INTEGER)`)\n}\n\nfunction isUnsupportedWorkerExport(error: unknown): boolean {\n\treturn error instanceof AdapterError && error.context?.code === 'EXPORT_NOT_SUPPORTED'\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","import { quoteIdent } from '@korajs/core'\nimport { PersistenceError } from '../errors'\n\n/**\n * Logical copy of every user table of a SQLite database. Used to move a\n * database between storage backends (OPFS <-> IndexedDB) explicitly, and as the\n * IndexedDB adapter's durable snapshot format.\n */\nexport interface DatabaseDump {\n\ttables: Array<{\n\t\tname: string\n\t\t/**\n\t\t * The table's `CREATE TABLE` statement (from `sqlite_master`), so a restore can\n\t\t * recreate a table the target database does not have yet with its constraints.\n\t\t * Absent in dumps written before it was recorded.\n\t\t */\n\t\tsql?: string\n\t\tcolumns: string[]\n\t\trows: Array<Record<string, unknown>>\n\t}>\n}\n\n/** A parameterized SQL statement. */\nexport interface DumpStatement {\n\tsql: string\n\tparams?: unknown[]\n}\n\ntype QueryFn = <T>(sql: string, params?: unknown[]) => Promise<T[]>\n\n/** Throws unless `identifier` is a plain SQL identifier (dump data is untrusted). */\nexport function ensureSafeIdentifier(identifier: string): string {\n\tif (!/^[a-zA-Z0-9_]+$/.test(identifier)) {\n\t\tthrow new PersistenceError(`Unsafe SQL identifier: ${identifier}`, {\n\t\t\tcode: 'UNSAFE_IDENTIFIER',\n\t\t\tidentifier,\n\t\t})\n\t}\n\treturn identifier\n}\n\n/**\n * Read every user table through `query`. Pass a transaction's `query` to get a\n * consistent snapshot.\n */\nexport async function exportDump(query: QueryFn): Promise<DatabaseDump> {\n\tconst tableRows = await query<{ name: string; sql: string | null }>(\n\t\t\"SELECT name, sql FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\",\n\t)\n\tconst tables: DatabaseDump['tables'] = []\n\tfor (const tableRow of tableRows) {\n\t\tconst tableName = ensureSafeIdentifier(tableRow.name)\n\t\tconst columns = await query<{ name: string }>(`PRAGMA table_info(${quoteIdent(tableName)})`)\n\t\tconst rows = await query<Record<string, unknown>>(`SELECT * FROM ${quoteIdent(tableName)}`)\n\t\ttables.push({\n\t\t\tname: tableName,\n\t\t\t...(typeof tableRow.sql === 'string' ? { sql: tableRow.sql } : {}),\n\t\t\tcolumns: columns.map((column) => column.name),\n\t\t\trows,\n\t\t})\n\t}\n\treturn { tables }\n}\n\n/**\n * Statements that replace the contents of each dumped table with the dump's\n * rows. Run them inside one transaction so a failure leaves the target as it was.\n *\n * A table the target does not have yet (the store creates some of its bookkeeping\n * tables after the schema DDL) is created first from the dump's recorded `CREATE TABLE`\n * statement, constraints included. A dump written before statements were recorded\n * creates it untyped, so its rows are still restored.\n *\n * @param createMissingTables - Kept for callers that restore into scratch databases\n * with no schema; missing tables are always created now.\n */\nexport function restoreDumpStatements(\n\tdump: DatabaseDump,\n\t_createMissingTables = true,\n): DumpStatement[] {\n\tconst statements: DumpStatement[] = []\n\tfor (const table of dump.tables) {\n\t\tconst name = quoteIdent(ensureSafeIdentifier(table.name))\n\t\tconst create = createTableIfNotExists(table.name, table.sql)\n\t\tif (create) {\n\t\t\tstatements.push({ sql: create })\n\t\t} else {\n\t\t\tconst columns = table.columns.map((column) => quoteIdent(ensureSafeIdentifier(column)))\n\t\t\tif (columns.length === 0) continue\n\t\t\tstatements.push({ sql: `CREATE TABLE IF NOT EXISTS ${name} (${columns.join(', ')})` })\n\t\t}\n\t\tstatements.push({ sql: `DELETE FROM ${name}` })\n\t\tfor (const row of table.rows) {\n\t\t\tconst columns = table.columns.filter((column) =>\n\t\t\t\tObject.prototype.hasOwnProperty.call(row, column),\n\t\t\t)\n\t\t\tif (columns.length === 0) continue\n\t\t\tconst quotedColumns = columns\n\t\t\t\t.map((column) => quoteIdent(ensureSafeIdentifier(column)))\n\t\t\t\t.join(', ')\n\t\t\tstatements.push({\n\t\t\t\tsql: `INSERT INTO ${name} (${quotedColumns}) VALUES (${columns.map(() => '?').join(', ')})`,\n\t\t\t\tparams: columns.map((column) => row[column]),\n\t\t\t})\n\t\t}\n\t}\n\treturn statements\n}\n\n/**\n * The recorded `CREATE TABLE` statement made idempotent, or null when there is none or\n * it is not a single plain `CREATE TABLE` of exactly this table (dump data is untrusted).\n */\nfunction createTableIfNotExists(tableName: string, sql: string | undefined): string | null {\n\tif (typeof sql !== 'string' || sql.includes(';')) return null\n\tconst match = /^\\s*CREATE\\s+TABLE\\s+(?:IF\\s+NOT\\s+EXISTS\\s+)?(\"?)([A-Za-z0-9_]+)\\1\\s*\\(/i.exec(\n\t\tsql,\n\t)\n\tif (!match || match[2] !== tableName) return null\n\treturn `CREATE TABLE IF NOT EXISTS ${quoteIdent(tableName)} ${sql.slice(match[0].length - 1)}`\n}\n","/// <reference lib=\"dom\" />\n\nconst DEFAULT_DEBOUNCE_MS = 500\n\nexport interface IndexedDbPersistenceSchedulerOptions {\n\t/** Debounce interval before writing a snapshot. Defaults to 500ms. */\n\tdebounceMs?: number\n\t/** Persist the in-memory database to IndexedDB. */\n\tflush: () => Promise<void>\n\t/** Called when persistence fails (mutation already committed in memory). */\n\tonError?: (error: unknown) => void\n}\n\n/**\n * Coalesces IndexedDB snapshot writes: debounces rapid mutations and flushes\n * immediately on tab hide (`visibilitychange`) or explicit {@link flushNow}.\n */\nexport class IndexedDbPersistenceScheduler {\n\tprivate readonly debounceMs: number\n\tprivate readonly flush: () => Promise<void>\n\tprivate readonly onError: ((error: unknown) => void) | undefined\n\tprivate timer: ReturnType<typeof setTimeout> | null = null\n\tprivate inFlight: Promise<void> | null = null\n\tprivate disposed = false\n\t/**\n\t * Mutation generations (RT-35): `scheduled` counts schedule() calls (one per committed\n\t * write), `persisted` is the generation the last SUCCESSFUL snapshot covered. A\n\t * snapshot covers every generation scheduled before it started.\n\t */\n\tprivate scheduledGeneration = 0\n\tprivate persistedGeneration = 0\n\t/** Error of the last snapshot attempt, cleared by the next success. */\n\tprivate lastFlushError: unknown = null\n\tprivate readonly onVisibilityChange: () => void\n\n\tconstructor(options: IndexedDbPersistenceSchedulerOptions) {\n\t\tthis.debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS\n\t\tthis.flush = options.flush\n\t\tthis.onError = options.onError\n\t\tthis.onVisibilityChange = () => {\n\t\t\tif (typeof document !== 'undefined' && document.visibilityState === 'hidden') {\n\t\t\t\tvoid this.flushNow()\n\t\t\t}\n\t\t}\n\t\tif (typeof document !== 'undefined') {\n\t\t\tdocument.addEventListener('visibilitychange', this.onVisibilityChange)\n\t\t}\n\t}\n\n\t/** Schedule a debounced snapshot write. */\n\tschedule(): void {\n\t\tif (this.disposed) return\n\t\tthis.scheduledGeneration++\n\t\tif (this.debounceMs <= 0) {\n\t\t\tvoid this.flushNow()\n\t\t\treturn\n\t\t}\n\t\tif (this.timer !== null) {\n\t\t\tclearTimeout(this.timer)\n\t\t}\n\t\tthis.timer = setTimeout(() => {\n\t\t\tthis.timer = null\n\t\t\tvoid this.flushNow()\n\t\t}, this.debounceMs)\n\t}\n\n\t/**\n\t * Cancel any pending debounce and persist immediately. Resolves only when a snapshot\n\t * taken after every write scheduled so far was written (STORE-7): a snapshot already in\n\t * flight may predate the latest writes, so the call waits for it and then writes again\n\t * while anything is still dirty. A failed snapshot ends the loop (the failure is\n\t * reported through `onError`; {@link flushBarrier} is the variant that rejects).\n\t */\n\tasync flushNow(): Promise<void> {\n\t\tif (this.disposed) return\n\t\tif (this.timer !== null) {\n\t\t\tclearTimeout(this.timer)\n\t\t\tthis.timer = null\n\t\t}\n\t\t// With nothing in flight, \"persist immediately\" writes one snapshot even when no\n\t\t// write was scheduled (callers use it to force the first snapshot).\n\t\tlet mustRun = this.inFlight === null\n\t\tfor (;;) {\n\t\t\tif (this.inFlight) {\n\t\t\t\tawait this.inFlight\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (!mustRun && this.persistedGeneration >= this.scheduledGeneration) return\n\t\t\tmustRun = false\n\t\t\tthis.inFlight = this.runFlush()\n\t\t\ttry {\n\t\t\t\tawait this.inFlight\n\t\t\t} finally {\n\t\t\t\tthis.inFlight = null\n\t\t\t}\n\t\t\tif (this.lastFlushError !== null) return\n\t\t}\n\t}\n\n\t/** Whether a write was scheduled that no successful snapshot covers yet. */\n\tisDirty(): boolean {\n\t\treturn this.persistedGeneration < this.scheduledGeneration\n\t}\n\n\t/**\n\t * Durability barrier (RT-35): resolve once every write scheduled before this call is\n\t * in a snapshot persisted to IndexedDB. A snapshot already in flight may predate the\n\t * latest write, so the barrier waits for it and then writes a fresh one when needed.\n\t * Rejects when the snapshot cannot be written: the caller must treat the writes as\n\t * not durable (for example, not upload them yet).\n\t */\n\tasync flushBarrier(): Promise<void> {\n\t\tconst target = this.scheduledGeneration\n\t\tif (this.persistedGeneration >= target) return\n\t\tif (this.disposed) {\n\t\t\tthrow new Error('IndexedDB persistence was disposed before the writes were persisted')\n\t\t}\n\t\tif (this.timer !== null) {\n\t\t\tclearTimeout(this.timer)\n\t\t\tthis.timer = null\n\t\t}\n\t\t// At most two rounds: one that may have started before `target`, then one after.\n\t\tfor (let round = 0; round < 3 && this.persistedGeneration < target; round++) {\n\t\t\tif (this.inFlight) {\n\t\t\t\ttry {\n\t\t\t\t\tawait this.inFlight\n\t\t\t\t} catch {\n\t\t\t\t\t// runFlush never rejects; the error is in lastFlushError.\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tthis.inFlight = this.runFlush()\n\t\t\ttry {\n\t\t\t\tawait this.inFlight\n\t\t\t} finally {\n\t\t\t\tthis.inFlight = null\n\t\t\t}\n\t\t\tif (this.persistedGeneration < target) break\n\t\t}\n\t\tif (this.persistedGeneration < target) {\n\t\t\tthrow this.lastFlushError instanceof Error\n\t\t\t\t? this.lastFlushError\n\t\t\t\t: new Error('IndexedDB snapshot was not persisted')\n\t\t}\n\t}\n\n\tdispose(): void {\n\t\tthis.disposed = true\n\t\tif (this.timer !== null) {\n\t\t\tclearTimeout(this.timer)\n\t\t\tthis.timer = null\n\t\t}\n\t\tif (typeof document !== 'undefined') {\n\t\t\tdocument.removeEventListener('visibilitychange', this.onVisibilityChange)\n\t\t}\n\t}\n\n\tprivate async runFlush(): Promise<void> {\n\t\tconst generation = this.scheduledGeneration\n\t\ttry {\n\t\t\tawait this.flush()\n\t\t\tthis.persistedGeneration = Math.max(this.persistedGeneration, generation)\n\t\t\tthis.lastFlushError = null\n\t\t} catch (error) {\n\t\t\tthis.lastFlushError = error\n\t\t\tthis.onError?.(error)\n\t\t}\n\t}\n}\n","import { generateFullDDL } from '@korajs/core'\nimport type { KoraEventEmitter, SchemaDefinition } from '@korajs/core'\nimport {\n\tAdapterError,\n\tBridgeTerminatedError,\n\tStorageDurabilityError,\n\tStoreNotOpenError,\n} from '../errors'\nimport {\n\tFollowerBroadcastBridge,\n\ttype TabStorageSession,\n\tacquireTabStorageSession,\n} from '../multi-tab/tab-storage'\nimport type {\n\tMigrationPlan,\n\tStorageAdapter,\n\tStorageFallbackReason,\n\tStorageOpenState,\n\tTransaction,\n} from '../types'\nimport { restoreDumpStatements } from './database-dump'\nimport type { DatabaseDump } from './database-dump'\nimport { Mutex } from './sqlite-wasm-channel'\nimport type {\n\tWebWorkerBridge,\n\tWorkerBridge,\n\tWorkerRequest,\n\tWorkerResponse,\n\tWorkerSendOptions,\n\tWorkerStatusEvent,\n} from './sqlite-wasm-channel'\nimport { deleteFromIndexedDB, loadDumpFromIndexedDB } from './sqlite-wasm-persistence'\nimport { isManifestAvailable, readManifestRecord, recordDatabase } from './storage-manifest'\n\ntype WorkerSuccessResponse = Extract<WorkerResponse, { type: 'success' }>\nlet warnedSharedWorkerDeprecated = false\n\n/** Per-request options for {@link SqliteWasmAdapter.execute} and `query`. */\nexport interface StorageRequestOptions {\n\t/**\n\t * Stop waiting for the response. The leader may still apply a write that\n\t * already reached it; retry with the same `requestId` to stay idempotent.\n\t */\n\tsignal?: AbortSignal\n\t/**\n\t * Stable id for this request. A follower tab's retry with the same id is\n\t * answered from the leader's response cache instead of being applied twice.\n\t */\n\trequestId?: string\n}\n\n/**\n * Options for creating a SqliteWasmAdapter.\n */\nexport interface SqliteWasmAdapterOptions {\n\t/**\n\t * Injected WorkerBridge for testing. If omitted, a WebWorkerBridge is created\n\t * in browser environments.\n\t */\n\tbridge?: WorkerBridge\n\n\t/**\n\t * Database name for persistence. Each database gets its own OPFS pool.\n\t */\n\tdbName?: string\n\n\t/**\n\t * URL to the sqlite-wasm-worker script. Required in browsers if no bridge is provided.\n\t */\n\tworkerUrl?: string | URL\n\n\t/** Timeout for worker / follower RPC responses. Defaults to 30000ms. */\n\tworkerResponseTimeoutMs?: number\n\n\t/**\n\t * @deprecated SharedWorker-hosted SQLite cannot use OPFS SyncAccessHandle and\n\t * is never durable. Kora ignores this option and uses the dedicated-worker\n\t * leader/follower path for durable multi-tab storage.\n\t */\n\tsharedWorkerUrl?: string | URL\n\n\t/**\n\t * When false, the adapter records a non-persistent open without emitting\n\t * `store:opfs-unavailable`. `createApp()` uses this while it promotes the store\n\t * to durable IndexedDB; direct adapter users keep the diagnostic by default.\n\t */\n\temitNonPersistentDiagnostic?: boolean\n\n\t/**\n\t * Accept writes when durable OPFS storage could not be obtained and the\n\t * database runs in memory. Defaults to false: a non-durable open or promotion\n\t * emits the blocking `store:durability-lost` event and every write is refused\n\t * with {@link StorageDurabilityError}, so data is never accepted and then lost\n\t * on reload. Set true only for apps that can tolerate losing local writes.\n\t */\n\tallowNonDurable?: boolean\n\n\t/**\n\t * For `createApp()`: skip the durability check at open only, because the caller\n\t * inspects {@link SqliteWasmAdapter.getStorageOpenState} right after open and\n\t * switches to a durable backend itself. A later leader promotion that loses\n\t * durable storage is still enforced.\n\t */\n\tdeferOpenDurabilityCheck?: boolean\n\n\t/**\n\t * Release storage ownership when the page is frozen or hidden into the\n\t * back/forward cache (`freeze` / `pagehide`), so a visible tab can take over\n\t * instead of waiting on a tab that cannot run; ownership is re-acquired on\n\t * `resume` / `pageshow`. Defaults to true.\n\t */\n\treleaseOnFreeze?: boolean\n\n\t/**\n\t * When set, storage diagnostics are emitted here: `store:opfs-unavailable`,\n\t * `store:durability-lost`, `store:storage-blocked` (another holder has the\n\t * storage; the open waits), `store:storage-migrated` and\n\t * `store:db-name-collision`.\n\t */\n\temitter?: KoraEventEmitter\n\n\t/**\n\t * @internal Used by the IndexedDB adapter: open an in-memory database that\n\t * never touches OPFS (the IndexedDB adapter persists snapshots itself).\n\t */\n\tstorage?: 'opfs' | 'memory'\n\n\t/**\n\t * @internal Do not read or write the storage manifest (the IndexedDB adapter's\n\t * inner database and its one-shot OPFS reader manage the manifest themselves).\n\t */\n\tskipBackendRecord?: boolean\n\n\t/**\n\t * @internal Used by the IndexedDB adapter (STORE-6): runs in one transaction on this\n\t * tab's OWN worker right after it opened the database as storage leader (first open\n\t * or promotion), before the worker serves any other tab. The IndexedDB adapter\n\t * restores its persisted snapshot here, so a snapshot is only ever restored into a\n\t * freshly created worker database and never over a leader's live one. Throwing fails\n\t * the open (or the promotion, which then refuses writes).\n\t */\n\tonLeaderWorkerOpened?: (tx: Transaction) => Promise<void>\n}\n\ninterface Gate {\n\tpromise: Promise<void>\n\topen(): void\n}\n\nfunction createGate(): Gate {\n\tlet open: () => void = () => {}\n\tconst promise = new Promise<void>((resolve) => {\n\t\topen = resolve\n\t})\n\treturn { promise, open }\n}\n\n/**\n * SQLite WASM adapter that communicates with a SQLite instance through a WorkerBridge.\n *\n * In browsers, one tab per database (the leader) runs a dedicated worker that\n * owns the database's OPFS pool; other tabs reach that worker over a\n * BroadcastChannel. In Node.js tests, the bridge is backed by MockWorkerBridge\n * wrapping better-sqlite3.\n *\n * Storage is durable or loud, never silently in memory (W8a): another holder of\n * the storage makes the open wait (reported with `store:storage-blocked`); only a\n * runtime without OPFS SAH support gets a non-durable database, which refuses\n * writes unless the app opted in with `allowNonDurable`.\n *\n * @example\n * ```typescript\n * // Browser usage\n * const adapter = new SqliteWasmAdapter({ workerUrl: '/sqlite-wasm-worker.js' })\n *\n * // Test usage with MockWorkerBridge\n * import { MockWorkerBridge } from './sqlite-wasm-mock-bridge'\n * const adapter = new SqliteWasmAdapter({ bridge: new MockWorkerBridge() })\n * ```\n */\nexport class SqliteWasmAdapter implements StorageAdapter {\n\tprivate bridge: WorkerBridge | null = null\n\t/** The dedicated worker this tab runs while it is the database's leader. */\n\tprivate ownWorker: WebWorkerBridge | null = null\n\tprivate opened = false\n\tprivate readonly mutex = new Mutex()\n\tprivate readonly injectedBridge: WorkerBridge | undefined\n\tprivate readonly workerUrl: string | URL | undefined\n\tprivate readonly sharedWorkerUrl: string | URL | undefined\n\tprivate readonly workerResponseTimeoutMs: number\n\tprivate readonly dbName: string\n\tprivate readonly emitter: KoraEventEmitter | undefined\n\tprivate tabSession: TabStorageSession | null = null\n\tprivate readonly emitNonPersistentDiagnostic: boolean\n\tprivate storageOpenState: StorageOpenState | null = null\n\t/** Retained so a follower promoted to leader can re-open its own worker. */\n\tprivate schema: SchemaDefinition | null = null\n\tprivate promoting = false\n\tprivate closing = false\n\tprivate suspended = false\n\t/** Requests wait here while leadership changes hands (promotion, resume). */\n\tprivate gate: Gate | null = null\n\tprivate removeLifecycleListeners: (() => void) | null = null\n\tprivate readonly allowNonDurable: boolean\n\tprivate readonly deferOpenDurabilityCheck: boolean\n\tprivate readonly releaseOnFreeze: boolean\n\tprivate readonly storage: 'opfs' | 'memory'\n\tprivate readonly skipBackendRecord: boolean\n\t/** Set when this adapter lost (or never had) durable storage; writes are refused. */\n\tprivate durabilityLoss: { phase: 'open' | 'promotion'; reason: string } | null = null\n\tprivate readonly onLeaderWorkerOpened: ((tx: Transaction) => Promise<void>) | undefined\n\n\tconstructor(options: SqliteWasmAdapterOptions = {}) {\n\t\tthis.injectedBridge = options.bridge\n\t\tthis.workerUrl = options.workerUrl\n\t\tthis.sharedWorkerUrl = options.sharedWorkerUrl\n\t\tthis.workerResponseTimeoutMs = options.workerResponseTimeoutMs ?? 30_000\n\t\tthis.dbName = options.dbName ?? 'kora-db'\n\t\tthis.emitter = options.emitter\n\t\tthis.emitNonPersistentDiagnostic = options.emitNonPersistentDiagnostic ?? true\n\t\tthis.allowNonDurable = options.allowNonDurable ?? false\n\t\tthis.deferOpenDurabilityCheck = options.deferOpenDurabilityCheck ?? false\n\t\tthis.releaseOnFreeze = options.releaseOnFreeze ?? true\n\t\tthis.storage = options.storage ?? 'opfs'\n\t\tthis.skipBackendRecord = options.skipBackendRecord ?? false\n\t\tthis.onLeaderWorkerOpened = options.onLeaderWorkerOpened\n\t}\n\n\tasync open(schema: SchemaDefinition): Promise<void> {\n\t\tif (this.opened) return\n\n\t\tconst ddlStatements = generateFullDDL(schema)\n\t\tif (this.injectedBridge) {\n\t\t\tthis.bridge = this.injectedBridge\n\t\t\tconst response = await this.openCurrentBridge(ddlStatements)\n\t\t\tthis.reportStorageMode(response.data)\n\t\t\tthis.enforceDurability('open')\n\t\t\tthis.opened = true\n\t\t\treturn\n\t\t}\n\t\tif (this.workerUrl) {\n\t\t\tthis.warnSharedWorkerDeprecated()\n\t\t\tthis.schema = schema\n\t\t\ttry {\n\t\t\t\tconst response = await this.attachAndOpen(ddlStatements)\n\t\t\t\tthis.reportStorageMode(response.data)\n\t\t\t\tthis.enforceDurability('open')\n\t\t\t\tawait this.afterLeaderOpen(response.data)\n\t\t\t\tthis.opened = true\n\t\t\t} catch (error) {\n\t\t\t\t// A failed open must not keep the worker, the OPFS pool or the leader\n\t\t\t\t// lock for the rest of the page's life (NEW-STORE-8).\n\t\t\t\tawait this.teardown()\n\t\t\t\tthrow error\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tthrow new AdapterError(\n\t\t\t'SqliteWasmAdapter requires either a bridge (for testing) or a workerUrl (for browsers). ' +\n\t\t\t\t'Pass { bridge: new MockWorkerBridge() } for tests, or { workerUrl: \"/worker.js\" } for browsers.',\n\t\t)\n\t}\n\n\t/**\n\t * Join the database as leader (own worker) or follower (RPC to the leader's\n\t * worker), then open it. The open waits, without a timeout, while another\n\t * holder still has the storage; the worker reports that as a blocking state.\n\t */\n\tprivate async attachAndOpen(ddlStatements: string[]): Promise<WorkerSuccessResponse> {\n\t\tconst workerUrl = this.workerUrl\n\t\tif (!workerUrl) {\n\t\t\tthrow new AdapterError('Durable SQLite WASM storage requires workerUrl.')\n\t\t}\n\t\tconst session = await acquireTabStorageSession(this.dbName, {\n\t\t\tonPromote: () => {\n\t\t\t\tvoid this.promoteToLeader()\n\t\t\t},\n\t\t})\n\t\tthis.tabSession = session\n\n\t\tif (session.role === 'leader') {\n\t\t\tconst { WebWorkerBridge } = await import('./sqlite-wasm-channel')\n\t\t\tconst worker = new WebWorkerBridge(workerUrl, this.workerResponseTimeoutMs, {\n\t\t\t\tonEvent: (event) => this.onWorkerEvent(event),\n\t\t\t})\n\t\t\tthis.ownWorker = worker\n\t\t\tthis.bridge = worker\n\t\t\tconst response = await this.openLeaderWorker(worker, ddlStatements)\n\t\t\tthis.installLifecycleListeners()\n\t\t\treturn response\n\t\t}\n\n\t\tconst followerBridge = new FollowerBroadcastBridge(\n\t\t\tsession.channelName,\n\t\t\tthis.workerResponseTimeoutMs,\n\t\t)\n\t\tthis.bridge = followerBridge\n\t\t// Another runtime on this origin already owns this database name. That is\n\t\t// expected for multiple tabs of the same app (they share one leader),\n\t\t// but a bug if these are logically separate apps, so surface it so the\n\t\t// developer can give them distinct store names.\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'store:db-name-collision',\n\t\t\tdbName: this.dbName,\n\t\t\tmessage: `Another runtime on this origin is already using database \"${this.dbName}\"; this runtime is sharing it as a follower. If these are separate apps, give each a distinct store name.`,\n\t\t})\n\t\t// Readiness handshake: give the leader relay a moment to answer before the\n\t\t// first RPC, so a follower opened during a leader's startup race retries the\n\t\t// handshake instead of firing into the void.\n\t\tawait followerBridge.waitForLeader()\n\t\tthis.installLifecycleListeners()\n\t\treturn this.checkedOpen(followerBridge, ddlStatements)\n\t}\n\n\t/** Open the database in this tab's own worker and start serving followers. */\n\tprivate async openLeaderWorker(\n\t\tworker: WebWorkerBridge,\n\t\tddlStatements: string[],\n\t): Promise<WorkerSuccessResponse> {\n\t\tconst hook = this.onLeaderWorkerOpened\n\t\tif (!hook) {\n\t\t\tconst pending = this.checkedOpen(worker, ddlStatements)\n\t\t\t// Posted after the open so follower requests queue behind it in the worker.\n\t\t\tworker.post({ id: 0, type: 'serve', channelName: this.tabSession?.channelName ?? '' })\n\t\t\treturn pending\n\t\t}\n\t\tconst response = await this.checkedOpen(worker, ddlStatements)\n\t\t// The hook (snapshot restore, STORE-6) runs before the worker serves other tabs,\n\t\t// so no follower ever reads or writes the database before it holds its data.\n\t\tawait runWorkerTransaction(worker, hook)\n\t\tworker.post({ id: 0, type: 'serve', channelName: this.tabSession?.channelName ?? '' })\n\t\treturn response\n\t}\n\n\t/**\n\t * Whether this runtime owns the database's worker: the storage leader in a browser,\n\t * or any adapter with an injected bridge (tests, Node). A follower tab relays every\n\t * request to the leader's worker (STORE-6).\n\t */\n\tisLeader(): boolean {\n\t\tif (this.injectedBridge) return true\n\t\treturn this.tabSession?.role === 'leader'\n\t}\n\n\tprivate async checkedOpen(\n\t\tbridge: WorkerBridge,\n\t\tddlStatements: string[],\n\t): Promise<WorkerSuccessResponse> {\n\t\tconst response = await bridge.send(\n\t\t\t{\n\t\t\t\tid: 0,\n\t\t\t\ttype: 'open',\n\t\t\t\tddlStatements,\n\t\t\t\tdbName: this.dbName,\n\t\t\t\t...(this.storage === 'memory' ? { storage: 'memory' as const } : {}),\n\t\t\t},\n\t\t\tundefined,\n\t\t\t{ timeoutMs: Number.POSITIVE_INFINITY },\n\t\t)\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`Failed to open database: ${response.message}`, {\n\t\t\t\tcode: response.code,\n\t\t\t\tdbName: this.dbName,\n\t\t\t\t...(response.context ?? {}),\n\t\t\t})\n\t\t}\n\t\treturn response\n\t}\n\n\tprivate async openCurrentBridge(ddlStatements: string[]): Promise<WorkerSuccessResponse> {\n\t\tconst bridge = this.bridge\n\t\tif (!bridge) throw new StoreNotOpenError()\n\t\treturn this.checkedOpen(bridge, ddlStatements)\n\t}\n\n\tprivate onWorkerEvent(event: WorkerStatusEvent): void {\n\t\tif (!this.emitter) return\n\t\tconst holder =\n\t\t\tevent.resource === 'legacy-pool'\n\t\t\t\t? 'the shared OPFS pool used by older Kora versions (a tab running an older version of this app may still be open)'\n\t\t\t\t: 'another tab or worker that is still releasing it'\n\t\tif (event.kind === 'storage-blocked') {\n\t\t\tthis.emitter.emit({\n\t\t\t\ttype: 'store:storage-blocked',\n\t\t\t\tdbName: this.dbName,\n\t\t\t\tresource: event.resource,\n\t\t\t\tstate: 'waiting',\n\t\t\t\tmessage: `Database \"${this.dbName}\" is waiting for ${holder}. Close other tabs of this app if this persists; Kora will not fall back to non-durable storage.`,\n\t\t\t})\n\t\t\treturn\n\t\t}\n\t\tthis.emitter.emit({\n\t\t\ttype: 'store:storage-blocked',\n\t\t\tdbName: this.dbName,\n\t\t\tresource: event.resource,\n\t\t\tstate: 'resolved',\n\t\t\twaitedMs: event.waitedMs,\n\t\t\tmessage: `Database \"${this.dbName}\" obtained its storage after waiting ${event.waitedMs}ms.`,\n\t\t})\n\t}\n\n\t/**\n\t * Leader-only bookkeeping after a durable open: report a legacy-pool\n\t * migration, move an IndexedDB-held copy into OPFS explicitly (never two\n\t * disjoint copies), and record OPFS as this database's backend.\n\t */\n\tprivate async afterLeaderOpen(data: unknown): Promise<void> {\n\t\tif (!this.ownWorker || this.skipBackendRecord || this.storageOpenState?.persistent !== true) {\n\t\t\treturn\n\t\t}\n\t\tconst info = (typeof data === 'object' && data !== null ? data : {}) as {\n\t\t\tmigratedFromLegacy?: boolean\n\t\t}\n\t\tif (info.migratedFromLegacy === true) {\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:storage-migrated',\n\t\t\t\tdbName: this.dbName,\n\t\t\t\tfrom: 'legacy-opfs-pool',\n\t\t\t\tto: 'opfs',\n\t\t\t\tmessage: `Database \"${this.dbName}\" moved from the shared pre-1.0 OPFS pool into its own pool.`,\n\t\t\t})\n\t\t}\n\t\tif (!isManifestAvailable()) return\n\n\t\tlet recordedBackend: string | null = null\n\t\ttry {\n\t\t\trecordedBackend = (await readManifestRecord(this.dbName))?.backend ?? null\n\t\t} catch (error) {\n\t\t\tconsole.warn(`[kora] Could not read the storage manifest for \"${this.dbName}\":`, error)\n\t\t}\n\t\tif (recordedBackend === 'indexeddb') {\n\t\t\t// The authoritative copy is in IndexedDB (an earlier session ran without\n\t\t\t// OPFS). Move it here before anything reads or writes; a failure fails\n\t\t\t// the open instead of showing the older OPFS copy.\n\t\t\tconst dump = await loadDumpFromIndexedDB<DatabaseDump>(this.dbName)\n\t\t\tif (dump) {\n\t\t\t\tawait this.applyDumpThroughWorker(this.ownWorker, dump)\n\t\t\t}\n\t\t\tthis.emitter?.emit({\n\t\t\t\ttype: 'store:storage-migrated',\n\t\t\t\tdbName: this.dbName,\n\t\t\t\tfrom: 'indexeddb',\n\t\t\t\tto: 'opfs',\n\t\t\t\tmessage: `Database \"${this.dbName}\" moved from IndexedDB into OPFS.`,\n\t\t\t})\n\t\t}\n\t\ttry {\n\t\t\tawait recordDatabase({\n\t\t\t\tdbName: this.dbName,\n\t\t\t\tbackend: 'opfs',\n\t\t\t\t...(this.storageOpenState.poolName ? { poolName: this.storageOpenState.poolName } : {}),\n\t\t\t})\n\t\t\tif (recordedBackend === 'indexeddb') {\n\t\t\t\tawait deleteFromIndexedDB(this.dbName)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tconsole.warn(`[kora] Could not update the storage manifest for \"${this.dbName}\":`, error)\n\t\t}\n\t}\n\n\tprivate async applyDumpThroughWorker(worker: WorkerBridge, dump: DatabaseDump): Promise<void> {\n\t\tconst check = async (request: WorkerRequest, what: string): Promise<void> => {\n\t\t\tconst response = await worker.send(request)\n\t\t\tif (response.type === 'error') {\n\t\t\t\tthrow new AdapterError(`${what} failed: ${response.message}`, { dbName: this.dbName })\n\t\t\t}\n\t\t}\n\t\tawait check({ id: 0, type: 'begin' }, 'BEGIN IndexedDB import')\n\t\ttry {\n\t\t\tfor (const statement of restoreDumpStatements(dump)) {\n\t\t\t\tawait check(\n\t\t\t\t\t{ id: 0, type: 'execute', sql: statement.sql, params: statement.params },\n\t\t\t\t\t'IndexedDB import',\n\t\t\t\t)\n\t\t\t}\n\t\t\tawait check({ id: 0, type: 'commit' }, 'COMMIT IndexedDB import')\n\t\t} catch (error) {\n\t\t\tawait worker.send({ id: 0, type: 'rollback' }).catch(() => undefined)\n\t\t\tthrow error\n\t\t}\n\t}\n\n\t/**\n\t * Surface a `store:opfs-unavailable` diagnostic when the worker reported that\n\t * persistence silently degraded to an in-memory database, so a data-loss\n\t * condition is observable instead of failing silently. A persistent open (or a\n\t * bridge that does not report a mode, e.g. the Node mock) emits nothing.\n\t */\n\tprivate reportStorageMode(data: unknown): void {\n\t\tthis.storageOpenState = parseStorageOpenState(data)\n\t\tif (!this.emitNonPersistentDiagnostic || !this.emitter || !this.storageOpenState) {\n\t\t\treturn\n\t\t}\n\t\tif (this.storageOpenState.persistent === false && this.storage === 'opfs') {\n\t\t\tconst reason = this.storageOpenState.fallbackReason ?? 'unsupported'\n\t\t\tthis.emitter.emit({\n\t\t\t\ttype: 'store:opfs-unavailable',\n\t\t\t\tdbName: this.dbName,\n\t\t\t\treason,\n\t\t\t\tmessage: `OPFS persistence is unavailable (${reason}) for database \"${this.dbName}\"; the store is running in memory and data will not survive a reload.`,\n\t\t\t})\n\t\t}\n\t}\n\n\tgetStorageOpenState(): StorageOpenState | null {\n\t\treturn this.storageOpenState\n\t}\n\n\t/**\n\t * Durable or loud, never silent (NEW-STORE-6). When the open or a promotion\n\t * ended on non-persistent storage and the app did not opt into that, emit the\n\t * blocking `store:durability-lost` event and refuse every later write. A\n\t * bridge that reports no mode (the Node mock) is not judged.\n\t */\n\tprivate enforceDurability(\n\t\tphase: 'open' | 'promotion',\n\t\tfailure?: { reason: 'open-failed'; message: string },\n\t): void {\n\t\tif (this.allowNonDurable) return\n\t\tif (phase === 'open' && this.deferOpenDurabilityCheck && !failure) return\n\t\tlet reason: 'lock-conflict' | 'timeout' | 'unsupported' | 'open-failed'\n\t\tif (failure) {\n\t\t\treason = failure.reason\n\t\t} else if (this.storageOpenState?.persistent === false) {\n\t\t\treason = this.storageOpenState.fallbackReason ?? 'unsupported'\n\t\t} else {\n\t\t\tthis.durabilityLoss = null\n\t\t\treturn\n\t\t}\n\t\tthis.durabilityLoss = { phase, reason }\n\t\tthis.emitter?.emit({\n\t\t\ttype: 'store:durability-lost',\n\t\t\tdbName: this.dbName,\n\t\t\tphase,\n\t\t\treason,\n\t\t\tmessage:\n\t\t\t\t`Database \"${this.dbName}\" has no durable storage (${reason} during ${phase}); ` +\n\t\t\t\t`writes are refused until the app reloads with durable storage.${failure ? ` ${failure.message}` : ''}`,\n\t\t})\n\t}\n\n\tprivate guardDurableWrite(): void {\n\t\tif (this.durabilityLoss) {\n\t\t\tthrow new StorageDurabilityError(\n\t\t\t\tthis.dbName,\n\t\t\t\tthis.durabilityLoss.phase,\n\t\t\t\tthis.durabilityLoss.reason,\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate warnSharedWorkerDeprecated(): void {\n\t\tif (!this.sharedWorkerUrl || warnedSharedWorkerDeprecated) {\n\t\t\treturn\n\t\t}\n\t\twarnedSharedWorkerDeprecated = true\n\t\tconsole.warn(\n\t\t\t'[kora] sharedWorkerUrl is deprecated and ignored: SharedWorker-hosted SQLite ' +\n\t\t\t\t'cannot use OPFS and is never durable. Kora uses the durable dedicated-worker ' +\n\t\t\t\t'leader/follower path for multi-tab storage.',\n\t\t)\n\t}\n\n\t/**\n\t * Close the database. A leader closes the database file and releases the OPFS\n\t * pool inside its worker, terminates the worker, and only then releases the\n\t * tab leader lock, so the next leader never races a still-running worker for\n\t * the file handles (NEW-STORE-10). A follower just leaves.\n\t */\n\tasync close(): Promise<void> {\n\t\tif (!this.bridge && !this.tabSession) return\n\t\tthis.closing = true\n\t\ttry {\n\t\t\tif (this.injectedBridge && this.bridge) {\n\t\t\t\tawait this.bridge.send({ id: 0, type: 'close' })\n\t\t\t} else if (this.ownWorker && !this.ownWorker.isTerminated()) {\n\t\t\t\tconst response = await this.ownWorker\n\t\t\t\t\t.send({ id: 0, type: 'close' }, undefined, { timeoutMs: 5000 })\n\t\t\t\t\t.catch((error: unknown) => ({\n\t\t\t\t\t\tid: 0,\n\t\t\t\t\t\ttype: 'error' as const,\n\t\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t\tcode: 'CLOSE_FAILED',\n\t\t\t\t\t}))\n\t\t\t\tif (response.type === 'error') {\n\t\t\t\t\t// Terminating the worker below still releases the pool.\n\t\t\t\t\tconsole.warn(`[kora] Closing \"${this.dbName}\" in its worker failed:`, response.message)\n\t\t\t\t}\n\t\t\t}\n\t\t} finally {\n\t\t\tawait this.teardown()\n\t\t\tthis.closing = false\n\t\t}\n\t}\n\n\t/**\n\t * Release everything this adapter holds, in ownership order: lifecycle\n\t * listeners, then the worker (which holds the OPFS pool and its Web Lock),\n\t * then the tab leader lock or queued promotion request.\n\t */\n\tprivate async teardown(): Promise<void> {\n\t\tthis.removeLifecycleListeners?.()\n\t\tthis.removeLifecycleListeners = null\n\t\tconst session = this.tabSession\n\t\tthis.tabSession = null\n\t\tsession?.stopRelay?.()\n\t\tthis.bridge?.terminate()\n\t\tthis.bridge = null\n\t\tthis.ownWorker = null\n\t\tif (session) {\n\t\t\tif (session.releaseLock) {\n\t\t\t\tawait session.releaseLock()\n\t\t\t}\n\t\t\tsession.cancelPromotionWatch?.()\n\t\t}\n\t\tthis.opened = false\n\t\tthis.suspended = false\n\t\tconst gate = this.gate\n\t\tthis.gate = null\n\t\tgate?.open()\n\t}\n\n\t/**\n\t * Promotes a follower to leader after the previous leader released the storage\n\t * lock (its tab closed, crashed, or suspended on freeze). The new worker waits\n\t * on the pool's Web Lock and handles, so it never races the previous worker.\n\t * Requests issued during the hand-off wait for it; requests that were in\n\t * flight to the old leader fail with a retriable `BridgeTerminatedError`.\n\t */\n\tprivate async promoteToLeader(): Promise<void> {\n\t\tif (\n\t\t\tthis.promoting ||\n\t\t\tthis.closing ||\n\t\t\t!this.opened ||\n\t\t\tthis.workerUrl === undefined ||\n\t\t\tthis.schema === null\n\t\t) {\n\t\t\treturn\n\t\t}\n\t\tthis.promoting = true\n\t\tconst gate = createGate()\n\t\tthis.gate = gate\n\n\t\ttry {\n\t\t\tconst { WebWorkerBridge } = await import('./sqlite-wasm-channel')\n\t\t\tif (this.closing || !this.opened) return\n\t\t\tconst worker = new WebWorkerBridge(this.workerUrl, this.workerResponseTimeoutMs, {\n\t\t\t\tonEvent: (event) => this.onWorkerEvent(event),\n\t\t\t})\n\t\t\tconst previousBridge = this.bridge\n\t\t\tthis.bridge = worker\n\t\t\tthis.ownWorker = worker\n\t\t\tif (this.tabSession) {\n\t\t\t\tthis.tabSession.role = 'leader'\n\t\t\t}\n\t\t\tpreviousBridge?.terminate()\n\n\t\t\t// Re-open against our own worker. DDL is idempotent, and the OPFS data the\n\t\t\t// old leader persisted is readable once its worker is gone.\n\t\t\tconst ddlStatements = generateFullDDL(this.schema)\n\t\t\tlet response: WorkerSuccessResponse\n\t\t\ttry {\n\t\t\t\tresponse = await this.openLeaderWorker(worker, ddlStatements)\n\t\t\t} catch (error) {\n\t\t\t\t// The promoted worker could not obtain durable storage. Never let it\n\t\t\t\t// run silently in memory.\n\t\t\t\tthis.storageOpenState = { persistent: false, mode: 'memory' }\n\t\t\t\tthis.enforceDurability('promotion', {\n\t\t\t\t\treason: 'open-failed',\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t})\n\t\t\t\treturn\n\t\t\t}\n\t\t\tthis.storageOpenState = parseStorageOpenState(response.data) ?? this.storageOpenState\n\t\t\tthis.enforceDurability('promotion')\n\t\t\ttry {\n\t\t\t\tawait this.afterLeaderOpen(response.data)\n\t\t\t} catch (error) {\n\t\t\t\tthis.enforceDurability('promotion', {\n\t\t\t\t\treason: 'open-failed',\n\t\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t\t})\n\t\t\t}\n\t\t} finally {\n\t\t\tthis.promoting = false\n\t\t\tif (this.gate === gate) this.gate = null\n\t\t\tgate.open()\n\t\t}\n\t}\n\n\t/**\n\t * Page Lifecycle: a frozen or bfcached page cannot run, so it must not keep\n\t * other tabs waiting on its storage. Leaders terminate their worker (freeing\n\t * the OPFS pool) and then release the leader lock, so a visible tab is\n\t * promoted; followers withdraw their queued lock request so a frozen tab is\n\t * never granted leadership it cannot use. Never uses Web Locks `steal`.\n\t */\n\tprivate installLifecycleListeners(): void {\n\t\tif (!this.releaseOnFreeze || this.removeLifecycleListeners) return\n\t\tif (typeof addEventListener !== 'function' || typeof document === 'undefined') return\n\t\tconst onFreeze = (): void => this.suspend()\n\t\tconst onPageHide = (): void => this.suspend()\n\t\tconst onResume = (): void => {\n\t\t\tvoid this.resume()\n\t\t}\n\t\tconst onPageShow = (event: Event): void => {\n\t\t\tif ((event as PageTransitionEvent).persisted) void this.resume()\n\t\t}\n\t\tdocument.addEventListener('freeze', onFreeze)\n\t\tdocument.addEventListener('resume', onResume)\n\t\taddEventListener('pagehide', onPageHide)\n\t\taddEventListener('pageshow', onPageShow)\n\t\tthis.removeLifecycleListeners = () => {\n\t\t\tdocument.removeEventListener('freeze', onFreeze)\n\t\t\tdocument.removeEventListener('resume', onResume)\n\t\t\tremoveEventListener('pagehide', onPageHide)\n\t\t\tremoveEventListener('pageshow', onPageShow)\n\t\t}\n\t}\n\n\t/** Synchronous by necessity: a `freeze` handler gets no further turns. */\n\tprivate suspend(): void {\n\t\tif (!this.opened || this.suspended || this.closing || this.promoting) return\n\t\tthis.suspended = true\n\t\tthis.gate = createGate()\n\t\tconst session = this.tabSession\n\t\tthis.tabSession = null\n\t\t// Worker (pool holder) first, then the leader lock (NEW-STORE-10).\n\t\tthis.bridge?.terminate()\n\t\tthis.bridge = null\n\t\tthis.ownWorker = null\n\t\tif (session) {\n\t\t\tvoid session.releaseLock?.()\n\t\t\tsession.cancelPromotionWatch?.()\n\t\t}\n\t}\n\n\tprivate async resume(): Promise<void> {\n\t\tif (!this.suspended || this.closing || this.schema === null) return\n\t\tconst gate = this.gate\n\t\ttry {\n\t\t\tconst response = await this.attachAndOpen(generateFullDDL(this.schema))\n\t\t\tthis.reportStorageMode(response.data)\n\t\t\tthis.enforceDurability('promotion')\n\t\t\tawait this.afterLeaderOpen(response.data)\n\t\t} catch (error) {\n\t\t\tthis.enforceDurability('promotion', {\n\t\t\t\treason: 'open-failed',\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error),\n\t\t\t})\n\t\t} finally {\n\t\t\tthis.suspended = false\n\t\t\tif (this.gate === gate) this.gate = null\n\t\t\tgate?.open()\n\t\t}\n\t}\n\n\t/**\n\t * Non-transactional write. Takes the same mutex as {@link transaction}, so it\n\t * never lands inside this tab's open transaction and is never rolled back\n\t * with it (STORE-8). Inside a transaction callback, use the `tx` handle.\n\t */\n\tasync execute(sql: string, params?: unknown[], options?: StorageRequestOptions): Promise<void> {\n\t\tthis.guardOpen()\n\t\tthis.guardDurableWrite()\n\t\tconst release = await this.mutex.acquire()\n\t\ttry {\n\t\t\tconst response = await this.sendRequest(\n\t\t\t\t{\n\t\t\t\t\tid: 0,\n\t\t\t\t\ttype: 'execute',\n\t\t\t\t\tsql,\n\t\t\t\t\tparams,\n\t\t\t\t\t...(options?.requestId ? { requestId: options.requestId } : {}),\n\t\t\t\t},\n\t\t\t\toptions?.signal ? { signal: options.signal } : undefined,\n\t\t\t)\n\t\t\tif (response.type === 'error') {\n\t\t\t\tthrow new AdapterError(`Execute failed: ${response.message}`, { sql, params })\n\t\t\t}\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\t/**\n\t * Non-transactional read. Waits for this tab's open transaction to finish, so\n\t * it never observes uncommitted (possibly rolled-back) rows (STORE-8).\n\t */\n\tasync query<T>(sql: string, params?: unknown[], options?: StorageRequestOptions): Promise<T[]> {\n\t\tthis.guardOpen()\n\t\tconst release = await this.mutex.acquire()\n\t\ttry {\n\t\t\tconst response = await this.sendRequest(\n\t\t\t\t{\n\t\t\t\t\tid: 0,\n\t\t\t\t\ttype: 'query',\n\t\t\t\t\tsql,\n\t\t\t\t\tparams,\n\t\t\t\t\t...(options?.requestId ? { requestId: options.requestId } : {}),\n\t\t\t\t},\n\t\t\t\toptions?.signal ? { signal: options.signal } : undefined,\n\t\t\t)\n\t\t\tif (response.type === 'error') {\n\t\t\t\tthrow new AdapterError(`Query failed: ${response.message}`, { sql, params })\n\t\t\t}\n\t\t\treturn (response.data as T[]) ?? []\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\tasync transaction(fn: (tx: Transaction) => Promise<void>): Promise<void> {\n\t\tthis.guardOpen()\n\t\tthis.guardDurableWrite()\n\n\t\tconst release = await this.mutex.acquire()\n\t\tlet txBridge: WorkerBridge | null = null\n\t\t// Every statement of the span must reach the worker that ran BEGIN. If\n\t\t// leadership changed hands mid-transaction, the old worker rolled it back\n\t\t// (or is gone); sending the rest to the new worker would autocommit a\n\t\t// partial transaction, so fail with a retriable error instead.\n\t\tconst sendInSpan = async (request: WorkerRequest): Promise<WorkerResponse> => {\n\t\t\tif (txBridge === null || this.bridge !== txBridge) {\n\t\t\t\tthrow new BridgeTerminatedError(request.type, 'storage leader changed during transaction')\n\t\t\t}\n\t\t\treturn txBridge.send(request)\n\t\t}\n\t\ttry {\n\t\t\twhile (this.gate) {\n\t\t\t\tawait this.gate.promise\n\t\t\t}\n\t\t\ttxBridge = this.bridge\n\t\t\tif (!txBridge) throw new StoreNotOpenError()\n\t\t\tconst begin = await sendInSpan({ id: 0, type: 'begin' })\n\t\t\tif (begin.type === 'error') {\n\t\t\t\tthrow new AdapterError(`BEGIN transaction failed: ${begin.message}`)\n\t\t\t}\n\n\t\t\tconst tx: Transaction = {\n\t\t\t\texecute: async (sql: string, params?: unknown[]): Promise<void> => {\n\t\t\t\t\tconst response = await sendInSpan({ id: 0, type: 'execute', sql, params })\n\t\t\t\t\tif (response.type === 'error') {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction execute failed: ${response.message}`, {\n\t\t\t\t\t\t\tsql,\n\t\t\t\t\t\t\tparams,\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\tquery: async <T>(sql: string, params?: unknown[]): Promise<T[]> => {\n\t\t\t\t\tconst response = await sendInSpan({ id: 0, type: 'query', sql, params })\n\t\t\t\t\tif (response.type === 'error') {\n\t\t\t\t\t\tthrow new AdapterError(`Transaction query failed: ${response.message}`, { sql, params })\n\t\t\t\t\t}\n\t\t\t\t\treturn (response.data as T[]) ?? []\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tawait fn(tx)\n\t\t\tconst commit = await sendInSpan({ id: 0, type: 'commit' })\n\t\t\tif (commit.type === 'error') {\n\t\t\t\tthrow new AdapterError(`COMMIT transaction failed: ${commit.message}`)\n\t\t\t}\n\t\t} catch (error) {\n\t\t\t// Attempt rollback on the worker that ran BEGIN, but don't mask the\n\t\t\t// original error.\n\t\t\ttry {\n\t\t\t\tif (txBridge && this.bridge === txBridge) {\n\t\t\t\t\tawait txBridge.send({ id: 0, type: 'rollback' })\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Rollback failure is secondary to the original error\n\t\t\t}\n\t\t\tthrow error\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\tasync migrate(from: number, to: number, migration: MigrationPlan): Promise<void> {\n\t\tthis.guardOpen()\n\t\tthis.guardDurableWrite()\n\n\t\tconst release = await this.mutex.acquire()\n\t\ttry {\n\t\t\tawait this.sendChecked({ id: 0, type: 'begin' }, 'BEGIN migration')\n\n\t\t\tfor (const sql of migration.statements) {\n\t\t\t\tconst response = await this.sendRequest({ id: 0, type: 'execute', sql })\n\t\t\t\tif (response.type === 'error') {\n\t\t\t\t\tthrow new AdapterError(`Migration from v${from} to v${to} failed: ${response.message}`, {\n\t\t\t\t\t\tfrom,\n\t\t\t\t\t\tto,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tawait this.sendChecked({ id: 0, type: 'commit' }, 'COMMIT migration')\n\t\t} catch (error) {\n\t\t\ttry {\n\t\t\t\tawait this.sendRequest({ id: 0, type: 'rollback' })\n\t\t\t} catch {\n\t\t\t\t// Rollback failure is secondary\n\t\t\t}\n\t\t\tif (error instanceof AdapterError) throw error\n\t\t\tthrow new AdapterError(\n\t\t\t\t`Migration from v${from} to v${to} failed: ${(error as Error).message}`,\n\t\t\t\t{ from, to },\n\t\t\t)\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\t/**\n\t * Export the database as a Uint8Array (for IndexedDB persistence).\n\t * Only available when the database is open.\n\t */\n\tasync exportDatabase(): Promise<Uint8Array> {\n\t\tthis.guardOpen()\n\t\tconst release = await this.mutex.acquire()\n\t\ttry {\n\t\t\tconst response = await this.sendRequest({ id: 0, type: 'export' })\n\t\t\tif (response.type === 'error') {\n\t\t\t\tthrow new AdapterError(`Export failed: ${response.message}`, {\n\t\t\t\t\tcode: response.code,\n\t\t\t\t\tcontext: response.context,\n\t\t\t\t})\n\t\t\t}\n\t\t\treturn response.data as Uint8Array\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\t/**\n\t * Import a serialized database snapshot.\n\t */\n\tasync importDatabase(data: Uint8Array): Promise<void> {\n\t\tthis.guardOpen()\n\t\tthis.guardDurableWrite()\n\t\tconst release = await this.mutex.acquire()\n\t\ttry {\n\t\t\tconst response = await this.sendRequest({ id: 0, type: 'import', data })\n\t\t\tif (response.type === 'error') {\n\t\t\t\tthrow new AdapterError(`Import failed: ${response.message}`)\n\t\t\t}\n\t\t} finally {\n\t\t\trelease()\n\t\t}\n\t}\n\n\tprivate guardOpen(): void {\n\t\tif (!this.opened || (!this.bridge && !this.gate)) {\n\t\t\tthrow new StoreNotOpenError()\n\t\t}\n\t}\n\n\tprivate async sendRequest(\n\t\trequest: WorkerRequest,\n\t\toptions?: WorkerSendOptions,\n\t): Promise<WorkerResponse> {\n\t\t// While leadership changes hands, wait for the new bridge instead of\n\t\t// sending into a torn-down one.\n\t\twhile (this.gate) {\n\t\t\tawait this.gate.promise\n\t\t}\n\t\tconst bridge = this.bridge\n\t\tif (!bridge) {\n\t\t\tthrow new StoreNotOpenError()\n\t\t}\n\t\treturn bridge.send(request, undefined, options)\n\t}\n\n\tprivate async sendChecked(request: WorkerRequest, description: string): Promise<void> {\n\t\tconst response = await this.sendRequest(request)\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`${description} failed: ${response.message}`)\n\t\t}\n\t}\n}\n\n/**\n * Run `fn` in one transaction sent straight to `worker`, bypassing the adapter's mutex\n * and leadership gate (the caller is the leader opening its own worker).\n */\nasync function runWorkerTransaction(\n\tworker: WorkerBridge,\n\tfn: (tx: Transaction) => Promise<void>,\n): Promise<void> {\n\tconst send = async (request: WorkerRequest, what: string): Promise<WorkerResponse> => {\n\t\tconst response = await worker.send(request)\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`${what} failed: ${response.message}`, {\n\t\t\t\tcode: response.code,\n\t\t\t\t...(request.type === 'execute' || request.type === 'query' ? { sql: request.sql } : {}),\n\t\t\t})\n\t\t}\n\t\treturn response\n\t}\n\tawait send({ id: 0, type: 'begin' }, 'BEGIN')\n\ttry {\n\t\tawait fn({\n\t\t\texecute: async (sql, params) => {\n\t\t\t\tawait send({ id: 0, type: 'execute', sql, params }, 'Leader open execute')\n\t\t\t},\n\t\t\tquery: async <T>(sql: string, params?: unknown[]): Promise<T[]> => {\n\t\t\t\tconst response = await send({ id: 0, type: 'query', sql, params }, 'Leader open query')\n\t\t\t\treturn ((response as WorkerSuccessResponse).data as T[]) ?? []\n\t\t\t},\n\t\t})\n\t\tawait send({ id: 0, type: 'commit' }, 'COMMIT')\n\t} catch (error) {\n\t\tawait worker.send({ id: 0, type: 'rollback' }).catch(() => undefined)\n\t\tthrow error\n\t}\n}\n\nfunction parseStorageOpenState(data: unknown): StorageOpenState | null {\n\tif (typeof data !== 'object' || data === null) {\n\t\treturn null\n\t}\n\tconst mode = data as {\n\t\tpersistent?: boolean\n\t\tfallbackReason?: StorageFallbackReason\n\t\tpoolName?: string\n\t\tjournalMode?: string\n\t}\n\tif (typeof mode.persistent !== 'boolean') {\n\t\treturn null\n\t}\n\treturn {\n\t\tpersistent: mode.persistent,\n\t\tmode: mode.persistent ? 'opfs' : 'memory',\n\t\t...(mode.fallbackReason ? { fallbackReason: mode.fallbackReason } : {}),\n\t\t...(typeof mode.poolName === 'string' ? { poolName: mode.poolName } : {}),\n\t\t...(typeof mode.journalMode === 'string' ? { journalMode: mode.journalMode } : {}),\n\t}\n}\n","/**\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","/**\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","/// <reference lib=\"dom\" />\nimport { PersistenceError } from '../errors'\n\nconst IDB_DATABASE_NAME = 'kora-persistence'\nconst IDB_STORE_NAME = 'databases'\nconst IDB_VERSION = 1\n\nconst DUMP_SUFFIX = '::dump'\n\n/**\n * Returns true when an IndexedDB error indicates storage quota was exceeded.\n */\nexport function isIndexedDbQuotaError(error: unknown): boolean {\n\tif (error instanceof DOMException && error.name === 'QuotaExceededError') {\n\t\treturn true\n\t}\n\tif (error instanceof PersistenceError && error.context?.quotaExceeded === true) {\n\t\treturn true\n\t}\n\tif (error instanceof Error && /quota/i.test(error.message)) {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunction rejectWithIdbError(\n\treject: (reason: unknown) => void,\n\tmessage: string,\n\tcontext: Record<string, unknown>,\n\tevent: Event,\n): void {\n\tconst domError = (event.target as IDBRequest | IDBTransaction | undefined)?.error\n\tif (domError && isIndexedDbQuotaError(domError)) {\n\t\treject(\n\t\t\tnew PersistenceError(message, {\n\t\t\t\t...context,\n\t\t\t\tquotaExceeded: true,\n\t\t\t\tcause: domError.message,\n\t\t\t}),\n\t\t)\n\t\treturn\n\t}\n\treject(new PersistenceError(message, context))\n}\n\n/**\n * Open the IndexedDB database used for SQLite persistence.\n * Creates the object store on first access.\n */\nfunction openIdb(): Promise<IDBDatabase> {\n\treturn new Promise<IDBDatabase>((resolve, reject) => {\n\t\tconst request = indexedDB.open(IDB_DATABASE_NAME, IDB_VERSION)\n\t\trequest.onupgradeneeded = () => {\n\t\t\tconst db = request.result\n\t\t\tif (!db.objectStoreNames.contains(IDB_STORE_NAME)) {\n\t\t\t\tdb.createObjectStore(IDB_STORE_NAME)\n\t\t\t}\n\t\t}\n\t\trequest.onsuccess = () => resolve(request.result)\n\t\trequest.onerror = () =>\n\t\t\treject(\n\t\t\t\tnew PersistenceError(\n\t\t\t\t\t`Failed to open IndexedDB: ${request.error?.message ?? 'unknown error'}`,\n\t\t\t\t\t{ database: IDB_DATABASE_NAME },\n\t\t\t\t),\n\t\t\t)\n\t})\n}\n\n/**\n * Save a serialized SQLite database to IndexedDB.\n *\n * @param dbName - Key under which to store the data\n * @param data - Serialized database as Uint8Array\n */\nexport async function saveToIndexedDB(dbName: string, data: Uint8Array): Promise<void> {\n\tconst idb = await openIdb()\n\ttry {\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst tx = idb.transaction(IDB_STORE_NAME, 'readwrite')\n\t\t\tconst store = tx.objectStore(IDB_STORE_NAME)\n\t\t\tstore.put(data, dbName)\n\t\t\ttx.oncomplete = () => resolve()\n\t\t\ttx.onerror = (event) =>\n\t\t\t\trejectWithIdbError(\n\t\t\t\t\treject,\n\t\t\t\t\t`Failed to save database \"${dbName}\" to IndexedDB`,\n\t\t\t\t\t{ dbName },\n\t\t\t\t\tevent,\n\t\t\t\t)\n\t\t})\n\t} finally {\n\t\tidb.close()\n\t}\n}\n\n/**\n * Save a logical SQL dump payload to IndexedDB for import-fallback restore.\n */\nexport async function saveDumpToIndexedDB(dbName: string, dump: unknown): Promise<void> {\n\tconst idb = await openIdb()\n\ttry {\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst tx = idb.transaction(IDB_STORE_NAME, 'readwrite')\n\t\t\tconst store = tx.objectStore(IDB_STORE_NAME)\n\t\t\tstore.put(dump, `${dbName}${DUMP_SUFFIX}`)\n\t\t\ttx.oncomplete = () => resolve()\n\t\t\ttx.onerror = (event) =>\n\t\t\t\trejectWithIdbError(\n\t\t\t\t\treject,\n\t\t\t\t\t`Failed to save dump for database \"${dbName}\"`,\n\t\t\t\t\t{ dbName },\n\t\t\t\t\tevent,\n\t\t\t\t)\n\t\t})\n\t} finally {\n\t\tidb.close()\n\t}\n}\n\n/**\n * Load a serialized SQLite database from IndexedDB.\n *\n * @param dbName - Key under which the data was stored\n * @returns The serialized database, or null if not found\n */\nexport async function loadFromIndexedDB(dbName: string): Promise<Uint8Array | null> {\n\tconst idb = await openIdb()\n\ttry {\n\t\treturn await new Promise<Uint8Array | null>((resolve, reject) => {\n\t\t\tconst tx = idb.transaction(IDB_STORE_NAME, 'readonly')\n\t\t\tconst store = tx.objectStore(IDB_STORE_NAME)\n\t\t\tconst request = store.get(dbName)\n\t\t\trequest.onsuccess = () => {\n\t\t\t\tconst result = request.result\n\t\t\t\tif (result instanceof Uint8Array) {\n\t\t\t\t\tresolve(result)\n\t\t\t\t} else if (result) {\n\t\t\t\t\t// Handle ArrayBuffer or other typed array forms\n\t\t\t\t\tresolve(new Uint8Array(result as ArrayBuffer))\n\t\t\t\t} else {\n\t\t\t\t\tresolve(null)\n\t\t\t\t}\n\t\t\t}\n\t\t\trequest.onerror = () =>\n\t\t\t\treject(\n\t\t\t\t\tnew PersistenceError(`Failed to load database \"${dbName}\" from IndexedDB`, { dbName }),\n\t\t\t\t)\n\t\t})\n\t} finally {\n\t\tidb.close()\n\t}\n}\n\n/**\n * Load a logical SQL dump payload from IndexedDB.\n */\nexport async function loadDumpFromIndexedDB<T>(dbName: string): Promise<T | null> {\n\tconst idb = await openIdb()\n\ttry {\n\t\treturn await new Promise<T | null>((resolve, reject) => {\n\t\t\tconst tx = idb.transaction(IDB_STORE_NAME, 'readonly')\n\t\t\tconst store = tx.objectStore(IDB_STORE_NAME)\n\t\t\tconst request = store.get(`${dbName}${DUMP_SUFFIX}`)\n\t\t\trequest.onsuccess = () => {\n\t\t\t\tresolve((request.result as T | undefined) ?? null)\n\t\t\t}\n\t\t\trequest.onerror = () =>\n\t\t\t\treject(\n\t\t\t\t\tnew PersistenceError(`Failed to load dump for database \"${dbName}\" from IndexedDB`, {\n\t\t\t\t\t\tdbName,\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t})\n\t} finally {\n\t\tidb.close()\n\t}\n}\n\n/**\n * Delete only the binary SQLite database snapshot from IndexedDB.\n *\n * The logical dump is stored under a separate key and may be the authoritative\n * durable fallback when a browser worker cannot export a binary SQLite image.\n */\nexport async function deleteSnapshotFromIndexedDB(dbName: string): Promise<void> {\n\tconst idb = await openIdb()\n\ttry {\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst tx = idb.transaction(IDB_STORE_NAME, 'readwrite')\n\t\t\tconst store = tx.objectStore(IDB_STORE_NAME)\n\t\t\tstore.delete(dbName)\n\t\t\ttx.oncomplete = () => resolve()\n\t\t\ttx.onerror = () =>\n\t\t\t\treject(\n\t\t\t\t\tnew PersistenceError(`Failed to delete database snapshot \"${dbName}\" from IndexedDB`, {\n\t\t\t\t\t\tdbName,\n\t\t\t\t\t}),\n\t\t\t\t)\n\t\t})\n\t} finally {\n\t\tidb.close()\n\t}\n}\n\n/**\n * Delete a serialized SQLite database from IndexedDB.\n *\n * @param dbName - Key to delete\n */\nexport async function deleteFromIndexedDB(dbName: string): Promise<void> {\n\tconst idb = await openIdb()\n\ttry {\n\t\tawait new Promise<void>((resolve, reject) => {\n\t\t\tconst tx = idb.transaction(IDB_STORE_NAME, 'readwrite')\n\t\t\tconst store = tx.objectStore(IDB_STORE_NAME)\n\t\t\tstore.delete(dbName)\n\t\t\tstore.delete(`${dbName}${DUMP_SUFFIX}`)\n\t\t\ttx.oncomplete = () => resolve()\n\t\t\ttx.onerror = () =>\n\t\t\t\treject(\n\t\t\t\t\tnew PersistenceError(`Failed to delete database \"${dbName}\" from IndexedDB`, { dbName }),\n\t\t\t\t)\n\t\t})\n\t} finally {\n\t\tidb.close()\n\t}\n}\n","/// <reference lib=\"dom\" />\n/**\n * Kora-owned manifest of the local databases this origin created (W8a).\n *\n * One record per database name, kept in its own IndexedDB database so it is\n * readable from any tab without opening the OPFS pools. It records which backend\n * holds the database's data, which is how Kora keeps OPFS and IndexedDB from\n * ever holding two disjoint copies: a backend switch is a recorded, explicit\n * migration, never a silent fresh start (NEW-STORE-5, LMS-5c).\n *\n * The manifest is advisory for listing and authoritative for the backend\n * choice. Nothing is ever evicted automatically; deletion is the explicit\n * `deleteDatabase()` API.\n */\n\nconst MANIFEST_DB_NAME = 'kora-manifest'\nconst MANIFEST_STORE = 'databases'\nconst MANIFEST_VERSION = 1\n\n/** Where a database's data lives. */\nexport type LocalDatabaseBackend = 'opfs' | 'indexeddb'\n\n/** One manifest entry, as returned by `listDatabases()`. */\nexport interface LocalDatabaseRecord {\n\t/** The store name (`store.name`, plus the per-user suffix if namespaced). */\n\tdbName: string\n\t/** The backend that holds the authoritative copy. */\n\tbackend: LocalDatabaseBackend\n\t/** OPFS pool holding the file (opfs backend only). */\n\tpoolName?: string\n\t/** Wall-clock milliseconds; informational only, never used for ordering. */\n\tcreatedAt: number\n\t/** Wall-clock milliseconds of the last durable open; informational only. */\n\tlastOpenedAt: number\n}\n\n/** True when this runtime can keep a manifest (browsers; not Node tests). */\nexport function isManifestAvailable(): boolean {\n\treturn typeof indexedDB !== 'undefined'\n}\n\nfunction openManifest(): Promise<IDBDatabase> {\n\treturn new Promise<IDBDatabase>((resolve, reject) => {\n\t\tconst request = indexedDB.open(MANIFEST_DB_NAME, MANIFEST_VERSION)\n\t\trequest.onupgradeneeded = () => {\n\t\t\tconst db = request.result\n\t\t\tif (!db.objectStoreNames.contains(MANIFEST_STORE)) {\n\t\t\t\tdb.createObjectStore(MANIFEST_STORE, { keyPath: 'dbName' })\n\t\t\t}\n\t\t}\n\t\trequest.onsuccess = () => resolve(request.result)\n\t\trequest.onerror = () =>\n\t\t\treject(request.error ?? new Error('Failed to open the Kora storage manifest'))\n\t})\n}\n\nasync function withStore<T>(\n\tmode: IDBTransactionMode,\n\trun: (store: IDBObjectStore) => IDBRequest<T> | null,\n): Promise<T | undefined> {\n\tconst db = await openManifest()\n\ttry {\n\t\treturn await new Promise<T | undefined>((resolve, reject) => {\n\t\t\tconst tx = db.transaction(MANIFEST_STORE, mode)\n\t\t\tconst request = run(tx.objectStore(MANIFEST_STORE))\n\t\t\tlet result: T | undefined\n\t\t\tif (request) {\n\t\t\t\trequest.onsuccess = () => {\n\t\t\t\t\tresult = request.result\n\t\t\t\t}\n\t\t\t}\n\t\t\ttx.oncomplete = () => resolve(result)\n\t\t\ttx.onerror = () => reject(tx.error ?? new Error('Kora storage manifest transaction failed'))\n\t\t\ttx.onabort = () => reject(tx.error ?? new Error('Kora storage manifest transaction aborted'))\n\t\t})\n\t} finally {\n\t\tdb.close()\n\t}\n}\n\nfunction isRecord(value: unknown): value is LocalDatabaseRecord {\n\tif (typeof value !== 'object' || value === null) return false\n\tconst record = value as Partial<LocalDatabaseRecord>\n\treturn (\n\t\ttypeof record.dbName === 'string' &&\n\t\t(record.backend === 'opfs' || record.backend === 'indexeddb')\n\t)\n}\n\n/** Read the manifest entry for a database, or null when Kora never recorded it. */\nexport async function readManifestRecord(dbName: string): Promise<LocalDatabaseRecord | null> {\n\tif (!isManifestAvailable()) return null\n\tconst value = await withStore<unknown>('readonly', (store) => store.get(dbName))\n\treturn isRecord(value) ? value : null\n}\n\n/** Every database Kora recorded on this origin, sorted by name. */\nexport async function listManifestRecords(): Promise<LocalDatabaseRecord[]> {\n\tif (!isManifestAvailable()) return []\n\tconst values = await withStore<unknown[]>('readonly', (store) => store.getAll())\n\treturn (values ?? []).filter(isRecord).sort((a, b) => (a.dbName < b.dbName ? -1 : 1))\n}\n\n/**\n * Record (or refresh) which backend holds a database after a durable open,\n * keeping the original creation time.\n */\nexport async function recordDatabase(entry: {\n\tdbName: string\n\tbackend: LocalDatabaseBackend\n\tpoolName?: string\n}): Promise<void> {\n\tif (!isManifestAvailable()) return\n\tconst existing = await readManifestRecord(entry.dbName)\n\tconst now = Date.now()\n\tconst record: LocalDatabaseRecord = {\n\t\tdbName: entry.dbName,\n\t\tbackend: entry.backend,\n\t\t...(entry.poolName ? { poolName: entry.poolName } : {}),\n\t\tcreatedAt: existing?.createdAt ?? now,\n\t\tlastOpenedAt: now,\n\t}\n\tawait withStore('readwrite', (store) => store.put(record))\n}\n\n/** Remove a database's manifest entry (after its data was deleted). */\nexport async function deleteManifestRecord(dbName: string): Promise<void> {\n\tif (!isManifestAvailable()) return\n\tawait withStore('readwrite', (store) => store.delete(dbName))\n}\n"],"mappings":"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","names":["import_core","import_core","WebWorkerBridge"]}