From 4a917971d0cd21b23788eb40a71f4ef100dfc0a5 Mon Sep 17 00:00:00 2001 From: Julien Oculi Date: Thu, 18 Jul 2024 14:18:26 +0200 Subject: [PATCH] feat(pwa): :zap: split pre-cache and dynamic cache in service worker --- src/serviceworker/mod.ts | 74 ++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/src/serviceworker/mod.ts b/src/serviceworker/mod.ts index 2e78d54..ff3eaec 100644 --- a/src/serviceworker/mod.ts +++ b/src/serviceworker/mod.ts @@ -31,19 +31,21 @@ const swStorage = { } async function getPreCachedUrls(): Promise { - const raw = await swStorage.getItem('$sw.cache.urls') + const raw = await swStorage.getItem('$sw.pre-cache.urls') + if (raw === null) { - await openCache() + await openPreCache() return getPreCachedUrls() } return raw } -async function getCache(): Promise { - const version = await swStorage.getItem('$sw.cache.version') +async function getPreCache(): Promise { + const version = await swStorage.getItem('$sw.pre-cache.version') + // Get cache from server if (version === null) { - await openCache() - return getCache() + await openPreCache() + return getPreCache() } return caches.open(version) } @@ -52,7 +54,7 @@ const IS_SW = 'onpushsubscriptionchange' in self if (IS_SW) { self.addEventListener('install', (event) => { // Assign global cache and pre-cached-urls - event.waitUntil(openCache()) + event.waitUntil(openPreCache()) }) self.addEventListener('activate', () => { @@ -85,18 +87,22 @@ if (IS_SW) { async function fetchHandler(event: FetchEvent) { const url = new URL(event.request.url) const method = event.request.method + const preCachedUrls = await getPreCachedUrls() - const cache = await getCache() - // Cache will be updated on each request if version change - updateCache() // Cache first for "pre-cached-urls" if (preCachedUrls.includes(url.pathname) && method === 'GET') { - const cached = await cache.match(event.request) ?? + const preCache = await getPreCache() + + // Cache request update on each request if version change + updatePreCache() + + // TODO handle search params + const cached = await preCache.match(event.request) ?? await fetch(event.request).then((response) => response.ok ? response : null ).catch(() => null) ?? - await cache.match(event.request, { ignoreSearch: true }) + await preCache.match(event.request, { ignoreSearch: true }) if (cached === undefined) { throw new Error(`no cache available for pre-cached-url "${url}"`) @@ -119,39 +125,41 @@ async function fetchHandler(event: FetchEvent) { cache.put(event.request, response.clone()) return response }) - .catch((cause) => { - // Try serve cache if no network - if (cached === undefined) { - throw new Error(`no cache available for "${url}"`, { cause }) - } - return cached - }) - - if (cached === undefined) { - return response - } - - return cached +async function getDynCache(): Promise { + const version = await swStorage.getItem('$sw.dyn-cache.version') + // Create cache + if (version === null) { + await swStorage.setItem('$sw.dyn-cache.version', now + lifeTime) + return getDynCache() } + // Clean outdated cache + if (version < Date.now()) { + await swStorage.setItem('$sw.dyn-cache.version', now + lifeTime) + await caches.delete(version.toString()) + return getDynCache() + } + + return cache +} + // Network only return fetch(event.request) } -async function updateCache() { - const serverVersion = await getServerCacheVersion() - const clientVersion = await swStorage.getItem('$sw.cache.version') + const serverVersion = await getServerPreCacheVersion() + const clientVersion = await swStorage.getItem('$sw.pre-cache.version') if (clientVersion === null) return if (serverVersion === undefined) return if (clientVersion === serverVersion) return // Open new pre-cache - await openCache() + await openPreCache() // Delete old pre-cache caches.delete(clientVersion) } -async function getServerCacheVersion() { +async function getServerPreCacheVersion(): Promise { const response = await fetch('/api/serviceworker/precache').then( (response) => response.json() as Promise>, ) @@ -161,7 +169,7 @@ async function getServerCacheVersion() { } } -async function openCache() { +async function openPreCache() { const response = await fetch('/api/serviceworker/precache').then( (response) => response.json() as Promise>, ) @@ -183,8 +191,8 @@ async function openCache() { } await Promise.allSettled(addList) - await swStorage.setItem('$sw.cache.version', version) - await swStorage.setItem('$sw.cache.urls', preCachedUrls) + await swStorage.setItem('$sw.pre-cache.version', version) + await swStorage.setItem('$sw.pre-cache.urls', preCachedUrls) return { cache, version, preCachedUrls } }