PageSourceSearch

https://final.red/sw.js

js final.red collected 2026-09-28 09:05:15 UTC 2,631 bytes, 81 lines download raw bytes

1const CACHE_NAME = 'final-red-v9';
2const STATIC_ASSETS = ['/', '/manifest.json', '/favicon.png'];
3
4self.addEventListener('install', (event) => {
5  event.waitUntil(caches.open(CACHE_NAME).then((cache) => cache.addAll(STATIC_ASSETS)));
6  self.skipWaiting();
7});
8
9self.addEventListener('activate', (event) => {
10  event.waitUntil(
11    caches.keys().then((names) =>
12      Promise.all(names.filter((n) => n !== CACHE_NAME).map((n) => caches.delete(n)))
13    )
14  );
15  self.clients.claim();
16});
17
18self.addEventListener('fetch', (event) => {
19  const url = new URL(event.request.url);
20  // Only handle http/https — skip chrome-extension, data, blob, etc.
21  if (!url.protocol.startsWith('http')) return;
22  if (event.request.method !== 'GET') return;
23
24  if (url.pathname.startsWith('/api/')) {
25    event.respondWith(
26      fetch(event.request).catch(() =>
27        new Response(JSON.stringify({ error: 'Offline' }), {
28          status: 503,
29          headers: { 'Content-Type': 'application/json' },
30        })
31      )
32    );
33    return;
34  }
35
36  event.respondWith(
37    fetch(event.request)
38      .then((response) => {
39        if (response.ok && response.type === 'basic') {
40          const clone = response.clone();
41          caches.open(CACHE_NAME).then((cache) => cache.put(event.request, clone));
42        }
43        return response;
44      })
45      .catch(() => caches.match(event.request))
46  );
47});
48
49// ── PUSH NOTIFICATIONS ──────────────────────────────────────────────────────
50
51self.addEventListener('push', (event) => {
52  let data = { title: 'final.red', body: 'A crisis estimate has changed.', url: '/' };
53  try { data = { ...data, ...event.data.json() }; } catch {}
54  event.waitUntil(
55    self.registration.showNotification(data.title, {
56      body: data.body,
57      icon: '/icons/icon-192x192.png',
58      badge: '/icons/icon-72x72.png',
59      data: { url: data.url },
60      vibrate: [200, 100, 200],
61      tag: 'finalred-update',
62      renotify: true,
63    })
64  );
65});
66
67self.addEventListener('notificationclick', (event) => {
68  event.notification.close();
69  const url = (event.notification.data && event.notification.data.url) || '/';
70  event.waitUntil(
71    clients.matchAll({ type: 'window', includeUncontrolled: true }).then((clientList) => {
72      for (const client of clientList) {
73        if (client.url.includes(self.location.origin) && 'focus' in client) {
74          client.navigate(url);
75          return client.focus();
76        }
77      }
78      if (clients.openWindow) return clients.openWindow(url);
79    })
80  );
81});

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.