@lunarhue/react-native-web-wa-sqlite 1.0.9

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