@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.
- package/LICENSE +21 -0
- package/README.md +78 -0
- package/dist/wa-sqlite-async.mjs +16 -0
- package/dist/wa-sqlite-async.wasm +0 -0
- package/dist/wa-sqlite-jspi.mjs +16 -0
- package/dist/wa-sqlite-jspi.wasm +0 -0
- package/dist/wa-sqlite.mjs +16 -0
- package/dist/wa-sqlite.wasm +0 -0
- package/package.json +45 -0
- package/src/FacadeVFS.js +508 -0
- package/src/VFS.js +222 -0
- package/src/WebLocksMixin.js +412 -0
- package/src/examples/AccessHandlePoolVFS.js +458 -0
- package/src/examples/IDBBatchAtomicVFS.js +820 -0
- package/src/examples/IDBMirrorVFS.js +875 -0
- package/src/examples/MemoryAsyncVFS.js +100 -0
- package/src/examples/MemoryVFS.js +176 -0
- package/src/examples/OPFSAdaptiveVFS.js +437 -0
- package/src/examples/OPFSAnyContextVFS.js +300 -0
- package/src/examples/OPFSCoopSyncVFS.js +590 -0
- package/src/examples/OPFSPermutedVFS.js +1214 -0
- package/src/examples/README.md +89 -0
- package/src/examples/tag.js +82 -0
- package/src/sqlite-api.js +914 -0
- package/src/sqlite-constants.js +275 -0
- package/src/types/globals.d.ts +60 -0
- package/src/types/index.d.ts +1302 -0
- package/src/types/tsconfig.json +6 -0
- package/test/AccessHandlePoolVFS.test.js +27 -0
- package/test/IDBBatchAtomicVFS.test.js +97 -0
- package/test/IDBMirrorVFS.test.js +27 -0
- package/test/MemoryAsyncVFS.test.js +27 -0
- package/test/MemoryVFS.test.js +27 -0
- package/test/OPFSAdaptiveVFS.test.js +27 -0
- package/test/OPFSAnyContextVFS.test.js +27 -0
- package/test/OPFSCoopSyncVFS.test.js +27 -0
- package/test/OPFSPermutedVFS.test.js +27 -0
- package/test/TestContext.js +96 -0
- package/test/WebLocksMixin.test.js +521 -0
- package/test/api.test.js +49 -0
- package/test/api_exec.js +89 -0
- package/test/api_misc.js +63 -0
- package/test/api_statements.js +426 -0
- package/test/callbacks.test.js +373 -0
- package/test/sql.test.js +64 -0
- package/test/sql_0001.js +49 -0
- package/test/sql_0002.js +52 -0
- package/test/sql_0003.js +83 -0
- package/test/sql_0004.js +81 -0
- package/test/sql_0005.js +76 -0
- package/test/test-worker.js +204 -0
- package/test/vfs_xAccess.js +2 -0
- package/test/vfs_xClose.js +52 -0
- package/test/vfs_xOpen.js +91 -0
- package/test/vfs_xRead.js +38 -0
- 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
|
+
}
|