@livestore/wa-sqlite 0.4.0-dev.22 → 0.4.0-dev.23

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 (62) hide show
  1. package/README.md +46 -36
  2. package/dist/README.md +13 -13
  3. package/dist/fts5/wa-sqlite.mjs +1 -1
  4. package/dist/fts5/wa-sqlite.node.mjs +1 -1
  5. package/dist/fts5/wa-sqlite.node.wasm +0 -0
  6. package/dist/fts5/wa-sqlite.wasm +0 -0
  7. package/dist/wa-sqlite-async.mjs +1 -1
  8. package/dist/wa-sqlite-async.wasm +0 -0
  9. package/dist/wa-sqlite-jspi.mjs +1 -1
  10. package/dist/wa-sqlite-jspi.wasm +0 -0
  11. package/dist/wa-sqlite.mjs +1 -1
  12. package/dist/wa-sqlite.node.mjs +1 -1
  13. package/dist/wa-sqlite.node.wasm +0 -0
  14. package/dist/wa-sqlite.wasm +0 -0
  15. package/package.json +40 -29
  16. package/src/FacadeVFS.js +252 -261
  17. package/src/VFS.js +84 -85
  18. package/src/WebLocksMixin.js +357 -351
  19. package/src/examples/AccessHandlePoolVFS.js +185 -194
  20. package/src/examples/IDBBatchAtomicVFS.js +429 -409
  21. package/src/examples/IDBMirrorVFS.js +402 -409
  22. package/src/examples/MemoryAsyncVFS.js +32 -37
  23. package/src/examples/MemoryVFS.js +71 -75
  24. package/src/examples/OPFSAdaptiveVFS.js +206 -206
  25. package/src/examples/OPFSAnyContextVFS.js +141 -140
  26. package/src/examples/OPFSCoopSyncVFS.js +297 -299
  27. package/src/examples/OPFSPermutedVFS.js +529 -540
  28. package/src/examples/README.md +27 -15
  29. package/src/examples/tag.js +27 -27
  30. package/src/sqlite-api.js +910 -941
  31. package/src/sqlite-constants.js +246 -232
  32. package/src/types/globals.d.ts +52 -52
  33. package/src/types/index.d.ts +586 -576
  34. package/test/AccessHandlePoolVFS.test.js +21 -21
  35. package/test/IDBBatchAtomicVFS.test.js +69 -69
  36. package/test/IDBMirrorVFS.test.js +21 -21
  37. package/test/MemoryAsyncVFS.test.js +21 -21
  38. package/test/MemoryVFS.test.js +21 -21
  39. package/test/OPFSAdaptiveVFS.test.js +21 -21
  40. package/test/OPFSAnyContextVFS.test.js +21 -21
  41. package/test/OPFSCoopSyncVFS.test.js +21 -21
  42. package/test/OPFSPermutedVFS.test.js +21 -21
  43. package/test/TestContext.js +44 -41
  44. package/test/WebLocksMixin.test.js +369 -360
  45. package/test/api.test.js +23 -23
  46. package/test/api_exec.js +72 -61
  47. package/test/api_misc.js +53 -54
  48. package/test/api_statements.js +271 -279
  49. package/test/callbacks.test.js +492 -478
  50. package/test/data/idbv5.json +1135 -1
  51. package/test/sql.test.js +30 -30
  52. package/test/sql_0001.js +49 -33
  53. package/test/sql_0002.js +55 -34
  54. package/test/sql_0003.js +85 -49
  55. package/test/sql_0004.js +76 -47
  56. package/test/sql_0005.js +60 -44
  57. package/test/test-worker.js +171 -163
  58. package/test/vfs_xAccess.js +1 -2
  59. package/test/vfs_xClose.js +50 -49
  60. package/test/vfs_xOpen.js +73 -72
  61. package/test/vfs_xRead.js +31 -31
  62. package/test/vfs_xWrite.js +30 -29
@@ -1,12 +1,9 @@
1
1
  // Copyright 2024 Roy T. Hashimoto. All Rights Reserved.
2
- import { FacadeVFS } from '../FacadeVFS.js';
3
- import * as VFS from '../VFS.js';
4
- import { WebLocksMixin } from '../WebLocksMixin.js';
2
+ import { FacadeVFS } from '../FacadeVFS.js'
3
+ import * as VFS from '../VFS.js'
4
+ import { WebLocksMixin } from '../WebLocksMixin.js'
5
5
 
6
- const RETRYABLE_ERRORS = new Set([
7
- 'TransactionInactiveError',
8
- 'InvalidStateError'
9
- ]);
6
+ const RETRYABLE_ERRORS = new Set(['TransactionInactiveError', 'InvalidStateError'])
10
7
 
11
8
  /**
12
9
  * @typedef Metadata
@@ -17,414 +14,419 @@ const RETRYABLE_ERRORS = new Set([
17
14
  */
18
15
 
19
16
  class File {
20
- /** @type {string} */ path;
21
- /** @type {number} */ flags;
17
+ /** @type {string} */ path
18
+ /** @type {number} */ flags
22
19
 
23
- /** @type {Metadata} */ metadata;
24
- /** @type {number} */ fileSize = 0;
20
+ /** @type {Metadata} */ metadata
21
+ /** @type {number} */ fileSize = 0
25
22
 
26
- /** @type {boolean} */ needsMetadataSync = false;
27
- /** @type {Metadata} */ rollback = null;
28
- /** @type {Set<number>} */ changedPages = new Set();
23
+ /** @type {boolean} */ needsMetadataSync = false
24
+ /** @type {Metadata} */ rollback = null
25
+ /** @type {Set<number>} */ changedPages = new Set()
29
26
 
30
- /** @type {string} */ synchronous = 'full';
31
- /** @type {IDBTransactionOptions} */ txOptions = { durability: 'strict' };
27
+ /** @type {string} */ synchronous = 'full'
28
+ /** @type {IDBTransactionOptions} */ txOptions = { durability: 'strict' }
32
29
 
33
30
  constructor(path, flags, metadata) {
34
- this.path = path;
35
- this.flags = flags;
36
- this.metadata = metadata;
31
+ this.path = path
32
+ this.flags = flags
33
+ this.metadata = metadata
37
34
  }
38
35
  }
39
36
 
