Spaces:
Sleeping
Sleeping
| // STEM Copilot β Service Worker (PWA installability + safe caching) | |
| // | |
| // Caching strategy: | |
| // β’ API calls (/chat, /auth, β¦) β never touched, always network. | |
| // β’ Navigations + app code (HTML/JS/ β NETWORK-FIRST. This is critical: a | |
| // CSS) cache-first shell used to serve a stale | |
| // index.html / app.js after every deploy, | |
| // which forced users to hard-refresh | |
| // (Ctrl+Shift+R) and could surface an old, | |
| // un-gated build of the app. Network-first | |
| // means everyone always gets the live code. | |
| // β’ Other static assets (images, β cache-first (fine to be stale). | |
| // fonts) | |
| const CACHE_NAME = 'stemcopilot-v3'; | |
| const ASSET_URLS = [ | |
| '/assets/bot.png', | |
| '/assets/stem_black.png', | |
| '/assets/stembotix.png', | |
| ]; | |
| self.addEventListener('install', (e) => { | |
| e.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSET_URLS))); | |
| self.skipWaiting(); | |
| }); | |
| self.addEventListener('activate', (e) => { | |
| e.waitUntil( | |
| caches.keys().then((keys) => | |
| Promise.all(keys.filter((k) => k !== CACHE_NAME).map((k) => caches.delete(k))) | |
| ) | |
| ); | |
| self.clients.claim(); | |
| }); | |
| function _isApi(url) { | |
| return ['/chat', '/auth', '/threads', '/history', '/user', '/export', | |
| '/health', '/feedback', '/notes', '/upload-doc', '/rename', '/thread'] | |
| .some((p) => url.includes(p)); | |
| } | |
| // HTML navigations and the core app code must always come fresh from the network. | |
| function _isAppCode(req, url) { | |
| return req.mode === 'navigate' | |
| || url.endsWith('/') | |
| || url.includes('/static/app.js') | |
| || url.includes('/static/style.css') | |
| || url.includes('/static/index.html'); | |
| } | |
| self.addEventListener('fetch', (e) => { | |
| const url = e.request.url; | |
| if (_isApi(url)) return; // straight to network, no SW involvement | |
| if (_isAppCode(e.request, url)) { | |
| // Network-first: fetch live, fall back to cache only when offline. | |
| e.respondWith( | |
| fetch(e.request) | |
| .then((resp) => { | |
| const copy = resp.clone(); | |
| caches.open(CACHE_NAME).then((c) => c.put(e.request, copy)).catch(() => {}); | |
| return resp; | |
| }) | |
| .catch(() => caches.match(e.request)) | |
| ); | |
| return; | |
| } | |
| // Everything else (images, fonts): cache-first. | |
| e.respondWith(caches.match(e.request).then((cached) => cached || fetch(e.request))); | |
| }); | |