@livestore/wa-sqlite 0.4.0-dev.21 → 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,35 +1,34 @@
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 LOCK_NOTIFY_INTERVAL = 1000;
6
+ const LOCK_NOTIFY_INTERVAL = 1000
7
7
 
8
- const hasUnsafeAccessHandle =
9
- globalThis.FileSystemSyncAccessHandle.prototype.hasOwnProperty('mode');
8
+ const hasUnsafeAccessHandle = globalThis.FileSystemSyncAccessHandle.prototype.hasOwnProperty('mode')
10
9
 
11
10
  /**
12
- * @param {string} pathname
13
- * @param {boolean} create
11
+ * @param {string} pathname
12
+ * @param {boolean} create
14
13
  * @returns {Promise<[FileSystemDirectoryHandle, string]>}
15
14
  */
16
15
  async function getPathComponents(pathname, create) {
17
- const [_, directories, filename] = pathname.match(/[/]?(.*)[/](.*)$/);
16
+ const [_, directories, filename] = pathname.match(/[/]?(.*)[/](.*)$/)
18
17
 
19
- let directoryHandle = await navigator.storage.getDirectory();
18
+ let directoryHandle = await navigator.storage.getDirectory()
20
19
  for (const directory of directories.split('/')) {
21
20
  if (directory) {
22
- directoryHandle = await directoryHandle.getDirectoryHandle(directory, { create });
21
+ directoryHandle = await directoryHandle.getDirectoryHandle(directory, { create })
23
22
  }
24
23
  }
25
- return [directoryHandle, filename];
26
- };
24
+ return [directoryHandle, filename]
25
+ }
27
26
 
