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
|
*
|
|
@@ -1924,58 +1924,6 @@
|
|
|
1924
1924
|
identity;
|
|
1925
1925
|
}
|
|
1926
1926
|
|
|
1927
|
-
var TimeoutError = createErrorClass(function (_super) {
|
|
1928
|
-
return function TimeoutErrorImpl(info) {
|
|
1929
|
-
if (info === void 0) { info = null; }
|
|
1930
|
-
_super(this);
|
|
1931
|
-
this.message = 'Timeout has occurred';
|
|
1932
|
-
this.name = 'TimeoutError';
|
|
1933
|
-
this.info = info;
|
|
1934
|
-
};
|
|
1935
|
-
});
|
|
1936
|
-
function timeout(config, schedulerArg) {
|
|
1937
|
-
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;
|
|
1938
|
-
if (first == null && each == null) {
|
|
1939
|
-
throw new TypeError('No timeout provided.');
|
|
1940
|
-
}
|
|
1941
|
-
return operate(function (source, subscriber) {
|
|
1942
|
-
var originalSourceSubscription;
|
|
1943
|
-
var timerSubscription;
|
|
1944
|
-
var lastValue = null;
|
|
1945
|
-
var seen = 0;
|
|
1946
|
-
var startTimer = function (delay) {
|
|
1947
|
-
timerSubscription = executeSchedule(subscriber, scheduler, function () {
|
|
1948
|
-
try {
|
|
1949
|
-
originalSourceSubscription.unsubscribe();
|
|
1950
|
-
innerFrom(_with({
|
|
1951
|
-
meta: meta,
|
|
1952
|
-
lastValue: lastValue,
|
|
1953
|
-
seen: seen,
|
|
1954
|
-
})).subscribe(subscriber);
|
|
1955
|
-
}
|
|
1956
|
-
catch (err) {
|
|
1957
|
-
subscriber.error(err);
|
|
1958
|
-
}
|
|
1959
|
-
}, delay);
|
|
1960
|
-
};
|
|
1961
|
-
originalSourceSubscription = source.subscribe(createOperatorSubscriber(subscriber, function (value) {
|
|
1962
|
-
timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.unsubscribe();
|
|
1963
|
-
seen++;
|
|
1964
|
-
subscriber.next((lastValue = value));
|
|
1965
|
-
each > 0 && startTimer(each);
|
|
1966
|
-
}, undefined, undefined, function () {
|
|
1967
|
-
if (!(timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.closed)) {
|
|
1968
|
-
timerSubscription === null || timerSubscription === void 0 ? void 0 : timerSubscription.unsubscribe();
|
|
1969
|
-
}
|
|
1970
|
-
lastValue = null;
|
|
1971
|
-
}));
|
|
1972
|
-
!seen && startTimer(first != null ? (typeof first === 'number' ? first : +first - scheduler.now()) : each);
|
|
1973
|
-
});
|
|
1974
|
-
}
|
|
1975
|
-
function timeoutErrorFactory(info) {
|
|
1976
|
-
throw new TimeoutError(info);
|
|
1977
|
-
}
|
|
1978
|
-
|
|
1979
1927
|
//const hasSW = 'serviceWorker' in navigator;
|
|
1980
1928
|
let hasComplainedAboutSyncEvent = false;
|
|
1981
1929
|
function registerSyncEvent(db, purpose) {
|
|
@@ -2036,6 +1984,7 @@
|
|
|
2036
1984
|
|
|
2037
1985
|
function triggerSync(db, purpose) {
|
|
2038
1986
|
if (db.cloud.usingServiceWorker) {
|
|
1987
|
+
console.debug('registering sync event');
|
|
2039
1988
|
registerSyncEvent(db, purpose);
|
|
2040
1989
|
}
|
|
2041
1990
|
else {
|
|
@@ -2067,14 +2016,24 @@
|
|
|
2067
2016
|
return btoa(String.fromCharCode.apply(null, ArrayBuffer.isView(b) ? b : new Uint8Array(b)));
|
|
2068
2017
|
};
|
|
2069
2018
|
|
|
2019
|
+
class TokenErrorResponseError extends Error {
|
|
2020
|
+
constructor({ title, message, messageCode, messageParams, }) {
|
|
2021
|
+
super(message);
|
|
2022
|
+
this.name = 'TokenErrorResponseError';
|
|
2023
|
+
this.title = title;
|
|
2024
|
+
this.messageCode = messageCode;
|
|
2025
|
+
this.messageParams = messageParams;
|
|
2026
|
+
}
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2070
2029
|
function interactWithUser(userInteraction, req) {
|
|
2071
2030
|
return new Promise((resolve, reject) => {
|
|
2072
|
-
const interactionProps = Object.assign(Object.assign({}, req), { onSubmit: (res) => {
|
|
2031
|
+
const interactionProps = Object.assign(Object.assign({ submitLabel: 'Submit', cancelLabel: 'Cancel' }, req), { onSubmit: (res) => {
|
|
2073
2032
|
userInteraction.next(undefined);
|
|
2074
2033
|
resolve(res);
|
|
2075
2034
|
}, onCancel: () => {
|
|
2076
2035
|
userInteraction.next(undefined);
|
|
2077
|
-
reject(new Dexie__default["default"].AbortError(
|
|
2036
|
+
reject(new Dexie__default["default"].AbortError('User cancelled'));
|
|
2078
2037
|
} });
|
|
2079
2038
|
userInteraction.next(interactionProps);
|
|
2080
2039
|
// Start subscribing for external updates to db.cloud.userInteraction, and if so, cancel this request.
|
|
@@ -2093,7 +2052,9 @@
|
|
|
2093
2052
|
type: 'message-alert',
|
|
2094
2053
|
title,
|
|
2095
2054
|
alerts,
|
|
2096
|
-
fields: {}
|
|
2055
|
+
fields: {},
|
|
2056
|
+
submitLabel: 'OK',
|
|
2057
|
+
cancelLabel: null,
|
|
2097
2058
|
});
|
|
2098
2059
|
}
|
|
2099
2060
|
function promptForEmail(userInteraction, title, emailHint) {
|
|
@@ -2152,22 +2113,48 @@
|
|
|
2152
2113
|
return otp;
|
|
2153
2114
|
});
|
|
2154
2115
|
}
|
|
2116
|
+
function confirmLogout(userInteraction, currentUserId, numUnsyncedChanges) {
|
|
2117
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2118
|
+
const alerts = [
|
|
2119
|
+
{
|
|
2120
|
+
type: 'warning',
|
|
2121
|
+
messageCode: 'LOGOUT_CONFIRMATION',
|
|
2122
|
+
message: `{numUnsyncedChanges} unsynced changes will get lost!
|
|
2123
|
+
Logout anyway?`,
|
|
2124
|
+
messageParams: {
|
|
2125
|
+
currentUserId,
|
|
2126
|
+
numUnsyncedChanges: numUnsyncedChanges.toString(),
|
|
2127
|
+
}
|
|
2128
|
+
},
|
|
2129
|
+
];
|
|
2130
|
+
return yield interactWithUser(userInteraction, {
|
|
2131
|
+
type: 'logout-confirmation',
|
|
2132
|
+
title: 'Confirm Logout',
|
|
2133
|
+
alerts,
|
|
2134
|
+
fields: {},
|
|
2135
|
+
submitLabel: 'Confirm logout',
|
|
2136
|
+
cancelLabel: 'Cancel'
|
|
2137
|
+
})
|
|
2138
|
+
.then(() => true)
|
|
2139
|
+
.catch(() => false);
|
|
2140
|
+
});
|
|
2141
|
+
}
|
|
2155
2142
|
|
|
2156
2143
|
function loadAccessToken(db) {
|
|
2157
|
-
var _a, _b;
|
|
2144
|
+
var _a, _b, _c;
|
|
2158
2145
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2159
2146
|
const currentUser = yield db.getCurrentUser();
|
|
2160
2147
|
const { accessToken, accessTokenExpiration, refreshToken, refreshTokenExpiration, claims, } = currentUser;
|
|
2161
2148
|
if (!accessToken)
|
|
2162
|
-
return;
|
|
2149
|
+
return null;
|
|
2163
2150
|
const expTime = (_a = accessTokenExpiration === null || accessTokenExpiration === void 0 ? void 0 : accessTokenExpiration.getTime()) !== null && _a !== void 0 ? _a : Infinity;
|
|
2164
|
-
if (expTime > Date.now()) {
|
|
2165
|
-
return
|
|
2151
|
+
if (expTime > Date.now() && (((_b = currentUser.license) === null || _b === void 0 ? void 0 : _b.status) || 'ok') === 'ok') {
|
|
2152
|
+
return currentUser;
|
|
2166
2153
|
}
|
|
2167
2154
|
if (!refreshToken) {
|
|
2168
2155
|
throw new Error(`Refresh token missing`);
|
|
2169
2156
|
}
|
|
2170
|
-
const refreshExpTime = (
|
|
2157
|
+
const refreshExpTime = (_c = refreshTokenExpiration === null || refreshTokenExpiration === void 0 ? void 0 : refreshTokenExpiration.getTime()) !== null && _c !== void 0 ? _c : Infinity;
|
|
2171
2158
|
if (refreshExpTime <= Date.now()) {
|
|
2172
2159
|
throw new Error(`Refresh token has expired`);
|
|
2173
2160
|
}
|
|
@@ -2175,8 +2162,10 @@
|
|
|
2175
2162
|
yield db.table('$logins').update(claims.sub, {
|
|
2176
2163
|
accessToken: refreshedLogin.accessToken,
|
|
2177
2164
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
2165
|
+
claims: refreshedLogin.claims,
|
|
2166
|
+
license: refreshedLogin.license,
|
|
2178
2167
|
});
|
|
2179
|
-
return refreshedLogin
|
|
2168
|
+
return refreshedLogin;
|
|
2180
2169
|
});
|
|
2181
2170
|
}
|
|
2182
2171
|
function authenticate(url, context, fetchToken, userInteraction, hints) {
|
|
@@ -2224,10 +2213,24 @@
|
|
|
2224
2213
|
if (res.status !== 200)
|
|
2225
2214
|
throw new Error(`RefreshToken: Status ${res.status} from ${url}/token`);
|
|
2226
2215
|
const response = yield res.json();
|
|
2216
|
+
if (response.type === 'error') {
|
|
2217
|
+
throw new TokenErrorResponseError(response);
|
|
2218
|
+
}
|
|
2227
2219
|
login.accessToken = response.accessToken;
|
|
2228
2220
|
login.accessTokenExpiration = response.accessTokenExpiration
|
|
2229
2221
|
? new Date(response.accessTokenExpiration)
|
|
2230
2222
|
: undefined;
|
|
2223
|
+
login.claims = response.claims;
|
|
2224
|
+
login.license = {
|
|
2225
|
+
type: response.userType,
|
|
2226
|
+
status: response.claims.license || 'ok',
|
|
2227
|
+
};
|
|
2228
|
+
if (response.evalDaysLeft != null) {
|
|
2229
|
+
login.license.evalDaysLeft = response.evalDaysLeft;
|
|
2230
|
+
}
|
|
2231
|
+
if (response.userValidUntil != null) {
|
|
2232
|
+
login.license.validUntil = new Date(response.userValidUntil);
|
|
2233
|
+
}
|
|
2231
2234
|
return login;
|
|
2232
2235
|
});
|
|
2233
2236
|
}
|
|
@@ -2259,8 +2262,15 @@
|
|
|
2259
2262
|
public_key: publicKeyPEM,
|
|
2260
2263
|
hints,
|
|
2261
2264
|
});
|
|
2265
|
+
if (response2.type === 'error') {
|
|
2266
|
+
throw new TokenErrorResponseError(response2);
|
|
2267
|
+
}
|
|
2262
2268
|
if (response2.type !== 'tokens')
|
|
2263
2269
|
throw new Error(`Unexpected response type from token endpoint: ${response2.type}`);
|
|
2270
|
+
/*const licenseStatus = response2.claims.license || 'ok';
|
|
2271
|
+
if (licenseStatus !== 'ok') {
|
|
2272
|
+
throw new InvalidLicenseError(licenseStatus);
|
|
2273
|
+
}*/
|
|
2264
2274
|
context.accessToken = response2.accessToken;
|
|
2265
2275
|
context.accessTokenExpiration = new Date(response2.accessTokenExpiration);
|
|
2266
2276
|
context.refreshToken = response2.refreshToken;
|
|
@@ -2271,6 +2281,16 @@
|
|
|
2271
2281
|
context.email = response2.claims.email;
|
|
2272
2282
|
context.name = response2.claims.name;
|
|
2273
2283
|
context.claims = response2.claims;
|
|
2284
|
+
context.license = {
|
|
2285
|
+
type: response2.userType,
|
|
2286
|
+
status: response2.claims.license || 'ok',
|
|
2287
|
+
};
|
|
2288
|
+
if (response2.evalDaysLeft != null) {
|
|
2289
|
+
context.license.evalDaysLeft = response2.evalDaysLeft;
|
|
2290
|
+
}
|
|
2291
|
+
if (response2.userValidUntil != null) {
|
|
2292
|
+
context.license.validUntil = new Date(response2.userValidUntil);
|
|
2293
|
+
}
|
|
2274
2294
|
if (response2.alerts && response2.alerts.length > 0) {
|
|
2275
2295
|
yield interactWithUser(userInteraction, {
|
|
2276
2296
|
type: 'message-alert',
|
|
@@ -2282,12 +2302,36 @@
|
|
|
2282
2302
|
return context;
|
|
2283
2303
|
}
|
|
2284
2304
|
catch (error) {
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2305
|
+
if (error instanceof TokenErrorResponseError) {
|
|
2306
|
+
yield alertUser(userInteraction, error.title, {
|
|
2307
|
+
type: 'error',
|
|
2308
|
+
messageCode: error.messageCode,
|
|
2309
|
+
message: error.message,
|
|
2310
|
+
messageParams: {},
|
|
2311
|
+
});
|
|
2312
|
+
throw error;
|
|
2313
|
+
}
|
|
2314
|
+
let message = `We're having a problem authenticating right now.`;
|
|
2315
|
+
console.error(`Error authenticating`, error);
|
|
2316
|
+
if (error instanceof TypeError) {
|
|
2317
|
+
const isOffline = typeof navigator !== undefined && !navigator.onLine;
|
|
2318
|
+
if (isOffline) {
|
|
2319
|
+
message = `You seem to be offline. Please connect to the internet and try again.`;
|
|
2320
|
+
}
|
|
2321
|
+
else if (Dexie__default["default"].debug || (typeof location !== 'undefined' && (location.hostname === 'localhost' || location.hostname === '127.0.0.1'))) {
|
|
2322
|
+
// The audience is most likely the developer. Suggest to whitelist the localhost origin:
|
|
2323
|
+
message = `Could not connect to server. Please verify that your origin '${location.origin}' is whitelisted using \`npx dexie-cloud whitelist\``;
|
|
2324
|
+
}
|
|
2325
|
+
else {
|
|
2326
|
+
message = `Could not connect to server. Please verify the connection.`;
|
|
2327
|
+
}
|
|
2328
|
+
yield alertUser(userInteraction, 'Authentication Failed', {
|
|
2329
|
+
type: 'error',
|
|
2330
|
+
messageCode: 'GENERIC_ERROR',
|
|
2331
|
+
message,
|
|
2332
|
+
messageParams: {},
|
|
2333
|
+
}).catch(() => { });
|
|
2334
|
+
}
|
|
2291
2335
|
throw error;
|
|
2292
2336
|
}
|
|
2293
2337
|
});
|
|
@@ -2334,6 +2378,75 @@
|
|
|
2334
2378
|
}
|
|
2335
2379
|
}
|
|
2336
2380
|
|
|
2381
|
+
const UNAUTHORIZED_USER = {
|
|
2382
|
+
userId: "unauthorized",
|
|
2383
|
+
name: "Unauthorized",
|
|
2384
|
+
claims: {
|
|
2385
|
+
sub: "unauthorized",
|
|
2386
|
+
},
|
|
2387
|
+
lastLogin: new Date(0)
|
|
2388
|
+
};
|
|
2389
|
+
try {
|
|
2390
|
+
Object.freeze(UNAUTHORIZED_USER);
|
|
2391
|
+
Object.freeze(UNAUTHORIZED_USER.claims);
|
|
2392
|
+
}
|
|
2393
|
+
catch (_a) { }
|
|
2394
|
+
|
|
2395
|
+
function waitUntil(o, // Works with Dexie's liveQuery observables if we'd need that
|
|
2396
|
+
predicate) {
|
|
2397
|
+
return rxjs.firstValueFrom(rxjs.from(o).pipe(rxjs.filter(predicate)));
|
|
2398
|
+
}
|
|
2399
|
+
|
|
2400
|
+
function logout(db) {
|
|
2401
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2402
|
+
const numUnsyncedChanges = yield _logout(db);
|
|
2403
|
+
if (numUnsyncedChanges) {
|
|
2404
|
+
if (yield confirmLogout(db.cloud.userInteraction, db.cloud.currentUserId, numUnsyncedChanges)) {
|
|
2405
|
+
yield _logout(db, { deleteUnsyncedData: true });
|
|
2406
|
+
}
|
|
2407
|
+
else {
|
|
2408
|
+
throw new Error(`User cancelled logout due to unsynced changes`);
|
|
2409
|
+
}
|
|
2410
|
+
}
|
|
2411
|
+
});
|
|
2412
|
+
}
|
|
2413
|
+
function _logout(db, { deleteUnsyncedData = false } = {}) {
|
|
2414
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2415
|
+
// Clear the database without emptying configuration options.
|
|
2416
|
+
const [numUnsynced, loggedOut] = yield db.dx.transaction('rw', db.dx.tables, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
2417
|
+
// @ts-ignore
|
|
2418
|
+
const idbtrans = tx.idbtrans;
|
|
2419
|
+
idbtrans.disableChangeTracking = true;
|
|
2420
|
+
idbtrans.disableAccessControl = true;
|
|
2421
|
+
const mutationTables = tx.storeNames.filter((tableName) => tableName.endsWith('_mutations'));
|
|
2422
|
+
// Count unsynced changes
|
|
2423
|
+
const unsyncCounts = yield Promise.all(mutationTables.map((mutationTable) => tx.table(mutationTable).count()));
|
|
2424
|
+
const sumUnSynced = unsyncCounts.reduce((a, b) => a + b, 0);
|
|
2425
|
+
if (sumUnSynced > 0 && !deleteUnsyncedData) {
|
|
2426
|
+
// Let caller ask user if they want to delete unsynced data.
|
|
2427
|
+
return [sumUnSynced, false];
|
|
2428
|
+
}
|
|
2429
|
+
// Either there are no unsynched changes, or caller provided flag deleteUnsynchedData = true.
|
|
2430
|
+
// Clear all tables except $jobs and $syncState (except the persisted sync state which is
|
|
2431
|
+
// also cleared because we're going to rebuild it using a fresh sync).
|
|
2432
|
+
db.$syncState.delete('syncState');
|
|
2433
|
+
for (const table of db.dx.tables) {
|
|
2434
|
+
if (table.name !== '$jobs' && table.name !== '$syncState') {
|
|
2435
|
+
table.clear();
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
return [sumUnSynced, true];
|
|
2439
|
+
}));
|
|
2440
|
+
if (loggedOut) {
|
|
2441
|
+
// Wait for currentUser observable to emit UNAUTHORIZED_USER
|
|
2442
|
+
yield waitUntil(db.cloud.currentUser, (user) => user.userId === UNAUTHORIZED_USER.userId);
|
|
2443
|
+
// Then perform an initial sync
|
|
2444
|
+
yield db.cloud.sync({ purpose: 'pull', wait: true });
|
|
2445
|
+
}
|
|
2446
|
+
return numUnsynced;
|
|
2447
|
+
});
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2337
2450
|
class HttpError extends Error {
|
|
2338
2451
|
constructor(res, message) {
|
|
2339
2452
|
super(message || `${res.status} ${res.statusText}`);
|
|
@@ -2387,8 +2500,9 @@
|
|
|
2387
2500
|
throw new HttpError(res1, errMsg);
|
|
2388
2501
|
}
|
|
2389
2502
|
const response = yield res1.json();
|
|
2390
|
-
if (response.type === 'tokens') {
|
|
2503
|
+
if (response.type === 'tokens' || response.type === 'error') {
|
|
2391
2504
|
// Demo user request can get a "tokens" response right away
|
|
2505
|
+
// Error can also be returned right away.
|
|
2392
2506
|
return response;
|
|
2393
2507
|
}
|
|
2394
2508
|
else if (tokenRequest.grant_type === 'otp') {
|
|
@@ -2420,12 +2534,6 @@
|
|
|
2420
2534
|
}
|
|
2421
2535
|
if (res2.status !== 200) {
|
|
2422
2536
|
const errMsg = yield res2.text();
|
|
2423
|
-
yield alertUser(userInteraction, "OTP Authentication Failed", {
|
|
2424
|
-
type: 'error',
|
|
2425
|
-
messageCode: 'GENERIC_ERROR',
|
|
2426
|
-
message: errMsg,
|
|
2427
|
-
messageParams: {}
|
|
2428
|
-
}).catch(() => { });
|
|
2429
2537
|
throw new HttpError(res2, errMsg);
|
|
2430
2538
|
}
|
|
2431
2539
|
const response2 = yield res2.json();
|
|
@@ -2438,6 +2546,18 @@
|
|
|
2438
2546
|
};
|
|
2439
2547
|
}
|
|
2440
2548
|
|
|
2549
|
+
/** A way to log to console in production without terser stripping out
|
|
2550
|
+
* it from the release bundle.
|
|
2551
|
+
* This should be used very rarely and only in places where it's
|
|
2552
|
+
* absolutely necessary to log something in production.
|
|
2553
|
+
*
|
|
2554
|
+
* @param level
|
|
2555
|
+
* @param args
|
|
2556
|
+
*/
|
|
2557
|
+
function prodLog(level, ...args) {
|
|
2558
|
+
globalThis["con" + "sole"][level](...args);
|
|
2559
|
+
}
|
|
2560
|
+
|
|
2441
2561
|
/** This function changes or sets the current user as requested.
|
|
2442
2562
|
*
|
|
2443
2563
|
* Use cases:
|
|
@@ -2464,102 +2584,76 @@
|
|
|
2464
2584
|
}));
|
|
2465
2585
|
user.isLoggedIn = true;
|
|
2466
2586
|
user.lastLogin = new Date();
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
}));
|
|
2470
|
-
yield new Promise((resolve) => {
|
|
2471
|
-
if (db.cloud.currentUserId === user.userId) {
|
|
2472
|
-
resolve(null);
|
|
2587
|
+
try {
|
|
2588
|
+
yield user.save();
|
|
2473
2589
|
}
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
if (
|
|
2477
|
-
|
|
2478
|
-
|
|
2590
|
+
catch (e) {
|
|
2591
|
+
try {
|
|
2592
|
+
if (e.name === 'DataCloneError') {
|
|
2593
|
+
// We've seen this buggy behavior in some browsers and in case it happens
|
|
2594
|
+
// again we really need to collect the details to understand what's going on.
|
|
2595
|
+
prodLog('debug', `Login context property names:`, Object.keys(user));
|
|
2596
|
+
prodLog('debug', `Login context property names:`, Object.keys(user));
|
|
2597
|
+
prodLog('debug', `Login context:`, user);
|
|
2598
|
+
prodLog('debug', `Login context JSON:`, JSON.stringify(user));
|
|
2479
2599
|
}
|
|
2480
|
-
}
|
|
2600
|
+
}
|
|
2601
|
+
catch (_a) { }
|
|
2602
|
+
throw e;
|
|
2481
2603
|
}
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
// V: Samma med andra windows.
|
|
2486
|
-
// V: Så kanske göra om den till att häröra från liveQuery som läser $logins.orderBy('lastLogin').last().
|
|
2487
|
-
// V: Då bara vara medveten om:
|
|
2488
|
-
// V: En sån observable börjar hämta data vid första subscribe
|
|
2489
|
-
// V: Vi har inget "inital value" men kan emulera det till att vara ANONYMOUS_USER
|
|
2490
|
-
// V: Om requireAuth är true, så borde db.on(ready) hålla databasen stängd för alla utom denna observable.
|
|
2491
|
-
// V: Om inte så behöver den inte blocka.
|
|
2492
|
-
// Andra tankar:
|
|
2493
|
-
// * Man kan inte byta användare när man är offline. Skulle gå att flytta realms till undanstuff-tabell vid user-change.
|
|
2494
|
-
// men troligen inte värt det.
|
|
2495
|
-
// * Istället: sälj inte inte switch-user funktionalitet utan tala enbart om inloggat vs icke inloggat läge.
|
|
2496
|
-
// * populate $logins med ANONYMOUS så att en påbörjad inloggning inte räknas, alternativt ha en boolean prop!
|
|
2497
|
-
// Kanske bäst ha en boolean prop!
|
|
2498
|
-
// * Alternativ switch-user funktionalitet:
|
|
2499
|
-
// * DBCore gömmer data från realms man inte har tillgång till.
|
|
2500
|
-
// * Cursor impl behövs också då.
|
|
2501
|
-
// * Då blir det snabba user switch.
|
|
2502
|
-
// * claims-settet som skickas till servern blir summan av alla claims. Då måste servern stödja multipla tokens eller
|
|
2503
|
-
// att ens token är ett samlad.
|
|
2604
|
+
console.debug('Saved new user', user.email);
|
|
2605
|
+
}));
|
|
2606
|
+
yield waitUntil(db.cloud.currentUser, (currentUser) => currentUser.userId === user.userId);
|
|
2504
2607
|
});
|
|
2505
2608
|
}
|
|
2506
2609
|
|
|
2507
2610
|
function login(db, hints) {
|
|
2611
|
+
var _a;
|
|
2508
2612
|
return __awaiter(this, void 0, void 0, function* () {
|
|
2509
2613
|
const currentUser = yield db.getCurrentUser();
|
|
2510
|
-
|
|
2511
|
-
|
|
2512
|
-
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
throw new Error(`Must logout before changing user`);
|
|
2517
|
-
}
|
|
2614
|
+
const origUserId = currentUser.userId;
|
|
2615
|
+
if (currentUser.isLoggedIn && (!hints || (!hints.email && !hints.userId))) {
|
|
2616
|
+
const licenseStatus = ((_a = currentUser.license) === null || _a === void 0 ? void 0 : _a.status) || 'ok';
|
|
2617
|
+
if (licenseStatus === 'ok' && currentUser.accessToken && (!currentUser.accessTokenExpiration || currentUser.accessTokenExpiration.getTime() > Date.now())) {
|
|
2618
|
+
// Already authenticated according to given hints. And license is valid.
|
|
2619
|
+
return false;
|
|
2518
2620
|
}
|
|
2519
|
-
|
|
2520
|
-
|
|
2621
|
+
if (currentUser.refreshToken && (!currentUser.refreshTokenExpiration || currentUser.refreshTokenExpiration.getTime() > Date.now())) {
|
|
2622
|
+
// Refresh the token
|
|
2623
|
+
yield loadAccessToken(db);
|
|
2624
|
+
return false;
|
|
2625
|
+
}
|
|
2626
|
+
// No refresh token - must re-authenticate:
|
|
2521
2627
|
}
|
|
2522
2628
|
const context = new AuthPersistedContext(db, {
|
|
2523
2629
|
claims: {},
|
|
2524
2630
|
lastLogin: new Date(0),
|
|
2525
2631
|
});
|
|
2526
2632
|
yield authenticate(db.cloud.options.databaseUrl, context, db.cloud.options.fetchTokens || otpFetchTokenCallback(db), db.cloud.userInteraction, hints);
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2537
|
-
|
|
2538
|
-
|
|
2539
|
-
|
|
2540
|
-
|
|
2633
|
+
if (origUserId !== UNAUTHORIZED_USER.userId && context.userId !== origUserId) {
|
|
2634
|
+
// User was logged in before, but now logged in as another user.
|
|
2635
|
+
yield logout(db);
|
|
2636
|
+
}
|
|
2637
|
+
/*try {
|
|
2638
|
+
await context.save();
|
|
2639
|
+
} catch (e) {
|
|
2640
|
+
try {
|
|
2641
|
+
if (e.name === 'DataCloneError') {
|
|
2642
|
+
console.debug(`Login context property names:`, Object.keys(context));
|
|
2643
|
+
console.debug(`Login context:`, context);
|
|
2644
|
+
console.debug(`Login context JSON:`, JSON.stringify(context));
|
|
2645
|
+
}
|
|
2646
|
+
} catch {}
|
|
2647
|
+
throw e;
|
|
2648
|
+
}*/
|
|
2541
2649
|
yield setCurrentUser(db, context);
|
|
2542
2650
|
// Make sure to resync as the new login will be authorized
|
|
2543
2651
|
// for new realms.
|
|
2544
2652
|
triggerSync(db, "pull");
|
|
2545
|
-
return
|
|
2653
|
+
return context.userId !== origUserId;
|
|
2546
2654
|
});
|
|
2547
2655
|
}
|
|
2548
2656
|
|
|
2549
|
-
const UNAUTHORIZED_USER = {
|
|
2550
|
-
userId: "unauthorized",
|
|
2551
|
-
name: "Unauthorized",
|
|
2552
|
-
claims: {
|
|
2553
|
-
sub: "unauthorized",
|
|
2554
|
-
},
|
|
2555
|
-
lastLogin: new Date(0)
|
|
2556
|
-
};
|
|
2557
|
-
try {
|
|
2558
|
-
Object.freeze(UNAUTHORIZED_USER);
|
|
2559
|
-
Object.freeze(UNAUTHORIZED_USER.claims);
|
|
2560
|
-
}
|
|
2561
|
-
catch (_a) { }
|
|
2562
|
-
|
|
2563
2657
|
const swHolder = {};
|
|
2564
2658
|
const swContainer = typeof self !== 'undefined' && self.document && // self.document is to verify we're not the SW ourself
|
|
2565
2659
|
typeof navigator !== 'undefined' && navigator.serviceWorker;
|
|
@@ -3413,9 +3507,19 @@
|
|
|
3413
3507
|
const mutClone = changeClone.muts[mutIndex];
|
|
3414
3508
|
const rewrittenKey = JSON.stringify(key);
|
|
3415
3509
|
mutClone.keys[keyIndex] = rewrittenKey;
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3510
|
+
/* Bug (#1777)
|
|
3511
|
+
We should not rewrite values. It will fail because the key is array and the value is string.
|
|
3512
|
+
Only the keys should be rewritten and it's already done on the server.
|
|
3513
|
+
We should take another round of revieweing how key transformations are being done between
|
|
3514
|
+
client and server and let the server do the key transformations entirely instead now that
|
|
3515
|
+
we have the primary key schema on the server making it possible to do so.
|
|
3516
|
+
if (rewriteValues) {
|
|
3517
|
+
Dexie.setByKeyPath(
|
|
3518
|
+
(mutClone as DBInsertOperation).values[keyIndex],
|
|
3519
|
+
primaryKey.keyPath!,
|
|
3520
|
+
rewrittenKey
|
|
3521
|
+
);
|
|
3522
|
+
}*/
|
|
3419
3523
|
}
|
|
3420
3524
|
else if (key[0] === '#') {
|
|
3421
3525
|
// Private ID - translate!
|
|
@@ -3446,6 +3550,40 @@
|
|
|
3446
3550
|
: change.muts.map((m) => (Object.assign(Object.assign({}, m), { keys: m.keys.slice() }))) });
|
|
3447
3551
|
}
|
|
3448
3552
|
|
|
3553
|
+
// If we get Ratelimit-Limit and Ratelimit-Remaining where Ratelimit-Remaining is below
|
|
3554
|
+
// (Ratelimit-Limit / 2), we should delay the next sync by (Ratelimit-Reset / Ratelimit-Remaining)
|
|
3555
|
+
// seconds (given that there is a Ratelimit-Reset header).
|
|
3556
|
+
let syncRatelimitDelays = new WeakMap();
|
|
3557
|
+
function checkSyncRateLimitDelay(db) {
|
|
3558
|
+
var _a, _b;
|
|
3559
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
3560
|
+
const delatMilliseconds = ((_b = (_a = syncRatelimitDelays.get(db)) === null || _a === void 0 ? void 0 : _a.getTime()) !== null && _b !== void 0 ? _b : 0) - Date.now();
|
|
3561
|
+
if (delatMilliseconds > 0) {
|
|
3562
|
+
console.debug(`Stalling sync request ${delatMilliseconds} ms to spare ratelimits`);
|
|
3563
|
+
yield new Promise(resolve => setTimeout(resolve, delatMilliseconds));
|
|
3564
|
+
}
|
|
3565
|
+
});
|
|
3566
|
+
}
|
|
3567
|
+
function updateSyncRateLimitDelays(db, res) {
|
|
3568
|
+
const limit = res.headers.get('Ratelimit-Limit');
|
|
3569
|
+
const remaining = res.headers.get('Ratelimit-Remaining');
|
|
3570
|
+
const reset = res.headers.get('Ratelimit-Reset');
|
|
3571
|
+
if (limit && remaining && reset) {
|
|
3572
|
+
const limitNum = Number(limit);
|
|
3573
|
+
const remainingNum = Math.max(0, Number(remaining));
|
|
3574
|
+
const willResetInSeconds = Number(reset);
|
|
3575
|
+
if (remainingNum < limitNum / 2) {
|
|
3576
|
+
const delay = Math.ceil(willResetInSeconds / (remainingNum + 1));
|
|
3577
|
+
syncRatelimitDelays.set(db, new Date(Date.now() + delay * 1000));
|
|
3578
|
+
console.debug(`Sync ratelimit delay set to ${delay} seconds`);
|
|
3579
|
+
}
|
|
3580
|
+
else {
|
|
3581
|
+
syncRatelimitDelays.delete(db);
|
|
3582
|
+
console.debug(`Sync ratelimit delay cleared`);
|
|
3583
|
+
}
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
3586
|
+
|
|
3449
3587
|
//import {BisonWebStreamReader} from "dreambase-library/dist/typeson-simplified/BisonWebStreamReader";
|
|
3450
3588
|
function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, clientIdentity, currentUser) {
|
|
3451
3589
|
return __awaiter(this, void 0, void 0, function* () {
|
|
@@ -3454,9 +3592,20 @@
|
|
|
3454
3592
|
//
|
|
3455
3593
|
const headers = {
|
|
3456
3594
|
Accept: 'application/json, application/x-bison, application/x-bison-stream',
|
|
3457
|
-
'Content-Type': 'application/tson'
|
|
3595
|
+
'Content-Type': 'application/tson',
|
|
3458
3596
|
};
|
|
3459
|
-
const
|
|
3597
|
+
const updatedUser = yield loadAccessToken(db);
|
|
3598
|
+
/*
|
|
3599
|
+
if (updatedUser?.license && changes.length > 0) {
|
|
3600
|
+
if (updatedUser.license.status === 'expired') {
|
|
3601
|
+
throw new Error(`License has expired`);
|
|
3602
|
+
}
|
|
3603
|
+
if (updatedUser.license.status === 'deactivated') {
|
|
3604
|
+
throw new Error(`License deactivated`);
|
|
3605
|
+
}
|
|
3606
|
+
}
|
|
3607
|
+
*/
|
|
3608
|
+
const accessToken = updatedUser === null || updatedUser === void 0 ? void 0 : updatedUser.accessToken;
|
|
3460
3609
|
if (accessToken) {
|
|
3461
3610
|
headers.Authorization = `Bearer ${accessToken}`;
|
|
3462
3611
|
}
|
|
@@ -3465,27 +3614,31 @@
|
|
|
3465
3614
|
dbID: syncState === null || syncState === void 0 ? void 0 : syncState.remoteDbId,
|
|
3466
3615
|
clientIdentity,
|
|
3467
3616
|
schema: schema || {},
|
|
3468
|
-
lastPull: syncState
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3617
|
+
lastPull: syncState
|
|
3618
|
+
? {
|
|
3619
|
+
serverRevision: syncState.serverRevision,
|
|
3620
|
+
realms: syncState.realms,
|
|
3621
|
+
inviteRealms: syncState.inviteRealms,
|
|
3622
|
+
}
|
|
3623
|
+
: undefined,
|
|
3473
3624
|
baseRevs,
|
|
3474
|
-
changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes)
|
|
3625
|
+
changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes),
|
|
3475
3626
|
};
|
|
3476
|
-
console.debug(
|
|
3627
|
+
console.debug('Sync request', syncRequest);
|
|
3477
3628
|
db.syncStateChangedEvent.next({
|
|
3478
3629
|
phase: 'pushing',
|
|
3479
3630
|
});
|
|
3480
3631
|
const res = yield fetch(`${databaseUrl}/sync`, {
|
|
3481
3632
|
method: 'post',
|
|
3482
3633
|
headers,
|
|
3483
|
-
|
|
3634
|
+
credentials: 'include',
|
|
3635
|
+
body: TSON.stringify(syncRequest),
|
|
3484
3636
|
});
|
|
3485
3637
|
//const contentLength = Number(res.headers.get('content-length'));
|
|
3486
3638
|
db.syncStateChangedEvent.next({
|
|
3487
|
-
phase: 'pulling'
|
|
3639
|
+
phase: 'pulling',
|
|
3488
3640
|
});
|
|
3641
|
+
updateSyncRateLimitDelays(db, res);
|
|
3489
3642
|
if (!res.ok) {
|
|
3490
3643
|
throw new HttpError(res);
|
|
3491
3644
|
}
|
|
@@ -3687,12 +3840,13 @@
|
|
|
3687
3840
|
function sync(db, options, schema, syncOptions) {
|
|
3688
3841
|
return _sync
|
|
3689
3842
|
.apply(this, arguments)
|
|
3690
|
-
.then(() => {
|
|
3843
|
+
.then((result) => {
|
|
3691
3844
|
if (!(syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)) { // && syncOptions?.purpose !== 'push') {
|
|
3692
3845
|
db.syncStateChangedEvent.next({
|
|
3693
3846
|
phase: 'in-sync',
|
|
3694
3847
|
});
|
|
3695
3848
|
}
|
|
3849
|
+
return result;
|
|
3696
3850
|
})
|
|
3697
3851
|
.catch((error) => __awaiter(this, void 0, void 0, function* () {
|
|
3698
3852
|
if (syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)
|
|
@@ -3722,7 +3876,7 @@
|
|
|
3722
3876
|
});
|
|
3723
3877
|
db.syncStateChangedEvent.next({
|
|
3724
3878
|
phase: isOnline ? 'error' : 'offline',
|
|
3725
|
-
error,
|
|
3879
|
+
error: new Error('' + (error === null || error === void 0 ? void 0 : error.message) || error),
|
|
3726
3880
|
});
|
|
3727
3881
|
return Promise.reject(error);
|
|
3728
3882
|
}));
|
|
@@ -3907,6 +4061,7 @@
|
|
|
3907
4061
|
}));
|
|
3908
4062
|
if (!done) {
|
|
3909
4063
|
console.debug('MORE SYNC NEEDED. Go for it again!');
|
|
4064
|
+
yield checkSyncRateLimitDelay(db);
|
|
3910
4065
|
return yield _sync(db, options, schema, { isInitialSync, cancelToken });
|
|
3911
4066
|
}
|
|
3912
4067
|
console.debug('SYNC DONE', { isInitialSync });
|
|
@@ -4043,6 +4198,8 @@
|
|
|
4043
4198
|
yield db.table('$logins').update(user.userId, {
|
|
4044
4199
|
accessToken: refreshedLogin.accessToken,
|
|
4045
4200
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
4201
|
+
claims: refreshedLogin.claims,
|
|
4202
|
+
license: refreshedLogin.license,
|
|
4046
4203
|
});
|
|
4047
4204
|
// Updating $logins will trigger emission of db.cloud.currentUser observable, which
|
|
4048
4205
|
// in turn will lead to that connectWebSocket.ts will reconnect the socket with the
|
|
@@ -4611,6 +4768,13 @@
|
|
|
4611
4768
|
|
|
4612
4769
|
const outstandingTransactions = new rxjs.BehaviorSubject(new Set());
|
|
4613
4770
|
|
|
4771
|
+
function isEagerSyncDisabled(db) {
|
|
4772
|
+
var _a, _b, _c, _d;
|
|
4773
|
+
return (((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.disableEagerSync) ||
|
|
4774
|
+
((_c = (_b = db.cloud.currentUser.value) === null || _b === void 0 ? void 0 : _b.license) === null || _c === void 0 ? void 0 : _c.status) !== 'ok' ||
|
|
4775
|
+
!((_d = db.cloud.options) === null || _d === void 0 ? void 0 : _d.databaseUrl));
|
|
4776
|
+
}
|
|
4777
|
+
|
|
4614
4778
|
/** Tracks all mutations in the same transaction as the mutations -
|
|
4615
4779
|
* so it is guaranteed that no mutation goes untracked - and if transaction
|
|
4616
4780
|
* aborts, the mutations won't be tracked.
|
|
@@ -4619,7 +4783,7 @@
|
|
|
4619
4783
|
* changes to server and cleanup the tracked mutations once the server has
|
|
4620
4784
|
* ackowledged that it got them.
|
|
4621
4785
|
*/
|
|
4622
|
-
function createMutationTrackingMiddleware({ currentUserObservable, db }) {
|
|
4786
|
+
function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
4623
4787
|
return {
|
|
4624
4788
|
stack: 'dbcore',
|
|
4625
4789
|
name: 'MutationTrackingMiddleware',
|
|
@@ -4630,7 +4794,7 @@
|
|
|
4630
4794
|
try {
|
|
4631
4795
|
mutTableMap = new Map(ordinaryTables.map((tbl) => [
|
|
4632
4796
|
tbl.name,
|
|
4633
|
-
core.table(`$${tbl.name}_mutations`)
|
|
4797
|
+
core.table(`$${tbl.name}_mutations`),
|
|
4634
4798
|
]));
|
|
4635
4799
|
}
|
|
4636
4800
|
catch (_a) {
|
|
@@ -4664,15 +4828,9 @@
|
|
|
4664
4828
|
outstandingTransactions.next(outstandingTransactions.value);
|
|
4665
4829
|
};
|
|
4666
4830
|
const txComplete = () => {
|
|
4667
|
-
|
|
4668
|
-
|
|
4669
|
-
|
|
4670
|
-
console.debug('registering sync event');
|
|
4671
|
-
registerSyncEvent(db, "push");
|
|
4672
|
-
}
|
|
4673
|
-
else {
|
|
4674
|
-
db.localSyncEvent.next({ purpose: "push" });
|
|
4675
|
-
}
|
|
4831
|
+
if (tx.mutationsAdded &&
|
|
4832
|
+
!isEagerSyncDisabled(db)) {
|
|
4833
|
+
triggerSync(db, 'push');
|
|
4676
4834
|
}
|
|
4677
4835
|
removeTransaction();
|
|
4678
4836
|
};
|
|
@@ -4739,7 +4897,7 @@
|
|
|
4739
4897
|
.query({
|
|
4740
4898
|
query: { range: req.range, index: schema.primaryKey },
|
|
4741
4899
|
trans: req.trans,
|
|
4742
|
-
values: false
|
|
4900
|
+
values: false,
|
|
4743
4901
|
})
|
|
4744
4902
|
// Do a delete request instead, but keep the criteria info for the server to execute
|
|
4745
4903
|
.then((res) => {
|
|
@@ -4747,7 +4905,7 @@
|
|
|
4747
4905
|
type: 'delete',
|
|
4748
4906
|
keys: res.result,
|
|
4749
4907
|
trans: req.trans,
|
|
4750
|
-
criteria: { index: null, range: req.range }
|
|
4908
|
+
criteria: { index: null, range: req.range },
|
|
4751
4909
|
});
|
|
4752
4910
|
})
|
|
4753
4911
|
: mutateAndLog(req);
|
|
@@ -4755,7 +4913,7 @@
|
|
|
4755
4913
|
function mutateAndLog(req) {
|
|
4756
4914
|
const trans = req.trans;
|
|
4757
4915
|
trans.mutationsAdded = true;
|
|
4758
|
-
const { txid, currentUser: { userId } } = trans;
|
|
4916
|
+
const { txid, currentUser: { userId }, } = trans;
|
|
4759
4917
|
const { type } = req;
|
|
4760
4918
|
const opNo = ++trans.opCount;
|
|
4761
4919
|
return table.mutate(req).then((res) => {
|
|
@@ -4776,7 +4934,7 @@
|
|
|
4776
4934
|
keys,
|
|
4777
4935
|
criteria: req.criteria,
|
|
4778
4936
|
txid,
|
|
4779
|
-
userId
|
|
4937
|
+
userId,
|
|
4780
4938
|
}
|
|
4781
4939
|
: req.type === 'add'
|
|
4782
4940
|
? {
|
|
@@ -4786,7 +4944,7 @@
|
|
|
4786
4944
|
keys,
|
|
4787
4945
|
txid,
|
|
4788
4946
|
userId,
|
|
4789
|
-
values
|
|
4947
|
+
values,
|
|
4790
4948
|
}
|
|
4791
4949
|
: req.criteria && req.changeSpec
|
|
4792
4950
|
? {
|
|
@@ -4798,7 +4956,7 @@
|
|
|
4798
4956
|
criteria: req.criteria,
|
|
4799
4957
|
changeSpec: req.changeSpec,
|
|
4800
4958
|
txid,
|
|
4801
|
-
userId
|
|
4959
|
+
userId,
|
|
4802
4960
|
}
|
|
4803
4961
|
: updates
|
|
4804
4962
|
? {
|
|
@@ -4809,7 +4967,7 @@
|
|
|
4809
4967
|
keys: updates.keys,
|
|
4810
4968
|
changeSpecs: updates.changeSpecs,
|
|
4811
4969
|
txid,
|
|
4812
|
-
userId
|
|
4970
|
+
userId,
|
|
4813
4971
|
}
|
|
4814
4972
|
: {
|
|
4815
4973
|
type: 'upsert',
|
|
@@ -4818,7 +4976,7 @@
|
|
|
4818
4976
|
keys,
|
|
4819
4977
|
values,
|
|
4820
4978
|
txid,
|
|
4821
|
-
userId
|
|
4979
|
+
userId,
|
|
4822
4980
|
};
|
|
4823
4981
|
return keys.length > 0 || ('criteria' in req && req.criteria)
|
|
4824
4982
|
? mutsTable
|
|
@@ -4828,7 +4986,7 @@
|
|
|
4828
4986
|
});
|
|
4829
4987
|
}
|
|
4830
4988
|
} });
|
|
4831
|
-
}
|
|
4989
|
+
},
|
|
4832
4990
|
};
|
|
4833
4991
|
}
|
|
4834
4992
|
|
|
@@ -5187,6 +5345,20 @@
|
|
|
5187
5345
|
}
|
|
5188
5346
|
}
|
|
5189
5347
|
|
|
5348
|
+
class InvalidLicenseError extends Error {
|
|
5349
|
+
constructor(license) {
|
|
5350
|
+
super(license === 'expired'
|
|
5351
|
+
? `License expired`
|
|
5352
|
+
: license === 'deactivated'
|
|
5353
|
+
? `User deactivated`
|
|
5354
|
+
: 'Invalid license');
|
|
5355
|
+
this.name = 'InvalidLicenseError';
|
|
5356
|
+
if (license) {
|
|
5357
|
+
this.license = license;
|
|
5358
|
+
}
|
|
5359
|
+
}
|
|
5360
|
+
}
|
|
5361
|
+
|
|
5190
5362
|
function sleep(ms) {
|
|
5191
5363
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5192
5364
|
}
|
|
@@ -5220,7 +5392,12 @@
|
|
|
5220
5392
|
function createObservable() {
|
|
5221
5393
|
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.
|
|
5222
5394
|
take(1), // Don't continue waking up whenever syncState change
|
|
5223
|
-
switchMap((syncState) => db.cloud.currentUser.pipe(map((userLogin) => [userLogin, syncState]))), switchMap(([userLogin, syncState]) =>
|
|
5395
|
+
switchMap((syncState) => db.cloud.currentUser.pipe(map((userLogin) => [userLogin, syncState]))), switchMap(([userLogin, syncState]) => {
|
|
5396
|
+
/*if (userLogin.license?.status && userLogin.license.status !== 'ok') {
|
|
5397
|
+
throw new InvalidLicenseError();
|
|
5398
|
+
}*/
|
|
5399
|
+
return userIsReallyActive.pipe(map((isActive) => [isActive ? userLogin : null, syncState]));
|
|
5400
|
+
}), switchMap(([userLogin, syncState]) => {
|
|
5224
5401
|
if ((userLogin === null || userLogin === void 0 ? void 0 : userLogin.isLoggedIn) && !(syncState === null || syncState === void 0 ? void 0 : syncState.realms.includes(userLogin.userId))) {
|
|
5225
5402
|
// We're in an in-between state when user is logged in but the user's realms are not yet synced.
|
|
5226
5403
|
// Don't make this change reconnect the websocket just yet. Wait till syncState is updated
|
|
@@ -5249,14 +5426,20 @@
|
|
|
5249
5426
|
yield db.table('$logins').update(user.userId, {
|
|
5250
5427
|
accessToken: refreshedLogin.accessToken,
|
|
5251
5428
|
accessTokenExpiration: refreshedLogin.accessTokenExpiration,
|
|
5429
|
+
claims: refreshedLogin.claims,
|
|
5430
|
+
license: refreshedLogin.license,
|
|
5252
5431
|
});
|
|
5253
5432
|
})), switchMap(() => createObservable()));
|
|
5254
5433
|
}
|
|
5255
5434
|
else {
|
|
5256
|
-
return rxjs.throwError(error);
|
|
5435
|
+
return rxjs.throwError(() => error);
|
|
5257
5436
|
}
|
|
5258
5437
|
}), catchError((error) => {
|
|
5259
5438
|
db.cloud.webSocketStatus.next("error");
|
|
5439
|
+
if (error instanceof InvalidLicenseError) {
|
|
5440
|
+
// Don't retry. Just throw and don't try connect again.
|
|
5441
|
+
return rxjs.throwError(() => error);
|
|
5442
|
+
}
|
|
5260
5443
|
return rxjs.from(waitAndReconnectWhenUserDoesSomething(error)).pipe(switchMap(() => createObservable()));
|
|
5261
5444
|
}));
|
|
5262
5445
|
}
|
|
@@ -5285,97 +5468,12 @@
|
|
|
5285
5468
|
});
|
|
5286
5469
|
}
|
|
5287
5470
|
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
const GUARDED_JOB_TIMEOUT = 1 * MINUTES;
|
|
5295
|
-
function performGuardedJob(db, jobName, jobsTableName, job, { awaitRemoteJob } = {}) {
|
|
5296
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
5297
|
-
// Start working.
|
|
5298
|
-
//
|
|
5299
|
-
// Check if someone else is working on this already.
|
|
5300
|
-
//
|
|
5301
|
-
const jobsTable = db.table(jobsTableName);
|
|
5302
|
-
function aquireLock() {
|
|
5303
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
5304
|
-
const gotTheLock = yield db.transaction('rw!', jobsTableName, () => __awaiter(this, void 0, void 0, function* () {
|
|
5305
|
-
const currentWork = yield jobsTable.get(jobName);
|
|
5306
|
-
if (!currentWork) {
|
|
5307
|
-
// No one else is working. Let's record that we are.
|
|
5308
|
-
yield jobsTable.add({
|
|
5309
|
-
nodeId: myId,
|
|
5310
|
-
started: new Date(),
|
|
5311
|
-
heartbeat: new Date()
|
|
5312
|
-
}, jobName);
|
|
5313
|
-
return true;
|
|
5314
|
-
}
|
|
5315
|
-
else if (currentWork.heartbeat.getTime() <
|
|
5316
|
-
Date.now() - GUARDED_JOB_TIMEOUT) {
|
|
5317
|
-
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!`);
|
|
5318
|
-
// Now, take over!
|
|
5319
|
-
yield jobsTable.put({
|
|
5320
|
-
nodeId: myId,
|
|
5321
|
-
started: new Date(),
|
|
5322
|
-
heartbeat: new Date()
|
|
5323
|
-
}, jobName);
|
|
5324
|
-
return true;
|
|
5325
|
-
}
|
|
5326
|
-
return false;
|
|
5327
|
-
}));
|
|
5328
|
-
if (gotTheLock)
|
|
5329
|
-
return true;
|
|
5330
|
-
// Someone else took the job.
|
|
5331
|
-
if (awaitRemoteJob) {
|
|
5332
|
-
try {
|
|
5333
|
-
const jobDoneObservable = rxjs.from(Dexie.liveQuery(() => jobsTable.get(jobName))).pipe(timeout(GUARDED_JOB_TIMEOUT), filter((job) => !job)); // Wait til job is not there anymore.
|
|
5334
|
-
yield jobDoneObservable.toPromise();
|
|
5335
|
-
return false;
|
|
5336
|
-
}
|
|
5337
|
-
catch (err) {
|
|
5338
|
-
if (err.name !== 'TimeoutError') {
|
|
5339
|
-
throw err;
|
|
5340
|
-
}
|
|
5341
|
-
// Timeout stopped us! Try aquire the lock now.
|
|
5342
|
-
// It will likely succeed this time unless
|
|
5343
|
-
// another client took it.
|
|
5344
|
-
return yield aquireLock();
|
|
5345
|
-
}
|
|
5346
|
-
}
|
|
5347
|
-
return false;
|
|
5348
|
-
});
|
|
5349
|
-
}
|
|
5350
|
-
if (yield aquireLock()) {
|
|
5351
|
-
// We own the lock entry and can do our job undisturbed.
|
|
5352
|
-
// We're not within a transaction, but these type of locks
|
|
5353
|
-
// spans over transactions.
|
|
5354
|
-
// Start our heart beat during the job.
|
|
5355
|
-
// Use setInterval to make sure we are updating heartbeat even during long-lived fetch calls.
|
|
5356
|
-
const heartbeat = setInterval(() => {
|
|
5357
|
-
jobsTable.update(jobName, (job) => {
|
|
5358
|
-
if (job.nodeId === myId) {
|
|
5359
|
-
job.heartbeat = new Date();
|
|
5360
|
-
}
|
|
5361
|
-
});
|
|
5362
|
-
}, GUARDED_JOB_HEARTBEAT);
|
|
5363
|
-
try {
|
|
5364
|
-
return yield job();
|
|
5365
|
-
}
|
|
5366
|
-
finally {
|
|
5367
|
-
// Stop heartbeat
|
|
5368
|
-
clearInterval(heartbeat);
|
|
5369
|
-
// Remove the persisted job state:
|
|
5370
|
-
yield db.transaction('rw!', jobsTableName, () => __awaiter(this, void 0, void 0, function* () {
|
|
5371
|
-
const currentWork = yield jobsTable.get(jobName);
|
|
5372
|
-
if (currentWork && currentWork.nodeId === myId) {
|
|
5373
|
-
yield jobsTable.delete(jobName);
|
|
5374
|
-
}
|
|
5375
|
-
}));
|
|
5376
|
-
}
|
|
5377
|
-
}
|
|
5378
|
-
});
|
|
5471
|
+
function performGuardedJob(db, jobName, job) {
|
|
5472
|
+
if (typeof navigator === 'undefined' || !navigator.locks) {
|
|
5473
|
+
// No support for guarding jobs. IE11, node.js, etc.
|
|
5474
|
+
return job();
|
|
5475
|
+
}
|
|
5476
|
+
return navigator.locks.request(db.name + '|' + jobName, () => job());
|
|
5379
5477
|
}
|
|
5380
5478
|
|
|
5381
5479
|
const ongoingSyncs = new WeakMap();
|
|
@@ -5429,6 +5527,9 @@
|
|
|
5429
5527
|
function _syncIfPossible() {
|
|
5430
5528
|
return __awaiter(this, void 0, void 0, function* () {
|
|
5431
5529
|
try {
|
|
5530
|
+
// Check if should delay sync due to ratelimit:
|
|
5531
|
+
yield checkSyncRateLimitDelay(db);
|
|
5532
|
+
// Check if we need to lock the sync job. Not needed if we are the service worker.
|
|
5432
5533
|
if (db.cloud.isServiceWorkerDB) {
|
|
5433
5534
|
// We are the dedicated sync SW:
|
|
5434
5535
|
yield sync(db, cloudOptions, cloudSchema, options);
|
|
@@ -5436,7 +5537,7 @@
|
|
|
5436
5537
|
else if (!db.cloud.usingServiceWorker) {
|
|
5437
5538
|
// We use a flow that is better suited for the case when multiple workers want to
|
|
5438
5539
|
// do the same thing.
|
|
5439
|
-
yield performGuardedJob(db, CURRENT_SYNC_WORKER,
|
|
5540
|
+
yield performGuardedJob(db, CURRENT_SYNC_WORKER, () => sync(db, cloudOptions, cloudSchema, options));
|
|
5440
5541
|
}
|
|
5441
5542
|
else {
|
|
5442
5543
|
assert(false);
|
|
@@ -5458,19 +5559,29 @@
|
|
|
5458
5559
|
}
|
|
5459
5560
|
}
|
|
5460
5561
|
|
|
5562
|
+
const SECONDS = 1000;
|
|
5563
|
+
const MINUTES = 60 * SECONDS;
|
|
5564
|
+
|
|
5461
5565
|
function LocalSyncWorker(db, cloudOptions, cloudSchema) {
|
|
5462
5566
|
let localSyncEventSubscription = null;
|
|
5463
5567
|
//let syncHandler: ((event: Event) => void) | null = null;
|
|
5464
5568
|
//let periodicSyncHandler: ((event: Event) => void) | null = null;
|
|
5465
5569
|
let cancelToken = { cancelled: false };
|
|
5570
|
+
let retryHandle = null;
|
|
5571
|
+
let retryPurpose = null; // "pull" is superset of "push"
|
|
5466
5572
|
function syncAndRetry(purpose, retryNum = 1) {
|
|
5467
5573
|
// Use setTimeout() to get onto a clean stack and
|
|
5468
5574
|
// break free from possible active transaction:
|
|
5469
5575
|
setTimeout(() => {
|
|
5576
|
+
if (retryHandle)
|
|
5577
|
+
clearTimeout(retryHandle);
|
|
5578
|
+
const combPurpose = retryPurpose === 'pull' ? 'pull' : purpose;
|
|
5579
|
+
retryHandle = null;
|
|
5580
|
+
retryPurpose = null;
|
|
5470
5581
|
syncIfPossible(db, cloudOptions, cloudSchema, {
|
|
5471
5582
|
cancelToken,
|
|
5472
5583
|
retryImmediatelyOnFetchError: true,
|
|
5473
|
-
purpose,
|
|
5584
|
+
purpose: combPurpose,
|
|
5474
5585
|
}).catch((e) => {
|
|
5475
5586
|
console.error('error in syncIfPossible()', e);
|
|
5476
5587
|
if (cancelToken.cancelled) {
|
|
@@ -5480,7 +5591,13 @@
|
|
|
5480
5591
|
// Mimic service worker sync event: retry 3 times
|
|
5481
5592
|
// * first retry after 5 minutes
|
|
5482
5593
|
// * second retry 15 minutes later
|
|
5483
|
-
|
|
5594
|
+
const combinedPurpose = retryPurpose && retryPurpose === 'pull' ? 'pull' : purpose;
|
|
5595
|
+
const handle = setTimeout(() => syncAndRetry(combinedPurpose, retryNum + 1), [0, 5, 15][retryNum] * MINUTES);
|
|
5596
|
+
// Cancel the previous retryHandle if it exists to avoid scheduling loads of retries.
|
|
5597
|
+
if (retryHandle)
|
|
5598
|
+
clearTimeout(retryHandle);
|
|
5599
|
+
retryHandle = handle;
|
|
5600
|
+
retryPurpose = combinedPurpose;
|
|
5484
5601
|
}
|
|
5485
5602
|
});
|
|
5486
5603
|
}, 0);
|
|
@@ -5547,10 +5664,12 @@
|
|
|
5547
5664
|
},
|
|
5548
5665
|
Alert: {
|
|
5549
5666
|
error: {
|
|
5550
|
-
color: "red"
|
|
5667
|
+
color: "red",
|
|
5668
|
+
fontWeight: "bold"
|
|
5551
5669
|
},
|
|
5552
5670
|
warning: {
|
|
5553
|
-
color: "
|
|
5671
|
+
color: "#f80",
|
|
5672
|
+
fontWeight: "bold"
|
|
5554
5673
|
},
|
|
5555
5674
|
info: {
|
|
5556
5675
|
color: "black"
|
|
@@ -5591,7 +5710,8 @@
|
|
|
5591
5710
|
border: "3px solid #3d3d5d",
|
|
5592
5711
|
borderRadius: "8px",
|
|
5593
5712
|
boxShadow: "0 0 80px 10px #666",
|
|
5594
|
-
width: "auto"
|
|
5713
|
+
width: "auto",
|
|
5714
|
+
fontFamily: "sans-serif",
|
|
5595
5715
|
},
|
|
5596
5716
|
Input: {
|
|
5597
5717
|
height: "35px",
|
|
@@ -5612,11 +5732,26 @@
|
|
|
5612
5732
|
|
|
5613
5733
|
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}
|
|
5614
5734
|
|
|
5735
|
+
/** Resolve a message template with parameters.
|
|
5736
|
+
*
|
|
5737
|
+
* Example:
|
|
5738
|
+
* resolveText({
|
|
5739
|
+
* message: "Hello {name}!",
|
|
5740
|
+
* messageCode: "HELLO",
|
|
5741
|
+
* messageParams: {name: "David"}
|
|
5742
|
+
* }) => "Hello David!"
|
|
5743
|
+
*
|
|
5744
|
+
* @param message Template message with {vars} in it.
|
|
5745
|
+
* @param messageCode Unique code for the message. Can be used for translation.
|
|
5746
|
+
* @param messageParams Parameters to be used in the message.
|
|
5747
|
+
* @returns A final message where parameters have been replaced with values.
|
|
5748
|
+
*/
|
|
5615
5749
|
function resolveText({ message, messageCode, messageParams }) {
|
|
5616
|
-
return message.replace(/\{\w+\}/ig, n => messageParams[n.
|
|
5750
|
+
return message.replace(/\{\w+\}/ig, n => messageParams[n.substring(1, n.length - 1)]);
|
|
5617
5751
|
}
|
|
5618
5752
|
|
|
5619
|
-
|
|
5753
|
+
const OTP_LENGTH = 8;
|
|
5754
|
+
function LoginDialog({ title, type, alerts, fields, submitLabel, cancelLabel, onCancel, onSubmit, }) {
|
|
5620
5755
|
const [params, setParams] = p({});
|
|
5621
5756
|
const firstFieldRef = _(null);
|
|
5622
5757
|
s(() => { var _a; return (_a = firstFieldRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, []);
|
|
@@ -5624,21 +5759,34 @@
|
|
|
5624
5759
|
h(p$1, null,
|
|
5625
5760
|
h("h3", { style: Styles.WindowHeader }, title),
|
|
5626
5761
|
alerts.map((alert) => (h("p", { style: Styles.Alert[alert.type] }, resolveText(alert)))),
|
|
5627
|
-
h("form", { onSubmit: ev => {
|
|
5762
|
+
h("form", { onSubmit: (ev) => {
|
|
5628
5763
|
ev.preventDefault();
|
|
5629
5764
|
onSubmit(params);
|
|
5630
|
-
} }, Object.entries(fields).map(([fieldName, { type, label, placeholder }], idx) => (h("label", { style: Styles.Label },
|
|
5765
|
+
} }, Object.entries(fields).map(([fieldName, { type, label, placeholder }], idx) => (h("label", { style: Styles.Label, key: idx },
|
|
5631
5766
|
label ? `${label}: ` : '',
|
|
5632
|
-
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) => {
|
|
5767
|
+
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) => {
|
|
5768
|
+
var _a;
|
|
5769
|
+
const value = valueTransformer(type, (_a = ev.target) === null || _a === void 0 ? void 0 : _a['value']);
|
|
5770
|
+
let updatedParams = Object.assign(Object.assign({}, params), { [fieldName]: value });
|
|
5771
|
+
setParams(updatedParams);
|
|
5772
|
+
if (type === 'otp' && (value === null || value === void 0 ? void 0 : value.trim().length) === OTP_LENGTH) {
|
|
5773
|
+
// Auto-submit when OTP is filled in.
|
|
5774
|
+
onSubmit(updatedParams);
|
|
5775
|
+
}
|
|
5776
|
+
} })))))),
|
|
5633
5777
|
h("div", { style: Styles.ButtonsDiv },
|
|
5634
|
-
h(
|
|
5635
|
-
|
|
5778
|
+
h(p$1, null,
|
|
5779
|
+
h("button", { type: "submit", style: Styles.Button, onClick: () => onSubmit(params) }, submitLabel),
|
|
5780
|
+
cancelLabel && (h("button", { style: Styles.Button, onClick: onCancel }, cancelLabel))))));
|
|
5636
5781
|
}
|
|
5637
5782
|
function valueTransformer(type, value) {
|
|
5638
5783
|
switch (type) {
|
|
5639
|
-
case
|
|
5640
|
-
|
|
5641
|
-
|
|
5784
|
+
case 'email':
|
|
5785
|
+
return value.toLowerCase();
|
|
5786
|
+
case 'otp':
|
|
5787
|
+
return value.toUpperCase();
|
|
5788
|
+
default:
|
|
5789
|
+
return value;
|
|
5642
5790
|
}
|
|
5643
5791
|
}
|
|
5644
5792
|
|
|
@@ -5692,11 +5840,20 @@
|
|
|
5692
5840
|
}
|
|
5693
5841
|
};
|
|
5694
5842
|
}
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5843
|
+
|
|
5844
|
+
function associate(factory) {
|
|
5845
|
+
const wm = new WeakMap();
|
|
5846
|
+
return (x) => {
|
|
5847
|
+
let rv = wm.get(x);
|
|
5848
|
+
if (!rv) {
|
|
5849
|
+
rv = factory(x);
|
|
5850
|
+
wm.set(x, rv);
|
|
5851
|
+
}
|
|
5852
|
+
return rv;
|
|
5853
|
+
};
|
|
5854
|
+
}
|
|
5855
|
+
|
|
5856
|
+
const getCurrentUserEmitter = associate((db) => new rxjs.BehaviorSubject(UNAUTHORIZED_USER));
|
|
5700
5857
|
|
|
5701
5858
|
function computeSyncState(db) {
|
|
5702
5859
|
let _prevStatus = db.cloud.webSocketStatus.value;
|
|
@@ -5724,8 +5881,17 @@
|
|
|
5724
5881
|
return rxjs.combineLatest([
|
|
5725
5882
|
lazyWebSocketStatus,
|
|
5726
5883
|
db.syncStateChangedEvent.pipe(startWith({ phase: 'initial' })),
|
|
5884
|
+
getCurrentUserEmitter(db.dx._novip),
|
|
5727
5885
|
userIsReallyActive
|
|
5728
|
-
]).pipe(map(([status, syncState, userIsActive]) => {
|
|
5886
|
+
]).pipe(map(([status, syncState, user, userIsActive]) => {
|
|
5887
|
+
var _a;
|
|
5888
|
+
if (((_a = user.license) === null || _a === void 0 ? void 0 : _a.status) && user.license.status !== 'ok') {
|
|
5889
|
+
return {
|
|
5890
|
+
phase: 'offline',
|
|
5891
|
+
status: 'offline',
|
|
5892
|
+
license: user.license.status
|
|
5893
|
+
};
|
|
5894
|
+
}
|
|
5729
5895
|
let { phase, error, progress } = syncState;
|
|
5730
5896
|
let adjustedStatus = status;
|
|
5731
5897
|
if (phase === 'error') {
|
|
@@ -5758,23 +5924,12 @@
|
|
|
5758
5924
|
error,
|
|
5759
5925
|
progress,
|
|
5760
5926
|
status: isOnline ? adjustedStatus : 'offline',
|
|
5927
|
+
license: 'ok'
|
|
5761
5928
|
};
|
|
5762
5929
|
return retState;
|
|
5763
5930
|
}));
|
|
5764
5931
|
}
|
|
5765
5932
|
|
|
5766
|
-
function associate(factory) {
|
|
5767
|
-
const wm = new WeakMap();
|
|
5768
|
-
return (x) => {
|
|
5769
|
-
let rv = wm.get(x);
|
|
5770
|
-
if (!rv) {
|
|
5771
|
-
rv = factory(x);
|
|
5772
|
-
wm.set(x, rv);
|
|
5773
|
-
}
|
|
5774
|
-
return rv;
|
|
5775
|
-
};
|
|
5776
|
-
}
|
|
5777
|
-
|
|
5778
5933
|
function createSharedValueObservable(o, defaultValue) {
|
|
5779
5934
|
let currentValue = defaultValue;
|
|
5780
5935
|
let shared = rxjs.from(o).pipe(rxjs.map((x) => (currentValue = x)), rxjs.share({ resetOnRefCountZero: () => rxjs.timer(1000) }));
|
|
@@ -5816,8 +5971,6 @@
|
|
|
5816
5971
|
})), {});
|
|
5817
5972
|
});
|
|
5818
5973
|
|
|
5819
|
-
const getCurrentUserEmitter = associate((db) => new rxjs.BehaviorSubject(UNAUTHORIZED_USER));
|
|
5820
|
-
|
|
5821
5974
|
const getInternalAccessControlObservable = associate((db) => {
|
|
5822
5975
|
return createSharedValueObservable(getCurrentUserEmitter(db._novip).pipe(switchMap((currentUser) => Dexie.liveQuery(() => db.transaction('r', 'realms', 'members', () => Promise.all([
|
|
5823
5976
|
db.members.where({ userId: currentUser.userId }).toArray(),
|
|
@@ -6107,7 +6260,7 @@
|
|
|
6107
6260
|
});
|
|
6108
6261
|
const syncComplete = new rxjs.Subject();
|
|
6109
6262
|
dexie.cloud = {
|
|
6110
|
-
version: '4.0.1-beta.
|
|
6263
|
+
version: '4.0.1-beta.47',
|
|
6111
6264
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
6112
6265
|
schema: null,
|
|
6113
6266
|
get currentUserId() {
|
|
@@ -6143,11 +6296,24 @@
|
|
|
6143
6296
|
}
|
|
6144
6297
|
updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
|
|
6145
6298
|
},
|
|
6299
|
+
logout({ force } = {}) {
|
|
6300
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6301
|
+
force
|
|
6302
|
+
? yield _logout(DexieCloudDB(dexie), { deleteUnsyncedData: true })
|
|
6303
|
+
: yield logout(DexieCloudDB(dexie));
|
|
6304
|
+
});
|
|
6305
|
+
},
|
|
6146
6306
|
sync({ wait, purpose } = { wait: true, purpose: 'push' }) {
|
|
6307
|
+
var _a;
|
|
6147
6308
|
return __awaiter(this, void 0, void 0, function* () {
|
|
6148
6309
|
if (wait === undefined)
|
|
6149
6310
|
wait = true;
|
|
6150
6311
|
const db = DexieCloudDB(dexie);
|
|
6312
|
+
const licenseStatus = ((_a = db.cloud.currentUser.value.license) === null || _a === void 0 ? void 0 : _a.status) || 'ok';
|
|
6313
|
+
if (licenseStatus !== 'ok') {
|
|
6314
|
+
// Refresh access token to check for updated license
|
|
6315
|
+
yield loadAccessToken(db);
|
|
6316
|
+
}
|
|
6151
6317
|
if (purpose === 'pull') {
|
|
6152
6318
|
const syncState = db.cloud.persistedSyncState.value;
|
|
6153
6319
|
triggerSync(db, purpose);
|
|
@@ -6351,7 +6517,9 @@
|
|
|
6351
6517
|
db.syncStateChangedEvent.next({
|
|
6352
6518
|
phase: 'not-in-sync',
|
|
6353
6519
|
});
|
|
6354
|
-
|
|
6520
|
+
if (!isEagerSyncDisabled(db)) {
|
|
6521
|
+
triggerSync(db, 'push');
|
|
6522
|
+
}
|
|
6355
6523
|
}), rxjs.fromEvent(self, 'offline').subscribe(() => {
|
|
6356
6524
|
console.debug('offline!');
|
|
6357
6525
|
db.syncStateChangedEvent.next({
|
|
@@ -6368,7 +6536,7 @@
|
|
|
6368
6536
|
});
|
|
6369
6537
|
}
|
|
6370
6538
|
}
|
|
6371
|
-
dexieCloud.version = '4.0.1-beta.
|
|
6539
|
+
dexieCloud.version = '4.0.1-beta.47';
|
|
6372
6540
|
Dexie__default["default"].Cloud = dexieCloud;
|
|
6373
6541
|
|
|
6374
6542
|
exports["default"] = dexieCloud;
|