Exemplos Netlify Edge Functions

Exemplos abrangentes de Netlify Edge Functions cobrindo middleware, autenticação, A/B testing, geolocalização e padrões avançados de edge computing

💻 Middleware Edge Básico javascript

🟢 simple ⭐⭐

Padrões essenciais de middleware edge para tratamento de requisições, modificação de respostas e roteamento básico

⏱️ 15 min 🏷️ netlify, edge functions, middleware
Prerequisites: JavaScript ES6+, HTTP basics, Netlify edge functions
// Basic Edge Middleware Examples
// File: edge-functions/middleware.js

// 1. Request logging and analytics middleware
export default async (request, context) => {
  const start = Date.now()
  const url = new URL(request.url)

  // Log request details
  console.log({
    method: request.method,
    url: url.pathname,
    userAgent: request.headers.get('user-agent'),
    ip: getClientIP(request),
    timestamp: new Date().toISOString()
  })

  // Continue to next middleware or page
  const response = await context.next()

  // Add response headers
  const duration = Date.now() - start
  response.headers.set('x-response-time', `${duration}ms`)
  response.headers.set('x-edge-function', 'middleware')

  return response
}

function getClientIP(request) {
  return request.headers.get('x-nf-client-connection-ip') ||
         request.headers.get('x-forwarded-for') ||
         '0.0.0.0'
}

// 2. CORS middleware
// File: edge-functions/cors.js
export default async (request, context) => {
  const response = await context.next()

  // Set CORS headers
  response.headers.set('Access-Control-Allow-Origin', '*')
  response.headers.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS')
  response.headers.set('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With')
  response.headers.set('Access-Control-Max-Age', '86400')

  // Handle preflight requests
  if (request.method === 'OPTIONS') {
    return new Response(null, { status: 200, headers: response.headers })
  }

  return response
}

// 3. Security headers middleware
// File: edge-functions/security.js
export default async (request, context) => {
  const response = await context.next()

  // Set security headers
  response.headers.set('X-Frame-Options', 'DENY')
  response.headers.set('X-Content-Type-Options', 'nosniff')
  response.headers.set('X-XSS-Protection', '1; mode=block')
  response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin')
  response.headers.set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()')

  // Content Security Policy
  const csp = [
    "default-src 'self'",
    "script-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net",
    "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
    "font-src 'self' https://fonts.gstatic.com",
    "img-src 'self' data: https:",
    "connect-src 'self' https://api.example.com"
  ].join('; ')

  response.headers.set('Content-Security-Policy', csp)

  return response
}

// 4. Path-based routing middleware
// File: edge-functions/router.js
export default async (request, context) => {
  const url = new URL(request.url)
  const pathname = url.pathname

  // API routes
  if (pathname.startsWith('/api/')) {
    return handleAPIRoute(request, pathname)
  }

  // Admin routes
  if (pathname.startsWith('/admin/')) {
    return handleAdminRoute(request, context)
  }

  // Static assets with custom headers
  if (pathname.startsWith('/static/') || pathname.startsWith('/_next/static/')) {
    const response = await context.next()
    response.headers.set('Cache-Control', 'public, max-age=31536000, immutable')
    return response
  }

  // Default handling
  return context.next()
}

async function handleAPIRoute(request, pathname) {
  // Route API requests to different handlers
  if (pathname.includes('/users')) {
    return new Response(JSON.stringify({ users: [] }), {
      headers: { 'Content-Type': 'application/json' }
    })
  }

  if (pathname.includes('/products')) {
    return new Response(JSON.stringify({ products: [] }), {
      headers: { 'Content-Type': 'application/json' }
    })
  }

  return new Response('API endpoint not found', { status: 404 })
}

async function handleAdminRoute(request, context) {
  // Check authentication for admin routes
  const authHeader = request.headers.get('authorization')

  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return new Response('Unauthorized', { status: 401 })
  }

  // Validate token (simplified)
  const token = authHeader.slice(7)
  if (!isValidAdminToken(token)) {
    return new Response('Invalid token', { status: 401 })
  }

  return context.next()
}

function isValidAdminToken(token) {
  // Implement proper token validation
  return token.length > 10
}

