dexie-cloud-addon 4.0.0-beta.17 → 4.0.0-beta.20

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.
Files changed (42) hide show
  1. package/dist/modern/dexie-cloud-addon.js +1630 -1572
  2. package/dist/modern/dexie-cloud-addon.js.map +1 -1
  3. package/dist/modern/dexie-cloud-addon.min.js +1 -1
  4. package/dist/modern/dexie-cloud-addon.min.js.map +1 -1
  5. package/dist/modern/service-worker.js +1694 -1630
  6. package/dist/modern/service-worker.js.map +1 -1
  7. package/dist/modern/service-worker.min.js +1 -1
  8. package/dist/modern/service-worker.min.js.map +1 -1
  9. package/dist/module-es5/dexie-cloud-addon.js +701 -662
  10. package/dist/module-es5/dexie-cloud-addon.js.map +1 -1
  11. package/dist/module-es5/dexie-cloud-addon.min.js +1 -1
  12. package/dist/module-es5/dexie-cloud-addon.min.js.map +1 -1
  13. package/dist/types/DexieCloudAPI.d.ts +1 -0
  14. package/dist/types/DexieCloudEntity.d.ts +8 -0
  15. package/dist/types/DexieCloudOptions.d.ts +1 -0
  16. package/dist/types/WebSocketStatus.d.ts +1 -0
  17. package/dist/types/createMyMembersObservable.d.ts +14 -0
  18. package/dist/types/currentUserObservable.d.ts +3 -0
  19. package/dist/types/default-ui/Dialog.d.ts +2 -1
  20. package/dist/types/helpers/BroadcastedLocalEvent.d.ts +8 -0
  21. package/dist/types/helpers/visibleState.d.ts +1 -0
  22. package/dist/types/permissionsLookup.d.ts +9 -0
  23. package/dist/types/permissionsLookupObservable.d.ts +14 -0
  24. package/dist/types/sync/globalizePrivateIds.d.ts +4 -0
  25. package/dist/types/sync/syncServerToClientOnly.d.ts +3 -0
  26. package/dist/types/types/CloudConnectionStatus.d.ts +0 -0
  27. package/dist/types/types/ConnectionStatus.d.ts +0 -0
  28. package/dist/types/types/LoginState.d.ts +41 -0
  29. package/dist/types/types/SyncConnectionStatus.d.ts +1 -0
  30. package/dist/types/types/SyncFlowStatus.d.ts +6 -0
  31. package/dist/types/types/SyncStatus.d.ts +6 -0
  32. package/dist/umd/dexie-cloud-addon.js +701 -662
  33. package/dist/umd/dexie-cloud-addon.js.map +1 -1
  34. package/dist/umd/dexie-cloud-addon.min.js +1 -1
  35. package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
  36. package/dist/umd/service-worker.js +1694 -1630
  37. package/dist/umd/service-worker.js.map +1 -1
  38. package/dist/umd/service-worker.min.js +1 -1
  39. package/dist/umd/service-worker.min.js.map +1 -1
  40. package/dist/umd-modern/dexie-cloud-addon.js +1627 -1569
  41. package/dist/umd-modern/dexie-cloud-addon.js.map +1 -1
  42. package/package.json +2 -2
@@ -26,6 +26,31 @@
26
26
 
27
27
  var Dexie__default = /*#__PURE__*/_interopDefaultLegacy(Dexie);
28
28
 
29
+ /*! *****************************************************************************
30
+ Copyright (c) Microsoft Corporation.
31
+
32
+ Permission to use, copy, modify, and/or distribute this software for any
33
+ purpose with or without fee is hereby granted.
34
+
35
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
36
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
37
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
38
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
39
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
40
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
41
+ PERFORMANCE OF THIS SOFTWARE.
42
+ ***************************************************************************** */
43
+
44
+ function __awaiter$1(thisArg, _arguments, P, generator) {
45
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
46
+ return new (P || (P = Promise))(function (resolve, reject) {
47
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
48
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
49
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
50
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
51
+ });
52
+ }
53
+
29
54
  //@ts-check
30
55
  const randomFillSync = crypto.getRandomValues;
31
56
 
@@ -1909,55 +1934,60 @@
1909
1934
 
1910
1935
  //const hasSW = 'serviceWorker' in navigator;
1911
1936
  let hasComplainedAboutSyncEvent = false;
