dexie-cloud-addon 4.1.0-alpha.2 → 4.1.0-alpha.20
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 +548 -231
- 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 +390 -229
- 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/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 +546 -228
- 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 +388 -227
- 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/listYClientMessagesAndStateVector.d.ts +3 -1
- package/package.json +3 -3
- 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.20, Wed Oct 23 2024
|
|
12
12
|
*
|
|
13
13
|
* https://dexie.org
|
|
14
14
|
*
|
|
@@ -2990,8 +2990,8 @@
|
|
|
2990
2990
|
});
|
|
2991
2991
|
}
|
|
2992
2992
|
function registerPeriodicSyncEvent(db) {
|
|
2993
|
-
var _a;
|
|
2994
2993
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2994
|
+
var _a;
|
|
2995
2995
|
try {
|
|
2996
2996
|
// Register periodicSync event to SW:
|
|
2997
2997
|
// @ts-ignore
|
|
@@ -3202,8 +3202,8 @@
|
|
|
3202
3202
|
}
|
|
3203
3203
|
|
|
3204
3204
|
function loadAccessToken(db) {
|
|
3205
|
-
var _a, _b, _c;
|
|
3206
3205
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3206
|
+
var _a, _b, _c;
|
|
3207
3207
|
const currentUser = yield db.getCurrentUser();
|
|
3208
3208
|
const { accessToken, accessTokenExpiration, refreshToken, refreshTokenExpiration, claims, } = currentUser;
|
|
3209
3209
|
if (!accessToken)
|
|
@@ -3475,8 +3475,8 @@
|
|
|
3475
3475
|
}
|
|
3476
3476
|
});
|
|
3477
3477
|
}
|
|
3478
|
-
function _logout(
|
|
3479
|
-
return __awaiter(this,
|
|
3478
|
+
function _logout(db_1) {
|
|
3479
|
+
return __awaiter(this, arguments, void 0, function* (db, { deleteUnsyncedData = false } = {}) {
|
|
3480
3480
|
// Clear the database without emptying configuration options.
|
|
3481
3481
|
const [numUnsynced, loggedOut] = yield db.dx.transaction('rw', db.dx.tables, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
3482
3482
|
// @ts-ignore
|
|
@@ -3524,11 +3524,11 @@
|
|
|
3524
3524
|
|
|
3525
3525
|
function otpFetchTokenCallback(db) {
|
|
3526
3526
|
const { userInteraction } = db.cloud;
|
|
3527
|
-
return function otpAuthenticate(
|
|
3528
|
-
|
|
3529
|
-
|
|
3527
|
+
return function otpAuthenticate(_a) {
|
|
3528
|
+
return __awaiter(this, arguments, void 0, function* ({ public_key, hints }) {
|
|
3529
|
+
var _b;
|
|
3530
3530
|
let tokenRequest;
|
|
3531
|
-
const url = (
|
|
3531
|
+
const url = (_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.databaseUrl;
|
|
3532
3532
|
if (!url)
|
|
3533
3533
|
throw new Error(`No database URL given.`);
|
|
3534
3534
|
if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'demo') {
|
|
@@ -3694,8 +3694,8 @@
|
|
|
3694
3694
|
}
|
|
3695
3695
|
|
|
3696
3696
|
function login(db, hints) {
|
|
3697
|
-
var _a;
|
|
3698
3697
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3698
|
+
var _a;
|
|
3699
3699
|
const currentUser = yield db.getCurrentUser();
|
|
3700
3700
|
const origUserId = currentUser.userId;
|
|
3701
3701
|
if (currentUser.isLoggedIn && (!hints || (!hints.email && !hints.userId))) {
|
|
@@ -3883,8 +3883,8 @@
|
|
|
3883
3883
|
}
|
|
3884
3884
|
}
|
|
3885
3885
|
|
|
3886
|
-
function computeRealmSetHash(
|
|
3887
|
-
return __awaiter(this,
|
|
3886
|
+
function computeRealmSetHash(_a) {
|
|
3887
|
+
return __awaiter(this, arguments, void 0, function* ({ realms, inviteRealms, }) {
|
|
3888
3888
|
const data = JSON.stringify([
|
|
3889
3889
|
...realms.map((realmId) => ({ realmId, accepted: true })),
|
|
3890
3890
|
...inviteRealms.map((realmId) => ({ realmId, accepted: false })),
|
|
@@ -3920,8 +3920,8 @@
|
|
|
3920
3920
|
return concat.apply([], a);
|
|
3921
3921
|
}
|
|
3922
3922
|
|
|
3923
|
-
function listClientChanges(
|
|
3924
|
-
return __awaiter(this,
|
|
3923
|
+
function listClientChanges(mutationTables_1, db_1) {
|
|
3924
|
+
return __awaiter(this, arguments, void 0, function* (mutationTables, db, { since = {}, limit = Infinity } = {}) {
|
|
3925
3925
|
const allMutsOnTables = yield Promise.all(mutationTables.map((mutationTable) => __awaiter(this, void 0, void 0, function* () {
|
|
3926
3926
|
const tableName = getTableFromMutationTable(mutationTable.name);
|
|
3927
3927
|
const lastRevision = since[tableName];
|
|
@@ -4656,8 +4656,8 @@
|
|
|
4656
4656
|
// seconds (given that there is a Ratelimit-Reset header).
|
|
4657
4657
|
let syncRatelimitDelays = new WeakMap();
|
|
4658
4658
|
function checkSyncRateLimitDelay(db) {
|
|
4659
|
-
var _a, _b;
|
|
4660
4659
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4660
|
+
var _a, _b;
|
|
4661
4661
|
const delatMilliseconds = ((_b = (_a = syncRatelimitDelays.get(db)) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) - Date.now();
|
|
4662
4662
|
if (delatMilliseconds > 0) {
|
|
4663
4663
|
console.debug(`Stalling sync request ${delatMilliseconds} ms to spare ratelimits`);
|
|
@@ -4947,7 +4947,7 @@
|
|
|
4947
4947
|
.toArray();
|
|
4948
4948
|
}
|
|
4949
4949
|
|
|
4950
|
-
function $Y(db) {
|
|
4950
|
+
function $Y$1(db) {
|
|
4951
4951
|
const $Y = db.dx._options.Y;
|
|
4952
4952
|
if (!$Y)
|
|
4953
4953
|
throw new Error('Y library not supplied to Dexie constructor');
|
|
@@ -4970,15 +4970,14 @@
|
|
|
4970
4970
|
* @param db
|
|
4971
4971
|
* @returns
|
|
4972
4972
|
*/
|
|
4973
|
-
function listYClientMessagesAndStateVector(db) {
|
|
4974
|
-
var _a;
|
|
4973
|
+
function listYClientMessagesAndStateVector(db, tablesToSync) {
|
|
4975
4974
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4976
4975
|
const result = [];
|
|
4977
4976
|
const lastUpdateIds = {};
|
|
4978
|
-
for (const table of
|
|
4979
|
-
if (table.schema.yProps
|
|
4977
|
+
for (const table of tablesToSync) {
|
|
4978
|
+
if (table.schema.yProps) {
|
|
4980
4979
|
for (const yProp of table.schema.yProps) {
|
|
4981
|
-
const Y = $Y(db); // This is how we retrieve the user-provided Y library
|
|
4980
|
+
const Y = $Y$1(db); // This is how we retrieve the user-provided Y library
|
|
4982
4981
|
const yTable = db.table(yProp.updatesTable); // the updates-table for this combo of table+propName
|
|
4983
4982
|
const syncState = (yield yTable.get(DEXIE_CLOUD_SYNCER_ID));
|
|
4984
4983
|
// unsentFrom = the `i` value of updates that aren't yet sent to server (or at least not acked by the server yet)
|
|
@@ -5063,6 +5062,7 @@
|
|
|
5063
5062
|
|
|
5064
5063
|
function applyYServerMessages(yMessages, db) {
|
|
5065
5064
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5065
|
+
var _a;
|
|
5066
5066
|
const result = {};
|
|
5067
5067
|
for (const m of yMessages) {
|
|
5068
5068
|
switch (m.type) {
|
|
@@ -5093,7 +5093,24 @@
|
|
|
5093
5093
|
// See my question in https://discuss.yjs.dev/t/generate-an-inverse-update/2765
|
|
5094
5094
|
console.debug(`Y update rejected. Deleting it.`);
|
|
5095
5095
|
const utbl = getUpdatesTable(db, m.table, m.prop);
|
|
5096
|
-
|
|
5096
|
+
// Delete the rejected update and all local updates since (avoid holes in the CRDT)
|
|
5097
|
+
// and destroy it's open document if there is one.
|
|
5098
|
+
const primaryKey = (_a = (yield utbl.get(m.i))) === null || _a === void 0 ? void 0 : _a.k;
|
|
5099
|
+
if (primaryKey != null) {
|
|
5100
|
+
yield db.transaction('rw', utbl, tx => {
|
|
5101
|
+
// @ts-ignore
|
|
5102
|
+
tx.idbtrans._rejecting_y_ypdate = true; // Inform ydoc triggers that we delete because of a rejection and not GC
|
|
5103
|
+
return utbl
|
|
5104
|
+
.where('i')
|
|
5105
|
+
.aboveOrEqual(m.i)
|
|
5106
|
+
.filter(u => Dexie.cmp(u.k, primaryKey) === 0 && ((u.f || 0) & 1) === 1)
|
|
5107
|
+
.delete();
|
|
5108
|
+
});
|
|
5109
|
+
// Destroy active doc
|
|
5110
|
+
const activeDoc = Dexie.DexieYProvider.getDocCache(db.dx).find(m.table, primaryKey, m.prop);
|
|
5111
|
+
if (activeDoc)
|
|
5112
|
+
activeDoc.destroy(); // Destroy the document so that editors don't continue to work on it
|
|
5113
|
+
}
|
|
5097
5114
|
break;
|
|
5098
5115
|
}
|
|
5099
5116
|
case 'in-sync': {
|
|
@@ -5110,15 +5127,15 @@
|
|
|
5110
5127
|
}
|
|
5111
5128
|
|
|
5112
5129
|
function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db, serverRevision) {
|
|
5113
|
-
var _a, _b;
|
|
5114
5130
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5131
|
+
var _a, _b, _c, _d, _e;
|
|
5115
5132
|
// We want to update unsentFrom for each yTable to the value specified in first argument
|
|
5116
5133
|
// because we got those values before we synced with server and here we are back from server
|
|
5117
5134
|
// that has successfully received all those messages - no matter if the last update was a client or server update,
|
|
5118
5135
|
// we can safely store unsentFrom to a value of the last update + 1 here.
|
|
5119
5136
|
// We also want to update receivedUntil for each yTable to the value specified in the second argument,
|
|
5120
5137
|
// because that contains the highest resulted id of each update from server after storing it.
|
|
5121
|
-
// We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
|
|
5138
|
+
// We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
|
|
5122
5139
|
// to optimize the dexie calls, we merge these two maps into a single one so we can do a single update request
|
|
5123
5140
|
// per yTable.
|
|
5124
5141
|
const mergedSpec = {};
|
|
@@ -5130,28 +5147,42 @@
|
|
|
5130
5147
|
(_b = mergedSpec[yTable]) !== null && _b !== void 0 ? _b : (mergedSpec[yTable] = {});
|
|
5131
5148
|
mergedSpec[yTable].receivedUntil = lastUpdateId;
|
|
5132
5149
|
}
|
|
5133
|
-
// Now go through
|
|
5134
|
-
|
|
5150
|
+
// Now go through all yTables and update their YSyncStates:
|
|
5151
|
+
const allYTables = Object.values(db.dx._dbSchema)
|
|
5152
|
+
.filter((tblSchema) => tblSchema.yProps)
|
|
5153
|
+
.map((tblSchema) => tblSchema.yProps.map((yProp) => yProp.updatesTable))
|
|
5154
|
+
.flat();
|
|
5155
|
+
for (const yTable of allYTables) {
|
|
5156
|
+
const mergedEntry = mergedSpec[yTable];
|
|
5157
|
+
const unsentFrom = (_c = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.unsentFrom) !== null && _c !== void 0 ? _c : 1;
|
|
5158
|
+
const receivedUntil = (_e = (_d = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.receivedUntil) !== null && _d !== void 0 ? _d :
|
|
5159
|
+
// from local because we are in the same parent transaction (in sync.ts) that
|
|
5160
|
+
// applied all updates from the server
|
|
5161
|
+
(yield db
|
|
5162
|
+
.table(yTable)
|
|
5163
|
+
.where('i')
|
|
5164
|
+
.between(1, Infinity) // Because i might be string DEXIE_CLOUD_SYNCER_ID if not a number.
|
|
5165
|
+
.reverse()
|
|
5166
|
+
.limit(1)
|
|
5167
|
+
.primaryKeys())[0]) !== null && _e !== void 0 ? _e : 0;
|
|
5135
5168
|
// We're already in a transaction, but for the sake of
|
|
5136
5169
|
// code readability and correctness, let's launch an atomic sub transaction:
|
|
5137
5170
|
yield db.transaction('rw', yTable, () => __awaiter(this, void 0, void 0, function* () {
|
|
5138
|
-
const state = yield db
|
|
5171
|
+
const state = yield db
|
|
5172
|
+
.table(yTable)
|
|
5173
|
+
.get(DEXIE_CLOUD_SYNCER_ID);
|
|
5139
5174
|
if (!state) {
|
|
5140
5175
|
yield db.table(yTable).add({
|
|
5141
5176
|
i: DEXIE_CLOUD_SYNCER_ID,
|
|
5142
|
-
unsentFrom
|
|
5143
|
-
receivedUntil
|
|
5177
|
+
unsentFrom,
|
|
5178
|
+
receivedUntil,
|
|
5144
5179
|
serverRev: serverRevision,
|
|
5145
5180
|
});
|
|
5146
5181
|
}
|
|
5147
5182
|
else {
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
if (receivedUntil) {
|
|
5152
|
-
state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
|
|
5153
|
-
state.serverRev = serverRevision;
|
|
5154
|
-
}
|
|
5183
|
+
state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
|
|
5184
|
+
state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
|
|
5185
|
+
state.serverRev = serverRevision;
|
|
5155
5186
|
yield db.table(yTable).put(state);
|
|
5156
5187
|
}
|
|
5157
5188
|
}));
|
|
@@ -5162,9 +5193,11 @@
|
|
|
5162
5193
|
const BINSTREAM_TYPE_REALMID = 1;
|
|
5163
5194
|
const BINSTREAM_TYPE_TABLE_AND_PROP = 2;
|
|
5164
5195
|
const BINSTREAM_TYPE_DOCUMENT = 3;
|
|
5165
|
-
function downloadYDocsFromServer(
|
|
5166
|
-
return __awaiter(this,
|
|
5167
|
-
if (yDownloadedRealms &&
|
|
5196
|
+
function downloadYDocsFromServer(db_1, databaseUrl_1, _a) {
|
|
5197
|
+
return __awaiter(this, arguments, void 0, function* (db, databaseUrl, { yDownloadedRealms, realms }) {
|
|
5198
|
+
if (yDownloadedRealms &&
|
|
5199
|
+
realms &&
|
|
5200
|
+
realms.every((realmId) => yDownloadedRealms[realmId] === '*')) {
|
|
5168
5201
|
return; // Already done!
|
|
5169
5202
|
}
|
|
5170
5203
|
console.debug('Downloading Y.Docs from added realms');
|
|
@@ -5204,16 +5237,19 @@
|
|
|
5204
5237
|
yield yTable.bulkAdd(docsToInsert);
|
|
5205
5238
|
docsToInsert = [];
|
|
5206
5239
|
}
|
|
5207
|
-
if (currentRealmId &&
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5240
|
+
if (currentRealmId &&
|
|
5241
|
+
((currentTable && currentProp && lastDoc) || completedRealm)) {
|
|
5242
|
+
yield db.$syncState.update('syncState', (syncState) => {
|
|
5243
|
+
const yDownloadedRealms = syncState.yDownloadedRealms || {};
|
|
5244
|
+
yDownloadedRealms[currentRealmId] = completedRealm
|
|
5245
|
+
? '*'
|
|
5246
|
+
: {
|
|
5212
5247
|
tbl: currentTable,
|
|
5213
5248
|
prop: currentProp,
|
|
5214
5249
|
key: lastDoc.k,
|
|
5215
|
-
}
|
|
5216
|
-
|
|
5250
|
+
};
|
|
5251
|
+
syncState.yDownloadedRealms = yDownloadedRealms;
|
|
5252
|
+
});
|
|
5217
5253
|
}
|
|
5218
5254
|
});
|
|
5219
5255
|
}
|
|
@@ -5316,11 +5352,11 @@
|
|
|
5316
5352
|
return Promise.reject(error);
|
|
5317
5353
|
}));
|
|
5318
5354
|
}
|
|
5319
|
-
function _sync(
|
|
5320
|
-
isInitialSync
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5355
|
+
function _sync(db_1, options_1, schema_1) {
|
|
5356
|
+
return __awaiter(this, arguments, void 0, function* (db, options, schema, { isInitialSync, cancelToken, justCheckIfNeeded, purpose } = {
|
|
5357
|
+
isInitialSync: false,
|
|
5358
|
+
}) {
|
|
5359
|
+
var _a;
|
|
5324
5360
|
if (!justCheckIfNeeded) {
|
|
5325
5361
|
console.debug('SYNC STARTED', { isInitialSync, purpose });
|
|
5326
5362
|
}
|
|
@@ -5362,8 +5398,8 @@
|
|
|
5362
5398
|
const [clientChangeSet, syncState, baseRevs, { yMessages, lastUpdateIds }] = yield db.transaction('r', db.tables, () => __awaiter(this, void 0, void 0, function* () {
|
|
5363
5399
|
const syncState = yield db.getPersistedSyncState();
|
|
5364
5400
|
const baseRevs = yield db.$baseRevs.toArray();
|
|
5365
|
-
let clientChanges = yield listClientChanges(mutationTables);
|
|
5366
|
-
const yResults = yield listYClientMessagesAndStateVector(db);
|
|
5401
|
+
let clientChanges = yield listClientChanges(mutationTables, db);
|
|
5402
|
+
const yResults = yield listYClientMessagesAndStateVector(db, tablesToSync);
|
|
5367
5403
|
throwIfCancelled(cancelToken);
|
|
5368
5404
|
if (doSyncify) {
|
|
5369
5405
|
const alreadySyncedRealms = [
|
|
@@ -5631,8 +5667,8 @@
|
|
|
5631
5667
|
event.next(null);
|
|
5632
5668
|
}
|
|
5633
5669
|
function consumeQueue() {
|
|
5634
|
-
var _a, _b, _c, _d, _e, _f;
|
|
5635
5670
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5671
|
+
var _a, _b, _c, _d, _e, _f;
|
|
5636
5672
|
while (queue.length > 0) {
|
|
5637
5673
|
const msg = queue.shift();
|
|
5638
5674
|
try {
|
|
@@ -6288,8 +6324,7 @@
|
|
|
6288
6324
|
outstandingTransactions.next(outstandingTransactions.value);
|
|
6289
6325
|
};
|
|
6290
6326
|
const txComplete = () => {
|
|
6291
|
-
if (tx.mutationsAdded &&
|
|
6292
|
-
!isEagerSyncDisabled(db)) {
|
|
6327
|
+
if (tx.mutationsAdded && !isEagerSyncDisabled(db)) {
|
|
6293
6328
|
triggerSync(db, 'push');
|
|
6294
6329
|
}
|
|
6295
6330
|
removeTransaction();
|
|
@@ -6371,26 +6406,107 @@
|
|
|
6371
6406
|
: mutateAndLog(req);
|
|
6372
6407
|
} }));
|
|
6373
6408
|
function mutateAndLog(req) {
|
|
6409
|
+
var _a, _b;
|
|
6374
6410
|
const trans = req.trans;
|
|
6375
|
-
|
|
6411
|
+
const unsyncedProps = (_b = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.unsyncedProperties) === null || _b === void 0 ? void 0 : _b[tableName];
|
|
6376
6412
|
const { txid, currentUser: { userId }, } = trans;
|
|
6377
6413
|
const { type } = req;
|
|
6378
6414
|
const opNo = ++trans.opCount;
|
|
6415
|
+
function stripChangeSpec(changeSpec) {
|
|
6416
|
+
if (!unsyncedProps)
|
|
6417
|
+
return changeSpec;
|
|
6418
|
+
let rv = changeSpec;
|
|
6419
|
+
for (const keyPath of Object.keys(changeSpec)) {
|
|
6420
|
+
if (unsyncedProps.some((p) => keyPath === p || keyPath.startsWith(p + '.'))) {
|
|
6421
|
+
if (rv === changeSpec)
|
|
6422
|
+
rv = Object.assign({}, changeSpec); // clone on demand
|
|
6423
|
+
delete rv[keyPath];
|
|
6424
|
+
}
|
|
6425
|
+
}
|
|
6426
|
+
return rv;
|
|
6427
|
+
}
|
|
6379
6428
|
return table.mutate(req).then((res) => {
|
|
6429
|
+
var _a;
|
|
6380
6430
|
const { numFailures: hasFailures, failures } = res;
|
|
6381
6431
|
let keys = type === 'delete' ? req.keys : res.results;
|
|
6382
6432
|
let values = 'values' in req ? req.values : [];
|
|
6383
|
-
let
|
|
6433
|
+
let changeSpec = 'changeSpec' in req ? req.changeSpec : undefined;
|
|
6434
|
+
let updates = 'updates' in req ? req.updates : undefined;
|
|
6384
6435
|
if (hasFailures) {
|
|
6385
6436
|
keys = keys.filter((_, idx) => !failures[idx]);
|
|
6386
6437
|
values = values.filter((_, idx) => !failures[idx]);
|
|
6387
6438
|
}
|
|
6439
|
+
if (unsyncedProps) {
|
|
6440
|
+
// Filter out unsynced properties
|
|
6441
|
+
values = values.map((value) => {
|
|
6442
|
+
const newValue = Object.assign({}, value);
|
|
6443
|
+
for (const prop of unsyncedProps) {
|
|
6444
|
+
delete newValue[prop];
|
|
6445
|
+
}
|
|
6446
|
+
return newValue;
|
|
6447
|
+
});
|
|
6448
|
+
if (changeSpec) {
|
|
6449
|
+
// modify operation with criteria and changeSpec.
|
|
6450
|
+
// We must strip out unsynced properties from changeSpec.
|
|
6451
|
+
// We deal with criteria later.
|
|
6452
|
+
changeSpec = stripChangeSpec(changeSpec);
|
|
6453
|
+
if (Object.keys(changeSpec).length === 0) {
|
|
6454
|
+
// Nothing to change on server
|
|
6455
|
+
return res;
|
|
6456
|
+
}
|
|
6457
|
+
}
|
|
6458
|
+
if (updates) {
|
|
6459
|
+
let strippedChangeSpecs = updates.changeSpecs.map(stripChangeSpec);
|
|
6460
|
+
let newUpdates = {
|
|
6461
|
+
keys: [],
|
|
6462
|
+
changeSpecs: [],
|
|
6463
|
+
};
|
|
6464
|
+
const validKeys = new Dexie.RangeSet();
|
|
6465
|
+
let anyChangeSpecBecameEmpty = false;
|
|
6466
|
+
for (let i = 0, l = strippedChangeSpecs.length; i < l; ++i) {
|
|
6467
|
+
if (Object.keys(strippedChangeSpecs[i]).length > 0) {
|
|
6468
|
+
newUpdates.keys.push(updates.keys[i]);
|
|
6469
|
+
newUpdates.changeSpecs.push(strippedChangeSpecs[i]);
|
|
6470
|
+
validKeys.addKey(updates.keys[i]);
|
|
6471
|
+
}
|
|
6472
|
+
else {
|
|
6473
|
+
anyChangeSpecBecameEmpty = true;
|
|
6474
|
+
}
|
|
6475
|
+
}
|
|
6476
|
+
updates = newUpdates;
|
|
6477
|
+
if (anyChangeSpecBecameEmpty) {
|
|
6478
|
+
// Some keys were stripped. We must also strip them from keys and values
|
|
6479
|
+
let newKeys = [];
|
|
6480
|
+
let newValues = [];
|
|
6481
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
6482
|
+
if (validKeys.hasKey(keys[i])) {
|
|
6483
|
+
newKeys.push(keys[i]);
|
|
6484
|
+
newValues.push(values[i]);
|
|
6485
|
+
}
|
|
6486
|
+
}
|
|
6487
|
+
keys = newKeys;
|
|
6488
|
+
values = newValues;
|
|
6489
|
+
}
|
|
6490
|
+
}
|
|
6491
|
+
}
|
|
6388
6492
|
const ts = Date.now();
|
|
6389
6493
|
// Canonicalize req.criteria.index to null if it's on the primary key.
|
|
6390
|
-
|
|
6494
|
+
let criteria = 'criteria' in req && req.criteria
|
|
6391
6495
|
? Object.assign(Object.assign({}, req.criteria), { index: req.criteria.index === schema.primaryKey.keyPath // Use null to inform server that criteria is on primary key
|
|
6392
6496
|
? null // This will disable the server from trying to log consistent operations where it shouldnt.
|
|
6393
6497
|
: req.criteria.index }) : undefined;
|
|
6498
|
+
if (unsyncedProps && (criteria === null || criteria === void 0 ? void 0 : criteria.index)) {
|
|
6499
|
+
const keyPaths = (_a = schema.indexes.find((idx) => idx.name === criteria.index)) === null || _a === void 0 ? void 0 : _a.keyPath;
|
|
6500
|
+
const involvedProps = keyPaths
|
|
6501
|
+
? typeof keyPaths === 'string'
|
|
6502
|
+
? [keyPaths]
|
|
6503
|
+
: keyPaths
|
|
6504
|
+
: [];
|
|
6505
|
+
if (involvedProps.some((p) => unsyncedProps === null || unsyncedProps === void 0 ? void 0 : unsyncedProps.includes(p))) {
|
|
6506
|
+
// Don't log criteria on unsynced properties as the server could not test them.
|
|
6507
|
+
criteria = undefined;
|
|
6508
|
+
}
|
|
6509
|
+
}
|
|
6394
6510
|
const mut = req.type === 'delete'
|
|
6395
6511
|
? {
|
|
6396
6512
|
type: 'delete',
|
|
@@ -6411,7 +6527,7 @@
|
|
|
6411
6527
|
userId,
|
|
6412
6528
|
values,
|
|
6413
6529
|
}
|
|
6414
|
-
: criteria &&
|
|
6530
|
+
: criteria && changeSpec
|
|
6415
6531
|
? {
|
|
6416
6532
|
// Common changeSpec for all keys
|
|
6417
6533
|
type: 'modify',
|
|
@@ -6419,37 +6535,51 @@
|
|
|
6419
6535
|
opNo,
|
|
6420
6536
|
keys,
|
|
6421
6537
|
criteria,
|
|
6422
|
-
changeSpec
|
|
6538
|
+
changeSpec,
|
|
6423
6539
|
txid,
|
|
6424
6540
|
userId,
|
|
6425
6541
|
}
|
|
6426
|
-
:
|
|
6542
|
+
: changeSpec
|
|
6427
6543
|
? {
|
|
6428
|
-
//
|
|
6544
|
+
// In case criteria involved an unsynced property, we go for keys instead.
|
|
6429
6545
|
type: 'update',
|
|
6430
6546
|
ts,
|
|
6431
6547
|
opNo,
|
|
6432
|
-
keys: updates.keys,
|
|
6433
|
-
changeSpecs: updates.changeSpecs,
|
|
6434
|
-
txid,
|
|
6435
|
-
userId,
|
|
6436
|
-
}
|
|
6437
|
-
: {
|
|
6438
|
-
type: 'upsert',
|
|
6439
|
-
ts,
|
|
6440
|
-
opNo,
|
|
6441
6548
|
keys,
|
|
6442
|
-
|
|
6549
|
+
changeSpecs: keys.map(() => changeSpec),
|
|
6443
6550
|
txid,
|
|
6444
6551
|
userId,
|
|
6445
|
-
}
|
|
6552
|
+
}
|
|
6553
|
+
: updates
|
|
6554
|
+
? {
|
|
6555
|
+
// One changeSpec per key
|
|
6556
|
+
type: 'update',
|
|
6557
|
+
ts,
|
|
6558
|
+
opNo,
|
|
6559
|
+
keys: updates.keys,
|
|
6560
|
+
changeSpecs: updates.changeSpecs,
|
|
6561
|
+
txid,
|
|
6562
|
+
userId,
|
|
6563
|
+
}
|
|
6564
|
+
: {
|
|
6565
|
+
type: 'upsert',
|
|
6566
|
+
ts,
|
|
6567
|
+
opNo,
|
|
6568
|
+
keys,
|
|
6569
|
+
values,
|
|
6570
|
+
txid,
|
|
6571
|
+
userId,
|
|
6572
|
+
};
|
|
6446
6573
|
if ('isAdditionalChunk' in req && req.isAdditionalChunk) {
|
|
6447
6574
|
mut.isAdditionalChunk = true;
|
|
6448
6575
|
}
|
|
6449
6576
|
return keys.length > 0 || criteria
|
|
6450
6577
|
? mutsTable
|
|
6451
6578
|
.mutate({ type: 'add', trans, values: [mut] }) // Log entry
|
|
6452
|
-
.then(() =>
|
|
6579
|
+
.then(() => {
|
|
6580
|
+
trans.mutationsAdded = true; // Mark transaction as having added mutations to trigger eager sync
|
|
6581
|
+
return res; // Return original response
|
|
6582
|
+
})
|
|
6453
6583
|
: res;
|
|
6454
6584
|
});
|
|
6455
6585
|
}
|
|
@@ -6616,38 +6746,51 @@
|
|
|
6616
6746
|
|
|
6617
6747
|
function createYClientUpdateObservable(db) {
|
|
6618
6748
|
const yTableRecords = flatten(db.tables
|
|
6619
|
-
.filter((table) => { var _a; return ((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[table.name].markedForSync) && table.schema.yProps; })
|
|
6749
|
+
.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; })
|
|
6620
6750
|
.map((table) => table.schema.yProps.map((p) => ({
|
|
6621
6751
|
table: table.name,
|
|
6622
6752
|
ydocProp: p.prop,
|
|
6623
6753
|
updatesTable: p.updatesTable,
|
|
6624
6754
|
}))));
|
|
6625
6755
|
return rxjs.merge(...yTableRecords.map(({ table, ydocProp, updatesTable }) => {
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6756
|
+
// Per updates table (table+prop combo), we first read syncer.unsentFrom,
|
|
6757
|
+
// and then start listening for updates since that number.
|
|
6758
|
+
const yTbl = db.table(updatesTable);
|
|
6759
|
+
return rxjs.from(yTbl.get(DEXIE_CLOUD_SYNCER_ID)).pipe(rxjs.switchMap((syncer) => {
|
|
6760
|
+
let currentUnsentFrom = (syncer === null || syncer === void 0 ? void 0 : syncer.unsentFrom) || 1;
|
|
6761
|
+
return rxjs.from(Dexie.liveQuery(() => __awaiter(this, void 0, void 0, function* () {
|
|
6762
|
+
const addedUpdates = yield listUpdatesSince(yTbl, currentUnsentFrom);
|
|
6763
|
+
return addedUpdates
|
|
6764
|
+
.filter((update) => update.f && update.f & 1) // Only include local updates
|
|
6765
|
+
.map((update) => {
|
|
6766
|
+
return {
|
|
6767
|
+
type: 'u-c',
|
|
6768
|
+
table,
|
|
6769
|
+
prop: ydocProp,
|
|
6770
|
+
k: update.k,
|
|
6771
|
+
u: update.u,
|
|
6772
|
+
i: update.i,
|
|
6773
|
+
};
|
|
6774
|
+
});
|
|
6775
|
+
}))).pipe(rxjs.tap((addedUpdates) => {
|
|
6776
|
+
// Update currentUnsentFrom to only listen for updates that will be newer than the ones we emitted.
|
|
6777
|
+
// (Before, we did this within the liveQuery, but that caused a bug because
|
|
6778
|
+
// a cancelled emittion of a liveQuery would update the currentUnsentFrom without
|
|
6779
|
+
// emitting anything, leading to that we jumped over some updates. Here we update it
|
|
6780
|
+
// after the liveQuery has emitted its updates)
|
|
6781
|
+
if (addedUpdates.length > 0) {
|
|
6782
|
+
currentUnsentFrom = addedUpdates.at(-1).i + 1;
|
|
6783
|
+
}
|
|
6784
|
+
}));
|
|
6649
6785
|
}));
|
|
6650
|
-
})).pipe(
|
|
6786
|
+
})).pipe(
|
|
6787
|
+
// Flatten the array of messages.
|
|
6788
|
+
// If messageProducer emits empty array, nothing is emitted
|
|
6789
|
+
// but if messageProducer emits array of messages, they are
|
|
6790
|
+
// emitted one by one.
|
|
6791
|
+
rxjs.mergeMap((messages) => messages), rxjs.tap((message) => {
|
|
6792
|
+
console.debug('dexie-cloud emitting y-c', message);
|
|
6793
|
+
}));
|
|
6651
6794
|
}
|
|
6652
6795
|
|
|
6653
6796
|
function getAwarenessLibrary(db) {
|
|
@@ -6664,25 +6807,24 @@
|
|
|
6664
6807
|
const CLIENT_PING_INTERVAL = 30000;
|
|
6665
6808
|
const FAIL_RETRY_WAIT_TIME = 60000;
|
|
6666
6809
|
class WSObservable extends rxjs.Observable {
|
|
6667
|
-
constructor(db, rev, realmSetHash, clientIdentity, messageProducer, webSocketStatus,
|
|
6668
|
-
super((subscriber) => new WSConnection(db, rev, realmSetHash, clientIdentity,
|
|
6810
|
+
constructor(db, rev, realmSetHash, clientIdentity, messageProducer, webSocketStatus, user) {
|
|
6811
|
+
super((subscriber) => new WSConnection(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus));
|
|
6669
6812
|
}
|
|
6670
6813
|
}
|
|
6671
6814
|
let counter = 0;
|
|
6672
6815
|
class WSConnection extends rxjs.Subscription {
|
|
6673
|
-
constructor(db, rev, realmSetHash, clientIdentity,
|
|
6816
|
+
constructor(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus) {
|
|
6674
6817
|
super(() => this.teardown());
|
|
6675
6818
|
this.id = ++counter;
|
|
6676
6819
|
this.subscriptions = new Set();
|
|
6677
6820
|
this.reconnecting = false;
|
|
6678
|
-
console.debug('New WebSocket Connection', this.id,
|
|
6821
|
+
console.debug('New WebSocket Connection', this.id, user.accessToken ? 'authorized' : 'unauthorized');
|
|
6679
6822
|
this.db = db;
|
|
6680
6823
|
this.databaseUrl = db.cloud.options.databaseUrl;
|
|
6681
6824
|
this.rev = rev;
|
|
6682
6825
|
this.realmSetHash = realmSetHash;
|
|
6683
6826
|
this.clientIdentity = clientIdentity;
|
|
6684
|
-
this.
|
|
6685
|
-
this.tokenExpiration = tokenExpiration;
|
|
6827
|
+
this.user = user;
|
|
6686
6828
|
this.subscriber = subscriber;
|
|
6687
6829
|
this.lastUserActivity = new Date();
|
|
6688
6830
|
this.messageProducer = messageProducer;
|
|
@@ -6742,7 +6884,8 @@
|
|
|
6742
6884
|
//console.debug('SyncStatus: DUBB: Ooops it was closed!');
|
|
6743
6885
|
return;
|
|
6744
6886
|
}
|
|
6745
|
-
|
|
6887
|
+
const tokenExpiration = this.user.accessTokenExpiration;
|
|
6888
|
+
if (tokenExpiration && tokenExpiration < new Date()) {
|
|
6746
6889
|
this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
|
|
6747
6890
|
return;
|
|
6748
6891
|
}
|
|
@@ -6797,8 +6940,8 @@
|
|
|
6797
6940
|
searchParams.set('rev', this.rev);
|
|
6798
6941
|
searchParams.set('realmsHash', this.realmSetHash);
|
|
6799
6942
|
searchParams.set('clientId', this.clientIdentity);
|
|
6800
|
-
if (this.
|
|
6801
|
-
searchParams.set('token', this.
|
|
6943
|
+
if (this.user.accessToken) {
|
|
6944
|
+
searchParams.set('token', this.user.accessToken);
|
|
6802
6945
|
}
|
|
6803
6946
|
// Connect the WebSocket to given url:
|
|
6804
6947
|
console.debug('dexie-cloud WebSocket create');
|
|
@@ -6813,12 +6956,12 @@
|
|
|
6813
6956
|
ws.onmessage = (event) => {
|
|
6814
6957
|
if (!this.pinger)
|
|
6815
6958
|
return;
|
|
6816
|
-
console.debug('dexie-cloud WebSocket onmessage', event.data);
|
|
6817
6959
|
this.lastServerActivity = new Date();
|
|
6818
6960
|
try {
|
|
6819
6961
|
const msg = typeof event.data === 'string'
|
|
6820
6962
|
? TSON.parse(event.data)
|
|
6821
6963
|
: decodeYMessage(new Uint8Array(event.data));
|
|
6964
|
+
console.debug('dexie-cloud WebSocket onmessage', msg.type, msg);
|
|
6822
6965
|
if (msg.type === 'error') {
|
|
6823
6966
|
throw new Error(`Error message from dexie-cloud: ${msg.error}`);
|
|
6824
6967
|
}
|
|
@@ -6874,6 +7017,7 @@
|
|
|
6874
7017
|
this.webSocketStatus.value !== 'connected') {
|
|
6875
7018
|
this.webSocketStatus.next('connected');
|
|
6876
7019
|
}
|
|
7020
|
+
console.debug('dexie-cloud WebSocket send', msg.type, msg);
|
|
6877
7021
|
if (msg.type === 'ready') {
|
|
6878
7022
|
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(TSON.stringify(msg));
|
|
6879
7023
|
}
|
|
@@ -6884,7 +7028,9 @@
|
|
|
6884
7028
|
}
|
|
6885
7029
|
}
|
|
6886
7030
|
}));
|
|
6887
|
-
|
|
7031
|
+
if (this.user.isLoggedIn && !isEagerSyncDisabled(this.db)) {
|
|
7032
|
+
this.subscriptions.add(createYClientUpdateObservable(this.db).subscribe(this.db.messageProducer));
|
|
7033
|
+
}
|
|
6888
7034
|
}
|
|
6889
7035
|
catch (error) {
|
|
6890
7036
|
this.pauseUntil = new Date(Date.now() + FAIL_RETRY_WAIT_TIME);
|
|
@@ -6954,7 +7100,7 @@
|
|
|
6954
7100
|
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]));
|
|
6955
7101
|
}
|
|
6956
7102
|
return new rxjs.BehaviorSubject([userLogin, syncState]);
|
|
6957
|
-
}), switchMap((
|
|
7103
|
+
}), 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]) => {
|
|
6958
7104
|
var _a;
|
|
6959
7105
|
if (!((_a = db.cloud.persistedSyncState) === null || _a === void 0 ? void 0 : _a.value)) {
|
|
6960
7106
|
// Restart the flow if persistedSyncState is not yet available.
|
|
@@ -6964,7 +7110,7 @@
|
|
|
6964
7110
|
// If no new entries, server won't bother the client. If new entries, server sends only those
|
|
6965
7111
|
// and the baseRev of the last from same client-ID.
|
|
6966
7112
|
if (userLogin) {
|
|
6967
|
-
return new WSObservable(db, db.cloud.persistedSyncState.value.serverRevision, realmSetHash, db.cloud.persistedSyncState.value.clientIdentity, messageProducer, db.cloud.webSocketStatus, userLogin
|
|
7113
|
+
return new WSObservable(db, db.cloud.persistedSyncState.value.serverRevision, realmSetHash, db.cloud.persistedSyncState.value.clientIdentity, messageProducer, db.cloud.webSocketStatus, userLogin);
|
|
6968
7114
|
}
|
|
6969
7115
|
else {
|
|
6970
7116
|
return rxjs.from([]);
|
|
@@ -7015,8 +7161,8 @@
|
|
|
7015
7161
|
}
|
|
7016
7162
|
|
|
7017
7163
|
function isSyncNeeded(db) {
|
|
7018
|
-
var _a;
|
|
7019
7164
|
return __awaiter(this, void 0, void 0, function* () {
|
|
7165
|
+
var _a;
|
|
7020
7166
|
return ((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl) && db.cloud.schema
|
|
7021
7167
|
? yield sync(db, db.cloud.options, db.cloud.schema, { justCheckIfNeeded: true })
|
|
7022
7168
|
: false;
|
|
@@ -7700,6 +7846,7 @@
|
|
|
7700
7846
|
// If user can update any prop in any table in this realm, return true unless
|
|
7701
7847
|
// it regards to ownership change:
|
|
7702
7848
|
if (this.permissions.update === '*') {
|
|
7849
|
+
// @ts-ignore
|
|
7703
7850
|
return props.every((prop) => prop !== 'owner');
|
|
7704
7851
|
}
|
|
7705
7852
|
const tablePermissions = (_b = this.permissions.update) === null || _b === void 0 ? void 0 : _b[this.tableName];
|
|
@@ -7771,125 +7918,139 @@
|
|
|
7771
7918
|
});
|
|
7772
7919
|
|
|
7773
7920
|
function createYHandler(db) {
|
|
7774
|
-
const awap = getAwarenessLibrary(db);
|
|
7775
7921
|
return (provider) => {
|
|
7776
7922
|
var _a;
|
|
7777
7923
|
const doc = provider.doc;
|
|
7778
|
-
const { parentTable
|
|
7924
|
+
const { parentTable } = doc.meta || {};
|
|
7779
7925
|
if (!((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[parentTable].markedForSync)) {
|
|
7780
7926
|
return; // The table that holds the doc is not marked for sync - leave it to dexie. No syncing, no awareness.
|
|
7781
7927
|
}
|
|
7782
|
-
let awareness
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7928
|
+
let awareness;
|
|
7929
|
+
Object.defineProperty(provider, "awareness", {
|
|
7930
|
+
get() {
|
|
7931
|
+
if (awareness)
|
|
7932
|
+
return awareness;
|
|
7933
|
+
awareness = createAwareness(db, doc, provider);
|
|
7934
|
+
awarenessWeakMap.set(doc, awareness);
|
|
7935
|
+
return awareness;
|
|
7936
|
+
}
|
|
7937
|
+
});
|
|
7938
|
+
};
|
|
7939
|
+
}
|
|
7940
|
+
function createAwareness(db, doc, provider) {
|
|
7941
|
+
const { parentTable, parentId, parentProp, updatesTable } = doc.meta;
|
|
7942
|
+
const awap = getAwarenessLibrary(db);
|
|
7943
|
+
const awareness = new awap.Awareness(doc);
|
|
7944
|
+
awareness.on('update', ({ added, updated, removed }, origin) => {
|
|
7945
|
+
// Send the update
|
|
7946
|
+
const changedClients = added.concat(updated).concat(removed);
|
|
7947
|
+
const user = db.cloud.currentUser.value;
|
|
7948
|
+
if (origin !== 'server' && user.isLoggedIn && !isEagerSyncDisabled(db)) {
|
|
7949
|
+
const update = awap.encodeAwarenessUpdate(awareness, changedClients);
|
|
7950
|
+
db.messageProducer.next({
|
|
7951
|
+
type: 'aware',
|
|
7952
|
+
table: parentTable,
|
|
7953
|
+
prop: parentProp,
|
|
7954
|
+
k: doc.meta.parentId,
|
|
7955
|
+
u: update,
|
|
7956
|
+
});
|
|
7957
|
+
if (provider.destroyed) {
|
|
7958
|
+
// We're called from awareness.on('destroy') that did
|
|
7959
|
+
// removeAwarenessStates.
|
|
7960
|
+
// It's time to also send the doc-close message that dexie-cloud understands
|
|
7961
|
+
// and uses to stop subscribing for updates and awareness updates and brings
|
|
7962
|
+
// down the cached information in memory on the WS connection for this.
|
|
7790
7963
|
db.messageProducer.next({
|
|
7791
|
-
type: '
|
|
7964
|
+
type: 'doc-close',
|
|
7792
7965
|
table: parentTable,
|
|
7793
7966
|
prop: parentProp,
|
|
7794
|
-
k: doc.meta.parentId
|
|
7795
|
-
u: update,
|
|
7967
|
+
k: doc.meta.parentId
|
|
7796
7968
|
});
|
|
7797
|
-
if (provider.destroyed) {
|
|
7798
|
-
// We're called from awareness.on('destroy') that did
|
|
7799
|
-
// removeAwarenessStates.
|
|
7800
|
-
// It's time to also send the doc-close message that dexie-cloud understands
|
|
7801
|
-
// and uses to stop subscribing for updates and awareness updates and brings
|
|
7802
|
-
// down the cached information in memory on the WS connection for this.
|
|
7803
|
-
db.messageProducer.next({
|
|
7804
|
-
type: 'doc-close',
|
|
7805
|
-
table: parentTable,
|
|
7806
|
-
prop: parentProp,
|
|
7807
|
-
k: doc.meta.parentId
|
|
7808
|
-
});
|
|
7809
|
-
}
|
|
7810
7969
|
}
|
|
7811
|
-
}
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
7818
|
-
|
|
7970
|
+
}
|
|
7971
|
+
});
|
|
7972
|
+
awareness.on('destroy', () => {
|
|
7973
|
+
// Signal to server that this provider is destroyed (the update event will be triggered, which
|
|
7974
|
+
// in turn will trigger db.messageProducer that will send the message to the server if WS is connected)
|
|
7975
|
+
awap.removeAwarenessStates(awareness, [doc.clientID], 'provider destroyed');
|
|
7976
|
+
});
|
|
7977
|
+
// Open the document on the server
|
|
7978
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
7979
|
+
if (provider.destroyed)
|
|
7980
|
+
return;
|
|
7981
|
+
let connected = false;
|
|
7982
|
+
let currentFlowId = 1;
|
|
7983
|
+
const subscription = db.cloud.webSocketStatus.subscribe((wsStatus) => {
|
|
7819
7984
|
if (provider.destroyed)
|
|
7820
7985
|
return;
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
7986
|
+
// Keep "connected" state in a variable so we can check it after async operations
|
|
7987
|
+
connected = wsStatus === 'connected';
|
|
7988
|
+
// We are or got connected. Open the document on the server.
|
|
7989
|
+
const user = db.cloud.currentUser.value;
|
|
7990
|
+
if (wsStatus === "connected" && user.isLoggedIn && !isEagerSyncDisabled(db)) {
|
|
7991
|
+
++currentFlowId;
|
|
7992
|
+
openDocumentOnServer().catch(error => {
|
|
7993
|
+
console.warn(`Error catched in createYHandler.ts: ${error}`);
|
|
7994
|
+
});
|
|
7995
|
+
}
|
|
7996
|
+
});
|
|
7997
|
+
// Wait until WebSocket is connected
|
|
7998
|
+
provider.addCleanupHandler(subscription);
|
|
7999
|
+
/** Sends an 'doc-open' message to server whenever websocket becomes
|
|
8000
|
+
* connected, or if it is already connected.
|
|
8001
|
+
* The flow is aborted in case websocket is disconnected while querying
|
|
8002
|
+
* information required to compute the state vector. Flow is also
|
|
8003
|
+
* aborted in case document or provider has been destroyed during
|
|
8004
|
+
* the async parts of the task.
|
|
8005
|
+
*
|
|
8006
|
+
* The state vector is only computed from the updates that have occured
|
|
8007
|
+
* after the last full sync - which could very often be zero - in which
|
|
8008
|
+
* case no state vector is sent (then the server already knows us by
|
|
8009
|
+
* revision)
|
|
8010
|
+
*
|
|
8011
|
+
* When server gets the doc-open message, it will authorized us for
|
|
8012
|
+
* whether we are allowed to read / write to this document, and then
|
|
8013
|
+
* keep the cached information in memory on the WS connection for this
|
|
8014
|
+
* particular document, as well as subscribe to updates and awareness updates
|
|
8015
|
+
* from other clients on the document.
|
|
8016
|
+
*/
|
|
8017
|
+
function openDocumentOnServer(wsStatus) {
|
|
8018
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8019
|
+
const myFlow = currentFlowId; // So we can abort when a new flow is started
|
|
8020
|
+
const yTbl = db.table(updatesTable);
|
|
8021
|
+
const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
|
|
8022
|
+
// After every await, check if we still should be working on this task.
|
|
8023
|
+
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7825
8024
|
return;
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
8025
|
+
const receivedUntil = (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0;
|
|
8026
|
+
const docOpenMsg = {
|
|
8027
|
+
type: 'doc-open',
|
|
8028
|
+
table: parentTable,
|
|
8029
|
+
prop: parentProp,
|
|
8030
|
+
k: parentId,
|
|
8031
|
+
serverRev: syncState === null || syncState === void 0 ? void 0 : syncState.serverRev,
|
|
8032
|
+
};
|
|
8033
|
+
const serverUpdatesSinceLastSync = yield yTbl
|
|
8034
|
+
.where('i')
|
|
8035
|
+
.between(receivedUntil, Infinity, false)
|
|
8036
|
+
.filter((update) => Dexie.cmp(update.k, parentId) === 0 && // Only updates for this document
|
|
8037
|
+
((update.f || 0) & 1) === 0 // Don't include local changes
|
|
8038
|
+
)
|
|
8039
|
+
.toArray();
|
|
8040
|
+
// After every await, check if we still should be working on this task.
|
|
8041
|
+
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
8042
|
+
return;
|
|
8043
|
+
if (serverUpdatesSinceLastSync.length > 0) {
|
|
8044
|
+
const Y = $Y$1(db); // Get the Yjs library from Dexie constructor options
|
|
8045
|
+
const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
|
|
8046
|
+
const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
|
|
8047
|
+
docOpenMsg.sv = stateVector;
|
|
7834
8048
|
}
|
|
8049
|
+
db.messageProducer.next(docOpenMsg);
|
|
7835
8050
|
});
|
|
7836
|
-
|
|
7837
|
-
|
|
7838
|
-
|
|
7839
|
-
* connected, or if it is already connected.
|
|
7840
|
-
* The flow is aborted in case websocket is disconnected while querying
|
|
7841
|
-
* information required to compute the state vector. Flow is also
|
|
7842
|
-
* aborted in case document or provider has been destroyed during
|
|
7843
|
-
* the async parts of the task.
|
|
7844
|
-
*
|
|
7845
|
-
* The state vector is only computed from the updates that have occured
|
|
7846
|
-
* after the last full sync - which could very often be zero - in which
|
|
7847
|
-
* case no state vector is sent (then the server already knows us by
|
|
7848
|
-
* revision)
|
|
7849
|
-
*
|
|
7850
|
-
* When server gets the doc-open message, it will authorized us for
|
|
7851
|
-
* whether we are allowed to read / write to this document, and then
|
|
7852
|
-
* keep the cached information in memory on the WS connection for this
|
|
7853
|
-
* particular document, as well as subscribe to updates and awareness updates
|
|
7854
|
-
* from other clients on the document.
|
|
7855
|
-
*/
|
|
7856
|
-
function openDocumentOnServer(wsStatus) {
|
|
7857
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
7858
|
-
const myFlow = currentFlowId; // So we can abort when a new flow is started
|
|
7859
|
-
const yTbl = db.table(updatesTable);
|
|
7860
|
-
const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
|
|
7861
|
-
// After every await, check if we still should be working on this task.
|
|
7862
|
-
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7863
|
-
return;
|
|
7864
|
-
const receivedUntil = (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0;
|
|
7865
|
-
const docOpenMsg = {
|
|
7866
|
-
type: 'doc-open',
|
|
7867
|
-
table: parentTable,
|
|
7868
|
-
prop: parentProp,
|
|
7869
|
-
k: parentId,
|
|
7870
|
-
serverRev: syncState === null || syncState === void 0 ? void 0 : syncState.serverRev,
|
|
7871
|
-
};
|
|
7872
|
-
const serverUpdatesSinceLastSync = yield yTbl
|
|
7873
|
-
.where('i')
|
|
7874
|
-
.between(receivedUntil, Infinity, false)
|
|
7875
|
-
.filter((update) => Dexie.cmp(update.k, parentId) === 0 && // Only updates for this document
|
|
7876
|
-
((update.f || 0) & 1) === 0 // Don't include local changes
|
|
7877
|
-
)
|
|
7878
|
-
.toArray();
|
|
7879
|
-
// After every await, check if we still should be working on this task.
|
|
7880
|
-
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7881
|
-
return;
|
|
7882
|
-
if (serverUpdatesSinceLastSync.length > 0) {
|
|
7883
|
-
const Y = $Y(db); // Get the Yjs library from Dexie constructor options
|
|
7884
|
-
const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
|
|
7885
|
-
const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
|
|
7886
|
-
docOpenMsg.sv = stateVector;
|
|
7887
|
-
}
|
|
7888
|
-
db.messageProducer.next(docOpenMsg);
|
|
7889
|
-
});
|
|
7890
|
-
}
|
|
7891
|
-
}));
|
|
7892
|
-
};
|
|
8051
|
+
}
|
|
8052
|
+
}))();
|
|
8053
|
+
return awareness;
|
|
7893
8054
|
}
|
|
7894
8055
|
|
|
7895
8056
|
function getTiedRealmId(objectId) {
|
|
@@ -7939,7 +8100,7 @@
|
|
|
7939
8100
|
const syncComplete = new rxjs.Subject();
|
|
7940
8101
|
dexie.cloud = {
|
|
7941
8102
|
// @ts-ignore
|
|
7942
|
-
version: "4.1.0-alpha.
|
|
8103
|
+
version: "4.1.0-alpha.20",
|
|
7943
8104
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
7944
8105
|
schema: null,
|
|
7945
8106
|
get currentUserId() {
|
|
@@ -7975,16 +8136,16 @@
|
|
|
7975
8136
|
}
|
|
7976
8137
|
updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
|
|
7977
8138
|
},
|
|
7978
|
-
logout(
|
|
7979
|
-
return __awaiter(this,
|
|
8139
|
+
logout() {
|
|
8140
|
+
return __awaiter(this, arguments, void 0, function* ({ force } = {}) {
|
|
7980
8141
|
force
|
|
7981
8142
|
? yield _logout(DexieCloudDB(dexie), { deleteUnsyncedData: true })
|
|
7982
8143
|
: yield logout(DexieCloudDB(dexie));
|
|
7983
8144
|
});
|
|
7984
8145
|
},
|
|
7985
|
-
sync(
|
|
7986
|
-
|
|
7987
|
-
|
|
8146
|
+
sync() {
|
|
8147
|
+
return __awaiter(this, arguments, void 0, function* ({ wait, purpose } = { wait: true, purpose: 'push' }) {
|
|
8148
|
+
var _a;
|
|
7988
8149
|
if (wait === undefined)
|
|
7989
8150
|
wait = true;
|
|
7990
8151
|
const db = DexieCloudDB(dexie);
|
|
@@ -8042,8 +8203,8 @@
|
|
|
8042
8203
|
dexie.use(createImplicitPropSetterMiddleware(DexieCloudDB(dexie)));
|
|
8043
8204
|
dexie.use(createIdGenerationMiddleware(DexieCloudDB(dexie)));
|
|
8044
8205
|
function onDbReady(dexie) {
|
|
8045
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
8046
8206
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8207
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
8047
8208
|
closed = false; // As Dexie calls us, we are not closed anymore. Maybe reopened? Remember db.ready event is registered with sticky flag!
|
|
8048
8209
|
const db = DexieCloudDB(dexie);
|
|
8049
8210
|
// Setup default GUI:
|
|
@@ -8066,7 +8227,7 @@
|
|
|
8066
8227
|
? yield navigator.serviceWorker.getRegistrations()
|
|
8067
8228
|
: [];
|
|
8068
8229
|
const [initiallySynced, lastSyncedRealms] = yield db.transaction('rw', db.$syncState, () => __awaiter(this, void 0, void 0, function* () {
|
|
8069
|
-
var
|
|
8230
|
+
var _a, _b;
|
|
8070
8231
|
const { options, schema } = db.cloud;
|
|
8071
8232
|
const [persistedOptions, persistedSchema, persistedSyncState] = yield Promise.all([
|
|
8072
8233
|
db.getOptions(),
|
|
@@ -8088,7 +8249,7 @@
|
|
|
8088
8249
|
delete newPersistedOptions.awarenessProtocol;
|
|
8089
8250
|
yield db.$syncState.put(newPersistedOptions, 'options');
|
|
8090
8251
|
}
|
|
8091
|
-
if (((
|
|
8252
|
+
if (((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.tryUseServiceWorker) &&
|
|
8092
8253
|
'serviceWorker' in navigator &&
|
|
8093
8254
|
swRegistrations.length > 0 &&
|
|
8094
8255
|
!DISABLE_SERVICEWORKER_STRATEGY) {
|
|
@@ -8102,7 +8263,7 @@
|
|
|
8102
8263
|
// Not configured for using service worker or no service worker
|
|
8103
8264
|
// registration exists. Don't rely on service worker to do any job.
|
|
8104
8265
|
// Use LocalSyncWorker instead.
|
|
8105
|
-
if (((
|
|
8266
|
+
if (((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.tryUseServiceWorker) &&
|
|
8106
8267
|
!db.cloud.isServiceWorkerDB) {
|
|
8107
8268
|
console.debug('dexie-cloud-addon: Not using service worker.', swRegistrations.length === 0
|
|
8108
8269
|
? 'No SW registrations found.'
|
|
@@ -8241,10 +8402,167 @@
|
|
|
8241
8402
|
}
|
|
8242
8403
|
}
|
|
8243
8404
|
// @ts-ignore
|
|
8244
|
-
dexieCloud.version = "4.1.0-alpha.
|
|
8405
|
+
dexieCloud.version = "4.1.0-alpha.20";
|
|
8245
8406
|
Dexie.Cloud = dexieCloud;
|
|
8246
8407
|
|
|
8408
|
+
const ydocTriggers = {};
|
|
8409
|
+
const docIsAlreadyHooked = new WeakSet();
|
|
8410
|
+
const middlewares = new WeakMap();
|
|
8411
|
+
const createMiddleware = (db) => ({
|
|
8412
|
+
stack: 'dbcore',
|
|
8413
|
+
level: 10,
|
|
8414
|
+
name: 'yTriggerMiddleware',
|
|
8415
|
+
create: (down) => {
|
|
8416
|
+
return Object.assign(Object.assign({}, down), { transaction: (stores, mode, options) => {
|
|
8417
|
+
const idbtrans = down.transaction(stores, mode, options);
|
|
8418
|
+
idbtrans.addEventListener('complete', onTransactionCommitted);
|
|
8419
|
+
return idbtrans;
|
|
8420
|
+
}, table: (tblName) => {
|
|
8421
|
+
const coreTable = down.table(tblName);
|
|
8422
|
+
const triggerSpec = ydocTriggers[tblName];
|
|
8423
|
+
if (!triggerSpec)
|
|
8424
|
+
return coreTable;
|
|
8425
|
+
const { trigger, parentTable, prop } = triggerSpec;
|
|
8426
|
+
return Object.assign(Object.assign({}, coreTable), { mutate(req) {
|
|
8427
|
+
switch (req.type) {
|
|
8428
|
+
case 'add': {
|
|
8429
|
+
for (const yUpdateRow of req.values) {
|
|
8430
|
+
if (yUpdateRow.k == undefined)
|
|
8431
|
+
continue; // A syncer or garbage collection state does not point to a key
|
|
8432
|
+
const primaryKey = yUpdateRow.k;
|
|
8433
|
+
const doc = Dexie.DexieYProvider.getDocCache(db).find(parentTable, primaryKey, prop);
|
|
8434
|
+
if (doc) {
|
|
8435
|
+
if (!docIsAlreadyHooked.has(doc)) {
|
|
8436
|
+
hookToDoc(doc, primaryKey, trigger);
|
|
8437
|
+
docIsAlreadyHooked.add(doc);
|
|
8438
|
+
}
|
|
8439
|
+
}
|
|
8440
|
+
else {
|
|
8441
|
+
enqueueTrigger(db, tblName, primaryKey, trigger);
|
|
8442
|
+
}
|
|
8443
|
+
}
|
|
8444
|
+
break;
|
|
8445
|
+
}
|
|
8446
|
+
case 'delete':
|
|
8447
|
+
// @ts-ignore
|
|
8448
|
+
if (req.trans._rejecting_y_ypdate) {
|
|
8449
|
+
// The deletion came from a rejection, not garbage collection.
|
|
8450
|
+
// When that happens, let the triggers run to compute new values
|
|
8451
|
+
// based on the deleted updates.
|
|
8452
|
+
coreTable
|
|
8453
|
+
.getMany({
|
|
8454
|
+
keys: req.keys,
|
|
8455
|
+
trans: req.trans,
|
|
8456
|
+
cache: 'immutable',
|
|
8457
|
+
})
|
|
8458
|
+
.then((updates) => {
|
|
8459
|
+
const keySet = new Dexie.RangeSet();
|
|
8460
|
+
for (const { k } of updates) {
|
|
8461
|
+
if (k != undefined)
|
|
8462
|
+
keySet.addKey(k);
|
|
8463
|
+
}
|
|
8464
|
+
for (const interval of keySet) {
|
|
8465
|
+
enqueueTrigger(db, tblName, interval.from, trigger);
|
|
8466
|
+
}
|
|
8467
|
+
});
|
|
8468
|
+
}
|
|
8469
|
+
break;
|
|
8470
|
+
}
|
|
8471
|
+
return coreTable.mutate(req);
|
|
8472
|
+
} });
|
|
8473
|
+
} });
|
|
8474
|
+
},
|
|
8475
|
+
});
|
|
8476
|
+
let triggerExecPromise = null;
|
|
8477
|
+
let triggerScheduled = false;
|
|
8478
|
+
let scheduledTriggers = [];
|
|
8479
|
+
function $Y(db) {
|
|
8480
|
+
const $Y = db._options.Y;
|
|
8481
|
+
if (!$Y)
|
|
8482
|
+
throw new Error('Y library not supplied to Dexie constructor');
|
|
8483
|
+
return $Y;
|
|
8484
|
+
}
|
|
8485
|
+
function executeTriggers(triggersToRun) {
|
|
8486
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8487
|
+
for (const { db, parentId, trigger, updatesTable } of triggersToRun) {
|
|
8488
|
+
// Load entire document into an Y.Doc instance:
|
|
8489
|
+
const updates = yield db
|
|
8490
|
+
.table(updatesTable)
|
|
8491
|
+
.where({ k: parentId })
|
|
8492
|
+
.toArray();
|
|
8493
|
+
const Y = $Y(db);
|
|
8494
|
+
const yDoc = new Y.Doc();
|
|
8495
|
+
for (const update of updates) {
|
|
8496
|
+
Y.applyUpdateV2(yDoc, update.u);
|
|
8497
|
+
}
|
|
8498
|
+
try {
|
|
8499
|
+
yield trigger(yDoc, parentId);
|
|
8500
|
+
}
|
|
8501
|
+
catch (error) {
|
|
8502
|
+
console.error(`Error in YDocTrigger ${error}`);
|
|
8503
|
+
}
|
|
8504
|
+
}
|
|
8505
|
+
});
|
|
8506
|
+
}
|
|
8507
|
+
function enqueueTrigger(db, updatesTable, parentId, trigger) {
|
|
8508
|
+
scheduledTriggers.push({
|
|
8509
|
+
db,
|
|
8510
|
+
updatesTable,
|
|
8511
|
+
parentId,
|
|
8512
|
+
trigger,
|
|
8513
|
+
});
|
|
8514
|
+
}
|
|
8515
|
+
function onTransactionCommitted() {
|
|
8516
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8517
|
+
if (!triggerScheduled && scheduledTriggers.length > 0) {
|
|
8518
|
+
triggerScheduled = true;
|
|
8519
|
+
if (triggerExecPromise)
|
|
8520
|
+
yield triggerExecPromise.catch(() => { });
|
|
8521
|
+
setTimeout(() => {
|
|
8522
|
+
// setTimeout() is to escape from Promise.PSD zones and never run within liveQueries or transaction scopes
|
|
8523
|
+
triggerScheduled = false;
|
|
8524
|
+
const triggersToRun = scheduledTriggers;
|
|
8525
|
+
scheduledTriggers = [];
|
|
8526
|
+
triggerExecPromise = executeTriggers(triggersToRun).finally(() => (triggerExecPromise = null));
|
|
8527
|
+
}, 0);
|
|
8528
|
+
}
|
|
8529
|
+
});
|
|
8530
|
+
}
|
|
8531
|
+
function hookToDoc(doc, parentId, trigger) {
|
|
8532
|
+
// From now on, keep listening to doc updates and execute the trigger when it happens there instead
|
|
8533
|
+
doc.on('updateV2', (update, origin) => {
|
|
8534
|
+
//Dexie.ignoreTransaction(()=>{
|
|
8535
|
+
trigger(doc, parentId);
|
|
8536
|
+
//});
|
|
8537
|
+
});
|
|
8538
|
+
/*
|
|
8539
|
+
NOT NEEDED because DexieYProvider's docCache will also listen to destroy and remove it from its cache:
|
|
8540
|
+
doc.on('destroy', ()=>{
|
|
8541
|
+
docIsAlreadyHooked.delete(doc);
|
|
8542
|
+
})
|
|
8543
|
+
*/
|
|
8544
|
+
}
|
|
8545
|
+
function defineYDocTrigger(table, prop, trigger) {
|
|
8546
|
+
var _a, _b;
|
|
8547
|
+
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;
|
|
8548
|
+
if (!updatesTable)
|
|
8549
|
+
throw new Error(`Table ${table.name} does not have a Yjs property named ${prop}`);
|
|
8550
|
+
ydocTriggers[updatesTable] = {
|
|
8551
|
+
trigger,
|
|
8552
|
+
parentTable: table.name,
|
|
8553
|
+
prop,
|
|
8554
|
+
};
|
|
8555
|
+
const db = table.db._novip;
|
|
8556
|
+
let mw = middlewares.get(db);
|
|
8557
|
+
if (!mw) {
|
|
8558
|
+
mw = createMiddleware(db);
|
|
8559
|
+
middlewares.set(db, mw);
|
|
8560
|
+
}
|
|
8561
|
+
db.use(mw);
|
|
8562
|
+
}
|
|
8563
|
+
|
|
8247
8564
|
exports.default = dexieCloud;
|
|
8565
|
+
exports.defineYDocTrigger = defineYDocTrigger;
|
|
8248
8566
|
exports.dexieCloud = dexieCloud;
|
|
8249
8567
|
exports.getTiedObjectId = getTiedObjectId;
|
|
8250
8568
|
exports.getTiedRealmId = getTiedRealmId;
|