@livestore/wa-sqlite 0.4.0-dev.22 → 0.4.0-dev.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (62) hide show
  1. package/README.md +46 -36
  2. package/dist/README.md +13 -13
  3. package/dist/fts5/wa-sqlite.mjs +1 -1
  4. package/dist/fts5/wa-sqlite.node.mjs +1 -1
  5. package/dist/fts5/wa-sqlite.node.wasm +0 -0
  6. package/dist/fts5/wa-sqlite.wasm +0 -0
  7. package/dist/wa-sqlite-async.mjs +1 -1
  8. package/dist/wa-sqlite-async.wasm +0 -0
  9. package/dist/wa-sqlite-jspi.mjs +1 -1
  10. package/dist/wa-sqlite-jspi.wasm +0 -0
  11. package/dist/wa-sqlite.mjs +1 -1
  12. package/dist/wa-sqlite.node.mjs +1 -1
  13. package/dist/wa-sqlite.node.wasm +0 -0
  14. package/dist/wa-sqlite.wasm +0 -0
  15. package/package.json +40 -29
  16. package/src/FacadeVFS.js +252 -261
  17. package/src/VFS.js +84 -85
  18. package/src/WebLocksMixin.js +357 -351
  19. package/src/examples/AccessHandlePoolVFS.js +185 -194
  20. package/src/examples/IDBBatchAtomicVFS.js +429 -409
  21. package/src/examples/IDBMirrorVFS.js +402 -409
  22. package/src/examples/MemoryAsyncVFS.js +32 -37
  23. package/src/examples/MemoryVFS.js +71 -75
  24. package/src/examples/OPFSAdaptiveVFS.js +206 -206
  25. package/src/examples/OPFSAnyContextVFS.js +141 -140
  26. package/src/examples/OPFSCoopSyncVFS.js +297 -299
  27. package/src/examples/OPFSPermutedVFS.js +529 -540
  28. package/src/examples/README.md +27 -15
  29. package/src/examples/tag.js +27 -27
  30. package/src/sqlite-api.js +910 -941
  31. package/src/sqlite-constants.js +246 -232
  32. package/src/types/globals.d.ts +52 -52
  33. package/src/types/index.d.ts +586 -576
  34. package/test/AccessHandlePoolVFS.test.js +21 -21
  35. package/test/IDBBatchAtomicVFS.test.js +69 -69
  36. package/test/IDBMirrorVFS.test.js +21 -21
  37. package/test/MemoryAsyncVFS.test.js +21 -21
  38. package/test/MemoryVFS.test.js +21 -21
  39. package/test/OPFSAdaptiveVFS.test.js +21 -21
  40. package/test/OPFSAnyContextVFS.test.js +21 -21
  41. package/test/OPFSCoopSyncVFS.test.js +21 -21
  42. package/test/OPFSPermutedVFS.test.js +21 -21
  43. package/test/TestContext.js +44 -41
  44. package/test/WebLocksMixin.test.js +369 -360
  45. package/test/api.test.js +23 -23
  46. package/test/api_exec.js +72 -61
  47. package/test/api_misc.js +53 -54
  48. package/test/api_statements.js +271 -279
  49. package/test/callbacks.test.js +492 -478
  50. package/test/data/idbv5.json +1135 -1
  51. package/test/sql.test.js +30 -30
  52. package/test/sql_0001.js +49 -33
  53. package/test/sql_0002.js +55 -34
  54. package/test/sql_0003.js +85 -49
  55. package/test/sql_0004.js +76 -47
  56. package/test/sql_0005.js +60 -44
  57. package/test/test-worker.js +171 -163
  58. package/test/vfs_xAccess.js +1 -2
  59. package/test/vfs_xClose.js +50 -49
  60. package/test/vfs_xOpen.js +73 -72
  61. package/test/vfs_xRead.js +31 -31
  62. package/test/vfs_xWrite.js +30 -29
@@ -1,28 +1,25 @@
1
1
  // Copyright 2023 Roy T. Hashimoto. All Rights Reserved.
2
- import { FacadeVFS } from '../FacadeVFS.js';
3
- import * as VFS from '../VFS.js';
2
+ import { FacadeVFS } from '../FacadeVFS.js'
3
+ import * as VFS from '../VFS.js'
4
4
 
