@lunarhue/react-native-web-wa-sqlite 1.0.9

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 +681 -0
  11. package/src/VFS.js +222 -0
  12. package/src/WebLocksMixin.js +411 -0
  13. package/src/examples/AccessHandlePoolVFS.js +458 -0
  14. package/src/examples/IDBBatchAtomicVFS.js +827 -0
  15. package/src/examples/IDBMirrorVFS.js +889 -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 +597 -0
  21. package/src/examples/OPFSPermutedVFS.js +1217 -0
  22. package/src/examples/README.md +89 -0
  23. package/src/examples/tag.js +82 -0
  24. package/src/sqlite-api.js +912 -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 +1330 -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 +447 -0
  44. package/test/callbacks.test.js +581 -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,52 @@
1
+ import * as Comlink from 'comlink';
2
+
3
+ export function sql_0002(context) {
4
+ describe('sql_0002', function() {
5
+ let proxy, sqlite3, db;
6
+ beforeEach(async function() {
7
+ proxy = await context.create();
8
+ sqlite3 = proxy.sqlite3;
9
+ db = await sqlite3.open_v2('demo');
10
+ });
11
+
12
+ afterEach(async function() {
13
+ await sqlite3.close(db);
14
+ await context.destroy(proxy);
15
+ });
16
+
17
+ it('should vacuum to minimize page count', async function() {
18
+ await sqlite3.exec(db, `
19
+ CREATE TABLE t AS
20
+ WITH numbers(n) AS
21
+ (SELECT 1 UNION ALL SELECT n + 1 FROM numbers LIMIT 10000)
22
+ SELECT n FROM numbers;
23
+ `);
24
+
25
+ let nPagesBeforeVacuum;
26
+ await sqlite3.exec(db, `
27
+ PRAGMA page_count;
28
+ `, Comlink.proxy(row => nPagesBeforeVacuum = row[0]));
29
+
30
+ await sqlite3.exec(db, `
31
+ DELETE FROM t WHERE sqrt(n) != floor(sqrt(n));
32
+ `);
33
+
34
+ await sqlite3.exec(db, `
35
+ VACUUM;
36
+ `);
37
+
38
+ let nPagesAfterVacuum;
39
+ await sqlite3.exec(db, `
40
+ PRAGMA page_count;
41
+ `, Comlink.proxy(row => nPagesAfterVacuum = row[0]));
42
+
43
+ expect(nPagesAfterVacuum).toBeLessThan(nPagesBeforeVacuum);
44
+
45
+ let checkStatus;
46
+ await sqlite3.exec(db, `
47
+ PRAGMA integrity_check;
48
+ `, Comlink.proxy(row => checkStatus = row[0]));
49
+ expect(checkStatus).toBe('ok');
50
+ });
51
+ });
52
+ }
@@ -0,0 +1,83 @@
1
+ import * as Comlink from 'comlink';
2
+
3
+ export function sql_0003(context) {
4
+ describe('sql_0003', function() {
5
+ let proxy, sqlite3, db;
6
+ beforeEach(async function() {
7
+ proxy = await context.create();
8
+ sqlite3 = proxy.sqlite3;
9
+ db = await sqlite3.open_v2('demo');
10
+ });
11
+
12
+ afterEach(async function() {
13
+ await sqlite3.close(db);
14
+ await context.destroy(proxy);
15
+ });
16
+
17
+ it('should vacuum to decrease page size', async function() {
18
+ await sqlite3.exec(db, `
19
+ PRAGMA page_size=8192;
20
+ CREATE TABLE t AS
21
+ WITH numbers(n) AS
22
+ (SELECT 1 UNION ALL SELECT n + 1 FROM numbers LIMIT 10000)
23
+ SELECT n FROM numbers;
24
+ `);
25
+
26
+ let pageSizeBeforeVacuum;
27
+ await sqlite3.exec(db, `
28
+ PRAGMA page_size;
29
+ `, Comlink.proxy(row => pageSizeBeforeVacuum = row[0]));
30
+ expect(pageSizeBeforeVacuum).toBe(8192);
31
+
32
+ await sqlite3.exec(db, `
33
+ PRAGMA page_size=4096;
34
+ VACUUM;
35
+ `);
36
+
37
+ let pageSizeAfterVacuum;
38
+ await sqlite3.exec(db, `
39
+ PRAGMA page_size;
40
+ `, Comlink.proxy(row => pageSizeAfterVacuum = row[0]));
41
+ expect(pageSizeAfterVacuum).toBe(4096);
42
+
43
+ let checkStatus;
44
+ await sqlite3.exec(db, `
45
+ PRAGMA integrity_check;
46
+ `, Comlink.proxy(row => checkStatus = row[0]));
47
+ expect(checkStatus).toBe('ok');
48
+ });
49
+
50
+ it('should vacuum to increase page size', async function() {
51
+ await sqlite3.exec(db, `
52
+ PRAGMA page_size=8192;
53
+ CREATE TABLE t AS
54
+ WITH numbers(n) AS
55
+ (SELECT 1 UNION ALL SELECT n + 1 FROM numbers LIMIT 10000)
56
+ SELECT n FROM numbers;
57
+ `);
58
+
59
+ let pageSizeBeforeVacuum;
60
+ await sqlite3.exec(db, `
61
+ PRAGMA page_size;
62
+ `, Comlink.proxy(row => pageSizeBeforeVacuum = row[0]));
63
+ expect(pageSizeBeforeVacuum).toBe(8192);
64
+
65
+ await sqlite3.exec(db, `
66
+ PRAGMA page_size=16384;
67
+ VACUUM;
68
+ `);
69
+
70
+ let pageSizeAfterVacuum;
71
+ await sqlite3.exec(db, `
72
+ PRAGMA page_size;
73
+ `, Comlink.proxy(row => pageSizeAfterVacuum = row[0]));
74
+ expect(pageSizeAfterVacuum).toBe(16384);
75
+
76
+ let checkStatus;
77
+ await sqlite3.exec(db, `
78
+ PRAGMA integrity_check;
79
+ `, Comlink.proxy(row => checkStatus = row[0]));
80
+ expect(checkStatus).toBe('ok');
81
+ });
82
+ });
83
+ }
@@ -0,0 +1,81 @@
1
+ import * as Comlink from 'comlink';
2
+
3
+ export function sql_0004(context) {
4
+ const cleanup = [];
5
+ beforeEach(async function() {
6
+ cleanup.splice(0);
7
+ });
8
+
9
+ afterEach(async function() {
10
+ for (const fn of cleanup) {
11
+ await fn();
12
+ }
13
+ });
14
+
15
+ describe('sql_0004', function() {
16
+ it('should recover after crash', async function() {
17
+ const proxyA = await context.create();
18
+ try {
19
+ const sqlite3 = proxyA.sqlite3;
20
+ const db = await sqlite3.open_v2('demo');
21
+ await sqlite3.exec(db, `
22
+ PRAGMA cache_size=0;
23
+ CREATE TABLE t(x);
24
+ INSERT INTO t VALUES (1), (2), (3);
25
+ `);
26
+
27
+ let sum;
28
+ await sqlite3.exec(db, `
29
+ SELECT sum(x) FROM t;
30
+ `, Comlink.proxy(row => sum = row[0]));
31
+ expect(sum).toBe(6);
32
+
33
+ let check;
34
+ await sqlite3.exec(db, `
35
+ PRAGMA integrity_check;
36
+ `, Comlink.proxy(row => check = row[0]));
37
+ expect(check).toBe('ok');
38
+
39
+ // Begin a transaction but don't commit it.
40
+ await sqlite3.exec(db, `
41
+ BEGIN TRANSACTION;
42
+ WITH RECURSIVE cnt(x) AS
43
+ (SELECT 1 UNION ALL SELECT x+1 FROM cnt LIMIT 10000)
44
+ INSERT INTO t SELECT * FROM cnt;
45
+ `);
46
+ } finally {
47
+ await context.destroy(proxyA);
48
+ }
49
+
50
+ await new Promise(resolve => setTimeout(resolve, 250));
51
+
52
+ const proxyB = await context.create({ reset: false });
53
+ try {
54
+ const sqlite3 = proxyB.sqlite3;
55
+ const db = await sqlite3.open_v2('demo');
56
+
57
+ let sum;
58
+ await sqlite3.exec(db, `
59
+ SELECT sum(x) FROM t;
60
+ `, Comlink.proxy(row => sum = row[0]));
61
+ expect(sum).toBe(6);
62
+
63
+ let check;
64
+ await sqlite3.exec(db, `
65
+ PRAGMA integrity_check;
66
+ `, Comlink.proxy(row => check = row[0]));
67
+ expect(check).toBe('ok');
68
+
69
+ await sqlite3.exec(db, `
70
+ INSERT INTO t VALUES (4), (5);
71
+ `);
72
+ await sqlite3.exec(db, `
73
+ SELECT sum(x) FROM t;
74
+ `, Comlink.proxy(row => sum = row[0]));
75
+ expect(sum).toBe(15);
76
+ } finally {
77
+ await context.destroy(proxyB);
78
+ }
79
+ });
80
+ });
81
+ }
@@ -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
+ }