1/** 2 * Repetio's service worker. It exists for web push and nothing else. 3 * 4 * It used to also make the site an installable offline app (network-first 5 * navigations with an `/offline` fallback, cache-first `/_next/static/*`). That 6 * was removed along with the web app manifest, so this worker now caches 7 * nothing and never answers a `fetch`: every request goes straight to the 8 * network as if no worker were installed. 9 * 10 * `activate` still deletes every `repetio-*` cache, which clears what the old 11 * caching worker left behind on devices that had it. 12 * 13 * `next.config.ts` serves this file with `Cache-Control: no-store`, so a fix to 14 * it reaches every browser on its next update check. `/reset` (public, works 15 * signed out) unregisters every worker and deletes every cache, and needs 16 * nothing from this file to work. 17 */ 18 19self.addEventListener("install", () => { 20 // Take over immediately. Waiting for every tab to close means a fix can sit 21 // unapplied for days on the machine of the person who never closes tabs. 22 self.skipWaiting(); 23}); 24 25self.addEventListener("activate", (event) => { 26 event.waitUntil( 27 (async () => { 28 const names = await caches.keys(); 29 await Promise.all( 30 names.filter((name) => name.startsWith("repetio-")).map((name) => caches.delete(name)) 31 ); 32 await self.clients.claim(); 33 })() 34 ); 35}); 36 37/** 38 * The manual kill switch, reachable from `/reset`. Clearing from inside the 39 * worker as well as from the page covers the case where the page's `caches` 40 * access is blocked. 41 */ 42self.addEventListener("message", (event) => { 43 const type = event.data && event.data.type; 44 45 if (type === "repetio-sw-reset") { 46 event.waitUntil( 47 (async () => { 48 const names = await caches.keys(); 49 await Promise.all(names.map((name) => caches.delete(name))); 50 await self.registration.unregister(); 51 })() 52 ); 53 } 54}); 55 56/** 57 * ## Push 58 * 59 * The payload is JSON written by `apps/api/src/modules/notifications/push/push-sender.ts`. 60 * A push event that shows no notification is a spec violation in every browser 61 * that implements `userVisibleOnly` (which is all of them) and will eventually 62 * cost the site its push permission, so the catch below still shows something. 63 * 64 * `tag` collapses repeats: a reminder redelivered by the push service replaces 65 * the one already on the lock screen instead of stacking a second copy. 66 */ 67self.addEventListener("push", (event) => { 68 let payload = {}; 69 try { 70 payload = event.data ? event.data.json() : {}; 71 } catch { 72 payload = {}; 73 } 74 75 const title = payload.title || "Repetio"; 76 const options = { 77 body: payload.body || "You have cards waiting.", 78 icon: "/icons/icon-192.png", 79 badge: "/icons/icon-192.png", 80 tag: payload.tag || "repetio-reminder", 81 // Replace rather than re-alert: a second buzz for the same reminder is how 82 // a notification channel gets turned off. 83 renotify: false, 84 data: { url: payload.url || "/dashboard" }, 85 }; 86 87 event.waitUntil(self.registration.showNotification(title, options)); 88}); 89 90self.addEventListener("notificationclick", (event) => { 91 event.notification.close(); 92 const target = (event.notification.data && event.notification.data.url) || "/dashboard"; 93 94 event.waitUntil( 95 (async () => { 96 const clientList = await self.clients.matchAll({ 97 type: "window", 98 includeUncontrolled: true, 99 }); 100 101 // Reuse an open window if there is one. Opening a second tab for a 102 // reminder is disorienting, and on a phone it looks like a bug. 103 for (const client of clientList) { 104 if (new URL(client.url).origin === self.location.origin) { 105 await client.focus(); 106 if ("navigate" in client) await client.navigate(target); 107 return; 108 } 109 } 110 111 await self.clients.openWindow(target); 112 })() 113 ); 114});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.