@powersync/common 1.52.0 → 1.53.1
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 +167 -767
- package/dist/bundle.cjs.map +1 -1
- package/dist/bundle.mjs +168 -756
- package/dist/bundle.mjs.map +1 -1
- package/dist/bundle.node.cjs +167 -767
- package/dist/bundle.node.cjs.map +1 -1
- package/dist/bundle.node.mjs +168 -756
- 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/lib/utils/stream_transform.js +6 -1
- package/lib/utils/stream_transform.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/src/utils/stream_transform.ts +7 -1
- 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.1";
|
|
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
|
*
|
|
@@ -10945,6 +10839,7 @@ function injectable(source) {
|
|
|
10945
10839
|
let waiter = undefined; // An active, waiting next() call.
|
|
10946
10840
|
// A pending upstream event that couldn't be dispatched because inject() has been called before it was resolved.
|
|
10947
10841
|
let pendingSourceEvent = null;
|
|
10842
|
+
let sourceFetchInFlight = false;
|
|
10948
10843
|
let pendingInjectedEvents = [];
|
|
10949
10844
|
const consumeWaiter = () => {
|
|
10950
10845
|
const pending = waiter;
|
|
@@ -10953,6 +10848,7 @@ function injectable(source) {
|
|
|
10953
10848
|
};
|
|
10954
10849
|
const fetchFromSource = () => {
|
|
10955
10850
|
const resolveWaiter = (propagate) => {
|
|
10851
|
+
sourceFetchInFlight = false;
|
|
10956
10852
|
const active = consumeWaiter();
|
|
10957
10853
|
if (active) {
|
|
10958
10854
|
propagate(active);
|
|
@@ -10961,6 +10857,7 @@ function injectable(source) {
|
|
|
10961
10857
|
pendingSourceEvent = propagate;
|
|
10962
10858
|
}
|
|
10963
10859
|
};
|
|
10860
|
+
sourceFetchInFlight = true;
|
|
10964
10861
|
const nextFromSource = source.next();
|
|
10965
10862
|
nextFromSource.then((value) => {
|
|
10966
10863
|
sourceIsDone = value.done == true;
|
|
@@ -10987,7 +10884,9 @@ function injectable(source) {
|
|
|
10987
10884
|
}
|
|
10988
10885
|
// Nothing pending? Fetch from source
|
|
10989
10886
|
waiter = { resolve, reject };
|
|
10990
|
-
|
|
10887
|
+
if (!sourceFetchInFlight) {
|
|
10888
|
+
fetchFromSource();
|
|
10889
|
+
}
|
|
10991
10890
|
});
|
|
10992
10891
|
},
|
|
10993
10892
|
inject: (event) => {
|
|
@@ -11302,22 +11201,12 @@ class AbstractRemote {
|
|
|
11302
11201
|
* Returns a data stream of sync line data, fetched via RSocket-over-WebSocket.
|
|
11303
11202
|
*
|
|
11304
11203
|
* 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
11204
|
*/
|
|
11309
|
-
async socketStreamRaw(options
|
|
11205
|
+
async socketStreamRaw(options) {
|
|
11310
11206
|
const { path, fetchStrategy = exports.FetchStrategy.Buffered } = options;
|
|
11311
|
-
const mimeType =
|
|
11207
|
+
const mimeType = 'application/json';
|
|
11312
11208
|
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);
|
|
11209
|
+
return bufferExports.Buffer.from(JSON.stringify(js));
|
|
11321
11210
|
}
|
|
11322
11211
|
const syncQueueRequestSize = fetchStrategy == exports.FetchStrategy.Buffered ? 10 : 1;
|
|
11323
11212
|
const request = await this.buildRequest(path);
|
|
@@ -11618,31 +11507,8 @@ function coreStatusToJs(status) {
|
|
|
11618
11507
|
priorityStatusEntries: status.priority_status.map(priorityToJs)
|
|
11619
11508
|
};
|
|
11620
11509
|
}
|
|
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';
|
|
11510
|
+
function isInterruptingInstruction(instruction) {
|
|
11511
|
+
return 'EstablishSyncStream' in instruction || 'CloseSyncStream' in instruction;
|
|
11646
11512
|
}
|
|
11647
11513
|
|
|
11648
11514
|
exports.LockType = void 0;
|
|
@@ -11657,35 +11523,21 @@ exports.SyncStreamConnectionMethod = void 0;
|
|
|
11657
11523
|
})(exports.SyncStreamConnectionMethod || (exports.SyncStreamConnectionMethod = {}));
|
|
11658
11524
|
exports.SyncClientImplementation = void 0;
|
|
11659
11525
|
(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
11526
|
/**
|
|
11671
11527
|
* This implementation offloads the sync line decoding and handling into the PowerSync
|
|
11672
11528
|
* core extension.
|
|
11673
11529
|
*
|
|
11674
|
-
* This
|
|
11675
|
-
* recommended client implementation for all apps.
|
|
11530
|
+
* This is the only option, as an older JavaScript client implementation has been removed from the SDK.
|
|
11676
11531
|
*
|
|
11677
11532
|
* ## Compatibility warning
|
|
11678
11533
|
*
|
|
11679
11534
|
* 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.
|
|
11535
|
+
* by the old JavaScript client. When adopting the {@link RUST} client on existing databases, the PowerSync SDK will
|
|
11536
|
+
* migrate the format automatically.
|
|
11685
11537
|
*
|
|
11686
|
-
*
|
|
11687
|
-
*
|
|
11688
|
-
*
|
|
11538
|
+
* SDK versions supporting both the JavaScript and the Rust client support both formats with the JavaScript client
|
|
11539
|
+
* implementaiton. However, downgrading to an SDK version that only supports the JavaScript client would not be
|
|
11540
|
+
* possible anymore. Problematic SDK versions have been released before 2025-06-09.
|
|
11689
11541
|
*/
|
|
11690
11542
|
SyncClientImplementation["RUST"] = "rust";
|
|
11691
11543
|
})(exports.SyncClientImplementation || (exports.SyncClientImplementation = {}));
|
|
@@ -11708,13 +11560,7 @@ const DEFAULT_STREAM_CONNECTION_OPTIONS = {
|
|
|
11708
11560
|
serializedSchema: undefined,
|
|
11709
11561
|
includeDefaultStreams: true
|
|
11710
11562
|
};
|
|
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
11563
|
class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
11717
|
-
_lastSyncedAt;
|
|
11718
11564
|
options;
|
|
11719
11565
|
abortController;
|
|
11720
11566
|
// In rare cases, mostly for tests, uploads can be triggered without being properly connected.
|
|
@@ -11724,6 +11570,7 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
11724
11570
|
streamingSyncPromise;
|
|
11725
11571
|
logger;
|
|
11726
11572
|
activeStreams;
|
|
11573
|
+
connectionMayHaveChanged = false;
|
|
11727
11574
|
isUploadingCrud = false;
|
|
11728
11575
|
notifyCompletedUploads;
|
|
11729
11576
|
handleActiveStreamsChange;
|
|
@@ -11803,9 +11650,6 @@ class AbstractStreamingSyncImplementation extends BaseObserver {
|
|
|
11803
11650
|
this.crudUpdateListener = undefined;
|
|
11804
11651
|
this.uploadAbortController?.abort();
|
|
11805
11652
|
}
|
|
11806
|
-
async hasCompletedSync() {
|
|
11807
|
-
return this.options.adapter.hasCompletedSync();
|
|
11808
|
-
}
|
|
11809
11653
|
async getWriteCheckpoint() {
|
|
11810
11654
|
const clientId = await this.options.adapter.getClientId();
|
|
11811
11655
|
let path = `/write-checkpoint2.json?client_id=${clientId}`;
|
|
@@ -11887,7 +11731,7 @@ The next upload iteration will be delayed.`);
|
|
|
11887
11731
|
});
|
|
11888
11732
|
}
|
|
11889
11733
|
}
|
|
11890
|
-
this.uploadAbortController =
|
|
11734
|
+
this.uploadAbortController = undefined;
|
|
11891
11735
|
}
|
|
11892
11736
|
});
|
|
11893
11737
|
}
|
|
@@ -12003,6 +11847,11 @@ The next upload iteration will be delayed.`);
|
|
|
12003
11847
|
shouldDelayRetry = false;
|
|
12004
11848
|
// A disconnect was requested, we should not delay since there is no explicit retry
|
|
12005
11849
|
}
|
|
11850
|
+
else if (this.connectionMayHaveChanged && ex.message?.indexOf('No iteration is active') >= 0) {
|
|
11851
|
+
this.connectionMayHaveChanged = false;
|
|
11852
|
+
this.logger.info('Sync error after changed connection, retrying immediately');
|
|
11853
|
+
shouldDelayRetry = false;
|
|
11854
|
+
}
|
|
12006
11855
|
else {
|
|
12007
11856
|
this.logger.error(ex);
|
|
12008
11857
|
}
|
|
@@ -12033,17 +11882,14 @@ The next upload iteration will be delayed.`);
|
|
|
12033
11882
|
// Mark as disconnected if here
|
|
12034
11883
|
this.updateSyncStatus({ connected: false, connecting: false });
|
|
12035
11884
|
}
|
|
12036
|
-
|
|
12037
|
-
|
|
12038
|
-
|
|
12039
|
-
|
|
12040
|
-
|
|
12041
|
-
|
|
12042
|
-
|
|
12043
|
-
|
|
12044
|
-
localDescriptions.set(entry.bucket, null);
|
|
12045
|
-
}
|
|
12046
|
-
return [req, localDescriptions];
|
|
11885
|
+
markConnectionMayHaveChanged() {
|
|
11886
|
+
// By setting this field, we'll immediately retry if the next sync event causes an error triggered by us not having
|
|
11887
|
+
// an active sync iteration on the connection in use.
|
|
11888
|
+
this.connectionMayHaveChanged = true;
|
|
11889
|
+
// This triggers a `powersync_control` invocation if a sync iteration is currently active. This is a cheap call to
|
|
11890
|
+
// make when no subscriptions have actually changed, we're mainly interested in this immediately throwing if no
|
|
11891
|
+
// iteration is active. That allows us to reconnect ASAP, instead of having to wait for the next sync line.
|
|
11892
|
+
this.handleActiveStreamsChange?.();
|
|
12047
11893
|
}
|
|
12048
11894
|
/**
|
|
12049
11895
|
* Older versions of the JS SDK used to encode subkeys as JSON in {@link OplogEntry.toJSON}.
|
|
@@ -12084,328 +11930,98 @@ The next upload iteration will be delayed.`);
|
|
|
12084
11930
|
if (invalidMetadata.length > 0) {
|
|
12085
11931
|
throw new Error(`Invalid appMetadata provided. Only string values are allowed. Invalid values: ${invalidMetadata.map(([key, value]) => `${key}: ${value}`).join(', ')}`);
|
|
12086
11932
|
}
|
|
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
|
-
}
|
|
11933
|
+
await this.requireKeyFormat(true);
|
|
11934
|
+
return await this.rustSyncIteration(signal, resolvedOptions);
|
|
12097
11935
|
}
|
|
12098
11936
|
});
|
|
12099
11937
|
}
|
|
12100
|
-
|
|
12101
|
-
const { options, connection
|
|
11938
|
+
receiveSyncLines(data) {
|
|
11939
|
+
const { options, connection } = data;
|
|
12102
11940
|
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);
|
|
11941
|
+
const openInner = async () => {
|
|
11942
|
+
if (connection.connectionMethod == exports.SyncStreamConnectionMethod.HTTP) {
|
|
11943
|
+
return await remote.fetchStream(options);
|
|
12151
11944
|
}
|
|
12152
11945
|
else {
|
|
12153
|
-
return
|
|
11946
|
+
return await this.options.remote.socketStreamRaw({
|
|
11947
|
+
...options,
|
|
11948
|
+
...{ fetchStrategy: connection.fetchStrategy }
|
|
11949
|
+
});
|
|
12154
11950
|
}
|
|
12155
|
-
}));
|
|
12156
|
-
this.logger.debug('Stream established. Processing events');
|
|
12157
|
-
this.notifyCompletedUploads = () => {
|
|
12158
|
-
stream.inject({ crud_upload_completed: null });
|
|
12159
11951
|
};
|
|
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
|
-
}
|
|
11952
|
+
let inner;
|
|
11953
|
+
let done = false;
|
|
11954
|
+
return {
|
|
11955
|
+
async next() {
|
|
11956
|
+
if (done) {
|
|
11957
|
+
return doneResult;
|
|
12175
11958
|
}
|
|
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
|
|
11959
|
+
else if (inner == null) {
|
|
11960
|
+
inner = await openInner();
|
|
11961
|
+
// We're connected here, so we can tell the core extension about it.
|
|
11962
|
+
return valueResult({
|
|
11963
|
+
command: exports.PowerSyncControlCommand.CONNECTION_STATE,
|
|
11964
|
+
payload: 'established'
|
|
12196
11965
|
});
|
|
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
11966
|
}
|
|
12217
11967
|
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
|
-
};
|
|
11968
|
+
const event = await inner.next();
|
|
11969
|
+
if (event.done) {
|
|
11970
|
+
done = true;
|
|
11971
|
+
return valueResult({ command: exports.PowerSyncControlCommand.CONNECTION_STATE, payload: 'end' });
|
|
12299
11972
|
}
|
|
12300
|
-
|
|
12301
|
-
|
|
12302
|
-
|
|
12303
|
-
|
|
12304
|
-
|
|
11973
|
+
else {
|
|
11974
|
+
return valueResult({
|
|
11975
|
+
command: typeof event.value == 'string'
|
|
11976
|
+
? exports.PowerSyncControlCommand.PROCESS_TEXT_LINE
|
|
11977
|
+
: exports.PowerSyncControlCommand.PROCESS_BSON_LINE,
|
|
11978
|
+
payload: event.value
|
|
11979
|
+
});
|
|
12305
11980
|
}
|
|
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
11981
|
}
|
|
12327
|
-
this.triggerCrudUpload();
|
|
12328
|
-
}
|
|
12329
|
-
else {
|
|
12330
|
-
this.logger.debug('Received unknown sync line', line);
|
|
12331
11982
|
}
|
|
12332
|
-
}
|
|
12333
|
-
this.logger.debug('Stream input empty');
|
|
12334
|
-
// Connection closed. Likely due to auth issue.
|
|
12335
|
-
return;
|
|
11983
|
+
};
|
|
12336
11984
|
}
|
|
12337
11985
|
async rustSyncIteration(signal, resolvedOptions) {
|
|
12338
11986
|
const syncImplementation = this;
|
|
12339
11987
|
const adapter = this.options.adapter;
|
|
12340
11988
|
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
11989
|
let hideDisconnectOnRestart = false;
|
|
11990
|
+
let notifyTokenRefreshed;
|
|
12349
11991
|
if (signal.aborted) {
|
|
12350
11992
|
throw new AbortOperation('Connection request has been aborted');
|
|
12351
11993
|
}
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
path: '/sync/stream',
|
|
12359
|
-
abortSignal: controller.signal,
|
|
12360
|
-
data: instr.request
|
|
11994
|
+
function startCommand() {
|
|
11995
|
+
const options = {
|
|
11996
|
+
parameters: resolvedOptions.params,
|
|
11997
|
+
app_metadata: resolvedOptions.appMetadata,
|
|
11998
|
+
active_streams: syncImplementation.activeStreams,
|
|
11999
|
+
include_defaults: resolvedOptions.includeDefaultStreams
|
|
12361
12000
|
};
|
|
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);
|
|
12001
|
+
if (resolvedOptions.serializedSchema) {
|
|
12002
|
+
options.schema = resolvedOptions.serializedSchema;
|
|
12399
12003
|
}
|
|
12004
|
+
return invokePowerSyncControl(exports.PowerSyncControlCommand.START, JSON.stringify(options));
|
|
12400
12005
|
}
|
|
12401
12006
|
async function stop() {
|
|
12402
|
-
await
|
|
12007
|
+
const instructions = await invokePowerSyncControl(exports.PowerSyncControlCommand.STOP);
|
|
12008
|
+
for (const instruction of instructions) {
|
|
12009
|
+
// We don't need to handle interrupting instructions since we're unconditionally ending the sync iteration at
|
|
12010
|
+
// this point.
|
|
12011
|
+
if (isInterruptingInstruction(instruction))
|
|
12012
|
+
continue;
|
|
12013
|
+
await handleInstruction(instruction);
|
|
12014
|
+
}
|
|
12403
12015
|
}
|
|
12404
|
-
async function
|
|
12016
|
+
async function invokePowerSyncControl(op, payload) {
|
|
12405
12017
|
const rawResponse = await adapter.control(op, payload ?? null);
|
|
12406
12018
|
const logger = syncImplementation.logger;
|
|
12407
12019
|
logger.trace('powersync_control', op, payload == null || typeof payload == 'string' ? payload : '<bytes>', rawResponse);
|
|
12408
|
-
|
|
12020
|
+
if (op != exports.PowerSyncControlCommand.STOP) {
|
|
12021
|
+
// Evidently we have a working connection here, otherwise powersync_control would have failed.
|
|
12022
|
+
syncImplementation.connectionMayHaveChanged = false;
|
|
12023
|
+
}
|
|
12024
|
+
return JSON.parse(rawResponse);
|
|
12409
12025
|
}
|
|
12410
12026
|
async function handleInstruction(instruction) {
|
|
12411
12027
|
if ('LogLine' in instruction) {
|
|
@@ -12424,13 +12040,6 @@ The next upload iteration will be delayed.`);
|
|
|
12424
12040
|
else if ('UpdateSyncStatus' in instruction) {
|
|
12425
12041
|
syncImplementation.updateSyncStatus(coreStatusToJs(instruction.UpdateSyncStatus.status));
|
|
12426
12042
|
}
|
|
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
12043
|
else if ('FetchCredentials' in instruction) {
|
|
12435
12044
|
if (instruction.FetchCredentials.did_expire) {
|
|
12436
12045
|
remote.invalidateCredentials();
|
|
@@ -12439,16 +12048,12 @@ The next upload iteration will be delayed.`);
|
|
|
12439
12048
|
remote.invalidateCredentials();
|
|
12440
12049
|
// Restart iteration after the credentials have been refreshed.
|
|
12441
12050
|
remote.fetchCredentials().then((_) => {
|
|
12442
|
-
|
|
12051
|
+
notifyTokenRefreshed?.();
|
|
12443
12052
|
}, (err) => {
|
|
12444
12053
|
syncImplementation.logger.warn('Could not prefetch credentials', err);
|
|
12445
12054
|
});
|
|
12446
12055
|
}
|
|
12447
12056
|
}
|
|
12448
|
-
else if ('CloseSyncStream' in instruction) {
|
|
12449
|
-
controller.abort();
|
|
12450
|
-
hideDisconnectOnRestart = instruction.CloseSyncStream.hide_disconnect;
|
|
12451
|
-
}
|
|
12452
12057
|
else if ('FlushFileSystem' in instruction) ;
|
|
12453
12058
|
else if ('DidCompleteSync' in instruction) {
|
|
12454
12059
|
syncImplementation.updateSyncStatus({
|
|
@@ -12458,101 +12063,83 @@ The next upload iteration will be delayed.`);
|
|
|
12458
12063
|
});
|
|
12459
12064
|
}
|
|
12460
12065
|
}
|
|
12461
|
-
async function handleInstructions(instructions) {
|
|
12462
|
-
for (const instr of instructions) {
|
|
12463
|
-
await handleInstruction(instr);
|
|
12464
|
-
}
|
|
12465
|
-
}
|
|
12466
12066
|
try {
|
|
12467
|
-
const
|
|
12468
|
-
|
|
12469
|
-
|
|
12470
|
-
|
|
12471
|
-
|
|
12472
|
-
|
|
12473
|
-
|
|
12474
|
-
|
|
12067
|
+
const defaultResult = { immediateRestart: false };
|
|
12068
|
+
// Pending sync lines received from the service, as well as local events that trigger a powersync_control
|
|
12069
|
+
// invocation (local events include refreshed tokens and completed uploads).
|
|
12070
|
+
// This is a single data stream so that we can handle all control calls from a single place.
|
|
12071
|
+
let controlInvocations = null;
|
|
12072
|
+
for (const startInstruction of await startCommand()) {
|
|
12073
|
+
if ('EstablishSyncStream' in startInstruction) {
|
|
12074
|
+
const syncOptions = {
|
|
12075
|
+
path: '/sync/stream',
|
|
12076
|
+
abortSignal: signal,
|
|
12077
|
+
data: startInstruction.EstablishSyncStream.request
|
|
12078
|
+
};
|
|
12079
|
+
controlInvocations = injectable(syncImplementation.receiveSyncLines({
|
|
12080
|
+
options: syncOptions,
|
|
12081
|
+
connection: resolvedOptions
|
|
12082
|
+
}));
|
|
12083
|
+
}
|
|
12084
|
+
else if ('CloseSyncStream' in startInstruction) {
|
|
12085
|
+
return defaultResult;
|
|
12086
|
+
}
|
|
12087
|
+
else {
|
|
12088
|
+
await handleInstruction(startInstruction);
|
|
12089
|
+
}
|
|
12475
12090
|
}
|
|
12476
|
-
|
|
12091
|
+
if (controlInvocations == null)
|
|
12092
|
+
return defaultResult;
|
|
12477
12093
|
this.notifyCompletedUploads = () => {
|
|
12478
|
-
controlInvocations
|
|
12094
|
+
controlInvocations.inject({ command: exports.PowerSyncControlCommand.NOTIFY_CRUD_UPLOAD_COMPLETED });
|
|
12479
12095
|
};
|
|
12480
12096
|
this.handleActiveStreamsChange = () => {
|
|
12481
|
-
controlInvocations
|
|
12097
|
+
controlInvocations.inject({
|
|
12482
12098
|
command: exports.PowerSyncControlCommand.UPDATE_SUBSCRIPTIONS,
|
|
12483
12099
|
payload: JSON.stringify(this.activeStreams)
|
|
12484
12100
|
});
|
|
12485
12101
|
};
|
|
12486
|
-
|
|
12102
|
+
notifyTokenRefreshed = () => {
|
|
12103
|
+
controlInvocations.inject({
|
|
12104
|
+
command: exports.PowerSyncControlCommand.NOTIFY_TOKEN_REFRESHED
|
|
12105
|
+
});
|
|
12106
|
+
};
|
|
12107
|
+
let hadSyncLine = false;
|
|
12108
|
+
loop: while (true) {
|
|
12109
|
+
const { done, value } = await controlInvocations.next();
|
|
12110
|
+
if (done)
|
|
12111
|
+
break;
|
|
12112
|
+
if (!hadSyncLine) {
|
|
12113
|
+
// Trigger a local CRUD upload when the first sync line has been received, this allows uploading local changes
|
|
12114
|
+
// that have been made while offline or disconnected.
|
|
12115
|
+
if (value.command == exports.PowerSyncControlCommand.PROCESS_TEXT_LINE ||
|
|
12116
|
+
value.command == exports.PowerSyncControlCommand.PROCESS_BSON_LINE) {
|
|
12117
|
+
hadSyncLine = true;
|
|
12118
|
+
this.triggerCrudUpload?.();
|
|
12119
|
+
}
|
|
12120
|
+
}
|
|
12121
|
+
const instructions = await invokePowerSyncControl(value.command, value.payload);
|
|
12122
|
+
for (const instruction of instructions) {
|
|
12123
|
+
if ('EstablishSyncStream' in instruction) {
|
|
12124
|
+
throw new Error('Received EstablishSyncStream while already connected.');
|
|
12125
|
+
}
|
|
12126
|
+
else if ('CloseSyncStream' in instruction) {
|
|
12127
|
+
hideDisconnectOnRestart = instruction.CloseSyncStream.hide_disconnect;
|
|
12128
|
+
break loop;
|
|
12129
|
+
}
|
|
12130
|
+
else {
|
|
12131
|
+
await handleInstruction(instruction);
|
|
12132
|
+
}
|
|
12133
|
+
}
|
|
12134
|
+
}
|
|
12487
12135
|
}
|
|
12488
12136
|
finally {
|
|
12489
12137
|
this.notifyCompletedUploads = this.handleActiveStreamsChange = undefined;
|
|
12138
|
+
notifyTokenRefreshed = undefined;
|
|
12490
12139
|
await stop();
|
|
12491
12140
|
}
|
|
12492
12141
|
return { immediateRestart: hideDisconnectOnRestart };
|
|
12493
12142
|
}
|
|
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
12143
|
updateSyncStatus(options) {
|
|
12557
12144
|
const updatedStatus = new SyncStatus({
|
|
12558
12145
|
connected: options.connected ?? this.syncStatus.connected,
|
|
@@ -14091,14 +13678,12 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
14091
13678
|
db;
|
|
14092
13679
|
logger;
|
|
14093
13680
|
tableNames;
|
|
14094
|
-
_hasCompletedSync;
|
|
14095
13681
|
updateListener;
|
|
14096
13682
|
_clientId;
|
|
14097
13683
|
constructor(db, logger = Logger.get('SqliteBucketStorage')) {
|
|
14098
13684
|
super();
|
|
14099
13685
|
this.db = db;
|
|
14100
13686
|
this.logger = logger;
|
|
14101
|
-
this._hasCompletedSync = false;
|
|
14102
13687
|
this.tableNames = new Set();
|
|
14103
13688
|
this.updateListener = db.registerListener({
|
|
14104
13689
|
tablesUpdated: (update) => {
|
|
@@ -14110,7 +13695,6 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
14110
13695
|
});
|
|
14111
13696
|
}
|
|
14112
13697
|
async init() {
|
|
14113
|
-
this._hasCompletedSync = false;
|
|
14114
13698
|
const existingTableRows = await this.db.getAll(`SELECT name FROM sqlite_master WHERE type='table' AND name GLOB 'ps_data_*'`);
|
|
14115
13699
|
for (const row of existingTableRows ?? []) {
|
|
14116
13700
|
this.tableNames.add(row.name);
|
|
@@ -14132,156 +13716,6 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
14132
13716
|
getMaxOpId() {
|
|
14133
13717
|
return MAX_OP_ID;
|
|
14134
13718
|
}
|
|
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
13719
|
async updateLocalTarget(cb) {
|
|
14286
13720
|
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
13721
|
if (!rs1.length) {
|
|
@@ -14372,12 +13806,6 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
14372
13806
|
async writeTransaction(callback, options) {
|
|
14373
13807
|
return this.db.writeTransaction(callback, options);
|
|
14374
13808
|
}
|
|
14375
|
-
/**
|
|
14376
|
-
* Set a target checkpoint.
|
|
14377
|
-
*/
|
|
14378
|
-
async setTargetCheckpoint(checkpoint) {
|
|
14379
|
-
// No-op for now
|
|
14380
|
-
}
|
|
14381
13809
|
async control(op, payload) {
|
|
14382
13810
|
return await this.writeTransaction(async (tx) => {
|
|
14383
13811
|
const [[raw]] = await tx.executeRaw('SELECT powersync_control(?, ?)', [op, payload]);
|
|
@@ -14401,20 +13829,6 @@ class SqliteBucketStorage extends BaseObserver {
|
|
|
14401
13829
|
}
|
|
14402
13830
|
static _subkeyMigrationKey = 'powersync_js_migrated_subkeys';
|
|
14403
13831
|
}
|
|
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
13832
|
|
|
14419
13833
|
/**
|
|
14420
13834
|
* Thrown when an underlying database connection is closed.
|
|
@@ -14474,10 +13888,8 @@ class Schema {
|
|
|
14474
13888
|
* developer instead of automatically by PowerSync.
|
|
14475
13889
|
* Since raw tables are not backed by JSON, running complex queries on them may be more efficient. Further, they allow
|
|
14476
13890
|
* using client-side table and column constraints.
|
|
14477
|
-
* Note that raw tables are only supported when using the new `SyncClientImplementation.rust` sync client.
|
|
14478
13891
|
*
|
|
14479
13892
|
* @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
13893
|
*/
|
|
14482
13894
|
withRawTables(tables) {
|
|
14483
13895
|
for (const [name, rawTableDefinition] of Object.entries(tables)) {
|
|
@@ -14727,13 +14139,9 @@ exports.MAX_OP_ID = MAX_OP_ID;
|
|
|
14727
14139
|
exports.MEMORY_TRIGGER_CLAIM_MANAGER = MEMORY_TRIGGER_CLAIM_MANAGER;
|
|
14728
14140
|
exports.Mutex = Mutex;
|
|
14729
14141
|
exports.OnChangeQueryProcessor = OnChangeQueryProcessor;
|
|
14730
|
-
exports.OpType = OpType;
|
|
14731
|
-
exports.OplogEntry = OplogEntry;
|
|
14732
14142
|
exports.Schema = Schema;
|
|
14733
14143
|
exports.Semaphore = Semaphore;
|
|
14734
14144
|
exports.SqliteBucketStorage = SqliteBucketStorage;
|
|
14735
|
-
exports.SyncDataBatch = SyncDataBatch;
|
|
14736
|
-
exports.SyncDataBucket = SyncDataBucket;
|
|
14737
14145
|
exports.SyncProgress = SyncProgress;
|
|
14738
14146
|
exports.SyncStatus = SyncStatus;
|
|
14739
14147
|
exports.SyncingService = SyncingService;
|
|
@@ -14748,18 +14156,10 @@ exports.createBaseLogger = createBaseLogger;
|
|
|
14748
14156
|
exports.createLogger = createLogger;
|
|
14749
14157
|
exports.extractTableUpdates = extractTableUpdates;
|
|
14750
14158
|
exports.isBatchedUpdateNotification = isBatchedUpdateNotification;
|
|
14751
|
-
exports.isContinueCheckpointRequest = isContinueCheckpointRequest;
|
|
14752
14159
|
exports.isDBAdapter = isDBAdapter;
|
|
14753
14160
|
exports.isPowerSyncDatabaseOptionsWithSettings = isPowerSyncDatabaseOptionsWithSettings;
|
|
14754
14161
|
exports.isSQLOpenFactory = isSQLOpenFactory;
|
|
14755
14162
|
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
14163
|
exports.parseQuery = parseQuery;
|
|
14764
14164
|
exports.runOnSchemaChange = runOnSchemaChange;
|
|
14765
14165
|
exports.sanitizeSQL = sanitizeSQL;
|