@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,18 +1,18 @@
1
- import * as VFS from './VFS.js';
1
+ import * as VFS from './VFS.js'
2
2
 
3
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' };
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
7
 
8
- const POLICIES = ['exclusive', 'shared', 'shared+hint'];
8
+ const POLICIES = ['exclusive', 'shared', 'shared+hint']
9
9
 
10
10
  /**
11
11
  * @typedef LockState
12
12
  * @property {string} baseName
13
13
  * @property {number} type
14
14
  * @property {boolean} writeHint
15
- *
15
+ *
16
16
  * These properties are functions that release a specific lock.
17
17
  * @property {(() => void)?} [gate]
18
18
  * @property {(() => void)?} [access]
@@ -23,392 +23,398 @@ const POLICIES = ['exclusive', 'shared', 'shared+hint'];
23
23
  /**
24
24
  * Mix-in for FacadeVFS that implements the SQLite VFS locking protocol.
25
25
  * @param {*} superclass FacadeVFS (or subclass)
26
- * @returns
26
+ * @returns
27
27
  */
28
- export const WebLocksMixin = superclass => class extends superclass {
29
- #options = {
30
- lockPolicy: 'exclusive',
31
- lockTimeout: Infinity
32
- };
28
+ export const WebLocksMixin = (superclass) =>
29
+ class extends superclass {
30
+ #options = {
31
+ lockPolicy: 'exclusive',
32
+ lockTimeout: Infinity,
33
+ }
33
34
 
