-
-
Notifications
You must be signed in to change notification settings - Fork 116
Expand file tree
/
Copy pathsw.js
More file actions
129 lines (120 loc) · 3.95 KB
/
Copy pathsw.js
File metadata and controls
129 lines (120 loc) · 3.95 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
const CACHE_NAME = 'markdown-viewer-cache-v3.10.2';
// PERF-011: Split precache into critical (local files) and lazy (CDN libraries)
// Critical assets are precached during SW install for instant offline startup
const CRITICAL_ASSETS = [
'./',
'./index.html',
'./workspace-storage.js',
'./script.js',
'./preview-worker.js',
'./styles.css',
'./assets/seo-metadata.mjs',
'./seo/locales.mjs',
'./assets/lucide-icons.css',
'./RELEASE_NOTES',
'./sample.md',
'./assets/icon.jpg',
'./manifest.json'
];
// CDN assets are cached lazily on first use via runtime cache-first strategy
// This prevents the SW install from downloading ~5.4 MB of CDN resources upfront
const CDN_ORIGINS = [
'cdnjs.cloudflare.com',
'cdn.jsdelivr.net'
];
const NETWORK_FIRST_LOCAL_PATHS = new Set([
'/',
'/index.html',
'/workspace-storage.js',
'/script.js',
'/preview-worker.js',
'/styles.css',
'/assets/seo-metadata.mjs',
'/seo/locales.mjs',
'/assets/lucide-icons.css',
'/sw.js'
]);
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(CRITICAL_ASSETS))
.then(() => self.skipWaiting())
);
});
self.addEventListener('activate', event => {
event.waitUntil(
caches.keys().then(keys => Promise.all(
keys.map(key => {
if (key !== CACHE_NAME) {
return caches.delete(key);
}
})
)).then(() => self.clients.claim())
);
});
self.addEventListener('fetch', event => {
const url = new URL(event.request.url);
const isLocal = url.origin === self.location.origin;
const isCDN = CDN_ORIGINS.some(origin => url.hostname.includes(origin));
if (isLocal) {
const localPath = url.pathname.endsWith('/') ? '/' : url.pathname;
const shouldUseNetworkFirst =
event.request.mode === 'navigate' ||
NETWORK_FIRST_LOCAL_PATHS.has(localPath) ||
localPath.startsWith('/assets/i18n/');
if (shouldUseNetworkFirst) {
event.respondWith(
caches.open(CACHE_NAME).then(cache => {
return fetch(event.request).then(networkResponse => {
if (networkResponse && networkResponse.status === 200) {
cache.put(event.request, networkResponse.clone());
}
return networkResponse;
}).catch(() => cache.match(event.request));
})
);
return;
}
// Stale-While-Revalidate strategy for non-code local assets
event.respondWith(
caches.open(CACHE_NAME).then(cache => {
return cache.match(event.request).then(cachedResponse => {
const fetchPromise = fetch(event.request).then(networkResponse => {
if (networkResponse && networkResponse.status === 200) {
cache.put(event.request, networkResponse.clone());
}
return networkResponse;
}).catch(err => {
console.warn('Background fetch failed for:', event.request.url, err);
});
return cachedResponse || fetchPromise;
});
})
);
} else if (isCDN) {
// Cache-First strategy for stable third-party CDN libraries
// PERF-011: CDN resources are cached on first use (lazy) rather than precached
event.respondWith(
caches.match(event.request)
.then(cachedResponse => {
if (cachedResponse) {
return cachedResponse;
}
return fetch(event.request).then(response => {
if (response && response.status === 200) {
const responseToCache = response.clone();
caches.open(CACHE_NAME).then(cache => {
cache.put(event.request, responseToCache);
});
}
return response;
});
})
);
} else {
// Let arbitrary external resources use the browser's normal request path.
// Wrapping user-supplied images in service-worker fetch() makes them subject
// to connect-src instead of img-src, which blocks otherwise valid images.
return;
}
});