diff --git a/public/sw.js b/public/sw.js index 21a57f712de..7c39ba46bbd 100644 --- a/public/sw.js +++ b/public/sw.js @@ -44,13 +44,16 @@ const cacheOfflinePageAndResources = async service => { console.log(`[SW v${version}] Cached offline page for ${service}`); const html = await resp.text(); - const scriptSrcs = [ - ...html.matchAll(/]+src=["']([^"']+)["']/g), - ].map(m => m[1]); - const linkHrefs = [...html.matchAll(/]+href=["']([^"']+)["']/g)].map( - m => m[1], + const doc = new DOMParser().parseFromString(html, 'text/html'); + const scriptSrcs = Array.from(doc.querySelectorAll('script[src]')).map(el => + el.getAttribute('src'), ); + const linkHrefs = Array.from(doc.querySelectorAll('link[href]')).map(el => + el.getAttribute('href'), + ); + const resources = [...scriptSrcs, ...linkHrefs] + .filter(Boolean) .filter(url => url.startsWith('/') || url.startsWith(self.location.origin)) .map(url => new URL(url, self.location.origin).href); @@ -79,38 +82,7 @@ const WEBP_IMAGE = // -------------Install event ------- self.addEventListener('install', event => { console.log(`[SW v${version}] Installing...`); - - event.waitUntil( - (async () => { - const cache = await caches.open(cacheName); - const clients = await self.clients.matchAll({ type: 'window' }); - - // Get unique services from PWA clients only - const pwaServices = [ - ...new Set( - clients - .filter(client => pwaClients.get(client.id)) - .map(client => getServiceFromUrl(client.url)) - .filter(Boolean), - ), - ]; - - if (pwaServices.length > 0) { - console.log( - `[SW v${version}] Caching offline pages for PWA:`, - pwaServices, - ); - } - - // Cache offline pages for PWA services only - await Promise.allSettled( - pwaServices.map(async service => { - return cacheOfflinePageAndResources(service); - }), - ); - self.skipWaiting(); - })(), - ); + self.skipWaiting(); }); // -------Activate Handler------------- @@ -134,16 +106,18 @@ self.addEventListener('message', async event => { if (event.data?.type === 'PWA_STATUS') { const clientId = event.source.id; const { isPWA } = event.data; - pwaClients.set(clientId, isPWA); + // pwaClients.set(clientId, isPWA); if (isPWA) { - const cache = await caches.open(cacheName); - await cache.put('pwa_installed', new Response('true')); + // const cache = await caches.open(cacheName); + // await cache.put('pwa_installed', new Response('true')); + pwaClients.set(clientId, isPWA); + const service = getServiceFromUrl(event.source.url); await cacheOfflinePageAndResources(service); } else { - const cache = await caches.open(cacheName); - await cache.delete('pwa_installed'); + // const cache = await caches.open(cacheName); + // await cache.delete('pwa_installed'); } } }); @@ -188,12 +162,43 @@ const fetchEventHandler = async event => { return response; })(), ); + } else if (event.request.url.includes('/_next/static/')) { + // Network-first for Next.js chunks (dev mode compatibility) + event.respondWith( + (async () => { + try { + const networkResp = await fetch(event.request); + const cache = await caches.open(cacheName); + cache.put(event.request, networkResp.clone()); + return networkResp; + } catch (err) { + const cache = await caches.open(cacheName); + const cachedResp = await cache.match(event.request); + if (cachedResp) return cachedResp; + throw err; + } + })(), + ); } else if (event.request.mode === 'navigate') { const { url } = event.request; - console.log(`[SW FETCH] Navigation: ${url}`); event.respondWith( (async () => { + const client = await self.clients.get(event.clientId); + const isPWA = client && pwaClients.get(client.id); + const cache = await caches.open(cacheName); + + // const pwaMarkerExists = await cache.match('pwa_installed'); + // if (!isPWA && pwaMarkerExists && isPWA !== undefined) { + // await cache.delete('pwa_installed'); + // } + console.log('[SW FETCH] Navigation', { + url: event.request.url, + clientId: event.clientId, + isPWA, + client, + event, + }); try { // Use preload if available const preloadResp = await event.preloadResponse; @@ -204,8 +209,8 @@ const fetchEventHandler = async event => { // Cache offline page if in PWA mode if (networkResp && networkResp.ok && event.clientId) { console.log('[SW] Caching offline page if PWA if network is ok'); - const client = await self.clients.get(event.clientId); - const isPWA = client && pwaClients.get(client.id); + // const client = await self.clients.get(event.clientId); + // const isPWA = client && pwaClients.get(client.id); if (isPWA) { const service = getServiceFromUrl(url); cacheOfflinePageAndResources(service).catch(err => @@ -218,12 +223,11 @@ const fetchEventHandler = async event => { } catch (err) { console.log('[SW] Navigation failed:', url, err); - const cache = await caches.open(cacheName); - const pwaMarker = await cache.match('pwa_installed'); - console.log('[SW] PWA Marker:', pwaMarker); + // const pwaMarker = await cache.match('pwa_installed'); + // console.log('[SW] PWA Marker:', pwaMarker); // Only show offline page for installed PWA - if (pwaMarker) { + if (isPWA) { const service = getServiceFromUrl(url); const offlineUrl = new URL( getOfflinePageUrl(service), @@ -232,18 +236,12 @@ const fetchEventHandler = async event => { const cachedOffline = await cache.match(offlineUrl); if (cachedOffline) { + console.log('[SW] Serving cached offline page'); return cachedOffline; } } - - // Canonical site offline fallback - return new Response( - 'You are offline. Please check your network and reload the page', - { - status: 503, - headers: { 'Content-Type': 'text/plain' }, - }, - ); + // fallback to browser default behavior + return Response.error(); } })(), );