/// // Offline app-shell service worker (vite-plugin-pwa, injectManifest strategy). It precaches the app // shell and its hashed assets so the installed PWA cold-launches with no network, and falls every // in-scope navigation back to the precached shell (the hash router resolves the route client-side, // so any deep link boots offline). It supersedes the former install-only public/sw.js. The // Connect-RPC stream and the runtime API calls are POSTs — never precached, never matched by the // navigation route — so the live app, its immutable hashed assets and the event stream are never // served stale. Registration stays manual and web-only (pwa.svelte.ts): skipped in the mock build // and inside a Telegram/VK Mini App. import { clientsClaim } from 'workbox-core'; import { cleanupOutdatedCaches, matchPrecache, precacheAndRoute } from 'workbox-precaching'; import { NavigationRoute, registerRoute } from 'workbox-routing'; // The injectManifest build replaces self.__WB_MANIFEST with the precache list; the DOM lib knows // neither it nor the service-worker scope, so narrow self here. declare const self: ServiceWorkerGlobalScope & { __WB_MANIFEST: (string | { url: string; revision: string | null })[]; }; // Activate a new revision immediately and take control of open clients, matching the former SW's // skipWaiting; the no-cache HTML shell + immutable hashed assets keep a mid-session update safe. self.skipWaiting(); clientsClaim(); // Drop precaches left by a previous SW revision (including the old install-only shell cache). cleanupOutdatedCaches(); // Navigations are network-first so a new deploy loads immediately when online: fetch the fresh // (no-cache) shell from the server — it references the new hashed assets, which are then fetched // fresh — and only fall back to the precached shell when the network is unreachable or too slow (a // short timeout). This keeps the app up to date online while still cold-launching offline. Only the // tiny HTML is re-fetched; the immutable hashed assets stay cache-first (precacheAndRoute below) and // re-download only when their hash — i.e. the version — changes. Deny-list the RPC path and the // admin console so those are never resolved to the app shell. // // This route MUST be registered BEFORE precacheAndRoute: Workbox matches routes in registration // order, and the precache route matches shell navigations too (its directoryIndex is index.html), so // registering it first would shadow this handler and serve the shell CACHE-FIRST — i.e. the old // build's version until the next load. NavigationRoute matches only request.mode === 'navigate', so // hashed-asset requests still fall through to the cache-first precache route below. const NAV_TIMEOUT_MS = 3000; async function freshShell({ request }: { request: Request }): Promise { const ctrl = new AbortController(); const timer = setTimeout(() => ctrl.abort(), NAV_TIMEOUT_MS); try { const res = await fetch(request, { signal: ctrl.signal }); if (res.ok) return res; } catch { /* offline or too slow — serve the precached shell below */ } finally { clearTimeout(timer); } return (await matchPrecache('index.html')) ?? Response.error(); } registerRoute(new NavigationRoute(freshShell, { denylist: [/^\/scrabble\.edge\.v1\.Gateway/, /^\/_gm\//] })); // Precache the shell + hashed assets injected at build time — exactly what a cold offline launch // needs (the precached index.html is also the offline fallback matchPrecache serves above). The // immutable hashed assets stay cache-first here; the landing page and the old-engine polyfill bundle // are excluded via the build globs. precacheAndRoute(self.__WB_MANIFEST);