@powersync/web 1.12.3 → 1.13.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 (62) hide show
  1. package/README.md +33 -0
  2. package/dist/3cb48be086dd9edd02ff.wasm +0 -0
  3. package/dist/{_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js-async-mutex-c-3cff7d0.index.umd.js → _journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js-_powersync_co-780aa20.index.umd.js} +18 -8
  4. package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js-_powersync_co-780aa20.index.umd.js.map +1 -0
  5. package/dist/{_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js-async-mutex-c-3cff7d1.index.umd.js → _journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js-_powersync_co-780aa21.index.umd.js} +18 -8
  6. package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js-_powersync_co-780aa21.index.umd.js.map +1 -0
  7. package/dist/df958358cadf945bd0fe.wasm +0 -0
  8. package/dist/f9c8ada26c59f5bf4339.wasm +0 -0
  9. package/dist/index.umd.js +3707 -497
  10. package/dist/index.umd.js.map +1 -1
  11. package/dist/worker/SharedSyncImplementation.umd.js +246 -2171
  12. package/dist/worker/SharedSyncImplementation.umd.js.map +1 -1
  13. package/dist/worker/WASQLiteDB.umd.js +767 -196
  14. package/dist/worker/WASQLiteDB.umd.js.map +1 -1
  15. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite-async_mjs.umd.js +45 -0
  16. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite-async_mjs.umd.js.map +1 -0
  17. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite_mjs.umd.js +45 -0
  18. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_mc-wa-sqlite_mjs.umd.js.map +1 -0
  19. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js +45 -0
  20. package/dist/worker/node_modules_journeyapps_wa-sqlite_dist_wa-sqlite_mjs.umd.js.map +1 -0
  21. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js.umd.js +1509 -0
  22. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_AccessHandlePoolVFS_js.umd.js.map +1 -0
  23. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_OPFSCoopSyncVFS_js.umd.js +1641 -0
  24. package/dist/worker/node_modules_journeyapps_wa-sqlite_src_examples_OPFSCoopSyncVFS_js.umd.js.map +1 -0
  25. package/lib/package.json +2 -2
  26. package/lib/src/db/PowerSyncDatabase.d.ts +10 -2
  27. package/lib/src/db/PowerSyncDatabase.js +20 -4
  28. package/lib/src/db/adapters/AbstractWebSQLOpenFactory.d.ts +2 -0
  29. package/lib/src/db/adapters/AbstractWebSQLOpenFactory.js +3 -0
  30. package/lib/src/db/adapters/AsyncDatabaseConnection.d.ts +26 -0
  31. package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.d.ts +82 -0
  32. package/lib/src/db/adapters/LockedAsyncDatabaseAdapter.js +239 -0
  33. package/lib/src/db/adapters/WebDBAdapter.d.ts +17 -0
  34. package/lib/src/db/adapters/WebDBAdapter.js +1 -0
  35. package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.d.ts +39 -0
  36. package/lib/src/db/adapters/WorkerWrappedAsyncDatabaseConnection.js +46 -0
  37. package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.d.ts +127 -0
  38. package/lib/src/db/adapters/wa-sqlite/WASQLiteConnection.js +343 -0
  39. package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.d.ts +10 -42
  40. package/lib/src/db/adapters/wa-sqlite/WASQLiteDBAdapter.js +36 -212
  41. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.d.ts +12 -0
  42. package/lib/src/db/adapters/wa-sqlite/WASQLiteOpenFactory.js +81 -4
  43. package/lib/src/db/adapters/web-sql-flags.d.ts +17 -0
  44. package/lib/src/db/sync/SharedWebStreamingSyncImplementation.d.ts +9 -2
  45. package/lib/src/db/sync/SharedWebStreamingSyncImplementation.js +16 -10
  46. package/lib/src/db/sync/WebStreamingSyncImplementation.d.ts +0 -5
  47. package/lib/src/index.d.ts +8 -7
  48. package/lib/src/index.js +8 -7
  49. package/lib/src/worker/db/WASQLiteDB.worker.js +38 -20
  50. package/lib/src/worker/db/open-worker-database.d.ts +5 -4
  51. package/lib/src/worker/db/open-worker-database.js +5 -3
  52. package/lib/src/worker/sync/AbstractSharedSyncClientProvider.d.ts +1 -0
  53. package/lib/src/worker/sync/SharedSyncImplementation.d.ts +20 -3
  54. package/lib/src/worker/sync/SharedSyncImplementation.js +40 -11
  55. package/lib/tsconfig.tsbuildinfo +1 -1
  56. package/package.json +2 -2
  57. package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js-async-mutex-c-3cff7d0.index.umd.js.map +0 -1
  58. package/dist/_journeyapps_wa-sqlite-_journeyapps_wa-sqlite_src_examples_IDBBatchAtomicVFS_js-async-mutex-c-3cff7d1.index.umd.js.map +0 -1
  59. package/lib/src/shared/open-db.d.ts +0 -5
  60. package/lib/src/shared/open-db.js +0 -193
  61. package/lib/src/shared/types.d.ts +0 -22
  62. /package/lib/src/{shared/types.js → db/adapters/AsyncDatabaseConnection.js} +0 -0
