refactor(pwa): ♻️ transform fetch Strategy into static only class

This commit is contained in:
Julien Oculi 2024-07-18 15:49:33 +02:00
parent 67e3330587
commit 96a4199fa2

View file

@ -1,15 +1,21 @@
export class Strategy {
#cache: Cache
constructor(cache: Cache) {
this.#cache = cache
private constructor() {
throw new Error(
'fetch Strategy is not instantiable and only expose static methods',
)
}
cacheFirst({ request, preloadResponse }: Pick<FetchEvent, 'request' | 'preloadResponse'>) {
static cacheFirst(
cache: Cache,
{ request, preloadResponse }: Pick<
FetchEvent,
'request' | 'preloadResponse'
>,
) {
const fetchRequest = async () => {
const response = await fetch(request)
if (response.ok) {
this.#cache.put(request, response)
cache.put(request, response)
}
}
@ -17,22 +23,26 @@ export class Strategy {
preloadResponse
.then((preload: Response | undefined) => {
if (preload?.ok) {
return this.#cache.put(request, preload)
return cache.put(request, preload)
}
throw new Error()
})
// Else fetch request
.catch(fetchRequest)
return this.#cache.match(request)
return cache.match(request)
}
fastestAndCacheRefresh(
{ request, preloadResponse }: Pick<FetchEvent, 'request' | 'preloadResponse'>,
ac: AbortController = new AbortController()
static fastestAndCacheRefresh(
cache: Cache,
{ request, preloadResponse }: Pick<
FetchEvent,
'request' | 'preloadResponse'
>,
ac: AbortController = new AbortController(),
) {
// Get cache or throw
const cachedOrError = this.#cache.match(request)
const cachedOrError = cache.match(request)
.then((response) => {
if (response) {
// Abort network request
@ -43,12 +53,12 @@ export class Strategy {
})
// Fetch and cache
const fetchedAndCached = this.networkOnly({ request, preloadResponse}, ac)
const fetchedAndCached = this.networkOnly({ request, preloadResponse }, ac)
.then((response) => {
// Abort cache request
ac.abort()
// Update cache
this.#cache.put(request, response.clone())
cache.put(request, response.clone())
return response
})
@ -56,8 +66,12 @@ export class Strategy {
return Promise.race([cachedOrError, fetchedAndCached])
}
async networkFirst(
{ request, preloadResponse }: Pick<FetchEvent, 'request' | 'preloadResponse'>,
static async networkFirst(
cache: Cache,
{ request, preloadResponse }: Pick<
FetchEvent,
'request' | 'preloadResponse'
>,
{ fallbackUrl, timeout }: { fallbackUrl?: string; timeout?: number },
) {
const signal = timeout ? AbortSignal.timeout(timeout) : undefined
@ -66,7 +80,7 @@ export class Strategy {
// Get navigator preload (see: https://developer.mozilla.org/en-US/docs/Web/API/Pick<FetchEvent, 'request' | 'preloadResponse'>/preloadResponse)
const preload = await getPreload(preloadResponse)
if (preload?.ok) {
this.#cache.put(request, preload.clone())
cache.put(request, preload.clone())
return preload
}
} catch {
@ -74,23 +88,26 @@ export class Strategy {
// Else fetch request
const response = await fetch(request, { signal })
if (response.ok) {
this.#cache.put(request, response.clone())
cache.put(request, response.clone())
return response
}
} catch {
// Else return fallback or cached response
return this.#cache.match(fallbackUrl ?? request)
return cache.match(fallbackUrl ?? request)
}
}
}
networkOnly(
{ request, preloadResponse }: Pick<FetchEvent, 'request' | 'preloadResponse'>,
ac: AbortController = new AbortController()
static networkOnly(
{ request, preloadResponse }: Pick<
FetchEvent,
'request' | 'preloadResponse'
>,
ac: AbortController = new AbortController(),
): Promise<Response> {
// Browser preload
const preload = getPreload(preloadResponse, ac)
.then(response => {
.then((response) => {
if (response === undefined) {
throw new Error(`no preload response for ${request.url}`)
}
@ -101,7 +118,7 @@ export class Strategy {
// Client fetch
const fetched = fetch(request, { signal: ac.signal })
.then(response => {
.then((response) => {
// Cancel preload event
ac.abort()
return response