fix: stale SW chunks serving HTML, PWA cache cleanup, server asset routing

- Server SPA fallback now returns 404 for missing .js/.css assets instead of index.html
- Prevents "Unexpected token '<'" errors from stale chunk URLs
- Serve PWA files (sw.js, workbox, icons) with correct cache headers
- Workbox: cleanupOutdatedCaches, skipWaiting, clientsClaim for instant SW updates
- navigateFallbackDenylist for /api/ routes
- AudioContext comment clarification

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-03-07 23:08:25 +00:00
co-authored by Claude Opus 4.6
parent 20e9f0f7a2
commit 7d5f4a6b8d
3 changed files with 20 additions and 1 deletions
+13 -1
View File
@@ -75,9 +75,21 @@ if (process.env.NODE_ENV === 'production' && existsSync(publicDir)) {
app.get('/robots.txt', (c) => serveFile(c, '/robots.txt', 'public, max-age=86400'))
app.get('/manifest.webmanifest', (c) => serveFile(c, '/manifest.webmanifest', 'public, max-age=86400'))
// SPA fallback: all non-API routes serve index.html
// PWA service worker + related root files
app.get('/sw.js', (c) => serveFile(c, '/sw.js', 'no-cache'))
app.get('/workbox-*.js', (c) => serveFile(c, c.req.path, 'public, max-age=31536000, immutable'))
app.get('/icon-*.png', (c) => serveFile(c, c.req.path, 'public, max-age=86400'))
app.get('/icon.svg', (c) => serveFile(c, '/icon.svg', 'public, max-age=86400'))
app.get('/apple-touch-icon.png', (c) => serveFile(c, '/apple-touch-icon.png', 'public, max-age=86400'))
// SPA fallback: only for navigation requests (not JS/CSS/asset files)
app.get('*', (c) => {
if (c.req.path.startsWith('/api/')) return c.notFound()
// Don't serve index.html for asset requests — return 404 so the browser gets a proper error
const ext = c.req.path.split('.').pop()
if (ext && ext !== c.req.path && ['js', 'css', 'map', 'json', 'png', 'jpg', 'svg', 'woff', 'woff2', 'webp', 'ico'].includes(ext)) {
return c.notFound()
}
const indexPath = join(publicDir, 'index.html')
c.header('Content-Type', 'text/html')
c.header('Cache-Control', 'no-cache')