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