dexie-cloud-addon 4.0.1-beta.46 → 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 +471 -313
- 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 +455 -245
- 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 +470 -312
- 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 +453 -243
- 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
|
});
|
|
@@ -1588,6 +1684,40 @@
|
|
|
1588
1684
|
: change.muts.map((m) => (Object.assign(Object.assign({}, m), { keys: m.keys.slice() }))) });
|
|
1589
1685
|
}
|
|
1590
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
|
+
|
|
1591
1721
|
//import {BisonWebStreamReader} from "dreambase-library/dist/typeson-simplified/BisonWebStreamReader";
|
|
1592
1722
|
function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, clientIdentity, currentUser) {
|
|
1593
1723
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1596,9 +1726,20 @@
|
|
|
1596
1726
|
//
|
|
1597
1727
|
const headers = {
|
|
1598
1728
|
Accept: 'application/json, application/x-bison, application/x-bison-stream',
|
|
1599
|
-
'Content-Type': 'application/tson'
|
|
1729
|
+
'Content-Type': 'application/tson',
|
|
1600
1730
|
};
|
|
1601
|
-
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;
|
|
1602
1743
|
if (accessToken) {
|
|
1603
1744
|
headers.Authorization = `Bearer ${accessToken}`;
|
|
1604
1745
|
}
|
|
@@ -1607,27 +1748,31 @@
|
|
|
1607
1748
|
dbID: syncState === null || syncState === void 0 ? void 0 : syncState.remoteDbId,
|
|
1608
1749
|
clientIdentity,
|
|
1609
1750
|
schema: schema || {},
|
|
1610
|
-
lastPull: syncState
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1751
|
+
lastPull: syncState
|
|
1752
|
+
? {
|
|
1753
|
+
serverRevision: syncState.serverRevision,
|
|
1754
|
+
realms: syncState.realms,
|
|
1755
|
+
inviteRealms: syncState.inviteRealms,
|
|
1756
|
+
}
|
|
1757
|
+
: undefined,
|
|
1615
1758
|
baseRevs,
|
|
1616
|
-
changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes)
|
|
1759
|
+
changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes),
|
|
1617
1760
|
};
|
|
1618
|
-
console.debug(
|
|
1761
|
+
console.debug('Sync request', syncRequest);
|
|
1619
1762
|
db.syncStateChangedEvent.next({
|
|
1620
1763
|
phase: 'pushing',
|
|
1621
1764
|
});
|
|
1622
1765
|
const res = yield fetch(`${databaseUrl}/sync`, {
|
|
1623
1766
|
method: 'post',
|
|
1624
1767
|
headers,
|
|
1625
|
-
|
|
1768
|
+
credentials: 'include',
|
|
1769
|
+
body: TSON.stringify(syncRequest),
|
|
1626
1770
|
});
|
|
1627
1771
|
//const contentLength = Number(res.headers.get('content-length'));
|
|
1628
1772
|
db.syncStateChangedEvent.next({
|
|
1629
|
-
phase: 'pulling'
|
|
1773
|
+
phase: 'pulling',
|
|
1630
1774
|
});
|
|
1775
|
+
updateSyncRateLimitDelays(db, res);
|
|
1631
1776
|
if (!res.ok) {
|
|
1632
1777
|
throw new HttpError(res);
|
|
1633
1778
|
}
|
|
@@ -1829,12 +1974,13 @@
|
|
|
1829
1974
|
function sync(db, options, schema, syncOptions) {
|
|
1830
1975
|
return _sync
|
|
1831
1976
|
.apply(this, arguments)
|
|
1832
|
-
.then(() => {
|
|
1977
|
+
.then((result) => {
|
|
1833
1978
|
if (!(syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)) { // && syncOptions?.purpose !== 'push') {
|
|
1834
1979
|
db.syncStateChangedEvent.next({
|
|
1835
1980
|
phase: 'in-sync',
|
|
1836
1981
|
});
|
|
1837
1982
|
}
|
|
1983
|
+
return result;
|
|
1838
1984
|
})
|
|
1839
1985
|
.catch((error) => __awaiter(this, void 0, void 0, function* () {
|
|
1840
1986
|
if (syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)
|
|
@@ -2049,6 +2195,7 @@
|
|
|
2049
2195
|
}));
|
|
2050
2196
|
if (!done) {
|
|
2051
2197
|
console.debug('MORE SYNC NEEDED. Go for it again!');
|
|
2198
|
+
yield checkSyncRateLimitDelay(db);
|
|
2052
2199
|
return yield _sync(db, options, schema, { isInitialSync, cancelToken });
|
|
2053
2200
|
}
|
|
2054
2201
|
console.debug('SYNC DONE', { isInitialSync });
|
|
@@ -2185,6 +2332,8 @@
|
|
|
2185
2332
|
yield db.table('$logins').update(user.userId, {
|
|
2186
2333
|
accessToken: refreshedLogin.accessToken,
|
|
2187
2334
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
2335
|
+
claims: refreshedLogin.claims,
|
|
2336
|
+
license: refreshedLogin.license,
|
|
2188
2337
|
});
|
|
2189
2338
|
// Updating $logins will trigger emission of db.cloud.currentUser observable, which
|
|
2190
2339
|
// in turn will lead to that connectWebSocket.ts will reconnect the socket with the
|
|
@@ -2455,6 +2604,61 @@
|
|
|
2455
2604
|
}
|
|
2456
2605
|
}
|
|
2457
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
|
+
|
|
2458
2662
|
function otpFetchTokenCallback(db) {
|
|
2459
2663
|
const { userInteraction } = db.cloud;
|
|
2460
2664
|
return function otpAuthenticate({ public_key, hints }) {
|
|
@@ -2498,8 +2702,9 @@
|
|
|
2498
2702
|
throw new HttpError(res1, errMsg);
|
|
2499
2703
|
}
|
|
2500
2704
|
const response = yield res1.json();
|
|
2501
|
-
if (response.type === 'tokens') {
|
|
2705
|
+
if (response.type === 'tokens' || response.type === 'error') {
|
|
2502
2706
|
// Demo user request can get a "tokens" response right away
|
|
2707
|
+
// Error can also be returned right away.
|
|
2503
2708
|
return response;
|
|
2504
2709
|
}
|
|
2505
2710
|
else if (tokenRequest.grant_type === 'otp') {
|
|
@@ -2531,12 +2736,6 @@
|
|
|
2531
2736
|
}
|
|
2532
2737
|
if (res2.status !== 200) {
|
|
2533
2738
|
const errMsg = yield res2.text();
|
|
2534
|
-
yield alertUser(userInteraction, "OTP Authentication Failed", {
|
|
2535
|
-
type: 'error',
|
|
2536
|
-
messageCode: 'GENERIC_ERROR',
|
|
2537
|
-
message: errMsg,
|
|
2538
|
-
messageParams: {}
|
|
2539
|
-
}).catch(() => { });
|
|
2540
2739
|
throw new HttpError(res2, errMsg);
|
|
2541
2740
|
}
|
|
2542
2741
|
const response2 = yield res2.json();
|
|
@@ -2549,6 +2748,18 @@
|
|
|
2549
2748
|
};
|
|
2550
2749
|
}
|
|
2551
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
|
+
|
|
2552
2763
|
/** This function changes or sets the current user as requested.
|
|
2553
2764
|
*
|
|
2554
2765
|
* Use cases:
|
|
@@ -2575,85 +2786,73 @@
|
|
|
2575
2786
|
}));
|
|
2576
2787
|
user.isLoggedIn = true;
|
|
2577
2788
|
user.lastLogin = new Date();
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
}));
|
|
2581
|
-
yield new Promise((resolve) => {
|
|
2582
|
-
if (db.cloud.currentUserId === user.userId) {
|
|
2583
|
-
resolve(null);
|
|
2789
|
+
try {
|
|
2790
|
+
yield user.save();
|
|
2584
2791
|
}
|
|
2585
|
-
|
|
2586
|
-
|
|
2587
|
-
if (
|
|
2588
|
-
|
|
2589
|
-
|
|
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));
|
|
2590
2801
|
}
|
|
2591
|
-
}
|
|
2802
|
+
}
|
|
2803
|
+
catch (_a) { }
|
|
2804
|
+
throw e;
|
|
2592
2805
|
}
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
// V: Samma med andra windows.
|
|
2597
|
-
// V: Så kanske göra om den till att häröra från liveQuery som läser $logins.orderBy('lastLogin').last().
|
|
2598
|
-
// V: Då bara vara medveten om:
|
|
2599
|
-
// V: En sån observable börjar hämta data vid första subscribe
|
|
2600
|
-
// V: Vi har inget "inital value" men kan emulera det till att vara ANONYMOUS_USER
|
|
2601
|
-
// V: Om requireAuth är true, så borde db.on(ready) hålla databasen stängd för alla utom denna observable.
|
|
2602
|
-
// V: Om inte så behöver den inte blocka.
|
|
2603
|
-
// Andra tankar:
|
|
2604
|
-
// * Man kan inte byta användare när man är offline. Skulle gå att flytta realms till undanstuff-tabell vid user-change.
|
|
2605
|
-
// men troligen inte värt det.
|
|
2606
|
-
// * Istället: sälj inte inte switch-user funktionalitet utan tala enbart om inloggat vs icke inloggat läge.
|
|
2607
|
-
// * populate $logins med ANONYMOUS så att en påbörjad inloggning inte räknas, alternativt ha en boolean prop!
|
|
2608
|
-
// Kanske bäst ha en boolean prop!
|
|
2609
|
-
// * Alternativ switch-user funktionalitet:
|
|
2610
|
-
// * DBCore gömmer data från realms man inte har tillgång till.
|
|
2611
|
-
// * Cursor impl behövs också då.
|
|
2612
|
-
// * Då blir det snabba user switch.
|
|
2613
|
-
// * claims-settet som skickas till servern blir summan av alla claims. Då måste servern stödja multipla tokens eller
|
|
2614
|
-
// 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);
|
|
2615
2809
|
});
|
|
2616
2810
|
}
|
|
2617
2811
|
|
|
2618
2812
|
function login(db, hints) {
|
|
2813
|
+
var _a;
|
|
2619
2814
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2620
2815
|
const currentUser = yield db.getCurrentUser();
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
|
|
2624
|
-
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
throw new Error(`Must logout before changing user`);
|
|
2628
|
-
}
|
|
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;
|
|
2629
2822
|
}
|
|
2630
|
-
|
|
2631
|
-
|
|
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:
|
|
2632
2829
|
}
|
|
2633
2830
|
const context = new AuthPersistedContext(db, {
|
|
2634
2831
|
claims: {},
|
|
2635
2832
|
lastLogin: new Date(0),
|
|
2636
2833
|
});
|
|
2637
2834
|
yield authenticate(db.cloud.options.databaseUrl, context, db.cloud.options.fetchTokens || otpFetchTokenCallback(db), db.cloud.userInteraction, hints);
|
|
2638
|
-
|
|
2639
|
-
|
|
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);
|
|
2640
2838
|
}
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
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));
|
|
2648
2847
|
}
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
}
|
|
2848
|
+
} catch {}
|
|
2849
|
+
throw e;
|
|
2850
|
+
}*/
|
|
2652
2851
|
yield setCurrentUser(db, context);
|
|
2653
2852
|
// Make sure to resync as the new login will be authorized
|
|
2654
2853
|
// for new realms.
|
|
2655
2854
|
triggerSync(db, "pull");
|
|
2656
|
-
return
|
|
2855
|
+
return context.userId !== origUserId;
|
|
2657
2856
|
});
|
|
2658
2857
|
}
|
|
2659
2858
|
|
|
@@ -2982,6 +3181,13 @@
|
|
|
2982
3181
|
|
|
2983
3182
|
const outstandingTransactions = new rxjs.BehaviorSubject(new Set());
|
|
2984
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
|
+
|
|
2985
3191
|
/** Tracks all mutations in the same transaction as the mutations -
|
|
2986
3192
|
* so it is guaranteed that no mutation goes untracked - and if transaction
|
|
2987
3193
|
* aborts, the mutations won't be tracked.
|
|
@@ -2990,7 +3196,7 @@
|
|
|
2990
3196
|
* changes to server and cleanup the tracked mutations once the server has
|
|
2991
3197
|
* ackowledged that it got them.
|
|
2992
3198
|
*/
|
|
2993
|
-
function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
3199
|
+
function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
2994
3200
|
return {
|
|
2995
3201
|
stack: 'dbcore',
|
|
2996
3202
|
name: 'MutationTrackingMiddleware',
|
|
@@ -3001,7 +3207,7 @@
|
|
|
3001
3207
|
try {
|
|
3002
3208
|
mutTableMap = new Map(ordinaryTables.map((tbl) => [
|
|
3003
3209
|
tbl.name,
|
|
3004
|
-
core.table(`$${tbl.name}_mutations`)
|
|
3210
|
+
core.table(`$${tbl.name}_mutations`),
|
|
3005
3211
|
]));
|
|
3006
3212
|
}
|
|
3007
3213
|
catch (_a) {
|
|
@@ -3035,15 +3241,9 @@
|
|
|
3035
3241
|
outstandingTransactions.next(outstandingTransactions.value);
|
|
3036
3242
|
};
|
|
3037
3243
|
const txComplete = () => {
|
|
3038
|
-
|
|
3039
|
-
|
|
3040
|
-
|
|
3041
|
-
console.debug('registering sync event');
|
|
3042
|
-
registerSyncEvent(db, "push");
|
|
3043
|
-
}
|
|
3044
|
-
else {
|
|
3045
|
-
db.localSyncEvent.next({ purpose: "push" });
|
|
3046
|
-
}
|
|
3244
|
+
if (tx.mutationsAdded &&
|
|
3245
|
+
!isEagerSyncDisabled(db)) {
|
|
3246
|
+
triggerSync(db, 'push');
|
|
3047
3247
|
}
|
|
3048
3248
|
removeTransaction();
|
|
3049
3249
|
};
|
|
@@ -3110,7 +3310,7 @@
|
|
|
3110
3310
|
.query({
|
|
3111
3311
|
query: { range: req.range, index: schema.primaryKey },
|
|
3112
3312
|
trans: req.trans,
|
|
3113
|
-
values: false
|
|
3313
|
+
values: false,
|
|
3114
3314
|
})
|
|
3115
3315
|
// Do a delete request instead, but keep the criteria info for the server to execute
|
|
3116
3316
|
.then((res) => {
|
|
@@ -3118,7 +3318,7 @@
|
|
|
3118
3318
|
type: 'delete',
|
|
3119
3319
|
keys: res.result,
|
|
3120
3320
|
trans: req.trans,
|
|
3121
|
-
criteria: { index: null, range: req.range }
|
|
3321
|
+
criteria: { index: null, range: req.range },
|
|
3122
3322
|
});
|
|
3123
3323
|
})
|
|
3124
3324
|
: mutateAndLog(req);
|
|
@@ -3126,7 +3326,7 @@
|
|
|
3126
3326
|
function mutateAndLog(req) {
|
|
3127
3327
|
const trans = req.trans;
|
|
3128
3328
|
trans.mutationsAdded = true;
|
|
3129
|
-
const { txid, currentUser: { userId } } = trans;
|
|
3329
|
+
const { txid, currentUser: { userId }, } = trans;
|
|
3130
3330
|
const { type } = req;
|
|
3131
3331
|
const opNo = ++trans.opCount;
|
|
3132
3332
|
return table.mutate(req).then((res) => {
|
|
@@ -3147,7 +3347,7 @@
|
|
|
3147
3347
|
keys,
|
|
3148
3348
|
criteria: req.criteria,
|
|
3149
3349
|
txid,
|
|
3150
|
-
userId
|
|
3350
|
+
userId,
|
|
3151
3351
|
}
|
|
3152
3352
|
: req.type === 'add'
|
|
3153
3353
|
? {
|
|
@@ -3157,7 +3357,7 @@
|
|
|
3157
3357
|
keys,
|
|
3158
3358
|
txid,
|
|
3159
3359
|
userId,
|
|
3160
|
-
values
|
|
3360
|
+
values,
|
|
3161
3361
|
}
|
|
3162
3362
|
: req.criteria && req.changeSpec
|
|
3163
3363
|
? {
|
|
@@ -3169,7 +3369,7 @@
|
|
|
3169
3369
|
criteria: req.criteria,
|
|
3170
3370
|
changeSpec: req.changeSpec,
|
|
3171
3371
|
txid,
|
|
3172
|
-
userId
|
|
3372
|
+
userId,
|
|
3173
3373
|
}
|
|
3174
3374
|
: updates
|
|
3175
3375
|
? {
|
|
@@ -3180,7 +3380,7 @@
|
|
|
3180
3380
|
keys: updates.keys,
|
|
3181
3381
|
changeSpecs: updates.changeSpecs,
|
|
3182
3382
|
txid,
|
|
3183
|
-
userId
|
|
3383
|
+
userId,
|
|
3184
3384
|
}
|
|
3185
3385
|
: {
|
|
3186
3386
|
type: 'upsert',
|
|
@@ -3189,7 +3389,7 @@
|
|
|
3189
3389
|
keys,
|
|
3190
3390
|
values,
|
|
3191
3391
|
txid,
|
|
3192
|
-
userId
|
|
3392
|
+
userId,
|
|
3193
3393
|
};
|
|
3194
3394
|
return keys.length > 0 || ('criteria' in req && req.criteria)
|
|
3195
3395
|
? mutsTable
|
|
@@ -3199,7 +3399,7 @@
|
|
|
3199
3399
|
});
|
|
3200
3400
|
}
|
|
3201
3401
|
} });
|
|
3202
|
-
}
|
|
3402
|
+
},
|
|
3203
3403
|
};
|
|
3204
3404
|
}
|
|
3205
3405
|
|
|
@@ -3558,6 +3758,20 @@
|
|
|
3558
3758
|
}
|
|
3559
3759
|
}
|
|
3560
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
|
+
|
|
3561
3775
|
function sleep$1(ms) {
|
|
3562
3776
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3563
3777
|
}
|
|
@@ -3591,7 +3805,12 @@
|
|
|
3591
3805
|
function createObservable() {
|
|
3592
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.
|
|
3593
3807
|
operators.take(1), // Don't continue waking up whenever syncState change
|
|
3594
|
-
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]) => {
|
|
3595
3814
|
if ((userLogin === null || userLogin === void 0 ? void 0 : userLogin.isLoggedIn) && !(syncState === null || syncState === void 0 ? void 0 : syncState.realms.includes(userLogin.userId))) {
|
|
3596
3815
|
// We're in an in-between state when user is logged in but the user's realms are not yet synced.
|
|
3597
3816
|
// Don't make this change reconnect the websocket just yet. Wait till syncState is updated
|
|
@@ -3620,14 +3839,20 @@
|
|
|
3620
3839
|
yield db.table('$logins').update(user.userId, {
|
|
3621
3840
|
accessToken: refreshedLogin.accessToken,
|
|
3622
3841
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
3842
|
+
claims: refreshedLogin.claims,
|
|
3843
|
+
license: refreshedLogin.license,
|
|
3623
3844
|
});
|
|
3624
3845
|
})), operators.switchMap(() => createObservable()));
|
|
3625
3846
|
}
|
|
3626
3847
|
else {
|
|
3627
|
-
return rxjs.throwError(error);
|
|
3848
|
+
return rxjs.throwError(() => error);
|
|
3628
3849
|
}
|
|
3629
3850
|
}), operators.catchError((error) => {
|
|
3630
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
|
+
}
|
|
3631
3856
|
return rxjs.from(waitAndReconnectWhenUserDoesSomething(error)).pipe(operators.switchMap(() => createObservable()));
|
|
3632
3857
|
}));
|
|
3633
3858
|
}
|
|
@@ -3656,97 +3881,12 @@
|
|
|
3656
3881
|
});
|
|
3657
3882
|
}
|
|
3658
3883
|
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
|
|
3662
|
-
|
|
3663
|
-
|
|
3664
|
-
|
|
3665
|
-
const GUARDED_JOB_TIMEOUT = 1 * MINUTES;
|
|
3666
|
-
function performGuardedJob(db, jobName, jobsTableName, job, { awaitRemoteJob } = {}) {
|
|
3667
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
3668
|
-
// Start working.
|
|
3669
|
-
//
|
|
3670
|
-
// Check if someone else is working on this already.
|
|
3671
|
-
//
|
|
3672
|
-
const jobsTable = db.table(jobsTableName);
|
|
3673
|
-
function aquireLock() {
|
|
3674
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
3675
|
-
const gotTheLock = yield db.transaction('rw!', jobsTableName, () => __awaiter(this, void 0, void 0, function* () {
|
|
3676
|
-
const currentWork = yield jobsTable.get(jobName);
|
|
3677
|
-
if (!currentWork) {
|
|
3678
|
-
// No one else is working. Let's record that we are.
|
|
3679
|
-
yield jobsTable.add({
|
|
3680
|
-
nodeId: myId,
|
|
3681
|
-
started: new Date(),
|
|
3682
|
-
heartbeat: new Date()
|
|
3683
|
-
}, jobName);
|
|
3684
|
-
return true;
|
|
3685
|
-
}
|
|
3686
|
-
else if (currentWork.heartbeat.getTime() <
|
|
3687
|
-
Date.now() - GUARDED_JOB_TIMEOUT) {
|
|
3688
|
-
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!`);
|
|
3689
|
-
// Now, take over!
|
|
3690
|
-
yield jobsTable.put({
|
|
3691
|
-
nodeId: myId,
|
|
3692
|
-
started: new Date(),
|
|
3693
|
-
heartbeat: new Date()
|
|
3694
|
-
}, jobName);
|
|
3695
|
-
return true;
|
|
3696
|
-
}
|
|
3697
|
-
return false;
|
|
3698
|
-
}));
|
|
3699
|
-
if (gotTheLock)
|
|
3700
|
-
return true;
|
|
3701
|
-
// Someone else took the job.
|
|
3702
|
-
if (awaitRemoteJob) {
|
|
3703
|
-
try {
|
|
3704
|
-
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.
|
|
3705
|
-
yield jobDoneObservable.toPromise();
|
|
3706
|
-
return false;
|
|
3707
|
-
}
|
|
3708
|
-
catch (err) {
|
|
3709
|
-
if (err.name !== 'TimeoutError') {
|
|
3710
|
-
throw err;
|
|
3711
|
-
}
|
|
3712
|
-
// Timeout stopped us! Try aquire the lock now.
|
|
3713
|
-
// It will likely succeed this time unless
|
|
3714
|
-
// another client took it.
|
|
3715
|
-
return yield aquireLock();
|
|
3716
|
-
}
|
|
3717
|
-
}
|
|
3718
|
-
return false;
|
|
3719
|
-
});
|
|
3720
|
-
}
|
|
3721
|
-
if (yield aquireLock()) {
|
|
3722
|
-
// We own the lock entry and can do our job undisturbed.
|
|
3723
|
-
// We're not within a transaction, but these type of locks
|
|
3724
|
-
// spans over transactions.
|
|
3725
|
-
// Start our heart beat during the job.
|
|
3726
|
-
// Use setInterval to make sure we are updating heartbeat even during long-lived fetch calls.
|
|
3727
|
-
const heartbeat = setInterval(() => {
|
|
3728
|
-
jobsTable.update(jobName, (job) => {
|
|
3729
|
-
if (job.nodeId === myId) {
|
|
3730
|
-
job.heartbeat = new Date();
|
|
3731
|
-
}
|
|
3732
|
-
});
|
|
3733
|
-
}, GUARDED_JOB_HEARTBEAT);
|
|
3734
|
-
try {
|
|
3735
|
-
return yield job();
|
|
3736
|
-
}
|
|
3737
|
-
finally {
|
|
3738
|
-
// Stop heartbeat
|
|
3739
|
-
clearInterval(heartbeat);
|
|
3740
|
-
// Remove the persisted job state:
|
|
3741
|
-
yield db.transaction('rw!', jobsTableName, () => __awaiter(this, void 0, void 0, function* () {
|
|
3742
|
-
const currentWork = yield jobsTable.get(jobName);
|
|
3743
|
-
if (currentWork && currentWork.nodeId === myId) {
|
|
3744
|
-
yield jobsTable.delete(jobName);
|
|
3745
|
-
}
|
|
3746
|
-
}));
|
|
3747
|
-
}
|
|
3748
|
-
}
|
|
3749
|
-
});
|
|
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());
|
|
3750
3890
|
}
|
|
3751
3891
|
|
|
3752
3892
|
const ongoingSyncs = new WeakMap();
|
|
@@ -3800,6 +3940,9 @@
|
|
|
3800
3940
|
function _syncIfPossible() {
|
|
3801
3941
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3802
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.
|
|
3803
3946
|
if (db.cloud.isServiceWorkerDB) {
|
|
3804
3947
|
// We are the dedicated sync SW:
|
|
3805
3948
|
yield sync(db, cloudOptions, cloudSchema, options);
|
|
@@ -3807,7 +3950,7 @@
|
|
|
3807
3950
|
else if (!db.cloud.usingServiceWorker) {
|
|
3808
3951
|
// We use a flow that is better suited for the case when multiple workers want to
|
|
3809
3952
|
// do the same thing.
|
|
3810
|
-
yield performGuardedJob(db, CURRENT_SYNC_WORKER,
|
|
3953
|
+
yield performGuardedJob(db, CURRENT_SYNC_WORKER, () => sync(db, cloudOptions, cloudSchema, options));
|
|
3811
3954
|
}
|
|
3812
3955
|
else {
|
|
3813
3956
|
assert(false);
|
|
@@ -3829,19 +3972,29 @@
|
|
|
3829
3972
|
}
|
|
3830
3973
|
}
|
|
3831
3974
|
|
|
3975
|
+
const SECONDS = 1000;
|
|
3976
|
+
const MINUTES = 60 * SECONDS;
|
|
3977
|
+
|
|
3832
3978
|
function LocalSyncWorker(db, cloudOptions, cloudSchema) {
|
|
3833
3979
|
let localSyncEventSubscription = null;
|
|
3834
3980
|
//let syncHandler: ((event: Event) => void) | null = null;
|
|
3835
3981
|
//let periodicSyncHandler: ((event: Event) => void) | null = null;
|
|
3836
3982
|
let cancelToken = { cancelled: false };
|
|
3983
|
+
let retryHandle = null;
|
|
3984
|
+
let retryPurpose = null; // "pull" is superset of "push"
|
|
3837
3985
|
function syncAndRetry(purpose, retryNum = 1) {
|
|
3838
3986
|
// Use setTimeout() to get onto a clean stack and
|
|
3839
3987
|
// break free from possible active transaction:
|
|
3840
3988
|
setTimeout(() => {
|
|
3989
|
+
if (retryHandle)
|
|
3990
|
+
clearTimeout(retryHandle);
|
|
3991
|
+
const combPurpose = retryPurpose === 'pull' ? 'pull' : purpose;
|
|
3992
|
+
retryHandle = null;
|
|
3993
|
+
retryPurpose = null;
|
|
3841
3994
|
syncIfPossible(db, cloudOptions, cloudSchema, {
|
|
3842
3995
|
cancelToken,
|
|
3843
3996
|
retryImmediatelyOnFetchError: true,
|
|
3844
|
-
purpose,
|
|
3997
|
+
purpose: combPurpose,
|
|
3845
3998
|
}).catch((e) => {
|
|
3846
3999
|
console.error('error in syncIfPossible()', e);
|
|
3847
4000
|
if (cancelToken.cancelled) {
|
|
@@ -3851,7 +4004,13 @@
|
|
|
3851
4004
|
// Mimic service worker sync event: retry 3 times
|
|
3852
4005
|
// * first retry after 5 minutes
|
|
3853
4006
|
// * second retry 15 minutes later
|
|
3854
|
-
|
|
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;
|
|
3855
4014
|
}
|
|
3856
4015
|
});
|
|
3857
4016
|
}, 0);
|
|
@@ -3918,10 +4077,12 @@
|
|
|
3918
4077
|
},
|
|
3919
4078
|
Alert: {
|
|
3920
4079
|
error: {
|
|
3921
|
-
color: "red"
|
|
4080
|
+
color: "red",
|
|
4081
|
+
fontWeight: "bold"
|
|
3922
4082
|
},
|
|
3923
4083
|
warning: {
|
|
3924
|
-
color: "
|
|
4084
|
+
color: "#f80",
|
|
4085
|
+
fontWeight: "bold"
|
|
3925
4086
|
},
|
|
3926
4087
|
info: {
|
|
3927
4088
|
color: "black"
|
|
@@ -3962,7 +4123,8 @@
|
|
|
3962
4123
|
border: "3px solid #3d3d5d",
|
|
3963
4124
|
borderRadius: "8px",
|
|
3964
4125
|
boxShadow: "0 0 80px 10px #666",
|
|
3965
|
-
width: "auto"
|
|
4126
|
+
width: "auto",
|
|
4127
|
+
fontFamily: "sans-serif",
|
|
3966
4128
|
},
|
|
3967
4129
|
Input: {
|
|
3968
4130
|
height: "35px",
|
|
@@ -3983,11 +4145,26 @@
|
|
|
3983
4145
|
|
|
3984
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}
|
|
3985
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
|
+
*/
|
|
3986
4162
|
function resolveText({ message, messageCode, messageParams }) {
|
|
3987
|
-
return message.replace(/\{\w+\}/ig, n => messageParams[n.
|
|
4163
|
+
return message.replace(/\{\w+\}/ig, n => messageParams[n.substring(1, n.length - 1)]);
|
|
3988
4164
|
}
|
|
3989
4165
|
|
|
3990
|
-
|
|
4166
|
+
const OTP_LENGTH = 8;
|
|
4167
|
+
function LoginDialog({ title, type, alerts, fields, submitLabel, cancelLabel, onCancel, onSubmit, }) {
|
|
3991
4168
|
const [params, setParams] = p({});
|
|
3992
4169
|
const firstFieldRef = _(null);
|
|
3993
4170
|
s(() => { var _a; return (_a = firstFieldRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, []);
|
|
@@ -3995,21 +4172,34 @@
|
|
|
3995
4172
|
h(p$1, null,
|
|
3996
4173
|
h("h3", { style: Styles.WindowHeader }, title),
|
|
3997
4174
|
alerts.map((alert) => (h("p", { style: Styles.Alert[alert.type] }, resolveText(alert)))),
|
|
3998
|
-
h("form", { onSubmit: ev => {
|
|
4175
|
+
h("form", { onSubmit: (ev) => {
|
|
3999
4176
|
ev.preventDefault();
|
|
4000
4177
|
onSubmit(params);
|
|
4001
|
-
} }, 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 },
|
|
4002
4179
|
label ? `${label}: ` : '',
|
|
4003
|
-
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
|
+
} })))))),
|
|
4004
4190
|
h("div", { style: Styles.ButtonsDiv },
|
|
4005
|
-
h(
|
|
4006
|
-
|
|
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))))));
|
|
4007
4194
|
}
|
|
4008
4195
|
function valueTransformer(type, value) {
|
|
4009
4196
|
switch (type) {
|
|
4010
|
-
case
|
|
4011
|
-
|
|
4012
|
-
|
|
4197
|
+
case 'email':
|
|
4198
|
+
return value.toLowerCase();
|
|
4199
|
+
case 'otp':
|
|
4200
|
+
return value.toUpperCase();
|
|
4201
|
+
default:
|
|
4202
|
+
return value;
|
|
4013
4203
|
}
|
|
4014
4204
|
}
|
|
4015
4205
|
|
|
@@ -4063,11 +4253,20 @@
|
|
|
4063
4253
|
}
|
|
4064
4254
|
};
|
|
4065
4255
|
}
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
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));
|
|
4071
4270
|
|
|
4072
4271
|
function computeSyncState(db) {
|
|
4073
4272
|
let _prevStatus = db.cloud.webSocketStatus.value;
|
|
@@ -4095,8 +4294,17 @@
|
|
|
4095
4294
|
return rxjs.combineLatest([
|
|
4096
4295
|
lazyWebSocketStatus,
|
|
4097
4296
|
db.syncStateChangedEvent.pipe(operators.startWith({ phase: 'initial' })),
|
|
4297
|
+
getCurrentUserEmitter(db.dx._novip),
|
|
4098
4298
|
userIsReallyActive
|
|
4099
|
-
]).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
|
+
}
|
|
4100
4308
|
let { phase, error, progress } = syncState;
|
|
4101
4309
|
let adjustedStatus = status;
|
|
4102
4310
|
if (phase === 'error') {
|
|
@@ -4129,23 +4337,12 @@
|
|
|
4129
4337
|
error,
|
|
4130
4338
|
progress,
|
|
4131
4339
|
status: isOnline ? adjustedStatus : 'offline',
|
|
4340
|
+
license: 'ok'
|
|
4132
4341
|
};
|
|
4133
4342
|
return retState;
|
|
4134
4343
|
}));
|
|
4135
4344
|
}
|
|
4136
4345
|
|
|
4137
|
-
function associate(factory) {
|
|
4138
|
-
const wm = new WeakMap();
|
|
4139
|
-
return (x) => {
|
|
4140
|
-
let rv = wm.get(x);
|
|
4141
|
-
if (!rv) {
|
|
4142
|
-
rv = factory(x);
|
|
4143
|
-
wm.set(x, rv);
|
|
4144
|
-
}
|
|
4145
|
-
return rv;
|
|
4146
|
-
};
|
|
4147
|
-
}
|
|
4148
|
-
|
|
4149
4346
|
function createSharedValueObservable(o, defaultValue) {
|
|
4150
4347
|
let currentValue = defaultValue;
|
|
4151
4348
|
let shared = rxjs.from(o).pipe(rxjs.map((x) => (currentValue = x)), rxjs.share({ resetOnRefCountZero: () => rxjs.timer(1000) }));
|
|
@@ -4187,8 +4384,6 @@
|
|
|
4187
4384
|
})), {});
|
|
4188
4385
|
});
|
|
4189
4386
|
|
|
4190
|
-
const getCurrentUserEmitter = associate((db) => new rxjs.BehaviorSubject(UNAUTHORIZED_USER));
|
|
4191
|
-
|
|
4192
4387
|
const getInternalAccessControlObservable = associate((db) => {
|
|
4193
4388
|
return createSharedValueObservable(getCurrentUserEmitter(db._novip).pipe(operators.switchMap((currentUser) => Dexie.liveQuery(() => db.transaction('r', 'realms', 'members', () => Promise.all([
|
|
4194
4389
|
db.members.where({ userId: currentUser.userId }).toArray(),
|
|
@@ -4471,7 +4666,7 @@
|
|
|
4471
4666
|
});
|
|
4472
4667
|
const syncComplete = new rxjs.Subject();
|
|
4473
4668
|
dexie.cloud = {
|
|
4474
|
-
version: '4.0.1-beta.
|
|
4669
|
+
version: '4.0.1-beta.47',
|
|
4475
4670
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
4476
4671
|
schema: null,
|
|
4477
4672
|
get currentUserId() {
|
|
@@ -4507,11 +4702,24 @@
|
|
|
4507
4702
|
}
|
|
4508
4703
|
updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
|
|
4509
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
|
+
},
|
|
4510
4712
|
sync({ wait, purpose } = { wait: true, purpose: 'push' }) {
|
|
4713
|
+
var _a;
|
|
4511
4714
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4512
4715
|
if (wait === undefined)
|
|
4513
4716
|
wait = true;
|
|
4514
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
|
+
}
|
|
4515
4723
|
if (purpose === 'pull') {
|
|
4516
4724
|
const syncState = db.cloud.persistedSyncState.value;
|
|
4517
4725
|
triggerSync(db, purpose);
|
|
@@ -4715,7 +4923,9 @@
|
|
|
4715
4923
|
db.syncStateChangedEvent.next({
|
|
4716
4924
|
phase: 'not-in-sync',
|
|
4717
4925
|
});
|
|
4718
|
-
|
|
4926
|
+
if (!isEagerSyncDisabled(db)) {
|
|
4927
|
+
triggerSync(db, 'push');
|
|
4928
|
+
}
|
|
4719
4929
|
}), rxjs.fromEvent(self, 'offline').subscribe(() => {
|
|
4720
4930
|
console.debug('offline!');
|
|
4721
4931
|
db.syncStateChangedEvent.next({
|
|
@@ -4732,7 +4942,7 @@
|
|
|
4732
4942
|
});
|
|
4733
4943
|
}
|
|
4734
4944
|
}
|
|
4735
|
-
dexieCloud.version = '4.0.1-beta.
|
|
4945
|
+
dexieCloud.version = '4.0.1-beta.47';
|
|
4736
4946
|
Dexie__default["default"].Cloud = dexieCloud;
|
|
4737
4947
|
|
|
4738
4948
|
// In case the SW lives for a while, let it reuse already opened connections:
|