PageSourceSearch

https://getcurrentoffers.com/sw.js

js getcurrentoffers.com collected 2026-10-02 01:31:01 UTC 7,164 bytes, 222 lines download raw bytes

1// public/sw.js - Service Worker for caching and offline support
2// Version: 2.2.0 - Per-user API responses and page data are no longer cached.
3// Bumping the version makes activate delete caches written by earlier workers.
4
5const CACHE_VERSION = '2.2.0';
6const STATIC_CACHE = 'cc-merch-static-v' + CACHE_VERSION;
7const API_CACHE = 'cc-merch-api-v' + CACHE_VERSION;
8
9// Only cache truly static assets on install (not HTML pages)
10const PRECACHE_FILES = [
11  '/manifest.json',
12  '/favicon.ico',
13  '/icon-192.png',
14  '/icon-96.png'
15];
16
17// Install event - cache static assets only
18self.addEventListener('install', (event) => {
19  console.log('[SW] Installing service worker v' + CACHE_VERSION);
20  event.waitUntil(
21    caches.open(STATIC_CACHE)
22      .then((cache) => {
23        console.log('[SW] Caching precache files');
24        return cache.addAll(PRECACHE_FILES);
25      })
26      .then(() => {
27        console.log('[SW] Precache complete');
28        return self.skipWaiting();
29      })
30      .catch((error) => {
31        console.error('[SW] Failed to cache precache files:', error);
32      })
33  );
34});
35
36// Activate event - clean up ALL old caches
37self.addEventListener('activate', (event) => {
38  console.log('[SW] Activating service worker v' + CACHE_VERSION);
39  event.waitUntil(
40    caches.keys()
41      .then((cacheNames) => {
42        return Promise.all(
43          cacheNames.map((cacheName) => {
44            // Delete any cache that doesn't match current version
45            if (cacheName !== STATIC_CACHE && cacheName !== API_CACHE) {
46              console.log('[SW] Deleting old cache:', cacheName);
47              return caches.delete(cacheName);
48            }
49          })
50        );
51      })
52      .then(() => {
53        console.log('[SW] Service worker activated');
54        return self.clients.claim();
55      })
56  );
57});
58
59// Fetch event - route requests to appropriate handler
60self.addEventListener('fetch', (event) => {
61  const { request } = event;
62  const url = new URL(request.url);
63
64  // Only handle same-origin requests
65  if (url.origin !== location.origin) return;
66
67  // The Cache API can only store GET responses, so anything else (the
68  // sendBeacon POSTs from AnalyticsTracker, form submissions, mutations)
69  // goes straight to the network untouched.
70  if (request.method !== 'GET') return;
71
72  // Handle API requests - network-first
73  if (url.pathname.startsWith('/api/')) {
74    event.respondWith(handleApiRequest(request));
75    return;
76  }
77
78  // Handle navigation requests (HTML pages) - ALWAYS network-first
79  if (request.mode === 'navigate') {
80    event.respondWith(handleNavigationRequest(request));
81    return;
82  }
83
84  // Handle immutable Next.js static assets - cache-first (these have content hashes)
85  if (url.pathname.startsWith('/_next/static/')) {
86    event.respondWith(handleImmutableStaticRequest(request));
87    return;
88  }
89
90  // Page data the client router fetches on a navigation (?_rsc=, RSC: 1)
91  // carries the signed-in user's view of the page, and next.config.mjs marks
92  // it public, so no header says to keep it out. Leave it to the network.
93  if (url.searchParams.has('_rsc') || request.headers.get('RSC') === '1') return;
94
95  // All other GET requests - network-first with cache fallback
96  event.respondWith(handleNetworkFirstRequest(request));
97});
98
99// Navigation requests (HTML pages) - ALWAYS network-first
100// This is critical: ensures fresh HTML that references correct CSS/JS chunk URLs
101async function handleNavigationRequest(request) {
102  try {
103    const networkResponse = await fetch(request);
104    return networkResponse;
105  } catch (error) {
106    console.log('[SW] Navigation failed, serving offline page');
107    const cachedOffline = await caches.match('/offline');
108    if (cachedOffline) return cachedOffline;
109
110    return new Response('Offline - Please check your connection', {
111      status: 503,
112      headers: { 'Content-Type': 'text/html' }
113    });
114  }
115}
116
117// Immutable static assets (/_next/static/*) - cache-first
118// These files have content hashes in their URLs, so cache-first is safe
119async function handleImmutableStaticRequest(request) {
120  const cachedResponse = await caches.match(request);
121  if (cachedResponse) {
122    return cachedResponse;
123  }
124
125  try {
126    const networkResponse = await fetch(request);
127    if (networkResponse.ok) {
128      const cache = await caches.open(STATIC_CACHE);
129      cache.put(request, networkResponse.clone());
130    }
131    return networkResponse;
132  } catch (error) {
133    console.log('[SW] Failed to fetch static asset:', request.url);
134    throw error;
135  }
136}
137
138// Generic network-first handler for other static files
139async function handleNetworkFirstRequest(request) {
140  try {
141    const networkResponse = await fetch(request);
142    if (networkResponse.ok) {
143      const cache = await caches.open(STATIC_CACHE);
144      cache.put(request, networkResponse.clone());
145    }
146    return networkResponse;
147  } catch (error) {
148    const cachedResponse = await caches.match(request);
149    if (cachedResponse) {
150      console.log('[SW] Serving from cache:', request.url);
151      return cachedResponse;
152    }
153    throw error;
154  }
155}
156
157// Only responses that are the same for every visitor may be kept. The Cache
158// API cannot tell signed-in users apart (cookies are not part of the stored
159// request), so a per-user response cached here would be served to whoever
160// uses this browser next - for example when a sign-out makes the network
161// answer 401 and the fallback below reaches for the cache.
162function isSharedResponse(response) {
163  const cacheControl = (response.headers.get('Cache-Control') || '').toLowerCase();
164  if (/\b(private|no-store)\b/.test(cacheControl)) return false;
165  const vary = (response.headers.get('Vary') || '').toLowerCase();
166  return !/\b(cookie|authorization)\b/.test(vary);
167}
168
169// Handle API requests with network-first strategy
170async function handleApiRequest(request) {
171  try {
172    const networkResponse = await fetch(request);
173
174    if (networkResponse.ok) {
175      if (isSharedResponse(networkResponse)) {
176        const cache = await caches.open(API_CACHE);
177        cache.put(request, networkResponse.clone());
178      }
179      return networkResponse;
180    }
181
182    const cachedResponse = await caches.match(request);
183    if (cachedResponse) {
184      console.log('[SW] Serving API from cache:', request.url);
185      return cachedResponse;
186    }
187
188    return networkResponse;
189  } catch (error) {
190    console.log('[SW] Network failed, trying cache for:', request.url);
191
192    const cachedResponse = await caches.match(request);
193    if (cachedResponse) {
194      return cachedResponse;
195    }
196
197    return new Response(
198      JSON.stringify({
199        message: 'Offline - Please check your connection',
200        offers: [],
201        currentPage: 1,
202        totalPages: 1,
203        totalResults: 0
204      }),
205      {
206        status: 503,
207        statusText: 'Service Unavailable',
208        headers: {
209          'Content-Type': 'application/json',
210          'Cache-Control': 'no-cache'
211        }
212      }
213    );
214  }
215}
216
217// Message handling for communication with main thread
218self.addEventListener('message', (event) => {
219  if (event.data && event.data.type === 'SKIP_WAITING') {
220    self.skipWaiting();
221  }
222});

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.