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
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* ==========================================================================
|
|
10
10
|
*
|
|
11
|
-
* Version 4.0.1-beta.
|
|
11
|
+
* Version 4.0.1-beta.47, Fri Oct 13 2023
|
|
12
12
|
*
|
|
13
13
|
* https://dexie.org
|
|
14
14
|
*
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import Dexie, { cmp, liveQuery } from 'dexie';
|
|
20
|
-
import { Observable as Observable$1, BehaviorSubject, Subject, fromEvent, of, merge, Subscription as Subscription$1,
|
|
20
|
+
import { firstValueFrom, from as from$1, filter as filter$1, Observable as Observable$1, BehaviorSubject, Subject, fromEvent, of, merge, Subscription as Subscription$1, throwError, combineLatest, map as map$1, share, timer as timer$1 } from 'rxjs';
|
|
21
21
|
|
|
22
22
|
/******************************************************************************
|
|
23
23
|
Copyright (c) Microsoft Corporation.
|
|
@@ -1917,58 +1917,6 @@ function tap(observerOrNext, error, complete) {
|
|
|
1917
1917
|
identity;
|
|
1918
1918
|
}
|
|
1919
1919
|
|
|
1920
|
-
var TimeoutError = createErrorClass(function (_super) {
|
|
1921
|
-
return function TimeoutErrorImpl(info) {
|
|
1922
|
-
if (info === void 0) { info = null; }
|
|
1923
|
-
_super(this);
|
|
1924
|
-
this.message = 'Timeout has occurred';
|
|
1925
|
-
this.name = 'TimeoutError';
|
|
1926
|
-
this.info = info;
|
|
1927
|
-
};
|
|
1928
|
-
});
|
|
1929
|
-
function timeout(config, schedulerArg) {
|
|
1930
|
-
var _a = (isValidDate(config) ? { first: config } : typeof config === 'number' ? { each: config } : config), first = _a.first, each = _a.each, _b = _a.with, _with = _b === void 0 ? timeoutErrorFactory : _b, _c = _a.scheduler, scheduler = _c === void 0 ? schedulerArg !== null && schedulerArg !== void 0 ? schedulerArg : asyncScheduler : _c, _d = _a.meta, meta = _d === void 0 ? null : _d;
|
|
1931
|
-
if (first == null && each == null) {
|
|
1932
|
-
throw new TypeError('No timeout provided.');
|
|
1933
|
-
}
|
|
1934
|
-
return operate(function (source, subscriber) {
|
|
1935
|
-
var originalSourceSubscription;
|
|
1936
|
-
var timerSubscription;
|
|
1937
|
-
var lastValue = null;
|
|
1938
|
-
var seen = 0;
|
|
1939
|
-
var startTimer = function (delay) {
|
|
1940
|
-
timerSubscription = executeSchedule(subscriber, scheduler, function () {
|
|
1941
|
-
try {
|
|
1942
|
-
originalSourceSubscription.unsubscribe();
|
|
1943
|
-
innerFrom(_with({
|
|
1944
|
-
meta: meta,
|
|
1945
|
-
lastValue: lastValue,
|
|
1946
|
-
seen: seen,
|
|
1947
|
-
})).subscribe(subscriber);
|
|
1948
|
-
}
|
|
1949
|
-
catch (err) {
|
|
1950
|
-
subscriber.error(err);
|
|
1951
|
-
}
|
|
1952
|
-
}, delay);
|
|
1953
|
-
};
|
|
1954
|
-
originalSourceSubscription = source.subscribe(createOperatorSubscriber(subscriber, function (value) {
|
|
1955
|
-
timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.unsubscribe();
|
|
1956
|
-
seen++;
|
|
1957
|
-
subscriber.next((lastValue = value));
|
|
1958
|
-
each > 0 && startTimer(each);
|
|
1959
|
-
}, undefined, undefined, function () {
|
|
1960
|
-
if (!(timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.closed)) {
|
|
1961
|
-
timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.unsubscribe();
|
|
1962
|
-
}
|
|
1963
|
-
lastValue = null;
|
|
1964
|
-
}));
|
|
1965
|
-
!seen && startTimer(first != null ? (typeof first === 'number' ? first : +first - scheduler.now()) : each);
|
|
1966
|
-
});
|
|
1967
|
-
}
|
|
1968
|
-
function timeoutErrorFactory(info) {
|
|
1969
|
-
throw new TimeoutError(info);
|
|
1970
|
-
}
|
|
1971
|
-
|
|
1972
1920
|
//const hasSW = 'serviceWorker' in navigator;
|
|
1973
1921
|
let hasComplainedAboutSyncEvent = false;
|
|
1974
1922
|
function registerSyncEvent(db, purpose) {
|
|
@@ -2029,6 +1977,7 @@ function registerPeriodicSyncEvent(db) {
|
|
|
2029
1977
|
|
|
2030
1978
|
function triggerSync(db, purpose) {
|
|
2031
1979
|
if (db.cloud.usingServiceWorker) {
|
|
1980
|
+
console.debug('registering sync event');
|
|
2032
1981
|
registerSyncEvent(db, purpose);
|
|
2033
1982
|
}
|
|
2034
1983
|
else {
|
|
@@ -2060,14 +2009,24 @@ const b64encode = typeof Buffer !== "undefined"
|
|
|
2060
2009
|
return btoa(String.fromCharCode.apply(null, ArrayBuffer.isView(b) ? b : new Uint8Array(b)));
|
|
2061
2010
|
};
|
|
2062
2011
|
|
|
2012
|
+
class TokenErrorResponseError extends Error {
|
|
2013
|
+
constructor({ title, message, messageCode, messageParams, }) {
|
|
2014
|
+
super(message);
|
|
2015
|
+
this.name = 'TokenErrorResponseError';
|
|
2016
|
+
this.title = title;
|
|
2017
|
+
this.messageCode = messageCode;
|
|
2018
|
+
this.messageParams = messageParams;
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2063
2022
|
function interactWithUser(userInteraction, req) {
|
|
2064
2023
|
return new Promise((resolve, reject) => {
|
|
2065
|
-
const interactionProps = Object.assign(Object.assign({}, req), { onSubmit: (res) => {
|
|
2024
|
+
const interactionProps = Object.assign(Object.assign({ submitLabel: 'Submit', cancelLabel: 'Cancel' }, req), { onSubmit: (res) => {
|
|
2066
2025
|
userInteraction.next(undefined);
|
|
2067
2026
|
resolve(res);
|
|
2068
2027
|
}, onCancel: () => {
|
|
2069
2028
|
userInteraction.next(undefined);
|
|
2070
|
-
reject(new Dexie.AbortError(
|
|
2029
|
+
reject(new Dexie.AbortError('User cancelled'));
|
|
2071
2030
|
} });
|
|
2072
2031
|
userInteraction.next(interactionProps);
|
|
2073
2032
|
// Start subscribing for external updates to db.cloud.userInteraction, and if so, cancel this request.
|
|
@@ -2086,7 +2045,9 @@ function alertUser(userInteraction, title, ...alerts) {
|
|
|
2086
2045
|
type: 'message-alert',
|
|
2087
2046
|
title,
|
|
2088
2047
|
alerts,
|
|
2089
|
-
fields: {}
|
|
2048
|
+
fields: {},
|
|
2049
|
+
submitLabel: 'OK',
|
|
2050
|
+
cancelLabel: null,
|
|
2090
2051
|
});
|
|
2091
2052
|
}
|
|
2092
2053
|
function promptForEmail(userInteraction, title, emailHint) {
|
|
@@ -2145,22 +2106,48 @@ function promptForOTP(userInteraction, email, alert) {
|
|
|
2145
2106
|
return otp;
|
|
2146
2107
|
});
|
|
2147
2108
|
}
|
|
2109
|
+
function confirmLogout(userInteraction, currentUserId, numUnsyncedChanges) {
|
|
2110
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2111
|
+
const alerts = [
|
|
2112
|
+
{
|
|
2113
|
+
type: 'warning',
|
|
2114
|
+
messageCode: 'LOGOUT_CONFIRMATION',
|
|
2115
|
+
message: `{numUnsyncedChanges} unsynced changes will get lost!
|
|
2116
|
+
Logout anyway?`,
|
|
2117
|
+
messageParams: {
|
|
2118
|
+
currentUserId,
|
|
2119
|
+
numUnsyncedChanges: numUnsyncedChanges.toString(),
|
|
2120
|
+
}
|
|
2121
|
+
},
|
|
2122
|
+
];
|
|
2123
|
+
return yield interactWithUser(userInteraction, {
|
|
2124
|
+
type: 'logout-confirmation',
|
|
2125
|
+
title: 'Confirm Logout',
|
|
2126
|
+
alerts,
|
|
2127
|
+
fields: {},
|
|
2128
|
+
submitLabel: 'Confirm logout',
|
|
2129
|
+
cancelLabel: 'Cancel'
|
|
2130
|
+
})
|
|
2131
|
+
.then(() => true)
|
|
2132
|
+
.catch(() => false);
|
|
2133
|
+
});
|
|
2134
|
+
}
|
|
2148
2135
|
|
|
2149
2136
|
function loadAccessToken(db) {
|
|
2150
|
-
var _a, _b;
|
|
2137
|
+
var _a, _b, _c;
|
|
2151
2138
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2152
2139
|
const currentUser = yield db.getCurrentUser();
|
|
2153
2140
|
const { accessToken, accessTokenExpiration, refreshToken, refreshTokenExpiration, claims, } = currentUser;
|
|
2154
2141
|
if (!accessToken)
|
|
2155
|
-
return;
|
|
2142
|
+
return null;
|
|
2156
2143
|
const expTime = (_a = accessTokenExpiration === null || accessTokenExpiration === void 0 ? void 0 : accessTokenExpiration.getTime()) !== null && _a !== void 0 ? _a : Infinity;
|
|
2157
|
-
if (expTime > Date.now()) {
|
|
2158
|
-
return
|
|
2144
|
+
if (expTime > Date.now() && (((_b = currentUser.license) === null || _b === void 0 ? void 0 : _b.status) || 'ok') === 'ok') {
|
|
2145
|
+
return currentUser;
|
|
2159
2146
|
}
|
|
2160
2147
|
if (!refreshToken) {
|
|
2161
2148
|
throw new Error(`Refresh token missing`);
|
|
2162
2149
|
}
|
|
2163
|
-
const refreshExpTime = (
|
|
2150
|
+
const refreshExpTime = (_c = refreshTokenExpiration === null || refreshTokenExpiration === void 0 ? void 0 : refreshTokenExpiration.getTime()) !== null && _c !== void 0 ? _c : Infinity;
|
|
2164
2151
|
if (refreshExpTime <= Date.now()) {
|
|
2165
2152
|
throw new Error(`Refresh token has expired`);
|
|
2166
2153
|
}
|
|
@@ -2168,8 +2155,10 @@ function loadAccessToken(db) {
|
|
|
2168
2155
|
yield db.table('$logins').update(claims.sub, {
|
|
2169
2156
|
accessToken: refreshedLogin.accessToken,
|
|
2170
2157
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
2158
|
+
claims: refreshedLogin.claims,
|
|
2159
|
+
license: refreshedLogin.license,
|
|
2171
2160
|
});
|
|
2172
|
-
return refreshedLogin
|
|
2161
|
+
return refreshedLogin;
|
|
2173
2162
|
});
|
|
2174
2163
|
}
|
|
2175
2164
|
function authenticate(url, context, fetchToken, userInteraction, hints) {
|
|
@@ -2217,10 +2206,24 @@ function refreshAccessToken(url, login) {
|
|
|
2217
2206
|
if (res.status !== 200)
|
|
2218
2207
|
throw new Error(`RefreshToken: Status ${res.status} from ${url}/token`);
|
|
2219
2208
|
const response = yield res.json();
|
|
2209
|
+
if (response.type === 'error') {
|
|
2210
|
+
throw new TokenErrorResponseError(response);
|
|
2211
|
+
}
|
|
2220
2212
|
login.accessToken = response.accessToken;
|
|
2221
2213
|
login.accessTokenExpiration = response.accessTokenExpiration
|
|
2222
2214
|
? new Date(response.accessTokenExpiration)
|
|
2223
2215
|
: undefined;
|
|
2216
|
+
login.claims = response.claims;
|
|
2217
|
+
login.license = {
|
|
2218
|
+
type: response.userType,
|
|
2219
|
+
status: response.claims.license || 'ok',
|
|
2220
|
+
};
|
|
2221
|
+
if (response.evalDaysLeft != null) {
|
|
2222
|
+
login.license.evalDaysLeft = response.evalDaysLeft;
|
|
2223
|
+
}
|
|
2224
|
+
if (response.userValidUntil != null) {
|
|
2225
|
+
login.license.validUntil = new Date(response.userValidUntil);
|
|
2226
|
+
}
|
|
2224
2227
|
return login;
|
|
2225
2228
|
});
|
|
2226
2229
|
}
|
|
@@ -2252,8 +2255,15 @@ function userAuthenticate(context, fetchToken, userInteraction, hints) {
|
|
|
2252
2255
|
public_key: publicKeyPEM,
|
|
2253
2256
|
hints,
|
|
2254
2257
|
});
|
|
2258
|
+
if (response2.type === 'error') {
|
|
2259
|
+
throw new TokenErrorResponseError(response2);
|
|
2260
|
+
}
|
|
2255
2261
|
if (response2.type !== 'tokens')
|
|
2256
2262
|
throw new Error(`Unexpected response type from token endpoint: ${response2.type}`);
|
|
2263
|
+
/*const licenseStatus = response2.claims.license || 'ok';
|
|
2264
|
+
if (licenseStatus !== 'ok') {
|
|
2265
|
+
throw new InvalidLicenseError(licenseStatus);
|
|
2266
|
+
}*/
|
|
2257
2267
|
context.accessToken = response2.accessToken;
|
|
2258
2268
|
context.accessTokenExpiration = new Date(response2.accessTokenExpiration);
|
|
2259
2269
|
context.refreshToken = response2.refreshToken;
|
|
@@ -2264,6 +2274,16 @@ function userAuthenticate(context, fetchToken, userInteraction, hints) {
|
|
|
2264
2274
|
context.email = response2.claims.email;
|
|
2265
2275
|
context.name = response2.claims.name;
|
|
2266
2276
|
context.claims = response2.claims;
|
|
2277
|
+
context.license = {
|
|
2278
|
+
type: response2.userType,
|
|
2279
|
+
status: response2.claims.license || 'ok',
|
|
2280
|
+
};
|
|
2281
|
+
if (response2.evalDaysLeft != null) {
|
|
2282
|
+
context.license.evalDaysLeft = response2.evalDaysLeft;
|
|
2283
|
+
}
|
|
2284
|
+
if (response2.userValidUntil != null) {
|
|
2285
|
+
context.license.validUntil = new Date(response2.userValidUntil);
|
|
2286
|
+
}
|
|
2267
2287
|
if (response2.alerts && response2.alerts.length > 0) {
|
|
2268
2288
|
yield interactWithUser(userInteraction, {
|
|
2269
2289
|
type: 'message-alert',
|
|
@@ -2275,12 +2295,36 @@ function userAuthenticate(context, fetchToken, userInteraction, hints) {
|
|
|
2275
2295
|
return context;
|
|
2276
2296
|
}
|
|
2277
2297
|
catch (error) {
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
|
|
2298
|
+
if (error instanceof TokenErrorResponseError) {
|
|
2299
|
+
yield alertUser(userInteraction, error.title, {
|
|
2300
|
+
type: 'error',
|
|
2301
|
+
messageCode: error.messageCode,
|
|
2302
|
+
message: error.message,
|
|
2303
|
+
messageParams: {},
|
|
2304
|
+
});
|
|
2305
|
+
throw error;
|
|
2306
|
+
}
|
|
2307
|
+
let message = `We're having a problem authenticating right now.`;
|
|
2308
|
+
console.error(`Error authenticating`, error);
|
|
2309
|
+
if (error instanceof TypeError) {
|
|
2310
|
+
const isOffline = typeof navigator !== undefined && !navigator.onLine;
|
|
2311
|
+
if (isOffline) {
|
|
2312
|
+
message = `You seem to be offline. Please connect to the internet and try again.`;
|
|
2313
|
+
}
|
|
2314
|
+
else if (Dexie.debug || (typeof location !== 'undefined' && (location.hostname === 'localhost' || location.hostname === '127.0.0.1'))) {
|
|
2315
|
+
// The audience is most likely the developer. Suggest to whitelist the localhost origin:
|
|
2316
|
+
message = `Could not connect to server. Please verify that your origin '${location.origin}' is whitelisted using \`npx dexie-cloud whitelist\``;
|
|
2317
|
+
}
|
|
2318
|
+
else {
|
|
2319
|
+
message = `Could not connect to server. Please verify the connection.`;
|
|
2320
|
+
}
|
|
2321
|
+
yield alertUser(userInteraction, 'Authentication Failed', {
|
|
2322
|
+
type: 'error',
|
|
2323
|
+
messageCode: 'GENERIC_ERROR',
|
|
2324
|
+
message,
|
|
2325
|
+
messageParams: {},
|
|
2326
|
+
}).catch(() => { });
|
|
2327
|
+
}
|
|
2284
2328
|
throw error;
|
|
2285
2329
|
}
|
|
2286
2330
|
});
|
|
@@ -2327,6 +2371,75 @@ class AuthPersistedContext {
|
|
|
2327
2371
|
}
|
|
2328
2372
|
}
|
|
2329
2373
|
|
|
2374
|
+
const UNAUTHORIZED_USER = {
|
|
2375
|
+
userId: "unauthorized",
|
|
2376
|
+
name: "Unauthorized",
|
|
2377
|
+
claims: {
|
|
2378
|
+
sub: "unauthorized",
|
|
2379
|
+
},
|
|
2380
|
+
lastLogin: new Date(0)
|
|
2381
|
+
};
|
|
2382
|
+
try {
|
|
2383
|
+
Object.freeze(UNAUTHORIZED_USER);
|
|
2384
|
+
Object.freeze(UNAUTHORIZED_USER.claims);
|
|
2385
|
+
}
|
|
2386
|
+
catch (_a) { }
|
|
2387
|
+
|
|
2388
|
+
function waitUntil(o, // Works with Dexie's liveQuery observables if we'd need that
|
|
2389
|
+
predicate) {
|
|
2390
|
+
return firstValueFrom(from$1(o).pipe(filter$1(predicate)));
|
|
2391
|
+
}
|
|
2392
|
+
|
|
2393
|
+
function logout(db) {
|
|
2394
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2395
|
+
const numUnsyncedChanges = yield _logout(db);
|
|
2396
|
+
if (numUnsyncedChanges) {
|
|
2397
|
+
if (yield confirmLogout(db.cloud.userInteraction, db.cloud.currentUserId, numUnsyncedChanges)) {
|
|
2398
|
+
yield _logout(db, { deleteUnsyncedData: true });
|
|
2399
|
+
}
|
|
2400
|
+
else {
|
|
2401
|
+
throw new Error(`User cancelled logout due to unsynced changes`);
|
|
2402
|
+
}
|
|
2403
|
+
}
|
|
2404
|
+
});
|
|
2405
|
+
}
|
|
2406
|
+
function _logout(db, { deleteUnsyncedData = false } = {}) {
|
|
2407
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2408
|
+
// Clear the database without emptying configuration options.
|
|
2409
|
+
const [numUnsynced, loggedOut] = yield db.dx.transaction('rw', db.dx.tables, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
2410
|
+
// @ts-ignore
|
|
2411
|
+
const idbtrans = tx.idbtrans;
|
|
2412
|
+
idbtrans.disableChangeTracking = true;
|
|
2413
|
+
idbtrans.disableAccessControl = true;
|
|
2414
|
+
const mutationTables = tx.storeNames.filter((tableName) => tableName.endsWith('_mutations'));
|
|
2415
|
+
// Count unsynced changes
|
|
2416
|
+
const unsyncCounts = yield Promise.all(mutationTables.map((mutationTable) => tx.table(mutationTable).count()));
|
|
2417
|
+
const sumUnSynced = unsyncCounts.reduce((a, b) => a + b, 0);
|
|
2418
|
+
if (sumUnSynced > 0 && !deleteUnsyncedData) {
|
|
2419
|
+
// Let caller ask user if they want to delete unsynced data.
|
|
2420
|
+
return [sumUnSynced, false];
|
|
2421
|
+
}
|
|
2422
|
+
// Either there are no unsynched changes, or caller provided flag deleteUnsynchedData = true.
|
|
2423
|
+
// Clear all tables except $jobs and $syncState (except the persisted sync state which is
|
|
2424
|
+
// also cleared because we're going to rebuild it using a fresh sync).
|
|
2425
|
+
db.$syncState.delete('syncState');
|
|
2426
|
+
for (const table of db.dx.tables) {
|
|
2427
|
+
if (table.name !== '$jobs' && table.name !== '$syncState') {
|
|
2428
|
+
table.clear();
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
return [sumUnSynced, true];
|
|
2432
|
+
}));
|
|
2433
|
+
if (loggedOut) {
|
|
2434
|
+
// Wait for currentUser observable to emit UNAUTHORIZED_USER
|
|
2435
|
+
yield waitUntil(db.cloud.currentUser, (user) => user.userId === UNAUTHORIZED_USER.userId);
|
|
2436
|
+
// Then perform an initial sync
|
|
2437
|
+
yield db.cloud.sync({ purpose: 'pull', wait: true });
|
|
2438
|
+
}
|
|
2439
|
+
return numUnsynced;
|
|
2440
|
+
});
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2330
2443
|
class HttpError extends Error {
|
|
2331
2444
|
constructor(res, message) {
|
|
2332
2445
|
super(message || `${res.status} ${res.statusText}`);
|
|
@@ -2380,8 +2493,9 @@ function otpFetchTokenCallback(db) {
|
|
|
2380
2493
|
throw new HttpError(res1, errMsg);
|
|
2381
2494
|
}
|
|
2382
2495
|
const response = yield res1.json();
|
|
2383
|
-
if (response.type === 'tokens') {
|
|
2496
|
+
if (response.type === 'tokens' || response.type === 'error') {
|
|
2384
2497
|
// Demo user request can get a "tokens" response right away
|
|
2498
|
+
// Error can also be returned right away.
|
|
2385
2499
|
return response;
|
|
2386
2500
|
}
|
|
2387
2501
|
else if (tokenRequest.grant_type === 'otp') {
|
|
@@ -2413,12 +2527,6 @@ function otpFetchTokenCallback(db) {
|
|
|
2413
2527
|
}
|
|
2414
2528
|
if (res2.status !== 200) {
|
|
2415
2529
|
const errMsg = yield res2.text();
|
|
2416
|
-
yield alertUser(userInteraction, "OTP Authentication Failed", {
|
|
2417
|
-
type: 'error',
|
|
2418
|
-
messageCode: 'GENERIC_ERROR',
|
|
2419
|
-
message: errMsg,
|
|
2420
|
-
messageParams: {}
|
|
2421
|
-
}).catch(() => { });
|
|
2422
2530
|
throw new HttpError(res2, errMsg);
|
|
2423
2531
|
}
|
|
2424
2532
|
const response2 = yield res2.json();
|
|
@@ -2431,6 +2539,18 @@ function otpFetchTokenCallback(db) {
|
|
|
2431
2539
|
};
|
|
2432
2540
|
}
|
|
2433
2541
|
|
|
2542
|
+
/** A way to log to console in production without terser stripping out
|
|
2543
|
+
* it from the release bundle.
|
|
2544
|
+
* This should be used very rarely and only in places where it's
|
|
2545
|
+
* absolutely necessary to log something in production.
|
|
2546
|
+
*
|
|
2547
|
+
* @param level
|
|
2548
|
+
* @param args
|
|
2549
|
+
*/
|
|
2550
|
+
function prodLog(level, ...args) {
|
|
2551
|
+
globalThis["con" + "sole"][level](...args);
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2434
2554
|
/** This function changes or sets the current user as requested.
|
|
2435
2555
|
*
|
|
2436
2556
|
* Use cases:
|
|
@@ -2457,102 +2577,76 @@ function setCurrentUser(db, user) {
|
|
|
2457
2577
|
}));
|
|
2458
2578
|
user.isLoggedIn = true;
|
|
2459
2579
|
user.lastLogin = new Date();
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
}));
|
|
2463
|
-
yield new Promise((resolve) => {
|
|
2464
|
-
if (db.cloud.currentUserId === user.userId) {
|
|
2465
|
-
resolve(null);
|
|
2580
|
+
try {
|
|
2581
|
+
yield user.save();
|
|
2466
2582
|
}
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
if (
|
|
2470
|
-
|
|
2471
|
-
|
|
2583
|
+
catch (e) {
|
|
2584
|
+
try {
|
|
2585
|
+
if (e.name === 'DataCloneError') {
|
|
2586
|
+
// We've seen this buggy behavior in some browsers and in case it happens
|
|
2587
|
+
// again we really need to collect the details to understand what's going on.
|
|
2588
|
+
prodLog('debug', `Login context property names:`, Object.keys(user));
|
|
2589
|
+
prodLog('debug', `Login context property names:`, Object.keys(user));
|
|
2590
|
+
prodLog('debug', `Login context:`, user);
|
|
2591
|
+
prodLog('debug', `Login context JSON:`, JSON.stringify(user));
|
|
2472
2592
|
}
|
|
2473
|
-
}
|
|
2593
|
+
}
|
|
2594
|
+
catch (_a) { }
|
|
2595
|
+
throw e;
|
|
2474
2596
|
}
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2478
|
-
// V: Samma med andra windows.
|
|
2479
|
-
// V: Så kanske göra om den till att häröra från liveQuery som läser $logins.orderBy('lastLogin').last().
|
|
2480
|
-
// V: Då bara vara medveten om:
|
|
2481
|
-
// V: En sån observable börjar hämta data vid första subscribe
|
|
2482
|
-
// V: Vi har inget "inital value" men kan emulera det till att vara ANONYMOUS_USER
|
|
2483
|
-
// V: Om requireAuth är true, så borde db.on(ready) hålla databasen stängd för alla utom denna observable.
|
|
2484
|
-
// V: Om inte så behöver den inte blocka.
|
|
2485
|
-
// Andra tankar:
|
|
2486
|
-
// * Man kan inte byta användare när man är offline. Skulle gå att flytta realms till undanstuff-tabell vid user-change.
|
|
2487
|
-
// men troligen inte värt det.
|
|
2488
|
-
// * Istället: sälj inte inte switch-user funktionalitet utan tala enbart om inloggat vs icke inloggat läge.
|
|
2489
|
-
// * populate $logins med ANONYMOUS så att en påbörjad inloggning inte räknas, alternativt ha en boolean prop!
|
|
2490
|
-
// Kanske bäst ha en boolean prop!
|
|
2491
|
-
// * Alternativ switch-user funktionalitet:
|
|
2492
|
-
// * DBCore gömmer data från realms man inte har tillgång till.
|
|
2493
|
-
// * Cursor impl behövs också då.
|
|
2494
|
-
// * Då blir det snabba user switch.
|
|
2495
|
-
// * claims-settet som skickas till servern blir summan av alla claims. Då måste servern stödja multipla tokens eller
|
|
2496
|
-
// att ens token är ett samlad.
|
|
2597
|
+
console.debug('Saved new user', user.email);
|
|
2598
|
+
}));
|
|
2599
|
+
yield waitUntil(db.cloud.currentUser, (currentUser) => currentUser.userId === user.userId);
|
|
2497
2600
|
});
|
|
2498
2601
|
}
|
|
2499
2602
|
|
|
2500
2603
|
function login(db, hints) {
|
|
2604
|
+
var _a;
|
|
2501
2605
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2502
2606
|
const currentUser = yield db.getCurrentUser();
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
throw new Error(`Must logout before changing user`);
|
|
2510
|
-
}
|
|
2607
|
+
const origUserId = currentUser.userId;
|
|
2608
|
+
if (currentUser.isLoggedIn && (!hints || (!hints.email && !hints.userId))) {
|
|
2609
|
+
const licenseStatus = ((_a = currentUser.license) === null || _a === void 0 ? void 0 : _a.status) || 'ok';
|
|
2610
|
+
if (licenseStatus === 'ok' && currentUser.accessToken && (!currentUser.accessTokenExpiration || currentUser.accessTokenExpiration.getTime() > Date.now())) {
|
|
2611
|
+
// Already authenticated according to given hints. And license is valid.
|
|
2612
|
+
return false;
|
|
2511
2613
|
}
|
|
2512
|
-
|
|
2513
|
-
|
|
2614
|
+
if (currentUser.refreshToken && (!currentUser.refreshTokenExpiration || currentUser.refreshTokenExpiration.getTime() > Date.now())) {
|
|
2615
|
+
// Refresh the token
|
|
2616
|
+
yield loadAccessToken(db);
|
|
2617
|
+
return false;
|
|
2618
|
+
}
|
|
2619
|
+
// No refresh token - must re-authenticate:
|
|
2514
2620
|
}
|
|
2515
2621
|
const context = new AuthPersistedContext(db, {
|
|
2516
2622
|
claims: {},
|
|
2517
2623
|
lastLogin: new Date(0),
|
|
2518
2624
|
});
|
|
2519
2625
|
yield authenticate(db.cloud.options.databaseUrl, context, db.cloud.options.fetchTokens || otpFetchTokenCallback(db), db.cloud.userInteraction, hints);
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2524
|
-
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2626
|
+
if (origUserId !== UNAUTHORIZED_USER.userId && context.userId !== origUserId) {
|
|
2627
|
+
// User was logged in before, but now logged in as another user.
|
|
2628
|
+
yield logout(db);
|
|
2629
|
+
}
|
|
2630
|
+
/*try {
|
|
2631
|
+
await context.save();
|
|
2632
|
+
} catch (e) {
|
|
2633
|
+
try {
|
|
2634
|
+
if (e.name === 'DataCloneError') {
|
|
2635
|
+
console.debug(`Login context property names:`, Object.keys(context));
|
|
2636
|
+
console.debug(`Login context:`, context);
|
|
2637
|
+
console.debug(`Login context JSON:`, JSON.stringify(context));
|
|
2638
|
+
}
|
|
2639
|
+
} catch {}
|
|
2640
|
+
throw e;
|
|
2641
|
+
}*/
|
|
2534
2642
|
yield setCurrentUser(db, context);
|
|
2535
2643
|
// Make sure to resync as the new login will be authorized
|
|
2536
2644
|
// for new realms.
|
|
2537
2645
|
triggerSync(db, "pull");
|
|
2538
|
-
return
|
|
2646
|
+
return context.userId !== origUserId;
|
|
2539
2647
|
});
|
|
2540
2648
|
}
|
|
2541
2649
|
|
|
2542
|
-
const UNAUTHORIZED_USER = {
|
|
2543
|
-
userId: "unauthorized",
|
|
2544
|
-
name: "Unauthorized",
|
|
2545
|
-
claims: {
|
|
2546
|
-
sub: "unauthorized",
|
|
2547
|
-
},
|
|
2548
|
-
lastLogin: new Date(0)
|
|
2549
|
-
};
|
|
2550
|
-
try {
|
|
2551
|
-
Object.freeze(UNAUTHORIZED_USER);
|
|
2552
|
-
Object.freeze(UNAUTHORIZED_USER.claims);
|
|
2553
|
-
}
|
|
2554
|
-
catch (_a) { }
|
|
2555
|
-
|
|
2556
2650
|
const swHolder = {};
|
|
2557
2651
|
const swContainer = typeof self !== 'undefined' && self.document && // self.document is to verify we're not the SW ourself
|
|
2558
2652
|
typeof navigator !== 'undefined' && navigator.serviceWorker;
|
|
@@ -3449,6 +3543,40 @@ function cloneChange(change, rewriteValues) {
|
|
|
3449
3543
|
: change.muts.map((m) => (Object.assign(Object.assign({}, m), { keys: m.keys.slice() }))) });
|
|
3450
3544
|
}
|
|
3451
3545
|
|
|
3546
|
+
// If we get Ratelimit-Limit and Ratelimit-Remaining where Ratelimit-Remaining is below
|
|
3547
|
+
// (Ratelimit-Limit / 2), we should delay the next sync by (Ratelimit-Reset / Ratelimit-Remaining)
|
|
3548
|
+
// seconds (given that there is a Ratelimit-Reset header).
|
|
3549
|
+
let syncRatelimitDelays = new WeakMap();
|
|
3550
|
+
function checkSyncRateLimitDelay(db) {
|
|
3551
|
+
var _a, _b;
|
|
3552
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3553
|
+
const delatMilliseconds = ((_b = (_a = syncRatelimitDelays.get(db)) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) - Date.now();
|
|
3554
|
+
if (delatMilliseconds > 0) {
|
|
3555
|
+
console.debug(`Stalling sync request ${delatMilliseconds} ms to spare ratelimits`);
|
|
3556
|
+
yield new Promise(resolve => setTimeout(resolve, delatMilliseconds));
|
|
3557
|
+
}
|
|
3558
|
+
});
|
|
3559
|
+
}
|
|
3560
|
+
function updateSyncRateLimitDelays(db, res) {
|
|
3561
|
+
const limit = res.headers.get('Ratelimit-Limit');
|
|
3562
|
+
const remaining = res.headers.get('Ratelimit-Remaining');
|
|
3563
|
+
const reset = res.headers.get('Ratelimit-Reset');
|
|
3564
|
+
if (limit && remaining && reset) {
|
|
3565
|
+
const limitNum = Number(limit);
|
|
3566
|
+
const remainingNum = Math.max(0, Number(remaining));
|
|
3567
|
+
const willResetInSeconds = Number(reset);
|
|
3568
|
+
if (remainingNum < limitNum / 2) {
|
|
3569
|
+
const delay = Math.ceil(willResetInSeconds / (remainingNum + 1));
|
|
3570
|
+
syncRatelimitDelays.set(db, new Date(Date.now() + delay * 1000));
|
|
3571
|
+
console.debug(`Sync ratelimit delay set to ${delay} seconds`);
|
|
3572
|
+
}
|
|
3573
|
+
else {
|
|
3574
|
+
syncRatelimitDelays.delete(db);
|
|
3575
|
+
console.debug(`Sync ratelimit delay cleared`);
|
|
3576
|
+
}
|
|
3577
|
+
}
|
|
3578
|
+
}
|
|
3579
|
+
|
|
3452
3580
|
//import {BisonWebStreamReader} from "dreambase-library/dist/typeson-simplified/BisonWebStreamReader";
|
|
3453
3581
|
function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, clientIdentity, currentUser) {
|
|
3454
3582
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -3457,9 +3585,20 @@ function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, c
|
|
|
3457
3585
|
//
|
|
3458
3586
|
const headers = {
|
|
3459
3587
|
Accept: 'application/json, application/x-bison, application/x-bison-stream',
|
|
3460
|
-
'Content-Type': 'application/tson'
|
|
3588
|
+
'Content-Type': 'application/tson',
|
|
3461
3589
|
};
|
|
3462
|
-
const
|
|
3590
|
+
const updatedUser = yield loadAccessToken(db);
|
|
3591
|
+
/*
|
|
3592
|
+
if (updatedUser?.license && changes.length > 0) {
|
|
3593
|
+
if (updatedUser.license.status === 'expired') {
|
|
3594
|
+
throw new Error(`License has expired`);
|
|
3595
|
+
}
|
|
3596
|
+
if (updatedUser.license.status === 'deactivated') {
|
|
3597
|
+
throw new Error(`License deactivated`);
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
*/
|
|
3601
|
+
const accessToken = updatedUser === null || updatedUser === void 0 ? void 0 : updatedUser.accessToken;
|
|
3463
3602
|
if (accessToken) {
|
|
3464
3603
|
headers.Authorization = `Bearer ${accessToken}`;
|
|
3465
3604
|
}
|
|
@@ -3468,27 +3607,31 @@ function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, c
|
|
|
3468
3607
|
dbID: syncState === null || syncState === void 0 ? void 0 : syncState.remoteDbId,
|
|
3469
3608
|
clientIdentity,
|
|
3470
3609
|
schema: schema || {},
|
|
3471
|
-
lastPull: syncState
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3610
|
+
lastPull: syncState
|
|
3611
|
+
? {
|
|
3612
|
+
serverRevision: syncState.serverRevision,
|
|
3613
|
+
realms: syncState.realms,
|
|
3614
|
+
inviteRealms: syncState.inviteRealms,
|
|
3615
|
+
}
|
|
3616
|
+
: undefined,
|
|
3476
3617
|
baseRevs,
|
|
3477
|
-
changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes)
|
|
3618
|
+
changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes),
|
|
3478
3619
|
};
|
|
3479
|
-
console.debug(
|
|
3620
|
+
console.debug('Sync request', syncRequest);
|
|
3480
3621
|
db.syncStateChangedEvent.next({
|
|
3481
3622
|
phase: 'pushing',
|
|
3482
3623
|
});
|
|
3483
3624
|
const res = yield fetch(`${databaseUrl}/sync`, {
|
|
3484
3625
|
method: 'post',
|
|
3485
3626
|
headers,
|
|
3486
|
-
|
|
3627
|
+
credentials: 'include',
|
|
3628
|
+
body: TSON.stringify(syncRequest),
|
|
3487
3629
|
});
|
|
3488
3630
|
//const contentLength = Number(res.headers.get('content-length'));
|
|
3489
3631
|
db.syncStateChangedEvent.next({
|
|
3490
|
-
phase: 'pulling'
|
|
3632
|
+
phase: 'pulling',
|
|
3491
3633
|
});
|
|
3634
|
+
updateSyncRateLimitDelays(db, res);
|
|
3492
3635
|
if (!res.ok) {
|
|
3493
3636
|
throw new HttpError(res);
|
|
3494
3637
|
}
|
|
@@ -3690,12 +3833,13 @@ const CURRENT_SYNC_WORKER = 'currentSyncWorker';
|
|
|
3690
3833
|
function sync(db, options, schema, syncOptions) {
|
|
3691
3834
|
return _sync
|
|
3692
3835
|
.apply(this, arguments)
|
|
3693
|
-
.then(() => {
|
|
3836
|
+
.then((result) => {
|
|
3694
3837
|
if (!(syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)) { // && syncOptions?.purpose !== 'push') {
|
|
3695
3838
|
db.syncStateChangedEvent.next({
|
|
3696
3839
|
phase: 'in-sync',
|
|
3697
3840
|
});
|
|
3698
3841
|
}
|
|
3842
|
+
return result;
|
|
3699
3843
|
})
|
|
3700
3844
|
.catch((error) => __awaiter(this, void 0, void 0, function* () {
|
|
3701
3845
|
if (syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)
|
|
@@ -3910,6 +4054,7 @@ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNee
|
|
|
3910
4054
|
}));
|
|
3911
4055
|
if (!done) {
|
|
3912
4056
|
console.debug('MORE SYNC NEEDED. Go for it again!');
|
|
4057
|
+
yield checkSyncRateLimitDelay(db);
|
|
3913
4058
|
return yield _sync(db, options, schema, { isInitialSync, cancelToken });
|
|
3914
4059
|
}
|
|
3915
4060
|
console.debug('SYNC DONE', { isInitialSync });
|
|
@@ -4046,6 +4191,8 @@ function MessagesFromServerConsumer(db) {
|
|
|
4046
4191
|
yield db.table('$logins').update(user.userId, {
|
|
4047
4192
|
accessToken: refreshedLogin.accessToken,
|
|
4048
4193
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
4194
|
+
claims: refreshedLogin.claims,
|
|
4195
|
+
license: refreshedLogin.license,
|
|
4049
4196
|
});
|
|
4050
4197
|
// Updating $logins will trigger emission of db.cloud.currentUser observable, which
|
|
4051
4198
|
// in turn will lead to that connectWebSocket.ts will reconnect the socket with the
|
|
@@ -4614,6 +4761,13 @@ function writeLock(fn, prop) {
|
|
|
4614
4761
|
|
|
4615
4762
|
const outstandingTransactions = new BehaviorSubject(new Set());
|
|
4616
4763
|
|
|
4764
|
+
function isEagerSyncDisabled(db) {
|
|
4765
|
+
var _a, _b, _c, _d;
|
|
4766
|
+
return (((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.disableEagerSync) ||
|
|
4767
|
+
((_c = (_b = db.cloud.currentUser.value) === null || _b === void 0 ? void 0 : _b.license) === null || _c === void 0 ? void 0 : _c.status) !== 'ok' ||
|
|
4768
|
+
!((_d = db.cloud.options) === null || _d === void 0 ? void 0 : _d.databaseUrl));
|
|
4769
|
+
}
|
|
4770
|
+
|
|
4617
4771
|
/** Tracks all mutations in the same transaction as the mutations -
|
|
4618
4772
|
* so it is guaranteed that no mutation goes untracked - and if transaction
|
|
4619
4773
|
* aborts, the mutations won't be tracked.
|
|
@@ -4622,7 +4776,7 @@ const outstandingTransactions = new BehaviorSubject(new Set());
|
|
|
4622
4776
|
* changes to server and cleanup the tracked mutations once the server has
|
|
4623
4777
|
* ackowledged that it got them.
|
|
4624
4778
|
*/
|
|
4625
|
-
function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
4779
|
+
function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
4626
4780
|
return {
|
|
4627
4781
|
stack: 'dbcore',
|
|
4628
4782
|
name: 'MutationTrackingMiddleware',
|
|
@@ -4633,7 +4787,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4633
4787
|
try {
|
|
4634
4788
|
mutTableMap = new Map(ordinaryTables.map((tbl) => [
|
|
4635
4789
|
tbl.name,
|
|
4636
|
-
core.table(`$${tbl.name}_mutations`)
|
|
4790
|
+
core.table(`$${tbl.name}_mutations`),
|
|
4637
4791
|
]));
|
|
4638
4792
|
}
|
|
4639
4793
|
catch (_a) {
|
|
@@ -4667,15 +4821,9 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4667
4821
|
outstandingTransactions.next(outstandingTransactions.value);
|
|
4668
4822
|
};
|
|
4669
4823
|
const txComplete = () => {
|
|
4670
|
-
|
|
4671
|
-
|
|
4672
|
-
|
|
4673
|
-
console.debug('registering sync event');
|
|
4674
|
-
registerSyncEvent(db, "push");
|
|
4675
|
-
}
|
|
4676
|
-
else {
|
|
4677
|
-
db.localSyncEvent.next({ purpose: "push" });
|
|
4678
|
-
}
|
|
4824
|
+
if (tx.mutationsAdded &&
|
|
4825
|
+
!isEagerSyncDisabled(db)) {
|
|
4826
|
+
triggerSync(db, 'push');
|
|
4679
4827
|
}
|
|
4680
4828
|
removeTransaction();
|
|
4681
4829
|
};
|
|
@@ -4742,7 +4890,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4742
4890
|
.query({
|
|
4743
4891
|
query: { range: req.range, index: schema.primaryKey },
|
|
4744
4892
|
trans: req.trans,
|
|
4745
|
-
values: false
|
|
4893
|
+
values: false,
|
|
4746
4894
|
})
|
|
4747
4895
|
// Do a delete request instead, but keep the criteria info for the server to execute
|
|
4748
4896
|
.then((res) => {
|
|
@@ -4750,7 +4898,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4750
4898
|
type: 'delete',
|
|
4751
4899
|
keys: res.result,
|
|
4752
4900
|
trans: req.trans,
|
|
4753
|
-
criteria: { index: null, range: req.range }
|
|
4901
|
+
criteria: { index: null, range: req.range },
|
|
4754
4902
|
});
|
|
4755
4903
|
})
|
|
4756
4904
|
: mutateAndLog(req);
|
|
@@ -4758,7 +4906,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4758
4906
|
function mutateAndLog(req) {
|
|
4759
4907
|
const trans = req.trans;
|
|
4760
4908
|
trans.mutationsAdded = true;
|
|
4761
|
-
const { txid, currentUser: { userId } } = trans;
|
|
4909
|
+
const { txid, currentUser: { userId }, } = trans;
|
|
4762
4910
|
const { type } = req;
|
|
4763
4911
|
const opNo = ++trans.opCount;
|
|
4764
4912
|
return table.mutate(req).then((res) => {
|
|
@@ -4779,7 +4927,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4779
4927
|
keys,
|
|
4780
4928
|
criteria: req.criteria,
|
|
4781
4929
|
txid,
|
|
4782
|
-
userId
|
|
4930
|
+
userId,
|
|
4783
4931
|
}
|
|
4784
4932
|
: req.type === 'add'
|
|
4785
4933
|
? {
|
|
@@ -4789,7 +4937,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4789
4937
|
keys,
|
|
4790
4938
|
txid,
|
|
4791
4939
|
userId,
|
|
4792
|
-
values
|
|
4940
|
+
values,
|
|
4793
4941
|
}
|
|
4794
4942
|
: req.criteria && req.changeSpec
|
|
4795
4943
|
? {
|
|
@@ -4801,7 +4949,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4801
4949
|
criteria: req.criteria,
|
|
4802
4950
|
changeSpec: req.changeSpec,
|
|
4803
4951
|
txid,
|
|
4804
|
-
userId
|
|
4952
|
+
userId,
|
|
4805
4953
|
}
|
|
4806
4954
|
: updates
|
|
4807
4955
|
? {
|
|
@@ -4812,7 +4960,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4812
4960
|
keys: updates.keys,
|
|
4813
4961
|
changeSpecs: updates.changeSpecs,
|
|
4814
4962
|
txid,
|
|
4815
|
-
userId
|
|
4963
|
+
userId,
|
|
4816
4964
|
}
|
|
4817
4965
|
: {
|
|
4818
4966
|
type: 'upsert',
|
|
@@ -4821,7 +4969,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4821
4969
|
keys,
|
|
4822
4970
|
values,
|
|
4823
4971
|
txid,
|
|
4824
|
-
userId
|
|
4972
|
+
userId,
|
|
4825
4973
|
};
|
|
4826
4974
|
return keys.length > 0 || ('criteria' in req && req.criteria)
|
|
4827
4975
|
? mutsTable
|
|
@@ -4831,7 +4979,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4831
4979
|
});
|
|
4832
4980
|
}
|
|
4833
4981
|
} });
|
|
4834
|
-
}
|
|
4982
|
+
},
|
|
4835
4983
|
};
|
|
4836
4984
|
}
|
|
4837
4985
|
|
|
@@ -5190,6 +5338,20 @@ class WSConnection extends Subscription$1 {
|
|
|
5190
5338
|
}
|
|
5191
5339
|
}
|
|
5192
5340
|
|
|
5341
|
+
class InvalidLicenseError extends Error {
|
|
5342
|
+
constructor(license) {
|
|
5343
|
+
super(license === 'expired'
|
|
5344
|
+
? `License expired`
|
|
5345
|
+
: license === 'deactivated'
|
|
5346
|
+
? `User deactivated`
|
|
5347
|
+
: 'Invalid license');
|
|
5348
|
+
this.name = 'InvalidLicenseError';
|
|
5349
|
+
if (license) {
|
|
5350
|
+
this.license = license;
|
|
5351
|
+
}
|
|
5352
|
+
}
|
|
5353
|
+
}
|
|
5354
|
+
|
|
5193
5355
|
function sleep(ms) {
|
|
5194
5356
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5195
5357
|
}
|
|
@@ -5223,7 +5385,12 @@ function connectWebSocket(db) {
|
|
|
5223
5385
|
function createObservable() {
|
|
5224
5386
|
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.
|
|
5225
5387
|
take(1), // Don't continue waking up whenever syncState change
|
|
5226
|
-
switchMap((syncState) => db.cloud.currentUser.pipe(map((userLogin) => [userLogin, syncState]))), switchMap(([userLogin, syncState]) =>
|
|
5388
|
+
switchMap((syncState) => db.cloud.currentUser.pipe(map((userLogin) => [userLogin, syncState]))), switchMap(([userLogin, syncState]) => {
|
|
5389
|
+
/*if (userLogin.license?.status && userLogin.license.status !== 'ok') {
|
|
5390
|
+
throw new InvalidLicenseError();
|
|
5391
|
+
}*/
|
|
5392
|
+
return userIsReallyActive.pipe(map((isActive) => [isActive ? userLogin : null, syncState]));
|
|
5393
|
+
}), switchMap(([userLogin, syncState]) => {
|
|
5227
5394
|
if ((userLogin === null || userLogin === void 0 ? void 0 : userLogin.isLoggedIn) && !(syncState === null || syncState === void 0 ? void 0 : syncState.realms.includes(userLogin.userId))) {
|
|
5228
5395
|
// We're in an in-between state when user is logged in but the user's realms are not yet synced.
|
|
5229
5396
|
// Don't make this change reconnect the websocket just yet. Wait till syncState is updated
|
|
@@ -5252,14 +5419,20 @@ function connectWebSocket(db) {
|
|
|
5252
5419
|
yield db.table('$logins').update(user.userId, {
|
|
5253
5420
|
accessToken: refreshedLogin.accessToken,
|
|
5254
5421
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
5422
|
+
claims: refreshedLogin.claims,
|
|
5423
|
+
license: refreshedLogin.license,
|
|
5255
5424
|
});
|
|
5256
5425
|
})), switchMap(() => createObservable()));
|
|
5257
5426
|
}
|
|
5258
5427
|
else {
|
|
5259
|
-
return throwError(error);
|
|
5428
|
+
return throwError(() => error);
|
|
5260
5429
|
}
|
|
5261
5430
|
}), catchError((error) => {
|
|
5262
5431
|
db.cloud.webSocketStatus.next("error");
|
|
5432
|
+
if (error instanceof InvalidLicenseError) {
|
|
5433
|
+
// Don't retry. Just throw and don't try connect again.
|
|
5434
|
+
return throwError(() => error);
|
|
5435
|
+
}
|
|
5263
5436
|
return from$1(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()));
|
|
5264
5437
|
}));
|
|
5265
5438
|
}
|
|
@@ -5288,97 +5461,12 @@ function isSyncNeeded(db) {
|
|
|
5288
5461
|
});
|
|
5289
5462
|
}
|
|
5290
5463
|
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
const GUARDED_JOB_TIMEOUT = 1 * MINUTES;
|
|
5298
|
-
function performGuardedJob(db, jobName, jobsTableName, job, { awaitRemoteJob } = {}) {
|
|
5299
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
5300
|
-
// Start working.
|
|
5301
|
-
//
|
|
5302
|
-
// Check if someone else is working on this already.
|
|
5303
|
-
//
|
|
5304
|
-
const jobsTable = db.table(jobsTableName);
|
|
5305
|
-
function aquireLock() {
|
|
5306
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
5307
|
-
const gotTheLock = yield db.transaction('rw!', jobsTableName, () => __awaiter(this, void 0, void 0, function* () {
|
|
5308
|
-
const currentWork = yield jobsTable.get(jobName);
|
|
5309
|
-
if (!currentWork) {
|
|
5310
|
-
// No one else is working. Let's record that we are.
|
|
5311
|
-
yield jobsTable.add({
|
|
5312
|
-
nodeId: myId,
|
|
5313
|
-
started: new Date(),
|
|
5314
|
-
heartbeat: new Date()
|
|
5315
|
-
}, jobName);
|
|
5316
|
-
return true;
|
|
5317
|
-
}
|
|
5318
|
-
else if (currentWork.heartbeat.getTime() <
|
|
5319
|
-
Date.now() - GUARDED_JOB_TIMEOUT) {
|
|
5320
|
-
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!`);
|
|
5321
|
-
// Now, take over!
|
|
5322
|
-
yield jobsTable.put({
|
|
5323
|
-
nodeId: myId,
|
|
5324
|
-
started: new Date(),
|
|
5325
|
-
heartbeat: new Date()
|
|
5326
|
-
}, jobName);
|
|
5327
|
-
return true;
|
|
5328
|
-
}
|
|
5329
|
-
return false;
|
|
5330
|
-
}));
|
|
5331
|
-
if (gotTheLock)
|
|
5332
|
-
return true;
|
|
5333
|
-
// Someone else took the job.
|
|
5334
|
-
if (awaitRemoteJob) {
|
|
5335
|
-
try {
|
|
5336
|
-
const jobDoneObservable = from$1(liveQuery(() => jobsTable.get(jobName))).pipe(timeout(GUARDED_JOB_TIMEOUT), filter((job) => !job)); // Wait til job is not there anymore.
|
|
5337
|
-
yield jobDoneObservable.toPromise();
|
|
5338
|
-
return false;
|
|
5339
|
-
}
|
|
5340
|
-
catch (err) {
|
|
5341
|
-
if (err.name !== 'TimeoutError') {
|
|
5342
|
-
throw err;
|
|
5343
|
-
}
|
|
5344
|
-
// Timeout stopped us! Try aquire the lock now.
|
|
5345
|
-
// It will likely succeed this time unless
|
|
5346
|
-
// another client took it.
|
|
5347
|
-
return yield aquireLock();
|
|
5348
|
-
}
|
|
5349
|
-
}
|
|
5350
|
-
return false;
|
|
5351
|
-
});
|
|
5352
|
-
}
|
|
5353
|
-
if (yield aquireLock()) {
|
|
5354
|
-
// We own the lock entry and can do our job undisturbed.
|
|
5355
|
-
// We're not within a transaction, but these type of locks
|
|
5356
|
-
// spans over transactions.
|
|
5357
|
-
// Start our heart beat during the job.
|
|
5358
|
-
// Use setInterval to make sure we are updating heartbeat even during long-lived fetch calls.
|
|
5359
|
-
const heartbeat = setInterval(() => {
|
|
5360
|
-
jobsTable.update(jobName, (job) => {
|
|
5361
|
-
if (job.nodeId === myId) {
|
|
5362
|
-
job.heartbeat = new Date();
|
|
5363
|
-
}
|
|
5364
|
-
});
|
|
5365
|
-
}, GUARDED_JOB_HEARTBEAT);
|
|
5366
|
-
try {
|
|
5367
|
-
return yield job();
|
|
5368
|
-
}
|
|
5369
|
-
finally {
|
|
5370
|
-
// Stop heartbeat
|
|
5371
|
-
clearInterval(heartbeat);
|
|
5372
|
-
// Remove the persisted job state:
|
|
5373
|
-
yield db.transaction('rw!', jobsTableName, () => __awaiter(this, void 0, void 0, function* () {
|
|
5374
|
-
const currentWork = yield jobsTable.get(jobName);
|
|
5375
|
-
if (currentWork && currentWork.nodeId === myId) {
|
|
5376
|
-
yield jobsTable.delete(jobName);
|
|
5377
|
-
}
|
|
5378
|
-
}));
|
|
5379
|
-
}
|
|
5380
|
-
}
|
|
5381
|
-
});
|
|
5464
|
+
function performGuardedJob(db, jobName, job) {
|
|
5465
|
+
if (typeof navigator === 'undefined' || !navigator.locks) {
|
|
5466
|
+
// No support for guarding jobs. IE11, node.js, etc.
|
|
5467
|
+
return job();
|
|
5468
|
+
}
|
|
5469
|
+
return navigator.locks.request(db.name + '|' + jobName, () => job());
|
|
5382
5470
|
}
|
|
5383
5471
|
|
|
5384
5472
|
const ongoingSyncs = new WeakMap();
|
|
@@ -5432,6 +5520,9 @@ function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
|
5432
5520
|
function _syncIfPossible() {
|
|
5433
5521
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5434
5522
|
try {
|
|
5523
|
+
// Check if should delay sync due to ratelimit:
|
|
5524
|
+
yield checkSyncRateLimitDelay(db);
|
|
5525
|
+
// Check if we need to lock the sync job. Not needed if we are the service worker.
|
|
5435
5526
|
if (db.cloud.isServiceWorkerDB) {
|
|
5436
5527
|
// We are the dedicated sync SW:
|
|
5437
5528
|
yield sync(db, cloudOptions, cloudSchema, options);
|
|
@@ -5439,7 +5530,7 @@ function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
|
5439
5530
|
else if (!db.cloud.usingServiceWorker) {
|
|
5440
5531
|
// We use a flow that is better suited for the case when multiple workers want to
|
|
5441
5532
|
// do the same thing.
|
|
5442
|
-
yield performGuardedJob(db, CURRENT_SYNC_WORKER,
|
|
5533
|
+
yield performGuardedJob(db, CURRENT_SYNC_WORKER, () => sync(db, cloudOptions, cloudSchema, options));
|
|
5443
5534
|
}
|
|
5444
5535
|
else {
|
|
5445
5536
|
assert(false);
|
|
@@ -5461,19 +5552,29 @@ function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
|
5461
5552
|
}
|
|
5462
5553
|
}
|
|
5463
5554
|
|
|
5555
|
+
const SECONDS = 1000;
|
|
5556
|
+
const MINUTES = 60 * SECONDS;
|
|
5557
|
+
|
|
5464
5558
|
function LocalSyncWorker(db, cloudOptions, cloudSchema) {
|
|
5465
5559
|
let localSyncEventSubscription = null;
|
|
5466
5560
|
//let syncHandler: ((event: Event) => void) | null = null;
|
|
5467
5561
|
//let periodicSyncHandler: ((event: Event) => void) | null = null;
|
|
5468
5562
|
let cancelToken = { cancelled: false };
|
|
5563
|
+
let retryHandle = null;
|
|
5564
|
+
let retryPurpose = null; // "pull" is superset of "push"
|
|
5469
5565
|
function syncAndRetry(purpose, retryNum = 1) {
|
|
5470
5566
|
// Use setTimeout() to get onto a clean stack and
|
|
5471
5567
|
// break free from possible active transaction:
|
|
5472
5568
|
setTimeout(() => {
|
|
5569
|
+
if (retryHandle)
|
|
5570
|
+
clearTimeout(retryHandle);
|
|
5571
|
+
const combPurpose = retryPurpose === 'pull' ? 'pull' : purpose;
|
|
5572
|
+
retryHandle = null;
|
|
5573
|
+
retryPurpose = null;
|
|
5473
5574
|
syncIfPossible(db, cloudOptions, cloudSchema, {
|
|
5474
5575
|
cancelToken,
|
|
5475
5576
|
retryImmediatelyOnFetchError: true,
|
|
5476
|
-
purpose,
|
|
5577
|
+
purpose: combPurpose,
|
|
5477
5578
|
}).catch((e) => {
|
|
5478
5579
|
console.error('error in syncIfPossible()', e);
|
|
5479
5580
|
if (cancelToken.cancelled) {
|
|
@@ -5483,7 +5584,13 @@ function LocalSyncWorker(db, cloudOptions, cloudSchema) {
|
|
|
5483
5584
|
// Mimic service worker sync event: retry 3 times
|
|
5484
5585
|
// * first retry after 5 minutes
|
|
5485
5586
|
// * second retry 15 minutes later
|
|
5486
|
-
|
|
5587
|
+
const combinedPurpose = retryPurpose && retryPurpose === 'pull' ? 'pull' : purpose;
|
|
5588
|
+
const handle = setTimeout(() => syncAndRetry(combinedPurpose, retryNum + 1), [0, 5, 15][retryNum] * MINUTES);
|
|
5589
|
+
// Cancel the previous retryHandle if it exists to avoid scheduling loads of retries.
|
|
5590
|
+
if (retryHandle)
|
|
5591
|
+
clearTimeout(retryHandle);
|
|
5592
|
+
retryHandle = handle;
|
|
5593
|
+
retryPurpose = combinedPurpose;
|
|
5487
5594
|
}
|
|
5488
5595
|
});
|
|
5489
5596
|
}, 0);
|
|
@@ -5550,10 +5657,12 @@ const Styles = {
|
|
|
5550
5657
|
},
|
|
5551
5658
|
Alert: {
|
|
5552
5659
|
error: {
|
|
5553
|
-
color: "red"
|
|
5660
|
+
color: "red",
|
|
5661
|
+
fontWeight: "bold"
|
|
5554
5662
|
},
|
|
5555
5663
|
warning: {
|
|
5556
|
-
color: "
|
|
5664
|
+
color: "#f80",
|
|
5665
|
+
fontWeight: "bold"
|
|
5557
5666
|
},
|
|
5558
5667
|
info: {
|
|
5559
5668
|
color: "black"
|
|
@@ -5594,7 +5703,8 @@ const Styles = {
|
|
|
5594
5703
|
border: "3px solid #3d3d5d",
|
|
5595
5704
|
borderRadius: "8px",
|
|
5596
5705
|
boxShadow: "0 0 80px 10px #666",
|
|
5597
|
-
width: "auto"
|
|
5706
|
+
width: "auto",
|
|
5707
|
+
fontFamily: "sans-serif",
|
|
5598
5708
|
},
|
|
5599
5709
|
Input: {
|
|
5600
5710
|
height: "35px",
|
|
@@ -5615,11 +5725,26 @@ function Dialog({ children, className }) {
|
|
|
5615
5725
|
|
|
5616
5726
|
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}
|
|
5617
5727
|
|
|
5728
|
+
/** Resolve a message template with parameters.
|
|
5729
|
+
*
|
|
5730
|
+
* Example:
|
|
5731
|
+
* resolveText({
|
|
5732
|
+
* message: "Hello {name}!",
|
|
5733
|
+
* messageCode: "HELLO",
|
|
5734
|
+
* messageParams: {name: "David"}
|
|
5735
|
+
* }) => "Hello David!"
|
|
5736
|
+
*
|
|
5737
|
+
* @param message Template message with {vars} in it.
|
|
5738
|
+
* @param messageCode Unique code for the message. Can be used for translation.
|
|
5739
|
+
* @param messageParams Parameters to be used in the message.
|
|
5740
|
+
* @returns A final message where parameters have been replaced with values.
|
|
5741
|
+
*/
|
|
5618
5742
|
function resolveText({ message, messageCode, messageParams }) {
|
|
5619
|
-
return message.replace(/\{\w+\}/ig, n => messageParams[n.
|
|
5743
|
+
return message.replace(/\{\w+\}/ig, n => messageParams[n.substring(1, n.length - 1)]);
|
|
5620
5744
|
}
|
|
5621
5745
|
|
|
5622
|
-
|
|
5746
|
+
const OTP_LENGTH = 8;
|
|
5747
|
+
function LoginDialog({ title, type, alerts, fields, submitLabel, cancelLabel, onCancel, onSubmit, }) {
|
|
5623
5748
|
const [params, setParams] = p({});
|
|
5624
5749
|
const firstFieldRef = _(null);
|
|
5625
5750
|
s(() => { var _a; return (_a = firstFieldRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, []);
|
|
@@ -5627,21 +5752,34 @@ function LoginDialog({ title, alerts, fields, onCancel, onSubmit, }) {
|
|
|
5627
5752
|
h(p$1, null,
|
|
5628
5753
|
h("h3", { style: Styles.WindowHeader }, title),
|
|
5629
5754
|
alerts.map((alert) => (h("p", { style: Styles.Alert[alert.type] }, resolveText(alert)))),
|
|
5630
|
-
h("form", { onSubmit: ev => {
|
|
5755
|
+
h("form", { onSubmit: (ev) => {
|
|
5631
5756
|
ev.preventDefault();
|
|
5632
5757
|
onSubmit(params);
|
|
5633
|
-
} }, Object.entries(fields).map(([fieldName, { type, label, placeholder }], idx) => (h("label", { style: Styles.Label },
|
|
5758
|
+
} }, Object.entries(fields).map(([fieldName, { type, label, placeholder }], idx) => (h("label", { style: Styles.Label, key: idx },
|
|
5634
5759
|
label ? `${label}: ` : '',
|
|
5635
|
-
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) => {
|
|
5760
|
+
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) => {
|
|
5761
|
+
var _a;
|
|
5762
|
+
const value = valueTransformer(type, (_a = ev.target) === null || _a === void 0 ? void 0 : _a['value']);
|
|
5763
|
+
let updatedParams = Object.assign(Object.assign({}, params), { [fieldName]: value });
|
|
5764
|
+
setParams(updatedParams);
|
|
5765
|
+
if (type === 'otp' && (value === null || value === void 0 ? void 0 : value.trim().length) === OTP_LENGTH) {
|
|
5766
|
+
// Auto-submit when OTP is filled in.
|
|
5767
|
+
onSubmit(updatedParams);
|
|
5768
|
+
}
|
|
5769
|
+
} })))))),
|
|
5636
5770
|
h("div", { style: Styles.ButtonsDiv },
|
|
5637
|
-
h(
|
|
5638
|
-
|
|
5771
|
+
h(p$1, null,
|
|
5772
|
+
h("button", { type: "submit", style: Styles.Button, onClick: () => onSubmit(params) }, submitLabel),
|
|
5773
|
+
cancelLabel && (h("button", { style: Styles.Button, onClick: onCancel }, cancelLabel))))));
|
|
5639
5774
|
}
|
|
5640
5775
|
function valueTransformer(type, value) {
|
|
5641
5776
|
switch (type) {
|
|
5642
|
-
case
|
|
5643
|
-
|
|
5644
|
-
|
|
5777
|
+
case 'email':
|
|
5778
|
+
return value.toLowerCase();
|
|
5779
|
+
case 'otp':
|
|
5780
|
+
return value.toUpperCase();
|
|
5781
|
+
default:
|
|
5782
|
+
return value;
|
|
5645
5783
|
}
|
|
5646
5784
|
}
|
|
5647
5785
|
|
|
@@ -5695,11 +5833,20 @@ function setupDefaultGUI(db) {
|
|
|
5695
5833
|
}
|
|
5696
5834
|
};
|
|
5697
5835
|
}
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5836
|
+
|
|
5837
|
+
function associate(factory) {
|
|
5838
|
+
const wm = new WeakMap();
|
|
5839
|
+
return (x) => {
|
|
5840
|
+
let rv = wm.get(x);
|
|
5841
|
+
if (!rv) {
|
|
5842
|
+
rv = factory(x);
|
|
5843
|
+
wm.set(x, rv);
|
|
5844
|
+
}
|
|
5845
|
+
return rv;
|
|
5846
|
+
};
|
|
5847
|
+
}
|
|
5848
|
+
|
|
5849
|
+
const getCurrentUserEmitter = associate((db) => new BehaviorSubject(UNAUTHORIZED_USER));
|
|
5703
5850
|
|
|
5704
5851
|
function computeSyncState(db) {
|
|
5705
5852
|
let _prevStatus = db.cloud.webSocketStatus.value;
|
|
@@ -5727,8 +5874,17 @@ function computeSyncState(db) {
|
|
|
5727
5874
|
return combineLatest([
|
|
5728
5875
|
lazyWebSocketStatus,
|
|
5729
5876
|
db.syncStateChangedEvent.pipe(startWith({ phase: 'initial' })),
|
|
5877
|
+
getCurrentUserEmitter(db.dx._novip),
|
|
5730
5878
|
userIsReallyActive
|
|
5731
|
-
]).pipe(map(([status, syncState, userIsActive]) => {
|
|
5879
|
+
]).pipe(map(([status, syncState, user, userIsActive]) => {
|
|
5880
|
+
var _a;
|
|
5881
|
+
if (((_a = user.license) === null || _a === void 0 ? void 0 : _a.status) && user.license.status !== 'ok') {
|
|
5882
|
+
return {
|
|
5883
|
+
phase: 'offline',
|
|
5884
|
+
status: 'offline',
|
|
5885
|
+
license: user.license.status
|
|
5886
|
+
};
|
|
5887
|
+
}
|
|
5732
5888
|
let { phase, error, progress } = syncState;
|
|
5733
5889
|
let adjustedStatus = status;
|
|
5734
5890
|
if (phase === 'error') {
|
|
@@ -5761,23 +5917,12 @@ function computeSyncState(db) {
|
|
|
5761
5917
|
error,
|
|
5762
5918
|
progress,
|
|
5763
5919
|
status: isOnline ? adjustedStatus : 'offline',
|
|
5920
|
+
license: 'ok'
|
|
5764
5921
|
};
|
|
5765
5922
|
return retState;
|
|
5766
5923
|
}));
|
|
5767
5924
|
}
|
|
5768
5925
|
|
|
5769
|
-
function associate(factory) {
|
|
5770
|
-
const wm = new WeakMap();
|
|
5771
|
-
return (x) => {
|
|
5772
|
-
let rv = wm.get(x);
|
|
5773
|
-
if (!rv) {
|
|
5774
|
-
rv = factory(x);
|
|
5775
|
-
wm.set(x, rv);
|
|
5776
|
-
}
|
|
5777
|
-
return rv;
|
|
5778
|
-
};
|
|
5779
|
-
}
|
|
5780
|
-
|
|
5781
5926
|
function createSharedValueObservable(o, defaultValue) {
|
|
5782
5927
|
let currentValue = defaultValue;
|
|
5783
5928
|
let shared = from$1(o).pipe(map$1((x) => (currentValue = x)), share({ resetOnRefCountZero: () => timer$1(1000) }));
|
|
@@ -5819,8 +5964,6 @@ const getGlobalRolesObservable = associate((db) => {
|
|
|
5819
5964
|
})), {});
|
|
5820
5965
|
});
|
|
5821
5966
|
|
|
5822
|
-
const getCurrentUserEmitter = associate((db) => new BehaviorSubject(UNAUTHORIZED_USER));
|
|
5823
|
-
|
|
5824
5967
|
const getInternalAccessControlObservable = associate((db) => {
|
|
5825
5968
|
return createSharedValueObservable(getCurrentUserEmitter(db._novip).pipe(switchMap((currentUser) => liveQuery(() => db.transaction('r', 'realms', 'members', () => Promise.all([
|
|
5826
5969
|
db.members.where({ userId: currentUser.userId }).toArray(),
|
|
@@ -6110,7 +6253,7 @@ function dexieCloud(dexie) {
|
|
|
6110
6253
|
});
|
|
6111
6254
|
const syncComplete = new Subject();
|
|
6112
6255
|
dexie.cloud = {
|
|
6113
|
-
version: '4.0.1-beta.
|
|
6256
|
+
version: '4.0.1-beta.47',
|
|
6114
6257
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
6115
6258
|
schema: null,
|
|
6116
6259
|
get currentUserId() {
|
|
@@ -6146,11 +6289,24 @@ function dexieCloud(dexie) {
|
|
|
6146
6289
|
}
|
|
6147
6290
|
updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
|
|
6148
6291
|
},
|
|
6292
|
+
logout({ force } = {}) {
|
|
6293
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6294
|
+
force
|
|
6295
|
+
? yield _logout(DexieCloudDB(dexie), { deleteUnsyncedData: true })
|
|
6296
|
+
: yield logout(DexieCloudDB(dexie));
|
|
6297
|
+
});
|
|
6298
|
+
},
|
|
6149
6299
|
sync({ wait, purpose } = { wait: true, purpose: 'push' }) {
|
|
6300
|
+
var _a;
|
|
6150
6301
|
return __awaiter(this, void 0, void 0, function* () {
|
|
6151
6302
|
if (wait === undefined)
|
|
6152
6303
|
wait = true;
|
|
6153
6304
|
const db = DexieCloudDB(dexie);
|
|
6305
|
+
const licenseStatus = ((_a = db.cloud.currentUser.value.license) === null || _a === void 0 ? void 0 : _a.status) || 'ok';
|
|
6306
|
+
if (licenseStatus !== 'ok') {
|
|
6307
|
+
// Refresh access token to check for updated license
|
|
6308
|
+
yield loadAccessToken(db);
|
|
6309
|
+
}
|
|
6154
6310
|
if (purpose === 'pull') {
|
|
6155
6311
|
const syncState = db.cloud.persistedSyncState.value;
|
|
6156
6312
|
triggerSync(db, purpose);
|
|
@@ -6354,7 +6510,9 @@ function dexieCloud(dexie) {
|
|
|
6354
6510
|
db.syncStateChangedEvent.next({
|
|
6355
6511
|
phase: 'not-in-sync',
|
|
6356
6512
|
});
|
|
6357
|
-
|
|
6513
|
+
if (!isEagerSyncDisabled(db)) {
|
|
6514
|
+
triggerSync(db, 'push');
|
|
6515
|
+
}
|
|
6358
6516
|
}), fromEvent(self, 'offline').subscribe(() => {
|
|
6359
6517
|
console.debug('offline!');
|
|
6360
6518
|
db.syncStateChangedEvent.next({
|
|
@@ -6371,7 +6529,7 @@ function dexieCloud(dexie) {
|
|
|
6371
6529
|
});
|
|
6372
6530
|
}
|
|
6373
6531
|
}
|
|
6374
|
-
dexieCloud.version = '4.0.1-beta.
|
|
6532
|
+
dexieCloud.version = '4.0.1-beta.47';
|
|
6375
6533
|
Dexie.Cloud = dexieCloud;
|
|
6376
6534
|
|
|
6377
6535
|
export { dexieCloud as default, dexieCloud, getTiedObjectId, getTiedRealmId };
|