@sparkvault/sdk 1.21.7 → 1.21.9

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.
@@ -28,12 +28,15 @@ export declare class IdentityApi {
28
28
  /**
29
29
  * Fetch SDK configuration (branding, enabled methods).
30
30
  * Uses caching to avoid redundant requests - safe to call multiple times.
31
+ * If a cached request failed (e.g., transient network error during preload),
32
+ * clears the cache and retries to avoid permanently caching a rejected promise.
31
33
  */
32
34
  getConfig(): Promise<SdkConfig>;
33
35
  /**
34
36
  * Preload the SDK configuration in the background.
35
37
  * Called on SDK init when preloadConfig is enabled.
36
38
  * The result is cached and used when verify() is called.
39
+ * If preload fails, getConfig() will clear the cache and retry.
37
40
  */
38
41
  preloadConfig(): void;
39
42
  /**
@@ -34,6 +34,12 @@ export declare class IdentityRenderer {
34
34
  * Otherwise shows loading state while fetching config.
35
35
  */
36
36
  start(): Promise<void>;
37
+ /**
38
+ * Fetch config with a single retry on network errors.
39
+ * Handles transient failures common during subdomain switches
40
+ * (DNS resolution, connection setup, Safari ITP).
41
+ */
42
+ private fetchConfigWithRetry;
37
43
  /**
38
44
  * Close the modal and clean up.
39
45
  * Cancels all pending API requests to prevent orphaned operations.
@@ -714,15 +714,28 @@ class IdentityApi {
714
714
  return (json.data ?? json);
715
715
  }
716
716
  catch (error) {
717
- // Convert AbortError to a more descriptive error
718
- if (error instanceof Error && error.name === 'AbortError') {
717
+ // Convert AbortError to a more descriptive error.
718
+ // Check error.name directly (DOMException may not extend Error in all environments).
719
+ if (error != null && typeof error === 'object' && 'name' in error && error.name === 'AbortError') {
719
720
  // Check which signal triggered the abort
720
721
  if (this.closeController.signal.aborted) {
721
722
  throw new IdentityApiError('Request cancelled', 'cancelled', 0);
722
723
  }
723
724
  throw new IdentityApiError('Request timed out', 'timeout', 0);
724
725
  }
725
- throw error;
726
+ // Convert TypeError to a user-friendly network error.
727
+ // WebKit (Safari/iOS) throws TypeError("Load failed"),
728
+ // Chrome/Firefox throw TypeError("Failed to fetch")
729
+ // when fetch() fails at the browser level (network error, CORS, DNS, etc.).
730
+ if (error instanceof TypeError) {
731
+ throw new IdentityApiError('Unable to connect. Please check your connection and try again.', 'network_error', 0);
732
+ }
733
+ // Re-throw IdentityApiError as-is (already has user-friendly message)
734
+ if (error instanceof IdentityApiError) {
735
+ throw error;
736
+ }
737
+ // Convert any other unexpected errors to a structured error
738
+ throw new IdentityApiError(error instanceof Error ? error.message : 'An unexpected error occurred', 'unknown_error', 0);
726
739
  }
727
740
  finally {
728
741
  clearTimeout(timeoutId);
@@ -731,21 +744,34 @@ class IdentityApi {
731
744
  /**
732
745
  * Fetch SDK configuration (branding, enabled methods).
733
746
  * Uses caching to avoid redundant requests - safe to call multiple times.
747
+ * If a cached request failed (e.g., transient network error during preload),
748
+ * clears the cache and retries to avoid permanently caching a rejected promise.
734
749
  */
735
750
  async getConfig() {
736
751
  if (!this.configCache) {
737
752
  this.configCache = this.request('GET', '/config');
738
753
  }
739
- return this.configCache;
754
+ try {
755
+ return await this.configCache;
756
+ }
757
+ catch (error) {
758
+ // Clear failed cache so the next call retries instead of returning
759
+ // the same rejected promise. This handles transient network failures
760
+ // (e.g., "Load failed" in Safari) that occurred during preloadConfig().
761
+ this.configCache = null;
762
+ throw error;
763
+ }
740
764
  }
741
765
  /**
742
766
  * Preload the SDK configuration in the background.
743
767
  * Called on SDK init when preloadConfig is enabled.
744
768
  * The result is cached and used when verify() is called.
769
+ * If preload fails, getConfig() will clear the cache and retry.
745
770
  */
746
771
  preloadConfig() {
747
772
  if (!this.configCache) {
748
- // Fire and forget - errors are handled when getConfig() is awaited
773
+ // Fire and forget - errors are handled when getConfig() is awaited.
774
+ // If this fails, getConfig() clears the cache and retries.
749
775
  this.configCache = this.request('GET', '/config');
750
776
  }
751
777
  }
@@ -4474,7 +4500,7 @@ class IdentityRenderer {
4474
4500
  if (!config) {
4475
4501
  // Config not ready yet - show loading and await it
4476
4502
  this.setState({ view: 'loading' });
4477
- config = await this.api.getConfig();
4503
+ config = await this.fetchConfigWithRetry();
4478
4504
  }
4479
4505
  this.verificationState.setConfig(config);
4480
4506
  // Update modal header with branding if provided
@@ -4500,6 +4526,27 @@ class IdentityRenderer {
4500
4526
  this.handleErrorWithRecovery(error, 'config_error');
4501
4527
  }
4502
4528
  }
4529
+ /**
4530
+ * Fetch config with a single retry on network errors.
4531
+ * Handles transient failures common during subdomain switches
4532
+ * (DNS resolution, connection setup, Safari ITP).
4533
+ */
4534
+ async fetchConfigWithRetry() {
4535
+ try {
4536
+ return await this.api.getConfig();
4537
+ }
4538
+ catch (error) {
4539
+ // Only retry on network errors (not API errors like 404, 500, etc.)
4540
+ const isNetworkError = error instanceof IdentityApiError && error.code === 'network_error';
4541
+ if (!isNetworkError) {
4542
+ throw error;
4543
+ }
4544
+ // Wait briefly before retry (exponential backoff: 1s)
4545
+ await new Promise((resolve) => setTimeout(resolve, 1000));
4546
+ // Retry once - getConfig() will clear the failed cache and make a fresh request
4547
+ return this.api.getConfig();
4548
+ }
4549
+ }
4503
4550
  /**
4504
4551
  * Close the modal and clean up.
4505
4552
  * Cancels all pending API requests to prevent orphaned operations.
@@ -5960,7 +6007,7 @@ function injectUploadStyles(options) {
5960
6007
  function getUploadStyles(options) {
5961
6008
  const { backdropBlur } = options;
5962
6009
  const enableBlur = backdropBlur !== false;
5963
- // Design tokens - using light theme for upload widget (matches SecureUploadPortal)
6010
+ // Design tokens - light theme for upload widget
5964
6011
  const tokens = {
5965
6012
  // Colors
5966
6013
  primary: '#4F46E5',