28
27
  class File {
29
- /** @type {string} */ pathname;
30
- /** @type {number} */ flags;
31
- /** @type {FileSystemFileHandle} */ fileHandle;
32
- /** @type {FileSystemSyncAccessHandle} */ accessHandle;
28
+ /** @type {string} */ pathname
29
+ /** @type {number} */ flags
30
+ /** @type {FileSystemFileHandle} */ fileHandle
31
+ /** @type {FileSystemSyncAccessHandle} */ accessHandle
33
32
 
34
33
  // The rest of the properties are for platforms without readwrite-unsafe
35
34
  // access handles. Only one connection can have an open access handle
@@ -37,349 +36,348 @@ class File {
37
36
  //
38
37
  // Opening and closing the access handle is expensive so we leave the
39
38
  // handle open unless another connection signals on BroadcastChannel.
40
- /** @type {BroadcastChannel} */ handleRequestChannel;
41
- /** @type {function} */ handleLockReleaser = null;
42
- /** @type {boolean} */ isHandleRequested = false;
43
- /** @type {boolean} */ isFileLocked = false;
39
+ /** @type {BroadcastChannel} */ handleRequestChannel
40
+ /** @type {function} */ handleLockReleaser = null
41
+ /** @type {boolean} */ isHandleRequested = false
42
+ /** @type {boolean} */ isFileLocked = false
44
43
 
45
44
  // SQLite makes one read on file open that is not protected by a lock.
46
45
  // This needs to be handled as a special case.
47
- /** @type {function} */ openLockReleaser = null;
46
+ /** @type {function} */ openLockReleaser = null
48
47
 
49
48
  constructor(pathname, flags) {
50
- this.pathname = pathname;
51
- this.flags = flags;
49
+ this.pathname = pathname
50
+ this.flags = flags
52
51
  }
53
52
  }
54
53
 
55
54
  export class OPFSAdaptiveVFS extends WebLocksMixin(FacadeVFS) {
56
- /** @type {Map<number, File>} */ mapIdToFile = new Map();
57
- lastError = null;
55
+ /** @type {Map<number, File>} */ mapIdToFile = new Map()
56
+ lastError = null
58
57
 
59
- log = null;
58
+ log = null
60
59
 
61
60
  static async create(name, module, options) {
62
- const vfs = new OPFSAdaptiveVFS(name, module, options);
63
- await vfs.isReady();
64
- return vfs;
61
+ const vfs = new OPFSAdaptiveVFS(name, module, options)
62
+ await vfs.isReady()
63
+ return vfs
65
64
  }
66
65
 
67
66
  constructor(name, module, options = {}) {
68
- super(name, module, options);
67
+ super(name, module, options)
69
68
  }
70
-
69
+
71
70
  getFilename(fileId) {
72
- const pathname = this.mapIdToFile.get(fileId).pathname;
71
+ const pathname = this.mapIdToFile.get(fileId).pathname
73
72
  return `OPFS:${pathname}`
74
73
  }
75
74
 
76
75
  /**
77
- * @param {string?} zName
78
- * @param {number} fileId
79
- * @param {number} flags
80
- * @param {DataView} pOutFlags
76
+ * @param {string?} zName
77
+ * @param {number} fileId
78
+ * @param {number} flags
79
+ * @param {DataView} pOutFlags
81
80
  * @returns {Promise<number>}
82
81
  */
83
82
  async jOpen(zName, fileId, flags, pOutFlags) {
84
83
  try {
85
- const url = new URL(zName || Math.random().toString(36).slice(2), 'file://');
86
- const pathname = url.pathname;
84
+ const url = new URL(zName || Math.random().toString(36).slice(2), 'file://')
85
+ const pathname = url.pathname
87
86
 
88
- const file = new File(pathname, flags);
89
- this.mapIdToFile.set(fileId, file);
87
+ const file = new File(pathname, flags)
88
+ this.mapIdToFile.set(fileId, file)
90
89
 
91
- const create = !!(flags & VFS.SQLITE_OPEN_CREATE);
92
- const [directoryHandle, filename] = await getPathComponents(pathname, create);
93
- file.fileHandle = await directoryHandle.getFileHandle(filename, { create });
90
+ const create = !!(flags & VFS.SQLITE_OPEN_CREATE)
91
+ const [directoryHandle, filename] = await getPathComponents(pathname, create)
92
+ file.fileHandle = await directoryHandle.getFileHandle(filename, { create })
94
93
 
95
- if ((flags & VFS.SQLITE_OPEN_MAIN_DB) && !hasUnsafeAccessHandle) {
96
- file.handleRequestChannel = new BroadcastChannel(this.getFilename(fileId));
94
+ if (flags & VFS.SQLITE_OPEN_MAIN_DB && !hasUnsafeAccessHandle) {
95
+ file.handleRequestChannel = new BroadcastChannel(this.getFilename(fileId))
97
96
 
98
97
  // Acquire the access handle lock. The first read of a database
99
98
  // file is done outside xLock/xUnlock so we get that lock here.
100
99
  function notify() {
101
- file.handleRequestChannel.postMessage(null);
100
+ file.handleRequestChannel.postMessage(null)
102
101
  }
103
- const notifyId = setInterval(notify, LOCK_NOTIFY_INTERVAL);
104
- setTimeout(notify);
102
+ const notifyId = setInterval(notify, LOCK_NOTIFY_INTERVAL)
103
+ setTimeout(notify)
105
104
 
106
105
  file.openLockReleaser = await new Promise((resolve, reject) => {
107
- navigator.locks.request(this.getFilename(fileId), lock => {
108
- clearInterval(notifyId);
109
- if (!lock) return reject();
110
- return new Promise(release => {
111
- resolve(release);
112
- });
113
- });
114
- });
115
- this.log?.('access handle acquired for open');
106
+ navigator.locks.request(this.getFilename(fileId), (lock) => {
107
+ clearInterval(notifyId)
108
+ if (!lock) return reject()
109
+ return new Promise((release) => {
110
+ resolve(release)
111
+ })
112
+ })
113
+ })
114
+ this.log?.('access handle acquired for open')
116
115
  }
117
116
 
118
117
  // @ts-ignore
119
118
  file.accessHandle = await file.fileHandle.createSyncAccessHandle({
120
- mode: 'readwrite-unsafe'
121
- });
122
-
123
- pOutFlags.setInt32(0, flags, true);
124
- return VFS.SQLITE_OK;
119
+ mode: 'readwrite-unsafe',
120
+ })
121
+
122
+ pOutFlags.setInt32(0, flags, true)
123
+ return VFS.SQLITE_OK
125
124
  } catch (e) {
126
- this.lastError = e;
127
- return VFS.SQLITE_CANTOPEN;
125
+ this.lastError = e
126
+ return VFS.SQLITE_CANTOPEN
128
127
  }
