Skip to content

Commit 685c99b

Browse files
Merge remote-tracking branch 'origin/main' into capacitor-sdk
2 parents 20b083d + eff8cbf commit 685c99b

File tree

63 files changed

+1779
-248
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+1779
-248
lines changed

.changeset/angry-ducks-sneeze.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
'@powersync/react-native': minor
3+
'@powersync/common': minor
4+
'@powersync/web': minor
5+
---
6+
7+
Add alpha support for sync streams, allowing different sets of data to be synced dynamically.

.github/workflows/test-simulators.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,8 @@ jobs:
141141
- name: Set up XCode
142142
uses: maxim-lobanov/setup-xcode@v1
143143
with:
144-
xcode-version: latest-stable
144+
# TODO: Update to latest-stable once GH installs iOS 26 simulators
145+
xcode-version: '^16.4.0'
145146

146147
- name: CocoaPods Cache
147148
uses: actions/cache@v3

packages/adapter-sql-js/CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,13 @@
11
# @powersync/adapter-sql-js
22

3+
## 0.0.6
4+
5+
### Patch Changes
6+
7+
- Updated dependencies [a0ee132]
8+
- Updated dependencies [ba72a58]
9+
- @powersync/common@1.38.1
10+
311
## 0.0.5
412

513
### Patch Changes

