@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,76 @@
1
+ import * as Comlink from 'comlink';
2
+
3
+ export function sql_0005(context) {
4
+ describe('sql_0005', function() {
5
+ beforeAll(async function() {
6
+ // Clear persistent storage.
7
+ const proxy = await context.create();
8
+ await context.destroy(proxy);
9
+ });
10
+
11
+ const cleanup = [];
12
+ beforeEach(async function() {
13
+ cleanup.splice(0);
14
+ });
15
+
16
+ afterEach(async function() {
17
+ for (const fn of cleanup) {
18
+ await fn();
19
+ }
20
+ });
21
+
22
+ it('should transact atomically', async function() {
23
+ const instances = [];
24
+ for (let i = 0; i < 8; ++i) {
25
+ const proxy = await context.create({ reset: false });
26
+ const sqlite3 = proxy.sqlite3;
27
+ const db = await sqlite3.open_v2('demo');
28
+ instances.push({ sqlite3, db });
29
+ cleanup.push(async () => {
30
+ await sqlite3.close(db);
31
+ await context.destroy(proxy);
32
+ });
33
+
34
+ if (i === 0) {
35
+ await sqlite3.exec(db, `
36
+ BEGIN IMMEDIATE;
37
+ CREATE TABLE IF NOT EXISTS t(key PRIMARY KEY, value);
38
+ INSERT OR IGNORE INTO t VALUES ('foo', 0);
39
+ COMMIT;
40
+ `);
41
+ }
42
+ }
43
+
44
+ const iterations = 32;
45
+ const values = new Set();
46
+ await Promise.all(instances.map(async instance => {
47
+ for (let i = 0; i < iterations; ++i) {
48
+ const rows = await transact(instance, `
49
+ BEGIN IMMEDIATE;
50
+ UPDATE t SET value = value + 1 WHERE key = 'foo';
51
+ SELECT value FROM t WHERE key = 'foo';
52
+ COMMIT;
53
+ `);
54
+ values.add(rows[0][0]);
55
+ }
56
+ }));
57
+
58
+ expect(values.size).toBe(instances.length * iterations);
59
+ expect(Array.from(values).sort((a, b) => b - a).at(0)).toBe(values.size);
60
+ });
61
+ });
62
+ }
63
+
64
+ async function transact({ sqlite3, db }, sql) {
65
+ while (true) {
66
+ try {
67
+ const rows = [];
68
+ await sqlite3.exec(db, sql, Comlink.proxy(row => rows.push(row)));
69
+ return rows;
70
+ } catch (e) {
71
+ if (e.message !== 'database is locked') {
72
+ throw e;
73
+ }
74
+ }
75
+ }
76
+ }
@@ -0,0 +1,204 @@
1
+ // Copyright 2024 Roy T. Hashimoto. All Rights Reserved.
2
+
3
+ import * as Comlink from 'comlink';
4
+ import * as SQLite from '../src/sqlite-api.js';
5
+
6
+ const BUILDS = new Map([
7
+ ['default', '../dist/wa-sqlite.mjs'],
8
+ ['asyncify', '../dist/wa-sqlite-async.mjs'],
9
+ ['jspi', '../dist/wa-sqlite-jspi.mjs'],
10
+ ]);
11
+
12
+ const MODULE = Symbol('module');
13
+ const VFS_CONFIGS = new Map([
14
+ {
15
+ name: 'default',
16
+ vfsModule: null
17
+ },
18
+ {
19
+ name: 'AccessHandlePoolVFS',
20
+ vfsModule: '../src/examples/AccessHandlePoolVFS.js',
21
+ },
22
+ {
23
+ name: 'OPFSCoopSyncVFS',
24
+ vfsModule: '../src/examples/OPFSCoopSyncVFS.js',
25
+ },
26
+ {
27
+ name: 'FLOOR',
28
+ vfsModule: '../src/examples/FLOOR.js',
29
+ },
30
+ {
31
+ name: 'MemoryVFS',
32
+ vfsModule: '../src/examples/MemoryVFS.js',
33
+ },
34
+ {
35
+ name: 'MemoryAsyncVFS',
36
+ vfsModule: '../src/examples/MemoryAsyncVFS.js',
37
+ },
38
+ {
39
+ name: 'IDBBatchAtomicVFS',
40
+ vfsModule: '../src/examples/IDBBatchAtomicVFS.js',
41
+ },
42
+ {
43
+ name: 'IDBMirrorVFS',
44
+ vfsModule: '../src/examples/IDBMirrorVFS.js',
45
+ },
46
+ {
47
+ name: 'OPFSAdaptiveVFS',
48
+ vfsModule: '../src/examples/OPFSAdaptiveVFS.js',
49
+ },
50
+ {
51
+ name: 'OPFSAnyContextVFS',
52
+ vfsModule: '../src/examples/OPFSAnyContextVFS.js',
53
+ },
54
+ {
55
+ name: 'OPFSPermutedVFS',
56
+ vfsModule: '../src/examples/OPFSPermutedVFS.js',
57
+ },
58
+ ].map(config => [config.name, config]));
59
+
60
+ const INDEXEDDB_DBNAMES = ['demo'];
61
+
62
+ const searchParams = new URLSearchParams(location.search);
63
+
64
+ maybeReset().then(async () => {
65
+ const buildName = searchParams.get('build') || BUILDS.keys().next().value;
66
+ const configName = searchParams.get('config') || VFS_CONFIGS.keys().next().value;
67
+ const config = VFS_CONFIGS.get(configName);
68
+
69
+ // Instantiate SQLite.
70
+ const { default: moduleFactory } = await import(BUILDS.get(buildName));
71
+ const module = await moduleFactory();
72
+ const sqlite3 = SQLite.Factory(module);
73
+
74
+ const vfs = await (async function() {
75
+ if (config.vfsModule) {
76
+ // Create the VFS and register it as the default file system.
77
+ const namespace = await import(config.vfsModule);
78
+ const className = config.vfsClass ?? config.vfsModule.match(/([^/]+)\.js$/)[1];
79
+ const vfsArgs = (config.vfsArgs ?? ['demo', MODULE])
80
+ .map(arg => arg === MODULE ? module : arg);
81
+ const vfs = await namespace[className].create(...vfsArgs);
82
+ sqlite3.vfs_register(vfs, true);
83
+ return vfs;
84
+ }
85
+ return {};
86
+ })();
87
+
88
+ const sqlite3Proxy = new Proxy(sqlite3, {
89
+ get(target, p, receiver) {
90
+ // Comlink intercepts some function property names, e.g. "bind",
91
+ // so allow aliases to avoid the problem.
92
+ if (typeof p === 'string') p = p.replaceAll('$', '');
93
+
94
+ const value = Reflect.get(target, p, receiver);
95
+ if (typeof value === 'function') {
96
+ return async (...args) => {
97
+ const result = await value.apply(target, args);
98
+ if (p === 'statements') {
99
+ return Comlink.proxy(result);
100
+ }
101
+ return result;
102
+ };
103
+ }
104
+ }
105
+ });
106
+
107
+ const vfsProxy = new Proxy(vfs, {
108
+ get(target, p, receiver) {
109
+ const value = Reflect.get(target, p, receiver);
110
+ if (typeof value === 'function') {
111
+ return async (...args) => {
112
+ if (p === 'jRead') {
113
+ // The read buffer Uint8Array will be passed by proxy so all
114
+ // access is asynchronous. Pass a local buffer to the VFS
115
+ // and copy the local buffer to the proxy on completion.
116
+ const proxyBuffer = args[1];
117
+ args[1] = new Uint8Array(await proxyBuffer.length);
118
+ const result = await value.apply(target, args);
119
+ await proxyBuffer.set(args[1]);
120
+ return result;
121
+ }
122
+ return value.apply(target, args);
123
+ };
124
+ }
125
+ }
126
+ });
127
+
128
+ const { port1, port2 } = new MessageChannel();
129
+ Comlink.expose({
130
+ module,
131
+ sqlite3: sqlite3Proxy,
132
+ vfs: vfsProxy,
133
+ }, port1);
134
+ postMessage(null, [port2]);
135
+ }).catch(e => {
136
+ console.error(e);
137
+ postMessage(cvtErrorToCloneable(e));
138
+ });
139
+
140
+ async function maybeReset() {
141
+ if (searchParams.get('reset') !== 'true') {
142
+ return;
143
+ }
144
+
145
+ // Limit the amount of time in this function.
146
+ const abortController = new AbortController();
147
+ setTimeout(() => abortController.abort(), 10_000);
148
+
149
+ // Clear OPFS.
150
+ const root = await navigator.storage?.getDirectory();
151
+ if (root) {
152
+ let opfsDeleted = false;
153
+ while (!opfsDeleted) {
154
+ abortController.signal.throwIfAborted();
155
+ try {
156
+ // @ts-ignore
157
+ for await (const name of root.keys()) {
158
+ await root.removeEntry(name, { recursive: true });
159
+ }
160
+ opfsDeleted = true;
161
+ } catch (e) {
162
+ // A NoModificationAllowedError is thrown if an entry can't be
163
+ // deleted because it isn't closed. Just try again.
164
+ if (e.name === 'NoModificationAllowedError') {
165
+ await new Promise(resolve => setTimeout(resolve));
166
+ continue;
167
+ }
168
+ throw e;
169
+ }
170
+ }
171
+ }
172
+
173
+ // Clear IndexedDB.
174
+ const dbList = indexedDB.databases ?
175
+ await indexedDB.databases() :
176
+ INDEXEDDB_DBNAMES.map(name => ({ name }));
177
+ await Promise.all(dbList.map(({name}) => {
178
+ return new Promise((resolve, reject) => {
179
+ const request = indexedDB.deleteDatabase(name);
180
+ request.onsuccess = resolve;
181
+ request.onerror = reject;
182
+ });
183
+ }));
184
+ }
185
+
186
+ function cvtErrorToCloneable(e) {
187
+ if (e instanceof Error) {
188
+ const props = new Set([
189
+ ...['name', 'message', 'stack'].filter(k => e[k] !== undefined),
190
+ ...Object.getOwnPropertyNames(e)
191
+ ]);
192
+ return Object.fromEntries(Array.from(props, k =>  [k, e[k]])
193
+ .filter(([_, v]) => {
194
+ // Skip any non-cloneable properties.
195
+ try {
196
+ structuredClone(v);
197
+ return true;
198
+ } catch (e) {
199
+ return false;
200
+ }
201
+ }));
202
+ }
203
+ return e;
204
+ }
@@ -0,0 +1,2 @@
1
+ export function vfs_xAccess(context) {
2
+ }
@@ -0,0 +1,52 @@
1
+ import * as Comlink from 'comlink';
2
+ import * as VFS from '../src/VFS.js';
3
+
4
+ const FILEID = 1;
5
+
6
+ export function vfs_xClose(context) {
7
+ describe('vfs_xClose', function() {
8
+ let proxy, vfs;
9
+ beforeEach(async function() {
10
+ proxy = await context.create();
11
+ vfs = proxy.vfs;
12
+ });
13
+
14
+ afterEach(async function() {
15
+ await context.destroy(proxy);
16
+ });
17
+
18
+ it('should leave an accessible file', async function() {
19
+ let rc;
20
+ const pOpenOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
21
+ const openFlags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE;
22
+ rc = await vfs.jOpen('test', FILEID, openFlags, pOpenOutput);
23
+ expect(rc).toEqual(VFS.SQLITE_OK);
24
+
25
+ await vfs.jClose(FILEID);
26
+
27
+ const pAccessOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
28
+ rc = await vfs.jAccess('test', VFS.SQLITE_ACCESS_READWRITE, pAccessOutput);
29
+ expect(rc).toEqual(VFS.SQLITE_OK);
30
+ expect(pAccessOutput.getInt32(0, true)).not.toEqual(0);
31
+ });
32
+
33
+ it('should delete on close', async function() {
34
+ let rc;
35
+ const pOpenOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
36
+ const openFlags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE | VFS.SQLITE_OPEN_DELETEONCLOSE;
37
+ rc = await vfs.jOpen('test', FILEID, openFlags, pOpenOutput);
38
+ expect(rc).toEqual(VFS.SQLITE_OK);
39
+
40
+ const pAccessOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
41
+ rc = await vfs.jAccess('test', VFS.SQLITE_ACCESS_READWRITE, pAccessOutput);
42
+ expect(rc).toEqual(VFS.SQLITE_OK);
43
+ expect(pAccessOutput.getInt32(0, true)).toEqual(1);
44
+
45
+ await vfs.jClose(FILEID);
46
+
47
+ rc = await vfs.jAccess('test', VFS.SQLITE_ACCESS_READWRITE, pAccessOutput);
48
+ expect(rc).toEqual(VFS.SQLITE_OK);
49
+ expect(pAccessOutput.getInt32(0, true)).toEqual(0);
50
+ });
51
+ });
52
+ }
@@ -0,0 +1,91 @@
1
+ import * as Comlink from 'comlink';
2
+ import * as VFS from '../src/VFS.js';
3
+
4
+ const FILEID = 1;
5
+
6
+ export function vfs_xOpen(context) {
7
+ describe('vfs_xOpen', function() {
8
+ let proxy, vfs;
9
+ beforeEach(async function() {
10
+ proxy = await context.create();
11
+ vfs = proxy.vfs;
12
+ });
13
+
14
+ afterEach(async function() {
15
+ await context.destroy(proxy);
16
+ });
17
+
18
+ it('should create a file', async function() {
19
+ let rc;
20
+ const pOpenOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
21
+ const openFlags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE;
22
+ rc = await vfs.jOpen('test', FILEID, openFlags, pOpenOutput);
23
+ expect(rc).toEqual(VFS.SQLITE_OK);
24
+ expect(pOpenOutput.getInt32(0, true)).toEqual(openFlags);
25
+
26
+ const pAccessOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
27
+ rc = await vfs.jAccess('test', VFS.SQLITE_ACCESS_READWRITE, pAccessOutput);
28
+ expect(rc).toEqual(VFS.SQLITE_OK);
29
+ expect(pAccessOutput.getInt32(0, true)).not.toEqual(0);
30
+ });
31
+
32
+ it('should create a database file', async function() {
33
+ let rc;
34
+ const pOpenOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
35
+ const openFlags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE | VFS.SQLITE_OPEN_MAIN_DB;
36
+
37
+ do {
38
+ const nRetryOps = await proxy.module.retryOps.length;
39
+ for (let i = 0; i < nRetryOps; i++) {
40
+ await proxy.module.retryOps[i];
41
+ }
42
+ rc = await vfs.jOpen('test', 1, openFlags, pOpenOutput);
43
+ } while (rc === VFS.SQLITE_BUSY);
44
+ expect(rc).toEqual(VFS.SQLITE_OK);
45
+ expect(pOpenOutput.getInt32(0, true)).toEqual(openFlags);
46
+
47
+ const pAccessOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
48
+ rc = await vfs.jAccess('test', VFS.SQLITE_ACCESS_READWRITE, pAccessOutput);
49
+ expect(rc).toEqual(VFS.SQLITE_OK);
50
+ expect(pAccessOutput.getInt32(0, true)).not.toEqual(0);
51
+ });
52
+
53
+ it('should not create a file', async function() {
54
+ let rc;
55
+ const pOpenOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
56
+ const openFlags = VFS.SQLITE_OPEN_READWRITE;
57
+ rc = await vfs.jOpen('test', 1, openFlags, pOpenOutput);
58
+ expect(rc).toEqual(VFS.SQLITE_CANTOPEN);
59
+
60
+ const pAccessOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
61
+ rc = await vfs.jAccess('test', VFS.SQLITE_ACCESS_READWRITE, pAccessOutput);
62
+ expect(rc).toEqual(VFS.SQLITE_OK);
63
+ expect(pAccessOutput.getInt32(0, true)).toEqual(0);
64
+ });
65
+
66
+ it('should open an existing file', async function() {
67
+ let rc;
68
+ const pOpenOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
69
+ const openFlags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE;
70
+ rc = await vfs.jOpen('test', FILEID, openFlags, pOpenOutput);
71
+ expect(rc).toEqual(VFS.SQLITE_OK);
72
+
73
+ // Close the file because some VFS implementations don't allow
74
+ // multiple open handles.
75
+ await vfs.jClose(FILEID);
76
+
77
+ rc = await vfs.jOpen('test', FILEID, VFS.SQLITE_OPEN_READWRITE, pOpenOutput);
78
+ expect(rc).toEqual(VFS.SQLITE_OK);
79
+ expect(pOpenOutput.getInt32(0, true)).toEqual(VFS.SQLITE_OPEN_READWRITE);
80
+ });
81
+
82
+ it('should create an anonymous file', async function() {
83
+ let rc;
84
+ const pOpenOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
85
+ const openFlags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE;
86
+ rc = await vfs.jOpen(null, FILEID, openFlags, pOpenOutput);
87
+ expect(rc).toEqual(VFS.SQLITE_OK);
88
+ expect(pOpenOutput.getInt32(0, true)).toEqual(openFlags);
89
+ });
90
+ });
91
+ }
@@ -0,0 +1,38 @@
1
+ import * as Comlink from 'comlink';
2
+ import * as VFS from '../src/VFS.js';
3
+
4
+ const FILEID = 1;
5
+
6
+ export function vfs_xRead(context) {
7
+ describe('vfs_xRead', function() {
8
+ let proxy, vfs;
9
+ beforeEach(async function() {
10
+ proxy = await context.create();
11
+ vfs = proxy.vfs;
12
+ });
13
+
14
+ afterEach(async function() {
15
+ await context.destroy(proxy);
16
+ });
17
+
18
+ it('should signal short read', async function() {
19
+ let rc;
20
+ const pOpenOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
21
+ const openFlags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE;
22
+ rc = await vfs.jOpen('test', FILEID, openFlags, pOpenOutput);
23
+ expect(rc).toEqual(VFS.SQLITE_OK);
24
+
25
+ const pData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
26
+ const iOffset = 0;
27
+ rc = await vfs.jWrite(FILEID, pData, iOffset);
28
+ expect(rc).toEqual(VFS.SQLITE_OK);
29
+
30
+ const pReadData = Comlink.proxy(new Uint8Array(pData.length * 2).fill(0xfb));
31
+ rc = await vfs.jRead(FILEID, pReadData, iOffset);
32
+ expect(rc).toEqual(VFS.SQLITE_IOERR_SHORT_READ);
33
+ expect(pReadData.subarray(0, pData.length)).toEqual(pData);
34
+ expect(pReadData.subarray(pData.length))
35
+ .toEqual(new Uint8Array(pReadData.length - pData.length));
36
+ });
37
+ });
38
+ }
@@ -0,0 +1,36 @@
1
+ import * as Comlink from 'comlink';
2
+ import * as VFS from '../src/VFS.js';
3
+
4
+ const FILEID = 1;
5
+
6
+ export function vfs_xWrite(context) {
7
+ describe('vfs_xWrite', function() {
8
+ let proxy, vfs;
9
+ beforeEach(async function() {
10
+ proxy = await context.create();
11
+ vfs = proxy.vfs;
12
+ });
13
+
14
+ afterEach(async function() {
15
+ await context.destroy(proxy);
16
+ });
17
+
18
+ it('should round-trip data', async function() {
19
+ let rc;
20
+ const pOpenOutput = Comlink.proxy(new DataView(new ArrayBuffer(4)));
21
+ const openFlags = VFS.SQLITE_OPEN_CREATE | VFS.SQLITE_OPEN_READWRITE;
22
+ rc = await vfs.jOpen('test', FILEID, openFlags, pOpenOutput);
23
+ expect(rc).toEqual(VFS.SQLITE_OK);
24
+
25
+ const pData = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
26
+ const iOffset = 0;
27
+ rc = await vfs.jWrite(FILEID, pData, iOffset);
28
+ expect(rc).toEqual(VFS.SQLITE_OK);
29
+
30
+ const pReadData = Comlink.proxy(new Uint8Array(pData.length));
31
+ rc = await vfs.jRead(FILEID, pReadData, iOffset);
32
+ expect(rc).toEqual(VFS.SQLITE_OK);
33
+ expect([...pReadData]).toEqual([...pData]);
34
+ });
35
+ });
36
+ }