5
- const SECTOR_SIZE = 4096;
5
+ const SECTOR_SIZE = 4096
6
6
 
7
7
  // Each OPFS file begins with a fixed-size header with metadata. The
8
8
  // contents of the file follow immediately after the header.
9
- const HEADER_MAX_PATH_SIZE = 512;
10
- const HEADER_FLAGS_SIZE = 4;
11
- const HEADER_DIGEST_SIZE = 8;
12
- const HEADER_CORPUS_SIZE = HEADER_MAX_PATH_SIZE + HEADER_FLAGS_SIZE;
13
- const HEADER_OFFSET_FLAGS = HEADER_MAX_PATH_SIZE;
14
- const HEADER_OFFSET_DIGEST = HEADER_CORPUS_SIZE;
15
- const HEADER_OFFSET_DATA = SECTOR_SIZE;
9
+ const HEADER_MAX_PATH_SIZE = 512
10
+ const HEADER_FLAGS_SIZE = 4
11
+ const HEADER_DIGEST_SIZE = 8
12
+ const HEADER_CORPUS_SIZE = HEADER_MAX_PATH_SIZE + HEADER_FLAGS_SIZE
13
+ const HEADER_OFFSET_FLAGS = HEADER_MAX_PATH_SIZE
14
+ const HEADER_OFFSET_DIGEST = HEADER_CORPUS_SIZE
15
+ const HEADER_OFFSET_DATA = SECTOR_SIZE
16
16
 
17
17
  // These file types are expected to persist in the file system outside
18
18
  // a session. Other files will be removed on VFS start.
19
19
  const PERSISTENT_FILE_TYPES =
20
- VFS.SQLITE_OPEN_MAIN_DB |
21
- VFS.SQLITE_OPEN_MAIN_JOURNAL |
22
- VFS.SQLITE_OPEN_SUPER_JOURNAL |
23
- VFS.SQLITE_OPEN_WAL;
20
+ VFS.SQLITE_OPEN_MAIN_DB | VFS.SQLITE_OPEN_MAIN_JOURNAL | VFS.SQLITE_OPEN_SUPER_JOURNAL | VFS.SQLITE_OPEN_WAL
24
21
 
25
- const DEFAULT_CAPACITY = 6;
22
+ const DEFAULT_CAPACITY = 6
26
23
 
27
24
  /**
28
25
  * This VFS uses the updated Access Handle API with all synchronous methods
@@ -31,211 +28,207 @@ const DEFAULT_CAPACITY = 6;
31
28
  * Asyncify.
32
29
  */
