fix(pwa): Zombie-Waiting-SW erkennen und stumm aufräumen
All checks were successful
Build & Publish Docker Image / build-and-push (push) Successful in 1m23s

Der vorige Fix (3d6f639) hat den Endlos-Toast "Neue Kochwas-Version
verfügbar" im Happy-Path beseitigt, aber das Grundproblem blieb:
pwaStore.init() hat blind `registration.waiting` als Update-Signal
verwendet.

Beobachtet auf der Live-PWA: Nach dem Reload-Klick existiert
registration.waiting weiter — als bit-identischer Zombie zum aktiven
SW (nur ein einziger shell-Cache `kochwas-shell-<version>`, Server-
Fetch liefert dieselbe Version-Konstante wie der active-SW). Der
Browser räumt diesen waiting-Slot nicht von selbst auf. Ergebnis:
beim nächsten init() steht `registration.waiting` wieder, Toast
kommt wieder.

Fix: SW bekommt einen GET_VERSION-MessageHandler. pwaStore fragt
active und waiting per MessageChannel nach ihrer Version. Sind sie
gleich, schickt er SKIP_WAITING silent an den Zombie und zeigt
KEINEN Toast. Nur bei echter Versions-Differenz erscheint das Update-
Angebot. Der alte onUpdateFound-Pfad geht den gleichen Weg.

Regression-Test: tests/unit/pwa-store.test.ts deckt Zombie-, Echt-
Update- und Fallback-Fall (alter SW ohne GET_VERSION) ab.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
hsiegeln
2026-04-18 17:48:55 +02:00
parent 42f79f122b
commit 858d4c1622
3 changed files with 168 additions and 5 deletions

View File

@@ -99,6 +99,14 @@ self.addEventListener('message', (event) => {
} else if (data.type === 'SKIP_WAITING') {
// Wird vom pwaStore nach User-Klick auf "Neu laden" geschickt.
void self.skipWaiting();
} else if (data.type === 'GET_VERSION') {
// pwaStore nutzt das, um active- und waiting-SW zu vergleichen: sind
// beide bit-gleich (gleicher $service-worker-Version-Hash), dann ist
// der waiting-SW ein Zombie aus einer vorigen Session und KEIN echtes
// Update — sonst würde der "Neue Version"-Toast unbegrenzt wieder-
// kehren, weil `registration.waiting` belegt bleibt.
const port = event.ports[0] as MessagePort | undefined;
port?.postMessage({ version });
}
});