dexie-cloud-addon 4.0.1-beta.45 → 4.0.1-beta.47
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/dexie-cloud-addon.js +485 -317
- 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/service-worker.js +469 -249
- 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/types/DexieCloudAPI.d.ts +3 -0
- package/dist/types/DexieCloudOptions.d.ts +1 -0
- package/dist/types/InvalidLicenseError.d.ts +5 -0
- package/dist/types/authentication/TokenErrorResponseError.d.ts +10 -0
- package/dist/types/authentication/authenticate.d.ts +3 -3
- package/dist/types/authentication/interactWithUser.d.ts +3 -0
- package/dist/types/authentication/logout.d.ts +5 -0
- package/dist/types/authentication/waitUntil.d.ts +3 -0
- package/dist/types/currentUserEmitter.d.ts +1 -1
- package/dist/types/db/entities/UserLogin.d.ts +6 -0
- package/dist/types/default-ui/LoginDialog.d.ts +2 -5
- package/dist/types/dexie-cloud-client.d.ts +2 -0
- package/dist/types/helpers/resolveText.d.ts +14 -0
- package/dist/types/isEagerSyncDisabled.d.ts +2 -0
- package/dist/types/middlewares/createMutationTrackingMiddleware.d.ts +1 -1
- package/dist/types/prodLog.d.ts +9 -0
- package/dist/types/sync/performGuardedJob.d.ts +2 -4
- package/dist/types/sync/ratelimit.d.ts +3 -0
- package/dist/types/sync/sync.d.ts +0 -1
- package/dist/types/types/DXCAlert.d.ts +1 -1
- package/dist/types/types/DXCUserInteraction.d.ts +40 -2
- package/dist/types/types/SyncState.d.ts +1 -0
- package/dist/umd/dexie-cloud-addon.js +484 -316
- 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/service-worker.js +467 -247
- 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/package.json +7 -6
- package/LICENSE +0 -202
|
@@ -244,6 +244,7 @@
|
|
|
244
244
|
|
|
245
245
|
function triggerSync(db, purpose) {
|
|
246
246
|
if (db.cloud.usingServiceWorker) {
|
|
247
|
+
console.debug('registering sync event');
|
|
247
248
|
registerSyncEvent(db, purpose);
|
|
248
249
|
}
|
|
249
250
|
else {
|
|
@@ -745,14 +746,24 @@
|
|
|
745
746
|
return tablesToSyncify;
|
|
746
747
|
}
|
|
747
748
|
|
|
749
|
+
class TokenErrorResponseError extends Error {
|
|
750
|
+
constructor({ title, message, messageCode, messageParams, }) {
|
|
751
|
+
super(message);
|
|
752
|
+
this.name = 'TokenErrorResponseError';
|
|
753
|
+
this.title = title;
|
|
754
|
+
this.messageCode = messageCode;
|
|
755
|
+
this.messageParams = messageParams;
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
|
|
748
759
|
function interactWithUser(userInteraction, req) {
|
|
749
760
|
return new Promise((resolve, reject) => {
|
|
750
|
-
const interactionProps = Object.assign(Object.assign({}, req), { onSubmit: (res) => {
|
|
761
|
+
const interactionProps = Object.assign(Object.assign({ submitLabel: 'Submit', cancelLabel: 'Cancel' }, req), { onSubmit: (res) => {
|
|
751
762
|
userInteraction.next(undefined);
|
|
752
763
|
resolve(res);
|
|
753
764
|
}, onCancel: () => {
|
|
754
765
|
userInteraction.next(undefined);
|
|
755
|
-
reject(new Dexie__default["default"].AbortError(
|
|
766
|
+
reject(new Dexie__default["default"].AbortError('User cancelled'));
|
|
756
767
|
} });
|
|
757
768
|
userInteraction.next(interactionProps);
|
|
758
769
|
// Start subscribing for external updates to db.cloud.userInteraction, and if so, cancel this request.
|
|
@@ -771,7 +782,9 @@
|
|
|
771
782
|
type: 'message-alert',
|
|
772
783
|
title,
|
|
773
784
|
alerts,
|
|
774
|
-
fields: {}
|
|
785
|
+
fields: {},
|
|
786
|
+
submitLabel: 'OK',
|
|
787
|
+
cancelLabel: null,
|
|
775
788
|
});
|
|
776
789
|
}
|
|
777
790
|
function promptForEmail(userInteraction, title, emailHint) {
|
|
@@ -830,22 +843,48 @@
|
|
|
830
843
|
return otp;
|
|
831
844
|
});
|
|
832
845
|
}
|
|
846
|
+
function confirmLogout(userInteraction, currentUserId, numUnsyncedChanges) {
|
|
847
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
848
|
+
const alerts = [
|
|
849
|
+
{
|
|
850
|
+
type: 'warning',
|
|
851
|
+
messageCode: 'LOGOUT_CONFIRMATION',
|
|
852
|
+
message: `{numUnsyncedChanges} unsynced changes will get lost!
|
|
853
|
+
Logout anyway?`,
|
|
854
|
+
messageParams: {
|
|
855
|
+
currentUserId,
|
|
856
|
+
numUnsyncedChanges: numUnsyncedChanges.toString(),
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
];
|
|
860
|
+
return yield interactWithUser(userInteraction, {
|
|
861
|
+
type: 'logout-confirmation',
|
|
862
|
+
title: 'Confirm Logout',
|
|
863
|
+
alerts,
|
|
864
|
+
fields: {},
|
|
865
|
+
submitLabel: 'Confirm logout',
|
|
866
|
+
cancelLabel: 'Cancel'
|
|
867
|
+
})
|
|
868
|
+
.then(() => true)
|
|
869
|
+
.catch(() => false);
|
|
870
|
+
});
|
|
871
|
+
}
|
|
833
872
|
|
|
834
873
|
function loadAccessToken(db) {
|
|
835
|
-
var _a, _b;
|
|
874
|
+
var _a, _b, _c;
|
|
836
875
|
return __awaiter(this, void 0, void 0, function* () {
|
|
837
876
|
const currentUser = yield db.getCurrentUser();
|
|
838
877
|
const { accessToken, accessTokenExpiration, refreshToken, refreshTokenExpiration, claims, } = currentUser;
|
|
839
878
|
if (!accessToken)
|
|
840
|
-
return;
|
|
879
|
+
return null;
|
|
841
880
|
const expTime = (_a = accessTokenExpiration === null || accessTokenExpiration === void 0 ? void 0 : accessTokenExpiration.getTime()) !== null && _a !== void 0 ? _a : Infinity;
|
|
842
|
-
if (expTime > Date.now()) {
|
|
843
|
-
return
|
|
881
|
+
if (expTime > Date.now() && (((_b = currentUser.license) === null || _b === void 0 ? void 0 : _b.status) || 'ok') === 'ok') {
|
|
882
|
+
return currentUser;
|
|
844
883
|
}
|
|
845
884
|
if (!refreshToken) {
|
|
846
885
|
throw new Error(`Refresh token missing`);
|
|
847
886
|
}
|
|
848
|
-
const refreshExpTime = (
|
|
887
|
+
const refreshExpTime = (_c = refreshTokenExpiration === null || refreshTokenExpiration === void 0 ? void 0 : refreshTokenExpiration.getTime()) !== null && _c !== void 0 ? _c : Infinity;
|
|
849
888
|
if (refreshExpTime <= Date.now()) {
|
|
850
889
|
throw new Error(`Refresh token has expired`);
|
|
851
890
|
}
|
|
@@ -853,8 +892,10 @@
|
|
|
853
892
|
yield db.table('$logins').update(claims.sub, {
|
|
854
893
|
accessToken: refreshedLogin.accessToken,
|
|
855
894
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
895
|
+
claims: refreshedLogin.claims,
|
|
896
|
+
license: refreshedLogin.license,
|
|
856
897
|
});
|
|
857
|
-
return refreshedLogin
|
|
898
|
+
return refreshedLogin;
|
|
858
899
|
});
|
|
859
900
|
}
|
|
860
901
|
function authenticate(url, context, fetchToken, userInteraction, hints) {
|
|
@@ -902,10 +943,24 @@
|
|
|
902
943
|
if (res.status !== 200)
|
|
903
944
|
throw new Error(`RefreshToken: Status ${res.status} from ${url}/token`);
|
|
904
945
|
const response = yield res.json();
|
|
946
|
+
if (response.type === 'error') {
|
|
947
|
+
throw new TokenErrorResponseError(response);
|
|
948
|
+
}
|
|
905
949
|
login.accessToken = response.accessToken;
|
|
906
950
|
login.accessTokenExpiration = response.accessTokenExpiration
|
|
907
951
|
? new Date(response.accessTokenExpiration)
|
|
908
952
|
: undefined;
|
|
953
|
+
login.claims = response.claims;
|
|
954
|
+
login.license = {
|
|
955
|
+
type: response.userType,
|
|
956
|
+
status: response.claims.license || 'ok',
|
|
957
|
+
};
|
|
958
|
+
if (response.evalDaysLeft != null) {
|
|
959
|
+
login.license.evalDaysLeft = response.evalDaysLeft;
|
|
960
|
+
}
|
|
961
|
+
if (response.userValidUntil != null) {
|
|
962
|
+
login.license.validUntil = new Date(response.userValidUntil);
|
|
963
|
+
}
|
|
909
964
|
return login;
|
|
910
965
|
});
|
|
911
966
|
}
|
|
@@ -937,8 +992,15 @@
|
|
|
937
992
|
public_key: publicKeyPEM,
|
|
938
993
|
hints,
|
|
939
994
|
});
|
|
995
|
+
if (response2.type === 'error') {
|
|
996
|
+
throw new TokenErrorResponseError(response2);
|
|
997
|
+
}
|
|
940
998
|
if (response2.type !== 'tokens')
|
|
941
999
|
throw new Error(`Unexpected response type from token endpoint: ${response2.type}`);
|
|
1000
|
+
/*const licenseStatus = response2.claims.license || 'ok';
|
|
1001
|
+
if (licenseStatus !== 'ok') {
|
|
1002
|
+
throw new InvalidLicenseError(licenseStatus);
|
|
1003
|
+
}*/
|
|
942
1004
|
context.accessToken = response2.accessToken;
|
|
943
1005
|
context.accessTokenExpiration = new Date(response2.accessTokenExpiration);
|
|
944
1006
|
context.refreshToken = response2.refreshToken;
|
|
@@ -949,6 +1011,16 @@
|
|
|
949
1011
|
context.email = response2.claims.email;
|
|
950
1012
|
context.name = response2.claims.name;
|
|
951
1013
|
context.claims = response2.claims;
|
|
1014
|
+
context.license = {
|
|
1015
|
+
type: response2.userType,
|
|
1016
|
+
status: response2.claims.license || 'ok',
|
|
1017
|
+
};
|
|
1018
|
+
if (response2.evalDaysLeft != null) {
|
|
1019
|
+
context.license.evalDaysLeft = response2.evalDaysLeft;
|
|
1020
|
+
}
|
|
1021
|
+
if (response2.userValidUntil != null) {
|
|
1022
|
+
context.license.validUntil = new Date(response2.userValidUntil);
|
|
1023
|
+
}
|
|
952
1024
|
if (response2.alerts && response2.alerts.length > 0) {
|
|
953
1025
|
yield interactWithUser(userInteraction, {
|
|
954
1026
|
type: 'message-alert',
|
|
@@ -960,12 +1032,36 @@
|
|
|
960
1032
|
return context;
|
|
961
1033
|
}
|
|
962
1034
|
catch (error) {
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1035
|
+
if (error instanceof TokenErrorResponseError) {
|
|
1036
|
+
yield alertUser(userInteraction, error.title, {
|
|
1037
|
+
type: 'error',
|
|
1038
|
+
messageCode: error.messageCode,
|
|
1039
|
+
message: error.message,
|
|
1040
|
+
messageParams: {},
|
|
1041
|
+
});
|
|
1042
|
+
throw error;
|
|
1043
|
+
}
|
|
1044
|
+
let message = `We're having a problem authenticating right now.`;
|
|
1045
|
+
console.error(`Error authenticating`, error);
|
|
1046
|
+
if (error instanceof TypeError) {
|
|
1047
|
+
const isOffline = typeof navigator !== undefined && !navigator.onLine;
|
|
1048
|
+
if (isOffline) {
|
|
1049
|
+
message = `You seem to be offline. Please connect to the internet and try again.`;
|
|
1050
|
+
}
|
|
1051
|
+
else if (Dexie__default["default"].debug || (typeof location !== 'undefined' && (location.hostname === 'localhost' || location.hostname === '127.0.0.1'))) {
|
|
1052
|
+
// The audience is most likely the developer. Suggest to whitelist the localhost origin:
|
|
1053
|
+
message = `Could not connect to server. Please verify that your origin '${location.origin}' is whitelisted using \`npx dexie-cloud whitelist\``;
|
|
1054
|
+
}
|
|
1055
|
+
else {
|
|
1056
|
+
message = `Could not connect to server. Please verify the connection.`;
|
|
1057
|
+
}
|
|
1058
|
+
yield alertUser(userInteraction, 'Authentication Failed', {
|
|
1059
|
+
type: 'error',
|
|
1060
|
+
messageCode: 'GENERIC_ERROR',
|
|
1061
|
+
message,
|
|
1062
|
+
messageParams: {},
|
|
1063
|
+
}).catch(() => { });
|
|
1064
|
+
}
|
|
969
1065
|
throw error;
|
|
970
1066
|
}
|
|
971
1067
|
});
|
|
@@ -1545,9 +1641,19 @@
|
|
|
1545
1641
|
const mutClone = changeClone.muts[mutIndex];
|
|
1546
1642
|
const rewrittenKey = JSON.stringify(key);
|
|
1547
1643
|
mutClone.keys[keyIndex] = rewrittenKey;
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1644
|
+
/* Bug (#1777)
|
|
1645
|
+
We should not rewrite values. It will fail because the key is array and the value is string.
|
|
1646
|
+
Only the keys should be rewritten and it's already done on the server.
|
|
1647
|
+
We should take another round of revieweing how key transformations are being done between
|
|
1648
|
+
client and server and let the server do the key transformations entirely instead now that
|
|
1649
|
+
we have the primary key schema on the server making it possible to do so.
|
|
1650
|
+
if (rewriteValues) {
|
|
1651
|
+
Dexie.setByKeyPath(
|
|
1652
|
+
(mutClone as DBInsertOperation).values[keyIndex],
|
|
1653
|
+
primaryKey.keyPath!,
|
|
1654
|
+
rewrittenKey
|
|
1655
|
+
);
|
|
1656
|
+
}*/
|
|
1551
1657
|
}
|
|
1552
1658
|
else if (key[0] === '#') {
|
|
1553
1659
|
// Private ID - translate!
|
|
@@ -1578,6 +1684,40 @@
|
|
|
1578
1684
|
: change.muts.map((m) => (Object.assign(Object.assign({}, m), { keys: m.keys.slice() }))) });
|
|
1579
1685
|
}
|
|
1580
1686
|
|
|
1687
|
+
// If we get Ratelimit-Limit and Ratelimit-Remaining where Ratelimit-Remaining is below
|
|
1688
|
+
// (Ratelimit-Limit / 2), we should delay the next sync by (Ratelimit-Reset / Ratelimit-Remaining)
|
|
1689
|
+
// seconds (given that there is a Ratelimit-Reset header).
|
|
1690
|
+
let syncRatelimitDelays = new WeakMap();
|
|
1691
|
+
function checkSyncRateLimitDelay(db) {
|
|
1692
|
+
var _a, _b;
|
|
1693
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1694
|
+
const delatMilliseconds = ((_b = (_a = syncRatelimitDelays.get(db)) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) - Date.now();
|
|
1695
|
+
if (delatMilliseconds > 0) {
|
|
1696
|
+
console.debug(`Stalling sync request ${delatMilliseconds} ms to spare ratelimits`);
|
|
1697
|
+
yield new Promise(resolve => setTimeout(resolve, delatMilliseconds));
|
|
1698
|
+
}
|
|
1699
|
+
});
|
|
1700
|
+
}
|
|
1701
|
+
function updateSyncRateLimitDelays(db, res) {
|
|
1702
|
+
const limit = res.headers.get('Ratelimit-Limit');
|
|
1703
|
+
const remaining = res.headers.get('Ratelimit-Remaining');
|
|
1704
|
+
const reset = res.headers.get('Ratelimit-Reset');
|
|
1705
|
+
if (limit && remaining && reset) {
|
|
1706
|
+
const limitNum = Number(limit);
|
|
1707
|
+
const remainingNum = Math.max(0, Number(remaining));
|
|
1708
|
+
const willResetInSeconds = Number(reset);
|
|
1709
|
+
if (remainingNum < limitNum / 2) {
|
|
1710
|
+
const delay = Math.ceil(willResetInSeconds / (remainingNum + 1));
|
|
1711
|
+
syncRatelimitDelays.set(db, new Date(Date.now() + delay * 1000));
|
|
1712
|
+
console.debug(`Sync ratelimit delay set to ${delay} seconds`);
|
|
1713
|
+
}
|
|
1714
|
+
else {
|
|
1715
|
+
syncRatelimitDelays.delete(db);
|
|
1716
|
+
console.debug(`Sync ratelimit delay cleared`);
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1581
1721
|
//import {BisonWebStreamReader} from "dreambase-library/dist/typeson-simplified/BisonWebStreamReader";
|
|
1582
1722
|
function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, clientIdentity, currentUser) {
|
|
1583
1723
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1586,9 +1726,20 @@
|
|
|
1586
1726
|
//
|
|
1587
1727
|
const headers = {
|
|
1588
1728
|
Accept: 'application/json, application/x-bison, application/x-bison-stream',
|
|
1589
|
-
'Content-Type': 'application/tson'
|
|
1729
|
+
'Content-Type': 'application/tson',
|
|
1590
1730
|
};
|
|
1591
|
-
const
|
|
1731
|
+
const updatedUser = yield loadAccessToken(db);
|
|
1732
|
+
/*
|
|
1733
|
+
if (updatedUser?.license && changes.length > 0) {
|
|
1734
|
+
if (updatedUser.license.status === 'expired') {
|
|
1735
|
+
throw new Error(`License has expired`);
|
|
1736
|
+
}
|
|
1737
|
+
if (updatedUser.license.status === 'deactivated') {
|
|
1738
|
+
throw new Error(`License deactivated`);
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
*/
|
|
1742
|
+
const accessToken = updatedUser === null || updatedUser === void 0 ? void 0 : updatedUser.accessToken;
|
|
1592
1743
|
if (accessToken) {
|
|
1593
1744
|
headers.Authorization = `Bearer ${accessToken}`;
|
|
1594
1745
|
}
|
|
@@ -1597,27 +1748,31 @@
|
|
|
1597
1748
|
dbID: syncState === null || syncState === void 0 ? void 0 : syncState.remoteDbId,
|
|
1598
1749
|
clientIdentity,
|
|
1599
1750
|
schema: schema || {},
|
|
1600
|
-
lastPull: syncState
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1751
|
+
lastPull: syncState
|
|
1752
|
+
? {
|
|
1753
|
+
serverRevision: syncState.serverRevision,
|
|
1754
|
+
realms: syncState.realms,
|
|
1755
|
+
inviteRealms: syncState.inviteRealms,
|
|
1756
|
+
}
|
|
1757
|
+
: undefined,
|
|
1605
1758
|
baseRevs,
|
|
1606
|
-
changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes)
|
|
1759
|
+
changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes),
|
|
1607
1760
|
};
|
|
1608
|
-
console.debug(
|
|
1761
|
+
console.debug('Sync request', syncRequest);
|
|
1609
1762
|
db.syncStateChangedEvent.next({
|
|
1610
1763
|
phase: 'pushing',
|
|
1611
1764
|
});
|
|
1612
1765
|
const res = yield fetch(`${databaseUrl}/sync`, {
|
|
1613
1766
|
method: 'post',
|
|
1614
1767
|
headers,
|
|
1615
|
-
|
|
1768
|
+
credentials: 'include',
|
|
1769
|
+
body: TSON.stringify(syncRequest),
|
|
1616
1770
|
});
|
|
1617
1771
|
//const contentLength = Number(res.headers.get('content-length'));
|
|
1618
1772
|
db.syncStateChangedEvent.next({
|
|
1619
|
-
phase: 'pulling'
|
|
1773
|
+
phase: 'pulling',
|
|
1620
1774
|
});
|
|
1775
|
+
updateSyncRateLimitDelays(db, res);
|
|
1621
1776
|
if (!res.ok) {
|
|
1622
1777
|
throw new HttpError(res);
|
|
1623
1778
|
}
|
|
@@ -1819,12 +1974,13 @@
|
|
|
1819
1974
|
function sync(db, options, schema, syncOptions) {
|
|
1820
1975
|
return _sync
|
|
1821
1976
|
.apply(this, arguments)
|
|
1822
|
-
.then(() => {
|
|
1977
|
+
.then((result) => {
|
|
1823
1978
|
if (!(syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)) { // && syncOptions?.purpose !== 'push') {
|
|
1824
1979
|
db.syncStateChangedEvent.next({
|
|
1825
1980
|
phase: 'in-sync',
|
|
1826
1981
|
});
|
|
1827
1982
|
}
|
|
1983
|
+
return result;
|
|
1828
1984
|
})
|
|
1829
1985
|
.catch((error) => __awaiter(this, void 0, void 0, function* () {
|
|
1830
1986
|
if (syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)
|
|
@@ -1854,7 +2010,7 @@
|
|
|
1854
2010
|
});
|
|
1855
2011
|
db.syncStateChangedEvent.next({
|
|
1856
2012
|
phase: isOnline ? 'error' : 'offline',
|
|
1857
|
-
error,
|
|
2013
|
+
error: new Error('' + (error === null || error === void 0 ? void 0 : error.message) || error),
|
|
1858
2014
|
});
|
|
1859
2015
|
return Promise.reject(error);
|
|
1860
2016
|
}));
|
|
@@ -2039,6 +2195,7 @@
|
|
|
2039
2195
|
}));
|
|
2040
2196
|
if (!done) {
|
|
2041
2197
|
console.debug('MORE SYNC NEEDED. Go for it again!');
|
|
2198
|
+
yield checkSyncRateLimitDelay(db);
|
|
2042
2199
|
return yield _sync(db, options, schema, { isInitialSync, cancelToken });
|
|
2043
2200
|
}
|
|
2044
2201
|
console.debug('SYNC DONE', { isInitialSync });
|
|
@@ -2175,6 +2332,8 @@
|
|
|
2175
2332
|
yield db.table('$logins').update(user.userId, {
|
|
2176
2333
|
accessToken: refreshedLogin.accessToken,
|
|
2177
2334
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
2335
|
+
claims: refreshedLogin.claims,
|
|
2336
|
+
license: refreshedLogin.license,
|
|
2178
2337
|
});
|
|
2179
2338
|
// Updating $logins will trigger emission of db.cloud.currentUser observable, which
|
|
2180
2339
|
// in turn will lead to that connectWebSocket.ts will reconnect the socket with the
|
|
@@ -2445,6 +2604,61 @@
|
|
|
2445
2604
|
}
|
|
2446
2605
|
}
|
|
2447
2606
|
|
|
2607
|
+
function waitUntil(o, // Works with Dexie's liveQuery observables if we'd need that
|
|
2608
|
+
predicate) {
|
|
2609
|
+
return rxjs.firstValueFrom(rxjs.from(o).pipe(rxjs.filter(predicate)));
|
|
2610
|
+
}
|
|
2611
|
+
|
|
2612
|
+
function logout(db) {
|
|
2613
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2614
|
+
const numUnsyncedChanges = yield _logout(db);
|
|
2615
|
+
if (numUnsyncedChanges) {
|
|
2616
|
+
if (yield confirmLogout(db.cloud.userInteraction, db.cloud.currentUserId, numUnsyncedChanges)) {
|
|
2617
|
+
yield _logout(db, { deleteUnsyncedData: true });
|
|
2618
|
+
}
|
|
2619
|
+
else {
|
|
2620
|
+
throw new Error(`User cancelled logout due to unsynced changes`);
|
|
2621
|
+
}
|
|
2622
|
+
}
|
|
2623
|
+
});
|
|
2624
|
+
}
|
|
2625
|
+
function _logout(db, { deleteUnsyncedData = false } = {}) {
|
|
2626
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2627
|
+
// Clear the database without emptying configuration options.
|
|
2628
|
+
const [numUnsynced, loggedOut] = yield db.dx.transaction('rw', db.dx.tables, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
2629
|
+
// @ts-ignore
|
|
2630
|
+
const idbtrans = tx.idbtrans;
|
|
2631
|
+
idbtrans.disableChangeTracking = true;
|
|
2632
|
+
idbtrans.disableAccessControl = true;
|
|
2633
|
+
const mutationTables = tx.storeNames.filter((tableName) => tableName.endsWith('_mutations'));
|
|
2634
|
+
// Count unsynced changes
|
|
2635
|
+
const unsyncCounts = yield Promise.all(mutationTables.map((mutationTable) => tx.table(mutationTable).count()));
|
|
2636
|
+
const sumUnSynced = unsyncCounts.reduce((a, b) => a + b, 0);
|
|
2637
|
+
if (sumUnSynced > 0 && !deleteUnsyncedData) {
|
|
2638
|
+
// Let caller ask user if they want to delete unsynced data.
|
|
2639
|
+
return [sumUnSynced, false];
|
|
2640
|
+
}
|
|
2641
|
+
// Either there are no unsynched changes, or caller provided flag deleteUnsynchedData = true.
|
|
2642
|
+
// Clear all tables except $jobs and $syncState (except the persisted sync state which is
|
|
2643
|
+
// also cleared because we're going to rebuild it using a fresh sync).
|
|
2644
|
+
db.$syncState.delete('syncState');
|
|
2645
|
+
for (const table of db.dx.tables) {
|
|
2646
|
+
if (table.name !== '$jobs' && table.name !== '$syncState') {
|
|
2647
|
+
table.clear();
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2650
|
+
return [sumUnSynced, true];
|
|
2651
|
+
}));
|
|
2652
|
+
if (loggedOut) {
|
|
2653
|
+
// Wait for currentUser observable to emit UNAUTHORIZED_USER
|
|
2654
|
+
yield waitUntil(db.cloud.currentUser, (user) => user.userId === UNAUTHORIZED_USER.userId);
|
|
2655
|
+
// Then perform an initial sync
|
|
2656
|
+
yield db.cloud.sync({ purpose: 'pull', wait: true });
|
|
2657
|
+
}
|
|
2658
|
+
return numUnsynced;
|
|
2659
|
+
});
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2448
2662
|
function otpFetchTokenCallback(db) {
|
|
2449
2663
|
const { userInteraction } = db.cloud;
|
|
2450
2664
|
return function otpAuthenticate({ public_key, hints }) {
|
|
@@ -2488,8 +2702,9 @@
|
|
|
2488
2702
|
throw new HttpError(res1, errMsg);
|
|
2489
2703
|
}
|
|
2490
2704
|
const response = yield res1.json();
|
|
2491
|
-
if (response.type === 'tokens') {
|
|
2705
|
+
if (response.type === 'tokens' || response.type === 'error') {
|
|
2492
2706
|
// Demo user request can get a "tokens" response right away
|
|
2707
|
+
// Error can also be returned right away.
|
|
2493
2708
|
return response;
|
|
2494
2709
|
}
|
|
2495
2710
|
else if (tokenRequest.grant_type === 'otp') {
|
|
@@ -2521,12 +2736,6 @@
|
|
|
2521
2736
|
}
|
|
2522
2737
|
if (res2.status !== 200) {
|
|
2523
2738
|
const errMsg = yield res2.text();
|
|
2524
|
-
yield alertUser(userInteraction, "OTP Authentication Failed", {
|
|
2525
|
-
type: 'error',
|
|
2526
|
-
messageCode: 'GENERIC_ERROR',
|
|
2527
|
-
message: errMsg,
|
|
2528
|
-
messageParams: {}
|
|
2529
|
-
}).catch(() => { });
|
|
2530
2739
|
throw new HttpError(res2, errMsg);
|
|
2531
2740
|
}
|
|
2532
2741
|
const response2 = yield res2.json();
|
|
@@ -2539,6 +2748,18 @@
|
|
|
2539
2748
|
};
|
|
2540
2749
|
}
|
|
2541
2750
|
|
|
2751
|
+
/** A way to log to console in production without terser stripping out
|
|
2752
|
+
* it from the release bundle.
|
|
2753
|
+
* This should be used very rarely and only in places where it's
|
|
2754
|
+
* absolutely necessary to log something in production.
|
|
2755
|
+
*
|
|
2756
|
+
* @param level
|
|
2757
|
+
* @param args
|
|
2758
|
+
*/
|
|
2759
|
+
function prodLog(level, ...args) {
|
|
2760
|
+
globalThis["con" + "sole"][level](...args);
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2542
2763
|
/** This function changes or sets the current user as requested.
|
|
2543
2764
|
*
|
|
2544
2765
|
* Use cases:
|
|
@@ -2565,85 +2786,73 @@
|
|
|
2565
2786
|
}));
|
|
2566
2787
|
user.isLoggedIn = true;
|
|
2567
2788
|
user.lastLogin = new Date();
|
|
2568
|
-
|
|
2569
|
-
|
|
2570
|
-
}));
|
|
2571
|
-
yield new Promise((resolve) => {
|
|
2572
|
-
if (db.cloud.currentUserId === user.userId) {
|
|
2573
|
-
resolve(null);
|
|
2789
|
+
try {
|
|
2790
|
+
yield user.save();
|
|
2574
2791
|
}
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
if (
|
|
2578
|
-
|
|
2579
|
-
|
|
2792
|
+
catch (e) {
|
|
2793
|
+
try {
|
|
2794
|
+
if (e.name === 'DataCloneError') {
|
|
2795
|
+
// We've seen this buggy behavior in some browsers and in case it happens
|
|
2796
|
+
// again we really need to collect the details to understand what's going on.
|
|
2797
|
+
prodLog('debug', `Login context property names:`, Object.keys(user));
|
|
2798
|
+
prodLog('debug', `Login context property names:`, Object.keys(user));
|
|
2799
|
+
prodLog('debug', `Login context:`, user);
|
|
2800
|
+
prodLog('debug', `Login context JSON:`, JSON.stringify(user));
|
|
2580
2801
|
}
|
|
2581
|
-
}
|
|
2802
|
+
}
|
|
2803
|
+
catch (_a) { }
|
|
2804
|
+
throw e;
|
|
2582
2805
|
}
|
|
2583
|
-
|
|
2584
|
-
|
|
2585
|
-
|
|
2586
|
-
// V: Samma med andra windows.
|
|
2587
|
-
// V: Så kanske göra om den till att häröra från liveQuery som läser $logins.orderBy('lastLogin').last().
|
|
2588
|
-
// V: Då bara vara medveten om:
|
|
2589
|
-
// V: En sån observable börjar hämta data vid första subscribe
|
|
2590
|
-
// V: Vi har inget "inital value" men kan emulera det till att vara ANONYMOUS_USER
|
|
2591
|
-
// V: Om requireAuth är true, så borde db.on(ready) hålla databasen stängd för alla utom denna observable.
|
|
2592
|
-
// V: Om inte så behöver den inte blocka.
|
|
2593
|
-
// Andra tankar:
|
|
2594
|
-
// * Man kan inte byta användare när man är offline. Skulle gå att flytta realms till undanstuff-tabell vid user-change.
|
|
2595
|
-
// men troligen inte värt det.
|
|
2596
|
-
// * Istället: sälj inte inte switch-user funktionalitet utan tala enbart om inloggat vs icke inloggat läge.
|
|
2597
|
-
// * populate $logins med ANONYMOUS så att en påbörjad inloggning inte räknas, alternativt ha en boolean prop!
|
|
2598
|
-
// Kanske bäst ha en boolean prop!
|
|
2599
|
-
// * Alternativ switch-user funktionalitet:
|
|
2600
|
-
// * DBCore gömmer data från realms man inte har tillgång till.
|
|
2601
|
-
// * Cursor impl behövs också då.
|
|
2602
|
-
// * Då blir det snabba user switch.
|
|
2603
|
-
// * claims-settet som skickas till servern blir summan av alla claims. Då måste servern stödja multipla tokens eller
|
|
2604
|
-
// att ens token är ett samlad.
|
|
2806
|
+
console.debug('Saved new user', user.email);
|
|
2807
|
+
}));
|
|
2808
|
+
yield waitUntil(db.cloud.currentUser, (currentUser) => currentUser.userId === user.userId);
|
|
2605
2809
|
});
|
|
2606
2810
|
}
|
|
2607
2811
|
|
|
2608
2812
|
function login(db, hints) {
|
|
2813
|
+
var _a;
|
|
2609
2814
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2610
2815
|
const currentUser = yield db.getCurrentUser();
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
|
|
2617
|
-
throw new Error(`Must logout before changing user`);
|
|
2618
|
-
}
|
|
2816
|
+
const origUserId = currentUser.userId;
|
|
2817
|
+
if (currentUser.isLoggedIn && (!hints || (!hints.email && !hints.userId))) {
|
|
2818
|
+
const licenseStatus = ((_a = currentUser.license) === null || _a === void 0 ? void 0 : _a.status) || 'ok';
|
|
2819
|
+
if (licenseStatus === 'ok' && currentUser.accessToken && (!currentUser.accessTokenExpiration || currentUser.accessTokenExpiration.getTime() > Date.now())) {
|
|
2820
|
+
// Already authenticated according to given hints. And license is valid.
|
|
2821
|
+
return false;
|
|
2619
2822
|
}
|
|
2620
|
-
|
|
2621
|
-
|
|
2823
|
+
if (currentUser.refreshToken && (!currentUser.refreshTokenExpiration || currentUser.refreshTokenExpiration.getTime() > Date.now())) {
|
|
2824
|
+
// Refresh the token
|
|
2825
|
+
yield loadAccessToken(db);
|
|
2826
|
+
return false;
|
|
2827
|
+
}
|
|
2828
|
+
// No refresh token - must re-authenticate:
|
|
2622
2829
|
}
|
|
2623
2830
|
const context = new AuthPersistedContext(db, {
|
|
2624
2831
|
claims: {},
|
|
2625
2832
|
lastLogin: new Date(0),
|
|
2626
2833
|
});
|
|
2627
2834
|
yield authenticate(db.cloud.options.databaseUrl, context, db.cloud.options.fetchTokens || otpFetchTokenCallback(db), db.cloud.userInteraction, hints);
|
|
2628
|
-
|
|
2629
|
-
|
|
2835
|
+
if (origUserId !== UNAUTHORIZED_USER.userId && context.userId !== origUserId) {
|
|
2836
|
+
// User was logged in before, but now logged in as another user.
|
|
2837
|
+
yield logout(db);
|
|
2630
2838
|
}
|
|
2631
|
-
|
|
2632
|
-
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
|
|
2839
|
+
/*try {
|
|
2840
|
+
await context.save();
|
|
2841
|
+
} catch (e) {
|
|
2842
|
+
try {
|
|
2843
|
+
if (e.name === 'DataCloneError') {
|
|
2844
|
+
console.debug(`Login context property names:`, Object.keys(context));
|
|
2845
|
+
console.debug(`Login context:`, context);
|
|
2846
|
+
console.debug(`Login context JSON:`, JSON.stringify(context));
|
|
2638
2847
|
}
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
}
|
|
2848
|
+
} catch {}
|
|
2849
|
+
throw e;
|
|
2850
|
+
}*/
|
|
2642
2851
|
yield setCurrentUser(db, context);
|
|
2643
2852
|
// Make sure to resync as the new login will be authorized
|
|
2644
2853
|
// for new realms.
|
|
2645
2854
|
triggerSync(db, "pull");
|
|
2646
|
-
return
|
|
2855
|
+
return context.userId !== origUserId;
|
|
2647
2856
|
});
|
|
2648
2857
|
}
|
|
2649
2858
|
|
|
@@ -2972,6 +3181,13 @@
|
|
|
2972
3181
|
|
|
2973
3182
|
const outstandingTransactions = new rxjs.BehaviorSubject(new Set());
|
|
2974
3183
|
|
|
3184
|
+
function isEagerSyncDisabled(db) {
|
|
3185
|
+
var _a, _b, _c, _d;
|
|
3186
|
+
return (((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.disableEagerSync) ||
|
|
3187
|
+
((_c = (_b = db.cloud.currentUser.value) === null || _b === void 0 ? void 0 : _b.license) === null || _c === void 0 ? void 0 : _c.status) !== 'ok' ||
|
|
3188
|
+
!((_d = db.cloud.options) === null || _d === void 0 ? void 0 : _d.databaseUrl));
|
|
3189
|
+
}
|
|
3190
|
+
|
|
2975
3191
|
/** Tracks all mutations in the same transaction as the mutations -
|
|
2976
3192
|
* so it is guaranteed that no mutation goes untracked - and if transaction
|
|
2977
3193
|
* aborts, the mutations won't be tracked.
|
|
@@ -2980,7 +3196,7 @@
|
|
|
2980
3196
|
* changes to server and cleanup the tracked mutations once the server has
|
|
2981
3197
|
* ackowledged that it got them.
|
|
2982
3198
|
*/
|
|
2983
|
-
function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
3199
|
+
function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
2984
3200
|
return {
|
|
2985
3201
|
stack: 'dbcore',
|
|
2986
3202
|
name: 'MutationTrackingMiddleware',
|
|
@@ -2991,7 +3207,7 @@
|
|
|
2991
3207
|
try {
|
|
2992
3208
|
mutTableMap = new Map(ordinaryTables.map((tbl) => [
|
|
2993
3209
|
tbl.name,
|
|
2994
|
-
core.table(`$${tbl.name}_mutations`)
|
|
3210
|
+
core.table(`$${tbl.name}_mutations`),
|
|
2995
3211
|
]));
|
|
2996
3212
|
}
|
|
2997
3213
|
catch (_a) {
|
|
@@ -3025,15 +3241,9 @@
|
|
|
3025
3241
|
outstandingTransactions.next(outstandingTransactions.value);
|
|
3026
3242
|
};
|
|
3027
3243
|
const txComplete = () => {
|
|
3028
|
-
|
|
3029
|
-
|
|
3030
|
-
|
|
3031
|
-
console.debug('registering sync event');
|
|
3032
|
-
registerSyncEvent(db, "push");
|
|
3033
|
-
}
|
|
3034
|
-
else {
|
|
3035
|
-
db.localSyncEvent.next({ purpose: "push" });
|
|
3036
|
-
}
|
|
3244
|
+
if (tx.mutationsAdded &&
|
|
3245
|
+
!isEagerSyncDisabled(db)) {
|
|
3246
|
+
triggerSync(db, 'push');
|
|
3037
3247
|
}
|
|
3038
3248
|
removeTransaction();
|
|
3039
3249
|
};
|
|
@@ -3100,7 +3310,7 @@
|
|
|
3100
3310
|
.query({
|
|
3101
3311
|
query: { range: req.range, index: schema.primaryKey },
|
|
3102
3312
|
trans: req.trans,
|
|
3103
|
-
values: false
|
|
3313
|
+
values: false,
|
|
3104
3314
|
})
|
|
3105
3315
|
// Do a delete request instead, but keep the criteria info for the server to execute
|
|
3106
3316
|
.then((res) => {
|
|
@@ -3108,7 +3318,7 @@
|
|
|
3108
3318
|
type: 'delete',
|
|
3109
3319
|
keys: res.result,
|
|
3110
3320
|
trans: req.trans,
|
|
3111
|
-
criteria: { index: null, range: req.range }
|
|
3321
|
+
criteria: { index: null, range: req.range },
|
|
3112
3322
|
});
|
|
3113
3323
|
})
|
|
3114
3324
|
: mutateAndLog(req);
|
|
@@ -3116,7 +3326,7 @@
|
|
|
3116
3326
|
function mutateAndLog(req) {
|
|
3117
3327
|
const trans = req.trans;
|
|
3118
3328
|
trans.mutationsAdded = true;
|
|
3119
|
-
const { txid, currentUser: { userId } } = trans;
|
|
3329
|
+
const { txid, currentUser: { userId }, } = trans;
|
|
3120
3330
|
const { type } = req;
|
|
3121
3331
|
const opNo = ++trans.opCount;
|
|
3122
3332
|
return table.mutate(req).then((res) => {
|
|
@@ -3137,7 +3347,7 @@
|
|
|
3137
3347
|
keys,
|
|
3138
3348
|
criteria: req.criteria,
|
|
3139
3349
|
txid,
|
|
3140
|
-
userId
|
|
3350
|
+
userId,
|
|
3141
3351
|
}
|
|
3142
3352
|
: req.type === 'add'
|
|
3143
3353
|
? {
|
|
@@ -3147,7 +3357,7 @@
|
|
|
3147
3357
|
keys,
|
|
3148
3358
|
txid,
|
|
3149
3359
|
userId,
|
|
3150
|
-
values
|
|
3360
|
+
values,
|
|
3151
3361
|
}
|
|
3152
3362
|
: req.criteria && req.changeSpec
|
|
3153
3363
|
? {
|
|
@@ -3159,7 +3369,7 @@
|
|
|
3159
3369
|
criteria: req.criteria,
|
|
3160
3370
|
changeSpec: req.changeSpec,
|
|
3161
3371
|
txid,
|
|
3162
|
-
userId
|
|
3372
|
+
userId,
|
|
3163
3373
|
}
|
|
3164
3374
|
: updates
|
|
3165
3375
|
? {
|
|
@@ -3170,7 +3380,7 @@
|
|
|
3170
3380
|
keys: updates.keys,
|
|
3171
3381
|
changeSpecs: updates.changeSpecs,
|
|
3172
3382
|
txid,
|
|
3173
|
-
userId
|
|
3383
|
+
userId,
|
|
3174
3384
|
}
|
|
3175
3385
|
: {
|
|
3176
3386
|
type: 'upsert',
|
|
@@ -3179,7 +3389,7 @@
|
|
|
3179
3389
|
keys,
|
|
3180
3390
|
values,
|
|
3181
3391
|
txid,
|
|
3182
|
-
userId
|
|
3392
|
+
userId,
|
|
3183
3393
|
};
|
|
3184
3394
|
return keys.length > 0 || ('criteria' in req && req.criteria)
|
|
3185
3395
|
? mutsTable
|
|
@@ -3189,7 +3399,7 @@
|
|
|
3189
3399
|
});
|
|
3190
3400
|
}
|
|
3191
3401
|
} });
|
|
3192
|
-
}
|
|
3402
|
+
},
|
|
3193
3403
|
};
|
|
3194
3404
|
}
|
|
3195
3405
|
|
|
@@ -3548,6 +3758,20 @@
|
|
|
3548
3758
|
}
|
|
3549
3759
|
}
|
|
3550
3760
|
|
|
3761
|
+
class InvalidLicenseError extends Error {
|
|
3762
|
+
constructor(license) {
|
|
3763
|
+
super(license === 'expired'
|
|
3764
|
+
? `License expired`
|
|
3765
|
+
: license === 'deactivated'
|
|
3766
|
+
? `User deactivated`
|
|
3767
|
+
: 'Invalid license');
|
|
3768
|
+
this.name = 'InvalidLicenseError';
|
|
3769
|
+
if (license) {
|
|
3770
|
+
this.license = license;
|
|
3771
|
+
}
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
|
|
3551
3775
|
function sleep$1(ms) {
|
|
3552
3776
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3553
3777
|
}
|
|
@@ -3581,7 +3805,12 @@
|
|
|
3581
3805
|
function createObservable() {
|
|
3582
3806
|
return db.cloud.persistedSyncState.pipe(operators.filter((syncState) => syncState === null || syncState === void 0 ? void 0 : syncState.serverRevision), // Don't connect before there's no initial sync performed.
|
|
3583
3807
|
operators.take(1), // Don't continue waking up whenever syncState change
|
|
3584
|
-
operators.switchMap((syncState) => db.cloud.currentUser.pipe(operators.map((userLogin) => [userLogin, syncState]))), operators.switchMap(([userLogin, syncState]) =>
|
|
3808
|
+
operators.switchMap((syncState) => db.cloud.currentUser.pipe(operators.map((userLogin) => [userLogin, syncState]))), operators.switchMap(([userLogin, syncState]) => {
|
|
3809
|
+
/*if (userLogin.license?.status && userLogin.license.status !== 'ok') {
|
|
3810
|
+
throw new InvalidLicenseError();
|
|
3811
|
+
}*/
|
|
3812
|
+
return userIsReallyActive.pipe(operators.map((isActive) => [isActive ? userLogin : null, syncState]));
|
|
3813
|
+
}), operators.switchMap(([userLogin, syncState]) => {
|
|
3585
3814
|
if ((userLogin === null || userLogin === void 0 ? void 0 : userLogin.isLoggedIn) && !(syncState === null || syncState === void 0 ? void 0 : syncState.realms.includes(userLogin.userId))) {
|
|
3586
3815
|
// We're in an in-between state when user is logged in but the user's realms are not yet synced.
|
|
3587
3816
|
// Don't make this change reconnect the websocket just yet. Wait till syncState is updated
|
|
@@ -3610,14 +3839,20 @@
|
|
|
3610
3839
|
yield db.table('$logins').update(user.userId, {
|
|
3611
3840
|
accessToken: refreshedLogin.accessToken,
|
|
3612
3841
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
3842
|
+
claims: refreshedLogin.claims,
|
|
3843
|
+
license: refreshedLogin.license,
|
|
3613
3844
|
});
|
|
3614
3845
|
})), operators.switchMap(() => createObservable()));
|
|
3615
3846
|
}
|
|
3616
3847
|
else {
|
|
3617
|
-
return rxjs.throwError(error);
|
|
3848
|
+
return rxjs.throwError(() => error);
|
|
3618
3849
|
}
|
|
3619
3850
|
}), operators.catchError((error) => {
|
|
3620
3851
|
db.cloud.webSocketStatus.next("error");
|
|
3852
|
+
if (error instanceof InvalidLicenseError) {
|
|
3853
|
+
// Don't retry. Just throw and don't try connect again.
|
|
3854
|
+
return rxjs.throwError(() => error);
|
|
3855
|
+
}
|
|
3621
3856
|
return rxjs.from(waitAndReconnectWhenUserDoesSomething(error)).pipe(operators.switchMap(() => createObservable()));
|
|
3622
3857
|
}));
|
|
3623
3858
|
}
|
|
@@ -3646,97 +3881,12 @@
|
|
|
3646
3881
|
});
|
|
3647
3882
|
}
|
|
3648
3883
|
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
|
|
3653
|
-
|
|
3654
|
-
|
|
3655
|
-
const GUARDED_JOB_TIMEOUT = 1 * MINUTES;
|
|
3656
|
-
function performGuardedJob(db, jobName, jobsTableName, job, { awaitRemoteJob } = {}) {
|
|
3657
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
3658
|
-
// Start working.
|
|
3659
|
-
//
|
|
3660
|
-
// Check if someone else is working on this already.
|
|
3661
|
-
//
|
|
3662
|
-
const jobsTable = db.table(jobsTableName);
|
|
3663
|
-
function aquireLock() {
|
|
3664
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
3665
|
-
const gotTheLock = yield db.transaction('rw!', jobsTableName, () => __awaiter(this, void 0, void 0, function* () {
|
|
3666
|
-
const currentWork = yield jobsTable.get(jobName);
|
|
3667
|
-
if (!currentWork) {
|
|
3668
|
-
// No one else is working. Let's record that we are.
|
|
3669
|
-
yield jobsTable.add({
|
|
3670
|
-
nodeId: myId,
|
|
3671
|
-
started: new Date(),
|
|
3672
|
-
heartbeat: new Date()
|
|
3673
|
-
}, jobName);
|
|
3674
|
-
return true;
|
|
3675
|
-
}
|
|
3676
|
-
else if (currentWork.heartbeat.getTime() <
|
|
3677
|
-
Date.now() - GUARDED_JOB_TIMEOUT) {
|
|
3678
|
-
console.warn(`Latest ${jobName} worker seem to have died.\n`, `The dead job started:`, currentWork.started, `\n`, `Last heart beat was:`, currentWork.heartbeat, '\n', `We're now taking over!`);
|
|
3679
|
-
// Now, take over!
|
|
3680
|
-
yield jobsTable.put({
|
|
3681
|
-
nodeId: myId,
|
|
3682
|
-
started: new Date(),
|
|
3683
|
-
heartbeat: new Date()
|
|
3684
|
-
}, jobName);
|
|
3685
|
-
return true;
|
|
3686
|
-
}
|
|
3687
|
-
return false;
|
|
3688
|
-
}));
|
|
3689
|
-
if (gotTheLock)
|
|
3690
|
-
return true;
|
|
3691
|
-
// Someone else took the job.
|
|
3692
|
-
if (awaitRemoteJob) {
|
|
3693
|
-
try {
|
|
3694
|
-
const jobDoneObservable = rxjs.from(Dexie.liveQuery(() => jobsTable.get(jobName))).pipe(operators.timeout(GUARDED_JOB_TIMEOUT), operators.filter((job) => !job)); // Wait til job is not there anymore.
|
|
3695
|
-
yield jobDoneObservable.toPromise();
|
|
3696
|
-
return false;
|
|
3697
|
-
}
|
|
3698
|
-
catch (err) {
|
|
3699
|
-
if (err.name !== 'TimeoutError') {
|
|
3700
|
-
throw err;
|
|
3701
|
-
}
|
|
3702
|
-
// Timeout stopped us! Try aquire the lock now.
|
|
3703
|
-
// It will likely succeed this time unless
|
|
3704
|
-
// another client took it.
|
|
3705
|
-
return yield aquireLock();
|
|
3706
|
-
}
|
|
3707
|
-
}
|
|
3708
|
-
return false;
|
|
3709
|
-
});
|
|
3710
|
-
}
|
|
3711
|
-
if (yield aquireLock()) {
|
|
3712
|
-
// We own the lock entry and can do our job undisturbed.
|
|
3713
|
-
// We're not within a transaction, but these type of locks
|
|
3714
|
-
// spans over transactions.
|
|
3715
|
-
// Start our heart beat during the job.
|
|
3716
|
-
// Use setInterval to make sure we are updating heartbeat even during long-lived fetch calls.
|
|
3717
|
-
const heartbeat = setInterval(() => {
|
|
3718
|
-
jobsTable.update(jobName, (job) => {
|
|
3719
|
-
if (job.nodeId === myId) {
|
|
3720
|
-
job.heartbeat = new Date();
|
|
3721
|
-
}
|
|
3722
|
-
});
|
|
3723
|
-
}, GUARDED_JOB_HEARTBEAT);
|
|
3724
|
-
try {
|
|
3725
|
-
return yield job();
|
|
3726
|
-
}
|
|
3727
|
-
finally {
|
|
3728
|
-
// Stop heartbeat
|
|
3729
|
-
clearInterval(heartbeat);
|
|
3730
|
-
// Remove the persisted job state:
|
|
3731
|
-
yield db.transaction('rw!', jobsTableName, () => __awaiter(this, void 0, void 0, function* () {
|
|
3732
|
-
const currentWork = yield jobsTable.get(jobName);
|
|
3733
|
-
if (currentWork && currentWork.nodeId === myId) {
|
|
3734
|
-
yield jobsTable.delete(jobName);
|
|
3735
|
-
}
|
|
3736
|
-
}));
|
|
3737
|
-
}
|
|
3738
|
-
}
|
|
3739
|
-
});
|
|
3884
|
+
function performGuardedJob(db, jobName, job) {
|
|
3885
|
+
if (typeof navigator === 'undefined' || !navigator.locks) {
|
|
3886
|
+
// No support for guarding jobs. IE11, node.js, etc.
|
|
3887
|
+
return job();
|
|
3888
|
+
}
|
|
3889
|
+
return navigator.locks.request(db.name + '|' + jobName, () => job());
|
|
3740
3890
|
}
|
|
3741
3891
|
|
|
3742
3892
|
const ongoingSyncs = new WeakMap();
|
|
@@ -3790,6 +3940,9 @@
|
|
|
3790
3940
|
function _syncIfPossible() {
|
|
3791
3941
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3792
3942
|
try {
|
|
3943
|
+
// Check if should delay sync due to ratelimit:
|
|
3944
|
+
yield checkSyncRateLimitDelay(db);
|
|
3945
|
+
// Check if we need to lock the sync job. Not needed if we are the service worker.
|
|
3793
3946
|
if (db.cloud.isServiceWorkerDB) {
|
|
3794
3947
|
// We are the dedicated sync SW:
|
|
3795
3948
|
yield sync(db, cloudOptions, cloudSchema, options);
|
|
@@ -3797,7 +3950,7 @@
|
|
|
3797
3950
|
else if (!db.cloud.usingServiceWorker) {
|
|
3798
3951
|
// We use a flow that is better suited for the case when multiple workers want to
|
|
3799
3952
|
// do the same thing.
|
|
3800
|
-
yield performGuardedJob(db, CURRENT_SYNC_WORKER,
|
|
3953
|
+
yield performGuardedJob(db, CURRENT_SYNC_WORKER, () => sync(db, cloudOptions, cloudSchema, options));
|
|
3801
3954
|
}
|
|
3802
3955
|
else {
|
|
3803
3956
|
assert(false);
|
|
@@ -3819,19 +3972,29 @@
|
|
|
3819
3972
|
}
|
|
3820
3973
|
}
|
|
3821
3974
|
|
|
3975
|
+
const SECONDS = 1000;
|
|
3976
|
+
const MINUTES = 60 * SECONDS;
|
|
3977
|
+
|
|
3822
3978
|
function LocalSyncWorker(db, cloudOptions, cloudSchema) {
|
|
3823
3979
|
let localSyncEventSubscription = null;
|
|
3824
3980
|
//let syncHandler: ((event: Event) => void) | null = null;
|
|
3825
3981
|
//let periodicSyncHandler: ((event: Event) => void) | null = null;
|
|
3826
3982
|
let cancelToken = { cancelled: false };
|
|
3983
|
+
let retryHandle = null;
|
|
3984
|
+
let retryPurpose = null; // "pull" is superset of "push"
|
|
3827
3985
|
function syncAndRetry(purpose, retryNum = 1) {
|
|
3828
3986
|
// Use setTimeout() to get onto a clean stack and
|
|
3829
3987
|
// break free from possible active transaction:
|
|
3830
3988
|
setTimeout(() => {
|
|
3989
|
+
if (retryHandle)
|
|
3990
|
+
clearTimeout(retryHandle);
|
|
3991
|
+
const combPurpose = retryPurpose === 'pull' ? 'pull' : purpose;
|
|
3992
|
+
retryHandle = null;
|
|
3993
|
+
retryPurpose = null;
|
|
3831
3994
|
syncIfPossible(db, cloudOptions, cloudSchema, {
|
|
3832
3995
|
cancelToken,
|
|
3833
3996
|
retryImmediatelyOnFetchError: true,
|
|
3834
|
-
purpose,
|
|
3997
|
+
purpose: combPurpose,
|
|
3835
3998
|
}).catch((e) => {
|
|
3836
3999
|
console.error('error in syncIfPossible()', e);
|
|
3837
4000
|
if (cancelToken.cancelled) {
|
|
@@ -3841,7 +4004,13 @@
|
|
|
3841
4004
|
// Mimic service worker sync event: retry 3 times
|
|
3842
4005
|
// * first retry after 5 minutes
|
|
3843
4006
|
// * second retry 15 minutes later
|
|
3844
|
-
|
|
4007
|
+
const combinedPurpose = retryPurpose && retryPurpose === 'pull' ? 'pull' : purpose;
|
|
4008
|
+
const handle = setTimeout(() => syncAndRetry(combinedPurpose, retryNum + 1), [0, 5, 15][retryNum] * MINUTES);
|
|
4009
|
+
// Cancel the previous retryHandle if it exists to avoid scheduling loads of retries.
|
|
4010
|
+
if (retryHandle)
|
|
4011
|
+
clearTimeout(retryHandle);
|
|
4012
|
+
retryHandle = handle;
|
|
4013
|
+
retryPurpose = combinedPurpose;
|
|
3845
4014
|
}
|
|
3846
4015
|
});
|
|
3847
4016
|
}, 0);
|
|
@@ -3908,10 +4077,12 @@
|
|
|
3908
4077
|
},
|
|
3909
4078
|
Alert: {
|
|
3910
4079
|
error: {
|
|
3911
|
-
color: "red"
|
|
4080
|
+
color: "red",
|
|
4081
|
+
fontWeight: "bold"
|
|
3912
4082
|
},
|
|
3913
4083
|
warning: {
|
|
3914
|
-
color: "
|
|
4084
|
+
color: "#f80",
|
|
4085
|
+
fontWeight: "bold"
|
|
3915
4086
|
},
|
|
3916
4087
|
info: {
|
|
3917
4088
|
color: "black"
|
|
@@ -3952,7 +4123,8 @@
|
|
|
3952
4123
|
border: "3px solid #3d3d5d",
|
|
3953
4124
|
borderRadius: "8px",
|
|
3954
4125
|
boxShadow: "0 0 80px 10px #666",
|
|
3955
|
-
width: "auto"
|
|
4126
|
+
width: "auto",
|
|
4127
|
+
fontFamily: "sans-serif",
|
|
3956
4128
|
},
|
|
3957
4129
|
Input: {
|
|
3958
4130
|
height: "35px",
|
|
@@ -3973,11 +4145,26 @@
|
|
|
3973
4145
|
|
|
3974
4146
|
var t,r,u,i,o=0,c=[],f=[],e=l$1.__b,a=l$1.__r,v=l$1.diffed,l=l$1.__c,m=l$1.unmount;function d(t,u){l$1.__h&&l$1.__h(r,t,o||u),o=0;var i=r.__H||(r.__H={__:[],__h:[]});return t>=i.__.length&&i.__.push({__V:f}),i.__[t]}function p(n){return o=1,y(z,n)}function y(n,u,i){var o=d(t++,2);if(o.t=n,!o.__c&&(o.__=[i?i(u):z(void 0,u),function(n){var t=o.__N?o.__N[0]:o.__[0],r=o.t(t,n);t!==r&&(o.__N=[r,o.__[1]],o.__c.setState({}));}],o.__c=r,!r.u)){r.u=!0;var c=r.shouldComponentUpdate;r.shouldComponentUpdate=function(n,t,r){if(!o.__c.__H)return !0;var u=o.__c.__H.__.filter(function(n){return n.__c});if(u.every(function(n){return !n.__N}))return !c||c.call(this,n,t,r);var i=!1;return u.forEach(function(n){if(n.__N){var t=n.__[0];n.__=n.__N,n.__N=void 0,t!==n.__[0]&&(i=!0);}}),!!i&&(!c||c.call(this,n,t,r))};}return o.__N||o.__}function s(u,i){var o=d(t++,4);!l$1.__s&&w(o.__H,i)&&(o.__=u,o.i=i,r.__h.push(o));}function _(n){return o=5,F(function(){return {current:n}},[])}function F(n,r){var u=d(t++,7);return w(u.__H,r)?(u.__V=n(),u.i=r,u.__h=n,u.__V):u.__}function b(){for(var t;t=c.shift();)if(t.__P&&t.__H)try{t.__H.__h.forEach(j),t.__H.__h.forEach(k),t.__H.__h=[];}catch(r){t.__H.__h=[],l$1.__e(r,t.__v);}}l$1.__b=function(n){r=null,e&&e(n);},l$1.__r=function(n){a&&a(n),t=0;var i=(r=n.__c).__H;i&&(u===r?(i.__h=[],r.__h=[],i.__.forEach(function(n){n.__N&&(n.__=n.__N),n.__V=f,n.__N=n.i=void 0;})):(i.__h.forEach(j),i.__h.forEach(k),i.__h=[])),u=r;},l$1.diffed=function(t){v&&v(t);var o=t.__c;o&&o.__H&&(o.__H.__h.length&&(1!==c.push(o)&&i===l$1.requestAnimationFrame||((i=l$1.requestAnimationFrame)||function(n){var t,r=function(){clearTimeout(u),g&&cancelAnimationFrame(t),setTimeout(n);},u=setTimeout(r,100);g&&(t=requestAnimationFrame(r));})(b)),o.__H.__.forEach(function(n){n.i&&(n.__H=n.i),n.__V!==f&&(n.__=n.__V),n.i=void 0,n.__V=f;})),u=r=null;},l$1.__c=function(t,r){r.some(function(t){try{t.__h.forEach(j),t.__h=t.__h.filter(function(n){return !n.__||k(n)});}catch(u){r.some(function(n){n.__h&&(n.__h=[]);}),r=[],l$1.__e(u,t.__v);}}),l&&l(t,r);},l$1.unmount=function(t){m&&m(t);var r,u=t.__c;u&&u.__H&&(u.__H.__.forEach(function(n){try{j(n);}catch(n){r=n;}}),r&&l$1.__e(r,u.__v));};var g="function"==typeof requestAnimationFrame;function j(n){var t=r,u=n.__c;"function"==typeof u&&(n.__c=void 0,u()),r=t;}function k(n){var t=r;n.__c=n.__(),r=t;}function w(n,t){return !n||n.length!==t.length||t.some(function(t,r){return t!==n[r]})}function z(n,t){return "function"==typeof t?t(n):t}
|
|
3975
4147
|
|
|
4148
|
+
/** Resolve a message template with parameters.
|
|
4149
|
+
*
|
|
4150
|
+
* Example:
|
|
4151
|
+
* resolveText({
|
|
4152
|
+
* message: "Hello {name}!",
|
|
4153
|
+
* messageCode: "HELLO",
|
|
4154
|
+
* messageParams: {name: "David"}
|
|
4155
|
+
* }) => "Hello David!"
|
|
4156
|
+
*
|
|
4157
|
+
* @param message Template message with {vars} in it.
|
|
4158
|
+
* @param messageCode Unique code for the message. Can be used for translation.
|
|
4159
|
+
* @param messageParams Parameters to be used in the message.
|
|
4160
|
+
* @returns A final message where parameters have been replaced with values.
|
|
4161
|
+
*/
|
|
3976
4162
|
function resolveText({ message, messageCode, messageParams }) {
|
|
3977
|
-
return message.replace(/\{\w+\}/ig, n => messageParams[n.
|
|
4163
|
+
return message.replace(/\{\w+\}/ig, n => messageParams[n.substring(1, n.length - 1)]);
|
|
3978
4164
|
}
|
|
3979
4165
|
|
|
3980
|
-
|
|
4166
|
+
const OTP_LENGTH = 8;
|
|
4167
|
+
function LoginDialog({ title, type, alerts, fields, submitLabel, cancelLabel, onCancel, onSubmit, }) {
|
|
3981
4168
|
const [params, setParams] = p({});
|
|
3982
4169
|
const firstFieldRef = _(null);
|
|
3983
4170
|
s(() => { var _a; return (_a = firstFieldRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, []);
|
|
@@ -3985,21 +4172,34 @@
|
|
|
3985
4172
|
h(p$1, null,
|
|
3986
4173
|
h("h3", { style: Styles.WindowHeader }, title),
|
|
3987
4174
|
alerts.map((alert) => (h("p", { style: Styles.Alert[alert.type] }, resolveText(alert)))),
|
|
3988
|
-
h("form", { onSubmit: ev => {
|
|
4175
|
+
h("form", { onSubmit: (ev) => {
|
|
3989
4176
|
ev.preventDefault();
|
|
3990
4177
|
onSubmit(params);
|
|
3991
|
-
} }, Object.entries(fields).map(([fieldName, { type, label, placeholder }], idx) => (h("label", { style: Styles.Label },
|
|
4178
|
+
} }, Object.entries(fields).map(([fieldName, { type, label, placeholder }], idx) => (h("label", { style: Styles.Label, key: idx },
|
|
3992
4179
|
label ? `${label}: ` : '',
|
|
3993
|
-
h("input", { ref: idx === 0 ? firstFieldRef : undefined, type: type, name: fieldName, autoComplete: "on", style: Styles.Input, autoFocus: true, placeholder: placeholder, value: params[fieldName] || '', onInput: (ev) => {
|
|
4180
|
+
h("input", { ref: idx === 0 ? firstFieldRef : undefined, type: type, name: fieldName, autoComplete: "on", style: Styles.Input, autoFocus: true, placeholder: placeholder, value: params[fieldName] || '', onInput: (ev) => {
|
|
4181
|
+
var _a;
|
|
4182
|
+
const value = valueTransformer(type, (_a = ev.target) === null || _a === void 0 ? void 0 : _a['value']);
|
|
4183
|
+
let updatedParams = Object.assign(Object.assign({}, params), { [fieldName]: value });
|
|
4184
|
+
setParams(updatedParams);
|
|
4185
|
+
if (type === 'otp' && (value === null || value === void 0 ? void 0 : value.trim().length) === OTP_LENGTH) {
|
|
4186
|
+
// Auto-submit when OTP is filled in.
|
|
4187
|
+
onSubmit(updatedParams);
|
|
4188
|
+
}
|
|
4189
|
+
} })))))),
|
|
3994
4190
|
h("div", { style: Styles.ButtonsDiv },
|
|
3995
|
-
h(
|
|
3996
|
-
|
|
4191
|
+
h(p$1, null,
|
|
4192
|
+
h("button", { type: "submit", style: Styles.Button, onClick: () => onSubmit(params) }, submitLabel),
|
|
4193
|
+
cancelLabel && (h("button", { style: Styles.Button, onClick: onCancel }, cancelLabel))))));
|
|
3997
4194
|
}
|
|
3998
4195
|
function valueTransformer(type, value) {
|
|
3999
4196
|
switch (type) {
|
|
4000
|
-
case
|
|
4001
|
-
|
|
4002
|
-
|
|
4197
|
+
case 'email':
|
|
4198
|
+
return value.toLowerCase();
|
|
4199
|
+
case 'otp':
|
|
4200
|
+
return value.toUpperCase();
|
|
4201
|
+
default:
|
|
4202
|
+
return value;
|
|
4003
4203
|
}
|
|
4004
4204
|
}
|
|
4005
4205
|
|
|
@@ -4053,11 +4253,20 @@
|
|
|
4053
4253
|
}
|
|
4054
4254
|
};
|
|
4055
4255
|
}
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4256
|
+
|
|
4257
|
+
function associate(factory) {
|
|
4258
|
+
const wm = new WeakMap();
|
|
4259
|
+
return (x) => {
|
|
4260
|
+
let rv = wm.get(x);
|
|
4261
|
+
if (!rv) {
|
|
4262
|
+
rv = factory(x);
|
|
4263
|
+
wm.set(x, rv);
|
|
4264
|
+
}
|
|
4265
|
+
return rv;
|
|
4266
|
+
};
|
|
4267
|
+
}
|
|
4268
|
+
|
|
4269
|
+
const getCurrentUserEmitter = associate((db) => new rxjs.BehaviorSubject(UNAUTHORIZED_USER));
|
|
4061
4270
|
|
|
4062
4271
|
function computeSyncState(db) {
|
|
4063
4272
|
let _prevStatus = db.cloud.webSocketStatus.value;
|
|
@@ -4085,8 +4294,17 @@
|
|
|
4085
4294
|
return rxjs.combineLatest([
|
|
4086
4295
|
lazyWebSocketStatus,
|
|
4087
4296
|
db.syncStateChangedEvent.pipe(operators.startWith({ phase: 'initial' })),
|
|
4297
|
+
getCurrentUserEmitter(db.dx._novip),
|
|
4088
4298
|
userIsReallyActive
|
|
4089
|
-
]).pipe(operators.map(([status, syncState, userIsActive]) => {
|
|
4299
|
+
]).pipe(operators.map(([status, syncState, user, userIsActive]) => {
|
|
4300
|
+
var _a;
|
|
4301
|
+
if (((_a = user.license) === null || _a === void 0 ? void 0 : _a.status) && user.license.status !== 'ok') {
|
|
4302
|
+
return {
|
|
4303
|
+
phase: 'offline',
|
|
4304
|
+
status: 'offline',
|
|
4305
|
+
license: user.license.status
|
|
4306
|
+
};
|
|
4307
|
+
}
|
|
4090
4308
|
let { phase, error, progress } = syncState;
|
|
4091
4309
|
let adjustedStatus = status;
|
|
4092
4310
|
if (phase === 'error') {
|
|
@@ -4119,23 +4337,12 @@
|
|
|
4119
4337
|
error,
|
|
4120
4338
|
progress,
|
|
4121
4339
|
status: isOnline ? adjustedStatus : 'offline',
|
|
4340
|
+
license: 'ok'
|
|
4122
4341
|
};
|
|
4123
4342
|
return retState;
|
|
4124
4343
|
}));
|
|
4125
4344
|
}
|
|
4126
4345
|
|
|
4127
|
-
function associate(factory) {
|
|
4128
|
-
const wm = new WeakMap();
|
|
4129
|
-
return (x) => {
|
|
4130
|
-
let rv = wm.get(x);
|
|
4131
|
-
if (!rv) {
|
|
4132
|
-
rv = factory(x);
|
|
4133
|
-
wm.set(x, rv);
|
|
4134
|
-
}
|
|
4135
|
-
return rv;
|
|
4136
|
-
};
|
|
4137
|
-
}
|
|
4138
|
-
|
|
4139
4346
|
function createSharedValueObservable(o, defaultValue) {
|
|
4140
4347
|
let currentValue = defaultValue;
|
|
4141
4348
|
let shared = rxjs.from(o).pipe(rxjs.map((x) => (currentValue = x)), rxjs.share({ resetOnRefCountZero: () => rxjs.timer(1000) }));
|
|
@@ -4177,8 +4384,6 @@
|
|
|
4177
4384
|
})), {});
|
|
4178
4385
|
});
|
|
4179
4386
|
|
|
4180
|
-
const getCurrentUserEmitter = associate((db) => new rxjs.BehaviorSubject(UNAUTHORIZED_USER));
|
|
4181
|
-
|
|
4182
4387
|
const getInternalAccessControlObservable = associate((db) => {
|
|
4183
4388
|
return createSharedValueObservable(getCurrentUserEmitter(db._novip).pipe(operators.switchMap((currentUser) => Dexie.liveQuery(() => db.transaction('r', 'realms', 'members', () => Promise.all([
|
|
4184
4389
|
db.members.where({ userId: currentUser.userId }).toArray(),
|
|
@@ -4461,7 +4666,7 @@
|
|
|
4461
4666
|
});
|
|
4462
4667
|
const syncComplete = new rxjs.Subject();
|
|
4463
4668
|
dexie.cloud = {
|
|
4464
|
-
version: '4.0.1-beta.
|
|
4669
|
+
version: '4.0.1-beta.47',
|
|
4465
4670
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
4466
4671
|
schema: null,
|
|
4467
4672
|
get currentUserId() {
|
|
@@ -4497,11 +4702,24 @@
|
|
|
4497
4702
|
}
|
|
4498
4703
|
updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
|
|
4499
4704
|
},
|
|
4705
|
+
logout({ force } = {}) {
|
|
4706
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4707
|
+
force
|
|
4708
|
+
? yield _logout(DexieCloudDB(dexie), { deleteUnsyncedData: true })
|
|
4709
|
+
: yield logout(DexieCloudDB(dexie));
|
|
4710
|
+
});
|
|
4711
|
+
},
|
|
4500
4712
|
sync({ wait, purpose } = { wait: true, purpose: 'push' }) {
|
|
4713
|
+
var _a;
|
|
4501
4714
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4502
4715
|
if (wait === undefined)
|
|
4503
4716
|
wait = true;
|
|
4504
4717
|
const db = DexieCloudDB(dexie);
|
|
4718
|
+
const licenseStatus = ((_a = db.cloud.currentUser.value.license) === null || _a === void 0 ? void 0 : _a.status) || 'ok';
|
|
4719
|
+
if (licenseStatus !== 'ok') {
|
|
4720
|
+
// Refresh access token to check for updated license
|
|
4721
|
+
yield loadAccessToken(db);
|
|
4722
|
+
}
|
|
4505
4723
|
if (purpose === 'pull') {
|
|
4506
4724
|
const syncState = db.cloud.persistedSyncState.value;
|
|
4507
4725
|
triggerSync(db, purpose);
|
|
@@ -4705,7 +4923,9 @@
|
|
|
4705
4923
|
db.syncStateChangedEvent.next({
|
|
4706
4924
|
phase: 'not-in-sync',
|
|
4707
4925
|
});
|
|
4708
|
-
|
|
4926
|
+
if (!isEagerSyncDisabled(db)) {
|
|
4927
|
+
triggerSync(db, 'push');
|
|
4928
|
+
}
|
|
4709
4929
|
}), rxjs.fromEvent(self, 'offline').subscribe(() => {
|
|
4710
4930
|
console.debug('offline!');
|
|
4711
4931
|
db.syncStateChangedEvent.next({
|
|
@@ -4722,7 +4942,7 @@
|
|
|
4722
4942
|
});
|
|
4723
4943
|
}
|
|
4724
4944
|
}
|
|
4725
|
-
dexieCloud.version = '4.0.1-beta.
|
|
4945
|
+
dexieCloud.version = '4.0.1-beta.47';
|
|
4726
4946
|
Dexie__default["default"].Cloud = dexieCloud;
|
|
4727
4947
|
|
|
4728
4948
|
// In case the SW lives for a while, let it reuse already opened connections:
|