33
30
  export class AccessHandlePoolVFS extends FacadeVFS {
34
- log = null; //function(...args) { console.log(`[${contextName}]`, ...args) };
31
+ log = null //function(...args) { console.log(`[${contextName}]`, ...args) };
35
32
 
36
33
  // All the OPFS files the VFS uses are contained in one flat directory
37
34
  // specified in the constructor. No other files should be written here.
38
- #directoryPath;
39
- #directoryHandle;
35
+ #directoryPath
36
+ #directoryHandle
40
37
 
41
38
  // The OPFS files all have randomly-generated names that do not match
42
39
  // the SQLite files whose data they contain. This map links those names
43
40
  // with their respective OPFS access handles.
44
- #mapAccessHandleToName = new Map();
41
+ #mapAccessHandleToName = new Map()
45
42
 
46
43
  // When a SQLite file is associated with an OPFS file, that association
47
44
  // is kept in #mapPathToAccessHandle. Each access handle is in exactly
48
45
  // one of #mapPathToAccessHandle or #availableAccessHandles.
49
- #mapPathToAccessHandle = new Map();
50
- #availableAccessHandles = new Set();
46
+ #mapPathToAccessHandle = new Map()
47
+ #availableAccessHandles = new Set()
51
48
 
52
- #mapIdToFile = new Map();
49
+ #mapIdToFile = new Map()
53
50
 
54
51
  static async create(name, module) {
55
- const vfs = new AccessHandlePoolVFS(name, module);
56
- await vfs.isReady();
57
- return vfs;
52
+ const vfs = new AccessHandlePoolVFS(name, module)
53
+ await vfs.isReady()
54
+ return vfs
58
55
  }
59
-
56
+
60
57
  constructor(name, module) {
61
- super(name, module);
62
- this.#directoryPath = name;
58
+ super(name, module)
59
+ this.#directoryPath = name
63
60
  }
64
61
 
65
62
  /**
66
- * @param {string?} zName
67
- * @param {number} fileId
68
- * @param {number} flags
69
- * @param {DataView} pOutFlags
63
+ * @param {string?} zName
64
+ * @param {number} fileId
65
+ * @param {number} flags
66
+ * @param {DataView} pOutFlags
70
67
  * @returns {number}
71
68
  */
72
69
  jOpen(zName, fileId, flags, pOutFlags) {
73
70
  try {
74
71
  // First try to open a path that already exists in the file system.
75
- const path = zName ? this.#getPath(zName) : Math.random().toString(36);
76
- let accessHandle = this.#mapPathToAccessHandle.get(path);
77
- if (!accessHandle && (flags & VFS.SQLITE_OPEN_CREATE)) {
72
+ const path = zName ? this.#getPath(zName) : Math.random().toString(36)
73
+ let accessHandle = this.#mapPathToAccessHandle.get(path)
74
+ if (!accessHandle && flags & VFS.SQLITE_OPEN_CREATE) {
78
75
  // File not found so try to create it.
79
76
  if (this.getSize() < this.getCapacity()) {
80
77
  // Choose an unassociated OPFS file from the pool.
81
- ([accessHandle] = this.#availableAccessHandles.keys());
82
- this.#setAssociatedPath(accessHandle, path, flags);
78
+ ;[accessHandle] = this.#availableAccessHandles.keys()
79
+ this.#setAssociatedPath(accessHandle, path, flags)
83
80
  } else {
84
81
  // Out of unassociated files. This can be fixed by calling
85
82
  // addCapacity() from the application.
86
- throw new Error('cannot create file');
83
+ throw new Error('cannot create file')
87
84
  }
88
85
  }
89
86
  if (!accessHandle) {
90
- throw new Error('file not found');
87
+ throw new Error('file not found')
91
88
  }
92
89
  // Subsequent methods are only passed the fileId, so make sure we have
93
90
  // a way to get the file resources.
94
- const file = { path, flags, accessHandle };
95
- this.#mapIdToFile.set(fileId, file);
91
+ const file = { path, flags, accessHandle }
92
+ this.#mapIdToFile.set(fileId, file)
96
93
 
97
- pOutFlags.setInt32(0, flags, true);
98
- return VFS.SQLITE_OK;
94
+ pOutFlags.setInt32(0, flags, true)
95
+ return VFS.SQLITE_OK
99
96
  } catch (e) {
100
- console.error(e.message);
101
- return VFS.SQLITE_CANTOPEN;
97
+ console.error(e.message)
98
+ return VFS.SQLITE_CANTOPEN
102
99
  }
103
100
  }
104
101
 
105
102
  /**
106
- * @param {number} fileId
103
+ * @param {number} fileId
107
104
  * @returns {number}
108
105
  */
109
106
  jClose(fileId) {
110
- const file = this.#mapIdToFile.get(fileId);
107
+ const file = this.#mapIdToFile.get(fileId)
111
108
  if (file) {
112
- file.accessHandle.flush();
113
- this.#mapIdToFile.delete(fileId);
109
+ file.accessHandle.flush()
110
+ this.#mapIdToFile.delete(fileId)
114
111
  if (file.flags & VFS.SQLITE_OPEN_DELETEONCLOSE) {
115
- this.#deletePath(file.path);
112
+ this.#deletePath(file.path)
116
113
  }
117
114
  }
118
- return VFS.SQLITE_OK;
115
+ return VFS.SQLITE_OK
119
116
  }
120
117
 
121
118
  /**
122
- * @param {number} fileId
123
- * @param {Uint8Array} pData
119
+ * @param {number} fileId
120
+ * @param {Uint8Array} pData
124
121
  * @param {number} iOffset
125
122
  * @returns {number}
126
123
  */
127
124
  jRead(fileId, pData, iOffset) {
128
- const file = this.#mapIdToFile.get(fileId);
129
- const nBytes = file.accessHandle.read(
130
- pData.subarray(),
131
- { at: HEADER_OFFSET_DATA + iOffset });
125
+ const file = this.#mapIdToFile.get(fileId)
126
+ const nBytes = file.accessHandle.read(pData.subarray(), { at: HEADER_OFFSET_DATA + iOffset })
132
127
  if (nBytes < pData.byteLength) {
133
- pData.fill(0, nBytes, pData.byteLength);
134
- return VFS.SQLITE_IOERR_SHORT_READ;
128
+ pData.fill(0, nBytes, pData.byteLength)
129
+ return VFS.SQLITE_IOERR_SHORT_READ
135
130
  }
136
- return VFS.SQLITE_OK;
131
+ return VFS.SQLITE_OK
137
132
  }
138
133
 
139
134
  /**
140
- * @param {number} fileId
141
- * @param {Uint8Array} pData
135
+ * @param {number} fileId
136
+ * @param {Uint8Array} pData
142
137
  * @param {number} iOffset
143
138
  * @returns {number}
144
139
  */
145
140
  jWrite(fileId, pData, iOffset) {
146
- const file = this.#mapIdToFile.get(fileId);
147
- const nBytes = file.accessHandle.write(
148
- pData.subarray(),
149
- { at: HEADER_OFFSET_DATA + iOffset });
150
- return nBytes === pData.byteLength ? VFS.SQLITE_OK : VFS.SQLITE_IOERR;
141
+ const file = this.#mapIdToFile.get(fileId)
142
+ const nBytes = file.accessHandle.write(pData.subarray(), { at: HEADER_OFFSET_DATA + iOffset })
143
+ return nBytes === pData.byteLength ? VFS.SQLITE_OK : VFS.SQLITE_IOERR
151
144
  }
152
145
 
153
146
  /**
154
- * @param {number} fileId
155
- * @param {number} iSize
147
+ * @param {number} fileId
148
+ * @param {number} iSize
156
149
  * @returns {number}
157
150
  */
158
151
  jTruncate(fileId, iSize) {
159
- const file = this.#mapIdToFile.get(fileId);
160
- file.accessHandle.truncate(HEADER_OFFSET_DATA + iSize);
161
- return VFS.SQLITE_OK;
152
+ const file = this.#mapIdToFile.get(fileId)
153
+ file.accessHandle.truncate(HEADER_OFFSET_DATA + iSize)
154
+ return VFS.SQLITE_OK
162
155
  }
163
156
 
164
157
  /**
165
- * @param {number} fileId
166
- * @param {number} flags
158
+ * @param {number} fileId
159
+ * @param {number} flags
167
160
  * @returns {number}
168
161
  */
169
162
  jSync(fileId, flags) {
170
- const file = this.#mapIdToFile.get(fileId);
171
- file.accessHandle.flush();
172
- return VFS.SQLITE_OK;
163
+ const file = this.#mapIdToFile.get(fileId)
164
+ file.accessHandle.flush()
165
+ return VFS.SQLITE_OK
173
166
  }
174
167
 
175
168
  /**
176
- * @param {number} fileId
177
- * @param {DataView} pSize64
169
+ * @param {number} fileId
170
+ * @param {DataView} pSize64
178
171
  * @returns {number}
179
172
  */
180
173
  jFileSize(fileId, pSize64) {
181
- const file = this.#mapIdToFile.get(fileId);
182
- const size = file.accessHandle.getSize() - HEADER_OFFSET_DATA;
183
- pSize64.setBigInt64(0, BigInt(size), true);
184
- return VFS.SQLITE_OK;
174
+ const file = this.#mapIdToFile.get(fileId)
175
+ const size = file.accessHandle.getSize() - HEADER_OFFSET_DATA
176
+ pSize64.setBigInt64(0, BigInt(size), true)
177
+ return VFS.SQLITE_OK
185
178
  }
186
179
 
187
180
  jSectorSize(fileId) {
188
- return SECTOR_SIZE;
181
+ return SECTOR_SIZE
189
182
  }
190
183
 
191
184
  jDeviceCharacteristics(fileId) {
192
- return VFS.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN;
185
+ return VFS.SQLITE_IOCAP_UNDELETABLE_WHEN_OPEN
193
186
  }
194
187
 
195
188
  /**
196
- * @param {string} zName
197
- * @param {number} flags
198
- * @param {DataView} pResOut
189
+ * @param {string} zName
190
+ * @param {number} flags
191
+ * @param {DataView} pResOut
199
192
  * @returns {number}
200
193
  */
201
194
  jAccess(zName, flags, pResOut) {
202
- const path = this.#getPath(zName);
203
- pResOut.setInt32(0, this.#mapPathToAccessHandle.has(path) ? 1 : 0, true);
204
- return VFS.SQLITE_OK;
195
+ const path = this.#getPath(zName)
196
+ pResOut.setInt32(0, this.#mapPathToAccessHandle.has(path) ? 1 : 0, true)
197
+ return VFS.SQLITE_OK
205
198
  }
206
199
 
207
200
  /**
208
- * @param {string} zName
209
- * @param {number} syncDir
201
+ * @param {string} zName
202
+ * @param {number} syncDir
210
203
  * @returns {number}
211
204
  */
212
205
  jDelete(zName, syncDir) {
213
- const path = this.#getPath(zName);
214
- this.#deletePath(path);
215
- return VFS.SQLITE_OK;
206
+ const path = this.#getPath(zName)
207
+ this.#deletePath(path)
208
+ return VFS.SQLITE_OK
216
209
  }
217
210
 
218
211
  async close() {
219
- await this.#releaseAccessHandles();
212
+ await this.#releaseAccessHandles()
220
213
  }
221
214
 
222
215
  async isReady() {
223
216
  if (!this.#directoryHandle) {
224
217
  // All files are stored in a single directory.
225
- let handle = await navigator.storage.getDirectory();
218
+ let handle = await navigator.storage.getDirectory()
226
219
  for (const d of this.#directoryPath.split('/')) {
227
220
  if (d) {
228
- handle = await handle.getDirectoryHandle(d, { create: true });
221
+ handle = await handle.getDirectoryHandle(d, { create: true })
229
222
  }
230
223
  }
231
- this.#directoryHandle = handle;
224
+ this.#directoryHandle = handle
232
225
 
233
- await this.#acquireAccessHandles();
226
+ await this.#acquireAccessHandles()
234
227
  if (this.getCapacity() === 0) {
235
- await this.addCapacity(DEFAULT_CAPACITY);
228
+ await this.addCapacity(DEFAULT_CAPACITY)
236
229
  }
237
230
  }
238
- return true;
231
+ return true
239
232
  }
240
233
 
241
234
  /**
@@ -243,7 +236,7 @@ export class AccessHandlePoolVFS extends FacadeVFS {
243
236
  * @returns {number}
244
237
  */
245
238
  getSize() {
246
- return this.#mapPathToAccessHandle.size;
239
+ return this.#mapPathToAccessHandle.size
247
240
  }
248
241
 
249
242
  /**
@@ -251,77 +244,79 @@ export class AccessHandlePoolVFS extends FacadeVFS {
251
244
  * @returns {number}
252
245
  */
253
246
  getCapacity() {
254
- return this.#mapAccessHandleToName.size;
247
+ return this.#mapAccessHandleToName.size
255
248
  }
256
249
 
257
250
  /**
258
251
  * Increase the capacity of the file system by n.
259
- * @param {number} n
260
- * @returns {Promise<number>}
252
+ * @param {number} n
253
+ * @returns {Promise<number>}
261
254
  */
262
255
  async addCapacity(n) {
263
256
  for (let i = 0; i < n; ++i) {
264
- const name = Math.random().toString(36).replace('0.', '');
265
- const handle = await this.#directoryHandle.getFileHandle(name, { create: true });
266
- const accessHandle = await handle.createSyncAccessHandle();
267
- this.#mapAccessHandleToName.set(accessHandle, name);
257
+ const name = Math.random().toString(36).replace('0.', '')
258
+ const handle = await this.#directoryHandle.getFileHandle(name, { create: true })
259
+ const accessHandle = await handle.createSyncAccessHandle()
260
+ this.#mapAccessHandleToName.set(accessHandle, name)
268
261
 
269
- this.#setAssociatedPath(accessHandle, '', 0);
262
+ this.#setAssociatedPath(accessHandle, '', 0)
270
263
  }
271
- return n;
264
+ return n
272
265
  }
273
266
 
274
267
  /**
275
268
  * Decrease the capacity of the file system by n. The capacity cannot be
276
269
  * decreased to fewer than the current number of SQLite files in the
277
270
  * file system.
278
- * @param {number} n
271
+ * @param {number} n
279
272
  * @returns {Promise<number>}
280
273
  */
281
274
  async removeCapacity(n) {
282
- let nRemoved = 0;
275
+ let nRemoved = 0
283
276
  for (const accessHandle of Array.from(this.#availableAccessHandles)) {
284
- if (nRemoved == n || this.getSize() === this.getCapacity()) return nRemoved;
285
-
286
- const name = this.#mapAccessHandleToName.get(accessHandle);
287
- await accessHandle.close();
288
- await this.#directoryHandle.removeEntry(name);
289
- this.#mapAccessHandleToName.delete(accessHandle);
290
- this.#availableAccessHandles.delete(accessHandle);
291
- ++nRemoved;
277
+ if (nRemoved == n || this.getSize() === this.getCapacity()) return nRemoved
278
+
279
+ const name = this.#mapAccessHandleToName.get(accessHandle)
280
+ await accessHandle.close()
281
+ await this.#directoryHandle.removeEntry(name)
282
+ this.#mapAccessHandleToName.delete(accessHandle)
283
+ this.#availableAccessHandles.delete(accessHandle)
284
+ ++nRemoved
292
285
  }
293
- return nRemoved;
286
+ return nRemoved
294
287
  }
295
288
 
296
289
  async #acquireAccessHandles() {
297
290
  // Enumerate all the files in the directory.
298
- const files = [];
291
+ const files = []
299
292
  for await (const [name, handle] of this.#directoryHandle) {
300
293
  if (handle.kind === 'file') {
301
- files.push([name, handle]);
294
+ files.push([name, handle])
302
295
  }
303
296
  }
304
297
 
305
298
  // Open access handles in parallel, separating associated and unassociated.
306
- await Promise.all(files.map(async ([name, handle]) => {
307
- const accessHandle = await handle.createSyncAccessHandle();
308
- this.#mapAccessHandleToName.set(accessHandle, name);
309
- const path = this.#getAssociatedPath(accessHandle);
310
- if (path) {
311
- this.#mapPathToAccessHandle.set(path, accessHandle);
312
- } else {
313
- this.#availableAccessHandles.add(accessHandle);
314
- }
315
- }));
299
+ await Promise.all(
300
+ files.map(async ([name, handle]) => {
301
+ const accessHandle = await handle.createSyncAccessHandle()
302
+ this.#mapAccessHandleToName.set(accessHandle, name)
303
+ const path = this.#getAssociatedPath(accessHandle)
304
+ if (path) {
305
+ this.#mapPathToAccessHandle.set(path, accessHandle)
306
+ } else {
307
+ this.#availableAccessHandles.add(accessHandle)
308
+ }
309
+ }),
310
+ )
316
311
  }
317
312
 
318
313
  #releaseAccessHandles() {
319
314
  for (const accessHandle of this.#mapAccessHandleToName.keys()) {
320
- accessHandle.close();
315
+ accessHandle.close()
321
316
  }
322
- this.#mapAccessHandleToName.clear();
323
- this.#mapPathToAccessHandle.clear();
324
- this.#availableAccessHandles.clear();
317
+ this.#mapAccessHandleToName.clear()
318
+ this.#mapPathToAccessHandle.clear()
319
+ this.#availableAccessHandles.clear()
325
320
  }
326
321
 
327
322
  /**
@@ -332,41 +327,39 @@ export class AccessHandlePoolVFS extends FacadeVFS {
332
327
  */
333
328
  #getAssociatedPath(accessHandle) {
334
329
  // Read the path and digest of the path from the file.
335
- const corpus = new Uint8Array(HEADER_CORPUS_SIZE);
330
+ const corpus = new Uint8Array(HEADER_CORPUS_SIZE)
336
331
  accessHandle.read(corpus, { at: 0 })
337
332
 
338
333
  // Delete files not expected to be present.
339
- const dataView = new DataView(corpus.buffer, corpus.byteOffset);
340
- const flags = dataView.getUint32(HEADER_OFFSET_FLAGS);
341
- if (corpus[0] &&
342
- ((flags & VFS.SQLITE_OPEN_DELETEONCLOSE) ||
343
- (flags & PERSISTENT_FILE_TYPES) === 0)) {
344
- console.warn(`Remove file with unexpected flags ${flags.toString(16)}`);
345
- this.#setAssociatedPath(accessHandle, '', 0);
346
- return '';
334
+ const dataView = new DataView(corpus.buffer, corpus.byteOffset)
335
+ const flags = dataView.getUint32(HEADER_OFFSET_FLAGS)
336
+ if (corpus[0] && (flags & VFS.SQLITE_OPEN_DELETEONCLOSE || (flags & PERSISTENT_FILE_TYPES) === 0)) {
337
+ console.warn(`Remove file with unexpected flags ${flags.toString(16)}`)
338
+ this.#setAssociatedPath(accessHandle, '', 0)
339
+ return ''
347
340
  }
348
341
 
349
- const fileDigest = new Uint32Array(HEADER_DIGEST_SIZE / 4);
350
- accessHandle.read(fileDigest, { at: HEADER_OFFSET_DIGEST });
342
+ const fileDigest = new Uint32Array(HEADER_DIGEST_SIZE / 4)
343
+ accessHandle.read(fileDigest, { at: HEADER_OFFSET_DIGEST })
351
344
 
352
345
  // Verify the digest.
353
- const computedDigest = this.#computeDigest(corpus);
346
+ const computedDigest = this.#computeDigest(corpus)
354
347
  if (fileDigest.every((value, i) => value === computedDigest[i])) {
355
348
  // Good digest. Decode the null-terminated path string.
356
- const pathBytes = corpus.findIndex(value => value === 0);
349
+ const pathBytes = corpus.findIndex((value) => value === 0)
357
350
  if (pathBytes === 0) {
358
351
  // Ensure that unassociated files are empty. Unassociated files are
359
352
  // truncated in #setAssociatedPath after the header is written. If
360
353
  // an interruption occurs right before the truncation then garbage
361
354
  // may remain in the file.
362
- accessHandle.truncate(HEADER_OFFSET_DATA);
355
+ accessHandle.truncate(HEADER_OFFSET_DATA)
363
356
  }
364
- return new TextDecoder().decode(corpus.subarray(0, pathBytes));
357
+ return new TextDecoder().decode(corpus.subarray(0, pathBytes))
365
358
  } else {
366
359
  // Bad digest. Repair this header.
367
- console.warn('Disassociating file with bad digest.');
368
- this.#setAssociatedPath(accessHandle, '', 0);
369
- return '';
360
+ console.warn('Disassociating file with bad digest.')
361
+ this.#setAssociatedPath(accessHandle, '', 0)
362
+ return ''
370
363
  }
371
364
  }
372
365
 
@@ -378,81 +371,79 @@ export class AccessHandlePoolVFS extends FacadeVFS {
378
371
  */
379
372
  #setAssociatedPath(accessHandle, path, flags) {
380
373
  // Convert the path string to UTF-8.
381
- const corpus = new Uint8Array(HEADER_CORPUS_SIZE);
382
- const encodedResult = new TextEncoder().encodeInto(path, corpus);
374
+ const corpus = new Uint8Array(HEADER_CORPUS_SIZE)
375
+ const encodedResult = new TextEncoder().encodeInto(path, corpus)
383
376
  if (encodedResult.written >= HEADER_MAX_PATH_SIZE) {
384
- throw new Error('path too long');
377
+ throw new Error('path too long')
385
378
  }
386
379
 
387
380
  // Add the creation flags.
388
- const dataView = new DataView(corpus.buffer, corpus.byteOffset);
389
- dataView.setUint32(HEADER_OFFSET_FLAGS, flags);
381
+ const dataView = new DataView(corpus.buffer, corpus.byteOffset)
382
+ dataView.setUint32(HEADER_OFFSET_FLAGS, flags)
390
383
 
391
384
  // Write the OPFS file header, including the digest.
392
- const digest = this.#computeDigest(corpus);
393
- accessHandle.write(corpus, { at: 0 });
394
- accessHandle.write(digest, { at: HEADER_OFFSET_DIGEST });
395
- accessHandle.flush();
385
+ const digest = this.#computeDigest(corpus)
386
+ accessHandle.write(corpus, { at: 0 })
387
+ accessHandle.write(digest, { at: HEADER_OFFSET_DIGEST })
388
+ accessHandle.flush()
396
389
 
397
390
  if (path) {
398
- this.#mapPathToAccessHandle.set(path, accessHandle);
399
- this.#availableAccessHandles.delete(accessHandle);
391
+ this.#mapPathToAccessHandle.set(path, accessHandle)
392
+ this.#availableAccessHandles.delete(accessHandle)
400
393
  } else {
401
394
  // This OPFS file doesn't represent any SQLite file so it doesn't
402
395
  // need to keep any data.
403
- accessHandle.truncate(HEADER_OFFSET_DATA);
404
- this.#availableAccessHandles.add(accessHandle);
396
+ accessHandle.truncate(HEADER_OFFSET_DATA)
397
+ this.#availableAccessHandles.add(accessHandle)
405
398
  }
406
399
  }
407
400
 
408
401
  /**
409
402
  * We need a synchronous digest function so can't use WebCrypto.
410
403
  * Adapted from https://github.com/bryc/code/blob/master/jshash/experimental/cyrb53.js
411
- * @param {Uint8Array} corpus
404
+ * @param {Uint8Array} corpus
412
405
  * @returns {ArrayBuffer} 64-bit digest
413
406
  */
414
407
  #computeDigest(corpus) {
415
408
  if (!corpus[0]) {
416
409
  // Optimization for deleted file.
417
- return new Uint32Array([0xfecc5f80, 0xaccec037]);
410
+ return new Uint32Array([0xfecc5f80, 0xaccec037])
418
411
  }
419
412
 
420
- let h1 = 0xdeadbeef;
421
- let h2 = 0x41c6ce57;
422
-
413
+ let h1 = 0xdeadbeef
414
+ let h2 = 0x41c6ce57
415
+
423
416
  for (const value of corpus) {
424
- h1 = Math.imul(h1 ^ value, 2654435761);
425
- h2 = Math.imul(h2 ^ value, 1597334677);
417
+ h1 = Math.imul(h1 ^ value, 2654435761)
418
+ h2 = Math.imul(h2 ^ value, 1597334677)
426
419
  }
427
-
428
- h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909);
429
- h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909);
430
-
431
- return new Uint32Array([h1 >>> 0, h2 >>> 0]);
432
- };
433
-
420
+
421
+ h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507) ^ Math.imul(h2 ^ (h2 >>> 13), 3266489909)
422
+ h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507) ^ Math.imul(h1 ^ (h1 >>> 13), 3266489909)
423
+
424
+ return new Uint32Array([h1 >>> 0, h2 >>> 0])
425
+ }
426
+
434
427
  /**
435
428
  * Convert a bare filename, path, or URL to a UNIX-style path.
436
429
  * @param {string|URL} nameOrURL
437
430
  * @returns {string} path
438
431
  */
439
432
  #getPath(nameOrURL) {
440
- const url = typeof nameOrURL === 'string' ?
441
- new URL(nameOrURL, 'file://localhost/') :
442
- nameOrURL;
443
- return url.pathname;
433
+ const url = typeof nameOrURL === 'string' ? new URL(nameOrURL, 'file://localhost/') : nameOrURL
434
+ return url.pathname
444
435
  }
445
436
 
446
437
  /**
447
438
  * Remove the association between a path and an OPFS file.
448
- * @param {string} path
439
+ * @param {string} path
449
440
  */
450
441
  #deletePath(path) {
451
- const accessHandle = this.#mapPathToAccessHandle.get(path);
442
+ const accessHandle = this.#mapPathToAccessHandle.get(path)
452
443
  if (accessHandle) {
453
444
  // Un-associate the SQLite path from the OPFS file.
454
- this.#mapPathToAccessHandle.delete(path);
455
- this.#setAssociatedPath(accessHandle, '', 0);
445
+ this.#mapPathToAccessHandle.delete(path)
446
+ this.#setAssociatedPath(accessHandle, '', 0)
456
447
  }
457
448
  }
458
- }
449
+ }