|
1 | | -const PWA_ASSET_VERSION = "0.3.2"; |
2 | | -const PWA_ASSET_CACHE_KEY = "632cb83037d9"; |
3 | | -const PWA_ASSET_MANIFEST = { |
4 | | - "/pwa/icon.svg": "/pwa/generated/icon.fe1d64d9758c.svg", |
5 | | - "/pwa/icon-192.png": "/pwa/generated/icon-192.d7a0049daaa7.png", |
6 | | - "/pwa/icon-512.png": "/pwa/generated/icon-512.bd37e5f66cc5.png", |
7 | | - "/pwa/icon-maskable-512.png": "/pwa/generated/icon-maskable-512.647d8497d850.png", |
8 | | - "/pwa/apple-touch-icon.png": "/pwa/generated/apple-touch-icon.0c62df73d41b.png" |
9 | | -}; |
10 | | -const STATIC_CACHE = `deeix-chat-static-${PWA_ASSET_VERSION}-${PWA_ASSET_CACHE_KEY}`; |
11 | | -const PAGE_CACHE = `deeix-chat-pages-${PWA_ASSET_VERSION}`; |
12 | | -const STATIC_CACHE_MAX_ENTRIES = 160; |
13 | | -const PAGE_CACHE_MAX_ENTRIES = 24; |
14 | | - |
15 | | -const APP_SHELL_URLS = [ |
16 | | - "/", |
17 | | - "/chat", |
18 | | - "/logo.svg", |
19 | | - "/logo-color.svg", |
20 | | - pwaAsset("/pwa/icon.svg"), |
21 | | - pwaAsset("/pwa/icon-192.png"), |
22 | | - pwaAsset("/pwa/icon-512.png"), |
23 | | -]; |
24 | | - |
25 | | -const BACKEND_PATH_PREFIXES = [ |
26 | | - "/api/", |
27 | | - "/swagger", |
28 | | - "/healthz", |
29 | | - "/readyz", |
30 | | -]; |
31 | | - |
32 | | -function pwaAsset(path) { |
33 | | - return PWA_ASSET_MANIFEST[path] ?? path; |
34 | | -} |
| 1 | +// Temporary tombstone for the retired PWA service worker. Keep this URL stable during migration. |
| 2 | +const LEGACY_CACHE_PREFIX = "deeix-chat-"; |
35 | 3 |
|
36 | 4 | self.addEventListener("install", (event) => { |
37 | | - event.waitUntil( |
38 | | - caches.open(STATIC_CACHE) |
39 | | - .then((cache) => cache.addAll(APP_SHELL_URLS)) |
40 | | - .catch(() => undefined) |
41 | | - .then(() => self.skipWaiting()), |
42 | | - ); |
| 5 | + event.waitUntil(self.skipWaiting()); |
43 | 6 | }); |
44 | 7 |
|
45 | 8 | self.addEventListener("activate", (event) => { |
46 | 9 | event.waitUntil( |
47 | 10 | caches.keys() |
48 | 11 | .then((keys) => Promise.all( |
49 | 12 | keys |
50 | | - .filter((key) => key !== STATIC_CACHE && key !== PAGE_CACHE) |
| 13 | + .filter((key) => key.startsWith(LEGACY_CACHE_PREFIX)) |
51 | 14 | .map((key) => caches.delete(key)), |
52 | 15 | )) |
53 | | - .then(() => self.clients.claim()), |
| 16 | + .then(() => self.clients.claim()) |
| 17 | + .then(() => self.registration.unregister()), |
54 | 18 | ); |
55 | 19 | }); |
56 | | - |
57 | | -self.addEventListener("fetch", (event) => { |
58 | | - const request = event.request; |
59 | | - if (request.method !== "GET") { |
60 | | - return; |
61 | | - } |
62 | | - |
63 | | - const url = new URL(request.url); |
64 | | - if (url.origin !== self.location.origin || shouldBypassCache(url)) { |
65 | | - return; |
66 | | - } |
67 | | - |
68 | | - if (request.mode === "navigate") { |
69 | | - event.respondWith(networkFirst(request, PAGE_CACHE, PAGE_CACHE_MAX_ENTRIES)); |
70 | | - return; |
71 | | - } |
72 | | - |
73 | | - if (isStaticAsset(url)) { |
74 | | - event.respondWith(staleWhileRevalidate(request, STATIC_CACHE, STATIC_CACHE_MAX_ENTRIES)); |
75 | | - } |
76 | | -}); |
77 | | - |
78 | | -function shouldBypassCache(url) { |
79 | | - if (BACKEND_PATH_PREFIXES.some((prefix) => url.pathname === prefix || url.pathname.startsWith(prefix))) { |
80 | | - return true; |
81 | | - } |
82 | | - if (url.pathname.includes("/content") || url.pathname.includes("/download")) { |
83 | | - return true; |
84 | | - } |
85 | | - return false; |
86 | | -} |
87 | | - |
88 | | -function isStaticAsset(url) { |
89 | | - return url.pathname.startsWith("/_next/static/") || |
90 | | - url.pathname.startsWith("/pwa/") || |
91 | | - url.pathname.startsWith("/vendor/") || |
92 | | - /\.(?:css|js|mjs|png|jpg|jpeg|gif|webp|svg|ico|woff2?|ttf|otf|wasm)$/i.test(url.pathname); |
93 | | -} |
94 | | - |
95 | | -async function networkFirst(request, cacheName, maxEntries) { |
96 | | - const cache = await caches.open(cacheName); |
97 | | - try { |
98 | | - const response = await fetch(request); |
99 | | - if (isCacheable(response)) { |
100 | | - await cache.put(request, response.clone()); |
101 | | - await trimCache(cacheName, maxEntries); |
102 | | - } |
103 | | - return response; |
104 | | - } catch { |
105 | | - const cached = await cache.match(request); |
106 | | - if (cached) { |
107 | | - return cached; |
108 | | - } |
109 | | - return cache.match("/") || Response.error(); |
110 | | - } |
111 | | -} |
112 | | - |
113 | | -async function staleWhileRevalidate(request, cacheName, maxEntries) { |
114 | | - const cache = await caches.open(cacheName); |
115 | | - const cached = await cache.match(request); |
116 | | - const fresh = fetch(request) |
117 | | - .then(async (response) => { |
118 | | - if (isCacheable(response)) { |
119 | | - await cache.put(request, response.clone()); |
120 | | - await trimCache(cacheName, maxEntries); |
121 | | - } |
122 | | - return response; |
123 | | - }) |
124 | | - .catch(() => undefined); |
125 | | - |
126 | | - return cached || fresh || Response.error(); |
127 | | -} |
128 | | - |
129 | | -function isCacheable(response) { |
130 | | - return response && response.ok && response.type === "basic"; |
131 | | -} |
132 | | - |
133 | | -async function trimCache(cacheName, maxEntries) { |
134 | | - const cache = await caches.open(cacheName); |
135 | | - const keys = await cache.keys(); |
136 | | - if (keys.length <= maxEntries) { |
137 | | - return; |
138 | | - } |
139 | | - await Promise.all(keys.slice(0, keys.length - maxEntries).map((key) => cache.delete(key))); |
140 | | -} |
0 commit comments