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.
@@ -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
  *
@@ -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;
@@ -14863,7 +14809,21 @@
14863
14809
  }
14864
14810
  // Handle OAuth provider login via redirect
14865
14811
  if (hints === null || hints === void 0 ? void 0 : hints.provider) {
14866
- initiateOAuthRedirect(db, hints.provider);
14812
+ let resolvedRedirectUri = undefined;
14813
+ if (hints.redirectPath) {
14814
+ // If redirectPath is absolute, use as is. If relative, resolve against current location
14815
+ if (/^https?:\/\//i.test(hints.redirectPath)) {
14816
+ resolvedRedirectUri = hints.redirectPath;
14817
+ }
14818
+ else if (typeof window !== 'undefined' && window.location) {
14819
+ // Use URL constructor to resolve relative path
14820
+ resolvedRedirectUri = new URL(hints.redirectPath, window.location.href).toString();
14821
+ }
14822
+ else if (typeof location !== 'undefined' && location.href) {
14823
+ resolvedRedirectUri = new URL(hints.redirectPath, location.href).toString();
14824
+ }
14825
+ }
14826
+ initiateOAuthRedirect(db, hints.provider, resolvedRedirectUri);
14867
14827
  // This function never returns - page navigates away
14868
14828
  throw new OAuthRedirectError(hints.provider);
14869
14829
  }
@@ -14943,7 +14903,8 @@
14943
14903
  const res1 = yield fetch(`${url}/token`, {
14944
14904
  body: JSON.stringify(tokenRequest),
14945
14905
  method: 'post',
14946
- headers: { 'Content-Type': 'application/json', mode: 'cors' },
14906
+ headers: { 'Content-Type': 'application/json' },
14907
+ mode: 'cors',
14947
14908
  });
14948
14909
  if (res1.status !== 200) {
14949
14910
  const errMsg = yield res1.text();
@@ -15007,13 +14968,18 @@
15007
14968
  * the user is redirected back with a dxc-auth query parameter that is
15008
14969
  * automatically detected by db.cloud.configure().
15009
14970
  */
15010
- function initiateOAuthRedirect(db, provider) {
14971
+ function initiateOAuthRedirect(db, provider, redirectUriOverride) {
15011
14972
  var _a, _b;
15012
14973
  const url = (_a = db.cloud.options) === null || _a === void 0 ? void 0 : _a.databaseUrl;
15013
14974
  if (!url)
15014
14975
  throw new Error(`No database URL given.`);
15015
- const redirectUri = ((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.oauthRedirectUri) ||
15016
- (typeof window !== 'undefined' ? window.location.href : undefined);
14976
+ const redirectUri = redirectUriOverride ||
14977
+ ((_b = db.cloud.options) === null || _b === void 0 ? void 0 : _b.oauthRedirectUri) ||
14978
+ (typeof location !== 'undefined' ? location.href : undefined);
14979
+ // CodeRabbit suggested to fail fast here, but the only situation where
14980
+ // redirectUri would be undefined is in non-browser environments, and
14981
+ // in those environments OAuth redirect does not make sense anyway
14982
+ // and will fail fast in startOAuthRedirect().
15017
14983
  // Start OAuth redirect flow - page navigates away
15018
14984
  startOAuthRedirect({
15019
14985
  databaseUrl: url,
@@ -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
  /**
@@ -17993,7 +17933,7 @@
17993
17933
  const syncComplete = new rxjs.Subject();
17994
17934
  dexie.cloud = {
17995
17935
  // @ts-ignore
17996
- version: "4.3.5",
17936
+ version: "4.3.7",
17997
17937
  options: Object.assign({}, DEFAULT_OPTIONS),
17998
17938
  schema: null,
17999
17939
  get currentUserId() {
@@ -18263,12 +18203,18 @@
18263
18203
  pendingOAuthError = null; // Clear pending error
18264
18204
  console.debug('[dexie-cloud] Showing OAuth error:', error.message);
18265
18205
  // Show alert to user about the OAuth error
18266
- yield alertUser(db.cloud.userInteraction, 'Authentication Error', {
18267
- type: 'error',
18268
- messageCode: 'GENERIC_ERROR',
18269
- message: error.message,
18270
- messageParams: { provider: error.provider || 'unknown' }
18271
- });
18206
+ // Guard so UI errors don't abort initialization
18207
+ try {
18208
+ yield alertUser(db.cloud.userInteraction, 'Authentication Error', {
18209
+ type: 'error',
18210
+ messageCode: 'GENERIC_ERROR',
18211
+ message: error.message,
18212
+ messageParams: { provider: error.provider || 'unknown' }
18213
+ });
18214
+ }
18215
+ catch (uiError) {
18216
+ console.error('[dexie-cloud] Failed to show OAuth error alert:', uiError);
18217
+ }
18272
18218
  }
18273
18219
  // Process pending OAuth callback if present (from dxc-auth redirect)
18274
18220
  if (pendingOAuthCode && !db.cloud.isServiceWorkerDB) {
@@ -18372,7 +18318,7 @@
18372
18318
  }
18373
18319
  }
18374
18320
  // @ts-ignore
18375
- dexieCloud.version = "4.3.5";
18321
+ dexieCloud.version = "4.3.7";
18376
18322
  Dexie.Cloud = dexieCloud;
18377
18323
 
18378
18324
  // In case the SW lives for a while, let it reuse already opened connections: