forked from whatwg/streams
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathservice-worker.js
113 lines (95 loc) · 3.38 KB
/
service-worker.js
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
"use strict";
const cacheKey = "v2";
const toCache = [
"/",
"https://resources.whatwg.org/standard.css",
"https://resources.whatwg.org/bikeshed.css",
"https://resources.whatwg.org/file-issue.js",
"https://resources.whatwg.org/commit-snapshot-shortcut-key.js",
"https://resources.whatwg.org/logo-streams.svg"
];
self.oninstall = e => {
e.waitUntil(caches.open(cacheKey).then(cache => cache.addAll(toCache)));
};
self.onfetch = e => {
if (e.request.method !== "GET") {
return;
}
if (needsToBeFresh(e.request)) {
// Since this is a Living Standard, it is imperative that you see the freshest content, so we use a
// network-then-cache strategy for the main content.
e.respondWith(
fetch(e.request).then(res => {
e.waitUntil(refreshCacheFromNetworkResponse(e.request, res));
return res;
})
.catch(() => {
return caches.match(e.request);
})
);
} else {
// For auxiliary resources, we can use a cache-then-network strategy; it is OK to not get the freshest.
e.respondWith(
caches.match(e.request).then(cachedResponse => {
const networkFetchPromise = fetch(e.request);
// Ignore network fetch or caching errors; they just mean we won't be able to refresh the cache.
e.waitUntil(
networkFetchPromise
.then(res => refreshCacheFromNetworkResponse(e.request, res))
.catch(() => {})
);
return cachedResponse || networkFetchPromise;
})
);
}
};
self.onactivate = e => {
e.waitUntil(caches.keys().then(keys => {
return Promise.all(keys.filter(key => key !== cacheKey).map(key => caches.delete(key)));
}));
};
function refreshCacheFromNetworkResponse(req, res) {
if (!res.ok) {
throw new Error(`${res.url} is responding with ${res.status}`);
}
const resForCache = res.clone();
return caches.open(cacheKey).then(cache => cache.put(req, resForCache));
}
function needsToBeFresh(req) {
const requestURL = new URL(req.url);
return requestURL.origin === location.origin && requestURL.pathname === "/";
}
// From https://github.com/jakearchibald/async-waituntil-polyfill
// Apache 2 License: https://github.com/jakearchibald/async-waituntil-polyfill/blob/master/LICENSE
{
const waitUntil = ExtendableEvent.prototype.waitUntil;
const respondWith = FetchEvent.prototype.respondWith;
const promisesMap = new WeakMap();
ExtendableEvent.prototype.waitUntil = function(promise) {
const extendableEvent = this;
let promises = promisesMap.get(extendableEvent);
if (promises) {
promises.push(Promise.resolve(promise));
return;
}
promises = [Promise.resolve(promise)];
promisesMap.set(extendableEvent, promises);
// call original method
return waitUntil.call(extendableEvent, Promise.resolve().then(function processPromises() {
const len = promises.length;
// wait for all to settle
return Promise.all(promises.map(p => p.catch(()=>{}))).then(() => {
// have new items been added? If so, wait again
if (promises.length != len) return processPromises();
// we're done!
promisesMap.delete(extendableEvent);
// reject if one of the promises rejected
return Promise.all(promises);
});
}));
};
FetchEvent.prototype.respondWith = function(promise) {
this.waitUntil(promise);
return respondWith.call(this, promise);
};
}