@korajs/store 1.0.0-beta.11 → 1.0.0-beta.13

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 (92) hide show
  1. package/README.md +29 -37
  2. package/dist/adapters/better-sqlite3.cjs +90 -4
  3. package/dist/adapters/better-sqlite3.cjs.map +1 -1
  4. package/dist/adapters/better-sqlite3.d.cts +21 -12
  5. package/dist/adapters/better-sqlite3.d.ts +21 -12
  6. package/dist/adapters/better-sqlite3.js +69 -5
  7. package/dist/adapters/better-sqlite3.js.map +1 -1
  8. package/dist/adapters/indexeddb.cjs +1316 -610
  9. package/dist/adapters/indexeddb.cjs.map +1 -1
  10. package/dist/adapters/indexeddb.d.cts +29 -3
  11. package/dist/adapters/indexeddb.d.ts +29 -3
  12. package/dist/adapters/indexeddb.js +247 -221
  13. package/dist/adapters/indexeddb.js.map +1 -1
  14. package/dist/adapters/sqlite-wasm-worker.cjs +803 -62
  15. package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -1
  16. package/dist/adapters/sqlite-wasm-worker.js +480 -62
  17. package/dist/adapters/sqlite-wasm-worker.js.map +1 -1
  18. package/dist/adapters/sqlite-wasm.cjs +1032 -344
  19. package/dist/adapters/sqlite-wasm.cjs.map +1 -1
  20. package/dist/adapters/sqlite-wasm.d.cts +239 -18
  21. package/dist/adapters/sqlite-wasm.d.ts +239 -18
  22. package/dist/adapters/sqlite-wasm.js +111 -5
  23. package/dist/adapters/sqlite-wasm.js.map +1 -1
  24. package/dist/backup-4NUPDEMB.js +25 -0
  25. package/dist/chunk-32EDKWMI.js +876 -0
  26. package/dist/chunk-32EDKWMI.js.map +1 -0
  27. package/dist/{chunk-CYO3YKFL.js → chunk-5ZRRF6R2.js} +2 -2
  28. package/dist/chunk-A6GFH5IG.js +36 -0
  29. package/dist/chunk-A6GFH5IG.js.map +1 -0
  30. package/dist/chunk-DUKTWVZW.js +175 -0
  31. package/dist/chunk-DUKTWVZW.js.map +1 -0
  32. package/dist/chunk-HZTDBLDB.js +607 -0
  33. package/dist/chunk-HZTDBLDB.js.map +1 -0
  34. package/dist/{chunk-GSJ7EEUC.js → chunk-J5AGXF3D.js} +58 -9
  35. package/dist/chunk-J5AGXF3D.js.map +1 -0
  36. package/dist/chunk-MCQKEHOD.js +112 -0
  37. package/dist/chunk-MCQKEHOD.js.map +1 -0
  38. package/dist/chunk-P4V375FY.js +810 -0
  39. package/dist/chunk-P4V375FY.js.map +1 -0
  40. package/dist/chunk-QDUSJMFV.js +1095 -0
  41. package/dist/chunk-QDUSJMFV.js.map +1 -0
  42. package/dist/chunk-SJP2HTYZ.js +1003 -0
  43. package/dist/chunk-SJP2HTYZ.js.map +1 -0
  44. package/dist/chunk-W5F2LWG6.js +591 -0
  45. package/dist/chunk-W5F2LWG6.js.map +1 -0
  46. package/dist/chunk-Y5LRH7KI.js +5341 -0
  47. package/dist/chunk-Y5LRH7KI.js.map +1 -0
  48. package/dist/{export-audit-ODGQTQIK.js → export-audit-J2QXRHD2.js} +3 -3
  49. package/dist/index.cjs +9354 -4157
  50. package/dist/index.cjs.map +1 -1
  51. package/dist/index.d.cts +1064 -108
  52. package/dist/index.d.ts +1064 -108
  53. package/dist/index.js +249 -3092
  54. package/dist/index.js.map +1 -1
  55. package/dist/internal.cjs +580 -264
  56. package/dist/internal.cjs.map +1 -1
  57. package/dist/internal.d.cts +15 -85
  58. package/dist/internal.d.ts +15 -85
  59. package/dist/internal.js +10 -8
  60. package/dist/restore-FR7XIKT2.js +299 -0
  61. package/dist/restore-FR7XIKT2.js.map +1 -0
  62. package/dist/sql-builder-C4u55jKW.d.cts +608 -0
  63. package/dist/sql-builder-CGckdBDL.d.ts +608 -0
  64. package/dist/sqlite-wasm-channel-DVX93Uiq.d.cts +141 -0
  65. package/dist/sqlite-wasm-channel-DVX93Uiq.d.ts +141 -0
  66. package/dist/sqlite-wasm-channel-MLHLKEED.js +12 -0
  67. package/dist/store-IMWKZLWB.js +16 -0
  68. package/dist/store-IMWKZLWB.js.map +1 -0
  69. package/dist/{types-BslDOTk0.d.ts → types-Dh-xyUjP.d.cts} +99 -26
  70. package/dist/{types-BslDOTk0.d.cts → types-Dh-xyUjP.d.ts} +99 -26
  71. package/package.json +3 -2
  72. package/dist/backup-5S7K2Z35.js +0 -14
  73. package/dist/chunk-GSJ7EEUC.js.map +0 -1
  74. package/dist/chunk-HS6DBMST.js +0 -383
  75. package/dist/chunk-HS6DBMST.js.map +0 -1
  76. package/dist/chunk-RJIEKEN4.js +0 -751
  77. package/dist/chunk-RJIEKEN4.js.map +0 -1
  78. package/dist/chunk-TUR7RGEP.js +0 -84
  79. package/dist/chunk-TUR7RGEP.js.map +0 -1
  80. package/dist/chunk-VDBJQ75K.js +0 -78
  81. package/dist/chunk-VDBJQ75K.js.map +0 -1
  82. package/dist/chunk-YT7MRFCP.js +0 -687
  83. package/dist/chunk-YT7MRFCP.js.map +0 -1
  84. package/dist/sqlite-wasm-channel-GGHA2BXU.js +0 -12
  85. package/dist/sqlite-wasm-channel-L6o3bWQK.d.cts +0 -92
  86. package/dist/sqlite-wasm-channel-L6o3bWQK.d.ts +0 -92
  87. package/dist/types-BOO9EPt1.d.ts +0 -170
  88. package/dist/types-C_GDavG-.d.cts +0 -170
  89. /package/dist/{backup-5S7K2Z35.js.map → backup-4NUPDEMB.js.map} +0 -0
  90. /package/dist/{chunk-CYO3YKFL.js.map → chunk-5ZRRF6R2.js.map} +0 -0
  91. /package/dist/{export-audit-ODGQTQIK.js.map → export-audit-J2QXRHD2.js.map} +0 -0
  92. /package/dist/{sqlite-wasm-channel-GGHA2BXU.js.map → sqlite-wasm-channel-MLHLKEED.js.map} +0 -0
