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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import Dexie, { cmp, liveQuery } from 'dexie';
|
|
2
|
-
import { Observable, BehaviorSubject, Subject, of, fromEvent, merge, Subscription,
|
|
3
|
-
import { filter, take, switchMap, delay, distinctUntilChanged, map, tap, catchError,
|
|
2
|
+
import { Observable, BehaviorSubject, Subject, firstValueFrom, from, filter as filter$1, of, fromEvent, merge, Subscription, throwError, combineLatest, map as map$1, share, timer } from 'rxjs';
|
|
3
|
+
import { filter, take, switchMap, delay, distinctUntilChanged, map, tap, catchError, debounceTime, startWith, skip } from 'rxjs/operators';
|
|
4
4
|
|
|
5
5
|
/******************************************************************************
|
|
6
6
|
Copyright (c) Microsoft Corporation.
|
|
@@ -238,6 +238,7 @@ function registerPeriodicSyncEvent(db) {
|
|
|
238
238
|
|
|
239
239
|
function triggerSync(db, purpose) {
|
|
240
240
|
if (db.cloud.usingServiceWorker) {
|
|
241
|
+
console.debug('registering sync event');
|
|
241
242
|
registerSyncEvent(db, purpose);
|
|
242
243
|
}
|
|
243
244
|
else {
|
|
@@ -739,14 +740,24 @@ function getTablesToSyncify(db, syncState) {
|
|
|
739
740
|
return tablesToSyncify;
|
|
740
741
|
}
|
|
741
742
|
|
|
743
|
+
class TokenErrorResponseError extends Error {
|
|
744
|
+
constructor({ title, message, messageCode, messageParams, }) {
|
|
745
|
+
super(message);
|
|
746
|
+
this.name = 'TokenErrorResponseError';
|
|
747
|
+
this.title = title;
|
|
748
|
+
this.messageCode = messageCode;
|
|
749
|
+
this.messageParams = messageParams;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
742
753
|
function interactWithUser(userInteraction, req) {
|
|
743
754
|
return new Promise((resolve, reject) => {
|
|
744
|
-
const interactionProps = Object.assign(Object.assign({}, req), { onSubmit: (res) => {
|
|
755
|
+
const interactionProps = Object.assign(Object.assign({ submitLabel: 'Submit', cancelLabel: 'Cancel' }, req), { onSubmit: (res) => {
|
|
745
756
|
userInteraction.next(undefined);
|
|
746
757
|
resolve(res);
|
|
747
758
|
}, onCancel: () => {
|
|
748
759
|
userInteraction.next(undefined);
|
|
749
|
-
reject(new Dexie.AbortError(
|
|
760
|
+
reject(new Dexie.AbortError('User cancelled'));
|
|
750
761
|
} });
|
|
751
762
|
userInteraction.next(interactionProps);
|
|
752
763
|
// Start subscribing for external updates to db.cloud.userInteraction, and if so, cancel this request.
|
|
@@ -765,7 +776,9 @@ function alertUser(userInteraction, title, ...alerts) {
|
|
|
765
776
|
type: 'message-alert',
|
|
766
777
|
title,
|
|
767
778
|
alerts,
|
|
768
|
-
fields: {}
|
|
779
|
+
fields: {},
|
|
780
|
+
submitLabel: 'OK',
|
|
781
|
+
cancelLabel: null,
|
|
769
782
|
});
|
|
770
783
|
}
|
|
771
784
|
function promptForEmail(userInteraction, title, emailHint) {
|
|
@@ -824,22 +837,48 @@ function promptForOTP(userInteraction, email, alert) {
|
|
|
824
837
|
return otp;
|
|
825
838
|
});
|
|
826
839
|
}
|
|
840
|
+
function confirmLogout(userInteraction, currentUserId, numUnsyncedChanges) {
|
|
841
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
842
|
+
const alerts = [
|
|
843
|
+
{
|
|
844
|
+
type: 'warning',
|
|
845
|
+
messageCode: 'LOGOUT_CONFIRMATION',
|
|
846
|
+
message: `{numUnsyncedChanges} unsynced changes will get lost!
|
|
847
|
+
Logout anyway?`,
|
|
848
|
+
messageParams: {
|
|
849
|
+
currentUserId,
|
|
850
|
+
numUnsyncedChanges: numUnsyncedChanges.toString(),
|
|
851
|
+
}
|
|
852
|
+
},
|
|
853
|
+
];
|
|
854
|
+
return yield interactWithUser(userInteraction, {
|
|
855
|
+
type: 'logout-confirmation',
|
|
856
|
+
title: 'Confirm Logout',
|
|
857
|
+
alerts,
|
|
858
|
+
fields: {},
|
|
859
|
+
submitLabel: 'Confirm logout',
|
|
860
|
+
cancelLabel: 'Cancel'
|
|
861
|
+
})
|
|
862
|
+
.then(() => true)
|
|
863
|
+
.catch(() => false);
|
|
864
|
+
});
|
|
865
|
+
}
|
|
827
866
|
|
|
828
867
|
function loadAccessToken(db) {
|
|
829
|
-
var _a, _b;
|
|
868
|
+
var _a, _b, _c;
|
|
830
869
|
return __awaiter(this, void 0, void 0, function* () {
|
|
831
870
|
const currentUser = yield db.getCurrentUser();
|
|
832
871
|
const { accessToken, accessTokenExpiration, refreshToken, refreshTokenExpiration, claims, } = currentUser;
|
|
833
872
|
if (!accessToken)
|
|
834
|
-
return;
|
|
873
|
+
return null;
|
|
835
874
|
const expTime = (_a = accessTokenExpiration === null || accessTokenExpiration === void 0 ? void 0 : accessTokenExpiration.getTime()) !== null && _a !== void 0 ? _a : Infinity;
|
|
836
|
-
if (expTime > Date.now()) {
|
|
837
|
-
return
|
|
875
|
+
if (expTime > Date.now() && (((_b = currentUser.license) === null || _b === void 0 ? void 0 : _b.status) || 'ok') === 'ok') {
|
|
876
|
+
return currentUser;
|
|
838
877
|
}
|
|
839
878
|
if (!refreshToken) {
|
|
840
879
|
throw new Error(`Refresh token missing`);
|
|
841
880
|
}
|
|
842
|
-
const refreshExpTime = (
|
|
881
|
+
const refreshExpTime = (_c = refreshTokenExpiration === null || refreshTokenExpiration === void 0 ? void 0 : refreshTokenExpiration.getTime()) !== null && _c !== void 0 ? _c : Infinity;
|
|
843
882
|
if (refreshExpTime <= Date.now()) {
|
|
844
883
|
throw new Error(`Refresh token has expired`);
|
|
845
884
|
}
|
|
@@ -847,8 +886,10 @@ function loadAccessToken(db) {
|
|
|
847
886
|
yield db.table('$logins').update(claims.sub, {
|
|
848
887
|
accessToken: refreshedLogin.accessToken,
|
|
849
888
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
889
|
+
claims: refreshedLogin.claims,
|
|
890
|
+
license: refreshedLogin.license,
|
|
850
891
|
});
|
|
851
|
-
return refreshedLogin
|
|
892
|
+
return refreshedLogin;
|
|
852
893
|
});
|
|
853
894
|
}
|
|
854
895
|
function authenticate(url, context, fetchToken, userInteraction, hints) {
|
|
@@ -896,10 +937,24 @@ function refreshAccessToken(url, login) {
|
|
|
896
937
|
if (res.status !== 200)
|
|
897
938
|
throw new Error(`RefreshToken: Status ${res.status} from ${url}/token`);
|
|
898
939
|
const response = yield res.json();
|
|
940
|
+
if (response.type === 'error') {
|
|
941
|
+
throw new TokenErrorResponseError(response);
|
|
942
|
+
}
|
|
899
943
|
login.accessToken = response.accessToken;
|
|
900
944
|
login.accessTokenExpiration = response.accessTokenExpiration
|
|
901
945
|
? new Date(response.accessTokenExpiration)
|
|
902
946
|
: undefined;
|
|
947
|
+
login.claims = response.claims;
|
|
948
|
+
login.license = {
|
|
949
|
+
type: response.userType,
|
|
950
|
+
status: response.claims.license || 'ok',
|
|
951
|
+
};
|
|
952
|
+
if (response.evalDaysLeft != null) {
|
|
953
|
+
login.license.evalDaysLeft = response.evalDaysLeft;
|
|
954
|
+
}
|
|
955
|
+
if (response.userValidUntil != null) {
|
|
956
|
+
login.license.validUntil = new Date(response.userValidUntil);
|
|
957
|
+
}
|
|
903
958
|
return login;
|
|
904
959
|
});
|
|
905
960
|
}
|
|
@@ -931,8 +986,15 @@ function userAuthenticate(context, fetchToken, userInteraction, hints) {
|
|
|
931
986
|
public_key: publicKeyPEM,
|
|
932
987
|
hints,
|
|
933
988
|
});
|
|
989
|
+
if (response2.type === 'error') {
|
|
990
|
+
throw new TokenErrorResponseError(response2);
|
|
991
|
+
}
|
|
934
992
|
if (response2.type !== 'tokens')
|
|
935
993
|
throw new Error(`Unexpected response type from token endpoint: ${response2.type}`);
|
|
994
|
+
/*const licenseStatus = response2.claims.license || 'ok';
|
|
995
|
+
if (licenseStatus !== 'ok') {
|
|
996
|
+
throw new InvalidLicenseError(licenseStatus);
|
|
997
|
+
}*/
|
|
936
998
|
context.accessToken = response2.accessToken;
|
|
937
999
|
context.accessTokenExpiration = new Date(response2.accessTokenExpiration);
|
|
938
1000
|
context.refreshToken = response2.refreshToken;
|
|
@@ -943,6 +1005,16 @@ function userAuthenticate(context, fetchToken, userInteraction, hints) {
|
|
|
943
1005
|
context.email = response2.claims.email;
|
|
944
1006
|
context.name = response2.claims.name;
|
|
945
1007
|
context.claims = response2.claims;
|
|
1008
|
+
context.license = {
|
|
1009
|
+
type: response2.userType,
|
|
1010
|
+
status: response2.claims.license || 'ok',
|
|
1011
|
+
};
|
|
1012
|
+
if (response2.evalDaysLeft != null) {
|
|
1013
|
+
context.license.evalDaysLeft = response2.evalDaysLeft;
|
|
1014
|
+
}
|
|
1015
|
+
if (response2.userValidUntil != null) {
|
|
1016
|
+
context.license.validUntil = new Date(response2.userValidUntil);
|
|
1017
|
+
}
|
|
946
1018
|
if (response2.alerts && response2.alerts.length > 0) {
|
|
947
1019
|
yield interactWithUser(userInteraction, {
|
|
948
1020
|
type: 'message-alert',
|
|
@@ -954,12 +1026,36 @@ function userAuthenticate(context, fetchToken, userInteraction, hints) {
|
|
|
954
1026
|
return context;
|
|
955
1027
|
}
|
|
956
1028
|
catch (error) {
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
1029
|
+
if (error instanceof TokenErrorResponseError) {
|
|
1030
|
+
yield alertUser(userInteraction, error.title, {
|
|
1031
|
+
type: 'error',
|
|
1032
|
+
messageCode: error.messageCode,
|
|
1033
|
+
message: error.message,
|
|
1034
|
+
messageParams: {},
|
|
1035
|
+
});
|
|
1036
|
+
throw error;
|
|
1037
|
+
}
|
|
1038
|
+
let message = `We're having a problem authenticating right now.`;
|
|
1039
|
+
console.error(`Error authenticating`, error);
|
|
1040
|
+
if (error instanceof TypeError) {
|
|
1041
|
+
const isOffline = typeof navigator !== undefined && !navigator.onLine;
|
|
1042
|
+
if (isOffline) {
|
|
1043
|
+
message = `You seem to be offline. Please connect to the internet and try again.`;
|
|
1044
|
+
}
|
|
1045
|
+
else if (Dexie.debug || (typeof location !== 'undefined' && (location.hostname === 'localhost' || location.hostname === '127.0.0.1'))) {
|
|
1046
|
+
// The audience is most likely the developer. Suggest to whitelist the localhost origin:
|
|
1047
|
+
message = `Could not connect to server. Please verify that your origin '${location.origin}' is whitelisted using \`npx dexie-cloud whitelist\``;
|
|
1048
|
+
}
|
|
1049
|
+
else {
|
|
1050
|
+
message = `Could not connect to server. Please verify the connection.`;
|
|
1051
|
+
}
|
|
1052
|
+
yield alertUser(userInteraction, 'Authentication Failed', {
|
|
1053
|
+
type: 'error',
|
|
1054
|
+
messageCode: 'GENERIC_ERROR',
|
|
1055
|
+
message,
|
|
1056
|
+
messageParams: {},
|
|
1057
|
+
}).catch(() => { });
|
|
1058
|
+
}
|
|
963
1059
|
throw error;
|
|
964
1060
|
}
|
|
965
1061
|
});
|
|
@@ -1539,9 +1635,19 @@ function encodeIdsForServer(schema, currentUser, changes) {
|
|
|
1539
1635
|
const mutClone = changeClone.muts[mutIndex];
|
|
1540
1636
|
const rewrittenKey = JSON.stringify(key);
|
|
1541
1637
|
mutClone.keys[keyIndex] = rewrittenKey;
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1638
|
+
/* Bug (#1777)
|
|
1639
|
+
We should not rewrite values. It will fail because the key is array and the value is string.
|
|
1640
|
+
Only the keys should be rewritten and it's already done on the server.
|
|
1641
|
+
We should take another round of revieweing how key transformations are being done between
|
|
1642
|
+
client and server and let the server do the key transformations entirely instead now that
|
|
1643
|
+
we have the primary key schema on the server making it possible to do so.
|
|
1644
|
+
if (rewriteValues) {
|
|
1645
|
+
Dexie.setByKeyPath(
|
|
1646
|
+
(mutClone as DBInsertOperation).values[keyIndex],
|
|
1647
|
+
primaryKey.keyPath!,
|
|
1648
|
+
rewrittenKey
|
|
1649
|
+
);
|
|
1650
|
+
}*/
|
|
1545
1651
|
}
|
|
1546
1652
|
else if (key[0] === '#') {
|
|
1547
1653
|
// Private ID - translate!
|
|
@@ -1572,6 +1678,40 @@ function cloneChange(change, rewriteValues) {
|
|
|
1572
1678
|
: change.muts.map((m) => (Object.assign(Object.assign({}, m), { keys: m.keys.slice() }))) });
|
|
1573
1679
|
}
|
|
1574
1680
|
|
|
1681
|
+
// If we get Ratelimit-Limit and Ratelimit-Remaining where Ratelimit-Remaining is below
|
|
1682
|
+
// (Ratelimit-Limit / 2), we should delay the next sync by (Ratelimit-Reset / Ratelimit-Remaining)
|
|
1683
|
+
// seconds (given that there is a Ratelimit-Reset header).
|
|
1684
|
+
let syncRatelimitDelays = new WeakMap();
|
|
1685
|
+
function checkSyncRateLimitDelay(db) {
|
|
1686
|
+
var _a, _b;
|
|
1687
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1688
|
+
const delatMilliseconds = ((_b = (_a = syncRatelimitDelays.get(db)) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) - Date.now();
|
|
1689
|
+
if (delatMilliseconds > 0) {
|
|
1690
|
+
console.debug(`Stalling sync request ${delatMilliseconds} ms to spare ratelimits`);
|
|
1691
|
+
yield new Promise(resolve => setTimeout(resolve, delatMilliseconds));
|
|
1692
|
+
}
|
|
1693
|
+
});
|
|
1694
|
+
}
|
|
1695
|
+
function updateSyncRateLimitDelays(db, res) {
|
|
1696
|
+
const limit = res.headers.get('Ratelimit-Limit');
|
|
1697
|
+
const remaining = res.headers.get('Ratelimit-Remaining');
|
|
1698
|
+
const reset = res.headers.get('Ratelimit-Reset');
|
|
1699
|
+
if (limit && remaining && reset) {
|
|
1700
|
+
const limitNum = Number(limit);
|
|
1701
|
+
const remainingNum = Math.max(0, Number(remaining));
|
|
1702
|
+
const willResetInSeconds = Number(reset);
|
|
1703
|
+
if (remainingNum < limitNum / 2) {
|
|
1704
|
+
const delay = Math.ceil(willResetInSeconds / (remainingNum + 1));
|
|
1705
|
+
syncRatelimitDelays.set(db, new Date(Date.now() + delay * 1000));
|
|
1706
|
+
console.debug(`Sync ratelimit delay set to ${delay} seconds`);
|
|
1707
|
+
}
|
|
1708
|
+
else {
|
|
1709
|
+
syncRatelimitDelays.delete(db);
|
|
1710
|
+
console.debug(`Sync ratelimit delay cleared`);
|
|
1711
|
+
}
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1575
1715
|
//import {BisonWebStreamReader} from "dreambase-library/dist/typeson-simplified/BisonWebStreamReader";
|
|
1576
1716
|
function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, clientIdentity, currentUser) {
|
|
1577
1717
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -1580,9 +1720,20 @@ function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, c
|
|
|
1580
1720
|
//
|
|
1581
1721
|
const headers = {
|
|
1582
1722
|
Accept: 'application/json, application/x-bison, application/x-bison-stream',
|
|
1583
|
-
'Content-Type': 'application/tson'
|
|
1723
|
+
'Content-Type': 'application/tson',
|
|
1584
1724
|
};
|
|
1585
|
-
const
|
|
1725
|
+
const updatedUser = yield loadAccessToken(db);
|
|
1726
|
+
/*
|
|
1727
|
+
if (updatedUser?.license && changes.length > 0) {
|
|
1728
|
+
if (updatedUser.license.status === 'expired') {
|
|
1729
|
+
throw new Error(`License has expired`);
|
|
1730
|
+
}
|
|
1731
|
+
if (updatedUser.license.status === 'deactivated') {
|
|
1732
|
+
throw new Error(`License deactivated`);
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
*/
|
|
1736
|
+
const accessToken = updatedUser === null || updatedUser === void 0 ? void 0 : updatedUser.accessToken;
|
|
1586
1737
|
if (accessToken) {
|
|
1587
1738
|
headers.Authorization = `Bearer ${accessToken}`;
|
|
1588
1739
|
}
|
|
@@ -1591,27 +1742,31 @@ function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, c
|
|
|
1591
1742
|
dbID: syncState === null || syncState === void 0 ? void 0 : syncState.remoteDbId,
|
|
1592
1743
|
clientIdentity,
|
|
1593
1744
|
schema: schema || {},
|
|
1594
|
-
lastPull: syncState
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1745
|
+
lastPull: syncState
|
|
1746
|
+
? {
|
|
1747
|
+
serverRevision: syncState.serverRevision,
|
|
1748
|
+
realms: syncState.realms,
|
|
1749
|
+
inviteRealms: syncState.inviteRealms,
|
|
1750
|
+
}
|
|
1751
|
+
: undefined,
|
|
1599
1752
|
baseRevs,
|
|
1600
|
-
changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes)
|
|
1753
|
+
changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes),
|
|
1601
1754
|
};
|
|
1602
|
-
console.debug(
|
|
1755
|
+
console.debug('Sync request', syncRequest);
|
|
1603
1756
|
db.syncStateChangedEvent.next({
|
|
1604
1757
|
phase: 'pushing',
|
|
1605
1758
|
});
|
|
1606
1759
|
const res = yield fetch(`${databaseUrl}/sync`, {
|
|
1607
1760
|
method: 'post',
|
|
1608
1761
|
headers,
|
|
1609
|
-
|
|
1762
|
+
credentials: 'include',
|
|
1763
|
+
body: TSON.stringify(syncRequest),
|
|
1610
1764
|
});
|
|
1611
1765
|
//const contentLength = Number(res.headers.get('content-length'));
|
|
1612
1766
|
db.syncStateChangedEvent.next({
|
|
1613
|
-
phase: 'pulling'
|
|
1767
|
+
phase: 'pulling',
|
|
1614
1768
|
});
|
|
1769
|
+
updateSyncRateLimitDelays(db, res);
|
|
1615
1770
|
if (!res.ok) {
|
|
1616
1771
|
throw new HttpError(res);
|
|
1617
1772
|
}
|
|
@@ -1813,12 +1968,13 @@ const CURRENT_SYNC_WORKER = 'currentSyncWorker';
|
|
|
1813
1968
|
function sync(db, options, schema, syncOptions) {
|
|
1814
1969
|
return _sync
|
|
1815
1970
|
.apply(this, arguments)
|
|
1816
|
-
.then(() => {
|
|
1971
|
+
.then((result) => {
|
|
1817
1972
|
if (!(syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)) { // && syncOptions?.purpose !== 'push') {
|
|
1818
1973
|
db.syncStateChangedEvent.next({
|
|
1819
1974
|
phase: 'in-sync',
|
|
1820
1975
|
});
|
|
1821
1976
|
}
|
|
1977
|
+
return result;
|
|
1822
1978
|
})
|
|
1823
1979
|
.catch((error) => __awaiter(this, void 0, void 0, function* () {
|
|
1824
1980
|
if (syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)
|
|
@@ -1848,7 +2004,7 @@ function sync(db, options, schema, syncOptions) {
|
|
|
1848
2004
|
});
|
|
1849
2005
|
db.syncStateChangedEvent.next({
|
|
1850
2006
|
phase: isOnline ? 'error' : 'offline',
|
|
1851
|
-
error,
|
|
2007
|
+
error: new Error('' + (error === null || error === void 0 ? void 0 : error.message) || error),
|
|
1852
2008
|
});
|
|
1853
2009
|
return Promise.reject(error);
|
|
1854
2010
|
}));
|
|
@@ -2033,6 +2189,7 @@ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNee
|
|
|
2033
2189
|
}));
|
|
2034
2190
|
if (!done) {
|
|
2035
2191
|
console.debug('MORE SYNC NEEDED. Go for it again!');
|
|
2192
|
+
yield checkSyncRateLimitDelay(db);
|
|
2036
2193
|
return yield _sync(db, options, schema, { isInitialSync, cancelToken });
|
|
2037
2194
|
}
|
|
2038
2195
|
console.debug('SYNC DONE', { isInitialSync });
|
|
@@ -2169,6 +2326,8 @@ function MessagesFromServerConsumer(db) {
|
|
|
2169
2326
|
yield db.table('$logins').update(user.userId, {
|
|
2170
2327
|
accessToken: refreshedLogin.accessToken,
|
|
2171
2328
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
2329
|
+
claims: refreshedLogin.claims,
|
|
2330
|
+
license: refreshedLogin.license,
|
|
2172
2331
|
});
|
|
2173
2332
|
// Updating $logins will trigger emission of db.cloud.currentUser observable, which
|
|
2174
2333
|
// in turn will lead to that connectWebSocket.ts will reconnect the socket with the
|
|
@@ -2439,6 +2598,61 @@ class AuthPersistedContext {
|
|
|
2439
2598
|
}
|
|
2440
2599
|
}
|
|
2441
2600
|
|
|
2601
|
+
function waitUntil(o, // Works with Dexie's liveQuery observables if we'd need that
|
|
2602
|
+
predicate) {
|
|
2603
|
+
return firstValueFrom(from(o).pipe(filter$1(predicate)));
|
|
2604
|
+
}
|
|
2605
|
+
|
|
2606
|
+
function logout(db) {
|
|
2607
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2608
|
+
const numUnsyncedChanges = yield _logout(db);
|
|
2609
|
+
if (numUnsyncedChanges) {
|
|
2610
|
+
if (yield confirmLogout(db.cloud.userInteraction, db.cloud.currentUserId, numUnsyncedChanges)) {
|
|
2611
|
+
yield _logout(db, { deleteUnsyncedData: true });
|
|
2612
|
+
}
|
|
2613
|
+
else {
|
|
2614
|
+
throw new Error(`User cancelled logout due to unsynced changes`);
|
|
2615
|
+
}
|
|
2616
|
+
}
|
|
2617
|
+
});
|
|
2618
|
+
}
|
|
2619
|
+
function _logout(db, { deleteUnsyncedData = false } = {}) {
|
|
2620
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2621
|
+
// Clear the database without emptying configuration options.
|
|
2622
|
+
const [numUnsynced, loggedOut] = yield db.dx.transaction('rw', db.dx.tables, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
2623
|
+
// @ts-ignore
|
|
2624
|
+
const idbtrans = tx.idbtrans;
|
|
2625
|
+
idbtrans.disableChangeTracking = true;
|
|
2626
|
+
idbtrans.disableAccessControl = true;
|
|
2627
|
+
const mutationTables = tx.storeNames.filter((tableName) => tableName.endsWith('_mutations'));
|
|
2628
|
+
// Count unsynced changes
|
|
2629
|
+
const unsyncCounts = yield Promise.all(mutationTables.map((mutationTable) => tx.table(mutationTable).count()));
|
|
2630
|
+
const sumUnSynced = unsyncCounts.reduce((a, b) => a + b, 0);
|
|
2631
|
+
if (sumUnSynced > 0 && !deleteUnsyncedData) {
|
|
2632
|
+
// Let caller ask user if they want to delete unsynced data.
|
|
2633
|
+
return [sumUnSynced, false];
|
|
2634
|
+
}
|
|
2635
|
+
// Either there are no unsynched changes, or caller provided flag deleteUnsynchedData = true.
|
|
2636
|
+
// Clear all tables except $jobs and $syncState (except the persisted sync state which is
|
|
2637
|
+
// also cleared because we're going to rebuild it using a fresh sync).
|
|
2638
|
+
db.$syncState.delete('syncState');
|
|
2639
|
+
for (const table of db.dx.tables) {
|
|
2640
|
+
if (table.name !== '$jobs' && table.name !== '$syncState') {
|
|
2641
|
+
table.clear();
|
|
2642
|
+
}
|
|
2643
|
+
}
|
|
2644
|
+
return [sumUnSynced, true];
|
|
2645
|
+
}));
|
|
2646
|
+
if (loggedOut) {
|
|
2647
|
+
// Wait for currentUser observable to emit UNAUTHORIZED_USER
|
|
2648
|
+
yield waitUntil(db.cloud.currentUser, (user) => user.userId === UNAUTHORIZED_USER.userId);
|
|
2649
|
+
// Then perform an initial sync
|
|
2650
|
+
yield db.cloud.sync({ purpose: 'pull', wait: true });
|
|
2651
|
+
}
|
|
2652
|
+
return numUnsynced;
|
|
2653
|
+
});
|
|
2654
|
+
}
|
|
2655
|
+
|
|
2442
2656
|
function otpFetchTokenCallback(db) {
|
|
2443
2657
|
const { userInteraction } = db.cloud;
|
|
2444
2658
|
return function otpAuthenticate({ public_key, hints }) {
|
|
@@ -2482,8 +2696,9 @@ function otpFetchTokenCallback(db) {
|
|
|
2482
2696
|
throw new HttpError(res1, errMsg);
|
|
2483
2697
|
}
|
|
2484
2698
|
const response = yield res1.json();
|
|
2485
|
-
if (response.type === 'tokens') {
|
|
2699
|
+
if (response.type === 'tokens' || response.type === 'error') {
|
|
2486
2700
|
// Demo user request can get a "tokens" response right away
|
|
2701
|
+
// Error can also be returned right away.
|
|
2487
2702
|
return response;
|
|
2488
2703
|
}
|
|
2489
2704
|
else if (tokenRequest.grant_type === 'otp') {
|
|
@@ -2515,12 +2730,6 @@ function otpFetchTokenCallback(db) {
|
|
|
2515
2730
|
}
|
|
2516
2731
|
if (res2.status !== 200) {
|
|
2517
2732
|
const errMsg = yield res2.text();
|
|
2518
|
-
yield alertUser(userInteraction, "OTP Authentication Failed", {
|
|
2519
|
-
type: 'error',
|
|
2520
|
-
messageCode: 'GENERIC_ERROR',
|
|
2521
|
-
message: errMsg,
|
|
2522
|
-
messageParams: {}
|
|
2523
|
-
}).catch(() => { });
|
|
2524
2733
|
throw new HttpError(res2, errMsg);
|
|
2525
2734
|
}
|
|
2526
2735
|
const response2 = yield res2.json();
|
|
@@ -2533,6 +2742,18 @@ function otpFetchTokenCallback(db) {
|
|
|
2533
2742
|
};
|
|
2534
2743
|
}
|
|
2535
2744
|
|
|
2745
|
+
/** A way to log to console in production without terser stripping out
|
|
2746
|
+
* it from the release bundle.
|
|
2747
|
+
* This should be used very rarely and only in places where it's
|
|
2748
|
+
* absolutely necessary to log something in production.
|
|
2749
|
+
*
|
|
2750
|
+
* @param level
|
|
2751
|
+
* @param args
|
|
2752
|
+
*/
|
|
2753
|
+
function prodLog(level, ...args) {
|
|
2754
|
+
globalThis["con" + "sole"][level](...args);
|
|
2755
|
+
}
|
|
2756
|
+
|
|
2536
2757
|
/** This function changes or sets the current user as requested.
|
|
2537
2758
|
*
|
|
2538
2759
|
* Use cases:
|
|
@@ -2559,85 +2780,73 @@ function setCurrentUser(db, user) {
|
|
|
2559
2780
|
}));
|
|
2560
2781
|
user.isLoggedIn = true;
|
|
2561
2782
|
user.lastLogin = new Date();
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
}));
|
|
2565
|
-
yield new Promise((resolve) => {
|
|
2566
|
-
if (db.cloud.currentUserId === user.userId) {
|
|
2567
|
-
resolve(null);
|
|
2783
|
+
try {
|
|
2784
|
+
yield user.save();
|
|
2568
2785
|
}
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
if (
|
|
2572
|
-
|
|
2573
|
-
|
|
2786
|
+
catch (e) {
|
|
2787
|
+
try {
|
|
2788
|
+
if (e.name === 'DataCloneError') {
|
|
2789
|
+
// We've seen this buggy behavior in some browsers and in case it happens
|
|
2790
|
+
// again we really need to collect the details to understand what's going on.
|
|
2791
|
+
prodLog('debug', `Login context property names:`, Object.keys(user));
|
|
2792
|
+
prodLog('debug', `Login context property names:`, Object.keys(user));
|
|
2793
|
+
prodLog('debug', `Login context:`, user);
|
|
2794
|
+
prodLog('debug', `Login context JSON:`, JSON.stringify(user));
|
|
2574
2795
|
}
|
|
2575
|
-
}
|
|
2796
|
+
}
|
|
2797
|
+
catch (_a) { }
|
|
2798
|
+
throw e;
|
|
2576
2799
|
}
|
|
2577
|
-
|
|
2578
|
-
|
|
2579
|
-
|
|
2580
|
-
// V: Samma med andra windows.
|
|
2581
|
-
// V: Så kanske göra om den till att häröra från liveQuery som läser $logins.orderBy('lastLogin').last().
|
|
2582
|
-
// V: Då bara vara medveten om:
|
|
2583
|
-
// V: En sån observable börjar hämta data vid första subscribe
|
|
2584
|
-
// V: Vi har inget "inital value" men kan emulera det till att vara ANONYMOUS_USER
|
|
2585
|
-
// V: Om requireAuth är true, så borde db.on(ready) hålla databasen stängd för alla utom denna observable.
|
|
2586
|
-
// V: Om inte så behöver den inte blocka.
|
|
2587
|
-
// Andra tankar:
|
|
2588
|
-
// * Man kan inte byta användare när man är offline. Skulle gå att flytta realms till undanstuff-tabell vid user-change.
|
|
2589
|
-
// men troligen inte värt det.
|
|
2590
|
-
// * Istället: sälj inte inte switch-user funktionalitet utan tala enbart om inloggat vs icke inloggat läge.
|
|
2591
|
-
// * populate $logins med ANONYMOUS så att en påbörjad inloggning inte räknas, alternativt ha en boolean prop!
|
|
2592
|
-
// Kanske bäst ha en boolean prop!
|
|
2593
|
-
// * Alternativ switch-user funktionalitet:
|
|
2594
|
-
// * DBCore gömmer data från realms man inte har tillgång till.
|
|
2595
|
-
// * Cursor impl behövs också då.
|
|
2596
|
-
// * Då blir det snabba user switch.
|
|
2597
|
-
// * claims-settet som skickas till servern blir summan av alla claims. Då måste servern stödja multipla tokens eller
|
|
2598
|
-
// att ens token är ett samlad.
|
|
2800
|
+
console.debug('Saved new user', user.email);
|
|
2801
|
+
}));
|
|
2802
|
+
yield waitUntil(db.cloud.currentUser, (currentUser) => currentUser.userId === user.userId);
|
|
2599
2803
|
});
|
|
2600
2804
|
}
|
|
2601
2805
|
|
|
2602
2806
|
function login(db, hints) {
|
|
2807
|
+
var _a;
|
|
2603
2808
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2604
2809
|
const currentUser = yield db.getCurrentUser();
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
throw new Error(`Must logout before changing user`);
|
|
2612
|
-
}
|
|
2810
|
+
const origUserId = currentUser.userId;
|
|
2811
|
+
if (currentUser.isLoggedIn && (!hints || (!hints.email && !hints.userId))) {
|
|
2812
|
+
const licenseStatus = ((_a = currentUser.license) === null || _a === void 0 ? void 0 : _a.status) || 'ok';
|
|
2813
|
+
if (licenseStatus === 'ok' && currentUser.accessToken && (!currentUser.accessTokenExpiration || currentUser.accessTokenExpiration.getTime() > Date.now())) {
|
|
2814
|
+
// Already authenticated according to given hints. And license is valid.
|
|
2815
|
+
return false;
|
|
2613
2816
|
}
|
|
2614
|
-
|
|
2615
|
-
|
|
2817
|
+
if (currentUser.refreshToken && (!currentUser.refreshTokenExpiration || currentUser.refreshTokenExpiration.getTime() > Date.now())) {
|
|
2818
|
+
// Refresh the token
|
|
2819
|
+
yield loadAccessToken(db);
|
|
2820
|
+
return false;
|
|
2821
|
+
}
|
|
2822
|
+
// No refresh token - must re-authenticate:
|
|
2616
2823
|
}
|
|
2617
2824
|
const context = new AuthPersistedContext(db, {
|
|
2618
2825
|
claims: {},
|
|
2619
2826
|
lastLogin: new Date(0),
|
|
2620
2827
|
});
|
|
2621
2828
|
yield authenticate(db.cloud.options.databaseUrl, context, db.cloud.options.fetchTokens || otpFetchTokenCallback(db), db.cloud.userInteraction, hints);
|
|
2622
|
-
|
|
2623
|
-
|
|
2829
|
+
if (origUserId !== UNAUTHORIZED_USER.userId && context.userId !== origUserId) {
|
|
2830
|
+
// User was logged in before, but now logged in as another user.
|
|
2831
|
+
yield logout(db);
|
|
2624
2832
|
}
|
|
2625
|
-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
|
|
2833
|
+
/*try {
|
|
2834
|
+
await context.save();
|
|
2835
|
+
} catch (e) {
|
|
2836
|
+
try {
|
|
2837
|
+
if (e.name === 'DataCloneError') {
|
|
2838
|
+
console.debug(`Login context property names:`, Object.keys(context));
|
|
2839
|
+
console.debug(`Login context:`, context);
|
|
2840
|
+
console.debug(`Login context JSON:`, JSON.stringify(context));
|
|
2632
2841
|
}
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
}
|
|
2842
|
+
} catch {}
|
|
2843
|
+
throw e;
|
|
2844
|
+
}*/
|
|
2636
2845
|
yield setCurrentUser(db, context);
|
|
2637
2846
|
// Make sure to resync as the new login will be authorized
|
|
2638
2847
|
// for new realms.
|
|
2639
2848
|
triggerSync(db, "pull");
|
|
2640
|
-
return
|
|
2849
|
+
return context.userId !== origUserId;
|
|
2641
2850
|
});
|
|
2642
2851
|
}
|
|
2643
2852
|
|
|
@@ -2966,6 +3175,13 @@ function writeLock(fn, prop) {
|
|
|
2966
3175
|
|
|
2967
3176
|
const outstandingTransactions = new BehaviorSubject(new Set());
|
|
2968
3177
|
|
|
3178
|
+
function isEagerSyncDisabled(db) {
|
|
3179
|
+
var _a, _b, _c, _d;
|
|
3180
|
+
return (((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.disableEagerSync) ||
|
|
3181
|
+
((_c = (_b = db.cloud.currentUser.value) === null || _b === void 0 ? void 0 : _b.license) === null || _c === void 0 ? void 0 : _c.status) !== 'ok' ||
|
|
3182
|
+
!((_d = db.cloud.options) === null || _d === void 0 ? void 0 : _d.databaseUrl));
|
|
3183
|
+
}
|
|
3184
|
+
|
|
2969
3185
|
/** Tracks all mutations in the same transaction as the mutations -
|
|
2970
3186
|
* so it is guaranteed that no mutation goes untracked - and if transaction
|
|
2971
3187
|
* aborts, the mutations won't be tracked.
|
|
@@ -2974,7 +3190,7 @@ const outstandingTransactions = new BehaviorSubject(new Set());
|
|
|
2974
3190
|
* changes to server and cleanup the tracked mutations once the server has
|
|
2975
3191
|
* ackowledged that it got them.
|
|
2976
3192
|
*/
|
|
2977
|
-
function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
3193
|
+
function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
2978
3194
|
return {
|
|
2979
3195
|
stack: 'dbcore',
|
|
2980
3196
|
name: 'MutationTrackingMiddleware',
|
|
@@ -2985,7 +3201,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
2985
3201
|
try {
|
|
2986
3202
|
mutTableMap = new Map(ordinaryTables.map((tbl) => [
|
|
2987
3203
|
tbl.name,
|
|
2988
|
-
core.table(`$${tbl.name}_mutations`)
|
|
3204
|
+
core.table(`$${tbl.name}_mutations`),
|
|
2989
3205
|
]));
|
|
2990
3206
|
}
|
|
2991
3207
|
catch (_a) {
|
|
@@ -3019,15 +3235,9 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
3019
3235
|
outstandingTransactions.next(outstandingTransactions.value);
|
|
3020
3236
|
};
|
|
3021
3237
|
const txComplete = () => {
|
|
3022
|
-
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
console.debug('registering sync event');
|
|
3026
|
-
registerSyncEvent(db, "push");
|
|
3027
|
-
}
|
|
3028
|
-
else {
|
|
3029
|
-
db.localSyncEvent.next({ purpose: "push" });
|
|
3030
|
-
}
|
|
3238
|
+
if (tx.mutationsAdded &&
|
|
3239
|
+
!isEagerSyncDisabled(db)) {
|
|
3240
|
+
triggerSync(db, 'push');
|
|
3031
3241
|
}
|
|
3032
3242
|
removeTransaction();
|
|
3033
3243
|
};
|
|
@@ -3094,7 +3304,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
3094
3304
|
.query({
|
|
3095
3305
|
query: { range: req.range, index: schema.primaryKey },
|
|
3096
3306
|
trans: req.trans,
|
|
3097
|
-
values: false
|
|
3307
|
+
values: false,
|
|
3098
3308
|
})
|
|
3099
3309
|
// Do a delete request instead, but keep the criteria info for the server to execute
|
|
3100
3310
|
.then((res) => {
|
|
@@ -3102,7 +3312,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
3102
3312
|
type: 'delete',
|
|
3103
3313
|
keys: res.result,
|
|
3104
3314
|
trans: req.trans,
|
|
3105
|
-
criteria: { index: null, range: req.range }
|
|
3315
|
+
criteria: { index: null, range: req.range },
|
|
3106
3316
|
});
|
|
3107
3317
|
})
|
|
3108
3318
|
: mutateAndLog(req);
|
|
@@ -3110,7 +3320,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
3110
3320
|
function mutateAndLog(req) {
|
|
3111
3321
|
const trans = req.trans;
|
|
3112
3322
|
trans.mutationsAdded = true;
|
|
3113
|
-
const { txid, currentUser: { userId } } = trans;
|
|
3323
|
+
const { txid, currentUser: { userId }, } = trans;
|
|
3114
3324
|
const { type } = req;
|
|
3115
3325
|
const opNo = ++trans.opCount;
|
|
3116
3326
|
return table.mutate(req).then((res) => {
|
|
@@ -3131,7 +3341,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
3131
3341
|
keys,
|
|
3132
3342
|
criteria: req.criteria,
|
|
3133
3343
|
txid,
|
|
3134
|
-
userId
|
|
3344
|
+
userId,
|
|
3135
3345
|
}
|
|
3136
3346
|
: req.type === 'add'
|
|
3137
3347
|
? {
|
|
@@ -3141,7 +3351,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
3141
3351
|
keys,
|
|
3142
3352
|
txid,
|
|
3143
3353
|
userId,
|
|
3144
|
-
values
|
|
3354
|
+
values,
|
|
3145
3355
|
}
|
|
3146
3356
|
: req.criteria && req.changeSpec
|
|
3147
3357
|
? {
|
|
@@ -3153,7 +3363,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
3153
3363
|
criteria: req.criteria,
|
|
3154
3364
|
changeSpec: req.changeSpec,
|
|
3155
3365
|
txid,
|
|
3156
|
-
userId
|
|
3366
|
+
userId,
|
|
3157
3367
|
}
|
|
3158
3368
|
: updates
|
|
3159
3369
|
? {
|
|
@@ -3164,7 +3374,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
3164
3374
|
keys: updates.keys,
|
|
3165
3375
|
changeSpecs: updates.changeSpecs,
|
|
3166
3376
|
txid,
|
|
3167
|
-
userId
|
|
3377
|
+
userId,
|
|
3168
3378
|
}
|
|
3169
3379
|
: {
|
|
3170
3380
|
type: 'upsert',
|
|
@@ -3173,7 +3383,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
3173
3383
|
keys,
|
|
3174
3384
|
values,
|
|
3175
3385
|
txid,
|
|
3176
|
-
userId
|
|
3386
|
+
userId,
|
|
3177
3387
|
};
|
|
3178
3388
|
return keys.length > 0 || ('criteria' in req && req.criteria)
|
|
3179
3389
|
? mutsTable
|
|
@@ -3183,7 +3393,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
3183
3393
|
});
|
|
3184
3394
|
}
|
|
3185
3395
|
} });
|
|
3186
|
-
}
|
|
3396
|
+
},
|
|
3187
3397
|
};
|
|
3188
3398
|
}
|
|
3189
3399
|
|
|
@@ -3542,6 +3752,20 @@ class WSConnection extends Subscription {
|
|
|
3542
3752
|
}
|
|
3543
3753
|
}
|
|
3544
3754
|
|
|
3755
|
+
class InvalidLicenseError extends Error {
|
|
3756
|
+
constructor(license) {
|
|
3757
|
+
super(license === 'expired'
|
|
3758
|
+
? `License expired`
|
|
3759
|
+
: license === 'deactivated'
|
|
3760
|
+
? `User deactivated`
|
|
3761
|
+
: 'Invalid license');
|
|
3762
|
+
this.name = 'InvalidLicenseError';
|
|
3763
|
+
if (license) {
|
|
3764
|
+
this.license = license;
|
|
3765
|
+
}
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
|
|
3545
3769
|
function sleep$1(ms) {
|
|
3546
3770
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
3547
3771
|
}
|
|
@@ -3575,7 +3799,12 @@ function connectWebSocket(db) {
|
|
|
3575
3799
|
function createObservable() {
|
|
3576
3800
|
return db.cloud.persistedSyncState.pipe(filter((syncState) => syncState === null || syncState === void 0 ? void 0 : syncState.serverRevision), // Don't connect before there's no initial sync performed.
|
|
3577
3801
|
take(1), // Don't continue waking up whenever syncState change
|
|
3578
|
-
switchMap((syncState) => db.cloud.currentUser.pipe(map((userLogin) => [userLogin, syncState]))), switchMap(([userLogin, syncState]) =>
|
|
3802
|
+
switchMap((syncState) => db.cloud.currentUser.pipe(map((userLogin) => [userLogin, syncState]))), switchMap(([userLogin, syncState]) => {
|
|
3803
|
+
/*if (userLogin.license?.status && userLogin.license.status !== 'ok') {
|
|
3804
|
+
throw new InvalidLicenseError();
|
|
3805
|
+
}*/
|
|
3806
|
+
return userIsReallyActive.pipe(map((isActive) => [isActive ? userLogin : null, syncState]));
|
|
3807
|
+
}), switchMap(([userLogin, syncState]) => {
|
|
3579
3808
|
if ((userLogin === null || userLogin === void 0 ? void 0 : userLogin.isLoggedIn) && !(syncState === null || syncState === void 0 ? void 0 : syncState.realms.includes(userLogin.userId))) {
|
|
3580
3809
|
// We're in an in-between state when user is logged in but the user's realms are not yet synced.
|
|
3581
3810
|
// Don't make this change reconnect the websocket just yet. Wait till syncState is updated
|
|
@@ -3604,14 +3833,20 @@ function connectWebSocket(db) {
|
|
|
3604
3833
|
yield db.table('$logins').update(user.userId, {
|
|
3605
3834
|
accessToken: refreshedLogin.accessToken,
|
|
3606
3835
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
3836
|
+
claims: refreshedLogin.claims,
|
|
3837
|
+
license: refreshedLogin.license,
|
|
3607
3838
|
});
|
|
3608
3839
|
})), switchMap(() => createObservable()));
|
|
3609
3840
|
}
|
|
3610
3841
|
else {
|
|
3611
|
-
return throwError(error);
|
|
3842
|
+
return throwError(() => error);
|
|
3612
3843
|
}
|
|
3613
3844
|
}), catchError((error) => {
|
|
3614
3845
|
db.cloud.webSocketStatus.next("error");
|
|
3846
|
+
if (error instanceof InvalidLicenseError) {
|
|
3847
|
+
// Don't retry. Just throw and don't try connect again.
|
|
3848
|
+
return throwError(() => error);
|
|
3849
|
+
}
|
|
3615
3850
|
return from(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()));
|
|
3616
3851
|
}));
|
|
3617
3852
|
}
|
|
@@ -3640,97 +3875,12 @@ function isSyncNeeded(db) {
|
|
|
3640
3875
|
});
|
|
3641
3876
|
}
|
|
3642
3877
|
|
|
3643
|
-
|
|
3644
|
-
|
|
3645
|
-
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
const GUARDED_JOB_TIMEOUT = 1 * MINUTES;
|
|
3650
|
-
function performGuardedJob(db, jobName, jobsTableName, job, { awaitRemoteJob } = {}) {
|
|
3651
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
3652
|
-
// Start working.
|
|
3653
|
-
//
|
|
3654
|
-
// Check if someone else is working on this already.
|
|
3655
|
-
//
|
|
3656
|
-
const jobsTable = db.table(jobsTableName);
|
|
3657
|
-
function aquireLock() {
|
|
3658
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
3659
|
-
const gotTheLock = yield db.transaction('rw!', jobsTableName, () => __awaiter(this, void 0, void 0, function* () {
|
|
3660
|
-
const currentWork = yield jobsTable.get(jobName);
|
|
3661
|
-
if (!currentWork) {
|
|
3662
|
-
// No one else is working. Let's record that we are.
|
|
3663
|
-
yield jobsTable.add({
|
|
3664
|
-
nodeId: myId,
|
|
3665
|
-
started: new Date(),
|
|
3666
|
-
heartbeat: new Date()
|
|
3667
|
-
}, jobName);
|
|
3668
|
-
return true;
|
|
3669
|
-
}
|
|
3670
|
-
else if (currentWork.heartbeat.getTime() <
|
|
3671
|
-
Date.now() - GUARDED_JOB_TIMEOUT) {
|
|
3672
|
-
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!`);
|
|
3673
|
-
// Now, take over!
|
|
3674
|
-
yield jobsTable.put({
|
|
3675
|
-
nodeId: myId,
|
|
3676
|
-
started: new Date(),
|
|
3677
|
-
heartbeat: new Date()
|
|
3678
|
-
}, jobName);
|
|
3679
|
-
return true;
|
|
3680
|
-
}
|
|
3681
|
-
return false;
|
|
3682
|
-
}));
|
|
3683
|
-
if (gotTheLock)
|
|
3684
|
-
return true;
|
|
3685
|
-
// Someone else took the job.
|
|
3686
|
-
if (awaitRemoteJob) {
|
|
3687
|
-
try {
|
|
3688
|
-
const jobDoneObservable = from(liveQuery(() => jobsTable.get(jobName))).pipe(timeout(GUARDED_JOB_TIMEOUT), filter((job) => !job)); // Wait til job is not there anymore.
|
|
3689
|
-
yield jobDoneObservable.toPromise();
|
|
3690
|
-
return false;
|
|
3691
|
-
}
|
|
3692
|
-
catch (err) {
|
|
3693
|
-
if (err.name !== 'TimeoutError') {
|
|
3694
|
-
throw err;
|
|
3695
|
-
}
|
|
3696
|
-
// Timeout stopped us! Try aquire the lock now.
|
|
3697
|
-
// It will likely succeed this time unless
|
|
3698
|
-
// another client took it.
|
|
3699
|
-
return yield aquireLock();
|
|
3700
|
-
}
|
|
3701
|
-
}
|
|
3702
|
-
return false;
|
|
3703
|
-
});
|
|
3704
|
-
}
|
|
3705
|
-
if (yield aquireLock()) {
|
|
3706
|
-
// We own the lock entry and can do our job undisturbed.
|
|
3707
|
-
// We're not within a transaction, but these type of locks
|
|
3708
|
-
// spans over transactions.
|
|
3709
|
-
// Start our heart beat during the job.
|
|
3710
|
-
// Use setInterval to make sure we are updating heartbeat even during long-lived fetch calls.
|
|
3711
|
-
const heartbeat = setInterval(() => {
|
|
3712
|
-
jobsTable.update(jobName, (job) => {
|
|
3713
|
-
if (job.nodeId === myId) {
|
|
3714
|
-
job.heartbeat = new Date();
|
|
3715
|
-
}
|
|
3716
|
-
});
|
|
3717
|
-
}, GUARDED_JOB_HEARTBEAT);
|
|
3718
|
-
try {
|
|
3719
|
-
return yield job();
|
|
3720
|
-
}
|
|
3721
|
-
finally {
|
|
3722
|
-
// Stop heartbeat
|
|
3723
|
-
clearInterval(heartbeat);
|
|
3724
|
-
// Remove the persisted job state:
|
|
3725
|
-
yield db.transaction('rw!', jobsTableName, () => __awaiter(this, void 0, void 0, function* () {
|
|
3726
|
-
const currentWork = yield jobsTable.get(jobName);
|
|
3727
|
-
if (currentWork && currentWork.nodeId === myId) {
|
|
3728
|
-
yield jobsTable.delete(jobName);
|
|
3729
|
-
}
|
|
3730
|
-
}));
|
|
3731
|
-
}
|
|
3732
|
-
}
|
|
3733
|
-
});
|
|
3878
|
+
function performGuardedJob(db, jobName, job) {
|
|
3879
|
+
if (typeof navigator === 'undefined' || !navigator.locks) {
|
|
3880
|
+
// No support for guarding jobs. IE11, node.js, etc.
|
|
3881
|
+
return job();
|
|
3882
|
+
}
|
|
3883
|
+
return navigator.locks.request(db.name + '|' + jobName, () => job());
|
|
3734
3884
|
}
|
|
3735
3885
|
|
|
3736
3886
|
const ongoingSyncs = new WeakMap();
|
|
@@ -3784,6 +3934,9 @@ function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
|
3784
3934
|
function _syncIfPossible() {
|
|
3785
3935
|
return __awaiter(this, void 0, void 0, function* () {
|
|
3786
3936
|
try {
|
|
3937
|
+
// Check if should delay sync due to ratelimit:
|
|
3938
|
+
yield checkSyncRateLimitDelay(db);
|
|
3939
|
+
// Check if we need to lock the sync job. Not needed if we are the service worker.
|
|
3787
3940
|
if (db.cloud.isServiceWorkerDB) {
|
|
3788
3941
|
// We are the dedicated sync SW:
|
|
3789
3942
|
yield sync(db, cloudOptions, cloudSchema, options);
|
|
@@ -3791,7 +3944,7 @@ function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
|
3791
3944
|
else if (!db.cloud.usingServiceWorker) {
|
|
3792
3945
|
// We use a flow that is better suited for the case when multiple workers want to
|
|
3793
3946
|
// do the same thing.
|
|
3794
|
-
yield performGuardedJob(db, CURRENT_SYNC_WORKER,
|
|
3947
|
+
yield performGuardedJob(db, CURRENT_SYNC_WORKER, () => sync(db, cloudOptions, cloudSchema, options));
|
|
3795
3948
|
}
|
|
3796
3949
|
else {
|
|
3797
3950
|
assert(false);
|
|
@@ -3813,19 +3966,29 @@ function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
|
3813
3966
|
}
|
|
3814
3967
|
}
|
|
3815
3968
|
|
|
3969
|
+
const SECONDS = 1000;
|
|
3970
|
+
const MINUTES = 60 * SECONDS;
|
|
3971
|
+
|
|
3816
3972
|
function LocalSyncWorker(db, cloudOptions, cloudSchema) {
|
|
3817
3973
|
let localSyncEventSubscription = null;
|
|
3818
3974
|
//let syncHandler: ((event: Event) => void) | null = null;
|
|
3819
3975
|
//let periodicSyncHandler: ((event: Event) => void) | null = null;
|
|
3820
3976
|
let cancelToken = { cancelled: false };
|
|
3977
|
+
let retryHandle = null;
|
|
3978
|
+
let retryPurpose = null; // "pull" is superset of "push"
|
|
3821
3979
|
function syncAndRetry(purpose, retryNum = 1) {
|
|
3822
3980
|
// Use setTimeout() to get onto a clean stack and
|
|
3823
3981
|
// break free from possible active transaction:
|
|
3824
3982
|
setTimeout(() => {
|
|
3983
|
+
if (retryHandle)
|
|
3984
|
+
clearTimeout(retryHandle);
|
|
3985
|
+
const combPurpose = retryPurpose === 'pull' ? 'pull' : purpose;
|
|
3986
|
+
retryHandle = null;
|
|
3987
|
+
retryPurpose = null;
|
|
3825
3988
|
syncIfPossible(db, cloudOptions, cloudSchema, {
|
|
3826
3989
|
cancelToken,
|
|
3827
3990
|
retryImmediatelyOnFetchError: true,
|
|
3828
|
-
purpose,
|
|
3991
|
+
purpose: combPurpose,
|
|
3829
3992
|
}).catch((e) => {
|
|
3830
3993
|
console.error('error in syncIfPossible()', e);
|
|
3831
3994
|
if (cancelToken.cancelled) {
|
|
@@ -3835,7 +3998,13 @@ function LocalSyncWorker(db, cloudOptions, cloudSchema) {
|
|
|
3835
3998
|
// Mimic service worker sync event: retry 3 times
|
|
3836
3999
|
// * first retry after 5 minutes
|
|
3837
4000
|
// * second retry 15 minutes later
|
|
3838
|
-
|
|
4001
|
+
const combinedPurpose = retryPurpose && retryPurpose === 'pull' ? 'pull' : purpose;
|
|
4002
|
+
const handle = setTimeout(() => syncAndRetry(combinedPurpose, retryNum + 1), [0, 5, 15][retryNum] * MINUTES);
|
|
4003
|
+
// Cancel the previous retryHandle if it exists to avoid scheduling loads of retries.
|
|
4004
|
+
if (retryHandle)
|
|
4005
|
+
clearTimeout(retryHandle);
|
|
4006
|
+
retryHandle = handle;
|
|
4007
|
+
retryPurpose = combinedPurpose;
|
|
3839
4008
|
}
|
|
3840
4009
|
});
|
|
3841
4010
|
}, 0);
|
|
@@ -3902,10 +4071,12 @@ const Styles = {
|
|
|
3902
4071
|
},
|
|
3903
4072
|
Alert: {
|
|
3904
4073
|
error: {
|
|
3905
|
-
color: "red"
|
|
4074
|
+
color: "red",
|
|
4075
|
+
fontWeight: "bold"
|
|
3906
4076
|
},
|
|
3907
4077
|
warning: {
|
|
3908
|
-
color: "
|
|
4078
|
+
color: "#f80",
|
|
4079
|
+
fontWeight: "bold"
|
|
3909
4080
|
},
|
|
3910
4081
|
info: {
|
|
3911
4082
|
color: "black"
|
|
@@ -3946,7 +4117,8 @@ const Styles = {
|
|
|
3946
4117
|
border: "3px solid #3d3d5d",
|
|
3947
4118
|
borderRadius: "8px",
|
|
3948
4119
|
boxShadow: "0 0 80px 10px #666",
|
|
3949
|
-
width: "auto"
|
|
4120
|
+
width: "auto",
|
|
4121
|
+
fontFamily: "sans-serif",
|
|
3950
4122
|
},
|
|
3951
4123
|
Input: {
|
|
3952
4124
|
height: "35px",
|
|
@@ -3967,11 +4139,26 @@ function Dialog({ children, className }) {
|
|
|
3967
4139
|
|
|
3968
4140
|
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}
|
|
3969
4141
|
|
|
4142
|
+
/** Resolve a message template with parameters.
|
|
4143
|
+
*
|
|
4144
|
+
* Example:
|
|
4145
|
+
* resolveText({
|
|
4146
|
+
* message: "Hello {name}!",
|
|
4147
|
+
* messageCode: "HELLO",
|
|
4148
|
+
* messageParams: {name: "David"}
|
|
4149
|
+
* }) => "Hello David!"
|
|
4150
|
+
*
|
|
4151
|
+
* @param message Template message with {vars} in it.
|
|
4152
|
+
* @param messageCode Unique code for the message. Can be used for translation.
|
|
4153
|
+
* @param messageParams Parameters to be used in the message.
|
|
4154
|
+
* @returns A final message where parameters have been replaced with values.
|
|
4155
|
+
*/
|
|
3970
4156
|
function resolveText({ message, messageCode, messageParams }) {
|
|
3971
|
-
return message.replace(/\{\w+\}/ig, n => messageParams[n.
|
|
4157
|
+
return message.replace(/\{\w+\}/ig, n => messageParams[n.substring(1, n.length - 1)]);
|
|
3972
4158
|
}
|
|
3973
4159
|
|
|
3974
|
-
|
|
4160
|
+
const OTP_LENGTH = 8;
|
|
4161
|
+
function LoginDialog({ title, type, alerts, fields, submitLabel, cancelLabel, onCancel, onSubmit, }) {
|
|
3975
4162
|
const [params, setParams] = p({});
|
|
3976
4163
|
const firstFieldRef = _(null);
|
|
3977
4164
|
s(() => { var _a; return (_a = firstFieldRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, []);
|
|
@@ -3979,21 +4166,34 @@ function LoginDialog({ title, alerts, fields, onCancel, onSubmit, }) {
|
|
|
3979
4166
|
h(p$1, null,
|
|
3980
4167
|
h("h3", { style: Styles.WindowHeader }, title),
|
|
3981
4168
|
alerts.map((alert) => (h("p", { style: Styles.Alert[alert.type] }, resolveText(alert)))),
|
|
3982
|
-
h("form", { onSubmit: ev => {
|
|
4169
|
+
h("form", { onSubmit: (ev) => {
|
|
3983
4170
|
ev.preventDefault();
|
|
3984
4171
|
onSubmit(params);
|
|
3985
|
-
} }, Object.entries(fields).map(([fieldName, { type, label, placeholder }], idx) => (h("label", { style: Styles.Label },
|
|
4172
|
+
} }, Object.entries(fields).map(([fieldName, { type, label, placeholder }], idx) => (h("label", { style: Styles.Label, key: idx },
|
|
3986
4173
|
label ? `${label}: ` : '',
|
|
3987
|
-
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) => {
|
|
4174
|
+
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) => {
|
|
4175
|
+
var _a;
|
|
4176
|
+
const value = valueTransformer(type, (_a = ev.target) === null || _a === void 0 ? void 0 : _a['value']);
|
|
4177
|
+
let updatedParams = Object.assign(Object.assign({}, params), { [fieldName]: value });
|
|
4178
|
+
setParams(updatedParams);
|
|
4179
|
+
if (type === 'otp' && (value === null || value === void 0 ? void 0 : value.trim().length) === OTP_LENGTH) {
|
|
4180
|
+
// Auto-submit when OTP is filled in.
|
|
4181
|
+
onSubmit(updatedParams);
|
|
4182
|
+
}
|
|
4183
|
+
} })))))),
|
|
3988
4184
|
h("div", { style: Styles.ButtonsDiv },
|
|
3989
|
-
h(
|
|
3990
|
-
|
|
4185
|
+
h(p$1, null,
|
|
4186
|
+
h("button", { type: "submit", style: Styles.Button, onClick: () => onSubmit(params) }, submitLabel),
|
|
4187
|
+
cancelLabel && (h("button", { style: Styles.Button, onClick: onCancel }, cancelLabel))))));
|
|
3991
4188
|
}
|
|
3992
4189
|
function valueTransformer(type, value) {
|
|
3993
4190
|
switch (type) {
|
|
3994
|
-
case
|
|
3995
|
-
|
|
3996
|
-
|
|
4191
|
+
case 'email':
|
|
4192
|
+
return value.toLowerCase();
|
|
4193
|
+
case 'otp':
|
|
4194
|
+
return value.toUpperCase();
|
|
4195
|
+
default:
|
|
4196
|
+
return value;
|
|
3997
4197
|
}
|
|
3998
4198
|
}
|
|
3999
4199
|
|
|
@@ -4047,11 +4247,20 @@ function setupDefaultGUI(db) {
|
|
|
4047
4247
|
}
|
|
4048
4248
|
};
|
|
4049
4249
|
}
|
|
4050
|
-
|
|
4051
|
-
|
|
4052
|
-
|
|
4053
|
-
|
|
4054
|
-
|
|
4250
|
+
|
|
4251
|
+
function associate(factory) {
|
|
4252
|
+
const wm = new WeakMap();
|
|
4253
|
+
return (x) => {
|
|
4254
|
+
let rv = wm.get(x);
|
|
4255
|
+
if (!rv) {
|
|
4256
|
+
rv = factory(x);
|
|
4257
|
+
wm.set(x, rv);
|
|
4258
|
+
}
|
|
4259
|
+
return rv;
|
|
4260
|
+
};
|
|
4261
|
+
}
|
|
4262
|
+
|
|
4263
|
+
const getCurrentUserEmitter = associate((db) => new BehaviorSubject(UNAUTHORIZED_USER));
|
|
4055
4264
|
|
|
4056
4265
|
function computeSyncState(db) {
|
|
4057
4266
|
let _prevStatus = db.cloud.webSocketStatus.value;
|
|
@@ -4079,8 +4288,17 @@ function computeSyncState(db) {
|
|
|
4079
4288
|
return combineLatest([
|
|
4080
4289
|
lazyWebSocketStatus,
|
|
4081
4290
|
db.syncStateChangedEvent.pipe(startWith({ phase: 'initial' })),
|
|
4291
|
+
getCurrentUserEmitter(db.dx._novip),
|
|
4082
4292
|
userIsReallyActive
|
|
4083
|
-
]).pipe(map(([status, syncState, userIsActive]) => {
|
|
4293
|
+
]).pipe(map(([status, syncState, user, userIsActive]) => {
|
|
4294
|
+
var _a;
|
|
4295
|
+
if (((_a = user.license) === null || _a === void 0 ? void 0 : _a.status) && user.license.status !== 'ok') {
|
|
4296
|
+
return {
|
|
4297
|
+
phase: 'offline',
|
|
4298
|
+
status: 'offline',
|
|
4299
|
+
license: user.license.status
|
|
4300
|
+
};
|
|
4301
|
+
}
|
|
4084
4302
|
let { phase, error, progress } = syncState;
|
|
4085
4303
|
let adjustedStatus = status;
|
|
4086
4304
|
if (phase === 'error') {
|
|
@@ -4113,23 +4331,12 @@ function computeSyncState(db) {
|
|
|
4113
4331
|
error,
|
|
4114
4332
|
progress,
|
|
4115
4333
|
status: isOnline ? adjustedStatus : 'offline',
|
|
4334
|
+
license: 'ok'
|
|
4116
4335
|
};
|
|
4117
4336
|
return retState;
|
|
4118
4337
|
}));
|
|
4119
4338
|
}
|
|
4120
4339
|
|
|
4121
|
-
function associate(factory) {
|
|
4122
|
-
const wm = new WeakMap();
|
|
4123
|
-
return (x) => {
|
|
4124
|
-
let rv = wm.get(x);
|
|
4125
|
-
if (!rv) {
|
|
4126
|
-
rv = factory(x);
|
|
4127
|
-
wm.set(x, rv);
|
|
4128
|
-
}
|
|
4129
|
-
return rv;
|
|
4130
|
-
};
|
|
4131
|
-
}
|
|
4132
|
-
|
|
4133
4340
|
function createSharedValueObservable(o, defaultValue) {
|
|
4134
4341
|
let currentValue = defaultValue;
|
|
4135
4342
|
let shared = from(o).pipe(map$1((x) => (currentValue = x)), share({ resetOnRefCountZero: () => timer(1000) }));
|
|
@@ -4171,8 +4378,6 @@ const getGlobalRolesObservable = associate((db) => {
|
|
|
4171
4378
|
})), {});
|
|
4172
4379
|
});
|
|
4173
4380
|
|
|
4174
|
-
const getCurrentUserEmitter = associate((db) => new BehaviorSubject(UNAUTHORIZED_USER));
|
|
4175
|
-
|
|
4176
4381
|
const getInternalAccessControlObservable = associate((db) => {
|
|
4177
4382
|
return createSharedValueObservable(getCurrentUserEmitter(db._novip).pipe(switchMap((currentUser) => liveQuery(() => db.transaction('r', 'realms', 'members', () => Promise.all([
|
|
4178
4383
|
db.members.where({ userId: currentUser.userId }).toArray(),
|
|
@@ -4455,7 +4660,7 @@ function dexieCloud(dexie) {
|
|
|
4455
4660
|
});
|
|
4456
4661
|
const syncComplete = new Subject();
|
|
4457
4662
|
dexie.cloud = {
|
|
4458
|
-
version: '4.0.1-beta.
|
|
4663
|
+
version: '4.0.1-beta.47',
|
|
4459
4664
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
4460
4665
|
schema: null,
|
|
4461
4666
|
get currentUserId() {
|
|
@@ -4491,11 +4696,24 @@ function dexieCloud(dexie) {
|
|
|
4491
4696
|
}
|
|
4492
4697
|
updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
|
|
4493
4698
|
},
|
|
4699
|
+
logout({ force } = {}) {
|
|
4700
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
4701
|
+
force
|
|
4702
|
+
? yield _logout(DexieCloudDB(dexie), { deleteUnsyncedData: true })
|
|
4703
|
+
: yield logout(DexieCloudDB(dexie));
|
|
4704
|
+
});
|
|
4705
|
+
},
|
|
4494
4706
|
sync({ wait, purpose } = { wait: true, purpose: 'push' }) {
|
|
4707
|
+
var _a;
|
|
4495
4708
|
return __awaiter(this, void 0, void 0, function* () {
|
|
4496
4709
|
if (wait === undefined)
|
|
4497
4710
|
wait = true;
|
|
4498
4711
|
const db = DexieCloudDB(dexie);
|
|
4712
|
+
const licenseStatus = ((_a = db.cloud.currentUser.value.license) === null || _a === void 0 ? void 0 : _a.status) || 'ok';
|
|
4713
|
+
if (licenseStatus !== 'ok') {
|
|
4714
|
+
// Refresh access token to check for updated license
|
|
4715
|
+
yield loadAccessToken(db);
|
|
4716
|
+
}
|
|
4499
4717
|
if (purpose === 'pull') {
|
|
4500
4718
|
const syncState = db.cloud.persistedSyncState.value;
|
|
4501
4719
|
triggerSync(db, purpose);
|
|
@@ -4699,7 +4917,9 @@ function dexieCloud(dexie) {
|
|
|
4699
4917
|
db.syncStateChangedEvent.next({
|
|
4700
4918
|
phase: 'not-in-sync',
|
|
4701
4919
|
});
|
|
4702
|
-
|
|
4920
|
+
if (!isEagerSyncDisabled(db)) {
|
|
4921
|
+
triggerSync(db, 'push');
|
|
4922
|
+
}
|
|
4703
4923
|
}), fromEvent(self, 'offline').subscribe(() => {
|
|
4704
4924
|
console.debug('offline!');
|
|
4705
4925
|
db.syncStateChangedEvent.next({
|
|
@@ -4716,7 +4936,7 @@ function dexieCloud(dexie) {
|
|
|
4716
4936
|
});
|
|
4717
4937
|
}
|
|
4718
4938
|
}
|
|
4719
|
-
dexieCloud.version = '4.0.1-beta.
|
|
4939
|
+
dexieCloud.version = '4.0.1-beta.47';
|
|
4720
4940
|
Dexie.Cloud = dexieCloud;
|
|
4721
4941
|
|
|
4722
4942
|
// In case the SW lives for a while, let it reuse already opened connections:
|