PageSourceSearch

https://cutoutstudio.org/app/sw.js

js cutoutstudio.org collected 2026-10-02 02:03:50 UTC 3,525 bytes, 86 lines download raw bytes

1/*
2 * Cutout service worker - intentionally minimal and conservative.
3 *
4 * WHY THIS EXISTS: Chrome's installability criteria for a PWA (which is
5 * what a TWA/Android app wraps) require a registered service worker with
6 * a fetch handler. Without one, "Add to Home Screen" / the eventual
7 * Android app wrapper has a weaker installability signal.
8 *
9 * WHY IT DOES SO LITTLE: Cutout is not a content site that benefits from
10 * aggressive caching - every real interaction (removing a background,
11 * batch processing) is a live POST to /api/remove-bg that must always
12 * hit the real network, never a cache. Caching anything related to that
13 * endpoint, even by accident, would risk serving a stale/wrong image
14 * result. This service worker deliberately:
15 *   - NEVER intercepts or caches anything under /api/ - those requests
16 *     are passed straight through, untouched, every time
17 *   - NEVER caches POST requests at all (the Cache API can only store
18 *     GET responses anyway, but this is explicit about the intent)
19 *   - Only caches a small, fixed "app shell" list (the page itself, the
20 *     manifest, and the icons) using a network-first strategy, so a
21 *     visitor with a live connection ALWAYS gets the current, live
22 *     version - the cached copy is purely a fallback for the rare case
23 *     of opening the app with genuinely no network connection at all
24 *   - Uses a versioned cache name (CACHE_VERSION below) - bump this any
25 *     time app/manifest.webmanifest or the icon set changes, so old
26 *     clients don't keep serving a stale shell indefinitely
27 */
28
29const CACHE_VERSION = 'cutout-shell-v1';
30const SHELL_URLS = [
31  '/',
32  '/app/manifest.webmanifest',
33  '/app/icons/icon-192.png',
34  '/app/icons/icon-512.png',
35];
36
37self.addEventListener('install', (event) => {
38  event.waitUntil(
39    caches.open(CACHE_VERSION).then((cache) => cache.addAll(SHELL_URLS))
40  );
41  self.skipWaiting();
42});
43
44self.addEventListener('activate', (event) => {
45  event.waitUntil(
46    caches.keys().then((keys) =>
47      Promise.all(
48        keys
49          .filter((key) => key.startsWith('cutout-shell-') && key !== CACHE_VERSION)
50          .map((key) => caches.delete(key))
51      )
52    )
53  );
54  self.clients.claim();
55});
56
57self.addEventListener('fetch', (event) => {
58  const { request } = event;
59
60  // Never touch anything that isn't a plain GET, and never touch the
61  // real backend API route under any circumstances - always go straight
62  // to the network for both, no caching logic applied at all.
63  if (request.method !== 'GET' || new URL(request.url).pathname.startsWith('/api/')) {
64    return;
65  }
66
67  // Network-first for everything else: always try the live network
68  // first so a connected visitor never sees stale content, and only
69  // fall back to the cached shell if the network request genuinely
70  // fails (offline, or the server is unreachable).
71  event.respondWith(
72    fetch(request)
73      .then((networkResponse) => {
74        // Opportunistically refresh the cached copy of shell URLs with
75        // whatever the network just returned, so the offline fallback
76        // stays reasonably current for next time - but only for the
77        // fixed shell list, never for arbitrary requests.
78        if (SHELL_URLS.includes(new URL(request.url).pathname)) {
79          const responseClone = networkResponse.clone();
80          caches.open(CACHE_VERSION).then((cache) => cache.put(request, responseClone));
81        }
82        return networkResponse;
83      })
84      .catch(() => caches.match(request))
85  );
86});

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.