PageSourceSearch

https://cruisehuddle.com/sw.js

js cruisehuddle.com collected 2026-10-05 11:35:08 UTC 5,605 bytes, 121 lines download raw bytes

1const CACHE_NAME = 'cruisehuddle-v2144';
2const STATIC_ASSETS = [
3    '/',
4    '/manifest.json',
5    '/pwa-icons/icon-192x192.png',
6    '/pwa-icons/icon-512x512.png',
7];
8
9self.addEventListener('install', event => {
10    event.waitUntil(
11        caches.open(CACHE_NAME).then(cache => cache.addAll(STATIC_ASSETS))
12    );
13    self.skipWaiting();
14});
15
16self.addEventListener('activate', event => {
17    event.waitUntil(
18        caches.keys().then(keys =>
19            Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
20        )
21    );
22    self.clients.claim();
23});
24
25// Network first, fallback to cache
26self.addEventListener('fetch', event => {
27    if (event.request.method !== 'GET') return;
28    if (event.request.url.includes('/admin')) return; // Don't cache admin
29    if (event.request.url.includes('livewire')) return; // Don't cache livewire
30
31    // Inertia visits are XHR, not page loads. They are never useful from cache,
32    // and Inertia cannot parse the offline HTML shell below - it just fails the
33    // visit. Worse, Inertia cancels superseded visits, which rejects the fetch
34    // and lands in the catch, so an ordinary cancelled navigation was being
35    // turned into a 503 offline page. Let these go straight to the network.
36    if (event.request.headers.get('X-Inertia')) return;
37
38    // Images are handled entirely by the HTTP cache. Intercepting them here
39    // did two harmful things: every listing photo was copied into Cache
40    // Storage on the member's device (5.2GB of photos exist), and the
41    // service-worker fetch bypassed Cloudflare's content negotiation, so
42    // Polish served the original JPEG instead of the WebP. Images already
43    // carry Cache-Control: max-age=31536000, immutable.
44    if (event.request.destination === 'image') return;
45    if (/\.(jpe?g|png|gif|webp|avif|svg|ico)(\?|$)/i.test(event.request.url)) return;
46    if (event.request.url.includes('/storage/')) return;
47
48    event.respondWith(
49        fetch(event.request)
50            .then(response => {
51                // Never store HTML documents. Cache Storage ignores
52                // Cache-Control, so 'no-store' from the server was being
53                // overridden here and every page a member visited was
54                // kept - Inertia props, auth state and all.
55                //
56                // It also caused the cold-start bug: tapping a push
57                // notification wakes the app before the radio is ready,
58                // the fetch fails, the catch below finds a cached
59                // document and serves a stale page that needs a manual
60                // refresh. A warm app never saw it, because Inertia
61                // visits skip this handler and the network is already up.
62                const isDocument = event.request.mode === 'navigate'
63                    || event.request.destination === 'document';
64
65                if (response.ok && ! isDocument) {
66                    const clone = response.clone();
67                    caches.open(CACHE_NAME).then(cache => cache.put(event.request, clone));
68                }
69                return response;
70            })
71            .catch(async (err) => {
72                // An aborted request is not an offline condition. Rethrow so the
73                // caller sees the abort it caused, rather than a fake 503.
74                if (err && err.name === 'AbortError') throw err;
75
76                // caches.match() resolves to undefined on a miss, and
77                // respondWith(undefined) throws "Failed to convert value to
78                // 'Response'". That killed the whole fetch handler, so a single
79                // failed request could leave a member stuck on a stale build
80                // with no way out short of clearing site data.
81                const cached = await caches.match(event.request);
82                if (cached) return cached;
83
84                if (event.request.mode === 'navigate') {
85                    return new Response(
86                        '<!doctype html><meta charset="utf-8">'
87                        + '<meta name="viewport" content="width=device-width,initial-scale=1">'
88                        + '<title>Offline | CruiseHuddle</title>'
89                        + '<body style="margin:0;display:grid;place-items:center;min-height:100vh;'
90                        + 'background:#050B14;color:#fff;font-family:system-ui,-apple-system,sans-serif;text-align:center;padding:2rem">'
91                        + '<div><h1 style="margin:0 0 .5rem;font-size:1.25rem;font-weight:800">You are offline</h1>'
92                        + '<p style="margin:0;color:#94a3b8;font-size:.95rem">Check your connection and try again.</p></div>',
93                        { status: 503, headers: { 'Content-Type': 'text/html; charset=utf-8' } }
94                    );
95                }
96
97                return new Response('', { status: 503, statusText: 'Offline' });
98            })
99    );
100});
101
102// Push notifications
103self.addEventListener('push', event => {
104    const data = event.data ? event.data.json() : {};
105    event.waitUntil(
106        self.registration.showNotification(data.title || 'CruiseHuddle', {
107            body: data.body || 'You have a new message',
108            icon: '/pwa-icons/icon-192x192.png',
109            badge: '/pwa-icons/icon-96x96.png',
110            data: { url: data.url || '/messages' },
111            vibrate: [200, 100, 200],
112        })
113    );
114});
115
116self.addEventListener('notificationclick', event => {
117    event.notification.close();
118    event.waitUntil(
119        clients.openWindow(event.notification.data.url || '/messages')
120    );
121});

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.