1const CACHE_VERSION = 'v3'; 2const STATIC_CACHE = 'floraflor-static-' + CACHE_VERSION; 3const DYNAMIC_CACHE = 'floraflor-dynamic-' + CACHE_VERSION; 4const APP_SHELL_CACHE = 'floraflor-shell-' + CACHE_VERSION; 5 6// Core app shell - cached aggressively for instant loads 7const APP_SHELL = [ 8 '/', 9 '/manifest.json', 10 '/favicon.png', 11 '/apple-touch-icon.png', 12 '/icon-192x192.png', 13 '/icon-512x512.png' 14]; 15 16// Install - cache app shell immediately 17self.addEventListener('install', function(event) { 18 console.log('[SW] Installing v3...'); 19 event.waitUntil( 20 caches.open(APP_SHELL_CACHE) 21 .then(function(cache) { 22 console.log('[SW] Caching app shell'); 23 return cache.addAll(APP_SHELL); 24 }) 25 .then(() => self.skipWaiting()) 26 ); 27}); 28 29// Activate - clean ALL old caches 30self.addEventListener('activate', function(event) { 31 console.log('[SW] Activating v3...'); 32 const currentCaches = [STATIC_CACHE, DYNAMIC_CACHE, APP_SHELL_CACHE]; 33 event.waitUntil( 34 caches.keys().then(function(cacheNames) { 35 return Promise.all( 36 cacheNames 37 .filter(name => !currentCaches.includes(name)) 38 .map(name => { 39 console.log('[SW] Deleting old cache:', name); 40 return caches.delete(name); 41 }) 42 ); 43 }).then(() => self.clients.claim()) 44 ); 45}); 46 47// Fetch strategy 48self.addEventListener('fetch', function(event) { 49 const url = new URL(event.request.url); 50 51 // Skip non-GET 52 if (event.request.method !== 'GET') return; 53 54 // Skip external origins we don't own 55 const isOwnOrigin = url.origin === self.location.origin || 56 url.origin.includes('lovableproject.com') || 57 url.origin.includes('lovable.app') || 58 url.origin.includes('floraflor.vercel.app'); 59 if (!isOwnOrigin) return; 60 61 // Never cache API / auth / edge functions 62 if (url.pathname.includes('/rest/') || 63 url.pathname.includes('/auth/') || 64 url.pathname.includes('/functions/') || 65 url.hostname.includes('supabase')) { 66 return; 67 } 68 69 // Navigation: stale-while-revalidate (show cache instantly, update in bg) 70 if (event.request.mode === 'navigate') { 71 event.respondWith( 72 caches.match(event.request).then(cached => { 73 const networkFetch = fetch(event.request).then(response => { 74 const clone = response.clone(); 75 caches.open(APP_SHELL_CACHE).then(c => c.put(event.request, clone)); 76 return response; 77 }).catch(() => { 78 // Offline fallback 79 if (cached) return cached; 80 return caches.match('/').then(index => { 81 if (index) return index; 82 return new Response('Offline - Por favor verifica tu conexión', { 83 headers: { 'Content-Type': 'text/html; charset=utf-8' } 84 }); 85 }); 86 }); 87 return cached || networkFetch; 88 }) 89 ); 90 return; 91 } 92 93 // JS/CSS bundles with hashes - cache forever (immutable) 94 if (url.pathname.match(/\/assets\/.*\.[a-f0-9]{8,}\.(js|css)$/)) { 95 event.respondWith( 96 caches.match(event.request).then(cached => { 97 if (cached) return cached; 98 return fetch(event.request).then(response => { 99 if (response.ok) { 100 const clone = response.clone(); 101 caches.open(STATIC_CACHE).then(c => c.put(event.request, clone)); 102 } 103 return response; 104 }); 105 }) 106 ); 107 return; 108 } 109 110 // Images - cache first with network fallback 111 if (url.pathname.match(/\.(png|jpg|jpeg|svg|ico|webp|gif)$/)) { 112 event.respondWith( 113 caches.match(event.request).then(cached => { 114 if (cached) return cached; 115 return fetch(event.request).then(response => { 116 if (response.ok) { 117 const clone = response.clone(); 118 caches.open(STATIC_CACHE).then(c => c.put(event.request, clone)); 119 } 120 return response; 121 }).catch(() => new Response('', { status: 404 })); 122 }) 123 ); 124 return; 125 } 126 127 // Everything else: network first, cache fallback 128 event.respondWith( 129 fetch(event.request).then(response => { 130 if (response.ok) { 131 const clone = response.clone(); 132 caches.open(DYNAMIC_CACHE).then(c => c.put(event.request, clone)); 133 } 134 return response; 135 }).catch(() => caches.match(event.request)) 136 ); 137}); 138
139// Push notifications 140self.addEventListener('push', function(event) { 141 const data = event.data ? event.data.json() : {}; 142 const title = data.title || 'Floraflor'; 143 const options = { 144 body: data.body || 'Nueva notificación', 145 icon: '/favicon.png', 146 badge: '/favicon.png', 147 data: data.data || {}, 148 tag: data.tag || 'default', 149 requireInteraction: data.requireInteraction || false, 150 actions: data.actions || [] 151 }; 152 event.waitUntil(self.registration.showNotification(title, options)); 153}); 154 155self.addEventListener('notificationclick', function(event) { 156 event.notification.close(); 157 event.waitUntil(clients.openWindow(event.notification.data.url || '/')); 158}); 159 160self.addEventListener('message', function(event) { 161 if (event.data === 'skipWaiting') self.skipWaiting(); 162 if (event.data === 'clearCache') { 163 caches.keys().then(names => Promise.all(names.map(n => caches.delete(n)))); 164 } 165});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.