@@ -1,6 +1,42 @@
1
- // src/adapters/sqlite-wasm-worker-core.ts
2
- var OPFS_POOL_NAME = "kora-opfs";
1
+ import {
2
+ STORED_SCHEMA_VERSION_SQL,
3
+ parseSchemaCeiling,
4
+ schemaAheadMessage,
5
+ storedSchemaVersion
6
+ } from "../chunk-A6GFH5IG.js";
7
+ import {
8
+ LEGACY_OPFS_POOL_NAME,
9
+ TransactionSerializingWorkerBridge,
10
+ opfsPoolDirectory,
11
+ opfsPoolLockName,
12
+ opfsPoolNameFor,
13
+ opfsPoolPath,
14
+ startWorkerRpcRelay
15
+ } from "../chunk-W5F2LWG6.js";
16
+ import "../chunk-DUKTWVZW.js";
17
+
18
+ // src/adapters/opfs-pool-ownership.ts
19
+ var OPFS_POOL_HEADROOM = 2;
20
+ var BLOCKED_REPORT_AFTER_MS = 750;
21
+ var INSTALL_RACE_ATTEMPTS = 20;
3
22
  var OPFS_INIT_TIMEOUT_MS = 1e4;
23
+ var OPAQUE_DIR_NAME = ".opaque";
24
+ var PoolUnavailableError = class extends Error {
25
+ constructor(message, reason) {
26
+ super(message);
27
+ this.reason = reason;
28
+ this.name = "PoolUnavailableError";
29
+ }
30
+ reason;
31
+ };
32
+ function opfsSahSupported(sqlite3) {
33
+ const g = globalThis;
34
+ const fileHandle = g.FileSystemFileHandle;
35
+ return typeof sqlite3.installOpfsSAHPoolVfs === "function" && typeof g.FileSystemDirectoryHandle !== "undefined" && typeof fileHandle?.prototype?.createSyncAccessHandle === "function" && typeof navigator !== "undefined" && typeof navigator.storage?.getDirectory === "function";
36
+ }
37
+ function sleep(ms) {
38
+ return new Promise((resolve) => setTimeout(resolve, ms));
39
+ }
4
40
  async function withTimeout(promise, timeoutMs, label) {
5
41
  let timer;
6
42
  try {
@@ -8,7 +44,7 @@ async function withTimeout(promise, timeoutMs, label) {
8
44
  promise,
9
45
  new Promise((_, reject) => {
10
46
  timer = setTimeout(
11
- () => reject(new Error(`${label} timed out after ${timeoutMs}ms`)),
47
+ () => reject(new PoolUnavailableError(`${label} timed out after ${timeoutMs}ms`, "timeout")),
12
48
  timeoutMs
13
49
  );
14
50
  })
@@ -17,25 +53,198 @@ async function withTimeout(promise, timeoutMs, label) {
17
53
  if (timer !== void 0) clearTimeout(timer);
18
54
  }
19
55
  }
20
- function opfsDatabaseFilename(dbName) {
21
- const base = dbName.replace(/[^a-zA-Z0-9._-]/g, "_");
22
- return base.endsWith(".db") ? base : `${base}.db`;
56
+ async function holdExclusiveLock(name, reporter) {
57
+ const locks = typeof navigator !== "undefined" ? navigator.locks : void 0;
58
+ if (!locks || typeof locks.request !== "function") {
59
+ return () => {
60
+ };
61
+ }
62
+ const grab = (ifAvailable) => new Promise((resolve, reject) => {
63
+ locks.request(name, { mode: "exclusive", ifAvailable }, (lock) => {
64
+ if (lock === null) {
65
+ resolve(null);
66
+ return void 0;
67
+ }
68
+ return new Promise((release2) => {
69
+ resolve(() => release2());
70
+ });
71
+ }).catch(reject);
72
+ });
73
+ const immediate = await grab(true);
74
+ if (immediate) return immediate;
75
+ const start = Date.now();
76
+ reporter?.blocked();
77
+ const release = await grab(false);
78
+ if (!release) {
79
+ throw new PoolUnavailableError(`Web Lock "${name}" was not granted`, "lock-conflict");
80
+ }
81
+ reporter?.unblocked(Date.now() - start);
82
+ return release;
83
+ }
84
+ async function opfsRoot() {
85
+ return await navigator.storage.getDirectory();
86
+ }
87
+ async function childDirectory(parent, name) {
88
+ try {
89
+ return await parent.getDirectoryHandle(name);
90
+ } catch {
91
+ return null;
92
+ }
93
+ }
94
+ async function poolDirectoryExists(poolName) {
95
+ return await childDirectory(await opfsRoot(), opfsPoolDirectory(poolName)) !== null;
96
+ }
97
+ function isHandleBusy(error) {
98
+ const name = error?.name;
99
+ return name === "NoModificationAllowedError" || name === "InvalidStateError";
100
+ }
101
+ async function poolHandlesBusy(poolName) {
102
+ const dir = await childDirectory(await opfsRoot(), opfsPoolDirectory(poolName));
103
+ if (!dir) return false;
104
+ const opaque = await childDirectory(dir, OPAQUE_DIR_NAME);
105
+ if (!opaque) return false;
106
+ for await (const [, handle] of opaque.entries()) {
107
+ if (handle.kind !== "file") continue;
108
+ try {
109
+ const access = await handle.createSyncAccessHandle();
110
+ access.close();
111
+ } catch (error) {
112
+ if (isHandleBusy(error)) return true;
113
+ throw error;
114
+ }
115
+ }
116
+ return false;
117
+ }
118
+ async function waitForPoolHandles(poolName, reporter) {
119
+ const start = Date.now();
120
+ let reported = false;
121
+ let delay = 15;
122
+ while (await poolHandlesBusy(poolName)) {
123
+ if (!reported && Date.now() - start >= BLOCKED_REPORT_AFTER_MS) {
124
+ reported = true;
125
+ reporter?.blocked();
126
+ }
127
+ await sleep(delay);
128
+ delay = Math.min(delay * 2, 500);
129
+ }
130
+ if (reported) reporter?.unblocked(Date.now() - start);
23
131
  }
24
- function classifyOpfsFailure(error) {
25
- const message = error instanceof Error ? error.message : String(error);
26
- if (/timed out|timeout/i.test(message)) {
27
- return "timeout";
132
+ async function installPool(sqlite3, poolName, reporter) {
133
+ const install = sqlite3.installOpfsSAHPoolVfs;
134
+ if (!install || !opfsSahSupported(sqlite3)) {
135
+ throw new PoolUnavailableError("OPFS SyncAccessHandle pool is not supported", "unsupported");
28
136
  }
29
- if (/lock|already|in use|in-use|NoModificationAllowed|acquire|SyncAccessHandle|being created/i.test(
30
- message
31
- )) {
32
- return "lock-conflict";
137
+ let lastError = null;
138
+ for (let attempt = 0; attempt < INSTALL_RACE_ATTEMPTS; attempt += 1) {
139
+ await waitForPoolHandles(poolName, reporter);
140
+ try {
141
+ const pool = await withTimeout(
142
+ install({ name: poolName, forceReinitIfPreviouslyFailed: true }),
143
+ OPFS_INIT_TIMEOUT_MS,
144
+ "OPFS VFS install"
145
+ );
146
+ if (pool.isPaused()) {
147
+ await pool.unpauseVfs();
148
+ }
149
+ return pool;
150
+ } catch (error) {
151
+ if (error instanceof PoolUnavailableError) throw error;
152
+ lastError = error;
153
+ if (!isHandleBusy(error)) break;
154
+ await sleep(Math.min(25 * 2 ** attempt, 250));
155
+ }
33
156
  }
34
- return "unsupported";
157
+ const message = lastError instanceof Error ? lastError.message : String(lastError);
158
+ if (isHandleBusy(lastError)) {
159
+ throw new PoolUnavailableError(
160
+ `OPFS pool "${poolName}" stayed busy: ${message}`,
161
+ "lock-conflict"
162
+ );
163
+ }
164
+ throw new PoolUnavailableError(
165
+ `OPFS pool "${poolName}" could not be installed: ${message}`,
166
+ "unsupported"
167
+ );
168
+ }
169
+ async function ensurePoolCapacity(pool) {
170
+ await pool.reserveMinimumCapacity(pool.getFileCount() + 2 + OPFS_POOL_HEADROOM);
35
171
  }
172
+ function assertDatabaseHealthy(db, label) {
173
+ const rows = db.exec({
174
+ sql: "PRAGMA quick_check",
175
+ returnValue: "resultRows",
176
+ rowMode: "array"
177
+ });
178
+ const verdict = rows?.[0]?.[0];
179
+ if (verdict !== "ok") {
180
+ throw new Error(`${label} failed integrity check: ${String(verdict)}`);
181
+ }
182
+ }
183
+ function pausePool(pool) {
184
+ if (!pool.isPaused()) {
185
+ pool.pauseVfs();
186
+ }
187
+ }
188
+ async function migrateFromLegacyPool(sqlite3, target, poolPath, reporter) {
189
+ if (!await poolDirectoryExists(LEGACY_OPFS_POOL_NAME)) {
190
+ return false;
191
+ }
192
+ const releaseLegacyLock = await holdExclusiveLock(
193
+ opfsPoolLockName(LEGACY_OPFS_POOL_NAME),
194
+ reporter
195
+ );
196
+ try {
197
+ if (!await poolDirectoryExists(LEGACY_OPFS_POOL_NAME)) {
198
+ return false;
199
+ }
200
+ const legacy = await installPool(sqlite3, LEGACY_OPFS_POOL_NAME, reporter);
201
+ try {
202
+ if (!legacy.getFileNames().includes(poolPath)) {
203
+ return false;
204
+ }
205
+ const legacyDb = new legacy.OpfsSAHPoolDb(poolPath);
206
+ try {
207
+ assertDatabaseHealthy(legacyDb, `Legacy database ${poolPath}`);
208
+ } finally {
209
+ legacyDb.close();
210
+ }
211
+ const bytes = legacy.exportFile(poolPath);
212
+ if (bytes.byteLength === 0) {
213
+ legacy.unlink(poolPath);
214
+ return false;
215
+ }
216
+ await ensurePoolCapacity(target);
217
+ await target.importDb(poolPath, bytes);
218
+ let migratedDb = null;
219
+ try {
220
+ migratedDb = new target.OpfsSAHPoolDb(poolPath);
221
+ assertDatabaseHealthy(migratedDb, `Migrated database ${poolPath}`);
222
+ } catch (error) {
223
+ migratedDb?.close();
224
+ migratedDb = null;
225
+ target.unlink(poolPath);
226
+ throw error;
227
+ }
228
+ migratedDb.close();
229
+ legacy.unlink(poolPath);
230
+ legacy.unlink(`${poolPath}-journal`);
231
+ return true;
232
+ } finally {
233
+ if (legacy.getFileCount() === 0) {
234
+ await legacy.removeVfs();
235
+ } else {
236
+ pausePool(legacy);
237
+ }
238
+ }
239
+ } finally {
240
+ releaseLegacyLock();
241
+ }
242
+ }
243
+
244
+ // src/adapters/sqlite-wasm-worker-core.ts
245
+ var PROGRESS_HEARTBEAT_MS = 500;
246
+ var PROGRESS_HANDLER_OPS = 2e4;
36
247
  var sqlite3Promise = null;
37
- var opfsPoolPromise = null;
38
- var opfsFallbackReason;
39
248
  async function loadSqlite3() {
40
249
  const sqlite3InitModule = (await import("@sqlite.org/sqlite-wasm")).default;
41
250
  const wasmUrl = globalThis.__KORA_SQLITE_WASM_URL;
@@ -43,7 +252,20 @@ async function loadSqlite3() {
43
252
  locateFile: (file) => file.endsWith(".wasm") ? wasmUrl : file
44
253
  } : void 0;
45
254
  const initFn = sqlite3InitModule;
46
- return await withTimeout(initFn(initOptions), 6e4, "SQLite3 module init");
255
+ let timer;
256
+ try {
257
+ return await Promise.race([
258
+ initFn(initOptions),
259
+ new Promise((_, reject) => {
260
+ timer = setTimeout(
261
+ () => reject(new Error("SQLite3 module init timed out after 60000ms")),
262
+ 6e4
263
+ );
264
+ })
265
+ ]);
266
+ } finally {
267
+ if (timer !== void 0) clearTimeout(timer);
268
+ }
47
269
  }
48
270
  function getSqlite3() {
49
271
  if (!sqlite3Promise) {
@@ -51,64 +273,213 @@ function getSqlite3() {
51
273
  }
52
274
  return sqlite3Promise;
53
275
  }
54
- async function installOpfsPool(sqlite3) {
55
- if (!sqlite3.installOpfsSAHPoolVfs) {
56
- opfsFallbackReason = "unsupported";
57
- return null;
276
+ function createSqliteWasmCore(options = {}) {
277
+ let db = null;
278
+ let sqlite3Api = null;
279
+ let persistent = false;
280
+ let fallbackReason;
281
+ let pool = null;
282
+ let poolName = null;
283
+ let releasePoolLock = null;
284
+ let openInfo = {
285
+ created: false,
286
+ migratedFromLegacy: false
287
+ };
288
+ let lastProgressBeat = 0;
289
+ function reporterFor(resource, name) {
290
+ return {
291
+ blocked: () => options.postEvent?.({ kind: "storage-blocked", resource, poolName: name }),
292
+ unblocked: (waitedMs) => options.postEvent?.({ kind: "storage-unblocked", resource, poolName: name, waitedMs })
293
+ };
58
294
  }
59
- try {
60
- return await withTimeout(
61
- sqlite3.installOpfsSAHPoolVfs({ name: OPFS_POOL_NAME }),
62
- OPFS_INIT_TIMEOUT_MS,
63
- "OPFS VFS install"
295
+ async function releaseStorage(mode) {
296
+ if (db) {
297
+ detachProgressHandler(db);
298
+ try {
299
+ db.close();
300
+ } finally {
301
+ db = null;
302
+ }
303
+ }
304
+ if (pool) {
305
+ if (mode === "remove") {
306
+ await pool.removeVfs();
307
+ } else {
308
+ pausePool(pool);
309
+ }
310
+ pool = null;
311
+ }
312
+ releasePoolLock?.();
313
+ releasePoolLock = null;
314
+ }
315
+ function attachProgressHandler(target) {
316
+ const register = sqlite3Api?.capi?.sqlite3_progress_handler;
317
+ if (!options.onProgress || typeof register !== "function" || !target.pointer) return;
318
+ register(
319
+ target.pointer,
320
+ PROGRESS_HANDLER_OPS,
321
+ () => {
322
+ const now = Date.now();
323
+ if (now - lastProgressBeat >= PROGRESS_HEARTBEAT_MS) {
324
+ lastProgressBeat = now;
325
+ options.onProgress?.();
326
+ }
327
+ return 0;
328
+ },
329
+ 0
64
330
  );
65
- } catch (error) {
66
- opfsFallbackReason = classifyOpfsFailure(error);
67
- return null;
68
331
  }
69
- }
70
- function getOpfsPool(sqlite3) {
71
- if (!opfsPoolPromise) {
72
- opfsPoolPromise = installOpfsPool(sqlite3);
332
+ function detachProgressHandler(target) {
333
+ const register = sqlite3Api?.capi?.sqlite3_progress_handler;
334
+ if (typeof register !== "function" || !target.pointer) return;
335
+ try {
336
+ register(target.pointer, 0, 0, 0);
337
+ } catch {
338
+ }
73
339
  }
74
- return opfsPoolPromise;
75
- }
76
- function createSqliteWasmCore() {
77
- let db = null;
78
- let sqlite3Api = null;
79
- let persistent = false;
80
- async function open(id, ddlStatements, dbName) {
340
+ async function openOpfs(sqlite3, dbName, mustExist) {
341
+ const name = opfsPoolNameFor(dbName);
342
+ const path = opfsPoolPath(dbName);
343
+ poolName = name;
344
+ releasePoolLock = await holdExclusiveLock(opfsPoolLockName(name), reporterFor("pool", name));
345
+ pool = await installPool(sqlite3, name, reporterFor("pool", name));
346
+ let migratedFromLegacy = false;
347
+ if (!pool.getFileNames().includes(path)) {
348
+ migratedFromLegacy = await migrateFromLegacyPool(
349
+ sqlite3,
350
+ pool,
351
+ path,
352
+ reporterFor("legacy-pool", name)
353
+ );
354
+ }
355
+ const existedBefore = pool.getFileNames().includes(path);
356
+ if (!existedBefore && mustExist) {
357
+ await releaseStorage(pool.getFileCount() === 0 ? "remove" : "pause");
358
+ return { id: 0, type: "error", message: `Database file ${path} not found`, code: "NOT_FOUND" };
359
+ }
360
+ await ensurePoolCapacity(pool);
81
361
  try {
82
- if (db) {
362
+ db = new pool.OpfsSAHPoolDb(path);
363
+ } catch (error) {
364
+ if (!existedBefore) pool.unlink(path);
365
+ throw error;
366
+ }
367
+ persistent = true;
368
+ openInfo = { created: !existedBefore, migratedFromLegacy };
369
+ return null;
370
+ }
371
+ async function open(id, ddlStatements, dbName, storage, mustExist) {
372
+ if (db) {
373
+ try {
83
374
  applyDdl(db, ddlStatements);
84
375
  return { id, type: "success", data: buildOpenData() };
376
+ } catch (error) {
377
+ return { id, type: "error", message: error.message, code: "INIT_ERROR" };
85
378
  }
379
+ }
380
+ let createdPath = null;
381
+ try {
86
382
  const sqlite3 = await getSqlite3();
87
383
  sqlite3Api = sqlite3;
88
- const pool = await getOpfsPool(sqlite3);
89
- if (pool) {
90
- db = new pool.OpfsSAHPoolDb(opfsDatabaseFilename(dbName ?? "kora-db"));
91
- persistent = true;
384
+ if (storage === "opfs" && opfsSahSupported(sqlite3)) {
385
+ try {
386
+ const early = await openOpfs(sqlite3, dbName, mustExist);
387
+ if (early) return { ...early, id };
388
+ if (openInfo.created) createdPath = opfsPoolPath(dbName);
389
+ } catch (error) {
390
+ if (!(error instanceof PoolUnavailableError) || error.reason === "lock-conflict") {
391
+ throw error;
392
+ }
393
+ await releaseStorage("pause");
394
+ fallbackReason = error.reason;
395
+ }
92
396
  } else {
397
+ fallbackReason = "unsupported";
398
+ }
399
+ if (!db) {
93
400
  db = new sqlite3.oo1.DB({ filename: ":memory:" });
94
401
  persistent = false;
95
402
  }
96
- db.exec({ sql: "PRAGMA journal_mode = WAL" });
97
403
  db.exec({ sql: "PRAGMA foreign_keys = ON" });
98
404
  applyDdl(db, ddlStatements);
405
+ attachProgressHandler(db);
99
406
  return { id, type: "success", data: buildOpenData() };
100
407
  } catch (error) {
101
- return { id, type: "error", message: error.message, code: "INIT_ERROR" };
408
+ const currentPool = pool;
409
+ if (db) {
410
+ try {
411
+ db.close();
412
+ } catch {
413
+ }
414
+ db = null;
415
+ }
416
+ if (createdPath && currentPool) {
417
+ try {
418
+ currentPool.unlink(createdPath);
419
+ currentPool.unlink(`${createdPath}-journal`);
420
+ } catch {
421
+ }
422
+ }
423
+ try {
424
+ await releaseStorage("pause");
425
+ } catch {
426
+ }
427
+ const reason = error instanceof PoolUnavailableError ? error.reason : void 0;
428
+ return {
429
+ id,
430
+ type: "error",
431
+ message: error.message,
432
+ code: "INIT_ERROR",
433
+ context: {
434
+ ...reason ? { reason } : {},
435
+ ...createdPath ? { removedCreatedFile: createdPath } : {}
436
+ }
437
+ };
438
+ }
439
+ }
440
+ function readJournalMode() {
441
+ if (!db) return void 0;
442
+ try {
443
+ let mode;
444
+ db.exec({
445
+ sql: "PRAGMA journal_mode",
446
+ rowMode: "object",
447
+ callback: (row) => {
448
+ if (typeof row.journal_mode === "string") mode = row.journal_mode;
449
+ }
450
+ });
451
+ return mode;
452
+ } catch {
453
+ return void 0;
102
454
  }
103
455
  }
104
456
  function buildOpenData() {
457
+ const journalMode = readJournalMode();
105
458
  return {
459
+ ...journalMode ? { journalMode } : {},
106
460
  persistent,
107
- ...persistent ? {} : { fallbackReason: opfsFallbackReason ?? "unsupported" }
461
+ ...persistent ? {} : { fallbackReason: fallbackReason ?? "unsupported" },
462
+ ...poolName && persistent ? { poolName } : {},
463
+ created: openInfo.created,
464
+ migratedFromLegacy: openInfo.migratedFromLegacy
108
465
  };
109
466
  }
110
467
  function applyDdl(target, ddlStatements) {
111
468
  for (const sql of ddlStatements) {
469
+ const ceiling = parseSchemaCeiling(sql);
470
+ if (ceiling !== null) {
471
+ const rows = [];
472
+ target.exec({
473
+ sql: STORED_SCHEMA_VERSION_SQL,
474
+ rowMode: "object",
475
+ callback: (row) => {
476
+ rows.push({ value: row.value });
477
+ }
478
+ });
479
+ const stored = storedSchemaVersion(rows);
480
+ if (stored > ceiling) throw new Error(schemaAheadMessage(stored, ceiling));
481
+ continue;
482
+ }
112
483
  if (sql.startsWith("--kora:safe-alter")) {
113
484
  try {
114
485
  target.exec({ sql: sql.replace("--kora:safe-alter\n", "") });
@@ -153,12 +524,13 @@ function createSqliteWasmCore() {
153
524
  return { id, type: "error", message: error.message, code: "QUERY_ERROR" };
154
525
  }
155
526
  }
156
- function close(id) {
157
- if (db) {
158
- db.close();
159
- db = null;
527
+ async function close(id, mode) {
528
+ try {
529
+ await releaseStorage(mode);
530
+ return { id, type: "success" };
531
+ } catch (error) {
532
+ return { id, type: "error", message: error.message, code: "CLOSE_ERROR" };
160
533
  }
161
- return { id, type: "success" };
162
534
  }
163
535
  function migrate(id, statements) {
164
536
  if (!db) {
@@ -177,10 +549,9 @@ function createSqliteWasmCore() {
177
549
  if (!db) {
178
550
  return { id, type: "error", message: "Database is not open", code: "DB_NOT_OPEN" };
179
551
  }
180
- const dbWithDeserialize = db;
181
- if (typeof dbWithDeserialize.deserialize === "function") {
552
+ if (typeof db.deserialize === "function") {
182
553
  try {
183
- dbWithDeserialize.deserialize(data);
554
+ db.deserialize(data);
184
555
  return { id, type: "success" };
185
556
  } catch (error) {
186
557
  return { id, type: "error", message: error.message, code: "IMPORT_ERROR" };
@@ -205,9 +576,17 @@ function createSqliteWasmCore() {
205
576
  try {
206
577
  switch (request.type) {
207
578
  case "open":
208
- return await open(request.id, request.ddlStatements, request.dbName);
579
+ return await open(
580
+ request.id,
581
+ request.ddlStatements,
582
+ request.dbName ?? "kora-db",
583
+ request.storage ?? "opfs",
584
+ request.mustExist === true
585
+ );
209
586
  case "close":
210
- return close(request.id);
587
+ return await close(request.id, "pause");
588
+ case "destroy":
589
+ return await close(request.id, "remove");
211
590
  case "execute":
212
591
  return execute(request.id, request.sql, request.params);
213
592
  case "query":
@@ -229,6 +608,8 @@ function createSqliteWasmCore() {
229
608
  message: "Export not yet supported in browser worker",
230
609
  code: "EXPORT_NOT_SUPPORTED"
231
610
  };
611
+ case "serve":
612
+ return { id: request.id, type: "success" };
232
613
  default:
233
614
  return {
234
615
  id: request.id,
@@ -250,10 +631,47 @@ function createSqliteWasmCore() {
250
631
  }
251
632
 
252
633
  // src/adapters/sqlite-wasm-worker.ts
253
- var core = createSqliteWasmCore();
634
+ var relay = null;
635
+ var core = createSqliteWasmCore({
636
+ postEvent: (event) => {
637
+ const message = { id: -1, type: "event", event };
638
+ self.postMessage(message);
639
+ },
640
+ // Keep followers' watchdogs fed while a long statement blocks this worker.
641
+ onProgress: () => relay?.beat()
642
+ });
643
+ var serializer = new TransactionSerializingWorkerBridge({
644
+ send: (request) => core.handle(request),
645
+ terminate: () => {
646
+ }
647
+ });
254
648
  self.onmessage = (event) => {
255
- void core.handle(event.data).then((response) => {
649
+ const { clientId, ...request } = event.data;
650
+ if (request.type === "serve") {
651
+ if (!relay) {
652
+ relay = startWorkerRpcRelay(request.channelName, serializer);
653
+ }
654
+ const response = { id: request.id, type: "success" };
256
655
  self.postMessage(response);
257
- });
656
+ return;
657
+ }
658
+ void serializer.send(request, clientId).then(
659
+ (response) => {
660
+ if (request.type === "close" || request.type === "destroy") {
661
+ relay?.stop();
662
+ relay = null;
663
+ }
664
+ self.postMessage(response);
665
+ },
666
+ (error) => {
667
+ const response = {
668
+ id: request.id,
669
+ type: "error",
670
+ message: error instanceof Error ? error.message : String(error),
671
+ code: "WORKER_ERROR"
672
+ };
673
+ self.postMessage(response);
674
+ }
675
+ );
258
676
  };
259
677
  //# sourceMappingURL=sqlite-wasm-worker.js.map