💻 Geolocalização e Localização javascript

🟡 intermediate ⭐⭐⭐

Roteamento baseado em geolocalização, localização de conteúdo e funcionalidade específica da região

⏱️ 25 min 🏷️ netlify, edge functions, geolocation
Prerequisites: JavaScript, Geolocation APIs, Content localization
// Geolocation and Localization Examples
// File: edge-functions/geolocation.js

export default async (request, context) => {
  const url = new URL(request.url)
  const geo = request.geo || {}

  // Extract location data
  const location = {
    country: geo.country?.code?.toLowerCase() || 'unknown',
    countryName: geo.country?.name || 'Unknown',
    city: geo.city || 'Unknown',
    region: geo.subdivision?.name || 'Unknown',
    timezone: geo.timezone || 'UTC',
    latitude: geo.latitude,
    longitude: geo.longitude
  }

  console.log('User location:', location)

  // Skip API routes
  if (url.pathname.startsWith('/api/')) {
    return context.next()
  }

  // Handle localized routes
  if (url.pathname === '/' || url.pathname === '/home') {
    return handleLocalizedHome(request, location, context)
  }

  // Handle product localization
  if (url.pathname.startsWith('/products/')) {
    return handleLocalizedProducts(request, location, context)
  }

  // Add location headers to all responses
  const response = await context.next()
  response.headers.set('x-user-country', location.country)
  response.headers.set('x-user-city', location.city)
  response.headers.set('x-user-timezone', location.timezone)

  return response
}

async function handleLocalizedHome(request, location, context) {
  const url = new URL(request.url)

  // Redirect to localized version based on country
  const localizedPaths = {
    'us': '/us',
    'gb': '/uk',
    'de': '/de',
    'fr': '/fr',
    'es': '/es',
    'it': '/it',
    'jp': '/ja',
    'cn': '/zh',
    'br': '/pt-br'
  }

  const localizedPath = localizedPaths[location.country]

  if (localizedPath && !url.pathname.startsWith(`/${location.country}`)) {
    const localizedUrl = new URL(localizedPath, url)
    return Response.redirect(localizedUrl, 302)
  }

  return context.next()
}

async function handleLocalizedProducts(request, location, context) {
  const url = new URL(request.url)

  // Add region-specific pricing
  const response = await context.next()

  if (response.headers.get('content-type')?.includes('text/html')) {
    const html = await response.text()

    // Inject pricing and availability script
    const pricingScript = generatePricingScript(location)
    const modifiedHtml = html.replace(
      '</head>',
      `${pricingScript}</head>`
    )

    return new Response(modifiedHtml, {
      headers: response.headers
    })
  }

  return response
}

function generatePricingScript(location) {
  const pricing = {
    'us': { currency: 'USD', symbol: '$', rate: 1 },
    'gb': { currency: 'GBP', symbol: '£', rate: 0.79 },
    'de': { currency: 'EUR', symbol: '€', rate: 0.92 },
    'fr': { currency: 'EUR', symbol: '€', rate: 0.92 },
    'es': { currency: 'EUR', symbol: '€', rate: 0.92 },
    'it': { currency: 'EUR', symbol: '€', rate: 0.92 },
    'br': { currency: 'BRL', symbol: 'R$', rate: 5.2 }
  }

  const localPricing = pricing[location.country] || pricing['us']

  return `
    <script>
      window.LOCAL_PRICING = ${JSON.stringify(localPricing)};
      window.USER_LOCATION = ${JSON.stringify(location)};

      // Apply localized pricing
      document.addEventListener('DOMContentLoaded', function() {
        const priceElements = document.querySelectorAll('[data-base-price]');
        priceElements.forEach(el => {
          const basePrice = parseFloat(el.dataset.basePrice);
          const localPrice = (basePrice * window.LOCAL_PRICING.rate).toFixed(2);
          el.textContent = '${localPricing.symbol}' + localPrice;
        });
      });
    </script>
  `
}