129
128
  }
130
129
 
131
130
  /**
132
- * @param {string} zName
133
- * @param {number} syncDir
131
+ * @param {string} zName
132
+ * @param {number} syncDir
134
133
  * @returns {Promise<number>}
135
134
  */
136
135
  async jDelete(zName, syncDir) {
137
136
  try {
138
- const url = new URL(zName, 'file://');
139
- const pathname = url.pathname;
140
-
141
- const [directoryHandle, name] = await getPathComponents(pathname, false);
142
- const result = directoryHandle.removeEntry(name, { recursive: false });
137
+ const url = new URL(zName, 'file://')
138
+ const pathname = url.pathname
139
+
140
+ const [directoryHandle, name] = await getPathComponents(pathname, false)
141
+ const result = directoryHandle.removeEntry(name, { recursive: false })
143
142
  if (syncDir) {
144
- await result;
143
+ await result
145
144
  }
146
- return VFS.SQLITE_OK;
145
+ return VFS.SQLITE_OK
147
146
  } catch (e) {
148
- return VFS.SQLITE_IOERR_DELETE;
147
+ return VFS.SQLITE_IOERR_DELETE
149
148
  }
150
149
  }
151
150
 
152
151
  /**
153
- * @param {string} zName
154
- * @param {number} flags
155
- * @param {DataView} pResOut
152
+ * @param {string} zName
153
+ * @param {number} flags
154
+ * @param {DataView} pResOut
156
155
  * @returns {Promise<number>}
157
156
  */
158
157
  async jAccess(zName, flags, pResOut) {
159
158
  try {
160
- const url = new URL(zName, 'file://');
161
- const pathname = url.pathname;
159
+ const url = new URL(zName, 'file://')
160
+ const pathname = url.pathname
162
161
 
163
- const [directoryHandle, dbName] = await getPathComponents(pathname, false);
164
- const fileHandle = await directoryHandle.getFileHandle(dbName, { create: false });
165
- pResOut.setInt32(0, 1, true);
166
- return VFS.SQLITE_OK;
162
+ const [directoryHandle, dbName] = await getPathComponents(pathname, false)
163
+ const fileHandle = await directoryHandle.getFileHandle(dbName, { create: false })
164
+ pResOut.setInt32(0, 1, true)
165
+ return VFS.SQLITE_OK
167
166
  } catch (e) {
168
167
  if (e.name === 'NotFoundError') {
169
- pResOut.setInt32(0, 0, true);
170
- return VFS.SQLITE_OK;
168
+ pResOut.setInt32(0, 0, true)
169
+ return VFS.SQLITE_OK
171
170
  }
172
- this.lastError = e;
173
- return VFS.SQLITE_IOERR_ACCESS;
171
+ this.lastError = e
172
+ return VFS.SQLITE_IOERR_ACCESS
174
173
  }
175
174
  }
176
175
 
177
176
  /**
178
- * @param {number} fileId
177
+ * @param {number} fileId
179
178
  * @returns {Promise<number>}
180
179
  */
