PageSourceSearch

https://fastfeastt.netlify.app/sw.js

js fastfeastt.netlify.app collected 2026-10-03 10:44:36 UTC 4,692 bytes, 177 lines download raw bytes

1// public/sw.js – AUTO-UPDATE + SAFE CACHE SERVICE WORKER
2
3const CACHE_NAME = "fastfeast-v2.1"; // 🔹 Increment version whenever you deploy
4const APP_VERSION = "2.1"; // 🔹 Version log
5
6/**
7 * ✅ Utility: Cache only valid HTTP(S) GET requests
8 */
9function canCache(request, response) {
10  try {
11    const url = new URL(request.url);
12    return (
13      request.method === "GET" &&
14      (url.protocol === "http:" || url.protocol === "https:") &&
15      response &&
16      response.status === 200 &&
17      response.type !== "opaqueredirect"
18    );
19  } catch {
20    return false;
21  }
22}
23
24/**
25 * ✅ INSTALL: Precache app shell & skip waiting immediately
26 */
27self.addEventListener("install", (event) => {
28  event.waitUntil(
29    caches
30      .open(CACHE_NAME)
31      .then((cache) =>
32        cache.addAll([
33          "/",
34          "/manifest.json",
35          "/icons/icon-192x192.png",
36          "/icons/icon-512x512.png",
37        ])
38      )
39      .then(() => self.skipWaiting())
40      .catch((err) => console.error("❌ Cache preload failed:", err))
41  );
42});
43
44/**
45 * ✅ ACTIVATE: Clear all old caches + claim clients
46 */
47self.addEventListener("activate", (event) => {
48  event.waitUntil(
49    caches.keys().then((keys) =>
50      Promise.all(
51        keys.map((key) => {
52          if (key !== CACHE_NAME && key.startsWith("fastfeast-v")) {
53            return caches.delete(key);
54          }
55        })
56      )
57    )
58  );
59  self.clients.claim();
60});
61
62/**
63 * ✅ FETCH: Intelligent caching strategies
64 */
65self.addEventListener("fetch", (event) => {
66  const req = event.request;
67
68  // Ignore non-http(s) URLs (like extensions)
69  try {
70    const url = new URL(req.url);
71    if (!["http:", "https:"].includes(url.protocol)) return;
72  } catch {
73    return;
74  }
75
76  // HTML navigation → Network-first, fallback to cache
77  if (req.mode === "navigate" || req.destination === "document") {
78    event.respondWith(
79      fetch(req)
80        .then((res) => {
81          const clone = res.clone();
82          if (canCache(req, clone))
83            caches.open(CACHE_NAME).then((c) => c.put(req, clone));
84          return res;
85        })
86        .catch(() => caches.match(req).then((r) => r || caches.match("/")))
87    );
88    return;
89  }
90
91  // Static files & images → Cache-first, update in background
92  if (
93    req.url.includes("/_next/static/") ||
94    req.url.includes("/icons/") ||
95    req.destination === "image" ||
96    req.destination === "audio" 
97  ) {
98    event.respondWith(
99      caches.match(req).then((cached) => {
100        if (cached) {
101          fetch(req)
102            .then((netRes) => {
103              const clone = netRes.clone();
104              if (canCache(req, clone))
105                caches.open(CACHE_NAME).then((c) => c.put(req, clone));
106            })
107            .catch(() => {}); // Background update
108          return cached;
109        }
110        return fetch(req)
111          .then((res) => {
112            const clone = res.clone();
113            if (canCache(req, clone))
114              caches.open(CACHE_NAME).then((c) => c.put(req, clone));
115            return res;
116          })
117          .catch(() =>
118            req.destination === "image"
119              ? new Response(
120                  `<svg width="100" height="100" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="#f3f4f6"/><text x="50%" y="50%" font-family="Arial" font-size="12" fill="#9ca3af" text-anchor="middle" dy=".3em">Image</text></svg>`,
121                  { headers: { "Content-Type": "image/svg+xml" } }
122                )
123              : new Response("Network error", { status: 408 })
124          );
125      })
126    );
127    return;
128  }
129
130  // API calls → Network-only (no caching)
131  if (req.url.includes("/api/")) {
132    event.respondWith(fetch(req));
133    return;
134  }
135
136  // Default: Cache-first fallback
137  event.respondWith(
138    caches.match(req).then((cached) => {
139      if (cached) return cached;
140      return fetch(req).then((res) => {
141        const clone = res.clone();
142        if (canCache(req, clone))
143          caches.open(CACHE_NAME).then((c) => c.put(req, clone));
144        return res;
145      });
146    })
147  );
148});
149
150/**
151 * ✅ MESSAGE HANDLER: Manual update commands
152 */
153self.addEventListener("message", (event) => {
154  const data = event.data;
155  if (!data) return;
156
157  if (data.type === "SKIP_WAITING") {
158    self.skipWaiting();
159  }
160
161  if (data.type === "GET_VERSION") {
162    event.ports?.[0]?.postMessage({ version: APP_VERSION });
163  }
164});
165
166/**
167 * ✅ BACKGROUND SYNC (future offline features)
168 */
169self.addEventListener("sync", (event) => {
170  if (event.tag === "background-sync") {
171    event.waitUntil(doBackgroundSync());
172  }
173});
174
175async function doBackgroundSync() {
176  // future offline upload logic here
177}

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.