@powersync/web 1.10.2 → 1.12.0
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.
- package/README.md +10 -1
- package/bin/powersync.js +43 -0
- package/dist/24cd027f23123a1360de.wasm +0 -0
- package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js-async-mutex-c-3cff7d0.index.umd.js +325 -0
- package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js-async-mutex-c-3cff7d0.index.umd.js.map +1 -0
- package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js-async-mutex-c-3cff7d1.index.umd.js +325 -0
- package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js-async-mutex-c-3cff7d1.index.umd.js.map +1 -0
- package/dist/index.umd.js +58 -177
- package/dist/index.umd.js.map +1 -1
- package/dist/worker/SharedSyncImplementation.umd.js +232 -233
- package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
- package/dist/worker/WASQLiteDB.umd.js +223 -233
- package/dist/worker/WASQLiteDB.umd.js.map +1 -1
- package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite-async_mjs.umd.js +2 -132
- package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite-async_mjs.umd.js.map +1 -1
- package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js.umd.js +1707 -1372
- package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js.umd.js.map +1 -1
- package/lib/package.json +8 -3
- package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.d.ts +2 -1
- package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.js +4 -1
- package/lib/src/db/adapters/web-sql-flags.d.ts +9 -0
- package/lib/src/db/adapters/web-sql-flags.js +5 -0
- package/lib/src/shared/open-db.js +36 -35
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -3
- package/dist/5fe5ed837a91c836c24f.wasm +0 -0
package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js.umd.js
CHANGED
|
@@ -1,6 +1,529 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
(self["webpackChunksdk_web"] = self["webpackChunksdk_web"] || []).push([["node_modules_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js"],{
|
|
3
3
|
|
|
4
|
+
/***/ "../../node_modules/@journeyapps/wa-sqlite/src/FacadeVFS.js":
|
|
5
|
+
/*!******************************************************************!*\
|
|
6
|
+
!*** ../../node_modules/@journeyapps/wa-sqlite/src/FacadeVFS.js ***!
|
|
7
|
+
\******************************************************************/
|
|
8
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
9
|
+
|
|
10
|
+
__webpack_require__.r(__webpack_exports__);
|
|
11
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
12
|
+
/* harmony export */ FacadeVFS: () => (/* binding */ FacadeVFS)
|
|
13
|
+
/* harmony export */ });
|
|
14
|
+
/* harmony import */ var _VFS_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./VFS.js */ "../../node_modules/@journeyapps/wa-sqlite/src/VFS.js");
|
|
15
|
+
// Copyright 2024 Roy T. Hashimoto. All Rights Reserved.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
const AsyncFunction = Object.getPrototypeOf(async function(){}).constructor;
|
|
19
|
+
|
|
20
|
+
// Convenience base class for a JavaScript VFS.
|
|
21
|
+
// The raw xOpen, xRead, etc. function signatures receive only C primitives
|
|
22
|
+
// which aren't easy to work with. This class provides corresponding calls
|
|
23
|
+
// like jOpen, jRead, etc., which receive JavaScript-friendlier arguments
|
|
24
|
+
// such as string, Uint8Array, and DataView.
|
|
25
|
+
class FacadeVFS extends _VFS_js__WEBPACK_IMPORTED_MODULE_0__.Base {
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} name
|
|
28
|
+
* @param {object} module
|
|
29
|
+
*/
|
|
30
|
+
constructor(name, module) {
|
|
31
|
+
super(name, module);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Override to indicate which methods are asynchronous.
|
|
36
|
+
* @param {string} methodName
|
|
37
|
+
* @returns {boolean}
|
|
38
|
+
*/
|
|
39
|
+
hasAsyncMethod(methodName) {
|
|
40
|
+
// The input argument is a string like "xOpen", so convert to "jOpen".
|
|
41
|
+
// Then check if the method exists and is async.
|
|
42
|
+
const jMethodName = `j${methodName.slice(1)}`;
|
|
43
|
+
return this[jMethodName] instanceof AsyncFunction;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Return the filename for a file id for use by mixins.
|
|
48
|
+
* @param {number} pFile
|
|
49
|
+
* @returns {string}
|
|
50
|
+
*/
|
|
51
|
+
getFilename(pFile) {
|
|
52
|
+
throw new Error('unimplemented');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* @param {string?} filename
|
|
57
|
+
* @param {number} pFile
|
|
58
|
+
* @param {number} flags
|
|
59
|
+
* @param {DataView} pOutFlags
|
|
60
|
+
* @returns {number|Promise<number>}
|
|
61
|
+
*/
|
|
62
|
+
jOpen(filename, pFile, flags, pOutFlags) {
|
|
63
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_CANTOPEN;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @param {string} filename
|
|
68
|
+
* @param {number} syncDir
|
|
69
|
+
* @returns {number|Promise<number>}
|
|
70
|
+
*/
|
|
71
|
+
jDelete(filename, syncDir) {
|
|
72
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* @param {string} filename
|
|
77
|
+
* @param {number} flags
|
|
78
|
+
* @param {DataView} pResOut
|
|
79
|
+
* @returns {number|Promise<number>}
|
|
80
|
+
*/
|
|
81
|
+
jAccess(filename, flags, pResOut) {
|
|
82
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {string} filename
|
|
87
|
+
* @param {Uint8Array} zOut
|
|
88
|
+
* @returns {number|Promise<number>}
|
|
89
|
+
*/
|
|
90
|
+
jFullPathname(filename, zOut) {
|
|
91
|
+
// Copy the filename to the output buffer.
|
|
92
|
+
const { read, written } = new TextEncoder().encodeInto(filename, zOut);
|
|
93
|
+
if (read < filename.length) return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
94
|
+
if (written >= zOut.length) return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
95
|
+
zOut[written] = 0;
|
|
96
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* @param {Uint8Array} zBuf
|
|
101
|
+
* @returns {number|Promise<number>}
|
|
102
|
+
*/
|
|
103
|
+
jGetLastError(zBuf) {
|
|
104
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @param {number} pFile
|
|
109
|
+
* @returns {number|Promise<number>}
|
|
110
|
+
*/
|
|
111
|
+
jClose(pFile) {
|
|
112
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* @param {number} pFile
|
|
117
|
+
* @param {Uint8Array} pData
|
|
118
|
+
* @param {number} iOffset
|
|
119
|
+
* @returns {number|Promise<number>}
|
|
120
|
+
*/
|
|
121
|
+
jRead(pFile, pData, iOffset) {
|
|
122
|
+
pData.fill(0);
|
|
123
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR_SHORT_READ;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* @param {number} pFile
|
|
128
|
+
* @param {Uint8Array} pData
|
|
129
|
+
* @param {number} iOffset
|
|
130
|
+
* @returns {number|Promise<number>}
|
|
131
|
+
*/
|
|
132
|
+
jWrite(pFile, pData, iOffset) {
|
|
133
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR_WRITE;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @param {number} pFile
|
|
138
|
+
* @param {number} size
|
|
139
|
+
* @returns {number|Promise<number>}
|
|
140
|
+
*/
|
|
141
|
+
jTruncate(pFile, size) {
|
|
142
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* @param {number} pFile
|
|
147
|
+
* @param {number} flags
|
|
148
|
+
* @returns {number|Promise<number>}
|
|
149
|
+
*/
|
|
150
|
+
jSync(pFile, flags) {
|
|
151
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* @param {number} pFile
|
|
156
|
+
* @param {DataView} pSize
|
|
157
|
+
* @returns {number|Promise<number>}
|
|
158
|
+
*/
|
|
159
|
+
jFileSize(pFile, pSize) {
|
|
160
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* @param {number} pFile
|
|
165
|
+
* @param {number} lockType
|
|
166
|
+
* @returns {number|Promise<number>}
|
|
167
|
+
*/
|
|
168
|
+
jLock(pFile, lockType) {
|
|
169
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* @param {number} pFile
|
|
174
|
+
* @param {number} lockType
|
|
175
|
+
* @returns {number|Promise<number>}
|
|
176
|
+
*/
|
|
177
|
+
jUnlock(pFile, lockType) {
|
|
178
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* @param {number} pFile
|
|
183
|
+
* @param {DataView} pResOut
|
|
184
|
+
* @returns {number|Promise<number>}
|
|
185
|
+
*/
|
|
186
|
+
jCheckReservedLock(pFile, pResOut) {
|
|
187
|
+
pResOut.setInt32(0, 0, true);
|
|
188
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* @param {number} pFile
|
|
193
|
+
* @param {number} op
|
|
194
|
+
* @param {DataView} pArg
|
|
195
|
+
* @returns {number|Promise<number>}
|
|
196
|
+
*/
|
|
197
|
+
jFileControl(pFile, op, pArg) {
|
|
198
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_NOTFOUND;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* @param {number} pFile
|
|
203
|
+
* @returns {number|Promise<number>}
|
|
204
|
+
*/
|
|
205
|
+
jSectorSize(pFile) {
|
|
206
|
+
return super.xSectorSize(pFile);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* @param {number} pFile
|
|
211
|
+
* @returns {number|Promise<number>}
|
|
212
|
+
*/
|
|
213
|
+
jDeviceCharacteristics(pFile) {
|
|
214
|
+
return 0;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* @param {number} pVfs
|
|
219
|
+
* @param {number} zName
|
|
220
|
+
* @param {number} pFile
|
|
221
|
+
* @param {number} flags
|
|
222
|
+
* @param {number} pOutFlags
|
|
223
|
+
* @returns {number|Promise<number>}
|
|
224
|
+
*/
|
|
225
|
+
xOpen(pVfs, zName, pFile, flags, pOutFlags) {
|
|
226
|
+
const filename = this.#decodeFilename(zName, flags);
|
|
227
|
+
const pOutFlagsView = this.#makeTypedDataView('Int32', pOutFlags);
|
|
228
|
+
this['log']?.('jOpen', filename, pFile, '0x' + flags.toString(16));
|
|
229
|
+
return this.jOpen(filename, pFile, flags, pOutFlagsView);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* @param {number} pVfs
|
|
234
|
+
* @param {number} zName
|
|
235
|
+
* @param {number} syncDir
|
|
236
|
+
* @returns {number|Promise<number>}
|
|
237
|
+
*/
|
|
238
|
+
xDelete(pVfs, zName, syncDir) {
|
|
239
|
+
const filename = this._module.UTF8ToString(zName);
|
|
240
|
+
this['log']?.('jDelete', filename, syncDir);
|
|
241
|
+
return this.jDelete(filename, syncDir);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* @param {number} pVfs
|
|
246
|
+
* @param {number} zName
|
|
247
|
+
* @param {number} flags
|
|
248
|
+
* @param {number} pResOut
|
|
249
|
+
* @returns {number|Promise<number>}
|
|
250
|
+
*/
|
|
251
|
+
xAccess(pVfs, zName, flags, pResOut) {
|
|
252
|
+
const filename = this._module.UTF8ToString(zName);
|
|
253
|
+
const pResOutView = this.#makeTypedDataView('Int32', pResOut);
|
|
254
|
+
this['log']?.('jAccess', filename, flags);
|
|
255
|
+
return this.jAccess(filename, flags, pResOutView);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* @param {number} pVfs
|
|
260
|
+
* @param {number} zName
|
|
261
|
+
* @param {number} nOut
|
|
262
|
+
* @param {number} zOut
|
|
263
|
+
* @returns {number|Promise<number>}
|
|
264
|
+
*/
|
|
265
|
+
xFullPathname(pVfs, zName, nOut, zOut) {
|
|
266
|
+
const filename = this._module.UTF8ToString(zName);
|
|
267
|
+
const zOutArray = this._module.HEAPU8.subarray(zOut, zOut + nOut);
|
|
268
|
+
this['log']?.('jFullPathname', filename, nOut);
|
|
269
|
+
return this.jFullPathname(filename, zOutArray);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* @param {number} pVfs
|
|
274
|
+
* @param {number} nBuf
|
|
275
|
+
* @param {number} zBuf
|
|
276
|
+
* @returns {number|Promise<number>}
|
|
277
|
+
*/
|
|
278
|
+
xGetLastError(pVfs, nBuf, zBuf) {
|
|
279
|
+
const zBufArray = this._module.HEAPU8.subarray(zBuf, zBuf + nBuf);
|
|
280
|
+
this['log']?.('jGetLastError', nBuf);
|
|
281
|
+
return this.jGetLastError(zBufArray);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* @param {number} pFile
|
|
286
|
+
* @returns {number|Promise<number>}
|
|
287
|
+
*/
|
|
288
|
+
xClose(pFile) {
|
|
289
|
+
this['log']?.('jClose', pFile);
|
|
290
|
+
return this.jClose(pFile);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* @param {number} pFile
|
|
295
|
+
* @param {number} pData
|
|
296
|
+
* @param {number} iAmt
|
|
297
|
+
* @param {number} iOffsetLo
|
|
298
|
+
* @param {number} iOffsetHi
|
|
299
|
+
* @returns {number|Promise<number>}
|
|
300
|
+
*/
|
|
301
|
+
xRead(pFile, pData, iAmt, iOffsetLo, iOffsetHi) {
|
|
302
|
+
const pDataArray = this.#makeDataArray(pData, iAmt);
|
|
303
|
+
const iOffset = delegalize(iOffsetLo, iOffsetHi);
|
|
304
|
+
this['log']?.('jRead', pFile, iAmt, iOffset);
|
|
305
|
+
return this.jRead(pFile, pDataArray, iOffset);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* @param {number} pFile
|
|
310
|
+
* @param {number} pData
|
|
311
|
+
* @param {number} iAmt
|
|
312
|
+
* @param {number} iOffsetLo
|
|
313
|
+
* @param {number} iOffsetHi
|
|
314
|
+
* @returns {number|Promise<number>}
|
|
315
|
+
*/
|
|
316
|
+
xWrite(pFile, pData, iAmt, iOffsetLo, iOffsetHi) {
|
|
317
|
+
const pDataArray = this.#makeDataArray(pData, iAmt);
|
|
318
|
+
const iOffset = delegalize(iOffsetLo, iOffsetHi);
|
|
319
|
+
this['log']?.('jWrite', pFile, pDataArray, iOffset);
|
|
320
|
+
return this.jWrite(pFile, pDataArray, iOffset);
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* @param {number} pFile
|
|
325
|
+
* @param {number} sizeLo
|
|
326
|
+
* @param {number} sizeHi
|
|
327
|
+
* @returns {number|Promise<number>}
|
|
328
|
+
*/
|
|
329
|
+
xTruncate(pFile, sizeLo, sizeHi) {
|
|
330
|
+
const size = delegalize(sizeLo, sizeHi);
|
|
331
|
+
this['log']?.('jTruncate', pFile, size);
|
|
332
|
+
return this.jTruncate(pFile, size);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* @param {number} pFile
|
|
337
|
+
* @param {number} flags
|
|
338
|
+
* @returns {number|Promise<number>}
|
|
339
|
+
*/
|
|
340
|
+
xSync(pFile, flags) {
|
|
341
|
+
this['log']?.('jSync', pFile, flags);
|
|
342
|
+
return this.jSync(pFile, flags);
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
*
|
|
347
|
+
* @param {number} pFile
|
|
348
|
+
* @param {number} pSize
|
|
349
|
+
* @returns {number|Promise<number>}
|
|
350
|
+
*/
|
|
351
|
+
xFileSize(pFile, pSize) {
|
|
352
|
+
const pSizeView = this.#makeTypedDataView('BigInt64', pSize);
|
|
353
|
+
this['log']?.('jFileSize', pFile);
|
|
354
|
+
return this.jFileSize(pFile, pSizeView);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* @param {number} pFile
|
|
359
|
+
* @param {number} lockType
|
|
360
|
+
* @returns {number|Promise<number>}
|
|
361
|
+
*/
|
|
362
|
+
xLock(pFile, lockType) {
|
|
363
|
+
this['log']?.('jLock', pFile, lockType);
|
|
364
|
+
return this.jLock(pFile, lockType);
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* @param {number} pFile
|
|
369
|
+
* @param {number} lockType
|
|
370
|
+
* @returns {number|Promise<number>}
|
|
371
|
+
*/
|
|
372
|
+
xUnlock(pFile, lockType) {
|
|
373
|
+
this['log']?.('jUnlock', pFile, lockType);
|
|
374
|
+
return this.jUnlock(pFile, lockType);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* @param {number} pFile
|
|
379
|
+
* @param {number} pResOut
|
|
380
|
+
* @returns {number|Promise<number>}
|
|
381
|
+
*/
|
|
382
|
+
xCheckReservedLock(pFile, pResOut) {
|
|
383
|
+
const pResOutView = this.#makeTypedDataView('Int32', pResOut);
|
|
384
|
+
this['log']?.('jCheckReservedLock', pFile);
|
|
385
|
+
return this.jCheckReservedLock(pFile, pResOutView);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* @param {number} pFile
|
|
390
|
+
* @param {number} op
|
|
391
|
+
* @param {number} pArg
|
|
392
|
+
* @returns {number|Promise<number>}
|
|
393
|
+
*/
|
|
394
|
+
xFileControl(pFile, op, pArg) {
|
|
395
|
+
const pArgView = new DataView(
|
|
396
|
+
this._module.HEAPU8.buffer,
|
|
397
|
+
this._module.HEAPU8.byteOffset + pArg);
|
|
398
|
+
this['log']?.('jFileControl', pFile, op, pArgView);
|
|
399
|
+
return this.jFileControl(pFile, op, pArgView);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* @param {number} pFile
|
|
404
|
+
* @returns {number|Promise<number>}
|
|
405
|
+
*/
|
|
406
|
+
xSectorSize(pFile) {
|
|
407
|
+
this['log']?.('jSectorSize', pFile);
|
|
408
|
+
return this.jSectorSize(pFile);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* @param {number} pFile
|
|
413
|
+
* @returns {number|Promise<number>}
|
|
414
|
+
*/
|
|
415
|
+
xDeviceCharacteristics(pFile) {
|
|
416
|
+
this['log']?.('jDeviceCharacteristics', pFile);
|
|
417
|
+
return this.jDeviceCharacteristics(pFile);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Wrapped DataView for pointer arguments.
|
|
422
|
+
* Pointers to a single value are passed using DataView. A Proxy
|
|
423
|
+
* wrapper prevents use of incorrect type or endianness.
|
|
424
|
+
* @param {'Int32'|'BigInt64'} type
|
|
425
|
+
* @param {number} byteOffset
|
|
426
|
+
* @returns {DataView}
|
|
427
|
+
*/
|
|
428
|
+
#makeTypedDataView(type, byteOffset) {
|
|
429
|
+
const byteLength = type === 'Int32' ? 4 : 8;
|
|
430
|
+
const getter = `get${type}`;
|
|
431
|
+
const setter = `set${type}`;
|
|
432
|
+
const makeDataView = () => new DataView(
|
|
433
|
+
this._module.HEAPU8.buffer,
|
|
434
|
+
this._module.HEAPU8.byteOffset + byteOffset,
|
|
435
|
+
byteLength);
|
|
436
|
+
let dataView = makeDataView();
|
|
437
|
+
return new Proxy(dataView, {
|
|
438
|
+
get(_, prop) {
|
|
439
|
+
if (dataView.buffer.byteLength === 0) {
|
|
440
|
+
// WebAssembly memory resize detached the buffer.
|
|
441
|
+
dataView = makeDataView();
|
|
442
|
+
}
|
|
443
|
+
if (prop === getter) {
|
|
444
|
+
return function(byteOffset, littleEndian) {
|
|
445
|
+
if (!littleEndian) throw new Error('must be little endian');
|
|
446
|
+
return dataView[prop](byteOffset, littleEndian);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (prop === setter) {
|
|
450
|
+
return function(byteOffset, value, littleEndian) {
|
|
451
|
+
if (!littleEndian) throw new Error('must be little endian');
|
|
452
|
+
return dataView[prop](byteOffset, value, littleEndian);
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (typeof prop === 'string' && (prop.match(/^(get)|(set)/))) {
|
|
456
|
+
throw new Error('invalid type');
|
|
457
|
+
}
|
|
458
|
+
const result = dataView[prop];
|
|
459
|
+
return typeof result === 'function' ? result.bind(dataView) : result;
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* @param {number} byteOffset
|
|
466
|
+
* @param {number} byteLength
|
|
467
|
+
*/
|
|
468
|
+
#makeDataArray(byteOffset, byteLength) {
|
|
469
|
+
let target = this._module.HEAPU8.subarray(byteOffset, byteOffset + byteLength);
|
|
470
|
+
return new Proxy(target, {
|
|
471
|
+
get: (_, prop, receiver) => {
|
|
472
|
+
if (target.buffer.byteLength === 0) {
|
|
473
|
+
// WebAssembly memory resize detached the buffer.
|
|
474
|
+
target = this._module.HEAPU8.subarray(byteOffset, byteOffset + byteLength);
|
|
475
|
+
}
|
|
476
|
+
const result = target[prop];
|
|
477
|
+
return typeof result === 'function' ? result.bind(target) : result;
|
|
478
|
+
}
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
#decodeFilename(zName, flags) {
|
|
483
|
+
if (flags & _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OPEN_URI) {
|
|
484
|
+
// The first null-terminated string is the URI path. Subsequent
|
|
485
|
+
// strings are query parameter keys and values.
|
|
486
|
+
// https://www.sqlite.org/c3ref/open.html#urifilenamesinsqlite3open
|
|
487
|
+
let pName = zName;
|
|
488
|
+
let state = 1;
|
|
489
|
+
const charCodes = [];
|
|
490
|
+
while (state) {
|
|
491
|
+
const charCode = this._module.HEAPU8[pName++];
|
|
492
|
+
if (charCode) {
|
|
493
|
+
charCodes.push(charCode);
|
|
494
|
+
} else {
|
|
495
|
+
if (!this._module.HEAPU8[pName]) state = null;
|
|
496
|
+
switch (state) {
|
|
497
|
+
case 1: // path
|
|
498
|
+
charCodes.push('?'.charCodeAt(0));
|
|
499
|
+
state = 2;
|
|
500
|
+
break;
|
|
501
|
+
case 2: // key
|
|
502
|
+
charCodes.push('='.charCodeAt(0));
|
|
503
|
+
state = 3;
|
|
504
|
+
break;
|
|
505
|
+
case 3: // value
|
|
506
|
+
charCodes.push('&'.charCodeAt(0));
|
|
507
|
+
state = 2;
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return new TextDecoder().decode(new Uint8Array(charCodes));
|
|
513
|
+
}
|
|
514
|
+
return zName ? this._module.UTF8ToString(zName) : null;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Emscripten "legalizes" 64-bit integer arguments by passing them as
|
|
519
|
+
// two 32-bit signed integers.
|
|
520
|
+
function delegalize(lo32, hi32) {
|
|
521
|
+
return (hi32 * 0x100000000) + lo32 + (lo32 < 0 ? 2**32 : 0);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
/***/ }),
|
|
526
|
+
|
|
4
527
|
/***/ "../../node_modules/@journeyapps/wa-sqlite/src/VFS.js":
|
|
5
528
|
/*!************************************************************!*\
|
|
6
529
|
!*** ../../node_modules/@journeyapps/wa-sqlite/src/VFS.js ***!
|
|
@@ -215,6 +738,9 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
215
738
|
/* harmony export */ SQLITE_OPEN_WAL: () => (/* reexport safe */ _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OPEN_WAL),
|
|
216
739
|
/* harmony export */ SQLITE_PERM: () => (/* reexport safe */ _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_PERM),
|
|
217
740
|
/* harmony export */ SQLITE_PRAGMA: () => (/* reexport safe */ _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_PRAGMA),
|
|
741
|
+
/* harmony export */ SQLITE_PREPARE_NORMALIZED: () => (/* reexport safe */ _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_PREPARE_NORMALIZED),
|
|
742
|
+
/* harmony export */ SQLITE_PREPARE_NO_VTAB: () => (/* reexport safe */ _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_PREPARE_NO_VTAB),
|
|
743
|
+
/* harmony export */ SQLITE_PREPARE_PERSISTENT: () => (/* reexport safe */ _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_PREPARE_PERSISTENT),
|
|
218
744
|
/* harmony export */ SQLITE_PROTOCOL: () => (/* reexport safe */ _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_PROTOCOL),
|
|
219
745
|
/* harmony export */ SQLITE_RANGE: () => (/* reexport safe */ _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_RANGE),
|
|
220
746
|
/* harmony export */ SQLITE_READ: () => (/* reexport safe */ _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_READ),
|
|
@@ -242,166 +768,215 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
242
768
|
/* harmony export */ SQLITE_WARNING: () => (/* reexport safe */ _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_WARNING)
|
|
243
769
|
/* harmony export */ });
|
|
244
770
|
/* harmony import */ var _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./sqlite-constants.js */ "../../node_modules/@journeyapps/wa-sqlite/src/sqlite-constants.js");
|
|
245
|
-
// Copyright
|
|
771
|
+
// Copyright 2024 Roy T. Hashimoto. All Rights Reserved.
|
|
772
|
+
|
|
246
773
|
|
|
247
774
|
|
|
775
|
+
const DEFAULT_SECTOR_SIZE = 512;
|
|
248
776
|
|
|
249
777
|
// Base class for a VFS.
|
|
250
778
|
class Base {
|
|
251
|
-
|
|
779
|
+
name;
|
|
780
|
+
mxPathname = 64;
|
|
781
|
+
_module;
|
|
252
782
|
|
|
253
783
|
/**
|
|
254
|
-
* @param {
|
|
255
|
-
* @
|
|
784
|
+
* @param {string} name
|
|
785
|
+
* @param {object} module
|
|
256
786
|
*/
|
|
257
|
-
|
|
258
|
-
|
|
787
|
+
constructor(name, module) {
|
|
788
|
+
this.name = name;
|
|
789
|
+
this._module = module;
|
|
259
790
|
}
|
|
260
791
|
|
|
261
792
|
/**
|
|
262
|
-
* @
|
|
263
|
-
* @param {Uint8Array} pData
|
|
264
|
-
* @param {number} iOffset
|
|
265
|
-
* @returns {number}
|
|
793
|
+
* @returns {void|Promise<void>}
|
|
266
794
|
*/
|
|
267
|
-
|
|
268
|
-
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
795
|
+
close() {
|
|
269
796
|
}
|
|
270
797
|
|
|
271
798
|
/**
|
|
272
|
-
* @
|
|
273
|
-
* @param {Uint8Array} pData
|
|
274
|
-
* @param {number} iOffset
|
|
275
|
-
* @returns {number}
|
|
799
|
+
* @returns {boolean|Promise<boolean>}
|
|
276
800
|
*/
|
|
277
|
-
|
|
278
|
-
return
|
|
801
|
+
isReady() {
|
|
802
|
+
return true;
|
|
279
803
|
}
|
|
280
804
|
|
|
281
805
|
/**
|
|
282
|
-
*
|
|
283
|
-
* @param {
|
|
284
|
-
* @returns {
|
|
806
|
+
* Overload in subclasses to indicate which methods are asynchronous.
|
|
807
|
+
* @param {string} methodName
|
|
808
|
+
* @returns {boolean}
|
|
285
809
|
*/
|
|
286
|
-
|
|
287
|
-
return
|
|
810
|
+
hasAsyncMethod(methodName) {
|
|
811
|
+
return false;
|
|
288
812
|
}
|
|
289
813
|
|
|
290
814
|
/**
|
|
291
|
-
* @param {number}
|
|
292
|
-
* @param {
|
|
293
|
-
* @
|
|
815
|
+
* @param {number} pVfs
|
|
816
|
+
* @param {number} zName
|
|
817
|
+
* @param {number} pFile
|
|
818
|
+
* @param {number} flags
|
|
819
|
+
* @param {number} pOutFlags
|
|
820
|
+
* @returns {number|Promise<number>}
|
|
294
821
|
*/
|
|
295
|
-
|
|
296
|
-
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.
|
|
822
|
+
xOpen(pVfs, zName, pFile, flags, pOutFlags) {
|
|
823
|
+
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_CANTOPEN;
|
|
297
824
|
}
|
|
298
825
|
|
|
299
826
|
/**
|
|
300
|
-
* @param {number}
|
|
301
|
-
* @param {
|
|
302
|
-
* @
|
|
827
|
+
* @param {number} pVfs
|
|
828
|
+
* @param {number} zName
|
|
829
|
+
* @param {number} syncDir
|
|
830
|
+
* @returns {number|Promise<number>}
|
|
303
831
|
*/
|
|
304
|
-
|
|
305
|
-
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.
|
|
832
|
+
xDelete(pVfs, zName, syncDir) {
|
|
833
|
+
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
306
834
|
}
|
|
307
835
|
|
|
308
836
|
/**
|
|
309
|
-
* @param {number}
|
|
837
|
+
* @param {number} pVfs
|
|
838
|
+
* @param {number} zName
|
|
310
839
|
* @param {number} flags
|
|
311
|
-
* @
|
|
840
|
+
* @param {number} pResOut
|
|
841
|
+
* @returns {number|Promise<number>}
|
|
312
842
|
*/
|
|
313
|
-
|
|
843
|
+
xAccess(pVfs, zName, flags, pResOut) {
|
|
314
844
|
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
315
845
|
}
|
|
316
846
|
|
|
317
847
|
/**
|
|
318
|
-
* @param {number}
|
|
319
|
-
* @param {number}
|
|
320
|
-
* @
|
|
848
|
+
* @param {number} pVfs
|
|
849
|
+
* @param {number} zName
|
|
850
|
+
* @param {number} nOut
|
|
851
|
+
* @param {number} zOut
|
|
852
|
+
* @returns {number|Promise<number>}
|
|
321
853
|
*/
|
|
322
|
-
|
|
854
|
+
xFullPathname(pVfs, zName, nOut, zOut) {
|
|
323
855
|
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
324
856
|
}
|
|
325
857
|
|
|
326
858
|
/**
|
|
327
|
-
* @param {number}
|
|
328
|
-
* @param {
|
|
329
|
-
* @
|
|
859
|
+
* @param {number} pVfs
|
|
860
|
+
* @param {number} nBuf
|
|
861
|
+
* @param {number} zBuf
|
|
862
|
+
* @returns {number|Promise<number>}
|
|
330
863
|
*/
|
|
331
|
-
|
|
332
|
-
pResOut.setInt32(0, 0, true);
|
|
864
|
+
xGetLastError(pVfs, nBuf, zBuf) {
|
|
333
865
|
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
334
866
|
}
|
|
335
867
|
|
|
336
868
|
/**
|
|
337
|
-
* @param {number}
|
|
338
|
-
* @
|
|
339
|
-
* @param {DataView} pArg
|
|
340
|
-
* @returns {number}
|
|
869
|
+
* @param {number} pFile
|
|
870
|
+
* @returns {number|Promise<number>}
|
|
341
871
|
*/
|
|
342
|
-
|
|
343
|
-
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.
|
|
872
|
+
xClose(pFile) {
|
|
873
|
+
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
344
874
|
}
|
|
345
875
|
|
|
346
876
|
/**
|
|
347
|
-
* @param {number}
|
|
348
|
-
* @
|
|
877
|
+
* @param {number} pFile
|
|
878
|
+
* @param {number} pData
|
|
879
|
+
* @param {number} iAmt
|
|
880
|
+
* @param {number} iOffsetLo
|
|
881
|
+
* @param {number} iOffsetHi
|
|
882
|
+
* @returns {number|Promise<number>}
|
|
349
883
|
*/
|
|
350
|
-
|
|
351
|
-
return
|
|
884
|
+
xRead(pFile, pData, iAmt, iOffsetLo, iOffsetHi) {
|
|
885
|
+
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
352
886
|
}
|
|
353
887
|
|
|
354
888
|
/**
|
|
355
|
-
* @param {number}
|
|
356
|
-
* @
|
|
889
|
+
* @param {number} pFile
|
|
890
|
+
* @param {number} pData
|
|
891
|
+
* @param {number} iAmt
|
|
892
|
+
* @param {number} iOffsetLo
|
|
893
|
+
* @param {number} iOffsetHi
|
|
894
|
+
* @returns {number|Promise<number>}
|
|
357
895
|
*/
|
|
358
|
-
|
|
359
|
-
return
|
|
896
|
+
xWrite(pFile, pData, iAmt, iOffsetLo, iOffsetHi) {
|
|
897
|
+
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
360
898
|
}
|
|
361
899
|
|
|
362
900
|
/**
|
|
363
|
-
* @param {
|
|
364
|
-
* @param {number}
|
|
901
|
+
* @param {number} pFile
|
|
902
|
+
* @param {number} sizeLo
|
|
903
|
+
* @param {number} sizeHi
|
|
904
|
+
* @returns {number|Promise<number>}
|
|
905
|
+
*/
|
|
906
|
+
xTruncate(pFile, sizeLo, sizeHi) {
|
|
907
|
+
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* @param {number} pFile
|
|
365
912
|
* @param {number} flags
|
|
366
|
-
* @
|
|
367
|
-
* @returns {number}
|
|
913
|
+
* @returns {number|Promise<number>}
|
|
368
914
|
*/
|
|
369
|
-
|
|
370
|
-
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.
|
|
915
|
+
xSync(pFile, flags) {
|
|
916
|
+
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
371
917
|
}
|
|
372
918
|
|
|
373
919
|
/**
|
|
374
|
-
*
|
|
375
|
-
* @param {number}
|
|
376
|
-
* @
|
|
920
|
+
*
|
|
921
|
+
* @param {number} pFile
|
|
922
|
+
* @param {number} pSize
|
|
923
|
+
* @returns {number|Promise<number>}
|
|
377
924
|
*/
|
|
378
|
-
|
|
379
|
-
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.
|
|
925
|
+
xFileSize(pFile, pSize) {
|
|
926
|
+
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
380
927
|
}
|
|
381
928
|
|
|
382
929
|
/**
|
|
383
|
-
* @param {
|
|
384
|
-
* @param {number}
|
|
385
|
-
* @
|
|
386
|
-
* @returns {number}
|
|
930
|
+
* @param {number} pFile
|
|
931
|
+
* @param {number} lockType
|
|
932
|
+
* @returns {number|Promise<number>}
|
|
387
933
|
*/
|
|
388
|
-
|
|
389
|
-
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.
|
|
934
|
+
xLock(pFile, lockType) {
|
|
935
|
+
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
390
936
|
}
|
|
391
937
|
|
|
392
938
|
/**
|
|
393
|
-
*
|
|
394
|
-
*
|
|
395
|
-
* @
|
|
396
|
-
|
|
939
|
+
* @param {number} pFile
|
|
940
|
+
* @param {number} lockType
|
|
941
|
+
* @returns {number|Promise<number>}
|
|
942
|
+
*/
|
|
943
|
+
xUnlock(pFile, lockType) {
|
|
944
|
+
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* @param {number} pFile
|
|
949
|
+
* @param {number} pResOut
|
|
950
|
+
* @returns {number|Promise<number>}
|
|
397
951
|
*/
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
952
|
+
xCheckReservedLock(pFile, pResOut) {
|
|
953
|
+
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
/**
|
|
957
|
+
* @param {number} pFile
|
|
958
|
+
* @param {number} op
|
|
959
|
+
* @param {number} pArg
|
|
960
|
+
* @returns {number|Promise<number>}
|
|
961
|
+
*/
|
|
962
|
+
xFileControl(pFile, op, pArg) {
|
|
963
|
+
return _sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_NOTFOUND;
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
/**
|
|
967
|
+
* @param {number} pFile
|
|
968
|
+
* @returns {number|Promise<number>}
|
|
969
|
+
*/
|
|
970
|
+
xSectorSize(pFile) {
|
|
971
|
+
return DEFAULT_SECTOR_SIZE;
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
/**
|
|
975
|
+
* @param {number} pFile
|
|
976
|
+
* @returns {number|Promise<number>}
|
|
977
|
+
*/
|
|
978
|
+
xDeviceCharacteristics(pFile) {
|
|
979
|
+
return 0;
|
|
405
980
|
}
|
|
406
981
|
}
|
|
407
982
|
|
|
@@ -412,1514 +987,1274 @@ const FILE_TYPE_MASK = [
|
|
|
412
987
|
_sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OPEN_TEMP_JOURNAL,
|
|
413
988
|
_sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OPEN_TRANSIENT_DB,
|
|
414
989
|
_sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OPEN_SUBJOURNAL,
|
|
415
|
-
_sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OPEN_SUPER_JOURNAL
|
|
990
|
+
_sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OPEN_SUPER_JOURNAL,
|
|
991
|
+
_sqlite_constants_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OPEN_WAL
|
|
416
992
|
].reduce((mask, element) => mask | element);
|
|
417
993
|
|
|
418
994
|
/***/ }),
|
|
419
995
|
|
|
420
|
-
/***/ "../../node_modules/@journeyapps/wa-sqlite/src/
|
|
421
|
-
|
|
422
|
-
!*** ../../node_modules/@journeyapps/wa-sqlite/src/
|
|
423
|
-
|
|
996
|
+
/***/ "../../node_modules/@journeyapps/wa-sqlite/src/WebLocksMixin.js":
|
|
997
|
+
/*!**********************************************************************!*\
|
|
998
|
+
!*** ../../node_modules/@journeyapps/wa-sqlite/src/WebLocksMixin.js ***!
|
|
999
|
+
\**********************************************************************/
|
|
424
1000
|
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
425
1001
|
|
|
426
1002
|
__webpack_require__.r(__webpack_exports__);
|
|
427
1003
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
428
|
-
/* harmony export */
|
|
1004
|
+
/* harmony export */ WebLocksMixin: () => (/* binding */ WebLocksMixin)
|
|
429
1005
|
/* harmony export */ });
|
|
430
|
-
/* harmony import */ var _VFS_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*!
|
|
431
|
-
/* harmony import */ var _WebLocks_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./WebLocks.js */ "../../node_modules/@journeyapps/wa-sqlite/src/examples/WebLocks.js");
|
|
432
|
-
/* harmony import */ var _IDBContext_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./IDBContext.js */ "../../node_modules/@journeyapps/wa-sqlite/src/examples/IDBContext.js");
|
|
433
|
-
// Copyright 2022 Roy T. Hashimoto. All Rights Reserved.
|
|
1006
|
+
/* harmony import */ var _VFS_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./VFS.js */ "../../node_modules/@journeyapps/wa-sqlite/src/VFS.js");
|
|
434
1007
|
|
|
435
1008
|
|
|
1009
|
+
// Options for navigator.locks.request().
|
|
1010
|
+
/** @type {LockOptions} */ const SHARED = { mode: 'shared' };
|
|
1011
|
+
/** @type {LockOptions} */ const POLL_SHARED = { ifAvailable: true, mode: 'shared' };
|
|
1012
|
+
/** @type {LockOptions} */ const POLL_EXCLUSIVE = { ifAvailable: true, mode: 'exclusive' };
|
|
436
1013
|
|
|
437
|
-
|
|
438
|
-
const SECTOR_SIZE = 512;
|
|
439
|
-
const MAX_TASK_MILLIS = 3000;
|
|
1014
|
+
const POLICIES = ['exclusive', 'shared', 'shared+hint'];
|
|
440
1015
|
|
|
441
1016
|
/**
|
|
442
|
-
* @typedef
|
|
443
|
-
* @property {
|
|
444
|
-
* @property {
|
|
445
|
-
* @property {
|
|
1017
|
+
* @typedef LockState
|
|
1018
|
+
* @property {string} baseName
|
|
1019
|
+
* @property {number} type
|
|
1020
|
+
* @property {boolean} writeHint
|
|
1021
|
+
*
|
|
1022
|
+
* These properties are functions that release a specific lock.
|
|
1023
|
+
* @property {(() => void)?} [gate]
|
|
1024
|
+
* @property {(() => void)?} [access]
|
|
1025
|
+
* @property {(() => void)?} [reserved]
|
|
1026
|
+
* @property {(() => void)?} [hint]
|
|
446
1027
|
*/
|
|
447
1028
|
|
|
448
|
-
/** @type {VFSOptions} */
|
|
449
|
-
const DEFAULT_OPTIONS = {
|
|
450
|
-
durability: "default",
|
|
451
|
-
purge: "deferred",
|
|
452
|
-
purgeAtLeast: 16
|
|
453
|
-
};
|
|
454
|
-
|
|
455
|
-
function log(...args) {
|
|
456
|
-
// console.debug(...args);
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
/**
|
|
460
|
-
* @typedef FileBlock IndexedDB object with key [path, offset, version]
|
|
461
|
-
* @property {string} path
|
|
462
|
-
* @property {number} offset negative of position in file
|
|
463
|
-
* @property {number} version
|
|
464
|
-
* @property {Uint8Array} data
|
|
465
|
-
*
|
|
466
|
-
* @property {number} [fileSize] Only present on block 0
|
|
467
|
-
*/
|
|
468
|
-
|
|
469
1029
|
/**
|
|
470
|
-
*
|
|
471
|
-
* @
|
|
472
|
-
* @
|
|
473
|
-
* @property {FileBlock} block0
|
|
474
|
-
* @property {boolean} isMetadataChanged
|
|
475
|
-
* @property {WebLocks} locks
|
|
476
|
-
*
|
|
477
|
-
* @property {Set<number>} [changedPages]
|
|
478
|
-
* @property {boolean} [overwrite]
|
|
1030
|
+
* Mix-in for FacadeVFS that implements the SQLite VFS locking protocol.
|
|
1031
|
+
* @param {*} superclass FacadeVFS (or subclass)
|
|
1032
|
+
* @returns
|
|
479
1033
|
*/
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
// Asyncify can grow WebAssembly memory during an asynchronous call.
|
|
494
|
-
// If this happens, then any array buffer arguments will be detached.
|
|
495
|
-
// The workaround is when finding a detached buffer, set this handler
|
|
496
|
-
// function to process the new buffer outside handlerAsync().
|
|
497
|
-
#growthHandler = null;
|
|
498
|
-
|
|
499
|
-
constructor(idbDatabaseName = 'wa-sqlite', options = DEFAULT_OPTIONS) {
|
|
500
|
-
super();
|
|
501
|
-
this.name = idbDatabaseName;
|
|
502
|
-
this.#options = Object.assign({}, DEFAULT_OPTIONS, options);
|
|
503
|
-
this.#idb = new _IDBContext_js__WEBPACK_IMPORTED_MODULE_2__.IDBContext(openDatabase(idbDatabaseName), {
|
|
504
|
-
durability: this.#options.durability
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
async close() {
|
|
509
|
-
for (const fileId of this.#mapIdToFile.keys()) {
|
|
510
|
-
await this.xClose(fileId);
|
|
1034
|
+
const WebLocksMixin = superclass => class extends superclass {
|
|
1035
|
+
#options = {
|
|
1036
|
+
lockPolicy: 'exclusive',
|
|
1037
|
+
lockTimeout: Infinity
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
/** @type {Map<number, LockState>} */ #mapIdToState = new Map();
|
|
1041
|
+
|
|
1042
|
+
constructor(name, module, options) {
|
|
1043
|
+
super(name, module, options);
|
|
1044
|
+
Object.assign(this.#options, options);
|
|
1045
|
+
if (POLICIES.indexOf(this.#options.lockPolicy) === -1) {
|
|
1046
|
+
throw new Error(`WebLocksMixin: invalid lock mode: ${options.lockPolicy}`);
|
|
511
1047
|
}
|
|
512
|
-
|
|
513
|
-
await this.#idb?.close();
|
|
514
|
-
this.#idb = null;
|
|
515
1048
|
}
|
|
516
1049
|
|
|
517
1050
|
/**
|
|
518
|
-
* @param {string?} name
|
|
519
1051
|
* @param {number} fileId
|
|
520
|
-
* @param {number}
|
|
521
|
-
* @
|
|
522
|
-
* @returns {number}
|
|
1052
|
+
* @param {number} lockType
|
|
1053
|
+
* @returns {Promise<number>}
|
|
523
1054
|
*/
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
path: url.pathname,
|
|
534
|
-
flags,
|
|
535
|
-
block0: null,
|
|
536
|
-
isMetadataChanged: true,
|
|
537
|
-
locks: new _WebLocks_js__WEBPACK_IMPORTED_MODULE_1__.WebLocksExclusive(url.pathname)
|
|
1055
|
+
async jLock(fileId, lockType) {
|
|
1056
|
+
try {
|
|
1057
|
+
// Create state on first lock.
|
|
1058
|
+
if (!this.#mapIdToState.has(fileId)) {
|
|
1059
|
+
const name = this.getFilename(fileId);
|
|
1060
|
+
const state = {
|
|
1061
|
+
baseName: name,
|
|
1062
|
+
type: _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_NONE,
|
|
1063
|
+
writeHint: false
|
|
538
1064
|
};
|
|
539
|
-
this.#
|
|
540
|
-
|
|
541
|
-
// Read the first block, which also contains the file metadata.
|
|
542
|
-
await this.#idb.run('readwrite', async ({blocks}) => {
|
|
543
|
-
file.block0 = await blocks.get(this.#bound(file, 0));
|
|
544
|
-
if (!file.block0) {
|
|
545
|
-
if (flags & _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OPEN_CREATE) {
|
|
546
|
-
file.block0 = {
|
|
547
|
-
path: file.path,
|
|
548
|
-
offset: 0,
|
|
549
|
-
version: 0,
|
|
550
|
-
data: new Uint8Array(0),
|
|
551
|
-
fileSize: 0
|
|
552
|
-
};
|
|
553
|
-
blocks.put(file.block0);
|
|
554
|
-
} else {
|
|
555
|
-
throw new Error(`file not found: ${file.path}`);
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
});
|
|
559
|
-
|
|
560
|
-
// @ts-ignore
|
|
561
|
-
if (pOutFlags.buffer.detached || !pOutFlags.buffer.byteLength) {
|
|
562
|
-
pOutFlags = new DataView(new ArrayBuffer(4));
|
|
563
|
-
this.#growthHandler = (pOutFlagsNew) => {
|
|
564
|
-
pOutFlagsNew.setInt32(0, pOutFlags.getInt32(0, true), true);
|
|
565
|
-
};
|
|
566
|
-
}
|
|
567
|
-
pOutFlags.setInt32(0, flags & _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OPEN_READONLY, true);
|
|
568
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
569
|
-
} catch (e) {
|
|
570
|
-
console.error(e);
|
|
571
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_CANTOPEN;
|
|
1065
|
+
this.#mapIdToState.set(fileId, state);
|
|
572
1066
|
}
|
|
573
|
-
});
|
|
574
1067
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
1068
|
+
const lockState = this.#mapIdToState.get(fileId);
|
|
1069
|
+
if (lockType <= lockState.type) return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
1070
|
+
|
|
1071
|
+
switch (this.#options.lockPolicy) {
|
|
1072
|
+
case 'exclusive':
|
|
1073
|
+
return await this.#lockExclusive(lockState, lockType);
|
|
1074
|
+
case 'shared':
|
|
1075
|
+
case 'shared+hint':
|
|
1076
|
+
return await this.#lockShared(lockState, lockType);
|
|
1077
|
+
}
|
|
1078
|
+
} catch (e) {
|
|
1079
|
+
console.error('WebLocksMixin: lock error', e);
|
|
1080
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR_LOCK;
|
|
1081
|
+
}
|
|
578
1082
|
}
|
|
579
|
-
|
|
1083
|
+
|
|
580
1084
|
/**
|
|
581
1085
|
* @param {number} fileId
|
|
582
|
-
* @
|
|
1086
|
+
* @param {number} lockType
|
|
1087
|
+
* @returns {Promise<number>}
|
|
583
1088
|
*/
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
this.#
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
});
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
599
|
-
} catch (e) {
|
|
600
|
-
console.error(e);
|
|
601
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
1089
|
+
async jUnlock(fileId, lockType) {
|
|
1090
|
+
try {
|
|
1091
|
+
const lockState = this.#mapIdToState.get(fileId);
|
|
1092
|
+
if (lockType >= lockState.type) return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
1093
|
+
|
|
1094
|
+
switch (this.#options.lockPolicy) {
|
|
1095
|
+
case 'exclusive':
|
|
1096
|
+
return await this.#unlockExclusive(lockState, lockType);
|
|
1097
|
+
case 'shared':
|
|
1098
|
+
case 'shared+hint':
|
|
1099
|
+
return await this.#unlockShared(lockState, lockType);
|
|
602
1100
|
}
|
|
603
|
-
})
|
|
1101
|
+
} catch (e) {
|
|
1102
|
+
console.error('WebLocksMixin: unlock error', e);
|
|
1103
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR_UNLOCK;
|
|
1104
|
+
}
|
|
604
1105
|
}
|
|
605
1106
|
|
|
606
1107
|
/**
|
|
607
1108
|
* @param {number} fileId
|
|
608
|
-
* @param {
|
|
609
|
-
* @
|
|
610
|
-
* @returns {number}
|
|
1109
|
+
* @param {DataView} pResOut
|
|
1110
|
+
* @returns {Promise<number>}
|
|
611
1111
|
*/
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
// one case - rollback after journal spill - where reads cross
|
|
622
|
-
// write boundaries so we have to allow for that.
|
|
623
|
-
const result = await this.#idb.run('readonly', async ({blocks}) => {
|
|
624
|
-
// @ts-ignore
|
|
625
|
-
if (pData.buffer.detached || !pData.buffer.byteLength) {
|
|
626
|
-
// WebAssembly memory has grown, invalidating our buffer. Use
|
|
627
|
-
// a temporary buffer and copy after this asynchronous call
|
|
628
|
-
// completes.
|
|
629
|
-
pData = new Uint8Array(byteLength);
|
|
630
|
-
this.#growthHandler = (pDataNew) => pDataNew.set(pData);
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
let pDataOffset = 0;
|
|
634
|
-
while (pDataOffset < pData.byteLength) {
|
|
635
|
-
// Fetch the IndexedDB block for this file location.
|
|
636
|
-
const fileOffset = iOffset + pDataOffset;
|
|
637
|
-
/** @type {FileBlock} */
|
|
638
|
-
const block = fileOffset < file.block0.data.byteLength ?
|
|
639
|
-
file.block0 :
|
|
640
|
-
await blocks.get(this.#bound(file, -fileOffset));
|
|
641
|
-
|
|
642
|
-
if (!block || block.data.byteLength - block.offset <= fileOffset) {
|
|
643
|
-
pData.fill(0, pDataOffset);
|
|
644
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR_SHORT_READ;
|
|
645
|
-
}
|
|
646
|
-
|
|
647
|
-
const buffer = pData.subarray(pDataOffset);
|
|
648
|
-
const blockOffset = fileOffset + block.offset;
|
|
649
|
-
const nBytesToCopy = Math.min(
|
|
650
|
-
Math.max(block.data.byteLength - blockOffset, 0), // source bytes
|
|
651
|
-
buffer.byteLength); // destination bytes
|
|
652
|
-
buffer.set(block.data.subarray(blockOffset, blockOffset + nBytesToCopy));
|
|
653
|
-
pDataOffset += nBytesToCopy;
|
|
654
|
-
}
|
|
655
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
656
|
-
});
|
|
657
|
-
return result;
|
|
658
|
-
} catch (e) {
|
|
659
|
-
console.error(e);
|
|
660
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
1112
|
+
async jCheckReservedLock(fileId, pResOut) {
|
|
1113
|
+
try {
|
|
1114
|
+
const lockState = this.#mapIdToState.get(fileId);
|
|
1115
|
+
switch (this.#options.lockPolicy) {
|
|
1116
|
+
case 'exclusive':
|
|
1117
|
+
return this.#checkReservedExclusive(lockState, pResOut);
|
|
1118
|
+
case 'shared':
|
|
1119
|
+
case 'shared+hint':
|
|
1120
|
+
return await this.#checkReservedShared(lockState, pResOut);
|
|
661
1121
|
}
|
|
662
|
-
})
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
1122
|
+
} catch (e) {
|
|
1123
|
+
console.error('WebLocksMixin: check reserved lock error', e);
|
|
1124
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR_CHECKRESERVEDLOCK;
|
|
1125
|
+
}
|
|
1126
|
+
pResOut.setInt32(0, 0, true);
|
|
1127
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
667
1128
|
}
|
|
668
1129
|
|
|
669
1130
|
/**
|
|
670
|
-
* @param {number}
|
|
671
|
-
* @param {
|
|
672
|
-
* @param {
|
|
673
|
-
* @returns {number}
|
|
1131
|
+
* @param {number} pFile
|
|
1132
|
+
* @param {number} op
|
|
1133
|
+
* @param {DataView} pArg
|
|
1134
|
+
* @returns {number|Promise<number>}
|
|
674
1135
|
*/
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
}
|
|
686
|
-
await new Promise(resolve => setTimeout(resolve));
|
|
687
|
-
|
|
688
|
-
const result = this.#xWriteHelper(fileId, pData.slice(), iOffset);
|
|
689
|
-
this.#taskTimestamp = performance.now();
|
|
690
|
-
return result;
|
|
691
|
-
});
|
|
692
|
-
|
|
693
|
-
if (rewound) this.#pendingAsync.delete(fileId);
|
|
694
|
-
return result;
|
|
1136
|
+
jFileControl(pFile, op, pArg) {
|
|
1137
|
+
const lockState = this.#mapIdToState.get(pFile) ??
|
|
1138
|
+
(() => {
|
|
1139
|
+
// Call jLock() to create the lock state.
|
|
1140
|
+
this.jLock(pFile, _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_NONE);
|
|
1141
|
+
return this.#mapIdToState.get(pFile);
|
|
1142
|
+
})();
|
|
1143
|
+
if (op === WebLocksMixin.WRITE_HINT_OP_CODE &&
|
|
1144
|
+
this.#options.lockPolicy === 'shared+hint'){
|
|
1145
|
+
lockState.writeHint = true;
|
|
695
1146
|
}
|
|
696
|
-
return
|
|
1147
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_NOTFOUND;
|
|
697
1148
|
}
|
|
698
1149
|
|
|
699
1150
|
/**
|
|
700
|
-
* @param {
|
|
701
|
-
* @param {
|
|
702
|
-
* @
|
|
703
|
-
* @returns {number}
|
|
1151
|
+
* @param {LockState} lockState
|
|
1152
|
+
* @param {number} lockType
|
|
1153
|
+
* @returns
|
|
704
1154
|
*/
|
|
705
|
-
#
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
try {
|
|
710
|
-
// Update file size if appending.
|
|
711
|
-
const prevFileSize = file.block0.fileSize;
|
|
712
|
-
if (file.block0.fileSize < iOffset + pData.byteLength) {
|
|
713
|
-
file.block0.fileSize = iOffset + pData.byteLength;
|
|
714
|
-
file.isMetadataChanged = true;
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
// Convert the write directly into an IndexedDB object. Our assumption
|
|
718
|
-
// is that SQLite will only overwrite data with an xWrite of the same
|
|
719
|
-
// offset and size unless the database page size changes, except when
|
|
720
|
-
// changing database page size which is handled by #reblockIfNeeded().
|
|
721
|
-
const block = iOffset === 0 ? file.block0 : {
|
|
722
|
-
path: file.path,
|
|
723
|
-
offset: -iOffset,
|
|
724
|
-
version: file.block0.version,
|
|
725
|
-
data: null
|
|
726
|
-
};
|
|
727
|
-
block.data = pData.slice();
|
|
728
|
-
|
|
729
|
-
if (file.changedPages) {
|
|
730
|
-
// This write is part of a batch atomic write. All writes in the
|
|
731
|
-
// batch have a new version, so update the changed list to allow
|
|
732
|
-
// old versions to be eventually deleted.
|
|
733
|
-
if (prevFileSize === file.block0.fileSize) {
|
|
734
|
-
file.changedPages.add(-iOffset);
|
|
735
|
-
}
|
|
736
|
-
|
|
737
|
-
// Defer writing block 0 to IndexedDB until batch commit.
|
|
738
|
-
if (iOffset !== 0) {
|
|
739
|
-
this.#idb.run('readwrite', ({blocks}) => blocks.put(block));
|
|
740
|
-
}
|
|
741
|
-
} else {
|
|
742
|
-
// Not a batch atomic write so write through.
|
|
743
|
-
this.#idb.run('readwrite', ({blocks}) => blocks.put(block));
|
|
1155
|
+
async #lockExclusive(lockState, lockType) {
|
|
1156
|
+
if (!lockState.access) {
|
|
1157
|
+
if (!await this.#acquire(lockState, 'access')) {
|
|
1158
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_BUSY;
|
|
744
1159
|
}
|
|
745
|
-
|
|
746
|
-
// Clear dirty flag if page 0 was written.
|
|
747
|
-
file.isMetadataChanged = iOffset === 0 ? false : file.isMetadataChanged;
|
|
748
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
749
|
-
} catch (e) {
|
|
750
|
-
console.error(e);
|
|
751
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
1160
|
+
console.assert(!!lockState.access);
|
|
752
1161
|
}
|
|
1162
|
+
lockState.type = lockType;
|
|
1163
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
753
1164
|
}
|
|
754
1165
|
|
|
755
1166
|
/**
|
|
756
|
-
* @param {
|
|
757
|
-
* @param {number}
|
|
1167
|
+
* @param {LockState} lockState
|
|
1168
|
+
* @param {number} lockType
|
|
758
1169
|
* @returns {number}
|
|
759
1170
|
*/
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
try {
|
|
765
|
-
Object.assign(file.block0, {
|
|
766
|
-
fileSize: iSize,
|
|
767
|
-
data: file.block0.data.slice(0, iSize)
|
|
768
|
-
});
|
|
769
|
-
|
|
770
|
-
// Delete all blocks beyond the file size and update metadata.
|
|
771
|
-
// This is never called within a transaction.
|
|
772
|
-
const block0 = Object.assign({}, file.block0);
|
|
773
|
-
this.#idb.run('readwrite', ({blocks})=> {
|
|
774
|
-
blocks.delete(this.#bound(file, -Infinity, -iSize));
|
|
775
|
-
blocks.put(block0);
|
|
776
|
-
});
|
|
777
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
778
|
-
} catch (e) {
|
|
779
|
-
console.error(e);
|
|
780
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
1171
|
+
#unlockExclusive(lockState, lockType) {
|
|
1172
|
+
if (lockType === _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_NONE) {
|
|
1173
|
+
lockState.access?.();
|
|
1174
|
+
console.assert(!lockState.access);
|
|
781
1175
|
}
|
|
1176
|
+
lockState.type = lockType;
|
|
1177
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
782
1178
|
}
|
|
783
1179
|
|
|
784
1180
|
/**
|
|
785
|
-
* @param {
|
|
786
|
-
* @param {
|
|
1181
|
+
* @param {LockState} lockState
|
|
1182
|
+
* @param {DataView} pResOut
|
|
787
1183
|
* @returns {number}
|
|
788
1184
|
*/
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1185
|
+
#checkReservedExclusive(lockState, pResOut) {
|
|
1186
|
+
pResOut.setInt32(0, 0, true);
|
|
1187
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
/**
|
|
1191
|
+
* @param {LockState} lockState
|
|
1192
|
+
* @param {number} lockType
|
|
1193
|
+
* @returns
|
|
1194
|
+
*/
|
|
1195
|
+
async #lockShared(lockState, lockType) {
|
|
1196
|
+
switch (lockState.type) {
|
|
1197
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_NONE:
|
|
1198
|
+
switch (lockType) {
|
|
1199
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_SHARED:
|
|
1200
|
+
if (lockState.writeHint) {
|
|
1201
|
+
// xFileControl() has hinted that this transaction will
|
|
1202
|
+
// write. Acquire the hint lock, which is required to reach
|
|
1203
|
+
// the RESERVED state.
|
|
1204
|
+
if (!await this.#acquire(lockState, 'hint')) {
|
|
1205
|
+
// Timeout before lock acquired.
|
|
1206
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_BUSY;
|
|
1207
|
+
}
|
|
1208
|
+
}
|
|
1209
|
+
|
|
1210
|
+
// Must have the gate lock to request the access lock.
|
|
1211
|
+
if (!await this.#acquire(lockState, 'gate', SHARED)) {
|
|
1212
|
+
// Timeout before lock acquired.
|
|
1213
|
+
lockState.hint?.();
|
|
1214
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_BUSY;
|
|
1215
|
+
}
|
|
1216
|
+
await this.#acquire(lockState, 'access', SHARED);
|
|
1217
|
+
lockState.gate();
|
|
1218
|
+
console.assert(!lockState.gate);
|
|
1219
|
+
console.assert(!!lockState.access);
|
|
1220
|
+
console.assert(!lockState.reserved);
|
|
1221
|
+
break;
|
|
1222
|
+
|
|
1223
|
+
default:
|
|
1224
|
+
throw new Error('unsupported lock transition');
|
|
798
1225
|
}
|
|
1226
|
+
break;
|
|
1227
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_SHARED:
|
|
1228
|
+
switch (lockType) {
|
|
1229
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_RESERVED:
|
|
1230
|
+
if (this.#options.lockPolicy === 'shared+hint') {
|
|
1231
|
+
// Ideally we should already have the hint lock, but if not
|
|
1232
|
+
// poll for it here.
|
|
1233
|
+
if (!lockState.hint &&
|
|
1234
|
+
!await this.#acquire(lockState, 'hint', POLL_EXCLUSIVE)) {
|
|
1235
|
+
// Another connection has the hint lock so this is a
|
|
1236
|
+
// deadlock. This connection must retry.
|
|
1237
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_BUSY;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
799
1240
|
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
1241
|
+
// Poll for the reserved lock. This should always succeed
|
|
1242
|
+
// if all clients use the 'shared+hint' policy.
|
|
1243
|
+
if (!await this.#acquire(lockState, 'reserved', POLL_EXCLUSIVE)) {
|
|
1244
|
+
// This is a deadlock. The connection holding the reserved
|
|
1245
|
+
// lock blocks us, and it can't acquire an exclusive access
|
|
1246
|
+
// lock because we hold a shared access lock. This connection
|
|
1247
|
+
// must retry.
|
|
1248
|
+
lockState.hint?.();
|
|
1249
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_BUSY;
|
|
1250
|
+
}
|
|
1251
|
+
lockState.access();
|
|
1252
|
+
console.assert(!lockState.gate);
|
|
1253
|
+
console.assert(!lockState.access);
|
|
1254
|
+
console.assert(!!lockState.reserved);
|
|
1255
|
+
break;
|
|
804
1256
|
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
1257
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_EXCLUSIVE:
|
|
1258
|
+
// Jumping directly from SHARED to EXCLUSIVE without passing
|
|
1259
|
+
// through RESERVED is only done with a hot journal.
|
|
1260
|
+
if (!await this.#acquire(lockState, 'gate')) {
|
|
1261
|
+
// Timeout before lock acquired.
|
|
1262
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_BUSY;
|
|
1263
|
+
}
|
|
1264
|
+
lockState.access();
|
|
1265
|
+
if (!await this.#acquire(lockState, 'access')) {
|
|
1266
|
+
// Timeout before lock acquired.
|
|
1267
|
+
lockState.gate();
|
|
1268
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_BUSY;
|
|
1269
|
+
}
|
|
1270
|
+
console.assert(!!lockState.gate);
|
|
1271
|
+
console.assert(!!lockState.access);
|
|
1272
|
+
console.assert(!lockState.reserved);
|
|
1273
|
+
break;
|
|
1274
|
+
|
|
1275
|
+
default:
|
|
1276
|
+
throw new Error('unsupported lock transition');
|
|
1277
|
+
}
|
|
1278
|
+
break;
|
|
1279
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_RESERVED:
|
|
1280
|
+
switch (lockType) {
|
|
1281
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_EXCLUSIVE:
|
|
1282
|
+
// Prevent other connections from entering the SHARED state.
|
|
1283
|
+
if (!await this.#acquire(lockState, 'gate')) {
|
|
1284
|
+
// Timeout before lock acquired.
|
|
1285
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_BUSY;
|
|
1286
|
+
}
|
|
808
1287
|
|
|
809
|
-
|
|
810
|
-
|
|
1288
|
+
// Block until all other connections exit the SHARED state.
|
|
1289
|
+
if (!await this.#acquire(lockState, 'access')) {
|
|
1290
|
+
// Timeout before lock acquired.
|
|
1291
|
+
lockState.gate();
|
|
1292
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_BUSY;
|
|
1293
|
+
}
|
|
1294
|
+
console.assert(!!lockState.gate);
|
|
1295
|
+
console.assert(!!lockState.access);
|
|
1296
|
+
console.assert(!!lockState.reserved);
|
|
1297
|
+
break;
|
|
1298
|
+
|
|
1299
|
+
default:
|
|
1300
|
+
throw new Error('unsupported lock transition');
|
|
1301
|
+
}
|
|
1302
|
+
break;
|
|
1303
|
+
}
|
|
1304
|
+
lockState.type = lockType;
|
|
811
1305
|
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
812
1306
|
}
|
|
813
1307
|
|
|
814
1308
|
/**
|
|
815
|
-
* @param {
|
|
816
|
-
* @param {number}
|
|
817
|
-
* @returns
|
|
1309
|
+
* @param {LockState} lockState
|
|
1310
|
+
* @param {number} lockType
|
|
1311
|
+
* @returns
|
|
818
1312
|
*/
|
|
819
|
-
async #
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
1313
|
+
async #unlockShared(lockState, lockType) {
|
|
1314
|
+
// lockType can only be SQLITE_LOCK_SHARED or SQLITE_LOCK_NONE.
|
|
1315
|
+
if (lockType === _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_NONE) {
|
|
1316
|
+
lockState.access?.();
|
|
1317
|
+
lockState.gate?.();
|
|
1318
|
+
lockState.reserved?.();
|
|
1319
|
+
lockState.hint?.();
|
|
1320
|
+
lockState.writeHint = false;
|
|
1321
|
+
console.assert(!lockState.access);
|
|
1322
|
+
console.assert(!lockState.gate);
|
|
1323
|
+
console.assert(!lockState.reserved);
|
|
1324
|
+
console.assert(!lockState.hint);
|
|
1325
|
+
} else { // lockType === VFS.SQLITE_LOCK_SHARED
|
|
1326
|
+
switch (lockState.type) {
|
|
1327
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_EXCLUSIVE:
|
|
1328
|
+
// Release our exclusive access lock and reacquire it with a
|
|
1329
|
+
// shared lock. This should always succeed because we hold
|
|
1330
|
+
// the gate lock.
|
|
1331
|
+
lockState.access();
|
|
1332
|
+
await this.#acquire(lockState, 'access', SHARED);
|
|
1333
|
+
|
|
1334
|
+
// Release our gate and reserved locks. We might not have a
|
|
1335
|
+
// reserved lock if we were handling a hot journal.
|
|
1336
|
+
lockState.gate();
|
|
1337
|
+
lockState.reserved?.();
|
|
1338
|
+
lockState.hint?.();
|
|
1339
|
+
console.assert(!!lockState.access);
|
|
1340
|
+
console.assert(!lockState.gate);
|
|
1341
|
+
console.assert(!lockState.reserved);
|
|
1342
|
+
break;
|
|
1343
|
+
|
|
1344
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_RESERVED:
|
|
1345
|
+
// This transition is rare, probably only on an I/O error
|
|
1346
|
+
// while writing to a journal file.
|
|
1347
|
+
await this.#acquire(lockState, 'access', SHARED);
|
|
1348
|
+
lockState.reserved();
|
|
1349
|
+
lockState.hint?.();
|
|
1350
|
+
console.assert(!!lockState.access);
|
|
1351
|
+
console.assert(!lockState.gate);
|
|
1352
|
+
console.assert(!lockState.reserved);
|
|
1353
|
+
break;
|
|
829
1354
|
}
|
|
830
|
-
await this.#idb.sync();
|
|
831
|
-
} catch (e) {
|
|
832
|
-
console.error(e);
|
|
833
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
834
1355
|
}
|
|
1356
|
+
lockState.type = lockType;
|
|
835
1357
|
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
836
1358
|
}
|
|
837
1359
|
|
|
838
1360
|
/**
|
|
839
|
-
* @param {
|
|
840
|
-
* @param {DataView}
|
|
841
|
-
* @returns {number}
|
|
1361
|
+
* @param {LockState} lockState
|
|
1362
|
+
* @param {DataView} pResOut
|
|
1363
|
+
* @returns {Promise<number>}
|
|
842
1364
|
*/
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
1365
|
+
async #checkReservedShared(lockState, pResOut) {
|
|
1366
|
+
if (await this.#acquire(lockState, 'reserved', POLL_SHARED)) {
|
|
1367
|
+
// We were able to get the lock so it was not reserved.
|
|
1368
|
+
lockState.reserved();
|
|
1369
|
+
pResOut.setInt32(0, 0, true);
|
|
1370
|
+
} else {
|
|
1371
|
+
pResOut.setInt32(0, 1, true);
|
|
1372
|
+
}
|
|
848
1373
|
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
849
1374
|
}
|
|
850
1375
|
|
|
851
1376
|
/**
|
|
852
|
-
* @param {
|
|
853
|
-
* @param {
|
|
854
|
-
* @
|
|
1377
|
+
* @param {LockState} lockState
|
|
1378
|
+
* @param {'gate'|'access'|'reserved'|'hint'} name
|
|
1379
|
+
* @param {LockOptions} options
|
|
1380
|
+
* @returns {Promise<boolean>}
|
|
855
1381
|
*/
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
1382
|
+
#acquire(lockState, name, options = {}) {
|
|
1383
|
+
console.assert(!lockState[name]);
|
|
1384
|
+
return new Promise(resolve => {
|
|
1385
|
+
if (!options.ifAvailable && this.#options.lockTimeout < Infinity) {
|
|
1386
|
+
// Add a timeout to the lock request.
|
|
1387
|
+
const controller = new AbortController();
|
|
1388
|
+
options = Object.assign({}, options, { signal: controller.signal });
|
|
1389
|
+
setTimeout(() => {
|
|
1390
|
+
controller.abort();
|
|
1391
|
+
resolve?.(false);
|
|
1392
|
+
}, this.#options.lockTimeout);
|
|
1393
|
+
}
|
|
860
1394
|
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
1395
|
+
const lockName = `lock##${lockState.baseName}##${name}`;
|
|
1396
|
+
navigator.locks.request(lockName, options, lock => {
|
|
1397
|
+
if (lock) {
|
|
1398
|
+
return new Promise(release => {
|
|
1399
|
+
lockState[name] = () => {
|
|
1400
|
+
release();
|
|
1401
|
+
lockState[name] = null;
|
|
1402
|
+
};
|
|
1403
|
+
resolve(true);
|
|
1404
|
+
resolve = null;
|
|
868
1405
|
});
|
|
1406
|
+
} else {
|
|
1407
|
+
lockState[name] = null;
|
|
1408
|
+
resolve(false);
|
|
1409
|
+
resolve = null;
|
|
869
1410
|
}
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
874
|
-
}
|
|
1411
|
+
}).catch(e => {
|
|
1412
|
+
if (e.name !== 'AbortError') throw e;
|
|
1413
|
+
});
|
|
875
1414
|
});
|
|
876
1415
|
}
|
|
1416
|
+
}
|
|
877
1417
|
|
|
878
|
-
|
|
879
|
-
* @param {number} fileId
|
|
880
|
-
* @param {number} flags
|
|
881
|
-
* @returns {number}
|
|
882
|
-
*/
|
|
883
|
-
xUnlock(fileId, flags) {
|
|
884
|
-
return this.handleAsync(async () => {
|
|
885
|
-
const file = this.#mapIdToFile.get(fileId);
|
|
886
|
-
log(`xUnlock ${file.path} ${flags}`);
|
|
887
|
-
|
|
888
|
-
try {
|
|
889
|
-
return file.locks.unlock(flags);
|
|
890
|
-
} catch(e) {
|
|
891
|
-
console.error(e);
|
|
892
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
893
|
-
}
|
|
894
|
-
});
|
|
895
|
-
}
|
|
1418
|
+
WebLocksMixin.WRITE_HINT_OP_CODE = -9999;
|
|
896
1419
|
|
|
897
|
-
|
|
898
|
-
* @param {number} fileId
|
|
899
|
-
* @param {DataView} pResOut
|
|
900
|
-
* @returns {number}
|
|
901
|
-
*/
|
|
902
|
-
xCheckReservedLock(fileId, pResOut) {
|
|
903
|
-
const result = this.handleAsync(async () => {
|
|
904
|
-
const file = this.#mapIdToFile.get(fileId);
|
|
905
|
-
log(`xCheckReservedLock ${file.path}`);
|
|
1420
|
+
/***/ }),
|
|
906
1421
|
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
1422
|
+
/***/ "../../node_modules/@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js":
|
|
1423
|
+
/*!***********************************************************************************!*\
|
|
1424
|
+
!*** ../../node_modules/@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js ***!
|
|
1425
|
+
\***********************************************************************************/
|
|
1426
|
+
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
910
1427
|
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
});
|
|
921
|
-
|
|
922
|
-
this.#growthHandler?.(pResOut);
|
|
923
|
-
this.#growthHandler = null;
|
|
924
|
-
return result;
|
|
925
|
-
}
|
|
1428
|
+
__webpack_require__.r(__webpack_exports__);
|
|
1429
|
+
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
1430
|
+
/* harmony export */ IDBBatchAtomicVFS: () => (/* binding */ IDBBatchAtomicVFS),
|
|
1431
|
+
/* harmony export */ IDBContext: () => (/* binding */ IDBContext)
|
|
1432
|
+
/* harmony export */ });
|
|
1433
|
+
/* harmony import */ var _FacadeVFS_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../FacadeVFS.js */ "../../node_modules/@journeyapps/wa-sqlite/src/FacadeVFS.js");
|
|
1434
|
+
/* harmony import */ var _VFS_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../VFS.js */ "../../node_modules/@journeyapps/wa-sqlite/src/VFS.js");
|
|
1435
|
+
/* harmony import */ var _WebLocksMixin_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ../WebLocksMixin.js */ "../../node_modules/@journeyapps/wa-sqlite/src/WebLocksMixin.js");
|
|
1436
|
+
// Copyright 2024 Roy T. Hashimoto. All Rights Reserved.
|
|
926
1437
|
|
|
927
|
-
/**
|
|
928
|
-
* @param {number} fileId
|
|
929
|
-
* @returns {number}
|
|
930
|
-
*/
|
|
931
|
-
xSectorSize(fileId) {
|
|
932
|
-
log('xSectorSize');
|
|
933
|
-
return SECTOR_SIZE;
|
|
934
|
-
}
|
|
935
1438
|
|
|
936
|
-
/**
|
|
937
|
-
* @param {number} fileId
|
|
938
|
-
* @returns {number}
|
|
939
|
-
*/
|
|
940
|
-
xDeviceCharacteristics(fileId) {
|
|
941
|
-
log('xDeviceCharacteristics');
|
|
942
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOCAP_BATCH_ATOMIC |
|
|
943
|
-
_VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOCAP_SAFE_APPEND |
|
|
944
|
-
_VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOCAP_SEQUENTIAL |
|
|
945
|
-
_VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
|
|
946
|
-
}
|
|
947
1439
|
|
|
948
|
-
/**
|
|
949
|
-
* @param {number} fileId
|
|
950
|
-
* @param {number} op
|
|
951
|
-
* @param {DataView} pArg
|
|
952
|
-
* @returns {number}
|
|
953
|
-
*/
|
|
954
|
-
xFileControl(fileId, op, pArg) {
|
|
955
|
-
const file = this.#mapIdToFile.get(fileId);
|
|
956
|
-
log(`xFileControl ${file.path} ${op}`);
|
|
957
|
-
|
|
958
|
-
switch (op) {
|
|
959
|
-
case 11: //SQLITE_FCNTL_OVERWRITE
|
|
960
|
-
// This called on VACUUM. Set a flag so we know whether to check
|
|
961
|
-
// later if the page size changed.
|
|
962
|
-
file.overwrite = true;
|
|
963
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
964
|
-
|
|
965
|
-
case 21: // SQLITE_FCNTL_SYNC
|
|
966
|
-
// This is called at the end of each database transaction, whether
|
|
967
|
-
// it is batch atomic or not. Handle page size changes here.
|
|
968
|
-
if (file.overwrite) {
|
|
969
|
-
// As an optimization we only check for and handle a page file
|
|
970
|
-
// changes if we know a VACUUM has been done because handleAsync()
|
|
971
|
-
// has to unwind and rewind the stack. We must be sure to follow
|
|
972
|
-
// the same conditional path in both calls.
|
|
973
|
-
try {
|
|
974
|
-
return this.handleAsync(async () => {
|
|
975
|
-
await this.#reblockIfNeeded(file);
|
|
976
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
977
|
-
});
|
|
978
|
-
} catch (e) {
|
|
979
|
-
console.error(e);
|
|
980
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
1440
|
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
} catch (e) {
|
|
992
|
-
console.error(e);
|
|
993
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
994
|
-
}
|
|
995
|
-
}
|
|
996
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
997
|
-
|
|
998
|
-
case 22: // SQLITE_FCNTL_COMMIT_PHASETWO
|
|
999
|
-
// This is called after a commit is completed.
|
|
1000
|
-
file.overwrite = false;
|
|
1001
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
1002
|
-
|
|
1003
|
-
case 31: // SQLITE_FCNTL_BEGIN_ATOMIC_WRITE
|
|
1004
|
-
return this.handleAsync(async () => {
|
|
1005
|
-
try {
|
|
1006
|
-
// Prepare a new version for IndexedDB blocks.
|
|
1007
|
-
file.block0.version--;
|
|
1008
|
-
file.changedPages = new Set();
|
|
1009
|
-
|
|
1010
|
-
// Clear blocks from abandoned transactions that would conflict
|
|
1011
|
-
// with the new transaction.
|
|
1012
|
-
this.#idb.run('readwrite', async ({blocks}) => {
|
|
1013
|
-
const keys = await blocks.index('version').getAllKeys(IDBKeyRange.bound(
|
|
1014
|
-
[file.path],
|
|
1015
|
-
[file.path, file.block0.version]));
|
|
1016
|
-
for (const key of keys) {
|
|
1017
|
-
blocks.delete(key);
|
|
1018
|
-
}
|
|
1019
|
-
});
|
|
1020
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
1021
|
-
} catch (e) {
|
|
1022
|
-
console.error(e);
|
|
1023
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
1024
|
-
}
|
|
1025
|
-
});
|
|
1441
|
+
/**
|
|
1442
|
+
* @typedef Metadata
|
|
1443
|
+
* @property {string} name
|
|
1444
|
+
* @property {number} fileSize
|
|
1445
|
+
* @property {number} version
|
|
1446
|
+
* @property {number} [pendingVersion]
|
|
1447
|
+
*/
|
|
1026
1448
|
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
block0.data = block0.data.slice();
|
|
1031
|
-
const changedPages = file.changedPages;
|
|
1032
|
-
file.changedPages = null;
|
|
1033
|
-
file.isMetadataChanged = false;
|
|
1034
|
-
this.#idb.run('readwrite', async ({blocks})=> {
|
|
1035
|
-
// Write block 0 to commit the new version.
|
|
1036
|
-
blocks.put(block0);
|
|
1037
|
-
|
|
1038
|
-
// Blocks to purge are saved in a special IndexedDB object with
|
|
1039
|
-
// an "index" of "purge". Add pages changed by this transaction.
|
|
1040
|
-
const purgeBlock = await blocks.get([file.path, 'purge', 0]) ?? {
|
|
1041
|
-
path: file.path,
|
|
1042
|
-
offset: 'purge',
|
|
1043
|
-
version: 0,
|
|
1044
|
-
data: new Map(),
|
|
1045
|
-
count: 0
|
|
1046
|
-
};
|
|
1449
|
+
class File {
|
|
1450
|
+
/** @type {string} */ path;
|
|
1451
|
+
/** @type {number} */ flags;
|
|
1047
1452
|
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
purgeBlock.data.set(pageIndex, block0.version);
|
|
1051
|
-
}
|
|
1453
|
+
/** @type {Metadata} */ metadata;
|
|
1454
|
+
/** @type {number} */ fileSize = 0;
|
|
1052
1455
|
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
1057
|
-
} catch (e) {
|
|
1058
|
-
console.error(e);
|
|
1059
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
1060
|
-
}
|
|
1456
|
+
/** @type {boolean} */ needsMetadataSync = false;
|
|
1457
|
+
/** @type {Metadata} */ rollback = null;
|
|
1458
|
+
/** @type {Set<number>} */ changedPages = new Set();
|
|
1061
1459
|
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
try {
|
|
1065
|
-
// Restore original state. Objects for the abandoned version will
|
|
1066
|
-
// be left in IndexedDB to be removed by the next atomic write
|
|
1067
|
-
// transaction.
|
|
1068
|
-
file.changedPages = null;
|
|
1069
|
-
file.isMetadataChanged = false;
|
|
1070
|
-
file.block0 = await this.#idb.run('readonly', ({blocks}) => {
|
|
1071
|
-
return blocks.get([file.path, 0, file.block0.version + 1]);
|
|
1072
|
-
});
|
|
1073
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
1074
|
-
} catch (e) {
|
|
1075
|
-
console.error(e);
|
|
1076
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
1077
|
-
}
|
|
1078
|
-
});
|
|
1460
|
+
/** @type {string} */ synchronous = 'full';
|
|
1461
|
+
/** @type {IDBTransactionOptions} */ txOptions = { durability: 'strict' };
|
|
1079
1462
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1463
|
+
constructor(path, flags, metadata) {
|
|
1464
|
+
this.path = path;
|
|
1465
|
+
this.flags = flags;
|
|
1466
|
+
this.metadata = metadata;
|
|
1083
1467
|
}
|
|
1468
|
+
}
|
|
1084
1469
|
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
* @param {DataView} pResOut
|
|
1089
|
-
* @returns {number}
|
|
1090
|
-
*/
|
|
1091
|
-
xAccess(name, flags, pResOut) {
|
|
1092
|
-
const result = this.handleAsync(async () => {
|
|
1093
|
-
try {
|
|
1094
|
-
const path = new URL(name, 'file://localhost/').pathname;
|
|
1095
|
-
log(`xAccess ${path} ${flags}`);
|
|
1470
|
+
class IDBBatchAtomicVFS extends (0,_WebLocksMixin_js__WEBPACK_IMPORTED_MODULE_2__.WebLocksMixin)(_FacadeVFS_js__WEBPACK_IMPORTED_MODULE_0__.FacadeVFS) {
|
|
1471
|
+
/** @type {Map<number, File>} */ mapIdToFile = new Map();
|
|
1472
|
+
lastError = null;
|
|
1096
1473
|
|
|
1097
|
-
|
|
1098
|
-
const key = await this.#idb.run('readonly', ({blocks}) => {
|
|
1099
|
-
return blocks.getKey(this.#bound({path}, 0));
|
|
1100
|
-
});
|
|
1474
|
+
log = null; // console.log
|
|
1101
1475
|
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
pResOut = new DataView(new ArrayBuffer(4));
|
|
1105
|
-
this.#growthHandler = (pResOutNew) => {
|
|
1106
|
-
pResOutNew.setInt32(0, pResOut.getInt32(0, true), true);
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
pResOut.setInt32(0, key ? 1 : 0, true);
|
|
1110
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
1111
|
-
} catch (e) {
|
|
1112
|
-
console.error(e);
|
|
1113
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
1114
|
-
}
|
|
1115
|
-
});
|
|
1476
|
+
/** @type {Promise} */ #isReady;
|
|
1477
|
+
/** @type {IDBContext} */ #idb;
|
|
1116
1478
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1479
|
+
static async create(name, module, options) {
|
|
1480
|
+
const vfs = new IDBBatchAtomicVFS(name, module, options);
|
|
1481
|
+
await vfs.isReady();
|
|
1482
|
+
return vfs;
|
|
1120
1483
|
}
|
|
1121
1484
|
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
* @returns {number}
|
|
1126
|
-
*/
|
|
1127
|
-
xDelete(name, syncDir) {
|
|
1128
|
-
return this.handleAsync(async () => {
|
|
1129
|
-
const path = new URL(name, 'file://localhost/').pathname;
|
|
1130
|
-
log(`xDelete ${path} ${syncDir}`);
|
|
1131
|
-
|
|
1132
|
-
try {
|
|
1133
|
-
this.#idb.run('readwrite', ({blocks}) => {
|
|
1134
|
-
return blocks.delete(IDBKeyRange.bound([path], [path, []]));
|
|
1135
|
-
});
|
|
1136
|
-
if (syncDir) {
|
|
1137
|
-
await this.#idb.sync();
|
|
1138
|
-
}
|
|
1139
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
1140
|
-
} catch (e) {
|
|
1141
|
-
console.error(e);
|
|
1142
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR;
|
|
1143
|
-
}
|
|
1144
|
-
});
|
|
1485
|
+
constructor(name, module, options = {}) {
|
|
1486
|
+
super(name, module, options);
|
|
1487
|
+
this.#isReady = this.#initialize(options.idbName ?? name);
|
|
1145
1488
|
}
|
|
1146
1489
|
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
* @param {string} path
|
|
1150
|
-
*/
|
|
1151
|
-
async purge(path) {
|
|
1152
|
-
const start = Date.now();
|
|
1153
|
-
await this.#idb.run('readwrite', async ({blocks}) => {
|
|
1154
|
-
const purgeBlock = await blocks.get([path, 'purge', 0]);
|
|
1155
|
-
if (purgeBlock) {
|
|
1156
|
-
for (const [pageOffset, version] of purgeBlock.data) {
|
|
1157
|
-
blocks.delete(IDBKeyRange.bound(
|
|
1158
|
-
[path, pageOffset, version],
|
|
1159
|
-
[path, pageOffset, Infinity],
|
|
1160
|
-
true, false));
|
|
1161
|
-
}
|
|
1162
|
-
await blocks.delete([path, 'purge', 0]);
|
|
1163
|
-
}
|
|
1164
|
-
log(`purge ${path} ${purgeBlock?.data.size ?? 0} pages in ${Date.now() - start} ms`);
|
|
1165
|
-
});
|
|
1490
|
+
async #initialize(name) {
|
|
1491
|
+
this.#idb = await IDBContext.create(name);
|
|
1166
1492
|
}
|
|
1167
1493
|
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
* @param {string} path
|
|
1171
|
-
* @param {number} nPages
|
|
1172
|
-
*/
|
|
1173
|
-
#maybePurge(path, nPages) {
|
|
1174
|
-
if (this.#options.purge === 'manual' ||
|
|
1175
|
-
this.#pendingPurges.has(path) ||
|
|
1176
|
-
nPages < this.#options.purgeAtLeast) {
|
|
1177
|
-
// No purge needed.
|
|
1178
|
-
return;
|
|
1179
|
-
}
|
|
1180
|
-
|
|
1181
|
-
if (globalThis.requestIdleCallback) {
|
|
1182
|
-
globalThis.requestIdleCallback(() => {
|
|
1183
|
-
this.purge(path);
|
|
1184
|
-
this.#pendingPurges.delete(path)
|
|
1185
|
-
});
|
|
1186
|
-
} else {
|
|
1187
|
-
setTimeout(() => {
|
|
1188
|
-
this.purge(path);
|
|
1189
|
-
this.#pendingPurges.delete(path)
|
|
1190
|
-
});
|
|
1191
|
-
}
|
|
1192
|
-
this.#pendingPurges.add(path);
|
|
1193
|
-
}
|
|
1194
|
-
|
|
1195
|
-
#bound(file, begin, end = 0) {
|
|
1196
|
-
// Fetch newest block 0. For other blocks, use block 0 version.
|
|
1197
|
-
const version = !begin || -begin < file.block0.data.length ?
|
|
1198
|
-
-Infinity :
|
|
1199
|
-
file.block0.version;
|
|
1200
|
-
return IDBKeyRange.bound(
|
|
1201
|
-
[file.path, begin, version],
|
|
1202
|
-
[file.path, end, Infinity]);
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
// The database page size can be changed with PRAGMA page_size and VACUUM.
|
|
1206
|
-
// The updated file will be overwritten with a regular transaction using
|
|
1207
|
-
// the old page size. After that it will be read and written using the
|
|
1208
|
-
// new page size, so the IndexedDB objects must be combined or split
|
|
1209
|
-
// appropriately.
|
|
1210
|
-
async #reblockIfNeeded(file) {
|
|
1211
|
-
const oldPageSize = file.block0.data.length;
|
|
1212
|
-
if (oldPageSize < 18) return; // no page size defined
|
|
1213
|
-
|
|
1214
|
-
const view = new DataView(file.block0.data.buffer, file.block0.data.byteOffset);
|
|
1215
|
-
let newPageSize = view.getUint16(16);
|
|
1216
|
-
if (newPageSize === 1) newPageSize = 65536;
|
|
1217
|
-
if (newPageSize === oldPageSize) return; // no page size change
|
|
1218
|
-
|
|
1219
|
-
const maxPageSize = Math.max(oldPageSize, newPageSize);
|
|
1220
|
-
const nOldPages = maxPageSize / oldPageSize;
|
|
1221
|
-
const nNewPages = maxPageSize / newPageSize;
|
|
1222
|
-
|
|
1223
|
-
const newPageCount = view.getUint32(28);
|
|
1224
|
-
const fileSize = newPageCount * newPageSize;
|
|
1225
|
-
|
|
1226
|
-
const version = file.block0.version;
|
|
1227
|
-
await this.#idb.run('readwrite', async ({blocks}) => {
|
|
1228
|
-
// When the block size changes, the entire file is rewritten. Delete
|
|
1229
|
-
// all blocks older than block 0 to leave a single version at every
|
|
1230
|
-
// offset.
|
|
1231
|
-
const keys = await blocks.index('version').getAllKeys(IDBKeyRange.bound(
|
|
1232
|
-
[file.path, version + 1],
|
|
1233
|
-
[file.path, Infinity]
|
|
1234
|
-
));
|
|
1235
|
-
for (const key of keys) {
|
|
1236
|
-
blocks.delete(key);
|
|
1237
|
-
}
|
|
1238
|
-
blocks.delete([file.path, 'purge', 0]);
|
|
1239
|
-
|
|
1240
|
-
// Do the conversion in chunks of the larger of the page sizes.
|
|
1241
|
-
for (let iOffset = 0; iOffset < fileSize; iOffset += maxPageSize) {
|
|
1242
|
-
// Fetch nOldPages. They can be fetched in one request because
|
|
1243
|
-
// there is now a single version in the file.
|
|
1244
|
-
const oldPages = await blocks.getAll(
|
|
1245
|
-
IDBKeyRange.lowerBound([file.path, -(iOffset + maxPageSize), Infinity]),
|
|
1246
|
-
nOldPages);
|
|
1247
|
-
for (const oldPage of oldPages) {
|
|
1248
|
-
blocks.delete([oldPage.path, oldPage.offset, oldPage.version]);
|
|
1249
|
-
}
|
|
1250
|
-
|
|
1251
|
-
// Convert to new pages.
|
|
1252
|
-
if (nNewPages === 1) {
|
|
1253
|
-
// Combine nOldPages old pages into a new page.
|
|
1254
|
-
const buffer = new Uint8Array(newPageSize);
|
|
1255
|
-
for (const oldPage of oldPages) {
|
|
1256
|
-
buffer.set(oldPage.data, -(iOffset + oldPage.offset));
|
|
1257
|
-
}
|
|
1258
|
-
const newPage = {
|
|
1259
|
-
path: file.path,
|
|
1260
|
-
offset: -iOffset,
|
|
1261
|
-
version,
|
|
1262
|
-
data: buffer
|
|
1263
|
-
};
|
|
1264
|
-
if (newPage.offset === 0) {
|
|
1265
|
-
newPage.fileSize = fileSize;
|
|
1266
|
-
file.block0 = newPage;
|
|
1267
|
-
}
|
|
1268
|
-
blocks.put(newPage);
|
|
1269
|
-
} else {
|
|
1270
|
-
// Split an old page into nNewPages new pages.
|
|
1271
|
-
const oldPage = oldPages[0];
|
|
1272
|
-
for (let i = 0; i < nNewPages; ++i) {
|
|
1273
|
-
const offset = -(iOffset + i * newPageSize);
|
|
1274
|
-
if (-offset >= fileSize) break;
|
|
1275
|
-
const newPage = {
|
|
1276
|
-
path: oldPage.path,
|
|
1277
|
-
offset,
|
|
1278
|
-
version,
|
|
1279
|
-
data: oldPage.data.subarray(i * newPageSize, (i + 1) * newPageSize)
|
|
1280
|
-
}
|
|
1281
|
-
if (newPage.offset === 0) {
|
|
1282
|
-
newPage.fileSize = fileSize;
|
|
1283
|
-
file.block0 = newPage;
|
|
1284
|
-
}
|
|
1285
|
-
blocks.put(newPage);
|
|
1286
|
-
}
|
|
1287
|
-
}
|
|
1288
|
-
}
|
|
1289
|
-
});
|
|
1494
|
+
close() {
|
|
1495
|
+
this.#idb.close();
|
|
1290
1496
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
const request = globalThis.indexedDB.open(idbDatabaseName, 5);
|
|
1296
|
-
request.addEventListener('upgradeneeded', function() {
|
|
1297
|
-
const blocks = request.result.createObjectStore('blocks', {
|
|
1298
|
-
keyPath: ['path', 'offset', 'version']
|
|
1299
|
-
});
|
|
1300
|
-
blocks.createIndex('version', ['path', 'version']);
|
|
1301
|
-
});
|
|
1302
|
-
request.addEventListener('success', () => {
|
|
1303
|
-
resolve(request.result);
|
|
1304
|
-
});
|
|
1305
|
-
request.addEventListener('error', () => {
|
|
1306
|
-
reject(request.error);
|
|
1307
|
-
});
|
|
1308
|
-
});
|
|
1309
|
-
}
|
|
1310
|
-
|
|
1311
|
-
/***/ }),
|
|
1312
|
-
|
|
1313
|
-
/***/ "../../node_modules/@journeyapps/wa-sqlite/src/examples/IDBContext.js":
|
|
1314
|
-
/*!****************************************************************************!*\
|
|
1315
|
-
!*** ../../node_modules/@journeyapps/wa-sqlite/src/examples/IDBContext.js ***!
|
|
1316
|
-
\****************************************************************************/
|
|
1317
|
-
/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => {
|
|
1318
|
-
|
|
1319
|
-
__webpack_require__.r(__webpack_exports__);
|
|
1320
|
-
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
1321
|
-
/* harmony export */ IDBContext: () => (/* binding */ IDBContext)
|
|
1322
|
-
/* harmony export */ });
|
|
1323
|
-
// Copyright 2022 Roy T. Hashimoto. All Rights Reserved.
|
|
1324
|
-
|
|
1325
|
-
// IndexedDB transactions older than this will be replaced.
|
|
1326
|
-
const MAX_TRANSACTION_LIFETIME_MILLIS = 5_000;
|
|
1327
|
-
|
|
1328
|
-
// For debugging.
|
|
1329
|
-
let nextTxId = 0;
|
|
1330
|
-
const mapTxToId = new WeakMap();
|
|
1331
|
-
function log(...args) {
|
|
1332
|
-
// console.debug(...args);
|
|
1333
|
-
}
|
|
1334
|
-
|
|
1335
|
-
// This class manages IDBTransaction and IDBRequest instances. It tries
|
|
1336
|
-
// to reuse transactions to minimize transaction overhead.
|
|
1337
|
-
class IDBContext {
|
|
1338
|
-
/** @type {IDBDatabase} */ #db;
|
|
1339
|
-
/** @type {Promise<IDBDatabase>} */ #dbReady;
|
|
1340
|
-
#txOptions;
|
|
1341
|
-
|
|
1342
|
-
/** @type {IDBTransaction} */ #tx = null;
|
|
1343
|
-
#txTimestamp = 0;
|
|
1344
|
-
#runChain = Promise.resolve();
|
|
1345
|
-
#putChain = Promise.resolve();
|
|
1346
|
-
|
|
1347
|
-
/**
|
|
1348
|
-
* @param {IDBDatabase|Promise<IDBDatabase>} idbDatabase
|
|
1349
|
-
*/
|
|
1350
|
-
constructor(idbDatabase, txOptions = { durability: 'default' }) {
|
|
1351
|
-
this.#dbReady = Promise.resolve(idbDatabase).then(db => this.#db = db);
|
|
1352
|
-
this.#txOptions = txOptions;
|
|
1497
|
+
|
|
1498
|
+
async isReady() {
|
|
1499
|
+
await super.isReady();
|
|
1500
|
+
await this.#isReady;
|
|
1353
1501
|
}
|
|
1354
1502
|
|
|
1355
|
-
|
|
1356
|
-
const
|
|
1357
|
-
|
|
1358
|
-
await this.sync();
|
|
1359
|
-
db.close();
|
|
1503
|
+
getFilename(fileId) {
|
|
1504
|
+
const pathname = this.mapIdToFile.get(fileId).path;
|
|
1505
|
+
return `IDB(${this.name}):${pathname}`
|
|
1360
1506
|
}
|
|
1361
1507
|
|
|
1362
1508
|
/**
|
|
1363
|
-
*
|
|
1364
|
-
*
|
|
1365
|
-
* @param {
|
|
1366
|
-
* @param {
|
|
1367
|
-
|
|
1368
|
-
async run(mode, f) {
|
|
1369
|
-
// Ensure that functions run sequentially.
|
|
1370
|
-
const result = this.#runChain.then(() => this.#run(mode, f));
|
|
1371
|
-
this.#runChain = result.catch(() => {});
|
|
1372
|
-
return result;
|
|
1373
|
-
}
|
|
1374
|
-
|
|
1375
|
-
/**
|
|
1376
|
-
* @param {IDBTransactionMode} mode
|
|
1377
|
-
* @param {(stores: Object.<string, ObjectStore>) => any} f
|
|
1378
|
-
* @returns
|
|
1509
|
+
* @param {string?} zName
|
|
1510
|
+
* @param {number} fileId
|
|
1511
|
+
* @param {number} flags
|
|
1512
|
+
* @param {DataView} pOutFlags
|
|
1513
|
+
* @returns {Promise<number>}
|
|
1379
1514
|
*/
|
|
1380
|
-
async
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1515
|
+
async jOpen(zName, fileId, flags, pOutFlags) {
|
|
1516
|
+
try {
|
|
1517
|
+
const url = new URL(zName || Math.random().toString(36).slice(2), 'file://');
|
|
1518
|
+
const path = url.pathname;
|
|
1519
|
+
|
|
1520
|
+
let meta = await this.#idb.q(({ metadata }) => metadata.get(path));
|
|
1521
|
+
if (!meta && (flags & _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OPEN_CREATE)) {
|
|
1522
|
+
meta = {
|
|
1523
|
+
name: path,
|
|
1524
|
+
fileSize: 0,
|
|
1525
|
+
version: 0
|
|
1526
|
+
};
|
|
1527
|
+
await this.#idb.q(({ metadata }) => metadata.put(meta), 'rw');
|
|
1393
1528
|
}
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
this.#tx = null;
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
// Run the user function with a retry in case the transaction is invalid.
|
|
1401
|
-
for (let i = 0; i < 2; ++i) {
|
|
1402
|
-
if (!this.#tx) {
|
|
1403
|
-
// @ts-ignore
|
|
1404
|
-
this.#tx = db.transaction(db.objectStoreNames, mode, this.#txOptions);
|
|
1405
|
-
const timestamp = this.#txTimestamp = performance.now();
|
|
1406
|
-
|
|
1407
|
-
// Chain the result of every transaction. If any transaction is
|
|
1408
|
-
// aborted then the next sync() call will throw.
|
|
1409
|
-
this.#putChain = this.#putChain.then(() => {
|
|
1410
|
-
return new Promise((resolve, reject) => {
|
|
1411
|
-
this.#tx.addEventListener('complete', event => {
|
|
1412
|
-
resolve();
|
|
1413
|
-
if (this.#tx === event.target) {
|
|
1414
|
-
this.#tx = null;
|
|
1415
|
-
}
|
|
1416
|
-
log(`transaction ${mapTxToId.get(event.target)} complete`);
|
|
1417
|
-
});
|
|
1418
|
-
this.#tx.addEventListener('abort', event => {
|
|
1419
|
-
console.warn('tx abort', (performance.now() - timestamp)/1000);
|
|
1420
|
-
// @ts-ignore
|
|
1421
|
-
const e = event.target.error;
|
|
1422
|
-
reject(e);
|
|
1423
|
-
if (this.#tx === event.target) {
|
|
1424
|
-
this.#tx = null;
|
|
1425
|
-
}
|
|
1426
|
-
log(`transaction ${mapTxToId.get(event.target)} aborted`, e);
|
|
1427
|
-
});
|
|
1428
|
-
});
|
|
1429
|
-
});
|
|
1430
|
-
|
|
1431
|
-
log(`new transaction ${nextTxId} ${mode}`);
|
|
1432
|
-
mapTxToId.set(this.#tx, nextTxId++);
|
|
1529
|
+
|
|
1530
|
+
if (!meta) {
|
|
1531
|
+
throw new Error(`File ${path} not found`);
|
|
1433
1532
|
}
|
|
1434
1533
|
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
if (i) throw e;
|
|
1443
|
-
// console.warn('retrying with new transaction');
|
|
1444
|
-
}
|
|
1534
|
+
const file = new File(path, flags, meta);
|
|
1535
|
+
this.mapIdToFile.set(fileId, file);
|
|
1536
|
+
pOutFlags.setInt32(0, flags, true);
|
|
1537
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OK;
|
|
1538
|
+
} catch (e) {
|
|
1539
|
+
this.lastError = e;
|
|
1540
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_CANTOPEN;
|
|
1445
1541
|
}
|
|
1446
1542
|
}
|
|
1447
1543
|
|
|
1448
|
-
async sync() {
|
|
1449
|
-
// Wait until all transactions since the previous sync have committed.
|
|
1450
|
-
// Throw if any transaction failed.
|
|
1451
|
-
await this.#runChain;
|
|
1452
|
-
await this.#putChain;
|
|
1453
|
-
this.#putChain = Promise.resolve();
|
|
1454
|
-
}
|
|
1455
|
-
}
|
|
1456
|
-
|
|
1457
|
-
/**
|
|
1458
|
-
* Helper to convert IDBRequest to Promise.
|
|
1459
|
-
* @param {IDBRequest} request
|
|
1460
|
-
* @returns {Promise}
|
|
1461
|
-
*/
|
|
1462
|
-
function wrapRequest(request) {
|
|
1463
|
-
return new Promise((resolve, reject) => {
|
|
1464
|
-
request.addEventListener('success', () => resolve(request.result));
|
|
1465
|
-
request.addEventListener('error', () => reject(request.error));
|
|
1466
|
-
});
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
|
-
// IDBObjectStore wrapper passed to IDBContext run functions.
|
|
1470
|
-
class ObjectStore {
|
|
1471
|
-
#objectStore;
|
|
1472
|
-
|
|
1473
1544
|
/**
|
|
1474
|
-
* @param {
|
|
1545
|
+
* @param {string} zName
|
|
1546
|
+
* @param {number} syncDir
|
|
1547
|
+
* @returns {Promise<number>}
|
|
1475
1548
|
*/
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1549
|
+
async jDelete(zName, syncDir) {
|
|
1550
|
+
try {
|
|
1551
|
+
const url = new URL(zName, 'file://');
|
|
1552
|
+
const path = url.pathname;
|
|
1479
1553
|
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
log(`get ${this.#objectStore.name}`, query);
|
|
1486
|
-
const request = this.#objectStore.get(query);
|
|
1487
|
-
return wrapRequest(request);
|
|
1488
|
-
}
|
|
1554
|
+
this.#idb.q(({ metadata, blocks }) => {
|
|
1555
|
+
const range = IDBKeyRange.bound([path, -Infinity], [path, Infinity]);
|
|
1556
|
+
blocks.delete(range);
|
|
1557
|
+
metadata.delete(path);
|
|
1558
|
+
}, 'rw');
|
|
1489
1559
|
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
return wrapRequest(request);
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
/**
|
|
1502
|
-
* @param {IDBValidKey|IDBKeyRange} query
|
|
1503
|
-
* @returns {Promise<IDBValidKey>}
|
|
1504
|
-
*/
|
|
1505
|
-
getKey(query) {
|
|
1506
|
-
log(`getKey ${this.#objectStore.name}`, query);
|
|
1507
|
-
const request = this.#objectStore.getKey(query);
|
|
1508
|
-
return wrapRequest(request);
|
|
1560
|
+
if (syncDir) {
|
|
1561
|
+
await this.#idb.sync(false);
|
|
1562
|
+
}
|
|
1563
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OK;
|
|
1564
|
+
} catch (e) {
|
|
1565
|
+
this.lastError = e;
|
|
1566
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_IOERR_DELETE;
|
|
1567
|
+
}
|
|
1509
1568
|
}
|
|
1510
1569
|
|
|
1511
1570
|
/**
|
|
1512
|
-
* @param {
|
|
1513
|
-
* @param {number}
|
|
1514
|
-
* @
|
|
1571
|
+
* @param {string} zName
|
|
1572
|
+
* @param {number} flags
|
|
1573
|
+
* @param {DataView} pResOut
|
|
1574
|
+
* @returns {Promise<number>}
|
|
1515
1575
|
*/
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
}
|
|
1576
|
+
async jAccess(zName, flags, pResOut) {
|
|
1577
|
+
try {
|
|
1578
|
+
const url = new URL(zName, 'file://');
|
|
1579
|
+
const path = url.pathname;
|
|
1521
1580
|
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
const request = this.#objectStore.put(value, key);
|
|
1530
|
-
return wrapRequest(request);
|
|
1581
|
+
const meta = await this.#idb.q(({ metadata }) => metadata.get(path));
|
|
1582
|
+
pResOut.setInt32(0, meta ? 1 : 0, true);
|
|
1583
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OK;
|
|
1584
|
+
} catch (e) {
|
|
1585
|
+
this.lastError = e;
|
|
1586
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_IOERR_ACCESS;
|
|
1587
|
+
}
|
|
1531
1588
|
}
|
|
1532
1589
|
|
|
1533
1590
|
/**
|
|
1534
|
-
* @param {
|
|
1535
|
-
* @returns {Promise}
|
|
1591
|
+
* @param {number} fileId
|
|
1592
|
+
* @returns {Promise<number>}
|
|
1536
1593
|
*/
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1594
|
+
async jClose(fileId) {
|
|
1595
|
+
try {
|
|
1596
|
+
const file = this.mapIdToFile.get(fileId);
|
|
1597
|
+
this.mapIdToFile.delete(fileId);
|
|
1598
|
+
|
|
1599
|
+
if (file.flags & _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OPEN_DELETEONCLOSE) {
|
|
1600
|
+
await this.#idb.q(({ metadata, blocks }) => {
|
|
1601
|
+
metadata.delete(file.path);
|
|
1602
|
+
blocks.delete(IDBKeyRange.bound([file.path, 0], [file.path, Infinity]));
|
|
1603
|
+
}, 'rw');
|
|
1604
|
+
}
|
|
1548
1605
|
|
|
1549
|
-
|
|
1550
|
-
|
|
1606
|
+
if (file.needsMetadataSync) {
|
|
1607
|
+
this.#idb.q(({ metadata }) => metadata.put(file.metadata), 'rw');
|
|
1608
|
+
}
|
|
1609
|
+
await this.#idb.sync(file.synchronous === 'full');
|
|
1610
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OK;
|
|
1611
|
+
} catch (e) {
|
|
1612
|
+
this.lastError = e;
|
|
1613
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_IOERR_CLOSE;
|
|
1614
|
+
}
|
|
1551
1615
|
}
|
|
1552
|
-
}
|
|
1553
|
-
|
|
1554
|
-
class Index {
|
|
1555
|
-
/** @type {IDBIndex} */ #index;
|
|
1556
1616
|
|
|
1557
1617
|
/**
|
|
1558
|
-
* @param {
|
|
1618
|
+
* @param {number} fileId
|
|
1619
|
+
* @param {Uint8Array} pData
|
|
1620
|
+
* @param {number} iOffset
|
|
1621
|
+
* @returns {Promise<number>}
|
|
1559
1622
|
*/
|
|
1560
|
-
|
|
1561
|
-
|
|
1623
|
+
async jRead(fileId, pData, iOffset) {
|
|
1624
|
+
try {
|
|
1625
|
+
const file = this.mapIdToFile.get(fileId);
|
|
1626
|
+
|
|
1627
|
+
let pDataOffset = 0;
|
|
1628
|
+
while (pDataOffset < pData.byteLength) {
|
|
1629
|
+
// Fetch the IndexedDB block for this file location.
|
|
1630
|
+
const fileOffset = iOffset + pDataOffset;
|
|
1631
|
+
const block = await this.#idb.q(({ blocks }) => {
|
|
1632
|
+
const range = IDBKeyRange.bound([file.path, -fileOffset], [file.path, Infinity]);
|
|
1633
|
+
return blocks.get(range);
|
|
1634
|
+
});
|
|
1635
|
+
|
|
1636
|
+
if (!block || block.data.byteLength - block.offset <= fileOffset) {
|
|
1637
|
+
pData.fill(0, pDataOffset);
|
|
1638
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_IOERR_SHORT_READ;
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
// Copy block data.
|
|
1642
|
+
const dst = pData.subarray(pDataOffset);
|
|
1643
|
+
const srcOffset = fileOffset + block.offset;
|
|
1644
|
+
const nBytesToCopy = Math.min(
|
|
1645
|
+
Math.max(block.data.byteLength - srcOffset, 0),
|
|
1646
|
+
dst.byteLength);
|
|
1647
|
+
dst.set(block.data.subarray(srcOffset, srcOffset + nBytesToCopy));
|
|
1648
|
+
pDataOffset += nBytesToCopy;
|
|
1649
|
+
}
|
|
1650
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OK;
|
|
1651
|
+
} catch (e) {
|
|
1652
|
+
this.lastError = e;
|
|
1653
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_IOERR_READ;
|
|
1654
|
+
}
|
|
1562
1655
|
}
|
|
1563
1656
|
|
|
1564
1657
|
/**
|
|
1565
|
-
* @param {
|
|
1566
|
-
* @param {
|
|
1567
|
-
* @
|
|
1658
|
+
* @param {number} fileId
|
|
1659
|
+
* @param {Uint8Array} pData
|
|
1660
|
+
* @param {number} iOffset
|
|
1661
|
+
* @returns {number}
|
|
1568
1662
|
*/
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
/* harmony export */ WebLocksExclusive: () => (/* binding */ WebLocksExclusive),
|
|
1588
|
-
/* harmony export */ WebLocksShared: () => (/* binding */ WebLocksShared)
|
|
1589
|
-
/* harmony export */ });
|
|
1590
|
-
/* harmony import */ var _VFS_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ../VFS.js */ "../../node_modules/@journeyapps/wa-sqlite/src/VFS.js");
|
|
1591
|
-
// Copyright 2022 Roy T. Hashimoto. All Rights Reserved.
|
|
1663
|
+
jWrite(fileId, pData, iOffset) {
|
|
1664
|
+
try {
|
|
1665
|
+
const file = this.mapIdToFile.get(fileId);
|
|
1666
|
+
if (file.flags & _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OPEN_MAIN_DB) {
|
|
1667
|
+
if (!file.rollback) {
|
|
1668
|
+
// Begin a new write transaction.
|
|
1669
|
+
// Add pendingVersion to the metadata in IndexedDB. If we crash
|
|
1670
|
+
// during the transaction, this lets subsequent connections
|
|
1671
|
+
// know to remove blocks from the failed transaction.
|
|
1672
|
+
const pending = Object.assign(
|
|
1673
|
+
{ pendingVersion: file.metadata.version - 1 },
|
|
1674
|
+
file.metadata);
|
|
1675
|
+
this.#idb.q(({ metadata }) => metadata.put(pending), 'rw', file.txOptions);
|
|
1676
|
+
|
|
1677
|
+
file.rollback = Object.assign({}, file.metadata);
|
|
1678
|
+
file.metadata.version--;
|
|
1679
|
+
}
|
|
1680
|
+
}
|
|
1592
1681
|
|
|
1682
|
+
if (file.flags & _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OPEN_MAIN_DB) {
|
|
1683
|
+
file.changedPages.add(iOffset);
|
|
1684
|
+
}
|
|
1593
1685
|
|
|
1594
|
-
const
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1686
|
+
const data = pData.slice();
|
|
1687
|
+
const version = file.metadata.version;
|
|
1688
|
+
const isOverwrite = iOffset < file.metadata.fileSize;
|
|
1689
|
+
if (!isOverwrite ||
|
|
1690
|
+
file.flags & _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OPEN_MAIN_DB ||
|
|
1691
|
+
file.flags & _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OPEN_TEMP_DB) {
|
|
1692
|
+
const block = {
|
|
1693
|
+
path: file.path,
|
|
1694
|
+
offset: -iOffset,
|
|
1695
|
+
version: version,
|
|
1696
|
+
data: pData.slice()
|
|
1697
|
+
};
|
|
1698
|
+
this.#idb.q(({ blocks }) => {
|
|
1699
|
+
blocks.put(block);
|
|
1700
|
+
file.changedPages.add(iOffset);
|
|
1701
|
+
}, 'rw', file.txOptions);
|
|
1702
|
+
} else {
|
|
1703
|
+
this.#idb.q(async ({ blocks }) => {
|
|
1704
|
+
// Read the existing block.
|
|
1705
|
+
const range = IDBKeyRange.bound(
|
|
1706
|
+
[file.path, -iOffset],
|
|
1707
|
+
[file.path, Infinity]);
|
|
1708
|
+
const block = await blocks.get(range);
|
|
1709
|
+
|
|
1710
|
+
// Modify the block data.
|
|
1711
|
+
// @ts-ignore
|
|
1712
|
+
block.data.subarray(iOffset + block.offset).set(data);
|
|
1600
1713
|
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1714
|
+
// Write back.
|
|
1715
|
+
blocks.put(block);
|
|
1716
|
+
}, 'rw', file.txOptions);
|
|
1604
1717
|
|
|
1605
|
-
|
|
1718
|
+
}
|
|
1606
1719
|
|
|
1607
|
-
|
|
1608
|
-
|
|
1720
|
+
if (file.metadata.fileSize < iOffset + pData.length) {
|
|
1721
|
+
file.metadata.fileSize = iOffset + pData.length;
|
|
1722
|
+
file.needsMetadataSync = true;
|
|
1723
|
+
}
|
|
1724
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OK;
|
|
1725
|
+
} catch (e) {
|
|
1726
|
+
this.lastError = e;
|
|
1727
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_IOERR_WRITE;
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1609
1730
|
|
|
1610
1731
|
/**
|
|
1611
|
-
* @param {number}
|
|
1612
|
-
* @
|
|
1732
|
+
* @param {number} fileId
|
|
1733
|
+
* @param {number} iSize
|
|
1734
|
+
* @returns {number}
|
|
1613
1735
|
*/
|
|
1614
|
-
|
|
1615
|
-
|
|
1736
|
+
jTruncate(fileId, iSize) {
|
|
1737
|
+
try {
|
|
1738
|
+
const file = this.mapIdToFile.get(fileId);
|
|
1739
|
+
if (iSize < file.metadata.fileSize) {
|
|
1740
|
+
this.#idb.q(({ blocks }) => {
|
|
1741
|
+
const range = IDBKeyRange.bound(
|
|
1742
|
+
[file.path, -Infinity],
|
|
1743
|
+
[file.path, -iSize, Infinity]);
|
|
1744
|
+
blocks.delete(range);
|
|
1745
|
+
}, 'rw', file.txOptions);
|
|
1746
|
+
file.metadata.fileSize = iSize;
|
|
1747
|
+
file.needsMetadataSync = true;
|
|
1748
|
+
}
|
|
1749
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OK;
|
|
1750
|
+
} catch (e) {
|
|
1751
|
+
this.lastError = e;
|
|
1752
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_IOERR_TRUNCATE;
|
|
1753
|
+
}
|
|
1616
1754
|
}
|
|
1617
1755
|
|
|
1618
1756
|
/**
|
|
1757
|
+
* @param {number} fileId
|
|
1619
1758
|
* @param {number} flags
|
|
1620
|
-
* @returns {Promise<
|
|
1759
|
+
* @returns {Promise<number>}
|
|
1621
1760
|
*/
|
|
1622
|
-
async
|
|
1623
|
-
|
|
1624
|
-
|
|
1761
|
+
async jSync(fileId, flags) {
|
|
1762
|
+
try {
|
|
1763
|
+
const file = this.mapIdToFile.get(fileId);
|
|
1764
|
+
if (file.needsMetadataSync) {
|
|
1765
|
+
this.#idb.q(({ metadata }) => metadata.put(file.metadata), 'rw', file.txOptions);
|
|
1766
|
+
file.needsMetadataSync = false;
|
|
1767
|
+
}
|
|
1625
1768
|
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1769
|
+
if (file.flags & _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OPEN_MAIN_DB) {
|
|
1770
|
+
// Sync is only needed here for durability. Visibility for other
|
|
1771
|
+
// connections is ensured in jUnlock().
|
|
1772
|
+
if (file.synchronous === 'full') {
|
|
1773
|
+
await this.#idb.sync(true);
|
|
1774
|
+
}
|
|
1775
|
+
} else {
|
|
1776
|
+
await this.#idb.sync(file.synchronous === 'full');
|
|
1777
|
+
}
|
|
1778
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OK;
|
|
1779
|
+
} catch (e) {
|
|
1780
|
+
this.lastError = e;
|
|
1781
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_IOERR_FSYNC;
|
|
1782
|
+
}
|
|
1631
1783
|
}
|
|
1632
1784
|
|
|
1633
1785
|
/**
|
|
1634
|
-
*
|
|
1635
|
-
* @param {
|
|
1636
|
-
* @
|
|
1786
|
+
* @param {number} fileId
|
|
1787
|
+
* @param {DataView} pSize64
|
|
1788
|
+
* @returns {number}
|
|
1637
1789
|
*/
|
|
1638
|
-
|
|
1639
|
-
const targetState = flags & LOCK_TYPE_MASK;
|
|
1790
|
+
jFileSize(fileId, pSize64) {
|
|
1640
1791
|
try {
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
await (this.#pending = this.#pending.then(call, call));
|
|
1645
|
-
this.#state = targetState;
|
|
1646
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_OK;
|
|
1792
|
+
const file = this.mapIdToFile.get(fileId);
|
|
1793
|
+
pSize64.setBigInt64(0, BigInt(file.metadata.fileSize), true);
|
|
1794
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OK;
|
|
1647
1795
|
} catch (e) {
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
}
|
|
1651
|
-
console.error(e);
|
|
1652
|
-
return _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_IOERR_LOCK;
|
|
1796
|
+
this.lastError = e;
|
|
1797
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_IOERR_FSTAT;
|
|
1653
1798
|
}
|
|
1654
1799
|
}
|
|
1655
1800
|
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1801
|
+
/**
|
|
1802
|
+
* @param {number} fileId
|
|
1803
|
+
* @param {number} lockType
|
|
1804
|
+
* @returns {Promise<number>}
|
|
1805
|
+
*/
|
|
1806
|
+
async jLock(fileId, lockType) {
|
|
1807
|
+
// Call the actual lock implementation.
|
|
1808
|
+
const file = this.mapIdToFile.get(fileId);
|
|
1809
|
+
const result = await super.jLock(fileId, lockType);
|
|
1810
|
+
|
|
1811
|
+
if (lockType === _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_LOCK_SHARED) {
|
|
1812
|
+
// Update metadata.
|
|
1813
|
+
file.metadata = await this.#idb.q(async ({ metadata, blocks }) => {
|
|
1814
|
+
// @ts-ignore
|
|
1815
|
+
/** @type {Metadata} */ const m = await metadata.get(file.path);
|
|
1816
|
+
if (m.pendingVersion) {
|
|
1817
|
+
console.warn(`removing failed transaction ${m.pendingVersion}`);
|
|
1818
|
+
await new Promise((resolve, reject) => {
|
|
1819
|
+
const range = IDBKeyRange.bound([m.name, -Infinity], [m.name, Infinity]);
|
|
1820
|
+
const request = blocks.openCursor(range);
|
|
1821
|
+
request.onsuccess = () => {
|
|
1822
|
+
const cursor = request.result;
|
|
1823
|
+
if (cursor) {
|
|
1824
|
+
const block = cursor.value;
|
|
1825
|
+
if (block.version < m.version) {
|
|
1826
|
+
cursor.delete();
|
|
1827
|
+
}
|
|
1828
|
+
cursor.continue();
|
|
1829
|
+
} else {
|
|
1830
|
+
resolve();
|
|
1831
|
+
}
|
|
1832
|
+
};
|
|
1833
|
+
request.onerror = () => reject(request.error);
|
|
1834
|
+
})
|
|
1666
1835
|
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_RESERVED:
|
|
1670
|
-
return this._SHAREDtoRESERVED();
|
|
1671
|
-
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_EXCLUSIVE:
|
|
1672
|
-
return this._SHAREDtoEXCLUSIVE();
|
|
1673
|
-
default:
|
|
1674
|
-
throw new Error(`unexpected transition ${this.#state} -> ${targetState}`);
|
|
1836
|
+
delete m.pendingVersion;
|
|
1837
|
+
metadata.put(m);
|
|
1675
1838
|
}
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
switch (targetState) {
|
|
1679
|
-
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_EXCLUSIVE:
|
|
1680
|
-
return this._RESERVEDtoEXCLUSIVE();
|
|
1681
|
-
default:
|
|
1682
|
-
throw new Error(`unexpected transition ${this.#state} -> ${targetState}`);
|
|
1683
|
-
}
|
|
1684
|
-
|
|
1685
|
-
default:
|
|
1686
|
-
throw new Error(`unexpected transition ${this.#state} -> ${targetState}`);
|
|
1839
|
+
return m;
|
|
1840
|
+
}, 'rw', file.txOptions);
|
|
1687
1841
|
}
|
|
1842
|
+
return result;
|
|
1688
1843
|
}
|
|
1689
1844
|
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
default:
|
|
1700
|
-
throw new Error(`unexpected transition ${this.#state} -> ${targetState}`);
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
|
-
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_RESERVED:
|
|
1704
|
-
switch (targetState) {
|
|
1705
|
-
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_SHARED:
|
|
1706
|
-
return this._RESERVEDtoSHARED();
|
|
1707
|
-
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_NONE:
|
|
1708
|
-
return this._RESERVEDtoNONE();
|
|
1709
|
-
default:
|
|
1710
|
-
throw new Error(`unexpected transition ${this.#state} -> ${targetState}`);
|
|
1711
|
-
}
|
|
1712
|
-
|
|
1713
|
-
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_SHARED:
|
|
1714
|
-
switch (targetState) {
|
|
1715
|
-
case _VFS_js__WEBPACK_IMPORTED_MODULE_0__.SQLITE_LOCK_NONE:
|
|
1716
|
-
return this._SHAREDtoNONE();
|
|
1717
|
-
default:
|
|
1718
|
-
throw new Error(`unexpected transition ${this.#state} -> ${targetState}`);
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
|
-
default:
|
|
1722
|
-
throw new Error(`unexpected transition ${this.#state} -> ${targetState}`);
|
|
1845
|
+
/**
|
|
1846
|
+
* @param {number} fileId
|
|
1847
|
+
* @param {number} lockType
|
|
1848
|
+
* @returns {Promise<number>}
|
|
1849
|
+
*/
|
|
1850
|
+
async jUnlock(fileId, lockType) {
|
|
1851
|
+
if (lockType === _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_LOCK_NONE) {
|
|
1852
|
+
const file = this.mapIdToFile.get(fileId);
|
|
1853
|
+
await this.#idb.sync(file.synchronous === 'full');
|
|
1723
1854
|
}
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
|
-
async _NONEtoSHARED() {
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
async _SHAREDtoEXCLUSIVE() {
|
|
1730
|
-
await this._SHAREDtoRESERVED();
|
|
1731
|
-
await this._RESERVEDtoEXCLUSIVE();
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
|
-
async _SHAREDtoRESERVED() {
|
|
1735
|
-
}
|
|
1736
|
-
|
|
1737
|
-
async _RESERVEDtoEXCLUSIVE() {
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
async _EXCLUSIVEtoRESERVED() {
|
|
1741
|
-
}
|
|
1742
1855
|
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
await this._RESERVEDtoSHARED();
|
|
1746
|
-
}
|
|
1747
|
-
|
|
1748
|
-
async _EXCLUSIVEtoNONE() {
|
|
1749
|
-
await this._EXCLUSIVEtoRESERVED();
|
|
1750
|
-
await this._RESERVEDtoSHARED();
|
|
1751
|
-
await this._SHAREDtoNONE();
|
|
1752
|
-
}
|
|
1753
|
-
|
|
1754
|
-
async _RESERVEDtoSHARED() {
|
|
1755
|
-
}
|
|
1756
|
-
|
|
1757
|
-
async _RESERVEDtoNONE() {
|
|
1758
|
-
await this._RESERVEDtoSHARED();
|
|
1759
|
-
await this._SHAREDtoNONE();
|
|
1760
|
-
}
|
|
1761
|
-
|
|
1762
|
-
async _SHAREDtoNONE() {
|
|
1856
|
+
// Call the actual unlock implementation.
|
|
1857
|
+
return super.jUnlock(fileId, lockType);
|
|
1763
1858
|
}
|
|
1764
1859
|
|
|
1765
1860
|
/**
|
|
1766
|
-
* @param {
|
|
1767
|
-
* @param {
|
|
1768
|
-
* @
|
|
1861
|
+
* @param {number} fileId
|
|
1862
|
+
* @param {number} op
|
|
1863
|
+
* @param {DataView} pArg
|
|
1864
|
+
* @returns {number|Promise<number>}
|
|
1769
1865
|
*/
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1866
|
+
jFileControl(fileId, op, pArg) {
|
|
1867
|
+
try {
|
|
1868
|
+
const file = this.mapIdToFile.get(fileId);
|
|
1869
|
+
switch (op) {
|
|
1870
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_FCNTL_PRAGMA:
|
|
1871
|
+
const key = extractString(pArg, 4);
|
|
1872
|
+
const value = extractString(pArg, 8);
|
|
1873
|
+
this.log?.('xFileControl', file.path, 'PRAGMA', key, value);
|
|
1874
|
+
const setPragmaResponse = response => {
|
|
1875
|
+
const encoded = new TextEncoder().encode(response);
|
|
1876
|
+
const out = this._module._sqlite3_malloc(encoded.byteLength);
|
|
1877
|
+
const outArray = this._module.HEAPU8.subarray(out, out + encoded.byteLength);
|
|
1878
|
+
outArray.set(encoded);
|
|
1879
|
+
pArg.setUint32(0, out, true);
|
|
1880
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_ERROR;
|
|
1881
|
+
};
|
|
1882
|
+
switch (key.toLowerCase()) {
|
|
1883
|
+
case 'page_size':
|
|
1884
|
+
if (file.flags & _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OPEN_MAIN_DB) {
|
|
1885
|
+
// Don't allow changing the page size.
|
|
1886
|
+
if (value && file.metadata.fileSize) {
|
|
1887
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_ERROR;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
break;
|
|
1891
|
+
case 'synchronous':
|
|
1892
|
+
if (value) {
|
|
1893
|
+
switch (value.toLowerCase()) {
|
|
1894
|
+
case '0':
|
|
1895
|
+
case 'off':
|
|
1896
|
+
file.synchronous = 'off';
|
|
1897
|
+
file.txOptions = { durability: 'relaxed' };
|
|
1898
|
+
break;
|
|
1899
|
+
case '1':
|
|
1900
|
+
case 'normal':
|
|
1901
|
+
file.synchronous = 'normal';
|
|
1902
|
+
file.txOptions = { durability: 'relaxed' };
|
|
1903
|
+
break;
|
|
1904
|
+
case '2':
|
|
1905
|
+
case '3':
|
|
1906
|
+
case 'full':
|
|
1907
|
+
case 'extra':
|
|
1908
|
+
file.synchronous = 'full';
|
|
1909
|
+
file.txOptions = { durability: 'strict' };
|
|
1910
|
+
break;
|
|
1911
|
+
}
|
|
1912
|
+
}
|
|
1913
|
+
break;
|
|
1914
|
+
case 'write_hint':
|
|
1915
|
+
return super.jFileControl(fileId, _WebLocksMixin_js__WEBPACK_IMPORTED_MODULE_2__.WebLocksMixin.WRITE_HINT_OP_CODE, null);
|
|
1916
|
+
}
|
|
1917
|
+
break;
|
|
1918
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_FCNTL_SYNC:
|
|
1919
|
+
this.log?.('xFileControl', file.path, 'SYNC');
|
|
1920
|
+
const commitMetadata = Object.assign({}, file.metadata);
|
|
1921
|
+
const prevFileSize = file.rollback.fileSize
|
|
1922
|
+
this.#idb.q(({ metadata, blocks }) => {
|
|
1923
|
+
metadata.put(commitMetadata);
|
|
1924
|
+
|
|
1925
|
+
// Remove old page versions.
|
|
1926
|
+
for (const offset of file.changedPages) {
|
|
1927
|
+
if (offset < prevFileSize) {
|
|
1928
|
+
const range = IDBKeyRange.bound(
|
|
1929
|
+
[file.path, -offset, commitMetadata.version],
|
|
1930
|
+
[file.path, -offset, Infinity],
|
|
1931
|
+
true);
|
|
1932
|
+
blocks.delete(range);
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
file.changedPages.clear();
|
|
1936
|
+
}, 'rw', file.txOptions);
|
|
1937
|
+
file.needsMetadataSync = false;
|
|
1938
|
+
file.rollback = null;
|
|
1939
|
+
break;
|
|
1940
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_FCNTL_BEGIN_ATOMIC_WRITE:
|
|
1941
|
+
// Every write transaction is atomic, so this is a no-op.
|
|
1942
|
+
this.log?.('xFileControl', file.path, 'BEGIN_ATOMIC_WRITE');
|
|
1943
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OK;
|
|
1944
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_FCNTL_COMMIT_ATOMIC_WRITE:
|
|
1945
|
+
// Every write transaction is atomic, so this is a no-op.
|
|
1946
|
+
this.log?.('xFileControl', file.path, 'COMMIT_ATOMIC_WRITE');
|
|
1947
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OK;
|
|
1948
|
+
case _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_FCNTL_ROLLBACK_ATOMIC_WRITE:
|
|
1949
|
+
this.log?.('xFileControl', file.path, 'ROLLBACK_ATOMIC_WRITE');
|
|
1950
|
+
file.metadata = file.rollback;
|
|
1951
|
+
const rollbackMetadata = Object.assign({}, file.metadata);
|
|
1952
|
+
this.#idb.q(({ metadata, blocks }) => {
|
|
1953
|
+
metadata.put(rollbackMetadata);
|
|
1954
|
+
|
|
1955
|
+
// Remove pages.
|
|
1956
|
+
for (const offset of file.changedPages) {
|
|
1957
|
+
blocks.delete([file.path, -offset, rollbackMetadata.version - 1]);
|
|
1958
|
+
}
|
|
1959
|
+
file.changedPages.clear();
|
|
1960
|
+
}, 'rw', file.txOptions);
|
|
1961
|
+
file.needsMetadataSync = false;
|
|
1962
|
+
file.rollback = null;
|
|
1963
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OK;
|
|
1781
1964
|
}
|
|
1782
|
-
})
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
*/
|
|
1788
|
-
_releaseWebLock(lockName) {
|
|
1789
|
-
this.#releasers.get(lockName)?.();
|
|
1790
|
-
this.#releasers.delete(lockName);
|
|
1965
|
+
} catch (e) {
|
|
1966
|
+
this.lastError = e;
|
|
1967
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_IOERR;
|
|
1968
|
+
}
|
|
1969
|
+
return super.jFileControl(fileId, op, pArg);
|
|
1791
1970
|
}
|
|
1792
|
-
|
|
1971
|
+
|
|
1793
1972
|
/**
|
|
1794
|
-
* @param {
|
|
1973
|
+
* @param {number} pFile
|
|
1974
|
+
* @returns {number|Promise<number>}
|
|
1795
1975
|
*/
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1976
|
+
jDeviceCharacteristics(pFile) {
|
|
1977
|
+
return 0
|
|
1978
|
+
| _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_IOCAP_BATCH_ATOMIC
|
|
1979
|
+
| _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
|
|
1799
1980
|
}
|
|
1800
1981
|
|
|
1801
1982
|
/**
|
|
1802
|
-
* @
|
|
1983
|
+
* @param {Uint8Array} zBuf
|
|
1984
|
+
* @returns {number|Promise<number>}
|
|
1803
1985
|
*/
|
|
1804
|
-
|
|
1805
|
-
if (this.
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1986
|
+
jGetLastError(zBuf) {
|
|
1987
|
+
if (this.lastError) {
|
|
1988
|
+
console.error(this.lastError);
|
|
1989
|
+
const outputArray = zBuf.subarray(0, zBuf.byteLength - 1);
|
|
1990
|
+
const { written } = new TextEncoder().encodeInto(this.lastError.message, outputArray);
|
|
1991
|
+
zBuf[written] = 0;
|
|
1809
1992
|
}
|
|
1810
|
-
return
|
|
1993
|
+
return _VFS_js__WEBPACK_IMPORTED_MODULE_1__.SQLITE_OK
|
|
1811
1994
|
}
|
|
1812
1995
|
}
|
|
1813
1996
|
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
super();
|
|
1820
|
-
this._lockName = name + '-outer';
|
|
1821
|
-
this._reservedName = name + '-reserved';
|
|
1997
|
+
function extractString(dataView, offset) {
|
|
1998
|
+
const p = dataView.getUint32(offset, true);
|
|
1999
|
+
if (p) {
|
|
2000
|
+
const chars = new Uint8Array(dataView.buffer, p);
|
|
2001
|
+
return new TextDecoder().decode(chars.subarray(0, chars.indexOf(0)));
|
|
1822
2002
|
}
|
|
2003
|
+
return null;
|
|
2004
|
+
}
|
|
1823
2005
|
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
return mode === 'exclusive';
|
|
1827
|
-
}
|
|
2006
|
+
class IDBContext {
|
|
2007
|
+
/** @type {IDBDatabase} */ #database;
|
|
1828
2008
|
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
2009
|
+
/** @type {Promise} */ #chain = null;
|
|
2010
|
+
/** @type {Promise<any>} */ #txComplete = Promise.resolve();
|
|
2011
|
+
/** @type {IDBRequest?} */ #request = null;
|
|
2012
|
+
/** @type {WeakSet<IDBTransaction>} */ #txPending = new WeakSet();
|
|
2013
|
+
|
|
2014
|
+
log = null;
|
|
2015
|
+
|
|
2016
|
+
static async create(name) {
|
|
2017
|
+
const database = await new Promise((resolve, reject) => {
|
|
2018
|
+
const request = indexedDB.open(name, 6);
|
|
2019
|
+
request.onupgradeneeded = async event => {
|
|
2020
|
+
const db = request.result;
|
|
2021
|
+
if (event.oldVersion) {
|
|
2022
|
+
console.log(`Upgrading IndexedDB from version ${event.oldVersion}`);
|
|
2023
|
+
}
|
|
2024
|
+
switch (event.oldVersion) {
|
|
2025
|
+
case 0:
|
|
2026
|
+
// Start with the original schema.
|
|
2027
|
+
db.createObjectStore('blocks', { keyPath: ['path', 'offset', 'version']})
|
|
2028
|
+
.createIndex('version', ['path', 'version']);
|
|
2029
|
+
// fall through intentionally
|
|
2030
|
+
case 5:
|
|
2031
|
+
const tx = request.transaction;
|
|
2032
|
+
const blocks = tx.objectStore('blocks');
|
|
2033
|
+
blocks.deleteIndex('version');
|
|
2034
|
+
const metadata = db.createObjectStore('metadata', { keyPath: 'name' });
|
|
2035
|
+
|
|
2036
|
+
await new Promise((resolve, reject) => {
|
|
2037
|
+
// Iterate over all the blocks.
|
|
2038
|
+
let lastBlock = {};
|
|
2039
|
+
const request = tx.objectStore('blocks').openCursor();
|
|
2040
|
+
request.onsuccess = () => {
|
|
2041
|
+
const cursor = request.result;
|
|
2042
|
+
if (cursor) {
|
|
2043
|
+
const block = cursor.value;
|
|
2044
|
+
if (typeof block.offset !== 'number' ||
|
|
2045
|
+
(block.path === lastBlock.path && block.offset === lastBlock.offset)) {
|
|
2046
|
+
// Remove superceded block (or the "purge" info).
|
|
2047
|
+
cursor.delete();
|
|
2048
|
+
} else if (block.offset === 0) {
|
|
2049
|
+
// Move metadata to its own store.
|
|
2050
|
+
metadata.put({
|
|
2051
|
+
name: block.path,
|
|
2052
|
+
fileSize: block.fileSize,
|
|
2053
|
+
version: block.version
|
|
2054
|
+
});
|
|
2055
|
+
|
|
2056
|
+
delete block.fileSize;
|
|
2057
|
+
cursor.update(block);
|
|
2058
|
+
}
|
|
2059
|
+
lastBlock = block;
|
|
2060
|
+
cursor.continue();
|
|
2061
|
+
} else {
|
|
2062
|
+
resolve();
|
|
2063
|
+
}
|
|
2064
|
+
};
|
|
2065
|
+
request.onerror = () => reject(request.error);
|
|
2066
|
+
});
|
|
2067
|
+
break;
|
|
2068
|
+
}
|
|
2069
|
+
};
|
|
2070
|
+
request.onsuccess = () => resolve(request.result);
|
|
2071
|
+
request.onerror = () => reject(request.error);
|
|
1833
2072
|
});
|
|
2073
|
+
return new IDBContext(database);
|
|
1834
2074
|
}
|
|
1835
2075
|
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
mode: 'exclusive',
|
|
1839
|
-
signal: this._getTimeoutSignal()
|
|
1840
|
-
});
|
|
2076
|
+
constructor(database) {
|
|
2077
|
+
this.#database = database;
|
|
1841
2078
|
}
|
|
1842
2079
|
|
|
1843
|
-
|
|
1844
|
-
this.
|
|
2080
|
+
close() {
|
|
2081
|
+
this.#database.close();
|
|
1845
2082
|
}
|
|
1846
2083
|
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
}
|
|
2084
|
+
/**
|
|
2085
|
+
* @param {(stores: Object.<string, IDBObjectStore>) => any} f
|
|
2086
|
+
* @param {'ro'|'rw'} mode
|
|
2087
|
+
* @returns {Promise<any>}
|
|
2088
|
+
*/
|
|
2089
|
+
q(f, mode = 'ro', options = {}) {
|
|
2090
|
+
/** @type {IDBTransactionMode} */
|
|
2091
|
+
const txMode = mode === 'ro' ? 'readonly' : 'readwrite';
|
|
2092
|
+
const txOptions = Object.assign({
|
|
2093
|
+
/** @type {IDBTransactionDurability} */ durability: 'default'
|
|
2094
|
+
}, options);
|
|
1851
2095
|
|
|
1852
|
-
|
|
1853
|
-
|
|
2096
|
+
// Ensure that queries run sequentially. If any function rejects,
|
|
2097
|
+
// or any request has an error, or the transaction does not commit,
|
|
2098
|
+
// then no subsequent functions will run until sync() or reset().
|
|
2099
|
+
this.#chain = (this.#chain || Promise.resolve())
|
|
2100
|
+
.then(() => this.#q(f, txMode, txOptions));
|
|
2101
|
+
return this.#chain;
|
|
2102
|
+
}
|
|
1854
2103
|
|
|
1855
2104
|
/**
|
|
1856
|
-
* @param {string}
|
|
2105
|
+
* @param {(stores: Object.<string, IDBObjectStore>) => any} f
|
|
2106
|
+
* @param {IDBTransactionMode} mode
|
|
2107
|
+
* @param {IDBTransactionOptions} options
|
|
2108
|
+
* @returns {Promise<any>}
|
|
1857
2109
|
*/
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
this
|
|
1861
|
-
|
|
1862
|
-
|
|
2110
|
+
async #q(f, mode, options) {
|
|
2111
|
+
/** @type {IDBTransaction} */ let tx;
|
|
2112
|
+
if (this.#request &&
|
|
2113
|
+
this.#txPending.has(this.#request.transaction) &&
|
|
2114
|
+
this.#request.transaction.mode >= mode &&
|
|
2115
|
+
this.#request.transaction.durability === options.durability) {
|
|
2116
|
+
// The previous request transaction is compatible and has
|
|
2117
|
+
// not yet completed.
|
|
2118
|
+
tx = this.#request.transaction;
|
|
2119
|
+
|
|
2120
|
+
// If the previous request is pending, wait for it to complete.
|
|
2121
|
+
// This ensures that the transaction will be active.
|
|
2122
|
+
if (this.#request.readyState === 'pending') {
|
|
2123
|
+
await new Promise(resolve => {
|
|
2124
|
+
this.#request.addEventListener('success', resolve, { once: true });
|
|
2125
|
+
this.#request.addEventListener('error', resolve, { once: true });
|
|
2126
|
+
});
|
|
2127
|
+
}
|
|
2128
|
+
}
|
|
1863
2129
|
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
2130
|
+
for (let i = 0; i < 2; ++i) {
|
|
2131
|
+
if (!tx) {
|
|
2132
|
+
// The current transaction is missing or doesn't match so
|
|
2133
|
+
// replace it with a new one. wait for the previous
|
|
2134
|
+
// transaction to complete so the lifetimes do not overlap.
|
|
2135
|
+
await this.#txComplete;
|
|
1868
2136
|
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
2137
|
+
// Create the new transaction.
|
|
2138
|
+
// @ts-ignore
|
|
2139
|
+
tx = this.#database.transaction(this.#database.objectStoreNames, mode, options);
|
|
2140
|
+
this.log?.('IDBTransaction open', mode);
|
|
2141
|
+
this.#txPending.add(tx);
|
|
2142
|
+
this.#txComplete = new Promise((resolve, reject) => {
|
|
2143
|
+
tx.addEventListener('complete', () => {
|
|
2144
|
+
this.log?.('IDBTransaction complete');
|
|
2145
|
+
this.#txPending.delete(tx);
|
|
2146
|
+
resolve();
|
|
2147
|
+
});
|
|
2148
|
+
tx.addEventListener('abort', () => {
|
|
2149
|
+
this.#txPending.delete(tx);
|
|
2150
|
+
reject(new Error('transaction aborted'));
|
|
2151
|
+
});
|
|
2152
|
+
});
|
|
2153
|
+
}
|
|
1880
2154
|
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
const isLocked = await this._acquireWebLock(this._outerName, {
|
|
1886
|
-
mode: 'exclusive',
|
|
1887
|
-
ifAvailable: true
|
|
2155
|
+
// @ts-ignore
|
|
2156
|
+
// Create object store proxies.
|
|
2157
|
+
const objectStores = [...tx.objectStoreNames].map(name => {
|
|
2158
|
+
return [name, this.proxyStoreOrIndex(tx.objectStore(name))];
|
|
1888
2159
|
});
|
|
1889
|
-
if (isLocked) break;
|
|
1890
2160
|
|
|
1891
|
-
|
|
1892
|
-
//
|
|
1893
|
-
|
|
2161
|
+
try {
|
|
2162
|
+
// Execute the function.
|
|
2163
|
+
return await f(Object.fromEntries(objectStores));
|
|
2164
|
+
} catch (e) {
|
|
2165
|
+
// Use a new transaction if this one was inactive. This will
|
|
2166
|
+
// happen if the last request in the transaction completed
|
|
2167
|
+
// in a previous task but the transaction has not yet committed.
|
|
2168
|
+
if (!i && e.name === 'TransactionInactiveError') {
|
|
2169
|
+
this.log?.('TransactionInactiveError, retrying');
|
|
2170
|
+
tx = null;
|
|
2171
|
+
continue;
|
|
2172
|
+
}
|
|
2173
|
+
throw e;
|
|
1894
2174
|
}
|
|
1895
|
-
|
|
1896
|
-
await new Promise(resolve => setTimeout(resolve, timeoutMillis));
|
|
1897
|
-
timeoutMillis = Math.min(2 * timeoutMillis, this.maxRetryMillis);
|
|
1898
2175
|
}
|
|
1899
|
-
this._releaseWebLock(this._innerName);
|
|
1900
2176
|
}
|
|
1901
2177
|
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
2178
|
+
/**
|
|
2179
|
+
* Object store methods that return an IDBRequest, except for cursor
|
|
2180
|
+
* creation, are wrapped to return a Promise. In addition, the
|
|
2181
|
+
* request is used internally for chaining.
|
|
2182
|
+
* @param {IDBObjectStore} objectStore
|
|
2183
|
+
* @returns
|
|
2184
|
+
*/
|
|
2185
|
+
proxyStoreOrIndex(objectStore) {
|
|
2186
|
+
return new Proxy(objectStore, {
|
|
2187
|
+
get: (target, property, receiver) => {
|
|
2188
|
+
const result = Reflect.get(target, property, receiver);
|
|
2189
|
+
if (typeof result === 'function') {
|
|
2190
|
+
return (...args) => {
|
|
2191
|
+
const maybeRequest = Reflect.apply(result, target, args);
|
|
2192
|
+
// @ts-ignore
|
|
2193
|
+
if (maybeRequest instanceof IDBRequest && !property.endsWith('Cursor')) {
|
|
2194
|
+
// // Debug logging.
|
|
2195
|
+
// this.log?.(`${target.name}.${String(property)}`, args);
|
|
2196
|
+
// maybeRequest.addEventListener('success', () => {
|
|
2197
|
+
// this.log?.(`${target.name}.${String(property)} success`, maybeRequest.result);
|
|
2198
|
+
// });
|
|
2199
|
+
// maybeRequest.addEventListener('error', () => {
|
|
2200
|
+
// this.log?.(`${target.name}.${String(property)} error`, maybeRequest.error);
|
|
2201
|
+
// });
|
|
2202
|
+
|
|
2203
|
+
// Save the request.
|
|
2204
|
+
this.#request = maybeRequest;
|
|
2205
|
+
|
|
2206
|
+
// Abort the transaction on error.
|
|
2207
|
+
maybeRequest.addEventListener('error', () => {
|
|
2208
|
+
console.error(maybeRequest.error);
|
|
2209
|
+
maybeRequest.transaction.abort();
|
|
2210
|
+
}, { once: true });
|
|
2211
|
+
|
|
2212
|
+
// Return a Promise.
|
|
2213
|
+
return wrap(maybeRequest);
|
|
2214
|
+
}
|
|
2215
|
+
return maybeRequest;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
return result;
|
|
2219
|
+
}
|
|
1906
2220
|
});
|
|
1907
2221
|
}
|
|
1908
2222
|
|
|
1909
|
-
|
|
1910
|
-
|
|
2223
|
+
/**
|
|
2224
|
+
* @param {boolean} durable
|
|
2225
|
+
*/
|
|
2226
|
+
async sync(durable) {
|
|
2227
|
+
if (this.#chain) {
|
|
2228
|
+
// This waits for all IndexedDB calls to be made.
|
|
2229
|
+
await this.#chain;
|
|
2230
|
+
if (durable) {
|
|
2231
|
+
// This waits for the final transaction to commit.
|
|
2232
|
+
await this.#txComplete;
|
|
2233
|
+
}
|
|
2234
|
+
this.reset();
|
|
2235
|
+
}
|
|
1911
2236
|
}
|
|
1912
2237
|
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
this.
|
|
2238
|
+
reset() {
|
|
2239
|
+
this.#chain = null;
|
|
2240
|
+
this.#txComplete = Promise.resolve();
|
|
2241
|
+
this.#request = null;
|
|
1916
2242
|
}
|
|
2243
|
+
}
|
|
1917
2244
|
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
2245
|
+
/**
|
|
2246
|
+
* @param {IDBRequest} request
|
|
2247
|
+
* @returns {Promise}
|
|
2248
|
+
*/
|
|
2249
|
+
function wrap(request) {
|
|
2250
|
+
return new Promise((resolve, reject) => {
|
|
2251
|
+
request.onsuccess = () => resolve(request.result);
|
|
2252
|
+
request.onerror = () => reject(request.error);
|
|
2253
|
+
});
|
|
1921
2254
|
}
|
|
1922
2255
|
|
|
2256
|
+
|
|
2257
|
+
|
|
1923
2258
|
/***/ })
|
|
1924
2259
|
|
|
1925
2260
|
}]);
|