181
180
  async jClose(fileId) {
182
181
  try {
183
- const file = this.mapIdToFile.get(fileId);
184
- this.mapIdToFile.delete(fileId);
185
- await file?.accessHandle?.close();
182
+ const file = this.mapIdToFile.get(fileId)
183
+ this.mapIdToFile.delete(fileId)
184
+ await file?.accessHandle?.close()
186
185
 
187
186
  if (file?.flags & VFS.SQLITE_OPEN_DELETEONCLOSE) {
188
- const [directoryHandle, name] = await getPathComponents(file.pathname, false);
189
- await directoryHandle.removeEntry(name, { recursive: false });
187
+ const [directoryHandle, name] = await getPathComponents(file.pathname, false)
188
+ await directoryHandle.removeEntry(name, { recursive: false })
190
189
  }
191
- return VFS.SQLITE_OK;
190
+ return VFS.SQLITE_OK
192
191
  } catch (e) {
193
- return VFS.SQLITE_IOERR_DELETE;
192
+ return VFS.SQLITE_IOERR_DELETE
194
193
  }
195
194
  }
196
195
 
197
196
  /**
198
- * @param {number} fileId
199
- * @param {Uint8Array} pData
197
+ * @param {number} fileId
198
+ * @param {Uint8Array} pData
200
199
  * @param {number} iOffset
201
200
  * @returns {number}
202
201
  */
203
202
  jRead(fileId, pData, iOffset) {
204
203
  try {
205
- const file = this.mapIdToFile.get(fileId);
204
+ const file = this.mapIdToFile.get(fileId)
206
205
 
207
206
  // On Chrome (at least), passing pData to accessHandle.read() is
208
207
  // an error because pData is a Proxy of a Uint8Array. Calling
209
208
  // subarray() produces a real Uint8Array and that works.
210
- const bytesRead = file.accessHandle.read(pData.subarray(), { at: iOffset });
209
+ const bytesRead = file.accessHandle.read(pData.subarray(), { at: iOffset })
211
210
  if (file.openLockReleaser) {
212
211
  // We obtained the access handle on file open.
213
- file.accessHandle.close();
214
- file.accessHandle = null;
215
- file.openLockReleaser();
216
- file.openLockReleaser = null;
217
- this.log?.('access handle released for open');
212
+ file.accessHandle.close()
213
+ file.accessHandle = null
214
+ file.openLockReleaser()
215
+ file.openLockReleaser = null
216
+ this.log?.('access handle released for open')
218
217
  }
219
218
 
220
219
  if (bytesRead < pData.byteLength) {
221
- pData.fill(0, bytesRead);
222
- return VFS.SQLITE_IOERR_SHORT_READ;
220
+ pData.fill(0, bytesRead)
221
+ return VFS.SQLITE_IOERR_SHORT_READ
223
222
  }
224
- return VFS.SQLITE_OK;
223
+ return VFS.SQLITE_OK
225
224
  } catch (e) {
226
- this.lastError = e;
227
- return VFS.SQLITE_IOERR_READ;
225
+ this.lastError = e
226
+ return VFS.SQLITE_IOERR_READ
228
227
  }
229
228
  }
230
229
 
231
230
  /**
232
- * @param {number} fileId
233
- * @param {Uint8Array} pData
231
+ * @param {number} fileId
232
+ * @param {Uint8Array} pData
234
233
  * @param {number} iOffset
235
234
  * @returns {number}
236
235
  */
237
236
  jWrite(fileId, pData, iOffset) {
238
237
  try {
239
- const file = this.mapIdToFile.get(fileId);
238
+ const file = this.mapIdToFile.get(fileId)
240
239
 
241
240
  // On Chrome (at least), passing pData to accessHandle.write() is
242
241
  // an error because pData is a Proxy of a Uint8Array. Calling
243
242
  // subarray() produces a real Uint8Array and that works.
244
- file.accessHandle.write(pData.subarray(), { at: iOffset });
245
- return VFS.SQLITE_OK;
243
+ file.accessHandle.write(pData.subarray(), { at: iOffset })
244
+ return VFS.SQLITE_OK
246
245
  } catch (e) {
247
- this.lastError = e;
248
- return VFS.SQLITE_IOERR_WRITE;
246
+ this.lastError = e
247
+ return VFS.SQLITE_IOERR_WRITE
249
248
  }
250
249
  }
