-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathsw.js
More file actions
133 lines (122 loc) · 4.92 KB
/
Copy pathsw.js
File metadata and controls
133 lines (122 loc) · 4.92 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
const CACHE = 'nym-' + '__CACHE_VERSION__';
const ASSET_RE = /\/(js|css|data)\//;
let PRECACHE = [];
try { PRECACHE = JSON.parse('__PRECACHE_ASSETS__'); } catch (_) { }
// Proxied media (avatars, banners, inline chat images, custom emoji) is served
// from /api/proxy. It lives in its own cache that survives deploys so reloads
// and cold webview launches reuse images instead of refetching every one.
const MEDIA_CACHE = 'nym-media-v1';
const MEDIA_MAX_ENTRIES = 600;
async function trimMediaCache() {
try {
const cache = await caches.open(MEDIA_CACHE);
const keys = await cache.keys();
const over = keys.length - MEDIA_MAX_ENTRIES;
if (over > 0) {
await Promise.all(keys.slice(0, over).map((k) => cache.delete(k)));
}
} catch (_) { }
}
self.addEventListener('install', (e) => {
e.waitUntil((async () => {
try {
const c = await caches.open(CACHE);
// Entry document plus the critical JS/CSS bundle.
await c.add(new Request('/', { cache: 'reload' }));
if (PRECACHE.length) {
await Promise.all(PRECACHE.map((u) =>
c.add(new Request(u, { cache: 'reload' })).catch(() => { })
));
}
} catch (_) { }
self.skipWaiting();
})());
});
self.addEventListener('activate', (e) => {
e.waitUntil((async () => {
const keys = await caches.keys();
await Promise.all(keys.filter((k) => k.startsWith('nym-') && k !== CACHE && k !== MEDIA_CACHE).map((k) => caches.delete(k)));
await self.clients.claim();
})());
});
self.addEventListener('fetch', (e) => {
const req = e.request;
if (req.method !== 'GET') return;
const url = new URL(req.url);
// Brand icons/images live on the marketing origin; cache-first so cold
// starts and offline launches don't refetch them on every load.
if (url.origin === 'https://nymchat.app' && url.pathname.startsWith('/images/')) {
e.respondWith((async () => {
const cached = await caches.match(req);
if (cached) return cached;
const resp = await fetch(req);
if (resp && resp.ok) {
const cache = await caches.open(CACHE);
cache.put(req, resp.clone());
}
return resp;
})());
return;
}
if (url.origin !== self.location.origin) return;
// Proxied media keyed by its source URL is effectively immutable, so serve
// cache-first and only refetch images the device hasn't seen. Range requests
// (video seeking) and non-image responses are passed through uncached.
if (url.pathname === '/api/proxy' && url.searchParams.has('url') && !req.headers.has('range')) {
e.respondWith((async () => {
const cache = await caches.open(MEDIA_CACHE);
const cached = await cache.match(req);
if (cached) return cached;
const resp = await fetch(req);
const type = resp && resp.headers.get('content-type') || '';
if (resp && resp.ok && type.indexOf('image/') === 0) {
cache.put(req, resp.clone());
trimMediaCache();
}
return resp;
})());
return;
}
if (req.mode === 'navigate') {
e.respondWith((async () => {
const cache = await caches.open(CACHE);
try {
const resp = await fetch(req);
if (resp && resp.ok) cache.put('/', resp.clone());
return resp;
} catch (_) {
const cached = (await cache.match(req)) || (await cache.match('/'));
return cached || Response.error();
}
})());
return;
}
// Pre-translated UI packs (/i18n/<lang>.json). Unhashed, so served from
// cache first and refreshed in the background: a language already used once
// keeps working offline, and a newer pack lands on the next switch.
if (url.origin === self.location.origin && url.pathname.startsWith('/i18n/')) {
e.respondWith((async () => {
const cache = await caches.open(CACHE);
const cached = await cache.match(req);
const network = fetch(req).then((resp) => {
if (resp && resp.ok) cache.put(req, resp.clone());
return resp;
}).catch(() => cached || Response.error());
return cached || network;
})());
return;
}
// Hashed assets are immutable, and the cache name rotates per build.
if (ASSET_RE.test(url.pathname)) {
e.respondWith((async () => {
const cached = await caches.match(req);
if (cached) return cached;
const resp = await fetch(req);
if (resp && resp.ok) {
const cache = await caches.open(CACHE);
cache.put(req, resp.clone());
}
return resp;
})());
}
});