From 216e3f3f10aaa5f5bdbeefe5c1c09fb0472f42dd Mon Sep 17 00:00:00 2001 From: nkburdick Date: Fri, 24 Apr 2026 13:06:24 -0700 Subject: [PATCH] fix(pwa): format-detection meta + autoUpdate SW registration (Stage 1) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three research agents converged on a single high-leverage diagnosis that most of yesterday's "iOS PWA reactivity is broken" patching was chasing: Nick's iPhone PWA has been running a stale service worker shell from 2026-04-17 for 6 days while the build was broken, then ran a sequence of hot-swapped modules across 8 deploys without a version-bumped SW to force the shell to reload. Classic iOS standalone PWA symptom cluster — lifecycle hooks look flaky, bind:value seems not to propagate, goto() stalls, send button "does nothing." In reality each deploy was fighting a service-worker cache that never turned over. Three changes, all small, all low-risk: - Add `` to app.html. Safari auto-detects phone numbers / dates / addresses and rewrites the DOM *before* Svelte hydration runs, which breaks hydration parity and makes SvelteKit fall back to client-only re-render. This masquerades as "lifecycle hooks don't fire" but is really hydration aborting. See github.com/sveltejs/svelte/issues/17357. - Bump package.json 0.2.1 → 0.3.0. Cache name derivation in vite-pwa includes the version, so every installed PWA will detect the new SW on next open and replace the old one. - Flip registerType 'prompt' → 'autoUpdate' in vite.config.ts. Silent replacement is the right UX for a personal app; the prompt flow assumes the user can see and tap a banner, but iOS standalone PWAs often can't surface that cleanly. Also see github.com/sveltejs/svelte/issues/12313 — stale SW + dynamic imports is exactly this session's symptom. The three research agents (web-research-analyst, codebase auditor, Architect) all agreed these are the three top-leverage SvelteKit+iOS PWA best practice fixes for this class of issue. Full synthesis in MEMORY/WORK/20260424-112736_oracle-chat-pwa-architect-review/PRD.md. Co-Authored-By: Claude Opus 4.7 (1M context) --- package.json | 2 +- src/app.html | 9 +++++++++ vite.config.ts | 7 ++++++- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 85ef100..ed97aa0 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "app", "private": true, - "version": "0.2.1", + "version": "0.3.0", "type": "module", "scripts": { "dev": "vite dev", diff --git a/src/app.html b/src/app.html index 16a81e0..a0d5260 100644 --- a/src/app.html +++ b/src/app.html @@ -3,6 +3,15 @@ + + diff --git a/vite.config.ts b/vite.config.ts index 364f664..e811ee0 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -15,7 +15,12 @@ export default defineConfig({ SvelteKitPWA({ srcDir: 'src', strategies: 'injectManifest', - registerType: 'prompt', + // `autoUpdate` silently replaces the SW as soon as a new version + // is detected, vs `prompt` which requires a user tap. iOS + // standalone PWAs can't easily surface that prompt (no reliable + // beforeinstallprompt-equivalent) and can get stuck on a broken + // old SW indefinitely — see github.com/sveltejs/svelte/issues/12313. + registerType: 'autoUpdate', injectManifest: { globPatterns: ['client/**/*.{js,css,ico,png,svg,webp,woff,woff2}'] },