251
250
 
252
251
  /**
253
- * @param {number} fileId
254
- * @param {number} iSize
252
+ * @param {number} fileId
253
+ * @param {number} iSize
255
254
  * @returns {number}
256
255
  */
257
256
  jTruncate(fileId, iSize) {
258
257
  try {
259
- const file = this.mapIdToFile.get(fileId);
260
- file.accessHandle.truncate(iSize);
261
- return VFS.SQLITE_OK;
258
+ const file = this.mapIdToFile.get(fileId)
259
+ file.accessHandle.truncate(iSize)
260
+ return VFS.SQLITE_OK
262
261
  } catch (e) {
263
- this.lastError = e;
264
- return VFS.SQLITE_IOERR_TRUNCATE;
262
+ this.lastError = e
263
+ return VFS.SQLITE_IOERR_TRUNCATE
265
264
  }
266
265
  }
267
266
 
268
267
  /**
269
- * @param {number} fileId
270
- * @param {number} flags
268
+ * @param {number} fileId
269
+ * @param {number} flags
271
270
  * @returns {number}
272
271
  */
273
272
  jSync(fileId, flags) {
274
273
  try {
275
- const file = this.mapIdToFile.get(fileId);
276
- file.accessHandle.flush();
277
- return VFS.SQLITE_OK;
274
+ const file = this.mapIdToFile.get(fileId)
275
+ file.accessHandle.flush()
276
+ return VFS.SQLITE_OK
278
277
  } catch (e) {
279
- this.lastError = e;
280
- return VFS.SQLITE_IOERR_FSYNC;
278
+ this.lastError = e
279
+ return VFS.SQLITE_IOERR_FSYNC
281
280
  }
282
281
  }
283
282
 
284
283
  /**
285
- * @param {number} fileId
286
- * @param {DataView} pSize64
284
+ * @param {number} fileId
285
+ * @param {DataView} pSize64
287
286
  * @returns {number}
288
287
  */
289
288
  jFileSize(fileId, pSize64) {
290
289
  try {
291
- const file = this.mapIdToFile.get(fileId);
292
- const size = file.accessHandle.getSize();
293
- pSize64.setBigInt64(0, BigInt(size), true);
294
- return VFS.SQLITE_OK;
290
+ const file = this.mapIdToFile.get(fileId)
291
+ const size = file.accessHandle.getSize()
292
+ pSize64.setBigInt64(0, BigInt(size), true)
293
+ return VFS.SQLITE_OK
295
294
  } catch (e) {
296
- this.lastError = e;
297
- return VFS.SQLITE_IOERR_FSTAT;
295
+ this.lastError = e
296
+ return VFS.SQLITE_IOERR_FSTAT
298
297
  }
299
298
  }
300
299
 
301
300
  /**
302
- * @param {number} fileId
303
- * @param {number} lockType
301
+ * @param {number} fileId
302
+ * @param {number} lockType
304
303
  * @returns {Promise<number>}
305
304
  */
