@powersync/capacitor 0.0.0-dev-20260305124002 → 0.0.0-dev-20260503073249
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/PowersyncCapacitor.podspec +1 -1
- package/README.md +1 -1
- package/android/build.gradle +1 -1
- package/dist/esm/PowerSyncDatabase.d.ts +2 -1
- package/dist/esm/PowerSyncDatabase.js +12 -2
- package/dist/esm/PowerSyncDatabase.js.map +1 -1
- package/dist/esm/adapter/CapacitorSQLiteAdapter.d.ts +24 -15
- package/dist/esm/adapter/CapacitorSQLiteAdapter.js +26 -85
- package/dist/esm/adapter/CapacitorSQLiteAdapter.js.map +1 -1
- package/dist/esm/sync/CapacitorBucketStorageAdapter.d.ts +4 -0
- package/dist/esm/sync/CapacitorBucketStorageAdapter.js +21 -0
- package/dist/esm/sync/CapacitorBucketStorageAdapter.js.map +1 -0
- package/dist/esm/sync/CapacitorRemote.d.ts +4 -0
- package/dist/esm/sync/CapacitorRemote.js +12 -0
- package/dist/esm/sync/CapacitorRemote.js.map +1 -0
- package/dist/esm/sync/CapacitorSyncImplementation.js +3 -8
- package/dist/esm/sync/CapacitorSyncImplementation.js.map +1 -1
- package/dist/plugin.cjs +68 -93
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.d.cts +24 -15
- package/dist/plugin.js +70 -94
- package/dist/plugin.js.map +1 -1
- package/package.json +2 -5
- package/src/PowerSyncDatabase.ts +13 -3
- package/src/adapter/CapacitorSQLiteAdapter.ts +48 -120
- package/src/sync/CapacitorBucketStorageAdapter.ts +22 -0
- package/src/sync/CapacitorRemote.ts +12 -0
- package/src/sync/CapacitorSyncImplementation.ts +3 -7
|
@@ -4,15 +4,17 @@ import { Capacitor } from '@capacitor/core';
|
|
|
4
4
|
import {
|
|
5
5
|
BaseObserver,
|
|
6
6
|
BatchedUpdateNotification,
|
|
7
|
+
ConnectionPool,
|
|
7
8
|
DBAdapter,
|
|
9
|
+
DBAdapterDefaultMixin,
|
|
8
10
|
DBAdapterListener,
|
|
9
11
|
DBLockOptions,
|
|
10
12
|
LockContext,
|
|
11
|
-
|
|
13
|
+
Mutex,
|
|
12
14
|
QueryResult,
|
|
15
|
+
timeoutSignal,
|
|
13
16
|
Transaction
|
|
14
17
|
} from '@powersync/web';
|
|
15
|
-
import { Mutex } from 'async-mutex';
|
|
16
18
|
import { PowerSyncCore } from '../plugin/PowerSyncCore.js';
|
|
17
19
|
import { messageForErrorCode } from '../plugin/PowerSyncPlugin.js';
|
|
18
20
|
import { CapacitorSQLiteOpenFactoryOptions, DEFAULT_SQLITE_OPTIONS } from './CapacitorSQLiteOpenFactory.js';
|
|
@@ -30,13 +32,8 @@ async function monitorQuery(sql: string, executor: () => Promise<QueryResult>):
|
|
|
30
32
|
throw e;
|
|
31
33
|
}
|
|
32
34
|
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
*
|
|
36
|
-
* @experimental
|
|
37
|
-
* @alpha This is currently experimental and may change without a major version bump.
|
|
38
|
-
*/
|
|
39
|
-
export class CapacitorSQLiteAdapter extends BaseObserver<DBAdapterListener> implements DBAdapter {
|
|
35
|
+
|
|
36
|
+
class CapacitorConnectionPool extends BaseObserver<DBAdapterListener> implements ConnectionPool {
|
|
40
37
|
protected _writeConnection: SQLiteDBConnection | null;
|
|
41
38
|
protected _readConnection: SQLiteDBConnection | null;
|
|
42
39
|
protected initializedPromise: Promise<void>;
|
|
@@ -206,26 +203,8 @@ export class CapacitorSQLiteAdapter extends BaseObserver<DBAdapterListener> impl
|
|
|
206
203
|
return results.rows?._array.map((row) => Object.values(row)) ?? [];
|
|
207
204
|
};
|
|
208
205
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
getOptional,
|
|
212
|
-
get,
|
|
213
|
-
executeRaw,
|
|
214
|
-
execute
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
execute(query: string, params?: any[]): Promise<QueryResult> {
|
|
219
|
-
return this.writeLock((tx) => tx.execute(query, params));
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
executeRaw(query: string, params?: any[]): Promise<any[][]> {
|
|
223
|
-
return this.writeLock((tx) => tx.executeRaw(query, params));
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
async executeBatch(query: string, params: any[][] = []): Promise<QueryResult> {
|
|
227
|
-
return this.writeLock(async (tx) => {
|
|
228
|
-
let result = await this.writeConnection.executeSet(
|
|
206
|
+
const executeBatch = async (query: string, params: any[][] = []): Promise<QueryResult> => {
|
|
207
|
+
let result = await db.executeSet(
|
|
229
208
|
params.map((param) => ({
|
|
230
209
|
statement: query,
|
|
231
210
|
values: param
|
|
@@ -236,55 +215,44 @@ export class CapacitorSQLiteAdapter extends BaseObserver<DBAdapterListener> impl
|
|
|
236
215
|
rowsAffected: result.changes?.changes ?? 0,
|
|
237
216
|
insertId: result.changes?.lastId
|
|
238
217
|
};
|
|
239
|
-
}
|
|
240
|
-
}
|
|
218
|
+
};
|
|
241
219
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
);
|
|
220
|
+
return {
|
|
221
|
+
getAll,
|
|
222
|
+
getOptional,
|
|
223
|
+
get,
|
|
224
|
+
executeRaw,
|
|
225
|
+
execute,
|
|
226
|
+
executeBatch
|
|
227
|
+
};
|
|
251
228
|
}
|
|
252
229
|
|
|
253
|
-
|
|
254
|
-
return this.
|
|
255
|
-
|
|
256
|
-
|
|
230
|
+
readLock<T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions): Promise<T> {
|
|
231
|
+
return this.readMutex.runExclusive(async () => {
|
|
232
|
+
await this.initializedPromise;
|
|
233
|
+
return fn(this.generateLockContext(this.readConnection));
|
|
234
|
+
}, timeoutSignal(options?.timeoutMs));
|
|
257
235
|
}
|
|
258
236
|
|
|
259
237
|
writeLock<T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions): Promise<T> {
|
|
260
|
-
return
|
|
261
|
-
this.
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
return result;
|
|
279
|
-
},
|
|
280
|
-
options
|
|
281
|
-
);
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
writeTransaction<T>(fn: (tx: Transaction) => Promise<T>, options?: DBLockOptions): Promise<T> {
|
|
285
|
-
return this.writeLock(async (ctx) => {
|
|
286
|
-
return this.internalTransaction(ctx, fn);
|
|
287
|
-
});
|
|
238
|
+
return this.writeMutex.runExclusive(async () => {
|
|
239
|
+
await this.initializedPromise;
|
|
240
|
+
const result = await fn(this.generateLockContext(this.writeConnection));
|
|
241
|
+
|
|
242
|
+
// Fetch table updates
|
|
243
|
+
const updates = await this.writeConnection.query("SELECT powersync_update_hooks('get') AS table_name");
|
|
244
|
+
const jsonUpdates = updates.values?.[0];
|
|
245
|
+
if (!jsonUpdates || !jsonUpdates.table_name) {
|
|
246
|
+
throw new Error('Could not fetch table updates');
|
|
247
|
+
}
|
|
248
|
+
const notification: BatchedUpdateNotification = {
|
|
249
|
+
rawUpdates: [],
|
|
250
|
+
tables: JSON.parse(jsonUpdates.table_name),
|
|
251
|
+
groupedUpdates: {}
|
|
252
|
+
};
|
|
253
|
+
this.iterateListeners((l) => l.tablesUpdated?.(notification));
|
|
254
|
+
return result;
|
|
255
|
+
}, timeoutSignal(options?.timeoutMs));
|
|
288
256
|
}
|
|
289
257
|
|
|
290
258
|
refreshSchema(): Promise<void> {
|
|
@@ -296,52 +264,12 @@ export class CapacitorSQLiteAdapter extends BaseObserver<DBAdapterListener> impl
|
|
|
296
264
|
});
|
|
297
265
|
});
|
|
298
266
|
}
|
|
299
|
-
|
|
300
|
-
getAll<T>(sql: string, parameters?: any[]): Promise<T[]> {
|
|
301
|
-
return this.readLock((tx) => tx.getAll<T>(sql, parameters));
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
getOptional<T>(sql: string, parameters?: any[]): Promise<T | null> {
|
|
305
|
-
return this.readLock((tx) => tx.getOptional<T>(sql, parameters));
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
get<T>(sql: string, parameters?: any[]): Promise<T> {
|
|
309
|
-
return this.readLock((tx) => tx.get<T>(sql, parameters));
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
protected async internalTransaction<T>(ctx: LockContext, fn: (tx: Transaction) => Promise<T>): Promise<T> {
|
|
313
|
-
let finalized = false;
|
|
314
|
-
const commit = async (): Promise<QueryResult> => {
|
|
315
|
-
if (finalized) {
|
|
316
|
-
return { rowsAffected: 0 };
|
|
317
|
-
}
|
|
318
|
-
finalized = true;
|
|
319
|
-
return ctx.execute('COMMIT');
|
|
320
|
-
};
|
|
321
|
-
const rollback = async (): Promise<QueryResult> => {
|
|
322
|
-
if (finalized) {
|
|
323
|
-
return { rowsAffected: 0 };
|
|
324
|
-
}
|
|
325
|
-
finalized = true;
|
|
326
|
-
return ctx.execute('ROLLBACK');
|
|
327
|
-
};
|
|
328
|
-
try {
|
|
329
|
-
await ctx.execute('BEGIN');
|
|
330
|
-
const result = await fn({
|
|
331
|
-
...ctx,
|
|
332
|
-
commit,
|
|
333
|
-
rollback
|
|
334
|
-
});
|
|
335
|
-
await commit();
|
|
336
|
-
return result;
|
|
337
|
-
} catch (ex) {
|
|
338
|
-
try {
|
|
339
|
-
await rollback();
|
|
340
|
-
} catch (ex2) {
|
|
341
|
-
// In rare cases, a rollback may fail.
|
|
342
|
-
// Safe to ignore.
|
|
343
|
-
}
|
|
344
|
-
throw ex;
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
267
|
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* An implementation of {@link DBAdapter} using the Capacitor Community SQLite [plugin](https://github.com/capacitor-community/sqlite).
|
|
271
|
+
*
|
|
272
|
+
* @experimental
|
|
273
|
+
* @alpha This is currently experimental and may change without a major version bump.
|
|
274
|
+
*/
|
|
275
|
+
export class CapacitorSQLiteAdapter extends DBAdapterDefaultMixin(CapacitorConnectionPool) {}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { PowerSyncControlCommand, SqliteBucketStorage } from '@powersync/web';
|
|
2
|
+
|
|
3
|
+
export class CapacitorBucketStorageAdapter extends SqliteBucketStorage {
|
|
4
|
+
control(op: PowerSyncControlCommand, payload: string | Uint8Array | ArrayBuffer | null): Promise<string> {
|
|
5
|
+
if (payload instanceof Uint8Array && (payload as any)['_isBuffer'] == true) {
|
|
6
|
+
/**
|
|
7
|
+
* The Buffer polyfill, used in @powersync/common, is a Uint8Array subclass which defines additional fields like
|
|
8
|
+
* `_isBuffer` and `parent` on its `prototype`. The additional fields are serialized when passed through the native bridge.
|
|
9
|
+
* The Capacitor Community SQLite lib expects a dictionarty of indexes to numerical bytes.
|
|
10
|
+
* The additiona fields (which are not an index to numerical byte mapping) cause the parsing logic in the SQLite lib to throw an error:
|
|
11
|
+
* "Error in reading buffer".
|
|
12
|
+
*
|
|
13
|
+
* Re-wrapping the same backing buffer as a plain Uint8Array removes the Buffer subclass wrapper
|
|
14
|
+
* while keeping the same underlying bytes. This creates a new view, not a byte copy, so the
|
|
15
|
+
* overhead should be minimal.
|
|
16
|
+
*/
|
|
17
|
+
payload = new Uint8Array(payload.buffer, payload.byteOffset, payload.byteLength);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return super.control(op, payload);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Capacitor } from '@capacitor/core';
|
|
2
|
+
import { WebRemote } from '@powersync/web';
|
|
3
|
+
|
|
4
|
+
export class CapacitorRemote extends WebRemote {
|
|
5
|
+
protected get supportsStreamingBinaryResponses(): boolean {
|
|
6
|
+
/**
|
|
7
|
+
* We'd like to avoid passing Binary buffers to SQLite when using
|
|
8
|
+
* iOS for now. This is due to inefficient binary processing.
|
|
9
|
+
*/
|
|
10
|
+
return Capacitor.getPlatform() !== 'ios';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { AbstractStreamingSyncImplementation, LockOptions, LockType,
|
|
2
|
-
import { Mutex } from 'async-mutex';
|
|
1
|
+
import { AbstractStreamingSyncImplementation, LockOptions, LockType, Mutex } from '@powersync/web';
|
|
3
2
|
|
|
4
3
|
type MutexMap = {
|
|
5
4
|
/**
|
|
@@ -56,11 +55,8 @@ export class CapacitorStreamingSyncImplementation extends AbstractStreamingSyncI
|
|
|
56
55
|
mutexRecord.tracking.add(this.instanceId);
|
|
57
56
|
const mutex = mutexRecord.locks[lockOptions.type];
|
|
58
57
|
|
|
59
|
-
return
|
|
60
|
-
if (lockOptions.signal?.aborted) {
|
|
61
|
-
throw new Error('Aborted');
|
|
62
|
-
}
|
|
58
|
+
return mutex.runExclusive(async () => {
|
|
63
59
|
return await lockOptions.callback();
|
|
64
|
-
});
|
|
60
|
+
}, lockOptions.signal);
|
|
65
61
|
}
|
|
66
62
|
}
|