@livestore/wa-sqlite 1.0.1-dev.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +78 -0
  3. package/dist/wa-sqlite-async.mjs +16 -0
  4. package/dist/wa-sqlite-async.wasm +0 -0
  5. package/dist/wa-sqlite-jspi.mjs +16 -0
  6. package/dist/wa-sqlite-jspi.wasm +0 -0
  7. package/dist/wa-sqlite.mjs +16 -0
  8. package/dist/wa-sqlite.wasm +0 -0
  9. package/package.json +45 -0
  10. package/src/FacadeVFS.js +508 -0
  11. package/src/VFS.js +222 -0
  12. package/src/WebLocksMixin.js +412 -0
  13. package/src/examples/AccessHandlePoolVFS.js +458 -0
  14. package/src/examples/IDBBatchAtomicVFS.js +820 -0
  15. package/src/examples/IDBMirrorVFS.js +875 -0
  16. package/src/examples/MemoryAsyncVFS.js +100 -0
  17. package/src/examples/MemoryVFS.js +176 -0
  18. package/src/examples/OPFSAdaptiveVFS.js +437 -0
  19. package/src/examples/OPFSAnyContextVFS.js +300 -0
  20. package/src/examples/OPFSCoopSyncVFS.js +590 -0
  21. package/src/examples/OPFSPermutedVFS.js +1214 -0
  22. package/src/examples/README.md +89 -0
  23. package/src/examples/tag.js +82 -0
  24. package/src/sqlite-api.js +914 -0
  25. package/src/sqlite-constants.js +275 -0
  26. package/src/types/globals.d.ts +60 -0
  27. package/src/types/index.d.ts +1302 -0
  28. package/src/types/tsconfig.json +6 -0
  29. package/test/AccessHandlePoolVFS.test.js +27 -0
  30. package/test/IDBBatchAtomicVFS.test.js +97 -0
  31. package/test/IDBMirrorVFS.test.js +27 -0
  32. package/test/MemoryAsyncVFS.test.js +27 -0
  33. package/test/MemoryVFS.test.js +27 -0
  34. package/test/OPFSAdaptiveVFS.test.js +27 -0
  35. package/test/OPFSAnyContextVFS.test.js +27 -0
  36. package/test/OPFSCoopSyncVFS.test.js +27 -0
  37. package/test/OPFSPermutedVFS.test.js +27 -0
  38. package/test/TestContext.js +96 -0
  39. package/test/WebLocksMixin.test.js +521 -0
  40. package/test/api.test.js +49 -0
  41. package/test/api_exec.js +89 -0
  42. package/test/api_misc.js +63 -0
  43. package/test/api_statements.js +426 -0
  44. package/test/callbacks.test.js +373 -0
  45. package/test/sql.test.js +64 -0
  46. package/test/sql_0001.js +49 -0
  47. package/test/sql_0002.js +52 -0
  48. package/test/sql_0003.js +83 -0
  49. package/test/sql_0004.js +81 -0
  50. package/test/sql_0005.js +76 -0
  51. package/test/test-worker.js +204 -0
  52. package/test/vfs_xAccess.js +2 -0
  53. package/test/vfs_xClose.js +52 -0
  54. package/test/vfs_xOpen.js +91 -0
  55. package/test/vfs_xRead.js +38 -0
  56. package/test/vfs_xWrite.js +36 -0
