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.
@@ -17,10 +17,8 @@ export interface DXCOption {
17
17
  value: string;
18
18
  /** Human-readable display label */
19
19
  displayName: string;
20
- /** URL to an icon image (mutually exclusive with iconSvg) */
20
+ /** URL to an icon image (can be a regular URL or a data: URL for inline images) */
21
21
  iconUrl?: string;
22
- /** Inline SVG markup for the icon (mutually exclusive with iconUrl) */
23
- iconSvg?: string;
24
22
  /** Optional style hint for the UI (e.g., 'google', 'github', 'microsoft', 'apple', 'otp') */
25
23
  styleHint?: string;
26
24
  }
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ==========================================================================
10
10
  *
11
- * Version 4.3.5, Fri Jan 23 2026
11
+ * Version 4.3.7, Wed Jan 28 2026
12
12
  *
13
13
  * https://dexie.org
14
14
  *
@@ -2061,73 +2061,19 @@
2061
2061
  }
2062
2062
  }
2063
2063
 
2064
- /** Cache for fetched SVG content to avoid re-fetching */
2065
- const svgCache = {};
2066
- /** Default SVG icons for built-in OAuth providers */
2067
- const ProviderIcons = {
2068
- 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>`,
2069
- 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>`,
2070
- 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>`,
2071
- 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>`,
2072
- };
2073
- /** Email/envelope icon for OTP option */
2074
- 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>`;
2075
- /**
2076
- * Fetches SVG content from a URL and caches it.
2077
- * Returns the SVG string or null if fetch fails.
2078
- */
2079
- function fetchSvgIcon(url) {
2080
- return __awaiter(this, void 0, void 0, function* () {
2081
- if (svgCache[url]) {
2082
- return svgCache[url];
2083
- }
2084
- try {
2085
- const res = yield fetch(url);
2086
- if (res.ok) {
2087
- const svg = yield res.text();
2088
- // Validate it looks like SVG
2089
- if (svg.includes('<svg')) {
2090
- svgCache[url] = svg;
2091
- return svg;
2092
- }
2093
- }
2094
- }
2095
- catch (_a) {
2096
- // Silently fail - will show no icon
2097
- }
2098
- return null;
2099
- });
2100
- }
2064
+ /** Email/envelope icon data URL for OTP option */
2065
+ 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>')}`;
2101
2066
  /**
2102
2067
  * Converts an OAuthProviderInfo to a generic DXCOption.
2103
- * Fetches SVG icons from URLs if needed.
2104
2068
  */
2105
2069
  function providerToOption(provider) {
2106
- return __awaiter(this, void 0, void 0, function* () {
2107
- var _a;
2108
- let iconSvg;
2109
- // First check for built-in icons
2110
- if (ProviderIcons[provider.type]) {
2111
- iconSvg = ProviderIcons[provider.type];
2112
- }
2113
- // If provider has iconUrl pointing to SVG, fetch and inline it
2114
- else if ((_a = provider.iconUrl) === null || _a === void 0 ? void 0 : _a.toLowerCase().endsWith('.svg')) {
2115
- const fetched = yield fetchSvgIcon(provider.iconUrl);
2116
- if (fetched) {
2117
- iconSvg = fetched;
2118
- }
2119
- }
2120
- return {
2121
- name: 'provider',
2122
- value: provider.name,
2123
- displayName: `Continue with ${provider.displayName}`,
2124
- iconSvg,
2125
- // If iconUrl is not SVG, pass it through for img tag rendering
2126
- iconUrl: (!iconSvg && provider.iconUrl) ? provider.iconUrl : undefined,
2127
- // Use provider type as style hint for branding
2128
- styleHint: provider.type,
2129
- };
2130
- });
2070
+ return {
2071
+ name: 'provider',
2072
+ value: provider.name,
2073
+ displayName: `Continue with ${provider.displayName}`,
2074
+ iconUrl: provider.iconUrl,
2075
+ styleHint: provider.type,
2076
+ };
2131
2077
  }