40
37
  export class IDBBatchAtomicVFS extends WebLocksMixin(FacadeVFS) {
41
- /** @type {Map<number, File>} */ mapIdToFile = new Map();
42
- lastError = null;
38
+ /** @type {Map<number, File>} */ mapIdToFile = new Map()
39
+ lastError = null
43
40
 
44
- log = null; // console.log
41
+ log = null // console.log
45
42
 
46
- /** @type {Promise} */ #isReady;
47
- /** @type {IDBContext} */ #idb;
43
+ /** @type {Promise} */ #isReady
44
+ /** @type {IDBContext} */ #idb
48
45
 
49
46
  static async create(name, module, options) {
50
- const vfs = new IDBBatchAtomicVFS(name, module, options);
51
- await vfs.isReady();
52
- return vfs;
47
+ const vfs = new IDBBatchAtomicVFS(name, module, options)
48
+ await vfs.isReady()
49
+ return vfs
53
50
  }
54
51
 
55
52
  constructor(name, module, options = {}) {
56
- super(name, module, options);
57
- this.#isReady = this.#initialize(options.idbName ?? name);
53
+ super(name, module, options)
54
+ this.#isReady = this.#initialize(options.idbName ?? name)
58
55
  }
59
56
 
60
57
  async #initialize(name) {
61
- this.#idb = await IDBContext.create(name);
58
+ this.#idb = await IDBContext.create(name)
62
59
  }
63
60
 
64
61
  close() {
65
- this.#idb.close();
62
+ this.#idb.close()
66
63
  }
67
-
64
+
68
65
  async isReady() {
69
- await super.isReady();
70
- await this.#isReady;
66
+ await super.isReady()
67
+ await this.#isReady
71
68
  }
72
69
 
73
70
  getFilename(fileId) {
74
- const pathname = this.mapIdToFile.get(fileId).path;
71
+ const pathname = this.mapIdToFile.get(fileId).path
75
72
  return `IDB(${this.name}):${pathname}`
76
73
  }
77
-
74
+
78
75
  /**
79
- * @param {string?} zName
80
- * @param {number} fileId
81
- * @param {number} flags
82
- * @param {DataView} pOutFlags
76
+ * @param {string?} zName
77
+ * @param {number} fileId
78
+ * @param {number} flags
79
+ * @param {DataView} pOutFlags
83
80
  * @returns {Promise<number>}
84
81
  */
85
82
  async jOpen(zName, fileId, flags, pOutFlags) {
86
83
  try {
87
- const url = new URL(zName || Math.random().toString(36).slice(2), 'file://');
88
- const path = url.pathname;
84
+ const url = new URL(zName || Math.random().toString(36).slice(2), 'file://')
85
+ const path = url.pathname
89
86
 
90
- let meta = await this.#idb.q(({ metadata }) => metadata.get(path));
91
- if (!meta && (flags & VFS.SQLITE_OPEN_CREATE)) {
87
+ let meta = await this.#idb.q(({ metadata }) => metadata.get(path))
88
+ if (!meta && flags & VFS.SQLITE_OPEN_CREATE) {
92
89
  meta = {
93
90
  name: path,
94
91
  fileSize: 0,
95
- version: 0
96
- };
97
- await this.#idb.q(({ metadata }) => metadata.put(meta), 'rw');
92
+ version: 0,
93
+ }
94
+ await this.#idb.q(({ metadata }) => metadata.put(meta), 'rw')
98
95
  }
99
-
96
+
100
97
  if (!meta) {
101
- throw new Error(`File ${path} not found`);
98
+ throw new Error(`File ${path} not found`)
102
99
  }
103
100
 
104
- const file = new File(path, flags, meta);
105
- this.mapIdToFile.set(fileId, file);
106
- pOutFlags.setInt32(0, flags, true);
107
- return VFS.SQLITE_OK;
101
+ const file = new File(path, flags, meta)
102
+ this.mapIdToFile.set(fileId, file)
103
+ pOutFlags.setInt32(0, flags, true)
104
+ return VFS.SQLITE_OK
108
105
  } catch (e) {
109
- this.lastError = e;
110
- return VFS.SQLITE_CANTOPEN;
106
+ this.lastError = e
107
+ return VFS.SQLITE_CANTOPEN
111
108
  }
112
109
  }
113
110
 
114
111
  /**
115
- * @param {string} zName
116
- * @param {number} syncDir
112
+ * @param {string} zName
113
+ * @param {number} syncDir
117
114
  * @returns {Promise<number>}
118
115
  */
119
116
  async jDelete(zName, syncDir) {
120
117
  try {
121
- const url = new URL(zName, 'file://');
122
- const path = url.pathname;
118
+ const url = new URL(zName, 'file://')
119
+ const path = url.pathname
123
120
 
124
121
  this.#idb.q(({ metadata, blocks }) => {
125
- const range = IDBKeyRange.bound([path, -Infinity], [path, Infinity]);
126
- blocks.delete(range);
127
- metadata.delete(path);
128
- }, 'rw');
122
+ const range = IDBKeyRange.bound([path, -Infinity], [path, Infinity])
123
+ blocks.delete(range)
124
+ metadata.delete(path)
125
+ }, 'rw')
129
126
 
130
127
  if (syncDir) {
131
- await this.#idb.sync(false);
128
+ await this.#idb.sync(false)
132
129
  }
133
- return VFS.SQLITE_OK;
130
+ return VFS.SQLITE_OK
134
131
  } catch (e) {
135
- this.lastError = e;
136
- return VFS.SQLITE_IOERR_DELETE;
132
+ this.lastError = e
133
+ return VFS.SQLITE_IOERR_DELETE
137
134
  }
138
135
  }
139
136
 
140
137
  /**
141
- * @param {string} zName
142
- * @param {number} flags
143
- * @param {DataView} pResOut
138
+ * @param {string} zName
139
+ * @param {number} flags
140
+ * @param {DataView} pResOut
144
141
  * @returns {Promise<number>}
145
142
  */
146
143
  async jAccess(zName, flags, pResOut) {
147
144
  try {
148
- const url = new URL(zName, 'file://');
149
- const path = url.pathname;
145
+ const url = new URL(zName, 'file://')
146
+ const path = url.pathname
150
147
 
151
- const meta = await this.#idb.q(({ metadata }) => metadata.get(path));
152
- pResOut.setInt32(0, meta ? 1 : 0, true);
153
- return VFS.SQLITE_OK;
148
+ const meta = await this.#idb.q(({ metadata }) => metadata.get(path))
149
+ pResOut.setInt32(0, meta ? 1 : 0, true)
150
+ return VFS.SQLITE_OK
154
151
  } catch (e) {
155
- this.lastError = e;
156
- return VFS.SQLITE_IOERR_ACCESS;
157
- }
152
+ this.lastError = e
153
+ return VFS.SQLITE_IOERR_ACCESS
154
+ }
158
155
  }
159
156
 
160
157
  /**
161
- * @param {number} fileId
158
+ * @param {number} fileId
162
159
  * @returns {Promise<number>}
163
160
  */
