@korajs/store 1.0.0-beta.12 → 1.0.0-beta.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -37
- package/dist/adapters/better-sqlite3.cjs +90 -4
- package/dist/adapters/better-sqlite3.cjs.map +1 -1
- package/dist/adapters/better-sqlite3.d.cts +21 -12
- package/dist/adapters/better-sqlite3.d.ts +21 -12
- package/dist/adapters/better-sqlite3.js +69 -5
- package/dist/adapters/better-sqlite3.js.map +1 -1
- package/dist/adapters/indexeddb.cjs +1316 -610
- package/dist/adapters/indexeddb.cjs.map +1 -1
- package/dist/adapters/indexeddb.d.cts +29 -3
- package/dist/adapters/indexeddb.d.ts +29 -3
- package/dist/adapters/indexeddb.js +247 -221
- package/dist/adapters/indexeddb.js.map +1 -1
- package/dist/adapters/sqlite-wasm-worker.cjs +856 -63
- package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -1
- package/dist/adapters/sqlite-wasm-worker.js +529 -63
- package/dist/adapters/sqlite-wasm-worker.js.map +1 -1
- package/dist/adapters/sqlite-wasm.cjs +1032 -344
- package/dist/adapters/sqlite-wasm.cjs.map +1 -1
- package/dist/adapters/sqlite-wasm.d.cts +239 -18
- package/dist/adapters/sqlite-wasm.d.ts +239 -18
- package/dist/adapters/sqlite-wasm.js +111 -5
- package/dist/adapters/sqlite-wasm.js.map +1 -1
- package/dist/backup-IHKVMUAM.js +25 -0
- package/dist/chunk-5AQSGMJV.js +5471 -0
- package/dist/chunk-5AQSGMJV.js.map +1 -0
- package/dist/chunk-5JMGMPUE.js +591 -0
- package/dist/chunk-5JMGMPUE.js.map +1 -0
- package/dist/{chunk-CYO3YKFL.js → chunk-5ZRRF6R2.js} +2 -2
- package/dist/chunk-A6GFH5IG.js +36 -0
- package/dist/chunk-A6GFH5IG.js.map +1 -0
- package/dist/chunk-BILSA6XZ.js +608 -0
- package/dist/chunk-BILSA6XZ.js.map +1 -0
- package/dist/chunk-BQSIL6TT.js +1095 -0
- package/dist/chunk-BQSIL6TT.js.map +1 -0
- package/dist/{chunk-GSJ7EEUC.js → chunk-J5AGXF3D.js} +58 -9
- package/dist/chunk-J5AGXF3D.js.map +1 -0
- package/dist/chunk-KK3GBONL.js +112 -0
- package/dist/chunk-KK3GBONL.js.map +1 -0
- package/dist/chunk-KTVNANUH.js +876 -0
- package/dist/chunk-KTVNANUH.js.map +1 -0
- package/dist/chunk-QW4UU7NG.js +1003 -0
- package/dist/chunk-QW4UU7NG.js.map +1 -0
- package/dist/chunk-SRHDJF5B.js +186 -0
- package/dist/chunk-SRHDJF5B.js.map +1 -0
- package/dist/chunk-UC2LGFPV.js +810 -0
- package/dist/chunk-UC2LGFPV.js.map +1 -0
- package/dist/{export-audit-ODGQTQIK.js → export-audit-J2QXRHD2.js} +3 -3
- package/dist/index.cjs +9533 -4246
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1143 -110
- package/dist/index.d.ts +1143 -110
- package/dist/index.js +298 -3191
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +581 -264
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +15 -85
- package/dist/internal.d.ts +15 -85
- package/dist/internal.js +10 -8
- package/dist/restore-MNDPR557.js +299 -0
- package/dist/restore-MNDPR557.js.map +1 -0
- package/dist/sql-builder-D0tC3sv9.d.cts +615 -0
- package/dist/sql-builder-D5UMzHEo.d.ts +615 -0
- package/dist/sqlite-wasm-channel-DVX93Uiq.d.cts +141 -0
- package/dist/sqlite-wasm-channel-DVX93Uiq.d.ts +141 -0
- package/dist/sqlite-wasm-channel-QCCNBYCU.js +12 -0
- package/dist/store-HNGX4KTT.js +16 -0
- package/dist/store-HNGX4KTT.js.map +1 -0
- package/dist/{types-BslDOTk0.d.ts → types-Dh-xyUjP.d.cts} +99 -26
- package/dist/{types-BslDOTk0.d.cts → types-Dh-xyUjP.d.ts} +99 -26
- package/package.json +3 -2
- package/dist/backup-5S7K2Z35.js +0 -14
- package/dist/chunk-GSJ7EEUC.js.map +0 -1
- package/dist/chunk-HS6DBMST.js +0 -383
- package/dist/chunk-HS6DBMST.js.map +0 -1
- package/dist/chunk-RJIEKEN4.js +0 -751
- package/dist/chunk-RJIEKEN4.js.map +0 -1
- package/dist/chunk-TUR7RGEP.js +0 -84
- package/dist/chunk-TUR7RGEP.js.map +0 -1
- package/dist/chunk-VDBJQ75K.js +0 -78
- package/dist/chunk-VDBJQ75K.js.map +0 -1
- package/dist/chunk-YT7MRFCP.js +0 -687
- package/dist/chunk-YT7MRFCP.js.map +0 -1
- package/dist/sqlite-wasm-channel-GGHA2BXU.js +0 -12
- package/dist/sqlite-wasm-channel-L6o3bWQK.d.cts +0 -92
- package/dist/sqlite-wasm-channel-L6o3bWQK.d.ts +0 -92
- package/dist/types-BOO9EPt1.d.ts +0 -170
- package/dist/types-C_GDavG-.d.cts +0 -170
- /package/dist/{backup-5S7K2Z35.js.map → backup-IHKVMUAM.js.map} +0 -0
- /package/dist/{chunk-CYO3YKFL.js.map → chunk-5ZRRF6R2.js.map} +0 -0
- /package/dist/{export-audit-ODGQTQIK.js.map → export-audit-J2QXRHD2.js.map} +0 -0
- /package/dist/{sqlite-wasm-channel-GGHA2BXU.js.map → sqlite-wasm-channel-QCCNBYCU.js.map} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/adapters/indexeddb-adapter.ts","../../src/adapters/indexeddb-persistence-scheduler.ts","../../src/adapters/sqlite-wasm-persistence.ts"],"sourcesContent":["import type { KoraEventEmitter, SchemaDefinition } from '@korajs/core'\nimport { quoteIdent } from '@korajs/core'\nimport { AdapterError, PersistenceError } from '../errors'\nimport type { MigrationPlan, StorageAdapter, StorageOpenState, Transaction } from '../types'\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'\n\ninterface DatabaseDump {\n\ttables: Array<{\n\t\tname: string\n\t\tcolumns: string[]\n\t\trows: Array<Record<string, unknown>>\n\t}>\n}\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\n\tconstructor(options: IndexedDbAdapterOptions = {}) {\n\t\tthis.dbName = options.dbName ?? 'kora-db'\n\t\tthis.emitter = options.emitter\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})\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\tawait this.inner.open(schema)\n\t\tthis.storageOpenState = { persistent: true, mode: 'indexeddb' }\n\n\t\tconst persisted = await loadFromIndexedDB(this.dbName)\n\t\tif (!persisted) {\n\t\t\tawait this.restoreFromDumpFallback()\n\t\t\treturn\n\t\t}\n\n\t\ttry {\n\t\t\tawait this.inner.importDatabase(persisted)\n\t\t} catch {\n\t\t\tawait this.restoreFromDumpFallback()\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\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\tprivate async restoreFromDumpFallback(): Promise<void> {\n\t\tconst dump = await loadDumpFromIndexedDB<DatabaseDump>(this.dbName)\n\t\tif (!dump) return\n\n\t\tfor (const table of dump.tables) {\n\t\t\tconst name = ensureSafeIdentifier(table.name)\n\t\t\tawait this.inner.execute(`DELETE FROM ${quoteIdent(name)}`)\n\n\t\t\tif (table.rows.length === 0) continue\n\n\t\t\tfor (const row of table.rows) {\n\t\t\t\tconst columns = table.columns.filter((column) =>\n\t\t\t\t\tObject.prototype.hasOwnProperty.call(row, column),\n\t\t\t\t)\n\t\t\t\tif (columns.length === 0) continue\n\n\t\t\t\tconst placeholders = columns.map(() => '?').join(', ')\n\t\t\t\tconst quotedColumns = columns\n\t\t\t\t\t.map((column) => quoteIdent(ensureSafeIdentifier(column)))\n\t\t\t\t\t.join(', ')\n\t\t\t\tconst values = columns.map((column) => row[column])\n\n\t\t\t\tawait this.inner.execute(\n\t\t\t\t\t`INSERT INTO ${quoteIdent(name)} (${quotedColumns}) VALUES (${placeholders})`,\n\t\t\t\t\tvalues,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate async exportDump(): Promise<DatabaseDump> {\n\t\tconst tableRows = await this.inner.query<{ name: string }>(\n\t\t\t\"SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'\",\n\t\t)\n\n\t\tconst tables: DatabaseDump['tables'] = []\n\t\tfor (const tableRow of tableRows) {\n\t\t\tconst tableName = ensureSafeIdentifier(tableRow.name)\n\t\t\tconst columns = await this.inner.query<{ name: string }>(\n\t\t\t\t`PRAGMA table_info(${quoteIdent(tableName)})`,\n\t\t\t)\n\t\t\tconst columnNames = columns.map((column) => column.name)\n\t\t\tconst rows = await this.inner.query<Record<string, unknown>>(\n\t\t\t\t`SELECT * FROM ${quoteIdent(tableName)}`,\n\t\t\t)\n\n\t\t\ttables.push({\n\t\t\t\tname: tableName,\n\t\t\t\tcolumns: columnNames,\n\t\t\t\trows,\n\t\t\t})\n\t\t}\n\n\t\treturn { tables }\n\t}\n}\n\nfunction 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\nfunction isUnsupportedWorkerExport(error: unknown): boolean {\n\treturn error instanceof AdapterError && error.context?.code === 'EXPORT_NOT_SUPPORTED'\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\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\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/** Cancel any pending debounce and persist immediately. */\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\tif (this.inFlight) {\n\t\t\tawait this.inFlight\n\t\t\treturn\n\t\t}\n\t\tthis.inFlight = this.runFlush()\n\t\ttry {\n\t\t\tawait this.inFlight\n\t\t} finally {\n\t\t\tthis.inFlight = null\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\ttry {\n\t\t\tawait this.flush()\n\t\t} catch (error) {\n\t\t\tthis.onError?.(error)\n\t\t}\n\t}\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"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/adapters/indexeddb-persistence-scheduler.ts","../../src/adapters/indexeddb-adapter.ts"],"sourcesContent":["/// <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 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"],"mappings":"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","names":[]}
|