020742fad3
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 18s
CI / ui (pull_request) Successful in 1m10s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m43s
The C1 service worker served the precached shell for every navigation, even online, so a new deploy only reached a client after the worker updated its precache in the background (a one-launch lag): a cold online start showed the old version until then, and only a reinstall forced it fresh. - sw.ts: navigations are now network-first — fetch the fresh (no-cache) shell from the server on each online launch (it references the new hashed assets, fetched fresh), with a 3s timeout falling back to the precached shell when the network is unreachable or too slow. Only the tiny HTML is re-fetched; the immutable hashed assets stay cache-first and re-download only when their hash (the version) changes. Offline cold-launch still works via the fallback. - webui.go: serve sw.js with Cache-Control: no-cache so the browser reliably detects a new deploy's worker (regression-tested). Online launch is contour-verified (the mock e2e disables the SW); e2e 196. No new deps — the network-first handler is hand-written (~12 lines).
53 lines
3.0 KiB
TypeScript
53 lines
3.0 KiB
TypeScript
/// <reference lib="webworker" />
|
|
// 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();
|
|
|
|
// Precache the shell + hashed assets injected at build time — exactly what a cold offline launch
|
|
// needs. The landing page and the old-engine polyfill bundle are excluded via the build globs.
|
|
precacheAndRoute(self.__WB_MANIFEST);
|
|
|
|
// 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 above) 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.
|
|
const NAV_TIMEOUT_MS = 3000;
|
|
async function freshShell({ request }: { request: Request }): Promise<Response> {
|
|
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\//] }));
|