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