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.
@@ -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
  *
@@ -2445,73 +2445,19 @@
2445
2445
  }
2446
2446
  }
2447
2447
 
2448
- /** Cache for fetched SVG content to avoid re-fetching */
2449
- const svgCache = {};
2450
- /** Default SVG icons for built-in OAuth providers */
2451
- const ProviderIcons = {
2452
- 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>`,
2453
- 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>`,
2454
- 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>`,
2455
- 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>`,
2456
- };
2457
- /** Email/envelope icon for OTP option */
2458
- 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>`;
2459
- /**
2460
- * Fetches SVG content from a URL and caches it.
2461
- * Returns the SVG string or null if fetch fails.
2462
- */
2463
- function fetchSvgIcon(url) {
2464
- return __awaiter(this, void 0, void 0, function* () {
2465
- if (svgCache[url]) {
2466
- return svgCache[url];
2467
- }
2468
- try {
2469
- const res = yield fetch(url);
2470
- if (res.ok) {
2471
- const svg = yield res.text();
2472
- // Validate it looks like SVG
2473
- if (svg.includes('<svg')) {
2474
- svgCache[url] = svg;
2475
- return svg;
2476
- }
2477
- }
2478
- }
2479
- catch (_a) {
2480
- // Silently fail - will show no icon
2481
- }
2482
- return null;
2483
- });
2484
- }
2448
+ /** Email/envelope icon data URL for OTP option */
2449
+ 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>')}`;
2485
2450
  /**
2486
2451
  * Converts an OAuthProviderInfo to a generic DXCOption.
2487
- * Fetches SVG icons from URLs if needed.
2488
2452
  */
2489
2453
  function providerToOption(provider) {
2490
- return __awaiter(this, void 0, void 0, function* () {
2491
- var _a;
2492
- let iconSvg;
2493
- // First check for built-in icons
2494
- if (ProviderIcons[provider.type]) {
2495
- iconSvg = ProviderIcons[provider.type];
2496
- }
2497
- // If provider has iconUrl pointing to SVG, fetch and inline it
2498
- else if ((_a = provider.iconUrl) === null || _a === void 0 ? void 0 : _a.toLowerCase().endsWith('.svg')) {
2499
- const fetched = yield fetchSvgIcon(provider.iconUrl);
2500
- if (fetched) {
2501
- iconSvg = fetched;
2502
- }
2503
- }
2504
- return {
2505
- name: 'provider',
2506
- value: provider.name,
2507
- displayName: `Continue with ${provider.displayName}`,
2508
- iconSvg,
2509
- // If iconUrl is not SVG, pass it through for img tag rendering
2510
- iconUrl: (!iconSvg && provider.iconUrl) ? provider.iconUrl : undefined,
2511
- // Use provider type as style hint for branding
2512
- styleHint: provider.type,
2513
- };
2514
- });
2454
+ return {
2455
+ name: 'provider',
2456
+ value: provider.name,
2457
+ displayName: `Continue with ${provider.displayName}`,
2458
+ iconUrl: provider.iconUrl,
2459
+ styleHint: provider.type,
2460
+ };
2515
2461
  }