164
161
  async jClose(fileId) {
165
162
  try {
166
- const file = this.mapIdToFile.get(fileId);
167
- this.mapIdToFile.delete(fileId);
163
+ const file = this.mapIdToFile.get(fileId)
164
+ this.mapIdToFile.delete(fileId)
168
165
 
169
166
  if (file.flags & VFS.SQLITE_OPEN_DELETEONCLOSE) {
170
167
  await this.#idb.q(({ metadata, blocks }) => {
171
- metadata.delete(file.path);
172
- blocks.delete(IDBKeyRange.bound([file.path, 0], [file.path, Infinity]));
173
- }, 'rw');
168
+ metadata.delete(file.path)
169
+ blocks.delete(IDBKeyRange.bound([file.path, 0], [file.path, Infinity]))
170
+ }, 'rw')
174
171
  }
175
172
 
176
173
  if (file.needsMetadataSync) {
177
- this.#idb.q(({ metadata }) => metadata.put(file.metadata), 'rw');
174
+ this.#idb.q(({ metadata }) => metadata.put(file.metadata), 'rw')
178
175
  }
179
- await this.#idb.sync(file.synchronous === 'full');
180
- return VFS.SQLITE_OK;
176
+ await this.#idb.sync(file.synchronous === 'full')
177
+ return VFS.SQLITE_OK
181
178
  } catch (e) {
182
- this.lastError = e;
183
- return VFS.SQLITE_IOERR_CLOSE;
179
+ this.lastError = e
180
+ return VFS.SQLITE_IOERR_CLOSE
184
181
  }
185
182
  }
186
183
 
187
184
  /**
188
- * @param {number} fileId
189
- * @param {Uint8Array} pData
185
+ * @param {number} fileId
186
+ * @param {Uint8Array} pData
190
187
  * @param {number} iOffset
191
188
  * @returns {Promise<number>}
192
189
  */
193
190
  async jRead(fileId, pData, iOffset) {
194
191
  try {
195
- const file = this.mapIdToFile.get(fileId);
192
+ const file = this.mapIdToFile.get(fileId)
196
193
 
197
- let pDataOffset = 0;
194
+ let pDataOffset = 0
198
195
  while (pDataOffset < pData.byteLength) {
199
196
  // Fetch the IndexedDB block for this file location.
200
- const fileOffset = iOffset + pDataOffset;
197
+ const fileOffset = iOffset + pDataOffset
201
198
  const block = await this.#idb.q(({ blocks }) => {
202
- const range = IDBKeyRange.bound([file.path, -fileOffset], [file.path, Infinity]);
203
- return blocks.get(range);
204
- });
205
-
199
+ const range = IDBKeyRange.bound([file.path, -fileOffset], [file.path, Infinity])
200
+ return blocks.get(range)
201
+ })
202
+
206
203
  if (!block || block.data.byteLength - block.offset <= fileOffset) {
207
- pData.fill(0, pDataOffset);
208
- return VFS.SQLITE_IOERR_SHORT_READ;
204
+ pData.fill(0, pDataOffset)
205
+ return VFS.SQLITE_IOERR_SHORT_READ
209
206
  }
210
207
 
211
208
  // Copy block data.
212
- const dst = pData.subarray(pDataOffset);
213
- const srcOffset = fileOffset + block.offset;
214
- const nBytesToCopy = Math.min(
215
- Math.max(block.data.byteLength - srcOffset, 0),
216
- dst.byteLength);
217
- dst.set(block.data.subarray(srcOffset, srcOffset + nBytesToCopy));
218
- pDataOffset += nBytesToCopy;
209
+ const dst = pData.subarray(pDataOffset)
210
+ const srcOffset = fileOffset + block.offset
211
+ const nBytesToCopy = Math.min(Math.max(block.data.byteLength - srcOffset, 0), dst.byteLength)
212
+ dst.set(block.data.subarray(srcOffset, srcOffset + nBytesToCopy))
213
+ pDataOffset += nBytesToCopy
219
214
  }
220
- return VFS.SQLITE_OK;
215
+ return VFS.SQLITE_OK
221
216
  } catch (e) {
222
- this.lastError = e;
223
- return VFS.SQLITE_IOERR_READ;
217
+ this.lastError = e
218
+ return VFS.SQLITE_IOERR_READ
224
219
  }
225
220
  }
226
221
 
227
222
  /**
228
- * @param {number} fileId
229
- * @param {Uint8Array} pData
223
+ * @param {number} fileId
224
+ * @param {Uint8Array} pData
230
225
  * @param {number} iOffset
231
226
  * @returns {number}
232
227
  */
233
228
  jWrite(fileId, pData, iOffset) {
234
229
  try {
235
- const file = this.mapIdToFile.get(fileId);
230
+ const file = this.mapIdToFile.get(fileId)
236
231
  if (file.flags & VFS.SQLITE_OPEN_MAIN_DB) {
237
232
  if (!file.rollback) {
238
233
  // Begin a new write transaction.
239
234
  // Add pendingVersion to the metadata in IndexedDB. If we crash
240
235
  // during the transaction, this lets subsequent connections
241
236
  // know to remove blocks from the failed transaction.
242
- const pending = Object.assign(
243
- { pendingVersion: file.metadata.version - 1 },
244
- file.metadata);
245
- this.#idb.q(({ metadata }) => metadata.put(pending), 'rw', file.txOptions);
237
+ const pending = Object.assign({ pendingVersion: file.metadata.version - 1 }, file.metadata)
238
+ this.#idb.q(({ metadata }) => metadata.put(pending), 'rw', file.txOptions)
246
239
 
247
- file.rollback = Object.assign({}, file.metadata);
248
- file.metadata.version--;
240
+ file.rollback = Object.assign({}, file.metadata)
241
+ file.metadata.version--
249
242
  }
250
243
  }
251
244
 
252
245
  if (file.flags & VFS.SQLITE_OPEN_MAIN_DB) {
253
- file.changedPages.add(iOffset);
246
+ file.changedPages.add(iOffset)
254
247
  }
255
248
 
256
- const data = pData.slice();
257
- const version = file.metadata.version;
258
- const isOverwrite = iOffset < file.metadata.fileSize;
259
- if (!isOverwrite ||
260
- file.flags & VFS.SQLITE_OPEN_MAIN_DB ||
261
- file.flags & VFS.SQLITE_OPEN_TEMP_DB) {
249
+ const data = pData.slice()
250
+ const version = file.metadata.version
251
+ const isOverwrite = iOffset < file.metadata.fileSize
252
+ if (!isOverwrite || file.flags & VFS.SQLITE_OPEN_MAIN_DB || file.flags & VFS.SQLITE_OPEN_TEMP_DB) {
262
253
  const block = {
263
254
  path: file.path,
264
255
  offset: -iOffset,
265
256
  version: version,
266
- data: pData.slice()
267
- };
268
- this.#idb.q(({ blocks }) => {
269
- blocks.put(block);
270
- file.changedPages.add(iOffset);
271
- }, 'rw', file.txOptions);
257
+ data: pData.slice(),
258
+ }
259
+ this.#idb.q(
260
+ ({ blocks }) => {
261
+ blocks.put(block)
262
+ file.changedPages.add(iOffset)
263
+ },
264
+ 'rw',
265
+ file.txOptions,
266
+ )
272
267
  } else {
273
- this.#idb.q(async ({ blocks }) => {
274
- // Read the existing block.
275
- const range = IDBKeyRange.bound(
276
- [file.path, -iOffset],
277
- [file.path, Infinity]);
278
- const block = await blocks.get(range);
279
-
280
- // Modify the block data.
281
- // @ts-ignore
282
- block.data.subarray(iOffset + block.offset).set(data);
283
-
284
- // Write back.
285
- blocks.put(block);
286
- }, 'rw', file.txOptions);
268
+ this.#idb.q(
269
+ async ({ blocks }) => {
270
+ // Read the existing block.
271
+ const range = IDBKeyRange.bound([file.path, -iOffset], [file.path, Infinity])
272
+ const block = await blocks.get(range)
287
273
 
274
+ // Modify the block data.
275
+ // @ts-ignore
276
+ block.data.subarray(iOffset + block.offset).set(data)
277
+
278
+ // Write back.
279
+ blocks.put(block)
280
+ },
281
+ 'rw',
282
+ file.txOptions,
283
+ )
288
284
  }
289
285
 
290
286
  if (file.metadata.fileSize < iOffset + pData.length) {
291
- file.metadata.fileSize = iOffset + pData.length;
292
- file.needsMetadataSync = true;
287
+ file.metadata.fileSize = iOffset + pData.length
288
+ file.needsMetadataSync = true
293
289
  }
294
- return VFS.SQLITE_OK;
290
+ return VFS.SQLITE_OK
295
291
  } catch (e) {
296
- this.lastError = e;
297
- return VFS.SQLITE_IOERR_WRITE;
292
+ this.lastError = e
293
+ return VFS.SQLITE_IOERR_WRITE
298
294
  }
299
295
  }
300
296
 
301
297
  /**
302
- * @param {number} fileId
303
- * @param {number} iSize
298
+ * @param {number} fileId
299
+ * @param {number} iSize
304
300
  * @returns {number}
305
301
  */
306
302
  jTruncate(fileId, iSize) {
307
303
  try {
308
- const file = this.mapIdToFile.get(fileId);
304
+ const file = this.mapIdToFile.get(fileId)
309
305
  if (iSize < file.metadata.fileSize) {
310
- this.#idb.q(({ blocks }) => {
311
- const range = IDBKeyRange.bound(
312
- [file.path, -Infinity],
313
- [file.path, -iSize, Infinity]);
314
- blocks.delete(range);
315
- }, 'rw', file.txOptions);
316
- file.metadata.fileSize = iSize;
317
- file.needsMetadataSync = true;
306
+ this.#idb.q(
307
+ ({ blocks }) => {
308
+ const range = IDBKeyRange.bound([file.path, -Infinity], [file.path, -iSize, Infinity])
309
+ blocks.delete(range)
310
+ },
311
+ 'rw',
312
+ file.txOptions,
313
+ )
314
+ file.metadata.fileSize = iSize
315
+ file.needsMetadataSync = true
318
316
  }
319
- return VFS.SQLITE_OK;
317
+ return VFS.SQLITE_OK
320
318
  } catch (e) {
321
- this.lastError = e;
322
- return VFS.SQLITE_IOERR_TRUNCATE;
319
+ this.lastError = e
320
+ return VFS.SQLITE_IOERR_TRUNCATE
323
321
  }
324
322
  }
325
323
 
326
324
  /**
327
- * @param {number} fileId
328
- * @param {number} flags
325
+ * @param {number} fileId
326
+ * @param {number} flags
329
327
  * @returns {Promise<number>}
330
328
  */
331
329
  async jSync(fileId, flags) {
332
330
  try {
333
- const file = this.mapIdToFile.get(fileId);
331
+ const file = this.mapIdToFile.get(fileId)
334
332
  if (file.needsMetadataSync) {
335
- this.#idb.q(({ metadata }) => metadata.put(file.metadata), 'rw', file.txOptions);
336
- file.needsMetadataSync = false;
333
+ this.#idb.q(({ metadata }) => metadata.put(file.metadata), 'rw', file.txOptions)
334
+ file.needsMetadataSync = false
337
335
  }
338
336
 
339
337
  if (file.flags & VFS.SQLITE_OPEN_MAIN_DB) {
340
338
  // Sync is only needed here for durability. Visibility for other
341
339
  // connections is ensured in jUnlock().
342
340
  if (file.synchronous === 'full') {
343
- await this.#idb.sync(true);
341
+ await this.#idb.sync(true)
344
342
  }
345
343
  } else {
346
- await this.#idb.sync(file.synchronous === 'full');
344
+ await this.#idb.sync(file.synchronous === 'full')
347
345
  }
348
- return VFS.SQLITE_OK;
346
+ return VFS.SQLITE_OK
349
347
  } catch (e) {
350
- this.lastError = e;
351
- return VFS.SQLITE_IOERR_FSYNC;
348
+ this.lastError = e
349
+ return VFS.SQLITE_IOERR_FSYNC
352
350
  }
353
351
  }
354
352
 
355
353
  /**
356
- * @param {number} fileId
357
- * @param {DataView} pSize64
354
+ * @param {number} fileId
355
+ * @param {DataView} pSize64
358
356
  * @returns {number}
359
357
  */
360
358
  jFileSize(fileId, pSize64) {
361
359
  try {
362
- const file = this.mapIdToFile.get(fileId);
363
- pSize64.setBigInt64(0, BigInt(file.metadata.fileSize), true);
364
- return VFS.SQLITE_OK;
360
+ const file = this.mapIdToFile.get(fileId)
361
+ pSize64.setBigInt64(0, BigInt(file.metadata.fileSize), true)
362
+ return VFS.SQLITE_OK
365
363
  } catch (e) {
366
- this.lastError = e;
367
- return VFS.SQLITE_IOERR_FSTAT;
364
+ this.lastError = e
365
+ return VFS.SQLITE_IOERR_FSTAT
368
366
  }
369
367
  }
370
368
 
371
369
  /**
372
- * @param {number} fileId
373
- * @param {number} lockType
370
+ * @param {number} fileId
371
+ * @param {number} lockType
374
372
  * @returns {Promise<number>}
375
373
  */
