dexie-cloud-addon 4.3.4 → 4.3.6

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.4, Fri Jan 23 2026
11
+ * Version 4.3.6, 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;
@@ -2897,6 +2843,25 @@
2897
2843
  public_key,
2898
2844
  };
2899
2845
  }
2846
+ else if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'otp' || (hints === null || hints === void 0 ? void 0 : hints.email)) {
2847
+ // User explicitly requested OTP flow - skip provider selection
2848
+ const email = (hints === null || hints === void 0 ? void 0 : hints.email) || (yield promptForEmail(userInteraction, 'Enter email address'));
2849
+ if (/@demo.local$/.test(email)) {
2850
+ tokenRequest = {
2851
+ demo_user: email,
2852
+ grant_type: 'demo',
2853
+ scopes: ['ACCESS_DB'],
2854
+ public_key
2855
+ };
2856
+ }
2857
+ else {
2858
+ tokenRequest = {
2859
+ email,
2860
+ grant_type: 'otp',
2861
+ scopes: ['ACCESS_DB'],
2862
+ };
2863
+ }
2864
+ }
2900
2865
  else {
2901
2866
  // Check for available auth providers (OAuth + OTP)
2902
2867
  const socialAuthEnabled = ((_c = db.cloud.options) === null || _c === void 0 ? void 0 : _c.socialAuth) !== false;
@@ -2932,7 +2897,8 @@
2932
2897
  const res1 = yield fetch(`${url}/token`, {
2933
2898
  body: JSON.stringify(tokenRequest),
2934
2899
  method: 'post',
2935
- headers: { 'Content-Type': 'application/json', mode: 'cors' },
2900
+ headers: { 'Content-Type': 'application/json' },
2901
+ mode: 'cors',
2936
2902
  });
2937
2903
  if (res1.status !== 200) {
2938
2904
  const errMsg = yield res1.text();
@@ -3002,7 +2968,11 @@
3002
2968
  if (!url)
3003
2969
  throw new Error(`No database URL given.`);
3004
2970
  const redirectUri = ((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.oauthRedirectUri) ||
3005
- (typeof window !== 'undefined' ? window.location.href : undefined);
2971
+ (typeof location !== 'undefined' ? location.href : undefined);
2972
+ // CodeRabbit suggested to fail fast here, but the only situation where
2973
+ // redirectUri would be undefined is in non-browser environments, and
2974
+ // in those environments OAuth redirect does not make sense anyway
2975
+ // and will fail fast in startOAuthRedirect().
3006
2976
  // Start OAuth redirect flow - page navigates away
3007
2977
  startOAuthRedirect({
3008
2978
  databaseUrl: url,
@@ -13808,7 +13778,7 @@
13808
13778
  *
13809
13779
  * ==========================================================================
13810
13780
  *
13811
- * Version 4.2.2, Fri Jan 23 2026
13781
+ * Version 4.2.2, Wed Jan 28 2026
13812
13782
  *
13813
13783
  * https://dexie.org
13814
13784
  *
@@ -17047,13 +17017,13 @@
17047
17017
  color: "#3c4043"
17048
17018
  },
17049
17019
  ProviderGitHub: {
17050
- backgroundColor: "#24292e",
17051
- border: "1px solid #24292e",
17052
- color: "#ffffff"
17020
+ backgroundColor: "#ffffff",
17021
+ border: "1px solid #dadce0",
17022
+ color: "#181717"
17053
17023
  },
17054
17024
  ProviderMicrosoft: {
17055
17025
  backgroundColor: "#ffffff",
17056
- border: "1px solid #8c8c8c",
17026
+ border: "1px solid #dadce0",
17057
17027
  color: "#5e5e5e"
17058
17028
  },
17059
17029
  ProviderApple: {
@@ -17062,9 +17032,9 @@
17062
17032
  color: "#ffffff"
17063
17033
  },
17064
17034
  ProviderCustom: {
17065
- backgroundColor: "#4f46e5",
17066
- border: "1px solid #4f46e5",
17067
- color: "#ffffff"
17035
+ backgroundColor: "#ffffff",
17036
+ border: "1px solid #dadce0",
17037
+ color: "#181717"
17068
17038
  },
17069
17039
  // Divider styles
17070
17040
  Divider: {
@@ -17155,39 +17125,13 @@
17155
17125
  * Generic button component for selectable options.
17156
17126
  * Displays the option's icon and display name.
17157
17127
  *
17158
- * The icon can be:
17159
- * - Inline SVG (iconSvg) - rendered directly with dangerouslySetInnerHTML
17160
- * - Image URL (iconUrl) - rendered as an img tag
17161
- *
17162
17128
  * Style is determined by the styleHint property for branding purposes.
17163
17129
  */
17164
17130
  function OptionButton({ option, onClick }) {
17165
- const { displayName, iconUrl, iconSvg, styleHint, value } = option;
17131
+ const { displayName, iconUrl, styleHint } = option;
17166
17132
  const style = getOptionStyle(styleHint);
17167
- // Get the text color from the button style for SVG fill processing
17168
- const textColor = style.color || '#000000';
17169
- // Process SVG to replace currentColor with actual text color
17170
- const processedSvg = iconSvg
17171
- ? iconSvg
17172
- .replace(/fill="currentColor"/gi, `fill="${textColor}"`)
17173
- .replace(/fill='currentColor'/gi, `fill='${textColor}'`)
17174
- .replace(/stroke="currentColor"/gi, `stroke="${textColor}"`)
17175
- .replace(/stroke='currentColor'/gi, `stroke='${textColor}'`)
17176
- : null;
17177
- // Render the appropriate icon
17178
- const renderIcon = () => {
17179
- // Inline SVG
17180
- if (processedSvg) {
17181
- return (_$1("span", { style: Styles.ProviderButtonIcon, "aria-hidden": "true", dangerouslySetInnerHTML: { __html: processedSvg } }));
17182
- }
17183
- // Image URL
17184
- if (iconUrl) {
17185
- return (_$1("img", { src: iconUrl, alt: "", style: Styles.ProviderButtonIcon, "aria-hidden": "true" }));
17186
- }
17187
- return null;
17188
- };
17189
17133
  return (_$1("button", { type: "button", style: style, onClick: onClick, class: `dxc-option-btn${styleHint ? ` dxc-option-${styleHint}` : ''}`, "aria-label": displayName },
17190
- renderIcon(),
17134
+ iconUrl && (_$1("img", { src: iconUrl, alt: "", style: Styles.ProviderButtonIcon, "aria-hidden": "true" })),
17191
17135
  _$1("span", { style: Styles.ProviderButtonText }, displayName)));
17192
17136
  }
17193
17137
  /**
@@ -18145,7 +18089,7 @@
18145
18089
  const syncComplete = new rxjs.Subject();
18146
18090
  dexie.cloud = {
18147
18091
  // @ts-ignore
18148
- version: "4.3.4",
18092
+ version: "4.3.6",
18149
18093
  options: Object.assign({}, DEFAULT_OPTIONS),
18150
18094
  schema: null,
18151
18095
  get currentUserId() {
@@ -18415,12 +18359,18 @@
18415
18359
  pendingOAuthError = null; // Clear pending error
18416
18360
  console.debug('[dexie-cloud] Showing OAuth error:', error.message);
18417
18361
  // Show alert to user about the OAuth error
18418
- yield alertUser(db.cloud.userInteraction, 'Authentication Error', {
18419
- type: 'error',
18420
- messageCode: 'GENERIC_ERROR',
18421
- message: error.message,
18422
- messageParams: { provider: error.provider || 'unknown' }
18423
- });
18362
+ // Guard so UI errors don't abort initialization
18363
+ try {
18364
+ yield alertUser(db.cloud.userInteraction, 'Authentication Error', {
18365
+ type: 'error',
18366
+ messageCode: 'GENERIC_ERROR',
18367
+ message: error.message,
18368
+ messageParams: { provider: error.provider || 'unknown' }
18369
+ });
18370
+ }
18371
+ catch (uiError) {
18372
+ console.error('[dexie-cloud] Failed to show OAuth error alert:', uiError);
18373
+ }
18424
18374
  }
18425
18375
  // Process pending OAuth callback if present (from dxc-auth redirect)
18426
18376
  if (pendingOAuthCode && !db.cloud.isServiceWorkerDB) {
@@ -18524,7 +18474,7 @@
18524
18474
  }
18525
18475
  }
18526
18476
  // @ts-ignore
18527
- dexieCloud.version = "4.3.4";
18477
+ dexieCloud.version = "4.3.6";
18528
18478
  Dexie.Cloud = dexieCloud;
18529
18479
 
18530
18480
  exports.default = dexieCloud;