import { createRoot } from "react-dom/client"; import App from "./App"; import "./index.css"; import { performanceMonitor } from "./lib/performance-observer"; import { resourceLoader } from "./lib/critical-resource-loader"; // Recover from stale-chunk blank screens caused by deploys with new asset hashes. // When a lazy-loaded JS module 404s after a deploy, the browser fires a global error // with "Importing a module script failed". We reload once to get fresh HTML + chunks. // The sessionStorage flag prevents an infinite reload loop if the new deploy is broken. (function installChunkErrorRecovery() { const FLAG = 'chunk_reload_attempted'; window.addEventListener('error', (event) => { const msg = event.message || ''; if ( msg.includes('Importing a module script failed') || msg.includes('Failed to fetch dynamically imported module') || msg.includes('error loading dynamically imported module') ) { if (sessionStorage.getItem(FLAG)) return; // already tried once this session sessionStorage.setItem(FLAG, '1'); // Tell PostHog a stale-chunk error was detected. The snippet loads // synchronously so window.posthog is available before React mounts. try { (window as any).posthog?.capture('stale_chunk_detected', { reload_already_attempted: false }); } catch {} // Show a friendly overlay before reloading so users aren't confused by // a sudden flash (React may not have mounted yet, so we inject raw HTML). const overlay = document.createElement('div'); overlay.style.cssText = [ 'position:fixed', 'inset:0', 'z-index:99999', 'display:flex', 'align-items:center', 'justify-content:center', 'background:rgba(0,0,0,0.55)', 'backdrop-filter:blur(4px)', '-webkit-backdrop-filter:blur(4px)', ].join(';'); overlay.innerHTML = `
New version available
Refreshing to load the latest update…
`; document.body.appendChild(overlay); // Attempt the reload automatically after a short delay. setTimeout(() => window.location.reload(), 1000); // If the page is still alive after 5 s (e.g. offline or slow connection // prevented the reload), show the manual "Reload now" button so the user // isn't left staring at a frozen overlay with no way out. setTimeout(() => { const btn = document.getElementById('_chunk_reload_btn'); if (btn) btn.style.display = 'inline-block'; }, 5000); } }); // NOTE: Do NOT clear the flag on 'load'. sessionStorage already expires naturally // when the tab closes. Clearing it on every load defeats the guard and causes an // infinite reload loop: the flag is set → reload fires → load event clears the flag // → the same chunk 404 fires again → flag is set → reload → repeat forever. })(); createRoot(document.getElementById("root")!).render(); // Defer non-critical startup work until the browser is idle so it doesn't // compete with hydration / first paint. const ric = window.requestIdleCallback?.bind(window); const idle: (cb: () => void) => void = ric ? (cb) => { ric(cb); } : (cb) => { setTimeout(cb, 1); }; idle(() => { performanceMonitor.initialize(); resourceLoader.preloadBasedOnNavigation(window.location.pathname); }); // Phase 4: Register service worker for advanced caching if ('serviceWorker' in navigator && import.meta.env.PROD) { window.addEventListener('load', () => { navigator.serviceWorker.register('/sw.js') .then((registration) => { console.log('SW registered: ', registration); }) .catch((registrationError) => { console.log('SW registration failed: ', registrationError); }); }); }