376
374
  async jLock(fileId, lockType) {
377
375
  // Call the actual lock implementation.
378
- const file = this.mapIdToFile.get(fileId);
379
- const result = await super.jLock(fileId, lockType);
376
+ const file = this.mapIdToFile.get(fileId)
377
+ const result = await super.jLock(fileId, lockType)
380
378
 
381
379
  if (lockType === VFS.SQLITE_LOCK_SHARED) {
382
380
  // Update metadata.
383
- file.metadata = await this.#idb.q(async ({ metadata, blocks }) => {
384
- // @ts-ignore
385
- /** @type {Metadata} */ const m = await metadata.get(file.path);
386
- if (m.pendingVersion) {
387
- console.warn(`removing failed transaction ${m.pendingVersion}`);
388
- await new Promise((resolve, reject) => {
389
- const range = IDBKeyRange.bound([m.name, -Infinity], [m.name, Infinity]);
390
- const request = blocks.openCursor(range);
391
- request.onsuccess = () => {
392
- const cursor = request.result;
393
- if (cursor) {
394
- const block = cursor.value;
395
- if (block.version < m.version) {
396
- cursor.delete();
381
+ file.metadata = await this.#idb.q(
382
+ async ({ metadata, blocks }) => {
383
+ // @ts-ignore
384
+ /** @type {Metadata} */ const m = await metadata.get(file.path)
385
+ if (m.pendingVersion) {
386
+ console.warn(`removing failed transaction ${m.pendingVersion}`)
387
+ await new Promise((resolve, reject) => {
388
+ const range = IDBKeyRange.bound([m.name, -Infinity], [m.name, Infinity])
389
+ const request = blocks.openCursor(range)
390
+ request.onsuccess = () => {
391
+ const cursor = request.result
392
+ if (cursor) {
393
+ const block = cursor.value
394
+ if (block.version < m.version) {
395
+ cursor.delete()
396
+ }
397
+ cursor.continue()
398
+ } else {
399
+ resolve()
397
400
  }
398
- cursor.continue();
399
- } else {
400
- resolve();
401
401
  }
402
- };
403
- request.onerror = () => reject(request.error);
404
- })
402
+ request.onerror = () => reject(request.error)
403
+ })
405
404
 
406
- delete m.pendingVersion;
407
- metadata.put(m);
408
- }
409
- return m;
410
- }, 'rw', file.txOptions);
405
+ delete m.pendingVersion
406
+ metadata.put(m)
407
+ }
408
+ return m
409
+ },
410
+ 'rw',
411
+ file.txOptions,
412
+ )
411
413
  }
412
- return result;
414
+ return result
413
415
  }
414
416
 
415
417
  /**
416
- * @param {number} fileId
417
- * @param {number} lockType
418
+ * @param {number} fileId
419
+ * @param {number} lockType
418
420
  * @returns {Promise<number>}
419
421
  */
420
422
  async jUnlock(fileId, lockType) {
421
423
  if (lockType === VFS.SQLITE_LOCK_NONE) {
422
- const file = this.mapIdToFile.get(fileId);
423
- await this.#idb.sync(file.synchronous === 'full');
424
+ const file = this.mapIdToFile.get(fileId)
425
+ await this.#idb.sync(file.synchronous === 'full')
424
426
  }
425
427
 
426
428
  // Call the actual unlock implementation.
427
- return super.jUnlock(fileId, lockType);
429
+ return super.jUnlock(fileId, lockType)
428
430
  }
429
431
 
430
432
  /**
@@ -435,267 +437,282 @@ export class IDBBatchAtomicVFS extends WebLocksMixin(FacadeVFS) {
435
437
  */
436
438
  jFileControl(fileId, op, pArg) {
437
439
  try {
438
- const file = this.mapIdToFile.get(fileId);
440
+ const file = this.mapIdToFile.get(fileId)
439
441
  switch (op) {
440
442
  case VFS.SQLITE_FCNTL_PRAGMA:
441
- const key = extractString(pArg, 4);
442
- const value = extractString(pArg, 8);
443
- this.log?.('xFileControl', file.path, 'PRAGMA', key, value);
444
- const setPragmaResponse = response => {
445
- const encoded = new TextEncoder().encode(response);
446
- const out = this._module._sqlite3_malloc(encoded.byteLength);
447
- const outArray = this._module.HEAPU8.subarray(out, out + encoded.byteLength);
448
- outArray.set(encoded);
449
- pArg.setUint32(0, out, true);
450
- return VFS.SQLITE_ERROR;
451
- };
443
+ const key = extractString(pArg, 4)
444
+ const value = extractString(pArg, 8)
445
+ this.log?.('xFileControl', file.path, 'PRAGMA', key, value)
446
+ const setPragmaResponse = (response) => {
447
+ const encoded = new TextEncoder().encode(response)
448
+ const out = this._module._sqlite3_malloc(encoded.byteLength)
449
+ const outArray = this._module.HEAPU8.subarray(out, out + encoded.byteLength)
450
+ outArray.set(encoded)
451
+ pArg.setUint32(0, out, true)
452
+ return VFS.SQLITE_ERROR
453
+ }
452
454
  switch (key.toLowerCase()) {
453
455
  case 'page_size':
454
456
  if (file.flags & VFS.SQLITE_OPEN_MAIN_DB) {
455
457
  // Don't allow changing the page size.
456
458
  if (value && file.metadata.fileSize) {
457
- return VFS.SQLITE_ERROR;
459
+ return VFS.SQLITE_ERROR
458
460
  }
459
461
  }
460
- break;
462
+ break
461
463
  case 'synchronous':
462
464
  if (value) {
463
465
  switch (value.toLowerCase()) {
464
466
  case '0':
465
467
  case 'off':
466
- file.synchronous = 'off';
467
- file.txOptions = { durability: 'relaxed' };
468
- break;
468
+ file.synchronous = 'off'
469
+ file.txOptions = { durability: 'relaxed' }
470
+ break
469
471
  case '1':
470
472
  case 'normal':
471
- file.synchronous = 'normal';
472
- file.txOptions = { durability: 'relaxed' };
473
- break;
473
+ file.synchronous = 'normal'
474
+ file.txOptions = { durability: 'relaxed' }
475
+ break
474
476
  case '2':
475
477
  case '3':
476
478
  case 'full':
477
479
  case 'extra':
478
- file.synchronous = 'full';
479
- file.txOptions = { durability: 'strict' };
480
- break;
480
+ file.synchronous = 'full'
481
+ file.txOptions = { durability: 'strict' }
482
+ break
481
483
  }
482
484
  }
483
- break;
485
+ break
484
486
  case 'write_hint':
485
- return super.jFileControl(fileId, WebLocksMixin.WRITE_HINT_OP_CODE, null);
486
- }
487
- break;
487
+ return super.jFileControl(fileId, WebLocksMixin.WRITE_HINT_OP_CODE, null)
488
+ }
489
+ break
488
490
  case VFS.SQLITE_FCNTL_SYNC:
489
- this.log?.('xFileControl', file.path, 'SYNC');
491
+ this.log?.('xFileControl', file.path, 'SYNC')
490
492
  if (file.rollback) {
491
- const commitMetadata = Object.assign({}, file.metadata);
493
+ const commitMetadata = Object.assign({}, file.metadata)
492
494
  const prevFileSize = file.rollback.fileSize
493
- this.#idb.q(({ metadata, blocks }) => {
494
- metadata.put(commitMetadata);
495
-
496
- // Remove old page versions.
497
- for (const offset of file.changedPages) {
498
- if (offset < prevFileSize) {
499
- const range = IDBKeyRange.bound(
500
- [file.path, -offset, commitMetadata.version],
501
- [file.path, -offset, Infinity],
502
- true);
503
- blocks.delete(range);
495
+ this.#idb.q(
496
+ ({ metadata, blocks }) => {
497
+ metadata.put(commitMetadata)
498
+
499
+ // Remove old page versions.
500
+ for (const offset of file.changedPages) {
501
+ if (offset < prevFileSize) {
502
+ const range = IDBKeyRange.bound(
503
+ [file.path, -offset, commitMetadata.version],
504
+ [file.path, -offset, Infinity],
505
+ true,
506
+ )
507
+ blocks.delete(range)
508
+ }
504
509
  }
505
- }
506
- file.changedPages.clear();
507
- }, 'rw', file.txOptions);
508
- file.needsMetadataSync = false;
509
- file.rollback = null;
510
+ file.changedPages.clear()
511
+ },
512
+ 'rw',
513
+ file.txOptions,
514
+ )
515
+ file.needsMetadataSync = false
516
+ file.rollback = null
510
517
  }
