# Mobile Safari Reload Loop - Final Fix ## Problem Description Users on Safari iPhone experienced endless reload loops on: - Signup page (`/signup`) - Email verification page (`/auth/verify`) - Password setup page (`/auth/setup-password`) The server logs showed repeated calls to: - `/api/recaptcha-config` - `/api/registration-config` ## Root Causes Identified ### 1. Incorrect Reactive Reference in Signup Page **Issue**: `cardClasses` was defined as a ref containing a function instead of the function's result: ```typescript // WRONG - causes reactivity issues const cardClasses = ref(() => { const classes = ['signup-card']; // ... return classes.join(' '); }); ``` **Fix**: Execute the function immediately and store the result: ```typescript // CORRECT const cardClasses = ref((() => { const classes = ['signup-card']; // ... return classes.join(' '); })()); // Note the immediate execution with () ``` ### 2. Config Cache Not Persisting Across Component Lifecycles **Issue**: The global config cache was using module-level variables that could be reset during Vue's reactivity cycles, causing repeated API calls. **Fix**: Use `window` object for true persistence: ```typescript // Use window object for true persistence across component lifecycle function getGlobalCache(): ConfigCache { if (typeof window === 'undefined') { return defaultCache; } if (!(window as any).__configCache) { (window as any).__configCache = defaultCache; } return (window as any).__configCache; } ``` ### 3. Missing Circuit Breaker Protection **Issue**: No protection against rapid successive API calls that could trigger reload loops. **Fix**: Implemented circuit breaker with threshold protection: - Max 5 calls in 10-second window - Automatic blocking when threshold reached - Fallback to default values when blocked ## Complete Solution Implementation ### Files Modified 1. **`pages/signup.vue`** - Fixed `cardClasses` ref definition - Ensured static device detection - Added initialization flag to prevent multiple setups 2. **`utils/config-cache.ts`** - Moved cache storage to `window` object - Added `getGlobalCache()` function for persistent storage - Improved circuit breaker implementation - Added proper logging for debugging 3. **`plugins/04.config-cache-init.client.ts`** (NEW) - Pre-initializes config cache structure - Sets up global error handlers to catch reload loops - Prevents multiple initializations - Adds unhandled rejection handler ## How The Fix Works ### 1. Plugin Initialization (runs first) - `04.config-cache-init.client.ts` runs before other plugins - Initializes `window.__configCache` structure - Sets up error handlers to catch potential reload loops - Marks initialization complete with `window.__configCacheInitialized` ### 2. Config Loading (on-demand) - When pages need config, they call `loadAllConfigs()` - Cache is checked first via `getGlobalCache()` - If cached, returns immediately (no API call) - If not cached, makes API call with circuit breaker protection - Results stored in `window.__configCache` for persistence ### 3. Circuit Breaker Protection - Tracks API call history in time windows - Blocks calls if threshold exceeded (5 calls in 10 seconds) - Returns fallback values when blocked - Prevents cascade failures and reload loops ## Testing Instructions ### Test on Safari iPhone: 1. Clear Safari cache and cookies 2. Navigate to `/signup` - should load without reload loop 3. Navigate to `/auth/verify?token=test` - should show error without loop 4. Navigate to `/auth/setup-password?email=test@test.com` - should load without loop ### Monitor Console Logs: - Look for `[config-cache-init]` messages confirming initialization - Check for `[config-cache] Returning cached` messages on subsequent loads - Watch for `Circuit breaker activated` if threshold reached ### Server Logs: - Should see initial calls to `/api/recaptcha-config` and `/api/registration-config` - Should NOT see repeated calls in quick succession - Maximum 2-3 calls per page load (initial + retry if needed) ## Prevention Measures ### 1. Static Detection Pattern All device detection uses static, non-reactive patterns: ```typescript const deviceInfo = getStaticDeviceInfo(); // Called once, never reactive const containerClasses = ref(getDeviceCssClasses('page-name')); // Computed once ``` ### 2. Configuration Caching All configuration loading uses cached utility: ```typescript const configs = await loadAllConfigs(); // Uses cache automatically ``` ### 3. Initialization Flags Prevent multiple initializations: ```typescript let initialized = false; onMounted(() => { if (initialized) return; initialized = true; // ... initialization code }); ``` ## Monitoring ### Key Metrics to Watch: 1. **API Call Frequency**: `/api/recaptcha-config` and `/api/registration-config` should be called max once per session 2. **Page Load Time**: Should be under 2 seconds on mobile 3. **Error Rate**: No "Maximum call stack" or recursion errors 4. **User Reports**: No complaints about infinite loading ### Debug Commands: ```javascript // Check cache status in browser console console.log(window.__configCache); console.log(window.__configCacheInitialized); // Force clear cache (for testing) window.__configCache = null; window.__configCacheInitialized = false; ``` ## Rollback Plan If issues persist, rollback changes: 1. Remove `plugins/04.config-cache-init.client.ts` 2. Revert `utils/config-cache.ts` to previous version 3. Revert `pages/signup.vue` changes ## Long-term Improvements 1. **Server-side caching**: Cache config in Redis/memory on server 2. **SSR config injection**: Inject config during SSR to avoid client calls 3. **PWA service worker**: Cache config in service worker 4. **Config versioning**: Add version check to invalidate stale cache ## Conclusion The mobile Safari reload loop has been resolved through: 1. Fixing reactive reference bugs 2. Implementing proper persistent caching 3. Adding circuit breaker protection 4. Setting up global error handlers The solution is backward compatible and doesn't affect desktop users or other browsers. The fix specifically targets the root causes while maintaining the existing functionality.