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
|
*
|
|
@@ -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, startWith as startWith$1 } from 'rxjs';
|
|
21
21
|
|
|
22
22
|
/******************************************************************************
|
|
23
23
|
Copyright (c) Microsoft Corporation.
|
|
@@ -979,13 +979,20 @@ 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;
|
|
993
|
+
case 'y-complete-sync-done':
|
|
994
|
+
writeVarString(encoder, msg.yServerRev);
|
|
995
|
+
break;
|
|
989
996
|
default:
|
|
990
997
|
writeAny(encoder, msg.k);
|
|
991
998
|
switch (msg.type) {
|
|
@@ -1329,6 +1336,12 @@ const readAny = decoder => readAnyLookupTable[127 - readUint8(decoder)](decoder)
|
|
|
1329
1336
|
function decodeYMessage(a) {
|
|
1330
1337
|
const decoder = new Decoder(a);
|
|
1331
1338
|
const type = readVarString(decoder);
|
|
1339
|
+
if (type === 'outdated-server-rev') {
|
|
1340
|
+
return { type };
|
|
1341
|
+
}
|
|
1342
|
+
if (type === 'y-complete-sync-done') {
|
|
1343
|
+
return { type, yServerRev: readVarString(decoder) };
|
|
1344
|
+
}
|
|
1332
1345
|
const table = readVarString(decoder);
|
|
1333
1346
|
const prop = readVarString(decoder);
|
|
1334
1347
|
switch (type) {
|
|
@@ -2987,8 +3000,8 @@ function registerSyncEvent(db, purpose) {
|
|
|
2987
3000
|
});
|
|
2988
3001
|
}
|
|
2989
3002
|
function registerPeriodicSyncEvent(db) {
|
|
2990
|
-
var _a;
|
|
2991
3003
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3004
|
+
var _a;
|
|
2992
3005
|
try {
|
|
2993
3006
|
// Register periodicSync event to SW:
|
|
2994
3007
|
// @ts-ignore
|
|
@@ -3199,8 +3212,8 @@ function confirmLogout(userInteraction, currentUserId, numUnsyncedChanges) {
|
|
|
3199
3212
|
}
|
|
3200
3213
|
|
|
3201
3214
|
function loadAccessToken(db) {
|
|
3202
|
-
var _a, _b, _c;
|
|
3203
3215
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3216
|
+
var _a, _b, _c;
|
|
3204
3217
|
const currentUser = yield db.getCurrentUser();
|
|
3205
3218
|
const { accessToken, accessTokenExpiration, refreshToken, refreshTokenExpiration, claims, } = currentUser;
|
|
3206
3219
|
if (!accessToken)
|
|
@@ -3414,10 +3427,10 @@ function formatAsPem(str) {
|
|
|
3414
3427
|
}
|
|
3415
3428
|
|
|
3416
3429
|
// Emulate true-private property db. Why? So it's not stored in DB.
|
|
3417
|
-
const wm$
|
|
3430
|
+
const wm$2 = new WeakMap();
|
|
3418
3431
|
class AuthPersistedContext {
|
|
3419
3432
|
constructor(db, userLogin) {
|
|
3420
|
-
wm$
|
|
3433
|
+
wm$2.set(this, db);
|
|
3421
3434
|
Object.assign(this, userLogin);
|
|
3422
3435
|
}
|
|
3423
3436
|
static load(db, userId) {
|
|
@@ -3434,7 +3447,7 @@ class AuthPersistedContext {
|
|
|
3434
3447
|
}
|
|
3435
3448
|
save() {
|
|
3436
3449
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3437
|
-
const db = wm$
|
|
3450
|
+
const db = wm$2.get(this);
|
|
3438
3451
|
db.table("$logins").put(this);
|
|
3439
3452
|
});
|
|
3440
3453
|
}
|
|
@@ -3472,8 +3485,8 @@ function logout(db) {
|
|
|
3472
3485
|
}
|
|
3473
3486
|
});
|
|
3474
3487
|
}
|
|
3475
|
-
function _logout(
|
|
3476
|
-
return __awaiter(this,
|
|
3488
|
+
function _logout(db_1) {
|
|
3489
|
+
return __awaiter(this, arguments, void 0, function* (db, { deleteUnsyncedData = false } = {}) {
|
|
3477
3490
|
// Clear the database without emptying configuration options.
|
|
3478
3491
|
const [numUnsynced, loggedOut] = yield db.dx.transaction('rw', db.dx.tables, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
3479
3492
|
// @ts-ignore
|
|
@@ -3521,11 +3534,11 @@ class HttpError extends Error {
|
|
|
3521
3534
|
|
|
3522
3535
|
function otpFetchTokenCallback(db) {
|
|
3523
3536
|
const { userInteraction } = db.cloud;
|
|
3524
|
-
return function otpAuthenticate(
|
|
3525
|
-
|
|
3526
|
-
|
|
3537
|
+
return function otpAuthenticate(_a) {
|
|
3538
|
+
return __awaiter(this, arguments, void 0, function* ({ public_key, hints }) {
|
|
3539
|
+
var _b;
|
|
3527
3540
|
let tokenRequest;
|
|
3528
|
-
const url = (
|
|
3541
|
+
const url = (_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.databaseUrl;
|
|
3529
3542
|
if (!url)
|
|
3530
3543
|
throw new Error(`No database URL given.`);
|
|
3531
3544
|
if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'demo') {
|
|
@@ -3691,8 +3704,8 @@ function setCurrentUser(db, user) {
|
|
|
3691
3704
|
}
|
|
3692
3705
|
|
|
3693
3706
|
function login(db, hints) {
|
|
3694
|
-
var _a;
|
|
3695
3707
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3708
|
+
var _a;
|
|
3696
3709
|
const currentUser = yield db.getCurrentUser();
|
|
3697
3710
|
const origUserId = currentUser.userId;
|
|
3698
3711
|
if (currentUser.isLoggedIn && (!hints || (!hints.email && !hints.userId))) {
|
|
@@ -3880,8 +3893,8 @@ class BroadcastedAndLocalEvent extends Observable$1 {
|
|
|
3880
3893
|
}
|
|
3881
3894
|
}
|
|
3882
3895
|
|
|
3883
|
-
function computeRealmSetHash(
|
|
3884
|
-
return __awaiter(this,
|
|
3896
|
+
function computeRealmSetHash(_a) {
|
|
3897
|
+
return __awaiter(this, arguments, void 0, function* ({ realms, inviteRealms, }) {
|
|
3885
3898
|
const data = JSON.stringify([
|
|
3886
3899
|
...realms.map((realmId) => ({ realmId, accepted: true })),
|
|
3887
3900
|
...inviteRealms.map((realmId) => ({ realmId, accepted: false })),
|
|
@@ -3917,8 +3930,8 @@ function flatten(a) {
|
|
|
3917
3930
|
return concat.apply([], a);
|
|
3918
3931
|
}
|
|
3919
3932
|
|
|
3920
|
-
function listClientChanges(
|
|
3921
|
-
return __awaiter(this,
|
|
3933
|
+
function listClientChanges(mutationTables_1, db_1) {
|
|
3934
|
+
return __awaiter(this, arguments, void 0, function* (mutationTables, db, { since = {}, limit = Infinity } = {}) {
|
|
3922
3935
|
const allMutsOnTables = yield Promise.all(mutationTables.map((mutationTable) => __awaiter(this, void 0, void 0, function* () {
|
|
3923
3936
|
const tableName = getTableFromMutationTable(mutationTable.name);
|
|
3924
3937
|
const lastRevision = since[tableName];
|
|
@@ -4653,8 +4666,8 @@ function cloneChange(change, rewriteValues) {
|
|
|
4653
4666
|
// seconds (given that there is a Ratelimit-Reset header).
|
|
4654
4667
|
let syncRatelimitDelays = new WeakMap();
|
|
4655
4668
|
function checkSyncRateLimitDelay(db) {
|
|
4656
|
-
var _a, _b;
|
|
4657
4669
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4670
|
+
var _a, _b;
|
|
4658
4671
|
const delatMilliseconds = ((_b = (_a = syncRatelimitDelays.get(db)) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) - Date.now();
|
|
4659
4672
|
if (delatMilliseconds > 0) {
|
|
4660
4673
|
console.debug(`Stalling sync request ${delatMilliseconds} ms to spare ratelimits`);
|
|
@@ -4715,6 +4728,7 @@ function syncWithServer(changes, y, syncState, baseRevs, db, databaseUrl, schema
|
|
|
4715
4728
|
lastPull: syncState
|
|
4716
4729
|
? {
|
|
4717
4730
|
serverRevision: syncState.serverRevision,
|
|
4731
|
+
yServerRevision: syncState.yServerRevision,
|
|
4718
4732
|
realms: syncState.realms,
|
|
4719
4733
|
inviteRealms: syncState.inviteRealms,
|
|
4720
4734
|
}
|
|
@@ -4944,7 +4958,7 @@ function listUpdatesSince(yTable, sinceIncluding) {
|
|
|
4944
4958
|
.toArray();
|
|
4945
4959
|
}
|
|
4946
4960
|
|
|
4947
|
-
function $Y(db) {
|
|
4961
|
+
function $Y$1(db) {
|
|
4948
4962
|
const $Y = db.dx._options.Y;
|
|
4949
4963
|
if (!$Y)
|
|
4950
4964
|
throw new Error('Y library not supplied to Dexie constructor');
|
|
@@ -4974,7 +4988,7 @@ function listYClientMessagesAndStateVector(db, tablesToSync) {
|
|
|
4974
4988
|
for (const table of tablesToSync) {
|
|
4975
4989
|
if (table.schema.yProps) {
|
|
4976
4990
|
for (const yProp of table.schema.yProps) {
|
|
4977
|
-
const Y = $Y(db); // This is how we retrieve the user-provided Y library
|
|
4991
|
+
const Y = $Y$1(db); // This is how we retrieve the user-provided Y library
|
|
4978
4992
|
const yTable = db.table(yProp.updatesTable); // the updates-table for this combo of table+propName
|
|
4979
4993
|
const syncState = (yield yTable.get(DEXIE_CLOUD_SYNCER_ID));
|
|
4980
4994
|
// unsentFrom = the `i` value of updates that aren't yet sent to server (or at least not acked by the server yet)
|
|
@@ -5059,12 +5073,15 @@ function getUpdatesTable(db, table, ydocProp) {
|
|
|
5059
5073
|
|
|
5060
5074
|
function applyYServerMessages(yMessages, db) {
|
|
5061
5075
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5062
|
-
|
|
5076
|
+
var _a;
|
|
5077
|
+
const receivedUntils = {};
|
|
5078
|
+
let resyncNeeded = false;
|
|
5079
|
+
let yServerRevision;
|
|
5063
5080
|
for (const m of yMessages) {
|
|
5064
5081
|
switch (m.type) {
|
|
5065
5082
|
case 'u-s': {
|
|
5066
5083
|
const utbl = getUpdatesTable(db, m.table, m.prop);
|
|
5067
|
-
|
|
5084
|
+
receivedUntils[utbl.name] = yield utbl.add({
|
|
5068
5085
|
k: m.k,
|
|
5069
5086
|
u: m.u,
|
|
5070
5087
|
});
|
|
@@ -5089,7 +5106,24 @@ function applyYServerMessages(yMessages, db) {
|
|
|
5089
5106
|
// See my question in https://discuss.yjs.dev/t/generate-an-inverse-update/2765
|
|
5090
5107
|
console.debug(`Y update rejected. Deleting it.`);
|
|
5091
5108
|
const utbl = getUpdatesTable(db, m.table, m.prop);
|
|
5092
|
-
|
|
5109
|
+
// Delete the rejected update and all local updates since (avoid holes in the CRDT)
|
|
5110
|
+
// and destroy it's open document if there is one.
|
|
5111
|
+
const primaryKey = (_a = (yield utbl.get(m.i))) === null || _a === void 0 ? void 0 : _a.k;
|
|
5112
|
+
if (primaryKey != null) {
|
|
5113
|
+
yield db.transaction('rw', utbl, (tx) => {
|
|
5114
|
+
// @ts-ignore
|
|
5115
|
+
tx.idbtrans._rejecting_y_ypdate = true; // Inform ydoc triggers that we delete because of a rejection and not GC
|
|
5116
|
+
return utbl
|
|
5117
|
+
.where('i')
|
|
5118
|
+
.aboveOrEqual(m.i)
|
|
5119
|
+
.filter((u) => cmp(u.k, primaryKey) === 0 && ((u.f || 0) & 1) === 1)
|
|
5120
|
+
.delete();
|
|
5121
|
+
});
|
|
5122
|
+
// Destroy active doc
|
|
5123
|
+
const activeDoc = DexieYProvider.getDocCache(db.dx).find(m.table, primaryKey, m.prop);
|
|
5124
|
+
if (activeDoc)
|
|
5125
|
+
activeDoc.destroy(); // Destroy the document so that editors don't continue to work on it
|
|
5126
|
+
}
|
|
5093
5127
|
break;
|
|
5094
5128
|
}
|
|
5095
5129
|
case 'in-sync': {
|
|
@@ -5099,15 +5133,26 @@ function applyYServerMessages(yMessages, db) {
|
|
|
5099
5133
|
}
|
|
5100
5134
|
break;
|
|
5101
5135
|
}
|
|
5136
|
+
case 'y-complete-sync-done': {
|
|
5137
|
+
yServerRevision = m.yServerRev;
|
|
5138
|
+
break;
|
|
5139
|
+
}
|
|
5140
|
+
case 'outdated-server-rev':
|
|
5141
|
+
resyncNeeded = true;
|
|
5142
|
+
break;
|
|
5102
5143
|
}
|
|
5103
5144
|
}
|
|
5104
|
-
return
|
|
5145
|
+
return {
|
|
5146
|
+
receivedUntils,
|
|
5147
|
+
resyncNeeded,
|
|
5148
|
+
yServerRevision
|
|
5149
|
+
};
|
|
5105
5150
|
});
|
|
5106
5151
|
}
|
|
5107
5152
|
|
|
5108
|
-
function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db
|
|
5109
|
-
var _a, _b, _c, _d, _e;
|
|
5153
|
+
function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db) {
|
|
5110
5154
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5155
|
+
var _a, _b, _c, _d, _e;
|
|
5111
5156
|
// We want to update unsentFrom for each yTable to the value specified in first argument
|
|
5112
5157
|
// because we got those values before we synced with server and here we are back from server
|
|
5113
5158
|
// that has successfully received all those messages - no matter if the last update was a client or server update,
|
|
@@ -5154,14 +5199,12 @@ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db,
|
|
|
5154
5199
|
yield db.table(yTable).add({
|
|
5155
5200
|
i: DEXIE_CLOUD_SYNCER_ID,
|
|
5156
5201
|
unsentFrom,
|
|
5157
|
-
receivedUntil
|
|
5158
|
-
serverRev: serverRevision,
|
|
5202
|
+
receivedUntil
|
|
5159
5203
|
});
|
|
5160
5204
|
}
|
|
5161
5205
|
else {
|
|
5162
5206
|
state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
|
|
5163
5207
|
state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
|
|
5164
|
-
state.serverRev = serverRevision;
|
|
5165
5208
|
yield db.table(yTable).put(state);
|
|
5166
5209
|
}
|
|
5167
5210
|
}));
|
|
@@ -5172,8 +5215,8 @@ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db,
|
|
|
5172
5215
|
const BINSTREAM_TYPE_REALMID = 1;
|
|
5173
5216
|
const BINSTREAM_TYPE_TABLE_AND_PROP = 2;
|
|
5174
5217
|
const BINSTREAM_TYPE_DOCUMENT = 3;
|
|
5175
|
-
function downloadYDocsFromServer(
|
|
5176
|
-
return __awaiter(this,
|
|
5218
|
+
function downloadYDocsFromServer(db_1, databaseUrl_1, _a) {
|
|
5219
|
+
return __awaiter(this, arguments, void 0, function* (db, databaseUrl, { yDownloadedRealms, realms }) {
|
|
5177
5220
|
if (yDownloadedRealms &&
|
|
5178
5221
|
realms &&
|
|
5179
5222
|
realms.every((realmId) => yDownloadedRealms[realmId] === '*')) {
|
|
@@ -5288,8 +5331,7 @@ function downloadYDocsFromServer(db, databaseUrl, { yDownloadedRealms, realms })
|
|
|
5288
5331
|
|
|
5289
5332
|
const CURRENT_SYNC_WORKER = 'currentSyncWorker';
|
|
5290
5333
|
function sync(db, options, schema, syncOptions) {
|
|
5291
|
-
return _sync
|
|
5292
|
-
.apply(this, arguments)
|
|
5334
|
+
return _sync(db, options, schema, syncOptions)
|
|
5293
5335
|
.then((result) => {
|
|
5294
5336
|
if (!(syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)) { // && syncOptions?.purpose !== 'push') {
|
|
5295
5337
|
db.syncStateChangedEvent.next({
|
|
@@ -5331,11 +5373,11 @@ function sync(db, options, schema, syncOptions) {
|
|
|
5331
5373
|
return Promise.reject(error);
|
|
5332
5374
|
}));
|
|
5333
5375
|
}
|
|
5334
|
-
function _sync(
|
|
5335
|
-
isInitialSync
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5376
|
+
function _sync(db_1, options_1, schema_1) {
|
|
5377
|
+
return __awaiter(this, arguments, void 0, function* (db, options, schema, { isInitialSync, cancelToken, justCheckIfNeeded, purpose } = {
|
|
5378
|
+
isInitialSync: false,
|
|
5379
|
+
}) {
|
|
5380
|
+
var _a;
|
|
5339
5381
|
if (!justCheckIfNeeded) {
|
|
5340
5382
|
console.debug('SYNC STARTED', { isInitialSync, purpose });
|
|
5341
5383
|
}
|
|
@@ -5352,7 +5394,7 @@ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNee
|
|
|
5352
5394
|
// Prepare for syncification by modifying locally unauthorized objects:
|
|
5353
5395
|
//
|
|
5354
5396
|
const persistedSyncState = yield db.getPersistedSyncState();
|
|
5355
|
-
const readyForSyncification =
|
|
5397
|
+
const readyForSyncification = currentUser.isLoggedIn;
|
|
5356
5398
|
const tablesToSyncify = readyForSyncification
|
|
5357
5399
|
? getTablesToSyncify(db, persistedSyncState)
|
|
5358
5400
|
: [];
|
|
@@ -5377,7 +5419,7 @@ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNee
|
|
|
5377
5419
|
const [clientChangeSet, syncState, baseRevs, { yMessages, lastUpdateIds }] = yield db.transaction('r', db.tables, () => __awaiter(this, void 0, void 0, function* () {
|
|
5378
5420
|
const syncState = yield db.getPersistedSyncState();
|
|
5379
5421
|
const baseRevs = yield db.$baseRevs.toArray();
|
|
5380
|
-
let clientChanges = yield listClientChanges(mutationTables);
|
|
5422
|
+
let clientChanges = yield listClientChanges(mutationTables, db);
|
|
5381
5423
|
const yResults = yield listYClientMessagesAndStateVector(db, tablesToSync);
|
|
5382
5424
|
throwIfCancelled(cancelToken);
|
|
5383
5425
|
if (doSyncify) {
|
|
@@ -5497,6 +5539,7 @@ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNee
|
|
|
5497
5539
|
newSyncState.realms = res.realms;
|
|
5498
5540
|
newSyncState.inviteRealms = res.inviteRealms;
|
|
5499
5541
|
newSyncState.serverRevision = res.serverRevision;
|
|
5542
|
+
newSyncState.yServerRevision = res.serverRevision;
|
|
5500
5543
|
newSyncState.timestamp = new Date();
|
|
5501
5544
|
delete newSyncState.error;
|
|
5502
5545
|
const filteredChanges = filterServerChangesThroughAddedClientChanges(res.changes, addedClientChanges);
|
|
@@ -5508,11 +5551,17 @@ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNee
|
|
|
5508
5551
|
//
|
|
5509
5552
|
// apply yMessages
|
|
5510
5553
|
//
|
|
5511
|
-
const receivedUntils = yield applyYServerMessages(res.yMessages, db);
|
|
5554
|
+
const { receivedUntils, resyncNeeded, yServerRevision } = yield applyYServerMessages(res.yMessages, db);
|
|
5555
|
+
if (yServerRevision) {
|
|
5556
|
+
newSyncState.yServerRevision = yServerRevision;
|
|
5557
|
+
}
|
|
5512
5558
|
//
|
|
5513
5559
|
// update Y SyncStates
|
|
5514
5560
|
//
|
|
5515
|
-
yield updateYSyncStates(lastUpdateIds, receivedUntils, db
|
|
5561
|
+
yield updateYSyncStates(lastUpdateIds, receivedUntils, db);
|
|
5562
|
+
if (resyncNeeded) {
|
|
5563
|
+
newSyncState.yDownloadedRealms = {}; // Will trigger a full download of Y-documents below...
|
|
5564
|
+
}
|
|
5516
5565
|
}
|
|
5517
5566
|
//
|
|
5518
5567
|
// Update regular syncState
|
|
@@ -5646,8 +5695,8 @@ function MessagesFromServerConsumer(db) {
|
|
|
5646
5695
|
event.next(null);
|
|
5647
5696
|
}
|
|
5648
5697
|
function consumeQueue() {
|
|
5649
|
-
var _a, _b, _c, _d, _e, _f;
|
|
5650
5698
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5699
|
+
var _a, _b, _c, _d, _e, _f;
|
|
5651
5700
|
while (queue.length > 0) {
|
|
5652
5701
|
const msg = queue.shift();
|
|
5653
5702
|
try {
|
|
@@ -5805,7 +5854,7 @@ function MessagesFromServerConsumer(db) {
|
|
|
5805
5854
|
};
|
|
5806
5855
|
}
|
|
5807
5856
|
|
|
5808
|
-
const wm = new WeakMap();
|
|
5857
|
+
const wm$1 = new WeakMap();
|
|
5809
5858
|
const DEXIE_CLOUD_SCHEMA = {
|
|
5810
5859
|
members: '@id, [userId+realmId], [email+realmId], realmId',
|
|
5811
5860
|
roles: '[realmId+name]',
|
|
@@ -5819,7 +5868,7 @@ let static_counter = 0;
|
|
|
5819
5868
|
function DexieCloudDB(dx) {
|
|
5820
5869
|
if ('vip' in dx)
|
|
5821
5870
|
dx = dx['vip']; // Avoid race condition. Always map to a vipped dexie that don't block during db.on.ready().
|
|
5822
|
-
let db = wm.get(dx.cloud);
|
|
5871
|
+
let db = wm$1.get(dx.cloud);
|
|
5823
5872
|
if (!db) {
|
|
5824
5873
|
const localSyncEvent = new Subject();
|
|
5825
5874
|
let syncStateChangedEvent = new BroadcastedAndLocalEvent(`syncstatechanged-${dx.name}`);
|
|
@@ -5907,7 +5956,7 @@ function DexieCloudDB(dx) {
|
|
|
5907
5956
|
Object.assign(db, helperMethods);
|
|
5908
5957
|
db.messageConsumer = MessagesFromServerConsumer(db);
|
|
5909
5958
|
db.messageProducer = new Subject();
|
|
5910
|
-
wm.set(dx.cloud, db);
|
|
5959
|
+
wm$1.set(dx.cloud, db);
|
|
5911
5960
|
}
|
|
5912
5961
|
return db;
|
|
5913
5962
|
}
|
|
@@ -6303,8 +6352,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
6303
6352
|
outstandingTransactions.next(outstandingTransactions.value);
|
|
6304
6353
|
};
|
|
6305
6354
|
const txComplete = () => {
|
|
6306
|
-
if (tx.mutationsAdded &&
|
|
6307
|
-
!isEagerSyncDisabled(db)) {
|
|
6355
|
+
if (tx.mutationsAdded && !isEagerSyncDisabled(db)) {
|
|
6308
6356
|
triggerSync(db, 'push');
|
|
6309
6357
|
}
|
|
6310
6358
|
removeTransaction();
|
|
@@ -6386,26 +6434,107 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
6386
6434
|
: mutateAndLog(req);
|
|
6387
6435
|
} }));
|
|
6388
6436
|
function mutateAndLog(req) {
|
|
6437
|
+
var _a, _b;
|
|
6389
6438
|
const trans = req.trans;
|
|
6390
|
-
|
|
6439
|
+
const unsyncedProps = (_b = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.unsyncedProperties) === null || _b === void 0 ? void 0 : _b[tableName];
|
|
6391
6440
|
const { txid, currentUser: { userId }, } = trans;
|
|
6392
6441
|
const { type } = req;
|
|
6393
6442
|
const opNo = ++trans.opCount;
|
|
6443
|
+
function stripChangeSpec(changeSpec) {
|
|
6444
|
+
if (!unsyncedProps)
|
|
6445
|
+
return changeSpec;
|
|
6446
|
+
let rv = changeSpec;
|
|
6447
|
+
for (const keyPath of Object.keys(changeSpec)) {
|
|
6448
|
+
if (unsyncedProps.some((p) => keyPath === p || keyPath.startsWith(p + '.'))) {
|
|
6449
|
+
if (rv === changeSpec)
|
|
6450
|
+
rv = Object.assign({}, changeSpec); // clone on demand
|
|
6451
|
+
delete rv[keyPath];
|
|
6452
|
+
}
|
|
6453
|
+
}
|
|
6454
|
+
return rv;
|
|
6455
|
+
}
|
|
6394
6456
|
return table.mutate(req).then((res) => {
|
|
6457
|
+
var _a;
|
|
6395
6458
|
const { numFailures: hasFailures, failures } = res;
|
|
6396
6459
|
let keys = type === 'delete' ? req.keys : res.results;
|
|
6397
6460
|
let values = 'values' in req ? req.values : [];
|
|
6398
|
-
let
|
|
6461
|
+
let changeSpec = 'changeSpec' in req ? req.changeSpec : undefined;
|
|
6462
|
+
let updates = 'updates' in req ? req.updates : undefined;
|
|
6399
6463
|
if (hasFailures) {
|
|
6400
6464
|
keys = keys.filter((_, idx) => !failures[idx]);
|
|
6401
6465
|
values = values.filter((_, idx) => !failures[idx]);
|
|
6402
6466
|
}
|
|
6467
|
+
if (unsyncedProps) {
|
|
6468
|
+
// Filter out unsynced properties
|
|
6469
|
+
values = values.map((value) => {
|
|
6470
|
+
const newValue = Object.assign({}, value);
|
|
6471
|
+
for (const prop of unsyncedProps) {
|
|
6472
|
+
delete newValue[prop];
|
|
6473
|
+
}
|
|
6474
|
+
return newValue;
|
|
6475
|
+
});
|
|
6476
|
+
if (changeSpec) {
|
|
6477
|
+
// modify operation with criteria and changeSpec.
|
|
6478
|
+
// We must strip out unsynced properties from changeSpec.
|
|
6479
|
+
// We deal with criteria later.
|
|
6480
|
+
changeSpec = stripChangeSpec(changeSpec);
|
|
6481
|
+
if (Object.keys(changeSpec).length === 0) {
|
|
6482
|
+
// Nothing to change on server
|
|
6483
|
+
return res;
|
|
6484
|
+
}
|
|
6485
|
+
}
|
|
6486
|
+
if (updates) {
|
|
6487
|
+
let strippedChangeSpecs = updates.changeSpecs.map(stripChangeSpec);
|
|
6488
|
+
let newUpdates = {
|
|
6489
|
+
keys: [],
|
|
6490
|
+
changeSpecs: [],
|
|
6491
|
+
};
|
|
6492
|
+
const validKeys = new RangeSet();
|
|
6493
|
+
let anyChangeSpecBecameEmpty = false;
|
|
6494
|
+
for (let i = 0, l = strippedChangeSpecs.length; i < l; ++i) {
|
|
6495
|
+
if (Object.keys(strippedChangeSpecs[i]).length > 0) {
|
|
6496
|
+
newUpdates.keys.push(updates.keys[i]);
|
|
6497
|
+
newUpdates.changeSpecs.push(strippedChangeSpecs[i]);
|
|
6498
|
+
validKeys.addKey(updates.keys[i]);
|
|
6499
|
+
}
|
|
6500
|
+
else {
|
|
6501
|
+
anyChangeSpecBecameEmpty = true;
|
|
6502
|
+
}
|
|
6503
|
+
}
|
|
6504
|
+
updates = newUpdates;
|
|
6505
|
+
if (anyChangeSpecBecameEmpty) {
|
|
6506
|
+
// Some keys were stripped. We must also strip them from keys and values
|
|
6507
|
+
let newKeys = [];
|
|
6508
|
+
let newValues = [];
|
|
6509
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
6510
|
+
if (validKeys.hasKey(keys[i])) {
|
|
6511
|
+
newKeys.push(keys[i]);
|
|
6512
|
+
newValues.push(values[i]);
|
|
6513
|
+
}
|
|
6514
|
+
}
|
|
6515
|
+
keys = newKeys;
|
|
6516
|
+
values = newValues;
|
|
6517
|
+
}
|
|
6518
|
+
}
|
|
6519
|
+
}
|
|
6403
6520
|
const ts = Date.now();
|
|
6404
6521
|
// Canonicalize req.criteria.index to null if it's on the primary key.
|
|
6405
|
-
|
|
6522
|
+
let criteria = 'criteria' in req && req.criteria
|
|
6406
6523
|
? Object.assign(Object.assign({}, req.criteria), { index: req.criteria.index === schema.primaryKey.keyPath // Use null to inform server that criteria is on primary key
|
|
6407
6524
|
? null // This will disable the server from trying to log consistent operations where it shouldnt.
|
|
6408
6525
|
: req.criteria.index }) : undefined;
|
|
6526
|
+
if (unsyncedProps && (criteria === null || criteria === void 0 ? void 0 : criteria.index)) {
|
|
6527
|
+
const keyPaths = (_a = schema.indexes.find((idx) => idx.name === criteria.index)) === null || _a === void 0 ? void 0 : _a.keyPath;
|
|
6528
|
+
const involvedProps = keyPaths
|
|
6529
|
+
? typeof keyPaths === 'string'
|
|
6530
|
+
? [keyPaths]
|
|
6531
|
+
: keyPaths
|
|
6532
|
+
: [];
|
|
6533
|
+
if (involvedProps.some((p) => unsyncedProps === null || unsyncedProps === void 0 ? void 0 : unsyncedProps.includes(p))) {
|
|
6534
|
+
// Don't log criteria on unsynced properties as the server could not test them.
|
|
6535
|
+
criteria = undefined;
|
|
6536
|
+
}
|
|
6537
|
+
}
|
|
6409
6538
|
const mut = req.type === 'delete'
|
|
6410
6539
|
? {
|
|
6411
6540
|
type: 'delete',
|
|
@@ -6426,7 +6555,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
6426
6555
|
userId,
|
|
6427
6556
|
values,
|
|
6428
6557
|
}
|
|
6429
|
-
: criteria &&
|
|
6558
|
+
: criteria && changeSpec
|
|
6430
6559
|
? {
|
|
6431
6560
|
// Common changeSpec for all keys
|
|
6432
6561
|
type: 'modify',
|
|
@@ -6434,37 +6563,51 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
6434
6563
|
opNo,
|
|
6435
6564
|
keys,
|
|
6436
6565
|
criteria,
|
|
6437
|
-
changeSpec
|
|
6566
|
+
changeSpec,
|
|
6438
6567
|
txid,
|
|
6439
6568
|
userId,
|
|
6440
6569
|
}
|
|
6441
|
-
:
|
|
6570
|
+
: changeSpec
|
|
6442
6571
|
? {
|
|
6443
|
-
//
|
|
6572
|
+
// In case criteria involved an unsynced property, we go for keys instead.
|
|
6444
6573
|
type: 'update',
|
|
6445
6574
|
ts,
|
|
6446
6575
|
opNo,
|
|
6447
|
-
keys: updates.keys,
|
|
6448
|
-
changeSpecs: updates.changeSpecs,
|
|
6449
|
-
txid,
|
|
6450
|
-
userId,
|
|
6451
|
-
}
|
|
6452
|
-
: {
|
|
6453
|
-
type: 'upsert',
|
|
6454
|
-
ts,
|
|
6455
|
-
opNo,
|
|
6456
6576
|
keys,
|
|
6457
|
-
|
|
6577
|
+
changeSpecs: keys.map(() => changeSpec),
|
|
6458
6578
|
txid,
|
|
6459
6579
|
userId,
|
|
6460
|
-
}
|
|
6580
|
+
}
|
|
6581
|
+
: updates
|
|
6582
|
+
? {
|
|
6583
|
+
// One changeSpec per key
|
|
6584
|
+
type: 'update',
|
|
6585
|
+
ts,
|
|
6586
|
+
opNo,
|
|
6587
|
+
keys: updates.keys,
|
|
6588
|
+
changeSpecs: updates.changeSpecs,
|
|
6589
|
+
txid,
|
|
6590
|
+
userId,
|
|
6591
|
+
}
|
|
6592
|
+
: {
|
|
6593
|
+
type: 'upsert',
|
|
6594
|
+
ts,
|
|
6595
|
+
opNo,
|
|
6596
|
+
keys,
|
|
6597
|
+
values,
|
|
6598
|
+
txid,
|
|
6599
|
+
userId,
|
|
6600
|
+
};
|
|
6461
6601
|
if ('isAdditionalChunk' in req && req.isAdditionalChunk) {
|
|
6462
6602
|
mut.isAdditionalChunk = true;
|
|
6463
6603
|
}
|
|
6464
6604
|
return keys.length > 0 || criteria
|
|
6465
6605
|
? mutsTable
|
|
6466
6606
|
.mutate({ type: 'add', trans, values: [mut] }) // Log entry
|
|
6467
|
-
.then(() =>
|
|
6607
|
+
.then(() => {
|
|
6608
|
+
trans.mutationsAdded = true; // Mark transaction as having added mutations to trigger eager sync
|
|
6609
|
+
return res; // Return original response
|
|
6610
|
+
})
|
|
6468
6611
|
: res;
|
|
6469
6612
|
});
|
|
6470
6613
|
}
|
|
@@ -6548,10 +6691,18 @@ function overrideParseStoresSpec(origFunc, dexie) {
|
|
|
6548
6691
|
};
|
|
6549
6692
|
}
|
|
6550
6693
|
|
|
6694
|
+
function performGuardedJob(db, jobName, job) {
|
|
6695
|
+
if (typeof navigator === 'undefined' || !navigator.locks) {
|
|
6696
|
+
// No support for guarding jobs. IE11, node.js, etc.
|
|
6697
|
+
return job();
|
|
6698
|
+
}
|
|
6699
|
+
return navigator.locks.request(db.name + '|' + jobName, () => job());
|
|
6700
|
+
}
|
|
6701
|
+
|
|
6551
6702
|
function performInitialSync(db, cloudOptions, cloudSchema) {
|
|
6552
6703
|
return __awaiter(this, void 0, void 0, function* () {
|
|
6553
6704
|
console.debug('Performing initial sync');
|
|
6554
|
-
yield sync(db, cloudOptions, cloudSchema, { isInitialSync: true });
|
|
6705
|
+
yield performGuardedJob(db, CURRENT_SYNC_WORKER, () => sync(db, cloudOptions, cloudSchema, { isInitialSync: true }));
|
|
6555
6706
|
console.debug('Done initial sync');
|
|
6556
6707
|
});
|
|
6557
6708
|
}
|
|
@@ -6638,29 +6789,35 @@ function createYClientUpdateObservable(db) {
|
|
|
6638
6789
|
updatesTable: p.updatesTable,
|
|
6639
6790
|
}))));
|
|
6640
6791
|
return merge(...yTableRecords.map(({ table, ydocProp, updatesTable }) => {
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
|
|
6652
|
-
|
|
6653
|
-
|
|
6654
|
-
|
|
6655
|
-
|
|
6656
|
-
|
|
6657
|
-
|
|
6658
|
-
|
|
6659
|
-
|
|
6660
|
-
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
6792
|
+
// Per updates table (table+prop combo), we first read syncer.unsentFrom,
|
|
6793
|
+
// and then start listening for updates since that number.
|
|
6794
|
+
const yTbl = db.table(updatesTable);
|
|
6795
|
+
return from$1(yTbl.get(DEXIE_CLOUD_SYNCER_ID)).pipe(switchMap$1((syncer) => {
|
|
6796
|
+
let currentUnsentFrom = (syncer === null || syncer === void 0 ? void 0 : syncer.unsentFrom) || 1;
|
|
6797
|
+
return from$1(liveQuery(() => __awaiter(this, void 0, void 0, function* () {
|
|
6798
|
+
const addedUpdates = yield listUpdatesSince(yTbl, currentUnsentFrom);
|
|
6799
|
+
return addedUpdates
|
|
6800
|
+
.filter((update) => update.f && update.f & 1) // Only include local updates
|
|
6801
|
+
.map((update) => {
|
|
6802
|
+
return {
|
|
6803
|
+
type: 'u-c',
|
|
6804
|
+
table,
|
|
6805
|
+
prop: ydocProp,
|
|
6806
|
+
k: update.k,
|
|
6807
|
+
u: update.u,
|
|
6808
|
+
i: update.i,
|
|
6809
|
+
};
|
|
6810
|
+
});
|
|
6811
|
+
}))).pipe(tap$1((addedUpdates) => {
|
|
6812
|
+
// Update currentUnsentFrom to only listen for updates that will be newer than the ones we emitted.
|
|
6813
|
+
// (Before, we did this within the liveQuery, but that caused a bug because
|
|
6814
|
+
// a cancelled emittion of a liveQuery would update the currentUnsentFrom without
|
|
6815
|
+
// emitting anything, leading to that we jumped over some updates. Here we update it
|
|
6816
|
+
// after the liveQuery has emitted its updates)
|
|
6817
|
+
if (addedUpdates.length > 0) {
|
|
6818
|
+
currentUnsentFrom = addedUpdates.at(-1).i + 1;
|
|
6819
|
+
}
|
|
6820
|
+
}));
|
|
6664
6821
|
}));
|
|
6665
6822
|
})).pipe(
|
|
6666
6823
|
// Flatten the array of messages.
|
|
@@ -6680,6 +6837,23 @@ function getAwarenessLibrary(db) {
|
|
|
6680
6837
|
const awarenessWeakMap = new WeakMap();
|
|
6681
6838
|
const getDocAwareness = (doc) => awarenessWeakMap.get(doc);
|
|
6682
6839
|
|
|
6840
|
+
const wm = new WeakMap();
|
|
6841
|
+
/** A property (package-private) on Y.Doc that is used
|
|
6842
|
+
* to signal that the server wants us to send a 'doc-open' message
|
|
6843
|
+
* to the server for this document.
|
|
6844
|
+
*
|
|
6845
|
+
* @param doc
|
|
6846
|
+
* @returns
|
|
6847
|
+
*/
|
|
6848
|
+
function getOpenDocSignal(doc) {
|
|
6849
|
+
let signal = wm.get(doc);
|
|
6850
|
+
if (!signal) {
|
|
6851
|
+
signal = new Subject();
|
|
6852
|
+
wm.set(doc, signal);
|
|
6853
|
+
}
|
|
6854
|
+
return signal;
|
|
6855
|
+
}
|
|
6856
|
+
|
|
6683
6857
|
const SERVER_PING_TIMEOUT = 20000;
|
|
6684
6858
|
const CLIENT_PING_INTERVAL = 30000;
|
|
6685
6859
|
const FAIL_RETRY_WAIT_TIME = 60000;
|
|
@@ -6842,9 +7016,6 @@ class WSConnection extends Subscription$1 {
|
|
|
6842
7016
|
if (msg.type === 'error') {
|
|
6843
7017
|
throw new Error(`Error message from dexie-cloud: ${msg.error}`);
|
|
6844
7018
|
}
|
|
6845
|
-
else if (msg.type === 'rev') {
|
|
6846
|
-
this.rev = msg.rev; // No meaning but seems reasonable.
|
|
6847
|
-
}
|
|
6848
7019
|
else if (msg.type === 'aware') {
|
|
6849
7020
|
const docCache = DexieYProvider.getDocCache(this.db.dx);
|
|
6850
7021
|
const doc = docCache.find(msg.table, msg.k, msg.prop);
|
|
@@ -6859,7 +7030,19 @@ class WSConnection extends Subscription$1 {
|
|
|
6859
7030
|
else if (msg.type === 'u-ack' || msg.type === 'u-reject' || msg.type === 'u-s' || msg.type === 'in-sync') {
|
|
6860
7031
|
applyYServerMessages([msg], this.db);
|
|
6861
7032
|
}
|
|
7033
|
+
else if (msg.type === 'doc-open') {
|
|
7034
|
+
const docCache = DexieYProvider.getDocCache(this.db.dx);
|
|
7035
|
+
const doc = docCache.find(msg.table, msg.k, msg.prop);
|
|
7036
|
+
if (doc) {
|
|
7037
|
+
getOpenDocSignal(doc).next(); // Make yHandler reopen the document on server.
|
|
7038
|
+
}
|
|
7039
|
+
}
|
|
7040
|
+
else if (msg.type === 'outdated-server-rev' || msg.type === 'y-complete-sync-done') {
|
|
7041
|
+
// Won't happen but need this for typing.
|
|
7042
|
+
throw new Error('Outdated server revision or y-complete-sync-done not expected over WebSocket - only in sync using fetch()');
|
|
7043
|
+
}
|
|
6862
7044
|
else if (msg.type !== 'pong') {
|
|
7045
|
+
// Forward the request to our subscriber, wich is in messageFromServerQueue.ts (via connectWebSocket's subscribe() at the end!)
|
|
6863
7046
|
this.subscriber.next(msg);
|
|
6864
7047
|
}
|
|
6865
7048
|
}
|
|
@@ -6896,6 +7079,10 @@ class WSConnection extends Subscription$1 {
|
|
|
6896
7079
|
}
|
|
6897
7080
|
console.debug('dexie-cloud WebSocket send', msg.type, msg);
|
|
6898
7081
|
if (msg.type === 'ready') {
|
|
7082
|
+
// Ok, we are certain to have stored everything up until revision msg.rev.
|
|
7083
|
+
// Update this.rev in case of reconnect - remember where we were and don't just start over!
|
|
7084
|
+
this.rev = msg.rev;
|
|
7085
|
+
// ... and then send along the request to the server so it would also be updated!
|
|
6899
7086
|
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(TSON.stringify(msg));
|
|
6900
7087
|
}
|
|
6901
7088
|
else {
|
|
@@ -6977,7 +7164,7 @@ function connectWebSocket(db) {
|
|
|
6977
7164
|
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]));
|
|
6978
7165
|
}
|
|
6979
7166
|
return new BehaviorSubject([userLogin, syncState]);
|
|
6980
|
-
}), switchMap((
|
|
7167
|
+
}), 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]) => {
|
|
6981
7168
|
var _a;
|
|
6982
7169
|
if (!((_a = db.cloud.persistedSyncState) === null || _a === void 0 ? void 0 : _a.value)) {
|
|
6983
7170
|
// Restart the flow if persistedSyncState is not yet available.
|
|
@@ -7038,22 +7225,14 @@ function connectWebSocket(db) {
|
|
|
7038
7225
|
}
|
|
7039
7226
|
|
|
7040
7227
|
function isSyncNeeded(db) {
|
|
7041
|
-
var _a;
|
|
7042
7228
|
return __awaiter(this, void 0, void 0, function* () {
|
|
7229
|
+
var _a;
|
|
7043
7230
|
return ((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl) && db.cloud.schema
|
|
7044
7231
|
? yield sync(db, db.cloud.options, db.cloud.schema, { justCheckIfNeeded: true })
|
|
7045
7232
|
: false;
|
|
7046
7233
|
});
|
|
7047
7234
|
}
|
|
7048
7235
|
|
|
7049
|
-
function performGuardedJob(db, jobName, job) {
|
|
7050
|
-
if (typeof navigator === 'undefined' || !navigator.locks) {
|
|
7051
|
-
// No support for guarding jobs. IE11, node.js, etc.
|
|
7052
|
-
return job();
|
|
7053
|
-
}
|
|
7054
|
-
return navigator.locks.request(db.name + '|' + jobName, () => job());
|
|
7055
|
-
}
|
|
7056
|
-
|
|
7057
7236
|
const ongoingSyncs = new WeakMap();
|
|
7058
7237
|
function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
7059
7238
|
const ongoing = ongoingSyncs.get(db);
|
|
@@ -7107,20 +7286,7 @@ function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
|
7107
7286
|
try {
|
|
7108
7287
|
// Check if should delay sync due to ratelimit:
|
|
7109
7288
|
yield checkSyncRateLimitDelay(db);
|
|
7110
|
-
|
|
7111
|
-
if (db.cloud.isServiceWorkerDB) {
|
|
7112
|
-
// We are the dedicated sync SW:
|
|
7113
|
-
yield sync(db, cloudOptions, cloudSchema, options);
|
|
7114
|
-
}
|
|
7115
|
-
else if (!db.cloud.usingServiceWorker) {
|
|
7116
|
-
// We use a flow that is better suited for the case when multiple workers want to
|
|
7117
|
-
// do the same thing.
|
|
7118
|
-
yield performGuardedJob(db, CURRENT_SYNC_WORKER, () => sync(db, cloudOptions, cloudSchema, options));
|
|
7119
|
-
}
|
|
7120
|
-
else {
|
|
7121
|
-
assert(false);
|
|
7122
|
-
throw new Error('Internal _syncIfPossible() - invalid precondition - should not have been called.');
|
|
7123
|
-
}
|
|
7289
|
+
yield performGuardedJob(db, CURRENT_SYNC_WORKER, () => sync(db, cloudOptions, cloudSchema, options));
|
|
7124
7290
|
ongoingSyncs.delete(db);
|
|
7125
7291
|
console.debug('Done sync');
|
|
7126
7292
|
}
|
|
@@ -7723,6 +7889,7 @@ class PermissionChecker {
|
|
|
7723
7889
|
// If user can update any prop in any table in this realm, return true unless
|
|
7724
7890
|
// it regards to ownership change:
|
|
7725
7891
|
if (this.permissions.update === '*') {
|
|
7892
|
+
// @ts-ignore
|
|
7726
7893
|
return props.every((prop) => prop !== 'owner');
|
|
7727
7894
|
}
|
|
7728
7895
|
const tablePermissions = (_b = this.permissions.update) === null || _b === void 0 ? void 0 : _b[this.tableName];
|
|
@@ -7796,125 +7963,151 @@ const getInvitesObservable = associate((db) => {
|
|
|
7796
7963
|
function createYHandler(db) {
|
|
7797
7964
|
return (provider) => {
|
|
7798
7965
|
var _a;
|
|
7799
|
-
const awap = getAwarenessLibrary(db);
|
|
7800
7966
|
const doc = provider.doc;
|
|
7801
|
-
const { parentTable
|
|
7967
|
+
const { parentTable } = doc.meta || {};
|
|
7802
7968
|
if (!((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[parentTable].markedForSync)) {
|
|
7803
7969
|
return; // The table that holds the doc is not marked for sync - leave it to dexie. No syncing, no awareness.
|
|
7804
7970
|
}
|
|
7805
|
-
let awareness
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7971
|
+
let awareness;
|
|
7972
|
+
Object.defineProperty(provider, 'awareness', {
|
|
7973
|
+
get() {
|
|
7974
|
+
if (awareness)
|
|
7975
|
+
return awareness;
|
|
7976
|
+
awareness = createAwareness(db, doc, provider);
|
|
7977
|
+
awarenessWeakMap.set(doc, awareness);
|
|
7978
|
+
return awareness;
|
|
7979
|
+
},
|
|
7980
|
+
});
|
|
7981
|
+
};
|
|
7982
|
+
}
|
|
7983
|
+
function createAwareness(db, doc, provider) {
|
|
7984
|
+
const { parentTable, parentId, parentProp, updatesTable } = doc.meta;
|
|
7985
|
+
const awap = getAwarenessLibrary(db);
|
|
7986
|
+
const awareness = new awap.Awareness(doc);
|
|
7987
|
+
const reopenDocSignal = getOpenDocSignal(doc);
|
|
7988
|
+
awareness.on('update', ({ added, updated, removed }, origin) => {
|
|
7989
|
+
// Send the update
|
|
7990
|
+
const changedClients = added.concat(updated).concat(removed);
|
|
7991
|
+
const user = db.cloud.currentUser.value;
|
|
7992
|
+
if (origin !== 'server' && user.isLoggedIn && !isEagerSyncDisabled(db)) {
|
|
7993
|
+
const update = awap.encodeAwarenessUpdate(awareness, changedClients);
|
|
7994
|
+
db.messageProducer.next({
|
|
7995
|
+
type: 'aware',
|
|
7996
|
+
table: parentTable,
|
|
7997
|
+
prop: parentProp,
|
|
7998
|
+
k: doc.meta.parentId,
|
|
7999
|
+
u: update,
|
|
8000
|
+
});
|
|
8001
|
+
if (provider.destroyed) {
|
|
8002
|
+
// We're called from awareness.on('destroy') that did
|
|
8003
|
+
// removeAwarenessStates.
|
|
8004
|
+
// It's time to also send the doc-close message that dexie-cloud understands
|
|
8005
|
+
// and uses to stop subscribing for updates and awareness updates and brings
|
|
8006
|
+
// down the cached information in memory on the WS connection for this.
|
|
7814
8007
|
db.messageProducer.next({
|
|
7815
|
-
type: '
|
|
8008
|
+
type: 'doc-close',
|
|
7816
8009
|
table: parentTable,
|
|
7817
8010
|
prop: parentProp,
|
|
7818
8011
|
k: doc.meta.parentId,
|
|
7819
|
-
u: update,
|
|
7820
8012
|
});
|
|
7821
|
-
if (provider.destroyed) {
|
|
7822
|
-
// We're called from awareness.on('destroy') that did
|
|
7823
|
-
// removeAwarenessStates.
|
|
7824
|
-
// It's time to also send the doc-close message that dexie-cloud understands
|
|
7825
|
-
// and uses to stop subscribing for updates and awareness updates and brings
|
|
7826
|
-
// down the cached information in memory on the WS connection for this.
|
|
7827
|
-
db.messageProducer.next({
|
|
7828
|
-
type: 'doc-close',
|
|
7829
|
-
table: parentTable,
|
|
7830
|
-
prop: parentProp,
|
|
7831
|
-
k: doc.meta.parentId
|
|
7832
|
-
});
|
|
7833
|
-
}
|
|
7834
8013
|
}
|
|
7835
|
-
}
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
|
|
7840
|
-
|
|
7841
|
-
|
|
7842
|
-
|
|
8014
|
+
}
|
|
8015
|
+
});
|
|
8016
|
+
awareness.on('destroy', () => {
|
|
8017
|
+
// Signal to server that this provider is destroyed (the update event will be triggered, which
|
|
8018
|
+
// in turn will trigger db.messageProducer that will send the message to the server if WS is connected)
|
|
8019
|
+
awap.removeAwarenessStates(awareness, [doc.clientID], 'provider destroyed');
|
|
8020
|
+
});
|
|
8021
|
+
// Open the document on the server
|
|
8022
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
8023
|
+
if (provider.destroyed)
|
|
8024
|
+
return;
|
|
8025
|
+
let connected = false;
|
|
8026
|
+
let currentFlowId = 1;
|
|
8027
|
+
const subscription = combineLatest([
|
|
8028
|
+
db.cloud.webSocketStatus, // Wake up when webSocket status changes
|
|
8029
|
+
reopenDocSignal.pipe(startWith$1(null)), // Wake up when reopenDocSignal emits
|
|
8030
|
+
]).subscribe(([wsStatus]) => {
|
|
7843
8031
|
if (provider.destroyed)
|
|
7844
8032
|
return;
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
8033
|
+
// Keep "connected" state in a variable so we can check it after async operations
|
|
8034
|
+
connected = wsStatus === 'connected';
|
|
8035
|
+
// We are or got connected. Open the document on the server.
|
|
8036
|
+
const user = db.cloud.currentUser.value;
|
|
8037
|
+
if (wsStatus === 'connected' &&
|
|
8038
|
+
user.isLoggedIn &&
|
|
8039
|
+
!isEagerSyncDisabled(db)) {
|
|
8040
|
+
++currentFlowId;
|
|
8041
|
+
openDocumentOnServer().catch((error) => {
|
|
8042
|
+
console.warn(`Error catched in createYHandler.ts: ${error}`);
|
|
8043
|
+
});
|
|
8044
|
+
}
|
|
8045
|
+
});
|
|
8046
|
+
// Wait until WebSocket is connected
|
|
8047
|
+
provider.addCleanupHandler(subscription);
|
|
8048
|
+
/** Sends an 'doc-open' message to server whenever websocket becomes
|
|
8049
|
+
* connected, or if it is already connected.
|
|
8050
|
+
* The flow is aborted in case websocket is disconnected while querying
|
|
8051
|
+
* information required to compute the state vector. Flow is also
|
|
8052
|
+
* aborted in case document or provider has been destroyed during
|
|
8053
|
+
* the async parts of the task.
|
|
8054
|
+
*
|
|
8055
|
+
* The state vector is only computed from the updates that have occured
|
|
8056
|
+
* after the last full sync - which could very often be zero - in which
|
|
8057
|
+
* case no state vector is sent (then the server already knows us by
|
|
8058
|
+
* revision)
|
|
8059
|
+
*
|
|
8060
|
+
* When server gets the doc-open message, it will authorize us for
|
|
8061
|
+
* whether we are allowed to read / write to this document, and then
|
|
8062
|
+
* keep the cached information in memory on the WS connection for this
|
|
8063
|
+
* particular document, as well as subscribe to updates and awareness updates
|
|
8064
|
+
* from other clients on the document.
|
|
8065
|
+
*/
|
|
8066
|
+
function openDocumentOnServer() {
|
|
8067
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8068
|
+
const myFlow = currentFlowId; // So we can abort when a new flow is started
|
|
8069
|
+
const yTbl = db.table(updatesTable);
|
|
8070
|
+
const syncStateTbl = db.$syncState;
|
|
8071
|
+
const [receivedUntil, yServerRev] = yield db.transaction('r', syncStateTbl, yTbl, () => __awaiter(this, void 0, void 0, function* () {
|
|
8072
|
+
const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
|
|
8073
|
+
const persistedSyncState = yield syncStateTbl.get('syncState');
|
|
8074
|
+
return [
|
|
8075
|
+
(syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0,
|
|
8076
|
+
(persistedSyncState === null || persistedSyncState === void 0 ? void 0 : persistedSyncState.yServerRevision) ||
|
|
8077
|
+
(persistedSyncState === null || persistedSyncState === void 0 ? void 0 : persistedSyncState.serverRevision),
|
|
8078
|
+
];
|
|
8079
|
+
}));
|
|
8080
|
+
// After every await, check if we still should be working on this task.
|
|
8081
|
+
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7849
8082
|
return;
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
8083
|
+
const docOpenMsg = {
|
|
8084
|
+
type: 'doc-open',
|
|
8085
|
+
table: parentTable,
|
|
8086
|
+
prop: parentProp,
|
|
8087
|
+
k: parentId,
|
|
8088
|
+
serverRev: yServerRev,
|
|
8089
|
+
};
|
|
8090
|
+
const serverUpdatesSinceLastSync = yield yTbl
|
|
8091
|
+
.where('i')
|
|
8092
|
+
.between(receivedUntil, Infinity, false)
|
|
8093
|
+
.filter((update) => cmp(update.k, parentId) === 0 && // Only updates for this document
|
|
8094
|
+
((update.f || 0) & 1) === 0 // Don't include local changes
|
|
8095
|
+
)
|
|
8096
|
+
.toArray();
|
|
8097
|
+
// After every await, check if we still should be working on this task.
|
|
8098
|
+
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
8099
|
+
return;
|
|
8100
|
+
if (serverUpdatesSinceLastSync.length > 0) {
|
|
8101
|
+
const Y = $Y$1(db); // Get the Yjs library from Dexie constructor options
|
|
8102
|
+
const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
|
|
8103
|
+
const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
|
|
8104
|
+
docOpenMsg.sv = stateVector;
|
|
7859
8105
|
}
|
|
8106
|
+
db.messageProducer.next(docOpenMsg);
|
|
7860
8107
|
});
|
|
7861
|
-
|
|
7862
|
-
|
|
7863
|
-
|
|
7864
|
-
* connected, or if it is already connected.
|
|
7865
|
-
* The flow is aborted in case websocket is disconnected while querying
|
|
7866
|
-
* information required to compute the state vector. Flow is also
|
|
7867
|
-
* aborted in case document or provider has been destroyed during
|
|
7868
|
-
* the async parts of the task.
|
|
7869
|
-
*
|
|
7870
|
-
* The state vector is only computed from the updates that have occured
|
|
7871
|
-
* after the last full sync - which could very often be zero - in which
|
|
7872
|
-
* case no state vector is sent (then the server already knows us by
|
|
7873
|
-
* revision)
|
|
7874
|
-
*
|
|
7875
|
-
* When server gets the doc-open message, it will authorized us for
|
|
7876
|
-
* whether we are allowed to read / write to this document, and then
|
|
7877
|
-
* keep the cached information in memory on the WS connection for this
|
|
7878
|
-
* particular document, as well as subscribe to updates and awareness updates
|
|
7879
|
-
* from other clients on the document.
|
|
7880
|
-
*/
|
|
7881
|
-
function openDocumentOnServer(wsStatus) {
|
|
7882
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
7883
|
-
const myFlow = currentFlowId; // So we can abort when a new flow is started
|
|
7884
|
-
const yTbl = db.table(updatesTable);
|
|
7885
|
-
const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
|
|
7886
|
-
// After every await, check if we still should be working on this task.
|
|
7887
|
-
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7888
|
-
return;
|
|
7889
|
-
const receivedUntil = (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0;
|
|
7890
|
-
const docOpenMsg = {
|
|
7891
|
-
type: 'doc-open',
|
|
7892
|
-
table: parentTable,
|
|
7893
|
-
prop: parentProp,
|
|
7894
|
-
k: parentId,
|
|
7895
|
-
serverRev: syncState === null || syncState === void 0 ? void 0 : syncState.serverRev,
|
|
7896
|
-
};
|
|
7897
|
-
const serverUpdatesSinceLastSync = yield yTbl
|
|
7898
|
-
.where('i')
|
|
7899
|
-
.between(receivedUntil, Infinity, false)
|
|
7900
|
-
.filter((update) => cmp(update.k, parentId) === 0 && // Only updates for this document
|
|
7901
|
-
((update.f || 0) & 1) === 0 // Don't include local changes
|
|
7902
|
-
)
|
|
7903
|
-
.toArray();
|
|
7904
|
-
// After every await, check if we still should be working on this task.
|
|
7905
|
-
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7906
|
-
return;
|
|
7907
|
-
if (serverUpdatesSinceLastSync.length > 0) {
|
|
7908
|
-
const Y = $Y(db); // Get the Yjs library from Dexie constructor options
|
|
7909
|
-
const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
|
|
7910
|
-
const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
|
|
7911
|
-
docOpenMsg.sv = stateVector;
|
|
7912
|
-
}
|
|
7913
|
-
db.messageProducer.next(docOpenMsg);
|
|
7914
|
-
});
|
|
7915
|
-
}
|
|
7916
|
-
}));
|
|
7917
|
-
};
|
|
8108
|
+
}
|
|
8109
|
+
}))();
|
|
8110
|
+
return awareness;
|
|
7918
8111
|
}
|
|
7919
8112
|
|
|
7920
8113
|
function getTiedRealmId(objectId) {
|
|
@@ -7964,7 +8157,7 @@ function dexieCloud(dexie) {
|
|
|
7964
8157
|
const syncComplete = new Subject();
|
|
7965
8158
|
dexie.cloud = {
|
|
7966
8159
|
// @ts-ignore
|
|
7967
|
-
version: "4.1.0-
|
|
8160
|
+
version: "4.1.0-beta.25",
|
|
7968
8161
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
7969
8162
|
schema: null,
|
|
7970
8163
|
get currentUserId() {
|
|
@@ -8000,16 +8193,16 @@ function dexieCloud(dexie) {
|
|
|
8000
8193
|
}
|
|
8001
8194
|
updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
|
|
8002
8195
|
},
|
|
8003
|
-
logout(
|
|
8004
|
-
return __awaiter(this,
|
|
8196
|
+
logout() {
|
|
8197
|
+
return __awaiter(this, arguments, void 0, function* ({ force } = {}) {
|
|
8005
8198
|
force
|
|
8006
8199
|
? yield _logout(DexieCloudDB(dexie), { deleteUnsyncedData: true })
|
|
8007
8200
|
: yield logout(DexieCloudDB(dexie));
|
|
8008
8201
|
});
|
|
8009
8202
|
},
|
|
8010
|
-
sync(
|
|
8011
|
-
|
|
8012
|
-
|
|
8203
|
+
sync() {
|
|
8204
|
+
return __awaiter(this, arguments, void 0, function* ({ wait, purpose } = { wait: true, purpose: 'push' }) {
|
|
8205
|
+
var _a;
|
|
8013
8206
|
if (wait === undefined)
|
|
8014
8207
|
wait = true;
|
|
8015
8208
|
const db = DexieCloudDB(dexie);
|
|
@@ -8067,8 +8260,8 @@ function dexieCloud(dexie) {
|
|
|
8067
8260
|
dexie.use(createImplicitPropSetterMiddleware(DexieCloudDB(dexie)));
|
|
8068
8261
|
dexie.use(createIdGenerationMiddleware(DexieCloudDB(dexie)));
|
|
8069
8262
|
function onDbReady(dexie) {
|
|
8070
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
8071
8263
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8264
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
8072
8265
|
closed = false; // As Dexie calls us, we are not closed anymore. Maybe reopened? Remember db.ready event is registered with sticky flag!
|
|
8073
8266
|
const db = DexieCloudDB(dexie);
|
|
8074
8267
|
// Setup default GUI:
|
|
@@ -8091,7 +8284,7 @@ function dexieCloud(dexie) {
|
|
|
8091
8284
|
? yield navigator.serviceWorker.getRegistrations()
|
|
8092
8285
|
: [];
|
|
8093
8286
|
const [initiallySynced, lastSyncedRealms] = yield db.transaction('rw', db.$syncState, () => __awaiter(this, void 0, void 0, function* () {
|
|
8094
|
-
var
|
|
8287
|
+
var _a, _b;
|
|
8095
8288
|
const { options, schema } = db.cloud;
|
|
8096
8289
|
const [persistedOptions, persistedSchema, persistedSyncState] = yield Promise.all([
|
|
8097
8290
|
db.getOptions(),
|
|
@@ -8113,7 +8306,7 @@ function dexieCloud(dexie) {
|
|
|
8113
8306
|
delete newPersistedOptions.awarenessProtocol;
|
|
8114
8307
|
yield db.$syncState.put(newPersistedOptions, 'options');
|
|
8115
8308
|
}
|
|
8116
|
-
if (((
|
|
8309
|
+
if (((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.tryUseServiceWorker) &&
|
|
8117
8310
|
'serviceWorker' in navigator &&
|
|
8118
8311
|
swRegistrations.length > 0 &&
|
|
8119
8312
|
!DISABLE_SERVICEWORKER_STRATEGY) {
|
|
@@ -8127,7 +8320,7 @@ function dexieCloud(dexie) {
|
|
|
8127
8320
|
// Not configured for using service worker or no service worker
|
|
8128
8321
|
// registration exists. Don't rely on service worker to do any job.
|
|
8129
8322
|
// Use LocalSyncWorker instead.
|
|
8130
|
-
if (((
|
|
8323
|
+
if (((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.tryUseServiceWorker) &&
|
|
8131
8324
|
!db.cloud.isServiceWorkerDB) {
|
|
8132
8325
|
console.debug('dexie-cloud-addon: Not using service worker.', swRegistrations.length === 0
|
|
8133
8326
|
? 'No SW registrations found.'
|
|
@@ -8170,10 +8363,6 @@ function dexieCloud(dexie) {
|
|
|
8170
8363
|
db.setInitiallySynced(true);
|
|
8171
8364
|
}
|
|
8172
8365
|
verifySchema(db);
|
|
8173
|
-
if (((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.databaseUrl) && !initiallySynced) {
|
|
8174
|
-
yield performInitialSync(db, db.cloud.options, db.cloud.schema);
|
|
8175
|
-
db.setInitiallySynced(true);
|
|
8176
|
-
}
|
|
8177
8366
|
// Manage CurrentUser observable:
|
|
8178
8367
|
throwIfClosed();
|
|
8179
8368
|
if (!db.cloud.isServiceWorkerDB) {
|
|
@@ -8198,20 +8387,29 @@ function dexieCloud(dexie) {
|
|
|
8198
8387
|
// HERE: If requireAuth, do athentication now.
|
|
8199
8388
|
let changedUser = false;
|
|
8200
8389
|
const user = yield db.getCurrentUser();
|
|
8201
|
-
const requireAuth = (
|
|
8390
|
+
const requireAuth = (_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.requireAuth;
|
|
8202
8391
|
if (requireAuth) {
|
|
8203
|
-
if (
|
|
8204
|
-
//
|
|
8205
|
-
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8209
|
-
changedUser = yield login(db, requireAuth);
|
|
8210
|
-
}
|
|
8392
|
+
if (db.cloud.isServiceWorkerDB) {
|
|
8393
|
+
// If this is a service worker DB, we can't do authentication here,
|
|
8394
|
+
// we just wait until the application has done it.
|
|
8395
|
+
console.debug('Dexie Cloud Service worker. Waiting for application to authenticate.');
|
|
8396
|
+
yield firstValueFrom(currentUserEmitter.pipe(filter((user) => !!user.isLoggedIn), take(1)));
|
|
8397
|
+
console.debug('Dexie Cloud Service worker. Application has authenticated.');
|
|
8211
8398
|
}
|
|
8212
|
-
else
|
|
8213
|
-
|
|
8214
|
-
|
|
8399
|
+
else {
|
|
8400
|
+
if (typeof requireAuth === 'object') {
|
|
8401
|
+
// requireAuth contains login hints. Check if we already fulfil it:
|
|
8402
|
+
if (!user.isLoggedIn ||
|
|
8403
|
+
(requireAuth.userId && user.userId !== requireAuth.userId) ||
|
|
8404
|
+
(requireAuth.email && user.email !== requireAuth.email)) {
|
|
8405
|
+
// If not, login the configured user:
|
|
8406
|
+
changedUser = yield login(db, requireAuth);
|
|
8407
|
+
}
|
|
8408
|
+
}
|
|
8409
|
+
else if (!user.isLoggedIn) {
|
|
8410
|
+
// requireAuth is true and user is not logged in
|
|
8411
|
+
changedUser = yield login(db);
|
|
8412
|
+
}
|
|
8215
8413
|
}
|
|
8216
8414
|
}
|
|
8217
8415
|
if (user.isLoggedIn && (!lastSyncedRealms || !lastSyncedRealms.includes(user.userId))) {
|
|
@@ -8226,8 +8424,17 @@ function dexieCloud(dexie) {
|
|
|
8226
8424
|
localSyncWorker.stop();
|
|
8227
8425
|
localSyncWorker = null;
|
|
8228
8426
|
throwIfClosed();
|
|
8427
|
+
const doInitialSync = ((_c = db.cloud.options) === null || _c === void 0 ? void 0 : _c.databaseUrl) && (!initiallySynced || changedUser);
|
|
8428
|
+
if (doInitialSync) {
|
|
8429
|
+
// Do the initial sync directly in the browser thread no matter if we are using service worker or not.
|
|
8430
|
+
yield performInitialSync(db, db.cloud.options, db.cloud.schema);
|
|
8431
|
+
db.setInitiallySynced(true);
|
|
8432
|
+
}
|
|
8433
|
+
throwIfClosed();
|
|
8229
8434
|
if (db.cloud.usingServiceWorker && ((_d = db.cloud.options) === null || _d === void 0 ? void 0 : _d.databaseUrl)) {
|
|
8230
|
-
|
|
8435
|
+
if (!doInitialSync) {
|
|
8436
|
+
registerSyncEvent(db, 'push').catch(() => { });
|
|
8437
|
+
}
|
|
8231
8438
|
registerPeriodicSyncEvent(db).catch(() => { });
|
|
8232
8439
|
}
|
|
8233
8440
|
else if (((_e = db.cloud.options) === null || _e === void 0 ? void 0 : _e.databaseUrl) &&
|
|
@@ -8236,7 +8443,9 @@ function dexieCloud(dexie) {
|
|
|
8236
8443
|
// There's no SW. Start SyncWorker instead.
|
|
8237
8444
|
localSyncWorker = LocalSyncWorker(db, db.cloud.options, db.cloud.schema);
|
|
8238
8445
|
localSyncWorker.start();
|
|
8239
|
-
|
|
8446
|
+
if (!doInitialSync) {
|
|
8447
|
+
triggerSync(db, 'push');
|
|
8448
|
+
}
|
|
8240
8449
|
}
|
|
8241
8450
|
// Listen to online event and do sync.
|
|
8242
8451
|
throwIfClosed();
|
|
@@ -8256,7 +8465,7 @@ function dexieCloud(dexie) {
|
|
|
8256
8465
|
});
|
|
8257
8466
|
}));
|
|
8258
8467
|
}
|
|
8259
|
-
// Connect WebSocket unless we
|
|
8468
|
+
// Connect WebSocket unless we are in a service worker or websocket is disabled.
|
|
8260
8469
|
if (((_f = db.cloud.options) === null || _f === void 0 ? void 0 : _f.databaseUrl) &&
|
|
8261
8470
|
!((_g = db.cloud.options) === null || _g === void 0 ? void 0 : _g.disableWebSocket) &&
|
|
8262
8471
|
!IS_SERVICE_WORKER) {
|
|
@@ -8266,8 +8475,164 @@ function dexieCloud(dexie) {
|
|
|
8266
8475
|
}
|
|
8267
8476
|
}
|
|
8268
8477
|
// @ts-ignore
|
|
8269
|
-
dexieCloud.version = "4.1.0-
|
|
8478
|
+
dexieCloud.version = "4.1.0-beta.25";
|
|
8270
8479
|
Dexie.Cloud = dexieCloud;
|
|
8271
8480
|
|
|
8272
|
-
|
|
8481
|
+
const ydocTriggers = {};
|
|
8482
|
+
const docIsAlreadyHooked = new WeakSet();
|
|
8483
|
+
const middlewares = new WeakMap();
|
|
8484
|
+
const createMiddleware = (db) => ({
|
|
8485
|
+
stack: 'dbcore',
|
|
8486
|
+
level: 10,
|
|
8487
|
+
name: 'yTriggerMiddleware',
|
|
8488
|
+
create: (down) => {
|
|
8489
|
+
return Object.assign(Object.assign({}, down), { transaction: (stores, mode, options) => {
|
|
8490
|
+
const idbtrans = down.transaction(stores, mode, options);
|
|
8491
|
+
idbtrans.addEventListener('complete', onTransactionCommitted);
|
|
8492
|
+
return idbtrans;
|
|
8493
|
+
}, table: (tblName) => {
|
|
8494
|
+
const coreTable = down.table(tblName);
|
|
8495
|
+
const triggerSpec = ydocTriggers[tblName];
|
|
8496
|
+
if (!triggerSpec)
|
|
8497
|
+
return coreTable;
|
|
8498
|
+
const { trigger, parentTable, prop } = triggerSpec;
|
|
8499
|
+
return Object.assign(Object.assign({}, coreTable), { mutate(req) {
|
|
8500
|
+
switch (req.type) {
|
|
8501
|
+
case 'add': {
|
|
8502
|
+
for (const yUpdateRow of req.values) {
|
|
8503
|
+
if (yUpdateRow.k == undefined)
|
|
8504
|
+
continue; // A syncer or garbage collection state does not point to a key
|
|
8505
|
+
const primaryKey = yUpdateRow.k;
|
|
8506
|
+
const doc = DexieYProvider.getDocCache(db).find(parentTable, primaryKey, prop);
|
|
8507
|
+
if (doc) {
|
|
8508
|
+
if (!docIsAlreadyHooked.has(doc)) {
|
|
8509
|
+
hookToDoc(doc, primaryKey, trigger);
|
|
8510
|
+
docIsAlreadyHooked.add(doc);
|
|
8511
|
+
}
|
|
8512
|
+
}
|
|
8513
|
+
else {
|
|
8514
|
+
enqueueTrigger(db, tblName, primaryKey, trigger);
|
|
8515
|
+
}
|
|
8516
|
+
}
|
|
8517
|
+
break;
|
|
8518
|
+
}
|
|
8519
|
+
case 'delete':
|
|
8520
|
+
// @ts-ignore
|
|
8521
|
+
if (req.trans._rejecting_y_ypdate) {
|
|
8522
|
+
// The deletion came from a rejection, not garbage collection.
|
|
8523
|
+
// When that happens, let the triggers run to compute new values
|
|
8524
|
+
// based on the deleted updates.
|
|
8525
|
+
coreTable
|
|
8526
|
+
.getMany({
|
|
8527
|
+
keys: req.keys,
|
|
8528
|
+
trans: req.trans,
|
|
8529
|
+
cache: 'immutable',
|
|
8530
|
+
})
|
|
8531
|
+
.then((updates) => {
|
|
8532
|
+
const keySet = new RangeSet();
|
|
8533
|
+
for (const { k } of updates) {
|
|
8534
|
+
if (k != undefined)
|
|
8535
|
+
keySet.addKey(k);
|
|
8536
|
+
}
|
|
8537
|
+
for (const interval of keySet) {
|
|
8538
|
+
enqueueTrigger(db, tblName, interval.from, trigger);
|
|
8539
|
+
}
|
|
8540
|
+
});
|
|
8541
|
+
}
|
|
8542
|
+
break;
|
|
8543
|
+
}
|
|
8544
|
+
return coreTable.mutate(req);
|
|
8545
|
+
} });
|
|
8546
|
+
} });
|
|
8547
|
+
},
|
|
8548
|
+
});
|
|
8549
|
+
let triggerExecPromise = null;
|
|
8550
|
+
let triggerScheduled = false;
|
|
8551
|
+
let scheduledTriggers = [];
|
|
8552
|
+
function $Y(db) {
|
|
8553
|
+
const $Y = db._options.Y;
|
|
8554
|
+
if (!$Y)
|
|
8555
|
+
throw new Error('Y library not supplied to Dexie constructor');
|
|
8556
|
+
return $Y;
|
|
8557
|
+
}
|
|
8558
|
+
function executeTriggers(triggersToRun) {
|
|
8559
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8560
|
+
for (const { db, parentId, trigger, updatesTable } of triggersToRun) {
|
|
8561
|
+
// Load entire document into an Y.Doc instance:
|
|
8562
|
+
const updates = yield db
|
|
8563
|
+
.table(updatesTable)
|
|
8564
|
+
.where({ k: parentId })
|
|
8565
|
+
.toArray();
|
|
8566
|
+
const Y = $Y(db);
|
|
8567
|
+
const yDoc = new Y.Doc();
|
|
8568
|
+
for (const update of updates) {
|
|
8569
|
+
Y.applyUpdateV2(yDoc, update.u);
|
|
8570
|
+
}
|
|
8571
|
+
try {
|
|
8572
|
+
yield trigger(yDoc, parentId);
|
|
8573
|
+
}
|
|
8574
|
+
catch (error) {
|
|
8575
|
+
console.error(`Error in YDocTrigger ${error}`);
|
|
8576
|
+
}
|
|
8577
|
+
}
|
|
8578
|
+
});
|
|
8579
|
+
}
|
|
8580
|
+
function enqueueTrigger(db, updatesTable, parentId, trigger) {
|
|
8581
|
+
scheduledTriggers.push({
|
|
8582
|
+
db,
|
|
8583
|
+
updatesTable,
|
|
8584
|
+
parentId,
|
|
8585
|
+
trigger,
|
|
8586
|
+
});
|
|
8587
|
+
}
|
|
8588
|
+
function onTransactionCommitted() {
|
|
8589
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8590
|
+
if (!triggerScheduled && scheduledTriggers.length > 0) {
|
|
8591
|
+
triggerScheduled = true;
|
|
8592
|
+
if (triggerExecPromise)
|
|
8593
|
+
yield triggerExecPromise.catch(() => { });
|
|
8594
|
+
setTimeout(() => {
|
|
8595
|
+
// setTimeout() is to escape from Promise.PSD zones and never run within liveQueries or transaction scopes
|
|
8596
|
+
triggerScheduled = false;
|
|
8597
|
+
const triggersToRun = scheduledTriggers;
|
|
8598
|
+
scheduledTriggers = [];
|
|
8599
|
+
triggerExecPromise = executeTriggers(triggersToRun).finally(() => (triggerExecPromise = null));
|
|
8600
|
+
}, 0);
|
|
8601
|
+
}
|
|
8602
|
+
});
|
|
8603
|
+
}
|
|
8604
|
+
function hookToDoc(doc, parentId, trigger) {
|
|
8605
|
+
// From now on, keep listening to doc updates and execute the trigger when it happens there instead
|
|
8606
|
+
doc.on('updateV2', (update, origin) => {
|
|
8607
|
+
//Dexie.ignoreTransaction(()=>{
|
|
8608
|
+
trigger(doc, parentId);
|
|
8609
|
+
//});
|
|
8610
|
+
});
|
|
8611
|
+
/*
|
|
8612
|
+
NOT NEEDED because DexieYProvider's docCache will also listen to destroy and remove it from its cache:
|
|
8613
|
+
doc.on('destroy', ()=>{
|
|
8614
|
+
docIsAlreadyHooked.delete(doc);
|
|
8615
|
+
})
|
|
8616
|
+
*/
|
|
8617
|
+
}
|
|
8618
|
+
function defineYDocTrigger(table, prop, trigger) {
|
|
8619
|
+
var _a, _b;
|
|
8620
|
+
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;
|
|
8621
|
+
if (!updatesTable)
|
|
8622
|
+
throw new Error(`Table ${table.name} does not have a Yjs property named ${prop}`);
|
|
8623
|
+
ydocTriggers[updatesTable] = {
|
|
8624
|
+
trigger,
|
|
8625
|
+
parentTable: table.name,
|
|
8626
|
+
prop,
|
|
8627
|
+
};
|
|
8628
|
+
const db = table.db._novip;
|
|
8629
|
+
let mw = middlewares.get(db);
|
|
8630
|
+
if (!mw) {
|
|
8631
|
+
mw = createMiddleware(db);
|
|
8632
|
+
middlewares.set(db, mw);
|
|
8633
|
+
}
|
|
8634
|
+
db.use(mw);
|
|
8635
|
+
}
|
|
8636
|
+
|
|
8637
|
+
export { dexieCloud as default, defineYDocTrigger, dexieCloud, getTiedObjectId, getTiedRealmId, resolveText };
|
|
8273
8638
|
//# sourceMappingURL=dexie-cloud-addon.js.map
|