dexie-cloud-addon 4.1.0-alpha.2 → 4.1.0-alpha.21
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/WSObservable.d.ts +4 -4
- 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 +584 -237
- 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 +426 -235
- 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/applyYMessages.d.ts +4 -1
- package/dist/modern/yjs/listYClientMessagesAndStateVector.d.ts +3 -1
- package/dist/umd/DexieCloudOptions.d.ts +3 -0
- package/dist/umd/TSON.d.ts +1 -1
- package/dist/umd/WSObservable.d.ts +4 -4
- 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 +582 -234
- 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 +424 -233
- 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/applyYMessages.d.ts +4 -1
- package/dist/umd/yjs/listYClientMessagesAndStateVector.d.ts +3 -1
- package/package.json +4 -4
- package/dist/modern/yjs/listYClientMessages.d.ts +0 -3
- package/dist/umd/yjs/listYClientMessages.d.ts +0 -3
- /package/dist/modern/yjs/{y.d.ts → Y.d.ts} +0 -0
- /package/dist/umd/yjs/{y.d.ts → Y.d.ts} +0 -0
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* ==========================================================================
|
|
10
10
|
*
|
|
11
|
-
* Version 4.1.0-alpha.
|
|
11
|
+
* Version 4.1.0-alpha.21, Mon Nov 18 2024
|
|
12
12
|
*
|
|
13
13
|
* https://dexie.org
|
|
14
14
|
*
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
*
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import Dexie, { PropModification, cmp, DexieYProvider, liveQuery } from 'dexie';
|
|
20
|
-
import { firstValueFrom, from as from$1, filter as filter$1, Observable as Observable$1, BehaviorSubject, Subject, fromEvent, of, merge, mergeMap as mergeMap$1, Subscription as Subscription$1, throwError, combineLatest, map as map$1, share, timer as timer$1 } from 'rxjs';
|
|
19
|
+
import Dexie, { PropModification, cmp, DexieYProvider, RangeSet, liveQuery } from 'dexie';
|
|
20
|
+
import { firstValueFrom, from as from$1, filter as filter$1, Observable as Observable$1, BehaviorSubject, Subject, fromEvent, of, merge, switchMap as switchMap$1, tap as tap$1, mergeMap as mergeMap$1, Subscription as Subscription$1, throwError, combineLatest, map as map$1, share, timer as timer$1 } from 'rxjs';
|
|
21
21
|
|
|
22
22
|
/******************************************************************************
|
|
23
23
|
Copyright (c) Microsoft Corporation.
|
|
@@ -979,13 +979,17 @@ const writeAny = (encoder, data) => {
|
|
|
979
979
|
function encodeYMessage(msg) {
|
|
980
980
|
const encoder = new Encoder();
|
|
981
981
|
writeVarString(encoder, msg.type);
|
|
982
|
-
|
|
983
|
-
|
|
982
|
+
if ('table' in msg)
|
|
983
|
+
writeVarString(encoder, msg.table);
|
|
984
|
+
if ('prop' in msg)
|
|
985
|
+
writeVarString(encoder, msg.prop);
|
|
984
986
|
switch (msg.type) {
|
|
985
987
|
case 'u-ack':
|
|
986
988
|
case 'u-reject':
|
|
987
989
|
writeBigUint64(encoder, BigInt(msg.i));
|
|
988
990
|
break;
|
|
991
|
+
case 'outdated-server-rev':
|
|
992
|
+
break;
|
|
989
993
|
default:
|
|
990
994
|
writeAny(encoder, msg.k);
|
|
991
995
|
switch (msg.type) {
|
|
@@ -1329,6 +1333,9 @@ const readAny = decoder => readAnyLookupTable[127 - readUint8(decoder)](decoder)
|
|
|
1329
1333
|
function decodeYMessage(a) {
|
|
1330
1334
|
const decoder = new Decoder(a);
|
|
1331
1335
|
const type = readVarString(decoder);
|
|
1336
|
+
if (type === 'outdated-server-rev') {
|
|
1337
|
+
return { type };
|
|
1338
|
+
}
|
|
1332
1339
|
const table = readVarString(decoder);
|
|
1333
1340
|
const prop = readVarString(decoder);
|
|
1334
1341
|
switch (type) {
|
|
@@ -2987,8 +2994,8 @@ function registerSyncEvent(db, purpose) {
|
|
|
2987
2994
|
});
|
|
2988
2995
|
}
|
|
2989
2996
|
function registerPeriodicSyncEvent(db) {
|
|
2990
|
-
var _a;
|
|
2991
2997
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2998
|
+
var _a;
|
|
2992
2999
|
try {
|
|
2993
3000
|
// Register periodicSync event to SW:
|
|
2994
3001
|
// @ts-ignore
|
|
@@ -3199,8 +3206,8 @@ function confirmLogout(userInteraction, currentUserId, numUnsyncedChanges) {
|
|
|
3199
3206
|
}
|
|
3200
3207
|
|
|
3201
3208
|
function loadAccessToken(db) {
|
|
3202
|
-
var _a, _b, _c;
|
|
3203
3209
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3210
|
+
var _a, _b, _c;
|
|
3204
3211
|
const currentUser = yield db.getCurrentUser();
|
|
3205
3212
|
const { accessToken, accessTokenExpiration, refreshToken, refreshTokenExpiration, claims, } = currentUser;
|
|
3206
3213
|
if (!accessToken)
|
|
@@ -3472,8 +3479,8 @@ function logout(db) {
|
|
|
3472
3479
|
}
|
|
3473
3480
|
});
|
|
3474
3481
|
}
|
|
3475
|
-
function _logout(
|
|
3476
|
-
return __awaiter(this,
|
|
3482
|
+
function _logout(db_1) {
|
|
3483
|
+
return __awaiter(this, arguments, void 0, function* (db, { deleteUnsyncedData = false } = {}) {
|
|
3477
3484
|
// Clear the database without emptying configuration options.
|
|
3478
3485
|
const [numUnsynced, loggedOut] = yield db.dx.transaction('rw', db.dx.tables, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
3479
3486
|
// @ts-ignore
|
|
@@ -3521,11 +3528,11 @@ class HttpError extends Error {
|
|
|
3521
3528
|
|
|
3522
3529
|
function otpFetchTokenCallback(db) {
|
|
3523
3530
|
const { userInteraction } = db.cloud;
|
|
3524
|
-
return function otpAuthenticate(
|
|
3525
|
-
|
|
3526
|
-
|
|
3531
|
+
return function otpAuthenticate(_a) {
|
|
3532
|
+
return __awaiter(this, arguments, void 0, function* ({ public_key, hints }) {
|
|
3533
|
+
var _b;
|
|
3527
3534
|
let tokenRequest;
|
|
3528
|
-
const url = (
|
|
3535
|
+
const url = (_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.databaseUrl;
|
|
3529
3536
|
if (!url)
|
|
3530
3537
|
throw new Error(`No database URL given.`);
|
|
3531
3538
|
if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'demo') {
|
|
@@ -3691,8 +3698,8 @@ function setCurrentUser(db, user) {
|
|
|
3691
3698
|
}
|
|
3692
3699
|
|
|
3693
3700
|
function login(db, hints) {
|
|
3694
|
-
var _a;
|
|
3695
3701
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3702
|
+
var _a;
|
|
3696
3703
|
const currentUser = yield db.getCurrentUser();
|
|
3697
3704
|
const origUserId = currentUser.userId;
|
|
3698
3705
|
if (currentUser.isLoggedIn && (!hints || (!hints.email && !hints.userId))) {
|
|
@@ -3880,8 +3887,8 @@ class BroadcastedAndLocalEvent extends Observable$1 {
|
|
|
3880
3887
|
}
|
|
3881
3888
|
}
|
|
3882
3889
|
|
|
3883
|
-
function computeRealmSetHash(
|
|
3884
|
-
return __awaiter(this,
|
|
3890
|
+
function computeRealmSetHash(_a) {
|
|
3891
|
+
return __awaiter(this, arguments, void 0, function* ({ realms, inviteRealms, }) {
|
|
3885
3892
|
const data = JSON.stringify([
|
|
3886
3893
|
...realms.map((realmId) => ({ realmId, accepted: true })),
|
|
3887
3894
|
...inviteRealms.map((realmId) => ({ realmId, accepted: false })),
|
|
@@ -3917,8 +3924,8 @@ function flatten(a) {
|
|
|
3917
3924
|
return concat.apply([], a);
|
|
3918
3925
|
}
|
|
3919
3926
|
|
|
3920
|
-
function listClientChanges(
|
|
3921
|
-
return __awaiter(this,
|
|
3927
|
+
function listClientChanges(mutationTables_1, db_1) {
|
|
3928
|
+
return __awaiter(this, arguments, void 0, function* (mutationTables, db, { since = {}, limit = Infinity } = {}) {
|
|
3922
3929
|
const allMutsOnTables = yield Promise.all(mutationTables.map((mutationTable) => __awaiter(this, void 0, void 0, function* () {
|
|
3923
3930
|
const tableName = getTableFromMutationTable(mutationTable.name);
|
|
3924
3931
|
const lastRevision = since[tableName];
|
|
@@ -4653,8 +4660,8 @@ function cloneChange(change, rewriteValues) {
|
|
|
4653
4660
|
// seconds (given that there is a Ratelimit-Reset header).
|
|
4654
4661
|
let syncRatelimitDelays = new WeakMap();
|
|
4655
4662
|
function checkSyncRateLimitDelay(db) {
|
|
4656
|
-
var _a, _b;
|
|
4657
4663
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4664
|
+
var _a, _b;
|
|
4658
4665
|
const delatMilliseconds = ((_b = (_a = syncRatelimitDelays.get(db)) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) - Date.now();
|
|
4659
4666
|
if (delatMilliseconds > 0) {
|
|
4660
4667
|
console.debug(`Stalling sync request ${delatMilliseconds} ms to spare ratelimits`);
|
|
@@ -4944,7 +4951,7 @@ function listUpdatesSince(yTable, sinceIncluding) {
|
|
|
4944
4951
|
.toArray();
|
|
4945
4952
|
}
|
|
4946
4953
|
|
|
4947
|
-
function $Y(db) {
|
|
4954
|
+
function $Y$1(db) {
|
|
4948
4955
|
const $Y = db.dx._options.Y;
|
|
4949
4956
|
if (!$Y)
|
|
4950
4957
|
throw new Error('Y library not supplied to Dexie constructor');
|
|
@@ -4967,15 +4974,14 @@ function $Y(db) {
|
|
|
4967
4974
|
* @param db
|
|
4968
4975
|
* @returns
|
|
4969
4976
|
*/
|
|
4970
|
-
function listYClientMessagesAndStateVector(db) {
|
|
4971
|
-
var _a;
|
|
4977
|
+
function listYClientMessagesAndStateVector(db, tablesToSync) {
|
|
4972
4978
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4973
4979
|
const result = [];
|
|
4974
4980
|
const lastUpdateIds = {};
|
|
4975
|
-
for (const table of
|
|
4976
|
-
if (table.schema.yProps
|
|
4981
|
+
for (const table of tablesToSync) {
|
|
4982
|
+
if (table.schema.yProps) {
|
|
4977
4983
|
for (const yProp of table.schema.yProps) {
|
|
4978
|
-
const Y = $Y(db); // This is how we retrieve the user-provided Y library
|
|
4984
|
+
const Y = $Y$1(db); // This is how we retrieve the user-provided Y library
|
|
4979
4985
|
const yTable = db.table(yProp.updatesTable); // the updates-table for this combo of table+propName
|
|
4980
4986
|
const syncState = (yield yTable.get(DEXIE_CLOUD_SYNCER_ID));
|
|
4981
4987
|
// unsentFrom = the `i` value of updates that aren't yet sent to server (or at least not acked by the server yet)
|
|
@@ -5060,12 +5066,14 @@ function getUpdatesTable(db, table, ydocProp) {
|
|
|
5060
5066
|
|
|
5061
5067
|
function applyYServerMessages(yMessages, db) {
|
|
5062
5068
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5063
|
-
|
|
5069
|
+
var _a;
|
|
5070
|
+
const receivedUntils = {};
|
|
5071
|
+
let resyncNeeded = false;
|
|
5064
5072
|
for (const m of yMessages) {
|
|
5065
5073
|
switch (m.type) {
|
|
5066
5074
|
case 'u-s': {
|
|
5067
5075
|
const utbl = getUpdatesTable(db, m.table, m.prop);
|
|
5068
|
-
|
|
5076
|
+
receivedUntils[utbl.name] = yield utbl.add({
|
|
5069
5077
|
k: m.k,
|
|
5070
5078
|
u: m.u,
|
|
5071
5079
|
});
|
|
@@ -5090,7 +5098,24 @@ function applyYServerMessages(yMessages, db) {
|
|
|
5090
5098
|
// See my question in https://discuss.yjs.dev/t/generate-an-inverse-update/2765
|
|
5091
5099
|
console.debug(`Y update rejected. Deleting it.`);
|
|
5092
5100
|
const utbl = getUpdatesTable(db, m.table, m.prop);
|
|
5093
|
-
|
|
5101
|
+
// Delete the rejected update and all local updates since (avoid holes in the CRDT)
|
|
5102
|
+
// and destroy it's open document if there is one.
|
|
5103
|
+
const primaryKey = (_a = (yield utbl.get(m.i))) === null || _a === void 0 ? void 0 : _a.k;
|
|
5104
|
+
if (primaryKey != null) {
|
|
5105
|
+
yield db.transaction('rw', utbl, (tx) => {
|
|
5106
|
+
// @ts-ignore
|
|
5107
|
+
tx.idbtrans._rejecting_y_ypdate = true; // Inform ydoc triggers that we delete because of a rejection and not GC
|
|
5108
|
+
return utbl
|
|
5109
|
+
.where('i')
|
|
5110
|
+
.aboveOrEqual(m.i)
|
|
5111
|
+
.filter((u) => cmp(u.k, primaryKey) === 0 && ((u.f || 0) & 1) === 1)
|
|
5112
|
+
.delete();
|
|
5113
|
+
});
|
|
5114
|
+
// Destroy active doc
|
|
5115
|
+
const activeDoc = DexieYProvider.getDocCache(db.dx).find(m.table, primaryKey, m.prop);
|
|
5116
|
+
if (activeDoc)
|
|
5117
|
+
activeDoc.destroy(); // Destroy the document so that editors don't continue to work on it
|
|
5118
|
+
}
|
|
5094
5119
|
break;
|
|
5095
5120
|
}
|
|
5096
5121
|
case 'in-sync': {
|
|
@@ -5100,22 +5125,28 @@ function applyYServerMessages(yMessages, db) {
|
|
|
5100
5125
|
}
|
|
5101
5126
|
break;
|
|
5102
5127
|
}
|
|
5128
|
+
case 'outdated-server-rev':
|
|
5129
|
+
resyncNeeded = true;
|
|
5130
|
+
break;
|
|
5103
5131
|
}
|
|
5104
5132
|
}
|
|
5105
|
-
return
|
|
5133
|
+
return {
|
|
5134
|
+
receivedUntils,
|
|
5135
|
+
resyncNeeded,
|
|
5136
|
+
};
|
|
5106
5137
|
});
|
|
5107
5138
|
}
|
|
5108
5139
|
|
|
5109
5140
|
function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db, serverRevision) {
|
|
5110
|
-
var _a, _b;
|
|
5111
5141
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5142
|
+
var _a, _b, _c, _d, _e;
|
|
5112
5143
|
// We want to update unsentFrom for each yTable to the value specified in first argument
|
|
5113
5144
|
// because we got those values before we synced with server and here we are back from server
|
|
5114
5145
|
// that has successfully received all those messages - no matter if the last update was a client or server update,
|
|
5115
5146
|
// we can safely store unsentFrom to a value of the last update + 1 here.
|
|
5116
5147
|
// We also want to update receivedUntil for each yTable to the value specified in the second argument,
|
|
5117
5148
|
// because that contains the highest resulted id of each update from server after storing it.
|
|
5118
|
-
// We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
|
|
5149
|
+
// We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
|
|
5119
5150
|
// to optimize the dexie calls, we merge these two maps into a single one so we can do a single update request
|
|
5120
5151
|
// per yTable.
|
|
5121
5152
|
const mergedSpec = {};
|
|
@@ -5127,28 +5158,42 @@ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db,
|
|
|
5127
5158
|
(_b = mergedSpec[yTable]) !== null && _b !== void 0 ? _b : (mergedSpec[yTable] = {});
|
|
5128
5159
|
mergedSpec[yTable].receivedUntil = lastUpdateId;
|
|
5129
5160
|
}
|
|
5130
|
-
// Now go through
|
|
5131
|
-
|
|
5161
|
+
// Now go through all yTables and update their YSyncStates:
|
|
5162
|
+
const allYTables = Object.values(db.dx._dbSchema)
|
|
5163
|
+
.filter((tblSchema) => tblSchema.yProps)
|
|
5164
|
+
.map((tblSchema) => tblSchema.yProps.map((yProp) => yProp.updatesTable))
|
|
5165
|
+
.flat();
|
|
5166
|
+
for (const yTable of allYTables) {
|
|
5167
|
+
const mergedEntry = mergedSpec[yTable];
|
|
5168
|
+
const unsentFrom = (_c = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.unsentFrom) !== null && _c !== void 0 ? _c : 1;
|
|
5169
|
+
const receivedUntil = (_e = (_d = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.receivedUntil) !== null && _d !== void 0 ? _d :
|
|
5170
|
+
// from local because we are in the same parent transaction (in sync.ts) that
|
|
5171
|
+
// applied all updates from the server
|
|
5172
|
+
(yield db
|
|
5173
|
+
.table(yTable)
|
|
5174
|
+
.where('i')
|
|
5175
|
+
.between(1, Infinity) // Because i might be string DEXIE_CLOUD_SYNCER_ID if not a number.
|
|
5176
|
+
.reverse()
|
|
5177
|
+
.limit(1)
|
|
5178
|
+
.primaryKeys())[0]) !== null && _e !== void 0 ? _e : 0;
|
|
5132
5179
|
// We're already in a transaction, but for the sake of
|
|
5133
5180
|
// code readability and correctness, let's launch an atomic sub transaction:
|
|
5134
5181
|
yield db.transaction('rw', yTable, () => __awaiter(this, void 0, void 0, function* () {
|
|
5135
|
-
const state = yield db
|
|
5182
|
+
const state = yield db
|
|
5183
|
+
.table(yTable)
|
|
5184
|
+
.get(DEXIE_CLOUD_SYNCER_ID);
|
|
5136
5185
|
if (!state) {
|
|
5137
5186
|
yield db.table(yTable).add({
|
|
5138
5187
|
i: DEXIE_CLOUD_SYNCER_ID,
|
|
5139
|
-
unsentFrom
|
|
5140
|
-
receivedUntil
|
|
5188
|
+
unsentFrom,
|
|
5189
|
+
receivedUntil,
|
|
5141
5190
|
serverRev: serverRevision,
|
|
5142
5191
|
});
|
|
5143
5192
|
}
|
|
5144
5193
|
else {
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
if (receivedUntil) {
|
|
5149
|
-
state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
|
|
5150
|
-
state.serverRev = serverRevision;
|
|
5151
|
-
}
|
|
5194
|
+
state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
|
|
5195
|
+
state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
|
|
5196
|
+
state.serverRev = serverRevision;
|
|
5152
5197
|
yield db.table(yTable).put(state);
|
|
5153
5198
|
}
|
|
5154
5199
|
}));
|
|
@@ -5159,9 +5204,11 @@ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db,
|
|
|
5159
5204
|
const BINSTREAM_TYPE_REALMID = 1;
|
|
5160
5205
|
const BINSTREAM_TYPE_TABLE_AND_PROP = 2;
|
|
5161
5206
|
const BINSTREAM_TYPE_DOCUMENT = 3;
|
|
5162
|
-
function downloadYDocsFromServer(
|
|
5163
|
-
return __awaiter(this,
|
|
5164
|
-
if (yDownloadedRealms &&
|
|
5207
|
+
function downloadYDocsFromServer(db_1, databaseUrl_1, _a) {
|
|
5208
|
+
return __awaiter(this, arguments, void 0, function* (db, databaseUrl, { yDownloadedRealms, realms }) {
|
|
5209
|
+
if (yDownloadedRealms &&
|
|
5210
|
+
realms &&
|
|
5211
|
+
realms.every((realmId) => yDownloadedRealms[realmId] === '*')) {
|
|
5165
5212
|
return; // Already done!
|
|
5166
5213
|
}
|
|
5167
5214
|
console.debug('Downloading Y.Docs from added realms');
|
|
@@ -5201,16 +5248,19 @@ function downloadYDocsFromServer(db, databaseUrl, { yDownloadedRealms, realms })
|
|
|
5201
5248
|
yield yTable.bulkAdd(docsToInsert);
|
|
5202
5249
|
docsToInsert = [];
|
|
5203
5250
|
}
|
|
5204
|
-
if (currentRealmId &&
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5251
|
+
if (currentRealmId &&
|
|
5252
|
+
((currentTable && currentProp && lastDoc) || completedRealm)) {
|
|
5253
|
+
yield db.$syncState.update('syncState', (syncState) => {
|
|
5254
|
+
const yDownloadedRealms = syncState.yDownloadedRealms || {};
|
|
5255
|
+
yDownloadedRealms[currentRealmId] = completedRealm
|
|
5256
|
+
? '*'
|
|
5257
|
+
: {
|
|
5209
5258
|
tbl: currentTable,
|
|
5210
5259
|
prop: currentProp,
|
|
5211
5260
|
key: lastDoc.k,
|
|
5212
|
-
}
|
|
5213
|
-
|
|
5261
|
+
};
|
|
5262
|
+
syncState.yDownloadedRealms = yDownloadedRealms;
|
|
5263
|
+
});
|
|
5214
5264
|
}
|
|
5215
5265
|
});
|
|
5216
5266
|
}
|
|
@@ -5313,11 +5363,11 @@ function sync(db, options, schema, syncOptions) {
|
|
|
5313
5363
|
return Promise.reject(error);
|
|
5314
5364
|
}));
|
|
5315
5365
|
}
|
|
5316
|
-
function _sync(
|
|
5317
|
-
isInitialSync
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5366
|
+
function _sync(db_1, options_1, schema_1) {
|
|
5367
|
+
return __awaiter(this, arguments, void 0, function* (db, options, schema, { isInitialSync, cancelToken, justCheckIfNeeded, purpose } = {
|
|
5368
|
+
isInitialSync: false,
|
|
5369
|
+
}) {
|
|
5370
|
+
var _a;
|
|
5321
5371
|
if (!justCheckIfNeeded) {
|
|
5322
5372
|
console.debug('SYNC STARTED', { isInitialSync, purpose });
|
|
5323
5373
|
}
|
|
@@ -5359,8 +5409,8 @@ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNee
|
|
|
5359
5409
|
const [clientChangeSet, syncState, baseRevs, { yMessages, lastUpdateIds }] = yield db.transaction('r', db.tables, () => __awaiter(this, void 0, void 0, function* () {
|
|
5360
5410
|
const syncState = yield db.getPersistedSyncState();
|
|
5361
5411
|
const baseRevs = yield db.$baseRevs.toArray();
|
|
5362
|
-
let clientChanges = yield listClientChanges(mutationTables);
|
|
5363
|
-
const yResults = yield listYClientMessagesAndStateVector(db);
|
|
5412
|
+
let clientChanges = yield listClientChanges(mutationTables, db);
|
|
5413
|
+
const yResults = yield listYClientMessagesAndStateVector(db, tablesToSync);
|
|
5364
5414
|
throwIfCancelled(cancelToken);
|
|
5365
5415
|
if (doSyncify) {
|
|
5366
5416
|
const alreadySyncedRealms = [
|
|
@@ -5490,11 +5540,14 @@ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNee
|
|
|
5490
5540
|
//
|
|
5491
5541
|
// apply yMessages
|
|
5492
5542
|
//
|
|
5493
|
-
const receivedUntils = yield applyYServerMessages(res.yMessages, db);
|
|
5543
|
+
const { receivedUntils, resyncNeeded } = yield applyYServerMessages(res.yMessages, db);
|
|
5494
5544
|
//
|
|
5495
5545
|
// update Y SyncStates
|
|
5496
5546
|
//
|
|
5497
5547
|
yield updateYSyncStates(lastUpdateIds, receivedUntils, db, res.serverRevision);
|
|
5548
|
+
if (resyncNeeded) {
|
|
5549
|
+
newSyncState.yDownloadedRealms = {}; // Will trigger a full download of Y-documents below...
|
|
5550
|
+
}
|
|
5498
5551
|
}
|
|
5499
5552
|
//
|
|
5500
5553
|
// Update regular syncState
|
|
@@ -5628,8 +5681,8 @@ function MessagesFromServerConsumer(db) {
|
|
|
5628
5681
|
event.next(null);
|
|
5629
5682
|
}
|
|
5630
5683
|
function consumeQueue() {
|
|
5631
|
-
var _a, _b, _c, _d, _e, _f;
|
|
5632
5684
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5685
|
+
var _a, _b, _c, _d, _e, _f;
|
|
5633
5686
|
while (queue.length > 0) {
|
|
5634
5687
|
const msg = queue.shift();
|
|
5635
5688
|
try {
|
|
@@ -6285,8 +6338,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
6285
6338
|
outstandingTransactions.next(outstandingTransactions.value);
|
|
6286
6339
|
};
|
|
6287
6340
|
const txComplete = () => {
|
|
6288
|
-
if (tx.mutationsAdded &&
|
|
6289
|
-
!isEagerSyncDisabled(db)) {
|
|
6341
|
+
if (tx.mutationsAdded && !isEagerSyncDisabled(db)) {
|
|
6290
6342
|
triggerSync(db, 'push');
|
|
6291
6343
|
}
|
|
6292
6344
|
removeTransaction();
|
|
@@ -6368,26 +6420,107 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
6368
6420
|
: mutateAndLog(req);
|
|
6369
6421
|
} }));
|
|
6370
6422
|
function mutateAndLog(req) {
|
|
6423
|
+
var _a, _b;
|
|
6371
6424
|
const trans = req.trans;
|
|
6372
|
-
|
|
6425
|
+
const unsyncedProps = (_b = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.unsyncedProperties) === null || _b === void 0 ? void 0 : _b[tableName];
|
|
6373
6426
|
const { txid, currentUser: { userId }, } = trans;
|
|
6374
6427
|
const { type } = req;
|
|
6375
6428
|
const opNo = ++trans.opCount;
|
|
6429
|
+
function stripChangeSpec(changeSpec) {
|
|
6430
|
+
if (!unsyncedProps)
|
|
6431
|
+
return changeSpec;
|
|
6432
|
+
let rv = changeSpec;
|
|
6433
|
+
for (const keyPath of Object.keys(changeSpec)) {
|
|
6434
|
+
if (unsyncedProps.some((p) => keyPath === p || keyPath.startsWith(p + '.'))) {
|
|
6435
|
+
if (rv === changeSpec)
|
|
6436
|
+
rv = Object.assign({}, changeSpec); // clone on demand
|
|
6437
|
+
delete rv[keyPath];
|
|
6438
|
+
}
|
|
6439
|
+
}
|
|
6440
|
+
return rv;
|
|
6441
|
+
}
|
|
6376
6442
|
return table.mutate(req).then((res) => {
|
|
6443
|
+
var _a;
|
|
6377
6444
|
const { numFailures: hasFailures, failures } = res;
|
|
6378
6445
|
let keys = type === 'delete' ? req.keys : res.results;
|
|
6379
6446
|
let values = 'values' in req ? req.values : [];
|
|
6380
|
-
let
|
|
6447
|
+
let changeSpec = 'changeSpec' in req ? req.changeSpec : undefined;
|
|
6448
|
+
let updates = 'updates' in req ? req.updates : undefined;
|
|
6381
6449
|
if (hasFailures) {
|
|
6382
6450
|
keys = keys.filter((_, idx) => !failures[idx]);
|
|
6383
6451
|
values = values.filter((_, idx) => !failures[idx]);
|
|
6384
6452
|
}
|
|
6453
|
+
if (unsyncedProps) {
|
|
6454
|
+
// Filter out unsynced properties
|
|
6455
|
+
values = values.map((value) => {
|
|
6456
|
+
const newValue = Object.assign({}, value);
|
|
6457
|
+
for (const prop of unsyncedProps) {
|
|
6458
|
+
delete newValue[prop];
|
|
6459
|
+
}
|
|
6460
|
+
return newValue;
|
|
6461
|
+
});
|
|
6462
|
+
if (changeSpec) {
|
|
6463
|
+
// modify operation with criteria and changeSpec.
|
|
6464
|
+
// We must strip out unsynced properties from changeSpec.
|
|
6465
|
+
// We deal with criteria later.
|
|
6466
|
+
changeSpec = stripChangeSpec(changeSpec);
|
|
6467
|
+
if (Object.keys(changeSpec).length === 0) {
|
|
6468
|
+
// Nothing to change on server
|
|
6469
|
+
return res;
|
|
6470
|
+
}
|
|
6471
|
+
}
|
|
6472
|
+
if (updates) {
|
|
6473
|
+
let strippedChangeSpecs = updates.changeSpecs.map(stripChangeSpec);
|
|
6474
|
+
let newUpdates = {
|
|
6475
|
+
keys: [],
|
|
6476
|
+
changeSpecs: [],
|
|
6477
|
+
};
|
|
6478
|
+
const validKeys = new RangeSet();
|
|
6479
|
+
let anyChangeSpecBecameEmpty = false;
|
|
6480
|
+
for (let i = 0, l = strippedChangeSpecs.length; i < l; ++i) {
|
|
6481
|
+
if (Object.keys(strippedChangeSpecs[i]).length > 0) {
|
|
6482
|
+
newUpdates.keys.push(updates.keys[i]);
|
|
6483
|
+
newUpdates.changeSpecs.push(strippedChangeSpecs[i]);
|
|
6484
|
+
validKeys.addKey(updates.keys[i]);
|
|
6485
|
+
}
|
|
6486
|
+
else {
|
|
6487
|
+
anyChangeSpecBecameEmpty = true;
|
|
6488
|
+
}
|
|
6489
|
+
}
|
|
6490
|
+
updates = newUpdates;
|
|
6491
|
+
if (anyChangeSpecBecameEmpty) {
|
|
6492
|
+
// Some keys were stripped. We must also strip them from keys and values
|
|
6493
|
+
let newKeys = [];
|
|
6494
|
+
let newValues = [];
|
|
6495
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
6496
|
+
if (validKeys.hasKey(keys[i])) {
|
|
6497
|
+
newKeys.push(keys[i]);
|
|
6498
|
+
newValues.push(values[i]);
|
|
6499
|
+
}
|
|
6500
|
+
}
|
|
6501
|
+
keys = newKeys;
|
|
6502
|
+
values = newValues;
|
|
6503
|
+
}
|
|
6504
|
+
}
|
|
6505
|
+
}
|
|
6385
6506
|
const ts = Date.now();
|
|
6386
6507
|
// Canonicalize req.criteria.index to null if it's on the primary key.
|
|
6387
|
-
|
|
6508
|
+
let criteria = 'criteria' in req && req.criteria
|
|
6388
6509
|
? Object.assign(Object.assign({}, req.criteria), { index: req.criteria.index === schema.primaryKey.keyPath // Use null to inform server that criteria is on primary key
|
|
6389
6510
|
? null // This will disable the server from trying to log consistent operations where it shouldnt.
|
|
6390
6511
|
: req.criteria.index }) : undefined;
|
|
6512
|
+
if (unsyncedProps && (criteria === null || criteria === void 0 ? void 0 : criteria.index)) {
|
|
6513
|
+
const keyPaths = (_a = schema.indexes.find((idx) => idx.name === criteria.index)) === null || _a === void 0 ? void 0 : _a.keyPath;
|
|
6514
|
+
const involvedProps = keyPaths
|
|
6515
|
+
? typeof keyPaths === 'string'
|
|
6516
|
+
? [keyPaths]
|
|
6517
|
+
: keyPaths
|
|
6518
|
+
: [];
|
|
6519
|
+
if (involvedProps.some((p) => unsyncedProps === null || unsyncedProps === void 0 ? void 0 : unsyncedProps.includes(p))) {
|
|
6520
|
+
// Don't log criteria on unsynced properties as the server could not test them.
|
|
6521
|
+
criteria = undefined;
|
|
6522
|
+
}
|
|
6523
|
+
}
|
|
6391
6524
|
const mut = req.type === 'delete'
|
|
6392
6525
|
? {
|
|
6393
6526
|
type: 'delete',
|
|
@@ -6408,7 +6541,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
6408
6541
|
userId,
|
|
6409
6542
|
values,
|
|
6410
6543
|
}
|
|
6411
|
-
: criteria &&
|
|
6544
|
+
: criteria && changeSpec
|
|
6412
6545
|
? {
|
|
6413
6546
|
// Common changeSpec for all keys
|
|
6414
6547
|
type: 'modify',
|
|
@@ -6416,37 +6549,51 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
6416
6549
|
opNo,
|
|
6417
6550
|
keys,
|
|
6418
6551
|
criteria,
|
|
6419
|
-
changeSpec
|
|
6552
|
+
changeSpec,
|
|
6420
6553
|
txid,
|
|
6421
6554
|
userId,
|
|
6422
6555
|
}
|
|
6423
|
-
:
|
|
6556
|
+
: changeSpec
|
|
6424
6557
|
? {
|
|
6425
|
-
//
|
|
6558
|
+
// In case criteria involved an unsynced property, we go for keys instead.
|
|
6426
6559
|
type: 'update',
|
|
6427
6560
|
ts,
|
|
6428
6561
|
opNo,
|
|
6429
|
-
keys: updates.keys,
|
|
6430
|
-
changeSpecs: updates.changeSpecs,
|
|
6431
|
-
txid,
|
|
6432
|
-
userId,
|
|
6433
|
-
}
|
|
6434
|
-
: {
|
|
6435
|
-
type: 'upsert',
|
|
6436
|
-
ts,
|
|
6437
|
-
opNo,
|
|
6438
6562
|
keys,
|
|
6439
|
-
|
|
6563
|
+
changeSpecs: keys.map(() => changeSpec),
|
|
6440
6564
|
txid,
|
|
6441
6565
|
userId,
|
|
6442
|
-
}
|
|
6566
|
+
}
|
|
6567
|
+
: updates
|
|
6568
|
+
? {
|
|
6569
|
+
// One changeSpec per key
|
|
6570
|
+
type: 'update',
|
|
6571
|
+
ts,
|
|
6572
|
+
opNo,
|
|
6573
|
+
keys: updates.keys,
|
|
6574
|
+
changeSpecs: updates.changeSpecs,
|
|
6575
|
+
txid,
|
|
6576
|
+
userId,
|
|
6577
|
+
}
|
|
6578
|
+
: {
|
|
6579
|
+
type: 'upsert',
|
|
6580
|
+
ts,
|
|
6581
|
+
opNo,
|
|
6582
|
+
keys,
|
|
6583
|
+
values,
|
|
6584
|
+
txid,
|
|
6585
|
+
userId,
|
|
6586
|
+
};
|
|
6443
6587
|
if ('isAdditionalChunk' in req && req.isAdditionalChunk) {
|
|
6444
6588
|
mut.isAdditionalChunk = true;
|
|
6445
6589
|
}
|
|
6446
6590
|
return keys.length > 0 || criteria
|
|
6447
6591
|
? mutsTable
|
|
6448
6592
|
.mutate({ type: 'add', trans, values: [mut] }) // Log entry
|
|
6449
|
-
.then(() =>
|
|
6593
|
+
.then(() => {
|
|
6594
|
+
trans.mutationsAdded = true; // Mark transaction as having added mutations to trigger eager sync
|
|
6595
|
+
return res; // Return original response
|
|
6596
|
+
})
|
|
6450
6597
|
: res;
|
|
6451
6598
|
});
|
|
6452
6599
|
}
|
|
@@ -6613,38 +6760,51 @@ class TokenExpiredError extends Error {
|
|
|
6613
6760
|
|
|
6614
6761
|
function createYClientUpdateObservable(db) {
|
|
6615
6762
|
const yTableRecords = flatten(db.tables
|
|
6616
|
-
.filter((table) => { var _a; return ((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[table.name].markedForSync) && table.schema.yProps; })
|
|
6763
|
+
.filter((table) => { var _a, _b; return ((_b = (_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[table.name]) === null || _b === void 0 ? void 0 : _b.markedForSync) && table.schema.yProps; })
|
|
6617
6764
|
.map((table) => table.schema.yProps.map((p) => ({
|
|
6618
6765
|
table: table.name,
|
|
6619
6766
|
ydocProp: p.prop,
|
|
6620
6767
|
updatesTable: p.updatesTable,
|
|
6621
6768
|
}))));
|
|
6622
6769
|
return merge(...yTableRecords.map(({ table, ydocProp, updatesTable }) => {
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6770
|
+
// Per updates table (table+prop combo), we first read syncer.unsentFrom,
|
|
6771
|
+
// and then start listening for updates since that number.
|
|
6772
|
+
const yTbl = db.table(updatesTable);
|
|
6773
|
+
return from$1(yTbl.get(DEXIE_CLOUD_SYNCER_ID)).pipe(switchMap$1((syncer) => {
|
|
6774
|
+
let currentUnsentFrom = (syncer === null || syncer === void 0 ? void 0 : syncer.unsentFrom) || 1;
|
|
6775
|
+
return from$1(liveQuery(() => __awaiter(this, void 0, void 0, function* () {
|
|
6776
|
+
const addedUpdates = yield listUpdatesSince(yTbl, currentUnsentFrom);
|
|
6777
|
+
return addedUpdates
|
|
6778
|
+
.filter((update) => update.f && update.f & 1) // Only include local updates
|
|
6779
|
+
.map((update) => {
|
|
6780
|
+
return {
|
|
6781
|
+
type: 'u-c',
|
|
6782
|
+
table,
|
|
6783
|
+
prop: ydocProp,
|
|
6784
|
+
k: update.k,
|
|
6785
|
+
u: update.u,
|
|
6786
|
+
i: update.i,
|
|
6787
|
+
};
|
|
6788
|
+
});
|
|
6789
|
+
}))).pipe(tap$1((addedUpdates) => {
|
|
6790
|
+
// Update currentUnsentFrom to only listen for updates that will be newer than the ones we emitted.
|
|
6791
|
+
// (Before, we did this within the liveQuery, but that caused a bug because
|
|
6792
|
+
// a cancelled emittion of a liveQuery would update the currentUnsentFrom without
|
|
6793
|
+
// emitting anything, leading to that we jumped over some updates. Here we update it
|
|
6794
|
+
// after the liveQuery has emitted its updates)
|
|
6795
|
+
if (addedUpdates.length > 0) {
|
|
6796
|
+
currentUnsentFrom = addedUpdates.at(-1).i + 1;
|
|
6797
|
+
}
|
|
6798
|
+
}));
|
|
6646
6799
|
}));
|
|
6647
|
-
})).pipe(
|
|
6800
|
+
})).pipe(
|
|
6801
|
+
// Flatten the array of messages.
|
|
6802
|
+
// If messageProducer emits empty array, nothing is emitted
|
|
6803
|
+
// but if messageProducer emits array of messages, they are
|
|
6804
|
+
// emitted one by one.
|
|
6805
|
+
mergeMap$1((messages) => messages), tap$1((message) => {
|
|
6806
|
+
console.debug('dexie-cloud emitting y-c', message);
|
|
6807
|
+
}));
|
|
6648
6808
|
}
|
|
6649
6809
|
|
|
6650
6810
|
function getAwarenessLibrary(db) {
|
|
@@ -6661,25 +6821,24 @@ const SERVER_PING_TIMEOUT = 20000;
|
|
|
6661
6821
|
const CLIENT_PING_INTERVAL = 30000;
|
|
6662
6822
|
const FAIL_RETRY_WAIT_TIME = 60000;
|
|
6663
6823
|
class WSObservable extends Observable$1 {
|
|
6664
|
-
constructor(db, rev, realmSetHash, clientIdentity, messageProducer, webSocketStatus,
|
|
6665
|
-
super((subscriber) => new WSConnection(db, rev, realmSetHash, clientIdentity,
|
|
6824
|
+
constructor(db, rev, realmSetHash, clientIdentity, messageProducer, webSocketStatus, user) {
|
|
6825
|
+
super((subscriber) => new WSConnection(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus));
|
|
6666
6826
|
}
|
|
6667
6827
|
}
|
|
6668
6828
|
let counter = 0;
|
|
6669
6829
|
class WSConnection extends Subscription$1 {
|
|
6670
|
-
constructor(db, rev, realmSetHash, clientIdentity,
|
|
6830
|
+
constructor(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus) {
|
|
6671
6831
|
super(() => this.teardown());
|
|
6672
6832
|
this.id = ++counter;
|
|
6673
6833
|
this.subscriptions = new Set();
|
|
6674
6834
|
this.reconnecting = false;
|
|
6675
|
-
console.debug('New WebSocket Connection', this.id,
|
|
6835
|
+
console.debug('New WebSocket Connection', this.id, user.accessToken ? 'authorized' : 'unauthorized');
|
|
6676
6836
|
this.db = db;
|
|
6677
6837
|
this.databaseUrl = db.cloud.options.databaseUrl;
|
|
6678
6838
|
this.rev = rev;
|
|
6679
6839
|
this.realmSetHash = realmSetHash;
|
|
6680
6840
|
this.clientIdentity = clientIdentity;
|
|
6681
|
-
this.
|
|
6682
|
-
this.tokenExpiration = tokenExpiration;
|
|
6841
|
+
this.user = user;
|
|
6683
6842
|
this.subscriber = subscriber;
|
|
6684
6843
|
this.lastUserActivity = new Date();
|
|
6685
6844
|
this.messageProducer = messageProducer;
|
|
@@ -6739,7 +6898,8 @@ class WSConnection extends Subscription$1 {
|
|
|
6739
6898
|
//console.debug('SyncStatus: DUBB: Ooops it was closed!');
|
|
6740
6899
|
return;
|
|
6741
6900
|
}
|
|
6742
|
-
|
|
6901
|
+
const tokenExpiration = this.user.accessTokenExpiration;
|
|
6902
|
+
if (tokenExpiration && tokenExpiration < new Date()) {
|
|
6743
6903
|
this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
|
|
6744
6904
|
return;
|
|
6745
6905
|
}
|
|
@@ -6794,8 +6954,8 @@ class WSConnection extends Subscription$1 {
|
|
|
6794
6954
|
searchParams.set('rev', this.rev);
|
|
6795
6955
|
searchParams.set('realmsHash', this.realmSetHash);
|
|
6796
6956
|
searchParams.set('clientId', this.clientIdentity);
|
|
6797
|
-
if (this.
|
|
6798
|
-
searchParams.set('token', this.
|
|
6957
|
+
if (this.user.accessToken) {
|
|
6958
|
+
searchParams.set('token', this.user.accessToken);
|
|
6799
6959
|
}
|
|
6800
6960
|
// Connect the WebSocket to given url:
|
|
6801
6961
|
console.debug('dexie-cloud WebSocket create');
|
|
@@ -6810,12 +6970,12 @@ class WSConnection extends Subscription$1 {
|
|
|
6810
6970
|
ws.onmessage = (event) => {
|
|
6811
6971
|
if (!this.pinger)
|
|
6812
6972
|
return;
|
|
6813
|
-
console.debug('dexie-cloud WebSocket onmessage', event.data);
|
|
6814
6973
|
this.lastServerActivity = new Date();
|
|
6815
6974
|
try {
|
|
6816
6975
|
const msg = typeof event.data === 'string'
|
|
6817
6976
|
? TSON.parse(event.data)
|
|
6818
6977
|
: decodeYMessage(new Uint8Array(event.data));
|
|
6978
|
+
console.debug('dexie-cloud WebSocket onmessage', msg.type, msg);
|
|
6819
6979
|
if (msg.type === 'error') {
|
|
6820
6980
|
throw new Error(`Error message from dexie-cloud: ${msg.error}`);
|
|
6821
6981
|
}
|
|
@@ -6836,6 +6996,10 @@ class WSConnection extends Subscription$1 {
|
|
|
6836
6996
|
else if (msg.type === 'u-ack' || msg.type === 'u-reject' || msg.type === 'u-s' || msg.type === 'in-sync') {
|
|
6837
6997
|
applyYServerMessages([msg], this.db);
|
|
6838
6998
|
}
|
|
6999
|
+
else if (msg.type === 'outdated-server-rev') {
|
|
7000
|
+
// Won't happen but need this for typing.
|
|
7001
|
+
throw new Error('Outdated server revision not expected over WebSocket - only in sync using fetch()');
|
|
7002
|
+
}
|
|
6839
7003
|
else if (msg.type !== 'pong') {
|
|
6840
7004
|
this.subscriber.next(msg);
|
|
6841
7005
|
}
|
|
@@ -6871,17 +7035,27 @@ class WSConnection extends Subscription$1 {
|
|
|
6871
7035
|
this.webSocketStatus.value !== 'connected') {
|
|
6872
7036
|
this.webSocketStatus.next('connected');
|
|
6873
7037
|
}
|
|
7038
|
+
console.debug('dexie-cloud WebSocket send', msg.type, msg);
|
|
6874
7039
|
if (msg.type === 'ready') {
|
|
6875
7040
|
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(TSON.stringify(msg));
|
|
6876
7041
|
}
|
|
6877
7042
|
else {
|
|
6878
7043
|
// If it's not a "ready" message, it's an YMessage.
|
|
6879
7044
|
// YMessages can be sent binary encoded.
|
|
7045
|
+
if (msg.type === 'u-c') {
|
|
7046
|
+
console.log("u-c:B", ++gotClientUpdateB);
|
|
7047
|
+
}
|
|
6880
7048
|
(_b = this.ws) === null || _b === void 0 ? void 0 : _b.send(encodeYMessage(msg));
|
|
6881
7049
|
}
|
|
6882
7050
|
}
|
|
6883
7051
|
}));
|
|
6884
|
-
|
|
7052
|
+
if (this.user.isLoggedIn && !isEagerSyncDisabled(this.db)) {
|
|
7053
|
+
this.subscriptions.add(createYClientUpdateObservable(this.db).pipe(tap$1((msg) => {
|
|
7054
|
+
if (msg.type === 'u-c') {
|
|
7055
|
+
console.log("u-c:A", ++gotClientUpdateA, msg.i);
|
|
7056
|
+
}
|
|
7057
|
+
})).subscribe(this.db.messageProducer));
|
|
7058
|
+
}
|
|
6885
7059
|
}
|
|
6886
7060
|
catch (error) {
|
|
6887
7061
|
this.pauseUntil = new Date(Date.now() + FAIL_RETRY_WAIT_TIME);
|
|
@@ -6889,6 +7063,8 @@ class WSConnection extends Subscription$1 {
|
|
|
6889
7063
|
});
|
|
6890
7064
|
}
|
|
6891
7065
|
}
|
|
7066
|
+
let gotClientUpdateA = 0;
|
|
7067
|
+
let gotClientUpdateB = 0;
|
|
6892
7068
|
|
|
6893
7069
|
class InvalidLicenseError extends Error {
|
|
6894
7070
|
constructor(license) {
|
|
@@ -6951,7 +7127,7 @@ function connectWebSocket(db) {
|
|
|
6951
7127
|
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]));
|
|
6952
7128
|
}
|
|
6953
7129
|
return new BehaviorSubject([userLogin, syncState]);
|
|
6954
|
-
}), switchMap((
|
|
7130
|
+
}), 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]) => {
|
|
6955
7131
|
var _a;
|
|
6956
7132
|
if (!((_a = db.cloud.persistedSyncState) === null || _a === void 0 ? void 0 : _a.value)) {
|
|
6957
7133
|
// Restart the flow if persistedSyncState is not yet available.
|
|
@@ -6961,7 +7137,7 @@ function connectWebSocket(db) {
|
|
|
6961
7137
|
// If no new entries, server won't bother the client. If new entries, server sends only those
|
|
6962
7138
|
// and the baseRev of the last from same client-ID.
|
|
6963
7139
|
if (userLogin) {
|
|
6964
|
-
return new WSObservable(db, db.cloud.persistedSyncState.value.serverRevision, realmSetHash, db.cloud.persistedSyncState.value.clientIdentity, messageProducer, db.cloud.webSocketStatus, userLogin
|
|
7140
|
+
return new WSObservable(db, db.cloud.persistedSyncState.value.serverRevision, realmSetHash, db.cloud.persistedSyncState.value.clientIdentity, messageProducer, db.cloud.webSocketStatus, userLogin);
|
|
6965
7141
|
}
|
|
6966
7142
|
else {
|
|
6967
7143
|
return from$1([]);
|
|
@@ -7012,8 +7188,8 @@ function connectWebSocket(db) {
|
|
|
7012
7188
|
}
|
|
7013
7189
|
|
|
7014
7190
|
function isSyncNeeded(db) {
|
|
7015
|
-
var _a;
|
|
7016
7191
|
return __awaiter(this, void 0, void 0, function* () {
|
|
7192
|
+
var _a;
|
|
7017
7193
|
return ((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl) && db.cloud.schema
|
|
7018
7194
|
? yield sync(db, db.cloud.options, db.cloud.schema, { justCheckIfNeeded: true })
|
|
7019
7195
|
: false;
|
|
@@ -7697,6 +7873,7 @@ class PermissionChecker {
|
|
|
7697
7873
|
// If user can update any prop in any table in this realm, return true unless
|
|
7698
7874
|
// it regards to ownership change:
|
|
7699
7875
|
if (this.permissions.update === '*') {
|
|
7876
|
+
// @ts-ignore
|
|
7700
7877
|
return props.every((prop) => prop !== 'owner');
|
|
7701
7878
|
}
|
|
7702
7879
|
const tablePermissions = (_b = this.permissions.update) === null || _b === void 0 ? void 0 : _b[this.tableName];
|
|
@@ -7768,125 +7945,139 @@ const getInvitesObservable = associate((db) => {
|
|
|
7768
7945
|
});
|
|
7769
7946
|
|
|
7770
7947
|
function createYHandler(db) {
|
|
7771
|
-
const awap = getAwarenessLibrary(db);
|
|
7772
7948
|
return (provider) => {
|
|
7773
7949
|
var _a;
|
|
7774
7950
|
const doc = provider.doc;
|
|
7775
|
-
const { parentTable
|
|
7951
|
+
const { parentTable } = doc.meta || {};
|
|
7776
7952
|
if (!((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[parentTable].markedForSync)) {
|
|
7777
7953
|
return; // The table that holds the doc is not marked for sync - leave it to dexie. No syncing, no awareness.
|
|
7778
7954
|
}
|
|
7779
|
-
let awareness
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7955
|
+
let awareness;
|
|
7956
|
+
Object.defineProperty(provider, "awareness", {
|
|
7957
|
+
get() {
|
|
7958
|
+
if (awareness)
|
|
7959
|
+
return awareness;
|
|
7960
|
+
awareness = createAwareness(db, doc, provider);
|
|
7961
|
+
awarenessWeakMap.set(doc, awareness);
|
|
7962
|
+
return awareness;
|
|
7963
|
+
}
|
|
7964
|
+
});
|
|
7965
|
+
};
|
|
7966
|
+
}
|
|
7967
|
+
function createAwareness(db, doc, provider) {
|
|
7968
|
+
const { parentTable, parentId, parentProp, updatesTable } = doc.meta;
|
|
7969
|
+
const awap = getAwarenessLibrary(db);
|
|
7970
|
+
const awareness = new awap.Awareness(doc);
|
|
7971
|
+
awareness.on('update', ({ added, updated, removed }, origin) => {
|
|
7972
|
+
// Send the update
|
|
7973
|
+
const changedClients = added.concat(updated).concat(removed);
|
|
7974
|
+
const user = db.cloud.currentUser.value;
|
|
7975
|
+
if (origin !== 'server' && user.isLoggedIn && !isEagerSyncDisabled(db)) {
|
|
7976
|
+
const update = awap.encodeAwarenessUpdate(awareness, changedClients);
|
|
7977
|
+
db.messageProducer.next({
|
|
7978
|
+
type: 'aware',
|
|
7979
|
+
table: parentTable,
|
|
7980
|
+
prop: parentProp,
|
|
7981
|
+
k: doc.meta.parentId,
|
|
7982
|
+
u: update,
|
|
7983
|
+
});
|
|
7984
|
+
if (provider.destroyed) {
|
|
7985
|
+
// We're called from awareness.on('destroy') that did
|
|
7986
|
+
// removeAwarenessStates.
|
|
7987
|
+
// It's time to also send the doc-close message that dexie-cloud understands
|
|
7988
|
+
// and uses to stop subscribing for updates and awareness updates and brings
|
|
7989
|
+
// down the cached information in memory on the WS connection for this.
|
|
7787
7990
|
db.messageProducer.next({
|
|
7788
|
-
type: '
|
|
7991
|
+
type: 'doc-close',
|
|
7789
7992
|
table: parentTable,
|
|
7790
7993
|
prop: parentProp,
|
|
7791
|
-
k: doc.meta.parentId
|
|
7792
|
-
u: update,
|
|
7994
|
+
k: doc.meta.parentId
|
|
7793
7995
|
});
|
|
7794
|
-
if (provider.destroyed) {
|
|
7795
|
-
// We're called from awareness.on('destroy') that did
|
|
7796
|
-
// removeAwarenessStates.
|
|
7797
|
-
// It's time to also send the doc-close message that dexie-cloud understands
|
|
7798
|
-
// and uses to stop subscribing for updates and awareness updates and brings
|
|
7799
|
-
// down the cached information in memory on the WS connection for this.
|
|
7800
|
-
db.messageProducer.next({
|
|
7801
|
-
type: 'doc-close',
|
|
7802
|
-
table: parentTable,
|
|
7803
|
-
prop: parentProp,
|
|
7804
|
-
k: doc.meta.parentId
|
|
7805
|
-
});
|
|
7806
|
-
}
|
|
7807
7996
|
}
|
|
7808
|
-
}
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7997
|
+
}
|
|
7998
|
+
});
|
|
7999
|
+
awareness.on('destroy', () => {
|
|
8000
|
+
// Signal to server that this provider is destroyed (the update event will be triggered, which
|
|
8001
|
+
// in turn will trigger db.messageProducer that will send the message to the server if WS is connected)
|
|
8002
|
+
awap.removeAwarenessStates(awareness, [doc.clientID], 'provider destroyed');
|
|
8003
|
+
});
|
|
8004
|
+
// Open the document on the server
|
|
8005
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
8006
|
+
if (provider.destroyed)
|
|
8007
|
+
return;
|
|
8008
|
+
let connected = false;
|
|
8009
|
+
let currentFlowId = 1;
|
|
8010
|
+
const subscription = db.cloud.webSocketStatus.subscribe((wsStatus) => {
|
|
7816
8011
|
if (provider.destroyed)
|
|
7817
8012
|
return;
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
8013
|
+
// Keep "connected" state in a variable so we can check it after async operations
|
|
8014
|
+
connected = wsStatus === 'connected';
|
|
8015
|
+
// We are or got connected. Open the document on the server.
|
|
8016
|
+
const user = db.cloud.currentUser.value;
|
|
8017
|
+
if (wsStatus === "connected" && user.isLoggedIn && !isEagerSyncDisabled(db)) {
|
|
8018
|
+
++currentFlowId;
|
|
8019
|
+
openDocumentOnServer().catch(error => {
|
|
8020
|
+
console.warn(`Error catched in createYHandler.ts: ${error}`);
|
|
8021
|
+
});
|
|
8022
|
+
}
|
|
8023
|
+
});
|
|
8024
|
+
// Wait until WebSocket is connected
|
|
8025
|
+
provider.addCleanupHandler(subscription);
|
|
8026
|
+
/** Sends an 'doc-open' message to server whenever websocket becomes
|
|
8027
|
+
* connected, or if it is already connected.
|
|
8028
|
+
* The flow is aborted in case websocket is disconnected while querying
|
|
8029
|
+
* information required to compute the state vector. Flow is also
|
|
8030
|
+
* aborted in case document or provider has been destroyed during
|
|
8031
|
+
* the async parts of the task.
|
|
8032
|
+
*
|
|
8033
|
+
* The state vector is only computed from the updates that have occured
|
|
8034
|
+
* after the last full sync - which could very often be zero - in which
|
|
8035
|
+
* case no state vector is sent (then the server already knows us by
|
|
8036
|
+
* revision)
|
|
8037
|
+
*
|
|
8038
|
+
* When server gets the doc-open message, it will authorized us for
|
|
8039
|
+
* whether we are allowed to read / write to this document, and then
|
|
8040
|
+
* keep the cached information in memory on the WS connection for this
|
|
8041
|
+
* particular document, as well as subscribe to updates and awareness updates
|
|
8042
|
+
* from other clients on the document.
|
|
8043
|
+
*/
|
|
8044
|
+
function openDocumentOnServer(wsStatus) {
|
|
8045
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8046
|
+
const myFlow = currentFlowId; // So we can abort when a new flow is started
|
|
8047
|
+
const yTbl = db.table(updatesTable);
|
|
8048
|
+
const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
|
|
8049
|
+
// After every await, check if we still should be working on this task.
|
|
8050
|
+
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7822
8051
|
return;
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
8052
|
+
const receivedUntil = (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0;
|
|
8053
|
+
const docOpenMsg = {
|
|
8054
|
+
type: 'doc-open',
|
|
8055
|
+
table: parentTable,
|
|
8056
|
+
prop: parentProp,
|
|
8057
|
+
k: parentId,
|
|
8058
|
+
serverRev: syncState === null || syncState === void 0 ? void 0 : syncState.serverRev,
|
|
8059
|
+
};
|
|
8060
|
+
const serverUpdatesSinceLastSync = yield yTbl
|
|
8061
|
+
.where('i')
|
|
8062
|
+
.between(receivedUntil, Infinity, false)
|
|
8063
|
+
.filter((update) => cmp(update.k, parentId) === 0 && // Only updates for this document
|
|
8064
|
+
((update.f || 0) & 1) === 0 // Don't include local changes
|
|
8065
|
+
)
|
|
8066
|
+
.toArray();
|
|
8067
|
+
// After every await, check if we still should be working on this task.
|
|
8068
|
+
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
8069
|
+
return;
|
|
8070
|
+
if (serverUpdatesSinceLastSync.length > 0) {
|
|
8071
|
+
const Y = $Y$1(db); // Get the Yjs library from Dexie constructor options
|
|
8072
|
+
const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
|
|
8073
|
+
const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
|
|
8074
|
+
docOpenMsg.sv = stateVector;
|
|
7831
8075
|
}
|
|
8076
|
+
db.messageProducer.next(docOpenMsg);
|
|
7832
8077
|
});
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
* connected, or if it is already connected.
|
|
7837
|
-
* The flow is aborted in case websocket is disconnected while querying
|
|
7838
|
-
* information required to compute the state vector. Flow is also
|
|
7839
|
-
* aborted in case document or provider has been destroyed during
|
|
7840
|
-
* the async parts of the task.
|
|
7841
|
-
*
|
|
7842
|
-
* The state vector is only computed from the updates that have occured
|
|
7843
|
-
* after the last full sync - which could very often be zero - in which
|
|
7844
|
-
* case no state vector is sent (then the server already knows us by
|
|
7845
|
-
* revision)
|
|
7846
|
-
*
|
|
7847
|
-
* When server gets the doc-open message, it will authorized us for
|
|
7848
|
-
* whether we are allowed to read / write to this document, and then
|
|
7849
|
-
* keep the cached information in memory on the WS connection for this
|
|
7850
|
-
* particular document, as well as subscribe to updates and awareness updates
|
|
7851
|
-
* from other clients on the document.
|
|
7852
|
-
*/
|
|
7853
|
-
function openDocumentOnServer(wsStatus) {
|
|
7854
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
7855
|
-
const myFlow = currentFlowId; // So we can abort when a new flow is started
|
|
7856
|
-
const yTbl = db.table(updatesTable);
|
|
7857
|
-
const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
|
|
7858
|
-
// After every await, check if we still should be working on this task.
|
|
7859
|
-
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7860
|
-
return;
|
|
7861
|
-
const receivedUntil = (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0;
|
|
7862
|
-
const docOpenMsg = {
|
|
7863
|
-
type: 'doc-open',
|
|
7864
|
-
table: parentTable,
|
|
7865
|
-
prop: parentProp,
|
|
7866
|
-
k: parentId,
|
|
7867
|
-
serverRev: syncState === null || syncState === void 0 ? void 0 : syncState.serverRev,
|
|
7868
|
-
};
|
|
7869
|
-
const serverUpdatesSinceLastSync = yield yTbl
|
|
7870
|
-
.where('i')
|
|
7871
|
-
.between(receivedUntil, Infinity, false)
|
|
7872
|
-
.filter((update) => cmp(update.k, parentId) === 0 && // Only updates for this document
|
|
7873
|
-
((update.f || 0) & 1) === 0 // Don't include local changes
|
|
7874
|
-
)
|
|
7875
|
-
.toArray();
|
|
7876
|
-
// After every await, check if we still should be working on this task.
|
|
7877
|
-
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7878
|
-
return;
|
|
7879
|
-
if (serverUpdatesSinceLastSync.length > 0) {
|
|
7880
|
-
const Y = $Y(db); // Get the Yjs library from Dexie constructor options
|
|
7881
|
-
const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
|
|
7882
|
-
const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
|
|
7883
|
-
docOpenMsg.sv = stateVector;
|
|
7884
|
-
}
|
|
7885
|
-
db.messageProducer.next(docOpenMsg);
|
|
7886
|
-
});
|
|
7887
|
-
}
|
|
7888
|
-
}));
|
|
7889
|
-
};
|
|
8078
|
+
}
|
|
8079
|
+
}))();
|
|
8080
|
+
return awareness;
|
|
7890
8081
|
}
|
|
7891
8082
|
|
|
7892
8083
|
function getTiedRealmId(objectId) {
|
|
@@ -7936,7 +8127,7 @@ function dexieCloud(dexie) {
|
|
|
7936
8127
|
const syncComplete = new Subject();
|
|
7937
8128
|
dexie.cloud = {
|
|
7938
8129
|
// @ts-ignore
|
|
7939
|
-
version: "4.1.0-alpha.
|
|
8130
|
+
version: "4.1.0-alpha.21",
|
|
7940
8131
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
7941
8132
|
schema: null,
|
|
7942
8133
|
get currentUserId() {
|
|
@@ -7972,16 +8163,16 @@ function dexieCloud(dexie) {
|
|
|
7972
8163
|
}
|
|
7973
8164
|
updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
|
|
7974
8165
|
},
|
|
7975
|
-
logout(
|
|
7976
|
-
return __awaiter(this,
|
|
8166
|
+
logout() {
|
|
8167
|
+
return __awaiter(this, arguments, void 0, function* ({ force } = {}) {
|
|
7977
8168
|
force
|
|
7978
8169
|
? yield _logout(DexieCloudDB(dexie), { deleteUnsyncedData: true })
|
|
7979
8170
|
: yield logout(DexieCloudDB(dexie));
|
|
7980
8171
|
});
|
|
7981
8172
|
},
|
|
7982
|
-
sync(
|
|
7983
|
-
|
|
7984
|
-
|
|
8173
|
+
sync() {
|
|
8174
|
+
return __awaiter(this, arguments, void 0, function* ({ wait, purpose } = { wait: true, purpose: 'push' }) {
|
|
8175
|
+
var _a;
|
|
7985
8176
|
if (wait === undefined)
|
|
7986
8177
|
wait = true;
|
|
7987
8178
|
const db = DexieCloudDB(dexie);
|
|
@@ -8039,8 +8230,8 @@ function dexieCloud(dexie) {
|
|
|
8039
8230
|
dexie.use(createImplicitPropSetterMiddleware(DexieCloudDB(dexie)));
|
|
8040
8231
|
dexie.use(createIdGenerationMiddleware(DexieCloudDB(dexie)));
|
|
8041
8232
|
function onDbReady(dexie) {
|
|
8042
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
8043
8233
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8234
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
8044
8235
|
closed = false; // As Dexie calls us, we are not closed anymore. Maybe reopened? Remember db.ready event is registered with sticky flag!
|
|
8045
8236
|
const db = DexieCloudDB(dexie);
|
|
8046
8237
|
// Setup default GUI:
|
|
@@ -8063,7 +8254,7 @@ function dexieCloud(dexie) {
|
|
|
8063
8254
|
? yield navigator.serviceWorker.getRegistrations()
|
|
8064
8255
|
: [];
|
|
8065
8256
|
const [initiallySynced, lastSyncedRealms] = yield db.transaction('rw', db.$syncState, () => __awaiter(this, void 0, void 0, function* () {
|
|
8066
|
-
var
|
|
8257
|
+
var _a, _b;
|
|
8067
8258
|
const { options, schema } = db.cloud;
|
|
8068
8259
|
const [persistedOptions, persistedSchema, persistedSyncState] = yield Promise.all([
|
|
8069
8260
|
db.getOptions(),
|
|
@@ -8085,7 +8276,7 @@ function dexieCloud(dexie) {
|
|
|
8085
8276
|
delete newPersistedOptions.awarenessProtocol;
|
|
8086
8277
|
yield db.$syncState.put(newPersistedOptions, 'options');
|
|
8087
8278
|
}
|
|
8088
|
-
if (((
|
|
8279
|
+
if (((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.tryUseServiceWorker) &&
|
|
8089
8280
|
'serviceWorker' in navigator &&
|
|
8090
8281
|
swRegistrations.length > 0 &&
|
|
8091
8282
|
!DISABLE_SERVICEWORKER_STRATEGY) {
|
|
@@ -8099,7 +8290,7 @@ function dexieCloud(dexie) {
|
|
|
8099
8290
|
// Not configured for using service worker or no service worker
|
|
8100
8291
|
// registration exists. Don't rely on service worker to do any job.
|
|
8101
8292
|
// Use LocalSyncWorker instead.
|
|
8102
|
-
if (((
|
|
8293
|
+
if (((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.tryUseServiceWorker) &&
|
|
8103
8294
|
!db.cloud.isServiceWorkerDB) {
|
|
8104
8295
|
console.debug('dexie-cloud-addon: Not using service worker.', swRegistrations.length === 0
|
|
8105
8296
|
? 'No SW registrations found.'
|
|
@@ -8238,8 +8429,164 @@ function dexieCloud(dexie) {
|
|
|
8238
8429
|
}
|
|
8239
8430
|
}
|
|
8240
8431
|
// @ts-ignore
|
|
8241
|
-
dexieCloud.version = "4.1.0-alpha.
|
|
8432
|
+
dexieCloud.version = "4.1.0-alpha.21";
|
|
8242
8433
|
Dexie.Cloud = dexieCloud;
|
|
8243
8434
|
|
|
8244
|
-
|
|
8435
|
+
const ydocTriggers = {};
|
|
8436
|
+
const docIsAlreadyHooked = new WeakSet();
|
|
8437
|
+
const middlewares = new WeakMap();
|
|
8438
|
+
const createMiddleware = (db) => ({
|
|
8439
|
+
stack: 'dbcore',
|
|
8440
|
+
level: 10,
|
|
8441
|
+
name: 'yTriggerMiddleware',
|
|
8442
|
+
create: (down) => {
|
|
8443
|
+
return Object.assign(Object.assign({}, down), { transaction: (stores, mode, options) => {
|
|
8444
|
+
const idbtrans = down.transaction(stores, mode, options);
|
|
8445
|
+
idbtrans.addEventListener('complete', onTransactionCommitted);
|
|
8446
|
+
return idbtrans;
|
|
8447
|
+
}, table: (tblName) => {
|
|
8448
|
+
const coreTable = down.table(tblName);
|
|
8449
|
+
const triggerSpec = ydocTriggers[tblName];
|
|
8450
|
+
if (!triggerSpec)
|
|
8451
|
+
return coreTable;
|
|
8452
|
+
const { trigger, parentTable, prop } = triggerSpec;
|
|
8453
|
+
return Object.assign(Object.assign({}, coreTable), { mutate(req) {
|
|
8454
|
+
switch (req.type) {
|
|
8455
|
+
case 'add': {
|
|
8456
|
+
for (const yUpdateRow of req.values) {
|
|
8457
|
+
if (yUpdateRow.k == undefined)
|
|
8458
|
+
continue; // A syncer or garbage collection state does not point to a key
|
|
8459
|
+
const primaryKey = yUpdateRow.k;
|
|
8460
|
+
const doc = DexieYProvider.getDocCache(db).find(parentTable, primaryKey, prop);
|
|
8461
|
+
if (doc) {
|
|
8462
|
+
if (!docIsAlreadyHooked.has(doc)) {
|
|
8463
|
+
hookToDoc(doc, primaryKey, trigger);
|
|
8464
|
+
docIsAlreadyHooked.add(doc);
|
|
8465
|
+
}
|
|
8466
|
+
}
|
|
8467
|
+
else {
|
|
8468
|
+
enqueueTrigger(db, tblName, primaryKey, trigger);
|
|
8469
|
+
}
|
|
8470
|
+
}
|
|
8471
|
+
break;
|
|
8472
|
+
}
|
|
8473
|
+
case 'delete':
|
|
8474
|
+
// @ts-ignore
|
|
8475
|
+
if (req.trans._rejecting_y_ypdate) {
|
|
8476
|
+
// The deletion came from a rejection, not garbage collection.
|
|
8477
|
+
// When that happens, let the triggers run to compute new values
|
|
8478
|
+
// based on the deleted updates.
|
|
8479
|
+
coreTable
|
|
8480
|
+
.getMany({
|
|
8481
|
+
keys: req.keys,
|
|
8482
|
+
trans: req.trans,
|
|
8483
|
+
cache: 'immutable',
|
|
8484
|
+
})
|
|
8485
|
+
.then((updates) => {
|
|
8486
|
+
const keySet = new RangeSet();
|
|
8487
|
+
for (const { k } of updates) {
|
|
8488
|
+
if (k != undefined)
|
|
8489
|
+
keySet.addKey(k);
|
|
8490
|
+
}
|
|
8491
|
+
for (const interval of keySet) {
|
|
8492
|
+
enqueueTrigger(db, tblName, interval.from, trigger);
|
|
8493
|
+
}
|
|
8494
|
+
});
|
|
8495
|
+
}
|
|
8496
|
+
break;
|
|
8497
|
+
}
|
|
8498
|
+
return coreTable.mutate(req);
|
|
8499
|
+
} });
|
|
8500
|
+
} });
|
|
8501
|
+
},
|
|
8502
|
+
});
|
|
8503
|
+
let triggerExecPromise = null;
|
|
8504
|
+
let triggerScheduled = false;
|
|
8505
|
+
let scheduledTriggers = [];
|
|
8506
|
+
function $Y(db) {
|
|
8507
|
+
const $Y = db._options.Y;
|
|
8508
|
+
if (!$Y)
|
|
8509
|
+
throw new Error('Y library not supplied to Dexie constructor');
|
|
8510
|
+
return $Y;
|
|
8511
|
+
}
|
|
8512
|
+
function executeTriggers(triggersToRun) {
|
|
8513
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8514
|
+
for (const { db, parentId, trigger, updatesTable } of triggersToRun) {
|
|
8515
|
+
// Load entire document into an Y.Doc instance:
|
|
8516
|
+
const updates = yield db
|
|
8517
|
+
.table(updatesTable)
|
|
8518
|
+
.where({ k: parentId })
|
|
8519
|
+
.toArray();
|
|
8520
|
+
const Y = $Y(db);
|
|
8521
|
+
const yDoc = new Y.Doc();
|
|
8522
|
+
for (const update of updates) {
|
|
8523
|
+
Y.applyUpdateV2(yDoc, update.u);
|
|
8524
|
+
}
|
|
8525
|
+
try {
|
|
8526
|
+
yield trigger(yDoc, parentId);
|
|
8527
|
+
}
|
|
8528
|
+
catch (error) {
|
|
8529
|
+
console.error(`Error in YDocTrigger ${error}`);
|
|
8530
|
+
}
|
|
8531
|
+
}
|
|
8532
|
+
});
|
|
8533
|
+
}
|
|
8534
|
+
function enqueueTrigger(db, updatesTable, parentId, trigger) {
|
|
8535
|
+
scheduledTriggers.push({
|
|
8536
|
+
db,
|
|
8537
|
+
updatesTable,
|
|
8538
|
+
parentId,
|
|
8539
|
+
trigger,
|
|
8540
|
+
});
|
|
8541
|
+
}
|
|
8542
|
+
function onTransactionCommitted() {
|
|
8543
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8544
|
+
if (!triggerScheduled && scheduledTriggers.length > 0) {
|
|
8545
|
+
triggerScheduled = true;
|
|
8546
|
+
if (triggerExecPromise)
|
|
8547
|
+
yield triggerExecPromise.catch(() => { });
|
|
8548
|
+
setTimeout(() => {
|
|
8549
|
+
// setTimeout() is to escape from Promise.PSD zones and never run within liveQueries or transaction scopes
|
|
8550
|
+
triggerScheduled = false;
|
|
8551
|
+
const triggersToRun = scheduledTriggers;
|
|
8552
|
+
scheduledTriggers = [];
|
|
8553
|
+
triggerExecPromise = executeTriggers(triggersToRun).finally(() => (triggerExecPromise = null));
|
|
8554
|
+
}, 0);
|
|
8555
|
+
}
|
|
8556
|
+
});
|
|
8557
|
+
}
|
|
8558
|
+
function hookToDoc(doc, parentId, trigger) {
|
|
8559
|
+
// From now on, keep listening to doc updates and execute the trigger when it happens there instead
|
|
8560
|
+
doc.on('updateV2', (update, origin) => {
|
|
8561
|
+
//Dexie.ignoreTransaction(()=>{
|
|
8562
|
+
trigger(doc, parentId);
|
|
8563
|
+
//});
|
|
8564
|
+
});
|
|
8565
|
+
/*
|
|
8566
|
+
NOT NEEDED because DexieYProvider's docCache will also listen to destroy and remove it from its cache:
|
|
8567
|
+
doc.on('destroy', ()=>{
|
|
8568
|
+
docIsAlreadyHooked.delete(doc);
|
|
8569
|
+
})
|
|
8570
|
+
*/
|
|
8571
|
+
}
|
|
8572
|
+
function defineYDocTrigger(table, prop, trigger) {
|
|
8573
|
+
var _a, _b;
|
|
8574
|
+
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;
|
|
8575
|
+
if (!updatesTable)
|
|
8576
|
+
throw new Error(`Table ${table.name} does not have a Yjs property named ${prop}`);
|
|
8577
|
+
ydocTriggers[updatesTable] = {
|
|
8578
|
+
trigger,
|
|
8579
|
+
parentTable: table.name,
|
|
8580
|
+
prop,
|
|
8581
|
+
};
|
|
8582
|
+
const db = table.db._novip;
|
|
8583
|
+
let mw = middlewares.get(db);
|
|
8584
|
+
if (!mw) {
|
|
8585
|
+
mw = createMiddleware(db);
|
|
8586
|
+
middlewares.set(db, mw);
|
|
8587
|
+
}
|
|
8588
|
+
db.use(mw);
|
|
8589
|
+
}
|
|
8590
|
+
|
|
8591
|
+
export { dexieCloud as default, defineYDocTrigger, dexieCloud, getTiedObjectId, getTiedRealmId, resolveText };
|
|
8245
8592
|
//# sourceMappingURL=dexie-cloud-addon.js.map
|