@powersync/common 1.52.0 → 1.53.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.cjs +161 -766
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +162 -755
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +161 -766
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +162 -755
- package/dist/bundle.node.mjs.map +1 -1
- package/dist/index.d.cts +39 -370
- package/legacy/sync_protocol.d.ts +103 -0
- package/lib/client/ConnectionManager.js +1 -1
- package/lib/client/ConnectionManager.js.map +1 -1
- package/lib/client/sync/bucket/BucketStorageAdapter.d.ts +6 -64
- package/lib/client/sync/bucket/BucketStorageAdapter.js +4 -0
- package/lib/client/sync/bucket/BucketStorageAdapter.js.map +1 -1
- package/lib/client/sync/bucket/SqliteBucketStorage.d.ts +1 -28
- package/lib/client/sync/bucket/SqliteBucketStorage.js +0 -162
- package/lib/client/sync/bucket/SqliteBucketStorage.js.map +1 -1
- package/lib/client/sync/stream/AbstractRemote.d.ts +2 -12
- package/lib/client/sync/stream/AbstractRemote.js +3 -13
- package/lib/client/sync/stream/AbstractRemote.js.map +1 -1
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.d.ts +12 -35
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js +145 -424
- package/lib/client/sync/stream/AbstractStreamingSyncImplementation.js.map +1 -1
- package/lib/client/sync/stream/JsonValue.d.ts +7 -0
- package/lib/client/sync/stream/JsonValue.js +2 -0
- package/lib/client/sync/stream/JsonValue.js.map +1 -0
- package/lib/client/sync/stream/core-instruction.d.ts +14 -9
- package/lib/client/sync/stream/core-instruction.js +3 -0
- package/lib/client/sync/stream/core-instruction.js.map +1 -1
- package/lib/db/DBAdapter.d.ts +9 -0
- package/lib/db/DBAdapter.js +8 -1
- package/lib/db/DBAdapter.js.map +1 -1
- package/lib/db/crud/SyncStatus.d.ts +3 -4
- package/lib/db/crud/SyncStatus.js +0 -4
- package/lib/db/crud/SyncStatus.js.map +1 -1
- package/lib/db/schema/RawTable.d.ts +0 -5
- package/lib/db/schema/Schema.d.ts +0 -2
- package/lib/db/schema/Schema.js +0 -2
- package/lib/db/schema/Schema.js.map +1 -1
- package/lib/index.d.ts +1 -5
- package/lib/index.js +1 -5
- package/lib/index.js.map +1 -1
- package/package.json +9 -6
- package/src/client/ConnectionManager.ts +1 -1
- package/src/client/sync/bucket/BucketStorageAdapter.ts +6 -71
- package/src/client/sync/bucket/SqliteBucketStorage.ts +1 -197
- package/src/client/sync/stream/AbstractRemote.ts +5 -27
- package/src/client/sync/stream/AbstractStreamingSyncImplementation.ts +168 -496
- package/src/client/sync/stream/JsonValue.ts +8 -0
- package/src/client/sync/stream/core-instruction.ts +15 -5
- package/src/db/DBAdapter.ts +20 -2
- package/src/db/crud/SyncStatus.ts +4 -5
- package/src/db/schema/RawTable.ts +0 -5
- package/src/db/schema/Schema.ts +0 -2
- package/src/index.ts +1 -5
- package/lib/client/sync/bucket/OpType.d.ts +0 -16
- package/lib/client/sync/bucket/OpType.js +0 -23
- package/lib/client/sync/bucket/OpType.js.map +0 -1
- package/lib/client/sync/bucket/OplogEntry.d.ts +0 -23
- package/lib/client/sync/bucket/OplogEntry.js +0 -36
- package/lib/client/sync/bucket/OplogEntry.js.map +0 -1
- package/lib/client/sync/bucket/SyncDataBatch.d.ts +0 -6
- package/lib/client/sync/bucket/SyncDataBatch.js +0 -12
- package/lib/client/sync/bucket/SyncDataBatch.js.map +0 -1
- package/lib/client/sync/bucket/SyncDataBucket.d.ts +0 -40
- package/lib/client/sync/bucket/SyncDataBucket.js +0 -40
- package/lib/client/sync/bucket/SyncDataBucket.js.map +0 -1
- package/lib/client/sync/stream/streaming-sync-types.d.ts +0 -143
- package/lib/client/sync/stream/streaming-sync-types.js +0 -26
- package/lib/client/sync/stream/streaming-sync-types.js.map +0 -1
- package/src/client/sync/bucket/OpType.ts +0 -23
- package/src/client/sync/bucket/OplogEntry.ts +0 -50
- package/src/client/sync/bucket/SyncDataBatch.ts +0 -11
- package/src/client/sync/bucket/SyncDataBucket.ts +0 -49
- package/src/client/sync/stream/streaming-sync-types.ts +0 -210
package/dist/bundle.cjs
CHANGED
|
@@ -1971,8 +1971,15 @@ class BaseTransaction {
|
|
|
1971
1971
|
class TransactionImplementation extends DBGetUtilsDefaultMixin(BaseTransaction) {
|
|
1972
1972
|
static async runWith(ctx, fn) {
|
|
1973
1973
|
let tx = new TransactionImplementation(ctx);
|
|
1974
|
+
// For write transactions, use BEGIN IMMEDIATE to immediately obtain a write lock on the database (instead of doing
|
|
1975
|
+
// that on the first statement). If we have a genuine read-only connection, we also use BEGIN IMMEDIATE there: In
|
|
1976
|
+
// WAL mode, that ensures we pin the current state of the database (instead of the state at the first statement in
|
|
1977
|
+
// the transaction). But if we have a "fake" read-only connection implemented through `pragma query_only = true`, we
|
|
1978
|
+
// can't use this trick because it would attempt to lock the connection. So there, we use a regular `BEGIN`
|
|
1979
|
+
// statement.
|
|
1980
|
+
const useBeginImmediate = ctx.connectionType != 'queryOnly';
|
|
1974
1981
|
try {
|
|
1975
|
-
await ctx.execute('BEGIN IMMEDIATE');
|
|
1982
|
+
await ctx.execute(useBeginImmediate ? 'BEGIN IMMEDIATE' : 'BEGIN');
|
|
1976
1983
|
const result = await fn(tx);
|
|
1977
1984
|
await tx.commit();
|
|
1978
1985
|
return result;
|
|
@@ -2131,16 +2138,12 @@ class SyncStatus {
|
|
|
2131
2138
|
*
|
|
2132
2139
|
* This returns null when the database is currently being opened and we don't have reliable information about all
|
|
2133
2140
|
* included streams yet.
|
|
2134
|
-
*
|
|
2135
|
-
* @experimental Sync streams are currently in alpha.
|
|
2136
2141
|
*/
|
|
2137
2142
|
get syncStreams() {
|
|
2138
2143
|
return this.options.dataFlow?.internalStreamSubscriptions?.map((core) => new SyncStreamStatusView(this, core));
|
|
2139
2144
|
}
|
|
2140
2145
|
/**
|
|
2141
2146
|
* If the `stream` appears in {@link syncStreams}, returns the current status for that stream.
|
|
2142
|
-
*
|
|
2143
|
-
* @experimental Sync streams are currently in alpha.
|
|
2144
2147
|
*/
|
|
2145
2148
|
forStream(stream) {
|
|
2146
2149
|
const asJson = JSON.stringify(stream.parameters);
|
|
@@ -2723,7 +2726,7 @@ class SyncStreamSubscriptionHandle {
|
|
|
2723
2726
|
constructor(subscription) {
|
|
2724
2727
|
this.subscription = subscription;
|
|
2725
2728
|
subscription.refcount++;
|
|
2726
|
-
_finalizer?.register(this, subscription);
|
|
2729
|
+
_finalizer?.register(this, subscription, this);
|
|
2727
2730
|
}
|
|
2728
2731
|
get name() {
|
|
2729
2732
|
return this.subscription.name;
|
|
@@ -3312,6 +3315,10 @@ exports.PowerSyncControlCommand = void 0;
|
|
|
3312
3315
|
PowerSyncControlCommand["NOTIFY_TOKEN_REFRESHED"] = "refreshed_token";
|
|
3313
3316
|
PowerSyncControlCommand["NOTIFY_CRUD_UPLOAD_COMPLETED"] = "completed_upload";
|
|
3314
3317
|
PowerSyncControlCommand["UPDATE_SUBSCRIPTIONS"] = "update_subscriptions";
|
|
3318
|
+
/**
|
|
3319
|
+
* An `established` or `end` event for response streams.
|
|
3320
|
+
*/
|
|
3321
|
+
PowerSyncControlCommand["CONNECTION_STATE"] = "connection";
|
|
3315
3322
|
})(exports.PowerSyncControlCommand || (exports.PowerSyncControlCommand = {}));
|
|
3316
3323
|
|
|
3317
3324
|
/**
|
|
@@ -3493,103 +3500,6 @@ class AbortOperation extends Error {
|
|
|
3493
3500
|
}
|
|
3494
3501
|
}
|
|
3495
3502
|
|
|
3496
|
-
exports.OpTypeEnum = void 0;
|
|
3497
|
-
(function (OpTypeEnum) {
|
|
3498
|
-
OpTypeEnum[OpTypeEnum["CLEAR"] = 1] = "CLEAR";
|
|
3499
|
-
OpTypeEnum[OpTypeEnum["MOVE"] = 2] = "MOVE";
|
|
3500
|
-
OpTypeEnum[OpTypeEnum["PUT"] = 3] = "PUT";
|
|
3501
|
-
OpTypeEnum[OpTypeEnum["REMOVE"] = 4] = "REMOVE";
|
|
3502
|
-
})(exports.OpTypeEnum || (exports.OpTypeEnum = {}));
|
|
3503
|
-
/**
|
|
3504
|
-
* Used internally for sync buckets.
|
|
3505
|
-
*/
|
|
3506
|
-
class OpType {
|
|
3507
|
-
value;
|
|
3508
|
-
static fromJSON(jsonValue) {
|
|
3509
|
-
return new OpType(exports.OpTypeEnum[jsonValue]);
|
|
3510
|
-
}
|
|
3511
|
-
constructor(value) {
|
|
3512
|
-
this.value = value;
|
|
3513
|
-
}
|
|
3514
|
-
toJSON() {
|
|
3515
|
-
return Object.entries(exports.OpTypeEnum).find(([, value]) => value === this.value)[0];
|
|
3516
|
-
}
|
|
3517
|
-
}
|
|
3518
|
-
|
|
3519
|
-
class OplogEntry {
|
|
3520
|
-
op_id;
|
|
3521
|
-
op;
|
|
3522
|
-
checksum;
|
|
3523
|
-
subkey;
|
|
3524
|
-
object_type;
|
|
3525
|
-
object_id;
|
|
3526
|
-
data;
|
|
3527
|
-
static fromRow(row) {
|
|
3528
|
-
return new OplogEntry(row.op_id, OpType.fromJSON(row.op), row.checksum, row.subkey, row.object_type, row.object_id, row.data);
|
|
3529
|
-
}
|
|
3530
|
-
constructor(op_id, op, checksum, subkey, object_type, object_id, data) {
|
|
3531
|
-
this.op_id = op_id;
|
|
3532
|
-
this.op = op;
|
|
3533
|
-
this.checksum = checksum;
|
|
3534
|
-
this.subkey = subkey;
|
|
3535
|
-
this.object_type = object_type;
|
|
3536
|
-
this.object_id = object_id;
|
|
3537
|
-
this.data = data;
|
|
3538
|
-
}
|
|
3539
|
-
toJSON(fixedKeyEncoding = false) {
|
|
3540
|
-
return {
|
|
3541
|
-
op_id: this.op_id,
|
|
3542
|
-
op: this.op.toJSON(),
|
|
3543
|
-
object_type: this.object_type,
|
|
3544
|
-
object_id: this.object_id,
|
|
3545
|
-
checksum: this.checksum,
|
|
3546
|
-
data: this.data,
|
|
3547
|
-
// Older versions of the JS SDK used to always JSON.stringify here. That has always been wrong,
|
|
3548
|
-
// but we need to migrate gradually to not break existing databases.
|
|
3549
|
-
subkey: fixedKeyEncoding ? this.subkey : JSON.stringify(this.subkey)
|
|
3550
|
-
};
|
|
3551
|
-
}
|
|
3552
|
-
}
|
|
3553
|
-
|
|
3554
|
-
class SyncDataBucket {
|
|
3555
|
-
bucket;
|
|
3556
|
-
data;
|
|
3557
|
-
has_more;
|
|
3558
|
-
after;
|
|
3559
|
-
next_after;
|
|
3560
|
-
static fromRow(row) {
|
|
3561
|
-
return new SyncDataBucket(row.bucket, row.data.map((entry) => OplogEntry.fromRow(entry)), row.has_more ?? false, row.after, row.next_after);
|
|
3562
|
-
}
|
|
3563
|
-
constructor(bucket, data,
|
|
3564
|
-
/**
|
|
3565
|
-
* True if the response does not contain all the data for this bucket, and another request must be made.
|
|
3566
|
-
*/
|
|
3567
|
-
has_more,
|
|
3568
|
-
/**
|
|
3569
|
-
* The `after` specified in the request.
|
|
3570
|
-
*/
|
|
3571
|
-
after,
|
|
3572
|
-
/**
|
|
3573
|
-
* Use this for the next request.
|
|
3574
|
-
*/
|
|
3575
|
-
next_after) {
|
|
3576
|
-
this.bucket = bucket;
|
|
3577
|
-
this.data = data;
|
|
3578
|
-
this.has_more = has_more;
|
|
3579
|
-
this.after = after;
|
|
3580
|
-
this.next_after = next_after;
|
|
3581
|
-
}
|
|
3582
|
-
toJSON(fixedKeyEncoding = false) {
|
|
3583
|
-
return {
|
|
3584
|
-
bucket: this.bucket,
|
|
3585
|
-
has_more: this.has_more,
|
|
3586
|
-
after: this.after,
|
|
3587
|
-
next_after: this.next_after,
|
|
3588
|
-
data: this.data.map((entry) => entry.toJSON(fixedKeyEncoding))
|
|
3589
|
-
};
|
|
3590
|
-
}
|
|
3591
|
-
}
|
|
3592
|
-
|
|
3593
3503
|
var buffer = {};
|
|
3594
3504
|
|
|
3595
3505
|
var base64Js = {};
|
|
@@ -10745,7 +10655,7 @@ function requireDist () {
|
|
|
10745
10655
|
|
|
10746
10656
|
var distExports = requireDist();
|
|
10747
10657
|
|
|
10748
|
-
var version = "1.
|
|
10658
|
+
var version = "1.53.0";
|
|
10749
10659
|
var PACKAGE = {
|
|
10750
10660
|
version: version};
|
|
10751
10661
|
|
|
@@ -10915,22 +10825,6 @@ const doneResult = { done: true, value: undefined };
|
|
|
10915
10825
|
function valueResult(value) {
|
|
10916
10826
|
return { done: false, value };
|
|
10917
10827
|
}
|
|
10918
|
-
/**
|
|
10919
|
-
* A variant of {@link Array.map} for async iterators.
|
|
10920
|
-
*/
|
|
10921
|
-
function map(source, map) {
|
|
10922
|
-
return {
|
|
10923
|
-
next: async () => {
|
|
10924
|
-
const value = await source.next();
|
|
10925
|
-
if (value.done) {
|
|
10926
|
-
return value;
|
|
10927
|
-
}
|
|
10928
|
-
else {
|
|
10929
|
-
return { value: map(value.value) };
|
|
10930
|
-
}
|
|
10931
|
-
}
|
|
10932
|
-
};
|
|
10933
|
-
}
|
|
10934
10828
|
/**
|
|
10935
10829
|
* Expands a source async iterator by allowing to inject events asynchronously.
|
|
10936
10830
|
*
|
|
@@ -11302,22 +11196,12 @@ class AbstractRemote {
|
|
|
11302
11196
|
* Returns a data stream of sync line data, fetched via RSocket-over-WebSocket.
|
|
11303
11197
|
*
|
|
11304
11198
|
* The only mechanism to abort the returned stream is to use the abort signal in {@link SocketSyncStreamOptions}.
|
|
11305
|
-
*
|
|
11306
|
-
* @param bson A BSON encoder and decoder. When set, the data stream will be requested with a BSON payload
|
|
11307
|
-
* (required for compatibility with older sync services).
|
|
11308
11199
|
*/
|
|
11309
|
-
async socketStreamRaw(options
|
|
11200
|
+
async socketStreamRaw(options) {
|
|
11310
11201
|
const { path, fetchStrategy = exports.FetchStrategy.Buffered } = options;
|
|
11311
|
-
const mimeType =
|
|
11202
|
+
const mimeType = 'application/json';
|
|
11312
11203
|
function toBuffer(js) {
|
|
11313
|
-
|
|
11314
|
-
if (bson != null) {
|
|
11315
|
-
contents = bson.serialize(js);
|
|
11316
|
-
}
|
|
11317
|
-
else {
|
|
11318
|
-
contents = JSON.stringify(js);
|
|
11319
|
-
}
|
|
11320
|
-
return bufferExports.Buffer.from(contents);
|
|
11204
|
+
return bufferExports.Buffer.from(JSON.stringify(js));
|
|
11321
11205
|
}
|
|
11322
11206
|
const syncQueueRequestSize = fetchStrategy == exports.FetchStrategy.Buffered ? 10 : 1;
|
|
11323
11207
|
const request = await this.buildRequest(path);
|
|
@@ -11618,31 +11502,8 @@ function coreStatusToJs(status) {
|
|
|
11618
11502
|
priorityStatusEntries: status.priority_status.map(priorityToJs)
|
|
11619
11503
|
};
|
|
11620
11504
|
}
|
|
11621
|
-
|
|
11622
|
-
|
|
11623
|
-
return line.data != null;
|
|
11624
|
-
}
|
|
11625
|
-
function isStreamingKeepalive(line) {
|
|
11626
|
-
return line.token_expires_in != null;
|
|
11627
|
-
}
|
|
11628
|
-
function isStreamingSyncCheckpoint(line) {
|
|
11629
|
-
return line.checkpoint != null;
|
|
11630
|
-
}
|
|
11631
|
-
function isStreamingSyncCheckpointComplete(line) {
|
|
11632
|
-
return line.checkpoint_complete != null;
|
|
11633
|
-
}
|
|
11634
|
-
function isStreamingSyncCheckpointPartiallyComplete(line) {
|
|
11635
|
-
return line.partial_checkpoint_complete != null;
|
|
11636
|
-
}
|
|
11637
|
-
function isStreamingSyncCheckpointDiff(line) {
|
|
11638
|
-
return line.checkpoint_diff != null;
|
|
11639
|
-
}
|
|
11640
|
-
function isContinueCheckpointRequest(request) {
|
|
11641
|
-
return (Array.isArray(request.buckets) &&
|
|
11642
|
-
typeof request.checkpoint_token == 'string');
|
|
11643
|
-
}
|
|
11644
|
-
function isSyncNewCheckpointRequest(request) {
|
|
11645
|
-
return typeof request.request_checkpoint == 'object';
|
|
11505
|
+
function isInterruptingInstruction(instruction) {
|
|
11506
|
+
return 'EstablishSyncStream' in instruction || 'CloseSyncStream' in instruction;
|
|
11646
11507
|
}
|
|
11647
11508
|
|
|
11648
11509
|
exports.LockType = void 0;
|
|
@@ -11657,35 +11518,21 @@ exports.SyncStreamConnectionMethod = void 0;
|
|
|
11657
11518
|
})(exports.SyncStreamConnectionMethod || (exports.SyncStreamConnectionMethod = {}));
|
|
11658
11519
|
exports.SyncClientImplementation = void 0;
|
|
11659
11520
|
(function (SyncClientImplementation) {
|
|
11660
|
-
/**
|
|
11661
|
-
* Decodes and handles sync lines received from the sync service in JavaScript.
|
|
11662
|
-
*
|
|
11663
|
-
* This is the default option.
|
|
11664
|
-
*
|
|
11665
|
-
* @deprecated We recommend the {@link RUST} client implementation for all apps. If you have issues with
|
|
11666
|
-
* the Rust client, please file an issue or reach out to us. The JavaScript client will be removed in a future
|
|
11667
|
-
* version of the PowerSync SDK.
|
|
11668
|
-
*/
|
|
11669
|
-
SyncClientImplementation["JAVASCRIPT"] = "js";
|
|
11670
11521
|
/**
|
|
11671
11522
|
* This implementation offloads the sync line decoding and handling into the PowerSync
|
|
11672
11523
|
* core extension.
|
|
11673
11524
|
*
|
|
11674
|
-
* This
|
|
11675
|
-
* recommended client implementation for all apps.
|
|
11525
|
+
* This is the only option, as an older JavaScript client implementation has been removed from the SDK.
|
|
11676
11526
|
*
|
|
11677
11527
|
* ## Compatibility warning
|
|
11678
11528
|
*
|
|
11679
11529
|
* The Rust sync client stores sync data in a format that is slightly different than the one used
|
|
11680
|
-
* by the old
|
|
11681
|
-
*
|
|
11682
|
-
* Further, the {@link JAVASCRIPT} client in recent versions of the PowerSync JS SDK (starting from
|
|
11683
|
-
* the version introducing {@link RUST} as an option) also supports the new format, so you can switch
|
|
11684
|
-
* back to {@link JAVASCRIPT} later.
|
|
11530
|
+
* by the old JavaScript client. When adopting the {@link RUST} client on existing databases, the PowerSync SDK will
|
|
11531
|
+
* migrate the format automatically.
|
|
11685
11532
|
*
|
|
11686
|
-
*
|
|
11687
|
-
*
|
|
11688
|
-
*
|
|
11533
|
+
* SDK versions supporting both the JavaScript and the Rust client support both formats with the JavaScript client
|
|
11534
|
+
* implementaiton. However, downgrading to an SDK version that only supports the JavaScript client would not be
|
|
11535
|
+
* possible anymore. Problematic SDK versions have been released before 2025-06-09.
|
|
11689
11536
|
*/
|
|
11690
11537
|
SyncClientImplementation["RUST"] = "rust";
|
|
11691
11538
|
})(exports.SyncClientImplementation || (exports.SyncClientImplementation = {}));
|
|
@@ -11708,13 +11555,7 @@ const DEFAULT_STREAM_CONNECTION_OPTIONS = {
|
|
|
11708
11555
|
serializedSchema: undefined,
|
|
11709
11556
|
includeDefaultStreams: true
|
|
11710
11557
|
};
|
|
11711
|
-
// The priority we assume when we receive checkpoint lines where no priority is set.
|
|
11712
|
-
// This is the default priority used by the sync service, but can be set to an arbitrary
|
|
11713
|
-
// value since sync services without priorities also won't send partial sync completion
|
|
11714
|
-
// messages.
|
|
11715
|
-
const FALLBACK_PRIORITY = 3;
|
|
11716
11558
|
class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
11717
|
-
_lastSyncedAt;
|
|
11718
11559
|
options;
|
|
11719
11560
|
abortController;
|
|
11720
11561
|
// In rare cases, mostly for tests, uploads can be triggered without being properly connected.
|
|
@@ -11724,6 +11565,7 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
11724
11565
|
streamingSyncPromise;
|
|
11725
11566
|
logger;
|
|
11726
11567
|
activeStreams;
|
|
11568
|
+
connectionMayHaveChanged = false;
|
|
11727
11569
|
isUploadingCrud = false;
|
|
11728
11570
|
notifyCompletedUploads;
|
|
11729
11571
|
handleActiveStreamsChange;
|
|
@@ -11803,9 +11645,6 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
11803
11645
|
this.crudUpdateListener = undefined;
|
|
11804
11646
|
this.uploadAbortController?.abort();
|
|
11805
11647
|
}
|
|
11806
|
-
async hasCompletedSync() {
|
|
11807
|
-
return this.options.adapter.hasCompletedSync();
|
|
11808
|
-
}
|
|
11809
11648
|
async getWriteCheckpoint() {
|
|
11810
11649
|
const clientId = await this.options.adapter.getClientId();
|
|
11811
11650
|
let path = `/write-checkpoint2.json?client_id=${clientId}`;
|
|
@@ -11887,7 +11726,7 @@ The next upload iteration will be delayed.`);
|
|
|
11887
11726
|
});
|
|
11888
11727
|
}
|
|
11889
11728
|
}
|
|
11890
|
-
this.uploadAbortController =
|
|
11729
|
+
this.uploadAbortController = undefined;
|
|
11891
11730
|
}
|
|
11892
11731
|
});
|
|
11893
11732
|
}
|
|
@@ -12003,6 +11842,11 @@ The next upload iteration will be delayed.`);
|
|
|
12003
11842
|
shouldDelayRetry = false;
|
|
12004
11843
|
// A disconnect was requested, we should not delay since there is no explicit retry
|
|
12005
11844
|
}
|
|
11845
|
+
else if (this.connectionMayHaveChanged && ex.message?.indexOf('No iteration is active') >= 0) {
|
|
11846
|
+
this.connectionMayHaveChanged = false;
|
|
11847
|
+
this.logger.info('Sync error after changed connection, retrying immediately');
|
|
11848
|
+
shouldDelayRetry = false;
|
|
11849
|
+
}
|
|
12006
11850
|
else {
|
|
12007
11851
|
this.logger.error(ex);
|
|
12008
11852
|
}
|
|
@@ -12033,17 +11877,14 @@ The next upload iteration will be delayed.`);
|
|
|
12033
11877
|
// Mark as disconnected if here
|
|
12034
11878
|
this.updateSyncStatus({ connected: false, connecting: false });
|
|
12035
11879
|
}
|
|
12036
|
-
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12040
|
-
|
|
12041
|
-
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
localDescriptions.set(entry.bucket, null);
|
|
12045
|
-
}
|
|
12046
|
-
return [req, localDescriptions];
|
|
11880
|
+
markConnectionMayHaveChanged() {
|
|
11881
|
+
// By setting this field, we'll immediately retry if the next sync event causes an error triggered by us not having
|
|
11882
|
+
// an active sync iteration on the connection in use.
|
|
11883
|
+
this.connectionMayHaveChanged = true;
|
|
11884
|
+
// This triggers a `powersync_control` invocation if a sync iteration is currently active. This is a cheap call to
|
|
11885
|
+
// make when no subscriptions have actually changed, we're mainly interested in this immediately throwing if no
|
|
11886
|
+
// iteration is active. That allows us to reconnect ASAP, instead of having to wait for the next sync line.
|
|
11887
|
+
this.handleActiveStreamsChange?.();
|
|
12047
11888
|
}
|
|
12048
11889
|
/**
|
|
12049
11890
|
* Older versions of the JS SDK used to encode subkeys as JSON in {@link OplogEntry.toJSON}.
|
|
@@ -12084,328 +11925,98 @@ The next upload iteration will be delayed.`);
|
|
|
12084
11925
|
if (invalidMetadata.length > 0) {
|
|
12085
11926
|
throw new Error(`Invalid appMetadata provided. Only string values are allowed. Invalid values: ${invalidMetadata.map(([key, value]) => `${key}: ${value}`).join(', ')}`);
|
|
12086
11927
|
}
|
|
12087
|
-
|
|
12088
|
-
this.
|
|
12089
|
-
if (clientImplementation == exports.SyncClientImplementation.JAVASCRIPT) {
|
|
12090
|
-
await this.legacyStreamingSyncIteration(signal, resolvedOptions);
|
|
12091
|
-
return null;
|
|
12092
|
-
}
|
|
12093
|
-
else {
|
|
12094
|
-
await this.requireKeyFormat(true);
|
|
12095
|
-
return await this.rustSyncIteration(signal, resolvedOptions);
|
|
12096
|
-
}
|
|
11928
|
+
await this.requireKeyFormat(true);
|
|
11929
|
+
return await this.rustSyncIteration(signal, resolvedOptions);
|
|
12097
11930
|
}
|
|
12098
11931
|
});
|
|
12099
11932
|
}
|
|
12100
|
-
|
|
12101
|
-
const { options, connection
|
|
11933
|
+
receiveSyncLines(data) {
|
|
11934
|
+
const { options, connection } = data;
|
|
12102
11935
|
const remote = this.options.remote;
|
|
12103
|
-
|
|
12104
|
-
|
|
12105
|
-
|
|
12106
|
-
else {
|
|
12107
|
-
return await this.options.remote.socketStreamRaw({
|
|
12108
|
-
...options,
|
|
12109
|
-
...{ fetchStrategy: connection.fetchStrategy }
|
|
12110
|
-
}, bson);
|
|
12111
|
-
}
|
|
12112
|
-
}
|
|
12113
|
-
async legacyStreamingSyncIteration(signal, resolvedOptions) {
|
|
12114
|
-
const rawTables = resolvedOptions.serializedSchema?.raw_tables;
|
|
12115
|
-
if (rawTables != null && rawTables.length) {
|
|
12116
|
-
this.logger.warn('Raw tables require the Rust-based sync client. The JS client will ignore them.');
|
|
12117
|
-
}
|
|
12118
|
-
if (this.activeStreams.length) {
|
|
12119
|
-
this.logger.error('Sync streams require `clientImplementation: SyncClientImplementation.RUST` when connecting.');
|
|
12120
|
-
}
|
|
12121
|
-
this.logger.debug('Streaming sync iteration started');
|
|
12122
|
-
this.options.adapter.startSession();
|
|
12123
|
-
let [req, bucketMap] = await this.collectLocalBucketState();
|
|
12124
|
-
let targetCheckpoint = null;
|
|
12125
|
-
// A checkpoint that has been validated but not applied (e.g. due to pending local writes)
|
|
12126
|
-
let pendingValidatedCheckpoint = null;
|
|
12127
|
-
const clientId = await this.options.adapter.getClientId();
|
|
12128
|
-
const usingFixedKeyFormat = await this.requireKeyFormat(false);
|
|
12129
|
-
this.logger.debug('Requesting stream from server');
|
|
12130
|
-
const syncOptions = {
|
|
12131
|
-
path: '/sync/stream',
|
|
12132
|
-
abortSignal: signal,
|
|
12133
|
-
data: {
|
|
12134
|
-
buckets: req,
|
|
12135
|
-
include_checksum: true,
|
|
12136
|
-
raw_data: true,
|
|
12137
|
-
parameters: resolvedOptions.params,
|
|
12138
|
-
app_metadata: resolvedOptions.appMetadata,
|
|
12139
|
-
client_id: clientId
|
|
12140
|
-
}
|
|
12141
|
-
};
|
|
12142
|
-
const bson = await this.options.remote.getBSON();
|
|
12143
|
-
const source = await this.receiveSyncLines({
|
|
12144
|
-
options: syncOptions,
|
|
12145
|
-
connection: resolvedOptions,
|
|
12146
|
-
bson
|
|
12147
|
-
});
|
|
12148
|
-
const stream = injectable(map(source, (line) => {
|
|
12149
|
-
if (typeof line == 'string') {
|
|
12150
|
-
return JSON.parse(line);
|
|
11936
|
+
const openInner = async () => {
|
|
11937
|
+
if (connection.connectionMethod == exports.SyncStreamConnectionMethod.HTTP) {
|
|
11938
|
+
return await remote.fetchStream(options);
|
|
12151
11939
|
}
|
|
12152
11940
|
else {
|
|
12153
|
-
return
|
|
11941
|
+
return await this.options.remote.socketStreamRaw({
|
|
11942
|
+
...options,
|
|
11943
|
+
...{ fetchStrategy: connection.fetchStrategy }
|
|
11944
|
+
});
|
|
12154
11945
|
}
|
|
12155
|
-
}));
|
|
12156
|
-
this.logger.debug('Stream established. Processing events');
|
|
12157
|
-
this.notifyCompletedUploads = () => {
|
|
12158
|
-
stream.inject({ crud_upload_completed: null });
|
|
12159
11946
|
};
|
|
12160
|
-
|
|
12161
|
-
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
if ('crud_upload_completed' in line) {
|
|
12167
|
-
if (pendingValidatedCheckpoint != null) {
|
|
12168
|
-
const { applied, endIteration } = await this.applyCheckpoint(pendingValidatedCheckpoint);
|
|
12169
|
-
if (applied) {
|
|
12170
|
-
pendingValidatedCheckpoint = null;
|
|
12171
|
-
}
|
|
12172
|
-
else if (endIteration) {
|
|
12173
|
-
break;
|
|
12174
|
-
}
|
|
11947
|
+
let inner;
|
|
11948
|
+
let done = false;
|
|
11949
|
+
return {
|
|
11950
|
+
async next() {
|
|
11951
|
+
if (done) {
|
|
11952
|
+
return doneResult;
|
|
12175
11953
|
}
|
|
12176
|
-
|
|
12177
|
-
|
|
12178
|
-
|
|
12179
|
-
|
|
12180
|
-
|
|
12181
|
-
|
|
12182
|
-
this.updateSyncStatus({
|
|
12183
|
-
connected: true
|
|
12184
|
-
});
|
|
12185
|
-
}
|
|
12186
|
-
if (isStreamingSyncCheckpoint(line)) {
|
|
12187
|
-
targetCheckpoint = line.checkpoint;
|
|
12188
|
-
// New checkpoint - existing validated checkpoint is no longer valid
|
|
12189
|
-
pendingValidatedCheckpoint = null;
|
|
12190
|
-
const bucketsToDelete = new Set(bucketMap.keys());
|
|
12191
|
-
const newBuckets = new Map();
|
|
12192
|
-
for (const checksum of line.checkpoint.buckets) {
|
|
12193
|
-
newBuckets.set(checksum.bucket, {
|
|
12194
|
-
name: checksum.bucket,
|
|
12195
|
-
priority: checksum.priority ?? FALLBACK_PRIORITY
|
|
11954
|
+
else if (inner == null) {
|
|
11955
|
+
inner = await openInner();
|
|
11956
|
+
// We're connected here, so we can tell the core extension about it.
|
|
11957
|
+
return valueResult({
|
|
11958
|
+
command: exports.PowerSyncControlCommand.CONNECTION_STATE,
|
|
11959
|
+
payload: 'established'
|
|
12196
11960
|
});
|
|
12197
|
-
bucketsToDelete.delete(checksum.bucket);
|
|
12198
|
-
}
|
|
12199
|
-
if (bucketsToDelete.size > 0) {
|
|
12200
|
-
this.logger.debug('Removing buckets', [...bucketsToDelete]);
|
|
12201
|
-
}
|
|
12202
|
-
bucketMap = newBuckets;
|
|
12203
|
-
await this.options.adapter.removeBuckets([...bucketsToDelete]);
|
|
12204
|
-
await this.options.adapter.setTargetCheckpoint(targetCheckpoint);
|
|
12205
|
-
await this.updateSyncStatusForStartingCheckpoint(targetCheckpoint);
|
|
12206
|
-
}
|
|
12207
|
-
else if (isStreamingSyncCheckpointComplete(line)) {
|
|
12208
|
-
const result = await this.applyCheckpoint(targetCheckpoint);
|
|
12209
|
-
if (result.endIteration) {
|
|
12210
|
-
return;
|
|
12211
|
-
}
|
|
12212
|
-
else if (!result.applied) {
|
|
12213
|
-
// "Could not apply checkpoint due to local data". We need to retry after
|
|
12214
|
-
// finishing uploads.
|
|
12215
|
-
pendingValidatedCheckpoint = targetCheckpoint;
|
|
12216
11961
|
}
|
|
12217
11962
|
else {
|
|
12218
|
-
|
|
12219
|
-
|
|
12220
|
-
|
|
12221
|
-
|
|
12222
|
-
}
|
|
12223
|
-
else if (isStreamingSyncCheckpointPartiallyComplete(line)) {
|
|
12224
|
-
const priority = line.partial_checkpoint_complete.priority;
|
|
12225
|
-
this.logger.debug('Partial checkpoint complete', priority);
|
|
12226
|
-
const result = await this.options.adapter.syncLocalDatabase(targetCheckpoint, priority);
|
|
12227
|
-
if (!result.checkpointValid) {
|
|
12228
|
-
// This means checksums failed. Start again with a new checkpoint.
|
|
12229
|
-
// TODO: better back-off
|
|
12230
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
12231
|
-
return;
|
|
12232
|
-
}
|
|
12233
|
-
else if (!result.ready) ;
|
|
12234
|
-
else {
|
|
12235
|
-
// We'll keep on downloading, but can report that this priority is synced now.
|
|
12236
|
-
this.logger.debug('partial checkpoint validation succeeded');
|
|
12237
|
-
// All states with a higher priority can be deleted since this partial sync includes them.
|
|
12238
|
-
const priorityStates = this.syncStatus.priorityStatusEntries.filter((s) => s.priority <= priority);
|
|
12239
|
-
priorityStates.push({
|
|
12240
|
-
priority,
|
|
12241
|
-
lastSyncedAt: new Date(),
|
|
12242
|
-
hasSynced: true
|
|
12243
|
-
});
|
|
12244
|
-
this.updateSyncStatus({
|
|
12245
|
-
connected: true,
|
|
12246
|
-
priorityStatusEntries: priorityStates
|
|
12247
|
-
});
|
|
12248
|
-
}
|
|
12249
|
-
}
|
|
12250
|
-
else if (isStreamingSyncCheckpointDiff(line)) {
|
|
12251
|
-
// TODO: It may be faster to just keep track of the diff, instead of the entire checkpoint
|
|
12252
|
-
if (targetCheckpoint == null) {
|
|
12253
|
-
throw new Error('Checkpoint diff without previous checkpoint');
|
|
12254
|
-
}
|
|
12255
|
-
// New checkpoint - existing validated checkpoint is no longer valid
|
|
12256
|
-
pendingValidatedCheckpoint = null;
|
|
12257
|
-
const diff = line.checkpoint_diff;
|
|
12258
|
-
const newBuckets = new Map();
|
|
12259
|
-
for (const checksum of targetCheckpoint.buckets) {
|
|
12260
|
-
newBuckets.set(checksum.bucket, checksum);
|
|
12261
|
-
}
|
|
12262
|
-
for (const checksum of diff.updated_buckets) {
|
|
12263
|
-
newBuckets.set(checksum.bucket, checksum);
|
|
12264
|
-
}
|
|
12265
|
-
for (const bucket of diff.removed_buckets) {
|
|
12266
|
-
newBuckets.delete(bucket);
|
|
12267
|
-
}
|
|
12268
|
-
const newCheckpoint = {
|
|
12269
|
-
last_op_id: diff.last_op_id,
|
|
12270
|
-
buckets: [...newBuckets.values()],
|
|
12271
|
-
write_checkpoint: diff.write_checkpoint
|
|
12272
|
-
};
|
|
12273
|
-
targetCheckpoint = newCheckpoint;
|
|
12274
|
-
await this.updateSyncStatusForStartingCheckpoint(targetCheckpoint);
|
|
12275
|
-
bucketMap = new Map();
|
|
12276
|
-
newBuckets.forEach((checksum, name) => bucketMap.set(name, {
|
|
12277
|
-
name: checksum.bucket,
|
|
12278
|
-
priority: checksum.priority ?? FALLBACK_PRIORITY
|
|
12279
|
-
}));
|
|
12280
|
-
const bucketsToDelete = diff.removed_buckets;
|
|
12281
|
-
if (bucketsToDelete.length > 0) {
|
|
12282
|
-
this.logger.debug('Remove buckets', bucketsToDelete);
|
|
12283
|
-
}
|
|
12284
|
-
await this.options.adapter.removeBuckets(bucketsToDelete);
|
|
12285
|
-
await this.options.adapter.setTargetCheckpoint(targetCheckpoint);
|
|
12286
|
-
}
|
|
12287
|
-
else if (isStreamingSyncData(line)) {
|
|
12288
|
-
const { data } = line;
|
|
12289
|
-
const previousProgress = this.syncStatus.dataFlowStatus.downloadProgress;
|
|
12290
|
-
let updatedProgress = null;
|
|
12291
|
-
if (previousProgress) {
|
|
12292
|
-
updatedProgress = { ...previousProgress };
|
|
12293
|
-
const progressForBucket = updatedProgress[data.bucket];
|
|
12294
|
-
if (progressForBucket) {
|
|
12295
|
-
updatedProgress[data.bucket] = {
|
|
12296
|
-
...progressForBucket,
|
|
12297
|
-
since_last: progressForBucket.since_last + data.data.length
|
|
12298
|
-
};
|
|
11963
|
+
const event = await inner.next();
|
|
11964
|
+
if (event.done) {
|
|
11965
|
+
done = true;
|
|
11966
|
+
return valueResult({ command: exports.PowerSyncControlCommand.CONNECTION_STATE, payload: 'end' });
|
|
12299
11967
|
}
|
|
12300
|
-
|
|
12301
|
-
|
|
12302
|
-
|
|
12303
|
-
|
|
12304
|
-
|
|
11968
|
+
else {
|
|
11969
|
+
return valueResult({
|
|
11970
|
+
command: typeof event.value == 'string'
|
|
11971
|
+
? exports.PowerSyncControlCommand.PROCESS_TEXT_LINE
|
|
11972
|
+
: exports.PowerSyncControlCommand.PROCESS_BSON_LINE,
|
|
11973
|
+
payload: event.value
|
|
11974
|
+
});
|
|
12305
11975
|
}
|
|
12306
|
-
});
|
|
12307
|
-
await this.options.adapter.saveSyncData({ buckets: [SyncDataBucket.fromRow(data)] }, usingFixedKeyFormat);
|
|
12308
|
-
}
|
|
12309
|
-
else if (isStreamingKeepalive(line)) {
|
|
12310
|
-
const remaining_seconds = line.token_expires_in;
|
|
12311
|
-
if (remaining_seconds == 0) {
|
|
12312
|
-
// Connection would be closed automatically right after this
|
|
12313
|
-
this.logger.debug('Token expiring; reconnect');
|
|
12314
|
-
/**
|
|
12315
|
-
* For a rare case where the backend connector does not update the token
|
|
12316
|
-
* (uses the same one), this should have some delay.
|
|
12317
|
-
*/
|
|
12318
|
-
await this.delayRetry();
|
|
12319
|
-
return;
|
|
12320
|
-
}
|
|
12321
|
-
else if (remaining_seconds < 30) {
|
|
12322
|
-
this.logger.debug('Token will expire soon; reconnect');
|
|
12323
|
-
// Pre-emptively refresh the token
|
|
12324
|
-
this.options.remote.invalidateCredentials();
|
|
12325
|
-
return;
|
|
12326
11976
|
}
|
|
12327
|
-
this.triggerCrudUpload();
|
|
12328
|
-
}
|
|
12329
|
-
else {
|
|
12330
|
-
this.logger.debug('Received unknown sync line', line);
|
|
12331
11977
|
}
|
|
12332
|
-
}
|
|
12333
|
-
this.logger.debug('Stream input empty');
|
|
12334
|
-
// Connection closed. Likely due to auth issue.
|
|
12335
|
-
return;
|
|
11978
|
+
};
|
|
12336
11979
|
}
|
|
12337
11980
|
async rustSyncIteration(signal, resolvedOptions) {
|
|
12338
11981
|
const syncImplementation = this;
|
|
12339
11982
|
const adapter = this.options.adapter;
|
|
12340
11983
|
const remote = this.options.remote;
|
|
12341
|
-
const controller = new AbortController();
|
|
12342
|
-
const abort = () => {
|
|
12343
|
-
return controller.abort(signal.reason);
|
|
12344
|
-
};
|
|
12345
|
-
signal.addEventListener('abort', abort);
|
|
12346
|
-
let receivingLines = null;
|
|
12347
|
-
let hadSyncLine = false;
|
|
12348
11984
|
let hideDisconnectOnRestart = false;
|
|
11985
|
+
let notifyTokenRefreshed;
|
|
12349
11986
|
if (signal.aborted) {
|
|
12350
11987
|
throw new AbortOperation('Connection request has been aborted');
|
|
12351
11988
|
}
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
path: '/sync/stream',
|
|
12359
|
-
abortSignal: controller.signal,
|
|
12360
|
-
data: instr.request
|
|
11989
|
+
function startCommand() {
|
|
11990
|
+
const options = {
|
|
11991
|
+
parameters: resolvedOptions.params,
|
|
11992
|
+
app_metadata: resolvedOptions.appMetadata,
|
|
11993
|
+
active_streams: syncImplementation.activeStreams,
|
|
11994
|
+
include_defaults: resolvedOptions.includeDefaultStreams
|
|
12361
11995
|
};
|
|
12362
|
-
|
|
12363
|
-
options
|
|
12364
|
-
connection: resolvedOptions
|
|
12365
|
-
}), (line) => {
|
|
12366
|
-
if (typeof line == 'string') {
|
|
12367
|
-
return {
|
|
12368
|
-
command: exports.PowerSyncControlCommand.PROCESS_TEXT_LINE,
|
|
12369
|
-
payload: line
|
|
12370
|
-
};
|
|
12371
|
-
}
|
|
12372
|
-
else {
|
|
12373
|
-
return {
|
|
12374
|
-
command: exports.PowerSyncControlCommand.PROCESS_BSON_LINE,
|
|
12375
|
-
payload: line
|
|
12376
|
-
};
|
|
12377
|
-
}
|
|
12378
|
-
}));
|
|
12379
|
-
// The rust client will set connected: true after the first sync line because that's when it gets invoked, but
|
|
12380
|
-
// we're already connected here and can report that.
|
|
12381
|
-
syncImplementation.updateSyncStatus({ connected: true });
|
|
12382
|
-
try {
|
|
12383
|
-
while (true) {
|
|
12384
|
-
let event = await controlInvocations.next();
|
|
12385
|
-
if (event.done) {
|
|
12386
|
-
break;
|
|
12387
|
-
}
|
|
12388
|
-
const line = event.value;
|
|
12389
|
-
await control(line.command, line.payload);
|
|
12390
|
-
if (!hadSyncLine) {
|
|
12391
|
-
syncImplementation.triggerCrudUpload();
|
|
12392
|
-
hadSyncLine = true;
|
|
12393
|
-
}
|
|
12394
|
-
}
|
|
12395
|
-
}
|
|
12396
|
-
finally {
|
|
12397
|
-
abort();
|
|
12398
|
-
signal.removeEventListener('abort', abort);
|
|
11996
|
+
if (resolvedOptions.serializedSchema) {
|
|
11997
|
+
options.schema = resolvedOptions.serializedSchema;
|
|
12399
11998
|
}
|
|
11999
|
+
return invokePowerSyncControl(exports.PowerSyncControlCommand.START, JSON.stringify(options));
|
|
12400
12000
|
}
|
|
12401
12001
|
async function stop() {
|
|
12402
|
-
await
|
|
12002
|
+
const instructions = await invokePowerSyncControl(exports.PowerSyncControlCommand.STOP);
|
|
12003
|
+
for (const instruction of instructions) {
|
|
12004
|
+
// We don't need to handle interrupting instructions since we're unconditionally ending the sync iteration at
|
|
12005
|
+
// this point.
|
|
12006
|
+
if (isInterruptingInstruction(instruction))
|
|
12007
|
+
continue;
|
|
12008
|
+
await handleInstruction(instruction);
|
|
12009
|
+
}
|
|
12403
12010
|
}
|
|
12404
|
-
async function
|
|
12011
|
+
async function invokePowerSyncControl(op, payload) {
|
|
12405
12012
|
const rawResponse = await adapter.control(op, payload ?? null);
|
|
12406
12013
|
const logger = syncImplementation.logger;
|
|
12407
12014
|
logger.trace('powersync_control', op, payload == null || typeof payload == 'string' ? payload : '<bytes>', rawResponse);
|
|
12408
|
-
|
|
12015
|
+
if (op != exports.PowerSyncControlCommand.STOP) {
|
|
12016
|
+
// Evidently we have a working connection here, otherwise powersync_control would have failed.
|
|
12017
|
+
syncImplementation.connectionMayHaveChanged = false;
|
|
12018
|
+
}
|
|
12019
|
+
return JSON.parse(rawResponse);
|
|
12409
12020
|
}
|
|
12410
12021
|
async function handleInstruction(instruction) {
|
|
12411
12022
|
if ('LogLine' in instruction) {
|
|
@@ -12424,13 +12035,6 @@ The next upload iteration will be delayed.`);
|
|
|
12424
12035
|
else if ('UpdateSyncStatus' in instruction) {
|
|
12425
12036
|
syncImplementation.updateSyncStatus(coreStatusToJs(instruction.UpdateSyncStatus.status));
|
|
12426
12037
|
}
|
|
12427
|
-
else if ('EstablishSyncStream' in instruction) {
|
|
12428
|
-
if (receivingLines != null) {
|
|
12429
|
-
// Already connected, this shouldn't happen during a single iteration.
|
|
12430
|
-
throw 'Unexpected request to establish sync stream, already connected';
|
|
12431
|
-
}
|
|
12432
|
-
receivingLines = connect(instruction.EstablishSyncStream);
|
|
12433
|
-
}
|
|
12434
12038
|
else if ('FetchCredentials' in instruction) {
|
|
12435
12039
|
if (instruction.FetchCredentials.did_expire) {
|
|
12436
12040
|
remote.invalidateCredentials();
|
|
@@ -12439,16 +12043,12 @@ The next upload iteration will be delayed.`);
|
|
|
12439
12043
|
remote.invalidateCredentials();
|
|
12440
12044
|
// Restart iteration after the credentials have been refreshed.
|
|
12441
12045
|
remote.fetchCredentials().then((_) => {
|
|
12442
|
-
|
|
12046
|
+
notifyTokenRefreshed?.();
|
|
12443
12047
|
}, (err) => {
|
|
12444
12048
|
syncImplementation.logger.warn('Could not prefetch credentials', err);
|
|
12445
12049
|
});
|
|
12446
12050
|
}
|
|
12447
12051
|
}
|
|
12448
|
-
else if ('CloseSyncStream' in instruction) {
|
|
12449
|
-
controller.abort();
|
|
12450
|
-
hideDisconnectOnRestart = instruction.CloseSyncStream.hide_disconnect;
|
|
12451
|
-
}
|
|
12452
12052
|
else if ('FlushFileSystem' in instruction) ;
|
|
12453
12053
|
else if ('DidCompleteSync' in instruction) {
|
|
12454
12054
|
syncImplementation.updateSyncStatus({
|
|
@@ -12458,101 +12058,83 @@ The next upload iteration will be delayed.`);
|
|
|
12458
12058
|
});
|
|
12459
12059
|
}
|
|
12460
12060
|
}
|
|
12461
|
-
async function handleInstructions(instructions) {
|
|
12462
|
-
for (const instr of instructions) {
|
|
12463
|
-
await handleInstruction(instr);
|
|
12464
|
-
}
|
|
12465
|
-
}
|
|
12466
12061
|
try {
|
|
12467
|
-
const
|
|
12468
|
-
|
|
12469
|
-
|
|
12470
|
-
|
|
12471
|
-
|
|
12472
|
-
|
|
12473
|
-
|
|
12474
|
-
|
|
12062
|
+
const defaultResult = { immediateRestart: false };
|
|
12063
|
+
// Pending sync lines received from the service, as well as local events that trigger a powersync_control
|
|
12064
|
+
// invocation (local events include refreshed tokens and completed uploads).
|
|
12065
|
+
// This is a single data stream so that we can handle all control calls from a single place.
|
|
12066
|
+
let controlInvocations = null;
|
|
12067
|
+
for (const startInstruction of await startCommand()) {
|
|
12068
|
+
if ('EstablishSyncStream' in startInstruction) {
|
|
12069
|
+
const syncOptions = {
|
|
12070
|
+
path: '/sync/stream',
|
|
12071
|
+
abortSignal: signal,
|
|
12072
|
+
data: startInstruction.EstablishSyncStream.request
|
|
12073
|
+
};
|
|
12074
|
+
controlInvocations = injectable(syncImplementation.receiveSyncLines({
|
|
12075
|
+
options: syncOptions,
|
|
12076
|
+
connection: resolvedOptions
|
|
12077
|
+
}));
|
|
12078
|
+
}
|
|
12079
|
+
else if ('CloseSyncStream' in startInstruction) {
|
|
12080
|
+
return defaultResult;
|
|
12081
|
+
}
|
|
12082
|
+
else {
|
|
12083
|
+
await handleInstruction(startInstruction);
|
|
12084
|
+
}
|
|
12475
12085
|
}
|
|
12476
|
-
|
|
12086
|
+
if (controlInvocations == null)
|
|
12087
|
+
return defaultResult;
|
|
12477
12088
|
this.notifyCompletedUploads = () => {
|
|
12478
|
-
controlInvocations
|
|
12089
|
+
controlInvocations.inject({ command: exports.PowerSyncControlCommand.NOTIFY_CRUD_UPLOAD_COMPLETED });
|
|
12479
12090
|
};
|
|
12480
12091
|
this.handleActiveStreamsChange = () => {
|
|
12481
|
-
controlInvocations
|
|
12092
|
+
controlInvocations.inject({
|
|
12482
12093
|
command: exports.PowerSyncControlCommand.UPDATE_SUBSCRIPTIONS,
|
|
12483
12094
|
payload: JSON.stringify(this.activeStreams)
|
|
12484
12095
|
});
|
|
12485
12096
|
};
|
|
12486
|
-
|
|
12097
|
+
notifyTokenRefreshed = () => {
|
|
12098
|
+
controlInvocations.inject({
|
|
12099
|
+
command: exports.PowerSyncControlCommand.NOTIFY_TOKEN_REFRESHED
|
|
12100
|
+
});
|
|
12101
|
+
};
|
|
12102
|
+
let hadSyncLine = false;
|
|
12103
|
+
loop: while (true) {
|
|
12104
|
+
const { done, value } = await controlInvocations.next();
|
|
12105
|
+
if (done)
|
|
12106
|
+
break;
|
|
12107
|
+
if (!hadSyncLine) {
|
|
12108
|
+
// Trigger a local CRUD upload when the first sync line has been received, this allows uploading local changes
|
|
12109
|
+
// that have been made while offline or disconnected.
|
|
12110
|
+
if (value.command == exports.PowerSyncControlCommand.PROCESS_TEXT_LINE ||
|
|
12111
|
+
value.command == exports.PowerSyncControlCommand.PROCESS_BSON_LINE) {
|
|
12112
|
+
hadSyncLine = true;
|
|
12113
|
+
this.triggerCrudUpload?.();
|
|
12114
|
+
}
|
|
12115
|
+
}
|
|
12116
|
+
const instructions = await invokePowerSyncControl(value.command, value.payload);
|
|
12117
|
+
for (const instruction of instructions) {
|
|
12118
|
+
if ('EstablishSyncStream' in instruction) {
|
|
12119
|
+
throw new Error('Received EstablishSyncStream while already connected.');
|
|
12120
|
+
}
|
|
12121
|
+
else if ('CloseSyncStream' in instruction) {
|
|
12122
|
+
hideDisconnectOnRestart = instruction.CloseSyncStream.hide_disconnect;
|
|
12123
|
+
break loop;
|
|
12124
|
+
}
|
|
12125
|
+
else {
|
|
12126
|
+
await handleInstruction(instruction);
|
|
12127
|
+
}
|
|
12128
|
+
}
|
|
12129
|
+
}
|
|
12487
12130
|
}
|
|
12488
12131
|
finally {
|
|
12489
12132
|
this.notifyCompletedUploads = this.handleActiveStreamsChange = undefined;
|
|
12133
|
+
notifyTokenRefreshed = undefined;
|
|
12490
12134
|
await stop();
|
|
12491
12135
|
}
|
|
12492
12136
|
return { immediateRestart: hideDisconnectOnRestart };
|
|
12493
12137
|
}
|
|
12494
|
-
async updateSyncStatusForStartingCheckpoint(checkpoint) {
|
|
12495
|
-
const localProgress = await this.options.adapter.getBucketOperationProgress();
|
|
12496
|
-
const progress = {};
|
|
12497
|
-
let invalidated = false;
|
|
12498
|
-
for (const bucket of checkpoint.buckets) {
|
|
12499
|
-
const savedProgress = localProgress[bucket.bucket];
|
|
12500
|
-
const atLast = savedProgress?.atLast ?? 0;
|
|
12501
|
-
const sinceLast = savedProgress?.sinceLast ?? 0;
|
|
12502
|
-
progress[bucket.bucket] = {
|
|
12503
|
-
// The fallback priority doesn't matter here, but 3 is the one newer versions of the sync service
|
|
12504
|
-
// will use by default.
|
|
12505
|
-
priority: bucket.priority ?? 3,
|
|
12506
|
-
at_last: atLast,
|
|
12507
|
-
since_last: sinceLast,
|
|
12508
|
-
target_count: bucket.count ?? 0
|
|
12509
|
-
};
|
|
12510
|
-
if (bucket.count != null && bucket.count < atLast + sinceLast) {
|
|
12511
|
-
// Either due to a defrag / sync rule deploy or a compaction operation, the size
|
|
12512
|
-
// of the bucket shrank so much that the local ops exceed the ops in the updated
|
|
12513
|
-
// bucket. We can't prossibly report progress in this case (it would overshoot 100%).
|
|
12514
|
-
invalidated = true;
|
|
12515
|
-
}
|
|
12516
|
-
}
|
|
12517
|
-
if (invalidated) {
|
|
12518
|
-
for (const bucket in progress) {
|
|
12519
|
-
const bucketProgress = progress[bucket];
|
|
12520
|
-
bucketProgress.at_last = 0;
|
|
12521
|
-
bucketProgress.since_last = 0;
|
|
12522
|
-
}
|
|
12523
|
-
}
|
|
12524
|
-
this.updateSyncStatus({
|
|
12525
|
-
dataFlow: {
|
|
12526
|
-
downloading: true,
|
|
12527
|
-
downloadProgress: progress
|
|
12528
|
-
}
|
|
12529
|
-
});
|
|
12530
|
-
}
|
|
12531
|
-
async applyCheckpoint(checkpoint) {
|
|
12532
|
-
let result = await this.options.adapter.syncLocalDatabase(checkpoint);
|
|
12533
|
-
if (!result.checkpointValid) {
|
|
12534
|
-
this.logger.debug(`Checksum mismatch in checkpoint ${checkpoint.last_op_id}, will reconnect`);
|
|
12535
|
-
// This means checksums failed. Start again with a new checkpoint.
|
|
12536
|
-
// TODO: better back-off
|
|
12537
|
-
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
12538
|
-
return { applied: false, endIteration: true };
|
|
12539
|
-
}
|
|
12540
|
-
else if (!result.ready) {
|
|
12541
|
-
this.logger.debug(`Could not apply checkpoint ${checkpoint.last_op_id} due to local data. We will retry applying the checkpoint after that upload is completed.`);
|
|
12542
|
-
return { applied: false, endIteration: false };
|
|
12543
|
-
}
|
|
12544
|
-
this.logger.debug(`Applied checkpoint ${checkpoint.last_op_id}`, checkpoint);
|
|
12545
|
-
this.updateSyncStatus({
|
|
12546
|
-
connected: true,
|
|
12547
|
-
lastSyncedAt: new Date(),
|
|
12548
|
-
dataFlow: {
|
|
12549
|
-
downloading: false,
|
|
12550
|
-
downloadProgress: null,
|
|
12551
|
-
downloadError: undefined
|
|
12552
|
-
}
|
|
12553
|
-
});
|
|
12554
|
-
return { applied: true, endIteration: false };
|
|
12555
|
-
}
|
|
12556
12138
|
updateSyncStatus(options) {
|
|
12557
12139
|
const updatedStatus = new SyncStatus({
|
|
12558
12140
|
connected: options.connected ?? this.syncStatus.connected,
|
|
@@ -14091,14 +13673,12 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
14091
13673
|
db;
|
|
14092
13674
|
logger;
|
|
14093
13675
|
tableNames;
|
|
14094
|
-
_hasCompletedSync;
|
|
14095
13676
|
updateListener;
|
|
14096
13677
|
_clientId;
|
|
14097
13678
|
constructor(db, logger = Logger.get('SqliteBucketStorage')) {
|
|
14098
13679
|
super();
|
|
14099
13680
|
this.db = db;
|
|
14100
13681
|
this.logger = logger;
|
|
14101
|
-
this._hasCompletedSync = false;
|
|
14102
13682
|
this.tableNames = new Set();
|
|
14103
13683
|
this.updateListener = db.registerListener({
|
|
14104
13684
|
tablesUpdated: (update) => {
|
|
@@ -14110,7 +13690,6 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
14110
13690
|
});
|
|
14111
13691
|
}
|
|
14112
13692
|
async init() {
|
|
14113
|
-
this._hasCompletedSync = false;
|
|
14114
13693
|
const existingTableRows = await this.db.getAll(`SELECT name FROM sqlite_master WHERE type='table' AND name GLOB 'ps_data_*'`);
|
|
14115
13694
|
for (const row of existingTableRows ?? []) {
|
|
14116
13695
|
this.tableNames.add(row.name);
|
|
@@ -14132,156 +13711,6 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
14132
13711
|
getMaxOpId() {
|
|
14133
13712
|
return MAX_OP_ID;
|
|
14134
13713
|
}
|
|
14135
|
-
/**
|
|
14136
|
-
* Reset any caches.
|
|
14137
|
-
*/
|
|
14138
|
-
startSession() { }
|
|
14139
|
-
async getBucketStates() {
|
|
14140
|
-
const result = await this.db.getAll("SELECT name as bucket, cast(last_op as TEXT) as op_id FROM ps_buckets WHERE pending_delete = 0 AND name != '$local'");
|
|
14141
|
-
return result;
|
|
14142
|
-
}
|
|
14143
|
-
async getBucketOperationProgress() {
|
|
14144
|
-
const rows = await this.db.getAll('SELECT name, count_at_last, count_since_last FROM ps_buckets');
|
|
14145
|
-
return Object.fromEntries(rows.map((r) => [r.name, { atLast: r.count_at_last, sinceLast: r.count_since_last }]));
|
|
14146
|
-
}
|
|
14147
|
-
async saveSyncData(batch, fixedKeyFormat = false) {
|
|
14148
|
-
await this.writeTransaction(async (tx) => {
|
|
14149
|
-
for (const b of batch.buckets) {
|
|
14150
|
-
await tx.execute('INSERT INTO powersync_operations(op, data) VALUES(?, ?)', [
|
|
14151
|
-
'save',
|
|
14152
|
-
JSON.stringify({ buckets: [b.toJSON(fixedKeyFormat)] })
|
|
14153
|
-
]);
|
|
14154
|
-
this.logger.debug(`Saved batch of data for bucket: ${b.bucket}, operations: ${b.data.length}`);
|
|
14155
|
-
}
|
|
14156
|
-
});
|
|
14157
|
-
}
|
|
14158
|
-
async removeBuckets(buckets) {
|
|
14159
|
-
for (const bucket of buckets) {
|
|
14160
|
-
await this.deleteBucket(bucket);
|
|
14161
|
-
}
|
|
14162
|
-
}
|
|
14163
|
-
/**
|
|
14164
|
-
* Mark a bucket for deletion.
|
|
14165
|
-
*/
|
|
14166
|
-
async deleteBucket(bucket) {
|
|
14167
|
-
await this.writeTransaction(async (tx) => {
|
|
14168
|
-
await tx.execute('INSERT INTO powersync_operations(op, data) VALUES(?, ?)', ['delete_bucket', bucket]);
|
|
14169
|
-
});
|
|
14170
|
-
this.logger.debug(`Done deleting bucket ${bucket}`);
|
|
14171
|
-
}
|
|
14172
|
-
async hasCompletedSync() {
|
|
14173
|
-
if (this._hasCompletedSync) {
|
|
14174
|
-
return true;
|
|
14175
|
-
}
|
|
14176
|
-
const r = await this.db.get(`SELECT powersync_last_synced_at() as synced_at`);
|
|
14177
|
-
const completed = r.synced_at != null;
|
|
14178
|
-
if (completed) {
|
|
14179
|
-
this._hasCompletedSync = true;
|
|
14180
|
-
}
|
|
14181
|
-
return completed;
|
|
14182
|
-
}
|
|
14183
|
-
async syncLocalDatabase(checkpoint, priority) {
|
|
14184
|
-
const r = await this.validateChecksums(checkpoint, priority);
|
|
14185
|
-
if (!r.checkpointValid) {
|
|
14186
|
-
this.logger.error('Checksums failed for', r.checkpointFailures);
|
|
14187
|
-
for (const b of r.checkpointFailures ?? []) {
|
|
14188
|
-
await this.deleteBucket(b);
|
|
14189
|
-
}
|
|
14190
|
-
return { ready: false, checkpointValid: false, checkpointFailures: r.checkpointFailures };
|
|
14191
|
-
}
|
|
14192
|
-
if (priority == null) {
|
|
14193
|
-
this.logger.debug(`Validated checksums checkpoint ${checkpoint.last_op_id}`);
|
|
14194
|
-
}
|
|
14195
|
-
else {
|
|
14196
|
-
this.logger.debug(`Validated checksums for partial checkpoint ${checkpoint.last_op_id}, priority ${priority}`);
|
|
14197
|
-
}
|
|
14198
|
-
let buckets = checkpoint.buckets;
|
|
14199
|
-
if (priority !== undefined) {
|
|
14200
|
-
buckets = buckets.filter((b) => hasMatchingPriority(priority, b));
|
|
14201
|
-
}
|
|
14202
|
-
const bucketNames = buckets.map((b) => b.bucket);
|
|
14203
|
-
await this.writeTransaction(async (tx) => {
|
|
14204
|
-
await tx.execute(`UPDATE ps_buckets SET last_op = ? WHERE name IN (SELECT json_each.value FROM json_each(?))`, [
|
|
14205
|
-
checkpoint.last_op_id,
|
|
14206
|
-
JSON.stringify(bucketNames)
|
|
14207
|
-
]);
|
|
14208
|
-
if (priority == null && checkpoint.write_checkpoint) {
|
|
14209
|
-
await tx.execute("UPDATE ps_buckets SET last_op = ? WHERE name = '$local'", [checkpoint.write_checkpoint]);
|
|
14210
|
-
}
|
|
14211
|
-
});
|
|
14212
|
-
const valid = await this.updateObjectsFromBuckets(checkpoint, priority);
|
|
14213
|
-
if (!valid) {
|
|
14214
|
-
return { ready: false, checkpointValid: true };
|
|
14215
|
-
}
|
|
14216
|
-
return {
|
|
14217
|
-
ready: true,
|
|
14218
|
-
checkpointValid: true
|
|
14219
|
-
};
|
|
14220
|
-
}
|
|
14221
|
-
/**
|
|
14222
|
-
* Atomically update the local state to the current checkpoint.
|
|
14223
|
-
*
|
|
14224
|
-
* This includes creating new tables, dropping old tables, and copying data over from the oplog.
|
|
14225
|
-
*/
|
|
14226
|
-
async updateObjectsFromBuckets(checkpoint, priority) {
|
|
14227
|
-
let arg = '';
|
|
14228
|
-
if (priority !== undefined) {
|
|
14229
|
-
const affectedBuckets = [];
|
|
14230
|
-
for (const desc of checkpoint.buckets) {
|
|
14231
|
-
if (hasMatchingPriority(priority, desc)) {
|
|
14232
|
-
affectedBuckets.push(desc.bucket);
|
|
14233
|
-
}
|
|
14234
|
-
}
|
|
14235
|
-
arg = JSON.stringify({ priority, buckets: affectedBuckets });
|
|
14236
|
-
}
|
|
14237
|
-
return this.writeTransaction(async (tx) => {
|
|
14238
|
-
const { insertId: result } = await tx.execute('INSERT INTO powersync_operations(op, data) VALUES(?, ?)', [
|
|
14239
|
-
'sync_local',
|
|
14240
|
-
arg
|
|
14241
|
-
]);
|
|
14242
|
-
if (result == 1) {
|
|
14243
|
-
if (priority == null) {
|
|
14244
|
-
const bucketToCount = Object.fromEntries(checkpoint.buckets.map((b) => [b.bucket, b.count]));
|
|
14245
|
-
// The two parameters could be replaced with one, but: https://github.com/powersync-ja/better-sqlite3/pull/6
|
|
14246
|
-
const jsonBucketCount = JSON.stringify(bucketToCount);
|
|
14247
|
-
await tx.execute("UPDATE ps_buckets SET count_since_last = 0, count_at_last = ?->name WHERE name != '$local' AND ?->name IS NOT NULL", [jsonBucketCount, jsonBucketCount]);
|
|
14248
|
-
}
|
|
14249
|
-
return true;
|
|
14250
|
-
}
|
|
14251
|
-
else {
|
|
14252
|
-
return false;
|
|
14253
|
-
}
|
|
14254
|
-
});
|
|
14255
|
-
}
|
|
14256
|
-
async validateChecksums(checkpoint, priority) {
|
|
14257
|
-
if (priority !== undefined) {
|
|
14258
|
-
// Only validate the buckets within the priority we care about
|
|
14259
|
-
const newBuckets = checkpoint.buckets.filter((cs) => hasMatchingPriority(priority, cs));
|
|
14260
|
-
checkpoint = { ...checkpoint, buckets: newBuckets };
|
|
14261
|
-
}
|
|
14262
|
-
const rs = await this.db.execute('SELECT powersync_validate_checkpoint(?) as result', [
|
|
14263
|
-
JSON.stringify({ ...checkpoint })
|
|
14264
|
-
]);
|
|
14265
|
-
const resultItem = rs.rows?.item(0);
|
|
14266
|
-
if (!resultItem) {
|
|
14267
|
-
return {
|
|
14268
|
-
checkpointValid: false,
|
|
14269
|
-
ready: false,
|
|
14270
|
-
checkpointFailures: []
|
|
14271
|
-
};
|
|
14272
|
-
}
|
|
14273
|
-
const result = JSON.parse(resultItem['result']);
|
|
14274
|
-
if (result['valid']) {
|
|
14275
|
-
return { ready: true, checkpointValid: true };
|
|
14276
|
-
}
|
|
14277
|
-
else {
|
|
14278
|
-
return {
|
|
14279
|
-
checkpointValid: false,
|
|
14280
|
-
ready: false,
|
|
14281
|
-
checkpointFailures: result['failed_buckets']
|
|
14282
|
-
};
|
|
14283
|
-
}
|
|
14284
|
-
}
|
|
14285
13714
|
async updateLocalTarget(cb) {
|
|
14286
13715
|
const rs1 = await this.db.getAll("SELECT target_op FROM ps_buckets WHERE name = '$local' AND target_op = CAST(? as INTEGER)", [MAX_OP_ID]);
|
|
14287
13716
|
if (!rs1.length) {
|
|
@@ -14372,12 +13801,6 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
14372
13801
|
async writeTransaction(callback, options) {
|
|
14373
13802
|
return this.db.writeTransaction(callback, options);
|
|
14374
13803
|
}
|
|
14375
|
-
/**
|
|
14376
|
-
* Set a target checkpoint.
|
|
14377
|
-
*/
|
|
14378
|
-
async setTargetCheckpoint(checkpoint) {
|
|
14379
|
-
// No-op for now
|
|
14380
|
-
}
|
|
14381
13804
|
async control(op, payload) {
|
|
14382
13805
|
return await this.writeTransaction(async (tx) => {
|
|
14383
13806
|
const [[raw]] = await tx.executeRaw('SELECT powersync_control(?, ?)', [op, payload]);
|
|
@@ -14401,20 +13824,6 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
14401
13824
|
}
|
|
14402
13825
|
static _subkeyMigrationKey = 'powersync_js_migrated_subkeys';
|
|
14403
13826
|
}
|
|
14404
|
-
function hasMatchingPriority(priority, bucket) {
|
|
14405
|
-
return bucket.priority != null && bucket.priority <= priority;
|
|
14406
|
-
}
|
|
14407
|
-
|
|
14408
|
-
// TODO JSON
|
|
14409
|
-
class SyncDataBatch {
|
|
14410
|
-
buckets;
|
|
14411
|
-
static fromJSON(json) {
|
|
14412
|
-
return new SyncDataBatch(json.buckets.map((bucket) => SyncDataBucket.fromRow(bucket)));
|
|
14413
|
-
}
|
|
14414
|
-
constructor(buckets) {
|
|
14415
|
-
this.buckets = buckets;
|
|
14416
|
-
}
|
|
14417
|
-
}
|
|
14418
13827
|
|
|
14419
13828
|
/**
|
|
14420
13829
|
* Thrown when an underlying database connection is closed.
|
|
@@ -14474,10 +13883,8 @@ class Schema {
|
|
|
14474
13883
|
* developer instead of automatically by PowerSync.
|
|
14475
13884
|
* Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
|
|
14476
13885
|
* using client-side table and column constraints.
|
|
14477
|
-
* Note that raw tables are only supported when using the new `SyncClientImplementation.rust` sync client.
|
|
14478
13886
|
*
|
|
14479
13887
|
* @param tables An object of (table name, raw table definition) entries.
|
|
14480
|
-
* @experimental Note that the raw tables API is still experimental and may change in the future.
|
|
14481
13888
|
*/
|
|
14482
13889
|
withRawTables(tables) {
|
|
14483
13890
|
for (const [name, rawTableDefinition] of Object.entries(tables)) {
|
|
@@ -14727,13 +14134,9 @@ exports.MAX_OP_ID = MAX_OP_ID;
|
|
|
14727
14134
|
exports.MEMORY_TRIGGER_CLAIM_MANAGER = MEMORY_TRIGGER_CLAIM_MANAGER;
|
|
14728
14135
|
exports.Mutex = Mutex;
|
|
14729
14136
|
exports.OnChangeQueryProcessor = OnChangeQueryProcessor;
|
|
14730
|
-
exports.OpType = OpType;
|
|
14731
|
-
exports.OplogEntry = OplogEntry;
|
|
14732
14137
|
exports.Schema = Schema;
|
|
14733
14138
|
exports.Semaphore = Semaphore;
|
|
14734
14139
|
exports.SqliteBucketStorage = SqliteBucketStorage;
|
|
14735
|
-
exports.SyncDataBatch = SyncDataBatch;
|
|
14736
|
-
exports.SyncDataBucket = SyncDataBucket;
|
|
14737
14140
|
exports.SyncProgress = SyncProgress;
|
|
14738
14141
|
exports.SyncStatus = SyncStatus;
|
|
14739
14142
|
exports.SyncingService = SyncingService;
|
|
@@ -14748,18 +14151,10 @@ exports.createBaseLogger = createBaseLogger;
|
|
|
14748
14151
|
exports.createLogger = createLogger;
|
|
14749
14152
|
exports.extractTableUpdates = extractTableUpdates;
|
|
14750
14153
|
exports.isBatchedUpdateNotification = isBatchedUpdateNotification;
|
|
14751
|
-
exports.isContinueCheckpointRequest = isContinueCheckpointRequest;
|
|
14752
14154
|
exports.isDBAdapter = isDBAdapter;
|
|
14753
14155
|
exports.isPowerSyncDatabaseOptionsWithSettings = isPowerSyncDatabaseOptionsWithSettings;
|
|
14754
14156
|
exports.isSQLOpenFactory = isSQLOpenFactory;
|
|
14755
14157
|
exports.isSQLOpenOptions = isSQLOpenOptions;
|
|
14756
|
-
exports.isStreamingKeepalive = isStreamingKeepalive;
|
|
14757
|
-
exports.isStreamingSyncCheckpoint = isStreamingSyncCheckpoint;
|
|
14758
|
-
exports.isStreamingSyncCheckpointComplete = isStreamingSyncCheckpointComplete;
|
|
14759
|
-
exports.isStreamingSyncCheckpointDiff = isStreamingSyncCheckpointDiff;
|
|
14760
|
-
exports.isStreamingSyncCheckpointPartiallyComplete = isStreamingSyncCheckpointPartiallyComplete;
|
|
14761
|
-
exports.isStreamingSyncData = isStreamingSyncData;
|
|
14762
|
-
exports.isSyncNewCheckpointRequest = isSyncNewCheckpointRequest;
|
|
14763
14158
|
exports.parseQuery = parseQuery;
|
|
14764
14159
|
exports.runOnSchemaChange = runOnSchemaChange;
|
|
14765
14160
|
exports.sanitizeSQL = sanitizeSQL;
|