2132
2078
  function interactWithUser(userInteraction, req) {
2133
2079
  return new Promise((resolve, reject) => {
@@ -2278,8 +2224,8 @@
2278
2224
  */
2279
2225
  function promptForProvider(userInteraction_1, providers_1, otpEnabled_1) {
2280
2226
  return __awaiter(this, arguments, void 0, function* (userInteraction, providers, otpEnabled, title = 'Choose login method', alerts = []) {
2281
- // Convert providers to generic options (with icon fetching)
2282
- const providerOptions = yield Promise.all(providers.map(providerToOption));
2227
+ // Convert providers to generic options
2228
+ const providerOptions = providers.map(providerToOption);
2283
2229
  // Build the options array
2284
2230
  const options = [...providerOptions];
2285
2231
  // Add OTP option if enabled
@@ -2288,7 +2234,7 @@
2288
2234
  name: 'otp',
2289
2235
  value: 'email',
2290
2236
  displayName: 'Continue with email',
2291
- iconSvg: EmailIcon,
2237
+ iconUrl: EmailIcon,
2292
2238
  styleHint: 'otp',
2293
2239
  });
2294
2240
  }
@@ -2716,10 +2662,12 @@
2716
2662
  mode: 'cors',
2717
2663
  });
2718
2664
  if (!res.ok) {
2665
+ // Read body once as text to avoid stream consumption issues
2666
+ const bodyText = yield res.text().catch(() => res.statusText);
2719
2667
  if (res.status === 400 || res.status === 401) {
2720
- // Try to parse error response
2668
+ // Try to parse error response as JSON
2721
2669
  try {
2722
- const errorResponse = yield res.json();
2670
+ const errorResponse = JSON.parse(bodyText);
2723
2671
  if (errorResponse.type === 'error') {
2724
2672
  // Check for specific error codes
2725
2673
  if (errorResponse.messageCode === 'INVALID_OTP') {
@@ -2736,8 +2684,7 @@
2736
2684
  // Fall through to generic error
2737
2685
  }
2738
2686
  }
2739
- const errorText = yield res.text().catch(() => res.statusText);
2740
- throw new OAuthError('provider_error', undefined, `Token exchange failed: ${res.status} ${errorText}`);
2687
+ throw new OAuthError('provider_error', undefined, `Token exchange failed: ${res.status} ${bodyText}`);
2741
2688
  }
2742
2689
  const response = yield res.json();
2743
2690
  if (response.type === 'error') {
@@ -2843,9 +2790,8 @@
2843
2790
  * ```
2844
2791
  */
2845
2792
  function startOAuthRedirect(options) {
2846
- // Store provider in sessionStorage for reference on callback
2847
- if (typeof sessionStorage !== 'undefined') {
2848
- sessionStorage.setItem('dexie-cloud-oauth-provider', options.provider);
2793
+ if (typeof window === 'undefined') {
2794
+ throw new Error('OAuth redirect requires a browser environment');
2849
2795
  }
2850
2796
  const loginUrl = buildOAuthLoginUrl(options);
2851
2797
  window.location.href = loginUrl;
@@ -2871,7 +2817,21 @@
2871
2817
  }
2872
2818
  // Handle OAuth provider login via redirect
2873
2819
  if (hints === null || hints === void 0 ? void 0 : hints.provider) {
2874
- initiateOAuthRedirect(db, hints.provider);
2820
+ let resolvedRedirectUri = undefined;
2821
+ if (hints.redirectPath) {
2822
+ // If redirectPath is absolute, use as is. If relative, resolve against current location
2823
+ if (/^https?:\/\//i.test(hints.redirectPath)) {
2824
+ resolvedRedirectUri = hints.redirectPath;
2825
+ }
2826
+ else if (typeof window !== 'undefined' && window.location) {
2827
+ // Use URL constructor to resolve relative path
2828
+ resolvedRedirectUri = new URL(hints.redirectPath, window.location.href).toString();
2829
+ }
2830
+ else if (typeof location !== 'undefined' && location.href) {
2831
+ resolvedRedirectUri = new URL(hints.redirectPath, location.href).toString();
2832
+ }
2833
+ }
2834
+ initiateOAuthRedirect(db, hints.provider, resolvedRedirectUri);
2875
2835
  // This function never returns - page navigates away
2876
2836
  throw new OAuthRedirectError(hints.provider);
2877
2837
  }
@@ -2951,7 +2911,8 @@
2951
2911
  const res1 = yield fetch(`${url}/token`, {
2952
2912
  body: JSON.stringify(tokenRequest),
2953
2913
  method: 'post',
2954
- headers: { 'Content-Type': 'application/json', mode: 'cors' },
2914
+ headers: { 'Content-Type': 'application/json' },
2915
+ mode: 'cors',
2955
2916
  });
2956
2917
  if (res1.status !== 200) {
2957
2918
  const errMsg = yield res1.text();
@@ -3015,13 +2976,18 @@
3015
2976
  * the user is redirected back with a dxc-auth query parameter that is
3016
2977
  * automatically detected by db.cloud.configure().
3017
2978
  */
3018
- function initiateOAuthRedirect(db, provider) {
2979
+ function initiateOAuthRedirect(db, provider, redirectUriOverride) {
3019
2980
  var _a, _b;
3020
2981
  const url = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl;
3021
2982
  if (!url)
3022
2983
  throw new Error(`No database URL given.`);
3023
- const redirectUri = ((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.oauthRedirectUri) ||
3024
- (typeof window !== 'undefined' ? window.location.href : undefined);
2984
+ const redirectUri = redirectUriOverride ||
2985
+ ((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.oauthRedirectUri) ||
2986
+ (typeof location !== 'undefined' ? location.href : undefined);
2987
+ // CodeRabbit suggested to fail fast here, but the only situation where
2988
+ // redirectUri would be undefined is in non-browser environments, and
2989
+ // in those environments OAuth redirect does not make sense anyway
2990
+ // and will fail fast in startOAuthRedirect().
3025
2991
  // Start OAuth redirect flow - page navigates away
3026
2992
  startOAuthRedirect({
3027
2993
  databaseUrl: url,
@@ -13827,7 +13793,7 @@
13827
13793
  *
13828
13794
  * ==========================================================================
13829
13795
  *
13830
- * Version 4.2.2, Fri Jan 23 2026
13796
+ * Version 4.2.2, Wed Jan 28 2026
13831
13797
  *
13832
13798
  * https://dexie.org
13833
13799
  *
@@ -17066,13 +17032,13 @@
17066
17032
  color: "#3c4043"
17067
17033
  },
17068
17034
  ProviderGitHub: {
17069
- backgroundColor: "#24292e",
17070
- border: "1px solid #24292e",
17071
- color: "#ffffff"
17035
+ backgroundColor: "#ffffff",
17036
+ border: "1px solid #dadce0",
17037
+ color: "#181717"
17072
17038
  },
17073
17039
  ProviderMicrosoft: {
17074
17040
  backgroundColor: "#ffffff",
17075
- border: "1px solid #8c8c8c",
17041
+ border: "1px solid #dadce0",
17076
17042
  color: "#5e5e5e"
17077
17043
  },
17078
17044
  ProviderApple: {
@@ -17081,9 +17047,9 @@
17081
17047
  color: "#ffffff"
17082
17048
  },
17083
17049
  ProviderCustom: {
17084
- backgroundColor: "#4f46e5",
17085
- border: "1px solid #4f46e5",
17086
- color: "#ffffff"
17050
+ backgroundColor: "#ffffff",
17051
+ border: "1px solid #dadce0",
17052
+ color: "#181717"
17087
17053
  },
17088
17054
  // Divider styles
17089
17055
  Divider: {
@@ -17174,39 +17140,13 @@
17174
17140
  * Generic button component for selectable options.
17175
17141
  * Displays the option's icon and display name.
17176
17142
  *
17177
- * The icon can be:
17178
- * - Inline SVG (iconSvg) - rendered directly with dangerouslySetInnerHTML
17179
- * - Image URL (iconUrl) - rendered as an img tag
17180
- *
17181
17143
  * Style is determined by the styleHint property for branding purposes.
17182
17144
  */
17183
17145
  function OptionButton({ option, onClick }) {
17184
- const { displayName, iconUrl, iconSvg, styleHint, value } = option;
17146
+ const { displayName, iconUrl, styleHint } = option;
17185
17147
  const style = getOptionStyle(styleHint);
17186
- // Get the text color from the button style for SVG fill processing
17187
- const textColor = style.color || '#000000';
17188
- // Process SVG to replace currentColor with actual text color
17189
- const processedSvg = iconSvg
17190
- ? iconSvg
17191
- .replace(/fill="currentColor"/gi, `fill="${textColor}"`)
17192
- .replace(/fill='currentColor'/gi, `fill='${textColor}'`)
17193
- .replace(/stroke="currentColor"/gi, `stroke="${textColor}"`)
17194
- .replace(/stroke='currentColor'/gi, `stroke='${textColor}'`)
17195
- : null;
17196
- // Render the appropriate icon
17197
- const renderIcon = () => {
17198
- // Inline SVG
17199
- if (processedSvg) {
17200
- return (_$1("span", { style: Styles.ProviderButtonIcon, "aria-hidden": "true", dangerouslySetInnerHTML: { __html: processedSvg } }));
17201
- }
17202
- // Image URL
17203
- if (iconUrl) {
17204
- return (_$1("img", { src: iconUrl, alt: "", style: Styles.ProviderButtonIcon, "aria-hidden": "true" }));
17205
- }
17206
- return null;
17207
- };
17208
17148
  return (_$1("button", { type: "button", style: style, onClick: onClick, class: `dxc-option-btn${styleHint ? ` dxc-option-${styleHint}` : ''}`, "aria-label": displayName },
17209
- renderIcon(),
17149
+ iconUrl && (_$1("img", { src: iconUrl, alt: "", style: Styles.ProviderButtonIcon, "aria-hidden": "true" })),
17210
17150
  _$1("span", { style: Styles.ProviderButtonText }, displayName)));
17211
17151
  }
17212
17152
  /**
@@ -18164,7 +18104,7 @@
18164
18104
  const syncComplete = new rxjs.Subject();
18165
18105
  dexie.cloud = {
18166
18106
  // @ts-ignore
18167
- version: "4.3.5",
18107
+ version: "4.3.7",
18168
18108
  options: Object.assign({}, DEFAULT_OPTIONS),
18169
18109
  schema: null,
18170
18110
  get currentUserId() {
@@ -18434,12 +18374,18 @@
18434
18374
  pendingOAuthError = null; // Clear pending error
18435
18375
  console.debug('[dexie-cloud] Showing OAuth error:', error.message);
18436
18376
  // Show alert to user about the OAuth error
18437
- yield alertUser(db.cloud.userInteraction, 'Authentication Error', {
18438
- type: 'error',
18439
- messageCode: 'GENERIC_ERROR',
18440
- message: error.message,
18441
- messageParams: { provider: error.provider || 'unknown' }
18442
- });
18377
+ // Guard so UI errors don't abort initialization
18378
+ try {
18379
+ yield alertUser(db.cloud.userInteraction, 'Authentication Error', {
18380
+ type: 'error',
18381
+ messageCode: 'GENERIC_ERROR',
18382
+ message: error.message,
18383
+ messageParams: { provider: error.provider || 'unknown' }
18384
+ });
18385
+ }
18386
+ catch (uiError) {
18387
+ console.error('[dexie-cloud] Failed to show OAuth error alert:', uiError);
18388
+ }
18443
18389
  }
18444
18390
  // Process pending OAuth callback if present (from dxc-auth redirect)
18445
18391
  if (pendingOAuthCode && !db.cloud.isServiceWorkerDB) {
@@ -18543,7 +18489,7 @@
18543
18489
  }
18544
18490
  }
18545
18491
  // @ts-ignore
18546
- dexieCloud.version = "4.3.5";
18492
+ dexieCloud.version = "4.3.7";
18547
18493
  Dexie.Cloud = dexieCloud;
18548
18494
 
18549
18495
  exports.default = dexieCloud;