511
- break;
518
+ break
512
519
  case VFS.SQLITE_FCNTL_BEGIN_ATOMIC_WRITE:
513
520
  // Every write transaction is atomic, so this is a no-op.
514
- this.log?.('xFileControl', file.path, 'BEGIN_ATOMIC_WRITE');
515
- return VFS.SQLITE_OK;
521
+ this.log?.('xFileControl', file.path, 'BEGIN_ATOMIC_WRITE')
522
+ return VFS.SQLITE_OK
516
523
  case VFS.SQLITE_FCNTL_COMMIT_ATOMIC_WRITE:
517
524
  // Every write transaction is atomic, so this is a no-op.
518
- this.log?.('xFileControl', file.path, 'COMMIT_ATOMIC_WRITE');
519
- return VFS.SQLITE_OK;
525
+ this.log?.('xFileControl', file.path, 'COMMIT_ATOMIC_WRITE')
526
+ return VFS.SQLITE_OK
520
527
  case VFS.SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE:
521
- this.log?.('xFileControl', file.path, 'ROLLBACK_ATOMIC_WRITE');
522
- file.metadata = file.rollback;
523
- const rollbackMetadata = Object.assign({}, file.metadata);
524
- this.#idb.q(({ metadata, blocks }) => {
525
- metadata.put(rollbackMetadata);
526
-
527
- // Remove pages.
528
- for (const offset of file.changedPages) {
529
- blocks.delete([file.path, -offset, rollbackMetadata.version - 1]);
530
- }
531
- file.changedPages.clear();
532
- }, 'rw', file.txOptions);
533
- file.needsMetadataSync = false;
534
- file.rollback = null;
535
- return VFS.SQLITE_OK;
528
+ this.log?.('xFileControl', file.path, 'ROLLBACK_ATOMIC_WRITE')
529
+ file.metadata = file.rollback
530
+ const rollbackMetadata = Object.assign({}, file.metadata)
531
+ this.#idb.q(
532
+ ({ metadata, blocks }) => {
533
+ metadata.put(rollbackMetadata)
534
+
535
+ // Remove pages.
536
+ for (const offset of file.changedPages) {
537
+ blocks.delete([file.path, -offset, rollbackMetadata.version - 1])
538
+ }
539
+ file.changedPages.clear()
540
+ },
541
+ 'rw',
542
+ file.txOptions,
543
+ )
544
+ file.needsMetadataSync = false
545
+ file.rollback = null
546
+ return VFS.SQLITE_OK
536
547
  }
537
548
  } catch (e) {
538
- this.lastError = e;
539
- return VFS.SQLITE_IOERR;
549
+ this.lastError = e
550
+ return VFS.SQLITE_IOERR
540
551
  }
541
- return super.jFileControl(fileId, op, pArg);
552
+ return super.jFileControl(fileId, op, pArg)
542
553
  }
543
-
554
+
544
555
  /**
545
556
  * @param {number} pFile
546
557
  * @returns {number|Promise<number>}
547
558
  */
548
559
  jDeviceCharacteristics(pFile) {
549
- return 0
550
- | VFS.SQLITE_IOCAP_BATCH_ATOMIC
551
- | VFS.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
560
+ return 0 | VFS.SQLITE_IOCAP_BATCH_ATOMIC | VFS.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
552
561
  }
553
562
 
554
563
  /**
555
- * @param {Uint8Array} zBuf
564
+ * @param {Uint8Array} zBuf
556
565
  * @returns {number|Promise<number>}
557
566
  */
558
567
  jGetLastError(zBuf) {
559
568
  if (this.lastError) {
560
- console.error(this.lastError);
561
- const outputArray = zBuf.subarray(0, zBuf.byteLength - 1);
562
- const { written } = new TextEncoder().encodeInto(this.lastError.message, outputArray);
563
- zBuf[written] = 0;
569
+ console.error(this.lastError)
570
+ const outputArray = zBuf.subarray(0, zBuf.byteLength - 1)
571
+ const { written } = new TextEncoder().encodeInto(this.lastError.message, outputArray)
572
+ zBuf[written] = 0
564
573
  }
565
574
  return VFS.SQLITE_OK
566
575
  }
567
576
  }
568
577
 
569
578
  function extractString(dataView, offset) {
570
- const p = dataView.getUint32(offset, true);
579
+ const p = dataView.getUint32(offset, true)
571
580
  if (p) {
572
- const chars = new Uint8Array(dataView.buffer, p);
573
- return new TextDecoder().decode(chars.subarray(0, chars.indexOf(0)));
581
+ const chars = new Uint8Array(dataView.buffer, p)
582
+ return new TextDecoder().decode(chars.subarray(0, chars.indexOf(0)))
574
583
  }
575
- return null;
584
+ return null
576
585
  }
577
586
 
