/* * Сервис-воркер glchat (AGENT.md, Фаза 5): офлайн-заглушка и кэш статики. * * Правила простые и безопасные: * - навигация — сеть, при ошибке офлайн-страница (данные всегда свежие); * - собранная статика `/assets/*` (хешированные имена) — из кэша; * - API, шлюз и файлы никогда не кэшируются: это приватные данные. */ const VERSION = 'glchat-v1'; const OFFLINE_URL = '/offline.html'; self.addEventListener('install', (event) => { event.waitUntil( caches.open(VERSION).then((cache) => cache.addAll([OFFLINE_URL, '/manifest.webmanifest'])), ); self.skipWaiting(); }); self.addEventListener('activate', (event) => { event.waitUntil( caches .keys() .then((keys) => Promise.all(keys.filter((key) => key !== VERSION).map((key) => caches.delete(key))), ) .then(() => self.clients.claim()), ); }); /** Приватные запросы: их нельзя ни кэшировать, ни отдавать из кэша. */ function isPrivate(url) { return ( url.pathname.startsWith('/api/') || url.pathname.startsWith('/files/') || url.pathname.startsWith('/gateway') || url.pathname.startsWith('/rtc') ); } self.addEventListener('fetch', (event) => { const request = event.request; if (request.method !== 'GET') { return; } const url = new URL(request.url); if (url.origin !== self.location.origin || isPrivate(url)) { return; } if (request.mode === 'navigate') { event.respondWith( fetch(request).catch(() => caches.match(request).then((cached) => cached ?? caches.match(OFFLINE_URL)), ), ); return; } if (url.pathname.startsWith('/assets/')) { event.respondWith( caches.match(request).then( (cached) => cached ?? fetch(request).then((response) => { if (response.ok) { const copy = response.clone(); void caches.open(VERSION).then((cache) => cache.put(request, copy)); } return response; }), ), ); return; } // Иконки, манифест и прочая статика: сеть, при недоступности — кэш. event.respondWith( fetch(request) .then((response) => { if ( response.ok && (url.pathname.startsWith('/icons/') || url.pathname === '/manifest.webmanifest') ) { const copy = response.clone(); void caches.open(VERSION).then((cache) => cache.put(request, copy)); } return response; }) .catch(() => caches.match(request).then((cached) => cached ?? Response.error())), ); });