@livestore/wa-sqlite 0.0.0-snapshot-b9a2e1dee494215d8c403be013e25cbf4d2cf380

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 (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +111 -0
  3. package/dist/README.md +64 -0
  4. package/dist/fts5/wa-sqlite.mjs +2 -0
  5. package/dist/fts5/wa-sqlite.node.mjs +2 -0
  6. package/dist/fts5/wa-sqlite.node.wasm +0 -0
  7. package/dist/fts5/wa-sqlite.wasm +0 -0
  8. package/dist/wa-sqlite-async.mjs +2 -0
  9. package/dist/wa-sqlite-async.wasm +0 -0
  10. package/dist/wa-sqlite-jspi.mjs +2 -0
  11. package/dist/wa-sqlite-jspi.wasm +0 -0
  12. package/dist/wa-sqlite.mjs +2 -0
  13. package/dist/wa-sqlite.node.mjs +2 -0
  14. package/dist/wa-sqlite.node.wasm +0 -0
  15. package/dist/wa-sqlite.wasm +0 -0
  16. package/package.json +57 -0
  17. package/src/FacadeVFS.js +681 -0
  18. package/src/VFS.js +222 -0
  19. package/src/WebLocksMixin.js +414 -0
  20. package/src/examples/AccessHandlePoolVFS.js +458 -0
  21. package/src/examples/IDBBatchAtomicVFS.js +827 -0
  22. package/src/examples/IDBMirrorVFS.js +889 -0
  23. package/src/examples/MemoryAsyncVFS.js +100 -0
  24. package/src/examples/MemoryVFS.js +176 -0
  25. package/src/examples/OPFSAdaptiveVFS.js +437 -0
  26. package/src/examples/OPFSAnyContextVFS.js +300 -0
  27. package/src/examples/OPFSCoopSyncVFS.js +590 -0
  28. package/src/examples/OPFSPermutedVFS.js +1217 -0
  29. package/src/examples/README.md +89 -0
  30. package/src/examples/tag.js +82 -0
  31. package/src/sqlite-api.js +1339 -0
  32. package/src/sqlite-constants.js +275 -0
  33. package/src/types/globals.d.ts +60 -0
  34. package/src/types/index.d.ts +1531 -0
  35. package/src/types/tsconfig.json +6 -0
  36. package/test/AccessHandlePoolVFS.test.js +27 -0
  37. package/test/IDBBatchAtomicVFS.test.js +97 -0
  38. package/test/IDBMirrorVFS.test.js +27 -0
  39. package/test/MemoryAsyncVFS.test.js +27 -0
  40. package/test/MemoryVFS.test.js +27 -0
  41. package/test/OPFSAdaptiveVFS.test.js +27 -0
  42. package/test/OPFSAnyContextVFS.test.js +27 -0
  43. package/test/OPFSCoopSyncVFS.test.js +27 -0
  44. package/test/OPFSPermutedVFS.test.js +27 -0
  45. package/test/TestContext.js +96 -0
  46. package/test/WebLocksMixin.test.js +521 -0
  47. package/test/api.test.js +49 -0
  48. package/test/api_exec.js +89 -0
  49. package/test/api_misc.js +63 -0
  50. package/test/api_statements.js +447 -0
  51. package/test/callbacks.test.js +581 -0
  52. package/test/data/idbv5.json +1 -0
  53. package/test/sql.test.js +64 -0
  54. package/test/sql_0001.js +49 -0
  55. package/test/sql_0002.js +52 -0
  56. package/test/sql_0003.js +83 -0
  57. package/test/sql_0004.js +81 -0
  58. package/test/sql_0005.js +76 -0
  59. package/test/test-worker.js +204 -0
  60. package/test/vfs_xAccess.js +2 -0
  61. package/test/vfs_xClose.js +52 -0
  62. package/test/vfs_xOpen.js +91 -0
  63. package/test/vfs_xRead.js +38 -0
  64. package/test/vfs_xWrite.js +36 -0
package/src/VFS.js ADDED
@@ -0,0 +1,222 @@
1
+ // Copyright 2024 Roy T. Hashimoto. All Rights Reserved.
2
+ import * as VFS from './sqlite-constants.js';
3
+ export * from './sqlite-constants.js';
4
+
5
+ const DEFAULT_SECTOR_SIZE = 512;
6
+
7
+ // Base class for a VFS.
8
+ export class Base {
9
+ name;
10
+ mxPathname = 64;
11
+ _module;
12
+
13
+ /**
14
+ * @param {string} name
15
+ * @param {object} module
16
+ */
17
+ constructor(name, module) {
18
+ this.name = name;
19
+ this._module = module;
20
+ }
21
+
22
+ /**
23
+ * @returns {void|Promise<void>}
24
+ */
25
+ close() {
26
+ }
27
+
28
+ /**
29
+ * @returns {boolean|Promise<boolean>}
30
+ */
31
+ isReady() {
32
+ return true;
33
+ }
34
+
35
+ /**
36
+ * Overload in subclasses to indicate which methods are asynchronous.
37
+ * @param {string} methodName
38
+ * @returns {boolean}
39
+ */
40
+ hasAsyncMethod(methodName) {
41
+ return false;
42
+ }
43
+
44
+ /**
45
+ * @param {number} pVfs
46
+ * @param {number} zName
47
+ * @param {number} pFile
48
+ * @param {number} flags
49
+ * @param {number} pOutFlags
50
+ * @returns {number|Promise<number>}
51
+ */
52
+ xOpen(pVfs, zName, pFile, flags, pOutFlags) {
53
+ return VFS.SQLITE_CANTOPEN;
54
+ }
55
+
56
+ /**
57
+ * @param {number} pVfs
58
+ * @param {number} zName
59
+ * @param {number} syncDir
60
+ * @returns {number|Promise<number>}
61
+ */
62
+ xDelete(pVfs, zName, syncDir) {
63
+ return VFS.SQLITE_OK;
64
+ }
65
+
66
+ /**
67
+ * @param {number} pVfs
68
+ * @param {number} zName
69
+ * @param {number} flags
70
+ * @param {number} pResOut
71
+ * @returns {number|Promise<number>}
72
+ */
73
+ xAccess(pVfs, zName, flags, pResOut) {
74
+ return VFS.SQLITE_OK;
75
+ }
76
+
77
+ /**
78
+ * @param {number} pVfs
79
+ * @param {number} zName
80
+ * @param {number} nOut
81
+ * @param {number} zOut
82
+ * @returns {number|Promise<number>}
83
+ */
84
+ xFullPathname(pVfs, zName, nOut, zOut) {
85
+ return VFS.SQLITE_OK;
86
+ }
87
+
88
+ /**
89
+ * @param {number} pVfs
90
+ * @param {number} nBuf
91
+ * @param {number} zBuf
92
+ * @returns {number|Promise<number>}
93
+ */
94
+ xGetLastError(pVfs, nBuf, zBuf) {
95
+ return VFS.SQLITE_OK;
96
+ }
97
+
98
+ /**
99
+ * @param {number} pFile
100
+ * @returns {number|Promise<number>}
101
+ */
102
+ xClose(pFile) {
103
+ return VFS.SQLITE_OK;
104
+ }
105
+
106
+ /**
107
+ * @param {number} pFile
108
+ * @param {number} pData
109
+ * @param {number} iAmt
110
+ * @param {number} iOffsetLo
111
+ * @param {number} iOffsetHi
112
+ * @returns {number|Promise<number>}
113
+ */
114
+ xRead(pFile, pData, iAmt, iOffsetLo, iOffsetHi) {
115
+ return VFS.SQLITE_OK;
116
+ }
117
+
118
+ /**
119
+ * @param {number} pFile
120
+ * @param {number} pData
121
+ * @param {number} iAmt
122
+ * @param {number} iOffsetLo
123
+ * @param {number} iOffsetHi
124
+ * @returns {number|Promise<number>}
125
+ */
126
+ xWrite(pFile, pData, iAmt, iOffsetLo, iOffsetHi) {
127
+ return VFS.SQLITE_OK;
128
+ }
129
+
130
+ /**
131
+ * @param {number} pFile
132
+ * @param {number} sizeLo
133
+ * @param {number} sizeHi
134
+ * @returns {number|Promise<number>}
135
+ */
136
+ xTruncate(pFile, sizeLo, sizeHi) {
137
+ return VFS.SQLITE_OK;
138
+ }
139
+
140
+ /**
141
+ * @param {number} pFile
142
+ * @param {number} flags
143
+ * @returns {number|Promise<number>}
144
+ */
145
+ xSync(pFile, flags) {
146
+ return VFS.SQLITE_OK;
147
+ }
148
+
149
+ /**
150
+ *
151
+ * @param {number} pFile
152
+ * @param {number} pSize
153
+ * @returns {number|Promise<number>}
154
+ */
155
+ xFileSize(pFile, pSize) {
156
+ return VFS.SQLITE_OK;
157
+ }
158
+
159
+ /**
160
+ * @param {number} pFile
161
+ * @param {number} lockType
162
+ * @returns {number|Promise<number>}
163
+ */
164
+ xLock(pFile, lockType) {
165
+ return VFS.SQLITE_OK;
166
+ }
167
+
168
+ /**
169
+ * @param {number} pFile
170
+ * @param {number} lockType
171
+ * @returns {number|Promise<number>}
172
+ */
173
+ xUnlock(pFile, lockType) {
174
+ return VFS.SQLITE_OK;
175
+ }
176
+
177
+ /**
178
+ * @param {number} pFile
179
+ * @param {number} pResOut
180
+ * @returns {number|Promise<number>}
181
+ */
182
+ xCheckReservedLock(pFile, pResOut) {
183
+ return VFS.SQLITE_OK;
184
+ }
185
+
186
+ /**
187
+ * @param {number} pFile
188
+ * @param {number} op
189
+ * @param {number} pArg
190
+ * @returns {number|Promise<number>}
191
+ */
192
+ xFileControl(pFile, op, pArg) {
193
+ return VFS.SQLITE_NOTFOUND;
194
+ }
195
+
196
+ /**
197
+ * @param {number} pFile
198
+ * @returns {number|Promise<number>}
199
+ */
200
+ xSectorSize(pFile) {
201
+ return DEFAULT_SECTOR_SIZE;
202
+ }
203
+
204
+ /**
205
+ * @param {number} pFile
206
+ * @returns {number|Promise<number>}
207
+ */
208
+ xDeviceCharacteristics(pFile) {
209
+ return 0;
210
+ }
211
+ }
212
+
213
+ export const FILE_TYPE_MASK = [
214
+ VFS.SQLITE_OPEN_MAIN_DB,
215
+ VFS.SQLITE_OPEN_MAIN_JOURNAL,
216
+ VFS.SQLITE_OPEN_TEMP_DB,
217
+ VFS.SQLITE_OPEN_TEMP_JOURNAL,
218
+ VFS.SQLITE_OPEN_TRANSIENT_DB,
219
+ VFS.SQLITE_OPEN_SUBJOURNAL,
220
+ VFS.SQLITE_OPEN_SUPER_JOURNAL,
221
+ VFS.SQLITE_OPEN_WAL
222
+ ].reduce((mask, element) => mask | element);
@@ -0,0 +1,414 @@
1
+ import * as VFS from './VFS.js';
2
+
3
+ // Options for navigator.locks.request().
4
+ /** @type {LockOptions} */ const SHARED = { mode: 'shared' };
5
+ /** @type {LockOptions} */ const POLL_SHARED = { ifAvailable: true, mode: 'shared' };
6
+ /** @type {LockOptions} */ const POLL_EXCLUSIVE = { ifAvailable: true, mode: 'exclusive' };
7
+
8
+ const POLICIES = ['exclusive', 'shared', 'shared+hint'];
9
+
10
+ /**
11
+ * @typedef LockState
12
+ * @property {string} baseName
13
+ * @property {number} type
14
+ * @property {boolean} writeHint
15
+ *
16
+ * These properties are functions that release a specific lock.
17
+ * @property {(() => void)?} [gate]
18
+ * @property {(() => void)?} [access]
19
+ * @property {(() => void)?} [reserved]
20
+ * @property {(() => void)?} [hint]
21
+ */
22
+
23
+ /**
24
+ * Mix-in for FacadeVFS that implements the SQLite VFS locking protocol.
25
+ * @param {*} superclass FacadeVFS (or subclass)
26
+ * @returns
27
+ */
28
+ export const WebLocksMixin = superclass => class extends superclass {
29
+ #options = {
30
+ lockPolicy: 'exclusive',
31
+ lockTimeout: Infinity
32
+ };
33
+
34
+ /** @type {Map<number, LockState>} */ #mapIdToState = new Map();
35
+
36
+ constructor(name, module, options) {
37
+ super(name, module, options);
38
+ Object.assign(this.#options, options);
39
+ if (POLICIES.indexOf(this.#options.lockPolicy) === -1) {
40
+ throw new Error(`WebLocksMixin: invalid lock mode: ${options.lockPolicy}`);
41
+ }
42
+ }
43
+
44
+ /**
45
+ * @param {number} fileId
46
+ * @param {number} lockType
47
+ * @returns {Promise<number>}
48
+ */
49
+ async jLock(fileId, lockType) {
50
+ try {
51
+ // Create state on first lock.
52
+ if (!this.#mapIdToState.has(fileId)) {
53
+ const name = this.getFilename(fileId);
54
+ const state = {
55
+ baseName: name,
56
+ type: VFS.SQLITE_LOCK_NONE,
57
+ writeHint: false
58
+ };
59
+ this.#mapIdToState.set(fileId, state);
60
+ }
61
+
62
+ const lockState = this.#mapIdToState.get(fileId);
63
+ if (lockType <= lockState.type) return VFS.SQLITE_OK;
64
+
65
+ switch (this.#options.lockPolicy) {
66
+ case 'exclusive':
67
+ return await this.#lockExclusive(lockState, lockType);
68
+ case 'shared':
69
+ case 'shared+hint':
70
+ return await this.#lockShared(lockState, lockType);
71
+ }
72
+ } catch (e) {
73
+ console.error('WebLocksMixin: lock error', e);
74
+ return VFS.SQLITE_IOERR_LOCK;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * @param {number} fileId
80
+ * @param {number} lockType
81
+ * @returns {Promise<number>}
82
+ */
83
+ async jUnlock(fileId, lockType) {
84
+ try {
85
+ // SQLite can call xUnlock() without ever calling xLock() so
86
+ // the state may not exist.
87
+ const lockState = this.#mapIdToState.get(fileId);
88
+ if (!(lockType < lockState?.type)) return VFS.SQLITE_OK;
89
+
90
+ switch (this.#options.lockPolicy) {
91
+ case 'exclusive':
92
+ return await this.#unlockExclusive(lockState, lockType);
93
+ case 'shared':
94
+ case 'shared+hint':
95
+ return await this.#unlockShared(lockState, lockType);
96
+ }
97
+ } catch (e) {
98
+ console.error('WebLocksMixin: unlock error', e);
99
+ return VFS.SQLITE_IOERR_UNLOCK;
100
+ }
101
+ }
102
+
103
+ /**
104
+ * @param {number} fileId
105
+ * @param {DataView} pResOut
106
+ * @returns {Promise<number>}
107
+ */
108
+ async jCheckReservedLock(fileId, pResOut) {
109
+ try {
110
+ const lockState = this.#mapIdToState.get(fileId);
111
+ switch (this.#options.lockPolicy) {
112
+ case 'exclusive':
113
+ return this.#checkReservedExclusive(lockState, pResOut);
114
+ case 'shared':
115
+ case 'shared+hint':
116
+ return await this.#checkReservedShared(lockState, pResOut);
117
+ }
118
+ } catch (e) {
119
+ console.error('WebLocksMixin: check reserved lock error', e);
120
+ return VFS.SQLITE_IOERR_CHECKRESERVEDLOCK;
121
+ }
122
+ pResOut.setInt32(0, 0, true);
123
+ return VFS.SQLITE_OK;
124
+ }
125
+
126
+ /**
127
+ * @param {number} fileId
128
+ * @param {number} op
129
+ * @param {DataView} pArg
130
+ * @returns {number|Promise<number>}
131
+ */
132
+ jFileControl(fileId, op, pArg) {
133
+ const lockState = this.#mapIdToState.get(fileId) ??
134
+ (() => {
135
+ // Call jLock() to create the lock state.
136
+ this.jLock(fileId, VFS.SQLITE_LOCK_NONE);
137
+ return this.#mapIdToState.get(fileId);
138
+ })();
139
+ if (op === WebLocksMixin.WRITE_HINT_OP_CODE &&
140
+ this.#options.lockPolicy === 'shared+hint'){
141
+ lockState.writeHint = true;
142
+ }
143
+ return VFS.SQLITE_NOTFOUND;
144
+ }
145
+
146
+ /**
147
+ * @param {LockState} lockState
148
+ * @param {number} lockType
149
+ * @returns
150
+ */
151
+ async #lockExclusive(lockState, lockType) {
152
+ if (!lockState.access) {
153
+ if (!await this.#acquire(lockState, 'access')) {
154
+ return VFS.SQLITE_BUSY;
155
+ }
156
+ console.assert(!!lockState.access);
157
+ }
158
+ lockState.type = lockType;
159
+ return VFS.SQLITE_OK;
160
+ }
161
+
162
+ /**
163
+ * @param {LockState} lockState
164
+ * @param {number} lockType
165
+ * @returns {number}
166
+ */
167
+ #unlockExclusive(lockState, lockType) {
168
+ if (lockType === VFS.SQLITE_LOCK_NONE) {
169
+ lockState.access?.();
170
+ console.assert(!lockState.access);
171
+ }
172
+ lockState.type = lockType;
173
+ return VFS.SQLITE_OK;
174
+ }
175
+
176
+ /**
177
+ * @param {LockState} lockState
178
+ * @param {DataView} pResOut
179
+ * @returns {number}
180
+ */
181
+ #checkReservedExclusive(lockState, pResOut) {
182
+ pResOut.setInt32(0, 0, true);
183
+ return VFS.SQLITE_OK;
184
+ }
185
+
186
+ /**
187
+ * @param {LockState} lockState
188
+ * @param {number} lockType
189
+ * @returns
190
+ */
191
+ async #lockShared(lockState, lockType) {
192
+ switch (lockState.type) {
193
+ case VFS.SQLITE_LOCK_NONE:
194
+ switch (lockType) {
195
+ case VFS.SQLITE_LOCK_SHARED:
196
+ if (lockState.writeHint) {
197
+ // xFileControl() has hinted that this transaction will
198
+ // write. Acquire the hint lock, which is required to reach
199
+ // the RESERVED state.
200
+ if (!await this.#acquire(lockState, 'hint')) {
201
+ // Timeout before lock acquired.
202
+ return VFS.SQLITE_BUSY;
203
+ }
204
+ }
205
+
206
+ // Must have the gate lock to request the access lock.
207
+ if (!await this.#acquire(lockState, 'gate', SHARED)) {
208
+ // Timeout before lock acquired.
209
+ lockState.hint?.();
210
+ return VFS.SQLITE_BUSY;
211
+ }
212
+ await this.#acquire(lockState, 'access', SHARED);
213
+ lockState.gate();
214
+ console.assert(!lockState.gate);
215
+ console.assert(!!lockState.access);
216
+ console.assert(!lockState.reserved);
217
+ break;
218
+
219
+ default:
220
+ throw new Error('unsupported lock transition');
221
+ }
222
+ break;
223
+ case VFS.SQLITE_LOCK_SHARED:
224
+ switch (lockType) {
225
+ case VFS.SQLITE_LOCK_RESERVED:
226
+ if (this.#options.lockPolicy === 'shared+hint') {
227
+ // Ideally we should already have the hint lock, but if not
228
+ // poll for it here.
229
+ if (!lockState.hint &&
230
+ !await this.#acquire(lockState, 'hint', POLL_EXCLUSIVE)) {
231
+ // Another connection has the hint lock so this is a
232
+ // deadlock. This connection must retry.
233
+ return VFS.SQLITE_BUSY;
234
+ }
235
+ }
236
+
237
+ // Poll for the reserved lock. This should always succeed
238
+ // if all clients use the 'shared+hint' policy.
239
+ if (!await this.#acquire(lockState, 'reserved', POLL_EXCLUSIVE)) {
240
+ // This is a deadlock. The connection holding the reserved
241
+ // lock blocks us, and it can't acquire an exclusive access
242
+ // lock because we hold a shared access lock. This connection
243
+ // must retry.
244
+ lockState.hint?.();
245
+ return VFS.SQLITE_BUSY;
246
+ }
247
+ lockState.access();
248
+ console.assert(!lockState.gate);
249
+ console.assert(!lockState.access);
250
+ console.assert(!!lockState.reserved);
251
+ break;
252
+
253
+ case VFS.SQLITE_LOCK_EXCLUSIVE:
254
+ // Jumping directly from SHARED to EXCLUSIVE without passing
255
+ // through RESERVED is only done with a hot journal.
256
+ if (!await this.#acquire(lockState, 'gate')) {
257
+ // Timeout before lock acquired.
258
+ return VFS.SQLITE_BUSY;
259
+ }
260
+ lockState.access();
261
+ if (!await this.#acquire(lockState, 'access')) {
262
+ // Timeout before lock acquired.
263
+ lockState.gate();
264
+ return VFS.SQLITE_BUSY;
265
+ }
266
+ console.assert(!!lockState.gate);
267
+ console.assert(!!lockState.access);
268
+ console.assert(!lockState.reserved);
269
+ break;
270
+
271
+ default:
272
+ throw new Error('unsupported lock transition');
273
+ }
274
+ break;
275
+ case VFS.SQLITE_LOCK_RESERVED:
276
+ switch (lockType) {
277
+ case VFS.SQLITE_LOCK_EXCLUSIVE:
278
+ // Prevent other connections from entering the SHARED state.
279
+ if (!await this.#acquire(lockState, 'gate')) {
280
+ // Timeout before lock acquired.
281
+ return VFS.SQLITE_BUSY;
282
+ }
283
+
284
+ // Block until all other connections exit the SHARED state.
285
+ if (!await this.#acquire(lockState, 'access')) {
286
+ // Timeout before lock acquired.
287
+ lockState.gate();
288
+ return VFS.SQLITE_BUSY;
289
+ }
290
+ console.assert(!!lockState.gate);
291
+ console.assert(!!lockState.access);
292
+ console.assert(!!lockState.reserved);
293
+ break;
294
+
295
+ default:
296
+ throw new Error('unsupported lock transition');
297
+ }
298
+ break;
299
+ }
300
+ lockState.type = lockType;
301
+ return VFS.SQLITE_OK;
302
+ }
303
+
304
+ /**
305
+ * @param {LockState} lockState
306
+ * @param {number} lockType
307
+ * @returns
308
+ */
309
+ async #unlockShared(lockState, lockType) {
310
+ // lockType can only be SQLITE_LOCK_SHARED or SQLITE_LOCK_NONE.
311
+ if (lockType === VFS.SQLITE_LOCK_NONE) {
312
+ lockState.access?.();
313
+ lockState.gate?.();
314
+ lockState.reserved?.();
315
+ lockState.hint?.();
316
+ lockState.writeHint = false;
317
+ console.assert(!lockState.access);
318
+ console.assert(!lockState.gate);
319
+ console.assert(!lockState.reserved);
320
+ console.assert(!lockState.hint);
321
+ } else { // lockType === VFS.SQLITE_LOCK_SHARED
322
+ switch (lockState.type) {
323
+ case VFS.SQLITE_LOCK_EXCLUSIVE:
324
+ // Release our exclusive access lock and reacquire it with a
325
+ // shared lock. This should always succeed because we hold
326
+ // the gate lock.
327
+ lockState.access();
328
+ await this.#acquire(lockState, 'access', SHARED);
329
+
330
+ // Release our gate and reserved locks. We might not have a
331
+ // reserved lock if we were handling a hot journal.
332
+ lockState.gate();
333
+ lockState.reserved?.();
334
+ lockState.hint?.();
335
+ console.assert(!!lockState.access);
336
+ console.assert(!lockState.gate);
337
+ console.assert(!lockState.reserved);
338
+ break;
339
+
340
+ case VFS.SQLITE_LOCK_RESERVED:
341
+ // This transition is rare, probably only on an I/O error
342
+ // while writing to a journal file.
343
+ await this.#acquire(lockState, 'access', SHARED);
344
+ lockState.reserved();
345
+ lockState.hint?.();
346
+ console.assert(!!lockState.access);
347
+ console.assert(!lockState.gate);
348
+ console.assert(!lockState.reserved);
349
+ break;
350
+ }
351
+ }
352
+ lockState.type = lockType;
353
+ return VFS.SQLITE_OK;
354
+ }
355
+
356
+ /**
357
+ * @param {LockState} lockState
358
+ * @param {DataView} pResOut
359
+ * @returns {Promise<number>}
360
+ */
361
+ async #checkReservedShared(lockState, pResOut) {
362
+ if (await this.#acquire(lockState, 'reserved', POLL_SHARED)) {
363
+ // We were able to get the lock so it was not reserved.
364
+ lockState.reserved();
365
+ pResOut.setInt32(0, 0, true);
366
+ } else {
367
+ pResOut.setInt32(0, 1, true);
368
+ }
369
+ return VFS.SQLITE_OK;
370
+ }
371
+
372
+ /**
373
+ * @param {LockState} lockState
374
+ * @param {'gate'|'access'|'reserved'|'hint'} name
375
+ * @param {LockOptions} options
376
+ * @returns {Promise<boolean>}
377
+ */
378
+ #acquire(lockState, name, options = {}) {
379
+ console.assert(!lockState[name]);
380
+ return new Promise(resolve => {
381
+ if (!options.ifAvailable && this.#options.lockTimeout < Infinity) {
382
+ // Add a timeout to the lock request.
383
+ const controller = new AbortController();
384
+ options = Object.assign({}, options, { signal: controller.signal });
385
+ setTimeout(() => {
386
+ controller.abort();
387
+ resolve?.(false);
388
+ }, this.#options.lockTimeout);
389
+ }
390
+
391
+ const lockName = `lock##${lockState.baseName}##${name}`;
392
+ navigator.locks.request(lockName, options, lock => {
393
+ if (lock) {
394
+ return new Promise(release => {
395
+ lockState[name] = () => {
396
+ release();
397
+ lockState[name] = null;
398
+ };
399
+ resolve(true);
400
+ resolve = null;
401
+ });
402
+ } else {
403
+ lockState[name] = null;
404
+ resolve(false);
405
+ resolve = null;
406
+ }
407
+ }).catch(e => {
408
+ if (e.name !== 'AbortError') throw e;
409
+ });
410
+ });
411
+ }
412
+ }
413
+
414
+ WebLocksMixin.WRITE_HINT_OP_CODE = -9999;