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( '
You are currently offline. Please reconnect to view new updates or visit previously cached pages.
Return to Home