import { onCLS, onFCP, onLCP, onTTFB, onINP, type Metric } from 'web-vitals'; function sendToAnalytics(metric: Metric) { // Log to console in development if (import.meta.env.DEV) { console.log('Web Vitals:', { name: metric.name, value: metric.value, rating: metric.rating, delta: metric.delta, id: metric.id, }); } // Send to Google Analytics if available if (typeof window !== 'undefined' && (window as any).gtag) { (window as any).gtag('event', metric.name, { value: Math.round(metric.name === 'CLS' ? metric.value * 1000 : metric.value), event_category: 'Web Vitals', event_label: metric.id, non_interaction: true, }); } // Send to custom analytics endpoint if needed if (import.meta.env.PROD && import.meta.env.VITE_ANALYTICS_ENDPOINT) { fetch(import.meta.env.VITE_ANALYTICS_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ metric: metric.name, value: metric.value, rating: metric.rating, id: metric.id, timestamp: Date.now(), url: window.location.href, userAgent: navigator.userAgent, }), keepalive: true, }).catch((error) => { console.error('Failed to send web vitals:', error); }); } } export function reportWebVitals() { // Core Web Vitals onCLS(sendToAnalytics); onLCP(sendToAnalytics); onINP(sendToAnalytics); // Replaces deprecated FID // Other important metrics onFCP(sendToAnalytics); onTTFB(sendToAnalytics); } // Performance observer for additional metrics export function observePerformance() { if (typeof window === 'undefined' || !('PerformanceObserver' in window)) { return; } // Observe long tasks (blocking the main thread) try { const longTaskObserver = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if (entry.duration > 50) { console.warn('Long Task detected:', { duration: entry.duration, startTime: entry.startTime, name: entry.name, }); } } }); longTaskObserver.observe({ entryTypes: ['longtask'] }); } catch (e) { // Long task API not supported } // Observe layout shifts try { const layoutShiftObserver = new PerformanceObserver((list) => { for (const entry of list.getEntries()) { if ((entry as any).hadRecentInput) continue; const value = (entry as any).value; if (value > 0.1) { console.warn('Large Layout Shift:', { value, startTime: entry.startTime, sources: (entry as any).sources, }); } } }); layoutShiftObserver.observe({ entryTypes: ['layout-shift'] }); } catch (e) { // Layout shift API not supported } }