summaryrefslogtreecommitdiff
path: root/apps/web/app/sw.ts
blob: 955856bc3ac9670dc71c47144f4557a64988101d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
/// <reference lib="webworker" />
import { defaultCache } from "@serwist/next/worker"
import type { PrecacheEntry, SerwistGlobalConfig } from "serwist"
import { Serwist, NetworkFirst, CacheFirst, ExpirationPlugin } from "serwist"

declare global {
  interface WorkerGlobalScope extends SerwistGlobalConfig {
    __SW_MANIFEST: (PrecacheEntry | string)[] | undefined
  }
}

declare const self: ServiceWorkerGlobalScope

let offlineReadingAllowed = false

self.addEventListener("message", (event) => {
  if (event.data?.type === "SET_OFFLINE_ACCESS") {
    offlineReadingAllowed = event.data.allowed === true
  }
})

const offlineGatePlugin = {
  cacheWillUpdate: async ({ response }: { response: Response }): Promise<Response | null> => {
    if (!offlineReadingAllowed) return null
    return response
  },
}

const sameOriginCache = defaultCache.map((entry) => ({
  ...entry,
  matcher: (parameters: Parameters<Exclude<(typeof defaultCache)[number]["matcher"], RegExp | string | undefined>>[0]) => {
    if (!parameters.sameOrigin) return false
    const original = entry.matcher
    if (typeof original === "function") return original(parameters)
    if (original instanceof RegExp) return original.test(parameters.url.href)
    return false
  },
}))

const serwist = new Serwist({
  precacheEntries: self.__SW_MANIFEST,
  skipWaiting: true,
  clientsClaim: true,
  navigationPreload: true,
  runtimeCaching: [
    {
      matcher: ({ url, request }) =>
        url.hostname.endsWith(".supabase.co") &&
        url.pathname.startsWith("/rest/v1/") &&
        request.method === "GET",
      handler: new NetworkFirst({
        cacheName: "supabase-rest-get",
        networkTimeoutSeconds: 10,
        plugins: [
          offlineGatePlugin,
          new ExpirationPlugin({
            maxEntries: 200,
            maxAgeSeconds: 60 * 60 * 24,
          }),
        ],
      }),
    },
    {
      matcher: ({ request, sameOrigin }) =>
        sameOrigin && request.destination === "image",
      handler: new CacheFirst({
        cacheName: "entry-images",
        plugins: [
          new ExpirationPlugin({
            maxEntries: 500,
            maxAgeSeconds: 60 * 60 * 24 * 7,
          }),
        ],
      }),
    },
    ...sameOriginCache,
  ],
})

serwist.addEventListeners()