feat(pwa): ✨ add service worker cache strategies
This commit is contained in:
parent
61d072cb06
commit
908c820cb0
74
src/serviceworker/src/strategy.ts
Normal file
74
src/serviceworker/src/strategy.ts
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
export class Strategy {
|
||||||
|
#cache: Cache
|
||||||
|
|
||||||
|
constructor(cache: Cache) {
|
||||||
|
this.#cache = cache
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheFirst({ request, preloadResponse }: FetchEvent) {
|
||||||
|
const fetchRequest = async () => {
|
||||||
|
const response = await fetch(request)
|
||||||
|
if (response.ok) {
|
||||||
|
this.#cache.put(request, response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get navigator preload request
|
||||||
|
preloadResponse
|
||||||
|
.then((preload: Response | undefined) => {
|
||||||
|
if (preload?.ok) {
|
||||||
|
return this.#cache.put(request, preload)
|
||||||
|
}
|
||||||
|
throw new Error()
|
||||||
|
})
|
||||||
|
// Else fetch request
|
||||||
|
.catch(fetchRequest)
|
||||||
|
|
||||||
|
return this.#cache.match(request)
|
||||||
|
}
|
||||||
|
|
||||||
|
async networkFirst(
|
||||||
|
{ request, preloadResponse }: FetchEvent,
|
||||||
|
{ fallbackUrl, timeout }: { fallbackUrl?: string; timeout?: number },
|
||||||
|
) {
|
||||||
|
const signal = timeout ? AbortSignal.timeout(timeout) : undefined
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Get navigator preload (see: https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent/preloadResponse)
|
||||||
|
const preload = await getPreload(preloadResponse)
|
||||||
|
if (preload?.ok) {
|
||||||
|
this.#cache.put(request, preload.clone())
|
||||||
|
return preload
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
try {
|
||||||
|
// Else fetch request
|
||||||
|
const response = await fetch(request, { signal })
|
||||||
|
if (response.ok) {
|
||||||
|
this.#cache.put(request, response.clone())
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// Else return fallback or cached response
|
||||||
|
return this.#cache.match(fallbackUrl ?? request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPreload(
|
||||||
|
preloadResponse: FetchEvent['preloadResponse'],
|
||||||
|
ac?: { signal?: AbortSignal },
|
||||||
|
): Promise<Response | undefined> {
|
||||||
|
const { promise, resolve, reject } = Promise.withResolvers<
|
||||||
|
Response | undefined
|
||||||
|
>()
|
||||||
|
|
||||||
|
ac?.signal?.addEventListener('abort', () => {
|
||||||
|
reject(new Error('aborted by signal'))
|
||||||
|
})
|
||||||
|
|
||||||
|
resolve(preloadResponse)
|
||||||
|
|
||||||
|
return promise
|
||||||
|
}
|
Loading…
Reference in a new issue