@korajs/store 0.1.0

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 (43) hide show
  1. package/dist/adapters/better-sqlite3.cjs +166 -0
  2. package/dist/adapters/better-sqlite3.cjs.map +1 -0
  3. package/dist/adapters/better-sqlite3.d.cts +31 -0
  4. package/dist/adapters/better-sqlite3.d.ts +31 -0
  5. package/dist/adapters/better-sqlite3.js +117 -0
  6. package/dist/adapters/better-sqlite3.js.map +1 -0
  7. package/dist/adapters/indexeddb.cjs +550 -0
  8. package/dist/adapters/indexeddb.cjs.map +1 -0
  9. package/dist/adapters/indexeddb.d.cts +52 -0
  10. package/dist/adapters/indexeddb.d.ts +52 -0
  11. package/dist/adapters/indexeddb.js +205 -0
  12. package/dist/adapters/indexeddb.js.map +1 -0
  13. package/dist/adapters/sqlite-wasm-worker.cjs +215 -0
  14. package/dist/adapters/sqlite-wasm-worker.cjs.map +1 -0
  15. package/dist/adapters/sqlite-wasm-worker.d.cts +2 -0
  16. package/dist/adapters/sqlite-wasm-worker.d.ts +2 -0
  17. package/dist/adapters/sqlite-wasm-worker.js +191 -0
  18. package/dist/adapters/sqlite-wasm-worker.js.map +1 -0
  19. package/dist/adapters/sqlite-wasm.cjs +354 -0
  20. package/dist/adapters/sqlite-wasm.cjs.map +1 -0
  21. package/dist/adapters/sqlite-wasm.d.cts +68 -0
  22. package/dist/adapters/sqlite-wasm.d.ts +68 -0
  23. package/dist/adapters/sqlite-wasm.js +14 -0
  24. package/dist/adapters/sqlite-wasm.js.map +1 -0
  25. package/dist/chunk-DXKLAQ6P.js +111 -0
  26. package/dist/chunk-DXKLAQ6P.js.map +1 -0
  27. package/dist/chunk-LAWV6CFH.js +62 -0
  28. package/dist/chunk-LAWV6CFH.js.map +1 -0
  29. package/dist/chunk-ZP5AXQ3Z.js +179 -0
  30. package/dist/chunk-ZP5AXQ3Z.js.map +1 -0
  31. package/dist/index.cjs +1288 -0
  32. package/dist/index.cjs.map +1 -0
  33. package/dist/index.d.cts +376 -0
  34. package/dist/index.d.ts +376 -0
  35. package/dist/index.js +1194 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/sqlite-wasm-channel-46AOWNPM.js +10 -0
  38. package/dist/sqlite-wasm-channel-46AOWNPM.js.map +1 -0
  39. package/dist/sqlite-wasm-channel-Lakjuk2E.d.cts +104 -0
  40. package/dist/sqlite-wasm-channel-Lakjuk2E.d.ts +104 -0
  41. package/dist/types-DF-KDSK1.d.cts +106 -0
  42. package/dist/types-DF-KDSK1.d.ts +106 -0
  43. package/package.json +95 -0
