@powersync/capacitor 0.5.2 → 0.5.3

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.
@@ -23,7 +23,7 @@ Pod::Spec.new do |s|
23
23
  s.ios.deployment_target = '14.0'
24
24
  s.dependency 'Capacitor'
25
25
  s.swift_version = '5.1'
26
- s.dependency "powersync-sqlite-core", "~> 0.4.11"
26
+ s.dependency "powersync-sqlite-core", "~> 0.4.12"
27
27
  s.xcconfig = {
28
28
  'OTHER_CFLAGS' => '$(inherited) -DSQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION=1',
29
29
  'HEADER_SEARCH_PATHS' => '$(inherited) "$(PODS_ROOT)/SQLCipher"'
package/README.md CHANGED
@@ -61,6 +61,8 @@ const db = new PowerSyncDatabase({
61
61
  - On Android and iOS, this SDK uses [Capacitor Community SQLite](https://github.com/capacitor-community/sqlite) for native database access.
62
62
  - On web, it falls back to the [PowerSync Web SDK](https://www.npmjs.com/package/@powersync/web).
63
63
 
64
+ When using the native Capacitor Community SQLite driver, `PowerSyncDatabase.connect()` defaults to HTTP with NDJSON streaming. This avoids slow binary payload processing in the native SQLite bridge. Web targets keep the default Web SDK connection behavior.
65
+
64
66
  When using custom database factories, be sure to specify the `CapacitorSQLiteOpenFactory` for Capacitor platforms.
65
67
 
66
68
  ```javascript
@@ -87,7 +89,7 @@ const db = new PowerSyncDatabase({
87
89
 
88
90
  ## Examples
89
91
 
90
- See the [`demos/example-capacitor/`](https://github.com/journeyapps/powersync-react-native-sdk/blob/main/demos/example-capacitor/README.md#L1) directory for a working example.
92
+ See the [`demos/example-capacitor/`](https://github.com/powersync-ja/powersync-js/tree/main/demos/example-capacitor) directory for a working example.
91
93
 
92
94
  ## Found a bug or need help?
93
95
 
@@ -3,7 +3,7 @@ ext {
3
3
  androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0'
4
4
  androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.2.1'
5
5
  androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.6.1'
6
- powerSyncCoreVersion = project.hasProperty('powerSyncCoreVersion') ? rootProject.ext.powerSyncCoreVersion : '0.4.11'
6
+ powerSyncCoreVersion = project.hasProperty('powerSyncCoreVersion') ? rootProject.ext.powerSyncCoreVersion : '0.4.12'
7
7
  }
8
8
 
9
9
  buildscript {
@@ -1,4 +1,4 @@
1
- import { DBAdapter, PowerSyncBackendConnector, RequiredAdditionalConnectionOptions, StreamingSyncImplementation, TriggerManagerConfig, PowerSyncDatabase as WebPowerSyncDatabase, WebPowerSyncDatabaseOptionsWithSettings } from '@powersync/web';
1
+ import { DBAdapter, PowerSyncBackendConnector, PowerSyncConnectionOptions, RequiredAdditionalConnectionOptions, StreamingSyncImplementation, TriggerManagerConfig, PowerSyncDatabase as WebPowerSyncDatabase, WebPowerSyncDatabaseOptionsWithSettings } from '@powersync/web';
2
2
  /**
3
3
  * PowerSyncDatabase class for managing database connections and sync implementations.
4
4
  * This extends the WebPowerSyncDatabase to provide platform-specific implementations
@@ -8,6 +8,13 @@ import { DBAdapter, PowerSyncBackendConnector, RequiredAdditionalConnectionOptio
8
8
  * @alpha
9
9
  */
10
10
  export declare class PowerSyncDatabase extends WebPowerSyncDatabase {
11
+ /**
12
+ * Connects to stream of events from the PowerSync instance.
13
+ * {@link PowerSyncConnectionOptions#connectionMethod} defaults to WebSocket connection on Web platforms
14
+ * or HTTP connections if using {@link CapacitorSQLiteAdapter} - this is due to poor performance with
15
+ * the Capacitor Community SQLite library and binary payloads.
16
+ */
17
+ connect(connector: PowerSyncBackendConnector, options?: PowerSyncConnectionOptions): Promise<void>;
11
18
  protected get isNativeCapacitorPlatform(): boolean;
12
19
  protected openDBAdapter(options: WebPowerSyncDatabaseOptionsWithSettings): DBAdapter;
13
20
  protected generateTriggerManagerConfig(): TriggerManagerConfig;
@@ -1,6 +1,7 @@
1
1
  import { Capacitor } from '@capacitor/core';
2
- import { MEMORY_TRIGGER_CLAIM_MANAGER, PowerSyncDatabase as WebPowerSyncDatabase, WebRemote } from '@powersync/web';
2
+ import { DEFAULT_STREAM_CONNECTION_OPTIONS, MEMORY_TRIGGER_CLAIM_MANAGER, SyncStreamConnectionMethod, PowerSyncDatabase as WebPowerSyncDatabase } from '@powersync/web';
3
3
  import { CapacitorSQLiteAdapter } from './adapter/CapacitorSQLiteAdapter.js';
4
+ import { CapacitorRemote } from './sync/CapacitorRemote.js';
4
5
  import { CapacitorStreamingSyncImplementation } from './sync/CapacitorSyncImplementation.js';
5
6
  /**
6
7
  * PowerSyncDatabase class for managing database connections and sync implementations.
@@ -11,6 +12,23 @@ import { CapacitorStreamingSyncImplementation } from './sync/CapacitorSyncImplem
11
12
  * @alpha
12
13
  */
13
14
  export class PowerSyncDatabase extends WebPowerSyncDatabase {
15
+ /**
16
+ * Connects to stream of events from the PowerSync instance.
17
+ * {@link PowerSyncConnectionOptions#connectionMethod} defaults to WebSocket connection on Web platforms
18
+ * or HTTP connections if using {@link CapacitorSQLiteAdapter} - this is due to poor performance with
19
+ * the Capacitor Community SQLite library and binary payloads.
20
+ */
21
+ connect(connector, options) {
22
+ var _a;
23
+ const isUsingCapacitorDriver = this.database instanceof CapacitorSQLiteAdapter;
24
+ const defaultConnectionMethod = isUsingCapacitorDriver
25
+ ? SyncStreamConnectionMethod.HTTP
26
+ : DEFAULT_STREAM_CONNECTION_OPTIONS.connectionMethod;
27
+ if ((options === null || options === void 0 ? void 0 : options.connectionMethod) == SyncStreamConnectionMethod.WEB_SOCKET && isUsingCapacitorDriver) {
28
+ this.logger.warn(`Connecting via 'SyncStreamConnectionMethod.WEB_SOCKET' when using the 'CapacitorSQLiteAdapter' will result in poor sync performance. Use 'SyncStreamConnectionMethod.HTTP' (the default for native) instead.`);
29
+ }
30
+ return super.connect(connector, Object.assign(Object.assign({}, (options !== null && options !== void 0 ? options : {})), { connectionMethod: (_a = options === null || options === void 0 ? void 0 : options.connectionMethod) !== null && _a !== void 0 ? _a : defaultConnectionMethod }));
31
+ }
14
32
  get isNativeCapacitorPlatform() {
15
33
  const platform = Capacitor.getPlatform();
16
34
  return platform == 'ios' || platform == 'android';
@@ -64,7 +82,7 @@ export class PowerSyncDatabase extends WebPowerSyncDatabase {
64
82
  if ((_a = this.options.flags) === null || _a === void 0 ? void 0 : _a.enableMultiTabs) {
65
83
  this.logger.warn(`enableMultiTabs is not supported on Capacitor mobile platforms. Ignoring the flag.`);
66
84
  }
67
- const remote = new WebRemote(connector, this.logger);
85
+ const remote = new CapacitorRemote(connector, this.logger);
68
86
  return new CapacitorStreamingSyncImplementation(Object.assign(Object.assign({}, this.options), { retryDelayMs: options.retryDelayMs, crudUploadThrottleMs: options.crudUploadThrottleMs, adapter: this.bucketStorageAdapter, remote, uploadCrud: async () => {
69
87
  await this.waitForReady();
70
88
  await connector.uploadData(this);
@@ -1 +1 @@
1
- {"version":3,"file":"PowerSyncDatabase.js","sourceRoot":"","sources":["../../src/PowerSyncDatabase.ts"],"names":[],"mappings":"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","sourcesContent":["import { Capacitor } from '@capacitor/core';\nimport {\n DBAdapter,\n MEMORY_TRIGGER_CLAIM_MANAGER,\n PowerSyncBackendConnector,\n RequiredAdditionalConnectionOptions,\n StreamingSyncImplementation,\n TriggerManagerConfig,\n PowerSyncDatabase as WebPowerSyncDatabase,\n WebPowerSyncDatabaseOptionsWithSettings,\n WebRemote\n} from '@powersync/web';\nimport { CapacitorSQLiteAdapter } from './adapter/CapacitorSQLiteAdapter.js';\nimport { CapacitorStreamingSyncImplementation } from './sync/CapacitorSyncImplementation.js';\n\n/**\n * PowerSyncDatabase class for managing database connections and sync implementations.\n * This extends the WebPowerSyncDatabase to provide platform-specific implementations\n * for Capacitor environments (iOS and Android).\n *\n * @experimental\n * @alpha\n */\nexport class PowerSyncDatabase extends WebPowerSyncDatabase {\n protected get isNativeCapacitorPlatform(): boolean {\n const platform = Capacitor.getPlatform();\n return platform == 'ios' || platform == 'android';\n }\n\n protected openDBAdapter(options: WebPowerSyncDatabaseOptionsWithSettings): DBAdapter {\n const platform = Capacitor.getPlatform();\n if (platform == 'ios' || platform == 'android') {\n if (options.database.dbLocation) {\n options.logger?.warn(`\n dbLocation is ignored on iOS and Android platforms. \n The database directory can be configured in the Capacitor project.\n See https://github.com/capacitor-community/sqlite?tab=readme-ov-file#installation`);\n }\n options.logger?.debug(`Using CapacitorSQLiteAdapter for platform: ${platform}`);\n return new CapacitorSQLiteAdapter({\n ...options.database\n });\n } else {\n options.logger?.debug(`Using default web adapter for web platform`);\n return super.openDBAdapter(options);\n }\n }\n\n protected generateTriggerManagerConfig(): TriggerManagerConfig {\n const config = super.generateTriggerManagerConfig();\n if (this.isNativeCapacitorPlatform) {\n /**\n * We usually only ever have a single tab for capacitor.\n * Avoiding navigator locks allows insecure contexts (during development).\n */\n config.claimManager = MEMORY_TRIGGER_CLAIM_MANAGER;\n }\n return config;\n }\n\n protected runExclusive<T>(cb: () => Promise<T>): Promise<T> {\n if (this.isNativeCapacitorPlatform) {\n // Use mutex for mobile platforms.\n // This is mainly for testing purposes since navigator.locks require secure contexts.\n return this.runExclusiveMutex.runExclusive(cb);\n } else {\n // Use navigator.locks for web platform\n return super.runExclusive(cb);\n }\n }\n\n protected generateSyncStreamImplementation(\n connector: PowerSyncBackendConnector,\n options: RequiredAdditionalConnectionOptions\n ): StreamingSyncImplementation {\n if (this.isNativeCapacitorPlatform) {\n // We don't want to support multi-tab on mobile platforms.\n // We technically can, but it's not a common use case and requires additional work/testing.\n this.logger.debug(`Using Capacitor sync implementation`);\n if (this.options.flags?.enableMultiTabs) {\n this.logger.warn(`enableMultiTabs is not supported on Capacitor mobile platforms. Ignoring the flag.`);\n }\n const remote = new WebRemote(connector, this.logger);\n\n return new CapacitorStreamingSyncImplementation({\n ...(this.options as {}),\n retryDelayMs: options.retryDelayMs,\n crudUploadThrottleMs: options.crudUploadThrottleMs,\n adapter: this.bucketStorageAdapter,\n remote,\n uploadCrud: async () => {\n await this.waitForReady();\n await connector.uploadData(this);\n },\n identifier: this.database.name,\n logger: this.logger,\n subscriptions: options.subscriptions\n });\n } else {\n this.logger.debug(`Using default web sync implementation for web platform`);\n return super.generateSyncStreamImplementation(connector, options);\n }\n }\n}\n"]}
1
+ {"version":3,"file":"PowerSyncDatabase.js","sourceRoot":"","sources":["../../src/PowerSyncDatabase.ts"],"names":[],"mappings":"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","sourcesContent":["import { Capacitor } from '@capacitor/core';\nimport {\n DBAdapter,\n DEFAULT_STREAM_CONNECTION_OPTIONS,\n MEMORY_TRIGGER_CLAIM_MANAGER,\n PowerSyncBackendConnector,\n PowerSyncConnectionOptions,\n RequiredAdditionalConnectionOptions,\n StreamingSyncImplementation,\n SyncStreamConnectionMethod,\n TriggerManagerConfig,\n PowerSyncDatabase as WebPowerSyncDatabase,\n WebPowerSyncDatabaseOptionsWithSettings\n} from '@powersync/web';\nimport { CapacitorSQLiteAdapter } from './adapter/CapacitorSQLiteAdapter.js';\nimport { CapacitorRemote } from './sync/CapacitorRemote.js';\nimport { CapacitorStreamingSyncImplementation } from './sync/CapacitorSyncImplementation.js';\n\n/**\n * PowerSyncDatabase class for managing database connections and sync implementations.\n * This extends the WebPowerSyncDatabase to provide platform-specific implementations\n * for Capacitor environments (iOS and Android).\n *\n * @experimental\n * @alpha\n */\nexport class PowerSyncDatabase extends WebPowerSyncDatabase {\n /**\n * Connects to stream of events from the PowerSync instance.\n * {@link PowerSyncConnectionOptions#connectionMethod} defaults to WebSocket connection on Web platforms\n * or HTTP connections if using {@link CapacitorSQLiteAdapter} - this is due to poor performance with\n * the Capacitor Community SQLite library and binary payloads.\n */\n connect(connector: PowerSyncBackendConnector, options?: PowerSyncConnectionOptions): Promise<void> {\n const isUsingCapacitorDriver = this.database instanceof CapacitorSQLiteAdapter;\n const defaultConnectionMethod = isUsingCapacitorDriver\n ? SyncStreamConnectionMethod.HTTP\n : DEFAULT_STREAM_CONNECTION_OPTIONS.connectionMethod;\n if (options?.connectionMethod == SyncStreamConnectionMethod.WEB_SOCKET && isUsingCapacitorDriver) {\n this.logger.warn(\n `Connecting via 'SyncStreamConnectionMethod.WEB_SOCKET' when using the 'CapacitorSQLiteAdapter' will result in poor sync performance. Use 'SyncStreamConnectionMethod.HTTP' (the default for native) instead.`\n );\n }\n\n return super.connect(connector, {\n ...(options ?? {}),\n connectionMethod: options?.connectionMethod ?? defaultConnectionMethod\n });\n }\n\n protected get isNativeCapacitorPlatform(): boolean {\n const platform = Capacitor.getPlatform();\n return platform == 'ios' || platform == 'android';\n }\n\n protected openDBAdapter(options: WebPowerSyncDatabaseOptionsWithSettings): DBAdapter {\n const platform = Capacitor.getPlatform();\n if (platform == 'ios' || platform == 'android') {\n if (options.database.dbLocation) {\n options.logger?.warn(`\n dbLocation is ignored on iOS and Android platforms. \n The database directory can be configured in the Capacitor project.\n See https://github.com/capacitor-community/sqlite?tab=readme-ov-file#installation`);\n }\n options.logger?.debug(`Using CapacitorSQLiteAdapter for platform: ${platform}`);\n return new CapacitorSQLiteAdapter({\n ...options.database\n });\n } else {\n options.logger?.debug(`Using default web adapter for web platform`);\n return super.openDBAdapter(options);\n }\n }\n\n protected generateTriggerManagerConfig(): TriggerManagerConfig {\n const config = super.generateTriggerManagerConfig();\n if (this.isNativeCapacitorPlatform) {\n /**\n * We usually only ever have a single tab for capacitor.\n * Avoiding navigator locks allows insecure contexts (during development).\n */\n config.claimManager = MEMORY_TRIGGER_CLAIM_MANAGER;\n }\n return config;\n }\n\n protected runExclusive<T>(cb: () => Promise<T>): Promise<T> {\n if (this.isNativeCapacitorPlatform) {\n // Use mutex for mobile platforms.\n // This is mainly for testing purposes since navigator.locks require secure contexts.\n return this.runExclusiveMutex.runExclusive(cb);\n } else {\n // Use navigator.locks for web platform\n return super.runExclusive(cb);\n }\n }\n\n protected generateSyncStreamImplementation(\n connector: PowerSyncBackendConnector,\n options: RequiredAdditionalConnectionOptions\n ): StreamingSyncImplementation {\n if (this.isNativeCapacitorPlatform) {\n // We don't want to support multi-tab on mobile platforms.\n // We technically can, but it's not a common use case and requires additional work/testing.\n this.logger.debug(`Using Capacitor sync implementation`);\n if (this.options.flags?.enableMultiTabs) {\n this.logger.warn(`enableMultiTabs is not supported on Capacitor mobile platforms. Ignoring the flag.`);\n }\n\n const remote = new CapacitorRemote(connector, this.logger);\n\n return new CapacitorStreamingSyncImplementation({\n ...(this.options as {}),\n retryDelayMs: options.retryDelayMs,\n crudUploadThrottleMs: options.crudUploadThrottleMs,\n adapter: this.bucketStorageAdapter,\n remote,\n uploadCrud: async () => {\n await this.waitForReady();\n await connector.uploadData(this);\n },\n identifier: this.database.name,\n logger: this.logger,\n subscriptions: options.subscriptions\n });\n } else {\n this.logger.debug(`Using default web sync implementation for web platform`);\n return super.generateSyncStreamImplementation(connector, options);\n }\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { SQLiteDBConnection } from '@capacitor-community/sqlite';
2
- import { BaseObserver, ConnectionPool, DBAdapterListener, DBLockOptions, LockContext, Mutex, QueryResult, Transaction } from '@powersync/web';
2
+ import { BaseObserver, ConnectionPool, DBAdapterListener, DBLockOptions, LockContext, Mutex, QueryResult } from '@powersync/web';
3
3
  import { CapacitorSQLiteOpenFactoryOptions } from './CapacitorSQLiteOpenFactory.js';
4
4
  declare class CapacitorConnectionPool extends BaseObserver<DBAdapterListener> implements ConnectionPool {
5
5
  protected options: CapacitorSQLiteOpenFactoryOptions;
@@ -20,8 +20,8 @@ declare class CapacitorConnectionPool extends BaseObserver<DBAdapterListener> im
20
20
  refreshSchema(): Promise<void>;
21
21
  }
22
22
  declare const CapacitorSQLiteAdapter_base: (new (...args: any[]) => {
23
- readTransaction<T>(fn: (tx: Transaction) => Promise<T>, options?: DBLockOptions): Promise<T>;
24
- writeTransaction<T>(fn: (tx: Transaction) => Promise<T>, options?: DBLockOptions): Promise<T>;
23
+ readTransaction<T>(fn: (tx: import("@powersync/web").Transaction) => Promise<T>, options?: DBLockOptions): Promise<T>;
24
+ writeTransaction<T>(fn: (tx: import("@powersync/web").Transaction) => Promise<T>, options?: DBLockOptions): Promise<T>;
25
25
  getAll<T>(sql: string, parameters?: any[]): Promise<T[]>;
26
26
  getOptional<T>(sql: string, parameters?: any[]): Promise<T | null>;
27
27
  get<T>(sql: string, parameters?: any[]): Promise<T>;
@@ -19,6 +19,44 @@ async function monitorQuery(sql, executor) {
19
19
  throw e;
20
20
  }
21
21
  }
22
+ /**
23
+ * Maps SQLite query parameter values to Capacitor Community supported formats.
24
+ * This handles binary payloads for both iOS and Android.
25
+ */
26
+ function mapSQLiteParameterValues({ platform, values }) {
27
+ return values.map((value) => {
28
+ if (value instanceof Uint8Array) {
29
+ switch (platform) {
30
+ case 'ios': {
31
+ /**
32
+ * The Buffer polyfill, used in @powersync/common, is a Uint8Array subclass which defines additional fields like
33
+ * `_isBuffer` and `parent` on its `prototype`. The additional fields are serialized when passed through the native bridge.
34
+ * The Capacitor Community SQLite library expects a dictionary of indexes to numerical bytes.
35
+ * The additional fields (which are not an index to numerical byte mapping) cause the parsing logic in the SQLite library to throw an error:
36
+ * "Error in reading buffer".
37
+ *
38
+ * Re-wrapping the same backing buffer as a plain Uint8Array removes the Buffer subclass wrapper
39
+ * while keeping the same underlying bytes. This creates a new view, not a byte copy, so the
40
+ * overhead should be minimal.
41
+ */
42
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
43
+ }
44
+ case 'android': {
45
+ /**
46
+ * Android expects an object of the form:
47
+ * { type: 'Buffer', data: [...]}
48
+ */
49
+ return {
50
+ type: 'Buffer',
51
+ data: Array.from(value)
52
+ };
53
+ }
54
+ }
55
+ }
56
+ // return value as-is
57
+ return value;
58
+ });
59
+ }
22
60
  class CapacitorConnectionPool extends BaseObserver {
23
61
  constructor(options) {
24
62
  super();
@@ -87,7 +125,11 @@ class CapacitorConnectionPool extends BaseObserver {
87
125
  generateLockContext(db) {
88
126
  const _query = async (query, params = []) => {
89
127
  var _a;
90
- const result = await db.query(query, params);
128
+ const mappedParams = mapSQLiteParameterValues({
129
+ platform: Capacitor.getPlatform(),
130
+ values: params
131
+ });
132
+ const result = await db.query(query, mappedParams);
91
133
  const arrayResult = (_a = result.values) !== null && _a !== void 0 ? _a : [];
92
134
  return {
93
135
  rowsAffected: 0,
@@ -101,30 +143,30 @@ class CapacitorConnectionPool extends BaseObserver {
101
143
  const _execute = async (query, params = []) => {
102
144
  var _a, _b, _c, _d, _e, _f, _g, _h;
103
145
  const platform = Capacitor.getPlatform();
104
- if (db.getConnectionReadOnly()) {
146
+ if (db.getConnectionReadOnly() ||
147
+ // Android: use query for SELECT and executeSet for mutations
148
+ // We cannot use `run` here for both cases.
149
+ (platform == 'android' && query.toLowerCase().trim().startsWith('select'))) {
105
150
  return _query(query, params);
106
151
  }
152
+ const mappedParams = mapSQLiteParameterValues({
153
+ platform,
154
+ values: params
155
+ });
107
156
  if (platform == 'android') {
108
- // Android: use query for SELECT and executeSet for mutations
109
- // We cannot use `run` here for both cases.
110
- if (query.toLowerCase().trim().startsWith('select')) {
111
- return _query(query, params);
112
- }
113
- else {
114
- const result = await db.executeSet([{ statement: query, values: params }], false);
115
- return {
116
- insertId: (_a = result.changes) === null || _a === void 0 ? void 0 : _a.lastId,
117
- rowsAffected: (_c = (_b = result.changes) === null || _b === void 0 ? void 0 : _b.changes) !== null && _c !== void 0 ? _c : 0,
118
- rows: {
119
- _array: [],
120
- length: 0,
121
- item: () => null
122
- }
123
- };
124
- }
157
+ const result = await db.executeSet([{ statement: query, values: mappedParams }], false);
158
+ return {
159
+ insertId: (_a = result.changes) === null || _a === void 0 ? void 0 : _a.lastId,
160
+ rowsAffected: (_c = (_b = result.changes) === null || _b === void 0 ? void 0 : _b.changes) !== null && _c !== void 0 ? _c : 0,
161
+ rows: {
162
+ _array: [],
163
+ length: 0,
164
+ item: () => null
165
+ }
166
+ };
125
167
  }
126
168
  // iOS (and other platforms): use run("all")
127
- const result = await db.run(query, params, false, 'all');
169
+ const result = await db.run(query, mappedParams, false, 'all');
128
170
  const resultSet = (_e = (_d = result.changes) === null || _d === void 0 ? void 0 : _d.values) !== null && _e !== void 0 ? _e : [];
129
171
  return {
130
172
  insertId: (_f = result.changes) === null || _f === void 0 ? void 0 : _f.lastId,
@@ -166,9 +208,13 @@ class CapacitorConnectionPool extends BaseObserver {
166
208
  };
167
209
  const executeBatch = async (query, params = []) => {
168
210
  var _a, _b, _c;
211
+ const platform = Capacitor.getPlatform();
169
212
  let result = await db.executeSet(params.map((param) => ({
170
213
  statement: query,
171
- values: param
214
+ values: mapSQLiteParameterValues({
215
+ platform,
216
+ values: param
217
+ })
172
218
  })));
173
219
  return {
174
220
  rowsAffected: (_b = (_a = result.changes) === null || _a === void 0 ? void 0 : _a.changes) !== null && _b !== void 0 ? _b : 0,
@@ -1 +1 @@
1
- {"version":3,"file":"CapacitorSQLiteAdapter.js","sourceRoot":"","sources":["../../../src/adapter/CapacitorSQLiteAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["import { CapacitorSQLite, SQLiteConnection, SQLiteDBConnection } from '@capacitor-community/sqlite';\nimport { Capacitor } from '@capacitor/core';\n\nimport {\n BaseObserver,\n BatchedUpdateNotification,\n ConnectionPool,\n DBAdapter,\n DBAdapterDefaultMixin,\n DBAdapterListener,\n DBLockOptions,\n LockContext,\n Mutex,\n QueryResult,\n timeoutSignal,\n Transaction\n} from '@powersync/web';\nimport { PowerSyncCore } from '../plugin/PowerSyncCore.js';\nimport { messageForErrorCode } from '../plugin/PowerSyncPlugin.js';\nimport { CapacitorSQLiteOpenFactoryOptions, DEFAULT_SQLITE_OPTIONS } from './CapacitorSQLiteOpenFactory.js';\n/**\n * Monitors the execution time of a query and logs it to the performance timeline.\n */\nasync function monitorQuery(sql: string, executor: () => Promise<QueryResult>): Promise<QueryResult> {\n const start = performance.now();\n try {\n const r = await executor();\n performance.measure(`[SQL] ${sql}`, { start });\n return r;\n } catch (e: any) {\n performance.measure(`[SQL] [ERROR: ${e.message}] ${sql}`, { start });\n throw e;\n }\n}\n\nclass CapacitorConnectionPool extends BaseObserver<DBAdapterListener> implements ConnectionPool {\n protected _writeConnection: SQLiteDBConnection | null;\n protected _readConnection: SQLiteDBConnection | null;\n protected initializedPromise: Promise<void>;\n protected writeMutex: Mutex;\n protected readMutex: Mutex;\n\n constructor(protected options: CapacitorSQLiteOpenFactoryOptions) {\n super();\n this._writeConnection = null;\n this._readConnection = null;\n this.writeMutex = new Mutex();\n this.readMutex = new Mutex();\n this.initializedPromise = this.init();\n }\n\n protected get writeConnection(): SQLiteDBConnection {\n if (!this._writeConnection) {\n throw new Error('Init not completed yet');\n }\n return this._writeConnection;\n }\n\n protected get readConnection(): SQLiteDBConnection {\n if (!this._readConnection) {\n throw new Error('Init not completed yet');\n }\n return this._readConnection;\n }\n\n get name() {\n return this.options.dbFilename;\n }\n\n private async init() {\n const { responseCode: registrationResponseCode } = await PowerSyncCore.registerCore();\n if (registrationResponseCode != 0) {\n throw new Error(`Could not register PowerSync core extension: ${messageForErrorCode(registrationResponseCode)}`);\n }\n\n const sqlite = new SQLiteConnection(CapacitorSQLite);\n\n // It seems like the isConnection and retrieveConnection methods\n // only check a JS side map of connections.\n // On hot reload this JS cache can be cleared, while the connection\n // still exists natively. and `createConnection` will fail if it already exists.\n await sqlite.closeConnection(this.options.dbFilename, false).catch(() => {});\n await sqlite.closeConnection(this.options.dbFilename, true).catch(() => {});\n\n // TODO support encryption eventually\n this._writeConnection = await sqlite.createConnection(this.options.dbFilename, false, 'no-encryption', 1, false);\n this._readConnection = await sqlite.createConnection(this.options.dbFilename, false, 'no-encryption', 1, true);\n\n await this._writeConnection.open();\n\n const { cacheSizeKb, journalSizeLimit, synchronous } = { ...DEFAULT_SQLITE_OPTIONS, ...this.options.sqliteOptions };\n\n await this.writeConnection.query('PRAGMA journal_mode = WAL');\n await this.writeConnection.query(`PRAGMA journal_size_limit = ${journalSizeLimit}`);\n await this.writeConnection.query(`PRAGMA temp_store = memory`);\n await this.writeConnection.query(`PRAGMA synchronous = ${synchronous}`);\n await this.writeConnection.query(`PRAGMA cache_size = -${cacheSizeKb}`);\n\n await this._readConnection.open();\n\n const platform = Capacitor.getPlatform();\n if (platform == 'android') {\n /**\n * SQLCipher for Android enables dynamic loading of extensions.\n * On iOS we use a static auto extension registration.\n */\n const extensionQuery = \"SELECT load_extension('libpowersync.so', 'sqlite3_powersync_init')\";\n await this.writeConnection.query(extensionQuery);\n await this.readConnection.query(extensionQuery);\n }\n await this.writeConnection.query(\"SELECT powersync_update_hooks('install')\");\n }\n\n async close(): Promise<void> {\n await this.initializedPromise;\n await this.writeConnection.close();\n await this.readConnection.close();\n }\n\n protected generateLockContext(db: SQLiteDBConnection): LockContext {\n const _query = async (query: string, params: any[] = []) => {\n const result = await db.query(query, params);\n const arrayResult = result.values ?? [];\n return {\n rowsAffected: 0,\n rows: {\n _array: arrayResult,\n length: arrayResult.length,\n item: (idx: number) => arrayResult[idx]\n }\n };\n };\n\n const _execute = async (query: string, params: any[] = []): Promise<QueryResult> => {\n const platform = Capacitor.getPlatform();\n\n if (db.getConnectionReadOnly()) {\n return _query(query, params);\n }\n\n if (platform == 'android') {\n // Android: use query for SELECT and executeSet for mutations\n // We cannot use `run` here for both cases.\n if (query.toLowerCase().trim().startsWith('select')) {\n return _query(query, params);\n } else {\n const result = await db.executeSet([{ statement: query, values: params }], false);\n return {\n insertId: result.changes?.lastId,\n rowsAffected: result.changes?.changes ?? 0,\n rows: {\n _array: [],\n length: 0,\n item: () => null\n }\n };\n }\n }\n\n // iOS (and other platforms): use run(\"all\")\n const result = await db.run(query, params, false, 'all');\n const resultSet = result.changes?.values ?? [];\n return {\n insertId: result.changes?.lastId,\n rowsAffected: result.changes?.changes ?? 0,\n rows: {\n _array: resultSet,\n length: resultSet.length,\n item: (idx) => resultSet[idx]\n }\n };\n };\n\n const execute = this.options.debugMode\n ? (sql: string, params?: any[]) => monitorQuery(sql, () => _execute(sql, params))\n : _execute;\n\n const executeQuery = this.options.debugMode\n ? (sql: string, params?: any[]) => monitorQuery(sql, () => _query(sql, params))\n : _query;\n\n const getAll = async <T>(query: string, params?: any[]): Promise<T[]> => {\n const result = await executeQuery(query, params);\n return result.rows?._array ?? ([] as T[]);\n };\n\n const getOptional = async <T>(query: string, params?: any[]): Promise<T | null> => {\n const results = await getAll<T>(query, params);\n return results.length > 0 ? results[0] : null;\n };\n\n const get = async <T>(query: string, params?: any[]): Promise<T> => {\n const result = await getOptional<T>(query, params);\n if (!result) {\n throw new Error(`No results for query: ${query}`);\n }\n return result;\n };\n\n const executeRaw = async (query: string, params?: any[]): Promise<any[][]> => {\n // This is a workaround, we don't support multiple columns of the same name\n const results = await execute(query, params);\n return results.rows?._array.map((row) => Object.values(row)) ?? [];\n };\n\n const executeBatch = async (query: string, params: any[][] = []): Promise<QueryResult> => {\n let result = await db.executeSet(\n params.map((param) => ({\n statement: query,\n values: param\n }))\n );\n\n return {\n rowsAffected: result.changes?.changes ?? 0,\n insertId: result.changes?.lastId\n };\n };\n\n return {\n getAll,\n getOptional,\n get,\n executeRaw,\n execute,\n executeBatch\n };\n }\n\n readLock<T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions): Promise<T> {\n return this.readMutex.runExclusive(async () => {\n await this.initializedPromise;\n return fn(this.generateLockContext(this.readConnection));\n }, timeoutSignal(options?.timeoutMs));\n }\n\n writeLock<T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions): Promise<T> {\n return this.writeMutex.runExclusive(async () => {\n await this.initializedPromise;\n const result = await fn(this.generateLockContext(this.writeConnection));\n\n // Fetch table updates\n const updates = await this.writeConnection.query(\"SELECT powersync_update_hooks('get') AS table_name\");\n const jsonUpdates = updates.values?.[0];\n if (!jsonUpdates || !jsonUpdates.table_name) {\n throw new Error('Could not fetch table updates');\n }\n const notification: BatchedUpdateNotification = {\n rawUpdates: [],\n tables: JSON.parse(jsonUpdates.table_name),\n groupedUpdates: {}\n };\n this.iterateListeners((l) => l.tablesUpdated?.(notification));\n return result;\n }, timeoutSignal(options?.timeoutMs));\n }\n\n refreshSchema(): Promise<void> {\n return this.writeLock(async (writeTx) => {\n return this.readLock(async (readTx) => {\n const updateQuery = `PRAGMA table_info('sqlite_master')`;\n await writeTx.get(updateQuery);\n await readTx.get(updateQuery);\n });\n });\n }\n}\n\n/**\n * An implementation of {@link DBAdapter} using the Capacitor Community SQLite [plugin](https://github.com/capacitor-community/sqlite).\n *\n * @experimental\n * @alpha This is currently experimental and may change without a major version bump.\n */\nexport class CapacitorSQLiteAdapter extends DBAdapterDefaultMixin(CapacitorConnectionPool) {}\n"]}
1
+ {"version":3,"file":"CapacitorSQLiteAdapter.js","sourceRoot":"","sources":["../../../src/adapter/CapacitorSQLiteAdapter.ts"],"names":[],"mappings":"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","sourcesContent":["import { CapacitorSQLite, SQLiteConnection, SQLiteDBConnection } from '@capacitor-community/sqlite';\nimport { Capacitor } from '@capacitor/core';\n\nimport {\n BaseObserver,\n BatchedUpdateNotification,\n ConnectionPool,\n DBAdapter,\n DBAdapterDefaultMixin,\n DBAdapterListener,\n DBLockOptions,\n LockContext,\n Mutex,\n QueryResult,\n timeoutSignal\n} from '@powersync/web';\nimport { PowerSyncCore } from '../plugin/PowerSyncCore.js';\nimport { messageForErrorCode } from '../plugin/PowerSyncPlugin.js';\nimport { CapacitorSQLiteOpenFactoryOptions, DEFAULT_SQLITE_OPTIONS } from './CapacitorSQLiteOpenFactory.js';\n/**\n * Monitors the execution time of a query and logs it to the performance timeline.\n */\nasync function monitorQuery(sql: string, executor: () => Promise<QueryResult>): Promise<QueryResult> {\n const start = performance.now();\n try {\n const r = await executor();\n performance.measure(`[SQL] ${sql}`, { start });\n return r;\n } catch (e: any) {\n performance.measure(`[SQL] [ERROR: ${e.message}] ${sql}`, { start });\n throw e;\n }\n}\n\n/**\n * Maps SQLite query parameter values to Capacitor Community supported formats.\n * This handles binary payloads for both iOS and Android.\n */\nfunction mapSQLiteParameterValues({ platform, values }: { platform: string; values: any[] }) {\n return values.map((value) => {\n if (value instanceof Uint8Array) {\n switch (platform) {\n case 'ios': {\n /**\n * The Buffer polyfill, used in @powersync/common, is a Uint8Array subclass which defines additional fields like\n * `_isBuffer` and `parent` on its `prototype`. The additional fields are serialized when passed through the native bridge.\n * The Capacitor Community SQLite library expects a dictionary of indexes to numerical bytes.\n * The additional fields (which are not an index to numerical byte mapping) cause the parsing logic in the SQLite library to throw an error:\n * \"Error in reading buffer\".\n *\n * Re-wrapping the same backing buffer as a plain Uint8Array removes the Buffer subclass wrapper\n * while keeping the same underlying bytes. This creates a new view, not a byte copy, so the\n * overhead should be minimal.\n */\n return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);\n }\n case 'android': {\n /**\n * Android expects an object of the form:\n * { type: 'Buffer', data: [...]}\n */\n return {\n type: 'Buffer',\n data: Array.from(value)\n };\n }\n }\n }\n\n // return value as-is\n return value;\n });\n}\n\nclass CapacitorConnectionPool extends BaseObserver<DBAdapterListener> implements ConnectionPool {\n protected _writeConnection: SQLiteDBConnection | null;\n protected _readConnection: SQLiteDBConnection | null;\n protected initializedPromise: Promise<void>;\n protected writeMutex: Mutex;\n protected readMutex: Mutex;\n\n constructor(protected options: CapacitorSQLiteOpenFactoryOptions) {\n super();\n this._writeConnection = null;\n this._readConnection = null;\n this.writeMutex = new Mutex();\n this.readMutex = new Mutex();\n this.initializedPromise = this.init();\n }\n\n protected get writeConnection(): SQLiteDBConnection {\n if (!this._writeConnection) {\n throw new Error('Init not completed yet');\n }\n return this._writeConnection;\n }\n\n protected get readConnection(): SQLiteDBConnection {\n if (!this._readConnection) {\n throw new Error('Init not completed yet');\n }\n return this._readConnection;\n }\n\n get name() {\n return this.options.dbFilename;\n }\n\n private async init() {\n const { responseCode: registrationResponseCode } = await PowerSyncCore.registerCore();\n if (registrationResponseCode != 0) {\n throw new Error(`Could not register PowerSync core extension: ${messageForErrorCode(registrationResponseCode)}`);\n }\n\n const sqlite = new SQLiteConnection(CapacitorSQLite);\n\n // It seems like the isConnection and retrieveConnection methods\n // only check a JS side map of connections.\n // On hot reload this JS cache can be cleared, while the connection\n // still exists natively. and `createConnection` will fail if it already exists.\n await sqlite.closeConnection(this.options.dbFilename, false).catch(() => {});\n await sqlite.closeConnection(this.options.dbFilename, true).catch(() => {});\n\n // TODO support encryption eventually\n this._writeConnection = await sqlite.createConnection(this.options.dbFilename, false, 'no-encryption', 1, false);\n this._readConnection = await sqlite.createConnection(this.options.dbFilename, false, 'no-encryption', 1, true);\n\n await this._writeConnection.open();\n\n const { cacheSizeKb, journalSizeLimit, synchronous } = { ...DEFAULT_SQLITE_OPTIONS, ...this.options.sqliteOptions };\n\n await this.writeConnection.query('PRAGMA journal_mode = WAL');\n await this.writeConnection.query(`PRAGMA journal_size_limit = ${journalSizeLimit}`);\n await this.writeConnection.query(`PRAGMA temp_store = memory`);\n await this.writeConnection.query(`PRAGMA synchronous = ${synchronous}`);\n await this.writeConnection.query(`PRAGMA cache_size = -${cacheSizeKb}`);\n\n await this._readConnection.open();\n\n const platform = Capacitor.getPlatform();\n if (platform == 'android') {\n /**\n * SQLCipher for Android enables dynamic loading of extensions.\n * On iOS we use a static auto extension registration.\n */\n const extensionQuery = \"SELECT load_extension('libpowersync.so', 'sqlite3_powersync_init')\";\n await this.writeConnection.query(extensionQuery);\n await this.readConnection.query(extensionQuery);\n }\n await this.writeConnection.query(\"SELECT powersync_update_hooks('install')\");\n }\n\n async close(): Promise<void> {\n await this.initializedPromise;\n await this.writeConnection.close();\n await this.readConnection.close();\n }\n\n protected generateLockContext(db: SQLiteDBConnection): LockContext {\n const _query = async (query: string, params: any[] = []) => {\n const mappedParams = mapSQLiteParameterValues({\n platform: Capacitor.getPlatform(),\n values: params\n });\n const result = await db.query(query, mappedParams);\n const arrayResult = result.values ?? [];\n return {\n rowsAffected: 0,\n rows: {\n _array: arrayResult,\n length: arrayResult.length,\n item: (idx: number) => arrayResult[idx]\n }\n };\n };\n\n const _execute = async (query: string, params: any[] = []): Promise<QueryResult> => {\n const platform = Capacitor.getPlatform();\n\n if (\n db.getConnectionReadOnly() ||\n // Android: use query for SELECT and executeSet for mutations\n // We cannot use `run` here for both cases.\n (platform == 'android' && query.toLowerCase().trim().startsWith('select'))\n ) {\n return _query(query, params);\n }\n\n const mappedParams = mapSQLiteParameterValues({\n platform,\n values: params\n });\n\n if (platform == 'android') {\n const result = await db.executeSet([{ statement: query, values: mappedParams }], false);\n return {\n insertId: result.changes?.lastId,\n rowsAffected: result.changes?.changes ?? 0,\n rows: {\n _array: [],\n length: 0,\n item: () => null\n }\n };\n }\n\n // iOS (and other platforms): use run(\"all\")\n const result = await db.run(query, mappedParams, false, 'all');\n const resultSet = result.changes?.values ?? [];\n return {\n insertId: result.changes?.lastId,\n rowsAffected: result.changes?.changes ?? 0,\n rows: {\n _array: resultSet,\n length: resultSet.length,\n item: (idx) => resultSet[idx]\n }\n };\n };\n\n const execute = this.options.debugMode\n ? (sql: string, params?: any[]) => monitorQuery(sql, () => _execute(sql, params))\n : _execute;\n\n const executeQuery = this.options.debugMode\n ? (sql: string, params?: any[]) => monitorQuery(sql, () => _query(sql, params))\n : _query;\n\n const getAll = async <T>(query: string, params?: any[]): Promise<T[]> => {\n const result = await executeQuery(query, params);\n return result.rows?._array ?? ([] as T[]);\n };\n\n const getOptional = async <T>(query: string, params?: any[]): Promise<T | null> => {\n const results = await getAll<T>(query, params);\n return results.length > 0 ? results[0] : null;\n };\n\n const get = async <T>(query: string, params?: any[]): Promise<T> => {\n const result = await getOptional<T>(query, params);\n if (!result) {\n throw new Error(`No results for query: ${query}`);\n }\n return result;\n };\n\n const executeRaw = async (query: string, params?: any[]): Promise<any[][]> => {\n // This is a workaround, we don't support multiple columns of the same name\n const results = await execute(query, params);\n return results.rows?._array.map((row) => Object.values(row)) ?? [];\n };\n\n const executeBatch = async (query: string, params: any[][] = []): Promise<QueryResult> => {\n const platform = Capacitor.getPlatform();\n let result = await db.executeSet(\n params.map((param) => ({\n statement: query,\n values: mapSQLiteParameterValues({\n platform,\n values: param\n })\n }))\n );\n\n return {\n rowsAffected: result.changes?.changes ?? 0,\n insertId: result.changes?.lastId\n };\n };\n\n return {\n getAll,\n getOptional,\n get,\n executeRaw,\n execute,\n executeBatch\n };\n }\n\n readLock<T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions): Promise<T> {\n return this.readMutex.runExclusive(async () => {\n await this.initializedPromise;\n return fn(this.generateLockContext(this.readConnection));\n }, timeoutSignal(options?.timeoutMs));\n }\n\n writeLock<T>(fn: (tx: LockContext) => Promise<T>, options?: DBLockOptions): Promise<T> {\n return this.writeMutex.runExclusive(async () => {\n await this.initializedPromise;\n const result = await fn(this.generateLockContext(this.writeConnection));\n\n // Fetch table updates\n const updates = await this.writeConnection.query(\"SELECT powersync_update_hooks('get') AS table_name\");\n const jsonUpdates = updates.values?.[0];\n if (!jsonUpdates || !jsonUpdates.table_name) {\n throw new Error('Could not fetch table updates');\n }\n const notification: BatchedUpdateNotification = {\n rawUpdates: [],\n tables: JSON.parse(jsonUpdates.table_name),\n groupedUpdates: {}\n };\n this.iterateListeners((l) => l.tablesUpdated?.(notification));\n return result;\n }, timeoutSignal(options?.timeoutMs));\n }\n\n refreshSchema(): Promise<void> {\n return this.writeLock(async (writeTx) => {\n return this.readLock(async (readTx) => {\n const updateQuery = `PRAGMA table_info('sqlite_master')`;\n await writeTx.get(updateQuery);\n await readTx.get(updateQuery);\n });\n });\n }\n}\n\n/**\n * An implementation of {@link DBAdapter} using the Capacitor Community SQLite [plugin](https://github.com/capacitor-community/sqlite).\n *\n * @experimental\n * @alpha This is currently experimental and may change without a major version bump.\n */\nexport class CapacitorSQLiteAdapter extends DBAdapterDefaultMixin(CapacitorConnectionPool) {}\n"]}
@@ -0,0 +1,4 @@
1
+ import { WebRemote } from '@powersync/web';
2
+ export declare class CapacitorRemote extends WebRemote {
3
+ protected get supportsStreamingBinaryResponses(): boolean;
4
+ }
@@ -0,0 +1,23 @@
1
+ import { WebRemote } from '@powersync/web';
2
+ export class CapacitorRemote extends WebRemote {
3
+ get supportsStreamingBinaryResponses() {
4
+ /**
5
+ * We'd like to avoid passing Binary buffers to SQLite when using
6
+ * iOS and Android for now. This is due to inefficient binary processing.
7
+ * Syncing using Buffers and Capacitor Community SQLite has been observed to be notably
8
+ * slower than the NDJSON option.
9
+ * Capacitor Community SQLite serializes Buffer objects, which causes slowdown
10
+ * ios: https://github.com/capacitor-community/sqlite/blob/f507a1e779688ea72b9d7e8744c647f7b688c568/ios/Plugin/CapacitorSQLite.swift#L888-L912
11
+ * android: https://github.com/capacitor-community/sqlite/blob/master/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLite.java#L141-L147
12
+ * As a rough guideline, the time to locally sync 10_000 small records was observed as:
13
+ * iOS:
14
+ * - NDJSON: 449ms
15
+ * - Binary: 68_982ms
16
+ * Android:
17
+ * - NDJSON: 452ms
18
+ * - Binary: 1_847ms
19
+ */
20
+ return false;
21
+ }
22
+ }
23
+ //# sourceMappingURL=CapacitorRemote.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CapacitorRemote.js","sourceRoot":"","sources":["../../../src/sync/CapacitorRemote.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,MAAM,OAAO,eAAgB,SAAQ,SAAS;IAC5C,IAAc,gCAAgC;QAC5C;;;;;;;;;;;;;;;WAeG;QACH,OAAO,KAAK,CAAC;IACf,CAAC;CACF","sourcesContent":["import { WebRemote } from '@powersync/web';\n\nexport class CapacitorRemote extends WebRemote {\n protected get supportsStreamingBinaryResponses(): boolean {\n /**\n * We'd like to avoid passing Binary buffers to SQLite when using\n * iOS and Android for now. This is due to inefficient binary processing.\n * Syncing using Buffers and Capacitor Community SQLite has been observed to be notably\n * slower than the NDJSON option.\n * Capacitor Community SQLite serializes Buffer objects, which causes slowdown\n * ios: https://github.com/capacitor-community/sqlite/blob/f507a1e779688ea72b9d7e8744c647f7b688c568/ios/Plugin/CapacitorSQLite.swift#L888-L912\n * android: https://github.com/capacitor-community/sqlite/blob/master/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLite.java#L141-L147\n * As a rough guideline, the time to locally sync 10_000 small records was observed as:\n * iOS:\n * - NDJSON: 449ms\n * - Binary: 68_982ms\n * Android:\n * - NDJSON: 452ms\n * - Binary: 1_847ms\n */\n return false;\n }\n}\n"]}
package/dist/plugin.cjs CHANGED
@@ -52,6 +52,44 @@ async function monitorQuery(sql, executor) {
52
52
  throw e;
53
53
  }
54
54
  }
55
+ /**
56
+ * Maps SQLite query parameter values to Capacitor Community supported formats.
57
+ * This handles binary payloads for both iOS and Android.
58
+ */
59
+ function mapSQLiteParameterValues({ platform, values }) {
60
+ return values.map((value) => {
61
+ if (value instanceof Uint8Array) {
62
+ switch (platform) {
63
+ case 'ios': {
64
+ /**
65
+ * The Buffer polyfill, used in @powersync/common, is a Uint8Array subclass which defines additional fields like
66
+ * `_isBuffer` and `parent` on its `prototype`. The additional fields are serialized when passed through the native bridge.
67
+ * The Capacitor Community SQLite library expects a dictionary of indexes to numerical bytes.
68
+ * The additional fields (which are not an index to numerical byte mapping) cause the parsing logic in the SQLite library to throw an error:
69
+ * "Error in reading buffer".
70
+ *
71
+ * Re-wrapping the same backing buffer as a plain Uint8Array removes the Buffer subclass wrapper
72
+ * while keeping the same underlying bytes. This creates a new view, not a byte copy, so the
73
+ * overhead should be minimal.
74
+ */
75
+ return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
76
+ }
77
+ case 'android': {
78
+ /**
79
+ * Android expects an object of the form:
80
+ * { type: 'Buffer', data: [...]}
81
+ */
82
+ return {
83
+ type: 'Buffer',
84
+ data: Array.from(value)
85
+ };
86
+ }
87
+ }
88
+ }
89
+ // return value as-is
90
+ return value;
91
+ });
92
+ }
55
93
  class CapacitorConnectionPool extends web$1.BaseObserver {
56
94
  constructor(options) {
57
95
  super();
@@ -120,7 +158,11 @@ class CapacitorConnectionPool extends web$1.BaseObserver {
120
158
  generateLockContext(db) {
121
159
  const _query = async (query, params = []) => {
122
160
  var _a;
123
- const result = await db.query(query, params);
161
+ const mappedParams = mapSQLiteParameterValues({
162
+ platform: core.Capacitor.getPlatform(),
163
+ values: params
164
+ });
165
+ const result = await db.query(query, mappedParams);
124
166
  const arrayResult = (_a = result.values) !== null && _a !== void 0 ? _a : [];
125
167
  return {
126
168
  rowsAffected: 0,
@@ -134,30 +176,30 @@ class CapacitorConnectionPool extends web$1.BaseObserver {
134
176
  const _execute = async (query, params = []) => {
135
177
  var _a, _b, _c, _d, _e, _f, _g, _h;
136
178
  const platform = core.Capacitor.getPlatform();
137
- if (db.getConnectionReadOnly()) {
179
+ if (db.getConnectionReadOnly() ||
180
+ // Android: use query for SELECT and executeSet for mutations
181
+ // We cannot use `run` here for both cases.
182
+ (platform == 'android' && query.toLowerCase().trim().startsWith('select'))) {
138
183
  return _query(query, params);
139
184
  }
185
+ const mappedParams = mapSQLiteParameterValues({
186
+ platform,
187
+ values: params
188
+ });
140
189
  if (platform == 'android') {
141
- // Android: use query for SELECT and executeSet for mutations
142
- // We cannot use `run` here for both cases.
143
- if (query.toLowerCase().trim().startsWith('select')) {
144
- return _query(query, params);
145
- }
146
- else {
147
- const result = await db.executeSet([{ statement: query, values: params }], false);
148
- return {
149
- insertId: (_a = result.changes) === null || _a === void 0 ? void 0 : _a.lastId,
150
- rowsAffected: (_c = (_b = result.changes) === null || _b === void 0 ? void 0 : _b.changes) !== null && _c !== void 0 ? _c : 0,
151
- rows: {
152
- _array: [],
153
- length: 0,
154
- item: () => null
155
- }
156
- };
157
- }
190
+ const result = await db.executeSet([{ statement: query, values: mappedParams }], false);
191
+ return {
192
+ insertId: (_a = result.changes) === null || _a === void 0 ? void 0 : _a.lastId,
193
+ rowsAffected: (_c = (_b = result.changes) === null || _b === void 0 ? void 0 : _b.changes) !== null && _c !== void 0 ? _c : 0,
194
+ rows: {
195
+ _array: [],
196
+ length: 0,
197
+ item: () => null
198
+ }
199
+ };
158
200
  }
159
201
  // iOS (and other platforms): use run("all")
160
- const result = await db.run(query, params, false, 'all');
202
+ const result = await db.run(query, mappedParams, false, 'all');
161
203
  const resultSet = (_e = (_d = result.changes) === null || _d === void 0 ? void 0 : _d.values) !== null && _e !== void 0 ? _e : [];
162
204
  return {
163
205
  insertId: (_f = result.changes) === null || _f === void 0 ? void 0 : _f.lastId,
@@ -199,9 +241,13 @@ class CapacitorConnectionPool extends web$1.BaseObserver {
199
241
  };
200
242
  const executeBatch = async (query, params = []) => {
201
243
  var _a, _b, _c;
244
+ const platform = core.Capacitor.getPlatform();
202
245
  let result = await db.executeSet(params.map((param) => ({
203
246
  statement: query,
204
- values: param
247
+ values: mapSQLiteParameterValues({
248
+ platform,
249
+ values: param
250
+ })
205
251
  })));
206
252
  return {
207
253
  rowsAffected: (_b = (_a = result.changes) === null || _a === void 0 ? void 0 : _a.changes) !== null && _b !== void 0 ? _b : 0,
@@ -262,6 +308,28 @@ class CapacitorConnectionPool extends web$1.BaseObserver {
262
308
  class CapacitorSQLiteAdapter extends web$1.DBAdapterDefaultMixin(CapacitorConnectionPool) {
263
309
  }
264
310
 
311
+ class CapacitorRemote extends web$1.WebRemote {
312
+ get supportsStreamingBinaryResponses() {
313
+ /**
314
+ * We'd like to avoid passing Binary buffers to SQLite when using
315
+ * iOS and Android for now. This is due to inefficient binary processing.
316
+ * Syncing using Buffers and Capacitor Community SQLite has been observed to be notably
317
+ * slower than the NDJSON option.
318
+ * Capacitor Community SQLite serializes Buffer objects, which causes slowdown
319
+ * ios: https://github.com/capacitor-community/sqlite/blob/f507a1e779688ea72b9d7e8744c647f7b688c568/ios/Plugin/CapacitorSQLite.swift#L888-L912
320
+ * android: https://github.com/capacitor-community/sqlite/blob/master/android/src/main/java/com/getcapacitor/community/database/sqlite/SQLite/UtilsSQLite.java#L141-L147
321
+ * As a rough guideline, the time to locally sync 10_000 small records was observed as:
322
+ * iOS:
323
+ * - NDJSON: 449ms
324
+ * - Binary: 68_982ms
325
+ * Android:
326
+ * - NDJSON: 452ms
327
+ * - Binary: 1_847ms
328
+ */
329
+ return false;
330
+ }
331
+ }
332
+
265
333
  const GLOBAL_MUTEX_STORE = new Map();
266
334
  /**
267
335
  * Used to identify multiple instances of CapacitorStreamingSyncImplementation
@@ -317,6 +385,23 @@ class CapacitorStreamingSyncImplementation extends web$1.AbstractStreamingSyncIm
317
385
  * @alpha
318
386
  */
319
387
  class PowerSyncDatabase extends web$1.PowerSyncDatabase {
388
+ /**
389
+ * Connects to stream of events from the PowerSync instance.
390
+ * {@link PowerSyncConnectionOptions#connectionMethod} defaults to WebSocket connection on Web platforms
391
+ * or HTTP connections if using {@link CapacitorSQLiteAdapter} - this is due to poor performance with
392
+ * the Capacitor Community SQLite library and binary payloads.
393
+ */
394
+ connect(connector, options) {
395
+ var _a;
396
+ const isUsingCapacitorDriver = this.database instanceof CapacitorSQLiteAdapter;
397
+ const defaultConnectionMethod = isUsingCapacitorDriver
398
+ ? web$1.SyncStreamConnectionMethod.HTTP
399
+ : web$1.DEFAULT_STREAM_CONNECTION_OPTIONS.connectionMethod;
400
+ if ((options === null || options === void 0 ? void 0 : options.connectionMethod) == web$1.SyncStreamConnectionMethod.WEB_SOCKET && isUsingCapacitorDriver) {
401
+ this.logger.warn(`Connecting via 'SyncStreamConnectionMethod.WEB_SOCKET' when using the 'CapacitorSQLiteAdapter' will result in poor sync performance. Use 'SyncStreamConnectionMethod.HTTP' (the default for native) instead.`);
402
+ }
403
+ return super.connect(connector, Object.assign(Object.assign({}, (options !== null && options !== void 0 ? options : {})), { connectionMethod: (_a = options === null || options === void 0 ? void 0 : options.connectionMethod) !== null && _a !== void 0 ? _a : defaultConnectionMethod }));
404
+ }
320
405
  get isNativeCapacitorPlatform() {
321
406
  const platform = core.Capacitor.getPlatform();
322
407
  return platform == 'ios' || platform == 'android';
@@ -370,7 +455,7 @@ class PowerSyncDatabase extends web$1.PowerSyncDatabase {
370
455
  if ((_a = this.options.flags) === null || _a === void 0 ? void 0 : _a.enableMultiTabs) {
371
456
  this.logger.warn(`enableMultiTabs is not supported on Capacitor mobile platforms. Ignoring the flag.`);
372
457
  }
373
- const remote = new web$1.WebRemote(connector, this.logger);
458
+ const remote = new CapacitorRemote(connector, this.logger);
374
459
  return new CapacitorStreamingSyncImplementation(Object.assign(Object.assign({}, this.options), { retryDelayMs: options.retryDelayMs, crudUploadThrottleMs: options.crudUploadThrottleMs, adapter: this.bucketStorageAdapter, remote, uploadCrud: async () => {
375
460
  await this.waitForReady();
376
461
  await connector.uploadData(this);