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
|
@@ -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;
|
|
@@ -3406,9 +3500,19 @@ function encodeIdsForServer(schema, currentUser, changes) {
|
|
|
3406
3500
|
const mutClone = changeClone.muts[mutIndex];
|
|
3407
3501
|
const rewrittenKey = JSON.stringify(key);
|
|
3408
3502
|
mutClone.keys[keyIndex] = rewrittenKey;
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3503
|
+
/* Bug (#1777)
|
|
3504
|
+
We should not rewrite values. It will fail because the key is array and the value is string.
|
|
3505
|
+
Only the keys should be rewritten and it's already done on the server.
|
|
3506
|
+
We should take another round of revieweing how key transformations are being done between
|
|
3507
|
+
client and server and let the server do the key transformations entirely instead now that
|
|
3508
|
+
we have the primary key schema on the server making it possible to do so.
|
|
3509
|
+
if (rewriteValues) {
|
|
3510
|
+
Dexie.setByKeyPath(
|
|
3511
|
+
(mutClone as DBInsertOperation).values[keyIndex],
|
|
3512
|
+
primaryKey.keyPath!,
|
|
3513
|
+
rewrittenKey
|
|
3514
|
+
);
|
|
3515
|
+
}*/
|
|
3412
3516
|
}
|
|
3413
3517
|
else if (key[0] === '#') {
|
|
3414
3518
|
// Private ID - translate!
|
|
@@ -3439,6 +3543,40 @@ function cloneChange(change, rewriteValues) {
|
|
|
3439
3543
|
: change.muts.map((m) => (Object.assign(Object.assign({}, m), { keys: m.keys.slice() }))) });
|
|
3440
3544
|
}
|
|
3441
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
|
+
|
|
3442
3580
|
//import {BisonWebStreamReader} from "dreambase-library/dist/typeson-simplified/BisonWebStreamReader";
|
|
3443
3581
|
function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, clientIdentity, currentUser) {
|
|
3444
3582
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -3447,9 +3585,20 @@ function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, c
|
|
|
3447
3585
|
//
|
|
3448
3586
|
const headers = {
|
|
3449
3587
|
Accept: 'application/json, application/x-bison, application/x-bison-stream',
|
|
3450
|
-
'Content-Type': 'application/tson'
|
|
3588
|
+
'Content-Type': 'application/tson',
|
|
3451
3589
|
};
|
|
3452
|
-
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;
|
|
3453
3602
|
if (accessToken) {
|
|
3454
3603
|
headers.Authorization = `Bearer ${accessToken}`;
|
|
3455
3604
|
}
|
|
@@ -3458,27 +3607,31 @@ function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, c
|
|
|
3458
3607
|
dbID: syncState === null || syncState === void 0 ? void 0 : syncState.remoteDbId,
|
|
3459
3608
|
clientIdentity,
|
|
3460
3609
|
schema: schema || {},
|
|
3461
|
-
lastPull: syncState
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3610
|
+
lastPull: syncState
|
|
3611
|
+
? {
|
|
3612
|
+
serverRevision: syncState.serverRevision,
|
|
3613
|
+
realms: syncState.realms,
|
|
3614
|
+
inviteRealms: syncState.inviteRealms,
|
|
3615
|
+
}
|
|
3616
|
+
: undefined,
|
|
3466
3617
|
baseRevs,
|
|
3467
|
-
changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes)
|
|
3618
|
+
changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes),
|
|
3468
3619
|
};
|
|
3469
|
-
console.debug(
|
|
3620
|
+
console.debug('Sync request', syncRequest);
|
|
3470
3621
|
db.syncStateChangedEvent.next({
|
|
3471
3622
|
phase: 'pushing',
|
|
3472
3623
|
});
|
|
3473
3624
|
const res = yield fetch(`${databaseUrl}/sync`, {
|
|
3474
3625
|
method: 'post',
|
|
3475
3626
|
headers,
|
|
3476
|
-
|
|
3627
|
+
credentials: 'include',
|
|
3628
|
+
body: TSON.stringify(syncRequest),
|
|
3477
3629
|
});
|
|
3478
3630
|
//const contentLength = Number(res.headers.get('content-length'));
|
|
3479
3631
|
db.syncStateChangedEvent.next({
|
|
3480
|
-
phase: 'pulling'
|
|
3632
|
+
phase: 'pulling',
|
|
3481
3633
|
});
|
|
3634
|
+
updateSyncRateLimitDelays(db, res);
|
|
3482
3635
|
if (!res.ok) {
|
|
3483
3636
|
throw new HttpError(res);
|
|
3484
3637
|
}
|
|
@@ -3680,12 +3833,13 @@ const CURRENT_SYNC_WORKER = 'currentSyncWorker';
|
|
|
3680
3833
|
function sync(db, options, schema, syncOptions) {
|
|
3681
3834
|
return _sync
|
|
3682
3835
|
.apply(this, arguments)
|
|
3683
|
-
.then(() => {
|
|
3836
|
+
.then((result) => {
|
|
3684
3837
|
if (!(syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)) { // && syncOptions?.purpose !== 'push') {
|
|
3685
3838
|
db.syncStateChangedEvent.next({
|
|
3686
3839
|
phase: 'in-sync',
|
|
3687
3840
|
});
|
|
3688
3841
|
}
|
|
3842
|
+
return result;
|
|
3689
3843
|
})
|
|
3690
3844
|
.catch((error) => __awaiter(this, void 0, void 0, function* () {
|
|
3691
3845
|
if (syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)
|
|
@@ -3715,7 +3869,7 @@ function sync(db, options, schema, syncOptions) {
|
|
|
3715
3869
|
});
|
|
3716
3870
|
db.syncStateChangedEvent.next({
|
|
3717
3871
|
phase: isOnline ? 'error' : 'offline',
|
|
3718
|
-
error,
|
|
3872
|
+
error: new Error('' + (error === null || error === void 0 ? void 0 : error.message) || error),
|
|
3719
3873
|
});
|
|
3720
3874
|
return Promise.reject(error);
|
|
3721
3875
|
}));
|
|
@@ -3900,6 +4054,7 @@ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNee
|
|
|
3900
4054
|
}));
|
|
3901
4055
|
if (!done) {
|
|
3902
4056
|
console.debug('MORE SYNC NEEDED. Go for it again!');
|
|
4057
|
+
yield checkSyncRateLimitDelay(db);
|
|
3903
4058
|
return yield _sync(db, options, schema, { isInitialSync, cancelToken });
|
|
3904
4059
|
}
|
|
3905
4060
|
console.debug('SYNC DONE', { isInitialSync });
|
|
@@ -4036,6 +4191,8 @@ function MessagesFromServerConsumer(db) {
|
|
|
4036
4191
|
yield db.table('$logins').update(user.userId, {
|
|
4037
4192
|
accessToken: refreshedLogin.accessToken,
|
|
4038
4193
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
4194
|
+
claims: refreshedLogin.claims,
|
|
4195
|
+
license: refreshedLogin.license,
|
|
4039
4196
|
});
|
|
4040
4197
|
// Updating $logins will trigger emission of db.cloud.currentUser observable, which
|
|
4041
4198
|
// in turn will lead to that connectWebSocket.ts will reconnect the socket with the
|
|
@@ -4604,6 +4761,13 @@ function writeLock(fn, prop) {
|
|
|
4604
4761
|
|
|
4605
4762
|
const outstandingTransactions = new BehaviorSubject(new Set());
|
|
4606
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
|
+
|
|
4607
4771
|
/** Tracks all mutations in the same transaction as the mutations -
|
|
4608
4772
|
* so it is guaranteed that no mutation goes untracked - and if transaction
|
|
4609
4773
|
* aborts, the mutations won't be tracked.
|
|
@@ -4612,7 +4776,7 @@ const outstandingTransactions = new BehaviorSubject(new Set());
|
|
|
4612
4776
|
* changes to server and cleanup the tracked mutations once the server has
|
|
4613
4777
|
* ackowledged that it got them.
|
|
4614
4778
|
*/
|
|
4615
|
-
function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
4779
|
+
function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
4616
4780
|
return {
|
|
4617
4781
|
stack: 'dbcore',
|
|
4618
4782
|
name: 'MutationTrackingMiddleware',
|
|
@@ -4623,7 +4787,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4623
4787
|
try {
|
|
4624
4788
|
mutTableMap = new Map(ordinaryTables.map((tbl) => [
|
|
4625
4789
|
tbl.name,
|
|
4626
|
-
core.table(`$${tbl.name}_mutations`)
|
|
4790
|
+
core.table(`$${tbl.name}_mutations`),
|
|
4627
4791
|
]));
|
|
4628
4792
|
}
|
|
4629
4793
|
catch (_a) {
|
|
@@ -4657,15 +4821,9 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4657
4821
|
outstandingTransactions.next(outstandingTransactions.value);
|
|
4658
4822
|
};
|
|
4659
4823
|
const txComplete = () => {
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
4663
|
-
console.debug('registering sync event');
|
|
4664
|
-
registerSyncEvent(db, "push");
|
|
4665
|
-
}
|
|
4666
|
-
else {
|
|
4667
|
-
db.localSyncEvent.next({ purpose: "push" });
|
|
4668
|
-
}
|
|
4824
|
+
if (tx.mutationsAdded &&
|
|
4825
|
+
!isEagerSyncDisabled(db)) {
|
|
4826
|
+
triggerSync(db, 'push');
|
|
4669
4827
|
}
|
|
4670
4828
|
removeTransaction();
|
|
4671
4829
|
};
|
|
@@ -4732,7 +4890,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4732
4890
|
.query({
|
|
4733
4891
|
query: { range: req.range, index: schema.primaryKey },
|
|
4734
4892
|
trans: req.trans,
|
|
4735
|
-
values: false
|
|
4893
|
+
values: false,
|
|
4736
4894
|
})
|
|
4737
4895
|
// Do a delete request instead, but keep the criteria info for the server to execute
|
|
4738
4896
|
.then((res) => {
|
|
@@ -4740,7 +4898,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4740
4898
|
type: 'delete',
|
|
4741
4899
|
keys: res.result,
|
|
4742
4900
|
trans: req.trans,
|
|
4743
|
-
criteria: { index: null, range: req.range }
|
|
4901
|
+
criteria: { index: null, range: req.range },
|
|
4744
4902
|
});
|
|
4745
4903
|
})
|
|
4746
4904
|
: mutateAndLog(req);
|
|
@@ -4748,7 +4906,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4748
4906
|
function mutateAndLog(req) {
|
|
4749
4907
|
const trans = req.trans;
|
|
4750
4908
|
trans.mutationsAdded = true;
|
|
4751
|
-
const { txid, currentUser: { userId } } = trans;
|
|
4909
|
+
const { txid, currentUser: { userId }, } = trans;
|
|
4752
4910
|
const { type } = req;
|
|
4753
4911
|
const opNo = ++trans.opCount;
|
|
4754
4912
|
return table.mutate(req).then((res) => {
|
|
@@ -4769,7 +4927,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4769
4927
|
keys,
|
|
4770
4928
|
criteria: req.criteria,
|
|
4771
4929
|
txid,
|
|
4772
|
-
userId
|
|
4930
|
+
userId,
|
|
4773
4931
|
}
|
|
4774
4932
|
: req.type === 'add'
|
|
4775
4933
|
? {
|
|
@@ -4779,7 +4937,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4779
4937
|
keys,
|
|
4780
4938
|
txid,
|
|
4781
4939
|
userId,
|
|
4782
|
-
values
|
|
4940
|
+
values,
|
|
4783
4941
|
}
|
|
4784
4942
|
: req.criteria && req.changeSpec
|
|
4785
4943
|
? {
|
|
@@ -4791,7 +4949,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4791
4949
|
criteria: req.criteria,
|
|
4792
4950
|
changeSpec: req.changeSpec,
|
|
4793
4951
|
txid,
|
|
4794
|
-
userId
|
|
4952
|
+
userId,
|
|
4795
4953
|
}
|
|
4796
4954
|
: updates
|
|
4797
4955
|
? {
|
|
@@ -4802,7 +4960,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4802
4960
|
keys: updates.keys,
|
|
4803
4961
|
changeSpecs: updates.changeSpecs,
|
|
4804
4962
|
txid,
|
|
4805
|
-
userId
|
|
4963
|
+
userId,
|
|
4806
4964
|
}
|
|
4807
4965
|
: {
|
|
4808
4966
|
type: 'upsert',
|
|
@@ -4811,7 +4969,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4811
4969
|
keys,
|
|
4812
4970
|
values,
|
|
4813
4971
|
txid,
|
|
4814
|
-
userId
|
|
4972
|
+
userId,
|
|
4815
4973
|
};
|
|
4816
4974
|
return keys.length > 0 || ('criteria' in req && req.criteria)
|
|
4817
4975
|
? mutsTable
|
|
@@ -4821,7 +4979,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
|
4821
4979
|
});
|
|
4822
4980
|
}
|
|
4823
4981
|
} });
|
|
4824
|
-
}
|
|
4982
|
+
},
|
|
4825
4983
|
};
|
|
4826
4984
|
}
|
|
4827
4985
|
|
|
@@ -5180,6 +5338,20 @@ class WSConnection extends Subscription$1 {
|
|
|
5180
5338
|
}
|
|
5181
5339
|
}
|
|
5182
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
|
+
|
|
5183
5355
|
function sleep(ms) {
|
|
5184
5356
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5185
5357
|
}
|
|
@@ -5213,7 +5385,12 @@ function connectWebSocket(db) {
|
|
|
5213
5385
|
function createObservable() {
|
|
5214
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.
|
|
5215
5387
|
take(1), // Don't continue waking up whenever syncState change
|
|
5216
|
-
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]) => {
|
|
5217
5394
|
if ((userLogin === null || userLogin === void 0 ? void 0 : userLogin.isLoggedIn) && !(syncState === null || syncState === void 0 ? void 0 : syncState.realms.includes(userLogin.userId))) {
|
|
5218
5395
|
// We're in an in-between state when user is logged in but the user's realms are not yet synced.
|
|
5219
5396
|
// Don't make this change reconnect the websocket just yet. Wait till syncState is updated
|
|
@@ -5242,14 +5419,20 @@ function connectWebSocket(db) {
|
|
|
5242
5419
|
yield db.table('$logins').update(user.userId, {
|
|
5243
5420
|
accessToken: refreshedLogin.accessToken,
|
|
5244
5421
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
5422
|
+
claims: refreshedLogin.claims,
|
|
5423
|
+
license: refreshedLogin.license,
|
|
5245
5424
|
});
|
|
5246
5425
|
})), switchMap(() => createObservable()));
|
|
5247
5426
|
}
|
|
5248
5427
|
else {
|
|
5249
|
-
return throwError(error);
|
|
5428
|
+
return throwError(() => error);
|
|
5250
5429
|
}
|
|
5251
5430
|
}), catchError((error) => {
|
|
5252
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
|
+
}
|
|
5253
5436
|
return from$1(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()));
|
|
5254
5437
|
}));
|
|
5255
5438
|
}
|
|
@@ -5278,97 +5461,12 @@ function isSyncNeeded(db) {
|
|
|
5278
5461
|
});
|
|
5279
5462
|
}
|
|
5280
5463
|
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
5287
|
-
const GUARDED_JOB_TIMEOUT = 1 * MINUTES;
|
|
5288
|
-
function performGuardedJob(db, jobName, jobsTableName, job, { awaitRemoteJob } = {}) {
|
|
5289
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
5290
|
-
// Start working.
|
|
5291
|
-
//
|
|
5292
|
-
// Check if someone else is working on this already.
|
|
5293
|
-
//
|
|
5294
|
-
const jobsTable = db.table(jobsTableName);
|
|
5295
|
-
function aquireLock() {
|
|
5296
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
5297
|
-
const gotTheLock = yield db.transaction('rw!', jobsTableName, () => __awaiter(this, void 0, void 0, function* () {
|
|
5298
|
-
const currentWork = yield jobsTable.get(jobName);
|
|
5299
|
-
if (!currentWork) {
|
|
5300
|
-
// No one else is working. Let's record that we are.
|
|
5301
|
-
yield jobsTable.add({
|
|
5302
|
-
nodeId: myId,
|
|
5303
|
-
started: new Date(),
|
|
5304
|
-
heartbeat: new Date()
|
|
5305
|
-
}, jobName);
|
|
5306
|
-
return true;
|
|
5307
|
-
}
|
|
5308
|
-
else if (currentWork.heartbeat.getTime() <
|
|
5309
|
-
Date.now() - GUARDED_JOB_TIMEOUT) {
|
|
5310
|
-
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!`);
|
|
5311
|
-
// Now, take over!
|
|
5312
|
-
yield jobsTable.put({
|
|
5313
|
-
nodeId: myId,
|
|
5314
|
-
started: new Date(),
|
|
5315
|
-
heartbeat: new Date()
|
|
5316
|
-
}, jobName);
|
|
5317
|
-
return true;
|
|
5318
|
-
}
|
|
5319
|
-
return false;
|
|
5320
|
-
}));
|
|
5321
|
-
if (gotTheLock)
|
|
5322
|
-
return true;
|
|
5323
|
-
// Someone else took the job.
|
|
5324
|
-
if (awaitRemoteJob) {
|
|
5325
|
-
try {
|
|
5326
|
-
const jobDoneObservable = from$1(liveQuery(() => jobsTable.get(jobName))).pipe(timeout(GUARDED_JOB_TIMEOUT), filter((job) => !job)); // Wait til job is not there anymore.
|
|
5327
|
-
yield jobDoneObservable.toPromise();
|
|
5328
|
-
return false;
|
|
5329
|
-
}
|
|
5330
|
-
catch (err) {
|
|
5331
|
-
if (err.name !== 'TimeoutError') {
|
|
5332
|
-
throw err;
|
|
5333
|
-
}
|
|
5334
|
-
// Timeout stopped us! Try aquire the lock now.
|
|
5335
|
-
// It will likely succeed this time unless
|
|
5336
|
-
// another client took it.
|
|
5337
|
-
return yield aquireLock();
|
|
5338
|
-
}
|
|
5339
|
-
}
|
|
5340
|
-
return false;
|
|
5341
|
-
});
|
|
5342
|
-
}
|
|
5343
|
-
if (yield aquireLock()) {
|
|
5344
|
-
// We own the lock entry and can do our job undisturbed.
|
|
5345
|
-
// We're not within a transaction, but these type of locks
|
|
5346
|
-
// spans over transactions.
|
|
5347
|
-
// Start our heart beat during the job.
|
|
5348
|
-
// Use setInterval to make sure we are updating heartbeat even during long-lived fetch calls.
|
|
5349
|
-
const heartbeat = setInterval(() => {
|
|
5350
|
-
jobsTable.update(jobName, (job) => {
|
|
5351
|
-
if (job.nodeId === myId) {
|
|
5352
|
-
job.heartbeat = new Date();
|
|
5353
|
-
}
|
|
5354
|
-
});
|
|
5355
|
-
}, GUARDED_JOB_HEARTBEAT);
|
|
5356
|
-
try {
|
|
5357
|
-
return yield job();
|
|
5358
|
-
}
|
|
5359
|
-
finally {
|
|
5360
|
-
// Stop heartbeat
|
|
5361
|
-
clearInterval(heartbeat);
|
|
5362
|
-
// Remove the persisted job state:
|
|
5363
|
-
yield db.transaction('rw!', jobsTableName, () => __awaiter(this, void 0, void 0, function* () {
|
|
5364
|
-
const currentWork = yield jobsTable.get(jobName);
|
|
5365
|
-
if (currentWork && currentWork.nodeId === myId) {
|
|
5366
|
-
yield jobsTable.delete(jobName);
|
|
5367
|
-
}
|
|
5368
|
-
}));
|
|
5369
|
-
}
|
|
5370
|
-
}
|
|
5371
|
-
});
|
|
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());
|
|
5372
5470
|
}
|
|
5373
5471
|
|
|
5374
5472
|
const ongoingSyncs = new WeakMap();
|
|
@@ -5422,6 +5520,9 @@ function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
|
5422
5520
|
function _syncIfPossible() {
|
|
5423
5521
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5424
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.
|
|
5425
5526
|
if (db.cloud.isServiceWorkerDB) {
|
|
5426
5527
|
// We are the dedicated sync SW:
|
|
5427
5528
|
yield sync(db, cloudOptions, cloudSchema, options);
|
|
@@ -5429,7 +5530,7 @@ function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
|
5429
5530
|
else if (!db.cloud.usingServiceWorker) {
|
|
5430
5531
|
// We use a flow that is better suited for the case when multiple workers want to
|
|
5431
5532
|
// do the same thing.
|
|
5432
|
-
yield performGuardedJob(db, CURRENT_SYNC_WORKER,
|
|
5533
|
+
yield performGuardedJob(db, CURRENT_SYNC_WORKER, () => sync(db, cloudOptions, cloudSchema, options));
|
|
5433
5534
|
}
|
|
5434
5535
|
else {
|
|
5435
5536
|
assert(false);
|
|
@@ -5451,19 +5552,29 @@ function syncIfPossible(db, cloudOptions, cloudSchema, options) {
|
|
|
5451
5552
|
}
|
|
5452
5553
|
}
|
|
5453
5554
|
|
|
5555
|
+
const SECONDS = 1000;
|
|
5556
|
+
const MINUTES = 60 * SECONDS;
|
|
5557
|
+
|
|
5454
5558
|
function LocalSyncWorker(db, cloudOptions, cloudSchema) {
|
|
5455
5559
|
let localSyncEventSubscription = null;
|
|
5456
5560
|
//let syncHandler: ((event: Event) => void) | null = null;
|
|
5457
5561
|
//let periodicSyncHandler: ((event: Event) => void) | null = null;
|
|
5458
5562
|
let cancelToken = { cancelled: false };
|
|
5563
|
+
let retryHandle = null;
|
|
5564
|
+
let retryPurpose = null; // "pull" is superset of "push"
|
|
5459
5565
|
function syncAndRetry(purpose, retryNum = 1) {
|
|
5460
5566
|
// Use setTimeout() to get onto a clean stack and
|
|
5461
5567
|
// break free from possible active transaction:
|
|
5462
5568
|
setTimeout(() => {
|
|
5569
|
+
if (retryHandle)
|
|
5570
|
+
clearTimeout(retryHandle);
|
|
5571
|
+
const combPurpose = retryPurpose === 'pull' ? 'pull' : purpose;
|
|
5572
|
+
retryHandle = null;
|
|
5573
|
+
retryPurpose = null;
|
|
5463
5574
|
syncIfPossible(db, cloudOptions, cloudSchema, {
|
|
5464
5575
|
cancelToken,
|
|
5465
5576
|
retryImmediatelyOnFetchError: true,
|
|
5466
|
-
purpose,
|
|
5577
|
+
purpose: combPurpose,
|
|
5467
5578
|
}).catch((e) => {
|
|
5468
5579
|
console.error('error in syncIfPossible()', e);
|
|
5469
5580
|
if (cancelToken.cancelled) {
|
|
@@ -5473,7 +5584,13 @@ function LocalSyncWorker(db, cloudOptions, cloudSchema) {
|
|
|
5473
5584
|
// Mimic service worker sync event: retry 3 times
|
|
5474
5585
|
// * first retry after 5 minutes
|
|
5475
5586
|
// * second retry 15 minutes later
|
|
5476
|
-
|
|
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;
|
|
5477
5594
|
}
|
|
5478
5595
|
});
|
|
5479
5596
|
}, 0);
|
|
@@ -5540,10 +5657,12 @@ const Styles = {
|
|
|
5540
5657
|
},
|
|
5541
5658
|
Alert: {
|
|
5542
5659
|
error: {
|
|
5543
|
-
color: "red"
|
|
5660
|
+
color: "red",
|
|
5661
|
+
fontWeight: "bold"
|
|
5544
5662
|
},
|
|
5545
5663
|
warning: {
|
|
5546
|
-
color: "
|
|
5664
|
+
color: "#f80",
|
|
5665
|
+
fontWeight: "bold"
|
|
5547
5666
|
},
|
|
5548
5667
|
info: {
|
|
5549
5668
|
color: "black"
|
|
@@ -5584,7 +5703,8 @@ const Styles = {
|
|
|
5584
5703
|
border: "3px solid #3d3d5d",
|
|
5585
5704
|
borderRadius: "8px",
|
|
5586
5705
|
boxShadow: "0 0 80px 10px #666",
|
|
5587
|
-
width: "auto"
|
|
5706
|
+
width: "auto",
|
|
5707
|
+
fontFamily: "sans-serif",
|
|
5588
5708
|
},
|
|
5589
5709
|
Input: {
|
|
5590
5710
|
height: "35px",
|
|
@@ -5605,11 +5725,26 @@ function Dialog({ children, className }) {
|
|
|
5605
5725
|
|
|
5606
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}
|
|
5607
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
|
+
*/
|
|
5608
5742
|
function resolveText({ message, messageCode, messageParams }) {
|
|
5609
|
-
return message.replace(/\{\w+\}/ig, n => messageParams[n.
|
|
5743
|
+
return message.replace(/\{\w+\}/ig, n => messageParams[n.substring(1, n.length - 1)]);
|
|
5610
5744
|
}
|
|
5611
5745
|
|
|
5612
|
-
|
|
5746
|
+
const OTP_LENGTH = 8;
|
|
5747
|
+
function LoginDialog({ title, type, alerts, fields, submitLabel, cancelLabel, onCancel, onSubmit, }) {
|
|
5613
5748
|
const [params, setParams] = p({});
|
|
5614
5749
|
const firstFieldRef = _(null);
|
|
5615
5750
|
s(() => { var _a; return (_a = firstFieldRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, []);
|
|
@@ -5617,21 +5752,34 @@ function LoginDialog({ title, alerts, fields, onCancel, onSubmit, }) {
|
|
|
5617
5752
|
h(p$1, null,
|
|
5618
5753
|
h("h3", { style: Styles.WindowHeader }, title),
|
|
5619
5754
|
alerts.map((alert) => (h("p", { style: Styles.Alert[alert.type] }, resolveText(alert)))),
|
|
5620
|
-
h("form", { onSubmit: ev => {
|
|
5755
|
+
h("form", { onSubmit: (ev) => {
|
|
5621
5756
|
ev.preventDefault();
|
|
5622
5757
|
onSubmit(params);
|
|
5623
|
-
} }, 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 },
|
|
5624
5759
|
label ? `${label}: ` : '',
|
|
5625
|
-
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
|
+
} })))))),
|
|
5626
5770
|
h("div", { style: Styles.ButtonsDiv },
|
|
5627
|
-
h(
|
|
5628
|
-
|
|
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))))));
|
|
5629
5774
|
}
|
|
5630
5775
|
function valueTransformer(type, value) {
|
|
5631
5776
|
switch (type) {
|
|
5632
|
-
case
|
|
5633
|
-
|
|
5634
|
-
|
|
5777
|
+
case 'email':
|
|
5778
|
+
return value.toLowerCase();
|
|
5779
|
+
case 'otp':
|
|
5780
|
+
return value.toUpperCase();
|
|
5781
|
+
default:
|
|
5782
|
+
return value;
|
|
5635
5783
|
}
|
|
5636
5784
|
}
|
|
5637
5785
|
|
|
@@ -5685,11 +5833,20 @@ function setupDefaultGUI(db) {
|
|
|
5685
5833
|
}
|
|
5686
5834
|
};
|
|
5687
5835
|
}
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
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));
|
|
5693
5850
|
|
|
5694
5851
|
function computeSyncState(db) {
|
|
5695
5852
|
let _prevStatus = db.cloud.webSocketStatus.value;
|
|
@@ -5717,8 +5874,17 @@ function computeSyncState(db) {
|
|
|
5717
5874
|
return combineLatest([
|
|
5718
5875
|
lazyWebSocketStatus,
|
|
5719
5876
|
db.syncStateChangedEvent.pipe(startWith({ phase: 'initial' })),
|
|
5877
|
+
getCurrentUserEmitter(db.dx._novip),
|
|
5720
5878
|
userIsReallyActive
|
|
5721
|
-
]).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
|
+
}
|
|
5722
5888
|
let { phase, error, progress } = syncState;
|
|
5723
5889
|
let adjustedStatus = status;
|
|
5724
5890
|
if (phase === 'error') {
|
|
@@ -5751,23 +5917,12 @@ function computeSyncState(db) {
|
|
|
5751
5917
|
error,
|
|
5752
5918
|
progress,
|
|
5753
5919
|
status: isOnline ? adjustedStatus : 'offline',
|
|
5920
|
+
license: 'ok'
|
|
5754
5921
|
};
|
|
5755
5922
|
return retState;
|
|
5756
5923
|
}));
|
|
5757
5924
|
}
|
|
5758
5925
|
|
|
5759
|
-
function associate(factory) {
|
|
5760
|
-
const wm = new WeakMap();
|
|
5761
|
-
return (x) => {
|
|
5762
|
-
let rv = wm.get(x);
|
|
5763
|
-
if (!rv) {
|
|
5764
|
-
rv = factory(x);
|
|
5765
|
-
wm.set(x, rv);
|
|
5766
|
-
}
|
|
5767
|
-
return rv;
|
|
5768
|
-
};
|
|
5769
|
-
}
|
|
5770
|
-
|
|
5771
5926
|
function createSharedValueObservable(o, defaultValue) {
|
|
5772
5927
|
let currentValue = defaultValue;
|
|
5773
5928
|
let shared = from$1(o).pipe(map$1((x) => (currentValue = x)), share({ resetOnRefCountZero: () => timer$1(1000) }));
|
|
@@ -5809,8 +5964,6 @@ const getGlobalRolesObservable = associate((db) => {
|
|
|
5809
5964
|
})), {});
|
|
5810
5965
|
});
|
|
5811
5966
|
|
|
5812
|
-
const getCurrentUserEmitter = associate((db) => new BehaviorSubject(UNAUTHORIZED_USER));
|
|
5813
|
-
|
|
5814
5967
|
const getInternalAccessControlObservable = associate((db) => {
|
|
5815
5968
|
return createSharedValueObservable(getCurrentUserEmitter(db._novip).pipe(switchMap((currentUser) => liveQuery(() => db.transaction('r', 'realms', 'members', () => Promise.all([
|
|
5816
5969
|
db.members.where({ userId: currentUser.userId }).toArray(),
|
|
@@ -6100,7 +6253,7 @@ function dexieCloud(dexie) {
|
|
|
6100
6253
|
});
|
|
6101
6254
|
const syncComplete = new Subject();
|
|
6102
6255
|
dexie.cloud = {
|
|
6103
|
-
version: '4.0.1-beta.
|
|
6256
|
+
version: '4.0.1-beta.47',
|
|
6104
6257
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
6105
6258
|
schema: null,
|
|
6106
6259
|
get currentUserId() {
|
|
@@ -6136,11 +6289,24 @@ function dexieCloud(dexie) {
|
|
|
6136
6289
|
}
|
|
6137
6290
|
updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
|
|
6138
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
|
+
},
|
|
6139
6299
|
sync({ wait, purpose } = { wait: true, purpose: 'push' }) {
|
|
6300
|
+
var _a;
|
|
6140
6301
|
return __awaiter(this, void 0, void 0, function* () {
|
|
6141
6302
|
if (wait === undefined)
|
|
6142
6303
|
wait = true;
|
|
6143
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
|
+
}
|
|
6144
6310
|
if (purpose === 'pull') {
|
|
6145
6311
|
const syncState = db.cloud.persistedSyncState.value;
|
|
6146
6312
|
triggerSync(db, purpose);
|
|
@@ -6344,7 +6510,9 @@ function dexieCloud(dexie) {
|
|
|
6344
6510
|
db.syncStateChangedEvent.next({
|
|
6345
6511
|
phase: 'not-in-sync',
|
|
6346
6512
|
});
|
|
6347
|
-
|
|
6513
|
+
if (!isEagerSyncDisabled(db)) {
|
|
6514
|
+
triggerSync(db, 'push');
|
|
6515
|
+
}
|
|
6348
6516
|
}), fromEvent(self, 'offline').subscribe(() => {
|
|
6349
6517
|
console.debug('offline!');
|
|
6350
6518
|
db.syncStateChangedEvent.next({
|
|
@@ -6361,7 +6529,7 @@ function dexieCloud(dexie) {
|
|
|
6361
6529
|
});
|
|
6362
6530
|
}
|
|
6363
6531
|
}
|
|
6364
|
-
dexieCloud.version = '4.0.1-beta.
|
|
6532
|
+
dexieCloud.version = '4.0.1-beta.47';
|
|
6365
6533
|
Dexie.Cloud = dexieCloud;
|
|
6366
6534
|
|
|
6367
6535
|
export { dexieCloud as default, dexieCloud, getTiedObjectId, getTiedRealmId };
|