// Geolocation-based content filtering
// File: edge-functions/content-filter.js
export default async (request, context) => {
  const geo = request.geo || {}
  const country = geo.country?.code?.toLowerCase()

  const response = await context.next()

  // Skip for non-HTML responses
  if (!response.headers.get('content-type')?.includes('text/html')) {
    return response
  }

  const html = await response.text()

  // Apply content restrictions based on location
  let modifiedHtml = html

  // GDPR compliance for EU countries
  const euCountries = ['de', 'fr', 'it', 'es', 'nl', 'be', 'at', 'se', 'dk', 'fi']
  if (euCountries.includes(country)) {
    modifiedHtml = addGDPRBanner(modifiedHtml)
  }

  // Content filtering for specific regions
  if (country === 'cn') {
    modifiedHtml = filterContentForChina(modifiedHtml)
  }

  return new Response(modifiedHtml, {
    headers: response.headers
  })
}

function addGDPRBanner(html) {
  const banner = `
    <div id="gdpr-banner" style="position: fixed; bottom: 0; left: 0; right: 0;
                background: #333; color: white; padding: 1rem; z-index: 9999;
                display: flex; justify-content: space-between; align-items: center;">
      <span>This website uses cookies to ensure you get the best experience on our website.</span>
      <div>
        <button onclick="acceptCookies()" style="margin-right: 10px; padding: 5px 15px;
                background: #4CAF50; color: white; border: none; cursor: pointer;">
          Accept
        </button>
        <button onclick="rejectCookies()" style="padding: 5px 15px;
                background: #f44336; color: white; border: none; cursor: pointer;">
          Reject
        </button>
      </div>
    </div>
    <script>
      function acceptCookies() {
        document.getElementById('gdpr-banner').style.display = 'none';
        localStorage.setItem('gdpr-consent', 'accepted');
      }
      function rejectCookies() {
        document.getElementById('gdpr-banner').style.display = 'none';
        localStorage.setItem('gdpr-consent', 'rejected');
      }
      if (localStorage.getItem('gdpr-consent')) {
        document.getElementById('gdpr-banner').style.display = 'none';
      }
    </script>
  `

  return html.replace('</body>', banner + '</body>')
}

function filterContentForChina(html) {
  // Remove or modify content not available in China
  return html
    .replace(/<iframe[^>]*youtube[^>]*><\/iframe>/gi,
           '<div>Video content not available in your region</div>')
    .replace(/<iframe[^>]*google[^>]*><\/iframe>/gi,
           '<div>Google services not available in your region</div>')
}

// Timezone-aware content
// File: edge-functions/timezone.js
export default async (request, context) => {
  const timezone = request.geo?.timezone || 'UTC'
  const url = new URL(request.url)

  const response = await context.next()

  // Skip for non-HTML responses
  if (!response.headers.get('content-type')?.includes('text/html')) {
    return response
  }

  const html = await response.text()

  // Inject timezone information
  const timezoneScript = `
    <script>
      window.USER_TIMEZONE = '${timezone}';
      window.USER_TIME = new Date().toLocaleString('en-US', {
        timeZone: '${timezone}',
        year: 'numeric',
        month: 'long',
        day: 'numeric',
        hour: '2-digit',
        minute: '2-digit'
      });

      // Convert timestamps to user timezone
      function convertToLocalTime(utcTime) {
        return new Date(utcTime).toLocaleString('en-US', {
          timeZone: window.USER_TIMEZONE
        });
      }

      // Update all timestamp elements
      document.addEventListener('DOMContentLoaded', function() {
        const timeElements = document.querySelectorAll('[data-utc-time]');
        timeElements.forEach(el => {
          const utcTime = el.dataset.utcTime;
          el.textContent = convertToLocalTime(utcTime);
        });
      });
    </script>
  `

  const modifiedHtml = html.replace('</head>', timezoneScript + '</head>')

  return new Response(modifiedHtml, {
    headers: response.headers
  })
}

💻 Autenticação e Autorização javascript

🔴 complex ⭐⭐⭐⭐

Sistema completo de autenticação com validação JWT, gerenciamento de sessão e controle de acesso baseado em roles

⏱️ 35 min 🏷️ netlify, edge functions, security, auth
Prerequisites: JWT, Authentication concepts, Cookie management, Role-based access control
// Authentication and Authorization Examples
// File: edge-functions/auth.js

