PageSourceSearch

https://odysseus-lxcq.onrender.com/static/sw.js

js odysseus-lxcq.onrender.com collected 2026-10-03 11:47:31 UTC 4,853 bytes, 144 lines download raw bytes

1// static/sw.js — Odysseus PWA Service Worker
2// Strategy:
3//   - HTML (navigation): stale-while-revalidate. Instant open from cache,
4//     background refresh so the next open has latest HTML.
5//   - JS/CSS (/static/*.js|.css): network-first, cache fallback for offline.
6//     (So code/style edits show up on a normal reload, no manual cache clear.)
7//   - Other static assets (images/fonts/libs): cache-first with bg refresh.
8//   - API / non-GET: never cached.
9// Bump CACHE_NAME whenever the precache list or SW logic changes.
10const CACHE_NAME = 'odysseus-v376-settings-title-icons';
11
12// Core shell precached on install so repeat opens are instant without any
13// network wait. Keep this list in sync with the <script type="module"> tags
14// and <link rel="stylesheet"> in index.html.
15const PRECACHE = [
16  '/',
17  '/static/style.css',
18  '/static/app.js',
19  '/static/js/storage.js',
20  '/static/js/ui.js',
21  '/static/js/markdown.js',
22  '/static/js/dragSort.js',
23  '/static/js/sessions.js',
24  '/static/js/memory.js',
25  '/static/js/skills.js',
26  '/static/js/tourHints.js',
27  '/static/js/fileHandler.js',
28  '/static/js/voiceRecorder.js',
29  '/static/js/models.js',
30  '/static/js/rag.js',
31  '/static/js/presets.js',
32  '/static/js/search.js',
33  '/static/js/spinner.js',
34  '/static/js/tts-ai.js',
35  '/static/js/document.js',
36  '/static/js/gallery.js',
37  '/static/js/chatRenderer.js',
38  '/static/js/codeRunner.js',
39  '/static/js/chatStream.js',
40  '/static/js/chat.js',
41  '/static/js/cookbook.js',
42  '/static/js/search-chat.js',
43  '/static/js/compare/index.js',
44  '/static/js/theme.js',
45  '/static/js/censor.js',
46  '/static/js/settings.js',
47  '/static/js/admin.js',
48  '/static/js/init.js',
49  '/static/js/slashCommands.js',
50  '/static/js/emailInbox.js',
51  '/static/js/emailLibrary/utils.js',
52  '/static/js/emailLibrary/signatureFold.js',
53  '/static/js/emailLibrary/state.js',
54  '/static/js/notes.js',
55  '/static/js/tasks.js',
56  '/static/js/calendar.js',
57  '/static/js/calendar/utils.js',
58  '/static/js/calendar/reminders.js',
59  '/static/js/group.js',
60  '/static/js/keyboard-shortcuts.js',
61  '/static/js/sidebar-layout.js',
62  '/static/js/section-management.js',
63  '/static/lib/highlight.min.js',
64];
65
66self.addEventListener('install', (e) => {
67  e.waitUntil(
68    caches.open(CACHE_NAME).then(cache =>
69      // addAll is atomic — if any item fails, none are cached. Use individual
70      // puts so a single 404 can't block the whole install.
71      Promise.all(
72        PRECACHE.map(url =>
73          fetch(url, { cache: 'reload' })
74            .then(res => res.ok ? cache.put(url, res) : null)
75            .catch(() => null)
76        )
77      )
78    )
79  );
80  self.skipWaiting();
81});
82
83self.addEventListener('activate', (e) => {
84  e.waitUntil(
85    caches.keys().then(keys =>
86      Promise.all(keys.filter(k => k !== CACHE_NAME).map(k => caches.delete(k)))
87    ).then(() => self.clients.claim())
88  );
89});
90
91self.addEventListener('fetch', (e) => {
92  const url = new URL(e.request.url);
93
94  // Never touch API calls or non-GET.
95  if (url.pathname.startsWith('/api/') || e.request.method !== 'GET') return;
96
97  // HTML navigation: stale-while-revalidate the app shell — but ONLY for the
98  // SPA root. Other navigations (e.g. a deep-linked /static/*.html page) must
99  // go to the network/static handlers below; otherwise every navigation was
100  // served the app index, replacing the page the user actually asked for.
101  if (e.request.mode === 'navigate' && url.pathname === '/') {
102    e.respondWith(
103      caches.open(CACHE_NAME).then(async cache => {
104        const cached = await cache.match('/');
105        const network = fetch(e.request).then(res => {
106          if (res && res.ok) cache.put('/', res.clone());
107          return res;
108        }).catch(() => cached);
109        return cached || network;
110      })
111    );
112    return;
113  }
114
115  // JS/CSS: network-first — always try the network so code/style edits show up
116  // on a normal reload; fall back to cache only when offline.
117  if (url.pathname.startsWith('/static/') && /\.(js|css)(\?|$)/.test(url.pathname + url.search)) {
118    e.respondWith(
119      fetch(e.request).then(res => {
120        if (res && res.ok) {
121          const copy = res.clone();
122          caches.open(CACHE_NAME).then(cache => cache.put(e.request, copy));
123        }
124        return res;
125      }).catch(() => caches.match(e.request))
126    );
127    return;
128  }
129
130  // Other static assets (images, fonts, libs): cache-first with background refresh.
131  if (url.pathname.startsWith('/static/')) {
132    e.respondWith(
133      caches.open(CACHE_NAME).then(async cache => {
134        const cached = await cache.match(e.request);
135        const fetching = fetch(e.request).then(res => {
136          if (res && res.ok) cache.put(e.request, res.clone());
137          return res;
138        }).catch(() => cached);
139        return cached || fetching;
140      })
141    );
142    return;
143  }
144});

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.