578
587
  export class IDBContext {
579
- /** @type {IDBDatabase} */ #database;
588
+ /** @type {IDBDatabase} */ #database
589
+
590
+ /** @type {Promise} */ #chain = null
591
+ /** @type {Promise<any>} */ #txComplete = Promise.resolve()
592
+ /** @type {IDBRequest?} */ #request = null
593
+ /** @type {WeakSet<IDBTransaction>} */ #txPending = new WeakSet()
580
594
 
581
- /** @type {Promise} */ #chain = null;
582
- /** @type {Promise<any>} */ #txComplete = Promise.resolve();
583
- /** @type {IDBRequest?} */ #request = null;
584
- /** @type {WeakSet<IDBTransaction>} */ #txPending = new WeakSet();
585
-
586
- log = null;
595
+ log = null
587
596
 
588
597
  static async create(name) {
589
598
  const database = await new Promise((resolve, reject) => {
590
- const request = indexedDB.open(name, 6);
591
- request.onupgradeneeded = async event => {
592
- const db = request.result;
599
+ const request = indexedDB.open(name, 6)
600
+ request.onupgradeneeded = async (event) => {
601
+ const db = request.result
593
602
  if (event.oldVersion) {
594
- console.log(`Upgrading IndexedDB from version ${event.oldVersion}`);
603
+ console.log(`Upgrading IndexedDB from version ${event.oldVersion}`)
595
604
  }
596
605
  switch (event.oldVersion) {
597
606
  case 0:
598
607
  // Start with the original schema.
599
- db.createObjectStore('blocks', { keyPath: ['path', 'offset', 'version']})
600
- .createIndex('version', ['path', 'version']);
601
- // fall through intentionally
608
+ db.createObjectStore('blocks', { keyPath: ['path', 'offset', 'version'] }).createIndex('version', [
609
+ 'path',
610
+ 'version',
611
+ ])
612
+ // fall through intentionally
602
613
  case 5:
603
- const tx = request.transaction;
604
- const blocks = tx.objectStore('blocks');
605
- blocks.deleteIndex('version');
606
- const metadata = db.createObjectStore('metadata', { keyPath: 'name' });
614
+ const tx = request.transaction
615
+ const blocks = tx.objectStore('blocks')
616
+ blocks.deleteIndex('version')
617
+ const metadata = db.createObjectStore('metadata', { keyPath: 'name' })
607
618
 
608
619
  await new Promise((resolve, reject) => {
609
620
  // Iterate over all the blocks.
610
- let lastBlock = {};
611
- const request = tx.objectStore('blocks').openCursor();
621
+ let lastBlock = {}
622
+ const request = tx.objectStore('blocks').openCursor()
612
623
  request.onsuccess = () => {
613
- const cursor = request.result;
624
+ const cursor = request.result
614
625
  if (cursor) {
615
- const block = cursor.value;
616
- if (typeof block.offset !== 'number' ||
617
- (block.path === lastBlock.path && block.offset === lastBlock.offset)) {
626
+ const block = cursor.value
627
+ if (
628
+ typeof block.offset !== 'number' ||
629
+ (block.path === lastBlock.path && block.offset === lastBlock.offset)
630
+ ) {
618
631
  // Remove superceded block (or the "purge" info).
619
- cursor.delete();
632
+ cursor.delete()
620
633
  } else if (block.offset === 0) {
621
634
  // Move metadata to its own store.
622
635
  metadata.put({
623
636
  name: block.path,
624
637
  fileSize: block.fileSize,
625
- version: block.version
626
- });
638
+ version: block.version,
639
+ })
627
640
 
628
- delete block.fileSize;
629
- cursor.update(block);
641
+ delete block.fileSize
642
+ cursor.update(block)
630
643
  }
631
- lastBlock = block;
632
- cursor.continue();
644
+ lastBlock = block
645
+ cursor.continue()
633
646
  } else {
634
- resolve();
647
+ resolve()
635
648
  }
636
- };
637
- request.onerror = () => reject(request.error);
638
- });
639
- break;
649
+ }
650
+ request.onerror = () => reject(request.error)
651
+ })
652
+ break
640
653
  }
641
- };
642
- request.onsuccess = () => resolve(request.result);
643
- request.onerror = () => reject(request.error);
644
- });
645
- return new IDBContext(database);
654
+ }
655
+ request.onsuccess = () => resolve(request.result)
656
+ request.onerror = () => reject(request.error)
657
+ })
658
+ return new IDBContext(database)
646
659
  }
647
660
 
648
661
  constructor(database) {
649
- this.#database = database;
662
+ this.#database = database
650
663
  }
651
664
 
652
665
  close() {
653
- this.#database.close();
666
+ this.#database.close()
654
667
  }
655
668
 
656
669
  /**
657
- * @param {(stores: Object.<string, IDBObjectStore>) => any} f
658
- * @param {'ro'|'rw'} mode
670
+ * @param {(stores: Object.<string, IDBObjectStore>) => any} f
671
+ * @param {'ro'|'rw'} mode
659
672
  * @returns {Promise<any>}
660
673
  */
661
674
  q(f, mode = 'ro', options = {}) {
662
675
  /** @type {IDBTransactionMode} */
663
- const txMode = mode === 'ro' ? 'readonly' : 'readwrite';
664
- const txOptions = Object.assign({
665
- /** @type {IDBTransactionDurability} */ durability: 'default'
666
- }, options);
676
+ const txMode = mode === 'ro' ? 'readonly' : 'readwrite'
677
+ const txOptions = Object.assign(
678
+ {
679
+ /** @type {IDBTransactionDurability} */ durability: 'default',
680
+ },
681
+ options,
682
+ )
667
683
 
668
684
  // Ensure that queries run sequentially. If any function rejects,
669
685
  // or any request has an error, or the transaction does not commit,
670
686
  // then no subsequent functions will run until sync() or reset().
671
- this.#chain = (this.#chain || Promise.resolve())
672
- .then(() => this.#q(f, txMode, txOptions));
673
- return this.#chain;
687
+ this.#chain = (this.#chain || Promise.resolve()).then(() => this.#q(f, txMode, txOptions))
688
+ return this.#chain
674
689
  }
675
690
 
676
691
  /**
677
- * @param {(stores: Object.<string, IDBObjectStore>) => any} f
678
- * @param {IDBTransactionMode} mode
692
+ * @param {(stores: Object.<string, IDBObjectStore>) => any} f
693
+ * @param {IDBTransactionMode} mode
679
694
  * @param {IDBTransactionOptions} options
680
695
  * @returns {Promise<any>}
681
696
  */
682
697
  async #q(f, mode, options) {
683
- /** @type {IDBTransaction} */ let tx;
684
- if (this.#request &&
685
- this.#txPending.has(this.#request.transaction) &&
686
- this.#request.transaction.mode >= mode &&
687
- this.#request.transaction.durability === options.durability) {
698
+ /** @type {IDBTransaction} */ let tx
699
+ if (
700
+ this.#request &&
701
+ this.#txPending.has(this.#request.transaction) &&
702
+ this.#request.transaction.mode >= mode &&
703
+ this.#request.transaction.durability === options.durability
704
+ ) {
688
705
  // The previous request transaction is compatible and has
689
706
  // not yet completed.
690
- tx = this.#request.transaction;
707
+ tx = this.#request.transaction
691
708
 
692
709
  // If the previous request is pending, wait for it to complete.
693
710
  // This ensures that the transaction will be active.
694
711
  if (this.#request.readyState === 'pending') {
695
- await new Promise(resolve => {
696
- this.#request.addEventListener('success', resolve, { once: true });
697
- this.#request.addEventListener('error', resolve, { once: true });
698
- });
712
+ await new Promise((resolve) => {
713
+ this.#request.addEventListener('success', resolve, { once: true })
714
+ this.#request.addEventListener('error', resolve, { once: true })
715
+ })
699
716
  }
700
717
  }
701
718
 
@@ -704,45 +721,45 @@ export class IDBContext {
704
721
  // The current transaction is missing or doesn't match so
705
722
  // replace it with a new one. wait for the previous
706
723
  // transaction to complete so the lifetimes do not overlap.
707
- await this.#txComplete;
724
+ await this.#txComplete
708
725
 
709
726
  // Create the new transaction.
710
727
  // @ts-ignore
711
- tx = this.#database.transaction(this.#database.objectStoreNames, mode, options);
712
- this.log?.('IDBTransaction open', mode);
713
- this.#txPending.add(tx);
728
+ tx = this.#database.transaction(this.#database.objectStoreNames, mode, options)
729
+ this.log?.('IDBTransaction open', mode)
730
+ this.#txPending.add(tx)
714
731
  this.#txComplete = new Promise((resolve, reject) => {
715
732
  tx.addEventListener('complete', () => {
716
- this.log?.('IDBTransaction complete');
717
- this.#txPending.delete(tx);
718
- resolve();
719
- });
733
+ this.log?.('IDBTransaction complete')
734
+ this.#txPending.delete(tx)
735
+ resolve()
736
+ })
720
737
  tx.addEventListener('abort', () => {
721
- this.#txPending.delete(tx);
722
- reject(new Error('transaction aborted'));
723
- });
724
- });
738
+ this.#txPending.delete(tx)
739
+ reject(new Error('transaction aborted'))
740
+ })
741
+ })
725
742
  }