packages/adapter-sql-js/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@powersync/adapter-sql-js",
3-
"version": "0.0.5",
3+
"version": "0.0.6",
44
"publishConfig": {
55
"registry": "https://registry.npmjs.org/",
66
"access": "public"

packages/attachments/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"test:exports": "attw --pack ."
4646
},
4747
"peerDependencies": {
48-
"@powersync/common": "workspace:^1.38.0"
48+
"@powersync/common": "workspace:^1.38.1"
4949
},
5050
"devDependencies": {
5151
"@powersync/common": "workspace:*",

packages/common/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# @powersync/common
22

3+
## 1.38.1
4+
5+
### Patch Changes
6+
7+
- a0ee132: Fixed potential race conditions in WatchedQueries when updateSettings is called frequently.
8+
- ba72a58: Update TriggerManager trackTableDiff API example
9+
310
## 1.38.0
411

512
### Minor Changes

packages/common/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@powersync/common",
3-
"version": "1.38.0",
3+
"version": "1.38.1",
44
"publishConfig": {
55
"registry": "https://registry.npmjs.org/",
66
"access": "public"

packages/common/src/client/AbstractPowerSyncDatabase.ts

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@ import {
99
UpdateNotification,
1010
isBatchedUpdateNotification
1111
} from '../db/DBAdapter.js';
12-
import { FULL_SYNC_PRIORITY } from '../db/crud/SyncProgress.js';
13-
import { SyncPriorityStatus, SyncStatus } from '../db/crud/SyncStatus.js';
12+
import { SyncStatus } from '../db/crud/SyncStatus.js';
1413
import { UploadQueueStats } from '../db/crud/UploadQueueStatus.js';
1514
import { Schema } from '../db/schema/Schema.js';
1615
import { BaseObserver } from '../utils/BaseObserver.js';
1716
import { ControlledExecutor } from '../utils/ControlledExecutor.js';
1817
import { symbolAsyncIterator, throttleTrailing } from '../utils/async.js';
19-
import { ConnectionManager } from './ConnectionManager.js';
18+
import {
19+
ConnectionManager,
20+
CreateSyncImplementationOptions,
21+
InternalSubscriptionAdapter
22+
} from './ConnectionManager.js';
2023
import { CustomQuery } from './CustomQuery.js';
2124
import { ArrayQueryDefinition, Query } from './Query.js';
2225
import { SQLOpenFactory, SQLOpenOptions, isDBAdapter, isSQLOpenFactory, isSQLOpenOptions } from './SQLOpenFactory.js';
@@ -40,6 +43,8 @@ import { TriggerManagerImpl } from './triggers/TriggerManagerImpl.js';
4043
import { DEFAULT_WATCH_THROTTLE_MS, WatchCompatibleQuery } from './watched/WatchedQuery.js';
4144
import { OnChangeQueryProcessor } from './watched/processors/OnChangeQueryProcessor.js';
4245
import { WatchedQueryComparator } from './watched/processors/comparators.js';
46+
import { coreStatusToJs, CoreSyncStatus } from './sync/stream/core-instruction.js';
47+
import { SyncStream } from './sync/sync-streams.js';
4348

4449
export interface DisconnectAndClearOptions {
4550
/** When set to false, data in local-only tables is preserved. */
@@ -182,6 +187,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
182187
protected bucketStorageAdapter: BucketStorageAdapter;
183188
protected _isReadyPromise: Promise<void>;
184189
protected connectionManager: ConnectionManager;
190+
private subscriptions: InternalSubscriptionAdapter;
185191

186192
get syncStreamImplementation() {
187193
return this.connectionManager.syncStreamImplementation;
@@ -236,10 +242,18 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
236242
this.runExclusiveMutex = new Mutex();
237243

238244
// Start async init
245+
this.subscriptions = {
246+
firstStatusMatching: (predicate, abort) => this.waitForStatus(predicate, abort),
247+
resolveOfflineSyncStatus: () => this.resolveOfflineSyncStatus(),
248+
rustSubscriptionsCommand: async (payload) => {
249+
await this.writeTransaction((tx) => {
250+
return tx.execute('select powersync_control(?,?)', ['subscriptions', JSON.stringify(payload)]);
251+
});
252+
}
253+
};
239254
this.connectionManager = new ConnectionManager({
240255
createSyncImplementation: async (connector, options) => {
241256
await this.waitForReady();
242-
243257
return this.runExclusive(async () => {
244258
const sync = this.generateSyncStreamImplementation(connector, this.resolvedConnectionOptions(options));
245259
const onDispose = sync.registerListener({
@@ -304,7 +318,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
304318

305319
protected abstract generateSyncStreamImplementation(
306320
connector: PowerSyncBackendConnector,
307-
options: RequiredAdditionalConnectionOptions
321+
options: CreateSyncImplementationOptions & RequiredAdditionalConnectionOptions
308322
): StreamingSyncImplementation;
309323

310324
protected abstract generateBucketStorageAdapter(): BucketStorageAdapter;
@@ -338,13 +352,18 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
338352
? (status: SyncStatus) => status.hasSynced
339353
: (status: SyncStatus) => status.statusForPriority(priority).hasSynced;
340354

341-
if (statusMatches(this.currentStatus)) {
355+
return this.waitForStatus(statusMatches, signal);
356+
}
357+
358+
private async waitForStatus(predicate: (status: SyncStatus) => any, signal?: AbortSignal): Promise<void> {
359+
if (predicate(this.currentStatus)) {
342360
return;
343361
}
362+
344363
return new Promise((resolve) => {
345364
const dispose = this.registerListener({
346365
statusChanged: (status) => {
347-
if (statusMatches(status)) {
366+
if (predicate(status)) {
348367
dispose();
349368
resolve();
350369
}
@@ -373,7 +392,7 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
373392
await this.bucketStorageAdapter.init();
374393
await this._loadVersion();
375394
await this.updateSchema(this.options.schema);
376-
await this.updateHasSynced();
395+
await this.resolveOfflineSyncStatus();
377396
await this.database.execute('PRAGMA RECURSIVE_TRIGGERS=TRUE');
378397
this.ready = true;
379398
this.iterateListeners((cb) => cb.initialized?.());
@@ -403,30 +422,13 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
403422
}
404423
}
405424

406-
protected async updateHasSynced() {
407-
const result = await this.database.getAll<{ priority: number; last_synced_at: string }>(
408-
'SELECT priority, last_synced_at FROM ps_sync_state ORDER BY priority DESC'
409-
);
410-
let lastCompleteSync: Date | undefined;
411-
const priorityStatusEntries: SyncPriorityStatus[] = [];
412-
413-
for (const { priority, last_synced_at } of result) {
414-
const parsedDate = new Date(last_synced_at + 'Z');
425+
protected async resolveOfflineSyncStatus() {
426+
const result = await this.database.get<{ r: string }>('SELECT powersync_offline_sync_status() as r');
427+
const parsed = JSON.parse(result.r) as CoreSyncStatus;
415428

416-
if (priority == FULL_SYNC_PRIORITY) {
417-
// This lowest-possible priority represents a complete sync.
418-
lastCompleteSync = parsedDate;
419-
} else {
420-
priorityStatusEntries.push({ priority, hasSynced: true, lastSyncedAt: parsedDate });
421-
}
422-
}
423-
424-
const hasSynced = lastCompleteSync != null;
425429
const updatedStatus = new SyncStatus({
426430
...this.currentStatus.toJSON(),
427-
hasSynced,
428-
priorityStatusEntries,
429-
lastSyncedAt: lastCompleteSync
431+
...coreStatusToJs(parsed)
430432
});
431433

432434
if (!updatedStatus.isEqual(this.currentStatus)) {
@@ -471,7 +473,9 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
471473
}
472474

473475
// Use the options passed in during connect, or fallback to the options set during database creation or fallback to the default options
474-
resolvedConnectionOptions(options?: PowerSyncConnectionOptions): RequiredAdditionalConnectionOptions {
476+
protected resolvedConnectionOptions(
477+
options: CreateSyncImplementationOptions
478+
): CreateSyncImplementationOptions & RequiredAdditionalConnectionOptions {
475479
return {
476480
...options,
477481
retryDelayMs:
@@ -540,6 +544,18 @@ export abstract class AbstractPowerSyncDatabase extends BaseObserver<PowerSyncDB
540544
this.iterateListeners((l) => l.statusChanged?.(this.currentStatus));
541545
}
542546

547+
/**
548+
* Create a sync stream to query its status or to subscribe to it.
549+
*
550+
* @param name The name of the stream to subscribe to.
551+
* @param params Optional parameters for the stream subscription.
552+
* @returns A {@link SyncStream} instance that can be subscribed to.
553+
* @experimental Sync streams are currently in alpha.
554+
*/
555+
syncStream(name: string, params?: Record<string, any>): SyncStream {
556+
return this.connectionManager.stream(this.subscriptions, name, params ?? null);
557+
}
558+
543559
/**
544560
* Close the database, releasing resources.
545561
*

0 commit comments

Comments
 (0)