PageSourceSearch

https://meetborderless.com/sw.js

js meetborderless.com collected 2026-10-09 22:43:17 UTC 5,644 bytes, 180 lines download raw bytes

1// Borderless Service Worker
2const CACHE_NAME = 'borderless-v2';
3const OFFLINE_URL = '/offline.html';
4
5// Assets to cache on install
6const PRECACHE_ASSETS = ['/', '/offline.html'];
7
8// Install event - cache essential assets
9self.addEventListener('install', (event) => {
10    event.waitUntil(
11        caches
12            .open(CACHE_NAME)
13            .then((cache) => cache.addAll(PRECACHE_ASSETS))
14            .then(() => self.skipWaiting()),
15    );
16});
17
18// Activate event - clean up old caches
19self.addEventListener('activate', (event) => {
20    event.waitUntil(
21        caches
22            .keys()
23            .then((cacheNames) => {
24                return Promise.all(
25                    cacheNames
26                        .filter((name) => name !== CACHE_NAME)
27                        .map((name) => caches.delete(name)),
28                );
29            })
30            .then(() => self.clients.claim()),
31    );
32});
33
34// Fetch event - network first, then cache
35self.addEventListener('fetch', (event) => {
36    // Skip non-GET requests
37    if (event.request.method !== 'GET') return;
38
39    // Skip API requests
40    if (event.request.url.includes('/api/')) return;
41
42    const requestUrl = new URL(event.request.url);
43
44    // Hashed Vite assets are immutable and should always come from the network.
45    // Caching them here can combine JavaScript chunks from different builds.
46    if (requestUrl.pathname.startsWith('/build/assets/')) return;
47
48    // Let the browser handle online navigations. Only provide the offline page
49    // if the navigation itself fails; never cache authenticated HTML responses.
50    if (event.request.mode === 'navigate') {
51        event.respondWith(
52            fetch(event.request).catch(() => caches.match(OFFLINE_URL)),
53        );
54        return;
55    }
56
57    event.respondWith(
58        fetch(event.request)
59            .then((response) => {
60                // Clone the response
61                const responseClone = response.clone();
62
63                // Cache successful responses
64                if (response.status === 200) {
65                    caches
66                        .open(CACHE_NAME)
67                        .then((cache) =>
68                            cache.put(event.request, responseClone),
69                        );
70                }
71
72                return response;
73            })
74            .catch(() => {
75                // Return cached version or offline page
76                return caches.match(event.request).then((cachedResponse) => {
77                    if (cachedResponse) {
78                        return cachedResponse;
79                    }
80
81                    return new Response('Network error', {
82                        status: 408,
83                        headers: { 'Content-Type': 'text/plain' },
84                    });
85                });
86            }),
87    );
88});
89
90// Push event - handle push notifications
91self.addEventListener('push', (event) => {
92    if (!event.data) return;
93
94    let data;
95    try {
96        data = event.data.json();
97    } catch (e) {
98        data = {
99            title: 'Borderless',
100            body: event.data.text(),
101        };
102    }
103
104    const options = {
105        body: data.body || '',
106        icon: data.icon || '/images/icon-192x192.png',
107        badge: data.badge || '/images/badge-72x72.png',
108        vibrate: [100, 50, 100],
109        data: {
110            url: data.data?.url || '/',
111            dateOfArrival: Date.now(),
112        },
113        actions: data.actions || [],
114        requireInteraction: data.requireInteraction || false,
115        tag: data.tag || 'borderless-notification',
116        renotify: true,
117    };
118
119    event.waitUntil(
120        self.registration.showNotification(data.title || 'Borderless', options),
121    );
122});
123
124// Notification click event - open the app
125self.addEventListener('notificationclick', (event) => {
126    event.notification.close();
127
128    const url = event.notification.data?.url || '/';
129
130    event.waitUntil(
131        clients
132            .matchAll({ type: 'window', includeUncontrolled: true })
133            .then((windowClients) => {
134                // Check if there's already a window/tab open
135                for (const client of windowClients) {
136                    if (
137                        client.url.includes(self.location.origin) &&
138                        'focus' in client
139                    ) {
140                        client.navigate(url);
141                        return client.focus();
142                    }
143                }
144
145                // If no window is open, open a new one
146                if (clients.openWindow) {
147                    return clients.openWindow(url);
148                }
149            }),
150    );
151});
152
153// Handle notification close
154self.addEventListener('notificationclose', (event) => {
155    // Track notification dismissal if needed
156    console.log('Notification closed:', event.notification.tag);
157});
158
159// Background sync for offline actions (optional)
160self.addEventListener('sync', (event) => {
161    if (event.tag === 'sync-notifications') {
162        event.waitUntil(syncNotifications());
163    }
164});
165
166async function syncNotifications() {
167    // Sync any pending notification actions when back online
168    // try {
169    //     const response = await fetch('/api/notifications/unread-count');
170    //     if (response.ok) {
171    //         const data = await response.json();
172    //         // Update badge if supported
173    //         if ('setAppBadge' in navigator && data.unread_count > 0) {
174    //             navigator.setAppBadge(data.unread_count);
175    //         }
176    //     }
177    // } catch (error) {
178    //     console.error('Failed to sync notifications:', error);
179    // }
180}

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.