1912
- async function registerSyncEvent(db, purpose) {
1913
- try {
1914
- // Send sync event to SW:
1915
- const sw = await navigator.serviceWorker.ready;
1916
- if (purpose === "push" && sw.sync) {
1917
- await sw.sync.register(`dexie-cloud:${db.name}`);
1918
- }
1919
- if (sw.active) {
1920
- // Use postMessage for pull syncs and for browsers not supporting sync event (Firefox, Safari).
1921
- // Also chromium based browsers with sw.sync as a fallback for sleepy sync events not taking action for a while.
1922
- sw.active.postMessage({
1923
- type: 'dexie-cloud-sync',
1924
- dbName: db.name,
1925
- purpose
1926
- });
1927
- }
1928
- else {
1929
- throw new Error(`Failed to trigger sync - there's no active service worker`);
1937
+ function registerSyncEvent(db, purpose) {
1938
+ return __awaiter$1(this, void 0, void 0, function* () {
1939
+ try {
1940
+ // Send sync event to SW:
1941
+ const sw = yield navigator.serviceWorker.ready;
1942
+ if (purpose === "push" && sw.sync) {
1943
+ yield sw.sync.register(`dexie-cloud:${db.name}`);
1944
+ }
1945
+ if (sw.active) {
1946
+ // Use postMessage for pull syncs and for browsers not supporting sync event (Firefox, Safari).
1947
+ // Also chromium based browsers with sw.sync as a fallback for sleepy sync events not taking action for a while.
1948
+ sw.active.postMessage({
1949
+ type: 'dexie-cloud-sync',
1950
+ dbName: db.name,
1951
+ purpose
1952
+ });
1953
+ }
1954
+ else {
1955
+ throw new Error(`Failed to trigger sync - there's no active service worker`);
1956
+ }
1957
+ return;
1930
1958
  }
1931
- return;
1932
- }
1933
- catch (e) {
1934
- if (!hasComplainedAboutSyncEvent) {
1935
- console.debug(`Dexie Cloud: Could not register sync event`, e);
1936
- hasComplainedAboutSyncEvent = true;
1959
+ catch (e) {
1960
+ if (!hasComplainedAboutSyncEvent) {
1961
+ console.debug(`Dexie Cloud: Could not register sync event`, e);
1962
+ hasComplainedAboutSyncEvent = true;
1963
+ }
1937
1964
  }
1938
- }
1965
+ });
1939
1966
  }
1940
- async function registerPeriodicSyncEvent(db) {
1941
- try {
1942
- // Register periodicSync event to SW:
1943
- // @ts-ignore
1944
- const { periodicSync } = await navigator.serviceWorker.ready;
1945
- if (periodicSync) {
1946
- try {
1947
- await periodicSync.register(`dexie-cloud:${db.name}`, db.cloud.options?.periodicSync);
1948
- console.debug(`Dexie Cloud: Successfully registered periodicsync event for ${db.name}`);
1967
+ function registerPeriodicSyncEvent(db) {
1968
+ var _a;
1969
+ return __awaiter$1(this, void 0, void 0, function* () {
1970
+ try {
1971
+ // Register periodicSync event to SW:
1972
+ // @ts-ignore
1973
+ const { periodicSync } = yield navigator.serviceWorker.ready;
1974
+ if (periodicSync) {
1975
+ try {
1976
+ yield periodicSync.register(`dexie-cloud:${db.name}`, (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.periodicSync);
1977
+ console.debug(`Dexie Cloud: Successfully registered periodicsync event for ${db.name}`);
1978
+ }
1979
+ catch (e) {
1980
+ console.debug(`Dexie Cloud: Failed to register periodic sync. Your PWA must be installed to allow background sync.`, e);
1981
+ }
1949
1982
  }
1950
- catch (e) {
1951
- console.debug(`Dexie Cloud: Failed to register periodic sync. Your PWA must be installed to allow background sync.`, e);
1983
+ else {
1984
+ console.debug(`Dexie Cloud: periodicSync not supported.`);
1952
1985
  }
1953
1986
  }
1954
- else {
1955
- console.debug(`Dexie Cloud: periodicSync not supported.`);
1987
+ catch (e) {
1988
+ console.debug(`Dexie Cloud: Could not register periodicSync for ${db.name}`, e);
1956
1989
  }
1957
- }
1958
- catch (e) {
1959
- console.debug(`Dexie Cloud: Could not register periodicSync for ${db.name}`, e);
1960
- }
1990
+ });
1961
1991
  }
1962
1992
 
1963
1993
  function triggerSync(db, purpose) {
@@ -1989,19 +2019,15 @@
1989
2019
  function interactWithUser(userInteraction, req) {
1990
2020
  let done = false;
1991
2021
  return new Promise((resolve, reject) => {
1992
- const interactionProps = {
1993
- ...req,
1994
- onSubmit: (res) => {
2022
+ const interactionProps = Object.assign(Object.assign({}, req), { onSubmit: (res) => {
1995
2023
  userInteraction.next(undefined);
1996
2024
  done = true;
1997
2025
  resolve(res);
1998
- },
1999
- onCancel: () => {
2026
+ }, onCancel: () => {
2000
2027
  userInteraction.next(undefined);
2001
2028
  done = true;
2002
2029
  reject(new Dexie__default["default"].AbortError("User cancelled"));
2003
- },
2004
- };
2030
+ } });
2005
2031
  userInteraction.next(interactionProps);
2006
2032
  // Start subscribing for external updates to db.cloud.userInteraction, and if so, cancel this request.
2007
2033
  /*const subscription = userInteraction.subscribe((currentInteractionProps) => {
@@ -2022,180 +2048,193 @@
2022
2048
  fields: {}
2023
2049
  });
2024
2050
  }
2025
- async function promptForEmail(userInteraction, title, emailHint) {
2026
- let email = emailHint || '';
2027
- while (!email || !/^[\w-\.]+@([\w-]+\.)+[\w-]{2,10}$/.test(email)) {
2028
- email = (await interactWithUser(userInteraction, {
2029
- type: 'email',
2030
- title,
2031
- alerts: email
2032
- ? [
2033
- {
2034
- type: 'error',
2035
- messageCode: 'INVALID_EMAIL',
2036
- message: 'Please enter a valid email address',
2037
- messageParams: {},
2051
+ function promptForEmail(userInteraction, title, emailHint) {
2052
+ return __awaiter$1(this, void 0, void 0, function* () {
2053
+ let email = emailHint || '';
2054
+ while (!email || !/^[\w-\.]+@([\w-]+\.)+[\w-]{2,10}$/.test(email)) {
2055
+ email = (yield interactWithUser(userInteraction, {
2056
+ type: 'email',
2057
+ title,
2058
+ alerts: email
2059
+ ? [
2060
+ {
2061
+ type: 'error',
2062
+ messageCode: 'INVALID_EMAIL',
2063
+ message: 'Please enter a valid email address',
2064
+ messageParams: {},
2065
+ },
2066
+ ]
2067
+ : [],
2068
+ fields: {
2069
+ email: {
2070
+ type: 'email',
2071
+ placeholder: 'you@somedomain.com',
2038
2072
  },
2039
- ]
2040
- : [],
2041
- fields: {
2042
- email: {
2043
- type: 'email',
2044
- placeholder: 'you@somedomain.com',
2045
2073
  },
2046
- },
2047
- })).email;
2048
- }
2049
- return email;
2074
+ })).email;
2075
+ }
2076
+ return email;
2077
+ });
2050
2078
  }
2051
- async function promptForOTP(userInteraction, email, alert) {
2052
- const alerts = [
2053
- {
2054
- type: 'info',
2055
- messageCode: 'OTP_SENT',
2056
- message: `A One-Time password has been sent to {email}`,
2057
- messageParams: { email },
2058
- },
2059
- ];
2060
- if (alert) {
2061
- alerts.push(alert);
2062
- }
2063
- const { otp } = await interactWithUser(userInteraction, {
2064
- type: 'otp',
2065
- title: 'Enter OTP',
2066
- alerts,
2067
- fields: {
2068
- otp: {
2069
- type: 'otp',
2070
- label: 'OTP',
2071
- placeholder: 'Paste OTP here',
2079
+ function promptForOTP(userInteraction, email, alert) {
2080
+ return __awaiter$1(this, void 0, void 0, function* () {
2081
+ const alerts = [
2082
+ {
2083
+ type: 'info',
2084
+ messageCode: 'OTP_SENT',
2085
+ message: `A One-Time password has been sent to {email}`,
2086
+ messageParams: { email },
2072
2087
  },
2073
- },
2088
+ ];
2089
+ if (alert) {
2090
+ alerts.push(alert);
2091
+ }
2092
+ const { otp } = yield interactWithUser(userInteraction, {
2093
+ type: 'otp',
2094
+ title: 'Enter OTP',
2095
+ alerts,
2096
+ fields: {
2097
+ otp: {
2098
+ type: 'otp',
2099
+ label: 'OTP',
2100
+ placeholder: 'Paste OTP here',
2101
+ },
2102
+ },
2103
+ });
2104
+ return otp;
2074
2105
  });
2075
- return otp;
2076
2106
  }
2077
2107
 
2078
- async function loadAccessToken(db) {
2079
- const currentUser = await db.getCurrentUser();
2080
- const { accessToken, accessTokenExpiration, refreshToken, refreshTokenExpiration, claims, } = currentUser;
2081
- if (!accessToken)
2082
- return;
2083
- const expTime = accessTokenExpiration?.getTime() ?? Infinity;
2084
- if (expTime > Date.now()) {
2085
- return accessToken;
2086
- }
2087
- if (!refreshToken) {
2088
- throw new Error(`Refresh token missing`);
2089
- }
2090
- const refreshExpTime = refreshTokenExpiration?.getTime() ?? Infinity;
2091
- if (refreshExpTime <= Date.now()) {
2092
- throw new Error(`Refresh token has expired`);
2093
- }
2094
- const refreshedLogin = await refreshAccessToken(db.cloud.options.databaseUrl, currentUser);
2095
- await db.table('$logins').update(claims.sub, {
2096
- accessToken: refreshedLogin.accessToken,
2097
- accessTokenExpiration: refreshedLogin.accessTokenExpiration,
2108
+ function loadAccessToken(db) {
2109
+ var _a, _b;
2110
+ return __awaiter$1(this, void 0, void 0, function* () {
2111
+ const currentUser = yield db.getCurrentUser();
2112
+ const { accessToken, accessTokenExpiration, refreshToken, refreshTokenExpiration, claims, } = currentUser;
2113
+ if (!accessToken)
2114
+ return;
2115
+ const expTime = (_a = accessTokenExpiration === null || accessTokenExpiration === void 0 ? void 0 : accessTokenExpiration.getTime()) !== null && _a !== void 0 ? _a : Infinity;
2116
+ if (expTime > Date.now()) {
2117
+ return accessToken;
2118
+ }
2119
+ if (!refreshToken) {
2120
+ throw new Error(`Refresh token missing`);
2121
+ }
2122
+ const refreshExpTime = (_b = refreshTokenExpiration === null || refreshTokenExpiration === void 0 ? void 0 : refreshTokenExpiration.getTime()) !== null && _b !== void 0 ? _b : Infinity;
2123
+ if (refreshExpTime <= Date.now()) {
2124
+ throw new Error(`Refresh token has expired`);
2125
+ }
2126
+ const refreshedLogin = yield refreshAccessToken(db.cloud.options.databaseUrl, currentUser);
2127
+ yield db.table('$logins').update(claims.sub, {
2128
+ accessToken: refreshedLogin.accessToken,
2129
+ accessTokenExpiration: refreshedLogin.accessTokenExpiration,
2130
+ });
2131
+ return refreshedLogin.accessToken;
2098
2132
  });
2099
- return refreshedLogin.accessToken;
2100
- }
2101
- async function authenticate(url, context, fetchToken, userInteraction, hints) {
2102
- if (context.accessToken &&
2103
- context.accessTokenExpiration.getTime() > Date.now()) {
2104
- return context;
2105
- }
2106
- else if (context.refreshToken &&
2107
- (!context.refreshTokenExpiration ||
2108
- context.refreshTokenExpiration.getTime() > Date.now())) {
2109
- return await refreshAccessToken(url, context);
2110
- }
2111
- else {
2112
- return await userAuthenticate(context, fetchToken, userInteraction, hints);
2113
- }
2114
2133
  }
2115
- async function refreshAccessToken(url, login) {
2116
- if (!login.refreshToken)
2117
- throw new Error(`Cannot refresh token - refresh token is missing.`);
2118
- if (!login.nonExportablePrivateKey)
2119
- throw new Error(`login.nonExportablePrivateKey is missing - cannot sign refresh token without a private key.`);
2120
- const time_stamp = Date.now();
2121
- const signing_algorithm = 'RSASSA-PKCS1-v1_5';
2122
- const textEncoder = new TextEncoder();
2123
- const data = textEncoder.encode(login.refreshToken + time_stamp);
2124
- const binarySignature = await crypto.subtle.sign(signing_algorithm, login.nonExportablePrivateKey, data);
2125
- const signature = b64encode(binarySignature);
2126
- const tokenRequest = {
2127
- grant_type: 'refresh_token',
2128
- refresh_token: login.refreshToken,
2129
- scopes: ['ACCESS_DB'],
2130
- signature,
2131
- signing_algorithm,
2132
- time_stamp,
2133
- };
2134
- const res = await fetch(`${url}/token`, {
2135
- body: JSON.stringify(tokenRequest),
2136
- method: 'post',
2137
- headers: { 'Content-Type': 'application/json' },
2138
- mode: 'cors',
2134
+ function authenticate(url, context, fetchToken, userInteraction, hints) {
2135
+ return __awaiter$1(this, void 0, void 0, function* () {
2136
+ if (context.accessToken &&
2137
+ context.accessTokenExpiration.getTime() > Date.now()) {
2138
+ return context;
2139
+ }
2140
+ else if (context.refreshToken &&
2141
+ (!context.refreshTokenExpiration ||
2142
+ context.refreshTokenExpiration.getTime() > Date.now())) {
2143
+ return yield refreshAccessToken(url, context);
2144
+ }
2145
+ else {
2146
+ return yield userAuthenticate(context, fetchToken, userInteraction, hints);
2147
+ }
2139
2148
  });
2140
- if (res.status !== 200)
2141
- throw new Error(`RefreshToken: Status ${res.status} from ${url}/token`);
2142
- const response = await res.json();
2143
- login.accessToken = response.accessToken;
2144
- login.accessTokenExpiration = response.accessTokenExpiration
2145
- ? new Date(response.accessTokenExpiration)
2146
- : undefined;
2147
- return login;
2148
- }
2149
- async function userAuthenticate(context, fetchToken, userInteraction, hints) {
2150
- const { privateKey, publicKey } = await crypto.subtle.generateKey({
2151
- name: 'RSASSA-PKCS1-v1_5',
2152
- modulusLength: 2048,
2153
- publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
2154
- hash: { name: 'SHA-256' },
2155
- }, false, // Non-exportable...
2156
- ['sign', 'verify']);
2157
- if (!privateKey || !publicKey)
2158
- throw new Error(`Could not generate RSA keypair`); // Typings suggest these can be undefined...
2159
- context.nonExportablePrivateKey = privateKey; //...but storable!
2160
- const publicKeySPKI = await crypto.subtle.exportKey('spki', publicKey);
2161
- const publicKeyPEM = spkiToPEM(publicKeySPKI);
2162
- context.publicKey = publicKey;
2163
- try {
2164
- const response2 = await fetchToken({
2165
- public_key: publicKeyPEM,
2166
- hints,
2149
+ }
2150
+ function refreshAccessToken(url, login) {
2151
+ return __awaiter$1(this, void 0, void 0, function* () {
2152
+ if (!login.refreshToken)
2153
+ throw new Error(`Cannot refresh token - refresh token is missing.`);
2154
+ if (!login.nonExportablePrivateKey)
2155
+ throw new Error(`login.nonExportablePrivateKey is missing - cannot sign refresh token without a private key.`);
2156
+ const time_stamp = Date.now();
2157
+ const signing_algorithm = 'RSASSA-PKCS1-v1_5';
2158
+ const textEncoder = new TextEncoder();
2159
+ const data = textEncoder.encode(login.refreshToken + time_stamp);
2160
+ const binarySignature = yield crypto.subtle.sign(signing_algorithm, login.nonExportablePrivateKey, data);
2161
+ const signature = b64encode(binarySignature);
2162
+ const tokenRequest = {
2163
+ grant_type: 'refresh_token',
2164
+ refresh_token: login.refreshToken,
2165
+ scopes: ['ACCESS_DB'],
2166
+ signature,
2167
+ signing_algorithm,
2168
+ time_stamp,
2169
+ };
2170
+ const res = yield fetch(`${url}/token`, {
2171
+ body: JSON.stringify(tokenRequest),
2172
+ method: 'post',
2173
+ headers: { 'Content-Type': 'application/json' },
2174
+ mode: 'cors',
2167
2175
  });
2168
- if (response2.type !== 'tokens')
2169
- throw new Error(`Unexpected response type from token endpoint: ${response2.type}`);
2170
- context.accessToken = response2.accessToken;
2171
- context.accessTokenExpiration = new Date(response2.accessTokenExpiration);
2172
- context.refreshToken = response2.refreshToken;
2173
- if (response2.refreshTokenExpiration) {
2174
- context.refreshTokenExpiration = new Date(response2.refreshTokenExpiration);
2175
- }
2176
- context.userId = response2.claims.sub;
2177
- context.email = response2.claims.email;
2178
- context.name = response2.claims.name;
2179
- context.claims = response2.claims;
2180
- if (response2.alerts && response2.alerts.length > 0) {
2181
- await interactWithUser(userInteraction, {
2182
- type: 'message-alert',
2183
- title: 'Authentication Alert',
2184
- fields: {},
2185
- alerts: response2.alerts,
2176
+ if (res.status !== 200)
2177
+ throw new Error(`RefreshToken: Status ${res.status} from ${url}/token`);
2178
+ const response = yield res.json();
2179
+ login.accessToken = response.accessToken;
2180
+ login.accessTokenExpiration = response.accessTokenExpiration
2181
+ ? new Date(response.accessTokenExpiration)
2182
+ : undefined;
2183
+ return login;
2184
+ });
2185
+ }
2186
+ function userAuthenticate(context, fetchToken, userInteraction, hints) {
2187
+ return __awaiter$1(this, void 0, void 0, function* () {
2188
+ const { privateKey, publicKey } = yield crypto.subtle.generateKey({
2189
+ name: 'RSASSA-PKCS1-v1_5',
2190
+ modulusLength: 2048,
2191
+ publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
2192
+ hash: { name: 'SHA-256' },
2193
+ }, false, // Non-exportable...
2194
+ ['sign', 'verify']);
2195
+ if (!privateKey || !publicKey)
2196
+ throw new Error(`Could not generate RSA keypair`); // Typings suggest these can be undefined...
2197
+ context.nonExportablePrivateKey = privateKey; //...but storable!
2198
+ const publicKeySPKI = yield crypto.subtle.exportKey('spki', publicKey);
2199
+ const publicKeyPEM = spkiToPEM(publicKeySPKI);
2200
+ context.publicKey = publicKey;
2201
+ try {
2202
+ const response2 = yield fetchToken({
2203
+ public_key: publicKeyPEM,
2204
+ hints,
2186
2205
  });
2206
+ if (response2.type !== 'tokens')
2207
+ throw new Error(`Unexpected response type from token endpoint: ${response2.type}`);
2208
+ context.accessToken = response2.accessToken;
2209
+ context.accessTokenExpiration = new Date(response2.accessTokenExpiration);
2210
+ context.refreshToken = response2.refreshToken;
2211
+ if (response2.refreshTokenExpiration) {
2212
+ context.refreshTokenExpiration = new Date(response2.refreshTokenExpiration);
2213
+ }
2214
+ context.userId = response2.claims.sub;
2215
+ context.email = response2.claims.email;
2216
+ context.name = response2.claims.name;
2217
+ context.claims = response2.claims;
2218
+ if (response2.alerts && response2.alerts.length > 0) {
2219
+ yield interactWithUser(userInteraction, {
2220
+ type: 'message-alert',
2221
+ title: 'Authentication Alert',
2222
+ fields: {},
2223
+ alerts: response2.alerts,
2224
+ });
2225
+ }
2226
+ return context;
2187
2227
  }
2188
- return context;
2189
- }
2190
- catch (error) {
2191
- await alertUser(userInteraction, 'Authentication Failed', {
2192
- type: 'error',
2193
- messageCode: 'GENERIC_ERROR',
2194
- message: `We're having a problem authenticating right now.`,
2195
- messageParams: {}
2196
- }).catch(() => { });
2197
- throw error;
2198
- }
2228
+ catch (error) {
2229
+ yield alertUser(userInteraction, 'Authentication Failed', {
2230
+ type: 'error',
2231
+ messageCode: 'GENERIC_ERROR',
2232
+ message: `We're having a problem authenticating right now.`,
2233
+ messageParams: {}
2234
+ }).catch(() => { });
2235
+ throw error;
2236
+ }
2237
+ });
2199
2238
  }
2200
2239
  function spkiToPEM(keydata) {
2201
2240
  const keydataB64 = b64encode(keydata);
@@ -2231,9 +2270,11 @@
2231
2270
  lastLogin: new Date(0)
2232
2271
  }));
2233
2272
  }
2234
- async save() {
2235
- const db = wm$1.get(this);
2236
- db.table("$logins").put(this);
2273
+ save() {
2274
+ return __awaiter$1(this, void 0, void 0, function* () {
2275
+ const db = wm$1.get(this);
2276
+ db.table("$logins").put(this);
2277
+ });
2237
2278
  }
2238
2279
  }
2239
2280
 
@@ -2249,92 +2290,95 @@
2249
2290
 
2250
2291
  function otpFetchTokenCallback(db) {
2251
2292
  const { userInteraction } = db.cloud;
2252
- return async function otpAuthenticate({ public_key, hints }) {
2253
- let tokenRequest;
2254
- const url = db.cloud.options?.databaseUrl;
2255
- if (!url)
2256
- throw new Error(`No database URL given.`);
2257
- if (hints?.grant_type === 'demo') {
2258
- const demo_user = await promptForEmail(userInteraction, 'Enter a demo user email', hints?.email || hints?.userId);
2259
- tokenRequest = {
2260
- demo_user,
2261
- grant_type: 'demo',
2262
- scopes: ['ACCESS_DB'],
2263
- public_key,
2264
- };
2265
- }
2266
- else {
2267
- const email = await promptForEmail(userInteraction, 'Enter email address', hints?.email);
2268
- tokenRequest = {
2269
- email,
2270
- grant_type: 'otp',
2271
- scopes: ['ACCESS_DB'],
2272
- public_key,
2273
- };
2274
- }
2275
- const res1 = await fetch(`${url}/token`, {
2276
- body: JSON.stringify(tokenRequest),
2277
- method: 'post',
2278
- headers: { 'Content-Type': 'application/json', mode: 'cors' },
2279
- });
2280
- if (res1.status !== 200) {
2281
- const errMsg = await res1.text();
2282
- await alertUser(userInteraction, "Token request failed", {
2283
- type: 'error',
2284
- messageCode: 'GENERIC_ERROR',
2285
- message: errMsg,
2286
- messageParams: {}
2287
- }).catch(() => { });
2288
- throw new HttpError(res1, errMsg);
2289
- }
2290
- const response = await res1.json();
2291
- if (response.type === 'tokens') {
2292
- // Demo user request can get a "tokens" response right away
2293
- return response;
2294
- }
2295
- else if (tokenRequest.grant_type === 'otp') {
2296
- if (response.type !== 'otp-sent')
2297
- throw new Error(`Unexpected response from ${url}/token`);
2298
- const otp = await promptForOTP(userInteraction, tokenRequest.email);
2299
- tokenRequest.otp = otp || '';
2300
- tokenRequest.otp_id = response.otp_id;
2301
- let res2 = await fetch(`${url}/token`, {
2293
+ return function otpAuthenticate({ public_key, hints }) {
2294
+ var _a;
2295
+ return __awaiter$1(this, void 0, void 0, function* () {
2296
+ let tokenRequest;
2297
+ const url = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl;
2298
+ if (!url)
2299
+ throw new Error(`No database URL given.`);
2300
+ if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'demo') {
2301
+ const demo_user = yield promptForEmail(userInteraction, 'Enter a demo user email', (hints === null || hints === void 0 ? void 0 : hints.email) || (hints === null || hints === void 0 ? void 0 : hints.userId));
2302
+ tokenRequest = {
2303
+ demo_user,
2304
+ grant_type: 'demo',
2305
+ scopes: ['ACCESS_DB'],
2306
+ public_key,
2307
+ };
2308
+ }
2309
+ else {
2310
+ const email = yield promptForEmail(userInteraction, 'Enter email address', hints === null || hints === void 0 ? void 0 : hints.email);
2311
+ tokenRequest = {
2312
+ email,
2313
+ grant_type: 'otp',
2314
+ scopes: ['ACCESS_DB'],
2315
+ public_key,
2316
+ };
2317
+ }
2318
+ const res1 = yield fetch(`${url}/token`, {
2302
2319
  body: JSON.stringify(tokenRequest),
2303
2320
  method: 'post',
2304
- headers: { 'Content-Type': 'application/json' },
2305
- mode: 'cors',
2321
+ headers: { 'Content-Type': 'application/json', mode: 'cors' },
2306
2322
  });
2307
- while (res2.status === 401) {
2308
- const errorText = await res2.text();
2309
- tokenRequest.otp = await promptForOTP(userInteraction, tokenRequest.email, {
2323
+ if (res1.status !== 200) {
2324
+ const errMsg = yield res1.text();
2325
+ yield alertUser(userInteraction, "Token request failed", {
2310
2326
  type: 'error',
2311
- messageCode: 'INVALID_OTP',
2312
- message: errorText,
2327
+ messageCode: 'GENERIC_ERROR',
2328
+ message: errMsg,
2313
2329
  messageParams: {}
2314
- });
2315
- res2 = await fetch(`${url}/token`, {
2330
+ }).catch(() => { });
2331
+ throw new HttpError(res1, errMsg);
2332
+ }
2333
+ const response = yield res1.json();
2334
+ if (response.type === 'tokens') {
2335
+ // Demo user request can get a "tokens" response right away
2336
+ return response;
2337
+ }
2338
+ else if (tokenRequest.grant_type === 'otp') {
2339
+ if (response.type !== 'otp-sent')
2340
+ throw new Error(`Unexpected response from ${url}/token`);
2341
+ const otp = yield promptForOTP(userInteraction, tokenRequest.email);
2342
+ tokenRequest.otp = otp || '';
2343
+ tokenRequest.otp_id = response.otp_id;
2344
+ let res2 = yield fetch(`${url}/token`, {
2316
2345
  body: JSON.stringify(tokenRequest),
2317
2346
  method: 'post',
2318
2347
  headers: { 'Content-Type': 'application/json' },
2319
2348
  mode: 'cors',
2320
2349
  });
2350
+ while (res2.status === 401) {
2351
+ const errorText = yield res2.text();
2352
+ tokenRequest.otp = yield promptForOTP(userInteraction, tokenRequest.email, {
2353
+ type: 'error',
2354
+ messageCode: 'INVALID_OTP',
2355
+ message: errorText,
2356
+ messageParams: {}
2357
+ });
2358
+ res2 = yield fetch(`${url}/token`, {
2359
+ body: JSON.stringify(tokenRequest),
2360
+ method: 'post',
2361
+ headers: { 'Content-Type': 'application/json' },
2362
+ mode: 'cors',
2363
+ });
2364
+ }
2365
+ if (res2.status !== 200) {
2366
+ const errMsg = yield res2.text();
2367
+ yield alertUser(userInteraction, "OTP Authentication Failed", {
2368
+ type: 'error',
2369
+ messageCode: 'GENERIC_ERROR',
2370
+ message: errMsg,
2371
+ messageParams: {}
2372
+ }).catch(() => { });
2373
+ throw new HttpError(res2, errMsg);
2374
+ }
2375
+ const response2 = yield res2.json();
2376
+ return response2;
2321
2377
  }
2322
- if (res2.status !== 200) {
2323
- const errMsg = await res2.text();
2324
- await alertUser(userInteraction, "OTP Authentication Failed", {
2325
- type: 'error',
2326
- messageCode: 'GENERIC_ERROR',
2327
- message: errMsg,
2328
- messageParams: {}
2329
- }).catch(() => { });
2330
- throw new HttpError(res2, errMsg);
2378
+ else {
2379
+ throw new Error(`Unexpected response from ${url}/token`);
2331
2380
  }
2332
- const response2 = await res2.json();
2333
- return response2;
2334
- }
2335
- else {
2336
- throw new Error(`Unexpected response from ${url}/token`);
2337
- }
2381
+ });
2338
2382
  };
2339
2383
  }
2340
2384
 
@@ -2349,83 +2393,87 @@
2349
2393
  * @param db
2350
2394
  * @param newUser
2351
2395
  */
2352
- async function setCurrentUser(db, user) {
2353
- if (user.userId === db.cloud.currentUserId)
2354
- return; // Already this user.
2355
- const $logins = db.table('$logins');
2356
- await db.transaction('rw', $logins, async (tx) => {
2357
- const existingLogins = await $logins.toArray();
2358
- await Promise.all(existingLogins
2359
- .filter((login) => login.userId !== user.userId && login.isLoggedIn)
2360
- .map((login) => {
2361
- login.isLoggedIn = false;
2362
- return $logins.put(login);
2396
+ function setCurrentUser(db, user) {
2397
+ return __awaiter$1(this, void 0, void 0, function* () {
2398
+ if (user.userId === db.cloud.currentUserId)
2399
+ return; // Already this user.
2400
+ const $logins = db.table('$logins');
2401
+ yield db.transaction('rw', $logins, (tx) => __awaiter$1(this, void 0, void 0, function* () {
2402
+ const existingLogins = yield $logins.toArray();
2403
+ yield Promise.all(existingLogins
2404
+ .filter((login) => login.userId !== user.userId && login.isLoggedIn)
2405
+ .map((login) => {
2406
+ login.isLoggedIn = false;
2407
+ return $logins.put(login);
2408
+ }));
2409
+ user.isLoggedIn = true;
2410
+ user.lastLogin = new Date();
2411
+ yield user.save();
2412
+ console.debug('Saved new user', user.email);
2363
2413
  }));
2364
- user.isLoggedIn = true;
2365
- user.lastLogin = new Date();
2366
- await user.save();
2367
- console.debug('Saved new user', user.email);
2414
+ yield new Promise((resolve) => {
2415
+ if (db.cloud.currentUserId === user.userId) {
2416
+ resolve(null);
2417
+ }
2418
+ else {
2419
+ const subscription = db.cloud.currentUser.subscribe((currentUser) => {
2420
+ if (currentUser.userId === user.userId) {
2421
+ subscription.unsubscribe();
2422
+ resolve(null);
2423
+ }
2424
+ });
2425
+ }
2426
+ });
2427
+ // TANKAR!!!!
2428
+ // V: Service workern kommer inte ha tillgång till currentUserObservable om den inte istället härrör från ett liveQuery.
2429
+ // V: Samma med andra windows.
2430
+ // V: Så kanske göra om den till att häröra från liveQuery som läser $logins.orderBy('lastLogin').last().
2431
+ // V: Då bara vara medveten om:
2432
+ // V: En sån observable börjar hämta data vid första subscribe
2433
+ // V: Vi har inget "inital value" men kan emulera det till att vara ANONYMOUS_USER
2434
+ // V: Om requireAuth är true, så borde db.on(ready) hålla databasen stängd för alla utom denna observable.
2435
+ // V: Om inte så behöver den inte blocka.
2436
+ // Andra tankar:
2437
+ // * Man kan inte byta användare när man är offline. Skulle gå att flytta realms till undanstuff-tabell vid user-change.
2438
+ // men troligen inte värt det.
2439
+ // * Istället: sälj inte inte switch-user funktionalitet utan tala enbart om inloggat vs icke inloggat läge.
2440
+ // * populate $logins med ANONYMOUS så att en påbörjad inloggning inte räknas, alternativt ha en boolean prop!
2441
+ // Kanske bäst ha en boolean prop!
2442
+ // * Alternativ switch-user funktionalitet:
2443
+ // * DBCore gömmer data från realms man inte har tillgång till.
2444
+ // * Cursor impl behövs också då.
2445
+ // * Då blir det snabba user switch.
2446
+ // * claims-settet som skickas till servern blir summan av alla claims. Då måste servern stödja multipla tokens eller
2447
+ // att ens token är ett samlad.
2368
2448
  });
2369
- await new Promise((resolve) => {
2370
- if (db.cloud.currentUserId === user.userId) {
2371
- resolve(null);
2372
- }
2373
- else {
2374
- const subscription = db.cloud.currentUser.subscribe((currentUser) => {
2375
- if (currentUser.userId === user.userId) {
2376
- subscription.unsubscribe();
2377
- resolve(null);
2449
+ }
2450
+
2451
+ function login(db, hints) {
2452
+ return __awaiter$1(this, void 0, void 0, function* () {
2453
+ const currentUser = yield db.getCurrentUser();
2454
+ if (currentUser.isLoggedIn) {
2455
+ if (hints) {
2456
+ if (hints.email && db.cloud.currentUser.value.email !== hints.email) {
2457
+ throw new Error(`Must logout before changing user`);
2458
+ }
2459
+ if (hints.userId && db.cloud.currentUserId !== hints.userId) {
2460
+ throw new Error(`Must logout before changing user`);
2378
2461
  }
2379
- });
2380
- }
2381
- });
2382
- // TANKAR!!!!
2383
- // V: Service workern kommer inte ha tillgång till currentUserObservable om den inte istället härrör från ett liveQuery.
2384
- // V: Samma med andra windows.
2385
- // V: Så kanske göra om den till att häröra från liveQuery som läser $logins.orderBy('lastLogin').last().
2386
- // V: Då bara vara medveten om:
2387
- // V: En sån observable börjar hämta data vid första subscribe
2388
- // V: Vi har inget "inital value" men kan emulera det till att vara ANONYMOUS_USER
2389
- // V: Om requireAuth är true, så borde db.on(ready) hålla databasen stängd för alla utom denna observable.
2390
- // V: Om inte så behöver den inte blocka.
2391
- // Andra tankar:
2392
- // * Man kan inte byta användare när man är offline. Skulle gå att flytta realms till undanstuff-tabell vid user-change.
2393
- // men troligen inte värt det.
2394
- // * Istället: sälj inte inte switch-user funktionalitet utan tala enbart om inloggat vs icke inloggat läge.
2395
- // * populate $logins med ANONYMOUS så att en påbörjad inloggning inte räknas, alternativt ha en boolean prop!
2396
- // Kanske bäst ha en boolean prop!
2397
- // * Alternativ switch-user funktionalitet:
2398
- // * DBCore gömmer data från realms man inte har tillgång till.
2399
- // * Cursor impl behövs också då.
2400
- // * Då blir det snabba user switch.
2401
- // * claims-settet som skickas till servern blir summan av alla claims. Då måste servern stödja multipla tokens eller
2402
- // att ens token är ett samlad.
2403
- }
2404
-
2405
- async function login(db, hints) {
2406
- const currentUser = await db.getCurrentUser();
2407
- if (currentUser.isLoggedIn) {
2408
- if (hints) {
2409
- if (hints.email && db.cloud.currentUser.value.email !== hints.email) {
2410
- throw new Error(`Must logout before changing user`);
2411
- }
2412
- if (hints.userId && db.cloud.currentUserId !== hints.userId) {
2413
- throw new Error(`Must logout before changing user`);
2414
2462
  }
2463
+ // Already authenticated according to given hints.
2464
+ return;
2415
2465
  }
2416
- // Already authenticated according to given hints.
2417
- return;
2418
- }
2419
- const context = new AuthPersistedContext(db, {
2420
- claims: {},
2421
- lastLogin: new Date(0),
2466
+ const context = new AuthPersistedContext(db, {
2467
+ claims: {},
2468
+ lastLogin: new Date(0),
2469
+ });
2470
+ yield authenticate(db.cloud.options.databaseUrl, context, db.cloud.options.fetchTokens || otpFetchTokenCallback(db), db.cloud.userInteraction, hints);
2471
+ yield context.save();
2472
+ yield setCurrentUser(db, context);
2473
+ // Make sure to resync as the new login will be authorized
2474
+ // for new realms.
2475
+ triggerSync(db, "pull");
2422
2476
  });
2423
- await authenticate(db.cloud.options.databaseUrl, context, db.cloud.options.fetchTokens || otpFetchTokenCallback(db), db.cloud.userInteraction, hints);
2424
- await context.save();
2425
- await setCurrentUser(db, context);
2426
- // Make sure to resync as the new login will be authorized
2427
- // for new realms.
2428
- triggerSync(db, "pull");
2429
2477
  }
2430
2478
 
2431
2479
  const UNAUTHORIZED_USER = {
@@ -2440,7 +2488,7 @@
2440
2488
  Object.freeze(UNAUTHORIZED_USER);
2441
2489
  Object.freeze(UNAUTHORIZED_USER.claims);
2442
2490
  }
2443
- catch { }
2491
+ catch (_a) { }
2444
2492
 
2445
2493
  const swHolder = {};
2446
2494
  const swContainer = self.document && navigator.serviceWorker; // self.document is to verify we're not the SW ourself
@@ -2449,8 +2497,9 @@
2449
2497
  if (typeof self !== 'undefined' && 'clients' in self && !self.document) {
2450
2498
  // We are the service worker. Propagate messages to all our clients.
2451
2499
  addEventListener('message', (ev) => {
2452
- if (ev.data?.type?.startsWith('sw-broadcast-')) {
2453
- [...self['clients'].matchAll({ includeUncontrolled: true })].forEach((client) => client.id !== ev.source?.id && client.postMessage(ev.data));
2500
+ var _a, _b;
2501
+ if ((_b = (_a = ev.data) === null || _a === void 0 ? void 0 : _a.type) === null || _b === void 0 ? void 0 : _b.startsWith('sw-broadcast-')) {
2502
+ [...self['clients'].matchAll({ includeUncontrolled: true })].forEach((client) => { var _a; return client.id !== ((_a = ev.source) === null || _a === void 0 ? void 0 : _a.id) && client.postMessage(ev.data); });
2454
2503
  }
2455
2504
  });
2456
2505
  }
@@ -2462,7 +2511,8 @@
2462
2511
  if (!swContainer)
2463
2512
  return () => { };
2464
2513
  const forwarder = (ev) => {
2465
- if (ev.data?.type === `sw-broadcast-${this.name}`) {
2514
+ var _a;
2515
+ if (((_a = ev.data) === null || _a === void 0 ? void 0 : _a.type) === `sw-broadcast-${this.name}`) {
2466
2516
  listener(ev.data.message);
2467
2517
  }
2468
2518
  };
@@ -2470,6 +2520,7 @@
2470
2520
  return () => swContainer.removeEventListener('message', forwarder);
2471
2521
  }
2472
2522
  postMessage(message) {
2523
+ var _a;
2473
2524
  if (typeof self['clients'] === 'object') {
2474
2525
  // We're a service worker. Propagate to our browser clients.
2475
2526
  [...self['clients'].matchAll({ includeUncontrolled: true })].forEach((client) => client.postMessage({
@@ -2480,7 +2531,7 @@
2480
2531
  else if (swHolder.registration) {
2481
2532
  // We're a client (browser window or other worker)
2482
2533
  // Post to SW so it can repost to all its clients and to itself
2483
- swHolder.registration.active?.postMessage({
2534
+ (_a = swHolder.registration.active) === null || _a === void 0 ? void 0 : _a.postMessage({
2484
2535
  type: `sw-broadcast-${this.name}`,
2485
2536
  message
2486
2537
  });
@@ -2523,22 +2574,24 @@
2523
2574
  this.bc = bc;
2524
2575
  }
2525
2576
  next(message) {
2526
- console.debug("BroadcastedAndLocalEvent: bc.postMessage()", { ...message }, "bc is a", this.bc);
2577
+ console.debug("BroadcastedAndLocalEvent: bc.postMessage()", Object.assign({}, message), "bc is a", this.bc);
2527
2578
  this.bc.postMessage(message);
2528
2579
  const ev = new CustomEvent(`lbc-${this.name}`, { detail: message });
2529
2580
  self.dispatchEvent(ev);
2530
2581
  }
2531
2582
  }
2532
2583
 
2533
- async function computeRealmSetHash({ realms, inviteRealms, }) {
2534
- const data = JSON.stringify([
2535
- ...realms.map((realmId) => ({ realmId, accepted: true })),
2536
- ...inviteRealms.map((realmId) => ({ realmId, accepted: false })),
2537
- ].sort((a, b) => a.realmId < b.realmId ? -1 : a.realmId > b.realmId ? 1 : 0));
2538
- const byteArray = new TextEncoder().encode(data);
2539
- const digestBytes = await crypto.subtle.digest('SHA-1', byteArray);
2540
- const base64 = b64encode(digestBytes);
2541
- return base64;
2584
+ function computeRealmSetHash({ realms, inviteRealms, }) {
2585
+ return __awaiter$1(this, void 0, void 0, function* () {
2586
+ const data = JSON.stringify([
2587
+ ...realms.map((realmId) => ({ realmId, accepted: true })),
2588
+ ...inviteRealms.map((realmId) => ({ realmId, accepted: false })),
2589
+ ].sort((a, b) => a.realmId < b.realmId ? -1 : a.realmId > b.realmId ? 1 : 0));
2590
+ const byteArray = new TextEncoder().encode(data);
2591
+ const digestBytes = yield crypto.subtle.digest('SHA-1', byteArray);
2592
+ const base64 = b64encode(digestBytes);
2593
+ return base64;
2594
+ });
2542
2595
  }
2543
2596
 
2544
2597
  function getSyncableTables(db) {
@@ -2553,7 +2606,8 @@
2553
2606
  }
2554
2607
 
2555
2608
  function getTableFromMutationTable(mutationTable) {
2556
- const tableName = /^\$(.*)_mutations$/.exec(mutationTable)?.[1];
2609
+ var _a;
2610
+ const tableName = (_a = /^\$(.*)_mutations$/.exec(mutationTable)) === null || _a === void 0 ? void 0 : _a[1];
2557
2611
  if (!tableName)
2558
2612
  throw new Error(`Given mutationTable ${mutationTable} is not correct`);
2559
2613
  return tableName;
@@ -2564,49 +2618,51 @@
2564
2618
  return concat.apply([], a);
2565
2619
  }
2566
2620
 
2567
- async function listClientChanges(mutationTables, db, { since = {}, limit = Infinity } = {}) {
2568
- const allMutsOnTables = await Promise.all(mutationTables.map(async (mutationTable) => {
2569
- const tableName = getTableFromMutationTable(mutationTable.name);
2570
- const lastRevision = since[tableName];
2571
- let query = lastRevision
2572
- ? mutationTable.where('rev').above(lastRevision)
2573
- : mutationTable;
2574
- if (limit < Infinity)
2575
- query = query.limit(limit);
2576
- const muts = await query.toArray();
2577
- //const objTable = db.table(tableName);
2578
- /*for (const mut of muts) {
2579
- if (mut.type === "insert" || mut.type === "upsert") {
2580
- mut.values = await objTable.bulkGet(mut.keys);
2581
- }
2582
- }*/
2583
- return muts.map((mut) => ({
2584
- table: tableName,
2585
- mut,
2586
- }));
2587
- }));
2588
- // Sort by time to get a true order of the operations (between tables)
2589
- const sorted = flatten(allMutsOnTables).sort((a, b) => a.mut.ts - b.mut.ts);
2590
- const result = [];
2591
- let currentEntry = null;
2592
- let currentTxid = null;
2593
- for (const { table, mut } of sorted) {
2594
- if (currentEntry &&
2595
- currentEntry.table === table &&
2596
- currentTxid === mut.txid) {
2597
- currentEntry.muts.push(mut);
2598
- }
2599
- else {
2600
- currentEntry = {
2601
- table,
2602
- muts: [mut],
2603
- };
2604
- currentTxid = mut.txid;
2605
- result.push(currentEntry);
2621
+ function listClientChanges(mutationTables, db, { since = {}, limit = Infinity } = {}) {
2622
+ return __awaiter$1(this, void 0, void 0, function* () {
2623
+ const allMutsOnTables = yield Promise.all(mutationTables.map((mutationTable) => __awaiter$1(this, void 0, void 0, function* () {
2624
+ const tableName = getTableFromMutationTable(mutationTable.name);
2625
+ const lastRevision = since[tableName];
2626
+ let query = lastRevision
2627
+ ? mutationTable.where('rev').above(lastRevision)
2628
+ : mutationTable;
2629
+ if (limit < Infinity)
2630
+ query = query.limit(limit);
2631
+ const muts = yield query.toArray();
2632
+ //const objTable = db.table(tableName);
2633
+ /*for (const mut of muts) {
2634
+ if (mut.type === "insert" || mut.type === "upsert") {
2635
+ mut.values = await objTable.bulkGet(mut.keys);
2636
+ }
2637
+ }*/
2638
+ return muts.map((mut) => ({
2639
+ table: tableName,
2640
+ mut,
2641
+ }));
2642
+ })));
2643
+ // Sort by time to get a true order of the operations (between tables)
2644
+ const sorted = flatten(allMutsOnTables).sort((a, b) => a.mut.ts - b.mut.ts);
2645
+ const result = [];
2646
+ let currentEntry = null;
2647
+ let currentTxid = null;
2648
+ for (const { table, mut } of sorted) {
2649
+ if (currentEntry &&
2650
+ currentEntry.table === table &&
2651
+ currentTxid === mut.txid) {
2652
+ currentEntry.muts.push(mut);
2653
+ }
2654
+ else {
2655
+ currentEntry = {
2656
+ table,
2657
+ muts: [mut],
2658
+ };
2659
+ currentTxid = mut.txid;
2660
+ result.push(currentEntry);
2661
+ }
2606
2662
  }
2607
- }
2608
- // Filter out those tables that doesn't have any mutations:
2609
- return result;
2663
+ // Filter out those tables that doesn't have any mutations:
2664
+ return result;
2665
+ });
2610
2666
  }
2611
2667
 
2612
2668
  function randomString(bytes) {
@@ -2615,58 +2671,60 @@
2615
2671
  return btoa(String.fromCharCode.apply(null, buf));
2616
2672
  }
2617
2673
 
2618
- async function listSyncifiedChanges(tablesToSyncify, currentUser, schema, alreadySyncedRealms) {
2619
- const txid = `upload-${randomString(8)}`;
2620
- if (currentUser.isLoggedIn) {
2621
- if (tablesToSyncify.length > 0) {
2622
- const ignoredRealms = new Set(alreadySyncedRealms || []);
2623
- const upserts = await Promise.all(tablesToSyncify.map(async (table) => {
2624
- const { extractKey } = table.core.schema.primaryKey;
2625
- if (!extractKey)
2626
- return { table: table.name, muts: [] }; // Outbound tables are not synced.
2627
- const dexieCloudTableSchema = schema[table.name];
2628
- const query = dexieCloudTableSchema?.generatedGlobalId
2629
- ? table.filter((item) => {
2630
- const id = extractKey(item);
2631
- return (!ignoredRealms.has(item.realmId || '') &&
2632
- //(id[0] !== '#' || !!item.$ts) && // Private obj need no sync if not changed
2633
- isValidSyncableID(id));
2634
- })
2635
- : table.filter((item) => {
2636
- extractKey(item);
2637
- return (!ignoredRealms.has(item.realmId || '') &&
2638
- //(id[0] !== '#' || !!item.$ts) && // Private obj need no sync if not changed
2639
- isValidAtID(extractKey(item), dexieCloudTableSchema?.idPrefix));
2640
- });
2641
- const unsyncedObjects = await query.toArray();
2642
- if (unsyncedObjects.length > 0) {
2643
- const mut = {
2644
- type: 'upsert',
2645
- values: unsyncedObjects,
2646
- keys: unsyncedObjects.map(extractKey),
2647
- userId: currentUser.userId,
2648
- txid,
2649
- };
2650
- return {
2651
- table: table.name,
2652
- muts: [mut],
2653
- };
2654
- }
2655
- else {
2656
- return {
2657
- table: table.name,
2658
- muts: [],
2659
- };
2660
- }
2661
- }));
2662
- return upserts.filter((op) => op.muts.length > 0);
2674
+ function listSyncifiedChanges(tablesToSyncify, currentUser, schema, alreadySyncedRealms) {
2675
+ return __awaiter$1(this, void 0, void 0, function* () {
2676
+ const txid = `upload-${randomString(8)}`;
2677
+ if (currentUser.isLoggedIn) {
2678
+ if (tablesToSyncify.length > 0) {
2679
+ const ignoredRealms = new Set(alreadySyncedRealms || []);
2680
+ const upserts = yield Promise.all(tablesToSyncify.map((table) => __awaiter$1(this, void 0, void 0, function* () {
2681
+ const { extractKey } = table.core.schema.primaryKey;
2682
+ if (!extractKey)
2683
+ return { table: table.name, muts: [] }; // Outbound tables are not synced.
2684
+ const dexieCloudTableSchema = schema[table.name];
2685
+ const query = (dexieCloudTableSchema === null || dexieCloudTableSchema === void 0 ? void 0 : dexieCloudTableSchema.generatedGlobalId)
2686
+ ? table.filter((item) => {
2687
+ const id = extractKey(item);
2688
+ return (!ignoredRealms.has(item.realmId || '') &&
2689
+ //(id[0] !== '#' || !!item.$ts) && // Private obj need no sync if not changed
2690
+ isValidSyncableID(id));
2691
+ })
2692
+ : table.filter((item) => {
2693
+ extractKey(item);
2694
+ return (!ignoredRealms.has(item.realmId || '') &&
2695
+ //(id[0] !== '#' || !!item.$ts) && // Private obj need no sync if not changed
2696
+ isValidAtID(extractKey(item), dexieCloudTableSchema === null || dexieCloudTableSchema === void 0 ? void 0 : dexieCloudTableSchema.idPrefix));
2697
+ });
2698
+ const unsyncedObjects = yield query.toArray();
2699
+ if (unsyncedObjects.length > 0) {
2700
+ const mut = {
2701
+ type: 'upsert',
2702
+ values: unsyncedObjects,
2703
+ keys: unsyncedObjects.map(extractKey),
2704
+ userId: currentUser.userId,
2705
+ txid,
2706
+ };
2707
+ return {
2708
+ table: table.name,
2709
+ muts: [mut],
2710
+ };
2711
+ }
2712
+ else {
2713
+ return {
2714
+ table: table.name,
2715
+ muts: [],
2716
+ };
2717
+ }
2718
+ })));
2719
+ return upserts.filter((op) => op.muts.length > 0);
2720
+ }
2663
2721
  }
2664
- }
2665
- return [];
2722
+ return [];
2723
+ });
2666
2724
  }
2667
2725
 
2668
2726
  function getTablesToSyncify(db, syncState) {
2669
- const syncedTables = syncState?.syncedTables || [];
2727
+ const syncedTables = (syncState === null || syncState === void 0 ? void 0 : syncState.syncedTables) || [];
2670
2728
  const syncableTables = getSyncableTables(db);
2671
2729
  const tablesToSyncify = syncableTables.filter((tbl) => !syncedTables.includes(tbl.name));
2672
2730
  return tablesToSyncify;
@@ -3188,23 +3246,17 @@
3188
3246
  return this.v;
3189
3247
  }
3190
3248
  }
3191
- const defs = {
3192
- ...undefinedDef,
3193
- ...(hasBigIntSupport
3194
- ? {}
3195
- : {
3196
- bigint: {
3197
- test: (val) => val instanceof FakeBigInt,
3198
- replace: (fakeBigInt) => {
3199
- return {
3200
- $t: 'bigint',
3201
- ...fakeBigInt
3202
- };
3203
- },
3204
- revive: ({ v, }) => new FakeBigInt(v)
3205
- }
3206
- })
3207
- };
3249
+ const defs = Object.assign(Object.assign({}, undefinedDef), (hasBigIntSupport
3250
+ ? {}
3251
+ : {
3252
+ bigint: {
3253
+ test: (val) => val instanceof FakeBigInt,
3254
+ replace: (fakeBigInt) => {
3255
+ return Object.assign({ $t: 'bigint' }, fakeBigInt);
3256
+ },
3257
+ revive: ({ v, }) => new FakeBigInt(v)
3258
+ }
3259
+ }));
3208
3260
  const TSON = TypesonSimplified(builtin, defs);
3209
3261
  const BISON = Bison(defs);
3210
3262
 
@@ -3253,110 +3305,107 @@
3253
3305
  }
3254
3306
  function cloneChange(change, rewriteValues) {
3255
3307
  // clone on demand:
3256
- return {
3257
- ...change,
3258
- muts: rewriteValues
3259
- ? change.muts.map((m) => ({
3260
- ...m,
3261
- keys: m.keys.slice(),
3262
- values: m.values.slice(),
3263
- }))
3264
- : change.muts.map((m) => ({ ...m, keys: m.keys.slice() })),
3265
- };
3308
+ return Object.assign(Object.assign({}, change), { muts: rewriteValues
3309
+ ? change.muts.map((m) => (Object.assign(Object.assign({}, m), { keys: m.keys.slice(), values: m.values.slice() })))
3310
+ : change.muts.map((m) => (Object.assign(Object.assign({}, m), { keys: m.keys.slice() }))) });
3266
3311
  }
3267
3312
 
3268
3313
  //import {BisonWebStreamReader} from "dreambase-library/dist/typeson-simplified/BisonWebStreamReader";
3269
- async function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, clientIdentity, currentUser) {
3270
- //
3271
- // Push changes to server using fetch
3272
- //
3273
- const headers = {
3274
- Accept: 'application/json, application/x-bison, application/x-bison-stream',
3275
- 'Content-Type': 'application/tson'
3276
- };
3277
- const accessToken = await loadAccessToken(db);
3278
- if (accessToken) {
3279
- headers.Authorization = `Bearer ${accessToken}`;
3280
- }
3281
- const syncRequest = {
3282
- v: 2,
3283
- dbID: syncState?.remoteDbId,
3284
- clientIdentity,
3285
- schema: schema || {},
3286
- lastPull: syncState ? {
3287
- serverRevision: syncState.serverRevision,
3288
- realms: syncState.realms,
3289
- inviteRealms: syncState.inviteRealms
3290
- } : undefined,
3291
- baseRevs,
3292
- changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes)
3293
- };
3294
- console.debug("Sync request", syncRequest);
3295
- db.syncStateChangedEvent.next({
3296
- phase: 'pushing',
3297
- });
3298
- const res = await fetch(`${databaseUrl}/sync`, {
3299
- method: 'post',
3300
- headers,
3301
- body: TSON.stringify(syncRequest)
3302
- });
3303
- //const contentLength = Number(res.headers.get('content-length'));
3304
- db.syncStateChangedEvent.next({
3305
- phase: 'pulling'
3306
- });
3307
- if (!res.ok) {
3308
- throw new HttpError(res);
3309
- }
3310
- switch (res.headers.get('content-type')) {
3311
- case 'application/x-bison':
3312
- return BISON.fromBinary(await res.blob());
3313
- case 'application/x-bison-stream': //return BisonWebStreamReader(BISON, res);
3314
- default:
3315
- case 'application/json': {
3316
- const text = await res.text();
3317
- const syncRes = TSON.parse(text);
3318
- return syncRes;
3314
+ function syncWithServer(changes, syncState, baseRevs, db, databaseUrl, schema, clientIdentity, currentUser) {
3315
+ return __awaiter$1(this, void 0, void 0, function* () {
3316
+ //
3317
+ // Push changes to server using fetch
3318
+ //
3319
+ const headers = {
3320
+ Accept: 'application/json, application/x-bison, application/x-bison-stream',
3321
+ 'Content-Type': 'application/tson'
3322
+ };
3323
+ const accessToken = yield loadAccessToken(db);
3324
+ if (accessToken) {
3325
+ headers.Authorization = `Bearer ${accessToken}`;
3319
3326
  }
3320
- }
3327
+ const syncRequest = {
3328
+ v: 2,
3329
+ dbID: syncState === null || syncState === void 0 ? void 0 : syncState.remoteDbId,
3330
+ clientIdentity,
3331
+ schema: schema || {},
3332
+ lastPull: syncState ? {
3333
+ serverRevision: syncState.serverRevision,
3334
+ realms: syncState.realms,
3335
+ inviteRealms: syncState.inviteRealms
3336
+ } : undefined,
3337
+ baseRevs,
3338
+ changes: encodeIdsForServer(db.dx.core.schema, currentUser, changes)
3339
+ };
3340
+ console.debug("Sync request", syncRequest);
3341
+ db.syncStateChangedEvent.next({
3342
+ phase: 'pushing',
3343
+ });
3344
+ const res = yield fetch(`${databaseUrl}/sync`, {
3345
+ method: 'post',
3346
+ headers,
3347
+ body: TSON.stringify(syncRequest)
3348
+ });
3349
+ //const contentLength = Number(res.headers.get('content-length'));
3350
+ db.syncStateChangedEvent.next({
3351
+ phase: 'pulling'
3352
+ });
3353
+ if (!res.ok) {
3354
+ throw new HttpError(res);
3355
+ }
3356
+ switch (res.headers.get('content-type')) {
3357
+ case 'application/x-bison':
3358
+ return BISON.fromBinary(yield res.blob());
3359
+ case 'application/x-bison-stream': //return BisonWebStreamReader(BISON, res);
3360
+ default:
3361
+ case 'application/json': {
3362
+ const text = yield res.text();
3363
+ const syncRes = TSON.parse(text);
3364
+ return syncRes;
3365
+ }
3366
+ }
3367
+ });
3321
3368
  }
3322
3369
 
3323
- async function modifyLocalObjectsWithNewUserId(syncifiedTables, currentUser, alreadySyncedRealms) {
3324
- const ignoredRealms = new Set(alreadySyncedRealms || []);
3325
- for (const table of syncifiedTables) {
3326
- if (table.name === "members") {
3327
- // members
3328
- await table.toCollection().modify((member) => {
3329
- if (!ignoredRealms.has(member.realmId) && (!member.userId || member.userId === UNAUTHORIZED_USER.userId)) {
3330
- member.userId = currentUser.userId;
3331
- }
3332
- });
3333
- }
3334
- else if (table.name === "roles") ;
3335
- else if (table.name === "realms") {
3336
- // realms
3337
- await table.toCollection().modify((realm) => {
3338
- if (!ignoredRealms.has(realm.realmId) && (realm.owner === undefined || realm.owner === UNAUTHORIZED_USER.userId)) {
3339
- realm.owner = currentUser.userId;
3340
- }
3341
- });
3342
- }
3343
- else {
3344
- // application entities
3345
- await table.toCollection().modify((obj) => {
3346
- if (!obj.realmId || !ignoredRealms.has(obj.realmId)) {
3347
- if (!obj.owner || obj.owner === UNAUTHORIZED_USER.userId)
3348
- obj.owner = currentUser.userId;
3349
- if (!obj.realmId || obj.realmId === UNAUTHORIZED_USER.userId) {
3350
- obj.realmId = currentUser.userId;
3370
+ function modifyLocalObjectsWithNewUserId(syncifiedTables, currentUser, alreadySyncedRealms) {
3371
+ return __awaiter$1(this, void 0, void 0, function* () {
3372
+ const ignoredRealms = new Set(alreadySyncedRealms || []);
3373
+ for (const table of syncifiedTables) {
3374
+ if (table.name === "members") {
3375
+ // members
3376
+ yield table.toCollection().modify((member) => {
3377
+ if (!ignoredRealms.has(member.realmId) && (!member.userId || member.userId === UNAUTHORIZED_USER.userId)) {
3378
+ member.userId = currentUser.userId;
3351
3379
  }
3352
- }
3353
- });
3380
+ });
3381
+ }
3382
+ else if (table.name === "roles") ;
3383
+ else if (table.name === "realms") {
3384
+ // realms
3385
+ yield table.toCollection().modify((realm) => {
3386
+ if (!ignoredRealms.has(realm.realmId) && (realm.owner === undefined || realm.owner === UNAUTHORIZED_USER.userId)) {
3387
+ realm.owner = currentUser.userId;
3388
+ }
3389
+ });
3390
+ }
3391
+ else {
3392
+ // application entities
3393
+ yield table.toCollection().modify((obj) => {
3394
+ if (!obj.realmId || !ignoredRealms.has(obj.realmId)) {
3395
+ if (!obj.owner || obj.owner === UNAUTHORIZED_USER.userId)
3396
+ obj.owner = currentUser.userId;
3397
+ if (!obj.realmId || obj.realmId === UNAUTHORIZED_USER.userId) {
3398
+ obj.realmId = currentUser.userId;
3399
+ }
3400
+ }
3401
+ });
3402
+ }
3354
3403
  }
3355
- }
3404
+ });
3356
3405
  }
3357
3406
 
3358
3407
  function throwIfCancelled(cancelToken) {
3359
- if (cancelToken?.cancelled)
3408
+ if (cancelToken === null || cancelToken === void 0 ? void 0 : cancelToken.cancelled)
3360
3409
  throw new Dexie__default["default"].AbortError(`Operation was cancelled`);
3361
3410
  }
3362
3411
 
@@ -3368,17 +3417,19 @@
3368
3417
  self.addEventListener('online', () => isOnline = true);
3369
3418
  self.addEventListener('offline', () => isOnline = false);
3370
3419
 
3371
- async function updateBaseRevs(db, schema, latestRevisions, serverRev) {
3372
- await db.$baseRevs.bulkPut(Object.keys(schema)
3373
- .filter((table) => schema[table].markedForSync)
3374
- .map((tableName) => {
3375
- const lastClientRevOnPreviousServerRev = latestRevisions[tableName] || 0;
3376
- return {
3377
- tableName,
3378
- clientRev: lastClientRevOnPreviousServerRev + 1,
3379
- serverRev,
3380
- };
3381
- }));
3420
+ function updateBaseRevs(db, schema, latestRevisions, serverRev) {
3421
+ return __awaiter$1(this, void 0, void 0, function* () {
3422
+ yield db.$baseRevs.bulkPut(Object.keys(schema)
3423
+ .filter((table) => schema[table].markedForSync)
3424
+ .map((tableName) => {
3425
+ const lastClientRevOnPreviousServerRev = latestRevisions[tableName] || 0;
3426
+ return {
3427
+ tableName,
3428
+ clientRev: lastClientRevOnPreviousServerRev + 1,
3429
+ serverRev,
3430
+ };
3431
+ }));
3432
+ });
3382
3433
  }
3383
3434
 
3384
3435
  function getLatestRevisionsPerTable(clientChangeSet, lastRevisions = {}) {
@@ -3389,104 +3440,108 @@
3389
3440
  return lastRevisions;
3390
3441
  }
3391
3442
 
3392
- async function bulkUpdate(table, keys, changeSpecs) {
3393
- const objs = await table.bulkGet(keys);
3394
- const resultKeys = [];
3395
- const resultObjs = [];
3396
- keys.forEach((key, idx) => {
3397
- const obj = objs[idx];
3398
- if (obj) {
3399
- for (const [keyPath, value] of Object.entries(changeSpecs[idx])) {
3400
- if (keyPath === table.schema.primKey.keyPath) {
3401
- if (Dexie.cmp(value, key) !== 0) {
3402
- throw new Error(`Cannot change primary key`);
3443
+ function bulkUpdate(table, keys, changeSpecs) {
3444
+ return __awaiter$1(this, void 0, void 0, function* () {
3445
+ const objs = yield table.bulkGet(keys);
3446
+ const resultKeys = [];
3447
+ const resultObjs = [];
3448
+ keys.forEach((key, idx) => {
3449
+ const obj = objs[idx];
3450
+ if (obj) {
3451
+ for (const [keyPath, value] of Object.entries(changeSpecs[idx])) {
3452
+ if (keyPath === table.schema.primKey.keyPath) {
3453
+ if (Dexie.cmp(value, key) !== 0) {
3454
+ throw new Error(`Cannot change primary key`);
3455
+ }
3456
+ }
3457
+ else {
3458
+ Dexie__default["default"].setByKeyPath(obj, keyPath, value);
3403
3459
  }
3404
3460
  }
3405
- else {
3406
- Dexie__default["default"].setByKeyPath(obj, keyPath, value);
3407
- }
3461
+ resultKeys.push(key);
3462
+ resultObjs.push(obj);
3408
3463
  }
3409
- resultKeys.push(key);
3410
- resultObjs.push(obj);
3411
- }
3464
+ });
3465
+ yield (table.schema.primKey.keyPath == null
3466
+ ? table.bulkPut(resultObjs, resultKeys)
3467
+ : table.bulkPut(resultObjs));
3412
3468
  });
3413
- await (table.schema.primKey.keyPath == null
3414
- ? table.bulkPut(resultObjs, resultKeys)
3415
- : table.bulkPut(resultObjs));
3416
- }
3417
-
3418
- async function applyServerChanges(changes, db) {
3419
- console.debug('Applying server changes', changes, Dexie__default["default"].currentTransaction);
3420
- for (const { table: tableName, muts } of changes) {
3421
- const table = db.table(tableName);
3422
- if (!table)
3423
- continue; // If server sends changes on a table we don't have, ignore it.
3424
- const { primaryKey } = table.core.schema;
3425
- const keyDecoder = (key) => {
3426
- switch (key[0]) {
3427
- case '[':
3428
- // Decode JSON array
3429
- if (key.endsWith(']'))
3430
- try {
3431
- // On server, array keys are transformed to JSON string representation
3432
- return JSON.parse(key);
3469
+ }
3470
+
3471
+ function applyServerChanges(changes, db) {
3472
+ return __awaiter$1(this, void 0, void 0, function* () {
3473
+ console.debug('Applying server changes', changes, Dexie__default["default"].currentTransaction);
3474
+ for (const { table: tableName, muts } of changes) {
3475
+ const table = db.table(tableName);
3476
+ if (!table)
3477
+ continue; // If server sends changes on a table we don't have, ignore it.
3478
+ const { primaryKey } = table.core.schema;
3479
+ const keyDecoder = (key) => {
3480
+ switch (key[0]) {
3481
+ case '[':
3482
+ // Decode JSON array
3483
+ if (key.endsWith(']'))
3484
+ try {
3485
+ // On server, array keys are transformed to JSON string representation
3486
+ return JSON.parse(key);
3487
+ }
3488
+ catch (_a) { }
3489
+ return key;
3490
+ case '#':
3491
+ // Decode private ID (do the opposite from what's done in encodeIdsForServer())
3492
+ if (key.endsWith(':' + db.cloud.currentUserId)) {
3493
+ return key.substr(0, key.length - db.cloud.currentUserId.length - 1);
3433
3494
  }
3434
- catch { }
3435
- return key;
3436
- case '#':
3437
- // Decode private ID (do the opposite from what's done in encodeIdsForServer())
3438
- if (key.endsWith(':' + db.cloud.currentUserId)) {
3439
- return key.substr(0, key.length - db.cloud.currentUserId.length - 1);
3440
- }
3441
- return key;
3442
- default:
3443
- return key;
3444
- }
3445
- };
3446
- for (const mut of muts) {
3447
- const keys = mut.keys.map(keyDecoder);
3448
- switch (mut.type) {
3449
- case 'insert':
3450
- if (primaryKey.outbound) {
3451
- await table.bulkAdd(mut.values, keys);
3452
- }
3453
- else {
3454
- keys.forEach((key, i) => {
3455
- // Make sure inbound keys are consistent
3456
- Dexie__default["default"].setByKeyPath(mut.values[i], primaryKey.keyPath, key);
3457
- });
3458
- await table.bulkAdd(mut.values);
3459
- }
3460
- break;
3461
- case 'upsert':
3462
- if (primaryKey.outbound) {
3463
- await table.bulkPut(mut.values, keys);
3464
- }
3465
- else {
3466
- keys.forEach((key, i) => {
3467
- // Make sure inbound keys are consistent
3468
- Dexie__default["default"].setByKeyPath(mut.values[i], primaryKey.keyPath, key);
3469
- });
3470
- await table.bulkPut(mut.values);
3471
- }
3472
- break;
3473
- case 'modify':
3474
- if (keys.length === 1) {
3475
- await table.update(keys[0], mut.changeSpec);
3476
- }
3477
- else {
3478
- await table.where(':id').anyOf(keys).modify(mut.changeSpec);
3479
- }
3480
- break;
3481
- case 'update':
3482
- await bulkUpdate(table, keys, mut.changeSpecs);
3483
- break;
3484
- case 'delete':
3485
- await table.bulkDelete(keys);
3486
- break;
3495
+ return key;
3496
+ default:
3497
+ return key;
3498
+ }
3499
+ };
3500
+ for (const mut of muts) {
3501
+ const keys = mut.keys.map(keyDecoder);
3502
+ switch (mut.type) {
3503
+ case 'insert':
3504
+ if (primaryKey.outbound) {
3505
+ yield table.bulkAdd(mut.values, keys);
3506
+ }
3507
+ else {
3508
+ keys.forEach((key, i) => {
3509
+ // Make sure inbound keys are consistent
3510
+ Dexie__default["default"].setByKeyPath(mut.values[i], primaryKey.keyPath, key);
3511
+ });
3512
+ yield table.bulkAdd(mut.values);
3513
+ }
3514
+ break;
3515
+ case 'upsert':
3516
+ if (primaryKey.outbound) {
3517
+ yield table.bulkPut(mut.values, keys);
3518
+ }
3519
+ else {
3520
+ keys.forEach((key, i) => {
3521
+ // Make sure inbound keys are consistent
3522
+ Dexie__default["default"].setByKeyPath(mut.values[i], primaryKey.keyPath, key);
3523
+ });
3524
+ yield table.bulkPut(mut.values);
3525
+ }
3526
+ break;
3527
+ case 'modify':
3528
+ if (keys.length === 1) {
3529
+ yield table.update(keys[0], mut.changeSpec);
3530
+ }
3531
+ else {
3532
+ yield table.where(':id').anyOf(keys).modify(mut.changeSpec);
3533
+ }
3534
+ break;
3535
+ case 'update':
3536
+ yield bulkUpdate(table, keys, mut.changeSpecs);
3537
+ break;
3538
+ case 'delete':
3539
+ yield table.bulkDelete(keys);
3540
+ break;
3541
+ }
3487
3542
  }
3488
3543
  }
3489
- }
3544
+ });
3490
3545
  }
3491
3546
 
3492
3547
  const CURRENT_SYNC_WORKER = 'currentSyncWorker';
@@ -3494,14 +3549,14 @@
3494
3549
  return _sync
3495
3550
  .apply(this, arguments)
3496
3551
  .then(() => {
3497
- if (!syncOptions?.justCheckIfNeeded) {
3552
+ if (!(syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)) {
3498
3553
  db.syncStateChangedEvent.next({
3499
3554
  phase: 'in-sync',
3500
3555
  });
3501
3556
  }
3502
3557
  })
3503
- .catch(async (error) => {
3504
- if (syncOptions?.justCheckIfNeeded)
3558
+ .catch((error) => __awaiter$1(this, void 0, void 0, function* () {
3559
+ if (syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.justCheckIfNeeded)
3505
3560
  return Promise.reject(error); // Just rethrow.
3506
3561
  console.debug('Error from _sync', {
3507
3562
  isOnline,
@@ -3509,23 +3564,20 @@
3509
3564
  error,
3510
3565
  });
3511
3566
  if (isOnline &&
3512
- syncOptions?.retryImmediatelyOnFetchError &&
3513
- error?.name === 'TypeError' &&
3514
- /fetch/.test(error?.message)) {
3567
+ (syncOptions === null || syncOptions === void 0 ? void 0 : syncOptions.retryImmediatelyOnFetchError) &&
3568
+ (error === null || error === void 0 ? void 0 : error.name) === 'TypeError' &&
3569
+ /fetch/.test(error === null || error === void 0 ? void 0 : error.message)) {
3515
3570
  db.syncStateChangedEvent.next({
3516
3571
  phase: 'error',
3517
3572
  error,
3518
3573
  });
3519
3574
  // Retry again in 500 ms but if it fails again, don't retry.
3520
- await new Promise((resolve) => setTimeout(resolve, 500));
3521
- return await sync(db, options, schema, {
3522
- ...syncOptions,
3523
- retryImmediatelyOnFetchError: false,
3524
- });
3575
+ yield new Promise((resolve) => setTimeout(resolve, 500));
3576
+ return yield sync(db, options, schema, Object.assign(Object.assign({}, syncOptions), { retryImmediatelyOnFetchError: false }));
3525
3577
  }
3526
3578
  // Make sure that no matter whether sync() explodes or not,
3527
3579
  // always update the timestamp. Also store the error.
3528
- await db.$syncState.update('syncState', {
3580
+ yield db.$syncState.update('syncState', {
3529
3581
  timestamp: new Date(),
3530
3582
  error: '' + error,
3531
3583
  });
@@ -3534,234 +3586,239 @@
3534
3586
  error,
3535
3587
  });
3536
3588
  return Promise.reject(error);
3537
- });
3589
+ }));
3538
3590
  }
3539
- async function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNeeded, purpose } = {
3591
+ function _sync(db, options, schema, { isInitialSync, cancelToken, justCheckIfNeeded, purpose } = {
3540
3592
  isInitialSync: false,
3541
3593
  }) {
3542
- if (!justCheckIfNeeded) {
3543
- console.debug('SYNC STARTED', { isInitialSync, purpose });
3544
- }
3545
- if (!db.cloud.options?.databaseUrl)
3546
- throw new Error(`Internal error: sync must not be called when no databaseUrl is configured`);
3547
- const { databaseUrl } = options;
3548
- const currentUser = await db.getCurrentUser(); // Keep same value across entire sync flow:
3549
- const tablesToSync = currentUser.isLoggedIn ? getSyncableTables(db) : [];
3550
- const mutationTables = tablesToSync.map((tbl) => db.table(getMutationTable(tbl.name)));
3551
- // If this is not the initial sync,
3552
- // go through tables that were previously not synced but should now be according to
3553
- // logged in state and the sync table whitelist in db.cloud.options.
3554
- //
3555
- // Prepare for syncification by modifying locally unauthorized objects:
3556
- //
3557
- const persistedSyncState = await db.getPersistedSyncState();
3558
- const tablesToSyncify = !isInitialSync && currentUser.isLoggedIn
3559
- ? getTablesToSyncify(db, persistedSyncState)
3560
- : [];
3561
- throwIfCancelled(cancelToken);
3562
- const doSyncify = tablesToSyncify.length > 0;
3563
- if (doSyncify) {
3564
- if (justCheckIfNeeded)
3565
- return true;
3566
- //console.debug('sync doSyncify is true');
3567
- await db.transaction('rw', tablesToSyncify, async (tx) => {
3568
- // @ts-ignore
3569
- tx.idbtrans.disableChangeTracking = true;
3570
- // @ts-ignore
3571
- tx.idbtrans.disableAccessControl = true; // TODO: Take care of this flag in access control middleware!
3572
- await modifyLocalObjectsWithNewUserId(tablesToSyncify, currentUser, persistedSyncState?.realms);
3573
- });
3574
- throwIfCancelled(cancelToken);
3575
- }
3576
- //
3577
- // List changes to sync
3578
- //
3579
- const [clientChangeSet, syncState, baseRevs] = await db.transaction('r', db.tables, async () => {
3580
- const syncState = await db.getPersistedSyncState();
3581
- const baseRevs = await db.$baseRevs.toArray();
3582
- let clientChanges = await listClientChanges(mutationTables);
3594
+ var _a;
3595
+ return __awaiter$1(this, void 0, void 0, function* () {
3596
+ if (!justCheckIfNeeded) {
3597
+ console.debug('SYNC STARTED', { isInitialSync, purpose });
3598
+ }
3599
+ if (!((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl))
3600
+ throw new Error(`Internal error: sync must not be called when no databaseUrl is configured`);
3601
+ const { databaseUrl } = options;
3602
+ const currentUser = yield db.getCurrentUser(); // Keep same value across entire sync flow:
3603
+ const tablesToSync = currentUser.isLoggedIn ? getSyncableTables(db) : [];
3604
+ const mutationTables = tablesToSync.map((tbl) => db.table(getMutationTable(tbl.name)));
3605
+ // If this is not the initial sync,
3606
+ // go through tables that were previously not synced but should now be according to
3607
+ // logged in state and the sync table whitelist in db.cloud.options.
3608
+ //
3609
+ // Prepare for syncification by modifying locally unauthorized objects:
3610
+ //
3611
+ const persistedSyncState = yield db.getPersistedSyncState();
3612
+ const tablesToSyncify = !isInitialSync && currentUser.isLoggedIn
3613
+ ? getTablesToSyncify(db, persistedSyncState)
3614
+ : [];
3583
3615
  throwIfCancelled(cancelToken);
3616
+ const doSyncify = tablesToSyncify.length > 0;
3584
3617
  if (doSyncify) {
3585
- const alreadySyncedRealms = [
3586
- ...(persistedSyncState?.realms || []),
3587
- ...(persistedSyncState?.inviteRealms || []),
3588
- ];
3589
- const syncificationInserts = await listSyncifiedChanges(tablesToSyncify, currentUser, schema, alreadySyncedRealms);
3618
+ if (justCheckIfNeeded)
3619
+ return true;
3620
+ //console.debug('sync doSyncify is true');
3621
+ yield db.transaction('rw', tablesToSyncify, (tx) => __awaiter$1(this, void 0, void 0, function* () {
3622
+ // @ts-ignore
3623
+ tx.idbtrans.disableChangeTracking = true;
3624
+ // @ts-ignore
3625
+ tx.idbtrans.disableAccessControl = true; // TODO: Take care of this flag in access control middleware!
3626
+ yield modifyLocalObjectsWithNewUserId(tablesToSyncify, currentUser, persistedSyncState === null || persistedSyncState === void 0 ? void 0 : persistedSyncState.realms);
3627
+ }));
3590
3628
  throwIfCancelled(cancelToken);
3591
- clientChanges = clientChanges.concat(syncificationInserts);
3592
- return [clientChanges, syncState, baseRevs];
3593
- }
3594
- return [clientChanges, syncState, baseRevs];
3595
- });
3596
- const syncIsNeeded = clientChangeSet.some((set) => set.muts.some((mut) => mut.keys.length > 0));
3597
- if (justCheckIfNeeded) {
3598
- console.debug('Sync is needed:', syncIsNeeded);
3599
- return syncIsNeeded;
3600
- }
3601
- if (purpose === 'push' && !syncIsNeeded) {
3602
- // The purpose of this request was to push changes
3603
- return false;
3604
- }
3605
- const latestRevisions = getLatestRevisionsPerTable(clientChangeSet, syncState?.latestRevisions);
3606
- const clientIdentity = syncState?.clientIdentity || randomString$1(16);
3607
- //
3608
- // Push changes to server
3609
- //
3610
- throwIfCancelled(cancelToken);
3611
- const res = await syncWithServer(clientChangeSet, syncState, baseRevs, db, databaseUrl, schema, clientIdentity, currentUser);
3612
- console.debug('Sync response', res);
3613
- //
3614
- // Apply changes locally and clear old change entries:
3615
- //
3616
- const done = await db.transaction('rw', db.tables, async (tx) => {
3617
- // @ts-ignore
3618
- tx.idbtrans.disableChangeTracking = true;
3619
- // @ts-ignore
3620
- tx.idbtrans.disableAccessControl = true; // TODO: Take care of this flag in access control middleware!
3621
- // Update db.cloud.schema from server response.
3622
- // Local schema MAY include a subset of tables, so do not force all tables into local schema.
3623
- for (const tableName of Object.keys(schema)) {
3624
- if (res.schema[tableName]) {
3625
- // Write directly into configured schema. This code can only be executed alone.
3626
- schema[tableName] = res.schema[tableName];
3627
- }
3628
3629
  }
3629
- await db.$syncState.put(schema, 'schema');
3630
- // List mutations that happened during our exchange with the server:
3631
- const addedClientChanges = await listClientChanges(mutationTables, db, {
3632
- since: latestRevisions,
3633
- });
3634
3630
  //
3635
- // Delete changes now as server has return success
3636
- // (but keep changes that haven't reached server yet)
3631
+ // List changes to sync
3637
3632
  //
3638
- for (const mutTable of mutationTables) {
3639
- const tableName = getTableFromMutationTable(mutTable.name);
3640
- if (!addedClientChanges.some((ch) => ch.table === tableName && ch.muts.length > 0)) {
3641
- // No added mutations for this table during the time we sent changes
3642
- // to the server.
3643
- // It is therefore safe to clear all changes (which is faster than
3644
- // deleting a range)
3645
- await Promise.all([
3646
- mutTable.clear(),
3647
- db.$baseRevs.where({ tableName }).delete(),
3648
- ]);
3649
- }
3650
- else if (latestRevisions[tableName]) {
3651
- const latestRev = latestRevisions[tableName] || 0;
3652
- await Promise.all([
3653
- mutTable.where('rev').belowOrEqual(latestRev).delete(),
3654
- db.$baseRevs
3655
- .where(':id')
3656
- .between([tableName, -Infinity], [tableName, latestRev + 1], true, true)
3657
- .reverse()
3658
- .offset(1) // Keep one entry (the one mapping muts that came during fetch --> previous server revision)
3659
- .delete(),
3660
- ]);
3633
+ const [clientChangeSet, syncState, baseRevs] = yield db.transaction('r', db.tables, () => __awaiter$1(this, void 0, void 0, function* () {
3634
+ const syncState = yield db.getPersistedSyncState();
3635
+ const baseRevs = yield db.$baseRevs.toArray();
3636
+ let clientChanges = yield listClientChanges(mutationTables);
3637
+ throwIfCancelled(cancelToken);
3638
+ if (doSyncify) {
3639
+ const alreadySyncedRealms = [
3640
+ ...((persistedSyncState === null || persistedSyncState === void 0 ? void 0 : persistedSyncState.realms) || []),
3641
+ ...((persistedSyncState === null || persistedSyncState === void 0 ? void 0 : persistedSyncState.inviteRealms) || []),
3642
+ ];
3643
+ const syncificationInserts = yield listSyncifiedChanges(tablesToSyncify, currentUser, schema, alreadySyncedRealms);
3644
+ throwIfCancelled(cancelToken);
3645
+ clientChanges = clientChanges.concat(syncificationInserts);
3646
+ return [clientChanges, syncState, baseRevs];
3661
3647
  }
3662
- else ;
3663
- }
3664
- // Update latestRevisions object according to additional changes:
3665
- getLatestRevisionsPerTable(addedClientChanges, latestRevisions);
3666
- // Update/add new entries into baseRevs map.
3667
- // * On tables without mutations since last serverRevision,
3668
- // this will update existing entry.
3669
- // * On tables where mutations have been recorded since last
3670
- // serverRevision, this will create a new entry.
3671
- // The purpose of this operation is to mark a start revision (per table)
3672
- // so that all client-mutations that come after this, will be mapped to current
3673
- // server revision.
3674
- await updateBaseRevs(db, schema, latestRevisions, res.serverRevision);
3675
- const syncState = await db.getPersistedSyncState();
3676
- //
3677
- // Delete objects from removed realms
3678
- //
3679
- await deleteObjectsFromRemovedRealms(db, res, syncState);
3680
- //
3681
- // Update syncState
3682
- //
3683
- const newSyncState = syncState || {
3684
- syncedTables: [],
3685
- latestRevisions: {},
3686
- realms: [],
3687
- inviteRealms: [],
3688
- clientIdentity,
3689
- };
3690
- newSyncState.syncedTables = tablesToSync
3691
- .map((tbl) => tbl.name)
3692
- .concat(tablesToSyncify.map((tbl) => tbl.name));
3693
- newSyncState.latestRevisions = latestRevisions;
3694
- newSyncState.remoteDbId = res.dbId;
3695
- newSyncState.initiallySynced = true;
3696
- newSyncState.realms = res.realms;
3697
- newSyncState.inviteRealms = res.inviteRealms;
3698
- newSyncState.serverRevision = res.serverRevision;
3699
- newSyncState.timestamp = new Date();
3700
- delete newSyncState.error;
3701
- const filteredChanges = filterServerChangesThroughAddedClientChanges(res.changes, addedClientChanges);
3648
+ return [clientChanges, syncState, baseRevs];
3649
+ }));
3650
+ const syncIsNeeded = clientChangeSet.some((set) => set.muts.some((mut) => mut.keys.length > 0));
3651
+ if (justCheckIfNeeded) {
3652
+ console.debug('Sync is needed:', syncIsNeeded);
3653
+ return syncIsNeeded;
3654
+ }
3655
+ if (purpose === 'push' && !syncIsNeeded) {
3656
+ // The purpose of this request was to push changes
3657
+ return false;
3658
+ }
3659
+ const latestRevisions = getLatestRevisionsPerTable(clientChangeSet, syncState === null || syncState === void 0 ? void 0 : syncState.latestRevisions);
3660
+ const clientIdentity = (syncState === null || syncState === void 0 ? void 0 : syncState.clientIdentity) || randomString$1(16);
3702
3661
  //
3703
- // apply server changes
3662
+ // Push changes to server
3704
3663
  //
3705
- await applyServerChanges(filteredChanges, db);
3664
+ throwIfCancelled(cancelToken);
3665
+ const res = yield syncWithServer(clientChangeSet, syncState, baseRevs, db, databaseUrl, schema, clientIdentity, currentUser);
3666
+ console.debug('Sync response', res);
3706
3667
  //
3707
- // Update syncState
3668
+ // Apply changes locally and clear old change entries:
3708
3669
  //
3709
- db.$syncState.put(newSyncState, 'syncState');
3710
- return addedClientChanges.length === 0;
3670
+ const done = yield db.transaction('rw', db.tables, (tx) => __awaiter$1(this, void 0, void 0, function* () {
3671
+ // @ts-ignore
3672
+ tx.idbtrans.disableChangeTracking = true;
3673
+ // @ts-ignore
3674
+ tx.idbtrans.disableAccessControl = true; // TODO: Take care of this flag in access control middleware!
3675
+ // Update db.cloud.schema from server response.
3676
+ // Local schema MAY include a subset of tables, so do not force all tables into local schema.
3677
+ for (const tableName of Object.keys(schema)) {
3678
+ if (res.schema[tableName]) {
3679
+ // Write directly into configured schema. This code can only be executed alone.
3680
+ schema[tableName] = res.schema[tableName];
3681
+ }
3682
+ }
3683
+ yield db.$syncState.put(schema, 'schema');
3684
+ // List mutations that happened during our exchange with the server:
3685
+ const addedClientChanges = yield listClientChanges(mutationTables, db, {
3686
+ since: latestRevisions,
3687
+ });
3688
+ //
3689
+ // Delete changes now as server has return success
3690
+ // (but keep changes that haven't reached server yet)
3691
+ //
3692
+ for (const mutTable of mutationTables) {
3693
+ const tableName = getTableFromMutationTable(mutTable.name);
3694
+ if (!addedClientChanges.some((ch) => ch.table === tableName && ch.muts.length > 0)) {
3695
+ // No added mutations for this table during the time we sent changes
3696
+ // to the server.
3697
+ // It is therefore safe to clear all changes (which is faster than
3698
+ // deleting a range)
3699
+ yield Promise.all([
3700
+ mutTable.clear(),
3701
+ db.$baseRevs.where({ tableName }).delete(),
3702
+ ]);
3703
+ }
3704
+ else if (latestRevisions[tableName]) {
3705
+ const latestRev = latestRevisions[tableName] || 0;
3706
+ yield Promise.all([
3707
+ mutTable.where('rev').belowOrEqual(latestRev).delete(),
3708
+ db.$baseRevs
3709
+ .where(':id')
3710
+ .between([tableName, -Infinity], [tableName, latestRev + 1], true, true)
3711
+ .reverse()
3712
+ .offset(1) // Keep one entry (the one mapping muts that came during fetch --> previous server revision)
3713
+ .delete(),
3714
+ ]);
3715
+ }
3716
+ else ;
3717
+ }
3718
+ // Update latestRevisions object according to additional changes:
3719
+ getLatestRevisionsPerTable(addedClientChanges, latestRevisions);
3720
+ // Update/add new entries into baseRevs map.
3721
+ // * On tables without mutations since last serverRevision,
3722
+ // this will update existing entry.
3723
+ // * On tables where mutations have been recorded since last
3724
+ // serverRevision, this will create a new entry.
3725
+ // The purpose of this operation is to mark a start revision (per table)
3726
+ // so that all client-mutations that come after this, will be mapped to current
3727
+ // server revision.
3728
+ yield updateBaseRevs(db, schema, latestRevisions, res.serverRevision);
3729
+ const syncState = yield db.getPersistedSyncState();
3730
+ //
3731
+ // Delete objects from removed realms
3732
+ //
3733
+ yield deleteObjectsFromRemovedRealms(db, res, syncState);
3734
+ //
3735
+ // Update syncState
3736
+ //
3737
+ const newSyncState = syncState || {
3738
+ syncedTables: [],
3739
+ latestRevisions: {},
3740
+ realms: [],
3741
+ inviteRealms: [],
3742
+ clientIdentity,
3743
+ };
3744
+ newSyncState.syncedTables = tablesToSync
3745
+ .map((tbl) => tbl.name)
3746
+ .concat(tablesToSyncify.map((tbl) => tbl.name));
3747
+ newSyncState.latestRevisions = latestRevisions;
3748
+ newSyncState.remoteDbId = res.dbId;
3749
+ newSyncState.initiallySynced = true;
3750
+ newSyncState.realms = res.realms;
3751
+ newSyncState.inviteRealms = res.inviteRealms;
3752
+ newSyncState.serverRevision = res.serverRevision;
3753
+ newSyncState.timestamp = new Date();
3754
+ delete newSyncState.error;
3755
+ const filteredChanges = filterServerChangesThroughAddedClientChanges(res.changes, addedClientChanges);
3756
+ //
3757
+ // apply server changes
3758
+ //
3759
+ yield applyServerChanges(filteredChanges, db);
3760
+ //
3761
+ // Update syncState
3762
+ //
3763
+ db.$syncState.put(newSyncState, 'syncState');
3764
+ return addedClientChanges.length === 0;
3765
+ }));
3766
+ if (!done) {
3767
+ console.debug('MORE SYNC NEEDED. Go for it again!');
3768
+ return yield _sync(db, options, schema, { isInitialSync, cancelToken });
3769
+ }
3770
+ console.debug('SYNC DONE', { isInitialSync });
3771
+ return false; // Not needed anymore
3711
3772
  });
3712
- if (!done) {
3713
- console.debug('MORE SYNC NEEDED. Go for it again!');
3714
- return await _sync(db, options, schema, { isInitialSync, cancelToken });
3715
- }
3716
- console.debug('SYNC DONE', { isInitialSync });
3717
- return false; // Not needed anymore
3718
- }
3719
- async function deleteObjectsFromRemovedRealms(db, res, prevState) {
3720
- const deletedRealms = new Set();
3721
- const rejectedRealms = new Set();
3722
- const previousRealmSet = prevState ? prevState.realms : [];
3723
- const previousInviteRealmSet = prevState ? prevState.inviteRealms : [];
3724
- const updatedRealmSet = new Set(res.realms);
3725
- const updatedTotalRealmSet = new Set(res.realms.concat(res.inviteRealms));
3726
- for (const realmId of previousRealmSet) {
3727
- if (!updatedRealmSet.has(realmId)) {
3728
- rejectedRealms.add(realmId);
3773
+ }
3774
+ function deleteObjectsFromRemovedRealms(db, res, prevState) {
3775
+ return __awaiter$1(this, void 0, void 0, function* () {
3776
+ const deletedRealms = new Set();
3777
+ const rejectedRealms = new Set();
3778
+ const previousRealmSet = prevState ? prevState.realms : [];
3779
+ const previousInviteRealmSet = prevState ? prevState.inviteRealms : [];
3780
+ const updatedRealmSet = new Set(res.realms);
3781
+ const updatedTotalRealmSet = new Set(res.realms.concat(res.inviteRealms));
3782
+ for (const realmId of previousRealmSet) {
3783
+ if (!updatedRealmSet.has(realmId)) {
3784
+ rejectedRealms.add(realmId);
3785
+ if (!updatedTotalRealmSet.has(realmId)) {
3786
+ deletedRealms.add(realmId);
3787
+ }
3788
+ }
3789
+ }
3790
+ for (const realmId of previousInviteRealmSet.concat(previousRealmSet)) {
3729
3791
  if (!updatedTotalRealmSet.has(realmId)) {
3730
3792
  deletedRealms.add(realmId);
3731
3793
  }
3732
3794
  }
3733
- }
3734
- for (const realmId of previousInviteRealmSet.concat(previousRealmSet)) {
3735
- if (!updatedTotalRealmSet.has(realmId)) {
3736
- deletedRealms.add(realmId);
3737
- }
3738
- }
3739
- if (deletedRealms.size > 0 || rejectedRealms.size > 0) {
3740
- const tables = getSyncableTables(db);
3741
- for (const table of tables) {
3742
- let realmsToDelete = ['realms', 'members', 'roles'].includes(table.name)
3743
- ? deletedRealms // These tables should spare rejected ones.
3744
- : rejectedRealms; // All other tables shoudl delete rejected+deleted ones
3745
- if (realmsToDelete.size === 0)
3746
- continue;
3747
- if (table.schema.indexes.some((idx) => idx.keyPath === 'realmId' ||
3748
- (Array.isArray(idx.keyPath) && idx.keyPath[0] === 'realmId'))) {
3749
- // There's an index to use:
3750
- //console.debug(`REMOVAL: deleting all ${table.name} where realmId anyOf `, JSON.stringify([...realmsToDelete]));
3751
- await table
3752
- .where('realmId')
3753
- .anyOf([...realmsToDelete])
3754
- .delete();
3755
- }
3756
- else {
3757
- // No index to use:
3758
- //console.debug(`REMOVAL: deleting all ${table.name} where realmId is any of `, JSON.stringify([...realmsToDelete]), realmsToDelete.size);
3759
- await table
3760
- .filter((obj) => !!obj?.realmId && realmsToDelete.has(obj.realmId))
3761
- .delete();
3795
+ if (deletedRealms.size > 0 || rejectedRealms.size > 0) {
3796
+ const tables = getSyncableTables(db);
3797
+ for (const table of tables) {
3798
+ let realmsToDelete = ['realms', 'members', 'roles'].includes(table.name)
3799
+ ? deletedRealms // These tables should spare rejected ones.
3800
+ : rejectedRealms; // All other tables shoudl delete rejected+deleted ones
3801
+ if (realmsToDelete.size === 0)
3802
+ continue;
3803
+ if (table.schema.indexes.some((idx) => idx.keyPath === 'realmId' ||
3804
+ (Array.isArray(idx.keyPath) && idx.keyPath[0] === 'realmId'))) {
3805
+ // There's an index to use:
3806
+ //console.debug(`REMOVAL: deleting all ${table.name} where realmId anyOf `, JSON.stringify([...realmsToDelete]));
3807
+ yield table
3808
+ .where('realmId')
3809
+ .anyOf([...realmsToDelete])
3810
+ .delete();
3811
+ }
3812
+ else {
3813
+ // No index to use:
3814
+ //console.debug(`REMOVAL: deleting all ${table.name} where realmId is any of `, JSON.stringify([...realmsToDelete]), realmsToDelete.size);
3815
+ yield table
3816
+ .filter((obj) => !!(obj === null || obj === void 0 ? void 0 : obj.realmId) && realmsToDelete.has(obj.realmId))
3817
+ .delete();
3818
+ }
3762
3819
  }
3763
3820
  }
3764
- }
3821
+ });
3765
3822
  }
3766
3823
  function filterServerChangesThroughAddedClientChanges(serverChanges, addedClientChanges) {
3767
3824
  const changes = {};
@@ -3779,7 +3836,7 @@
3779
3836
  let isWorking = false;
3780
3837
  let loopWarning = 0;
3781
3838
  let loopDetection = [0, 0, 0, 0, 0, 0, 0, 0, 0, Date.now()];
3782
- event.subscribe(async () => {
3839
+ event.subscribe(() => __awaiter$1(this, void 0, void 0, function* () {
3783
3840
  if (isWorking)
3784
3841
  return;
3785
3842
  if (queue.length > 0) {
@@ -3788,7 +3845,7 @@
3788
3845
  loopDetection.push(Date.now());
3789
3846
  readyToServe.next(false);
3790
3847
  try {
3791
- await consumeQueue();
3848
+ yield consumeQueue();
3792
3849
  }
3793
3850
  finally {
3794
3851
  if (loopDetection[loopDetection.length - 1] - loopDetection[0] <
@@ -3798,170 +3855,173 @@
3798
3855
  // Last time we did this, we ended up here too. Wait for a minute.
3799
3856
  console.warn(`Slowing down websocket loop for one minute`);
3800
3857
  loopWarning = Date.now() + 60000;
3801
- await new Promise((resolve) => setTimeout(resolve, 60000));
3858
+ yield new Promise((resolve) => setTimeout(resolve, 60000));
3802
3859
  }
3803
3860
  else {
3804
3861
  // This is a one-time event. Just pause 10 seconds.
3805
3862
  console.warn(`Slowing down websocket loop for 10 seconds`);
3806
3863
  loopWarning = Date.now() + 10000;
3807
- await new Promise((resolve) => setTimeout(resolve, 10000));
3864
+ yield new Promise((resolve) => setTimeout(resolve, 10000));
3808
3865
  }
3809
3866
  }
3810
3867
  isWorking = false;
3811
3868
  readyToServe.next(true);
3812
3869
  }
3813
3870
  }
3814
- });
3871
+ }));
3815
3872
  function enqueue(msg) {
3816
3873
  queue.push(msg);
3817
3874
  event.next(null);
3818
3875
  }
3819
- async function consumeQueue() {
3820
- while (queue.length > 0) {
3821
- const msg = queue.shift();
3822
- try {
3823
- // If the sync worker or service worker is syncing, wait 'til thei're done.
3824
- // It's no need to have two channels at the same time - even though it wouldnt
3825
- // be a problem - this is an optimization.
3826
- await db.cloud.syncState
3827
- .pipe(filter(({ phase }) => phase === 'in-sync' || phase === 'error'), take(1))
3828
- .toPromise();
3829
- console.debug('processing msg', msg);
3830
- const persistedSyncState = db.cloud.persistedSyncState.value;
3831
- //syncState.
3832
- if (!msg)
3833
- continue;
3834
- switch (msg.type) {
3835
- case 'token-expired':
3836
- console.debug('WebSocket observable: Token expired. Refreshing token...');
3837
- const user = db.cloud.currentUser.value;
3838
- // Refresh access token
3839
- const refreshedLogin = await refreshAccessToken(db.cloud.options.databaseUrl, user);
3840
- // Persist updated access token
3841
- await db.table('$logins').update(user.userId, {
3842
- accessToken: refreshedLogin.accessToken,
3843
- accessTokenExpiration: refreshedLogin.accessTokenExpiration,
3844
- });
3845
- // Updating $logins will trigger emission of db.cloud.currentUser observable, which
3846
- // in turn will lead to that connectWebSocket.ts will reconnect the socket with the
3847
- // new token. So we don't need to do anything more here.
3848
- break;
3849
- case 'realm-added':
3850
- //if (!persistedSyncState?.realms?.includes(msg.realm) && !persistedSyncState?.inviteRealms?.includes(msg.realm)) {
3851
- triggerSync(db, 'pull');
3852
- //}
3853
- break;
3854
- case 'realm-accepted':
3855
- //if (!persistedSyncState?.realms?.includes(msg.realm)) {
3856
- triggerSync(db, 'pull');
3857
- //}
3858
- break;
3859
- case 'realm-removed':
3860
- //if (
3861
- persistedSyncState?.realms?.includes(msg.realm) ||
3862
- persistedSyncState?.inviteRealms?.includes(msg.realm);
3863
- //) {
3864
- triggerSync(db, 'pull');
3865
- //}
3866
- break;
3867
- case 'realms-changed':
3868
- triggerSync(db, 'pull');
3869
- break;
3870
- case 'changes':
3871
- console.debug('changes');
3872
- if (db.cloud.syncState.value?.phase === 'error') {
3876
+ function consumeQueue() {
3877
+ var _a, _b, _c;
3878
+ return __awaiter$1(this, void 0, void 0, function* () {
3879
+ while (queue.length > 0) {
3880
+ const msg = queue.shift();
3881
+ try {
3882
+ // If the sync worker or service worker is syncing, wait 'til thei're done.
3883
+ // It's no need to have two channels at the same time - even though it wouldnt
3884
+ // be a problem - this is an optimization.
3885
+ yield db.cloud.syncState
3886
+ .pipe(filter(({ phase }) => phase === 'in-sync' || phase === 'error'), take(1))
3887
+ .toPromise();
3888
+ console.debug('processing msg', msg);
3889
+ const persistedSyncState = db.cloud.persistedSyncState.value;
3890
+ //syncState.
3891
+ if (!msg)
3892
+ continue;
3893
+ switch (msg.type) {
3894
+ case 'token-expired':
3895
+ console.debug('WebSocket observable: Token expired. Refreshing token...');
3896
+ const user = db.cloud.currentUser.value;
3897
+ // Refresh access token
3898
+ const refreshedLogin = yield refreshAccessToken(db.cloud.options.databaseUrl, user);
3899
+ // Persist updated access token
3900
+ yield db.table('$logins').update(user.userId, {
3901
+ accessToken: refreshedLogin.accessToken,
3902
+ accessTokenExpiration: refreshedLogin.accessTokenExpiration,
3903
+ });
3904
+ // Updating $logins will trigger emission of db.cloud.currentUser observable, which
3905
+ // in turn will lead to that connectWebSocket.ts will reconnect the socket with the
3906
+ // new token. So we don't need to do anything more here.
3907
+ break;
3908
+ case 'realm-added':
3909
+ //if (!persistedSyncState?.realms?.includes(msg.realm) && !persistedSyncState?.inviteRealms?.includes(msg.realm)) {
3873
3910
  triggerSync(db, 'pull');
3911
+ //}
3874
3912
  break;
3875
- }
3876
- await db.transaction('rw', db.dx.tables, async (tx) => {
3877
- // @ts-ignore
3878
- tx.idbtrans.disableChangeTracking = true;
3879
- // @ts-ignore
3880
- tx.idbtrans.disableAccessControl = true;
3881
- const [schema, syncState, currentUser] = await Promise.all([
3882
- db.getSchema(),
3883
- db.getPersistedSyncState(),
3884
- db.getCurrentUser(),
3885
- ]);
3886
- console.debug('ws message queue: in transaction');
3887
- if (!syncState || !schema || !currentUser) {
3888
- console.debug('required vars not present', {
3889
- syncState,
3890
- schema,
3891
- currentUser,
3892
- });
3893
- return; // Initial sync must have taken place - otherwise, ignore this.
3913
+ case 'realm-accepted':
3914
+ //if (!persistedSyncState?.realms?.includes(msg.realm)) {
3915
+ triggerSync(db, 'pull');
3916
+ //}
3917
+ break;
3918
+ case 'realm-removed':
3919
+ //if (
3920
+ ((_a = persistedSyncState === null || persistedSyncState === void 0 ? void 0 : persistedSyncState.realms) === null || _a === void 0 ? void 0 : _a.includes(msg.realm)) ||
3921
+ ((_b = persistedSyncState === null || persistedSyncState === void 0 ? void 0 : persistedSyncState.inviteRealms) === null || _b === void 0 ? void 0 : _b.includes(msg.realm));
3922
+ //) {
3923
+ triggerSync(db, 'pull');
3924
+ //}
3925
+ break;
3926
+ case 'realms-changed':
3927
+ triggerSync(db, 'pull');
3928
+ break;
3929
+ case 'changes':
3930
+ console.debug('changes');
3931
+ if (((_c = db.cloud.syncState.value) === null || _c === void 0 ? void 0 : _c.phase) === 'error') {
3932
+ triggerSync(db, 'pull');
3933
+ break;
3894
3934
  }
3895
- // Verify again in ACID tx that we're on same server revision.
3896
- if (msg.baseRev !== syncState.serverRevision) {
3897
- console.debug(`baseRev (${msg.baseRev}) differs from our serverRevision in syncState (${syncState.serverRevision})`);
3898
- // Should we trigger a sync now? No. This is a normal case
3899
- // when another local peer (such as the SW or a websocket channel on other tab) has
3900
- // updated syncState from new server information but we are not aware yet. It would
3901
- // be unnescessary to do a sync in that case. Instead, the caller of this consumeQueue()
3902
- // function will do readyToServe.next(true) right after this return, which will lead
3903
- // to a "ready" message being sent to server with the new accurate serverRev we have,
3904
- // so that the next message indeed will be correct.
3905
- if (typeof msg.baseRev === 'string' && // v2 format
3906
- (typeof syncState.serverRevision === 'bigint' || // v1 format
3907
- typeof syncState.serverRevision === 'object') // v1 format old browser
3908
- ) {
3909
- // The reason for the diff seems to be that server has migrated the revision format.
3910
- // Do a full sync to update revision format.
3911
- // If we don't do a sync request now, we could stuck in an endless loop.
3935
+ yield db.transaction('rw', db.dx.tables, (tx) => __awaiter$1(this, void 0, void 0, function* () {
3936
+ // @ts-ignore
3937
+ tx.idbtrans.disableChangeTracking = true;
3938
+ // @ts-ignore
3939
+ tx.idbtrans.disableAccessControl = true;
3940
+ const [schema, syncState, currentUser] = yield Promise.all([
3941
+ db.getSchema(),
3942
+ db.getPersistedSyncState(),
3943
+ db.getCurrentUser(),
3944
+ ]);
3945
+ console.debug('ws message queue: in transaction');
3946
+ if (!syncState || !schema || !currentUser) {
3947
+ console.debug('required vars not present', {
3948
+ syncState,
3949
+ schema,
3950
+ currentUser,
3951
+ });
3952
+ return; // Initial sync must have taken place - otherwise, ignore this.
3953
+ }
3954
+ // Verify again in ACID tx that we're on same server revision.
3955
+ if (msg.baseRev !== syncState.serverRevision) {
3956
+ console.debug(`baseRev (${msg.baseRev}) differs from our serverRevision in syncState (${syncState.serverRevision})`);
3957
+ // Should we trigger a sync now? No. This is a normal case
3958
+ // when another local peer (such as the SW or a websocket channel on other tab) has
3959
+ // updated syncState from new server information but we are not aware yet. It would
3960
+ // be unnescessary to do a sync in that case. Instead, the caller of this consumeQueue()
3961
+ // function will do readyToServe.next(true) right after this return, which will lead
3962
+ // to a "ready" message being sent to server with the new accurate serverRev we have,
3963
+ // so that the next message indeed will be correct.
3964
+ if (typeof msg.baseRev === 'string' && // v2 format
3965
+ (typeof syncState.serverRevision === 'bigint' || // v1 format
3966
+ typeof syncState.serverRevision === 'object') // v1 format old browser
3967
+ ) {
3968
+ // The reason for the diff seems to be that server has migrated the revision format.
3969
+ // Do a full sync to update revision format.
3970
+ // If we don't do a sync request now, we could stuck in an endless loop.
3971
+ triggerSync(db, 'pull');
3972
+ }
3973
+ return; // Ignore message
3974
+ }
3975
+ // Verify also that the message is based on the exact same set of realms
3976
+ const ourRealmSetHash = yield Dexie__default["default"].waitFor(
3977
+ // Keep TX in non-IDB work
3978
+ computeRealmSetHash(syncState));
3979
+ console.debug('ourRealmSetHash', ourRealmSetHash);
3980
+ if (ourRealmSetHash !== msg.realmSetHash) {
3981
+ console.debug('not same realmSetHash', msg.realmSetHash);
3912
3982
  triggerSync(db, 'pull');
3983
+ // The message isn't based on the same realms.
3984
+ // Trigger a sync instead to resolve all things up.
3985
+ return;
3913
3986
  }
3914
- return; // Ignore message
3915
- }
3916
- // Verify also that the message is based on the exact same set of realms
3917
- const ourRealmSetHash = await Dexie__default["default"].waitFor(
3918
- // Keep TX in non-IDB work
3919
- computeRealmSetHash(syncState));
3920
- console.debug('ourRealmSetHash', ourRealmSetHash);
3921
- if (ourRealmSetHash !== msg.realmSetHash) {
3922
- console.debug('not same realmSetHash', msg.realmSetHash);
3923
- triggerSync(db, 'pull');
3924
- // The message isn't based on the same realms.
3925
- // Trigger a sync instead to resolve all things up.
3926
- return;
3927
- }
3928
- // Get clientChanges
3929
- let clientChanges = [];
3930
- if (currentUser.isLoggedIn) {
3931
- const mutationTables = getSyncableTables(db).map((tbl) => db.table(getMutationTable(tbl.name)));
3932
- clientChanges = await listClientChanges(mutationTables, db);
3933
- console.debug('msg queue: client changes', clientChanges);
3934
- }
3935
- if (msg.changes.length > 0) {
3936
- const filteredChanges = filterServerChangesThroughAddedClientChanges(msg.changes, clientChanges);
3987
+ // Get clientChanges
3988
+ let clientChanges = [];
3989
+ if (currentUser.isLoggedIn) {
3990
+ const mutationTables = getSyncableTables(db).map((tbl) => db.table(getMutationTable(tbl.name)));
3991
+ clientChanges = yield listClientChanges(mutationTables, db);
3992
+ console.debug('msg queue: client changes', clientChanges);
3993
+ }
3994
+ if (msg.changes.length > 0) {
3995
+ const filteredChanges = filterServerChangesThroughAddedClientChanges(msg.changes, clientChanges);
3996
+ //
3997
+ // apply server changes
3998
+ //
3999
+ console.debug('applying filtered server changes', filteredChanges);
4000
+ yield applyServerChanges(filteredChanges, db);
4001
+ }
4002
+ // Update latest revisions per table in case there are unsynced changes
4003
+ // This can be a real case in future when we allow non-eagery sync.
4004
+ // And it can actually be realistic now also, but very rare.
4005
+ syncState.latestRevisions = getLatestRevisionsPerTable(clientChanges, syncState.latestRevisions);
4006
+ syncState.serverRevision = msg.newRev;
4007
+ // Update base revs
4008
+ console.debug('Updating baseRefs', syncState.latestRevisions);
4009
+ yield updateBaseRevs(db, schema, syncState.latestRevisions, msg.newRev);
3937
4010
  //
3938
- // apply server changes
4011
+ // Update syncState
3939
4012
  //
3940
- console.debug('applying filtered server changes', filteredChanges);
3941
- await applyServerChanges(filteredChanges, db);
3942
- }
3943
- // Update latest revisions per table in case there are unsynced changes
3944
- // This can be a real case in future when we allow non-eagery sync.
3945
- // And it can actually be realistic now also, but very rare.
3946
- syncState.latestRevisions = getLatestRevisionsPerTable(clientChanges, syncState.latestRevisions);
3947
- syncState.serverRevision = msg.newRev;
3948
- // Update base revs
3949
- console.debug('Updating baseRefs', syncState.latestRevisions);
3950
- await updateBaseRevs(db, schema, syncState.latestRevisions, msg.newRev);
3951
- //
3952
- // Update syncState
3953
- //
3954
- console.debug('Updating syncState', syncState);
3955
- await db.$syncState.put(syncState, 'syncState');
3956
- });
3957
- console.debug('msg queue: done with rw transaction');
3958
- break;
4013
+ console.debug('Updating syncState', syncState);
4014
+ yield db.$syncState.put(syncState, 'syncState');
4015
+ }));
4016
+ console.debug('msg queue: done with rw transaction');
4017
+ break;
4018
+ }
4019
+ }
4020
+ catch (error) {
4021
+ console.error(`Error in msg queue`, error);
3959
4022
  }
3960
4023
  }
3961
- catch (error) {
3962
- console.error(`Error in msg queue`, error);
3963
- }
3964
- }
4024
+ });
3965
4025
  }
3966
4026
  return {
3967
4027
  enqueue,
@@ -4106,9 +4166,10 @@
4106
4166
  return toString.call(o).slice(8, -1);
4107
4167
  }
4108
4168
  function getEffectiveKeys(primaryKey, req) {
4169
+ var _a;
4109
4170
  if (req.type === 'delete')
4110
4171
  return req.keys;
4111
- return req.keys?.slice() || req.values.map(primaryKey.extractKey);
4172
+ return ((_a = req.keys) === null || _a === void 0 ? void 0 : _a.slice()) || req.values.map(primaryKey.extractKey);
4112
4173
  }
4113
4174
  function applyToUpperBitFix(orig, bits) {
4114
4175
  return ((bits & 1 ? orig[0].toUpperCase() : orig[0].toLowerCase()) +
@@ -4199,9 +4260,7 @@
4199
4260
  name: 'idGenerationMiddleware',
4200
4261
  level: 1,
4201
4262
  create: (core) => {
4202
- return {
4203
- ...core,
4204
- table: (tableName) => {
4263
+ return Object.assign(Object.assign({}, core), { table: (tableName) => {
4205
4264
  const table = core.table(tableName);
4206
4265
  function generateOrVerifyAtKeys(req, idPrefix) {
4207
4266
  let valueClones = null;
@@ -4227,24 +4286,19 @@
4227
4286
  `If you want to generate IDs programmatically, remove '@' from the schema to get rid of this constraint. Dexie Cloud supports custom IDs as long as they are random and globally unique.`);
4228
4287
  }
4229
4288
  });
4230
- return table.mutate({
4231
- ...req,
4232
- keys,
4233
- values: valueClones || req.values,
4234
- });
4289
+ return table.mutate(Object.assign(Object.assign({}, req), { keys, values: valueClones || req.values }));
4235
4290
  }
4236
- return {
4237
- ...table,
4238
- mutate: (req) => {
4291
+ return Object.assign(Object.assign({}, table), { mutate: (req) => {
4292
+ var _a, _b;
4239
4293
  // @ts-ignore
4240
4294
  if (req.trans.disableChangeTracking) {
4241
4295
  // Disable ID policy checks and ID generation
4242
4296
  return table.mutate(req);
4243
4297
  }
4244
4298
  if (req.type === 'add' || req.type === 'put') {
4245
- const cloudTableSchema = db.cloud.schema?.[tableName];
4246
- if (!cloudTableSchema?.generatedGlobalId) {
4247
- if (cloudTableSchema?.markedForSync) {
4299
+ const cloudTableSchema = (_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[tableName];
4300
+ if (!(cloudTableSchema === null || cloudTableSchema === void 0 ? void 0 : cloudTableSchema.generatedGlobalId)) {
4301
+ if (cloudTableSchema === null || cloudTableSchema === void 0 ? void 0 : cloudTableSchema.markedForSync) {
4248
4302
  // Just make sure primary key is of a supported type:
4249
4303
  const keys = getEffectiveKeys(table.schema.primaryKey, req);
4250
4304
  keys.forEach((key, idx) => {
@@ -4258,7 +4312,7 @@
4258
4312
  }
4259
4313
  }
4260
4314
  else {
4261
- if (db.cloud.options?.databaseUrl && !db.initiallySynced) {
4315
+ if (((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.databaseUrl) && !db.initiallySynced) {
4262
4316
  // A database URL is configured but no initial sync has been performed.
4263
4317
  const keys = getEffectiveKeys(table.schema.primaryKey, req);
4264
4318
  // Check if the operation would yield any INSERT. If so, complain! We never want wrong ID prefixes stored.
@@ -4279,10 +4333,8 @@
4279
4333
  }
4280
4334
  }
4281
4335
  return table.mutate(req);
4282
- },
4283
- };
4284
- },
4285
- };
4336
+ } });
4337
+ } });
4286
4338
  },
4287
4339
  };
4288
4340
  }
@@ -4293,19 +4345,16 @@
4293
4345
  name: 'implicitPropSetterMiddleware',
4294
4346
  level: 1,
4295
4347
  create: (core) => {
4296
- return {
4297
- ...core,
4298
- table: (tableName) => {
4348
+ return Object.assign(Object.assign({}, core), { table: (tableName) => {
4299
4349
  const table = core.table(tableName);
4300
- return {
4301
- ...table,
4302
- mutate: (req) => {
4350
+ return Object.assign(Object.assign({}, table), { mutate: (req) => {
4351
+ var _a, _b, _c, _d;
4303
4352
  // @ts-ignore
4304
4353
  if (req.trans.disableChangeTracking) {
4305
4354
  return table.mutate(req);
4306
4355
  }
4307
4356
  const trans = req.trans;
4308
- if (db.cloud.schema?.[tableName]?.markedForSync) {
4357
+ if ((_b = (_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[tableName]) === null || _b === void 0 ? void 0 : _b.markedForSync) {
4309
4358
  if (req.type === 'add' || req.type === 'put') {
4310
4359
  // No matter if user is logged in or not, make sure "owner" and "realmId" props are set properly.
4311
4360
  // If not logged in, this will be changed upon syncification of the tables (next sync after login),
@@ -4319,7 +4368,7 @@
4319
4368
  if (!obj.realmId) {
4320
4369
  obj.realmId = trans.currentUser.userId;
4321
4370
  }
4322
- const key = table.schema.primaryKey.extractKey?.(obj);
4371
+ const key = (_d = (_c = table.schema.primaryKey).extractKey) === null || _d === void 0 ? void 0 : _d.call(_c, obj);
4323
4372
  if (typeof key === 'string' && key[0] === '#') {
4324
4373
  // Add $ts prop for put operations and
4325
4374
  // disable update operations as well as consistent
@@ -4346,10 +4395,8 @@
4346
4395
  }
4347
4396
  }
4348
4397
  return table.mutate(req);
4349
- },
4350
- };
4351
- },
4352
- };
4398
+ } });
4399
+ } });
4353
4400
  },
4354
4401
  };
4355
4402
  }
@@ -4368,15 +4415,7 @@
4368
4415
  let counter$1 = 0;
4369
4416
  function guardedTable(table) {
4370
4417
  const prop = "$lock" + (++counter$1);
4371
- return {
4372
- ...table,
4373
- count: readLock(table.count, prop),
4374
- get: readLock(table.get, prop),
4375
- getMany: readLock(table.getMany, prop),
4376
- openCursor: readLock(table.openCursor, prop),
4377
- query: readLock(table.query, prop),
4378
- mutate: writeLock(table.mutate, prop),
4379
- };
4418
+ return Object.assign(Object.assign({}, table), { count: readLock(table.count, prop), get: readLock(table.get, prop), getMany: readLock(table.getMany, prop), openCursor: readLock(table.openCursor, prop), query: readLock(table.query, prop), mutate: writeLock(table.mutate, prop) });
4380
4419
  }
4381
4420
  function readLock(fn, prop) {
4382
4421
  return function readLocker(req) {
@@ -4426,16 +4465,14 @@
4426
4465
  core.table(`$${tbl.name}_mutations`)
4427
4466
  ]));
4428
4467
  }
4429
- catch {
4468
+ catch (_a) {
4430
4469
  throwVersionIncrementNeeded();
4431
4470
  }
4432
- return {
4433
- ...core,
4434
- transaction: (tables, mode) => {
4471
+ return Object.assign(Object.assign({}, core), { transaction: (tables, mode) => {
4435
4472
  let tx;
4436
4473
  if (mode === 'readwrite') {
4437
4474
  const mutationTables = tables
4438
- .filter((tbl) => db.cloud.schema?.[tbl]?.markedForSync)
4475
+ .filter((tbl) => { var _a, _b; return (_b = (_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[tbl]) === null || _b === void 0 ? void 0 : _b.markedForSync; })
4439
4476
  .map((tbl) => getMutationTable(tbl));
4440
4477
  tx = core.transaction([...tables, ...mutationTables], mode);
4441
4478
  }
@@ -4458,7 +4495,8 @@
4458
4495
  outstandingTransactions.next(outstandingTransactions.value);
4459
4496
  };
4460
4497
  const txComplete = () => {
4461
- if (tx.mutationsAdded && db.cloud.options?.databaseUrl) {
4498
+ var _a;
4499
+ if (tx.mutationsAdded && ((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl)) {
4462
4500
  if (db.cloud.usingServiceWorker) {
4463
4501
  console.debug('registering sync event');
4464
4502
  registerSyncEvent(db, "push");
@@ -4474,8 +4512,7 @@
4474
4512
  tx.addEventListener('abort', removeTransaction);
4475
4513
  }
4476
4514
  return tx;
4477
- },
4478
- table: (tableName) => {
4515
+ }, table: (tableName) => {
4479
4516
  const table = core.table(tableName);
4480
4517
  if (/^\$/.test(tableName)) {
4481
4518
  if (tableName.endsWith('_mutations')) {
@@ -4483,20 +4520,15 @@
4483
4520
  // make sure to set the mutationsAdded flag on transaction.
4484
4521
  // This is also done in mutateAndLog() as that function talks to a
4485
4522
  // lower level DBCore and wouldn't be catched by this code.
4486
- return {
4487
- ...table,
4488
- mutate: (req) => {
4523
+ return Object.assign(Object.assign({}, table), { mutate: (req) => {
4489
4524
  if (req.type === 'add' || req.type === 'put') {
4490
4525
  req.trans.mutationsAdded = true;
4491
4526
  }
4492
4527
  return table.mutate(req);
4493
- }
4494
- };
4528
+ } });
4495
4529
  }
4496
4530
  else if (tableName === '$logins') {
4497
- return {
4498
- ...table,
4499
- mutate: (req) => {
4531
+ return Object.assign(Object.assign({}, table), { mutate: (req) => {
4500
4532
  //console.debug('Mutating $logins table', req);
4501
4533
  return table
4502
4534
  .mutate(req)
@@ -4510,8 +4542,7 @@
4510
4542
  console.debug('Failed mutation $logins', err);
4511
4543
  return Promise.reject(err);
4512
4544
  });
4513
- }
4514
- };
4545
+ } });
4515
4546
  }
4516
4547
  else {
4517
4548
  return table;
@@ -4519,17 +4550,16 @@
4519
4550
  }
4520
4551
  const { schema } = table;
4521
4552
  const mutsTable = mutTableMap.get(tableName);
4522
- return guardedTable({
4523
- ...table,
4524
- mutate: (req) => {
4553
+ return guardedTable(Object.assign(Object.assign({}, table), { mutate: (req) => {
4554
+ var _a, _b, _c;
4525
4555
  const trans = req.trans;
4526
4556
  if (!trans.txid)
4527
4557
  return table.mutate(req); // Upgrade transactions not guarded by us.
4528
4558
  if (trans.disableChangeTracking)
4529
4559
  return table.mutate(req);
4530
- if (!db.cloud.schema?.[tableName]?.markedForSync)
4560
+ if (!((_b = (_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[tableName]) === null || _b === void 0 ? void 0 : _b.markedForSync))
4531
4561
  return table.mutate(req);
4532
- if (!trans.currentUser?.isLoggedIn) {
4562
+ if (!((_c = trans.currentUser) === null || _c === void 0 ? void 0 : _c.isLoggedIn)) {
4533
4563
  // Unauthorized user should not log mutations.
4534
4564
  // Instead, after login all local data should be logged at once.
4535
4565
  return table.mutate(req);
@@ -4552,8 +4582,7 @@
4552
4582
  });
4553
4583
  })
4554
4584
  : mutateAndLog(req);
4555
- }
4556
- });
4585
+ } }));
4557
4586
  function mutateAndLog(req) {
4558
4587
  const trans = req.trans;
4559
4588
  trans.mutationsAdded = true;
@@ -4624,18 +4653,14 @@
4624
4653
  : res;
4625
4654
  });
4626
4655
  }
4627
- }
4628
- };
4656
+ } });
4629
4657
  }
4630
4658
  };
4631
4659
  }
4632
4660
 
4633
4661
  function overrideParseStoresSpec(origFunc, dexie) {
4634
4662
  return function (stores, dbSchema) {
4635
- const storesClone = {
4636
- ...DEXIE_CLOUD_SCHEMA,
4637
- ...stores,
4638
- };
4663
+ const storesClone = Object.assign(Object.assign({}, DEXIE_CLOUD_SCHEMA), stores);
4639
4664
  const cloudSchema = dexie.cloud.schema || (dexie.cloud.schema = {});
4640
4665
  const allPrefixes = new Set();
4641
4666
  Object.keys(storesClone).forEach(tableName => {
@@ -4667,10 +4692,12 @@
4667
4692
  };
4668
4693
  }
4669
4694
 
4670
- async function performInitialSync(db, cloudOptions, cloudSchema) {
4671
- console.debug('Performing initial sync');
4672
- await sync(db, cloudOptions, cloudSchema, { isInitialSync: true });
4673
- console.debug('Done initial sync');
4695
+ function performInitialSync(db, cloudOptions, cloudSchema) {
4696
+ return __awaiter$1(this, void 0, void 0, function* () {
4697
+ console.debug('Performing initial sync');
4698
+ yield sync(db, cloudOptions, cloudSchema, { isInitialSync: true });
4699
+ console.debug('Done initial sync');
4700
+ });
4674
4701
  }
4675
4702
 
4676
4703
  const USER_INACTIVITY_TIMEOUT = 180000; // 3 minutes
@@ -4788,7 +4815,7 @@
4788
4815
  try {
4789
4816
  this.ws.close();
4790
4817
  }
4791
- catch { }
4818
+ catch (_a) { }
4792
4819
  }
4793
4820
  this.ws = null;
4794
4821
  if (this.messageProducerSubscription) {
@@ -4803,168 +4830,174 @@
4803
4830
  try {
4804
4831
  this.disconnect();
4805
4832
  }
4806
- catch { }
4833
+ catch (_a) { }
4807
4834
  this.connect()
4808
4835
  .catch(() => { })
4809
4836
  .then(() => (this.reconnecting = false)); // finally()
4810
4837
  }
4811
- async connect() {
4812
- this.lastServerActivity = new Date();
4813
- if (this.pauseUntil && this.pauseUntil > new Date()) {
4814
- console.debug('WS not reconnecting just yet', {
4815
- id: this.id,
4816
- pauseUntil: this.pauseUntil,
4817
- });
4818
- return;
4819
- }
4820
- if (this.ws) {
4821
- throw new Error(`Called connect() when a connection is already open`);
4822
- }
4823
- if (!this.databaseUrl)
4824
- throw new Error(`Cannot connect without a database URL`);
4825
- if (this.closed) {
4826
- //console.debug('SyncStatus: DUBB: Ooops it was closed!');
4827
- return;
4828
- }
4829
- if (this.tokenExpiration && this.tokenExpiration < new Date()) {
4830
- this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
4831
- return;
4832
- }
4833
- this.webSocketStatus.next('connecting');
4834
- this.pinger = setInterval(async () => {
4835
- if (this.closed) {
4836
- console.debug('pinger check', this.id, 'CLOSED.');
4837
- this.teardown();
4838
+ connect() {
4839
+ return __awaiter$1(this, void 0, void 0, function* () {
4840
+ this.lastServerActivity = new Date();
4841
+ if (this.pauseUntil && this.pauseUntil > new Date()) {
4842
+ console.debug('WS not reconnecting just yet', {
4843
+ id: this.id,
4844
+ pauseUntil: this.pauseUntil,
4845
+ });
4838
4846
  return;
4839
4847
  }
4840
4848
  if (this.ws) {
4841
- try {
4842
- this.ws.send(JSON.stringify({ type: 'ping' }));
4843
- setTimeout(() => {
4844
- console.debug('pinger setTimeout', this.id, this.pinger ? `alive` : 'dead');
4845
- if (!this.pinger)
4846
- return;
4847
- if (this.closed) {
4848
- console.debug('pinger setTimeout', this.id, 'subscription is closed');
4849
- this.teardown();
4850
- return;
4851
- }
4852
- if (this.lastServerActivity <
4853
- new Date(Date.now() - SERVER_PING_TIMEOUT)) {
4854
- // Server inactive. Reconnect if user is active.
4855
- console.debug('pinger: server is inactive');
4856
- console.debug('pinger reconnecting');
4857
- this.reconnect();
4858
- }
4859
- else {
4860
- console.debug('pinger: server still active');
4861
- }
4862
- }, SERVER_PING_TIMEOUT);
4863
- }
4864
- catch {
4865
- console.debug('pinger catch error', this.id, 'reconnecting');
4866
- this.reconnect();
4867
- }
4868
- }
4869
- else {
4870
- console.debug('pinger', this.id, 'reconnecting');
4871
- this.reconnect();
4849
+ throw new Error(`Called connect() when a connection is already open`);
4872
4850
  }
4873
- }, CLIENT_PING_INTERVAL);
4874
- // The following vars are needed because we must know which callback to ack when server sends it's ack to us.
4875
- const wsUrl = new URL(this.databaseUrl);
4876
- wsUrl.protocol = wsUrl.protocol === 'http:' ? 'ws' : 'wss';
4877
- const searchParams = new URLSearchParams();
4878
- if (this.subscriber.closed)
4879
- return;
4880
- searchParams.set('v', '2');
4881
- searchParams.set('rev', this.rev);
4882
- searchParams.set('realmsHash', this.realmSetHash);
4883
- searchParams.set('clientId', this.clientIdentity);
4884
- if (this.token) {
4885
- searchParams.set('token', this.token);
4886
- }
4887
- // Connect the WebSocket to given url:
4888
- console.debug('dexie-cloud WebSocket create');
4889
- const ws = (this.ws = new WebSocket(`${wsUrl}/changes?${searchParams}`));
4890
- //ws.binaryType = "arraybuffer"; // For future when subscribing to actual changes.
4891
- ws.onclose = (event) => {
4892
- if (!this.pinger)
4851
+ if (!this.databaseUrl)
4852
+ throw new Error(`Cannot connect without a database URL`);
4853
+ if (this.closed) {
4854
+ //console.debug('SyncStatus: DUBB: Ooops it was closed!');
4893
4855
  return;
4894
- console.debug('dexie-cloud WebSocket onclosed', this.id);
4895
- this.reconnect();
4896
- };
4897
- ws.onmessage = (event) => {
4898
- if (!this.pinger)
4856
+ }
4857
+ if (this.tokenExpiration && this.tokenExpiration < new Date()) {
4858
+ this.subscriber.error(new TokenExpiredError()); // Will be handled in connectWebSocket.ts.
4899
4859
  return;
4900
- console.debug('dexie-cloud WebSocket onmessage', event.data);
4901
- this.lastServerActivity = new Date();
4902
- try {
4903
- const msg = TSON.parse(event.data);
4904
- if (msg.type === 'error') {
4905
- throw new Error(`Error message from dexie-cloud: ${msg.error}`);
4860
+ }
4861
+ this.webSocketStatus.next('connecting');
4862
+ this.pinger = setInterval(() => __awaiter$1(this, void 0, void 0, function* () {
4863
+ if (this.closed) {
4864
+ console.debug('pinger check', this.id, 'CLOSED.');
4865
+ this.teardown();
4866
+ return;
4906
4867
  }
4907
- if (msg.type === 'rev') {
4908
- this.rev = msg.rev; // No meaning but seems reasonable.
4868
+ if (this.ws) {
4869
+ try {
4870
+ this.ws.send(JSON.stringify({ type: 'ping' }));
4871
+ setTimeout(() => {
4872
+ console.debug('pinger setTimeout', this.id, this.pinger ? `alive` : 'dead');
4873
+ if (!this.pinger)
4874
+ return;
4875
+ if (this.closed) {
4876
+ console.debug('pinger setTimeout', this.id, 'subscription is closed');
4877
+ this.teardown();
4878
+ return;
4879
+ }
4880
+ if (this.lastServerActivity <
4881
+ new Date(Date.now() - SERVER_PING_TIMEOUT)) {
4882
+ // Server inactive. Reconnect if user is active.
4883
+ console.debug('pinger: server is inactive');
4884
+ console.debug('pinger reconnecting');
4885
+ this.reconnect();
4886
+ }
4887
+ else {
4888
+ console.debug('pinger: server still active');
4889
+ }
4890
+ }, SERVER_PING_TIMEOUT);
4891
+ }
4892
+ catch (_a) {
4893
+ console.debug('pinger catch error', this.id, 'reconnecting');
4894
+ this.reconnect();
4895
+ }
4909
4896
  }
4910
- if (msg.type !== 'pong') {
4911
- this.subscriber.next(msg);
4897
+ else {
4898
+ console.debug('pinger', this.id, 'reconnecting');
4899
+ this.reconnect();
4912
4900
  }
4901
+ }), CLIENT_PING_INTERVAL);
4902
+ // The following vars are needed because we must know which callback to ack when server sends it's ack to us.
4903
+ const wsUrl = new URL(this.databaseUrl);
4904
+ wsUrl.protocol = wsUrl.protocol === 'http:' ? 'ws' : 'wss';
4905
+ const searchParams = new URLSearchParams();
4906
+ if (this.subscriber.closed)
4907
+ return;
4908
+ searchParams.set('v', '2');
4909
+ searchParams.set('rev', this.rev);
4910
+ searchParams.set('realmsHash', this.realmSetHash);
4911
+ searchParams.set('clientId', this.clientIdentity);
4912
+ if (this.token) {
4913
+ searchParams.set('token', this.token);
4913
4914
  }
4914
- catch (e) {
4915
- this.subscriber.error(e);
4916
- }
4917
- };
4918
- try {
4919
- let everConnected = false;
4920
- await new Promise((resolve, reject) => {
4921
- ws.onopen = (event) => {
4922
- console.debug('dexie-cloud WebSocket onopen');
4923
- everConnected = true;
4924
- resolve(null);
4925
- };
4926
- ws.onerror = (event) => {
4927
- if (!everConnected) {
4928
- const error = event.error || new Error('WebSocket Error');
4929
- this.subscriber.error(error);
4930
- this.webSocketStatus.next('error');
4931
- reject(error);
4915
+ // Connect the WebSocket to given url:
4916
+ console.debug('dexie-cloud WebSocket create');
4917
+ const ws = (this.ws = new WebSocket(`${wsUrl}/changes?${searchParams}`));
4918
+ //ws.binaryType = "arraybuffer"; // For future when subscribing to actual changes.
4919
+ ws.onclose = (event) => {
4920
+ if (!this.pinger)
4921
+ return;
4922
+ console.debug('dexie-cloud WebSocket onclosed', this.id);
4923
+ this.reconnect();
4924
+ };
4925
+ ws.onmessage = (event) => {
4926
+ if (!this.pinger)
4927
+ return;
4928
+ console.debug('dexie-cloud WebSocket onmessage', event.data);
4929
+ this.lastServerActivity = new Date();
4930
+ try {
4931
+ const msg = TSON.parse(event.data);
4932
+ if (msg.type === 'error') {
4933
+ throw new Error(`Error message from dexie-cloud: ${msg.error}`);
4932
4934
  }
4933
- else {
4934
- this.reconnect();
4935
+ if (msg.type === 'rev') {
4936
+ this.rev = msg.rev; // No meaning but seems reasonable.
4935
4937
  }
4936
- };
4937
- });
4938
- this.messageProducerSubscription = this.messageProducer.subscribe((msg) => {
4939
- if (!this.closed) {
4940
- if (msg.type === 'ready' &&
4941
- this.webSocketStatus.value !== 'connected') {
4942
- this.webSocketStatus.next('connected');
4938
+ if (msg.type !== 'pong') {
4939
+ this.subscriber.next(msg);
4943
4940
  }
4944
- this.ws?.send(TSON.stringify(msg));
4945
4941
  }
4946
- });
4947
- }
4948
- catch (error) {
4949
- this.pauseUntil = new Date(Date.now() + FAIL_RETRY_WAIT_TIME);
4950
- }
4942
+ catch (e) {
4943
+ this.subscriber.error(e);
4944
+ }
4945
+ };
4946
+ try {
4947
+ let everConnected = false;
4948
+ yield new Promise((resolve, reject) => {
4949
+ ws.onopen = (event) => {
4950
+ console.debug('dexie-cloud WebSocket onopen');
4951
+ everConnected = true;
4952
+ resolve(null);
4953
+ };
4954
+ ws.onerror = (event) => {
4955
+ if (!everConnected) {
4956
+ const error = event.error || new Error('WebSocket Error');
4957
+ this.subscriber.error(error);
4958
+ this.webSocketStatus.next('error');
4959
+ reject(error);
4960
+ }
4961
+ else {
4962
+ this.reconnect();
4963
+ }
4964
+ };
4965
+ });
4966
+ this.messageProducerSubscription = this.messageProducer.subscribe((msg) => {
4967
+ var _a;
4968
+ if (!this.closed) {
4969
+ if (msg.type === 'ready' &&
4970
+ this.webSocketStatus.value !== 'connected') {
4971
+ this.webSocketStatus.next('connected');
4972
+ }
4973
+ (_a = this.ws) === null || _a === void 0 ? void 0 : _a.send(TSON.stringify(msg));
4974
+ }
4975
+ });
4976
+ }
4977
+ catch (error) {
4978
+ this.pauseUntil = new Date(Date.now() + FAIL_RETRY_WAIT_TIME);
4979
+ }
4980
+ });
4951
4981
  }
4952
4982
  }
4953
4983
 
4954
4984
  function sleep(ms) {
4955
4985
  return new Promise((resolve) => setTimeout(resolve, ms));
4956
4986
  }
4957
- async function waitAndReconnectWhenUserDoesSomething(error) {
4958
- console.error(`WebSocket observable: error but revive when user does some active thing...`, error);
4959
- // Sleep some seconds...
4960
- await sleep(3000);
4961
- // Wait til user does something (move mouse, tap, scroll, click etc)
4962
- console.debug('waiting for someone to do something');
4963
- await userDoesSomething.pipe(take(1)).toPromise();
4964
- console.debug('someone did something!');
4987
+ function waitAndReconnectWhenUserDoesSomething(error) {
4988
+ return __awaiter$1(this, void 0, void 0, function* () {
4989
+ console.error(`WebSocket observable: error but revive when user does some active thing...`, error);
4990
+ // Sleep some seconds...
4991
+ yield sleep(3000);
4992
+ // Wait til user does something (move mouse, tap, scroll, click etc)
4993
+ console.debug('waiting for someone to do something');
4994
+ yield userDoesSomething.pipe(take(1)).toPromise();
4995
+ console.debug('someone did something!');
4996
+ });
4965
4997
  }
4966
4998
  function connectWebSocket(db) {
4967
- if (!db.cloud.options?.databaseUrl) {
4999
+ var _a;
5000
+ if (!((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl)) {
4968
5001
  throw new Error(`No database URL to connect WebSocket to`);
4969
5002
  }
4970
5003
  const messageProducer = db.messageConsumer.readyToServe.pipe(filter((isReady) => isReady), // When consumer is ready for new messages, produce such a message to inform server about it
@@ -4976,27 +5009,27 @@
4976
5009
  rev: syncState.serverRevision,
4977
5010
  })));
4978
5011
  function createObservable() {
4979
- return db.cloud.persistedSyncState.pipe(filter((syncState) => syncState?.serverRevision), // Don't connect before there's no initial sync performed.
5012
+ 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.
4980
5013
  take(1), // Don't continue waking up whenever syncState change
4981
- switchMap((syncState) => db.cloud.currentUser.pipe(map((userLogin) => [userLogin, syncState]))), switchMap(([userLogin, syncState]) => userIsReallyActive.pipe(map((isActive) => [isActive ? userLogin : null, syncState]))), switchMap(async ([userLogin, syncState]) => [userLogin, await computeRealmSetHash(syncState)]), switchMap(([userLogin, realmSetHash]) =>
5014
+ switchMap((syncState) => db.cloud.currentUser.pipe(map((userLogin) => [userLogin, syncState]))), switchMap(([userLogin, syncState]) => userIsReallyActive.pipe(map((isActive) => [isActive ? userLogin : null, syncState]))), switchMap(([userLogin, syncState]) => __awaiter$1(this, void 0, void 0, function* () { return [userLogin, yield computeRealmSetHash(syncState)]; })), switchMap(([userLogin, realmSetHash]) =>
4982
5015
  // Let server end query changes from last entry of same client-ID and forward.
4983
5016
  // If no new entries, server won't bother the client. If new entries, server sends only those
4984
5017
  // and the baseRev of the last from same client-ID.
4985
5018
  userLogin
4986
5019
  ? new WSObservable(db.cloud.options.databaseUrl, db.cloud.persistedSyncState.value.serverRevision, realmSetHash, db.cloud.persistedSyncState.value.clientIdentity, messageProducer, db.cloud.webSocketStatus, userLogin.accessToken, userLogin.accessTokenExpiration)
4987
5020
  : rxjs.from([])), catchError((error) => {
4988
- if (error?.name === 'TokenExpiredError') {
5021
+ if ((error === null || error === void 0 ? void 0 : error.name) === 'TokenExpiredError') {
4989
5022
  console.debug('WebSocket observable: Token expired. Refreshing token...');
4990
- return rxjs.of(true).pipe(switchMap(async () => {
5023
+ return rxjs.of(true).pipe(switchMap(() => __awaiter$1(this, void 0, void 0, function* () {
4991
5024
  // Refresh access token
4992
- const user = await db.getCurrentUser();
4993
- const refreshedLogin = await refreshAccessToken(db.cloud.options.databaseUrl, user);
5025
+ const user = yield db.getCurrentUser();
5026
+ const refreshedLogin = yield refreshAccessToken(db.cloud.options.databaseUrl, user);
4994
5027
  // Persist updated access token
4995
- await db.table('$logins').update(user.userId, {
5028
+ yield db.table('$logins').update(user.userId, {
4996
5029
  accessToken: refreshedLogin.accessToken,
4997
5030
  accessTokenExpiration: refreshedLogin.accessTokenExpiration,
4998
5031
  });
4999
- }), switchMap(() => createObservable()));
5032
+ })), switchMap(() => createObservable()));
5000
5033
  }
5001
5034
  else {
5002
5035
  return rxjs.throwError(error);
@@ -5018,10 +5051,13 @@
5018
5051
  });
5019
5052
  }
5020
5053
 
5021
- async function isSyncNeeded(db) {
5022
- return db.cloud.options?.databaseUrl && db.cloud.schema
5023
- ? await sync(db, db.cloud.options, db.cloud.schema, { justCheckIfNeeded: true })
5024
- : false;
5054
+ function isSyncNeeded(db) {
5055
+ var _a;
5056
+ return __awaiter$1(this, void 0, void 0, function* () {
5057
+ return ((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl) && db.cloud.schema
5058
+ ? yield sync(db, db.cloud.options, db.cloud.schema, { justCheckIfNeeded: true })
5059
+ : false;
5060
+ });
5025
5061
  }
5026
5062
 
5027
5063
  const SECONDS = 1000;
@@ -5031,93 +5067,97 @@
5031
5067
 
5032
5068
  const GUARDED_JOB_HEARTBEAT = 1 * SECONDS;
5033
5069
  const GUARDED_JOB_TIMEOUT = 1 * MINUTES;
5034
- async function performGuardedJob(db, jobName, jobsTableName, job, { awaitRemoteJob } = {}) {
5035
- // Start working.
5036
- //
5037
- // Check if someone else is working on this already.
5038
- //
5039
- const jobsTable = db.table(jobsTableName);
5040
- async function aquireLock() {
5041
- const gotTheLock = await db.transaction('rw!', jobsTableName, async () => {
5042
- const currentWork = await jobsTable.get(jobName);
5043
- if (!currentWork) {
5044
- // No one else is working. Let's record that we are.
5045
- await jobsTable.add({
5046
- nodeId: myId,
5047
- started: new Date(),
5048
- heartbeat: new Date()
5049
- }, jobName);
5050
- return true;
5051
- }
5052
- else if (currentWork.heartbeat.getTime() <
5053
- Date.now() - GUARDED_JOB_TIMEOUT) {
5054
- 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!`);
5055
- // Now, take over!
5056
- await jobsTable.put({
5057
- nodeId: myId,
5058
- started: new Date(),
5059
- heartbeat: new Date()
5060
- }, jobName);
5061
- return true;
5062
- }
5063
- return false;
5064
- });
5065
- if (gotTheLock)
5066
- return true;
5067
- // Someone else took the job.
5068
- if (awaitRemoteJob) {
5069
- try {
5070
- const jobDoneObservable = rxjs.from(Dexie.liveQuery(() => jobsTable.get(jobName))).pipe(timeout(GUARDED_JOB_TIMEOUT), filter((job) => !job)); // Wait til job is not there anymore.
5071
- await jobDoneObservable.toPromise();
5072
- return false;
5073
- }
5074
- catch (err) {
5075
- if (err.name !== 'TimeoutError') {
5076
- throw err;
5077
- }
5078
- // Timeout stopped us! Try aquire the lock now.
5079
- // It will likely succeed this time unless
5080
- // another client took it.
5081
- return await aquireLock();
5082
- }
5083
- }
5084
- return false;
5085
- }
5086
- if (await aquireLock()) {
5087
- // We own the lock entry and can do our job undisturbed.
5088
- // We're not within a transaction, but these type of locks
5089
- // spans over transactions.
5090
- // Start our heart beat during the job.
5091
- // Use setInterval to make sure we are updating heartbeat even during long-lived fetch calls.
5092
- const heartbeat = setInterval(() => {
5093
- jobsTable.update(jobName, (job) => {
5094
- if (job.nodeId === myId) {
5095
- job.heartbeat = new Date();
5070
+ function performGuardedJob(db, jobName, jobsTableName, job, { awaitRemoteJob } = {}) {
5071
+ return __awaiter$1(this, void 0, void 0, function* () {
5072
+ // Start working.
5073
+ //
5074
+ // Check if someone else is working on this already.
5075
+ //
5076
+ const jobsTable = db.table(jobsTableName);
5077
+ function aquireLock() {
5078
+ return __awaiter$1(this, void 0, void 0, function* () {
5079
+ const gotTheLock = yield db.transaction('rw!', jobsTableName, () => __awaiter$1(this, void 0, void 0, function* () {
5080
+ const currentWork = yield jobsTable.get(jobName);
5081
+ if (!currentWork) {
5082
+ // No one else is working. Let's record that we are.
5083
+ yield jobsTable.add({
5084
+ nodeId: myId,
5085
+ started: new Date(),
5086
+ heartbeat: new Date()
5087
+ }, jobName);
5088
+ return true;
5089
+ }
5090
+ else if (currentWork.heartbeat.getTime() <
5091
+ Date.now() - GUARDED_JOB_TIMEOUT) {
5092
+ 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!`);
5093
+ // Now, take over!
5094
+ yield jobsTable.put({
5095
+ nodeId: myId,
5096
+ started: new Date(),
5097
+ heartbeat: new Date()
5098
+ }, jobName);
5099
+ return true;
5100
+ }
5101
+ return false;
5102
+ }));
5103
+ if (gotTheLock)
5104
+ return true;
5105
+ // Someone else took the job.
5106
+ if (awaitRemoteJob) {
5107
+ try {
5108
+ const jobDoneObservable = rxjs.from(Dexie.liveQuery(() => jobsTable.get(jobName))).pipe(timeout(GUARDED_JOB_TIMEOUT), filter((job) => !job)); // Wait til job is not there anymore.
5109
+ yield jobDoneObservable.toPromise();
5110
+ return false;
5111
+ }
5112
+ catch (err) {
5113
+ if (err.name !== 'TimeoutError') {
5114
+ throw err;
5115
+ }
5116
+ // Timeout stopped us! Try aquire the lock now.
5117
+ // It will likely succeed this time unless
5118
+ // another client took it.
5119
+ return yield aquireLock();
5120
+ }
5096
5121
  }
5122
+ return false;
5097
5123
  });
5098
- }, GUARDED_JOB_HEARTBEAT);
5099
- try {
5100
- return await job();
5101
5124
  }
5102
- finally {
5103
- // Stop heartbeat
5104
- clearInterval(heartbeat);
5105
- // Remove the persisted job state:
5106
- await db.transaction('rw!', jobsTableName, async () => {
5107
- const currentWork = await jobsTable.get(jobName);
5108
- if (currentWork && currentWork.nodeId === myId) {
5109
- await jobsTable.delete(jobName);
5110
- }
5111
- });
5125
+ if (yield aquireLock()) {
5126
+ // We own the lock entry and can do our job undisturbed.
5127
+ // We're not within a transaction, but these type of locks
5128
+ // spans over transactions.
5129
+ // Start our heart beat during the job.
5130
+ // Use setInterval to make sure we are updating heartbeat even during long-lived fetch calls.
5131
+ const heartbeat = setInterval(() => {
5132
+ jobsTable.update(jobName, (job) => {
5133
+ if (job.nodeId === myId) {
5134
+ job.heartbeat = new Date();
5135
+ }
5136
+ });
5137
+ }, GUARDED_JOB_HEARTBEAT);
5138
+ try {
5139
+ return yield job();
5140
+ }
5141
+ finally {
5142
+ // Stop heartbeat
5143
+ clearInterval(heartbeat);
5144
+ // Remove the persisted job state:
5145
+ yield db.transaction('rw!', jobsTableName, () => __awaiter$1(this, void 0, void 0, function* () {
5146
+ const currentWork = yield jobsTable.get(jobName);
5147
+ if (currentWork && currentWork.nodeId === myId) {
5148
+ yield jobsTable.delete(jobName);
5149
+ }
5150
+ }));
5151
+ }
5112
5152
  }
5113
- }
5153
+ });
5114
5154
  }
5115
5155
 
5116
5156
  const ongoingSyncs = new WeakMap();
5117
5157
  function syncIfPossible(db, cloudOptions, cloudSchema, options) {
5118
5158
  const ongoing = ongoingSyncs.get(db);
5119
5159
  if (ongoing) {
5120
- if (ongoing.pull || options?.purpose === 'push') {
5160
+ if (ongoing.pull || (options === null || options === void 0 ? void 0 : options.purpose) === 'push') {
5121
5161
  console.debug('syncIfPossible(): returning the ongoing sync promise.');
5122
5162
  return ongoing.promise;
5123
5163
  }
@@ -5159,32 +5199,37 @@
5159
5199
  }
5160
5200
  }
5161
5201
  const promise = _syncIfPossible();
5162
- ongoingSyncs.set(db, { promise, pull: options?.purpose !== 'push' });
5202
+ ongoingSyncs.set(db, { promise, pull: (options === null || options === void 0 ? void 0 : options.purpose) !== 'push' });
5163
5203
  return promise;
5164
- async function _syncIfPossible() {
5165
- try {
5166
- if (db.cloud.usingServiceWorker) {
5167
- if (IS_SERVICE_WORKER) {
5168
- await sync(db, cloudOptions, cloudSchema, options);
5204
+ function _syncIfPossible() {
5205
+ return __awaiter$1(this, void 0, void 0, function* () {
5206
+ try {
5207
+ if (db.cloud.isServiceWorkerDB) {
5208
+ // We are the dedicated sync SW:
5209
+ yield sync(db, cloudOptions, cloudSchema, options);
5169
5210
  }
5211
+ else if (!db.cloud.usingServiceWorker) {
5212
+ // We use a flow that is better suited for the case when multiple workers want to
5213
+ // do the same thing.
5214
+ yield performGuardedJob(db, CURRENT_SYNC_WORKER, '$jobs', () => sync(db, cloudOptions, cloudSchema, options));
5215
+ }
5216
+ else {
5217
+ assert(false);
5218
+ throw new Error('Internal _syncIfPossible() - invalid precondition - should not have been called.');
5219
+ }
5220
+ ongoingSyncs.delete(db);
5221
+ console.debug('Done sync');
5170
5222
  }
5171
- else {
5172
- // We use a flow that is better suited for the case when multiple workers want to
5173
- // do the same thing.
5174
- await performGuardedJob(db, CURRENT_SYNC_WORKER, '$jobs', () => sync(db, cloudOptions, cloudSchema, options));
5223
+ catch (error) {
5224
+ ongoingSyncs.delete(db);
5225
+ console.error(`Failed to sync client changes`, error);
5226
+ throw error; // Make sure we rethrow error so that sync event is retried.
5227
+ // I don't think we should setTimout or so here.
5228
+ // Unless server tells us to in some response.
5229
+ // Then we could follow that advice but not by waiting here but by registering
5230
+ // Something that triggers an event listened to in startPushWorker()
5175
5231
  }
5176
- ongoingSyncs.delete(db);
5177
- console.debug('Done sync');
5178
- }
5179
- catch (error) {
5180
- ongoingSyncs.delete(db);
5181
- console.error(`Failed to sync client changes`, error);
5182
- throw error; // Make sure we rethrow error so that sync event is retried.
5183
- // I don't think we should setTimout or so here.
5184
- // Unless server tells us to in some response.
5185
- // Then we could follow that advice but not by waiting here but by registering
5186
- // Something that triggers an event listened to in startPushWorker()
5187
- }
5232
+ });
5188
5233
  }
5189
5234
  }
5190
5235
 
@@ -5254,8 +5299,9 @@
5254
5299
  }
5255
5300
 
5256
5301
  function verifySchema(db) {
5302
+ var _a, _b;
5257
5303
  for (const table of db.tables) {
5258
- if (db.cloud.schema?.[table.name]?.markedForSync) {
5304
+ if ((_b = (_a = db.cloud.schema) === null || _a === void 0 ? void 0 : _a[table.name]) === null || _b === void 0 ? void 0 : _b.markedForSync) {
5259
5305
  if (table.schema.primKey.auto) {
5260
5306
  throw new Dexie__default["default"].SchemaError(`Table ${table.name} is both autoIncremented and synced. ` +
5261
5307
  `Use db.cloud.configure({unsyncedTables: [${JSON.stringify(table.name)}]}) to blacklist it from sync`);
@@ -5332,8 +5378,8 @@
5332
5378
  }
5333
5379
  };
5334
5380
 
5335
- function Dialog({ children }) {
5336
- return (a$1("div", null,
5381
+ function Dialog({ children, className }) {
5382
+ return (a$1("div", { className: className },
5337
5383
  a$1("div", { style: Styles.Darken }),
5338
5384
  a$1("div", { style: Styles.DialogOuter },
5339
5385
  a$1("div", { style: Styles.DialogInner }, children))));
@@ -5348,8 +5394,8 @@
5348
5394
  function LoginDialog({ title, alerts, fields, onCancel, onSubmit, }) {
5349
5395
  const [params, setParams] = l({});
5350
5396
  const firstFieldRef = s();
5351
- h(() => firstFieldRef.current?.focus(), []);
5352
- return (a$1(Dialog, null,
5397
+ h(() => { var _a; return (_a = firstFieldRef.current) === null || _a === void 0 ? void 0 : _a.focus(); }, []);
5398
+ return (a$1(Dialog, { className: "dxc-login-dlg" },
5353
5399
  a$1(y, null,
5354
5400
  a$1("h3", { style: Styles.WindowHeader }, title),
5355
5401
  alerts.map((alert) => (a$1("p", { style: Styles.Alert[alert.type] }, resolveText(alert)))),
@@ -5358,7 +5404,7 @@
5358
5404
  onSubmit(params);
5359
5405
  } }, Object.entries(fields).map(([fieldName, { type, label, placeholder }], idx) => (a$1("label", { style: Styles.Label },
5360
5406
  label ? `${label}: ` : '',
5361
- a$1("input", { ref: idx === 0 ? firstFieldRef : undefined, type: type, name: fieldName, autoComplete: "on", style: Styles.Input, autoFocus: true, placeholder: placeholder, value: params[fieldName] || '', onInput: (ev) => setParams({ ...params, [fieldName]: valueTransformer(type, ev.target?.['value']) }) })))))),
5407
+ a$1("input", { ref: idx === 0 ? firstFieldRef : undefined, type: type, name: fieldName, autoComplete: "on", style: Styles.Input, autoFocus: true, placeholder: placeholder, value: params[fieldName] || '', onInput: (ev) => { var _a; return setParams(Object.assign(Object.assign({}, params), { [fieldName]: valueTransformer(type, (_a = ev.target) === null || _a === void 0 ? void 0 : _a['value']) })); } })))))),
5362
5408
  a$1("div", { style: Styles.ButtonsDiv },
5363
5409
  a$1("button", { type: "submit", style: Styles.Button, onClick: () => onSubmit(params) }, "Submit"),
5364
5410
  a$1("button", { style: Styles.Button, onClick: onCancel }, "Cancel"))));
@@ -5390,7 +5436,7 @@
5390
5436
  if (!userInteraction)
5391
5437
  return null;
5392
5438
  //if (props.db.cloud.userInteraction.observers.length > 1) return null; // Someone else subscribes.
5393
- return a$1(LoginDialog, { ...userInteraction });
5439
+ return a$1(LoginDialog, Object.assign({}, userInteraction));
5394
5440
  }
5395
5441
  }
5396
5442
  function setupDefaultGUI(db) {
@@ -5576,7 +5622,7 @@
5576
5622
  if (permissions.length === 0)
5577
5623
  return {};
5578
5624
  const reduced = permissions.reduce((result, next) => {
5579
- const ret = { ...result };
5625
+ const ret = Object.assign({}, result);
5580
5626
  for (const [verb, rights] of Object.entries(next)) {
5581
5627
  if (verb in ret && ret[verb]) {
5582
5628
  if (ret[verb] === '*')
@@ -5658,14 +5704,11 @@
5658
5704
  .map((role) => globalRoles[role])
5659
5705
  .filter((role) => role)
5660
5706
  .map((role) => role.permissions);
5661
- return {
5662
- ...realm,
5663
- permissions: realm.owner === userId
5707
+ return Object.assign(Object.assign({}, realm), { permissions: realm.owner === userId
5664
5708
  ? { manage: '*' }
5665
- : mergePermissions(...directPermissionSets, ...rolePermissionSets),
5666
- };
5709
+ : mergePermissions(...directPermissionSets, ...rolePermissionSets) });
5667
5710
  })
5668
- .reduce((p, c) => ({ ...p, [c.realmId]: c }), {
5711
+ .reduce((p, c) => (Object.assign(Object.assign({}, p), { [c.realmId]: c })), {
5669
5712
  [userId]: {
5670
5713
  realmId: userId,
5671
5714
  owner: userId,
@@ -5684,47 +5727,50 @@
5684
5727
  this.isOwner = isOwner;
5685
5728
  }
5686
5729
  add(...tableNames) {
5730
+ var _a;
5687
5731
  // If user can manage the whole realm, return true.
5688
5732
  if (this.permissions.manage === '*')
5689
5733
  return true;
5690
5734
  // If user can manage given table in realm, return true
5691
- if (this.permissions.manage?.includes(this.tableName))
5735
+ if ((_a = this.permissions.manage) === null || _a === void 0 ? void 0 : _a.includes(this.tableName))
5692
5736
  return true;
5693
5737
  // If user can add any type, return true
5694
5738
  if (this.permissions.add === '*')
5695
5739
  return true;
5696
5740
  // If user can add objects into given table names in the realm, return true
5697
- if (tableNames.every((tableName) => this.permissions.add?.includes(tableName))) {
5741
+ if (tableNames.every((tableName) => { var _a; return (_a = this.permissions.add) === null || _a === void 0 ? void 0 : _a.includes(tableName); })) {
5698
5742
  return true;
5699
5743
  }
5700
5744
  return false;
5701
5745
  }
5702
5746
  update(...props) {
5747
+ var _a, _b;
5703
5748
  // If user is owner of this object, or if user can manage the whole realm, return true.
5704
5749
  if (this.isOwner || this.permissions.manage === '*')
5705
5750
  return true;
5706
5751
  // If user can manage given table in realm, return true
5707
- if (this.permissions.manage?.includes(this.tableName))
5752
+ if ((_a = this.permissions.manage) === null || _a === void 0 ? void 0 : _a.includes(this.tableName))
5708
5753
  return true;
5709
5754
  // If user can update any prop in any table in this realm, return true unless
5710
5755
  // it regards to ownership change:
5711
5756
  if (this.permissions.update === '*') {
5712
5757
  return props.every((prop) => prop !== 'owner');
5713
5758
  }
5714
- const tablePermissions = this.permissions.update?.[this.tableName];
5759
+ const tablePermissions = (_b = this.permissions.update) === null || _b === void 0 ? void 0 : _b[this.tableName];
5715
5760
  // If user can update any prop in table and realm, return true unless
5716
5761
  // accessing special props owner or realmId
5717
5762
  if (tablePermissions === '*')
5718
5763
  return props.every((prop) => prop !== 'owner');
5719
5764
  // Explicitely listed properties to allow updates on:
5720
- return props.every((prop) => tablePermissions?.some((permittedProp) => permittedProp === prop || (permittedProp === '*' && prop !== 'owner')));
5765
+ return props.every((prop) => tablePermissions === null || tablePermissions === void 0 ? void 0 : tablePermissions.some((permittedProp) => permittedProp === prop || (permittedProp === '*' && prop !== 'owner')));
5721
5766
  }
5722
5767
  delete() {
5768
+ var _a;
5723
5769
  // If user is owner of this object, or if user can manage the whole realm, return true.
5724
5770
  if (this.isOwner || this.permissions.manage === '*')
5725
5771
  return true;
5726
5772
  // If user can manage given table in realm, return true
5727
- if (this.permissions.manage?.includes(this.tableName))
5773
+ if ((_a = this.permissions.manage) === null || _a === void 0 ? void 0 : _a.includes(this.tableName))
5728
5774
  return true;
5729
5775
  return false;
5730
5776
  }
@@ -5760,7 +5806,7 @@
5760
5806
  const permissions = getPermissionsLookupObservable(db._novip);
5761
5807
  const accessControl = getInternalAccessControlObservable(db._novip);
5762
5808
  return createSharedValueObservable(rxjs.combineLatest([membersByEmail, accessControl, permissions]).pipe(rxjs.map(([membersByEmail, accessControl, realmLookup]) => {
5763
- const reducer = (result, m) => ({ ...result, [m.id]: { ...m, realm: realmLookup[m.realmId] } });
5809
+ const reducer = (result, m) => (Object.assign(Object.assign({}, result), { [m.id]: Object.assign(Object.assign({}, m), { realm: realmLookup[m.realmId] }) }));
5764
5810
  const emailMembersById = membersByEmail.reduce(reducer, {});
5765
5811
  const membersById = accessControl.selfMembers.reduce(reducer, emailMembersById);
5766
5812
  return Object.values(membersById).filter(m => !m.accepted);
@@ -5787,15 +5833,15 @@
5787
5833
  let configuredProgramatically = false;
5788
5834
  // local sync worker - used when there's no service worker.
5789
5835
  let localSyncWorker = null;
5790
- dexie.on('ready', async (dexie) => {
5836
+ dexie.on('ready', (dexie) => __awaiter$1(this, void 0, void 0, function* () {
5791
5837
  try {
5792
- await onDbReady(dexie);
5838
+ yield onDbReady(dexie);
5793
5839
  }
5794
5840
  catch (error) {
5795
5841
  console.error(error);
5796
5842
  // Make sure to succeed with database open even if network is down.
5797
5843
  }
5798
- }, true // true = sticky
5844
+ }), true // true = sticky
5799
5845
  );
5800
5846
  /** Void starting subscribers after a close has happened. */
5801
5847
  let closed = false;
@@ -5812,7 +5858,7 @@
5812
5858
  });
5813
5859
  dexie.cloud = {
5814
5860
  version: '{version}',
5815
- options: { ...DEFAULT_OPTIONS },
5861
+ options: Object.assign({}, DEFAULT_OPTIONS),
5816
5862
  schema: null,
5817
5863
  serverState: null,
5818
5864
  get currentUserId() {
@@ -5826,15 +5872,17 @@
5826
5872
  persistedSyncState: new rxjs.BehaviorSubject(undefined),
5827
5873
  userInteraction: new rxjs.BehaviorSubject(undefined),
5828
5874
  webSocketStatus: new rxjs.BehaviorSubject('not-started'),
5829
- async login(hint) {
5830
- const db = DexieCloudDB(dexie);
5831
- await db.cloud.sync();
5832
- await login(db, hint);
5875
+ login(hint) {
5876
+ return __awaiter$1(this, void 0, void 0, function* () {
5877
+ const db = DexieCloudDB(dexie);
5878
+ yield db.cloud.sync();
5879
+ yield login(db, hint);
5880
+ });
5833
5881
  },
5834
5882
  invites: getInvitesObservable(dexie),
5835
5883
  roles: getGlobalRolesObservable(dexie),
5836
5884
  configure(options) {
5837
- options = dexie.cloud.options = { ...dexie.cloud.options, ...options };
5885
+ options = dexie.cloud.options = Object.assign(Object.assign({}, dexie.cloud.options), options);
5838
5886
  configuredProgramatically = true;
5839
5887
  if (options.databaseUrl && options.nameSuffix) {
5840
5888
  // @ts-ignore
@@ -5843,41 +5891,43 @@
5843
5891
  }
5844
5892
  updateSchemaFromOptions(dexie.cloud.schema, dexie.cloud.options);
5845
5893
  },
5846
- async sync({ wait, purpose } = { wait: true, purpose: 'push' }) {
5847
- if (wait === undefined)
5848
- wait = true;
5849
- const db = DexieCloudDB(dexie);
5850
- if (purpose === 'pull') {
5851
- const syncState = db.cloud.persistedSyncState.value;
5852
- triggerSync(db, purpose);
5853
- if (wait) {
5854
- const newSyncState = await db.cloud.persistedSyncState
5855
- .pipe(filter((newSyncState) => newSyncState?.timestamp != null &&
5856
- (!syncState || newSyncState.timestamp > syncState.timestamp)), take(1))
5857
- .toPromise();
5858
- if (newSyncState?.error) {
5859
- throw new Error(`Sync error: ` + newSyncState.error);
5894
+ sync({ wait, purpose } = { wait: true, purpose: 'push' }) {
5895
+ return __awaiter$1(this, void 0, void 0, function* () {
5896
+ if (wait === undefined)
5897
+ wait = true;
5898
+ const db = DexieCloudDB(dexie);
5899
+ if (purpose === 'pull') {
5900
+ const syncState = db.cloud.persistedSyncState.value;
5901
+ triggerSync(db, purpose);
5902
+ if (wait) {
5903
+ const newSyncState = yield db.cloud.persistedSyncState
5904
+ .pipe(filter((newSyncState) => (newSyncState === null || newSyncState === void 0 ? void 0 : newSyncState.timestamp) != null &&
5905
+ (!syncState || newSyncState.timestamp > syncState.timestamp)), take(1))
5906
+ .toPromise();
5907
+ if (newSyncState === null || newSyncState === void 0 ? void 0 : newSyncState.error) {
5908
+ throw new Error(`Sync error: ` + newSyncState.error);
5909
+ }
5860
5910
  }
5861
5911
  }
5862
- }
5863
- else if (await isSyncNeeded(db)) {
5864
- const syncState = db.cloud.persistedSyncState.value;
5865
- triggerSync(db, purpose);
5866
- if (wait) {
5867
- console.debug('db.cloud.login() is waiting for sync completion...');
5868
- await rxjs.from(Dexie.liveQuery(async () => {
5869
- const syncNeeded = await isSyncNeeded(db);
5870
- const newSyncState = await db.getPersistedSyncState();
5871
- if (newSyncState?.timestamp !== syncState?.timestamp &&
5872
- newSyncState?.error)
5873
- throw new Error(`Sync error: ` + newSyncState.error);
5874
- return syncNeeded;
5875
- }))
5876
- .pipe(filter((isNeeded) => !isNeeded), take(1))
5877
- .toPromise();
5878
- console.debug('Done waiting for sync completion because we have nothing to push anymore');
5912
+ else if (yield isSyncNeeded(db)) {
5913
+ const syncState = db.cloud.persistedSyncState.value;
5914
+ triggerSync(db, purpose);
5915
+ if (wait) {
5916
+ console.debug('db.cloud.login() is waiting for sync completion...');
5917
+ yield rxjs.from(Dexie.liveQuery(() => __awaiter$1(this, void 0, void 0, function* () {
5918
+ const syncNeeded = yield isSyncNeeded(db);
5919
+ const newSyncState = yield db.getPersistedSyncState();
5920
+ if ((newSyncState === null || newSyncState === void 0 ? void 0 : newSyncState.timestamp) !== (syncState === null || syncState === void 0 ? void 0 : syncState.timestamp) &&
5921
+ (newSyncState === null || newSyncState === void 0 ? void 0 : newSyncState.error))
5922
+ throw new Error(`Sync error: ` + newSyncState.error);
5923
+ return syncNeeded;
5924
+ })))
5925
+ .pipe(filter((isNeeded) => !isNeeded), take(1))
5926
+ .toPromise();
5927
+ console.debug('Done waiting for sync completion because we have nothing to push anymore');
5928
+ }
5879
5929
  }
5880
- }
5930
+ });
5881
5931
  },
5882
5932
  permissions(obj, tableName) {
5883
5933
  return permissions(dexie._novip, obj, tableName);
@@ -5889,7 +5939,8 @@
5889
5939
  return generateKey(dexie.cloud.schema[this.name].idPrefix || '', shardKey);
5890
5940
  };
5891
5941
  dexie.Table.prototype.idPrefix = function () {
5892
- return this.db.cloud.schema?.[this.name]?.idPrefix || '';
5942
+ var _a, _b;
5943
+ return ((_b = (_a = this.db.cloud.schema) === null || _a === void 0 ? void 0 : _a[this.name]) === null || _b === void 0 ? void 0 : _b.idPrefix) || '';
5893
5944
  };
5894
5945
  dexie.use(createMutationTrackingMiddleware({
5895
5946
  currentUserObservable: dexie.cloud.currentUser,
@@ -5897,160 +5948,167 @@
5897
5948
  }));
5898
5949
  dexie.use(createImplicitPropSetterMiddleware(DexieCloudDB(dexie)));
5899
5950
  dexie.use(createIdGenerationMiddleware(DexieCloudDB(dexie)));
5900
- async function onDbReady(dexie) {
5901
- closed = false; // As Dexie calls us, we are not closed anymore. Maybe reopened? Remember db.ready event is registered with sticky flag!
5902
- const db = DexieCloudDB(dexie);
5903
- // Setup default GUI:
5904
- if (!IS_SERVICE_WORKER) {
5905
- if (!db.cloud.options?.customLoginGui) {
5906
- subscriptions.push(setupDefaultGUI(dexie));
5907
- }
5908
- subscriptions.push(computeSyncState(db).subscribe(dexie.cloud.syncState));
5909
- }
5910
- //verifyConfig(db.cloud.options); Not needed (yet at least!)
5911
- // Verify the user has allowed version increment.
5912
- if (!db.tables.every((table) => table.core)) {
5913
- throwVersionIncrementNeeded();
5914
- }
5915
- const swRegistrations = 'serviceWorker' in navigator
5916
- ? await navigator.serviceWorker.getRegistrations()
5917
- : [];
5918
- const initiallySynced = await db.transaction('rw', db.$syncState, async () => {
5919
- const { options, schema } = db.cloud;
5920
- const [persistedOptions, persistedSchema, persistedSyncState] = await Promise.all([
5921
- db.getOptions(),
5922
- db.getSchema(),
5923
- db.getPersistedSyncState(),
5924
- ]);
5925
- if (!configuredProgramatically) {
5926
- // Options not specified programatically (use case for SW!)
5927
- // Take persisted options:
5928
- db.cloud.options = persistedOptions || null;
5951
+ function onDbReady(dexie) {
5952
+ var _a, _b, _c, _d, _e, _f, _g;
5953
+ return __awaiter$1(this, void 0, void 0, function* () {
5954
+ closed = false; // As Dexie calls us, we are not closed anymore. Maybe reopened? Remember db.ready event is registered with sticky flag!
5955
+ const db = DexieCloudDB(dexie);
5956
+ // Setup default GUI:
5957
+ if (typeof window !== 'undefined' && typeof document !== 'undefined') {
5958
+ if (!((_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.customLoginGui)) {
5959
+ subscriptions.push(setupDefaultGUI(dexie));
5960
+ }
5929
5961
  }
5930
- else if (!persistedOptions ||
5931
- JSON.stringify(persistedOptions) !== JSON.stringify(options)) {
5932
- // Update persisted options:
5933
- if (!options)
5934
- throw new Error(`Internal error`); // options cannot be null if configuredProgramatically is set.
5935
- await db.$syncState.put(options, 'options');
5962
+ if (!db.cloud.isServiceWorkerDB) {
5963
+ subscriptions.push(computeSyncState(db).subscribe(dexie.cloud.syncState));
5936
5964
  }
5937
- if (db.cloud.options?.tryUseServiceWorker &&
5938
- 'serviceWorker' in navigator &&
5939
- swRegistrations.length > 0 &&
5940
- !DISABLE_SERVICEWORKER_STRATEGY) {
5941
- // * Configured for using service worker if available.
5942
- // * Browser supports service workers
5943
- // * There are at least one service worker registration
5944
- console.debug('Dexie Cloud Addon: Using service worker');
5945
- db.cloud.usingServiceWorker = true;
5965
+ //verifyConfig(db.cloud.options); Not needed (yet at least!)
5966
+ // Verify the user has allowed version increment.
5967
+ if (!db.tables.every((table) => table.core)) {
5968
+ throwVersionIncrementNeeded();
5946
5969
  }
5947
- else {
5948
- // Not configured for using service worker or no service worker
5949
- // registration exists. Don't rely on service worker to do any job.
5950
- // Use LocalSyncWorker instead.
5951
- if (db.cloud.options?.tryUseServiceWorker && !IS_SERVICE_WORKER) {
5952
- console.debug('dexie-cloud-addon: Not using service worker.', swRegistrations.length === 0
5953
- ? 'No SW registrations found.'
5954
- : 'serviceWorker' in navigator && DISABLE_SERVICEWORKER_STRATEGY
5955
- ? 'Avoiding SW background sync and SW periodic bg sync for this browser due to browser bugs.'
5956
- : 'navigator.serviceWorker not present');
5970
+ const swRegistrations = 'serviceWorker' in navigator
5971
+ ? yield navigator.serviceWorker.getRegistrations()
5972
+ : [];
5973
+ const initiallySynced = yield db.transaction('rw', db.$syncState, () => __awaiter$1(this, void 0, void 0, function* () {
5974
+ var _h, _j;
5975
+ const { options, schema } = db.cloud;
5976
+ const [persistedOptions, persistedSchema, persistedSyncState] = yield Promise.all([
5977
+ db.getOptions(),
5978
+ db.getSchema(),
5979
+ db.getPersistedSyncState(),
5980
+ ]);
5981
+ if (!configuredProgramatically) {
5982
+ // Options not specified programatically (use case for SW!)
5983
+ // Take persisted options:
5984
+ db.cloud.options = persistedOptions || null;
5957
5985
  }
5958
- db.cloud.usingServiceWorker = false;
5959
- }
5960
- updateSchemaFromOptions(schema, db.cloud.options);
5961
- updateSchemaFromOptions(persistedSchema, db.cloud.options);
5962
- if (!schema) {
5963
- // Database opened dynamically (use case for SW!)
5964
- // Take persisted schema:
5965
- db.cloud.schema = persistedSchema || null;
5966
- }
5967
- else if (!persistedSchema ||
5968
- JSON.stringify(persistedSchema) !== JSON.stringify(schema)) {
5969
- // Update persisted schema (but don't overwrite table prefixes)
5970
- const newPersistedSchema = persistedSchema || {};
5971
- for (const [table, tblSchema] of Object.entries(schema)) {
5972
- const newTblSchema = newPersistedSchema[table];
5973
- if (!newTblSchema) {
5974
- newPersistedSchema[table] = { ...tblSchema };
5986
+ else if (!persistedOptions ||
5987
+ JSON.stringify(persistedOptions) !== JSON.stringify(options)) {
5988
+ // Update persisted options:
5989
+ if (!options)
5990
+ throw new Error(`Internal error`); // options cannot be null if configuredProgramatically is set.
5991
+ yield db.$syncState.put(options, 'options');
5992
+ }
5993
+ if (((_h = db.cloud.options) === null || _h === void 0 ? void 0 : _h.tryUseServiceWorker) &&
5994
+ 'serviceWorker' in navigator &&
5995
+ swRegistrations.length > 0 &&
5996
+ !DISABLE_SERVICEWORKER_STRATEGY) {
5997
+ // * Configured for using service worker if available.
5998
+ // * Browser supports service workers
5999
+ // * There are at least one service worker registration
6000
+ console.debug('Dexie Cloud Addon: Using service worker');
6001
+ db.cloud.usingServiceWorker = true;
6002
+ }
6003
+ else {
6004
+ // Not configured for using service worker or no service worker
6005
+ // registration exists. Don't rely on service worker to do any job.
6006
+ // Use LocalSyncWorker instead.
6007
+ if (((_j = db.cloud.options) === null || _j === void 0 ? void 0 : _j.tryUseServiceWorker) &&
6008
+ !db.cloud.isServiceWorkerDB) {
6009
+ console.debug('dexie-cloud-addon: Not using service worker.', swRegistrations.length === 0
6010
+ ? 'No SW registrations found.'
6011
+ : 'serviceWorker' in navigator && DISABLE_SERVICEWORKER_STRATEGY
6012
+ ? 'Avoiding SW background sync and SW periodic bg sync for this browser due to browser bugs.'
6013
+ : 'navigator.serviceWorker not present');
5975
6014
  }
5976
- else {
5977
- newTblSchema.markedForSync = tblSchema.markedForSync;
5978
- tblSchema.deleted = newTblSchema.deleted;
5979
- newTblSchema.generatedGlobalId = tblSchema.generatedGlobalId;
6015
+ db.cloud.usingServiceWorker = false;
6016
+ }
6017
+ updateSchemaFromOptions(schema, db.cloud.options);
6018
+ updateSchemaFromOptions(persistedSchema, db.cloud.options);
6019
+ if (!schema) {
6020
+ // Database opened dynamically (use case for SW!)
6021
+ // Take persisted schema:
6022
+ db.cloud.schema = persistedSchema || null;
6023
+ }
6024
+ else if (!persistedSchema ||
6025
+ JSON.stringify(persistedSchema) !== JSON.stringify(schema)) {
6026
+ // Update persisted schema (but don't overwrite table prefixes)
6027
+ const newPersistedSchema = persistedSchema || {};
6028
+ for (const [table, tblSchema] of Object.entries(schema)) {
6029
+ const newTblSchema = newPersistedSchema[table];
6030
+ if (!newTblSchema) {
6031
+ newPersistedSchema[table] = Object.assign({}, tblSchema);
6032
+ }
6033
+ else {
6034
+ newTblSchema.markedForSync = tblSchema.markedForSync;
6035
+ tblSchema.deleted = newTblSchema.deleted;
6036
+ newTblSchema.generatedGlobalId = tblSchema.generatedGlobalId;
6037
+ }
5980
6038
  }
6039
+ yield db.$syncState.put(newPersistedSchema, 'schema');
6040
+ // Make sure persisted table prefixes are being used instead of computed ones:
6041
+ // Let's assign all props as the newPersistedSchems should be what we should be working with.
6042
+ Object.assign(schema, newPersistedSchema);
5981
6043
  }
5982
- await db.$syncState.put(newPersistedSchema, 'schema');
5983
- // Make sure persisted table prefixes are being used instead of computed ones:
5984
- // Let's assign all props as the newPersistedSchems should be what we should be working with.
5985
- Object.assign(schema, newPersistedSchema);
6044
+ return persistedSyncState === null || persistedSyncState === void 0 ? void 0 : persistedSyncState.initiallySynced;
6045
+ }));
6046
+ if (initiallySynced) {
6047
+ db.setInitiallySynced(true);
5986
6048
  }
5987
- return persistedSyncState?.initiallySynced;
5988
- });
5989
- if (initiallySynced) {
5990
- db.setInitiallySynced(true);
5991
- }
5992
- verifySchema(db);
5993
- if (db.cloud.options?.databaseUrl && !initiallySynced) {
5994
- await performInitialSync(db, db.cloud.options, db.cloud.schema);
5995
- db.setInitiallySynced(true);
5996
- }
5997
- // Manage CurrentUser observable:
5998
- throwIfClosed();
5999
- if (!IS_SERVICE_WORKER) {
6000
- subscriptions.push(Dexie.liveQuery(() => db.getCurrentUser()).subscribe(currentUserEmitter));
6001
- // Manage PersistendSyncState observable:
6002
- subscriptions.push(Dexie.liveQuery(() => db.getPersistedSyncState()).subscribe(db.cloud.persistedSyncState));
6003
- // Wait till currentUser and persistedSyncState gets populated
6004
- // with things from the database and not just the default values.
6005
- // This is so that when db.open() completes, user should be safe
6006
- // to subscribe to these observables and get actual data.
6007
- await rxjs.combineLatest([
6008
- currentUserEmitter.pipe(skip(1), take(1)),
6009
- db.cloud.persistedSyncState.pipe(skip(1), take(1)),
6010
- ]).toPromise();
6011
- }
6012
- // HERE: If requireAuth, do athentication now.
6013
- if (db.cloud.options?.requireAuth) {
6014
- await login(db);
6015
- }
6016
- if (localSyncWorker)
6017
- localSyncWorker.stop();
6018
- localSyncWorker = null;
6019
- throwIfClosed();
6020
- if (db.cloud.usingServiceWorker && db.cloud.options?.databaseUrl) {
6021
- registerSyncEvent(db, 'push').catch(() => { });
6022
- registerPeriodicSyncEvent(db).catch(() => { });
6023
- }
6024
- else if (db.cloud.options?.databaseUrl &&
6025
- db.cloud.schema &&
6026
- !IS_SERVICE_WORKER) {
6027
- // There's no SW. Start SyncWorker instead.
6028
- localSyncWorker = LocalSyncWorker(db, db.cloud.options, db.cloud.schema);
6029
- localSyncWorker.start();
6030
- triggerSync(db, 'push');
6031
- }
6032
- // Listen to online event and do sync.
6033
- throwIfClosed();
6034
- if (!IS_SERVICE_WORKER) {
6035
- subscriptions.push(rxjs.fromEvent(self, 'online').subscribe(() => {
6036
- console.debug('online!');
6037
- db.syncStateChangedEvent.next({
6038
- phase: 'not-in-sync',
6039
- });
6049
+ verifySchema(db);
6050
+ if (((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.databaseUrl) && !initiallySynced) {
6051
+ yield performInitialSync(db, db.cloud.options, db.cloud.schema);
6052
+ db.setInitiallySynced(true);
6053
+ }
6054
+ // Manage CurrentUser observable:
6055
+ throwIfClosed();
6056
+ if (!db.cloud.isServiceWorkerDB) {
6057
+ subscriptions.push(Dexie.liveQuery(() => db.getCurrentUser()).subscribe(currentUserEmitter));
6058
+ // Manage PersistendSyncState observable:
6059
+ subscriptions.push(Dexie.liveQuery(() => db.getPersistedSyncState()).subscribe(db.cloud.persistedSyncState));
6060
+ // Wait till currentUser and persistedSyncState gets populated
6061
+ // with things from the database and not just the default values.
6062
+ // This is so that when db.open() completes, user should be safe
6063
+ // to subscribe to these observables and get actual data.
6064
+ yield rxjs.combineLatest([
6065
+ currentUserEmitter.pipe(skip(1), take(1)),
6066
+ db.cloud.persistedSyncState.pipe(skip(1), take(1)),
6067
+ ]).toPromise();
6068
+ }
6069
+ // HERE: If requireAuth, do athentication now.
6070
+ if ((_c = db.cloud.options) === null || _c === void 0 ? void 0 : _c.requireAuth) {
6071
+ yield login(db);
6072
+ }
6073
+ if (localSyncWorker)
6074
+ localSyncWorker.stop();
6075
+ localSyncWorker = null;
6076
+ throwIfClosed();
6077
+ if (db.cloud.usingServiceWorker && ((_d = db.cloud.options) === null || _d === void 0 ? void 0 : _d.databaseUrl)) {
6078
+ registerSyncEvent(db, 'push').catch(() => { });
6079
+ registerPeriodicSyncEvent(db).catch(() => { });
6080
+ }
6081
+ else if (((_e = db.cloud.options) === null || _e === void 0 ? void 0 : _e.databaseUrl) &&
6082
+ db.cloud.schema &&
6083
+ !db.cloud.isServiceWorkerDB) {
6084
+ // There's no SW. Start SyncWorker instead.
6085
+ localSyncWorker = LocalSyncWorker(db, db.cloud.options, db.cloud.schema);
6086
+ localSyncWorker.start();
6040
6087
  triggerSync(db, 'push');
6041
- }), rxjs.fromEvent(self, 'offline').subscribe(() => {
6042
- console.debug('offline!');
6043
- db.syncStateChangedEvent.next({
6044
- phase: 'offline',
6045
- });
6046
- }));
6047
- }
6048
- // Connect WebSocket only if we're a browser window
6049
- if (typeof window !== 'undefined' &&
6050
- !IS_SERVICE_WORKER &&
6051
- db.cloud.options?.databaseUrl) {
6052
- subscriptions.push(connectWebSocket(db));
6053
- }
6088
+ }
6089
+ // Listen to online event and do sync.
6090
+ throwIfClosed();
6091
+ if (!db.cloud.isServiceWorkerDB) {
6092
+ subscriptions.push(rxjs.fromEvent(self, 'online').subscribe(() => {
6093
+ console.debug('online!');
6094
+ db.syncStateChangedEvent.next({
6095
+ phase: 'not-in-sync',
6096
+ });
6097
+ triggerSync(db, 'push');
6098
+ }), rxjs.fromEvent(self, 'offline').subscribe(() => {
6099
+ console.debug('offline!');
6100
+ db.syncStateChangedEvent.next({
6101
+ phase: 'offline',
6102
+ });
6103
+ }));
6104
+ }
6105
+ // Connect WebSocket unless we
6106
+ if (((_f = db.cloud.options) === null || _f === void 0 ? void 0 : _f.databaseUrl) &&
6107
+ !((_g = db.cloud.options) === null || _g === void 0 ? void 0 : _g.disableWebSocket) &&
6108
+ !IS_SERVICE_WORKER) {
6109
+ subscriptions.push(connectWebSocket(db));
6110
+ }
6111
+ });
6054
6112
  }
6055
6113
  }
6056
6114
  dexieCloud.version = '{version}';