@@ -0,0 +1,46 @@
1
+ import * as Comlink from 'comlink';
2
+ /**
3
+ * Wraps a provided instance of {@link AsyncDatabaseConnection}, providing necessary proxy
4
+ * functions for worker listeners.
5
+ */
6
+ export class WorkerWrappedAsyncDatabaseConnection {
7
+ options;
8
+ constructor(options) {
9
+ this.options = options;
10
+ }
11
+ get baseConnection() {
12
+ return this.options.baseConnection;
13
+ }
14
+ init() {
15
+ return this.baseConnection.init();
16
+ }
17
+ /**
18
+ * Get a MessagePort which can be used to share the internals of this connection.
19
+ */
20
+ async shareConnection() {
21
+ const { identifier, remote } = this.options;
22
+ const newPort = await remote[Comlink.createEndpoint]();
23
+ return { port: newPort, identifier };
24
+ }
25
+ /**
26
+ * Registers a table change notification callback with the base database.
27
+ * This can be extended by custom implementations in order to handle proxy events.
28
+ */
29
+ async registerOnTableChange(callback) {
30
+ return this.baseConnection.registerOnTableChange(Comlink.proxy(callback));
31
+ }
32
+ async close() {
33
+ await this.baseConnection.close();
34
+ this.options.remote[Comlink.releaseProxy]();
35
+ this.options.onClose?.();
36
+ }
37
+ execute(sql, params) {
38
+ return this.baseConnection.execute(sql, params);
39
+ }
40
+ executeBatch(sql, params) {
41
+ return this.baseConnection.executeBatch(sql, params);
42
+ }
43
+ getConfig() {
44
+ return this.baseConnection.getConfig();
45
+ }
46
+ }
@@ -0,0 +1,127 @@
1
+ import * as SQLite from '@journeyapps/wa-sqlite';
2
+ import { BaseObserver, BatchedUpdateNotification } from '@powersync/common';
3
+ import { Mutex } from 'async-mutex';
4
+ import { AsyncDatabaseConnection, OnTableChangeCallback, ProxiedQueryResult } from '../AsyncDatabaseConnection';
5
+ import { ResolvedWASQLiteOpenFactoryOptions } from './WASQLiteOpenFactory';
6
+ /**
7
+ * List of currently tested virtual filesystems
8
+ */
9
+ export declare enum WASQLiteVFS {
10
+ IDBBatchAtomicVFS = "IDBBatchAtomicVFS",
11
+ OPFSCoopSyncVFS = "OPFSCoopSyncVFS",
12
+ AccessHandlePoolVFS = "AccessHandlePoolVFS"
13
+ }
14
+ /**
15
+ * @internal
16
+ */
17
+ export type WASQLiteBroadCastTableUpdateEvent = {
18
+ changedTables: Set<string>;
19
+ connectionId: number;
20
+ };
21
+ /**
22
+ * @internal
23
+ */
24
+ export type WASQLiteConnectionListener = {
25
+ tablesUpdated: (event: BatchedUpdateNotification) => void;
26
+ };
27
+ /**
28
+ * @internal
29
+ */
30
+ export type SQLiteModule = Parameters<typeof SQLite.Factory>[0];
31
+ /**
32
+ * @internal
33
+ */
34
+ export type WASQLiteModuleFactoryOptions = {
35
+ dbFileName: string;
36
+ encryptionKey?: string;
37
+ };
38
+ /**
39
+ * @internal
40
+ */
41
+ export type WASQLiteModuleFactory = (options: WASQLiteModuleFactoryOptions) => Promise<{
42
+ module: SQLiteModule;
43
+ vfs: SQLiteVFS;
44
+ }>;
45
+ /**
46
+ * @internal
47
+ */
48
+ export declare const AsyncWASQLiteModuleFactory: () => Promise<any>;
49
+ /**
50
+ * @internal
51
+ */
52
+ export declare const MultiCipherAsyncWASQLiteModuleFactory: () => Promise<any>;
53
+ /**
54
+ * @internal
55
+ */
56
+ export declare const SyncWASQLiteModuleFactory: () => Promise<any>;
57
+ /**
58
+ * @internal
59
+ */
60
+ export declare const MultiCipherSyncWASQLiteModuleFactory: () => Promise<any>;
61
+ /**
62
+ * @internal
63
+ */
64
+ export declare const DEFAULT_MODULE_FACTORIES: {
65
+ IDBBatchAtomicVFS: (options: WASQLiteModuleFactoryOptions) => Promise<{
66
+ module: any;
67
+ vfs: any;
68
+ }>;
69
+ AccessHandlePoolVFS: (options: WASQLiteModuleFactoryOptions) => Promise<{
70
+ module: any;
71
+ vfs: any;
72
+ }>;
73
+ OPFSCoopSyncVFS: (options: WASQLiteModuleFactoryOptions) => Promise<{
74
+ module: any;
75
+ vfs: any;
76
+ }>;
77
+ };
78
+ /**
79
+ * @internal
80
+ * WA-SQLite connection which directly interfaces with WA-SQLite.
81
+ * This is usually instantiated inside a worker.
82
+ */
83
+ export declare class WASqliteConnection extends BaseObserver<WASQLiteConnectionListener> implements AsyncDatabaseConnection<ResolvedWASQLiteOpenFactoryOptions> {
84
+ protected options: ResolvedWASQLiteOpenFactoryOptions;
85
+ private _sqliteAPI;
86
+ private _dbP;
87
+ private _moduleFactory;
88
+ protected updatedTables: Set<string>;
89
+ protected updateTimer: ReturnType<typeof setTimeout> | null;
90
+ protected statementMutex: Mutex;
91
+ protected broadcastChannel: BroadcastChannel | null;
92
+ /**
93
+ * Unique id for this specific connection. This is used to prevent broadcast table change
94
+ * notification loops.
95
+ */
96
+ protected connectionId: number;
97
+ constructor(options: ResolvedWASQLiteOpenFactoryOptions);
98
+ protected get sqliteAPI(): SQLiteAPI;
99
+ protected get dbP(): number;
100
+ protected openDB(): Promise<number>;
101
+ protected executeEncryptionPragma(): Promise<void>;
102
+ protected openSQLiteAPI(): Promise<SQLiteAPI>;
103
+ protected registerBroadcastListeners(): void;
104
+ protected queueTableUpdate(tableNames: Set<string>, shouldBroadcast?: boolean): void;
105
+ init(): Promise<void>;
106
+ getConfig(): Promise<ResolvedWASQLiteOpenFactoryOptions>;
107
+ fireUpdates(shouldBroadcast?: boolean): void;
108
+ /**
109
+ * This executes SQL statements in a batch.
110
+ */
111
+ executeBatch(sql: string, bindings?: any[][]): Promise<ProxiedQueryResult>;
112
+ /**
113
+ * This executes single SQL statements inside a requested lock.
114
+ */
115
+ execute(sql: string | TemplateStringsArray, bindings?: any[]): Promise<ProxiedQueryResult>;
116
+ close(): Promise<void>;
117
+ registerOnTableChange(callback: OnTableChangeCallback): Promise<() => void>;
118
+ /**
119
+ * This requests a lock for executing statements.
120
+ * Should only be used internally.
121
+ */
122
+ protected acquireExecuteLock: <T>(callback: () => Promise<T>) => Promise<T>;
123
+ /**
124
+ * This executes a single statement using SQLite3.
125
+ */
126
+ protected executeSingleStatement(sql: string | TemplateStringsArray, bindings?: any[]): Promise<ProxiedQueryResult>;
127
+ }
@@ -0,0 +1,343 @@
1
+ import * as SQLite from '@journeyapps/wa-sqlite';
2
+ import { BaseObserver } from '@powersync/common';
3
+ import { Mutex } from 'async-mutex';
4
+ /**
5
+ * List of currently tested virtual filesystems
6
+ */
7
+ export var WASQLiteVFS;
8
+ (function (WASQLiteVFS) {
9
+ WASQLiteVFS["IDBBatchAtomicVFS"] = "IDBBatchAtomicVFS";
10
+ WASQLiteVFS["OPFSCoopSyncVFS"] = "OPFSCoopSyncVFS";
11
+ WASQLiteVFS["AccessHandlePoolVFS"] = "AccessHandlePoolVFS";
12
+ })(WASQLiteVFS || (WASQLiteVFS = {}));
13
+ /**
14
+ * @internal
15
+ */
16
+ export const AsyncWASQLiteModuleFactory = async () => {
17
+ const { default: factory } = await import('@journeyapps/wa-sqlite/dist/wa-sqlite-async.mjs');
18
+ return factory();
19
+ };
20
+ /**
21
+ * @internal
22
+ */
23
+ export const MultiCipherAsyncWASQLiteModuleFactory = async () => {
24
+ const { default: factory } = await import('@journeyapps/wa-sqlite/dist/mc-wa-sqlite-async.mjs');
25
+ return factory();
26
+ };
27
+ /**
28
+ * @internal
29
+ */
30
+ export const SyncWASQLiteModuleFactory = async () => {
31
+ const { default: factory } = await import('@journeyapps/wa-sqlite/dist/wa-sqlite.mjs');
32
+ return factory();
33
+ };
34
+ /**
35
+ * @internal
36
+ */
37
+ export const MultiCipherSyncWASQLiteModuleFactory = async () => {
38
+ const { default: factory } = await import('@journeyapps/wa-sqlite/dist/mc-wa-sqlite.mjs');
39
+ return factory();
40
+ };
41
+ /**
42
+ * @internal
43
+ */
44
+ export const DEFAULT_MODULE_FACTORIES = {
45
+ [WASQLiteVFS.IDBBatchAtomicVFS]: async (options) => {
46
+ let module;
47
+ if (options.encryptionKey) {
48
+ module = await MultiCipherAsyncWASQLiteModuleFactory();
49
+ }
50
+ else {
51
+ module = await AsyncWASQLiteModuleFactory();
52
+ }
53
+ const { IDBBatchAtomicVFS } = await import('@journeyapps/wa-sqlite/src/examples/IDBBatchAtomicVFS.js');
54
+ return {
55
+ module,
56
+ // @ts-expect-error The types for this static method are missing upstream
57
+ vfs: await IDBBatchAtomicVFS.create(options.dbFileName, module, { lockPolicy: 'exclusive' })
58
+ };
59
+ },
60
+ [WASQLiteVFS.AccessHandlePoolVFS]: async (options) => {
61
+ let module;
62
+ if (options.encryptionKey) {
63
+ module = await MultiCipherSyncWASQLiteModuleFactory();
64
+ }
65
+ else {
66
+ module = await SyncWASQLiteModuleFactory();
67
+ }
68
+ // @ts-expect-error The types for this static method are missing upstream
69
+ const { AccessHandlePoolVFS } = await import('@journeyapps/wa-sqlite/src/examples/AccessHandlePoolVFS.js');
70
+ return {
71
+ module,
72
+ vfs: await AccessHandlePoolVFS.create(options.dbFileName, module)
73
+ };
74
+ },
75
+ [WASQLiteVFS.OPFSCoopSyncVFS]: async (options) => {
76
+ let module;
77
+ if (options.encryptionKey) {
78
+ module = await MultiCipherSyncWASQLiteModuleFactory();
79
+ }
80
+ else {
81
+ module = await SyncWASQLiteModuleFactory();
82
+ }
83
+ // @ts-expect-error The types for this static method are missing upstream
84
+ const { OPFSCoopSyncVFS } = await import('@journeyapps/wa-sqlite/src/examples/OPFSCoopSyncVFS.js');
85
+ return {
86
+ module,
87
+ vfs: await OPFSCoopSyncVFS.create(options.dbFileName, module)
88
+ };
89
+ }
90
+ };
91
+ /**
92
+ * @internal
93
+ * WA-SQLite connection which directly interfaces with WA-SQLite.
94
+ * This is usually instantiated inside a worker.
95
+ */
96
+ export class WASqliteConnection extends BaseObserver {
97
+ options;
98
+ _sqliteAPI = null;
99
+ _dbP = null;
100
+ _moduleFactory;
101
+ updatedTables;
102
+ updateTimer;
103
+ statementMutex;
104
+ broadcastChannel;
105
+ /**
106
+ * Unique id for this specific connection. This is used to prevent broadcast table change
107
+ * notification loops.
108
+ */
109
+ connectionId;
110
+ constructor(options) {
111
+ super();
112
+ this.options = options;
113
+ this.updatedTables = new Set();
114
+ this.updateTimer = null;
115
+ this.broadcastChannel = null;
116
+ this.connectionId = new Date().valueOf() + Math.random();
117
+ this.statementMutex = new Mutex();
118
+ this._moduleFactory = DEFAULT_MODULE_FACTORIES[this.options.vfs];
119
+ }
120
+ get sqliteAPI() {
121
+ if (!this._sqliteAPI) {
122
+ throw new Error(`Initialization has not completed`);
123
+ }
124
+ return this._sqliteAPI;
125
+ }
126
+ get dbP() {
127
+ if (!this._dbP) {
128
+ throw new Error(`Initialization has not completed`);
129
+ }
130
+ return this._dbP;
131
+ }
132
+ async openDB() {
133
+ this._dbP = await this.sqliteAPI.open_v2(this.options.dbFilename);
134
+ return this._dbP;
135
+ }
136
+ async executeEncryptionPragma() {
137
+ if (this.options.encryptionKey) {
138
+ await this.executeSingleStatement(`PRAGMA key = "${this.options.encryptionKey}"`);
139
+ }
140
+ return;
141
+ }
142
+ async openSQLiteAPI() {
143
+ const { module, vfs } = await this._moduleFactory({
144
+ dbFileName: this.options.dbFilename,
145
+ encryptionKey: this.options.encryptionKey
146
+ });
147
+ const sqlite3 = SQLite.Factory(module);
148
+ sqlite3.vfs_register(vfs, true);
149
+ /**
150
+ * Register the PowerSync core SQLite extension
151
+ */
152
+ module.ccall('powersync_init_static', 'int', []);
153
+ /**
154
+ * Create the multiple cipher vfs if an encryption key is provided
155
+ */
156
+ if (this.options.encryptionKey) {
157
+ const createResult = module.ccall('sqlite3mc_vfs_create', 'int', ['string', 'int'], [this.options.dbFilename, 1]);
158
+ if (createResult !== 0) {
159
+ throw new Error('Failed to create multiple cipher vfs, Database encryption will not work');
160
+ }
161
+ }
162
+ return sqlite3;
163
+ }
164
+ registerBroadcastListeners() {
165
+ this.broadcastChannel = new BroadcastChannel(`${this.options.dbFilename}-table-updates`);
166
+ this.broadcastChannel.addEventListener('message', (event) => {
167
+ const data = event.data;
168
+ if (this.connectionId == data.connectionId) {
169
+ // Ignore messages from the same connection
170
+ return;
171
+ }
172
+ // Ensuring that we don't rebroadcast the same message
173
+ this.queueTableUpdate(data.changedTables, false);
174
+ });
175
+ }
176
+ queueTableUpdate(tableNames, shouldBroadcast = true) {
177
+ tableNames.forEach((tableName) => this.updatedTables.add(tableName));
178
+ if (this.updateTimer == null) {
179
+ this.updateTimer = setTimeout(() => this.fireUpdates(shouldBroadcast), 0);
180
+ }
181
+ }
182
+ async init() {
183
+ this._sqliteAPI = await this.openSQLiteAPI();
184
+ await this.openDB();
185
+ this.registerBroadcastListeners();
186
+ await this.executeSingleStatement(`PRAGMA temp_store = ${this.options.temporaryStorage};`);
187
+ await this.executeEncryptionPragma();
188
+ this.sqliteAPI.update_hook(this.dbP, (updateType, dbName, tableName) => {
189
+ if (!tableName) {
190
+ return;
191
+ }
192
+ const changedTables = new Set([tableName]);
193
+ this.queueTableUpdate(changedTables);
194
+ });
195
+ }
196
+ async getConfig() {
197
+ return this.options;
198
+ }
199
+ fireUpdates(shouldBroadcast = true) {
200
+ this.updateTimer = null;
201
+ const event = { tables: [...this.updatedTables], groupedUpdates: {}, rawUpdates: [] };
202
+ // Share to other connections
203
+ if (shouldBroadcast) {
204
+ this.broadcastChannel.postMessage({
205
+ changedTables: this.updatedTables,
206
+ connectionId: this.connectionId
207
+ });
208
+ }
209
+ this.updatedTables.clear();
210
+ this.iterateListeners((cb) => cb.tablesUpdated?.(event));
211
+ }
212
+ /**
213
+ * This executes SQL statements in a batch.
214
+ */
215
+ async executeBatch(sql, bindings) {
216
+ return this.acquireExecuteLock(async () => {
217
+ let affectedRows = 0;
218
+ try {
219
+ await this.executeSingleStatement('BEGIN TRANSACTION');
220
+ const wrappedBindings = bindings ? bindings : [];
221
+ for await (const stmt of this.sqliteAPI.statements(this.dbP, sql)) {
222
+ if (stmt === null) {
223
+ return {
224
+ rowsAffected: 0,
225
+ rows: { _array: [], length: 0 }
226
+ };
227
+ }
228
+ //Prepare statement once
229
+ for (const binding of wrappedBindings) {
230
+ // TODO not sure why this is needed currently, but booleans break
231
+ for (let i = 0; i < binding.length; i++) {
232
+ const b = binding[i];
233
+ if (typeof b == 'boolean') {
234
+ binding[i] = b ? 1 : 0;
235
+ }
236
+ }
237
+ if (bindings) {
238
+ this.sqliteAPI.bind_collection(stmt, binding);
239
+ }
240
+ const result = await this.sqliteAPI.step(stmt);
241
+ if (result === SQLite.SQLITE_DONE) {
242
+ //The value returned by sqlite3_changes() immediately after an INSERT, UPDATE or DELETE statement run on a view is always zero.
243
+ affectedRows += this.sqliteAPI.changes(this.dbP);
244
+ }
245
+ this.sqliteAPI.reset(stmt);
246
+ }
247
+ }
248
+ await this.executeSingleStatement('COMMIT');
249
+ }
250
+ catch (err) {
251
+ await this.executeSingleStatement('ROLLBACK');
252
+ return {
253
+ rowsAffected: 0,
254
+ rows: { _array: [], length: 0 }
255
+ };
256
+ }
257
+ const result = {
258
+ rowsAffected: affectedRows,
259
+ rows: { _array: [], length: 0 }
260
+ };
261
+ return result;
262
+ });
263
+ }
264
+ /**
265
+ * This executes single SQL statements inside a requested lock.
266
+ */
267
+ async execute(sql, bindings) {
268
+ // Running multiple statements on the same connection concurrently should not be allowed
269
+ return this.acquireExecuteLock(async () => {
270
+ return this.executeSingleStatement(sql, bindings);
271
+ });
272
+ }
273
+ async close() {
274
+ this.broadcastChannel?.close();
275
+ await this.sqliteAPI.close(this.dbP);
276
+ }
277
+ async registerOnTableChange(callback) {
278
+ return this.registerListener({
279
+ tablesUpdated: (event) => callback(event)
280
+ });
281
+ }
282
+ /**
283
+ * This requests a lock for executing statements.
284
+ * Should only be used internally.
285
+ */
286
+ acquireExecuteLock = (callback) => {
287
+ return this.statementMutex.runExclusive(callback);
288
+ };
289
+ /**
290
+ * This executes a single statement using SQLite3.
291
+ */
292
+ async executeSingleStatement(sql, bindings) {
293
+ const results = [];
294
+ for await (const stmt of this.sqliteAPI.statements(this.dbP, sql)) {
295
+ let columns;
296
+ const wrappedBindings = bindings ? [bindings] : [[]];
297
+ for (const binding of wrappedBindings) {
298
+ // TODO not sure why this is needed currently, but booleans break
299
+ binding.forEach((b, index, arr) => {
300
+ if (typeof b == 'boolean') {
301
+ arr[index] = b ? 1 : 0;
302
+ }
303
+ });
304
+ this.sqliteAPI.reset(stmt);
305
+ if (bindings) {
306
+ this.sqliteAPI.bind_collection(stmt, binding);
307
+ }
308
+ const rows = [];
309
+ while ((await this.sqliteAPI.step(stmt)) === SQLite.SQLITE_ROW) {
310
+ const row = this.sqliteAPI.row(stmt);
311
+ rows.push(row);
312
+ }
313
+ columns = columns ?? this.sqliteAPI.column_names(stmt);
314
+ if (columns.length) {
315
+ results.push({ columns, rows });
316
+ }
317
+ }
318
+ // When binding parameters, only a single statement is executed.
319
+ if (bindings) {
320
+ break;
321
+ }
322
+ }
323
+ const rows = [];
324
+ for (const resultSet of results) {
325
+ for (const row of resultSet.rows) {
326
+ const outRow = {};
327
+ resultSet.columns.forEach((key, index) => {
328
+ outRow[key] = row[index];
329
+ });
330
+ rows.push(outRow);
331
+ }
332
+ }
333
+ const result = {
334
+ insertId: this.sqliteAPI.last_insert_id(this.dbP),
335
+ rowsAffected: this.sqliteAPI.changes(this.dbP),
336
+ rows: {
337
+ _array: rows,
338
+ length: rows.length
339
+ }
340
+ };
341
+ return result;
342
+ }
343
+ }
@@ -1,5 +1,7 @@
1
- import { type DBAdapter, type DBAdapterListener, type DBLockOptions, type LockContext, type PowerSyncOpenFactoryOptions, type QueryResult, type Transaction, BaseObserver } from '@powersync/common';
1
+ import { type PowerSyncOpenFactoryOptions } from '@powersync/common';
2
+ import { LockedAsyncDatabaseAdapter } from '../LockedAsyncDatabaseAdapter';
2
3
  import { ResolvedWebSQLOpenOptions, TemporaryStorageOption, WebSQLFlags } from '../web-sql-flags';