306
305
  async jLock(fileId, lockType) {
307
- if (hasUnsafeAccessHandle) return super.jLock(fileId, lockType);
306
+ if (hasUnsafeAccessHandle) return super.jLock(fileId, lockType)
308
307
 
309
- const file = this.mapIdToFile.get(fileId);
308
+ const file = this.mapIdToFile.get(fileId)
310
309
  if (!file.isFileLocked) {
311
- file.isFileLocked = true;
310
+ file.isFileLocked = true
312
311
  if (!file.handleLockReleaser) {
313
312
  // Listen for other connections wanting the access handle.
314
- file.handleRequestChannel.onmessage = event => {
315
- if(!file.isFileLocked) {
313
+ file.handleRequestChannel.onmessage = (event) => {
314
+ if (!file.isFileLocked) {
316
315
  // We have the access handle but the file is not locked.
317
316
  // Release the access handle for the requester.
318
- file.accessHandle.close();
319
- file.accessHandle = null;
320
- file.handleLockReleaser();
321
- file.handleLockReleaser = null;
322
- this.log?.('access handle requested and released');
317
+ file.accessHandle.close()
318
+ file.accessHandle = null
319
+ file.handleLockReleaser()
320
+ file.handleLockReleaser = null
321
+ this.log?.('access handle requested and released')
323
322
  } else {
324
323
  // We're still using the access handle, so mark it to be
325
324
  // released when we're done.
326
- file.isHandleRequested = true;
327
- this.log?.('access handle requested');
325
+ file.isHandleRequested = true
326
+ this.log?.('access handle requested')
328
327
  }
329
- file.handleRequestChannel.onmessage = null;
330
- };
328
+ file.handleRequestChannel.onmessage = null
329
+ }
331
330
 
332
331
  // We don't have the access handle. First acquire the lock.
333
332
  file.handleLockReleaser = await new Promise((resolve, reject) => {
334
333
  // Tell everyone we want the access handle.
335
334
  function notify() {
336
- file.handleRequestChannel.postMessage(null);
335
+ file.handleRequestChannel.postMessage(null)
337
336
  }
338
- const notifyId = setInterval(notify, LOCK_NOTIFY_INTERVAL);
339
- setTimeout(notify);
340
-
341
- navigator.locks.request(this.getFilename(fileId), lock => {
342
- clearInterval(notifyId);
343
- if (!lock) return reject();
344
- return new Promise(release => {
345
- resolve(release);
346
- });
347
- });
348
- });
337
+ const notifyId = setInterval(notify, LOCK_NOTIFY_INTERVAL)
338
+ setTimeout(notify)
339
+
340
+ navigator.locks.request(this.getFilename(fileId), (lock) => {
341
+ clearInterval(notifyId)
342
+ if (!lock) return reject()
343
+ return new Promise((release) => {
344
+ resolve(release)
345
+ })
346
+ })
347
+ })
349
348
 
350
349
  // The access handle should now be available.
351
- file.accessHandle = await file.fileHandle.createSyncAccessHandle();
352
- this.log?.('access handle acquired');
350
+ file.accessHandle = await file.fileHandle.createSyncAccessHandle()
351
+ this.log?.('access handle acquired')
353
352
  }
354
-
355
353
  }
356
- return VFS.SQLITE_OK;
354
+ return VFS.SQLITE_OK
357
355
  }
358
356
 
359
357
  /**
360
- * @param {number} fileId
361
- * @param {number} lockType
358
+ * @param {number} fileId
359
+ * @param {number} lockType
362
360
  * @returns {Promise<number>}
363
361
  */
364
362
  async jUnlock(fileId, lockType) {
365
- if (hasUnsafeAccessHandle) return super.jUnlock(fileId, lockType);
363
+ if (hasUnsafeAccessHandle) return super.jUnlock(fileId, lockType)
366
364
 
367
365
  if (lockType === VFS.SQLITE_LOCK_NONE) {
368
- const file = this.mapIdToFile.get(fileId);
366
+ const file = this.mapIdToFile.get(fileId)
369
367
  if (file.isHandleRequested) {
370
368
  if (file.handleLockReleaser) {
371
369
  // Another connection wants the access handle.
372
- file.accessHandle.close();
373
- file.accessHandle = null;
374
- file.handleLockReleaser();
375
- file.handleLockReleaser = null;
376
- this.log?.('access handle released');
370
+ file.accessHandle.close()
371
+ file.accessHandle = null
372
+ file.handleLockReleaser()
373
+ file.handleLockReleaser = null
374
+ this.log?.('access handle released')
377
375
  }
378
- file.isHandleRequested = false;
376
+ file.isHandleRequested = false
379
377
  }
380
- file.isFileLocked = false;
378
+ file.isFileLocked = false
381
379
  }
382
- return VFS.SQLITE_OK;
380
+ return VFS.SQLITE_OK
383
381
  }
