PageSourceSearch

https://nowtempmail.com/sw.js

js nowtempmail.com collected 2026-10-02 01:37:36 UTC 6,442 bytes, 218 lines download raw bytes

1// NowTempMail Service Worker - Minimal PWA Support + Email Notifications
2const CACHE_NAME = 'nowtempmail-v3';
3
4// Offline fallback copy.
5//
6// A service worker has no access to the i18n runtime, so this text cannot
7// come from the catalog and lives here instead, keyed by the locale prefix in
8// the request URL. It was previously a hardcoded English string with no lang
9// attribute, which would have stayed English on every localised page.
10// Adding a locale is one entry here plus the matching sw.offline.* keys.
11const OFFLINE_COPY = {
12  en: {
13    lang: 'en',
14    title: 'Offline',
15    body: 'Please check your internet connection.',
16  },
17};
18
19function offlineResponse(url) {
20  let locale = "en";
21  try {
22    const first = new URL(url).pathname.split("/")[1];
23    if (OFFLINE_COPY[first]) locale = first;
24  } catch (e) {
25    // Malformed URL: fall through to the default locale.
26  }
27  const copy = OFFLINE_COPY[locale];
28  const html =
29    "<!DOCTYPE html><html lang=" + JSON.stringify(copy.lang) + ">" +
30    "<head><meta charset=utf-8><meta name=viewport " +
31    "content=\"width=device-width, initial-scale=1\"><title>" + copy.title +
32    "</title></head><body><h1>" + copy.title + "</h1><p>" + copy.body +
33    "</p></body></html>";
34  return new Response(html, {
35    status: 503,
36    headers: { 'Content-Type': 'text/html; charset=utf-8' },
37  });
38}
39
40// Notification state (synced from main page via postMessage)
41let currentMailbox = null;
42let knownEmailIds = new Set();
43
44const STATIC_ASSETS = [
45  '/images/android-chrome-192x192.png',
46  '/images/android-chrome-512x512.png',
47  '/images/favicon-32x32.png',
48  '/images/favicon-16x16.png',
49  '/logo.png'
50];
51
52// Install event - cache static assets
53self.addEventListener('install', (event) => {
54  event.waitUntil(
55    caches.open(CACHE_NAME)
56      .then((cache) => {
57        return cache.addAll(STATIC_ASSETS);
58      })
59      .catch((error) => {
60        console.warn('Service Worker: Failed to cache assets during install', error);
61      })
62  );
63  
64  // Skip waiting to activate immediately
65  self.skipWaiting();
66});
67
68// Activate event - clean up old caches
69self.addEventListener('activate', (event) => {
70  event.waitUntil(
71    caches.keys().then((cacheNames) => {
72      return Promise.all(
73        cacheNames.map((cacheName) => {
74          if (cacheName !== CACHE_NAME) {
75            return caches.delete(cacheName);
76          }
77        })
78      );
79    })
80  );
81  
82  // Take control of all clients immediately
83  self.clients.claim();
84});
85
86// Fetch event - network first with cache fallback for static assets
87self.addEventListener('fetch', (event) => {
88  const { request } = event;
89  
90  // Skip non-GET requests
91  if (request.method !== 'GET') {
92    return;
93  }
94  
95  // Skip external requests
96  if (!request.url.startsWith(self.location.origin)) {
97    return;
98  }
99
100  // Navigation requests (HTML pages) - always network first to ensure latest JS/CSS
101  if (request.mode === 'navigate') {
102    event.respondWith(
103      fetch(request)
104        .catch(() => offlineResponse(request.url))
105    );
106    return;
107  }
108
109  // For static image assets, try cache first, then network
110  if (STATIC_ASSETS.some(asset => request.url.endsWith(asset))) {
111    event.respondWith(
112      caches.match(request)
113        .then((cachedResponse) => {
114          if (cachedResponse) {
115            return cachedResponse;
116          }
117
118          return fetch(request)
119            .then((networkResponse) => {
120              // Cache the network response for future use
121              if (networkResponse.status === 200) {
122                const responseClone = networkResponse.clone();
123                caches.open(CACHE_NAME)
124                  .then((cache) => {
125                    cache.put(request, responseClone);
126                  });
127              }
128              return networkResponse;
129            });
130        })
131        .catch(() => {
132          // Static asset failed - return nothing
133          return undefined;
134        })
135    );
136    return;
137  }
138
139  // Everything else (dynamic API calls like /messages, /mailbox, /message/*,
140  // plus JS/CSS bundles) is intentionally NOT intercepted — it goes straight to
141  // the network. The SW added no caching value for these, only overhead and a
142  // confusing duplicate entry in devtools. No respondWith => default handling.
143  // (Notifications are unaffected: they run through the `message` handler below,
144  //  driven by the main thread, not through this fetch path.)
145});
146
147// Handle messages from the main page (session sync + new email notifications)
148self.addEventListener('message', (event) => {
149  const { data } = event;
150
151  if (data.type === 'SET_SESSION') {
152    currentMailbox = data.mailbox;
153    knownEmailIds.clear();
154  }
155
156  if (data.type === 'SHOW_NOTIFICATION' && data.title) {
157    event.waitUntil(
158      self.registration.showNotification(data.title, data.options || {})
159    );
160  }
161});
162
163// Handle background sync (optional - for future email notifications)
164self.addEventListener('sync', (event) => {
165  if (event.tag === 'background-sync') {
166    // Future: implement background email checking
167    console.log('Background sync triggered');
168  }
169});
170
171// Handle push notifications (optional - for future email notifications)
172self.addEventListener('push', (event) => {
173  if (event.data) {
174    const data = event.data.json();
175    const options = {
176      body: data.body || 'You have a new temporary email',
177      icon: '/images/android-chrome-192x192.png',
178      badge: '/images/favicon-32x32.png',
179      tag: 'email-notification',
180      requireInteraction: false,
181      actions: [
182        {
183          action: 'view',
184          title: 'View Email'
185        },
186        {
187          action: 'dismiss',
188          title: 'Dismiss'
189        }
190      ]
191    };
192    
193    event.waitUntil(
194      self.registration.showNotification(data.title || 'New Email', options)
195    );
196  }
197});
198
199// Handle notification clicks
200self.addEventListener('notificationclick', (event) => {
201  event.notification.close();
202
203  const targetUrl = event.notification.data?.url || '/';
204
205  event.waitUntil(
206    clients.matchAll({ type: 'window', includeUncontrolled: true }).then(windowClients => {
207      // Focus existing tab if available
208      for (const client of windowClients) {
209        if (client.url.includes(self.location.origin)) {
210          return client.focus().then(c => c.navigate(targetUrl));
211        }
212      }
213      // Otherwise open new window
214      return clients.openWindow(targetUrl);
215    })
216  );
217});
218

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.