@powersync/common 0.0.0-dev-20250915110424 → 0.0.0-dev-20250922104723
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/dist/bundle.cjs +4 -4
- package/dist/bundle.mjs +5 -5
- package/dist/index.d.cts +284 -83
- package/lib/client/AbstractPowerSyncDatabase.d.ts +16 -4
- package/lib/client/AbstractPowerSyncDatabase.js +32 -22
- package/lib/client/ConnectionManager.d.ts +26 -2
- package/lib/client/ConnectionManager.js +114 -2
- package/lib/client/sync/bucket/BucketStorageAdapter.d.ts +9 -1
- package/lib/client/sync/bucket/BucketStorageAdapter.js +1 -0
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +24 -3
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +46 -37
- package/lib/client/sync/stream/core-instruction.d.ts +20 -1
- package/lib/client/sync/stream/core-instruction.js +26 -1
- package/lib/client/sync/sync-streams.d.ts +98 -0
- package/lib/client/sync/sync-streams.js +1 -0
- package/lib/client/watched/WatchedQuery.d.ts +2 -0
- package/lib/client/watched/WatchedQuery.js +1 -0
- package/lib/client/watched/processors/AbstractQueryProcessor.d.ts +1 -1
- package/lib/client/watched/processors/AbstractQueryProcessor.js +11 -7
- package/lib/db/crud/SyncStatus.d.ts +28 -1
- package/lib/db/crud/SyncStatus.js +52 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/package.json +1 -1
|
@@ -1 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
import { FULL_SYNC_PRIORITY } from '../../../db/crud/SyncProgress.js';
|
|
2
|
+
function priorityToJs(status) {
|
|
3
|
+
return {
|
|
4
|
+
priority: status.priority,
|
|
5
|
+
hasSynced: status.has_synced ?? undefined,
|
|
6
|
+
lastSyncedAt: status.last_synced_at != null ? new Date(status.last_synced_at * 1000) : undefined
|
|
7
|
+
};
|
|
8
|
+
}
|
|
9
|
+
export function coreStatusToJs(status) {
|
|
10
|
+
const coreCompleteSync = status.priority_status.find((s) => s.priority == FULL_SYNC_PRIORITY);
|
|
11
|
+
const completeSync = coreCompleteSync != null ? priorityToJs(coreCompleteSync) : null;
|
|
12
|
+
return {
|
|
13
|
+
connected: status.connected,
|
|
14
|
+
connecting: status.connecting,
|
|
15
|
+
dataFlow: {
|
|
16
|
+
// We expose downloading as a boolean field, the core extension reports download information as a nullable
|
|
17
|
+
// download status. When that status is non-null, a download is in progress.
|
|
18
|
+
downloading: status.downloading != null,
|
|
19
|
+
downloadProgress: status.downloading?.buckets,
|
|
20
|
+
internalStreamSubscriptions: status.streams
|
|
21
|
+
},
|
|
22
|
+
lastSyncedAt: completeSync?.lastSyncedAt,
|
|
23
|
+
hasSynced: completeSync?.hasSynced,
|
|
24
|
+
priorityStatusEntries: status.priority_status.map(priorityToJs)
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A description of a sync stream, consisting of its {@link name} and the {@link parameters} used when subscribing.
|
|
3
|
+
*/
|
|
4
|
+
export interface SyncStreamDescription {
|
|
5
|
+
/**
|
|
6
|
+
* The name of the stream as it appears in the stream definition for the PowerSync service.
|
|
7
|
+
*/
|
|
8
|
+
name: string;
|
|
9
|
+
/**
|
|
10
|
+
* The parameters used to subscribe to the stream, if any.
|
|
11
|
+
*
|
|
12
|
+
* The same stream can be subscribed to multiple times with different parameters.
|
|
13
|
+
*/
|
|
14
|
+
parameters: Record<string, any> | null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Information about a subscribed sync stream.
|
|
18
|
+
*
|
|
19
|
+
* This includes the {@link SyncStreamDescription}, along with information about the current sync status.
|
|
20
|
+
*/
|
|
21
|
+
export interface SyncSubscriptionDescription extends SyncStreamDescription {
|
|
22
|
+
active: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Whether this stream subscription is included by default, regardless of whether the stream has explicitly been
|
|
25
|
+
* subscribed to or not.
|
|
26
|
+
*
|
|
27
|
+
* It's possible for both {@link isDefault} and {@link hasExplicitSubscription} to be true at the same time - this
|
|
28
|
+
* happens when a default stream was subscribed explicitly.
|
|
29
|
+
*/
|
|
30
|
+
isDefault: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* Whether this stream has been subscribed to explicitly.
|
|
33
|
+
*
|
|
34
|
+
* It's possible for both {@link isDefault} and {@link hasExplicitSubscription} to be true at the same time - this
|
|
35
|
+
* happens when a default stream was subscribed explicitly.
|
|
36
|
+
*/
|
|
37
|
+
hasExplicitSubscription: boolean;
|
|
38
|
+
/**
|
|
39
|
+
* For sync streams that have a time-to-live, the current time at which the stream would expire if not subscribed to
|
|
40
|
+
* again.
|
|
41
|
+
*/
|
|
42
|
+
expiresAt: Date | null;
|
|
43
|
+
/**
|
|
44
|
+
* Whether this stream subscription has been synced at least once.
|
|
45
|
+
*/
|
|
46
|
+
hasSynced: boolean;
|
|
47
|
+
/**
|
|
48
|
+
* If {@link hasSynced} is true, the last time data from this stream has been synced.
|
|
49
|
+
*/
|
|
50
|
+
lastSyncedAt: Date | null;
|
|
51
|
+
}
|
|
52
|
+
export interface SyncStreamSubscribeOptions {
|
|
53
|
+
/**
|
|
54
|
+
* A "time to live" for this stream subscription, in seconds.
|
|
55
|
+
*
|
|
56
|
+
* The TTL control when a stream gets evicted after not having an active {@link SyncStreamSubscription} object
|
|
57
|
+
* attached to it.
|
|
58
|
+
*/
|
|
59
|
+
ttl?: number;
|
|
60
|
+
/**
|
|
61
|
+
* A priority to assign to this subscription. This overrides the default priority that may have been set on streams.
|
|
62
|
+
*
|
|
63
|
+
* For details on priorities, see [priotized sync](https://docs.powersync.com/usage/use-case-examples/prioritized-sync).
|
|
64
|
+
*/
|
|
65
|
+
priority?: 0 | 1 | 2 | 3;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* A handle to a {@link SyncStreamDescription} that allows subscribing to the stream.
|
|
69
|
+
*
|
|
70
|
+
* To obtain an instance of {@link SyncStream}, call {@link AbstractPowerSyncDatabase.syncStream}.
|
|
71
|
+
*/
|
|
72
|
+
export interface SyncStream extends SyncStreamDescription {
|
|
73
|
+
/**
|
|
74
|
+
* Adds a subscription to this stream, requesting it to be included when connecting to the sync service.
|
|
75
|
+
*
|
|
76
|
+
* You should keep a reference to the returned {@link SyncStreamSubscription} object along as you need data for that
|
|
77
|
+
* stream. As soon as {@link SyncStreamSubscription.unsubscribe} is called for all subscriptions on this stream
|
|
78
|
+
* (including subscriptions created on other tabs), the {@link SyncStreamSubscribeOptions.ttl} starts ticking and will
|
|
79
|
+
* eventually evict the stream (unless {@link subscribe} is called again).
|
|
80
|
+
*/
|
|
81
|
+
subscribe(options?: SyncStreamSubscribeOptions): Promise<SyncStreamSubscription>;
|
|
82
|
+
/**
|
|
83
|
+
* Clears all subscriptions attached to this stream and resets the TTL for the stream.
|
|
84
|
+
*
|
|
85
|
+
* This is a potentially dangerous operations, as it interferes with other stream subscriptions.
|
|
86
|
+
*/
|
|
87
|
+
unsubscribeAll(): Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
export interface SyncStreamSubscription extends SyncStreamDescription {
|
|
90
|
+
/**
|
|
91
|
+
* A promise that resolves once data from in this sync stream has been synced and applied.
|
|
92
|
+
*/
|
|
93
|
+
waitForFirstSync(abort?: AbortSignal): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Removes this stream subscription.
|
|
96
|
+
*/
|
|
97
|
+
unsubscribe(): void;
|
|
98
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -65,12 +65,14 @@ export declare enum WatchedQueryListenerEvent {
|
|
|
65
65
|
ON_DATA = "onData",
|
|
66
66
|
ON_ERROR = "onError",
|
|
67
67
|
ON_STATE_CHANGE = "onStateChange",
|
|
68
|
+
SETTINGS_WILL_UPDATE = "settingsWillUpdate",
|
|
68
69
|
CLOSED = "closed"
|
|
69
70
|
}
|
|
70
71
|
export interface WatchedQueryListener<Data> extends BaseListener {
|
|
71
72
|
[WatchedQueryListenerEvent.ON_DATA]?: (data: Data) => void | Promise<void>;
|
|
72
73
|
[WatchedQueryListenerEvent.ON_ERROR]?: (error: Error) => void | Promise<void>;
|
|
73
74
|
[WatchedQueryListenerEvent.ON_STATE_CHANGE]?: (state: WatchedQueryState<Data>) => void | Promise<void>;
|
|
75
|
+
[WatchedQueryListenerEvent.SETTINGS_WILL_UPDATE]?: () => void;
|
|
74
76
|
[WatchedQueryListenerEvent.CLOSED]?: () => void | Promise<void>;
|
|
75
77
|
}
|
|
76
78
|
export declare const DEFAULT_WATCH_THROTTLE_MS = 30;
|
|
@@ -3,6 +3,7 @@ export var WatchedQueryListenerEvent;
|
|
|
3
3
|
WatchedQueryListenerEvent["ON_DATA"] = "onData";
|
|
4
4
|
WatchedQueryListenerEvent["ON_ERROR"] = "onError";
|
|
5
5
|
WatchedQueryListenerEvent["ON_STATE_CHANGE"] = "onStateChange";
|
|
6
|
+
WatchedQueryListenerEvent["SETTINGS_WILL_UPDATE"] = "settingsWillUpdate";
|
|
6
7
|
WatchedQueryListenerEvent["CLOSED"] = "closed";
|
|
7
8
|
})(WatchedQueryListenerEvent || (WatchedQueryListenerEvent = {}));
|
|
8
9
|
export const DEFAULT_WATCH_THROTTLE_MS = 30;
|
|
@@ -54,7 +54,7 @@ export declare abstract class AbstractQueryProcessor<Data = unknown[], Settings
|
|
|
54
54
|
/**
|
|
55
55
|
* Configures base DB listeners and links the query to listeners.
|
|
56
56
|
*/
|
|
57
|
-
protected init(): Promise<void>;
|
|
57
|
+
protected init(signal: AbortSignal): Promise<void>;
|
|
58
58
|
close(): Promise<void>;
|
|
59
59
|
/**
|
|
60
60
|
* Runs a callback and reports errors to the error listeners.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { MetaBaseObserver } from '../../../utils/MetaBaseObserver.js';
|
|
2
|
+
import { WatchedQueryListenerEvent } from '../WatchedQuery.js';
|
|
2
3
|
/**
|
|
3
4
|
* Performs underlying watching and yields a stream of results.
|
|
4
5
|
* @internal
|
|
@@ -20,7 +21,7 @@ export class AbstractQueryProcessor extends MetaBaseObserver {
|
|
|
20
21
|
this._closed = false;
|
|
21
22
|
this.state = this.constructInitialState();
|
|
22
23
|
this.disposeListeners = null;
|
|
23
|
-
this.initialized = this.init();
|
|
24
|
+
this.initialized = this.init(this.abortController.signal);
|
|
24
25
|
}
|
|
25
26
|
constructInitialState() {
|
|
26
27
|
return {
|
|
@@ -36,10 +37,11 @@ export class AbstractQueryProcessor extends MetaBaseObserver {
|
|
|
36
37
|
}
|
|
37
38
|
async updateSettingsInternal(settings, signal) {
|
|
38
39
|
// This may have been aborted while awaiting or if multiple calls to `updateSettings` were made
|
|
39
|
-
if (signal.aborted) {
|
|
40
|
+
if (this._closed || signal.aborted) {
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
42
43
|
this.options.watchOptions = settings;
|
|
44
|
+
this.iterateListeners((l) => l[WatchedQueryListenerEvent.SETTINGS_WILL_UPDATE]?.());
|
|
43
45
|
if (!this.state.isFetching && this.reportFetching) {
|
|
44
46
|
await this.updateState({
|
|
45
47
|
isFetching: true
|
|
@@ -54,7 +56,7 @@ export class AbstractQueryProcessor extends MetaBaseObserver {
|
|
|
54
56
|
* Updates the underlying query.
|
|
55
57
|
*/
|
|
56
58
|
async updateSettings(settings) {
|
|
57
|
-
// Abort
|
|
59
|
+
// Abort the previous request
|
|
58
60
|
this.abortController.abort();
|
|
59
61
|
// Keep track of this controller's abort status
|
|
60
62
|
const abortController = new AbortController();
|
|
@@ -64,6 +66,9 @@ export class AbstractQueryProcessor extends MetaBaseObserver {
|
|
|
64
66
|
return this.updateSettingsInternal(settings, abortController.signal);
|
|
65
67
|
}
|
|
66
68
|
async updateState(update) {
|
|
69
|
+
if (this._closed) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
67
72
|
if (typeof update.error !== 'undefined') {
|
|
68
73
|
await this.iterateAsyncListenersWithError(async (l) => l.onError?.(update.error));
|
|
69
74
|
// An error always stops for the current fetching state
|
|
@@ -79,7 +84,7 @@ export class AbstractQueryProcessor extends MetaBaseObserver {
|
|
|
79
84
|
/**
|
|
80
85
|
* Configures base DB listeners and links the query to listeners.
|
|
81
86
|
*/
|
|
82
|
-
async init() {
|
|
87
|
+
async init(signal) {
|
|
83
88
|
const { db } = this.options;
|
|
84
89
|
const disposeCloseListener = db.registerListener({
|
|
85
90
|
closing: async () => {
|
|
@@ -101,15 +106,14 @@ export class AbstractQueryProcessor extends MetaBaseObserver {
|
|
|
101
106
|
};
|
|
102
107
|
// Initial setup
|
|
103
108
|
await this.runWithReporting(async () => {
|
|
104
|
-
await this.updateSettingsInternal(this.options.watchOptions,
|
|
109
|
+
await this.updateSettingsInternal(this.options.watchOptions, signal);
|
|
105
110
|
});
|
|
106
111
|
}
|
|
107
112
|
async close() {
|
|
108
|
-
|
|
113
|
+
this._closed = true;
|
|
109
114
|
this.abortController.abort();
|
|
110
115
|
this.disposeListeners?.();
|
|
111
116
|
this.disposeListeners = null;
|
|
112
|
-
this._closed = true;
|
|
113
117
|
this.iterateListeners((l) => l.closed?.());
|
|
114
118
|
this.listeners.clear();
|
|
115
119
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { CoreStreamSubscription } from '../../client/sync/stream/core-instruction.js';
|
|
1
2
|
import { SyncClientImplementation } from '../../client/sync/stream/AbstractStreamingSyncImplementation.js';
|
|
2
|
-
import { InternalProgressInformation, SyncProgress } from './SyncProgress.js';
|
|
3
|
+
import { InternalProgressInformation, ProgressWithOperations, SyncProgress } from './SyncProgress.js';
|
|
4
|
+
import { SyncStreamDescription, SyncSubscriptionDescription } from '../../client/sync/sync-streams.js';
|
|
3
5
|
export type SyncDataFlowStatus = Partial<{
|
|
4
6
|
downloading: boolean;
|
|
5
7
|
uploading: boolean;
|
|
@@ -20,6 +22,7 @@ export type SyncDataFlowStatus = Partial<{
|
|
|
20
22
|
* Please use the {@link SyncStatus#downloadProgress} property to track sync progress.
|
|
21
23
|
*/
|
|
22
24
|
downloadProgress: InternalProgressInformation | null;
|
|
25
|
+
internalStreamSubscriptions: CoreStreamSubscription[] | null;
|
|
23
26
|
}>;
|
|
24
27
|
export interface SyncPriorityStatus {
|
|
25
28
|
priority: number;
|
|
@@ -99,7 +102,23 @@ export declare class SyncStatus {
|
|
|
99
102
|
* Please use the {@link SyncStatus#downloadProgress} property to track sync progress.
|
|
100
103
|
*/
|
|
101
104
|
downloadProgress: InternalProgressInformation | null;
|
|
105
|
+
internalStreamSubscriptions: CoreStreamSubscription[] | null;
|
|
102
106
|
}>;
|
|
107
|
+
/**
|
|
108
|
+
* All sync streams currently being tracked in teh database.
|
|
109
|
+
*
|
|
110
|
+
* This returns null when the database is currently being opened and we don't have reliable information about all
|
|
111
|
+
* included streams yet.
|
|
112
|
+
*
|
|
113
|
+
* @experimental Sync streams are currently in alpha.
|
|
114
|
+
*/
|
|
115
|
+
get syncStreams(): SyncStreamStatus[] | undefined;
|
|
116
|
+
/**
|
|
117
|
+
* If the `stream` appears in {@link syncStreams}, returns the current status for that stream.
|
|
118
|
+
*
|
|
119
|
+
* @experimental Sync streams are currently in alpha.
|
|
120
|
+
*/
|
|
121
|
+
forStream(stream: SyncStreamDescription): SyncStreamStatus | undefined;
|
|
103
122
|
/**
|
|
104
123
|
* Provides sync status information for all bucket priorities, sorted by priority (highest first).
|
|
105
124
|
*
|
|
@@ -157,3 +176,11 @@ export declare class SyncStatus {
|
|
|
157
176
|
toJSON(): SyncStatusOptions;
|
|
158
177
|
private static comparePriorities;
|
|
159
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Information about a sync stream subscription.
|
|
181
|
+
*/
|
|
182
|
+
export interface SyncStreamStatus {
|
|
183
|
+
progress: ProgressWithOperations | null;
|
|
184
|
+
subscription: SyncSubscriptionDescription;
|
|
185
|
+
priority: number | null;
|
|
186
|
+
}
|
|
@@ -68,6 +68,27 @@ export class SyncStatus {
|
|
|
68
68
|
uploading: false
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* All sync streams currently being tracked in teh database.
|
|
73
|
+
*
|
|
74
|
+
* This returns null when the database is currently being opened and we don't have reliable information about all
|
|
75
|
+
* included streams yet.
|
|
76
|
+
*
|
|
77
|
+
* @experimental Sync streams are currently in alpha.
|
|
78
|
+
*/
|
|
79
|
+
get syncStreams() {
|
|
80
|
+
return this.options.dataFlow?.internalStreamSubscriptions?.map((core) => new SyncStreamStatusView(this, core));
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* If the `stream` appears in {@link syncStreams}, returns the current status for that stream.
|
|
84
|
+
*
|
|
85
|
+
* @experimental Sync streams are currently in alpha.
|
|
86
|
+
*/
|
|
87
|
+
forStream(stream) {
|
|
88
|
+
const asJson = JSON.stringify(stream.parameters);
|
|
89
|
+
const raw = this.options.dataFlow?.internalStreamSubscriptions?.find((r) => r.name == stream.name && asJson == JSON.stringify(r.parameters));
|
|
90
|
+
return raw && new SyncStreamStatusView(this, raw);
|
|
91
|
+
}
|
|
71
92
|
/**
|
|
72
93
|
* Provides sync status information for all bucket priorities, sorted by priority (highest first).
|
|
73
94
|
*
|
|
@@ -177,3 +198,34 @@ export class SyncStatus {
|
|
|
177
198
|
return b.priority - a.priority; // Reverse because higher priorities have lower numbers
|
|
178
199
|
}
|
|
179
200
|
}
|
|
201
|
+
class SyncStreamStatusView {
|
|
202
|
+
status;
|
|
203
|
+
core;
|
|
204
|
+
subscription;
|
|
205
|
+
constructor(status, core) {
|
|
206
|
+
this.status = status;
|
|
207
|
+
this.core = core;
|
|
208
|
+
this.subscription = {
|
|
209
|
+
name: core.name,
|
|
210
|
+
parameters: core.parameters,
|
|
211
|
+
active: core.active,
|
|
212
|
+
isDefault: core.is_default,
|
|
213
|
+
hasExplicitSubscription: core.has_explicit_subscription,
|
|
214
|
+
expiresAt: core.expires_at != null ? new Date(core.expires_at * 1000) : null,
|
|
215
|
+
hasSynced: core.last_synced_at != null,
|
|
216
|
+
lastSyncedAt: core.last_synced_at != null ? new Date(core.last_synced_at * 1000) : null
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
get progress() {
|
|
220
|
+
if (this.status.dataFlowStatus.downloadProgress == null) {
|
|
221
|
+
// Don't make download progress public if we're not currently downloading.
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
const { total, downloaded } = this.core.progress;
|
|
225
|
+
const progress = total == 0 ? 0.0 : downloaded / total;
|
|
226
|
+
return { totalOperations: total, downloadedOperations: downloaded, downloadedFraction: progress };
|
|
227
|
+
}
|
|
228
|
+
get priority() {
|
|
229
|
+
return this.core.priority;
|
|
230
|
+
}
|
|
231
|
+
}
|
package/lib/index.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export * from './client/sync/bucket/SyncDataBucket.js';
|
|
|
18
18
|
export * from './client/sync/stream/AbstractRemote.js';
|
|
19
19
|
export * from './client/sync/stream/AbstractStreamingSyncImplementation.js';
|
|
20
20
|
export * from './client/sync/stream/streaming-sync-types.js';
|
|
21
|
+
export * from './client/sync/sync-streams.js';
|
|
21
22
|
export * from './client/ConnectionManager.js';
|
|
22
23
|
export { ProgressWithOperations, SyncProgress } from './db/crud/SyncProgress.js';
|
|
23
24
|
export * from './db/crud/SyncStatus.js';
|
package/lib/index.js
CHANGED
|
@@ -18,6 +18,7 @@ export * from './client/sync/bucket/SyncDataBucket.js';
|
|
|
18
18
|
export * from './client/sync/stream/AbstractRemote.js';
|
|
19
19
|
export * from './client/sync/stream/AbstractStreamingSyncImplementation.js';
|
|
20
20
|
export * from './client/sync/stream/streaming-sync-types.js';
|
|
21
|
+
export * from './client/sync/sync-streams.js';
|
|
21
22
|
export * from './client/ConnectionManager.js';
|
|
22
23
|
export { SyncProgress } from './db/crud/SyncProgress.js';
|
|
23
24
|
export * from './db/crud/SyncStatus.js';
|