34
- /** @type {Map<number, LockState>} */ #mapIdToState = new Map();
35
+ /** @type {Map<number, LockState>} */ #mapIdToState = new Map()
35
36
 
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}`);
37
+ constructor(name, module, options) {
38
+ super(name, module, options)
39
+ Object.assign(this.#options, options)
40
+ if (POLICIES.indexOf(this.#options.lockPolicy) === -1) {
41
+ throw new Error(`WebLocksMixin: invalid lock mode: ${options.lockPolicy}`)
42
+ }
41
43
  }
42
- }
43
44
 
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
- }
45
+ /**
46
+ * @param {number} fileId
47
+ * @param {number} lockType
48
+ * @returns {Promise<number>}
49
+ */
50
+ async jLock(fileId, lockType) {
51
+ try {
52
+ // Create state on first lock.
53
+ if (!this.#mapIdToState.has(fileId)) {
54
+ const name = this.getFilename(fileId)
55
+ const state = {
56
+ baseName: name,
57
+ type: VFS.SQLITE_LOCK_NONE,
58
+ writeHint: false,
59
+ }
60
+ this.#mapIdToState.set(fileId, state)
61
+ }
62
+
63
+ const lockState = this.#mapIdToState.get(fileId)
64
+ if (lockType <= lockState.type) return VFS.SQLITE_OK
61
65
 
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);
66
+ switch (this.#options.lockPolicy) {
67
+ case 'exclusive':
68
+ return await this.#lockExclusive(lockState, lockType)
69
+ case 'shared':
70
+ case 'shared+hint':
71
+ return await this.#lockShared(lockState, lockType)
72
+ }
73
+ } catch (e) {
74
+ console.error('WebLocksMixin: lock error', e)
75
+ return VFS.SQLITE_IOERR_LOCK
71
76
  }
72
- } catch (e) {
73
- console.error('WebLocksMixin: lock error', e);
74
- return VFS.SQLITE_IOERR_LOCK;
75
77
  }
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);
78
+
79
+ /**
80
+ * @param {number} fileId
81
+ * @param {number} lockType
82
+ * @returns {Promise<number>}
83
+ */
84
+ async jUnlock(fileId, lockType) {
85
+ try {
86
+ // SQLite can call xUnlock() without ever calling xLock() so
87
+ // the state may not exist.
88
+ const lockState = this.#mapIdToState.get(fileId)
89
+ if (!(lockType < lockState?.type)) return VFS.SQLITE_OK
90
+
91
+ switch (this.#options.lockPolicy) {
92
+ case 'exclusive':
93
+ return await this.#unlockExclusive(lockState, lockType)
94
+ case 'shared':
95
+ case 'shared+hint':
96
+ return await this.#unlockShared(lockState, lockType)
97
+ }
98
+ } catch (e) {
99
+ console.error('WebLocksMixin: unlock error', e)
100
+ return VFS.SQLITE_IOERR_UNLOCK
96
101
  }
97
- } catch (e) {
98
- console.error('WebLocksMixin: unlock error', e);
99
- return VFS.SQLITE_IOERR_UNLOCK;
100
102
  }
101
- }
102
103
 
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);
104
+ /**
105
+ * @param {number} fileId
106
+ * @param {DataView} pResOut
107
+ * @returns {Promise<number>}
108
+ */
109
+ async jCheckReservedLock(fileId, pResOut) {
110
+ try {
111
+ const lockState = this.#mapIdToState.get(fileId)
112
+ switch (this.#options.lockPolicy) {
113
+ case 'exclusive':
114
+ return this.#checkReservedExclusive(lockState, pResOut)
115
+ case 'shared':
116
+ case 'shared+hint':
117
+ return await this.#checkReservedShared(lockState, pResOut)
118
+ }
119
+ } catch (e) {
120
+ console.error('WebLocksMixin: check reserved lock error', e)
121
+ return VFS.SQLITE_IOERR_CHECKRESERVEDLOCK
117
122
  }
118
- } catch (e) {
119
- console.error('WebLocksMixin: check reserved lock error', e);
120
- return VFS.SQLITE_IOERR_CHECKRESERVEDLOCK;
123
+ pResOut.setInt32(0, 0, true)
124
+ return VFS.SQLITE_OK
121
125
  }
122
- pResOut.setInt32(0, 0, true);
123
- return VFS.SQLITE_OK;
124
- }
125
126
 
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;
127
+ /**
128
+ * @param {number} fileId
129
+ * @param {number} op
130
+ * @param {DataView} pArg
131
+ * @returns {number|Promise<number>}
132
+ */
133
+ jFileControl(fileId, op, pArg) {
134
+ const lockState =
135
+ this.#mapIdToState.get(fileId) ??
136
+ (() => {
137
+ // Call jLock() to create the lock state.
138
+ this.jLock(fileId, VFS.SQLITE_LOCK_NONE)
139
+ return this.#mapIdToState.get(fileId)
140
+ })()
141
+ if (op === WebLocksMixin.WRITE_HINT_OP_CODE && this.#options.lockPolicy === 'shared+hint') {
142
+ lockState.writeHint = true
143
+ }
144
+ return VFS.SQLITE_NOTFOUND
142
145
  }
143
- return VFS.SQLITE_NOTFOUND;
144
- }
145
146
 
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;
147
+ /**
148
+ * @param {LockState} lockState
149
+ * @param {number} lockType
150
+ * @returns
151
+ */
152
+ async #lockExclusive(lockState, lockType) {
153
+ if (!lockState.access) {
154
+ if (!(await this.#acquire(lockState, 'access'))) {
155
+ return VFS.SQLITE_BUSY
156
+ }
157
+ console.assert(!!lockState.access)
155
158
  }
156
- console.assert(!!lockState.access);
159
+ lockState.type = lockType
160
+ return VFS.SQLITE_OK
157
161
  }
158
- lockState.type = lockType;
159
- return VFS.SQLITE_OK;
160
- }
161
162
 
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);
163
+ /**
164
+ * @param {LockState} lockState
165
+ * @param {number} lockType
166
+ * @returns {number}
167
+ */
168
+ #unlockExclusive(lockState, lockType) {
169
+ if (lockType === VFS.SQLITE_LOCK_NONE) {
170
+ lockState.access?.()
171
+ console.assert(!lockState.access)
172
+ }
173
+ lockState.type = lockType
174
+ return VFS.SQLITE_OK
171
175
  }
172
- lockState.type = lockType;
173
- return VFS.SQLITE_OK;
174
- }
175
176
 
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
- }
177
+ /**
178
+ * @param {LockState} lockState
179
+ * @param {DataView} pResOut
180
+ * @returns {number}
181
+ */
182
+ #checkReservedExclusive(lockState, pResOut) {
183
+ pResOut.setInt32(0, 0, true)
184
+ return VFS.SQLITE_OK
185
+ }
185
186
 
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;
187
+ /**
188
+ * @param {LockState} lockState
189
+ * @param {number} lockType
190
+ * @returns
191
+ */
192
+ async #lockShared(lockState, lockType) {
193
+ switch (lockState.type) {
194
+ case VFS.SQLITE_LOCK_NONE:
195
+ switch (lockType) {
196
+ case VFS.SQLITE_LOCK_SHARED:
197
+ if (lockState.writeHint) {
198
+ // xFileControl() has hinted that this transaction will
199
+ // write. Acquire the hint lock, which is required to reach
200
+ // the RESERVED state.
201
+ if (!(await this.#acquire(lockState, 'hint'))) {
202
+ // Timeout before lock acquired.
203
+ return VFS.SQLITE_BUSY
204
+ }
203
205
  }
204
- }
205
206
 
206
- // Must have the gate lock to request the access lock.
207
- if (!await this.#acquire(lockState, 'gate', SHARED)) {
207
+ // Must have the gate lock to request the access lock.
208
+ if (!(await this.#acquire(lockState, 'gate', SHARED))) {
208
209
  // 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;
210
+ lockState.hint?.()
211
+ return VFS.SQLITE_BUSY
212
+ }
213
+ await this.#acquire(lockState, 'access', SHARED)
214
+ lockState.gate()
215
+ console.assert(!lockState.gate)
216
+ console.assert(!!lockState.access)
217
+ console.assert(!lockState.reserved)
218
+ break
218
219
 
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;
220
+ default:
221
+ throw new Error('unsupported lock transition')
222
+ }
223
+ break
224
+ case VFS.SQLITE_LOCK_SHARED:
225
+ switch (lockType) {
226
+ case VFS.SQLITE_LOCK_RESERVED:
227
+ if (this.#options.lockPolicy === 'shared+hint') {
228
+ // Ideally we should already have the hint lock, but if not
229
+ // poll for it here.
230
+ if (!lockState.hint && !(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
+ }
234
235
  }
235
- }
236
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;
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
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;
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
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
- }
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
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;
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
294
 
295
- default:
296
- throw new Error('unsupported lock transition');
297
- }
298
- break;
295
+ default:
296
+ throw new Error('unsupported lock transition')
297
+ }
298
+ break
299
+ }
300
+ lockState.type = lockType
301
+ return VFS.SQLITE_OK
299
302
  }
300
- lockState.type = lockType;
301
- return VFS.SQLITE_OK;
302
- }
303
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);
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 {
322
+ // lockType === VFS.SQLITE_LOCK_SHARED
323
+ switch (lockState.type) {
324
+ case VFS.SQLITE_LOCK_EXCLUSIVE:
325
+ // Release our exclusive access lock and reacquire it with a
326
+ // shared lock. This should always succeed because we hold
327
+ // the gate lock.
328
+ lockState.access()
329
+ await this.#acquire(lockState, 'access', SHARED)
329
330
 
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;
331
+ // Release our gate and reserved locks. We might not have a
332
+ // reserved lock if we were handling a hot journal.
333
+ lockState.gate()
334
+ lockState.reserved?.()
335
+ lockState.hint?.()
336
+ console.assert(!!lockState.access)
337
+ console.assert(!lockState.gate)
338
+ console.assert(!lockState.reserved)
339
+ break
339
340
 
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;
341
+ case VFS.SQLITE_LOCK_RESERVED:
342
+ // This transition is rare, probably only on an I/O error
343
+ // while writing to a journal file.
344
+ await this.#acquire(lockState, 'access', SHARED)
345
+ lockState.reserved()
346
+ lockState.hint?.()
347
+ console.assert(!!lockState.access)
348
+ console.assert(!lockState.gate)
349
+ console.assert(!lockState.reserved)
350
+ break
351
+ }
350
352
  }
353
+ lockState.type = lockType
354
+ return VFS.SQLITE_OK
351
355
  }
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
356
 
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);
357
+ /**
358
+ * @param {LockState} lockState
359
+ * @param {DataView} pResOut
360
+ * @returns {Promise<number>}
361
+ */
362
+ async #checkReservedShared(lockState, pResOut) {
363
+ if (await this.#acquire(lockState, 'reserved', POLL_SHARED)) {
364
+ // We were able to get the lock so it was not reserved.
365
+ lockState.reserved()
366
+ pResOut.setInt32(0, 0, true)
367
+ } else {
368
+ pResOut.setInt32(0, 1, true)
389
369
  }
370
+ return VFS.SQLITE_OK
371
+ }
390
372
 
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;
373
+ /**
374
+ * @param {LockState} lockState
375
+ * @param {'gate'|'access'|'reserved'|'hint'} name
376
+ * @param {LockOptions} options
377
+ * @returns {Promise<boolean>}
378
+ */
379
+ #acquire(lockState, name, options = {}) {
380
+ console.assert(!lockState[name])
381
+ return new Promise((resolve) => {
382
+ if (!options.ifAvailable && this.#options.lockTimeout < Infinity) {
383
+ // Add a timeout to the lock request.
384
+ const controller = new AbortController()
385
+ options = Object.assign({}, options, { signal: controller.signal })
386
+ setTimeout(
387
+ () => {
388
+ controller.abort()
389
+ resolve?.(false)
390
+ },
391
+ this.#options.lockTimeout,
392
+ )
406
393
  }
407
- }).catch(e => {
408
- if (e.name !== 'AbortError') throw e;
409
- });
410
- });
394
+
395
+ const lockName = `lock##${lockState.baseName}##${name}`
396
+ navigator.locks
397
+ .request(lockName, options, (lock) => {
398
+ if (lock) {
399
+ return new Promise((release) => {
400
+ lockState[name] = () => {
401
+ release()
402
+ lockState[name] = null
403
+ }
404
+ resolve(true)
405
+ resolve = null
406
+ })
407
+ } else {
408
+ lockState[name] = null
409
+ resolve(false)
410
+ resolve = null
411
+ }
412
+ })
413
+ .catch((e) => {
414
+ if (e.name !== 'AbortError') throw e
415
+ })
416
+ })
417
+ }
411
418
  }
412
- }
413
419
 
414
- WebLocksMixin.WRITE_HINT_OP_CODE = -9999;
420
+ WebLocksMixin.WRITE_HINT_OP_CODE = -9999