/** * Reload Loop Detection and Prevention Utility * Advanced mobile Safari reload loop prevention system */ interface PageLoadInfo { url: string; timestamp: number; userAgent: string; loadCount: number; } interface ReloadLoopState { enabled: boolean; pageLoads: PageLoadInfo[]; blockedPages: Set; emergencyMode: boolean; debugMode: boolean; } const RELOAD_LOOP_THRESHOLD = 5; // Max page loads in time window const TIME_WINDOW = 10000; // 10 seconds const EMERGENCY_BLOCK_TIME = 30000; // 30 seconds const STORAGE_KEY = 'reload_loop_prevention'; /** * Get or initialize reload loop state */ function getReloadLoopState(): ReloadLoopState { if (typeof window === 'undefined') { return { enabled: false, pageLoads: [], blockedPages: new Set(), emergencyMode: false, debugMode: false }; } // Use sessionStorage for persistence across reloads try { const stored = sessionStorage.getItem(STORAGE_KEY); if (stored) { const parsed = JSON.parse(stored); return { ...parsed, blockedPages: new Set(parsed.blockedPages || []) }; } } catch (error) { console.warn('[reload-prevention] Failed to parse stored state:', error); } // Initialize new state const initialState: ReloadLoopState = { enabled: true, pageLoads: [], blockedPages: new Set(), emergencyMode: false, debugMode: process.env.NODE_ENV === 'development' }; saveReloadLoopState(initialState); return initialState; } /** * Save reload loop state to sessionStorage */ function saveReloadLoopState(state: ReloadLoopState): void { if (typeof window === 'undefined') return; try { const toStore = { ...state, blockedPages: Array.from(state.blockedPages) }; sessionStorage.setItem(STORAGE_KEY, JSON.stringify(toStore)); } catch (error) { console.warn('[reload-prevention] Failed to save state:', error); } } /** * Record a page load and check for reload loops */ export function recordPageLoad(url: string): boolean { const state = getReloadLoopState(); if (!state.enabled) { return true; // Allow load } const now = Date.now(); const userAgent = navigator.userAgent || ''; // Clean old page loads state.pageLoads = state.pageLoads.filter( load => now - load.timestamp < TIME_WINDOW ); // Find existing load for this URL const existingLoad = state.pageLoads.find(load => load.url === url); if (existingLoad) { existingLoad.loadCount++; existingLoad.timestamp = now; } else { state.pageLoads.push({ url, timestamp: now, userAgent, loadCount: 1 }); } // Check for reload loop const urlLoads = state.pageLoads.filter(load => load.url === url); const totalLoads = urlLoads.reduce((sum, load) => sum + load.loadCount, 0); if (totalLoads >= RELOAD_LOOP_THRESHOLD) { console.error(`[reload-prevention] Reload loop detected for ${url} (${totalLoads} loads)`); // Block this page state.blockedPages.add(url); state.emergencyMode = true; // Set emergency timeout setTimeout(() => { state.emergencyMode = false; state.blockedPages.delete(url); saveReloadLoopState(state); console.log(`[reload-prevention] Emergency block lifted for ${url}`); }, EMERGENCY_BLOCK_TIME); saveReloadLoopState(state); return false; // Block load } saveReloadLoopState(state); return true; // Allow load } /** * Check if a page is currently blocked */ export function isPageBlocked(url: string): boolean { const state = getReloadLoopState(); return state.blockedPages.has(url); } /** * Initialize reload loop prevention for a page */ export function initReloadLoopPrevention(pageName: string): boolean { if (typeof window === 'undefined') { return true; } const currentUrl = window.location.pathname; const canLoad = recordPageLoad(currentUrl); if (!canLoad) { console.error(`[reload-prevention] Page load blocked: ${pageName} (${currentUrl})`); // Show emergency message showEmergencyMessage(pageName); return false; } if (getReloadLoopState().debugMode) { console.log(`[reload-prevention] Page load allowed: ${pageName} (${currentUrl})`); } return true; } /** * Show emergency message when page is blocked */ function showEmergencyMessage(pageName: string): void { const message = `

Page Loading Temporarily Blocked

Multiple rapid page loads detected for ${pageName}.
This is a safety measure to prevent infinite loading loops.

The block will be automatically lifted in 30 seconds.

`; document.body.innerHTML = message; } /** * Clear reload loop prevention state (for testing) */ export function clearReloadLoopPrevention(): void { if (typeof window === 'undefined') return; try { sessionStorage.removeItem(STORAGE_KEY); console.log('[reload-prevention] State cleared'); } catch (error) { console.warn('[reload-prevention] Failed to clear state:', error); } } /** * Get current reload loop prevention status */ export function getReloadLoopStatus(): { enabled: boolean; emergencyMode: boolean; blockedPages: string[]; pageLoads: PageLoadInfo[]; } { const state = getReloadLoopState(); return { enabled: state.enabled, emergencyMode: state.emergencyMode, blockedPages: Array.from(state.blockedPages), pageLoads: state.pageLoads }; } /** * Mobile Safari specific optimizations */ export function applyMobileSafariReloadLoopFixes(): void { if (typeof window === 'undefined') return; const userAgent = navigator.userAgent || ''; const isMobileSafari = /iPad|iPhone|iPod/.test(userAgent) && /Safari/i.test(userAgent) && !/Chrome/i.test(userAgent); if (!isMobileSafari) return; console.log('[reload-prevention] Applying mobile Safari specific fixes'); // Prevent Safari's aggressive caching that can cause reload loops window.addEventListener('pageshow', (event) => { if (event.persisted) { console.log('[reload-prevention] Page restored from bfcache'); // Force a small delay to prevent immediate re-execution setTimeout(() => { console.log('[reload-prevention] bfcache restore handled'); }, 100); } }); // Handle Safari's navigation timing issues window.addEventListener('beforeunload', () => { // Mark navigation in progress to prevent reload loop detection const state = getReloadLoopState(); state.enabled = false; saveReloadLoopState(state); }); // Re-enable after navigation completes window.addEventListener('load', () => { setTimeout(() => { const state = getReloadLoopState(); state.enabled = true; saveReloadLoopState(state); }, 500); }); } // Auto-initialize on module load if (typeof window !== 'undefined') { applyMobileSafariReloadLoopFixes(); }