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
|
*
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
*
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import Dexie, { PropModification, cmp, DexieYProvider, liveQuery } from 'dexie';
|
|
20
|
-
import { Observable as Observable$1, BehaviorSubject, firstValueFrom, Subject, from as from$1, filter as filter$1, 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 { Observable as Observable$1, BehaviorSubject, firstValueFrom, Subject, from as from$1, filter as filter$1, fromEvent, of, merge, switchMap as switchMap$1, tap as tap$1, mergeMap as mergeMap$1, Subscription as Subscription$1, throwError, combineLatest, map as map$1, share, timer as timer$1 } from 'rxjs';
|
|
21
21
|
|
|
22
22
|
/******************************************************************************
|
|
23
23
|
Copyright (c) Microsoft Corporation.
|
|
@@ -1765,8 +1765,8 @@ function registerSyncEvent(db, purpose) {
|
|
|
1765
1765
|
});
|
|
1766
1766
|
}
|
|
1767
1767
|
function registerPeriodicSyncEvent(db) {
|
|
1768
|
-
var _a;
|
|
1769
1768
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1769
|
+
var _a;
|
|
1770
1770
|
try {
|
|
1771
1771
|
// Register periodicSync event to SW:
|
|
1772
1772
|
// @ts-ignore
|
|
@@ -1831,8 +1831,8 @@ const b64encode = typeof Buffer !== "undefined"
|
|
|
1831
1831
|
return btoa(strs.join(""));
|
|
1832
1832
|
};
|
|
1833
1833
|
|
|
1834
|
-
function computeRealmSetHash(
|
|
1835
|
-
return __awaiter(this,
|
|
1834
|
+
function computeRealmSetHash(_a) {
|
|
1835
|
+
return __awaiter(this, arguments, void 0, function* ({ realms, inviteRealms, }) {
|
|
1836
1836
|
const data = JSON.stringify([
|
|
1837
1837
|
...realms.map((realmId) => ({ realmId, accepted: true })),
|
|
1838
1838
|
...inviteRealms.map((realmId) => ({ realmId, accepted: false })),
|
|
@@ -1868,8 +1868,8 @@ function flatten(a) {
|
|
|
1868
1868
|
return concat.apply([], a);
|
|
1869
1869
|
}
|
|
1870
1870
|
|
|
1871
|
-
function listClientChanges(
|
|
1872
|
-
return __awaiter(this,
|
|
1871
|
+
function listClientChanges(mutationTables_1, db_1) {
|
|
1872
|
+
return __awaiter(this, arguments, void 0, function* (mutationTables, db, { since = {}, limit = Infinity } = {}) {
|
|
1873
1873
|
const allMutsOnTables = yield Promise.all(mutationTables.map((mutationTable) => __awaiter(this, void 0, void 0, function* () {
|
|
1874
1874
|
const tableName = getTableFromMutationTable(mutationTable.name);
|
|
1875
1875
|
const lastRevision = since[tableName];
|
|
@@ -3516,8 +3516,8 @@ function confirmLogout(userInteraction, currentUserId, numUnsyncedChanges) {
|
|
|
3516
3516
|
}
|
|
3517
3517
|
|
|
3518
3518
|
function loadAccessToken(db) {
|
|
3519
|
-
var _a, _b, _c;
|
|
3520
3519
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3520
|
+
var _a, _b, _c;
|
|
3521
3521
|
const currentUser = yield db.getCurrentUser();
|
|
3522
3522
|
const { accessToken, accessTokenExpiration, refreshToken, refreshTokenExpiration, claims, } = currentUser;
|
|
3523
3523
|
if (!accessToken)
|
|
@@ -4347,8 +4347,8 @@ function cloneChange(change, rewriteValues) {
|
|
|
4347
4347
|
// seconds (given that there is a Ratelimit-Reset header).
|
|
4348
4348
|
let syncRatelimitDelays = new WeakMap();
|
|
4349
4349
|
function checkSyncRateLimitDelay(db) {
|
|
4350
|
-
var _a, _b;
|
|
4351
4350
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4351
|
+
var _a, _b;
|
|
4352
4352
|
const delatMilliseconds = ((_b = (_a = syncRatelimitDelays.get(db)) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) - Date.now();
|
|
4353
4353
|
if (delatMilliseconds > 0) {
|
|
4354
4354
|
console.debug(`Stalling sync request ${delatMilliseconds} ms to spare ratelimits`);
|
|
@@ -4661,13 +4661,12 @@ function $Y(db) {
|
|
|
4661
4661
|
* @param db
|
|
4662
4662
|
* @returns
|
|
4663
4663
|
*/
|
|
4664
|
-
function listYClientMessagesAndStateVector(db) {
|
|
4665
|
-
var _a;
|
|
4664
|
+
function listYClientMessagesAndStateVector(db, tablesToSync) {
|
|
4666
4665
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4667
4666
|
const result = [];
|
|
4668
4667
|
const lastUpdateIds = {};
|
|
4669
|
-
for (const table of
|
|
4670
|
-
if (table.schema.yProps
|
|
4668
|
+
for (const table of tablesToSync) {
|
|
4669
|
+
if (table.schema.yProps) {
|
|
4671
4670
|
for (const yProp of table.schema.yProps) {
|
|
4672
4671
|
const Y = $Y(db); // This is how we retrieve the user-provided Y library
|
|
4673
4672
|
const yTable = db.table(yProp.updatesTable); // the updates-table for this combo of table+propName
|
|
@@ -4754,6 +4753,7 @@ function getUpdatesTable(db, table, ydocProp) {
|
|
|
4754
4753
|
|
|
4755
4754
|
function applyYServerMessages(yMessages, db) {
|
|
4756
4755
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4756
|
+
var _a;
|
|
4757
4757
|
const result = {};
|
|
4758
4758
|
for (const m of yMessages) {
|
|
4759
4759
|
switch (m.type) {
|
|
@@ -4784,7 +4784,24 @@ function applyYServerMessages(yMessages, db) {
|
|
|
4784
4784
|
// See my question in https://discuss.yjs.dev/t/generate-an-inverse-update/2765
|
|
4785
4785
|
console.debug(`Y update rejected. Deleting it.`);
|
|
4786
4786
|
const utbl = getUpdatesTable(db, m.table, m.prop);
|
|
4787
|
-
|
|
4787
|
+
// Delete the rejected update and all local updates since (avoid holes in the CRDT)
|
|
4788
|
+
// and destroy it's open document if there is one.
|
|
4789
|
+
const primaryKey = (_a = (yield utbl.get(m.i))) === null || _a === void 0 ? void 0 : _a.k;
|
|
4790
|
+
if (primaryKey != null) {
|
|
4791
|
+
yield db.transaction('rw', utbl, tx => {
|
|
4792
|
+
// @ts-ignore
|
|
4793
|
+
tx.idbtrans._rejecting_y_ypdate = true; // Inform ydoc triggers that we delete because of a rejection and not GC
|
|
4794
|
+
return utbl
|
|
4795
|
+
.where('i')
|
|
4796
|
+
.aboveOrEqual(m.i)
|
|
4797
|
+
.filter(u => cmp(u.k, primaryKey) === 0 && ((u.f || 0) & 1) === 1)
|
|
4798
|
+
.delete();
|
|
4799
|
+
});
|
|
4800
|
+
// Destroy active doc
|
|
4801
|
+
const activeDoc = DexieYProvider.getDocCache(db.dx).find(m.table, primaryKey, m.prop);
|
|
4802
|
+
if (activeDoc)
|
|
4803
|
+
activeDoc.destroy(); // Destroy the document so that editors don't continue to work on it
|
|
4804
|
+
}
|
|
4788
4805
|
break;
|
|
4789
4806
|
}
|
|
4790
4807
|
case 'in-sync': {
|
|
@@ -4801,15 +4818,15 @@ function applyYServerMessages(yMessages, db) {
|
|
|
4801
4818
|
}
|
|
4802
4819
|
|
|
4803
4820
|
function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db, serverRevision) {
|
|
4804
|
-
var _a, _b;
|
|
4805
4821
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4822
|
+
var _a, _b, _c, _d, _e;
|
|
4806
4823
|
// We want to update unsentFrom for each yTable to the value specified in first argument
|
|
4807
4824
|
// because we got those values before we synced with server and here we are back from server
|
|
4808
4825
|
// that has successfully received all those messages - no matter if the last update was a client or server update,
|
|
4809
4826
|
// we can safely store unsentFrom to a value of the last update + 1 here.
|
|
4810
4827
|
// We also want to update receivedUntil for each yTable to the value specified in the second argument,
|
|
4811
4828
|
// because that contains the highest resulted id of each update from server after storing it.
|
|
4812
|
-
// We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
|
|
4829
|
+
// We could do these two tasks separately, but that would require two update calls on the same YSyncState, so
|
|
4813
4830
|
// to optimize the dexie calls, we merge these two maps into a single one so we can do a single update request
|
|
4814
4831
|
// per yTable.
|
|
4815
4832
|
const mergedSpec = {};
|
|
@@ -4821,28 +4838,42 @@ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db,
|
|
|
4821
4838
|
(_b = mergedSpec[yTable]) !== null && _b !== void 0 ? _b : (mergedSpec[yTable] = {});
|
|
4822
4839
|
mergedSpec[yTable].receivedUntil = lastUpdateId;
|
|
4823
4840
|
}
|
|
4824
|
-
// Now go through
|
|
4825
|
-
|
|
4841
|
+
// Now go through all yTables and update their YSyncStates:
|
|
4842
|
+
const allYTables = Object.values(db.dx._dbSchema)
|
|
4843
|
+
.filter((tblSchema) => tblSchema.yProps)
|
|
4844
|
+
.map((tblSchema) => tblSchema.yProps.map((yProp) => yProp.updatesTable))
|
|
4845
|
+
.flat();
|
|
4846
|
+
for (const yTable of allYTables) {
|
|
4847
|
+
const mergedEntry = mergedSpec[yTable];
|
|
4848
|
+
const unsentFrom = (_c = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.unsentFrom) !== null && _c !== void 0 ? _c : 1;
|
|
4849
|
+
const receivedUntil = (_e = (_d = mergedEntry === null || mergedEntry === void 0 ? void 0 : mergedEntry.receivedUntil) !== null && _d !== void 0 ? _d :
|
|
4850
|
+
// from local because we are in the same parent transaction (in sync.ts) that
|
|
4851
|
+
// applied all updates from the server
|
|
4852
|
+
(yield db
|
|
4853
|
+
.table(yTable)
|
|
4854
|
+
.where('i')
|
|
4855
|
+
.between(1, Infinity) // Because i might be string DEXIE_CLOUD_SYNCER_ID if not a number.
|
|
4856
|
+
.reverse()
|
|
4857
|
+
.limit(1)
|
|
4858
|
+
.primaryKeys())[0]) !== null && _e !== void 0 ? _e : 0;
|
|
4826
4859
|
// We're already in a transaction, but for the sake of
|
|
4827
4860
|
// code readability and correctness, let's launch an atomic sub transaction:
|
|
4828
4861
|
yield db.transaction('rw', yTable, () => __awaiter(this, void 0, void 0, function* () {
|
|
4829
|
-
const state = yield db
|
|
4862
|
+
const state = yield db
|
|
4863
|
+
.table(yTable)
|
|
4864
|
+
.get(DEXIE_CLOUD_SYNCER_ID);
|
|
4830
4865
|
if (!state) {
|
|
4831
4866
|
yield db.table(yTable).add({
|
|
4832
4867
|
i: DEXIE_CLOUD_SYNCER_ID,
|
|
4833
|
-
unsentFrom
|
|
4834
|
-
receivedUntil
|
|
4868
|
+
unsentFrom,
|
|
4869
|
+
receivedUntil,
|
|
4835
4870
|
serverRev: serverRevision,
|
|
4836
4871
|
});
|
|
4837
4872
|
}
|
|
4838
4873
|
else {
|
|
4839
|
-
|
|
4840
|
-
|
|
4841
|
-
|
|
4842
|
-
if (receivedUntil) {
|
|
4843
|
-
state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
|
|
4844
|
-
state.serverRev = serverRevision;
|
|
4845
|
-
}
|
|
4874
|
+
state.unsentFrom = Math.max(unsentFrom, state.unsentFrom || 1);
|
|
4875
|
+
state.receivedUntil = Math.max(receivedUntil, state.receivedUntil || 0);
|
|
4876
|
+
state.serverRev = serverRevision;
|
|
4846
4877
|
yield db.table(yTable).put(state);
|
|
4847
4878
|
}
|
|
4848
4879
|
}));
|
|
@@ -4853,9 +4884,11 @@ function updateYSyncStates(lastUpdateIdsBeforeSync, receivedUntilsAfterSync, db,
|
|
|
4853
4884
|
const BINSTREAM_TYPE_REALMID = 1;
|
|
4854
4885
|
const BINSTREAM_TYPE_TABLE_AND_PROP = 2;
|
|
4855
4886
|
const BINSTREAM_TYPE_DOCUMENT = 3;
|
|
4856
|
-
function downloadYDocsFromServer(
|
|
4857
|
-
return __awaiter(this,
|
|
4858
|
-
if (yDownloadedRealms &&
|
|
4887
|
+
function downloadYDocsFromServer(db_1, databaseUrl_1, _a) {
|
|
4888
|
+
return __awaiter(this, arguments, void 0, function* (db, databaseUrl, { yDownloadedRealms, realms }) {
|
|
4889
|
+
if (yDownloadedRealms &&
|
|
4890
|
+
realms &&
|
|
4891
|
+
realms.every((realmId) => yDownloadedRealms[realmId] === '*')) {
|
|
4859
4892
|
return; // Already done!
|
|
4860
4893
|
}
|
|
4861
4894
|
console.debug('Downloading Y.Docs from added realms');
|
|
@@ -4895,16 +4928,19 @@ function downloadYDocsFromServer(db, databaseUrl, { yDownloadedRealms, realms })
|
|
|
4895
4928
|
yield yTable.bulkAdd(docsToInsert);
|
|
4896
4929
|
docsToInsert = [];
|
|
4897
4930
|
}
|
|
4898
|
-
if (currentRealmId &&
|
|
4899
|
-
|
|
4900
|
-
|
|
4901
|
-
|
|
4902
|
-
|
|
4931
|
+
if (currentRealmId &&
|
|
4932
|
+
((currentTable && currentProp && lastDoc) || completedRealm)) {
|
|
4933
|
+
yield db.$syncState.update('syncState', (syncState) => {
|
|
4934
|
+
const yDownloadedRealms = syncState.yDownloadedRealms || {};
|
|
4935
|
+
yDownloadedRealms[currentRealmId] = completedRealm
|
|
4936
|
+
? '*'
|
|
4937
|
+
: {
|
|
4903
4938
|
tbl: currentTable,
|
|
4904
4939
|
prop: currentProp,
|
|
4905
4940
|
key: lastDoc.k,
|
|
4906
|
-
}
|
|
4907
|
-
|
|
4941
|
+
};
|
|
4942
|
+
syncState.yDownloadedRealms = yDownloadedRealms;
|
|
4943
|
+
});
|
|
4908
4944
|
}
|
|
4909
4945
|
});
|
|
4910
4946
|
}
|
|
@@ -5007,11 +5043,11 @@ function sync(db, options, schema, syncOptions) {
|
|
|
5007
5043
|
return Promise.reject(error);
|
|
5008
5044
|
}));
|
|
5009
5045
|
}
|
|
5010
|
-
function _sync(
|
|
5011
|
-
isInitialSync
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5046
|
+
function _sync(db_1, options_1, schema_1) {
|
|
5047
|
+
return __awaiter(this, arguments, void 0, function* (db, options, schema, { isInitialSync, cancelToken, justCheckIfNeeded, purpose } = {
|
|
5048
|
+
isInitialSync: false,
|
|
5049
|
+
}) {
|
|
5050
|
+
var _a;
|
|
5015
5051
|
if (!justCheckIfNeeded) {
|
|
5016
5052
|
console.debug('SYNC STARTED', { isInitialSync, purpose });
|
|
5017
5053
|
}
|
|
@@ -5053,8 +5089,8 @@ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNee
|
|
|
5053
5089
|
const [clientChangeSet, syncState, baseRevs, { yMessages, lastUpdateIds }] = yield db.transaction('r', db.tables, () => __awaiter(this, void 0, void 0, function* () {
|
|
5054
5090
|
const syncState = yield db.getPersistedSyncState();
|
|
5055
5091
|
const baseRevs = yield db.$baseRevs.toArray();
|
|
5056
|
-
let clientChanges = yield listClientChanges(mutationTables);
|
|
5057
|
-
const yResults = yield listYClientMessagesAndStateVector(db);
|
|
5092
|
+
let clientChanges = yield listClientChanges(mutationTables, db);
|
|
5093
|
+
const yResults = yield listYClientMessagesAndStateVector(db, tablesToSync);
|
|
5058
5094
|
throwIfCancelled(cancelToken);
|
|
5059
5095
|
if (doSyncify) {
|
|
5060
5096
|
const alreadySyncedRealms = [
|
|
@@ -5322,8 +5358,8 @@ function MessagesFromServerConsumer(db) {
|
|
|
5322
5358
|
event.next(null);
|
|
5323
5359
|
}
|
|
5324
5360
|
function consumeQueue() {
|
|
5325
|
-
var _a, _b, _c, _d, _e, _f;
|
|
5326
5361
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5362
|
+
var _a, _b, _c, _d, _e, _f;
|
|
5327
5363
|
while (queue.length > 0) {
|
|
5328
5364
|
const msg = queue.shift();
|
|
5329
5365
|
try {
|
|
@@ -5638,8 +5674,8 @@ function logout(db) {
|
|
|
5638
5674
|
}
|
|
5639
5675
|
});
|
|
5640
5676
|
}
|
|
5641
|
-
function _logout(
|
|
5642
|
-
return __awaiter(this,
|
|
5677
|
+
function _logout(db_1) {
|
|
5678
|
+
return __awaiter(this, arguments, void 0, function* (db, { deleteUnsyncedData = false } = {}) {
|
|
5643
5679
|
// Clear the database without emptying configuration options.
|
|
5644
5680
|
const [numUnsynced, loggedOut] = yield db.dx.transaction('rw', db.dx.tables, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
5645
5681
|
// @ts-ignore
|
|
@@ -5677,11 +5713,11 @@ function _logout(db, { deleteUnsyncedData = false } = {}) {
|
|
|
5677
5713
|
|
|
5678
5714
|
function otpFetchTokenCallback(db) {
|
|
5679
5715
|
const { userInteraction } = db.cloud;
|
|
5680
|
-
return function otpAuthenticate(
|
|
5681
|
-
|
|
5682
|
-
|
|
5716
|
+
return function otpAuthenticate(_a) {
|
|
5717
|
+
return __awaiter(this, arguments, void 0, function* ({ public_key, hints }) {
|
|
5718
|
+
var _b;
|
|
5683
5719
|
let tokenRequest;
|
|
5684
|
-
const url = (
|
|
5720
|
+
const url = (_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.databaseUrl;
|
|
5685
5721
|
if (!url)
|
|
5686
5722
|
throw new Error(`No database URL given.`);
|
|
5687
5723
|
if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'demo') {
|
|
@@ -5847,8 +5883,8 @@ function setCurrentUser(db, user) {
|
|
|
5847
5883
|
}
|
|
5848
5884
|
|
|
5849
5885
|
function login(db, hints) {
|
|
5850
|
-
var _a;
|
|
5851
5886
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5887
|
+
var _a;
|
|
5852
5888
|
const currentUser = yield db.getCurrentUser();
|
|
5853
5889
|
const origUserId = currentUser.userId;
|
|
5854
5890
|
if (currentUser.isLoggedIn && (!hints || (!hints.email && !hints.userId))) {
|
|
@@ -6285,8 +6321,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
6285
6321
|
outstandingTransactions.next(outstandingTransactions.value);
|
|
6286
6322
|
};
|
|
6287
6323
|
const txComplete = () => {
|
|
6288
|
-
if (tx.mutationsAdded &&
|
|
6289
|
-
!isEagerSyncDisabled(db)) {
|
|
6324
|
+
if (tx.mutationsAdded && !isEagerSyncDisabled(db)) {
|
|
6290
6325
|
triggerSync(db, 'push');
|
|
6291
6326
|
}
|
|
6292
6327
|
removeTransaction();
|
|
@@ -6368,26 +6403,107 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
6368
6403
|
: mutateAndLog(req);
|
|
6369
6404
|
} }));
|
|
6370
6405
|
function mutateAndLog(req) {
|
|
6406
|
+
var _a, _b;
|
|
6371
6407
|
const trans = req.trans;
|
|
6372
|
-
|
|
6408
|
+
const unsyncedProps = (_b = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.unsyncedProperties) === null || _b === void 0 ? void 0 : _b[tableName];
|
|
6373
6409
|
const { txid, currentUser: { userId }, } = trans;
|
|
6374
6410
|
const { type } = req;
|
|
6375
6411
|
const opNo = ++trans.opCount;
|
|
6412
|
+
function stripChangeSpec(changeSpec) {
|
|
6413
|
+
if (!unsyncedProps)
|
|
6414
|
+
return changeSpec;
|
|
6415
|
+
let rv = changeSpec;
|
|
6416
|
+
for (const keyPath of Object.keys(changeSpec)) {
|
|
6417
|
+
if (unsyncedProps.some((p) => keyPath === p || keyPath.startsWith(p + '.'))) {
|
|
6418
|
+
if (rv === changeSpec)
|
|
6419
|
+
rv = Object.assign({}, changeSpec); // clone on demand
|
|
6420
|
+
delete rv[keyPath];
|
|
6421
|
+
}
|
|
6422
|
+
}
|
|
6423
|
+
return rv;
|
|
6424
|
+
}
|
|
6376
6425
|
return table.mutate(req).then((res) => {
|
|
6426
|
+
var _a;
|
|
6377
6427
|
const { numFailures: hasFailures, failures } = res;
|
|
6378
6428
|
let keys = type === 'delete' ? req.keys : res.results;
|
|
6379
6429
|
let values = 'values' in req ? req.values : [];
|
|
6380
|
-
let
|
|
6430
|
+
let changeSpec = 'changeSpec' in req ? req.changeSpec : undefined;
|
|
6431
|
+
let updates = 'updates' in req ? req.updates : undefined;
|
|
6381
6432
|
if (hasFailures) {
|
|
6382
6433
|
keys = keys.filter((_, idx) => !failures[idx]);
|
|
6383
6434
|
values = values.filter((_, idx) => !failures[idx]);
|
|
6384
6435
|
}
|
|
6436
|
+
if (unsyncedProps) {
|
|
6437
|
+
// Filter out unsynced properties
|
|
6438
|
+
values = values.map((value) => {
|
|
6439
|
+
const newValue = Object.assign({}, value);
|
|
6440
|
+
for (const prop of unsyncedProps) {
|
|
6441
|
+
delete newValue[prop];
|
|
6442
|
+
}
|
|
6443
|
+
return newValue;
|
|
6444
|
+
});
|
|
6445
|
+
if (changeSpec) {
|
|
6446
|
+
// modify operation with criteria and changeSpec.
|
|
6447
|
+
// We must strip out unsynced properties from changeSpec.
|
|
6448
|
+
// We deal with criteria later.
|
|
6449
|
+
changeSpec = stripChangeSpec(changeSpec);
|
|
6450
|
+
if (Object.keys(changeSpec).length === 0) {
|
|
6451
|
+
// Nothing to change on server
|
|
6452
|
+
return res;
|
|
6453
|
+
}
|
|
6454
|
+
}
|
|
6455
|
+
if (updates) {
|
|
6456
|
+
let strippedChangeSpecs = updates.changeSpecs.map(stripChangeSpec);
|
|
6457
|
+
let newUpdates = {
|
|
6458
|
+
keys: [],
|
|
6459
|
+
changeSpecs: [],
|
|
6460
|
+
};
|
|
6461
|
+
const validKeys = new RangeSet();
|
|
6462
|
+
let anyChangeSpecBecameEmpty = false;
|
|
6463
|
+
for (let i = 0, l = strippedChangeSpecs.length; i < l; ++i) {
|
|
6464
|
+
if (Object.keys(strippedChangeSpecs[i]).length > 0) {
|
|
6465
|
+
newUpdates.keys.push(updates.keys[i]);
|
|
6466
|
+
newUpdates.changeSpecs.push(strippedChangeSpecs[i]);
|
|
6467
|
+
validKeys.addKey(updates.keys[i]);
|
|
6468
|
+
}
|
|
6469
|
+
else {
|
|
6470
|
+
anyChangeSpecBecameEmpty = true;
|
|
6471
|
+
}
|
|
6472
|
+
}
|
|
6473
|
+
updates = newUpdates;
|
|
6474
|
+
if (anyChangeSpecBecameEmpty) {
|
|
6475
|
+
// Some keys were stripped. We must also strip them from keys and values
|
|
6476
|
+
let newKeys = [];
|
|
6477
|
+
let newValues = [];
|
|
6478
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
6479
|
+
if (validKeys.hasKey(keys[i])) {
|
|
6480
|
+
newKeys.push(keys[i]);
|
|
6481
|
+
newValues.push(values[i]);
|
|
6482
|
+
}
|
|
6483
|
+
}
|
|
6484
|
+
keys = newKeys;
|
|
6485
|
+
values = newValues;
|
|
6486
|
+
}
|
|
6487
|
+
}
|
|
6488
|
+
}
|
|
6385
6489
|
const ts = Date.now();
|
|
6386
6490
|
// Canonicalize req.criteria.index to null if it's on the primary key.
|
|
6387
|
-
|
|
6491
|
+
let criteria = 'criteria' in req && req.criteria
|
|
6388
6492
|
? Object.assign(Object.assign({}, req.criteria), { index: req.criteria.index === schema.primaryKey.keyPath // Use null to inform server that criteria is on primary key
|
|
6389
6493
|
? null // This will disable the server from trying to log consistent operations where it shouldnt.
|
|
6390
6494
|
: req.criteria.index }) : undefined;
|
|
6495
|
+
if (unsyncedProps && (criteria === null || criteria === void 0 ? void 0 : criteria.index)) {
|
|
6496
|
+
const keyPaths = (_a = schema.indexes.find((idx) => idx.name === criteria.index)) === null || _a === void 0 ? void 0 : _a.keyPath;
|
|
6497
|
+
const involvedProps = keyPaths
|
|
6498
|
+
? typeof keyPaths === 'string'
|
|
6499
|
+
? [keyPaths]
|
|
6500
|
+
: keyPaths
|
|
6501
|
+
: [];
|
|
6502
|
+
if (involvedProps.some((p) => unsyncedProps === null || unsyncedProps === void 0 ? void 0 : unsyncedProps.includes(p))) {
|
|
6503
|
+
// Don't log criteria on unsynced properties as the server could not test them.
|
|
6504
|
+
criteria = undefined;
|
|
6505
|
+
}
|
|
6506
|
+
}
|
|
6391
6507
|
const mut = req.type === 'delete'
|
|
6392
6508
|
? {
|
|
6393
6509
|
type: 'delete',
|
|
@@ -6408,7 +6524,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
6408
6524
|
userId,
|
|
6409
6525
|
values,
|
|
6410
6526
|
}
|
|
6411
|
-
: criteria &&
|
|
6527
|
+
: criteria && changeSpec
|
|
6412
6528
|
? {
|
|
6413
6529
|
// Common changeSpec for all keys
|
|
6414
6530
|
type: 'modify',
|
|
@@ -6416,37 +6532,51 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
6416
6532
|
opNo,
|
|
6417
6533
|
keys,
|
|
6418
6534
|
criteria,
|
|
6419
|
-
changeSpec
|
|
6535
|
+
changeSpec,
|
|
6420
6536
|
txid,
|
|
6421
6537
|
userId,
|
|
6422
6538
|
}
|
|
6423
|
-
:
|
|
6539
|
+
: changeSpec
|
|
6424
6540
|
? {
|
|
6425
|
-
//
|
|
6541
|
+
// In case criteria involved an unsynced property, we go for keys instead.
|
|
6426
6542
|
type: 'update',
|
|
6427
6543
|
ts,
|
|
6428
6544
|
opNo,
|
|
6429
|
-
keys: updates.keys,
|
|
6430
|
-
changeSpecs: updates.changeSpecs,
|
|
6431
|
-
txid,
|
|
6432
|
-
userId,
|
|
6433
|
-
}
|
|
6434
|
-
: {
|
|
6435
|
-
type: 'upsert',
|
|
6436
|
-
ts,
|
|
6437
|
-
opNo,
|
|
6438
6545
|
keys,
|
|
6439
|
-
|
|
6546
|
+
changeSpecs: keys.map(() => changeSpec),
|
|
6440
6547
|
txid,
|
|
6441
6548
|
userId,
|
|
6442
|
-
}
|
|
6549
|
+
}
|
|
6550
|
+
: updates
|
|
6551
|
+
? {
|
|
6552
|
+
// One changeSpec per key
|
|
6553
|
+
type: 'update',
|
|
6554
|
+
ts,
|
|
6555
|
+
opNo,
|
|
6556
|
+
keys: updates.keys,
|
|
6557
|
+
changeSpecs: updates.changeSpecs,
|
|
6558
|
+
txid,
|
|
6559
|
+
userId,
|
|
6560
|
+
}
|
|
6561
|
+
: {
|
|
6562
|
+
type: 'upsert',
|
|
6563
|
+
ts,
|
|
6564
|
+
opNo,
|
|
6565
|
+
keys,
|
|
6566
|
+
values,
|
|
6567
|
+
txid,
|
|
6568
|
+
userId,
|
|
6569
|
+
};
|
|
6443
6570
|
if ('isAdditionalChunk' in req && req.isAdditionalChunk) {
|
|
6444
6571
|
mut.isAdditionalChunk = true;
|
|
6445
6572
|
}
|
|
6446
6573
|
return keys.length > 0 || criteria
|
|
6447
6574
|
? mutsTable
|
|
6448
6575
|
.mutate({ type: 'add', trans, values: [mut] }) // Log entry
|
|
6449
|
-
.then(() =>
|
|
6576
|
+
.then(() => {
|
|
6577
|
+
trans.mutationsAdded = true; // Mark transaction as having added mutations to trigger eager sync
|
|
6578
|
+
return res; // Return original response
|
|
6579
|
+
})
|
|
6450
6580
|
: res;
|
|
6451
6581
|
});
|
|
6452
6582
|
}
|
|
@@ -6613,38 +6743,51 @@ class TokenExpiredError extends Error {
|
|
|
6613
6743
|
|
|
6614
6744
|
function createYClientUpdateObservable(db) {
|
|
6615
6745
|
const yTableRecords = flatten(db.tables
|
|
6616
|
-
.filter((table) => { var _a; return ((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[table.name].markedForSync) && table.schema.yProps; })
|
|
6746
|
+
.filter((table) => { var _a, _b; return ((_b = (_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[table.name]) === null || _b === void 0 ? void 0 : _b.markedForSync) && table.schema.yProps; })
|
|
6617
6747
|
.map((table) => table.schema.yProps.map((p) => ({
|
|
6618
6748
|
table: table.name,
|
|
6619
6749
|
ydocProp: p.prop,
|
|
6620
6750
|
updatesTable: p.updatesTable,
|
|
6621
6751
|
}))));
|
|
6622
6752
|
return merge(...yTableRecords.map(({ table, ydocProp, updatesTable }) => {
|
|
6623
|
-
|
|
6624
|
-
|
|
6625
|
-
|
|
6626
|
-
|
|
6627
|
-
|
|
6628
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
|
|
6632
|
-
|
|
6633
|
-
|
|
6634
|
-
|
|
6635
|
-
|
|
6636
|
-
|
|
6637
|
-
|
|
6638
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
6641
|
-
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
6645
|
-
|
|
6753
|
+
// Per updates table (table+prop combo), we first read syncer.unsentFrom,
|
|
6754
|
+
// and then start listening for updates since that number.
|
|
6755
|
+
const yTbl = db.table(updatesTable);
|
|
6756
|
+
return from$1(yTbl.get(DEXIE_CLOUD_SYNCER_ID)).pipe(switchMap$1((syncer) => {
|
|
6757
|
+
let currentUnsentFrom = (syncer === null || syncer === void 0 ? void 0 : syncer.unsentFrom) || 1;
|
|
6758
|
+
return from$1(liveQuery(() => __awaiter(this, void 0, void 0, function* () {
|
|
6759
|
+
const addedUpdates = yield listUpdatesSince(yTbl, currentUnsentFrom);
|
|
6760
|
+
return addedUpdates
|
|
6761
|
+
.filter((update) => update.f && update.f & 1) // Only include local updates
|
|
6762
|
+
.map((update) => {
|
|
6763
|
+
return {
|
|
6764
|
+
type: 'u-c',
|
|
6765
|
+
table,
|
|
6766
|
+
prop: ydocProp,
|
|
6767
|
+
k: update.k,
|
|
6768
|
+
u: update.u,
|
|
6769
|
+
i: update.i,
|
|
6770
|
+
};
|
|
6771
|
+
});
|
|
6772
|
+
}))).pipe(tap$1((addedUpdates) => {
|
|
6773
|
+
// Update currentUnsentFrom to only listen for updates that will be newer than the ones we emitted.
|
|
6774
|
+
// (Before, we did this within the liveQuery, but that caused a bug because
|
|
6775
|
+
// a cancelled emittion of a liveQuery would update the currentUnsentFrom without
|
|
6776
|
+
// emitting anything, leading to that we jumped over some updates. Here we update it
|
|
6777
|
+
// after the liveQuery has emitted its updates)
|
|
6778
|
+
if (addedUpdates.length > 0) {
|
|
6779
|
+
currentUnsentFrom = addedUpdates.at(-1).i + 1;
|
|
6780
|
+
}
|
|
6781
|
+
}));
|
|
6646
6782
|
}));
|
|
6647
|
-
})).pipe(
|
|
6783
|
+
})).pipe(
|
|
6784
|
+
// Flatten the array of messages.
|
|
6785
|
+
// If messageProducer emits empty array, nothing is emitted
|
|
6786
|
+
// but if messageProducer emits array of messages, they are
|
|
6787
|
+
// emitted one by one.
|
|
6788
|
+
mergeMap$1((messages) => messages), tap$1((message) => {
|
|
6789
|
+
console.debug('dexie-cloud emitting y-c', message);
|
|
6790
|
+
}));
|
|
6648
6791
|
}
|
|
6649
6792
|
|
|
6650
6793
|
function getAwarenessLibrary(db) {
|
|
@@ -6661,25 +6804,24 @@ const SERVER_PING_TIMEOUT = 20000;
|
|
|
6661
6804
|
const CLIENT_PING_INTERVAL = 30000;
|
|
6662
6805
|
const FAIL_RETRY_WAIT_TIME = 60000;
|
|
6663
6806
|
class WSObservable extends Observable$1 {
|
|
6664
|
-
constructor(db, rev, realmSetHash, clientIdentity, messageProducer, webSocketStatus,
|
|
6665
|
-
super((subscriber) => new WSConnection(db, rev, realmSetHash, clientIdentity,
|
|
6807
|
+
constructor(db, rev, realmSetHash, clientIdentity, messageProducer, webSocketStatus, user) {
|
|
6808
|
+
super((subscriber) => new WSConnection(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus));
|
|
6666
6809
|
}
|
|
6667
6810
|
}
|
|
6668
6811
|
let counter = 0;
|
|
6669
6812
|
class WSConnection extends Subscription$1 {
|
|
6670
|
-
constructor(db, rev, realmSetHash, clientIdentity,
|
|
6813
|
+
constructor(db, rev, realmSetHash, clientIdentity, user, subscriber, messageProducer, webSocketStatus) {
|
|
6671
6814
|
super(() => this.teardown());
|
|
6672
6815
|
this.id = ++counter;
|
|
6673
6816
|
this.subscriptions = new Set();
|
|
6674
6817
|
this.reconnecting = false;
|
|
6675
|
-
console.debug('New WebSocket Connection', this.id,
|
|
6818
|
+
console.debug('New WebSocket Connection', this.id, user.accessToken ? 'authorized' : 'unauthorized');
|
|
6676
6819
|
this.db = db;
|
|
6677
6820
|
this.databaseUrl = db.cloud.options.databaseUrl;
|
|
6678
6821
|
this.rev = rev;
|
|
6679
6822
|
this.realmSetHash = realmSetHash;
|
|
6680
6823
|
this.clientIdentity = clientIdentity;
|
|
6681
|
-
this.
|
|
6682
|
-
this.tokenExpiration = tokenExpiration;
|
|
6824
|
+
this.user = user;
|
|
6683
6825
|
this.subscriber = subscriber;
|
|
6684
6826
|
this.lastUserActivity = new Date();
|
|
6685
6827
|
this.messageProducer = messageProducer;
|
|
@@ -6739,7 +6881,8 @@ class WSConnection extends Subscription$1 {
|
|
|
6739
6881
|
//console.debug('SyncStatus: DUBB: Ooops it was closed!');
|
|
6740
6882
|
return;
|
|
6741
6883
|
}
|
|
6742
|
-
|
|
6884
|
+
const tokenExpiration = this.user.accessTokenExpiration;
|
|
6885
|
+
if (tokenExpiration && tokenExpiration < new Date()) {
|
|
6743
6886
|
this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
|
|
6744
6887
|
return;
|
|
6745
6888
|
}
|
|
@@ -6794,8 +6937,8 @@ class WSConnection extends Subscription$1 {
|
|
|
6794
6937
|
searchParams.set('rev', this.rev);
|
|
6795
6938
|
searchParams.set('realmsHash', this.realmSetHash);
|
|
6796
6939
|
searchParams.set('clientId', this.clientIdentity);
|
|
6797
|
-
if (this.
|
|
6798
|
-
searchParams.set('token', this.
|
|
6940
|
+
if (this.user.accessToken) {
|
|
6941
|
+
searchParams.set('token', this.user.accessToken);
|
|
6799
6942
|
}
|
|
6800
6943
|
// Connect the WebSocket to given url:
|
|
6801
6944
|
console.debug('dexie-cloud WebSocket create');
|
|
@@ -6810,12 +6953,12 @@ class WSConnection extends Subscription$1 {
|
|
|
6810
6953
|
ws.onmessage = (event) => {
|
|
6811
6954
|
if (!this.pinger)
|
|
6812
6955
|
return;
|
|
6813
|
-
console.debug('dexie-cloud WebSocket onmessage', event.data);
|
|
6814
6956
|
this.lastServerActivity = new Date();
|
|
6815
6957
|
try {
|
|
6816
6958
|
const msg = typeof event.data === 'string'
|
|
6817
6959
|
? TSON.parse(event.data)
|
|
6818
6960
|
: decodeYMessage(new Uint8Array(event.data));
|
|
6961
|
+
console.debug('dexie-cloud WebSocket onmessage', msg.type, msg);
|
|
6819
6962
|
if (msg.type === 'error') {
|
|
6820
6963
|
throw new Error(`Error message from dexie-cloud: ${msg.error}`);
|
|
6821
6964
|
}
|
|
@@ -6871,6 +7014,7 @@ class WSConnection extends Subscription$1 {
|
|
|
6871
7014
|
this.webSocketStatus.value !== 'connected') {
|
|
6872
7015
|
this.webSocketStatus.next('connected');
|
|
6873
7016
|
}
|
|
7017
|
+
console.debug('dexie-cloud WebSocket send', msg.type, msg);
|
|
6874
7018
|
if (msg.type === 'ready') {
|
|
6875
7019
|
(_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(TSON.stringify(msg));
|
|
6876
7020
|
}
|
|
@@ -6881,7 +7025,9 @@ class WSConnection extends Subscription$1 {
|
|
|
6881
7025
|
}
|
|
6882
7026
|
}
|
|
6883
7027
|
}));
|
|
6884
|
-
|
|
7028
|
+
if (this.user.isLoggedIn && !isEagerSyncDisabled(this.db)) {
|
|
7029
|
+
this.subscriptions.add(createYClientUpdateObservable(this.db).subscribe(this.db.messageProducer));
|
|
7030
|
+
}
|
|
6885
7031
|
}
|
|
6886
7032
|
catch (error) {
|
|
6887
7033
|
this.pauseUntil = new Date(Date.now() + FAIL_RETRY_WAIT_TIME);
|
|
@@ -6951,7 +7097,7 @@ function connectWebSocket(db) {
|
|
|
6951
7097
|
return db.cloud.persistedSyncState.pipe(filter((syncState) => (syncState === null || syncState === void 0 ? void 0 : syncState.realms.includes(userLogin.userId)) || false), take(1), map((syncState) => [userLogin, syncState]));
|
|
6952
7098
|
}
|
|
6953
7099
|
return new BehaviorSubject([userLogin, syncState]);
|
|
6954
|
-
}), switchMap((
|
|
7100
|
+
}), switchMap((_a) => __awaiter(this, [_a], void 0, function* ([userLogin, syncState]) { return [userLogin, yield computeRealmSetHash(syncState)]; })), distinctUntilChanged(([prevUser, prevHash], [currUser, currHash]) => prevUser === currUser && prevHash === currHash), switchMap(([userLogin, realmSetHash]) => {
|
|
6955
7101
|
var _a;
|
|
6956
7102
|
if (!((_a = db.cloud.persistedSyncState) === null || _a === void 0 ? void 0 : _a.value)) {
|
|
6957
7103
|
// Restart the flow if persistedSyncState is not yet available.
|
|
@@ -6961,7 +7107,7 @@ function connectWebSocket(db) {
|
|
|
6961
7107
|
// If no new entries, server won't bother the client. If new entries, server sends only those
|
|
6962
7108
|
// and the baseRev of the last from same client-ID.
|
|
6963
7109
|
if (userLogin) {
|
|
6964
|
-
return new WSObservable(db, db.cloud.persistedSyncState.value.serverRevision, realmSetHash, db.cloud.persistedSyncState.value.clientIdentity, messageProducer, db.cloud.webSocketStatus, userLogin
|
|
7110
|
+
return new WSObservable(db, db.cloud.persistedSyncState.value.serverRevision, realmSetHash, db.cloud.persistedSyncState.value.clientIdentity, messageProducer, db.cloud.webSocketStatus, userLogin);
|
|
6965
7111
|
}
|
|
6966
7112
|
else {
|
|
6967
7113
|
return from$1([]);
|
|
@@ -7012,8 +7158,8 @@ function connectWebSocket(db) {
|
|
|
7012
7158
|
}
|
|
7013
7159
|
|
|
7014
7160
|
function isSyncNeeded(db) {
|
|
7015
|
-
var _a;
|
|
7016
7161
|
return __awaiter(this, void 0, void 0, function* () {
|
|
7162
|
+
var _a;
|
|
7017
7163
|
return ((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl) && db.cloud.schema
|
|
7018
7164
|
? yield sync(db, db.cloud.options, db.cloud.schema, { justCheckIfNeeded: true })
|
|
7019
7165
|
: false;
|
|
@@ -7697,6 +7843,7 @@ class PermissionChecker {
|
|
|
7697
7843
|
// If user can update any prop in any table in this realm, return true unless
|
|
7698
7844
|
// it regards to ownership change:
|
|
7699
7845
|
if (this.permissions.update === '*') {
|
|
7846
|
+
// @ts-ignore
|
|
7700
7847
|
return props.every((prop) => prop !== 'owner');
|
|
7701
7848
|
}
|
|
7702
7849
|
const tablePermissions = (_b = this.permissions.update) === null || _b === void 0 ? void 0 : _b[this.tableName];
|
|
@@ -7768,125 +7915,139 @@ const getInvitesObservable = associate((db) => {
|
|
|
7768
7915
|
});
|
|
7769
7916
|
|
|
7770
7917
|
function createYHandler(db) {
|
|
7771
|
-
const awap = getAwarenessLibrary(db);
|
|
7772
7918
|
return (provider) => {
|
|
7773
7919
|
var _a;
|
|
7774
7920
|
const doc = provider.doc;
|
|
7775
|
-
const { parentTable
|
|
7921
|
+
const { parentTable } = doc.meta || {};
|
|
7776
7922
|
if (!((_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[parentTable].markedForSync)) {
|
|
7777
7923
|
return; // The table that holds the doc is not marked for sync - leave it to dexie. No syncing, no awareness.
|
|
7778
7924
|
}
|
|
7779
|
-
let awareness
|
|
7780
|
-
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7925
|
+
let awareness;
|
|
7926
|
+
Object.defineProperty(provider, "awareness", {
|
|
7927
|
+
get() {
|
|
7928
|
+
if (awareness)
|
|
7929
|
+
return awareness;
|
|
7930
|
+
awareness = createAwareness(db, doc, provider);
|
|
7931
|
+
awarenessWeakMap.set(doc, awareness);
|
|
7932
|
+
return awareness;
|
|
7933
|
+
}
|
|
7934
|
+
});
|
|
7935
|
+
};
|
|
7936
|
+
}
|
|
7937
|
+
function createAwareness(db, doc, provider) {
|
|
7938
|
+
const { parentTable, parentId, parentProp, updatesTable } = doc.meta;
|
|
7939
|
+
const awap = getAwarenessLibrary(db);
|
|
7940
|
+
const awareness = new awap.Awareness(doc);
|
|
7941
|
+
awareness.on('update', ({ added, updated, removed }, origin) => {
|
|
7942
|
+
// Send the update
|
|
7943
|
+
const changedClients = added.concat(updated).concat(removed);
|
|
7944
|
+
const user = db.cloud.currentUser.value;
|
|
7945
|
+
if (origin !== 'server' && user.isLoggedIn && !isEagerSyncDisabled(db)) {
|
|
7946
|
+
const update = awap.encodeAwarenessUpdate(awareness, changedClients);
|
|
7947
|
+
db.messageProducer.next({
|
|
7948
|
+
type: 'aware',
|
|
7949
|
+
table: parentTable,
|
|
7950
|
+
prop: parentProp,
|
|
7951
|
+
k: doc.meta.parentId,
|
|
7952
|
+
u: update,
|
|
7953
|
+
});
|
|
7954
|
+
if (provider.destroyed) {
|
|
7955
|
+
// We're called from awareness.on('destroy') that did
|
|
7956
|
+
// removeAwarenessStates.
|
|
7957
|
+
// It's time to also send the doc-close message that dexie-cloud understands
|
|
7958
|
+
// and uses to stop subscribing for updates and awareness updates and brings
|
|
7959
|
+
// down the cached information in memory on the WS connection for this.
|
|
7787
7960
|
db.messageProducer.next({
|
|
7788
|
-
type: '
|
|
7961
|
+
type: 'doc-close',
|
|
7789
7962
|
table: parentTable,
|
|
7790
7963
|
prop: parentProp,
|
|
7791
|
-
k: doc.meta.parentId
|
|
7792
|
-
u: update,
|
|
7964
|
+
k: doc.meta.parentId
|
|
7793
7965
|
});
|
|
7794
|
-
if (provider.destroyed) {
|
|
7795
|
-
// We're called from awareness.on('destroy') that did
|
|
7796
|
-
// removeAwarenessStates.
|
|
7797
|
-
// It's time to also send the doc-close message that dexie-cloud understands
|
|
7798
|
-
// and uses to stop subscribing for updates and awareness updates and brings
|
|
7799
|
-
// down the cached information in memory on the WS connection for this.
|
|
7800
|
-
db.messageProducer.next({
|
|
7801
|
-
type: 'doc-close',
|
|
7802
|
-
table: parentTable,
|
|
7803
|
-
prop: parentProp,
|
|
7804
|
-
k: doc.meta.parentId
|
|
7805
|
-
});
|
|
7806
|
-
}
|
|
7807
7966
|
}
|
|
7808
|
-
}
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7967
|
+
}
|
|
7968
|
+
});
|
|
7969
|
+
awareness.on('destroy', () => {
|
|
7970
|
+
// Signal to server that this provider is destroyed (the update event will be triggered, which
|
|
7971
|
+
// in turn will trigger db.messageProducer that will send the message to the server if WS is connected)
|
|
7972
|
+
awap.removeAwarenessStates(awareness, [doc.clientID], 'provider destroyed');
|
|
7973
|
+
});
|
|
7974
|
+
// Open the document on the server
|
|
7975
|
+
(() => __awaiter(this, void 0, void 0, function* () {
|
|
7976
|
+
if (provider.destroyed)
|
|
7977
|
+
return;
|
|
7978
|
+
let connected = false;
|
|
7979
|
+
let currentFlowId = 1;
|
|
7980
|
+
const subscription = db.cloud.webSocketStatus.subscribe((wsStatus) => {
|
|
7816
7981
|
if (provider.destroyed)
|
|
7817
7982
|
return;
|
|
7818
|
-
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7983
|
+
// Keep "connected" state in a variable so we can check it after async operations
|
|
7984
|
+
connected = wsStatus === 'connected';
|
|
7985
|
+
// We are or got connected. Open the document on the server.
|
|
7986
|
+
const user = db.cloud.currentUser.value;
|
|
7987
|
+
if (wsStatus === "connected" && user.isLoggedIn && !isEagerSyncDisabled(db)) {
|
|
7988
|
+
++currentFlowId;
|
|
7989
|
+
openDocumentOnServer().catch(error => {
|
|
7990
|
+
console.warn(`Error catched in createYHandler.ts: ${error}`);
|
|
7991
|
+
});
|
|
7992
|
+
}
|
|
7993
|
+
});
|
|
7994
|
+
// Wait until WebSocket is connected
|
|
7995
|
+
provider.addCleanupHandler(subscription);
|
|
7996
|
+
/** Sends an 'doc-open' message to server whenever websocket becomes
|
|
7997
|
+
* connected, or if it is already connected.
|
|
7998
|
+
* The flow is aborted in case websocket is disconnected while querying
|
|
7999
|
+
* information required to compute the state vector. Flow is also
|
|
8000
|
+
* aborted in case document or provider has been destroyed during
|
|
8001
|
+
* the async parts of the task.
|
|
8002
|
+
*
|
|
8003
|
+
* The state vector is only computed from the updates that have occured
|
|
8004
|
+
* after the last full sync - which could very often be zero - in which
|
|
8005
|
+
* case no state vector is sent (then the server already knows us by
|
|
8006
|
+
* revision)
|
|
8007
|
+
*
|
|
8008
|
+
* When server gets the doc-open message, it will authorized us for
|
|
8009
|
+
* whether we are allowed to read / write to this document, and then
|
|
8010
|
+
* keep the cached information in memory on the WS connection for this
|
|
8011
|
+
* particular document, as well as subscribe to updates and awareness updates
|
|
8012
|
+
* from other clients on the document.
|
|
8013
|
+
*/
|
|
8014
|
+
function openDocumentOnServer(wsStatus) {
|
|
8015
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
8016
|
+
const myFlow = currentFlowId; // So we can abort when a new flow is started
|
|
8017
|
+
const yTbl = db.table(updatesTable);
|
|
8018
|
+
const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
|
|
8019
|
+
// After every await, check if we still should be working on this task.
|
|
8020
|
+
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7822
8021
|
return;
|
|
7823
|
-
|
|
7824
|
-
|
|
7825
|
-
|
|
7826
|
-
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
|
|
8022
|
+
const receivedUntil = (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0;
|
|
8023
|
+
const docOpenMsg = {
|
|
8024
|
+
type: 'doc-open',
|
|
8025
|
+
table: parentTable,
|
|
8026
|
+
prop: parentProp,
|
|
8027
|
+
k: parentId,
|
|
8028
|
+
serverRev: syncState === null || syncState === void 0 ? void 0 : syncState.serverRev,
|
|
8029
|
+
};
|
|
8030
|
+
const serverUpdatesSinceLastSync = yield yTbl
|
|
8031
|
+
.where('i')
|
|
8032
|
+
.between(receivedUntil, Infinity, false)
|
|
8033
|
+
.filter((update) => cmp(update.k, parentId) === 0 && // Only updates for this document
|
|
8034
|
+
((update.f || 0) & 1) === 0 // Don't include local changes
|
|
8035
|
+
)
|
|
8036
|
+
.toArray();
|
|
8037
|
+
// After every await, check if we still should be working on this task.
|
|
8038
|
+
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
8039
|
+
return;
|
|
8040
|
+
if (serverUpdatesSinceLastSync.length > 0) {
|
|
8041
|
+
const Y = $Y(db); // Get the Yjs library from Dexie constructor options
|
|
8042
|
+
const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
|
|
8043
|
+
const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
|
|
8044
|
+
docOpenMsg.sv = stateVector;
|
|
7831
8045
|
}
|
|
8046
|
+
db.messageProducer.next(docOpenMsg);
|
|
7832
8047
|
});
|
|
7833
|
-
|
|
7834
|
-
|
|
7835
|
-
|
|
7836
|
-
* connected, or if it is already connected.
|
|
7837
|
-
* The flow is aborted in case websocket is disconnected while querying
|
|
7838
|
-
* information required to compute the state vector. Flow is also
|
|
7839
|
-
* aborted in case document or provider has been destroyed during
|
|
7840
|
-
* the async parts of the task.
|
|
7841
|
-
*
|
|
7842
|
-
* The state vector is only computed from the updates that have occured
|
|
7843
|
-
* after the last full sync - which could very often be zero - in which
|
|
7844
|
-
* case no state vector is sent (then the server already knows us by
|
|
7845
|
-
* revision)
|
|
7846
|
-
*
|
|
7847
|
-
* When server gets the doc-open message, it will authorized us for
|
|
7848
|
-
* whether we are allowed to read / write to this document, and then
|
|
7849
|
-
* keep the cached information in memory on the WS connection for this
|
|
7850
|
-
* particular document, as well as subscribe to updates and awareness updates
|
|
7851
|
-
* from other clients on the document.
|
|
7852
|
-
*/
|
|
7853
|
-
function openDocumentOnServer(wsStatus) {
|
|
7854
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
7855
|
-
const myFlow = currentFlowId; // So we can abort when a new flow is started
|
|
7856
|
-
const yTbl = db.table(updatesTable);
|
|
7857
|
-
const syncState = yield yTbl.get(DEXIE_CLOUD_SYNCER_ID);
|
|
7858
|
-
// After every await, check if we still should be working on this task.
|
|
7859
|
-
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7860
|
-
return;
|
|
7861
|
-
const receivedUntil = (syncState === null || syncState === void 0 ? void 0 : syncState.receivedUntil) || 0;
|
|
7862
|
-
const docOpenMsg = {
|
|
7863
|
-
type: 'doc-open',
|
|
7864
|
-
table: parentTable,
|
|
7865
|
-
prop: parentProp,
|
|
7866
|
-
k: parentId,
|
|
7867
|
-
serverRev: syncState === null || syncState === void 0 ? void 0 : syncState.serverRev,
|
|
7868
|
-
};
|
|
7869
|
-
const serverUpdatesSinceLastSync = yield yTbl
|
|
7870
|
-
.where('i')
|
|
7871
|
-
.between(receivedUntil, Infinity, false)
|
|
7872
|
-
.filter((update) => cmp(update.k, parentId) === 0 && // Only updates for this document
|
|
7873
|
-
((update.f || 0) & 1) === 0 // Don't include local changes
|
|
7874
|
-
)
|
|
7875
|
-
.toArray();
|
|
7876
|
-
// After every await, check if we still should be working on this task.
|
|
7877
|
-
if (provider.destroyed || currentFlowId !== myFlow || !connected)
|
|
7878
|
-
return;
|
|
7879
|
-
if (serverUpdatesSinceLastSync.length > 0) {
|
|
7880
|
-
const Y = $Y(db); // Get the Yjs library from Dexie constructor options
|
|
7881
|
-
const mergedUpdate = Y.mergeUpdatesV2(serverUpdatesSinceLastSync.map((update) => update.u));
|
|
7882
|
-
const stateVector = Y.encodeStateVectorFromUpdateV2(mergedUpdate);
|
|
7883
|
-
docOpenMsg.sv = stateVector;
|
|
7884
|
-
}
|
|
7885
|
-
db.messageProducer.next(docOpenMsg);
|
|
7886
|
-
});
|
|
7887
|
-
}
|
|
7888
|
-
}));
|
|
7889
|
-
};
|
|
8048
|
+
}
|
|
8049
|
+
}))();
|
|
8050
|
+
return awareness;
|
|
7890
8051
|
}
|
|
7891
8052
|
|
|
7892
8053
|
const DEFAULT_OPTIONS = {
|
|
@@ -7929,7 +8090,7 @@ function dexieCloud(dexie) {
|
|
|
7929
8090
|
const syncComplete = new Subject();
|
|
7930
8091
|
dexie.cloud = {
|
|
7931
8092
|
// @ts-ignore
|
|
7932
|
-
version: "4.1.0-alpha.
|
|
8093
|
+
version: "4.1.0-alpha.20",
|
|
7933
8094
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
7934
8095
|
schema: null,
|
|
7935
8096
|
get currentUserId() {
|
|
@@ -7965,16 +8126,16 @@ function dexieCloud(dexie) {
|
|
|
7965
8126
|
}
|
|
7966
8127
|
updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
|
|
7967
8128
|
},
|
|
7968
|
-
logout(
|
|
7969
|
-
return __awaiter(this,
|
|
8129
|
+
logout() {
|
|
8130
|
+
return __awaiter(this, arguments, void 0, function* ({ force } = {}) {
|
|
7970
8131
|
force
|
|
7971
8132
|
? yield _logout(DexieCloudDB(dexie), { deleteUnsyncedData: true })
|
|
7972
8133
|
: yield logout(DexieCloudDB(dexie));
|
|
7973
8134
|
});
|
|
7974
8135
|
},
|
|
7975
|
-
sync(
|
|
7976
|
-
|
|
7977
|
-
|
|
8136
|
+
sync() {
|
|
8137
|
+
return __awaiter(this, arguments, void 0, function* ({ wait, purpose } = { wait: true, purpose: 'push' }) {
|
|
8138
|
+
var _a;
|
|
7978
8139
|
if (wait === undefined)
|
|
7979
8140
|
wait = true;
|
|
7980
8141
|
const db = DexieCloudDB(dexie);
|
|
@@ -8032,8 +8193,8 @@ function dexieCloud(dexie) {
|
|
|
8032
8193
|
dexie.use(createImplicitPropSetterMiddleware(DexieCloudDB(dexie)));
|
|
8033
8194
|
dexie.use(createIdGenerationMiddleware(DexieCloudDB(dexie)));
|
|
8034
8195
|
function onDbReady(dexie) {
|
|
8035
|
-
var _a, _b, _c, _d, _e, _f, _g;
|
|
8036
8196
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8197
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
8037
8198
|
closed = false; // As Dexie calls us, we are not closed anymore. Maybe reopened? Remember db.ready event is registered with sticky flag!
|
|
8038
8199
|
const db = DexieCloudDB(dexie);
|
|
8039
8200
|
// Setup default GUI:
|
|
@@ -8056,7 +8217,7 @@ function dexieCloud(dexie) {
|
|
|
8056
8217
|
? yield navigator.serviceWorker.getRegistrations()
|
|
8057
8218
|
: [];
|
|
8058
8219
|
const [initiallySynced, lastSyncedRealms] = yield db.transaction('rw', db.$syncState, () => __awaiter(this, void 0, void 0, function* () {
|
|
8059
|
-
var
|
|
8220
|
+
var _a, _b;
|
|
8060
8221
|
const { options, schema } = db.cloud;
|
|
8061
8222
|
const [persistedOptions, persistedSchema, persistedSyncState] = yield Promise.all([
|
|
8062
8223
|
db.getOptions(),
|
|
@@ -8078,7 +8239,7 @@ function dexieCloud(dexie) {
|
|
|
8078
8239
|
delete newPersistedOptions.awarenessProtocol;
|
|
8079
8240
|
yield db.$syncState.put(newPersistedOptions, 'options');
|
|
8080
8241
|
}
|
|
8081
|
-
if (((
|
|
8242
|
+
if (((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.tryUseServiceWorker) &&
|
|
8082
8243
|
'serviceWorker' in navigator &&
|
|
8083
8244
|
swRegistrations.length > 0 &&
|
|
8084
8245
|
!DISABLE_SERVICEWORKER_STRATEGY) {
|
|
@@ -8092,7 +8253,7 @@ function dexieCloud(dexie) {
|
|
|
8092
8253
|
// Not configured for using service worker or no service worker
|
|
8093
8254
|
// registration exists. Don't rely on service worker to do any job.
|
|
8094
8255
|
// Use LocalSyncWorker instead.
|
|
8095
|
-
if (((
|
|
8256
|
+
if (((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.tryUseServiceWorker) &&
|
|
8096
8257
|
!db.cloud.isServiceWorkerDB) {
|
|
8097
8258
|
console.debug('dexie-cloud-addon: Not using service worker.', swRegistrations.length === 0
|
|
8098
8259
|
? 'No SW registrations found.'
|
|
@@ -8231,7 +8392,7 @@ function dexieCloud(dexie) {
|
|
|
8231
8392
|
}
|
|
8232
8393
|
}
|
|
8233
8394
|
// @ts-ignore
|
|
8234
|
-
dexieCloud.version = "4.1.0-alpha.
|
|
8395
|
+
dexieCloud.version = "4.1.0-alpha.20";
|
|
8235
8396
|
Dexie.Cloud = dexieCloud;
|
|
8236
8397
|
|
|
8237
8398
|
// In case the SW lives for a while, let it reuse already opened connections:
|
|
@@ -8261,8 +8422,8 @@ function syncDB(dbName, purpose) {
|
|
|
8261
8422
|
}
|
|
8262
8423
|
return promise;
|
|
8263
8424
|
function _syncDB(dbName, purpose) {
|
|
8264
|
-
var _a;
|
|
8265
8425
|
return __awaiter(this, void 0, void 0, function* () {
|
|
8426
|
+
var _a;
|
|
8266
8427
|
let db = managedDBs.get(dbName);
|
|
8267
8428
|
if (!db) {
|
|
8268
8429
|
console.debug('Dexie Cloud SW: Creating new Dexie instance for', dbName);
|