-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathsw.js
More file actions
156 lines (141 loc) · 5.1 KB
/
sw.js
File metadata and controls
156 lines (141 loc) · 5.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/**
* Meow Decoder - Service Worker for Caching
*
* Caches WASM modules and static assets for instant loading on repeat visits.
* This is a performance optimization only - no crypto operations happen here.
*/
const CACHE_NAME = 'meow-decoder-v2';
// Assets to cache on install
const PRECACHE_ASSETS = [
'./',
'./index.html',
'./crypto-worker.js',
'./crypto_core.js',
'./crypto_core_bg.wasm',
'./assets/meow-decoder-logo.svg'
];
// Install event - cache critical assets
self.addEventListener('install', (event) => {
console.log('[SW] Installing service worker...');
event.waitUntil(
caches.open(CACHE_NAME)
.then((cache) => {
console.log('[SW] Caching critical assets');
// Use addAll with error handling for each asset
return Promise.allSettled(
PRECACHE_ASSETS.map(url =>
cache.add(url).catch(err => {
console.warn(`[SW] Failed to cache ${url}:`, err.message);
})
)
);
})
.then(() => {
console.log('[SW] Precache complete');
return self.skipWaiting();
})
);
});
// Activate event - clean up old caches
self.addEventListener('activate', (event) => {
console.log('[SW] Activating service worker...');
event.waitUntil(
caches.keys()
.then((cacheNames) => {
return Promise.all(
cacheNames
.filter((name) => name !== CACHE_NAME)
.map((name) => {
console.log('[SW] Deleting old cache:', name);
return caches.delete(name);
})
);
})
.then(() => {
console.log('[SW] Claiming clients');
return self.clients.claim();
})
);
});
// Fetch event - use appropriate caching strategy per resource type
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
// Only cache same-origin requests
if (url.origin !== location.origin) {
return;
}
// HTML files: ALWAYS network-first (so code changes load immediately)
if (url.pathname.endsWith('.html') || url.pathname === '/' || url.pathname.endsWith('/')) {
event.respondWith(
fetch(event.request)
.then((response) => {
if (response.ok) {
const responseClone = response.clone();
caches.open(CACHE_NAME)
.then((cache) => cache.put(event.request, responseClone));
}
return response;
})
.catch(() => {
// Network failed - fall back to cache for offline support
return caches.match(event.request);
})
);
return;
}
// Static assets (WASM, JS, images): cache-first with background revalidation
if (shouldCache(url.pathname)) {
event.respondWith(
caches.match(event.request)
.then((cachedResponse) => {
if (cachedResponse) {
// Return cached version, but update cache in background
updateCache(event.request);
return cachedResponse;
}
// Not in cache - fetch from network and cache it
return fetch(event.request)
.then((response) => {
if (response.ok) {
const responseClone = response.clone();
caches.open(CACHE_NAME)
.then((cache) => cache.put(event.request, responseClone));
}
return response;
});
})
);
}
});
// Determine if a request should be cached (static assets only)
function shouldCache(pathname) {
// Cache WASM, JS, and image assets (NOT HTML - handled separately)
return pathname.endsWith('.wasm') ||
pathname.endsWith('.js') ||
pathname.endsWith('.svg') ||
pathname.endsWith('.png') ||
pathname.includes('/crypto_core');
}
// Update cache in background (stale-while-revalidate)
function updateCache(request) {
fetch(request)
.then((response) => {
if (response.ok) {
caches.open(CACHE_NAME)
.then((cache) => cache.put(request, response));
}
})
.catch(() => {
// Network failed, that's okay - we already served from cache
});
}
// Handle messages from main thread
self.addEventListener('message', (event) => {
if (event.data === 'skipWaiting') {
self.skipWaiting();
}
if (event.data === 'clearCache') {
caches.delete(CACHE_NAME)
.then(() => console.log('[SW] Cache cleared'));
}
});