chore(pwa): tighten audio cache eviction (30d → 7d, purgeOnQuotaError)

Workbox's CacheFirst rule for /audio/*.mp3 used a 30-day age-based
TTL. Approximate LRU by dropping to 7d — a voice clip stays cached as
long as it's played at least once a week, otherwise it falls out and
re-fetches on next play. Each clip is <200KB and same-origin so the
re-fetch cost is negligible. Also flip purgeOnQuotaError so the
runtime cache yields first under storage pressure.

Default voice precache (vite.config.js additionalManifestEntries)
unaffected — it's a separate workbox flow with its own revision.
This commit is contained in:
tiennm99 committed 2026-04-28 11:10:51 +07:00
1 parent 7c893aa3b5
commit 9f24b6da7d
3 files changed
+12 -3

No files matched your search

@@ -1,9 +1,10 @@
---
name: Audio cache LRU rule
phase: 8
status: todo
status: completed
priority: low
effort: 30m
completed: 2026-04-28
---
# Phase 8 — Audio cache LRU rule
@@ -24,7 +24,7 @@ YAGNI — parking-lot features and upstream-blocked items skipped.
| 5 | Per-row "Chờ" indicator ✅ | `phase-05-cho-row-indicator.md` |
| 6 | Confetti polish (threshold + variety) ✅ | `phase-06-confetti-polish.md` |
| 7 | Strict CSP via hashed inline script ✅ | `phase-07-strict-csp-hashed.md` |
| 8 | Audio cache LRU rule | `phase-08-audio-cache-lru.md` |
| 8 | Audio cache LRU rule ✅ | `phase-08-audio-cache-lru.md` |
| 9 | PWA install verification checklist | `phase-09-pwa-verify-install.md` |
## Cuts (YAGNI)
+9 -1
View File
@@ -61,7 +61,15 @@ export default defineConfig(({ mode }) => {
expiration: {
// Headroom for future voice growth: 4× current 184 clips.
maxEntries: 400,
maxAgeSeconds: 60 * 60 * 24 * 30,
// 7d (was 30d). A clip stays cached as long as it gets
// played at least once a week. Voices that go unused
// for 7d fall out and re-fetch on next play —
// approximates LRU without a custom workbox plugin.
// Each clip <200KB so re-fetch cost is negligible.
maxAgeSeconds: 60 * 60 * 24 * 7,
// Belt-and-suspenders for the rare quota-pressure case
// (lots of voices precached + other origins competing).
purgeOnQuotaError: true,
},
// Audio is same-origin → no need to allow opaque (0).
cacheableResponse: { statuses: [200] },