dexie-cloud-addon 4.1.0-alpha.8 → 4.1.0-beta.25
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/modern/DexieCloudOptions.d.ts +3 -0
- package/dist/modern/TSON.d.ts +1 -1
- package/dist/modern/db/entities/PersistedSyncState.d.ts +1 -0
- package/dist/modern/define-ydoc-trigger.d.ts +2 -0
- package/dist/modern/dexie-cloud-addon.d.ts +1 -0
- package/dist/modern/dexie-cloud-addon.js +622 -257
- package/dist/modern/dexie-cloud-addon.js.map +1 -1
- package/dist/modern/dexie-cloud-addon.min.js +1 -1
- package/dist/modern/dexie-cloud-addon.min.js.map +1 -1
- package/dist/modern/getInvitesObservable.d.ts +11 -11
- package/dist/modern/service-worker.js +464 -255
- package/dist/modern/service-worker.js.map +1 -1
- package/dist/modern/service-worker.min.js +1 -1
- package/dist/modern/service-worker.min.js.map +1 -1
- package/dist/modern/yjs/YDexieCloudSyncState.d.ts +0 -1
- package/dist/modern/yjs/applyYMessages.d.ts +5 -1
- package/dist/modern/yjs/reopenDocSignal.d.ts +10 -0
- package/dist/modern/yjs/updateYSyncStates.d.ts +1 -1
- package/dist/umd/DexieCloudOptions.d.ts +3 -0
- package/dist/umd/TSON.d.ts +1 -1
- package/dist/umd/db/entities/PersistedSyncState.d.ts +1 -0
- package/dist/umd/define-ydoc-trigger.d.ts +2 -0
- package/dist/umd/dexie-cloud-addon.d.ts +1 -0
- package/dist/umd/dexie-cloud-addon.js +620 -254
- package/dist/umd/dexie-cloud-addon.js.map +1 -1
- package/dist/umd/dexie-cloud-addon.min.js +1 -1
- package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
- package/dist/umd/getInvitesObservable.d.ts +11 -11
- package/dist/umd/service-worker.js +462 -253
- package/dist/umd/service-worker.js.map +1 -1
- package/dist/umd/service-worker.min.js +1 -1
- package/dist/umd/service-worker.min.js.map +1 -1
- package/dist/umd/yjs/YDexieCloudSyncState.d.ts +0 -1
- package/dist/umd/yjs/applyYMessages.d.ts +5 -1
- package/dist/umd/yjs/reopenDocSignal.d.ts +10 -0
- package/dist/umd/yjs/updateYSyncStates.d.ts +1 -1
- package/package.json +6 -5
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* ==========================================================================
|
|
10
10
|
*
|
|
11
|
-
* Version 4.1.0-
|
|
11
|
+
* Version 4.1.0-beta.25, Wed Dec 04 2024
|
|
12
12
|
*
|
|
13
13
|
* https://dexie.org
|
|
14
14
|
*
|
|
@@ -982,13 +982,20 @@
|
|
|
982
982
|
function encodeYMessage(msg) {
|
|
983
983
|
const encoder = new Encoder();
|
|
984
984
|
writeVarString(encoder, msg.type);
|
|
985
|
-
|
|
986
|
-
|
|
985
|
+
if ('table' in msg)
|
|
986
|
+
writeVarString(encoder, msg.table);
|
|
987
|
+
if ('prop' in msg)
|
|
988
|
+
writeVarString(encoder, msg.prop);
|
|
987
989
|
switch (msg.type) {
|
|
988
990
|
case 'u-ack':
|
|
989
991
|
case 'u-reject':
|
|
990
992
|
writeBigUint64(encoder, BigInt(msg.i));
|
|
991
993
|
break;
|
|
994
|
+
case 'outdated-server-rev':
|
|
995
|
+
break;
|
|
996
|
+
case 'y-complete-sync-done':
|
|
997
|
+
writeVarString(encoder, msg.yServerRev);
|
|
998
|
+
break;
|
|
992
999
|
default:
|
|
993
1000
|
writeAny(encoder, msg.k);
|
|
994
1001
|
switch (msg.type) {
|
|
@@ -1332,6 +1339,12 @@
|
|
|
1332
1339
|
function decodeYMessage(a) {
|
|
1333
1340
|
const decoder = new Decoder(a);
|
|
1334
1341
|
const type = readVarString(decoder);
|
|
1342
|
+
if (type === 'outdated-server-rev') {
|
|
1343
|
+
return { type };
|
|
1344
|
+
}
|
|
1345
|
+
if (type === 'y-complete-sync-done') {
|
|
1346
|
+
return { type, yServerRev: readVarString(decoder) };
|
|
1347
|
+
}
|
|
1335
1348
|
const table = readVarString(decoder);
|
|
1336
1349
|
const prop = readVarString(decoder);
|
|
1337
1350
|
switch (type) {
|
|
@@ -2990,8 +3003,8 @@
|
|
|
2990
3003
|
});
|
|
2991
3004
|
}
|
|
2992
3005
|
function registerPeriodicSyncEvent(db) {
|
|
2993
|
-
var _a;
|
|
2994
3006
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3007
|
+
var _a;
|
|
2995
3008
|
try {
|
|
2996
3009
|
// Register periodicSync event to SW:
|
|
2997
3010
|
// @ts-ignore
|
|
@@ -3202,8 +3215,8 @@
|
|
|
3202
3215
|
}
|
|
3203
3216
|
|
|
3204
3217
|
function loadAccessToken(db) {
|
|
3205
|
-
var _a, _b, _c;
|
|
3206
3218
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3219
|
+
var _a, _b, _c;
|
|
3207
3220
|
const currentUser = yield db.getCurrentUser();
|
|
3208
3221
|
const { accessToken, accessTokenExpiration, refreshToken, refreshTokenExpiration, claims, } = currentUser;
|
|
3209
3222
|
if (!accessToken)
|
|
@@ -3417,10 +3430,10 @@
|
|
|
3417
3430
|
}
|
|
3418
3431
|
|
|
3419
3432
|
// Emulate true-private property db. Why? So it's not stored in DB.
|
|
3420
|
-
const wm$
|
|
3433
|
+
const wm$2 = new WeakMap();
|
|
3421
3434
|
class AuthPersistedContext {
|
|
3422
3435
|
constructor(db, userLogin) {
|
|
3423
|
-
wm$
|
|
3436
|
+
wm$2.set(this, db);
|
|
3424
3437
|
Object.assign(this, userLogin);
|
|
3425
3438
|
}
|
|
3426
3439
|
static load(db, userId) {
|
|
@@ -3437,7 +3450,7 @@
|
|
|
3437
3450
|
}
|
|
3438
3451
|
save() {
|
|
3439
3452
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3440
|
-
const db = wm$
|
|
3453
|
+
const db = wm$2.get(this);
|
|
3441
3454
|
db.table("$logins").put(this);
|
|
3442
3455
|
});
|
|
3443
3456
|
}
|
|
@@ -3475,8 +3488,8 @@
|
|
|
3475
3488
|
}
|
|
3476
3489
|
});
|
|
3477
3490
|
}
|
|
3478
|
-
function _logout(
|
|
3479
|
-
return __awaiter(this,
|
|
3491
|
+
function _logout(db_1) {
|
|
3492
|
+
return __awaiter(this, arguments, void 0, function* (db, { deleteUnsyncedData = false } = {}) {
|
|
3480
3493
|
// Clear the database without emptying configuration options.
|
|
3481
3494
|
const [numUnsynced, loggedOut] = yield db.dx.transaction('rw', db.dx.tables, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
3482
3495
|
// @ts-ignore
|
|
@@ -3524,11 +3537,11 @@
|
|
|
3524
3537
|
|
|
3525
3538
|
function otpFetchTokenCallback(db) {
|
|
3526
3539
|
const { userInteraction } = db.cloud;
|
|
3527
|
-
return function otpAuthenticate(
|
|
3528
|
-
|
|
3529
|
-
|
|
3540
|
+
return function otpAuthenticate(_a) {
|
|
3541
|
+
return __awaiter(this, arguments, void 0, function* ({ public_key, hints }) {
|
|
3542
|
+
var _b;
|
|
3530
3543
|
let tokenRequest;
|
|
3531
|
-
const url = (
|
|
3544
|
+
const url = (_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.databaseUrl;
|
|
3532
3545
|
if (!url)
|
|
3533
3546
|
throw new Error(`No database URL given.`);
|
|
3534
3547
|
if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'demo') {
|
|
@@ -3694,8 +3707,8 @@
|
|
|
3694
3707
|
}
|
|
3695
3708
|
|
|
3696
3709
|
function login(db, hints) {
|
|
3697
|
-
var _a;
|
|
3698
3710
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3711
|
+
var _a;
|
|
3699
3712
|
const currentUser = yield db.getCurrentUser();
|
|
3700
3713
|
const origUserId = currentUser.userId;
|
|
3701
3714
|
if (currentUser.isLoggedIn && (!hints || (!hints.email && !hints.userId))) {
|
|
@@ -3883,8 +3896,8 @@
|
|
|
3883
3896
|
}
|
|
3884
3897
|
}
|
|
3885
3898
|
|
|
3886
|
-
function computeRealmSetHash(
|
|
3887
|
-
return __awaiter(this,
|
|
3899
|
+
function computeRealmSetHash(_a) {
|
|
3900
|
+
return __awaiter(this, arguments, void 0, function* ({ realms, inviteRealms, }) {
|
|
3888
3901
|
const data = JSON.stringify([
|
|
3889
3902
|
...realms.map((realmId) => ({ realmId, accepted: true })),
|
|
3890
3903
|
...inviteRealms.map((realmId) => ({ realmId, accepted: false })),
|
|
@@ -3920,8 +3933,8 @@
|
|
|
3920
3933
|
return concat.apply([], a);
|
|
3921
3934
|
}
|
|
3922
3935
|
|
|
3923
|
-
function listClientChanges(
|
|
3924
|
-
return __awaiter(this,
|
|
3936
|
+
function listClientChanges(mutationTables_1, db_1) {
|
|
3937
|
+
return __awaiter(this, arguments, void 0, function* (mutationTables, db, { since = {}, limit = Infinity } = {}) {
|
|
3925
3938
|
const allMutsOnTables = yield Promise.all(mutationTables.map((mutationTable) => __awaiter(this, void 0, void 0, function* () {
|
|
3926
3939
|
const tableName = getTableFromMutationTable(mutationTable.name);
|
|
3927
3940
|
const lastRevision = since[tableName];
|
|
@@ -4656,8 +4669,8 @@
|
|
|
4656
4669
|
// seconds (given that there is a Ratelimit-Reset header).
|
|
4657
4670
|
let syncRatelimitDelays = new WeakMap();
|
|
4658
4671
|
function checkSyncRateLimitDelay(db) {
|
|
4659
|
-
var _a, _b;
|
|
4660
4672
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4673
|
+
var _a, _b;
|
|
4661
4674
|
const delatMilliseconds = ((_b = (_a = syncRatelimitDelays.get(db)) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) - Date.now();
|
|
4662
4675
|
if (delatMilliseconds > 0) {
|
|
4663
4676
|
console.debug(`Stalling sync request ${delatMilliseconds} ms to spare ratelimits`);
|
|
@@ -4718,6 +4731,7 @@
|
|
|
4718
4731
|
lastPull: syncState
|
|
4719
4732
|
? {
|
|
4720
4733
|
serverRevision: syncState.serverRevision,
|
|
4734
|
+
yServerRevision: syncState.yServerRevision,
|
|
4721
4735
|
realms: syncState.realms,
|
|
4722
4736
|
inviteRealms: syncState.inviteRealms,
|
|
4723
4737
|
}
|
|
@@ -4947,7 +4961,7 @@
|
|
|
4947
4961
|
.toArray();
|
|
4948
4962
|
}
|
|
4949
4963
|
|
|
4950
|
-
function $Y(db) {
|
|
4964
|
+
function $Y$1(db) {
|
|
4951
4965
|
const $Y = db.dx._options.Y;
|
|
4952
4966
|
if (!$Y)
|
|
4953
4967
|
throw new Error('Y library not supplied to Dexie constructor');
|
|
@@ -4977,7 +4991,7 @@
|
|
|
4977
4991
|
for (const table of tablesToSync) {
|
|
4978
4992
|
if (table.schema.yProps) {
|
|
4979
4993
|
for (const yProp of table.schema.yProps) {
|
|
4980
|
-
const Y = $Y(db); // This is how we retrieve the user-provided Y library
|
|
4994
|
+
const Y = $Y$1(db); // This is how we retrieve the user-provided Y library
|
|
4981
4995
|
const yTable = db.table(yProp.updatesTable); // the updates-table for this combo of table+propName
|
|
4982
4996
|
const syncState = (yield yTable.get(DEXIE_CLOUD_SYNCER_ID));
|
|
4983
4997
|
// unsentFrom = the `i` value of updates that aren't yet sent to server (or at least not acked by the server yet)
|
|
@@ -5062,12 +5076,15 @@
|
|
|
5062
5076
|
|
|
5063
5077
|
function applyYServerMessages(yMessages, db) {
|
|
5064
5078
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5065
|
-
|
|
5079
|
+
var _a;
|
|
5080
|
+
const receivedUntils = {};
|
|
5081
|
+
let resyncNeeded = false;
|
|
5082
|
+
let yServerRevision;
|
|
5066
5083
|
for (const m of yMessages) {
|
|
5067
5084
|
switch (m.type) {
|
|
5068
5085
|
case 'u-s': {
|
|
5069
5086
|
const utbl = getUpdatesTable(db, m.table, m.prop);
|
|
5070
|
-
|
|
5087
|
+
receivedUntils[utbl.name] = yield utbl.add({
|
|
5071
5088
|
k: m.k,
|
|
5072
5089
|
u: m.u,
|
|
5073
5090
|
});
|
|
@@ -5092,7 +5109,24 @@
|
|
|
5092
5109
|
// See my question in https://discuss.yjs.dev/t/generate-an-inverse-update/2765
|
|
5093
5110
|
console.debug(`Y update rejected. Deleting it.`);
|
|
5094
5111
|
const utbl = getUpdatesTable(db, m.table, m.prop);
|
|
5095
|
-
|
|
5112
|
+
// Delete the rejected update and all local updates since (avoid holes in the CRDT)
|
|
5113
|
+
// and destroy it's open document if there is one.
|
|
5114
|
+
const primaryKey = (_a = (yield utbl.get(m.i))) === null || _a === void 0 ? void 0 : _a.k;
|
|
5115
|
+
if (primaryKey != null) {
|
|
5116
|
+
yield db.transaction('rw', utbl, (tx) => {
|
|
5117
|
+
// @ts-ignore
|
|
5118
|
+
tx.idbtrans._rejecting_y_ypdate = true; // Inform ydoc triggers that we delete because of a rejection and not GC
|
|
5119
|
+
return utbl
|
|
5120
|
+
.where('i')
|
|
5121
|
+
.aboveOrEqual(m.i)
|
|
5122
|
+
.filter((u) => Dexie.cmp(u.k, primaryKey) === 0 && ((u.f || 0) & 1) === 1)
|
|
5123
|
+
.delete();
|
|
5124
|
+
});
|
|
5125
|
+
// Destroy active doc
|
|
5126
|
+
const activeDoc = Dexie.DexieYProvider.getDocCache(db.dx).find(m.table, primaryKey, m.prop);
|
|
5127
|
+
if (activeDoc)
|
|
5128
|
+
activeDoc.destroy(); // Destroy the document so that editors don't continue to work on it
|
|
5129
|
+
}
|
|
5096
5130
|
break;
|
|
5097
5131
|
}
|
|
5098
5132
|
case 'in-sync': {
|
|
@@ -5102,15 +5136,26 @@
|
|
|
5102
5136
|
}
|
|
5103
5137
|
break;
|
|
5104
5138
|
}
|
|
5139
|
+
case 'y-complete-sync-done': {
|
|
5140
|
+
yServerRevision = m.yServerRev;
|
|
5141
|
+
break;
|
|
5142
|
+
}
|
|
5143
|
+
case 'outdated-server-rev':
|
|
5144
|
+
resyncNeeded = true;
|
|
5145
|
+
break;
|
|
5105
5146
|
}
|
|
5106
5147
|
}
|
|
5107
|
-
return
|
|
5148
|
+
return {
|
|
5149
|
+
receivedUntils,
|
|
5150
|
+
resyncNeeded,
|
|
5151
|
+
yServerRevision
|
|
5152
|
+
};
|
|
5108
5153
|
});
|
|
5109
5154
|
}
|
|
5110
5155
|
|
|
5111
|
-
function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db
|
|
5112
|
-
var _a, _b, _c, _d, _e;
|
|
5156
|
+
function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db) {
|
|
5113
5157
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5158
|
+
var _a, _b, _c, _d, _e;
|
|
5114
5159
|
// We want to update unsentFrom for each yTable to the value specified in first argument
|
|
5115
5160
|
// because we got those values before we synced with server and here we are back from server
|
|
5116
5161
|
// that has successfully received all those messages - no matter if the last update was a client or server update,
|
|
@@ -5157,14 +5202,12 @@
|
|
|
5157
5202
|
yield db.table(yTable).add({
|
|
5158
5203
|
i: DEXIE_CLOUD_SYNCER_ID,
|
|
5159
5204
|
unsentFrom,
|
|
5160
|
-
receivedUntil
|
|
5161
|
-
serverRev: serverRevision,
|
|
5205
|
+
receivedUntil
|
|
5162
5206
|
});
|
|
5163
5207
|
}
|
|
5164
5208
|
else {
|
|
5165
5209
|
state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
|
|
5166
5210
|
state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
|
|
5167
|
-
state.serverRev = serverRevision;
|
|
5168
5211
|
yield db.table(yTable).put(state);
|
|
5169
5212
|
}
|
|
5170
5213
|
}));
|
|
@@ -5175,8 +5218,8 @@
|
|
|
5175
5218
|
const BINSTREAM_TYPE_REALMID = 1;
|
|
5176
5219
|
const BINSTREAM_TYPE_TABLE_AND_PROP = 2;
|
|
5177
5220
|
const BINSTREAM_TYPE_DOCUMENT = 3;
|
|
5178
|
-
function downloadYDocsFromServer(
|
|
5179
|
-
return __awaiter(this,
|
|
5221
|
+
function downloadYDocsFromServer(db_1, databaseUrl_1, _a) {
|
|
5222
|
+
return __awaiter(this, arguments, void 0, function* (db, databaseUrl, { yDownloadedRealms, realms }) {
|
|
5180
5223
|
if (yDownloadedRealms &&
|
|
5181
5224
|
realms &&
|
|
5182
5225
|
realms.every((realmId) => yDownloadedRealms[realmId] === '*')) {
|
|
@@ -5291,8 +5334,7 @@
|
|
|
5291
5334
|
|
|
5292
5335
|
const CURRENT_SYNC_WORKER = 'currentSyncWorker';
|
|
5293
5336
|
function sync(db, options, schema, syncOptions) {
|
|
5294
|
-
return _sync
|
|
5295
|
-
.apply(this, arguments)
|
|
5337
|
+
return _sync(db, options, schema, syncOptions)
|
|
5296
5338
|
.then((result) => {
|
|
5297
5339
|
if (!(syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)) { // && syncOptions?.purpose !== 'push') {
|
|
5298
5340
|
db.syncStateChangedEvent.next({
|
|
@@ -5334,11 +5376,11 @@
|
|
|
5334
5376
|
return Promise.reject(error);
|
|
5335
5377
|
}));
|
|
5336
5378
|
}
|
|
5337
|
-
function _sync(
|
|
5338
|
-
isInitialSync
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5379
|
+
function _sync(db_1, options_1, schema_1) {
|
|
5380
|
+
return __awaiter(this, arguments, void 0, function* (db, options, schema, { isInitialSync, cancelToken, justCheckIfNeeded, purpose } = {
|
|
5381
|
+
isInitialSync: false,
|
|
5382
|
+
}) {
|
|
5383
|
+
var _a;
|
|
5342
5384
|
if (!justCheckIfNeeded) {
|
|
5343
5385
|
console.debug('SYNC STARTED', { isInitialSync, purpose });
|
|
5344
5386
|
}
|
|
@@ -5355,7 +5397,7 @@
|
|
|
5355
5397
|
// Prepare for syncification by modifying locally unauthorized objects:
|
|
5356
5398
|
//
|
|
5357
5399
|
const persistedSyncState = yield db.getPersistedSyncState();
|
|
5358
|
-
const readyForSyncification =
|
|
5400
|
+
const readyForSyncification = currentUser.isLoggedIn;
|
|
5359
5401
|
const tablesToSyncify = readyForSyncification
|
|
5360
5402
|
? getTablesToSyncify(db, persistedSyncState)
|
|
5361
5403
|
: [];
|
|
@@ -5380,7 +5422,7 @@
|
|
|
5380
5422
|
const [clientChangeSet, syncState, baseRevs, { yMessages, lastUpdateIds }] = yield db.transaction('r', db.tables, () => __awaiter(this, void 0, void 0, function* () {
|
|
5381
5423
|
const syncState = yield db.getPersistedSyncState();
|
|
5382
5424
|
const baseRevs = yield db.$baseRevs.toArray();
|
|
5383
|
-
let clientChanges = yield listClientChanges(mutationTables);
|
|
5425
|
+
let clientChanges = yield listClientChanges(mutationTables, db);
|
|
5384
5426
|
const yResults = yield listYClientMessagesAndStateVector(db, tablesToSync);
|
|
5385
5427
|
throwIfCancelled(cancelToken);
|
|
5386
5428
|
if (doSyncify) {
|
|
@@ -5500,6 +5542,7 @@
|
|
|
5500
5542
|
newSyncState.realms = res.realms;
|
|
5501
5543
|
newSyncState.inviteRealms = res.inviteRealms;
|
|
5502
5544
|
newSyncState.serverRevision = res.serverRevision;
|
|
5545
|
+
newSyncState.yServerRevision = res.serverRevision;
|
|
5503
5546
|
newSyncState.timestamp = new Date();
|
|
5504
5547
|
delete newSyncState.error;
|
|
5505
5548
|
const filteredChanges = filterServerChangesThroughAddedClientChanges(res.changes, addedClientChanges);
|
|
@@ -5511,11 +5554,17 @@
|
|
|
5511
5554
|
//
|
|
5512
5555
|
// apply yMessages
|
|
5513
5556
|
//
|
|
5514
|
-
const receivedUntils = yield applyYServerMessages(res.yMessages, db);
|
|
5557
|
+
const { receivedUntils, resyncNeeded, yServerRevision } = yield applyYServerMessages(res.yMessages, db);
|
|
5558
|
+
if (yServerRevision) {
|
|
5559
|
+
newSyncState.yServerRevision = yServerRevision;
|
|
5560
|
+
}
|
|
5515
5561
|
//
|
|
5516
5562
|
// update Y SyncStates
|
|
5517
5563
|
//
|
|
5518
|
-
yield updateYSyncStates(lastUpdateIds, receivedUntils, db
|
|
5564
|
+
yield updateYSyncStates(lastUpdateIds, receivedUntils, db);
|
|
5565
|
+
if (resyncNeeded) {
|
|
5566
|
+
newSyncState.yDownloadedRealms = {}; // Will trigger a full download of Y-documents below...
|
|
5567
|
+
}
|
|
5519
5568
|
}
|
|
5520
5569
|
//
|
|
5521
5570
|
// Update regular syncState
|
|
@@ -5649,8 +5698,8 @@
|
|
|
5649
5698
|
event.next(null);
|
|
5650
5699
|
}
|
|
5651
5700
|
function consumeQueue() {
|
|
5652
|
-
var _a, _b, _c, _d, _e, _f;
|
|
5653
5701
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5702
|
+
var _a, _b, _c, _d, _e, _f;
|
|
5654
5703
|
while (queue.length > 0) {
|
|
5655
5704
|
const msg = queue.shift();
|
|
5656
5705
|
try {
|
|
@@ -5808,7 +5857,7 @@
|
|
|
5808
5857
|
};
|
|
5809
5858
|
}
|
|
5810
5859
|
|
|
5811
|
-
const wm = new WeakMap();
|
|
5860
|
+
const wm$1 = new WeakMap();
|
|
5812
5861
|
const DEXIE_CLOUD_SCHEMA = {
|
|
5813
5862
|
members: '@id, [userId+realmId], [email+realmId], realmId',
|
|
5814
5863
|
roles: '[realmId+name]',
|
|
@@ -5822,7 +5871,7 @@
|
|
|
5822
5871
|
function DexieCloudDB(dx) {
|
|
5823
5872
|
if ('vip' in dx)
|
|
5824
5873
|
dx = dx['vip']; // Avoid race condition. Always map to a vipped dexie that don't block during db.on.ready().
|
|
5825
|
-
let db = wm.get(dx.cloud);
|
|
5874
|
+
let db = wm$1.get(dx.cloud);
|
|
5826
5875
|
if (!db) {
|
|
5827
5876
|
const localSyncEvent = new rxjs.Subject();
|
|
5828
5877
|
let syncStateChangedEvent = new BroadcastedAndLocalEvent(`syncstatechanged-${dx.name}`);
|
|
@@ -5910,7 +5959,7 @@
|
|
|
5910
5959
|
Object.assign(db, helperMethods);
|
|
5911
5960
|
db.messageConsumer = MessagesFromServerConsumer(db);
|
|
5912
5961
|
db.messageProducer = new rxjs.Subject();
|
|
5913
|
-
wm.set(dx.cloud, db);
|
|
5962
|
+
wm$1.set(dx.cloud, db);
|
|
5914
5963
|
}
|
|
5915
5964
|
return db;
|
|
5916
5965
|
}
|
|
@@ -6306,8 +6355,7 @@
|
|
|
6306
6355
|
outstandingTransactions.next(outstandingTransactions.value);
|
|
6307
6356
|
};
|
|
6308
6357
|
const txComplete = () => {
|
|
6309
|
-
if (tx.mutationsAdded &&
|
|
6310
|
-
!isEagerSyncDisabled(db)) {
|
|
6358
|
+
if (tx.mutationsAdded && !isEagerSyncDisabled(db)) {
|
|
6311
6359
|
triggerSync(db, 'push');
|
|
6312
6360
|
}
|
|
6313
6361
|
removeTransaction();
|
|
@@ -6389,26 +6437,107 @@
|
|
|
6389
6437
|
: mutateAndLog(req);
|
|
6390
6438
|
} }));
|
|
6391
6439
|
function mutateAndLog(req) {
|
|
6440
|
+
var _a, _b;
|
|
6392
6441
|
const trans = req.trans;
|
|
6393
|
-
|
|
6442
|
+
const unsyncedProps = (_b = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.unsyncedProperties) === null || _b === void 0 ? void 0 : _b[tableName];
|
|
6394
6443
|
const { txid, currentUser: { userId }, } = trans;
|
|
6395
6444
|
const { type } = req;
|
|
6396
6445
|
const opNo = ++trans.opCount;
|
|
6446
|
+
function stripChangeSpec(changeSpec) {
|
|
6447
|
+
if (!unsyncedProps)
|
|
6448
|
+
return changeSpec;
|
|
6449
|
+
let rv = changeSpec;
|
|
6450
|
+
for (const keyPath of Object.keys(changeSpec)) {
|
|
6451
|
+
if (unsyncedProps.some((p) => keyPath === p || keyPath.startsWith(p + '.'))) {
|
|
6452
|
+
if (rv === changeSpec)
|
|
6453
|
+
rv = Object.assign({}, changeSpec); // clone on demand
|
|
6454
|
+
delete rv[keyPath];
|
|
6455
|
+
}
|
|
6456
|
+
}
|
|
6457
|
+
return rv;
|
|
6458
|
+
}
|
|
6397
6459
|
return table.mutate(req).then((res) => {
|
|
6460
|
+
var _a;
|
|
6398
6461
|
const { numFailures: hasFailures, failures } = res;
|
|
6399
6462
|
let keys = type === 'delete' ? req.keys : res.results;
|
|
6400
6463
|
let values = 'values' in req ? req.values : [];
|
|
6401
|
-
let
|
|
6464
|
+
let changeSpec = 'changeSpec' in req ? req.changeSpec : undefined;
|
|
6465
|
+
let updates = 'updates' in req ? req.updates : undefined;
|
|
6402
6466
|
if (hasFailures) {
|
|
6403
6467
|
keys = keys.filter((_, idx) => !failures[idx]);
|
|
6404
6468
|
values = values.filter((_, idx) => !failures[idx]);
|
|
6405
6469
|
}
|
|
6470
|
+
if (unsyncedProps) {
|
|
6471
|
+
// Filter out unsynced properties
|
|
6472
|
+
values = values.map((value) => {
|
|
6473
|
+
const newValue = Object.assign({}, value);
|
|
6474
|
+
for (const prop of unsyncedProps) {
|
|
6475
|
+
delete newValue[prop];
|
|
6476
|
+
}
|
|
6477
|
+
return newValue;
|
|
6478
|
+
});
|
|
6479
|
+
if (changeSpec) {
|
|
6480
|
+
// modify operation with criteria and changeSpec.
|
|
6481
|
+
// We must strip out unsynced properties from changeSpec.
|
|
6482
|
+
// We deal with criteria later.
|
|
6483
|
+
changeSpec = stripChangeSpec(changeSpec);
|
|
6484
|
+
if (Object.keys(changeSpec).length === 0) {
|
|
6485
|
+
// Nothing to change on server
|
|
6486
|
+
return res;
|
|
6487
|
+
}
|
|
6488
|
+
}
|
|
6489
|
+
if (updates) {
|
|
6490
|
+
let strippedChangeSpecs = updates.changeSpecs.map(stripChangeSpec);
|
|
6491
|
+
let newUpdates = {
|
|
6492
|
+
keys: [],
|
|
6493
|
+
changeSpecs: [],
|
|
6494
|
+
};
|
|
6495
|
+
const validKeys = new Dexie.RangeSet();
|
|
6496
|
+
let anyChangeSpecBecameEmpty = false;
|
|
6497
|
+
for (let i = 0, l = strippedChangeSpecs.length; i < l; ++i) {
|
|
6498
|
+
if (Object.keys(strippedChangeSpecs[i]).length > 0) {
|
|
6499
|
+
newUpdates.keys.push(updates.keys[i]);
|
|
6500
|
+
newUpdates.changeSpecs.push(strippedChangeSpecs[i]);
|
|
6501
|
+
validKeys.addKey(updates.keys[i]);
|
|
6502
|
+
}
|
|
6503
|
+
else {
|
|
6504
|
+
anyChangeSpecBecameEmpty = true;
|
|
6505
|
+
}
|
|
6506
|
+
}
|
|
6507
|
+
updates = newUpdates;
|
|
6508
|
+
if (anyChangeSpecBecameEmpty) {
|
|
6509
|
+
// Some keys were stripped. We must also strip them from keys and values
|
|
6510
|
+
let newKeys = [];
|
|
6511
|
+
let newValues = [];
|
|
6512
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
6513
|
+
if (validKeys.hasKey(keys[i])) {
|
|
6514
|
+
newKeys.push(keys[i]);
|
|
6515
|
+
newValues.push(values[i]);
|
|
6516
|
+
}
|
|
6517
|
+
}
|
|
6518
|
+
keys = newKeys;
|
|
6519
|
+
values = newValues;
|
|
6520
|
+
}
|
|
6521
|
+
}
|
|
6522
|
+
}
|
|
6406
6523
|
const ts = Date.now();
|
|
6407
6524
|
// Canonicalize req.criteria.index to null if it's on the primary key.
|
|
6408
|
-
|
|
6525
|
+
let criteria = 'criteria' in req && req.criteria
|
|
6409
6526
|
? Object.assign(Object.assign({}, req.criteria), { index: req.criteria.index === schema.primaryKey.keyPath // Use null to inform server that criteria is on primary key
|
|
6410
6527
|
? null // This will disable the server from trying to log consistent operations where it shouldnt.
|
|
6411
6528
|
: req.criteria.index }) : undefined;
|
|
6529
|
+
if (unsyncedProps && (criteria === null || criteria === void 0 ? void 0 : criteria.index)) {
|
|
6530
|
+
const keyPaths = (_a = schema.indexes.find((idx) => idx.name === criteria.index)) === null || _a === void 0 ? void 0 : _a.keyPath;
|
|
6531
|
+
const involvedProps = keyPaths
|
|
6532
|
+
? typeof keyPaths === 'string'
|
|
6533
|
+
? [keyPaths]
|
|
6534
|
+
: keyPaths
|
|
6535
|
+
: [];
|
|
6536
|
+
if (involvedProps.some((p) => unsyncedProps === null || unsyncedProps === void 0 ? void 0 : unsyncedProps.includes(p))) {
|
|
6537
|
+
// Don't log criteria on unsynced properties as the server could not test them.
|
|
6538
|
+
criteria = undefined;
|
|
6539
|
+
}
|
|
6540
|
+
}
|
|
6412
6541
|
const mut = req.type === 'delete'
|
|
6413
6542
|
? {
|
|
6414
6543
|
type: 'delete',
|
|
@@ -6429,7 +6558,7 @@
|
|
|
6429
6558
|
userId,
|
|
6430
6559
|
values,
|
|
6431
6560
|
}
|
|
6432
|
-
: criteria &&
|
|
6561
|
+
: criteria && changeSpec
|
|
6433
6562
|
? {
|
|
6434
6563
|
// Common changeSpec for all keys
|
|
6435
6564
|
type: 'modify',
|
|
@@ -6437,37 +6566,51 @@
|
|
|
6437
6566
|
opNo,
|
|
6438
6567
|
keys,
|
|
6439
6568
|
criteria,
|
|
6440
|
-
changeSpec
|
|
6569
|
+
changeSpec,
|
|
6441
6570
|
txid,
|
|
6442
6571
|
userId,
|
|
6443
6572
|
}
|
|
6444
|
-
:
|
|
6573
|
+
: changeSpec
|
|
6445
6574
|
? {
|
|
6446
|
-
//
|
|
6575
|
+
// In case criteria involved an unsynced property, we go for keys instead.
|
|
6447
6576
|
type: 'update',
|
|
6448
6577
|
ts,
|
|
6449
6578
|
opNo,
|
|
6450
|
-
keys: updates.keys,
|
|
6451
|
-
changeSpecs: updates.changeSpecs,
|
|
6452
|
-
txid,
|
|
6453
|
-
userId,
|
|
6454
|
-
}
|
|
6455
|
-
: {
|
|
6456
|
-
type: 'upsert',
|
|
6457
|
-
ts,
|
|
6458
|
-
opNo,
|
|
6459
6579
|
keys,
|
|
6460
|
-
|
|
6580
|
+
changeSpecs: keys.map(() => changeSpec),
|
|
6461
6581
|
txid,
|
|
6462
6582
|
userId,
|
|
6463
|
-
}
|
|
6583
|
+
}
|
|
6584
|
+
: updates
|
|
6585
|
+
? {
|
|
6586
|
+
// One changeSpec per key
|
|
6587
|
+
type: 'update',
|
|
6588
|
+
ts,
|
|
6589
|
+
opNo,
|
|
6590
|
+
keys: updates.keys,
|
|
6591
|
+
changeSpecs: updates.changeSpecs,
|
|
6592
|
+
txid,
|
|
6593
|
+
userId,
|
|
6594
|
+
}
|
|
6595
|
+
: {
|
|
6596
|
+
type: 'upsert',
|
|
6597
|
+
ts,
|
|
6598
|
+
opNo,
|
|
6599
|
+
keys,
|
|
6600
|
+
values,
|
|
6601
|
+
txid,
|
|
6602
|
+
userId,
|
|
6603
|
+
};
|
|
6464
6604
|
if ('isAdditionalChunk' in req && req.isAdditionalChunk) {
|
|
6465
6605
|
mut.isAdditionalChunk = true;
|
|
6466
6606
|
}
|
|
6467
6607
|
return keys.length > 0 || criteria
|
|
6468
6608
|
? mutsTable
|
|
6469
6609
|
.mutate({ type: 'add', trans, values: [mut] }) // Log entry
|
|
6470
|
-
.then(() =>
|
|
6610
|
+
.then(() => {
|
|
6611
|
+
trans.mutationsAdded = true; // Mark transaction as having added mutations to trigger eager sync
|
|
6612
|
+
return res; // Return original response
|
|
6613
|
+
})
|
|
6471
6614
|
: res;
|
|
6472
6615
|
});
|
|
6473
6616
|
}
|
|
@@ -6551,10 +6694,18 @@
|
|
|
6551
6694
|
};
|
|
6552
6695
|
}
|
|
6553
6696
|
|
|
6697
|
+
function performGuardedJob(db, jobName, job) {
|
|
6698
|
+
if (typeof navigator === 'undefined' || !navigator.locks) {
|
|
6699
|
+
// No support for guarding jobs. IE11, node.js, etc.
|
|
6700
|
+
return job();
|
|
6701
|
+
}
|
|
6702
|
+
return navigator.locks.request(db.name + '|' + jobName, () => job());
|
|
6703
|
+
}
|
|
6704
|
+
|
|
6554
6705
|
function performInitialSync(db, cloudOptions, cloudSchema) {
|
|
6555
6706
|
return __awaiter(this, void 0, void 0, function* () {
|
|
6556
6707
|
console.debug('Performing initial sync');
|
|
6557
|
-
yield sync(db, cloudOptions, cloudSchema, { isInitialSync: true });
|
|
6708
|
+
yield performGuardedJob(db, CURRENT_SYNC_WORKER, () => sync(db, cloudOptions, cloudSchema, { isInitialSync: true }));
|
|
6558
6709
|
console.debug('Done initial sync');
|
|
6559
6710
|
});
|
|
6560
6711
|
}
|
|
@@ -6641,29 +6792,35 @@
|
|
|
6641
6792
|
updatesTable: p.updatesTable,
|
|
6642
6793
|
}))));
|
|
6643
6794
|
return rxjs.merge(...yTableRecords.map(({ table, ydocProp, updatesTable }) => {
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6664
|
-
|
|
6665
|
-
|
|
6666
|
-
|
|
6795
|
+
// Per updates table (table+prop combo), we first read syncer.unsentFrom,
|
|
6796
|
+
// and then start listening for updates since that number.
|
|
6797
|
+
const yTbl = db.table(updatesTable);
|
|
6798
|
+
return rxjs.from(yTbl.get(DEXIE_CLOUD_SYNCER_ID)).pipe(rxjs.switchMap((syncer) => {
|
|
6799
|
+
let currentUnsentFrom = (syncer === null || syncer === void 0 ? void 0 : syncer.unsentFrom) || 1;
|
|
6800
|
+
return rxjs.from(Dexie.liveQuery(() => __awaiter(this, void 0, void 0, function* () {
|
|
6801
|
+
const addedUpdates = yield listUpdatesSince(yTbl, currentUnsentFrom);
|
|
6802
|
+
return addedUpdates
|
|
6803
|
+
.filter((update) => update.f && update.f & 1) // Only include local updates
|
|
6804
|
+
.map((update) => {
|
|
6805
|
+
return {
|
|
6806
|
+
type: 'u-c',
|
|
6807
|
+
table,
|
|
6808
|
+
prop: ydocProp,
|
|
6809
|
+
k: update.k,
|
|
6810
|
+
u: update.u,
|
|
6811
|
+
i: update.i,
|
|
6812
|
+
};
|
|
6813
|
+
});
|
|
6814
|
+
}))).pipe(rxjs.tap((addedUpdates) => {
|
|
6815
|
+
// Update currentUnsentFrom to only listen for updates that will be newer than the ones we emitted.
|
|
6816
|
+
// (Before, we did this within the liveQuery, but that caused a bug because
|
|
6817
|
+
// a cancelled emittion of a liveQuery would update the currentUnsentFrom without
|
|
6818
|
+
// emitting anything, leading to that we jumped over some updates. Here we update it
|
|
6819
|
+
// after the liveQuery has emitted its updates)
|
|
6820
|
+
if (addedUpdates.length > 0) {
|
|
6821
|
+
currentUnsentFrom = addedUpdates.at(-1).i + 1;
|
|
6822
|
+
}
|
|
6823
|
+
}));
|
|
6667
6824
|
}));
|
|
6668
6825
|
})).pipe(
|
|
6669
6826
|
// Flatten the array of messages.
|
|
@@ -6683,6 +6840,23 @@
|
|
|
6683
6840
|
const awarenessWeakMap = new WeakMap();
|
|
6684
6841
|
const getDocAwareness = (doc) => awarenessWeakMap.get(doc);
|
|
6685
6842
|
|
|
6843
|
+
const wm = new WeakMap();
|
|
6844
|
+
/** A property (package-private) on Y.Doc that is used
|
|
6845
|
+
* to signal that the server wants us to send a 'doc-open' message
|
|
6846
|
+
* to the server for this document.
|
|
6847
|
+
*
|
|
6848
|
+
* @param doc
|
|
6849
|
+
* @returns
|
|
6850
|
+
*/
|
|
6851
|
+
function getOpenDocSignal(doc) {
|
|
6852
|
+
let signal = wm.get(doc);
|
|
6853
|
+
if (!signal) {
|
|
6854
|
+
signal = new rxjs.Subject();
|
|
6855
|
+
wm.set(doc, signal);
|
|
6856
|
+
}
|
|
6857
|
+
return signal;
|
|
6858
|
+
}
|
|
6859
|
+
|
|
6686
6860
|
const SERVER_PING_TIMEOUT = 20000;
|
|
6687
6861
|
const CLIENT_PING_INTERVAL = 30000;
|
|
6688
6862
|
const FAIL_RETRY_WAIT_TIME = 60000;
|
|
@@ -6845,9 +7019,6 @@
|
|
|
6845
7019
|
if (msg.type === 'error') {
|
|
6846
7020
|
throw new Error(`Error message from dexie-cloud: ${msg.error}`);
|
|
6847
7021
|
}
|
|
6848
|
-
else if (msg.type === 'rev') {
|
|
6849
|
-
this.rev = msg.rev; // No meaning but seems reasonable.
|
|
6850
|
-
}
|
|
6851
7022
|
else if (msg.type === 'aware') {
|
|
6852
7023
|
const docCache = Dexie.DexieYProvider.getDocCache(this.db.dx);
|
|
6853
7024
|
const doc = docCache.find(msg.table, msg.k, msg.prop);
|
|
@@ -6862,7 +7033,19 @@
|
|
|
6862
7033
|
else if (msg.type === 'u-ack' || msg.type === 'u-reject' || msg.type === 'u-s' || msg.type === 'in-sync') {
|
|
6863
7034
|
applyYServerMessages([msg], this.db);
|
|
6864
7035
|
}
|
|
7036
|
+
else if (msg.type === 'doc-open') {
|
|
7037
|
+
const docCache = Dexie.DexieYProvider.getDocCache(this.db.dx);
|
|
7038
|
+
const doc = docCache.find(msg.table, msg.k, msg.prop);
|
|
7039
|
+
if (doc) {
|
|
7040
|
+
getOpenDocSignal(doc).next(); // Make yHandler reopen the document on server.
|
|
7041
|
+
}
|
|
7042
|
+
}
|
|
7043
|
+
else if (msg.type === 'outdated-server-rev' || msg.type === 'y-complete-sync-done') {
|
|
7044
|
+
// Won't happen but need this for typing.
|
|
7045
|
+
throw new Error('Outdated server revision or y-complete-sync-done not expected over WebSocket - only in sync using fetch()');
|
|
7046
|
+
}
|
|
6865
7047
|
else if (msg.type !== 'pong') {
|
|
7048
|
+
// Forward the request to our subscriber, wich is in messageFromServerQueue.ts (via connectWebSocket's subscribe() at the end!)
|
|
6866
7049
|
this.subscriber.next(msg);
|
|
6867
7050
|
}
|
|
6868
7051
|
}
|
|
@@ -6899,6 +7082,10 @@
|
|
|
6899
7082
|
}
|
|
6900
7083
|
console.debug('dexie-cloud WebSocket send', msg.type, msg);
|
|
6901
7084
|
if (msg.type === 'ready') {
|
|
7085
|
+
// Ok, we are certain to have stored everything up until revision msg.rev.
|
|
7086
|
+
// Update this.rev in case of reconnect - remember where we were and don't just start over!
|
|
7087
|
+
this.rev = msg.rev;
|
|
7088
|
+
// ... and then send along the request to the server so it would also be updated!
|
|
6902
7089
|
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(TSON.stringify(msg));
|
|
6903
7090
|
}
|
|
6904
7091
|
else {
|
|
@@ -6980,7 +7167,7 @@
|
|
|
6980
7167
|
return db.cloud.persistedSyncState.pipe(filter((syncState) => (syncState === null || syncState === void 0 ? void 0 : syncState.realms.includes(userLogin.userId)) || false), take(1), map((syncState) => [userLogin, syncState]));
|
|
6981
7168
|
}
|
|
6982
7169
|
return new rxjs.BehaviorSubject([userLogin, syncState]);
|
|
6983
|
-
}), switchMap((
|
|
7170
|
+
}), switchMap((_a) => __awaiter(this, [_a], void 0, function* ([userLogin, syncState]) { return [userLogin, yield computeRealmSetHash(syncState)]; })), distinctUntilChanged(([prevUser, prevHash], [currUser, currHash]) => prevUser === currUser && prevHash === currHash), switchMap(([userLogin, realmSetHash]) => {
|
|
6984
7171
|
var _a;
|
|
6985
7172
|
if (!((_a = db.cloud.persistedSyncState) === null || _a === void 0 ? void 0 : _a.value)) {
|
|
6986
7173
|
// Restart the flow if persistedSyncState is not yet available.
|
|
@@ -7041,22 +7228,14 @@
|
|
|
7041
7228
|
}
|
|
7042
7229
|
|
|
7043
7230
|
function isSyncNeeded(db) {
|
|
7044
|
-
var _a;
|
|
7045
7231
|
return __awaiter(this, void 0, void 0, function* () {
|
|
7232
|
+
var _a;
|
|
7046
7233
|
return ((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl) && db.cloud.schema
|
|
7047
7234
|
? yield sync(db, db.cloud.options, db.cloud.schema, { justCheckIfNeeded: true })
|
|
7048
7235
|
: false;
|
|
7049
7236
|
});
|
|
7050
7237
|
}
|
|
7051
7238
|
|
|
7052
|
-
function performGuardedJob(db, jobName, job) {
|
|
7053
|
-
if (typeof navigator === 'undefined' || !navigator.locks) {
|
|
7054
|
-
// No support for guarding jobs. IE11, node.js, etc.
|
|
7055
|
-
return job();
|
|
7056
|
-
}
|
|
7057
|
-
return navigator.locks.request(db.name + '|' + jobName, () => job());
|
|
7058
|
-
}
|
|
7059
|
-
|
|
7060
7239
|
const ongoingSyncs = new WeakMap();
|
|
7061
7240
|
function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
7062
7241
|
const ongoing = ongoingSyncs.get(db);
|
|
@@ -7110,20 +7289,7 @@
|
|
|
7110
7289
|
try {
|
|
7111
7290
|
// Check if should delay sync due to ratelimit:
|
|
7112
7291
|
yield checkSyncRateLimitDelay(db);
|
|
7113
|
-
|
|
7114
|
-
if (db.cloud.isServiceWorkerDB) {
|
|
7115
|
-
// We are the dedicated sync SW:
|
|
7116
|
-
yield sync(db, cloudOptions, cloudSchema, options);
|
|
7117
|
-
}
|
|
7118
|
-
else if (!db.cloud.usingServiceWorker) {
|
|
7119
|
-
// We use a flow that is better suited for the case when multiple workers want to
|
|
7120
|
-
// do the same thing.
|
|
7121
|
-
yield performGuardedJob(db, CURRENT_SYNC_WORKER, () => sync(db, cloudOptions, cloudSchema, options));
|
|
7122
|
-
}
|
|
7123
|
-
else {
|
|
7124
|
-
assert(false);
|
|
7125
|
-
throw new Error('Internal _syncIfPossible() - invalid precondition - should not have been called.');
|
|
7126
|
-
}
|
|
7292
|
+
yield performGuardedJob(db, CURRENT_SYNC_WORKER, () => sync(db, cloudOptions, cloudSchema, options));
|
|
7127
7293
|
ongoingSyncs.delete(db);
|
|
7128
7294
|
console.debug('Done sync');
|
|
7129
7295
|
}
|
|
@@ -7726,6 +7892,7 @@
|
|
|
7726
7892
|
// If user can update any prop in any table in this realm, return true unless
|
|
7727
7893
|
// it regards to ownership change:
|
|
7728
7894
|
if (this.permissions.update === '*') {
|
|
7895
|
+
// @ts-ignore
|
|
7729
7896
|
return props.every((prop) => prop !== 'owner');
|
|
7730
7897
|
}
|
|
7731
7898
|
const tablePermissions = (_b = this.permissions.update) === null || _b === void 0 ? void 0 : _b[this.tableName];
|
|
@@ -7799,125 +7966,151 @@
|
|
|
7799
7966
|
function createYHandler(db) {
|
|
7800
7967
|
return (provider) => {
|
|
7801
7968
|
var _a;
|
|
7802
|
-
const awap = getAwarenessLibrary(db);
|
|
7803
7969
|
const doc = provider.doc;
|
|
7804
|
-
const { parentTable
|
|
7970
|
+
const { parentTable } = doc.meta || {};
|
|
7805
7971
|
if (!((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[parentTable].markedForSync)) {
|
|
7806
7972
|
return; // The table that holds the doc is not marked for sync - leave it to dexie. No syncing, no awareness.
|
|
7807
7973
|
}
|
|
7808
|
-
let awareness
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7974
|
+
let awareness;
|
|
7975
|
+
Object.defineProperty(provider, 'awareness', {
|
|
7976
|
+
get() {
|
|
7977
|
+
if (awareness)
|
|
7978
|
+
return awareness;
|
|
7979
|
+
awareness = createAwareness(db, doc, provider);
|
|
7980
|
+
awarenessWeakMap.set(doc, awareness);
|
|
7981
|
+
return awareness;
|
|
7982
|
+
},
|
|
7983
|
+
});
|
|
7984
|
+
};
|
|
7985
|
+
}
|
|
7986
|
+
function createAwareness(db, doc, provider) {
|
|
7987
|
+
const { parentTable, parentId, parentProp, updatesTable } = doc.meta;
|
|
7988
|
+
const awap = getAwarenessLibrary(db);
|
|
7989
|
+
const awareness = new awap.Awareness(doc);
|
|
7990
|
+
const reopenDocSignal = getOpenDocSignal(doc);
|
|
7991
|
+
awareness.on('update', ({ added, updated, removed }, origin) => {
|
|
7992
|
+
// Send the update
|
|
7993
|
+
const changedClients = added.concat(updated).concat(removed);
|
|
7994
|
+
const user = db.cloud.currentUser.value;
|
|
7995
|
+
if (origin !== 'server' && user.isLoggedIn && !isEagerSyncDisabled(db)) {
|
|
7996
|
+
const update = awap.encodeAwarenessUpdate(awareness, changedClients);
|
|
7997
|
+
db.messageProducer.next({
|
|
7998
|
+
type: 'aware',
|
|
7999
|
+
table: parentTable,
|
|
8000
|
+
prop: parentProp,
|
|
8001
|
+
k: doc.meta.parentId,
|
|
8002
|
+
u: update,
|
|
8003
|
+
});
|
|
8004
|
+
if (provider.destroyed) {
|
|
8005
|
+
// We're called from awareness.on('destroy') that did
|
|
8006
|
+
// removeAwarenessStates.
|
|
8007
|
+
// It's time to also send the doc-close message that dexie-cloud understands
|
|
8008
|
+
// and uses to stop subscribing for updates and awareness updates and brings
|
|
8009
|
+
// down the cached information in memory on the WS connection for this.
|
|
7817
8010
|
db.messageProducer.next({
|
|
7818
|
-
type: '
|
|
8011
|
+
type: 'doc-close',
|
|
7819
8012
|
table: parentTable,
|
|
7820
8013
|
prop: parentProp,
|
|
7821
8014
|
k: doc.meta.parentId,
|
|
7822
|
-
u: update,
|
|
7823
8015
|
});
|
|
7824
|
-
if (provider.destroyed) {
|
|
7825
|
-
// We're called from awareness.on('destroy') that did
|
|
7826
|
-
// removeAwarenessStates.
|
|
7827
|
-
// It's time to also send the doc-close message that dexie-cloud understands
|
|
7828
|
-
// and uses to stop subscribing for updates and awareness updates and brings
|
|
7829
|
-
// down the cached information in memory on the WS connection for this.
|
|
7830
|
-
db.messageProducer.next({
|
|
7831
|
-
type: 'doc-close',
|
|
7832
|
-
table: parentTable,
|
|
7833
|
-
prop: parentProp,
|
|
7834
|
-
k: doc.meta.parentId
|
|
7835
|
-
});
|
|
7836
|
-
}
|
|
7837
8016
|
}
|
|
7838
|
-
}
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7845
|
-
|
|
8017
|
+
}
|
|
8018
|
+
});
|
|
8019
|
+
awareness.on('destroy', () => {
|
|
8020
|
+
// Signal to server that this provider is destroyed (the update event will be triggered, which
|
|
8021
|
+
// in turn will trigger db.messageProducer that will send the message to the server if WS is connected)
|
|
8022
|
+
awap.removeAwarenessStates(awareness, [doc.clientID], 'provider destroyed');
|
|
8023
|
+
});
|
|
8024
|
+
// Open the document on the server
|
|
8025
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
8026
|
+
if (provider.destroyed)
|
|
8027
|
+
return;
|
|
8028
|
+
let connected = false;
|
|
8029
|
+
let currentFlowId = 1;
|
|
8030
|
+
const subscription = rxjs.combineLatest([
|
|
8031
|
+
db.cloud.webSocketStatus, // Wake up when webSocket status changes
|
|
8032
|
+
reopenDocSignal.pipe(rxjs.startWith(null)), // Wake up when reopenDocSignal emits
|
|
8033
|
+
]).subscribe(([wsStatus]) => {
|
|
7846
8034
|
if (provider.destroyed)
|
|
7847
8035
|
return;
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
8036
|
+
// Keep "connected" state in a variable so we can check it after async operations
|
|
8037
|
+
connected = wsStatus === 'connected';
|
|
8038
|
+
// We are or got connected. Open the document on the server.
|
|
8039
|
+
const user = db.cloud.currentUser.value;
|
|
8040
|
+
if (wsStatus === 'connected' &&
|
|
8041
|
+
user.isLoggedIn &&
|
|
8042
|
+
!isEagerSyncDisabled(db)) {
|
|
8043
|
+
++currentFlowId;
|
|
8044
|
+
openDocumentOnServer().catch((error) => {
|
|
8045
|
+
console.warn(`Error catched in createYHandler.ts: ${error}`);
|
|
8046
|
+
});
|
|
8047
|
+
}
|
|
8048
|
+
});
|
|
8049
|
+
// Wait until WebSocket is connected
|
|
8050
|
+
provider.addCleanupHandler(subscription);
|
|
8051
|
+
/** Sends an 'doc-open' message to server whenever websocket becomes
|
|
8052
|
+
* connected, or if it is already connected.
|
|
8053
|
+
* The flow is aborted in case websocket is disconnected while querying
|
|
8054
|
+
* information required to compute the state vector. Flow is also
|
|
8055
|
+
* aborted in case document or provider has been destroyed during
|
|
8056
|
+
* the async parts of the task.
|
|
8057
|
+
*
|
|
8058
|
+
* The state vector is only computed from the updates that have occured
|
|
8059
|
+
* after the last full sync - which could very often be zero - in which
|
|
8060
|
+
* case no state vector is sent (then the server already knows us by
|
|
8061
|
+
* revision)
|
|
8062
|
+
*
|
|
8063
|
+
* When server gets the doc-open message, it will authorize us for
|
|
8064
|
+
* whether we are allowed to read / write to this document, and then
|
|
8065
|
+
* keep the cached information in memory on the WS connection for this
|
|
8066
|
+
* particular document, as well as subscribe to updates and awareness updates
|
|
8067
|
+
* from other clients on the document.
|
|
8068
|
+
*/
|
|
8069
|
+
function openDocumentOnServer() {
|
|
8070
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8071
|
+
const myFlow = currentFlowId; // So we can abort when a new flow is started
|
|
8072
|
+
const yTbl = db.table(updatesTable);
|
|
8073
|
+
const syncStateTbl = db.$syncState;
|
|
8074
|
+
const [receivedUntil, yServerRev] = yield db.transaction('r', syncStateTbl, yTbl, () => __awaiter(this, void 0, void 0, function* () {
|
|
8075
|
+
const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
|
|
8076
|
+
const persistedSyncState = yield syncStateTbl.get('syncState');
|
|
8077
|
+
return [
|
|
8078
|
+
(syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0,
|
|
8079
|
+
(persistedSyncState === null || persistedSyncState === void 0 ? void 0 : persistedSyncState.yServerRevision) ||
|
|
8080
|
+
(persistedSyncState === null || persistedSyncState === void 0 ? void 0 : persistedSyncState.serverRevision),
|
|
8081
|
+
];
|
|
8082
|
+
}));
|
|
8083
|
+
// After every await, check if we still should be working on this task.
|
|
8084
|
+
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7852
8085
|
return;
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7859
|
-
|
|
7860
|
-
|
|
7861
|
-
|
|
8086
|
+
const docOpenMsg = {
|
|
8087
|
+
type: 'doc-open',
|
|
8088
|
+
table: parentTable,
|
|
8089
|
+
prop: parentProp,
|
|
8090
|
+
k: parentId,
|
|
8091
|
+
serverRev: yServerRev,
|
|
8092
|
+
};
|
|
8093
|
+
const serverUpdatesSinceLastSync = yield yTbl
|
|
8094
|
+
.where('i')
|
|
8095
|
+
.between(receivedUntil, Infinity, false)
|
|
8096
|
+
.filter((update) => Dexie.cmp(update.k, parentId) === 0 && // Only updates for this document
|
|
8097
|
+
((update.f || 0) & 1) === 0 // Don't include local changes
|
|
8098
|
+
)
|
|
8099
|
+
.toArray();
|
|
8100
|
+
// After every await, check if we still should be working on this task.
|
|
8101
|
+
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
8102
|
+
return;
|
|
8103
|
+
if (serverUpdatesSinceLastSync.length > 0) {
|
|
8104
|
+
const Y = $Y$1(db); // Get the Yjs library from Dexie constructor options
|
|
8105
|
+
const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
|
|
8106
|
+
const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
|
|
8107
|
+
docOpenMsg.sv = stateVector;
|
|
7862
8108
|
}
|
|
8109
|
+
db.messageProducer.next(docOpenMsg);
|
|
7863
8110
|
});
|
|
7864
|
-
|
|
7865
|
-
|
|
7866
|
-
|
|
7867
|
-
* connected, or if it is already connected.
|
|
7868
|
-
* The flow is aborted in case websocket is disconnected while querying
|
|
7869
|
-
* information required to compute the state vector. Flow is also
|
|
7870
|
-
* aborted in case document or provider has been destroyed during
|
|
7871
|
-
* the async parts of the task.
|
|
7872
|
-
*
|
|
7873
|
-
* The state vector is only computed from the updates that have occured
|
|
7874
|
-
* after the last full sync - which could very often be zero - in which
|
|
7875
|
-
* case no state vector is sent (then the server already knows us by
|
|
7876
|
-
* revision)
|
|
7877
|
-
*
|
|
7878
|
-
* When server gets the doc-open message, it will authorized us for
|
|
7879
|
-
* whether we are allowed to read / write to this document, and then
|
|
7880
|
-
* keep the cached information in memory on the WS connection for this
|
|
7881
|
-
* particular document, as well as subscribe to updates and awareness updates
|
|
7882
|
-
* from other clients on the document.
|
|
7883
|
-
*/
|
|
7884
|
-
function openDocumentOnServer(wsStatus) {
|
|
7885
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
7886
|
-
const myFlow = currentFlowId; // So we can abort when a new flow is started
|
|
7887
|
-
const yTbl = db.table(updatesTable);
|
|
7888
|
-
const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
|
|
7889
|
-
// After every await, check if we still should be working on this task.
|
|
7890
|
-
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7891
|
-
return;
|
|
7892
|
-
const receivedUntil = (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0;
|
|
7893
|
-
const docOpenMsg = {
|
|
7894
|
-
type: 'doc-open',
|
|
7895
|
-
table: parentTable,
|
|
7896
|
-
prop: parentProp,
|
|
7897
|
-
k: parentId,
|
|
7898
|
-
serverRev: syncState === null || syncState === void 0 ? void 0 : syncState.serverRev,
|
|
7899
|
-
};
|
|
7900
|
-
const serverUpdatesSinceLastSync = yield yTbl
|
|
7901
|
-
.where('i')
|
|
7902
|
-
.between(receivedUntil, Infinity, false)
|
|
7903
|
-
.filter((update) => Dexie.cmp(update.k, parentId) === 0 && // Only updates for this document
|
|
7904
|
-
((update.f || 0) & 1) === 0 // Don't include local changes
|
|
7905
|
-
)
|
|
7906
|
-
.toArray();
|
|
7907
|
-
// After every await, check if we still should be working on this task.
|
|
7908
|
-
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7909
|
-
return;
|
|
7910
|
-
if (serverUpdatesSinceLastSync.length > 0) {
|
|
7911
|
-
const Y = $Y(db); // Get the Yjs library from Dexie constructor options
|
|
7912
|
-
const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
|
|
7913
|
-
const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
|
|
7914
|
-
docOpenMsg.sv = stateVector;
|
|
7915
|
-
}
|
|
7916
|
-
db.messageProducer.next(docOpenMsg);
|
|
7917
|
-
});
|
|
7918
|
-
}
|
|
7919
|
-
}));
|
|
7920
|
-
};
|
|
8111
|
+
}
|
|
8112
|
+
}))();
|
|
8113
|
+
return awareness;
|
|
7921
8114
|
}
|
|
7922
8115
|
|
|
7923
8116
|
function getTiedRealmId(objectId) {
|
|
@@ -7967,7 +8160,7 @@
|
|
|
7967
8160
|
const syncComplete = new rxjs.Subject();
|
|
7968
8161
|
dexie.cloud = {
|
|
7969
8162
|
// @ts-ignore
|
|
7970
|
-
version: "4.1.0-
|
|
8163
|
+
version: "4.1.0-beta.25",
|
|
7971
8164
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
7972
8165
|
schema: null,
|
|
7973
8166
|
get currentUserId() {
|
|
@@ -8003,16 +8196,16 @@
|
|
|
8003
8196
|
}
|
|
8004
8197
|
updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
|
|
8005
8198
|
},
|
|
8006
|
-
logout(
|
|
8007
|
-
return __awaiter(this,
|
|
8199
|
+
logout() {
|
|
8200
|
+
return __awaiter(this, arguments, void 0, function* ({ force } = {}) {
|
|
8008
8201
|
force
|
|
8009
8202
|
? yield _logout(DexieCloudDB(dexie), { deleteUnsyncedData: true })
|
|
8010
8203
|
: yield logout(DexieCloudDB(dexie));
|
|
8011
8204
|
});
|
|
8012
8205
|
},
|
|
8013
|
-
sync(
|
|
8014
|
-
|
|
8015
|
-
|
|
8206
|
+
sync() {
|
|
8207
|
+
return __awaiter(this, arguments, void 0, function* ({ wait, purpose } = { wait: true, purpose: 'push' }) {
|
|
8208
|
+
var _a;
|
|
8016
8209
|
if (wait === undefined)
|
|
8017
8210
|
wait = true;
|
|
8018
8211
|
const db = DexieCloudDB(dexie);
|
|
@@ -8070,8 +8263,8 @@
|
|
|
8070
8263
|
dexie.use(createImplicitPropSetterMiddleware(DexieCloudDB(dexie)));
|
|
8071
8264
|
dexie.use(createIdGenerationMiddleware(DexieCloudDB(dexie)));
|
|
8072
8265
|
function onDbReady(dexie) {
|
|
8073
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
8074
8266
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8267
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
8075
8268
|
closed = false; // As Dexie calls us, we are not closed anymore. Maybe reopened? Remember db.ready event is registered with sticky flag!
|
|
8076
8269
|
const db = DexieCloudDB(dexie);
|
|
8077
8270
|
// Setup default GUI:
|
|
@@ -8094,7 +8287,7 @@
|
|
|
8094
8287
|
? yield navigator.serviceWorker.getRegistrations()
|
|
8095
8288
|
: [];
|
|
8096
8289
|
const [initiallySynced, lastSyncedRealms] = yield db.transaction('rw', db.$syncState, () => __awaiter(this, void 0, void 0, function* () {
|
|
8097
|
-
var
|
|
8290
|
+
var _a, _b;
|
|
8098
8291
|
const { options, schema } = db.cloud;
|
|
8099
8292
|
const [persistedOptions, persistedSchema, persistedSyncState] = yield Promise.all([
|
|
8100
8293
|
db.getOptions(),
|
|
@@ -8116,7 +8309,7 @@
|
|
|
8116
8309
|
delete newPersistedOptions.awarenessProtocol;
|
|
8117
8310
|
yield db.$syncState.put(newPersistedOptions, 'options');
|
|
8118
8311
|
}
|
|
8119
|
-
if (((
|
|
8312
|
+
if (((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.tryUseServiceWorker) &&
|
|
8120
8313
|
'serviceWorker' in navigator &&
|
|
8121
8314
|
swRegistrations.length > 0 &&
|
|
8122
8315
|
!DISABLE_SERVICEWORKER_STRATEGY) {
|
|
@@ -8130,7 +8323,7 @@
|
|
|
8130
8323
|
// Not configured for using service worker or no service worker
|
|
8131
8324
|
// registration exists. Don't rely on service worker to do any job.
|
|
8132
8325
|
// Use LocalSyncWorker instead.
|
|
8133
|
-
if (((
|
|
8326
|
+
if (((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.tryUseServiceWorker) &&
|
|
8134
8327
|
!db.cloud.isServiceWorkerDB) {
|
|
8135
8328
|
console.debug('dexie-cloud-addon: Not using service worker.', swRegistrations.length === 0
|
|
8136
8329
|
? 'No SW registrations found.'
|
|
@@ -8173,10 +8366,6 @@
|
|
|
8173
8366
|
db.setInitiallySynced(true);
|
|
8174
8367
|
}
|
|
8175
8368
|
verifySchema(db);
|
|
8176
|
-
if (((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.databaseUrl) && !initiallySynced) {
|
|
8177
|
-
yield performInitialSync(db, db.cloud.options, db.cloud.schema);
|
|
8178
|
-
db.setInitiallySynced(true);
|
|
8179
|
-
}
|
|
8180
8369
|
// Manage CurrentUser observable:
|
|
8181
8370
|
throwIfClosed();
|
|
8182
8371
|
if (!db.cloud.isServiceWorkerDB) {
|
|
@@ -8201,20 +8390,29 @@
|
|
|
8201
8390
|
// HERE: If requireAuth, do athentication now.
|
|
8202
8391
|
let changedUser = false;
|
|
8203
8392
|
const user = yield db.getCurrentUser();
|
|
8204
|
-
const requireAuth = (
|
|
8393
|
+
const requireAuth = (_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.requireAuth;
|
|
8205
8394
|
if (requireAuth) {
|
|
8206
|
-
if (
|
|
8207
|
-
//
|
|
8208
|
-
|
|
8209
|
-
|
|
8210
|
-
|
|
8211
|
-
|
|
8212
|
-
changedUser = yield login(db, requireAuth);
|
|
8213
|
-
}
|
|
8395
|
+
if (db.cloud.isServiceWorkerDB) {
|
|
8396
|
+
// If this is a service worker DB, we can't do authentication here,
|
|
8397
|
+
// we just wait until the application has done it.
|
|
8398
|
+
console.debug('Dexie Cloud Service worker. Waiting for application to authenticate.');
|
|
8399
|
+
yield rxjs.firstValueFrom(currentUserEmitter.pipe(filter((user) => !!user.isLoggedIn), take(1)));
|
|
8400
|
+
console.debug('Dexie Cloud Service worker. Application has authenticated.');
|
|
8214
8401
|
}
|
|
8215
|
-
else
|
|
8216
|
-
|
|
8217
|
-
|
|
8402
|
+
else {
|
|
8403
|
+
if (typeof requireAuth === 'object') {
|
|
8404
|
+
// requireAuth contains login hints. Check if we already fulfil it:
|
|
8405
|
+
if (!user.isLoggedIn ||
|
|
8406
|
+
(requireAuth.userId && user.userId !== requireAuth.userId) ||
|
|
8407
|
+
(requireAuth.email && user.email !== requireAuth.email)) {
|
|
8408
|
+
// If not, login the configured user:
|
|
8409
|
+
changedUser = yield login(db, requireAuth);
|
|
8410
|
+
}
|
|
8411
|
+
}
|
|
8412
|
+
else if (!user.isLoggedIn) {
|
|
8413
|
+
// requireAuth is true and user is not logged in
|
|
8414
|
+
changedUser = yield login(db);
|
|
8415
|
+
}
|
|
8218
8416
|
}
|
|
8219
8417
|
}
|
|
8220
8418
|
if (user.isLoggedIn && (!lastSyncedRealms || !lastSyncedRealms.includes(user.userId))) {
|
|
@@ -8229,8 +8427,17 @@
|
|
|
8229
8427
|
localSyncWorker.stop();
|
|
8230
8428
|
localSyncWorker = null;
|
|
8231
8429
|
throwIfClosed();
|
|
8430
|
+
const doInitialSync = ((_c = db.cloud.options) === null || _c === void 0 ? void 0 : _c.databaseUrl) && (!initiallySynced || changedUser);
|
|
8431
|
+
if (doInitialSync) {
|
|
8432
|
+
// Do the initial sync directly in the browser thread no matter if we are using service worker or not.
|
|
8433
|
+
yield performInitialSync(db, db.cloud.options, db.cloud.schema);
|
|
8434
|
+
db.setInitiallySynced(true);
|
|
8435
|
+
}
|
|
8436
|
+
throwIfClosed();
|
|
8232
8437
|
if (db.cloud.usingServiceWorker && ((_d = db.cloud.options) === null || _d === void 0 ? void 0 : _d.databaseUrl)) {
|
|
8233
|
-
|
|
8438
|
+
if (!doInitialSync) {
|
|
8439
|
+
registerSyncEvent(db, 'push').catch(() => { });
|
|
8440
|
+
}
|
|
8234
8441
|
registerPeriodicSyncEvent(db).catch(() => { });
|
|
8235
8442
|
}
|
|
8236
8443
|
else if (((_e = db.cloud.options) === null || _e === void 0 ? void 0 : _e.databaseUrl) &&
|
|
@@ -8239,7 +8446,9 @@
|
|
|
8239
8446
|
// There's no SW. Start SyncWorker instead.
|
|
8240
8447
|
localSyncWorker = LocalSyncWorker(db, db.cloud.options, db.cloud.schema);
|
|
8241
8448
|
localSyncWorker.start();
|
|
8242
|
-
|
|
8449
|
+
if (!doInitialSync) {
|
|
8450
|
+
triggerSync(db, 'push');
|
|
8451
|
+
}
|
|
8243
8452
|
}
|
|
8244
8453
|
// Listen to online event and do sync.
|
|
8245
8454
|
throwIfClosed();
|
|
@@ -8259,7 +8468,7 @@
|
|
|
8259
8468
|
});
|
|
8260
8469
|
}));
|
|
8261
8470
|
}
|
|
8262
|
-
// Connect WebSocket unless we
|
|
8471
|
+
// Connect WebSocket unless we are in a service worker or websocket is disabled.
|
|
8263
8472
|
if (((_f = db.cloud.options) === null || _f === void 0 ? void 0 : _f.databaseUrl) &&
|
|
8264
8473
|
!((_g = db.cloud.options) === null || _g === void 0 ? void 0 : _g.disableWebSocket) &&
|
|
8265
8474
|
!IS_SERVICE_WORKER) {
|
|
@@ -8269,10 +8478,167 @@
|
|
|
8269
8478
|
}
|
|
8270
8479
|
}
|
|
8271
8480
|
// @ts-ignore
|
|
8272
|
-
dexieCloud.version = "4.1.0-
|
|
8481
|
+
dexieCloud.version = "4.1.0-beta.25";
|
|
8273
8482
|
Dexie.Cloud = dexieCloud;
|
|
8274
8483
|
|
|
8484
|
+
const ydocTriggers = {};
|
|
8485
|
+
const docIsAlreadyHooked = new WeakSet();
|
|
8486
|
+
const middlewares = new WeakMap();
|
|
8487
|
+
const createMiddleware = (db) => ({
|
|
8488
|
+
stack: 'dbcore',
|
|
8489
|
+
level: 10,
|
|
8490
|
+
name: 'yTriggerMiddleware',
|
|
8491
|
+
create: (down) => {
|
|
8492
|
+
return Object.assign(Object.assign({}, down), { transaction: (stores, mode, options) => {
|
|
8493
|
+
const idbtrans = down.transaction(stores, mode, options);
|
|
8494
|
+
idbtrans.addEventListener('complete', onTransactionCommitted);
|
|
8495
|
+
return idbtrans;
|
|
8496
|
+
}, table: (tblName) => {
|
|
8497
|
+
const coreTable = down.table(tblName);
|
|
8498
|
+
const triggerSpec = ydocTriggers[tblName];
|
|
8499
|
+
if (!triggerSpec)
|
|
8500
|
+
return coreTable;
|
|
8501
|
+
const { trigger, parentTable, prop } = triggerSpec;
|
|
8502
|
+
return Object.assign(Object.assign({}, coreTable), { mutate(req) {
|
|
8503
|
+
switch (req.type) {
|
|
8504
|
+
case 'add': {
|
|
8505
|
+
for (const yUpdateRow of req.values) {
|
|
8506
|
+
if (yUpdateRow.k == undefined)
|
|
8507
|
+
continue; // A syncer or garbage collection state does not point to a key
|
|
8508
|
+
const primaryKey = yUpdateRow.k;
|
|
8509
|
+
const doc = Dexie.DexieYProvider.getDocCache(db).find(parentTable, primaryKey, prop);
|
|
8510
|
+
if (doc) {
|
|
8511
|
+
if (!docIsAlreadyHooked.has(doc)) {
|
|
8512
|
+
hookToDoc(doc, primaryKey, trigger);
|
|
8513
|
+
docIsAlreadyHooked.add(doc);
|
|
8514
|
+
}
|
|
8515
|
+
}
|
|
8516
|
+
else {
|
|
8517
|
+
enqueueTrigger(db, tblName, primaryKey, trigger);
|
|
8518
|
+
}
|
|
8519
|
+
}
|
|
8520
|
+
break;
|
|
8521
|
+
}
|
|
8522
|
+
case 'delete':
|
|
8523
|
+
// @ts-ignore
|
|
8524
|
+
if (req.trans._rejecting_y_ypdate) {
|
|
8525
|
+
// The deletion came from a rejection, not garbage collection.
|
|
8526
|
+
// When that happens, let the triggers run to compute new values
|
|
8527
|
+
// based on the deleted updates.
|
|
8528
|
+
coreTable
|
|
8529
|
+
.getMany({
|
|
8530
|
+
keys: req.keys,
|
|
8531
|
+
trans: req.trans,
|
|
8532
|
+
cache: 'immutable',
|
|
8533
|
+
})
|
|
8534
|
+
.then((updates) => {
|
|
8535
|
+
const keySet = new Dexie.RangeSet();
|
|
8536
|
+
for (const { k } of updates) {
|
|
8537
|
+
if (k != undefined)
|
|
8538
|
+
keySet.addKey(k);
|
|
8539
|
+
}
|
|
8540
|
+
for (const interval of keySet) {
|
|
8541
|
+
enqueueTrigger(db, tblName, interval.from, trigger);
|
|
8542
|
+
}
|
|
8543
|
+
});
|
|
8544
|
+
}
|
|
8545
|
+
break;
|
|
8546
|
+
}
|
|
8547
|
+
return coreTable.mutate(req);
|
|
8548
|
+
} });
|
|
8549
|
+
} });
|
|
8550
|
+
},
|
|
8551
|
+
});
|
|
8552
|
+
let triggerExecPromise = null;
|
|
8553
|
+
let triggerScheduled = false;
|
|
8554
|
+
let scheduledTriggers = [];
|
|
8555
|
+
function $Y(db) {
|
|
8556
|
+
const $Y = db._options.Y;
|
|
8557
|
+
if (!$Y)
|
|
8558
|
+
throw new Error('Y library not supplied to Dexie constructor');
|
|
8559
|
+
return $Y;
|
|
8560
|
+
}
|
|
8561
|
+
function executeTriggers(triggersToRun) {
|
|
8562
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8563
|
+
for (const { db, parentId, trigger, updatesTable } of triggersToRun) {
|
|
8564
|
+
// Load entire document into an Y.Doc instance:
|
|
8565
|
+
const updates = yield db
|
|
8566
|
+
.table(updatesTable)
|
|
8567
|
+
.where({ k: parentId })
|
|
8568
|
+
.toArray();
|
|
8569
|
+
const Y = $Y(db);
|
|
8570
|
+
const yDoc = new Y.Doc();
|
|
8571
|
+
for (const update of updates) {
|
|
8572
|
+
Y.applyUpdateV2(yDoc, update.u);
|
|
8573
|
+
}
|
|
8574
|
+
try {
|
|
8575
|
+
yield trigger(yDoc, parentId);
|
|
8576
|
+
}
|
|
8577
|
+
catch (error) {
|
|
8578
|
+
console.error(`Error in YDocTrigger ${error}`);
|
|
8579
|
+
}
|
|
8580
|
+
}
|
|
8581
|
+
});
|
|
8582
|
+
}
|
|
8583
|
+
function enqueueTrigger(db, updatesTable, parentId, trigger) {
|
|
8584
|
+
scheduledTriggers.push({
|
|
8585
|
+
db,
|
|
8586
|
+
updatesTable,
|
|
8587
|
+
parentId,
|
|
8588
|
+
trigger,
|
|
8589
|
+
});
|
|
8590
|
+
}
|
|
8591
|
+
function onTransactionCommitted() {
|
|
8592
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8593
|
+
if (!triggerScheduled && scheduledTriggers.length > 0) {
|
|
8594
|
+
triggerScheduled = true;
|
|
8595
|
+
if (triggerExecPromise)
|
|
8596
|
+
yield triggerExecPromise.catch(() => { });
|
|
8597
|
+
setTimeout(() => {
|
|
8598
|
+
// setTimeout() is to escape from Promise.PSD zones and never run within liveQueries or transaction scopes
|
|
8599
|
+
triggerScheduled = false;
|
|
8600
|
+
const triggersToRun = scheduledTriggers;
|
|
8601
|
+
scheduledTriggers = [];
|
|
8602
|
+
triggerExecPromise = executeTriggers(triggersToRun).finally(() => (triggerExecPromise = null));
|
|
8603
|
+
}, 0);
|
|
8604
|
+
}
|
|
8605
|
+
});
|
|
8606
|
+
}
|
|
8607
|
+
function hookToDoc(doc, parentId, trigger) {
|
|
8608
|
+
// From now on, keep listening to doc updates and execute the trigger when it happens there instead
|
|
8609
|
+
doc.on('updateV2', (update, origin) => {
|
|
8610
|
+
//Dexie.ignoreTransaction(()=>{
|
|
8611
|
+
trigger(doc, parentId);
|
|
8612
|
+
//});
|
|
8613
|
+
});
|
|
8614
|
+
/*
|
|
8615
|
+
NOT NEEDED because DexieYProvider's docCache will also listen to destroy and remove it from its cache:
|
|
8616
|
+
doc.on('destroy', ()=>{
|
|
8617
|
+
docIsAlreadyHooked.delete(doc);
|
|
8618
|
+
})
|
|
8619
|
+
*/
|
|
8620
|
+
}
|
|
8621
|
+
function defineYDocTrigger(table, prop, trigger) {
|
|
8622
|
+
var _a, _b;
|
|
8623
|
+
const updatesTable = (_b = (_a = table.schema.yProps) === null || _a === void 0 ? void 0 : _a.find((p) => p.prop === prop)) === null || _b === void 0 ? void 0 : _b.updatesTable;
|
|
8624
|
+
if (!updatesTable)
|
|
8625
|
+
throw new Error(`Table ${table.name} does not have a Yjs property named ${prop}`);
|
|
8626
|
+
ydocTriggers[updatesTable] = {
|
|
8627
|
+
trigger,
|
|
8628
|
+
parentTable: table.name,
|
|
8629
|
+
prop,
|
|
8630
|
+
};
|
|
8631
|
+
const db = table.db._novip;
|
|
8632
|
+
let mw = middlewares.get(db);
|
|
8633
|
+
if (!mw) {
|
|
8634
|
+
mw = createMiddleware(db);
|
|
8635
|
+
middlewares.set(db, mw);
|
|
8636
|
+
}
|
|
8637
|
+
db.use(mw);
|
|
8638
|
+
}
|
|
8639
|
+
|
|
8275
8640
|
exports.default = dexieCloud;
|
|
8641
|
+
exports.defineYDocTrigger = defineYDocTrigger;
|
|
8276
8642
|
exports.dexieCloud = dexieCloud;
|
|
8277
8643
|
exports.getTiedObjectId = getTiedObjectId;
|
|
8278
8644
|
exports.getTiedRealmId = getTiedRealmId;
|