@@ -0,0 +1,550 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __esm = (fn, res) => function __init() {
7
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
8
+ };
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
22
+
23
+ // src/errors.ts
24
+ var import_core, AdapterError, StoreNotOpenError, WorkerTimeoutError, PersistenceError;
25
+ var init_errors = __esm({
26
+ "src/errors.ts"() {
27
+ "use strict";
28
+ import_core = require("@korajs/core");
29
+ AdapterError = class extends import_core.KoraError {
30
+ constructor(message, context) {
31
+ super(message, "ADAPTER_ERROR", context);
32
+ this.name = "AdapterError";
33
+ }
34
+ };
35
+ StoreNotOpenError = class extends import_core.KoraError {
36
+ constructor() {
37
+ super("Store is not open. Call store.open() before performing operations.", "STORE_NOT_OPEN");
38
+ this.name = "StoreNotOpenError";
39
+ }
40
+ };
41
+ WorkerTimeoutError = class extends import_core.KoraError {
42
+ constructor(operation, timeoutMs) {
43
+ super(
44
+ `Worker did not respond within ${timeoutMs}ms for operation "${operation}"`,
45
+ "WORKER_TIMEOUT",
46
+ { operation, timeoutMs }
47
+ );
48
+ this.name = "WorkerTimeoutError";
49
+ }
50
+ };
51
+ PersistenceError = class extends import_core.KoraError {
52
+ constructor(message, context) {
53
+ super(`Persistence error: ${message}`, "PERSISTENCE_ERROR", context);
54
+ this.name = "PersistenceError";
55
+ }
56
+ };
57
+ }
58
+ });
59
+
60
+ // src/adapters/sqlite-wasm-channel.ts
61
+ var sqlite_wasm_channel_exports = {};
62
+ __export(sqlite_wasm_channel_exports, {
63
+ Mutex: () => Mutex,
64
+ WebWorkerBridge: () => WebWorkerBridge
65
+ });
66
+ var Mutex, WebWorkerBridge;
67
+ var init_sqlite_wasm_channel = __esm({
68
+ "src/adapters/sqlite-wasm-channel.ts"() {
69
+ "use strict";
70
+ init_errors();
71
+ Mutex = class {
72
+ locked = false;
73
+ waiters = [];
74
+ /**
75
+ * Acquire the mutex. Returns a release function.
76
+ * If the mutex is already held, the caller waits until it's released.
77
+ */
78
+ async acquire() {
79
+ if (!this.locked) {
80
+ this.locked = true;
81
+ return this.createRelease();
82
+ }
83
+ return new Promise((resolve) => {
84
+ this.waiters.push(() => {
85
+ resolve(this.createRelease());
86
+ });
87
+ });
88
+ }
89
+ createRelease() {
90
+ let released = false;
91
+ return () => {
92
+ if (released) return;
93
+ released = true;
94
+ const next = this.waiters.shift();
95
+ if (next) {
96
+ next();
97
+ } else {
98
+ this.locked = false;
99
+ }
100
+ };
101
+ }
102
+ };
103
+ WebWorkerBridge = class {
104
+ worker;
105
+ pending = /* @__PURE__ */ new Map();
106
+ nextId = 1;
107
+ terminated = false;
108
+ timeoutMs;
109
+ /**
110
+ * @param workerUrl - URL to the sqlite-wasm-worker script
111
+ * @param timeoutMs - Timeout for worker responses in milliseconds (default: 30000)
112
+ */
113
+ constructor(workerUrl, timeoutMs = 3e4) {
114
+ this.timeoutMs = timeoutMs;
115
+ this.worker = new Worker(workerUrl, { type: "module" });
116
+ this.worker.onmessage = (event) => {
117
+ const response = event.data;
118
+ const entry = this.pending.get(response.id);
119
+ if (entry) {
120
+ this.pending.delete(response.id);
121
+ entry.resolve(response);
122
+ }
123
+ };
124
+ this.worker.onerror = (event) => {
125
+ const error = new Error(`Worker error: ${event.message}`);
126
+ for (const [id, entry] of this.pending) {
127
+ this.pending.delete(id);
128
+ entry.reject(error);
129
+ }
130
+ };
131
+ }
132
+ async send(request) {
133
+ if (this.terminated) {
134
+ return {
135
+ id: request.id,
136
+ type: "error",
137
+ message: "Worker has been terminated",
138
+ code: "WORKER_TERMINATED"
139
+ };
140
+ }
141
+ const id = this.nextId++;
142
+ const req = { ...request, id };
143
+ return new Promise((resolve, reject) => {
144
+ const timer = setTimeout(() => {
145
+ this.pending.delete(id);
146
+ reject(new WorkerTimeoutError(req.type, this.timeoutMs));
147
+ }, this.timeoutMs);
148
+ this.pending.set(id, {
149
+ resolve: (response) => {
150
+ clearTimeout(timer);
151
+ resolve(response);
152
+ },
153
+ reject: (error) => {
154
+ clearTimeout(timer);
155
+ reject(error);
156
+ }
157
+ });
158
+ this.worker.postMessage(req);
159
+ });
160
+ }
161
+ terminate() {
162
+ if (this.terminated) return;
163
+ this.terminated = true;
164
+ this.worker.terminate();
165
+ for (const [id, entry] of this.pending) {
166
+ this.pending.delete(id);
167
+ entry.reject(new Error("Worker terminated"));
168
+ }
169
+ }
170
+ };
171
+ }
172
+ });
173
+
174
+ // src/adapters/indexeddb.ts
175
+ var indexeddb_exports = {};
176
+ __export(indexeddb_exports, {
177
+ IndexedDbAdapter: () => IndexedDbAdapter
178
+ });
179
+ module.exports = __toCommonJS(indexeddb_exports);
180
+
181
+ // src/adapters/indexeddb-adapter.ts
182
+ init_errors();
183
+
184
+ // src/adapters/sqlite-wasm-adapter.ts
185
+ var import_core2 = require("@korajs/core");
186
+ init_errors();
187
+ init_sqlite_wasm_channel();
188
+ var SqliteWasmAdapter = class {
189
+ bridge = null;
190
+ opened = false;
191
+ mutex = new Mutex();
192
+ injectedBridge;
193
+ workerUrl;
194
+ dbName;
195
+ constructor(options = {}) {
196
+ this.injectedBridge = options.bridge;
197
+ this.workerUrl = options.workerUrl;
198
+ this.dbName = options.dbName ?? "kora-db";
199
+ }
200
+ async open(schema) {
201
+ if (this.opened) return;
202
+ if (this.injectedBridge) {
203
+ this.bridge = this.injectedBridge;
204
+ } else if (this.workerUrl) {
205
+ const { WebWorkerBridge: WebWorkerBridge2 } = await Promise.resolve().then(() => (init_sqlite_wasm_channel(), sqlite_wasm_channel_exports));
206
+ this.bridge = new WebWorkerBridge2(this.workerUrl);
207
+ } else {
208
+ throw new AdapterError(
209
+ 'SqliteWasmAdapter requires either a bridge (for testing) or a workerUrl (for browsers). Pass { bridge: new MockWorkerBridge() } for tests, or { workerUrl: "/worker.js" } for browsers.'
210
+ );
211
+ }
212
+ const ddlStatements = (0, import_core2.generateFullDDL)(schema);
213
+ const response = await this.sendRequest({ id: 0, type: "open", ddlStatements });
214
+ if (response.type === "error") {
215
+ throw new AdapterError(`Failed to open database: ${response.message}`, {
216
+ code: response.code,
217
+ dbName: this.dbName
218
+ });
219
+ }
220
+ this.opened = true;
221
+ }
222
+ async close() {
223
+ if (!this.bridge) return;
224
+ try {
225
+ await this.sendRequest({ id: 0, type: "close" });
226
+ } finally {
227
+ this.bridge.terminate();
228
+ this.bridge = null;
229
+ this.opened = false;
230
+ }
231
+ }
232
+ async execute(sql, params) {
233
+ this.guardOpen();
234
+ const response = await this.sendRequest({ id: 0, type: "execute", sql, params });
235
+ if (response.type === "error") {
236
+ throw new AdapterError(`Execute failed: ${response.message}`, { sql, params });
237
+ }
238
+ }
239
+ async query(sql, params) {
240
+ this.guardOpen();
241
+ const response = await this.sendRequest({ id: 0, type: "query", sql, params });
242
+ if (response.type === "error") {
243
+ throw new AdapterError(`Query failed: ${response.message}`, { sql, params });
244
+ }
245
+ return response.data ?? [];
246
+ }
247
+ async transaction(fn) {
248
+ this.guardOpen();
249
+ const release = await this.mutex.acquire();
250
+ try {
251
+ await this.sendChecked({ id: 0, type: "begin" }, "BEGIN transaction");
252
+ const tx = {
253
+ execute: async (sql, params) => {
254
+ const response = await this.sendRequest({ id: 0, type: "execute", sql, params });
255
+ if (response.type === "error") {
256
+ throw new AdapterError(`Transaction execute failed: ${response.message}`, {
257
+ sql,
258
+ params
259
+ });
260
+ }
261
+ },
262
+ query: async (sql, params) => {
263
+ const response = await this.sendRequest({ id: 0, type: "query", sql, params });
264
+ if (response.type === "error") {
265
+ throw new AdapterError(`Transaction query failed: ${response.message}`, { sql, params });
266
+ }
267
+ return response.data ?? [];
268
+ }
269
+ };
270
+ await fn(tx);
271
+ await this.sendChecked({ id: 0, type: "commit" }, "COMMIT transaction");
272
+ } catch (error) {
273
+ try {
274
+ await this.sendRequest({ id: 0, type: "rollback" });
275
+ } catch {
276
+ }
277
+ throw error;
278
+ } finally {
279
+ release();
280
+ }
281
+ }
282
+ async migrate(from, to, migration) {
283
+ this.guardOpen();
284
+ const release = await this.mutex.acquire();
285
+ try {
286
+ await this.sendChecked({ id: 0, type: "begin" }, "BEGIN migration");
287
+ for (const sql of migration.statements) {
288
+ const response = await this.sendRequest({ id: 0, type: "execute", sql });
289
+ if (response.type === "error") {
290
+ throw new AdapterError(`Migration from v${from} to v${to} failed: ${response.message}`, {
291
+ from,
292
+ to
293
+ });
294
+ }
295
+ }
296
+ await this.sendChecked({ id: 0, type: "commit" }, "COMMIT migration");
297
+ } catch (error) {
298
+ try {
299
+ await this.sendRequest({ id: 0, type: "rollback" });
300
+ } catch {
301
+ }
302
+ if (error instanceof AdapterError) throw error;
303
+ throw new AdapterError(
304
+ `Migration from v${from} to v${to} failed: ${error.message}`,
305
+ { from, to }
306
+ );
307
+ } finally {
308
+ release();
309
+ }
310
+ }
311
+ /**
312
+ * Export the database as a Uint8Array (for IndexedDB persistence).
313
+ * Only available when the database is open.
314
+ */
315
+ async exportDatabase() {
316
+ this.guardOpen();
317
+ const response = await this.sendRequest({ id: 0, type: "export" });
318
+ if (response.type === "error") {
319
+ throw new AdapterError(`Export failed: ${response.message}`);
320
+ }
321
+ return response.data;
322
+ }
323
+ /**
324
+ * Import a serialized database snapshot.
325
+ */
326
+ async importDatabase(data) {
327
+ this.guardOpen();
328
+ const response = await this.sendRequest({ id: 0, type: "import", data });
329
+ if (response.type === "error") {
330
+ throw new AdapterError(`Import failed: ${response.message}`);
331
+ }
332
+ }
333
+ guardOpen() {
334
+ if (!this.opened || !this.bridge) {
335
+ throw new StoreNotOpenError();
336
+ }
337
+ }
338
+ async sendRequest(request) {
339
+ const bridge = this.bridge;
340
+ if (!bridge) {
341
+ throw new StoreNotOpenError();
342
+ }
343
+ return bridge.send(request);
344
+ }
345
+ async sendChecked(request, description) {
346
+ const response = await this.sendRequest(request);
347
+ if (response.type === "error") {
348
+ throw new AdapterError(`${description} failed: ${response.message}`);
349
+ }
350
+ }
351
+ };
352
+
353
+ // src/adapters/sqlite-wasm-persistence.ts
354
+ init_errors();
355
+ var IDB_DATABASE_NAME = "kora-persistence";
356
+ var IDB_STORE_NAME = "databases";
357
+ var IDB_VERSION = 1;
358
+ var DUMP_SUFFIX = "::dump";
359
+ function openIdb() {
360
+ return new Promise((resolve, reject) => {
361
+ const request = indexedDB.open(IDB_DATABASE_NAME, IDB_VERSION);
362
+ request.onupgradeneeded = () => {
363
+ const db = request.result;
364
+ if (!db.objectStoreNames.contains(IDB_STORE_NAME)) {
365
+ db.createObjectStore(IDB_STORE_NAME);
366
+ }
367
+ };
368
+ request.onsuccess = () => resolve(request.result);
369
+ request.onerror = () => reject(
370
+ new PersistenceError(
371
+ `Failed to open IndexedDB: ${request.error?.message ?? "unknown error"}`,
372
+ { database: IDB_DATABASE_NAME }
373
+ )
374
+ );
375
+ });
376
+ }
377
+ async function saveToIndexedDB(dbName, data) {
378
+ const idb = await openIdb();
379
+ try {
380
+ await new Promise((resolve, reject) => {
381
+ const tx = idb.transaction(IDB_STORE_NAME, "readwrite");
382
+ const store = tx.objectStore(IDB_STORE_NAME);
383
+ store.put(data, dbName);
384
+ tx.oncomplete = () => resolve();
385
+ tx.onerror = () => reject(new PersistenceError(`Failed to save database "${dbName}" to IndexedDB`, { dbName }));
386
+ });
387
+ } finally {
388
+ idb.close();
389
+ }
390
+ }
391
+ async function saveDumpToIndexedDB(dbName, dump) {
392
+ const idb = await openIdb();
393
+ try {
394
+ await new Promise((resolve, reject) => {
395
+ const tx = idb.transaction(IDB_STORE_NAME, "readwrite");
396
+ const store = tx.objectStore(IDB_STORE_NAME);
397
+ store.put(dump, `${dbName}${DUMP_SUFFIX}`);
398
+ tx.oncomplete = () => resolve();
399
+ tx.onerror = () => reject(new PersistenceError(`Failed to save dump for database "${dbName}"`, { dbName }));
400
+ });
401
+ } finally {
402
+ idb.close();
403
+ }
404
+ }
405
+ async function loadFromIndexedDB(dbName) {
406
+ const idb = await openIdb();
407
+ try {
408
+ return await new Promise((resolve, reject) => {
409
+ const tx = idb.transaction(IDB_STORE_NAME, "readonly");
410
+ const store = tx.objectStore(IDB_STORE_NAME);
411
+ const request = store.get(dbName);
412
+ request.onsuccess = () => {
413
+ const result = request.result;
414
+ if (result instanceof Uint8Array) {
415
+ resolve(result);
416
+ } else if (result) {
417
+ resolve(new Uint8Array(result));
418
+ } else {
419
+ resolve(null);
420
+ }
421
+ };
422
+ request.onerror = () => reject(
423
+ new PersistenceError(`Failed to load database "${dbName}" from IndexedDB`, { dbName })
424
+ );
425
+ });
426
+ } finally {
427
+ idb.close();
428
+ }
429
+ }
430
+ async function loadDumpFromIndexedDB(dbName) {
431
+ const idb = await openIdb();
432
+ try {
433
+ return await new Promise((resolve, reject) => {
434
+ const tx = idb.transaction(IDB_STORE_NAME, "readonly");
435
+ const store = tx.objectStore(IDB_STORE_NAME);
436
+ const request = store.get(`${dbName}${DUMP_SUFFIX}`);
437
+ request.onsuccess = () => {
438
+ resolve(request.result ?? null);
439
+ };
440
+ request.onerror = () => reject(
441
+ new PersistenceError(`Failed to load dump for database "${dbName}" from IndexedDB`, {
442
+ dbName
443
+ })
444
+ );
445
+ });
446
+ } finally {
447
+ idb.close();
448
+ }
449
+ }
450
+
451
+ // src/adapters/indexeddb-adapter.ts
452
+ var IndexedDbAdapter = class {
453
+ inner;
454
+ dbName;
455
+ constructor(options = {}) {
456
+ this.dbName = options.dbName ?? "kora-db";
457
+ this.inner = new SqliteWasmAdapter({
458
+ bridge: options.bridge,
459
+ workerUrl: options.workerUrl,
460
+ dbName: this.dbName
461
+ });
462
+ }
463
+ async open(schema) {
464
+ await this.inner.open(schema);
465
+ const persisted = await loadFromIndexedDB(this.dbName);
466
+ if (!persisted) return;
467
+ try {
468
+ await this.inner.importDatabase(persisted);
469
+ } catch {
470
+ await this.restoreFromDumpFallback();
471
+ }
472
+ }
473
+ async close() {
474
+ await this.persistSnapshot();
475
+ await this.inner.close();
476
+ }
477
+ async execute(sql, params) {
478
+ await this.inner.execute(sql, params);
479
+ await this.persistSnapshot();
480
+ }
481
+ async query(sql, params) {
482
+ return this.inner.query(sql, params);
483
+ }
484
+ async transaction(fn) {
485
+ await this.inner.transaction(fn);
486
+ await this.persistSnapshot();
487
+ }
488
+ async migrate(from, to, migration) {
489
+ await this.inner.migrate(from, to, migration);
490
+ await this.persistSnapshot();
491
+ }
492
+ async persistSnapshot() {
493
+ try {
494
+ const data = await this.inner.exportDatabase();
495
+ await saveToIndexedDB(this.dbName, data);
496
+ const dump = await this.exportDump();
497
+ await saveDumpToIndexedDB(this.dbName, dump);
498
+ } catch {
499
+ }
500
+ }
501
+ async restoreFromDumpFallback() {
502
+ const dump = await loadDumpFromIndexedDB(this.dbName);
503
+ if (!dump) return;
504
+ for (const table of dump.tables) {
505
+ const name = ensureSafeIdentifier(table.name);
506
+ await this.inner.execute(`DELETE FROM ${name}`);
507
+ if (table.rows.length === 0) continue;
508
+ for (const row of table.rows) {
509
+ const columns = table.columns.filter((column) => Object.prototype.hasOwnProperty.call(row, column));
510
+ if (columns.length === 0) continue;
511
+ const placeholders = columns.map(() => "?").join(", ");
512
+ const quotedColumns = columns.map((column) => ensureSafeIdentifier(column)).join(", ");
513
+ const values = columns.map((column) => row[column]);
514
+ await this.inner.execute(
515
+ `INSERT INTO ${name} (${quotedColumns}) VALUES (${placeholders})`,
516
+ values
517
+ );
518
+ }
519
+ }
520
+ }
521
+ async exportDump() {
522
+ const tableRows = await this.inner.query(
523
+ "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"
524
+ );
525
+ const tables = [];
526
+ for (const tableRow of tableRows) {
527
+ const tableName = ensureSafeIdentifier(tableRow.name);
528
+ const columns = await this.inner.query(`PRAGMA table_info(${tableName})`);
529
+ const columnNames = columns.map((column) => column.name);
530
+ const rows = await this.inner.query(`SELECT * FROM ${tableName}`);
531
+ tables.push({
532
+ name: tableName,
533
+ columns: columnNames,
534
+ rows
535
+ });
536
+ }
537
+ return { tables };
538
+ }
539
+ };
540
+ function ensureSafeIdentifier(identifier) {
541
+ if (!/^[a-zA-Z0-9_]+$/.test(identifier)) {
542
+ throw new AdapterError(`Unsafe SQL identifier: ${identifier}`);
543
+ }
544
+ return identifier;
545
+ }
546
+ // Annotate the CommonJS export names for ESM import in node:
547
+ 0 && (module.exports = {
548
+ IndexedDbAdapter
549
+ });
550
+ //# sourceMappingURL=indexeddb.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/errors.ts","../../src/adapters/sqlite-wasm-channel.ts","../../src/adapters/indexeddb.ts","../../src/adapters/indexeddb-adapter.ts","../../src/adapters/sqlite-wasm-adapter.ts","../../src/adapters/sqlite-wasm-persistence.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 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 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 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","/// <reference lib=\"dom\" />\nimport { WorkerTimeoutError } from '../errors'\n\n// === Message Protocol ===\n\n/**\n * Request message sent from the main thread to the SQLite WASM worker.\n * Each request carries a unique `id` for response correlation.\n */\nexport type WorkerRequest =\n\t| { id: number; type: 'open'; ddlStatements: string[] }\n\t| { id: number; type: 'close' }\n\t| { id: number; type: 'execute'; sql: string; params?: unknown[] }\n\t| { id: number; type: 'query'; sql: string; params?: unknown[] }\n\t| { id: number; type: 'begin' }\n\t| { id: number; type: 'commit' }\n\t| { id: number; type: 'rollback' }\n\t| { id: number; type: 'migrate'; from: number; to: number; statements: string[] }\n\t| { id: number; type: 'export' }\n\t| { id: number; type: 'import'; data: Uint8Array }\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// === 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(request: WorkerRequest): Promise<WorkerResponse>\n\n\t/** Terminate the worker. Safe to call multiple times. */\n\tterminate(): void\n}\n\n// === Mutex ===\n\n/**\n * Async mutex for serializing transaction access across the async worker boundary.\n * Only one transaction may be active at a time.\n */\nexport class Mutex {\n\tprivate locked = false\n\tprivate waiters: Array<() => void> = []\n\n\t/**\n\t * Acquire the mutex. Returns a release function.\n\t * If the mutex is already held, the caller waits until it's 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) return\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\n// === WebWorkerBridge ===\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 }\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 */\n\tconstructor(workerUrl: string | URL, timeoutMs = 30000) {\n\t\tthis.timeoutMs = timeoutMs\n\t\tthis.worker = new Worker(workerUrl, { type: 'module' })\n\t\tthis.worker.onmessage = (event: MessageEvent<WorkerResponse>) => {\n\t\t\tconst response = event.data\n\t\t\tconst entry = this.pending.get(response.id)\n\t\t\tif (entry) {\n\t\t\t\tthis.pending.delete(response.id)\n\t\t\t\tentry.resolve(response)\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\tasync send(request: WorkerRequest): 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 req = { ...request, id }\n\n\t\treturn new Promise<WorkerResponse>((resolve, reject) => {\n\t\t\tconst timer = setTimeout(() => {\n\t\t\t\tthis.pending.delete(id)\n\t\t\t\treject(new WorkerTimeoutError(req.type, this.timeoutMs))\n\t\t\t}, this.timeoutMs)\n\n\t\t\tthis.pending.set(id, {\n\t\t\t\tresolve: (response) => {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\tresolve(response)\n\t\t\t\t},\n\t\t\t\treject: (error) => {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\treject(error)\n\t\t\t\t},\n\t\t\t})\n\n\t\t\tthis.worker.postMessage(req)\n\t\t})\n\t}\n\n\tterminate(): void {\n\t\tif (this.terminated) return\n\t\tthis.terminated = true\n\t\tthis.worker.terminate()\n\t\t// Reject any pending requests\n\t\tfor (const [id, entry] of this.pending) {\n\t\t\tthis.pending.delete(id)\n\t\t\tentry.reject(new Error('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 { SchemaDefinition } from '@korajs/core'\nimport { AdapterError } from '../errors'\nimport type { MigrationPlan, StorageAdapter, Transaction } from '../types'\nimport { SqliteWasmAdapter } from './sqlite-wasm-adapter'\nimport type { WorkerBridge } from './sqlite-wasm-channel'\nimport {\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\n/**\n * IndexedDB-backed adapter that uses SQLite WASM in-memory and serializes\n * the entire database to IndexedDB after each transaction.\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\n\tconstructor(options: IndexedDbAdapterOptions = {}) {\n\t\tthis.dbName = options.dbName ?? 'kora-db'\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})\n\t}\n\n\tasync open(schema: SchemaDefinition): Promise<void> {\n\t\tawait this.inner.open(schema)\n\n\t\tconst persisted = await loadFromIndexedDB(this.dbName)\n\t\tif (!persisted) return\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.persistSnapshot()\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\tawait this.persistSnapshot()\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\tawait this.persistSnapshot()\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\tawait this.persistSnapshot()\n\t}\n\n\tprivate async persistSnapshot(): Promise<void> {\n\t\ttry {\n\t\t\tconst data = await this.inner.exportDatabase()\n\t\t\tawait saveToIndexedDB(this.dbName, data)\n\t\t\tconst dump = await this.exportDump()\n\t\t\tawait saveDumpToIndexedDB(this.dbName, dump)\n\t\t} catch {\n\t\t\t// Non-fatal persistence failure. Next write/close retries persistence.\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 ${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) => Object.prototype.hasOwnProperty.call(row, column))\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.map((column) => ensureSafeIdentifier(column)).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 ${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 }>(`PRAGMA table_info(${tableName})`)\n\t\t\tconst columnNames = columns.map((column) => column.name)\n\t\t\tconst rows = await this.inner.query<Record<string, unknown>>(`SELECT * FROM ${tableName}`)\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 AdapterError(`Unsafe SQL identifier: ${identifier}`)\n\t}\n\treturn identifier\n}\n","import { generateFullDDL } from '@korajs/core'\nimport type { SchemaDefinition } from '@korajs/core'\nimport { AdapterError, StoreNotOpenError } from '../errors'\nimport type { MigrationPlan, StorageAdapter, Transaction } from '../types'\nimport { Mutex } from './sqlite-wasm-channel'\nimport type { WorkerBridge, WorkerRequest, WorkerResponse } from './sqlite-wasm-channel'\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. Used as the OPFS file name or IDB key.\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\n/**\n * SQLite WASM adapter that communicates with a SQLite instance through a WorkerBridge.\n *\n * In browsers, the bridge is backed by a Web Worker running SQLite WASM with OPFS persistence.\n * In Node.js tests, the bridge is backed by MockWorkerBridge wrapping better-sqlite3.\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\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 dbName: string\n\n\tconstructor(options: SqliteWasmAdapterOptions = {}) {\n\t\tthis.injectedBridge = options.bridge\n\t\tthis.workerUrl = options.workerUrl\n\t\tthis.dbName = options.dbName ?? 'kora-db'\n\t}\n\n\tasync open(schema: SchemaDefinition): Promise<void> {\n\t\tif (this.opened) return\n\n\t\tif (this.injectedBridge) {\n\t\t\tthis.bridge = this.injectedBridge\n\t\t} else if (this.workerUrl) {\n\t\t\t// Dynamic import to avoid loading WebWorkerBridge in Node.js\n\t\t\tconst { WebWorkerBridge } = await import('./sqlite-wasm-channel')\n\t\t\tthis.bridge = new WebWorkerBridge(this.workerUrl)\n\t\t} else {\n\t\t\tthrow new AdapterError(\n\t\t\t\t'SqliteWasmAdapter requires either a bridge (for testing) or a workerUrl (for browsers). ' +\n\t\t\t\t\t'Pass { bridge: new MockWorkerBridge() } for tests, or { workerUrl: \"/worker.js\" } for browsers.',\n\t\t\t)\n\t\t}\n\n\t\tconst ddlStatements = generateFullDDL(schema)\n\t\tconst response = await this.sendRequest({ id: 0, type: 'open', ddlStatements })\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})\n\t\t}\n\t\tthis.opened = true\n\t}\n\n\tasync close(): Promise<void> {\n\t\tif (!this.bridge) return\n\n\t\ttry {\n\t\t\tawait this.sendRequest({ id: 0, type: 'close' })\n\t\t} finally {\n\t\t\tthis.bridge.terminate()\n\t\t\tthis.bridge = null\n\t\t\tthis.opened = false\n\t\t}\n\t}\n\n\tasync execute(sql: string, params?: unknown[]): Promise<void> {\n\t\tthis.guardOpen()\n\t\tconst response = await this.sendRequest({ id: 0, type: 'execute', sql, params })\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`Execute failed: ${response.message}`, { sql, params })\n\t\t}\n\t}\n\n\tasync query<T>(sql: string, params?: unknown[]): Promise<T[]> {\n\t\tthis.guardOpen()\n\t\tconst response = await this.sendRequest({ id: 0, type: 'query', sql, params })\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`Query failed: ${response.message}`, { sql, params })\n\t\t}\n\t\treturn (response.data as T[]) ?? []\n\t}\n\n\tasync transaction(fn: (tx: Transaction) => Promise<void>): Promise<void> {\n\t\tthis.guardOpen()\n\n\t\tconst release = await this.mutex.acquire()\n\t\ttry {\n\t\t\tawait this.sendChecked({ id: 0, type: 'begin' }, 'BEGIN transaction')\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 this.sendRequest({ 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 this.sendRequest({ 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\tawait this.sendChecked({ id: 0, type: 'commit' }, 'COMMIT transaction')\n\t\t} catch (error) {\n\t\t\t// Attempt rollback, but don't mask the original 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 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\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 response = await this.sendRequest({ id: 0, type: 'export' })\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`Export failed: ${response.message}`)\n\t\t}\n\t\treturn response.data as Uint8Array\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\tconst response = await this.sendRequest({ id: 0, type: 'import', data })\n\t\tif (response.type === 'error') {\n\t\t\tthrow new AdapterError(`Import failed: ${response.message}`)\n\t\t}\n\t}\n\n\tprivate guardOpen(): void {\n\t\tif (!this.opened || !this.bridge) {\n\t\t\tthrow new StoreNotOpenError()\n\t\t}\n\t}\n\n\tprivate async sendRequest(request: WorkerRequest): Promise<WorkerResponse> {\n\t\t// guardOpen() is always called before sendRequest, so bridge is guaranteed non-null\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)\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","/// <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 * 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 = () =>\n\t\t\t\treject(new PersistenceError(`Failed to save database \"${dbName}\" to IndexedDB`, { dbName }))\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 = () =>\n\t\t\t\treject(new PersistenceError(`Failed to save dump for database \"${dbName}\"`, { dbName }))\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 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":["import_core","WebWorkerBridge"]}
@@ -0,0 +1,52 @@
1
+ import { SchemaDefinition } from '@korajs/core';
2
+ import { S as StorageAdapter, T as Transaction, M as MigrationPlan } from '../types-DF-KDSK1.cjs';
3
+ import { W as WorkerBridge } from '../sqlite-wasm-channel-Lakjuk2E.cjs';
4
+
5
+ /**
6
+ * Options for creating an IndexedDbAdapter.
7
+ */
8
+ interface IndexedDbAdapterOptions {
9
+ /**
10
+ * Database name used as the IndexedDB key for persistence.
11
+ * Defaults to 'kora-db'.
12
+ */
13
+ dbName?: string;
14
+ /**
15
+ * Injected WorkerBridge for testing. If omitted, a WebWorkerBridge is created
16
+ * in browser environments.
17
+ */
18
+ bridge?: WorkerBridge;
19
+ /**
20
+ * URL to the sqlite-wasm-worker script. Required in browsers if no bridge is provided.
21
+ */
22
+ workerUrl?: string | URL;
23
+ }
24
+ /**
25
+ * IndexedDB-backed adapter that uses SQLite WASM in-memory and serializes
26
+ * the entire database to IndexedDB after each transaction.
27
+ *
28
+ * This is the fallback adapter for browsers where OPFS is not available.
29
+ * It provides the same SQL interface as SqliteWasmAdapter, but persists by
30
+ * serializing the full SQLite database to a single IndexedDB blob.
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * const adapter = new IndexedDbAdapter({ workerUrl: '/sqlite-wasm-worker.js' })
35
+ * ```
36
+ */
37
+ declare class IndexedDbAdapter implements StorageAdapter {
38
+ private inner;
39
+ private readonly dbName;
40
+ constructor(options?: IndexedDbAdapterOptions);
41
+ open(schema: SchemaDefinition): Promise<void>;
42
+ close(): Promise<void>;
43
+ execute(sql: string, params?: unknown[]): Promise<void>;
44
+ query<T>(sql: string, params?: unknown[]): Promise<T[]>;
45
+ transaction(fn: (tx: Transaction) => Promise<void>): Promise<void>;
46
+ migrate(from: number, to: number, migration: MigrationPlan): Promise<void>;
47
+ private persistSnapshot;
48
+ private restoreFromDumpFallback;
49
+ private exportDump;
50
+ }
51
+
52
+ export { IndexedDbAdapter, type IndexedDbAdapterOptions };