@powersync/common 1.31.1 → 1.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bundle.mjs +5 -5
- package/lib/client/AbstractPowerSyncDatabase.d.ts +9 -2
- package/lib/client/AbstractPowerSyncDatabase.js +42 -30
- package/lib/client/ConnectionManager.d.ts +80 -0
- package/lib/client/ConnectionManager.js +175 -0
- package/lib/client/sync/bucket/BucketStorageAdapter.d.ts +15 -1
- package/lib/client/sync/bucket/BucketStorageAdapter.js +9 -0
- package/lib/client/sync/bucket/CrudEntry.d.ts +2 -0
- package/lib/client/sync/bucket/CrudEntry.js +13 -2
- package/lib/client/sync/bucket/OplogEntry.d.ts +4 -4
- package/lib/client/sync/bucket/OplogEntry.js +5 -3
- package/lib/client/sync/bucket/SqliteBucketStorage.d.ts +6 -2
- package/lib/client/sync/bucket/SqliteBucketStorage.js +24 -2
- package/lib/client/sync/bucket/SyncDataBucket.d.ts +1 -1
- package/lib/client/sync/bucket/SyncDataBucket.js +2 -2
- package/lib/client/sync/stream/AbstractRemote.d.ts +16 -3
- package/lib/client/sync/stream/AbstractRemote.js +74 -92
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +69 -0
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +469 -212
- package/lib/client/sync/stream/WebsocketClientTransport.d.ts +15 -0
- package/lib/client/sync/stream/WebsocketClientTransport.js +60 -0
- package/lib/client/sync/stream/core-instruction.d.ts +53 -0
- package/lib/client/sync/stream/core-instruction.js +1 -0
- package/lib/db/crud/SyncProgress.d.ts +2 -6
- package/lib/db/crud/SyncProgress.js +2 -2
- package/lib/db/crud/SyncStatus.js +15 -1
- package/lib/index.d.ts +13 -14
- package/lib/index.js +13 -14
- package/package.json +1 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { BSON } from 'bson';
|
|
2
|
+
import { Buffer } from 'buffer';
|
|
2
3
|
import { type fetch } from 'cross-fetch';
|
|
3
4
|
import Logger, { ILogger } from 'js-logger';
|
|
4
5
|
import { DataStream } from '../../../utils/DataStream.js';
|
|
@@ -115,18 +116,30 @@ export declare abstract class AbstractRemote {
|
|
|
115
116
|
}>;
|
|
116
117
|
post(path: string, data: any, headers?: Record<string, string>): Promise<any>;
|
|
117
118
|
get(path: string, headers?: Record<string, string>): Promise<any>;
|
|
118
|
-
postStreaming(path: string, data: any, headers?: Record<string, string>, signal?: AbortSignal): Promise<any>;
|
|
119
119
|
/**
|
|
120
120
|
* Provides a BSON implementation. The import nature of this varies depending on the platform
|
|
121
121
|
*/
|
|
122
122
|
abstract getBSON(): Promise<BSONImplementation>;
|
|
123
123
|
protected createSocket(url: string): WebSocket;
|
|
124
124
|
/**
|
|
125
|
-
* Connects to the sync/stream websocket endpoint
|
|
125
|
+
* Connects to the sync/stream websocket endpoint and delivers sync lines by decoding the BSON events
|
|
126
|
+
* sent by the server.
|
|
126
127
|
*/
|
|
127
128
|
socketStream(options: SocketSyncStreamOptions): Promise<DataStream<StreamingSyncLine>>;
|
|
128
129
|
/**
|
|
129
|
-
*
|
|
130
|
+
* Returns a data stream of sync line data.
|
|
131
|
+
*
|
|
132
|
+
* @param map Maps received payload frames to the typed event value.
|
|
133
|
+
* @param bson A BSON encoder and decoder. When set, the data stream will be requested with a BSON payload
|
|
134
|
+
* (required for compatibility with older sync services).
|
|
135
|
+
*/
|
|
136
|
+
socketStreamRaw<T>(options: SocketSyncStreamOptions, map: (buffer: Buffer) => T, bson?: typeof BSON): Promise<DataStream>;
|
|
137
|
+
/**
|
|
138
|
+
* Connects to the sync/stream http endpoint, parsing lines as JSON.
|
|
130
139
|
*/
|
|
131
140
|
postStream(options: SyncStreamOptions): Promise<DataStream<StreamingSyncLine>>;
|
|
141
|
+
/**
|
|
142
|
+
* Connects to the sync/stream http endpoint, mapping and emitting each received string line.
|
|
143
|
+
*/
|
|
144
|
+
postStreamRaw<T>(options: SyncStreamOptions, mapLine: (line: string) => T): Promise<DataStream<T>>;
|
|
132
145
|
}
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { Buffer } from 'buffer';
|
|
2
|
-
import ndjsonStream from 'can-ndjson-stream';
|
|
3
2
|
import Logger from 'js-logger';
|
|
4
3
|
import { RSocketConnector } from 'rsocket-core';
|
|
5
|
-
import { WebsocketClientTransport } from 'rsocket-websocket-client';
|
|
6
4
|
import PACKAGE from '../../../../package.json' with { type: 'json' };
|
|
7
5
|
import { AbortOperation } from '../../../utils/AbortOperation.js';
|
|
8
6
|
import { DataStream } from '../../../utils/DataStream.js';
|
|
7
|
+
import { WebsocketClientTransport } from './WebsocketClientTransport.js';
|
|
9
8
|
const POWERSYNC_TRAILING_SLASH_MATCH = /\/+$/;
|
|
10
9
|
const POWERSYNC_JS_VERSION = PACKAGE.version;
|
|
11
10
|
const SYNC_QUEUE_REQUEST_LOW_WATER = 5;
|
|
@@ -172,69 +171,62 @@ export class AbstractRemote {
|
|
|
172
171
|
}
|
|
173
172
|
return res.json();
|
|
174
173
|
}
|
|
175
|
-
async postStreaming(path, data, headers = {}, signal) {
|
|
176
|
-
const request = await this.buildRequest(path);
|
|
177
|
-
const res = await this.fetch(request.url, {
|
|
178
|
-
method: 'POST',
|
|
179
|
-
headers: { ...headers, ...request.headers },
|
|
180
|
-
body: JSON.stringify(data),
|
|
181
|
-
signal,
|
|
182
|
-
cache: 'no-store'
|
|
183
|
-
}).catch((ex) => {
|
|
184
|
-
this.logger.error(`Caught ex when POST streaming to ${path}`, ex);
|
|
185
|
-
throw ex;
|
|
186
|
-
});
|
|
187
|
-
if (res.status === 401) {
|
|
188
|
-
this.invalidateCredentials();
|
|
189
|
-
}
|
|
190
|
-
if (!res.ok) {
|
|
191
|
-
const text = await res.text();
|
|
192
|
-
this.logger.error(`Could not POST streaming to ${path} - ${res.status} - ${res.statusText}: ${text}`);
|
|
193
|
-
const error = new Error(`HTTP ${res.statusText}: ${text}`);
|
|
194
|
-
error.status = res.status;
|
|
195
|
-
throw error;
|
|
196
|
-
}
|
|
197
|
-
return res;
|
|
198
|
-
}
|
|
199
174
|
createSocket(url) {
|
|
200
175
|
return new WebSocket(url);
|
|
201
176
|
}
|
|
202
177
|
/**
|
|
203
|
-
* Connects to the sync/stream websocket endpoint
|
|
178
|
+
* Connects to the sync/stream websocket endpoint and delivers sync lines by decoding the BSON events
|
|
179
|
+
* sent by the server.
|
|
204
180
|
*/
|
|
205
181
|
async socketStream(options) {
|
|
182
|
+
const bson = await this.getBSON();
|
|
183
|
+
return await this.socketStreamRaw(options, (data) => bson.deserialize(data), bson);
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Returns a data stream of sync line data.
|
|
187
|
+
*
|
|
188
|
+
* @param map Maps received payload frames to the typed event value.
|
|
189
|
+
* @param bson A BSON encoder and decoder. When set, the data stream will be requested with a BSON payload
|
|
190
|
+
* (required for compatibility with older sync services).
|
|
191
|
+
*/
|
|
192
|
+
async socketStreamRaw(options, map, bson) {
|
|
206
193
|
const { path, fetchStrategy = FetchStrategy.Buffered } = options;
|
|
194
|
+
const mimeType = bson == null ? 'application/json' : 'application/bson';
|
|
195
|
+
function toBuffer(js) {
|
|
196
|
+
let contents;
|
|
197
|
+
if (bson != null) {
|
|
198
|
+
contents = bson.serialize(js);
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
contents = JSON.stringify(js);
|
|
202
|
+
}
|
|
203
|
+
return Buffer.from(contents);
|
|
204
|
+
}
|
|
207
205
|
const syncQueueRequestSize = fetchStrategy == FetchStrategy.Buffered ? 10 : 1;
|
|
208
206
|
const request = await this.buildRequest(path);
|
|
209
|
-
const bson = await this.getBSON();
|
|
210
207
|
// Add the user agent in the setup payload - we can't set custom
|
|
211
208
|
// headers with websockets on web. The browser userAgent is however added
|
|
212
209
|
// automatically as a header.
|
|
213
210
|
const userAgent = this.getUserAgent();
|
|
214
|
-
|
|
211
|
+
const url = this.options.socketUrlTransformer(request.url);
|
|
215
212
|
const connector = new RSocketConnector({
|
|
216
213
|
transport: new WebsocketClientTransport({
|
|
217
|
-
url
|
|
214
|
+
url,
|
|
218
215
|
wsCreator: (url) => {
|
|
219
|
-
|
|
220
|
-
s.addEventListener('error', (e) => {
|
|
221
|
-
socketCreationError = new Error('Failed to create connection to websocket: ', e.target.url ?? '');
|
|
222
|
-
this.logger.warn('Socket error', e);
|
|
223
|
-
});
|
|
224
|
-
return s;
|
|
216
|
+
return this.createSocket(url);
|
|
225
217
|
}
|
|
226
218
|
}),
|
|
227
219
|
setup: {
|
|
228
220
|
keepAlive: KEEP_ALIVE_MS,
|
|
229
221
|
lifetime: KEEP_ALIVE_LIFETIME_MS,
|
|
230
|
-
dataMimeType:
|
|
231
|
-
metadataMimeType:
|
|
222
|
+
dataMimeType: mimeType,
|
|
223
|
+
metadataMimeType: mimeType,
|
|
232
224
|
payload: {
|
|
233
225
|
data: null,
|
|
234
|
-
metadata:
|
|
226
|
+
metadata: toBuffer({
|
|
235
227
|
token: request.headers.Authorization,
|
|
236
228
|
user_agent: userAgent
|
|
237
|
-
})
|
|
229
|
+
})
|
|
238
230
|
}
|
|
239
231
|
}
|
|
240
232
|
});
|
|
@@ -243,11 +235,8 @@ export class AbstractRemote {
|
|
|
243
235
|
rsocket = await connector.connect();
|
|
244
236
|
}
|
|
245
237
|
catch (ex) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
* with detecting the exception inside async-mutex
|
|
249
|
-
*/
|
|
250
|
-
throw new Error(`Could not connect to PowerSync instance: ${JSON.stringify(ex ?? socketCreationError)}`);
|
|
238
|
+
this.logger.error(`Failed to connect WebSocket`, ex);
|
|
239
|
+
throw ex;
|
|
251
240
|
}
|
|
252
241
|
const stream = new DataStream({
|
|
253
242
|
logger: this.logger,
|
|
@@ -276,10 +265,10 @@ export class AbstractRemote {
|
|
|
276
265
|
const socket = await new Promise((resolve, reject) => {
|
|
277
266
|
let connectionEstablished = false;
|
|
278
267
|
const res = rsocket.requestStream({
|
|
279
|
-
data:
|
|
280
|
-
metadata:
|
|
268
|
+
data: toBuffer(options.data),
|
|
269
|
+
metadata: toBuffer({
|
|
281
270
|
path
|
|
282
|
-
})
|
|
271
|
+
})
|
|
283
272
|
}, syncQueueRequestSize, // The initial N amount
|
|
284
273
|
{
|
|
285
274
|
onError: (e) => {
|
|
@@ -318,8 +307,7 @@ export class AbstractRemote {
|
|
|
318
307
|
if (!data) {
|
|
319
308
|
return;
|
|
320
309
|
}
|
|
321
|
-
|
|
322
|
-
stream.enqueueData(deserializedData);
|
|
310
|
+
stream.enqueueData(map(data));
|
|
323
311
|
},
|
|
324
312
|
onComplete: () => {
|
|
325
313
|
stream.close();
|
|
@@ -355,9 +343,17 @@ export class AbstractRemote {
|
|
|
355
343
|
return stream;
|
|
356
344
|
}
|
|
357
345
|
/**
|
|
358
|
-
* Connects to the sync/stream http endpoint
|
|
346
|
+
* Connects to the sync/stream http endpoint, parsing lines as JSON.
|
|
359
347
|
*/
|
|
360
348
|
async postStream(options) {
|
|
349
|
+
return await this.postStreamRaw(options, (line) => {
|
|
350
|
+
return JSON.parse(line);
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Connects to the sync/stream http endpoint, mapping and emitting each received string line.
|
|
355
|
+
*/
|
|
356
|
+
async postStreamRaw(options, mapLine) {
|
|
361
357
|
const { data, path, headers, abortSignal } = options;
|
|
362
358
|
const request = await this.buildRequest(path);
|
|
363
359
|
/**
|
|
@@ -403,11 +399,8 @@ export class AbstractRemote {
|
|
|
403
399
|
error.status = res.status;
|
|
404
400
|
throw error;
|
|
405
401
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
* This will intercept the readable stream and close the stream if
|
|
409
|
-
* aborted.
|
|
410
|
-
*/
|
|
402
|
+
// Create a new stream splitting the response at line endings while also handling cancellations
|
|
403
|
+
// by closing the reader.
|
|
411
404
|
const reader = res.body.getReader();
|
|
412
405
|
// This will close the network request and read stream
|
|
413
406
|
const closeReader = async () => {
|
|
@@ -422,49 +415,38 @@ export class AbstractRemote {
|
|
|
422
415
|
abortSignal?.addEventListener('abort', () => {
|
|
423
416
|
closeReader();
|
|
424
417
|
});
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
const processStream = async () => {
|
|
428
|
-
while (!abortSignal?.aborted) {
|
|
429
|
-
try {
|
|
430
|
-
const { done, value } = await reader.read();
|
|
431
|
-
// When no more data needs to be consumed, close the stream
|
|
432
|
-
if (done) {
|
|
433
|
-
break;
|
|
434
|
-
}
|
|
435
|
-
// Enqueue the next data chunk into our target stream
|
|
436
|
-
controller.enqueue(value);
|
|
437
|
-
}
|
|
438
|
-
catch (ex) {
|
|
439
|
-
this.logger.error('Caught exception when reading sync stream', ex);
|
|
440
|
-
break;
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
if (!abortSignal?.aborted) {
|
|
444
|
-
// Close the downstream readable stream
|
|
445
|
-
await closeReader();
|
|
446
|
-
}
|
|
447
|
-
controller.close();
|
|
448
|
-
};
|
|
449
|
-
processStream();
|
|
450
|
-
}
|
|
451
|
-
});
|
|
452
|
-
const jsonS = ndjsonStream(outputStream);
|
|
418
|
+
const decoder = new TextDecoder();
|
|
419
|
+
let buffer = '';
|
|
453
420
|
const stream = new DataStream({
|
|
454
421
|
logger: this.logger
|
|
455
422
|
});
|
|
456
|
-
const r = jsonS.getReader();
|
|
457
423
|
const l = stream.registerListener({
|
|
458
424
|
lowWater: async () => {
|
|
459
425
|
try {
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
426
|
+
let didCompleteLine = false;
|
|
427
|
+
while (!didCompleteLine) {
|
|
428
|
+
const { done, value } = await reader.read();
|
|
429
|
+
if (done) {
|
|
430
|
+
const remaining = buffer.trim();
|
|
431
|
+
if (remaining.length != 0) {
|
|
432
|
+
stream.enqueueData(mapLine(remaining));
|
|
433
|
+
}
|
|
434
|
+
stream.close();
|
|
435
|
+
await closeReader();
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
const data = decoder.decode(value, { stream: true });
|
|
439
|
+
buffer += data;
|
|
440
|
+
const lines = buffer.split('\n');
|
|
441
|
+
for (var i = 0; i < lines.length - 1; i++) {
|
|
442
|
+
var l = lines[i].trim();
|
|
443
|
+
if (l.length > 0) {
|
|
444
|
+
stream.enqueueData(mapLine(l));
|
|
445
|
+
didCompleteLine = true;
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
buffer = lines[lines.length - 1];
|
|
466
449
|
}
|
|
467
|
-
stream.enqueueData(value);
|
|
468
450
|
}
|
|
469
451
|
catch (ex) {
|
|
470
452
|
stream.close();
|
|
@@ -12,6 +12,48 @@ export declare enum SyncStreamConnectionMethod {
|
|
|
12
12
|
HTTP = "http",
|
|
13
13
|
WEB_SOCKET = "web-socket"
|
|
14
14
|
}
|
|
15
|
+
export declare enum SyncClientImplementation {
|
|
16
|
+
/**
|
|
17
|
+
* Decodes and handles sync lines received from the sync service in JavaScript.
|
|
18
|
+
*
|
|
19
|
+
* This is the default option.
|
|
20
|
+
*
|
|
21
|
+
* @deprecated Don't use {@link SyncClientImplementation.JAVASCRIPT} directly. Instead, use
|
|
22
|
+
* {@link DEFAULT_SYNC_CLIENT_IMPLEMENTATION} or omit the option. The explicit choice to use
|
|
23
|
+
* the JavaScript-based sync implementation will be removed from a future version of the SDK.
|
|
24
|
+
*/
|
|
25
|
+
JAVASCRIPT = "js",
|
|
26
|
+
/**
|
|
27
|
+
* This implementation offloads the sync line decoding and handling into the PowerSync
|
|
28
|
+
* core extension.
|
|
29
|
+
*
|
|
30
|
+
* @experimental
|
|
31
|
+
* While this implementation is more performant than {@link SyncClientImplementation.JAVASCRIPT},
|
|
32
|
+
* it has seen less real-world testing and is marked as __experimental__ at the moment.
|
|
33
|
+
*
|
|
34
|
+
* ## Compatibility warning
|
|
35
|
+
*
|
|
36
|
+
* The Rust sync client stores sync data in a format that is slightly different than the one used
|
|
37
|
+
* by the old {@link JAVASCRIPT} implementation. When adopting the {@link RUST} client on existing
|
|
38
|
+
* databases, the PowerSync SDK will migrate the format automatically.
|
|
39
|
+
* Further, the {@link JAVASCRIPT} client in recent versions of the PowerSync JS SDK (starting from
|
|
40
|
+
* the version introducing {@link RUST} as an option) also supports the new format, so you can switch
|
|
41
|
+
* back to {@link JAVASCRIPT} later.
|
|
42
|
+
*
|
|
43
|
+
* __However__: Upgrading the SDK version, then adopting {@link RUST} as a sync client and later
|
|
44
|
+
* downgrading the SDK to an older version (necessarily using the JavaScript-based implementation then)
|
|
45
|
+
* can lead to sync issues.
|
|
46
|
+
*/
|
|
47
|
+
RUST = "rust"
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* The default {@link SyncClientImplementation} to use.
|
|
51
|
+
*
|
|
52
|
+
* Please use this field instead of {@link SyncClientImplementation.JAVASCRIPT} directly. A future version
|
|
53
|
+
* of the PowerSync SDK will enable {@link SyncClientImplementation.RUST} by default and remove the JavaScript
|
|
54
|
+
* option.
|
|
55
|
+
*/
|
|
56
|
+
export declare const DEFAULT_SYNC_CLIENT_IMPLEMENTATION = SyncClientImplementation.JAVASCRIPT;
|
|
15
57
|
/**
|
|
16
58
|
* Abstract Lock to be implemented by various JS environments
|
|
17
59
|
*/
|
|
@@ -50,6 +92,14 @@ export interface PowerSyncConnectionOptions extends BaseConnectionOptions, Addit
|
|
|
50
92
|
}
|
|
51
93
|
/** @internal */
|
|
52
94
|
export interface BaseConnectionOptions {
|
|
95
|
+
/**
|
|
96
|
+
* Whether to use a JavaScript implementation to handle received sync lines from the sync
|
|
97
|
+
* service, or whether this work should be offloaded to the PowerSync core extension.
|
|
98
|
+
*
|
|
99
|
+
* This defaults to the JavaScript implementation ({@link SyncClientImplementation.JAVASCRIPT})
|
|
100
|
+
* since the ({@link SyncClientImplementation.RUST}) implementation is experimental at the moment.
|
|
101
|
+
*/
|
|
102
|
+
clientImplementation?: SyncClientImplementation;
|
|
53
103
|
/**
|
|
54
104
|
* The connection method to use when streaming updates from
|
|
55
105
|
* the PowerSync backend instance.
|
|
@@ -117,6 +167,7 @@ export declare abstract class AbstractStreamingSyncImplementation extends BaseOb
|
|
|
117
167
|
protected crudUpdateListener?: () => void;
|
|
118
168
|
protected streamingSyncPromise?: Promise<void>;
|
|
119
169
|
private pendingCrudUpload?;
|
|
170
|
+
private notifyCompletedUploads?;
|
|
120
171
|
syncStatus: SyncStatus;
|
|
121
172
|
triggerCrudUpload: () => void;
|
|
122
173
|
constructor(options: AbstractStreamingSyncImplementationOptions);
|
|
@@ -138,7 +189,25 @@ export declare abstract class AbstractStreamingSyncImplementation extends BaseOb
|
|
|
138
189
|
*/
|
|
139
190
|
streamingSync(signal?: AbortSignal, options?: PowerSyncConnectionOptions): Promise<void>;
|
|
140
191
|
private collectLocalBucketState;
|
|
192
|
+
/**
|
|
193
|
+
* Older versions of the JS SDK used to encode subkeys as JSON in {@link OplogEntry.toJSON}.
|
|
194
|
+
* Because subkeys are always strings, this leads to quotes being added around them in `ps_oplog`.
|
|
195
|
+
* While this is not a problem as long as it's done consistently, it causes issues when a database
|
|
196
|
+
* created by the JS SDK is used with other SDKs, or (more likely) when the new Rust sync client
|
|
197
|
+
* is enabled.
|
|
198
|
+
*
|
|
199
|
+
* So, we add a migration from the old key format (with quotes) to the new one (no quotes). The
|
|
200
|
+
* migration is only triggered when necessary (for now). The function returns whether the new format
|
|
201
|
+
* should be used, so that the JS SDK is able to write to updated databases.
|
|
202
|
+
*
|
|
203
|
+
* @param requireFixedKeyFormat Whether we require the new format or also support the old one.
|
|
204
|
+
* The Rust client requires the new subkey format.
|
|
205
|
+
* @returns Whether the database is now using the new, fixed subkey format.
|
|
206
|
+
*/
|
|
207
|
+
private requireKeyFormat;
|
|
141
208
|
protected streamingSyncIteration(signal: AbortSignal, options?: PowerSyncConnectionOptions): Promise<void>;
|
|
209
|
+
private legacyStreamingSyncIteration;
|
|
210
|
+
private rustSyncIteration;
|
|
142
211
|
private updateSyncStatusForStartingCheckpoint;
|
|
143
212
|
private applyCheckpoint;
|
|
144
213
|
protected updateSyncStatus(options: SyncStatusOptions): void;
|