#!/usr/bin/env node /** * Web Vitals Tracker * Real-time monitoring of Core Web Vitals in production */ const fs = require("fs"); const path = require("path"); const WEB_VITALS_DIR = path.join(__dirname, "..", ".next", "web-vitals"); class WebVitalsTracker { constructor() { this.metrics = { timestamp: new Date().toISOString(), vitals: { lcp: [], fid: [], cls: [], fcp: [], ttfb: [], }, summary: {}, }; } /** * Track Web Vitals from client-side */ trackWebVitals() { const trackingCode = ` // Web Vitals Tracking Script (function() { // Import web-vitals library import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { const vitals = {}; // Track Largest Contentful Paint getLCP((metric) => { vitals.lcp = { value: metric.value, rating: metric.rating, delta: metric.delta, timestamp: Date.now() }; sendVitals('lcp', vitals.lcp); }); // Track First Input Delay getFID((metric) => { vitals.fid = { value: metric.value, rating: metric.rating, delta: metric.delta, timestamp: Date.now() }; sendVitals('fid', vitals.fid); }); // Track Cumulative Layout Shift getCLS((metric) => { vitals.cls = { value: metric.value, rating: metric.rating, delta: metric.delta, timestamp: Date.now() }; sendVitals('cls', vitals.cls); }); // Track First Contentful Paint getFCP((metric) => { vitals.fcp = { value: metric.value, rating: metric.rating, delta: metric.delta, timestamp: Date.now() }; sendVitals('fcp', vitals.fcp); }); // Track Time to First Byte getTTFB((metric) => { vitals.ttfb = { value: metric.value, rating: metric.rating, delta: metric.delta, timestamp: Date.now() }; sendVitals('ttfb', vitals.ttfb); }); }); // Send vitals to server function sendVitals(metric, data) { if (typeof window !== 'undefined' && window.fetch) { fetch('/api/web-vitals', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ metric, data, url: window.location.href, userAgent: navigator.userAgent, timestamp: Date.now() }) }).catch(console.error); } } })(); `; return trackingCode; } /** * Create API endpoint for receiving Web Vitals */ createAPIEndpoint() { const apiCode = ` // API endpoint for Web Vitals tracking export default function handler(req, res) { if (req.method !== 'POST') { return res.status(405).json({ error: 'Method not allowed' }); } try { const { metric, data, url, userAgent, timestamp } = req.body; // Store the metric data const vitalsData = { metric, data, url, userAgent, timestamp: new Date(timestamp).toISOString() }; // In production, you would save this to a database // For now, we'll log it console.log('Web Vital received:', vitalsData); res.status(200).json({ success: true }); } catch (error) { console.error('Error processing web vital:', error); res.status(500).json({ error: 'Internal server error' }); } } `; return apiCode; } /** * Generate Web Vitals dashboard */ generateDashboard() { const dashboardCode = ` import React, { useState, useEffect } from 'react'; const WebVitalsDashboard = () => { const [vitals, setVitals] = useState({ lcp: { value: 0, rating: 'unknown' }, fid: { value: 0, rating: 'unknown' }, cls: { value: 0, rating: 'unknown' }, fcp: { value: 0, rating: 'unknown' }, ttfb: { value: 0, rating: 'unknown' } }); useEffect(() => { // In a real implementation, you would fetch from your database // For now, we'll use localStorage for demo purposes const storedVitals = localStorage.getItem('web-vitals'); if (storedVitals) { setVitals(JSON.parse(storedVitals)); } }, []); const getRatingColor = (rating) => { switch (rating) { case 'good': return 'text-green-600'; case 'needs-improvement': return 'text-yellow-600'; case 'poor': return 'text-red-600'; default: return 'text-gray-600'; } }; const getRatingIcon = (rating) => { switch (rating) { case 'good': return '✅'; case 'needs-improvement': return '⚠️'; case 'poor': return '❌'; default: return '❓'; } }; return (