Spaces:
Running on Zero
Running on Zero
| /** | |
| * HearthNet Service Worker | |
| * | |
| * Enables offline-first functionality and caching for PWA. | |
| * Installed via manifest.json | |
| */ | |
| const CACHE_NAME = 'hearthnet-v0.1.0'; | |
| const STATIC_ASSETS = [ | |
| '/', | |
| '/index.html', | |
| '/manifest.json', | |
| '/static/icon-192.png', | |
| '/static/icon-512.png', | |
| '/static/styles.css', | |
| ]; | |
| /** | |
| * Install event: Pre-cache static assets | |
| */ | |
| self.addEventListener('install', (event) => { | |
| console.log('[Service Worker] Installing...'); | |
| event.waitUntil( | |
| caches.open(CACHE_NAME).then((cache) => { | |
| console.log('[Service Worker] Caching static assets'); | |
| return cache.addAll(STATIC_ASSETS).catch((err) => { | |
| console.warn('[Service Worker] Some assets failed to cache:', err); | |
| // Don't fail on cache errors (some assets may not exist) | |
| }); | |
| }) | |
| ); | |
| self.skipWaiting(); | |
| }); | |
| /** | |
| * Activate event: Clean up old caches | |
| */ | |
| self.addEventListener('activate', (event) => { | |
| console.log('[Service Worker] Activating...'); | |
| event.waitUntil( | |
| caches.keys().then((cacheNames) => { | |
| return Promise.all( | |
| cacheNames.map((cacheName) => { | |
| if (cacheName !== CACHE_NAME) { | |
| console.log('[Service Worker] Deleting old cache:', cacheName); | |
| return caches.delete(cacheName); | |
| } | |
| }) | |
| ); | |
| }) | |
| ); | |
| self.clients.claim(); | |
| }); | |
| /** | |
| * Fetch event: Cache-first strategy for static assets, network-first for API calls | |
| */ | |
| self.addEventListener('fetch', (event) => { | |
| const { request } = event; | |
| const url = new URL(request.url); | |
| // Skip non-GET requests | |
| if (request.method !== 'GET') { | |
| return; | |
| } | |
| // API calls: Network-first (always try server) | |
| if (url.pathname.startsWith('/api/') || | |
| url.pathname.startsWith('/bus/') || | |
| url.pathname.startsWith('/trace/')) { | |
| event.respondWith( | |
| fetch(request) | |
| .then((response) => { | |
| // Cache successful responses | |
| if (response.status === 200) { | |
| const cache = caches.open(CACHE_NAME); | |
| cache.then((c) => c.put(request, response.clone())); | |
| } | |
| return response; | |
| }) | |
| .catch(() => { | |
| // Fallback to cache if offline | |
| return caches.match(request).then((cached) => { | |
| if (cached) { | |
| console.log('[Service Worker] Serving from cache (offline):', request.url); | |
| return cached; | |
| } | |
| // Return offline page or error | |
| return new Response('Offline - API unavailable', { | |
| status: 503, | |
| statusText: 'Service Unavailable', | |
| }); | |
| }); | |
| }) | |
| ); | |
| return; | |
| } | |
| // Static assets: Cache-first | |
| event.respondWith( | |
| caches.match(request).then((cached) => { | |
| if (cached) { | |
| return cached; | |
| } | |
| return fetch(request).then((response) => { | |
| if (response.status === 200) { | |
| caches.open(CACHE_NAME).then((cache) => { | |
| cache.put(request, response.clone()); | |
| }); | |
| } | |
| return response; | |
| }); | |
| }) | |
| ); | |
| }); | |
| /** | |
| * Background sync: Queue API calls when offline | |
| */ | |
| self.addEventListener('sync', (event) => { | |
| if (event.tag === 'sync-messages') { | |
| event.waitUntil( | |
| // Implement message queue sync here | |
| Promise.resolve() | |
| ); | |
| } | |
| }); | |
| /** | |
| * Push notifications | |
| */ | |
| self.addEventListener('push', (event) => { | |
| const options = { | |
| body: event.data?.text() || 'New notification from HearthNet', | |
| icon: '/static/icon-192.png', | |
| badge: '/static/icon-96.png', | |
| tag: 'hearthnet-notification', | |
| requireInteraction: false, | |
| }; | |
| event.waitUntil( | |
| self.registration.showNotification('HearthNet', options) | |
| ); | |
| }); | |
| console.log('[Service Worker] Loaded and ready'); | |