feat(offline): app-shell precache service worker (vite-plugin-pwa)
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 1m11s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m43s
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 1m11s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m43s
Migrate the former install-only public/sw.js to a custom (injectManifest)
service worker built from ui/src/sw.ts by vite-plugin-pwa. It precaches the
app shell + hashed assets (Workbox) so an installed web PWA cold-launches with
no network, and falls in-scope navigations back to the precached shell (the
hash router resolves the route client-side). This is the C1 prerequisite for a
usable offline mode: without it a cold offline launch cannot load the bundle.
- ui/src/sw.ts: skipWaiting + clientsClaim + cleanupOutdatedCaches +
precacheAndRoute(__WB_MANIFEST) + a NavigationRoute fallback to index.html,
deny-listing the RPC path and /_gm. The Connect stream and API POSTs are
never precached nor intercepted.
- vite.config.ts: VitePWA(injectManifest); manifest:false (we ship our own),
injectRegister:false (registration stays manual + web-only in pwa.svelte.ts),
disabled in the mock build (Playwright unperturbed); landing + polyfills
excluded from the precache.
- Removed public/sw.js; the registration path (dist/sw.js at /app/) is unchanged.
- New dev deps: vite-plugin-pwa + workbox-{core,precaching,routing}.
Docs: ARCHITECTURE + gateway/README. Offline cold-launch is contour-verified
(the mock e2e harness disables the SW).
This commit is contained in:
@@ -76,9 +76,11 @@ export async function promptInstall(): Promise<void> {
|
||||
}
|
||||
|
||||
/**
|
||||
* registerServiceWorker registers the install-only worker (ui/public/sw.js). Web-only: skipped in
|
||||
* the mock build (a real worker would perturb the Playwright run) and inside a Telegram/VK Mini
|
||||
* App. Best-effort — a failed registration only means the app is not installable.
|
||||
* registerServiceWorker registers the app-shell service worker (built from ui/src/sw.ts to
|
||||
* dist/sw.js by vite-plugin-pwa): it satisfies Chromium's installability requirement and precaches
|
||||
* the shell + assets so an installed PWA cold-launches offline. Web-only: skipped in the mock build
|
||||
* (a real worker would perturb the Playwright run) and inside a Telegram/VK Mini App. Best-effort —
|
||||
* a failed registration only means the app is not installable and cannot launch offline.
|
||||
*/
|
||||
export function registerServiceWorker(): void {
|
||||
if (typeof navigator === 'undefined' || !('serviceWorker' in navigator)) return;
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/// <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, createHandlerBoundToURL, 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);
|
||||
|
||||
// Serve the precached shell for any in-scope top-level navigation. Deny-list the RPC path and the
|
||||
// admin console so those are never resolved to the app shell.
|
||||
registerRoute(
|
||||
new NavigationRoute(createHandlerBoundToURL('index.html'), {
|
||||
denylist: [/^\/scrabble\.edge\.v1\.Gateway/, /^\/_gm\//],
|
||||
}),
|
||||
);
|
||||
Reference in New Issue
Block a user