726
743
 
727
744
  try {
728
745
  // @ts-ignore
729
746
  // Create object store proxies.
730
- const objectStores = [...tx.objectStoreNames].map(name => {
731
- return [name, this.proxyStoreOrIndex(tx.objectStore(name))];
732
- });
747
+ const objectStores = [...tx.objectStoreNames].map((name) => {
748
+ return [name, this.proxyStoreOrIndex(tx.objectStore(name))]
749
+ })
733
750
 
734
751
  // Execute the function.
735
- return await f(Object.fromEntries(objectStores));
752
+ return await f(Object.fromEntries(objectStores))
736
753
  } catch (e) {
737
754
  // Use a new transaction if this one was inactive. This will
738
755
  // happen if the last request in the transaction completed
739
756
  // in a previous task but the transaction has not yet committed.
740
757
  if (!i && RETRYABLE_ERRORS.has(e.name)) {
741
- this.log?.(`${e.name}, retrying`);
742
- tx = null;
743
- continue;
758
+ this.log?.(`${e.name}, retrying`)
759
+ tx = null
760
+ continue
744
761
  }
745
- throw e;
762
+ throw e
746
763
  }
747
764
  }
748
765
  }
@@ -751,16 +768,16 @@ export class IDBContext {
751
768
  * Object store methods that return an IDBRequest, except for cursor
752
769
  * creation, are wrapped to return a Promise. In addition, the
753
770
  * request is used internally for chaining.
754
- * @param {IDBObjectStore} objectStore
755
- * @returns
771
+ * @param {IDBObjectStore} objectStore
772
+ * @returns
756
773
  */
757
774
  proxyStoreOrIndex(objectStore) {
758
775
  return new Proxy(objectStore, {
759
776
  get: (target, property, receiver) => {
760
- const result = Reflect.get(target, property, receiver);
777
+ const result = Reflect.get(target, property, receiver)
761
778
  if (typeof result === 'function') {
762
779
  return (...args) => {
763
- const maybeRequest = Reflect.apply(result, target, args);
780
+ const maybeRequest = Reflect.apply(result, target, args)
764
781
  // @ts-ignore
765
782
  if (maybeRequest instanceof IDBRequest && !property.endsWith('Cursor')) {
766
783
  // // Debug logging.
@@ -771,57 +788,60 @@ export class IDBContext {
771
788
  // maybeRequest.addEventListener('error', () => {
772
789
  // this.log?.(`${target.name}.${String(property)} error`, maybeRequest.error);
773
790
  // });
774
-
791
+
775
792
  // Save the request.
776
- this.#request = maybeRequest;
793
+ this.#request = maybeRequest
777
794
 
778
795
  // Abort the transaction on error.
779
- maybeRequest.addEventListener('error', () => {
780
- console.error(maybeRequest.error);
781
- maybeRequest.transaction.abort();
782
- }, { once: true });
796
+ maybeRequest.addEventListener(
797
+ 'error',
798
+ () => {
799
+ console.error(maybeRequest.error)
800
+ maybeRequest.transaction.abort()
801
+ },
802
+ { once: true },
803
+ )
783
804
 
784
805
  // Return a Promise.
785
- return wrap(maybeRequest);
806
+ return wrap(maybeRequest)
786
807
  }
787
- return maybeRequest;
808
+ return maybeRequest
788
809
  }
789
810
  }
790
- return result;
791
- }
792
- });
811
+ return result
812
+ },
813
+ })
793
814
  }
794
815
 
795
816
  /**
796
- * @param {boolean} durable
817
+ * @param {boolean} durable
797
818
  */
798
819
  async sync(durable) {
799
820
  if (this.#chain) {
800
821
  // This waits for all IndexedDB calls to be made.
801
- await this.#chain;
822
+ await this.#chain
802
823
  if (durable) {
803
824
  // This waits for the final transaction to commit.
804
- await this.#txComplete;
825
+ await this.#txComplete
805
826
  }
806
- this.reset();
827
+ this.reset()
807
828
  }
808
829
  }
809
830
 
810
831
  reset() {
811
- this.#chain = null;
812
- this.#txComplete = Promise.resolve();
813
- this.#request = null;
832
+ this.#chain = null
833
+ this.#txComplete = Promise.resolve()
834
+ this.#request = null
814
835
  }
815
836
  }
816
837
 
817
838
  /**
818
- * @param {IDBRequest} request
839
+ * @param {IDBRequest} request
819
840
  * @returns {Promise}
820
841
  */
821
842
  function wrap(request) {
822
843
  return new Promise((resolve, reject) => {
823
- request.onsuccess = () => resolve(request.result);
824
- request.onerror = () => reject(request.error);
825
- });
844
+ request.onsuccess = () => resolve(request.result)
845
+ request.onerror = () => reject(request.error)
846
+ })
826
847
  }
827
-