export default async (request, context) => {
  const url = new URL(request.url)

  // Public routes that don't require authentication
  const publicRoutes = ['/login', '/signup', '/forgot-password', '/api/auth/login', '/api/auth/register', '/_next/static/']
  const isPublicRoute = publicRoutes.some(route => url.pathname.startsWith(route))

  if (isPublicRoute) {
    return context.next()
  }

  // Check for authentication
  const authResult = await authenticateRequest(request)

  if (!authResult.authenticated) {
    return handleUnauthenticated(request, authResult.reason)
  }

  // Add user context to request
  const response = await context.next()
  response.headers.set('x-user-id', authResult.userId)
  response.headers.set('x-user-role', authResult.role)
  response.headers.set('x-user-email', authResult.email)

  return response
}

async function authenticateRequest(request) {
  const authHeader = request.headers.get('authorization')
  const cookieHeader = request.headers.get('cookie') || ''

  // Try JWT token from Authorization header
  if (authHeader && authHeader.startsWith('Bearer ')) {
    const token = authHeader.slice(7)
    return await validateJWTToken(token)
  }

  // Try JWT token from cookies
  const tokenMatch = cookieHeader.match(/token=([^;]+)/)
  if (tokenMatch) {
    const token = tokenMatch[1]
    return await validateJWTToken(token)
  }

  return { authenticated: false, reason: 'no_token' }
}

async function validateJWTToken(token) {
  try {
    // Split JWT token
    const [, payloadBase64] = token.split('.')
    const payload = JSON.parse(atob(payloadBase64))

    // Check expiration
    if (payload.exp && Date.now() / 1000 > payload.exp) {
      return { authenticated: false, reason: 'token_expired' }
    }

    // In production, verify signature with secret key
    // For demo purposes, we'll just validate structure
    if (!payload.sub || !payload.email) {
      return { authenticated: false, reason: 'invalid_token' }
    }

    return {
      authenticated: true,
      userId: payload.sub,
      email: payload.email,
      role: payload.role || 'user',
      permissions: payload.permissions || []
    }

  } catch (error) {
    console.error('JWT validation error:', error)
    return { authenticated: false, reason: 'invalid_token' }
  }
}

function handleUnauthenticated(request, reason) {
  const url = new URL(request.url)

  // Return 401 for API routes
  if (url.pathname.startsWith('/api/')) {
    return new Response(JSON.stringify({
      error: 'Authentication required',
      reason: reason
    }), {
      status: 401,
      headers: { 'Content-Type': 'application/json' }
    })
  }

  // Redirect to login for web routes
  const loginUrl = new URL('/login', url)
  loginUrl.searchParams.set('redirect', url.pathname + url.search)
  loginUrl.searchParams.set('reason', reason)

  return Response.redirect(loginUrl, 302)
}

// Role-based access control
// File: edge-functions/rbac.js
export default async (request, context) => {
  const url = new URL(request.url)

  // Define protected routes and required roles
  const protectedRoutes = {
    '/admin': ['admin'],
    '/admin/users': ['admin'],
    '/admin/settings': ['admin'],
    '/dashboard': ['user', 'admin', 'moderator'],
    '/api/admin': ['admin'],
    '/api/moderator': ['admin', 'moderator'],
    '/profile': ['user', 'admin', 'moderator']
  }

  // Check if route requires specific role
  const requiredRole = getRequiredRole(url.pathname, protectedRoutes)

  if (requiredRole) {
    const authResult = await authenticateRequest(request)

    if (!authResult.authenticated) {
      return handleUnauthenticated(request, 'no_token')
    }

    if (!hasRequiredRole(authResult.role, requiredRole)) {
      return handleUnauthorized(authResult.role, requiredRole)
    }

    // Add user info to headers for downstream processing
    const response = await context.next()
    response.headers.set('x-user-id', authResult.userId)
    response.headers.set('x-user-role', authResult.role)
    response.headers.set('x-user-email', authResult.email)

    return response
  }

  return context.next()
}

function getRequiredRole(pathname, protectedRoutes) {
  for (const [route, roles] of Object.entries(protectedRoutes)) {
    if (pathname.startsWith(route)) {
      return roles
    }
  }
  return null
}

function hasRequiredRole(userRole, requiredRoles) {
  return requiredRoles.includes(userRole)
}

function handleUnauthorized(userRole, requiredRoles) {
  const url = new URL(request.url)

  // Return 403 for API routes
  if (url.pathname.startsWith('/api/')) {
    return new Response(JSON.stringify({
      error: 'Insufficient permissions',
      userRole: userRole,
      requiredRoles: requiredRoles
    }), {
      status: 403,
      headers: { 'Content-Type': 'application/json' }
    })
  }

  // Show unauthorized page for web routes
  return new Response(`
    <!DOCTYPE html>
    <html>
    <head>
      <title>Unauthorized Access</title>
      <style>
        body {
          font-family: Arial, sans-serif;
          display: flex;
          justify-content: center;
          align-items: center;
          height: 100vh;
          margin: 0;
          background: #f5f5f5;
        }
        .container {
          text-align: center;
          background: white;
          padding: 2rem;
          border-radius: 8px;
          box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 { color: #e74c3c; margin-bottom: 1rem; }
        p { color: #666; margin-bottom: 1.5rem; }
        button {
          background: #3498db;
          color: white;
          border: none;
          padding: 0.5rem 1rem;
          border-radius: 4px;
          cursor: pointer;
          text-decoration: none;
          display: inline-block;
        }
      </style>
    </head>
    <body>
      <div class="container">
        <h1>🚫 Unauthorized Access</h1>
        <p>You don't have permission to access this resource.</p>
        <p>Your role: <strong>${userRole}</strong></p>
        <p>Required roles: <strong>${requiredRoles.join(', ')}</strong></p>
        <button onclick="history.back()">Go Back</button>
      </div>
    </body>
    </html>
  `, {
    status: 403,
    headers: { 'Content-Type': 'text/html' }
  })
}

// Session management with refresh tokens
// File: edge-functions/session.js
export default async (request, context) => {
  const url = new URL(request.url)

  // Skip for auth routes
  if (url.pathname.startsWith('/api/auth/')) {
    return context.next()
  }

  const cookieHeader = request.headers.get('cookie') || ''
  const accessToken = getCookieValue(cookieHeader, 'access_token')
  const refreshToken = getCookieValue(cookieHeader, 'refresh_token')

  // Check access token
  if (accessToken) {
    const authResult = await validateJWTToken(accessToken)

    if (authResult.authenticated) {
      const response = await context.next()
      addUserHeaders(response, authResult)
      return response
    }

    // Access token expired, try refresh
    if (refreshToken && (await shouldRefreshToken(accessToken))) {
      const refreshResult = await refreshAccessToken(refreshToken)

      if (refreshResult.success) {
        const response = await context.next()
        addAuthCookies(response, refreshResult.tokens)
        addUserHeaders(response, refreshResult.user)
        return response
      }
    }
  }

  // No valid tokens, redirect to login
  if (!url.pathname.startsWith('/login')) {
    const loginUrl = new URL('/login', url)
    return Response.redirect(loginUrl, 302)
  }

  return context.next()
}

function getCookieValue(cookieHeader, name) {
  const match = cookieHeader.match(new RegExp(`(^| )${name}=([^;]+)`))
  return match ? match[2] : null
}

async function shouldRefreshToken(accessToken) {
  try {
    const [, payloadBase64] = accessToken.split('.')
    const payload = JSON.parse(atob(payloadBase64))

    // Refresh if token expires within 5 minutes
    return payload.exp - (Date.now() / 1000) < 300
  } catch {
    return false
  }
}

async function refreshAccessToken(refreshToken) {
  try {
    // In production, call your authentication service
    // For demo, we'll simulate a refresh
    const response = await fetch('https://your-auth-service.com/refresh', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${refreshToken}`
      }
    })

    if (response.ok) {
      const data = await response.json()
      return {
        success: true,
        tokens: data.tokens,
        user: data.user
      }
    }

    return { success: false }
  } catch (error) {
    console.error('Token refresh error:', error)
    return { success: false }
  }
}

function addAuthCookies(response, tokens) {
  const cookieOptions = 'Path=/; HttpOnly; Secure; SameSite=Strict'

  response.headers.append('Set-Cookie', `access_token=${tokens.accessToken}; ${cookieOptions}`)
  response.headers.append('Set-Cookie', `refresh_token=${tokens.refreshToken}; ${cookieOptions}`)
}

function addUserHeaders(response, user) {
  response.headers.set('x-user-id', user.userId)
  response.headers.set('x-user-email', user.email)
  response.headers.set('x-user-role', user.role)
}

💻 Otimização de Performance javascript

🔴 complex ⭐⭐⭐⭐⭐

Otimização avançada de performance com caching, compressão, integração CDN e otimização de respostas

⏱️ 45 min 🏷️ netlify, edge functions, performance, optimization
Prerequisites: Performance optimization, Caching strategies, Web vitals, Service workers
// Performance Optimization Examples
// File: edge-functions/performance.js

export default async (request, context) => {
  const url = new URL(request.url)

  // Skip API routes and static assets
  if (url.pathname.startsWith('/api/') || url.pathname.startsWith('/static/')) {
    return context.next()
  }

  const response = await context.next()

  // Optimize based on content type
  const contentType = response.headers.get('content-type') || ''

  if (contentType.includes('text/html')) {
    return optimizeHTMLResponse(request, response)
  } else if (contentType.includes('application/json')) {
    return optimizeJSONResponse(response)
  } else if (contentType.includes('text/css')) {
    return optimizeCSSResponse(response)
  } else if (contentType.includes('application/javascript')) {
    return optimizeJSResponse(response)
  }

  return optimizeResponse(response)
}

async function optimizeHTMLResponse(request, response) {
  const html = await response.text()

  // Critical CSS inlining
  const optimizedHTML = await inlineCriticalCSS(html)

  // Resource hints for performance
  const resourceHints = generateResourceHints()

  // Service worker registration
  const serviceWorkerScript = generateServiceWorkerScript()

  // Performance monitoring script
  const performanceScript = generatePerformanceScript()

  const modifiedHTML = optimizedHTML
    .replace('</head>', resourceHints + serviceWorkerScript + performanceScript + '</head>')
    .replace('</body>', generatePerformanceBanner() + '</body>')

  // Add performance headers
  const performanceHeaders = {
    'Cache-Control': 'public, max-age=3600, must-revalidate',
    'X-Content-Type-Options': 'nosniff',
    'X-DNS-Prefetch-Control': 'on'
  }

  const newResponse = new Response(modifiedHTML, {
    status: response.status,
    statusText: response.statusText,
    headers: { ...Object.fromEntries(response.headers), ...performanceHeaders }
  })

  return newResponse
}

async function inlineCriticalCSS(html) {
  // Extract critical CSS for above-the-fold content
  const criticalCSS = await extractCriticalCSS(html)

  // Find the first CSS link
  const cssLinkMatch = html.match(/<link[^>]*rel=["']stylesheet["'][^>]*>/i)

  if (cssLinkMatch && criticalCSS) {
    // Insert critical CSS inline
    const inlineCSS = `<style data-inline="critical">${criticalCSS}</style>`
    return html.replace(cssLinkMatch[0], inlineCSS + cssLinkMatch[0])
  }

  return html
}

async function extractCriticalCSS(html) {
  // Mock critical CSS extraction
  // In production, use services like Penthouse or CriticalCSS
  return `
    body { font-family: system-ui, -apple-system, sans-serif; line-height: 1.6; }
    h1, h2, h3 { margin-top: 0; }
    img { max-width: 100%; height: auto; }
    .container { max-width: 1200px; margin: 0 auto; padding: 0 20px; }
  `
}

function generateResourceHints() {
  return `
    <!-- DNS prefetch for external resources -->
    <link rel="dns-prefetch" href="//fonts.googleapis.com">
    <link rel="dns-prefetch" href="//fonts.gstatic.com">
    <link rel="dns-prefetch" href="//cdn.jsdelivr.net">
    <link rel="dns-prefetch" href="//api.example.com">

    <!-- Preconnect for critical resources -->
    <link rel="preconnect" href="https://fonts.googleapis.com" crossorigin>
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>

    <!-- Preload critical resources -->
    <link rel="preload" href="/fonts/main.woff2" as="font" type="font/woff2" crossorigin>
    <link rel="preload" href="/css/critical.css" as="style">

    <!-- Prefetch likely next pages -->
    <link rel="prefetch" href="/about">
    <link rel="prefetch" href="/contact">
  `
}

function generateServiceWorkerScript() {
  return `
    <script>
      // Service Worker Registration
      if ('serviceWorker' in navigator) {
        window.addEventListener('load', () => {
          navigator.serviceWorker.register('/sw.js')
            .then(registration => {
              console.log('SW registered: ', registration);
            })
            .catch(registrationError => {
              console.log('SW registration failed: ', registrationError);
            });
        });
      }
    </script>
  `
}

function generatePerformanceScript() {
  return `
    <script>
      // Performance Monitoring
      window.addEventListener('load', () => {
        // Core Web Vitals
        if ('PerformanceObserver' in window) {
          const observer = new PerformanceObserver((list) => {
            for (const entry of list.getEntries()) {
              if (entry.entryType === 'largest-contentful-paint') {
                console.log('LCP:', entry.startTime);
                // Send to analytics
                gtag('event', 'LCP', { value: entry.startTime });
              }
              if (entry.entryType === 'first-input') {
                console.log('FID:', entry.processingStart - entry.startTime);
                gtag('event', 'FID', { value: entry.processingStart - entry.startTime });
              }
              if (entry.entryType === 'layout-shift') {
                if (!entry.hadRecentInput) {
                  console.log('CLS:', entry.value);
                  gtag('event', 'CLS', { value: entry.value });
                }
              }
            }
          });

          observer.observe({ entryTypes: ['largest-contentful-paint', 'first-input', 'layout-shift'] });
        }

        // Navigation timing
        const navigation = performance.getEntriesByType('navigation')[0];
        if (navigation) {
          const loadTime = navigation.loadEventEnd - navigation.fetchStart;
          console.log('Page load time:', loadTime);
          gtag('event', 'page_load_time', { value: loadTime });
        }
      });

      // Lazy loading for images
      document.addEventListener('DOMContentLoaded', () => {
        if ('IntersectionObserver' in window) {
          const imageObserver = new IntersectionObserver((entries, observer) => {
            entries.forEach(entry => {
              if (entry.isIntersecting) {
                const img = entry.target;
                img.src = img.dataset.src;
                img.classList.remove('lazy');
                imageObserver.unobserve(img);
              }
            });
          });

          document.querySelectorAll('img[data-src]').forEach(img => {
            imageObserver.observe(img);
          });
        }
      });
    </script>
  `
}

function generatePerformanceBanner() {
  return `
    <div id="performance-banner" style="position: fixed; bottom: 20px; right: 20px;
                background: rgba(0,0,0,0.8); color: white; padding: 10px 15px;
                border-radius: 5px; font-size: 12px; z-index: 9999; display: none;">
      <div>Performance Metrics:</div>
      <div id="perf-metrics"></div>
    </div>
    <script>
      // Show performance metrics in development
      if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1') {
        document.getElementById('performance-banner').style.display = 'block';

        setInterval(() => {
          const metrics = {
            memory: performance.memory ? Math.round(performance.memory.usedJSHeapSize / 1048576) + 'MB' : 'N/A',
            timing: Math.round(performance.now()) + 'ms'
          };
          document.getElementById('perf-metrics').innerHTML =
            'Memory: ' + metrics.memory + ' | Time: ' + metrics.timing;
        }, 1000);
      }
    </script>
  `
}

function optimizeJSONResponse(response) {
  // Compress JSON responses
  const headers = {
    'Content-Type': 'application/json',
    'Cache-Control': 'public, max-age=300, must-revalidate',
    'Vary': 'Accept-Encoding'
  }

  return new Response(response.body, {
    status: response.status,
    headers: { ...Object.fromEntries(response.headers), ...headers }
  })
}

function optimizeCSSResponse(response) {
  // Optimize CSS delivery
  const headers = {
    'Cache-Control': 'public, max-age=31536000, immutable',
    'X-Content-Type-Options': 'nosniff'
  }

  return new Response(response.body, {
    status: response.status,
    headers: { ...Object.fromEntries(response.headers), ...headers }
  })
}

function optimizeJSResponse(response) {
  // Optimize JavaScript delivery
  const headers = {
    'Cache-Control': 'public, max-age=31536000, immutable',
    'X-Content-Type-Options': 'nosniff'
  }

  return new Response(response.body, {
    status: response.status,
    headers: { ...Object.fromEntries(response.headers), ...headers }
  })
}

function optimizeResponse(response) {
  // General optimization for other content types
  const headers = {
    'Cache-Control': 'public, max-age=3600',
    'Vary': 'Accept-Encoding'
  }

  return new Response(response.body, {
    status: response.status,
    headers: { ...Object.fromEntries(response.headers), ...headers }
  })
}

// Advanced caching strategies
// File: edge-functions/caching.js
export default async (request, context) => {
  const url = new URL(request.url)
  const cacheKey = generateCacheKey(request)

  // Try to get from cache first
  const cachedResponse = await getFromCache(cacheKey)
  if (cachedResponse && !shouldBypassCache(request)) {
    return cachedResponse
  }

  const response = await context.next()

  // Cache successful responses
  if (response.ok) {
    await setCache(cacheKey, response, getCacheDuration(request))
  }

  // Add cache headers
  const cacheHeaders = generateCacheHeaders(request)
  cacheHeaders.forEach((value, key) => {
    response.headers.set(key, value)
  })

  return response
}

function generateCacheKey(request) {
  const url = new URL(request.url)
  const authHeader = request.headers.get('authorization')
  const cookieHeader = request.headers.get('cookie')

  // Include user-specific data in cache key for personalized content
  const userContext = authHeader || cookieHeader || 'anonymous'

  return `${request.method}:${url.pathname}:${url.search}:${userContext}`
}

function shouldBypassCache(request) {
  const url = new URL(request.url)

  // Bypass cache for specific conditions
  return (
    request.headers.get('cache-control') === 'no-cache' ||
    request.headers.get('pragma') === 'no-cache' ||
    url.searchParams.has('nocache') ||
    url.pathname.startsWith('/api/') && request.method !== 'GET'
  )
}

function getCacheDuration(request) {
  const url = new URL(request.url)

  // Different cache durations for different content
  if (url.pathname.startsWith('/api/')) {
    return 300 // 5 minutes for API
  } else if (url.pathname.startsWith('/static/')) {
    return 31536000 // 1 year for static assets
  } else if (url.pathname.includes('/blog/')) {
    return 3600 // 1 hour for blog posts
  } else {
    return 1800 // 30 minutes for regular pages
  }
}

function generateCacheHeaders(request) {
  const url = new URL(request.url)
  const duration = getCacheDuration(request)

  const headers = new Map()

  if (url.pathname.startsWith('/api/')) {
    headers.set('Cache-Control', `public, max-age=${duration}`)
    headers.set('Vary', 'Authorization, Cookie')
  } else if (url.pathname.startsWith('/static/')) {
    headers.set('Cache-Control', `public, max-age=${duration}, immutable`)
    headers.set('Vary', 'Accept-Encoding')
  } else {
    headers.set('Cache-Control', `public, max-age=${duration}, must-revalidate`)
    headers.set('Vary', 'Accept-Encoding, Cookie')
  }

  return headers
}

// Mock cache functions (replace with real KV storage in production)
async function getFromCache(key) {
  try {
    // In production, use Netlify KV or similar
    // const cached = await KV.get(key)
    // return cached ? new Response(cached) : null

    return null // Mock implementation
  } catch (error) {
    console.error('Cache get error:', error)
    return null
  }
}

async function setCache(key, response, duration) {
  try {
    // In production, use Netlify KV or similar
    // const body = await response.text()
    // await KV.put(key, body, { expirationTtl: duration })

    console.log('Cached:', key, 'for', duration, 'seconds')
  } catch (error) {
    console.error('Cache set error:', error)
  }
}