PageSourceSearch

https://cdn.supademo.com/offline-redirect-sw.js

js supademo.com collected 2026-10-01 08:43:16 UTC 3,154 bytes, 91 lines download raw bytes

1/**
2 * Offline navigation fallback.
3 *
4 * Scope: "/" (everywhere except "/offline/*", which is covered by the more
5 * specific sw.js registration).
6 *
7 * Only intercepts top-level page navigations. When the network fetch fails
8 * (offline / DNS failure), we serve a precached, self-contained fallback page
9 * (/offline.html) AS THE RESPONSE to the original request — we do NOT redirect.
10 *
11 * Why not redirect? A 302 redirect to /offline replaces the current history
12 * entry with /offline, which destroys the user's location: hitting Back no
13 * longer returns to the page they were on, and refreshing once back online
14 * reloads /offline instead of where they were. Answering the original
15 * navigation request with respondWith() keeps the URL and history intact, so
16 * Back works and a refresh after reconnecting restores the real page.
17 *
18 * Subresource requests (images, scripts, CSS, API calls) are not intercepted
19 * — they pass straight through to the network.
20 */
21
22// ? This SW is hand-written and byte-stable across deploys, so the browser
23// ? only reinstalls it (and re-runs the precache below) when this file's bytes
24// ? change. If you edit offline.html, BUMP this version (v1 -> v2) so the SW
25// ? reinstalls and re-precaches the new page; the activate handler then deletes
26// ? the stale cache. Without a bump, clients keep serving the old offline.html.
27const OFFLINE_FALLBACK_CACHE = "offline-fallback-v2";
28const OFFLINE_FALLBACK_URL = "/offline.html";
29const EXCLUDED_NAVIGATION_PATHS = ["/auth/mcp"];
30const EXCLUDED_NAVIGATION_PREFIXES = ["/api/auth/mcp/"];
31
32self.addEventListener("install", (event) => {
33  event.waitUntil(
34    caches
35      .open(OFFLINE_FALLBACK_CACHE)
36      .then((cache) => cache.add(OFFLINE_FALLBACK_URL))
37      .catch(() => {
38        // ? If precaching fails (e.g. offline at install), the fetch handler
39        // ? falls back to an inline response, so registration still succeeds.
40      }),
41  );
42  self.skipWaiting();
43});
44
45self.addEventListener("activate", (event) => {
46  event.waitUntil(
47    caches
48      .keys()
49      .then((keys) =>
50        Promise.all(
51          keys
52            .filter(
53              (key) =>
54                key.startsWith("offline-fallback-") &&
55                key !== OFFLINE_FALLBACK_CACHE,
56            )
57            .map((key) => caches.delete(key)),
58        ),
59      )
60      .then(() => self.clients.claim()),
61  );
62});
63
64self.addEventListener("fetch", (event) => {
65  if (event.request.mode !== "navigate") return;
66
67  const url = new URL(event.request.url);
68  if (
69    EXCLUDED_NAVIGATION_PATHS.includes(url.pathname) ||
70    EXCLUDED_NAVIGATION_PREFIXES.some((prefix) =>
71      url.pathname.startsWith(prefix),
72    )
73  ) {
74    return;
75  }
76
77  event.respondWith(
78    fetch(event.request).catch(async () => {
79      const cached = await caches.match(OFFLINE_FALLBACK_URL);
80      if (cached) return cached;
81
82      return new Response(
83        "<!doctype html><title>Offline</title><h1>You're offline</h1><p>Check your connection and try again.</p>",
84        {
85          status: 503,
86          headers: { "Content-Type": "text/html; charset=utf-8" },
87        },
88      );
89    }),
90  );
91});

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.