384
382
 
385
383
  /**
@@ -390,48 +388,50 @@ export class OPFSAdaptiveVFS extends WebLocksMixin(FacadeVFS) {
390
388
  */
391
389
  jFileControl(fileId, op, pArg) {
392
390
  try {
393
- const file = this.mapIdToFile.get(fileId);
391
+ const file = this.mapIdToFile.get(fileId)
394
392
  switch (op) {
395
393
  case VFS.SQLITE_FCNTL_PRAGMA:
396
- const key = extractString(pArg, 4);
397
- const value = extractString(pArg, 8);
398
- this.log?.('xFileControl', file.pathname, 'PRAGMA', key, value);
394
+ const key = extractString(pArg, 4)
395
+ const value = extractString(pArg, 8)
396
+ this.log?.('xFileControl', file.pathname, 'PRAGMA', key, value)
399
397
  switch (key.toLowerCase()) {
400
398
  case 'journal_mode':
401
- if (value &&
402
- !hasUnsafeAccessHandle &&
403
- !['off', 'memory', 'delete', 'wal'].includes(value.toLowerCase())) {
404
- throw new Error('journal_mode must be "off", "memory", "delete", or "wal"');
399
+ if (
400
+ value &&
401
+ !hasUnsafeAccessHandle &&
402
+ !['off', 'memory', 'delete', 'wal'].includes(value.toLowerCase())
403
+ ) {
404
+ throw new Error('journal_mode must be "off", "memory", "delete", or "wal"')
405
405
  }
406
- break;
406
+ break
407
407
  case 'write_hint':
408
- return super.jFileControl(fileId, WebLocksMixin.WRITE_HINT_OP_CODE, null);
408
+ return super.jFileControl(fileId, WebLocksMixin.WRITE_HINT_OP_CODE, null)
409
409
  }
410
- break;
410
+ break
411
411
  }
412
412
  } catch (e) {
413
- this.lastError = e;
414
- return VFS.SQLITE_IOERR;
413
+ this.lastError = e
414
+ return VFS.SQLITE_IOERR
415
415
  }
416
- return super.jFileControl(fileId, op, pArg);
416
+ return super.jFileControl(fileId, op, pArg)
417
417
  }
418
418
 
419
419
  jGetLastError(zBuf) {
420
420
  if (this.lastError) {
421
- console.error(this.lastError);
422
- const outputArray = zBuf.subarray(0, zBuf.byteLength - 1);
423
- const { written } = new TextEncoder().encodeInto(this.lastError.message, outputArray);
424
- zBuf[written] = 0;
421
+ console.error(this.lastError)
422
+ const outputArray = zBuf.subarray(0, zBuf.byteLength - 1)
423
+ const { written } = new TextEncoder().encodeInto(this.lastError.message, outputArray)
424
+ zBuf[written] = 0
425
425
  }
426
426
  return VFS.SQLITE_OK
427
427
  }
428
428
  }
429
429
 
430
430
  function extractString(dataView, offset) {
431
- const p = dataView.getUint32(offset, true);
431
+ const p = dataView.getUint32(offset, true)
432
432
  if (p) {
433
- const chars = new Uint8Array(dataView.buffer, p);
434
- return new TextDecoder().decode(chars.subarray(0, chars.indexOf(0)));
433
+ const chars = new Uint8Array(dataView.buffer, p)
434
+ return new TextDecoder().decode(chars.subarray(0, chars.indexOf(0)))
435
435
  }
436
- return null;
437
- }
436
+ return null
437
+ }