2516
2462
  function interactWithUser(userInteraction, req) {
2517
2463
  return new Promise((resolve, reject) => {
@@ -2662,8 +2608,8 @@
2662
2608
  */
2663
2609
  function promptForProvider(userInteraction_1, providers_1, otpEnabled_1) {
2664
2610
  return __awaiter(this, arguments, void 0, function* (userInteraction, providers, otpEnabled, title = 'Choose login method', alerts = []) {
2665
- // Convert providers to generic options (with icon fetching)
2666
- const providerOptions = yield Promise.all(providers.map(providerToOption));
2611
+ // Convert providers to generic options
2612
+ const providerOptions = providers.map(providerToOption);
2667
2613
  // Build the options array
2668
2614
  const options = [...providerOptions];
2669
2615
  // Add OTP option if enabled
@@ -2672,7 +2618,7 @@
2672
2618
  name: 'otp',
2673
2619
  value: 'email',
2674
2620
  displayName: 'Continue with email',
2675
- iconSvg: EmailIcon,
2621
+ iconUrl: EmailIcon,
2676
2622
  styleHint: 'otp',
2677
2623
  });
2678
2624
  }
@@ -13261,7 +13207,7 @@
13261
13207
  *
13262
13208
  * ==========================================================================
13263
13209
  *
13264
- * Version 4.2.2, Fri Jan 23 2026
13210
+ * Version 4.2.2, Wed Jan 28 2026
13265
13211
  *
13266
13212
  * https://dexie.org
13267
13213
  *
@@ -14708,10 +14654,12 @@
14708
14654
  mode: 'cors',
14709
14655
  });
14710
14656
  if (!res.ok) {
14657
+ // Read body once as text to avoid stream consumption issues
14658
+ const bodyText = yield res.text().catch(() => res.statusText);
14711
14659
  if (res.status === 400 || res.status === 401) {
14712
- // Try to parse error response
14660
+ // Try to parse error response as JSON
14713
14661
  try {
14714
- const errorResponse = yield res.json();
14662
+ const errorResponse = JSON.parse(bodyText);
14715
14663
  if (errorResponse.type === 'error') {
14716
14664
  // Check for specific error codes
14717
14665
  if (errorResponse.messageCode === 'INVALID_OTP') {
@@ -14728,8 +14676,7 @@
14728
14676
  // Fall through to generic error
14729
14677
  }
14730
14678
  }
14731
- const errorText = yield res.text().catch(() => res.statusText);
14732
- throw new OAuthError('provider_error', undefined, `Token exchange failed: ${res.status} ${errorText}`);
14679
+ throw new OAuthError('provider_error', undefined, `Token exchange failed: ${res.status} ${bodyText}`);
14733
14680
  }
14734
14681
  const response = yield res.json();
14735
14682
  if (response.type === 'error') {
@@ -14835,9 +14782,8 @@
14835
14782
  * ```
14836
14783
  */
14837
14784
  function startOAuthRedirect(options) {
14838
- // Store provider in sessionStorage for reference on callback
14839
- if (typeof sessionStorage !== 'undefined') {
14840
- sessionStorage.setItem('dexie-cloud-oauth-provider', options.provider);
14785
+ if (typeof window === 'undefined') {
14786
+ throw new Error('OAuth redirect requires a browser environment');
14841
14787
  }
14842
14788
  const loginUrl = buildOAuthLoginUrl(options);
14843
14789
  window.location.href = loginUrl;
@@ -14889,6 +14835,25 @@
14889
14835
  public_key,
14890
14836
  };
14891
14837
  }
14838
+ else if ((hints === null || hints === void 0 ? void 0 : hints.grant_type) === 'otp' || (hints === null || hints === void 0 ? void 0 : hints.email)) {
14839
+ // User explicitly requested OTP flow - skip provider selection
14840
+ const email = (hints === null || hints === void 0 ? void 0 : hints.email) || (yield promptForEmail(userInteraction, 'Enter email address'));
14841
+ if (/@demo.local$/.test(email)) {
14842
+ tokenRequest = {
14843
+ demo_user: email,
14844
+ grant_type: 'demo',
14845
+ scopes: ['ACCESS_DB'],
14846
+ public_key
14847
+ };
14848
+ }
14849
+ else {
14850
+ tokenRequest = {
14851
+ email,
14852
+ grant_type: 'otp',
14853
+ scopes: ['ACCESS_DB'],
14854
+ };
14855
+ }
14856
+ }
14892
14857
  else {
14893
14858
  // Check for available auth providers (OAuth + OTP)
14894
14859
  const socialAuthEnabled = ((_c = db.cloud.options) === null || _c === void 0 ? void 0 : _c.socialAuth) !== false;
@@ -14924,7 +14889,8 @@
14924
14889
  const res1 = yield fetch(`${url}/token`, {
14925
14890
  body: JSON.stringify(tokenRequest),
14926
14891
  method: 'post',
14927
- headers: { 'Content-Type': 'application/json', mode: 'cors' },
14892
+ headers: { 'Content-Type': 'application/json' },
14893
+ mode: 'cors',
14928
14894
  });
14929
14895
  if (res1.status !== 200) {
14930
14896
  const errMsg = yield res1.text();
@@ -14994,7 +14960,11 @@
14994
14960
  if (!url)
14995
14961
  throw new Error(`No database URL given.`);
14996
14962
  const redirectUri = ((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.oauthRedirectUri) ||
14997
- (typeof window !== 'undefined' ? window.location.href : undefined);
14963
+ (typeof location !== 'undefined' ? location.href : undefined);
14964
+ // CodeRabbit suggested to fail fast here, but the only situation where
14965
+ // redirectUri would be undefined is in non-browser environments, and
14966
+ // in those environments OAuth redirect does not make sense anyway
14967
+ // and will fail fast in startOAuthRedirect().
14998
14968
  // Start OAuth redirect flow - page navigates away
14999
14969
  startOAuthRedirect({
15000
14970
  databaseUrl: url,
@@ -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
  /**
@@ -17974,7 +17918,7 @@
17974
17918
  const syncComplete = new rxjs.Subject();
17975
17919
  dexie.cloud = {
17976
17920
  // @ts-ignore
17977
- version: "4.3.4",
17921
+ version: "4.3.6",
17978
17922
  options: Object.assign({}, DEFAULT_OPTIONS),
17979
17923
  schema: null,
17980
17924
  get currentUserId() {
@@ -18244,12 +18188,18 @@
18244
18188
  pendingOAuthError = null; // Clear pending error
18245
18189
  console.debug('[dexie-cloud] Showing OAuth error:', error.message);
18246
18190
  // Show alert to user about the OAuth error
18247
- yield alertUser(db.cloud.userInteraction, 'Authentication Error', {
18248
- type: 'error',
18249
- messageCode: 'GENERIC_ERROR',
18250
- message: error.message,
18251
- messageParams: { provider: error.provider || 'unknown' }
18252
- });
18191
+ // Guard so UI errors don't abort initialization
18192
+ try {
18193
+ yield alertUser(db.cloud.userInteraction, 'Authentication Error', {
18194
+ type: 'error',
18195
+ messageCode: 'GENERIC_ERROR',
18196
+ message: error.message,
18197
+ messageParams: { provider: error.provider || 'unknown' }
18198
+ });
18199
+ }
18200
+ catch (uiError) {
18201
+ console.error('[dexie-cloud] Failed to show OAuth error alert:', uiError);
18202
+ }
18253
18203
  }
18254
18204
  // Process pending OAuth callback if present (from dxc-auth redirect)
18255
18205
  if (pendingOAuthCode && !db.cloud.isServiceWorkerDB) {
@@ -18353,7 +18303,7 @@
18353
18303
  }
18354
18304
  }
18355
18305
  // @ts-ignore
18356
- dexieCloud.version = "4.3.4";
18306
+ dexieCloud.version = "4.3.6";
18357
18307
  Dexie.Cloud = dexieCloud;
18358
18308
 
18359
18309
  // In case the SW lives for a while, let it reuse already opened connections: