const CACHE_NAME = 'portfolio-cache-v1'; // Core assets to precache immediately on install const PRECACHE_ASSETS = [ '/', '/manifest.webmanifest', '/logo.png', '/favicon.ico', '/CV-Cris-Charles-Garcia.pdf' ]; // Install: precache shell self.addEventListener('install', (event) => { event.waitUntil( caches.open(CACHE_NAME).then((cache) => { return cache.addAll(PRECACHE_ASSETS).catch((err) => { console.warn('SW: Precache partial failure (non-critical):', err); }); }).then(() => self.skipWaiting()) ); }); // Activate: clean up old cache versions self.addEventListener('activate', (event) => { event.waitUntil( caches.keys().then((cacheNames) => { return Promise.all( cacheNames .filter((name) => name !== CACHE_NAME) .map((name) => caches.delete(name)) ); }).then(() => self.clients.claim()) ); }); // Fetch: Network-First for HTML navigation, Stale-While-Revalidate for static assets self.addEventListener('fetch', (event) => { const { request } = event; const url = new URL(request.url); // Skip non-GET requests and browser extensions if (request.method !== 'GET' || !url.protocol.startsWith('http')) { return; } // 1. Navigation requests (HTML pages): Network-First with Cache Fallback if (request.mode === 'navigate') { event.respondWith( fetch(request) .then((networkResponse) => { if (networkResponse && networkResponse.status === 200) { const responseClone = networkResponse.clone(); caches.open(CACHE_NAME).then((cache) => { cache.put(request, responseClone); }); } return networkResponse; }) .catch(async () => { // Offline fallback const cachedResponse = await caches.match(request); if (cachedResponse) { return cachedResponse; } // Fallback to home page root if specific subpage isn't cached const rootCached = await caches.match('/'); if (rootCached) { return rootCached; } return new Response( 'Offline - Cris Charles

Offline Mode

You are currently offline. Please reconnect to view new updates or visit previously cached pages.

Return to Home
', { headers: { 'Content-Type': 'text/html' } } ); }) ); return; } // 2. Static Assets (CSS, JS, WebP, SVG, Fonts, PNG): Stale-While-Revalidate event.respondWith( caches.match(request).then((cachedResponse) => { const fetchPromise = fetch(request) .then((networkResponse) => { if (networkResponse && networkResponse.status === 200) { const responseClone = networkResponse.clone(); caches.open(CACHE_NAME).then((cache) => { cache.put(request, responseClone); }); } return networkResponse; }) .catch(() => { // Network failed, nothing extra to do if cached response is returned }); return cachedResponse || fetchPromise; }) ); });