dexie-cloud-addon 4.3.5 → 4.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/TODO-SOCIALAUTH.md +1 -1
- package/dist/modern/DexieCloudAPI.d.ts +2 -0
- package/dist/modern/authentication/handleOAuthCallback.d.ts +0 -4
- package/dist/modern/default-ui/OptionButton.d.ts +0 -4
- package/dist/modern/dexie-cloud-addon.js +68 -122
- package/dist/modern/dexie-cloud-addon.js.map +1 -1
- package/dist/modern/dexie-cloud-addon.min.js +1 -1
- package/dist/modern/dexie-cloud-addon.min.js.map +1 -1
- package/dist/modern/service-worker.js +68 -122
- package/dist/modern/service-worker.js.map +1 -1
- package/dist/modern/service-worker.min.js +1 -1
- package/dist/modern/service-worker.min.js.map +1 -1
- package/dist/modern/types/DXCUserInteraction.d.ts +1 -3
- package/dist/umd/dexie-cloud-addon.js +69 -123
- package/dist/umd/dexie-cloud-addon.js.map +1 -1
- package/dist/umd/dexie-cloud-addon.min.js +1 -1
- package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
- package/dist/umd/service-worker.js +69 -123
- package/dist/umd/service-worker.js.map +1 -1
- package/dist/umd/service-worker.min.js +1 -1
- package/dist/umd/service-worker.min.js.map +1 -1
- package/package.json +1 -1
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* ==========================================================================
|
|
10
10
|
*
|
|
11
|
-
* Version 4.3.
|
|
11
|
+
* Version 4.3.7, Wed Jan 28 2026
|
|
12
12
|
*
|
|
13
13
|
* https://dexie.org
|
|
14
14
|
*
|
|
@@ -1145,73 +1145,19 @@ class TokenErrorResponseError extends Error {
|
|
|
1145
1145
|
}
|
|
1146
1146
|
}
|
|
1147
1147
|
|
|
1148
|
-
/**
|
|
1149
|
-
const
|
|
1150
|
-
/** Default SVG icons for built-in OAuth providers */
|
|
1151
|
-
const ProviderIcons = {
|
|
1152
|
-
google: `<svg viewBox="0 0 24 24" width="20" height="20"><path fill="#4285F4" d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z"/><path fill="#34A853" d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z"/><path fill="#FBBC05" d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z"/><path fill="#EA4335" d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z"/></svg>`,
|
|
1153
|
-
github: `<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z"/></svg>`,
|
|
1154
|
-
microsoft: `<svg viewBox="0 0 24 24" width="20" height="20"><rect fill="#F25022" x="1" y="1" width="10" height="10"/><rect fill="#00A4EF" x="1" y="13" width="10" height="10"/><rect fill="#7FBA00" x="13" y="1" width="10" height="10"/><rect fill="#FFB900" x="13" y="13" width="10" height="10"/></svg>`,
|
|
1155
|
-
apple: `<svg viewBox="0 0 24 24" width="20" height="20" fill="currentColor"><path d="M18.71 19.5c-.83 1.24-1.71 2.45-3.05 2.47-1.34.03-1.77-.79-3.29-.79-1.53 0-2 .77-3.27.82-1.31.05-2.3-1.32-3.14-2.53C4.25 17 2.94 12.45 4.7 9.39c.87-1.52 2.43-2.48 4.12-2.51 1.28-.02 2.5.87 3.29.87.78 0 2.26-1.07 3.81-.91.65.03 2.47.26 3.64 1.98-.09.06-2.17 1.28-2.15 3.81.03 3.02 2.65 4.03 2.68 4.04-.03.07-.42 1.44-1.38 2.83M13 3.5c.73-.83 1.94-1.46 2.94-1.5.13 1.17-.34 2.35-1.04 3.19-.69.85-1.83 1.51-2.95 1.42-.15-1.15.41-2.35 1.05-3.11z"/></svg>`,
|
|
1156
|
-
};
|
|
1157
|
-
/** Email/envelope icon for OTP option */
|
|
1158
|
-
const EmailIcon = `<svg viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M22 6L12 13 2 6"/></svg>`;
|
|
1159
|
-
/**
|
|
1160
|
-
* Fetches SVG content from a URL and caches it.
|
|
1161
|
-
* Returns the SVG string or null if fetch fails.
|
|
1162
|
-
*/
|
|
1163
|
-
function fetchSvgIcon(url) {
|
|
1164
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1165
|
-
if (svgCache[url]) {
|
|
1166
|
-
return svgCache[url];
|
|
1167
|
-
}
|
|
1168
|
-
try {
|
|
1169
|
-
const res = yield fetch(url);
|
|
1170
|
-
if (res.ok) {
|
|
1171
|
-
const svg = yield res.text();
|
|
1172
|
-
// Validate it looks like SVG
|
|
1173
|
-
if (svg.includes('<svg')) {
|
|
1174
|
-
svgCache[url] = svg;
|
|
1175
|
-
return svg;
|
|
1176
|
-
}
|
|
1177
|
-
}
|
|
1178
|
-
}
|
|
1179
|
-
catch (_a) {
|
|
1180
|
-
// Silently fail - will show no icon
|
|
1181
|
-
}
|
|
1182
|
-
return null;
|
|
1183
|
-
});
|
|
1184
|
-
}
|
|
1148
|
+
/** Email/envelope icon data URL for OTP option */
|
|
1149
|
+
const EmailIcon = `data:image/svg+xml;base64,${btoa('<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="#666666" stroke-width="2"><rect x="2" y="4" width="20" height="16" rx="2"/><path d="M22 6L12 13 2 6"/></svg>')}`;
|
|
1185
1150
|
/**
|
|
1186
1151
|
* Converts an OAuthProviderInfo to a generic DXCOption.
|
|
1187
|
-
* Fetches SVG icons from URLs if needed.
|
|
1188
1152
|
*/
|
|
1189
1153
|
function providerToOption(provider) {
|
|
1190
|
-
return
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
// If provider has iconUrl pointing to SVG, fetch and inline it
|
|
1198
|
-
else if ((_a = provider.iconUrl) === null || _a === void 0 ? void 0 : _a.toLowerCase().endsWith('.svg')) {
|
|
1199
|
-
const fetched = yield fetchSvgIcon(provider.iconUrl);
|
|
1200
|
-
if (fetched) {
|
|
1201
|
-
iconSvg = fetched;
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
return {
|
|
1205
|
-
name: 'provider',
|
|
1206
|
-
value: provider.name,
|
|
1207
|
-
displayName: `Continue with ${provider.displayName}`,
|
|
1208
|
-
iconSvg,
|
|
1209
|
-
// If iconUrl is not SVG, pass it through for img tag rendering
|
|
1210
|
-
iconUrl: (!iconSvg && provider.iconUrl) ? provider.iconUrl : undefined,
|
|
1211
|
-
// Use provider type as style hint for branding
|
|
1212
|
-
styleHint: provider.type,
|
|
1213
|
-
};
|
|
1214
|
-
});
|
|
1154
|
+
return {
|
|
1155
|
+
name: 'provider',
|
|
1156
|
+
value: provider.name,
|
|
1157
|
+
displayName: `Continue with ${provider.displayName}`,
|
|
1158
|
+
iconUrl: provider.iconUrl,
|
|
1159
|
+
styleHint: provider.type,
|
|
1160
|
+
};
|
|
1215
1161
|
}
|
|
1216
1162
|
function interactWithUser(userInteraction, req) {
|
|
1217
1163
|
return new Promise((resolve, reject) => {
|
|
@@ -1362,8 +1308,8 @@ function confirmLogout(userInteraction, currentUserId, numUnsyncedChanges) {
|
|
|
1362
1308
|
*/
|
|
1363
1309
|
function promptForProvider(userInteraction_1, providers_1, otpEnabled_1) {
|
|
1364
1310
|
return __awaiter(this, arguments, void 0, function* (userInteraction, providers, otpEnabled, title = 'Choose login method', alerts = []) {
|
|
1365
|
-
// Convert providers to generic options
|
|
1366
|
-
const providerOptions =
|
|
1311
|
+
// Convert providers to generic options
|
|
1312
|
+
const providerOptions = providers.map(providerToOption);
|
|
1367
1313
|
// Build the options array
|
|
1368
1314
|
const options = [...providerOptions];
|
|
1369
1315
|
// Add OTP option if enabled
|
|
@@ -1372,7 +1318,7 @@ function promptForProvider(userInteraction_1, providers_1, otpEnabled_1) {
|
|
|
1372
1318
|
name: 'otp',
|
|
1373
1319
|
value: 'email',
|
|
1374
1320
|
displayName: 'Continue with email',
|
|
1375
|
-
|
|
1321
|
+
iconUrl: EmailIcon,
|
|
1376
1322
|
styleHint: 'otp',
|
|
1377
1323
|
});
|
|
1378
1324
|
}
|
|
@@ -3740,10 +3686,12 @@ function exchangeOAuthCode(options) {
|
|
|
3740
3686
|
mode: 'cors',
|
|
3741
3687
|
});
|
|
3742
3688
|
if (!res.ok) {
|
|
3689
|
+
// Read body once as text to avoid stream consumption issues
|
|
3690
|
+
const bodyText = yield res.text().catch(() => res.statusText);
|
|
3743
3691
|
if (res.status === 400 || res.status === 401) {
|
|
3744
|
-
// Try to parse error response
|
|
3692
|
+
// Try to parse error response as JSON
|
|
3745
3693
|
try {
|
|
3746
|
-
const errorResponse =
|
|
3694
|
+
const errorResponse = JSON.parse(bodyText);
|
|
3747
3695
|
if (errorResponse.type === 'error') {
|
|
3748
3696
|
// Check for specific error codes
|
|
3749
3697
|
if (errorResponse.messageCode === 'INVALID_OTP') {
|
|
@@ -3760,8 +3708,7 @@ function exchangeOAuthCode(options) {
|
|
|
3760
3708
|
// Fall through to generic error
|
|
3761
3709
|
}
|
|
3762
3710
|
}
|
|
3763
|
-
|
|
3764
|
-
throw new OAuthError('provider_error', undefined, `Token exchange failed: ${res.status} ${errorText}`);
|
|
3711
|
+
throw new OAuthError('provider_error', undefined, `Token exchange failed: ${res.status} ${bodyText}`);
|
|
3765
3712
|
}
|
|
3766
3713
|
const response = yield res.json();
|
|
3767
3714
|
if (response.type === 'error') {
|
|
@@ -3867,9 +3814,8 @@ function buildOAuthLoginUrl(options) {
|
|
|
3867
3814
|
* ```
|
|
3868
3815
|
*/
|
|
3869
3816
|
function startOAuthRedirect(options) {
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
sessionStorage.setItem('dexie-cloud-oauth-provider', options.provider);
|
|
3817
|
+
if (typeof window === 'undefined') {
|
|
3818
|
+
throw new Error('OAuth redirect requires a browser environment');
|
|
3873
3819
|
}
|
|
3874
3820
|
const loginUrl = buildOAuthLoginUrl(options);
|
|
3875
3821
|
window.location.href = loginUrl;
|
|
@@ -3895,7 +3841,21 @@ function otpFetchTokenCallback(db) {
|
|
|
3895
3841
|
}
|
|
3896
3842
|
// Handle OAuth provider login via redirect
|
|
3897
3843
|
if (hints === null || hints === void 0 ? void 0 : hints.provider) {
|
|
3898
|
-
|
|
3844
|
+
let resolvedRedirectUri = undefined;
|
|
3845
|
+
if (hints.redirectPath) {
|
|
3846
|
+
// If redirectPath is absolute, use as is. If relative, resolve against current location
|
|
3847
|
+
if (/^https?:\/\//i.test(hints.redirectPath)) {
|
|
3848
|
+
resolvedRedirectUri = hints.redirectPath;
|
|
3849
|
+
}
|
|
3850
|
+
else if (typeof window !== 'undefined' && window.location) {
|
|
3851
|
+
// Use URL constructor to resolve relative path
|
|
3852
|
+
resolvedRedirectUri = new URL(hints.redirectPath, window.location.href).toString();
|
|
3853
|
+
}
|
|
3854
|
+
else if (typeof location !== 'undefined' && location.href) {
|
|
3855
|
+
resolvedRedirectUri = new URL(hints.redirectPath, location.href).toString();
|
|
3856
|
+
}
|
|
3857
|
+
}
|
|
3858
|
+
initiateOAuthRedirect(db, hints.provider, resolvedRedirectUri);
|
|
3899
3859
|
// This function never returns - page navigates away
|
|
3900
3860
|
throw new OAuthRedirectError(hints.provider);
|
|
3901
3861
|
}
|
|
@@ -3975,7 +3935,8 @@ function otpFetchTokenCallback(db) {
|
|
|
3975
3935
|
const res1 = yield fetch(`${url}/token`, {
|
|
3976
3936
|
body: JSON.stringify(tokenRequest),
|
|
3977
3937
|
method: 'post',
|
|
3978
|
-
headers: { 'Content-Type': 'application/json'
|
|
3938
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3939
|
+
mode: 'cors',
|
|
3979
3940
|
});
|
|
3980
3941
|
if (res1.status !== 200) {
|
|
3981
3942
|
const errMsg = yield res1.text();
|
|
@@ -4039,13 +4000,18 @@ function otpFetchTokenCallback(db) {
|
|
|
4039
4000
|
* the user is redirected back with a dxc-auth query parameter that is
|
|
4040
4001
|
* automatically detected by db.cloud.configure().
|
|
4041
4002
|
*/
|
|
4042
|
-
function initiateOAuthRedirect(db, provider) {
|
|
4003
|
+
function initiateOAuthRedirect(db, provider, redirectUriOverride) {
|
|
4043
4004
|
var _a, _b;
|
|
4044
4005
|
const url = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl;
|
|
4045
4006
|
if (!url)
|
|
4046
4007
|
throw new Error(`No database URL given.`);
|
|
4047
|
-
const redirectUri =
|
|
4048
|
-
(
|
|
4008
|
+
const redirectUri = redirectUriOverride ||
|
|
4009
|
+
((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.oauthRedirectUri) ||
|
|
4010
|
+
(typeof location !== 'undefined' ? location.href : undefined);
|
|
4011
|
+
// CodeRabbit suggested to fail fast here, but the only situation where
|
|
4012
|
+
// redirectUri would be undefined is in non-browser environments, and
|
|
4013
|
+
// in those environments OAuth redirect does not make sense anyway
|
|
4014
|
+
// and will fail fast in startOAuthRedirect().
|
|
4049
4015
|
// Start OAuth redirect flow - page navigates away
|
|
4050
4016
|
startOAuthRedirect({
|
|
4051
4017
|
databaseUrl: url,
|
|
@@ -5836,13 +5802,13 @@ const Styles = {
|
|
|
5836
5802
|
color: "#3c4043"
|
|
5837
5803
|
},
|
|
5838
5804
|
ProviderGitHub: {
|
|
5839
|
-
backgroundColor: "#
|
|
5840
|
-
border: "1px solid #
|
|
5841
|
-
color: "#
|
|
5805
|
+
backgroundColor: "#ffffff",
|
|
5806
|
+
border: "1px solid #dadce0",
|
|
5807
|
+
color: "#181717"
|
|
5842
5808
|
},
|
|
5843
5809
|
ProviderMicrosoft: {
|
|
5844
5810
|
backgroundColor: "#ffffff",
|
|
5845
|
-
border: "1px solid #
|
|
5811
|
+
border: "1px solid #dadce0",
|
|
5846
5812
|
color: "#5e5e5e"
|
|
5847
5813
|
},
|
|
5848
5814
|
ProviderApple: {
|
|
@@ -5851,9 +5817,9 @@ const Styles = {
|
|
|
5851
5817
|
color: "#ffffff"
|
|
5852
5818
|
},
|
|
5853
5819
|
ProviderCustom: {
|
|
5854
|
-
backgroundColor: "#
|
|
5855
|
-
border: "1px solid #
|
|
5856
|
-
color: "#
|
|
5820
|
+
backgroundColor: "#ffffff",
|
|
5821
|
+
border: "1px solid #dadce0",
|
|
5822
|
+
color: "#181717"
|
|
5857
5823
|
},
|
|
5858
5824
|
// Divider styles
|
|
5859
5825
|
Divider: {
|
|
@@ -5944,39 +5910,13 @@ function getOptionStyle(styleHint) {
|
|
|
5944
5910
|
* Generic button component for selectable options.
|
|
5945
5911
|
* Displays the option's icon and display name.
|
|
5946
5912
|
*
|
|
5947
|
-
* The icon can be:
|
|
5948
|
-
* - Inline SVG (iconSvg) - rendered directly with dangerouslySetInnerHTML
|
|
5949
|
-
* - Image URL (iconUrl) - rendered as an img tag
|
|
5950
|
-
*
|
|
5951
5913
|
* Style is determined by the styleHint property for branding purposes.
|
|
5952
5914
|
*/
|
|
5953
5915
|
function OptionButton({ option, onClick }) {
|
|
5954
|
-
const { displayName, iconUrl,
|
|
5916
|
+
const { displayName, iconUrl, styleHint } = option;
|
|
5955
5917
|
const style = getOptionStyle(styleHint);
|
|
5956
|
-
// Get the text color from the button style for SVG fill processing
|
|
5957
|
-
const textColor = style.color || '#000000';
|
|
5958
|
-
// Process SVG to replace currentColor with actual text color
|
|
5959
|
-
const processedSvg = iconSvg
|
|
5960
|
-
? iconSvg
|
|
5961
|
-
.replace(/fill="currentColor"/gi, `fill="${textColor}"`)
|
|
5962
|
-
.replace(/fill='currentColor'/gi, `fill='${textColor}'`)
|
|
5963
|
-
.replace(/stroke="currentColor"/gi, `stroke="${textColor}"`)
|
|
5964
|
-
.replace(/stroke='currentColor'/gi, `stroke='${textColor}'`)
|
|
5965
|
-
: null;
|
|
5966
|
-
// Render the appropriate icon
|
|
5967
|
-
const renderIcon = () => {
|
|
5968
|
-
// Inline SVG
|
|
5969
|
-
if (processedSvg) {
|
|
5970
|
-
return (_$1("span", { style: Styles.ProviderButtonIcon, "aria-hidden": "true", dangerouslySetInnerHTML: { __html: processedSvg } }));
|
|
5971
|
-
}
|
|
5972
|
-
// Image URL
|
|
5973
|
-
if (iconUrl) {
|
|
5974
|
-
return (_$1("img", { src: iconUrl, alt: "", style: Styles.ProviderButtonIcon, "aria-hidden": "true" }));
|
|
5975
|
-
}
|
|
5976
|
-
return null;
|
|
5977
|
-
};
|
|
5978
5918
|
return (_$1("button", { type: "button", style: style, onClick: onClick, class: `dxc-option-btn${styleHint ? ` dxc-option-${styleHint}` : ''}`, "aria-label": displayName },
|
|
5979
|
-
|
|
5919
|
+
iconUrl && (_$1("img", { src: iconUrl, alt: "", style: Styles.ProviderButtonIcon, "aria-hidden": "true" })),
|
|
5980
5920
|
_$1("span", { style: Styles.ProviderButtonText }, displayName)));
|
|
5981
5921
|
}
|
|
5982
5922
|
/**
|
|
@@ -6763,7 +6703,7 @@ function dexieCloud(dexie) {
|
|
|
6763
6703
|
const syncComplete = new Subject();
|
|
6764
6704
|
dexie.cloud = {
|
|
6765
6705
|
// @ts-ignore
|
|
6766
|
-
version: "4.3.
|
|
6706
|
+
version: "4.3.7",
|
|
6767
6707
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
6768
6708
|
schema: null,
|
|
6769
6709
|
get currentUserId() {
|
|
@@ -7033,12 +6973,18 @@ function dexieCloud(dexie) {
|
|
|
7033
6973
|
pendingOAuthError = null; // Clear pending error
|
|
7034
6974
|
console.debug('[dexie-cloud] Showing OAuth error:', error.message);
|
|
7035
6975
|
// Show alert to user about the OAuth error
|
|
7036
|
-
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
6976
|
+
// Guard so UI errors don't abort initialization
|
|
6977
|
+
try {
|
|
6978
|
+
yield alertUser(db.cloud.userInteraction, 'Authentication Error', {
|
|
6979
|
+
type: 'error',
|
|
6980
|
+
messageCode: 'GENERIC_ERROR',
|
|
6981
|
+
message: error.message,
|
|
6982
|
+
messageParams: { provider: error.provider || 'unknown' }
|
|
6983
|
+
});
|
|
6984
|
+
}
|
|
6985
|
+
catch (uiError) {
|
|
6986
|
+
console.error('[dexie-cloud] Failed to show OAuth error alert:', uiError);
|
|
6987
|
+
}
|
|
7042
6988
|
}
|
|
7043
6989
|
// Process pending OAuth callback if present (from dxc-auth redirect)
|
|
7044
6990
|
if (pendingOAuthCode && !db.cloud.isServiceWorkerDB) {
|
|
@@ -7142,7 +7088,7 @@ function dexieCloud(dexie) {
|
|
|
7142
7088
|
}
|
|
7143
7089
|
}
|
|
7144
7090
|
// @ts-ignore
|
|
7145
|
-
dexieCloud.version = "4.3.
|
|
7091
|
+
dexieCloud.version = "4.3.7";
|
|
7146
7092
|
Dexie.Cloud = dexieCloud;
|
|
7147
7093
|
|
|
7148
7094
|
// In case the SW lives for a while, let it reuse already opened connections:
|