@@ -0,0 +1,300 @@
1
+ // Copyright 2024 Roy T. Hashimoto. All Rights Reserved.
2
+ import { FacadeVFS } from '../FacadeVFS.js';
3
+ import * as VFS from '../VFS.js';
4
+ import { WebLocksMixin } from '../WebLocksMixin.js';
5
+
6
+ /**
7
+ * @param {string} pathname
8
+ * @param {boolean} create
9
+ * @returns {Promise<[FileSystemDirectoryHandle, string]>}
10
+ */
11
+ async function getPathComponents(pathname, create) {
12
+ const [_, directories, filename] = pathname.match(/[/]?(.*)[/](.*)$/);
13
+
14
+ let directoryHandle = await navigator.storage.getDirectory();
15
+ for (const directory of directories.split('/')) {
16
+ if (directory) {
17
+ directoryHandle = await directoryHandle.getDirectoryHandle(directory, { create });
18
+ }
19
+ }
20
+ return [directoryHandle, filename];
21
+ };
22
+
23
+ class File {
24
+ /** @type {string} */ pathname;
25
+ /** @type {number} */ flags;
26
+ /** @type {FileSystemFileHandle} */ fileHandle;
27
+ /** @type {Blob?} */ blob;
28
+ /** @type {FileSystemWritableFileStream?} */ writable;
29
+
30
+ constructor(pathname, flags) {
31
+ this.pathname = pathname;
32
+ this.flags = flags;
33
+ }
34
+ }
35
+
36
+ export class OPFSAnyContextVFS extends WebLocksMixin(FacadeVFS) {
37
+ /** @type {Map<number, File>} */ mapIdToFile = new Map();
38
+ lastError = null;
39
+
40
+ log = null;
41
+
42
+ static async create(name, module, options) {
43
+ const vfs = new OPFSAnyContextVFS(name, module, options);
44
+ await vfs.isReady();
45
+ return vfs;
46
+ }
47
+
48
+ constructor(name, module, options = {}) {
49
+ super(name, module, options);
50
+ }
51
+
52
+ getFilename(fileId) {
53
+ const pathname = this.mapIdToFile.get(fileId).pathname;
54
+ return `OPFS:${pathname}`
55
+ }
56
+
57
+ /**
58
+ * @param {string?} zName
59
+ * @param {number} fileId
60
+ * @param {number} flags
61
+ * @param {DataView} pOutFlags
62
+ * @returns {Promise<number>}
63
+ */
64
+ async jOpen(zName, fileId, flags, pOutFlags) {
65
+ try {
66
+ const url = new URL(zName || Math.random().toString(36).slice(2), 'file://');
67
+ const pathname = url.pathname;
68
+
69
+ const file = new File(pathname, flags);
70
+ this.mapIdToFile.set(fileId, file);
71
+
72
+ const create = !!(flags & VFS.SQLITE_OPEN_CREATE);
73
+ const [directoryHandle, filename] = await getPathComponents(pathname, create);
74
+ file.fileHandle = await directoryHandle.getFileHandle(filename, { create });
75
+
76
+ pOutFlags.setInt32(0, flags, true);
77
+ return VFS.SQLITE_OK;
78
+ } catch (e) {
79
+ this.lastError = e;
80
+ return VFS.SQLITE_CANTOPEN;
81
+ }
82
+ }
83
+
84
+ /**
85
+ * @param {string} zName
86
+ * @param {number} syncDir
87
+ * @returns {Promise<number>}
88
+ */
89
+ async jDelete(zName, syncDir) {
90
+ try {
91
+ const url = new URL(zName, 'file://');
92
+ const pathname = url.pathname;
93
+
94
+ const [directoryHandle, name] = await getPathComponents(pathname, false);
95
+ const result = directoryHandle.removeEntry(name, { recursive: false });
96
+ if (syncDir) {
97
+ await result;
98
+ }
99
+ return VFS.SQLITE_OK;
100
+ } catch (e) {
101
+ return VFS.SQLITE_IOERR_DELETE;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * @param {string} zName
107
+ * @param {number} flags
108
+ * @param {DataView} pResOut
109
+ * @returns {Promise<number>}
110
+ */
111
+ async jAccess(zName, flags, pResOut) {
112
+ try {
113
+ const url = new URL(zName, 'file://');
114
+ const pathname = url.pathname;
115
+
116
+ const [directoryHandle, dbName] = await getPathComponents(pathname, false);
117
+ const fileHandle = await directoryHandle.getFileHandle(dbName, { create: false });
118
+ pResOut.setInt32(0, 1, true);
119
+ return VFS.SQLITE_OK;
120
+ } catch (e) {
121
+ if (e.name === 'NotFoundError') {
122
+ pResOut.setInt32(0, 0, true);
123
+ return VFS.SQLITE_OK;
124
+ }
125
+ this.lastError = e;
126
+ return VFS.SQLITE_IOERR_ACCESS;
127
+ }
128
+ }
129
+
130
+ /**
131
+ * @param {number} fileId
132
+ * @returns {Promise<number>}
133
+ */
134
+ async jClose(fileId) {
135
+ try {
136
+ const file = this.mapIdToFile.get(fileId);
137
+ this.mapIdToFile.delete(fileId);
138
+
139
+ await file.writable?.close();
140
+ if (file?.flags & VFS.SQLITE_OPEN_DELETEONCLOSE) {
141
+ const [directoryHandle, name] = await getPathComponents(file.pathname, false);
142
+ await directoryHandle.removeEntry(name, { recursive: false });
143
+ }
144
+ return VFS.SQLITE_OK;
145
+ } catch (e) {
146
+ return VFS.SQLITE_IOERR_DELETE;
147
+ }
148
+ }
149
+
150
+ /**
151
+ * @param {number} fileId
152
+ * @param {Uint8Array} pData
153
+ * @param {number} iOffset
154
+ * @returns {Promise<number>}
155
+ */
156
+ async jRead(fileId, pData, iOffset) {
157
+ try {
158
+ const file = this.mapIdToFile.get(fileId);
159
+
160
+ if (file.writable) {
161
+ await file.writable.close();
162
+ file.writable = null;
163
+ file.blob = null;
164
+ }
165
+ if (!file.blob) {
166
+ file.blob = await file.fileHandle.getFile();
167
+ }
168
+
169
+ const bytesRead = await file.blob.slice(iOffset, iOffset + pData.byteLength)
170
+ .arrayBuffer()
171
+ .then(arrayBuffer => {
172
+ pData.set(new Uint8Array(arrayBuffer));
173
+ return arrayBuffer.byteLength;
174
+ });
175
+
176
+ if (bytesRead < pData.byteLength) {
177
+ pData.fill(0, bytesRead);
178
+ return VFS.SQLITE_IOERR_SHORT_READ;
179
+ }
180
+ return VFS.SQLITE_OK;
181
+ } catch (e) {
182
+ this.lastError = e;
183
+ return VFS.SQLITE_IOERR_READ;
184
+ }
185
+ }
186
+
187
+ /**
188
+ * @param {number} fileId
189
+ * @param {Uint8Array} pData
190
+ * @param {number} iOffset
191
+ * @returns {Promise<number>}
192
+ */
193
+ async jWrite(fileId, pData, iOffset) {
194
+ try {
195
+ const file = this.mapIdToFile.get(fileId);
196
+
197
+ if (!file.writable) {
198
+ file.writable = await file.fileHandle.createWritable({ keepExistingData: true });
199
+ }
200
+ await file.writable.seek(iOffset);
201
+ await file.writable.write(pData.subarray());
202
+ file.blob = null;
203
+
204
+ return VFS.SQLITE_OK;
205
+ } catch (e) {
206
+ this.lastError = e;
207
+ return VFS.SQLITE_IOERR_WRITE;
208
+ }
209
+ }
210
+
211
+ /**
212
+ * @param {number} fileId
213
+ * @param {number} iSize
214
+ * @returns {Promise<number>}
215
+ */
216
+ async jTruncate(fileId, iSize) {
217
+ try {
218
+ const file = this.mapIdToFile.get(fileId);
219
+
220
+ if (!file.writable) {
221
+ file.writable = await file.fileHandle.createWritable({ keepExistingData: true });
222
+ }
223
+ await file.writable.truncate(iSize);
224
+ file.blob = null;
225
+ return VFS.SQLITE_OK;
226
+ } catch (e) {
227
+ this.lastError = e;
228
+ return VFS.SQLITE_IOERR_TRUNCATE;
229
+ }
230
+ }
231
+
232
+ /**
233
+ * @param {number} fileId
234
+ * @param {number} flags
235
+ * @returns {Promise<number>}
236
+ */
237
+ async jSync(fileId, flags) {
238
+ try {
239
+ const file = this.mapIdToFile.get(fileId);
240
+ await file.writable?.close();
241
+ file.writable = null;
242
+ file.blob = null;
243
+ return VFS.SQLITE_OK;
244
+ } catch (e) {
245
+ this.lastError = e;
246
+ return VFS.SQLITE_IOERR_FSYNC;
247
+ }
248
+ }
249
+
250
+ /**
251
+ * @param {number} fileId
252
+ * @param {DataView} pSize64
253
+ * @returns {Promise<number>}
254
+ */
255
+ async jFileSize(fileId, pSize64) {
256
+ try {
257
+ const file = this.mapIdToFile.get(fileId);
258
+
259
+ if (file.writable) {
260
+ await file.writable.close();
261
+ file.writable = null;
262
+ file.blob = null;
263
+ }
264
+ if (!file.blob) {
265
+ file.blob = await file.fileHandle.getFile();
266
+ }
267
+ pSize64.setBigInt64(0, BigInt(file.blob.size), true);
268
+ return VFS.SQLITE_OK;
269
+ } catch (e) {
270
+ this.lastError = e;
271
+ return VFS.SQLITE_IOERR_FSTAT;
272
+ }
273
+ }
274
+
275
+ /**
276
+ * @param {number} fileId
277
+ * @param {number} lockType
278
+ * @returns {Promise<number>}
279
+ */
280
+ async jLock(fileId, lockType) {
281
+ if (lockType === VFS.SQLITE_LOCK_SHARED) {
282
+ // Make sure to get a current readable view of the file.
283
+ const file = this.mapIdToFile.get(fileId);
284
+ file.blob = null;
285
+ }
286
+
287
+ // Call the actual unlock implementation.
288
+ return super.jLock(fileId, lockType);
289
+ }
290
+
291
+ jGetLastError(zBuf) {
292
+ if (this.lastError) {
293
+ console.error(this.lastError);
294
+ const outputArray = zBuf.subarray(0, zBuf.byteLength - 1);
295
+ const { written } = new TextEncoder().encodeInto(this.lastError.message, outputArray);
296
+ zBuf[written] = 0;
297
+ }
298
+ return VFS.SQLITE_OK
299
+ }
300
+ }