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