// Service Worker для GrechkaCraft. // // Раньше на любую сетевую ошибку отдавался `new Response('Offline', {status: 503})`. // Из-за этого обычный обрыв сети (моргнувший Wi-Fi, потерянный пакет) выглядел // как «сервер упал»: страница грузилась, но весь JS и мир приходили как 503, // и пользователь видел либо пустой экран, либо сообщение об ошибке. // // Теперь: // - статика (HTML/JS/CSS/ассеты) кэшируется и отдаётся из кэша при сбое; // - навигация при сбое отдаёт закэшированную копию страницы; // - если кэша нет — отдаём 200 с минимальной страницей-заглушкой, а НЕ 503, // потому что 503 для пользователя неотличим от «сервис умер». const CACHE = 'grechka-v3'; const PRECACHE_URLS = [ './', './index.html', './index-v2.html', './game.js', './game3d.js', './style.css', './manifest.json', ]; self.addEventListener('install', (e) => { self.skipWaiting(); e.waitUntil( caches.open(CACHE) // addAll падает целиком, если хоть один URL недоступен — кэшируем по одному. .then((c) => Promise.all( PRECACHE_URLS.map((u) => c.add(u).catch(() => null)) )) .then(() => self.clients.claim()) ); }); self.addEventListener('activate', (e) => { e.waitUntil( caches.keys() .then((keys) => Promise.all( keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)) )) .then(() => self.clients.claim()) ); }); const OFFLINE_PAGE = ` Гречка — нет связи

Нет связи с сервером

Игра не смогла загрузиться. Проверь интернет и нажми «Попробовать снова».

`; self.addEventListener('fetch', (e) => { const req = e.request; if (req.method !== 'GET') return; const url = new URL(req.url); // Кросс-доменные запросы (socket.io CDN, apigrech, telegram) не трогаем. if (url.origin !== self.location.origin) return; // Навигация: сначала сеть, при сбое — кэш страницы, иначе заглушка. if (req.mode === 'navigate') { e.respondWith( fetch(req) .then((res) => { const copy = res.clone(); caches.open(CACHE).then((c) => c.put(req, copy)); return res; }) .catch(() => caches.match(req, { ignoreSearch: true }) .then((r) => r || caches.match('./index.html')) .then((r) => r || new Response(OFFLINE_PAGE, { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8' }, }))) ); return; } // Статика: сначала сеть, кэшируем свежее; при сбое — из кэша. e.respondWith( fetch(req) .then((res) => { if (res && res.ok && res.type === 'basic') { const copy = res.clone(); caches.open(CACHE).then((c) => c.put(req, copy)); } return res; }) .catch(() => caches.match(req, { ignoreSearch: true }) .then((r) => r || new Response('', { status: 200 }))) ); });