4
+ import { WASQLiteVFS } from './WASQLiteConnection';
3
5
  /**
4
6
  * These flags are the same as {@link WebSQLFlags}.
5
7
  * This export is maintained only for API consistency
@@ -13,51 +15,17 @@ export interface WASQLiteDBAdapterOptions extends Omit<PowerSyncOpenFactoryOptio
13
15
  */
14
16
  workerPort?: MessagePort;
15
17
  worker?: string | URL | ((options: ResolvedWebSQLOpenOptions) => Worker | SharedWorker);
18
+ vfs?: WASQLiteVFS;
16
19
  temporaryStorage?: TemporaryStorageOption;
20
+ /**
21
+ * Encryption key for the database.
22
+ * If set, the database will be encrypted using multiple-ciphers.
23
+ */
24
+ encryptionKey?: string;
17
25
  }
18
26
  /**
19
27
  * Adapter for WA-SQLite SQLite connections.
20
28
  */
21
- export declare class WASQLiteDBAdapter extends BaseObserver<DBAdapterListener> implements DBAdapter {
22
- protected options: WASQLiteDBAdapterOptions;
23
- private initialized;
24
- private logger;
25
- private dbGetHelpers;
26
- private methods;
27
- private debugMode;
29
+ export declare class WASQLiteDBAdapter extends LockedAsyncDatabaseAdapter {
28
30
  constructor(options: WASQLiteDBAdapterOptions);
29
- get name(): string;
30
- protected get flags(): Required<WASQLiteFlags>;
31
- getWorker(): void;
32
- protected init(): Promise<void>;
33
- execute(query: string, params?: any[] | undefined): Promise<QueryResult>;
34
- executeBatch(query: string, params?: any[][]): Promise<QueryResult>;
35
- /**
36
- * Wraps the worker execute function, awaiting for it to be available
37
- */
38
- private _execute;
39
- /**
40
- * Wraps the worker executeBatch function, awaiting for it to be available
41
- */
42
- private _executeBatch;
43
- /**
44
- * Attempts to close the connection.
45
- * Shared workers might not actually close the connection if other
46
- * tabs are still using it.
47
- */
48
- close(): void;
49
- getAll<T>(sql: string, parameters?: any[] | undefined): Promise<T[]>;
50
- getOptional<T>(sql: string, parameters?: any[] | undefined): Promise<T | null>;
51
- get<T>(sql: string, parameters?: any[] | undefined): Promise<T>;
52
- readLock<T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions | undefined): Promise<T>;
53
- writeLock<T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions | undefined): Promise<T>;
54
- protected acquireLock(callback: () => Promise<any>): Promise<any>;
55
- readTransaction<T>(fn: (tx: Transaction) => Promise<T>, options?: DBLockOptions | undefined): Promise<T>;
56
- writeTransaction<T>(fn: (tx: Transaction) => Promise<T>, options?: DBLockOptions | undefined): Promise<T>;
57
- /**
58
- * Wraps a lock context into a transaction context
59
- */
60
- private wrapTransaction;
61
- private generateDBHelpers;
62
- refreshSchema(): Promise<void>;
63
31
  }