cv-web PWA Cache & Update Strategy¶
Last reviewed: 2026-05-21 (CV-195)
cv-web ships as a Progressive Web App on S3 + CloudFront under
simplestruct.com/cv-web/. This document captures how the service worker
caches assets, when caches invalidate, what the user sees during an
update, and how the app behaves offline.
Build configuration¶
webpage/Simplestruct/cv-web/vite.config.ts configures vite-plugin-pwa:
Setting |
Value |
Effect |
|---|---|---|
|
|
Plugin injects the SW registration into |
|
|
A new SW activates immediately instead of waiting for all tabs to close. |
|
|
The new SW takes control of open pages on activate. |
|
|
App shell is precached at install time. |
|
|
manifold-3d wasm chunks cached across runs. |
What gets cached¶
Precache: every static asset emitted by Vite — JS/CSS chunks,
index.html, icons, sample images. Listed indist/sw.jsas__precacheManifest.Runtime cache:
*.wasm(manifold-3d) viaCacheFirst.IndexedDB (Dexie): project state, panel data, and snapshots. Lives in the browser, not the SW cache. See
src/store/db.ts.localStorage:
sw_v4one-shot flag used byindex.htmlto clear legacy service workers (see “Stale SW recovery” below).
What does NOT get cached¶
OpenStreetMap Nominatim geocoding (
ProjectView.tsx) — lazy, fires only when the user enters an address. Failure is silent.Open-Meteo weather forecast (
ProjectView.tsx) — same as above.Sentry telemetry — opportunistic; failure is silent.
These external APIs are not on the offline path. The app boots and operates without any of them.
Cache invalidation — what happens after a deploy¶
The Deploy SimpleStruct workflow uploads new chunks to S3 and (since the
Vite build emits hashed filenames) creates a new sw.js content. The
sequence on the user’s next visit:
Browser fetches
index.html(CloudFront edge-cached briefly, typically ≤ 60 s).The injected SW registration loads the new
sw.js.Workbox detects a precache manifest change, downloads the new chunks, and (because
skipWaiting + clientsClaimare set) activates the new SW.The old tabs are now controlled by the new SW but still holding the old JS chunks in memory. Until reload, the user sees the previous version.
The
useRegisterSWhook inPwaUpdateBannerfiresneedRefresh. The user sees a banner: “A new version of ConstructiVision Web is available. Reload.” Click reloads with the fresh chunks.
If the banner is missed: a hard reload (Ctrl+Shift+R) or closing and reopening the tab also picks up the new build.
Offline behavior¶
After at least one online visit the entire app shell sits in the SW cache. Subsequent loads — including with the network disabled — boot the same way:
index.htmlis served from the SW precache.All JS/CSS chunks come from the precache.
Dexie restores the project state from IndexedDB.
Last-loaded panel renders identically to an online session.
The Welcome screen “Recent Projects” list and the saved snapshots both work offline because both read from Dexie.
Features that require network are gracefully degraded:
Geocoding/weather lookups in ProjectView fail silently.
Sentry events queue locally and replay when connectivity returns.
Install flow¶
PwaInstallButton listens for the Chromium beforeinstallprompt
event. When the browser decides the app is installable (HTTPS, valid
manifest, SW active, sufficient engagement) the button appears
top-right. Clicking it calls the captured prompt() and clears the
state on userChoice. The button hides when the app is already running
in display-mode: standalone (or navigator.standalone on iOS).
Stale SW recovery¶
If a previously-broken SW is stuck in the user’s browser,
index.html includes a one-shot recovery: on load, it unregisters any
existing SW registrations the first time a new sw_v4 localStorage key
is observed. Bump the key (sw_v5, etc.) to force a one-time SW reset
across the user base.
Verification checklist (manual)¶
Run after any change that touches caching, the SW config, or boot path:
Build + serve:
npm run buildthennpm run previewover HTTPS.First load: DevTools → Application → Service Workers → Status =
activated. Application → Cache Storage → precache containsindex.html, JS chunks, icons.Install: Click “Install app” → confirm prompt → confirm icon on home screen / app launcher.
Offline reload: DevTools → Network → “Offline” → reload. App renders. Last-loaded panel visible.
Update flow: rebuild with a one-line change. Reload preview.
PwaUpdateBannershould appear within seconds. Click “Reload” → new chunks active.Standalone test: open the installed app from the home screen while offline. Same result as step 4.
References¶
vite-plugin-pwa: https://vite-pwa-org.netlify.app/
Workbox precache: https://developer.chrome.com/docs/workbox/modules/workbox-precaching/
beforeinstallpromptevent: https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeinstallprompt_eventSource:
webpage/Simplestruct/cv-web/vite.config.ts,src/components/PwaUpdateBanner.tsx,src/components/PwaInstallButton.tsx,src/utils/pwa.ts.