PageSourceSearch

https://getcurrentoffers.com/sw.js

js getcurrentoffers.com collected 2026-09-24 14:43:38 UTC 7,397 bytes, 262 lines download raw bytes

1// public/sw.js - Service Worker for caching and offline support
2// Version: 2.1.0 - Fixed caching strategy to prevent stale CSS/JS
3
4const CACHE_VERSION = '2.1.0';
5const STATIC_CACHE = 'cc-merch-static-v' + CACHE_VERSION;
6const API_CACHE = 'cc-merch-api-v' + CACHE_VERSION;
7
8// Only cache truly static assets on install (not HTML pages)
9const PRECACHE_FILES = [
10  '/manifest.json',
11  '/favicon.ico',
12  '/icon-192.png',
13  '/icon-96.png'
14];
15
16// Install event - cache static assets only
17self.addEventListener('install', (event) => {
18  console.log('[SW] Installing service worker v' + CACHE_VERSION);
19  event.waitUntil(
20    caches.open(STATIC_CACHE)
21      .then((cache) => {
22        console.log('[SW] Caching precache files');
23        return cache.addAll(PRECACHE_FILES);
24      })
25      .then(() => {
26        console.log('[SW] Precache complete');
27        return self.skipWaiting();
28      })
29      .catch((error) => {
30        console.error('[SW] Failed to cache precache files:', error);
31      })
32  );
33});
34
35// Activate event - clean up ALL old caches
36self.addEventListener('activate', (event) => {
37  console.log('[SW] Activating service worker v' + CACHE_VERSION);
38  event.waitUntil(
39    caches.keys()
40      .then((cacheNames) => {
41        return Promise.all(
42          cacheNames.map((cacheName) => {
43            // Delete any cache that doesn't match current version
44            if (cacheName !== STATIC_CACHE && cacheName !== API_CACHE) {
45              console.log('[SW] Deleting old cache:', cacheName);
46              return caches.delete(cacheName);
47            }
48          })
49        );
50      })
51      .then(() => {
52        console.log('[SW] Service worker activated');
53        return self.clients.claim();
54      })
55  );
56});
57
58// Fetch event - route requests to appropriate handler
59self.addEventListener('fetch', (event) => {
60  const { request } = event;
61  const url = new URL(request.url);
62
63  // Only handle same-origin requests
64  if (url.origin !== location.origin) return;
65
66  // The Cache API can only store GET responses, so anything else (the
67  // sendBeacon POSTs from AnalyticsTracker, form submissions, mutations)
68  // goes straight to the network untouched.
69  if (request.method !== 'GET') return;
70
71  // Handle API requests - network-first
72  if (url.pathname.startsWith('/api/')) {
73    event.respondWith(handleApiRequest(request));
74    return;
75  }
76
77  // Handle navigation requests (HTML pages) - ALWAYS network-first
78  if (request.mode === 'navigate') {
79    event.respondWith(handleNavigationRequest(request));
80    return;
81  }
82
83  // Handle immutable Next.js static assets - cache-first (these have content hashes)
84  if (url.pathname.startsWith('/_next/static/')) {
85    event.respondWith(handleImmutableStaticRequest(request));
86    return;
87  }
88
89  // All other GET requests - network-first with cache fallback
90  event.respondWith(handleNetworkFirstRequest(request));
91});
92
93// Navigation requests (HTML pages) - ALWAYS network-first
94// This is critical: ensures fresh HTML that references correct CSS/JS chunk URLs
95async function handleNavigationRequest(request) {
96  try {
97    const networkResponse = await fetch(request);
98    return networkResponse;
99  } catch (error) {
100    console.log('[SW] Navigation failed, serving offline page');
101    const cachedOffline = await caches.match('/offline');
102    if (cachedOffline) return cachedOffline;
103
104    return new Response('Offline - Please check your connection', {
105      status: 503,
106      headers: { 'Content-Type': 'text/html' }
107    });
108  }
109}
110
111// Immutable static assets (/_next/static/*) - cache-first
112// These files have content hashes in their URLs, so cache-first is safe
113async function handleImmutableStaticRequest(request) {
114  const cachedResponse = await caches.match(request);
115  if (cachedResponse) {
116    return cachedResponse;
117  }
118
119  try {
120    const networkResponse = await fetch(request);
121    if (networkResponse.ok) {
122      const cache = await caches.open(STATIC_CACHE);
123      cache.put(request, networkResponse.clone());
124    }
125    return networkResponse;
126  } catch (error) {
127    console.log('[SW] Failed to fetch static asset:', request.url);
128    throw error;
129  }
130}
131
132// Generic network-first handler for other static files
133async function handleNetworkFirstRequest(request) {
134  try {
135    const networkResponse = await fetch(request);
136    if (networkResponse.ok) {
137      const cache = await caches.open(STATIC_CACHE);
138      cache.put(request, networkResponse.clone());
139    }
140    return networkResponse;
141  } catch (error) {
142    const cachedResponse = await caches.match(request);
143    if (cachedResponse) {
144      console.log('[SW] Serving from cache:', request.url);
145      return cachedResponse;
146    }
147    throw error;
148  }
149}
150
151// Handle API requests with network-first strategy
152async function handleApiRequest(request) {
153  try {
154    const networkResponse = await fetch(request);
155
156    if (networkResponse.ok) {
157      const cache = await caches.open(API_CACHE);
158      cache.put(request, networkResponse.clone());
159      return networkResponse;
160    }
161
162    const cachedResponse = await caches.match(request);
163    if (cachedResponse) {
164      console.log('[SW] Serving API from cache:', request.url);
165      return cachedResponse;
166    }
167
168    return networkResponse;
169  } catch (error) {
170    console.log('[SW] Network failed, trying cache for:', request.url);
171
172    const cachedResponse = await caches.match(request);
173    if (cachedResponse) {
174      return cachedResponse;
175    }
176
177    return new Response(
178      JSON.stringify({
179        message: 'Offline - Please check your connection',
180        offers: [],
181        currentPage: 1,
182        totalPages: 1,
183        totalResults: 0
184      }),
185      {
186        status: 503,
187        statusText: 'Service Unavailable',
188        headers: {
189          'Content-Type': 'application/json',
190          'Cache-Control': 'no-cache'
191        }
192      }
193    );
194  }
195}
196
197// Background sync for offline actions
198self.addEventListener('sync', (event) => {
199  if (event.tag === 'background-sync') {
200    console.log('[SW] Background sync triggered');
201    event.waitUntil(doBackgroundSync());
202  }
203});
204
205async function doBackgroundSync() {
206  try {
207    console.log('[SW] Background sync completed');
208  } catch (error) {
209    console.error('[SW] Background sync failed:', error);
210  }
211}
212
213// Push notification handling
214self.addEventListener('push', (event) => {
215  if (event.data) {
216    const data = event.data.json();
217    const options = {
218      body: data.body || 'New offers available!',
219      icon: '/icon-192.png',
220      badge: '/icon-96.png',
221      vibrate: [100, 50, 100],
222      data: {
223        dateOfArrival: Date.now(),
224        primaryKey: 1
225      },
226      actions: [
227        {
228          action: 'explore',
229          title: 'View Offers',
230          icon: '/icon-96.png'
231        },
232        {
233          action: 'close',
234          title: 'Close',
235          icon: '/icon-96.png'
236        }
237      ]
238    };
239
240    event.waitUntil(
241      self.registration.showNotification(data.title || 'CC Merch Offers', options)
242    );
243  }
244});
245
246// Notification click handling
247self.addEventListener('notificationclick', (event) => {
248  event.notification.close();
249
250  if (event.action === 'explore') {
251    event.waitUntil(
252      clients.openWindow('/')
253    );
254  }
255});
256
257// Message handling for communication with main thread
258self.addEventListener('message', (event) => {
259  if (event.data && event.data.type === 'SKIP_WAITING') {
260    self.skipWaiting();
261  }
262});

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.