import { Component, type ErrorInfo, type ReactNode } from 'react'; interface ErrorBoundaryProps { children: ReactNode; level?: 'app' | 'page' | 'widget'; onReset?: () => void; } interface ErrorBoundaryState { hasError: boolean; error: Error | null; } function isChunkLoadError(error: Error): boolean { const msg = error.message || ''; return ( msg.includes('dynamically imported module') || msg.includes('Importing a module script failed') || msg.includes('Failed to fetch dynamically imported module') || msg.includes('Loading chunk') || msg.includes('ChunkLoadError') || error.name === 'ChunkLoadError' ); } export class ErrorBoundary extends Component { constructor(props: ErrorBoundaryProps) { super(props); this.state = { hasError: false, error: null }; } static getDerivedStateFromError(error: Error): ErrorBoundaryState { return { hasError: true, error }; } componentDidCatch(error: Error, errorInfo: ErrorInfo) { console.error('[ErrorBoundary]', error, errorInfo); // Auto-reload on chunk load failures (stale deploy) if (isChunkLoadError(error)) { const reloadKey = 'chunk_reload_ts'; const lastReload = Number(sessionStorage.getItem(reloadKey) || '0'); const now = Date.now(); // Prevent reload loop — only auto-reload once per 30 seconds if (now - lastReload > 30_000) { sessionStorage.setItem(reloadKey, String(now)); window.location.reload(); return; } } } handleReset = () => { this.setState({ hasError: false, error: null }); this.props.onReset?.(); }; render() { if (!this.state.hasError) { return this.props.children; } const { level = 'page' } = this.props; const isChunk = this.state.error ? isChunkLoadError(this.state.error) : false; if (level === 'app') { return (
⚠️

Something went wrong

An unexpected error occurred. Please try reloading the page.

); } if (level === 'widget') { return (

Failed to load this section

); } // level === 'page' (default) return (
⚠️

Something went wrong

{isChunk ? 'App was updated. Reloading...' : this.state.error?.message || 'An unexpected error occurred'}

); } }