serwist 9.1.0-preview.0 → 10.0.0-preview.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Serwist.d.ts +74 -52
- package/dist/Serwist.d.ts.map +1 -1
- package/dist/cacheNames.d.ts.map +1 -1
- package/dist/chunks/resultingClientExists.js +426 -2
- package/dist/{legacy/utils → controllers/PrecacheController}/PrecacheCacheKeyPlugin.d.ts +4 -4
- package/dist/controllers/PrecacheController/PrecacheCacheKeyPlugin.d.ts.map +1 -0
- package/dist/{legacy → controllers/PrecacheController}/PrecacheController.d.ts +73 -64
- package/dist/controllers/PrecacheController/PrecacheController.d.ts.map +1 -0
- package/dist/controllers/PrecacheController/PrecacheInstallReportPlugin.d.ts +14 -0
- package/dist/controllers/PrecacheController/PrecacheInstallReportPlugin.d.ts.map +1 -0
- package/dist/{PrecacheRoute.d.ts → controllers/PrecacheController/PrecacheRoute.d.ts} +4 -5
- package/dist/controllers/PrecacheController/PrecacheRoute.d.ts.map +1 -0
- package/dist/{lib/strategies → controllers/PrecacheController}/PrecacheStrategy.d.ts +6 -6
- package/dist/controllers/PrecacheController/PrecacheStrategy.d.ts.map +1 -0
- package/dist/controllers/PrecacheController/parsePrecacheOptions.d.ts +25 -0
- package/dist/controllers/PrecacheController/parsePrecacheOptions.d.ts.map +1 -0
- package/dist/controllers/RuntimeCacheController.d.ts +65 -0
- package/dist/controllers/RuntimeCacheController.d.ts.map +1 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.internal.js +25 -3
- package/dist/index.js +1868 -211
- package/dist/lib/backgroundSync/BackgroundSyncPlugin.d.ts +2 -2
- package/dist/lib/backgroundSync/BackgroundSyncPlugin.d.ts.map +1 -1
- package/dist/lib/broadcastUpdate/BroadcastUpdatePlugin.d.ts +2 -2
- package/dist/lib/broadcastUpdate/BroadcastUpdatePlugin.d.ts.map +1 -1
- package/dist/lib/cacheableResponse/CacheableResponsePlugin.d.ts +3 -3
- package/dist/lib/cacheableResponse/CacheableResponsePlugin.d.ts.map +1 -1
- package/dist/lib/expiration/ExpirationPlugin.d.ts +2 -2
- package/dist/lib/expiration/ExpirationPlugin.d.ts.map +1 -1
- package/dist/lib/precaching/PrecacheFallbackPlugin.d.ts +7 -7
- package/dist/lib/precaching/PrecacheFallbackPlugin.d.ts.map +1 -1
- package/dist/lib/rangeRequests/RangeRequestsPlugin.d.ts +3 -3
- package/dist/lib/rangeRequests/RangeRequestsPlugin.d.ts.map +1 -1
- package/dist/lib/strategies/Strategy.d.ts +3 -3
- package/dist/lib/strategies/Strategy.d.ts.map +1 -1
- package/dist/lib/strategies/StrategyHandler.d.ts +4 -4
- package/dist/lib/strategies/StrategyHandler.d.ts.map +1 -1
- package/dist/lib/strategies/plugins/cacheOkAndOpaquePlugin.d.ts +2 -2
- package/dist/lib/strategies/plugins/cacheOkAndOpaquePlugin.d.ts.map +1 -1
- package/dist/types.d.ts +16 -64
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/pluginUtils.d.ts +2 -2
- package/dist/utils/pluginUtils.d.ts.map +1 -1
- package/package.json +6 -13
- package/src/Serwist.ts +157 -262
- package/src/{legacy/utils → controllers/PrecacheController}/PrecacheCacheKeyPlugin.ts +5 -6
- package/src/{legacy → controllers/PrecacheController}/PrecacheController.ts +143 -155
- package/src/{utils → controllers/PrecacheController}/PrecacheInstallReportPlugin.ts +5 -5
- package/src/{PrecacheRoute.ts → controllers/PrecacheController/PrecacheRoute.ts} +11 -12
- package/src/{lib/strategies → controllers/PrecacheController}/PrecacheStrategy.ts +7 -7
- package/src/controllers/PrecacheController/parsePrecacheOptions.ts +46 -0
- package/src/controllers/RuntimeCacheController.ts +119 -0
- package/src/index.ts +5 -2
- package/src/lib/backgroundSync/BackgroundSyncPlugin.ts +2 -2
- package/src/lib/broadcastUpdate/BroadcastUpdatePlugin.ts +2 -2
- package/src/lib/cacheableResponse/CacheableResponsePlugin.ts +3 -3
- package/src/lib/expiration/ExpirationPlugin.ts +2 -2
- package/src/lib/precaching/PrecacheFallbackPlugin.ts +10 -10
- package/src/lib/rangeRequests/RangeRequestsPlugin.ts +3 -3
- package/src/lib/strategies/Strategy.ts +3 -3
- package/src/lib/strategies/StrategyHandler.ts +9 -9
- package/src/lib/strategies/plugins/cacheOkAndOpaquePlugin.ts +2 -2
- package/src/types.ts +24 -67
- package/src/utils/pluginUtils.ts +2 -2
- package/dist/PrecacheRoute.d.ts.map +0 -1
- package/dist/chunks/printInstallDetails.js +0 -1601
- package/dist/chunks/waitUntil.js +0 -449
- package/dist/index.legacy.d.ts +0 -28
- package/dist/index.legacy.d.ts.map +0 -1
- package/dist/index.legacy.js +0 -790
- package/dist/legacy/PrecacheController.d.ts.map +0 -1
- package/dist/legacy/PrecacheFallbackPlugin.d.ts +0 -61
- package/dist/legacy/PrecacheFallbackPlugin.d.ts.map +0 -1
- package/dist/legacy/PrecacheRoute.d.ts +0 -19
- package/dist/legacy/PrecacheRoute.d.ts.map +0 -1
- package/dist/legacy/Router.d.ts +0 -151
- package/dist/legacy/Router.d.ts.map +0 -1
- package/dist/legacy/addPlugins.d.ts +0 -9
- package/dist/legacy/addPlugins.d.ts.map +0 -1
- package/dist/legacy/addRoute.d.ts +0 -14
- package/dist/legacy/addRoute.d.ts.map +0 -1
- package/dist/legacy/constants.d.ts +0 -10
- package/dist/legacy/constants.d.ts.map +0 -1
- package/dist/legacy/createHandlerBoundToURL.d.ts +0 -17
- package/dist/legacy/createHandlerBoundToURL.d.ts.map +0 -1
- package/dist/legacy/fallbacks.d.ts +0 -59
- package/dist/legacy/fallbacks.d.ts.map +0 -1
- package/dist/legacy/getCacheKeyForURL.d.ts +0 -20
- package/dist/legacy/getCacheKeyForURL.d.ts.map +0 -1
- package/dist/legacy/handlePrecaching.d.ts +0 -54
- package/dist/legacy/handlePrecaching.d.ts.map +0 -1
- package/dist/legacy/initializeGoogleAnalytics.d.ts +0 -38
- package/dist/legacy/initializeGoogleAnalytics.d.ts.map +0 -1
- package/dist/legacy/installSerwist.d.ts +0 -81
- package/dist/legacy/installSerwist.d.ts.map +0 -1
- package/dist/legacy/matchPrecache.d.ts +0 -15
- package/dist/legacy/matchPrecache.d.ts.map +0 -1
- package/dist/legacy/precache.d.ts +0 -20
- package/dist/legacy/precache.d.ts.map +0 -1
- package/dist/legacy/precacheAndRoute.d.ts +0 -14
- package/dist/legacy/precacheAndRoute.d.ts.map +0 -1
- package/dist/legacy/registerRoute.d.ts +0 -16
- package/dist/legacy/registerRoute.d.ts.map +0 -1
- package/dist/legacy/registerRuntimeCaching.d.ts +0 -11
- package/dist/legacy/registerRuntimeCaching.d.ts.map +0 -1
- package/dist/legacy/setCatchHandler.d.ts +0 -10
- package/dist/legacy/setCatchHandler.d.ts.map +0 -1
- package/dist/legacy/setDefaultHandler.d.ts +0 -13
- package/dist/legacy/setDefaultHandler.d.ts.map +0 -1
- package/dist/legacy/singletonPrecacheController.d.ts +0 -34
- package/dist/legacy/singletonPrecacheController.d.ts.map +0 -1
- package/dist/legacy/singletonRouter.d.ts +0 -41
- package/dist/legacy/singletonRouter.d.ts.map +0 -1
- package/dist/legacy/unregisterRoute.d.ts +0 -9
- package/dist/legacy/unregisterRoute.d.ts.map +0 -1
- package/dist/legacy/utils/PrecacheCacheKeyPlugin.d.ts.map +0 -1
- package/dist/legacy/utils/getCacheKeyForURL.d.ts +0 -14
- package/dist/legacy/utils/getCacheKeyForURL.d.ts.map +0 -1
- package/dist/lib/strategies/PrecacheStrategy.d.ts.map +0 -1
- package/dist/utils/PrecacheCacheKeyPlugin.d.ts +0 -16
- package/dist/utils/PrecacheCacheKeyPlugin.d.ts.map +0 -1
- package/dist/utils/PrecacheInstallReportPlugin.d.ts +0 -14
- package/dist/utils/PrecacheInstallReportPlugin.d.ts.map +0 -1
- package/dist/utils/parsePrecacheOptions.d.ts +0 -26
- package/dist/utils/parsePrecacheOptions.d.ts.map +0 -1
- package/src/index.legacy.ts +0 -62
- package/src/legacy/PrecacheFallbackPlugin.ts +0 -92
- package/src/legacy/PrecacheRoute.ts +0 -48
- package/src/legacy/Router.ts +0 -484
- package/src/legacy/addPlugins.ts +0 -21
- package/src/legacy/addRoute.ts +0 -27
- package/src/legacy/constants.ts +0 -22
- package/src/legacy/createHandlerBoundToURL.ts +0 -30
- package/src/legacy/fallbacks.ts +0 -94
- package/src/legacy/getCacheKeyForURL.ts +0 -32
- package/src/legacy/handlePrecaching.ts +0 -86
- package/src/legacy/initializeGoogleAnalytics.ts +0 -218
- package/src/legacy/installSerwist.ts +0 -170
- package/src/legacy/matchPrecache.ts +0 -27
- package/src/legacy/precache.ts +0 -33
- package/src/legacy/precacheAndRoute.ts +0 -27
- package/src/legacy/registerRoute.ts +0 -28
- package/src/legacy/registerRuntimeCaching.ts +0 -17
- package/src/legacy/setCatchHandler.ts +0 -21
- package/src/legacy/setDefaultHandler.ts +0 -24
- package/src/legacy/singletonPrecacheController.ts +0 -53
- package/src/legacy/singletonRouter.ts +0 -70
- package/src/legacy/unregisterRoute.ts +0 -13
- package/src/legacy/utils/getCacheKeyForURL.ts +0 -36
- package/src/utils/PrecacheCacheKeyPlugin.ts +0 -33
- package/src/utils/parsePrecacheOptions.ts +0 -47
package/dist/index.js
CHANGED
|
@@ -1,17 +1,668 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { S as SerwistError, c as canConstructResponseFromBodyStream, f as finalAssertExports, D as Deferred, l as logger, g as getFriendlyURL, t as timeout, a as cacheMatchIgnoreParams, e as executeQuotaErrorCallbacks, b as cacheNames$1, d as clientsClaim, w as waitUntil, r as resultingClientExists, q as quotaErrorCallbacks } from './chunks/resultingClientExists.js';
|
|
2
|
+
import { parallel } from '@serwist/utils';
|
|
3
|
+
import { openDB, deleteDB } from 'idb';
|
|
4
|
+
|
|
5
|
+
const copyResponse = async (response, modifier)=>{
|
|
6
|
+
let origin = null;
|
|
7
|
+
if (response.url) {
|
|
8
|
+
const responseURL = new URL(response.url);
|
|
9
|
+
origin = responseURL.origin;
|
|
10
|
+
}
|
|
11
|
+
if (origin !== self.location.origin) {
|
|
12
|
+
throw new SerwistError("cross-origin-copy-response", {
|
|
13
|
+
origin
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
const clonedResponse = response.clone();
|
|
17
|
+
const responseInit = {
|
|
18
|
+
headers: new Headers(clonedResponse.headers),
|
|
19
|
+
status: clonedResponse.status,
|
|
20
|
+
statusText: clonedResponse.statusText
|
|
21
|
+
};
|
|
22
|
+
const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit;
|
|
23
|
+
const body = canConstructResponseFromBodyStream() ? clonedResponse.body : await clonedResponse.blob();
|
|
24
|
+
return new Response(body, modifiedResponseInit);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function toRequest(input) {
|
|
28
|
+
return typeof input === "string" ? new Request(input) : input;
|
|
29
|
+
}
|
|
30
|
+
class StrategyHandler {
|
|
31
|
+
event;
|
|
32
|
+
request;
|
|
33
|
+
url;
|
|
34
|
+
params;
|
|
35
|
+
_cacheKeys = {};
|
|
36
|
+
_strategy;
|
|
37
|
+
_handlerDeferred;
|
|
38
|
+
_extendLifetimePromises;
|
|
39
|
+
_plugins;
|
|
40
|
+
_pluginStateMap;
|
|
41
|
+
constructor(strategy, options){
|
|
42
|
+
if (process.env.NODE_ENV !== "production") {
|
|
43
|
+
finalAssertExports.isInstance(options.event, ExtendableEvent, {
|
|
44
|
+
moduleName: "serwist",
|
|
45
|
+
className: "StrategyHandler",
|
|
46
|
+
funcName: "constructor",
|
|
47
|
+
paramName: "options.event"
|
|
48
|
+
});
|
|
49
|
+
finalAssertExports.isInstance(options.request, Request, {
|
|
50
|
+
moduleName: "serwist",
|
|
51
|
+
className: "StrategyHandler",
|
|
52
|
+
funcName: "constructor",
|
|
53
|
+
paramName: "options.request"
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
this.event = options.event;
|
|
57
|
+
this.request = options.request;
|
|
58
|
+
if (options.url) {
|
|
59
|
+
this.url = options.url;
|
|
60
|
+
this.params = options.params;
|
|
61
|
+
}
|
|
62
|
+
this._strategy = strategy;
|
|
63
|
+
this._handlerDeferred = new Deferred();
|
|
64
|
+
this._extendLifetimePromises = [];
|
|
65
|
+
this._plugins = [
|
|
66
|
+
...strategy.plugins
|
|
67
|
+
];
|
|
68
|
+
this._pluginStateMap = new Map();
|
|
69
|
+
for (const plugin of this._plugins){
|
|
70
|
+
this._pluginStateMap.set(plugin, {});
|
|
71
|
+
}
|
|
72
|
+
this.event.waitUntil(this._handlerDeferred.promise);
|
|
73
|
+
}
|
|
74
|
+
async fetch(input) {
|
|
75
|
+
const { event } = this;
|
|
76
|
+
let request = toRequest(input);
|
|
77
|
+
const preloadResponse = await this.getPreloadResponse();
|
|
78
|
+
if (preloadResponse) {
|
|
79
|
+
return preloadResponse;
|
|
80
|
+
}
|
|
81
|
+
const originalRequest = this.hasCallback("fetchDidFail") ? request.clone() : null;
|
|
82
|
+
try {
|
|
83
|
+
for (const cb of this.iterateCallbacks("requestWillFetch")){
|
|
84
|
+
request = await cb({
|
|
85
|
+
request: request.clone(),
|
|
86
|
+
event
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
} catch (err) {
|
|
90
|
+
if (err instanceof Error) {
|
|
91
|
+
throw new SerwistError("plugin-error-request-will-fetch", {
|
|
92
|
+
thrownErrorMessage: err.message
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
const pluginFilteredRequest = request.clone();
|
|
97
|
+
try {
|
|
98
|
+
let fetchResponse;
|
|
99
|
+
fetchResponse = await fetch(request, request.mode === "navigate" ? undefined : this._strategy.fetchOptions);
|
|
100
|
+
if (process.env.NODE_ENV !== "production") {
|
|
101
|
+
logger.debug(`Network request for '${getFriendlyURL(request.url)}' returned a response with status '${fetchResponse.status}'.`);
|
|
102
|
+
}
|
|
103
|
+
for (const callback of this.iterateCallbacks("fetchDidSucceed")){
|
|
104
|
+
fetchResponse = await callback({
|
|
105
|
+
event,
|
|
106
|
+
request: pluginFilteredRequest,
|
|
107
|
+
response: fetchResponse
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return fetchResponse;
|
|
111
|
+
} catch (error) {
|
|
112
|
+
if (process.env.NODE_ENV !== "production") {
|
|
113
|
+
logger.log(`Network request for '${getFriendlyURL(request.url)}' threw an error.`, error);
|
|
114
|
+
}
|
|
115
|
+
if (originalRequest) {
|
|
116
|
+
await this.runCallbacks("fetchDidFail", {
|
|
117
|
+
error: error,
|
|
118
|
+
event,
|
|
119
|
+
originalRequest: originalRequest.clone(),
|
|
120
|
+
request: pluginFilteredRequest.clone()
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
throw error;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
async fetchAndCachePut(input) {
|
|
127
|
+
const response = await this.fetch(input);
|
|
128
|
+
const responseClone = response.clone();
|
|
129
|
+
void this.waitUntil(this.cachePut(input, responseClone));
|
|
130
|
+
return response;
|
|
131
|
+
}
|
|
132
|
+
async cacheMatch(key) {
|
|
133
|
+
const request = toRequest(key);
|
|
134
|
+
let cachedResponse;
|
|
135
|
+
const { cacheName, matchOptions } = this._strategy;
|
|
136
|
+
const effectiveRequest = await this.getCacheKey(request, "read");
|
|
137
|
+
const multiMatchOptions = {
|
|
138
|
+
...matchOptions,
|
|
139
|
+
...{
|
|
140
|
+
cacheName
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);
|
|
144
|
+
if (process.env.NODE_ENV !== "production") {
|
|
145
|
+
if (cachedResponse) {
|
|
146
|
+
logger.debug(`Found a cached response in '${cacheName}'.`);
|
|
147
|
+
} else {
|
|
148
|
+
logger.debug(`No cached response found in '${cacheName}'.`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
for (const callback of this.iterateCallbacks("cachedResponseWillBeUsed")){
|
|
152
|
+
cachedResponse = await callback({
|
|
153
|
+
cacheName,
|
|
154
|
+
matchOptions,
|
|
155
|
+
cachedResponse,
|
|
156
|
+
request: effectiveRequest,
|
|
157
|
+
event: this.event
|
|
158
|
+
}) || undefined;
|
|
159
|
+
}
|
|
160
|
+
return cachedResponse;
|
|
161
|
+
}
|
|
162
|
+
async cachePut(key, response) {
|
|
163
|
+
const request = toRequest(key);
|
|
164
|
+
await timeout(0);
|
|
165
|
+
const effectiveRequest = await this.getCacheKey(request, "write");
|
|
166
|
+
if (process.env.NODE_ENV !== "production") {
|
|
167
|
+
if (effectiveRequest.method && effectiveRequest.method !== "GET") {
|
|
168
|
+
throw new SerwistError("attempt-to-cache-non-get-request", {
|
|
169
|
+
url: getFriendlyURL(effectiveRequest.url),
|
|
170
|
+
method: effectiveRequest.method
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (!response) {
|
|
175
|
+
if (process.env.NODE_ENV !== "production") {
|
|
176
|
+
logger.error(`Cannot cache non-existent response for '${getFriendlyURL(effectiveRequest.url)}'.`);
|
|
177
|
+
}
|
|
178
|
+
throw new SerwistError("cache-put-with-no-response", {
|
|
179
|
+
url: getFriendlyURL(effectiveRequest.url)
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
const responseToCache = await this._ensureResponseSafeToCache(response);
|
|
183
|
+
if (!responseToCache) {
|
|
184
|
+
if (process.env.NODE_ENV !== "production") {
|
|
185
|
+
logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' will not be cached.`, responseToCache);
|
|
186
|
+
}
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
const { cacheName, matchOptions } = this._strategy;
|
|
190
|
+
const cache = await self.caches.open(cacheName);
|
|
191
|
+
if (process.env.NODE_ENV !== "production") {
|
|
192
|
+
const vary = response.headers.get("Vary");
|
|
193
|
+
if (vary && matchOptions?.ignoreVary !== true) {
|
|
194
|
+
logger.debug(`The response for ${getFriendlyURL(effectiveRequest.url)} has a 'Vary: ${vary}' header. Consider setting the {ignoreVary: true} option on your strategy to ensure cache matching and deletion works as expected.`);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
const hasCacheUpdateCallback = this.hasCallback("cacheDidUpdate");
|
|
198
|
+
const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams(cache, effectiveRequest.clone(), [
|
|
199
|
+
"__WB_REVISION__"
|
|
200
|
+
], matchOptions) : null;
|
|
201
|
+
if (process.env.NODE_ENV !== "production") {
|
|
202
|
+
logger.debug(`Updating the '${cacheName}' cache with a new Response for ${getFriendlyURL(effectiveRequest.url)}.`);
|
|
203
|
+
}
|
|
204
|
+
try {
|
|
205
|
+
await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);
|
|
206
|
+
} catch (error) {
|
|
207
|
+
if (error instanceof Error) {
|
|
208
|
+
if (error.name === "QuotaExceededError") {
|
|
209
|
+
await executeQuotaErrorCallbacks();
|
|
210
|
+
}
|
|
211
|
+
throw error;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
for (const callback of this.iterateCallbacks("cacheDidUpdate")){
|
|
215
|
+
await callback({
|
|
216
|
+
cacheName,
|
|
217
|
+
oldResponse,
|
|
218
|
+
newResponse: responseToCache.clone(),
|
|
219
|
+
request: effectiveRequest,
|
|
220
|
+
event: this.event
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
return true;
|
|
224
|
+
}
|
|
225
|
+
async getCacheKey(request, mode) {
|
|
226
|
+
const key = `${request.url} | ${mode}`;
|
|
227
|
+
if (!this._cacheKeys[key]) {
|
|
228
|
+
let effectiveRequest = request;
|
|
229
|
+
for (const callback of this.iterateCallbacks("cacheKeyWillBeUsed")){
|
|
230
|
+
effectiveRequest = toRequest(await callback({
|
|
231
|
+
mode,
|
|
232
|
+
request: effectiveRequest,
|
|
233
|
+
event: this.event,
|
|
234
|
+
params: this.params
|
|
235
|
+
}));
|
|
236
|
+
}
|
|
237
|
+
this._cacheKeys[key] = effectiveRequest;
|
|
238
|
+
}
|
|
239
|
+
return this._cacheKeys[key];
|
|
240
|
+
}
|
|
241
|
+
hasCallback(name) {
|
|
242
|
+
for (const plugin of this._strategy.plugins){
|
|
243
|
+
if (name in plugin) {
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
async runCallbacks(name, param) {
|
|
250
|
+
for (const callback of this.iterateCallbacks(name)){
|
|
251
|
+
await callback(param);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
*iterateCallbacks(name) {
|
|
255
|
+
for (const plugin of this._strategy.plugins){
|
|
256
|
+
if (typeof plugin[name] === "function") {
|
|
257
|
+
const state = this._pluginStateMap.get(plugin);
|
|
258
|
+
const statefulCallback = (param)=>{
|
|
259
|
+
const statefulParam = {
|
|
260
|
+
...param,
|
|
261
|
+
state
|
|
262
|
+
};
|
|
263
|
+
return plugin[name](statefulParam);
|
|
264
|
+
};
|
|
265
|
+
yield statefulCallback;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
waitUntil(promise) {
|
|
270
|
+
this._extendLifetimePromises.push(promise);
|
|
271
|
+
return promise;
|
|
272
|
+
}
|
|
273
|
+
async doneWaiting() {
|
|
274
|
+
let promise = undefined;
|
|
275
|
+
while(promise = this._extendLifetimePromises.shift()){
|
|
276
|
+
await promise;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
destroy() {
|
|
280
|
+
this._handlerDeferred.resolve(null);
|
|
281
|
+
}
|
|
282
|
+
async getPreloadResponse() {
|
|
283
|
+
if (this.event instanceof FetchEvent && this.event.request.mode === "navigate" && "preloadResponse" in this.event) {
|
|
284
|
+
try {
|
|
285
|
+
const possiblePreloadResponse = await this.event.preloadResponse;
|
|
286
|
+
if (possiblePreloadResponse) {
|
|
287
|
+
if (process.env.NODE_ENV !== "production") {
|
|
288
|
+
logger.log(`Using a preloaded navigation response for '${getFriendlyURL(this.event.request.url)}'`);
|
|
289
|
+
}
|
|
290
|
+
return possiblePreloadResponse;
|
|
291
|
+
}
|
|
292
|
+
} catch (error) {
|
|
293
|
+
if (process.env.NODE_ENV !== "production") {
|
|
294
|
+
logger.error(error);
|
|
295
|
+
}
|
|
296
|
+
return undefined;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return undefined;
|
|
300
|
+
}
|
|
301
|
+
async _ensureResponseSafeToCache(response) {
|
|
302
|
+
let responseToCache = response;
|
|
303
|
+
let pluginsUsed = false;
|
|
304
|
+
for (const callback of this.iterateCallbacks("cacheWillUpdate")){
|
|
305
|
+
responseToCache = await callback({
|
|
306
|
+
request: this.request,
|
|
307
|
+
response: responseToCache,
|
|
308
|
+
event: this.event
|
|
309
|
+
}) || undefined;
|
|
310
|
+
pluginsUsed = true;
|
|
311
|
+
if (!responseToCache) {
|
|
312
|
+
break;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (!pluginsUsed) {
|
|
316
|
+
if (responseToCache && responseToCache.status !== 200) {
|
|
317
|
+
if (process.env.NODE_ENV !== "production") {
|
|
318
|
+
if (responseToCache.status === 0) {
|
|
319
|
+
logger.warn(`The response for '${this.request.url}' is an opaque response. The caching strategy that you're using will not cache opaque responses by default.`);
|
|
320
|
+
} else {
|
|
321
|
+
logger.debug(`The response for '${this.request.url}' returned a status code of '${response.status}' and won't be cached as a result.`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
responseToCache = undefined;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return responseToCache;
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
class Strategy {
|
|
332
|
+
cacheName;
|
|
333
|
+
plugins;
|
|
334
|
+
fetchOptions;
|
|
335
|
+
matchOptions;
|
|
336
|
+
constructor(options = {}){
|
|
337
|
+
this.cacheName = cacheNames$1.getRuntimeName(options.cacheName);
|
|
338
|
+
this.plugins = options.plugins || [];
|
|
339
|
+
this.fetchOptions = options.fetchOptions;
|
|
340
|
+
this.matchOptions = options.matchOptions;
|
|
341
|
+
}
|
|
342
|
+
handle(options) {
|
|
343
|
+
const [responseDone] = this.handleAll(options);
|
|
344
|
+
return responseDone;
|
|
345
|
+
}
|
|
346
|
+
handleAll(options) {
|
|
347
|
+
if (options instanceof FetchEvent) {
|
|
348
|
+
options = {
|
|
349
|
+
event: options,
|
|
350
|
+
request: options.request
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
const event = options.event;
|
|
354
|
+
const request = typeof options.request === "string" ? new Request(options.request) : options.request;
|
|
355
|
+
const handler = new StrategyHandler(this, options.url ? {
|
|
356
|
+
event,
|
|
357
|
+
request,
|
|
358
|
+
url: options.url,
|
|
359
|
+
params: options.params
|
|
360
|
+
} : {
|
|
361
|
+
event,
|
|
362
|
+
request
|
|
363
|
+
});
|
|
364
|
+
const responseDone = this._getResponse(handler, request, event);
|
|
365
|
+
const handlerDone = this._awaitComplete(responseDone, handler, request, event);
|
|
366
|
+
return [
|
|
367
|
+
responseDone,
|
|
368
|
+
handlerDone
|
|
369
|
+
];
|
|
370
|
+
}
|
|
371
|
+
async _getResponse(handler, request, event) {
|
|
372
|
+
await handler.runCallbacks("handlerWillStart", {
|
|
373
|
+
event,
|
|
374
|
+
request
|
|
375
|
+
});
|
|
376
|
+
let response = undefined;
|
|
377
|
+
try {
|
|
378
|
+
response = await this._handle(request, handler);
|
|
379
|
+
if (response === undefined || response.type === "error") {
|
|
380
|
+
throw new SerwistError("no-response", {
|
|
381
|
+
url: request.url
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
} catch (error) {
|
|
385
|
+
if (error instanceof Error) {
|
|
386
|
+
for (const callback of handler.iterateCallbacks("handlerDidError")){
|
|
387
|
+
response = await callback({
|
|
388
|
+
error,
|
|
389
|
+
event,
|
|
390
|
+
request
|
|
391
|
+
});
|
|
392
|
+
if (response !== undefined) {
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
if (!response) {
|
|
398
|
+
throw error;
|
|
399
|
+
}
|
|
400
|
+
if (process.env.NODE_ENV !== "production") {
|
|
401
|
+
throw logger.log(`While responding to '${getFriendlyURL(request.url)}', an ${error instanceof Error ? error.toString() : ""} error occurred. Using a fallback response provided by a handlerDidError plugin.`);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
for (const callback of handler.iterateCallbacks("handlerWillRespond")){
|
|
405
|
+
response = await callback({
|
|
406
|
+
event,
|
|
407
|
+
request,
|
|
408
|
+
response
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
return response;
|
|
412
|
+
}
|
|
413
|
+
async _awaitComplete(responseDone, handler, request, event) {
|
|
414
|
+
let response = undefined;
|
|
415
|
+
let error = undefined;
|
|
416
|
+
try {
|
|
417
|
+
response = await responseDone;
|
|
418
|
+
} catch (error) {}
|
|
419
|
+
try {
|
|
420
|
+
await handler.runCallbacks("handlerDidRespond", {
|
|
421
|
+
event,
|
|
422
|
+
request,
|
|
423
|
+
response
|
|
424
|
+
});
|
|
425
|
+
await handler.doneWaiting();
|
|
426
|
+
} catch (waitUntilError) {
|
|
427
|
+
if (waitUntilError instanceof Error) {
|
|
428
|
+
error = waitUntilError;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
await handler.runCallbacks("handlerDidComplete", {
|
|
432
|
+
event,
|
|
433
|
+
request,
|
|
434
|
+
response,
|
|
435
|
+
error
|
|
436
|
+
});
|
|
437
|
+
handler.destroy();
|
|
438
|
+
if (error) {
|
|
439
|
+
throw error;
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
class PrecacheStrategy extends Strategy {
|
|
445
|
+
_fallbackToNetwork;
|
|
446
|
+
static defaultPrecacheCacheabilityPlugin = {
|
|
447
|
+
async cacheWillUpdate ({ response }) {
|
|
448
|
+
if (!response || response.status >= 400) {
|
|
449
|
+
return null;
|
|
450
|
+
}
|
|
451
|
+
return response;
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
static copyRedirectedCacheableResponsesPlugin = {
|
|
455
|
+
async cacheWillUpdate ({ response }) {
|
|
456
|
+
return response.redirected ? await copyResponse(response) : response;
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
constructor(options = {}){
|
|
460
|
+
options.cacheName = cacheNames$1.getPrecacheName(options.cacheName);
|
|
461
|
+
super(options);
|
|
462
|
+
this._fallbackToNetwork = options.fallbackToNetwork !== false;
|
|
463
|
+
this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);
|
|
464
|
+
}
|
|
465
|
+
async _handle(request, handler) {
|
|
466
|
+
const preloadResponse = await handler.getPreloadResponse();
|
|
467
|
+
if (preloadResponse) {
|
|
468
|
+
return preloadResponse;
|
|
469
|
+
}
|
|
470
|
+
const response = await handler.cacheMatch(request);
|
|
471
|
+
if (response) {
|
|
472
|
+
return response;
|
|
473
|
+
}
|
|
474
|
+
if (handler.event && handler.event.type === "install") {
|
|
475
|
+
return await this._handleInstall(request, handler);
|
|
476
|
+
}
|
|
477
|
+
return await this._handleFetch(request, handler);
|
|
478
|
+
}
|
|
479
|
+
async _handleFetch(request, handler) {
|
|
480
|
+
let response = undefined;
|
|
481
|
+
const params = handler.params || {};
|
|
482
|
+
if (this._fallbackToNetwork) {
|
|
483
|
+
if (process.env.NODE_ENV !== "production") {
|
|
484
|
+
logger.warn(`The precached response for ${getFriendlyURL(request.url)} in ${this.cacheName} was not found. Falling back to the network.`);
|
|
485
|
+
}
|
|
486
|
+
const integrityInManifest = params.integrity;
|
|
487
|
+
const integrityInRequest = request.integrity;
|
|
488
|
+
const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest;
|
|
489
|
+
response = await handler.fetch(new Request(request, {
|
|
490
|
+
integrity: request.mode !== "no-cors" ? integrityInRequest || integrityInManifest : undefined
|
|
491
|
+
}));
|
|
492
|
+
if (integrityInManifest && noIntegrityConflict && request.mode !== "no-cors") {
|
|
493
|
+
this._useDefaultCacheabilityPluginIfNeeded();
|
|
494
|
+
const wasCached = await handler.cachePut(request, response.clone());
|
|
495
|
+
if (process.env.NODE_ENV !== "production") {
|
|
496
|
+
if (wasCached) {
|
|
497
|
+
logger.log(`A response for ${getFriendlyURL(request.url)} was used to "repair" the precache.`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
} else {
|
|
502
|
+
throw new SerwistError("missing-precache-entry", {
|
|
503
|
+
cacheName: this.cacheName,
|
|
504
|
+
url: request.url
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
if (process.env.NODE_ENV !== "production") {
|
|
508
|
+
const cacheKey = params.cacheKey || await handler.getCacheKey(request, "read");
|
|
509
|
+
logger.groupCollapsed(`Precaching is responding to: ${getFriendlyURL(request.url)}`);
|
|
510
|
+
logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`);
|
|
511
|
+
logger.groupCollapsed("View request details here.");
|
|
512
|
+
logger.log(request);
|
|
513
|
+
logger.groupEnd();
|
|
514
|
+
logger.groupCollapsed("View response details here.");
|
|
515
|
+
logger.log(response);
|
|
516
|
+
logger.groupEnd();
|
|
517
|
+
logger.groupEnd();
|
|
518
|
+
}
|
|
519
|
+
return response;
|
|
520
|
+
}
|
|
521
|
+
async _handleInstall(request, handler) {
|
|
522
|
+
this._useDefaultCacheabilityPluginIfNeeded();
|
|
523
|
+
const response = await handler.fetch(request);
|
|
524
|
+
const wasCached = await handler.cachePut(request, response.clone());
|
|
525
|
+
if (!wasCached) {
|
|
526
|
+
throw new SerwistError("bad-precaching-response", {
|
|
527
|
+
url: request.url,
|
|
528
|
+
status: response.status
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
return response;
|
|
532
|
+
}
|
|
533
|
+
_useDefaultCacheabilityPluginIfNeeded() {
|
|
534
|
+
let defaultPluginIndex = null;
|
|
535
|
+
let cacheWillUpdatePluginCount = 0;
|
|
536
|
+
for (const [index, plugin] of this.plugins.entries()){
|
|
537
|
+
if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) {
|
|
538
|
+
continue;
|
|
539
|
+
}
|
|
540
|
+
if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) {
|
|
541
|
+
defaultPluginIndex = index;
|
|
542
|
+
}
|
|
543
|
+
if (plugin.cacheWillUpdate) {
|
|
544
|
+
cacheWillUpdatePluginCount++;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
if (cacheWillUpdatePluginCount === 0) {
|
|
548
|
+
this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin);
|
|
549
|
+
} else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) {
|
|
550
|
+
this.plugins.splice(defaultPluginIndex, 1);
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
const defaultMethod = "GET";
|
|
556
|
+
const validMethods = [
|
|
557
|
+
"DELETE",
|
|
558
|
+
"GET",
|
|
559
|
+
"HEAD",
|
|
560
|
+
"PATCH",
|
|
561
|
+
"POST",
|
|
562
|
+
"PUT"
|
|
563
|
+
];
|
|
564
|
+
|
|
565
|
+
const normalizeHandler = (handler)=>{
|
|
566
|
+
if (handler && typeof handler === "object") {
|
|
567
|
+
if (process.env.NODE_ENV !== "production") {
|
|
568
|
+
finalAssertExports.hasMethod(handler, "handle", {
|
|
569
|
+
moduleName: "serwist",
|
|
570
|
+
className: "Route",
|
|
571
|
+
funcName: "constructor",
|
|
572
|
+
paramName: "handler"
|
|
573
|
+
});
|
|
574
|
+
}
|
|
575
|
+
return handler;
|
|
576
|
+
}
|
|
577
|
+
if (process.env.NODE_ENV !== "production") {
|
|
578
|
+
finalAssertExports.isType(handler, "function", {
|
|
579
|
+
moduleName: "serwist",
|
|
580
|
+
className: "Route",
|
|
581
|
+
funcName: "constructor",
|
|
582
|
+
paramName: "handler"
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
return {
|
|
586
|
+
handle: handler
|
|
587
|
+
};
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
class Route {
|
|
591
|
+
handler;
|
|
592
|
+
match;
|
|
593
|
+
method;
|
|
594
|
+
catchHandler;
|
|
595
|
+
constructor(match, handler, method = defaultMethod){
|
|
596
|
+
if (process.env.NODE_ENV !== "production") {
|
|
597
|
+
finalAssertExports.isType(match, "function", {
|
|
598
|
+
moduleName: "serwist",
|
|
599
|
+
className: "Route",
|
|
600
|
+
funcName: "constructor",
|
|
601
|
+
paramName: "match"
|
|
602
|
+
});
|
|
603
|
+
if (method) {
|
|
604
|
+
finalAssertExports.isOneOf(method, validMethods, {
|
|
605
|
+
paramName: "method"
|
|
606
|
+
});
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
this.handler = normalizeHandler(handler);
|
|
610
|
+
this.match = match;
|
|
611
|
+
this.method = method;
|
|
612
|
+
}
|
|
613
|
+
setCatchHandler(handler) {
|
|
614
|
+
this.catchHandler = normalizeHandler(handler);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
const removeIgnoredSearchParams = (urlObject, ignoreURLParametersMatching = [])=>{
|
|
619
|
+
for (const paramName of [
|
|
620
|
+
...urlObject.searchParams.keys()
|
|
621
|
+
]){
|
|
622
|
+
if (ignoreURLParametersMatching.some((regExp)=>regExp.test(paramName))) {
|
|
623
|
+
urlObject.searchParams.delete(paramName);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
return urlObject;
|
|
627
|
+
};
|
|
628
|
+
|
|
629
|
+
function* generateURLVariations(url, { directoryIndex = "index.html", ignoreURLParametersMatching = [
|
|
630
|
+
/^utm_/,
|
|
631
|
+
/^fbclid$/
|
|
632
|
+
], cleanURLs = true, urlManipulation } = {}) {
|
|
633
|
+
const urlObject = new URL(url, location.href);
|
|
634
|
+
urlObject.hash = "";
|
|
635
|
+
yield urlObject.href;
|
|
636
|
+
const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching);
|
|
637
|
+
yield urlWithoutIgnoredParams.href;
|
|
638
|
+
if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith("/")) {
|
|
639
|
+
const directoryURL = new URL(urlWithoutIgnoredParams.href);
|
|
640
|
+
directoryURL.pathname += directoryIndex;
|
|
641
|
+
yield directoryURL.href;
|
|
642
|
+
}
|
|
643
|
+
if (cleanURLs) {
|
|
644
|
+
const cleanURL = new URL(urlWithoutIgnoredParams.href);
|
|
645
|
+
cleanURL.pathname += ".html";
|
|
646
|
+
yield cleanURL.href;
|
|
647
|
+
}
|
|
648
|
+
if (urlManipulation) {
|
|
649
|
+
const additionalURLs = urlManipulation({
|
|
650
|
+
url: urlObject
|
|
651
|
+
});
|
|
652
|
+
for (const urlToAttempt of additionalURLs){
|
|
653
|
+
yield urlToAttempt.href;
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
6
657
|
|
|
7
658
|
class PrecacheRoute extends Route {
|
|
8
|
-
constructor(
|
|
659
|
+
constructor(controller, options){
|
|
9
660
|
const match = ({ request })=>{
|
|
10
|
-
const urlsToCacheKeys =
|
|
661
|
+
const urlsToCacheKeys = controller.getUrlsToPrecacheKeys();
|
|
11
662
|
for (const possibleURL of generateURLVariations(request.url, options)){
|
|
12
663
|
const cacheKey = urlsToCacheKeys.get(possibleURL);
|
|
13
664
|
if (cacheKey) {
|
|
14
|
-
const integrity =
|
|
665
|
+
const integrity = controller.getIntegrityForPrecacheKey(cacheKey);
|
|
15
666
|
return {
|
|
16
667
|
cacheKey,
|
|
17
668
|
integrity
|
|
@@ -19,11 +670,789 @@ class PrecacheRoute extends Route {
|
|
|
19
670
|
}
|
|
20
671
|
}
|
|
21
672
|
if (process.env.NODE_ENV !== "production") {
|
|
22
|
-
logger.debug(`Precaching did not find a match for ${getFriendlyURL(request.url)}.`);
|
|
673
|
+
logger.debug(`Precaching did not find a match for ${getFriendlyURL(request.url)}.`);
|
|
674
|
+
}
|
|
675
|
+
return;
|
|
676
|
+
};
|
|
677
|
+
super(match, controller.strategy);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
class PrecacheFallbackPlugin {
|
|
682
|
+
_fallbackUrls;
|
|
683
|
+
_precacheController;
|
|
684
|
+
constructor({ fallbackUrls, precacheController }){
|
|
685
|
+
this._fallbackUrls = fallbackUrls;
|
|
686
|
+
this._precacheController = precacheController;
|
|
687
|
+
}
|
|
688
|
+
async handlerDidError(param) {
|
|
689
|
+
for (const fallback of this._fallbackUrls){
|
|
690
|
+
if (typeof fallback === "string") {
|
|
691
|
+
const fallbackResponse = await this._precacheController.matchPrecache(fallback);
|
|
692
|
+
if (fallbackResponse !== undefined) {
|
|
693
|
+
return fallbackResponse;
|
|
694
|
+
}
|
|
695
|
+
} else if (fallback.matcher(param)) {
|
|
696
|
+
const fallbackResponse = await this._precacheController.matchPrecache(fallback.url);
|
|
697
|
+
if (fallbackResponse !== undefined) {
|
|
698
|
+
return fallbackResponse;
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
return undefined;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
class RuntimeCacheController {
|
|
707
|
+
_entries;
|
|
708
|
+
_options;
|
|
709
|
+
constructor(entries, options = {}){
|
|
710
|
+
this._entries = entries;
|
|
711
|
+
this._options = options;
|
|
712
|
+
this.init = this.init.bind(this);
|
|
713
|
+
this.install = this.install.bind(this);
|
|
714
|
+
}
|
|
715
|
+
init(serwist) {
|
|
716
|
+
if (this._options.fallbacks !== undefined) {
|
|
717
|
+
const fallbackPlugin = new PrecacheFallbackPlugin({
|
|
718
|
+
fallbackUrls: this._options.fallbacks.entries,
|
|
719
|
+
precacheController: serwist.precache
|
|
720
|
+
});
|
|
721
|
+
this._entries.forEach((cacheEntry)=>{
|
|
722
|
+
if (cacheEntry.handler instanceof Strategy && !cacheEntry.handler.plugins.some((plugin)=>"handlerDidError" in plugin)) {
|
|
723
|
+
cacheEntry.handler.plugins.push(fallbackPlugin);
|
|
724
|
+
}
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
for (const entry of this._entries){
|
|
728
|
+
serwist.registerCapture(entry.matcher, entry.handler, entry.method);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
async install(event, serwist) {
|
|
732
|
+
const concurrency = this._options.warmOptions?.concurrency ?? 10;
|
|
733
|
+
if (this._options.warmEntries) {
|
|
734
|
+
await parallel(concurrency, this._options.warmEntries, async (entry)=>{
|
|
735
|
+
const request = entry instanceof Request ? entry : new Request(typeof entry === "string" ? entry : entry.url, {
|
|
736
|
+
integrity: typeof entry !== "string" ? entry.integrity : undefined,
|
|
737
|
+
credentials: "same-origin"
|
|
738
|
+
});
|
|
739
|
+
await serwist.handleRequest({
|
|
740
|
+
request,
|
|
741
|
+
event
|
|
742
|
+
});
|
|
743
|
+
});
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
warmRuntimeCache(entries) {
|
|
747
|
+
if (!this._options.warmEntries) this._options.warmEntries = [];
|
|
748
|
+
this._options.warmEntries.push(...entries);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
class NavigationRoute extends Route {
|
|
753
|
+
_allowlist;
|
|
754
|
+
_denylist;
|
|
755
|
+
constructor(handler, { allowlist = [
|
|
756
|
+
/./
|
|
757
|
+
], denylist = [] } = {}){
|
|
758
|
+
if (process.env.NODE_ENV !== "production") {
|
|
759
|
+
finalAssertExports.isArrayOfClass(allowlist, RegExp, {
|
|
760
|
+
moduleName: "serwist",
|
|
761
|
+
className: "NavigationRoute",
|
|
762
|
+
funcName: "constructor",
|
|
763
|
+
paramName: "options.allowlist"
|
|
764
|
+
});
|
|
765
|
+
finalAssertExports.isArrayOfClass(denylist, RegExp, {
|
|
766
|
+
moduleName: "serwist",
|
|
767
|
+
className: "NavigationRoute",
|
|
768
|
+
funcName: "constructor",
|
|
769
|
+
paramName: "options.denylist"
|
|
770
|
+
});
|
|
771
|
+
}
|
|
772
|
+
super((options)=>this._match(options), handler);
|
|
773
|
+
this._allowlist = allowlist;
|
|
774
|
+
this._denylist = denylist;
|
|
775
|
+
}
|
|
776
|
+
_match({ url, request }) {
|
|
777
|
+
if (request && request.mode !== "navigate") {
|
|
778
|
+
return false;
|
|
779
|
+
}
|
|
780
|
+
const pathnameAndSearch = url.pathname + url.search;
|
|
781
|
+
for (const regExp of this._denylist){
|
|
782
|
+
if (regExp.test(pathnameAndSearch)) {
|
|
783
|
+
if (process.env.NODE_ENV !== "production") {
|
|
784
|
+
logger.log(`The navigation route ${pathnameAndSearch} is not being used, since the URL matches this denylist pattern: ${regExp.toString()}`);
|
|
785
|
+
}
|
|
786
|
+
return false;
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
if (this._allowlist.some((regExp)=>regExp.test(pathnameAndSearch))) {
|
|
790
|
+
if (process.env.NODE_ENV !== "production") {
|
|
791
|
+
logger.debug(`The navigation route ${pathnameAndSearch} is being used.`);
|
|
792
|
+
}
|
|
793
|
+
return true;
|
|
794
|
+
}
|
|
795
|
+
if (process.env.NODE_ENV !== "production") {
|
|
796
|
+
logger.log(`The navigation route ${pathnameAndSearch} is not being used, since the URL being navigated to doesn't match the allowlist.`);
|
|
797
|
+
}
|
|
798
|
+
return false;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
class RegExpRoute extends Route {
|
|
803
|
+
constructor(regExp, handler, method){
|
|
804
|
+
if (process.env.NODE_ENV !== "production") {
|
|
805
|
+
finalAssertExports.isInstance(regExp, RegExp, {
|
|
806
|
+
moduleName: "serwist",
|
|
807
|
+
className: "RegExpRoute",
|
|
808
|
+
funcName: "constructor",
|
|
809
|
+
paramName: "pattern"
|
|
810
|
+
});
|
|
811
|
+
}
|
|
812
|
+
const match = ({ url })=>{
|
|
813
|
+
const result = regExp.exec(url.href);
|
|
814
|
+
if (!result) {
|
|
815
|
+
return;
|
|
816
|
+
}
|
|
817
|
+
if (url.origin !== location.origin && result.index !== 0) {
|
|
818
|
+
if (process.env.NODE_ENV !== "production") {
|
|
819
|
+
logger.debug(`The regular expression '${regExp.toString()}' only partially matched against the cross-origin URL '${url.toString()}'. RegExpRoute's will only handle cross-origin requests if they match the entire URL.`);
|
|
820
|
+
}
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
return result.slice(1);
|
|
824
|
+
};
|
|
825
|
+
super(match, handler, method);
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
const disableDevLogs = ()=>{
|
|
830
|
+
self.__WB_DISABLE_DEV_LOGS = true;
|
|
831
|
+
};
|
|
832
|
+
|
|
833
|
+
const cacheOkAndOpaquePlugin = {
|
|
834
|
+
cacheWillUpdate: async ({ response })=>{
|
|
835
|
+
if (response.status === 200 || response.status === 0) {
|
|
836
|
+
return response;
|
|
837
|
+
}
|
|
838
|
+
return null;
|
|
839
|
+
}
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
const messages = {
|
|
843
|
+
strategyStart: (strategyName, request)=>`Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`,
|
|
844
|
+
printFinalResponse: (response)=>{
|
|
845
|
+
if (response) {
|
|
846
|
+
logger.groupCollapsed("View the final response here.");
|
|
847
|
+
logger.log(response || "[No response returned]");
|
|
848
|
+
logger.groupEnd();
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
class NetworkFirst extends Strategy {
|
|
854
|
+
_networkTimeoutSeconds;
|
|
855
|
+
constructor(options = {}){
|
|
856
|
+
super(options);
|
|
857
|
+
if (!this.plugins.some((p)=>"cacheWillUpdate" in p)) {
|
|
858
|
+
this.plugins.unshift(cacheOkAndOpaquePlugin);
|
|
859
|
+
}
|
|
860
|
+
this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
|
|
861
|
+
if (process.env.NODE_ENV !== "production") {
|
|
862
|
+
if (this._networkTimeoutSeconds) {
|
|
863
|
+
finalAssertExports.isType(this._networkTimeoutSeconds, "number", {
|
|
864
|
+
moduleName: "serwist",
|
|
865
|
+
className: this.constructor.name,
|
|
866
|
+
funcName: "constructor",
|
|
867
|
+
paramName: "networkTimeoutSeconds"
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
async _handle(request, handler) {
|
|
873
|
+
const logs = [];
|
|
874
|
+
if (process.env.NODE_ENV !== "production") {
|
|
875
|
+
finalAssertExports.isInstance(request, Request, {
|
|
876
|
+
moduleName: "serwist",
|
|
877
|
+
className: this.constructor.name,
|
|
878
|
+
funcName: "handle",
|
|
879
|
+
paramName: "makeRequest"
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
const promises = [];
|
|
883
|
+
let timeoutId;
|
|
884
|
+
if (this._networkTimeoutSeconds) {
|
|
885
|
+
const { id, promise } = this._getTimeoutPromise({
|
|
886
|
+
request,
|
|
887
|
+
logs,
|
|
888
|
+
handler
|
|
889
|
+
});
|
|
890
|
+
timeoutId = id;
|
|
891
|
+
promises.push(promise);
|
|
892
|
+
}
|
|
893
|
+
const networkPromise = this._getNetworkPromise({
|
|
894
|
+
timeoutId,
|
|
895
|
+
request,
|
|
896
|
+
logs,
|
|
897
|
+
handler
|
|
898
|
+
});
|
|
899
|
+
promises.push(networkPromise);
|
|
900
|
+
const response = await handler.waitUntil((async ()=>{
|
|
901
|
+
return await handler.waitUntil(Promise.race(promises)) || await networkPromise;
|
|
902
|
+
})());
|
|
903
|
+
if (process.env.NODE_ENV !== "production") {
|
|
904
|
+
logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
|
|
905
|
+
for (const log of logs){
|
|
906
|
+
logger.log(log);
|
|
907
|
+
}
|
|
908
|
+
messages.printFinalResponse(response);
|
|
909
|
+
logger.groupEnd();
|
|
910
|
+
}
|
|
911
|
+
if (!response) {
|
|
912
|
+
throw new SerwistError("no-response", {
|
|
913
|
+
url: request.url
|
|
914
|
+
});
|
|
915
|
+
}
|
|
916
|
+
return response;
|
|
917
|
+
}
|
|
918
|
+
_getTimeoutPromise({ request, logs, handler }) {
|
|
919
|
+
let timeoutId;
|
|
920
|
+
const timeoutPromise = new Promise((resolve)=>{
|
|
921
|
+
const onNetworkTimeout = async ()=>{
|
|
922
|
+
if (process.env.NODE_ENV !== "production") {
|
|
923
|
+
logs.push(`Timing out the network response at ${this._networkTimeoutSeconds} seconds.`);
|
|
924
|
+
}
|
|
925
|
+
resolve(await handler.cacheMatch(request));
|
|
926
|
+
};
|
|
927
|
+
timeoutId = setTimeout(onNetworkTimeout, this._networkTimeoutSeconds * 1000);
|
|
928
|
+
});
|
|
929
|
+
return {
|
|
930
|
+
promise: timeoutPromise,
|
|
931
|
+
id: timeoutId
|
|
932
|
+
};
|
|
933
|
+
}
|
|
934
|
+
async _getNetworkPromise({ timeoutId, request, logs, handler }) {
|
|
935
|
+
let error = undefined;
|
|
936
|
+
let response = undefined;
|
|
937
|
+
try {
|
|
938
|
+
response = await handler.fetchAndCachePut(request);
|
|
939
|
+
} catch (fetchError) {
|
|
940
|
+
if (fetchError instanceof Error) {
|
|
941
|
+
error = fetchError;
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
if (timeoutId) {
|
|
945
|
+
clearTimeout(timeoutId);
|
|
946
|
+
}
|
|
947
|
+
if (process.env.NODE_ENV !== "production") {
|
|
948
|
+
if (response) {
|
|
949
|
+
logs.push("Got response from network.");
|
|
950
|
+
} else {
|
|
951
|
+
logs.push("Unable to get a response from the network. Will respond " + "with a cached response.");
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
if (error || !response) {
|
|
955
|
+
response = await handler.cacheMatch(request);
|
|
956
|
+
if (process.env.NODE_ENV !== "production") {
|
|
957
|
+
if (response) {
|
|
958
|
+
logs.push(`Found a cached response in the '${this.cacheName}' cache.`);
|
|
959
|
+
} else {
|
|
960
|
+
logs.push(`No response found in the '${this.cacheName}' cache.`);
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
return response;
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
class NetworkOnly extends Strategy {
|
|
969
|
+
_networkTimeoutSeconds;
|
|
970
|
+
constructor(options = {}){
|
|
971
|
+
super(options);
|
|
972
|
+
this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
|
|
973
|
+
}
|
|
974
|
+
async _handle(request, handler) {
|
|
975
|
+
if (process.env.NODE_ENV !== "production") {
|
|
976
|
+
finalAssertExports.isInstance(request, Request, {
|
|
977
|
+
moduleName: "serwist",
|
|
978
|
+
className: this.constructor.name,
|
|
979
|
+
funcName: "_handle",
|
|
980
|
+
paramName: "request"
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
let error = undefined;
|
|
984
|
+
let response;
|
|
985
|
+
try {
|
|
986
|
+
const promises = [
|
|
987
|
+
handler.fetch(request)
|
|
988
|
+
];
|
|
989
|
+
if (this._networkTimeoutSeconds) {
|
|
990
|
+
const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000);
|
|
991
|
+
promises.push(timeoutPromise);
|
|
992
|
+
}
|
|
993
|
+
response = await Promise.race(promises);
|
|
994
|
+
if (!response) {
|
|
995
|
+
throw new Error(`Timed out the network response after ${this._networkTimeoutSeconds} seconds.`);
|
|
996
|
+
}
|
|
997
|
+
} catch (err) {
|
|
998
|
+
if (err instanceof Error) {
|
|
999
|
+
error = err;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1003
|
+
logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
|
|
1004
|
+
if (response) {
|
|
1005
|
+
logger.log("Got response from network.");
|
|
1006
|
+
} else {
|
|
1007
|
+
logger.log("Unable to get a response from the network.");
|
|
1008
|
+
}
|
|
1009
|
+
messages.printFinalResponse(response);
|
|
1010
|
+
logger.groupEnd();
|
|
1011
|
+
}
|
|
1012
|
+
if (!response) {
|
|
1013
|
+
throw new SerwistError("no-response", {
|
|
1014
|
+
url: request.url,
|
|
1015
|
+
error
|
|
1016
|
+
});
|
|
1017
|
+
}
|
|
1018
|
+
return response;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
const BACKGROUND_SYNC_DB_VERSION = 3;
|
|
1023
|
+
const BACKGROUND_SYNC_DB_NAME = "serwist-background-sync";
|
|
1024
|
+
const REQUEST_OBJECT_STORE_NAME = "requests";
|
|
1025
|
+
const QUEUE_NAME_INDEX = "queueName";
|
|
1026
|
+
class BackgroundSyncQueueDb {
|
|
1027
|
+
_db = null;
|
|
1028
|
+
async addEntry(entry) {
|
|
1029
|
+
const db = await this.getDb();
|
|
1030
|
+
const tx = db.transaction(REQUEST_OBJECT_STORE_NAME, "readwrite", {
|
|
1031
|
+
durability: "relaxed"
|
|
1032
|
+
});
|
|
1033
|
+
await tx.store.add(entry);
|
|
1034
|
+
await tx.done;
|
|
1035
|
+
}
|
|
1036
|
+
async getFirstEntryId() {
|
|
1037
|
+
const db = await this.getDb();
|
|
1038
|
+
const cursor = await db.transaction(REQUEST_OBJECT_STORE_NAME).store.openCursor();
|
|
1039
|
+
return cursor?.value.id;
|
|
1040
|
+
}
|
|
1041
|
+
async getAllEntriesByQueueName(queueName) {
|
|
1042
|
+
const db = await this.getDb();
|
|
1043
|
+
const results = await db.getAllFromIndex(REQUEST_OBJECT_STORE_NAME, QUEUE_NAME_INDEX, IDBKeyRange.only(queueName));
|
|
1044
|
+
return results ? results : new Array();
|
|
1045
|
+
}
|
|
1046
|
+
async getEntryCountByQueueName(queueName) {
|
|
1047
|
+
const db = await this.getDb();
|
|
1048
|
+
return db.countFromIndex(REQUEST_OBJECT_STORE_NAME, QUEUE_NAME_INDEX, IDBKeyRange.only(queueName));
|
|
1049
|
+
}
|
|
1050
|
+
async deleteEntry(id) {
|
|
1051
|
+
const db = await this.getDb();
|
|
1052
|
+
await db.delete(REQUEST_OBJECT_STORE_NAME, id);
|
|
1053
|
+
}
|
|
1054
|
+
async getFirstEntryByQueueName(queueName) {
|
|
1055
|
+
return await this.getEndEntryFromIndex(IDBKeyRange.only(queueName), "next");
|
|
1056
|
+
}
|
|
1057
|
+
async getLastEntryByQueueName(queueName) {
|
|
1058
|
+
return await this.getEndEntryFromIndex(IDBKeyRange.only(queueName), "prev");
|
|
1059
|
+
}
|
|
1060
|
+
async getEndEntryFromIndex(query, direction) {
|
|
1061
|
+
const db = await this.getDb();
|
|
1062
|
+
const cursor = await db.transaction(REQUEST_OBJECT_STORE_NAME).store.index(QUEUE_NAME_INDEX).openCursor(query, direction);
|
|
1063
|
+
return cursor?.value;
|
|
1064
|
+
}
|
|
1065
|
+
async getDb() {
|
|
1066
|
+
if (!this._db) {
|
|
1067
|
+
this._db = await openDB(BACKGROUND_SYNC_DB_NAME, BACKGROUND_SYNC_DB_VERSION, {
|
|
1068
|
+
upgrade: this._upgradeDb
|
|
1069
|
+
});
|
|
1070
|
+
}
|
|
1071
|
+
return this._db;
|
|
1072
|
+
}
|
|
1073
|
+
_upgradeDb(db, oldVersion) {
|
|
1074
|
+
if (oldVersion > 0 && oldVersion < BACKGROUND_SYNC_DB_VERSION) {
|
|
1075
|
+
if (db.objectStoreNames.contains(REQUEST_OBJECT_STORE_NAME)) {
|
|
1076
|
+
db.deleteObjectStore(REQUEST_OBJECT_STORE_NAME);
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
const objStore = db.createObjectStore(REQUEST_OBJECT_STORE_NAME, {
|
|
1080
|
+
autoIncrement: true,
|
|
1081
|
+
keyPath: "id"
|
|
1082
|
+
});
|
|
1083
|
+
objStore.createIndex(QUEUE_NAME_INDEX, QUEUE_NAME_INDEX, {
|
|
1084
|
+
unique: false
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
class BackgroundSyncQueueStore {
|
|
1090
|
+
_queueName;
|
|
1091
|
+
_queueDb;
|
|
1092
|
+
constructor(queueName){
|
|
1093
|
+
this._queueName = queueName;
|
|
1094
|
+
this._queueDb = new BackgroundSyncQueueDb();
|
|
1095
|
+
}
|
|
1096
|
+
async pushEntry(entry) {
|
|
1097
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1098
|
+
finalAssertExports.isType(entry, "object", {
|
|
1099
|
+
moduleName: "serwist",
|
|
1100
|
+
className: "BackgroundSyncQueueStore",
|
|
1101
|
+
funcName: "pushEntry",
|
|
1102
|
+
paramName: "entry"
|
|
1103
|
+
});
|
|
1104
|
+
finalAssertExports.isType(entry.requestData, "object", {
|
|
1105
|
+
moduleName: "serwist",
|
|
1106
|
+
className: "BackgroundSyncQueueStore",
|
|
1107
|
+
funcName: "pushEntry",
|
|
1108
|
+
paramName: "entry.requestData"
|
|
1109
|
+
});
|
|
1110
|
+
}
|
|
1111
|
+
delete entry.id;
|
|
1112
|
+
entry.queueName = this._queueName;
|
|
1113
|
+
await this._queueDb.addEntry(entry);
|
|
1114
|
+
}
|
|
1115
|
+
async unshiftEntry(entry) {
|
|
1116
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1117
|
+
finalAssertExports.isType(entry, "object", {
|
|
1118
|
+
moduleName: "serwist",
|
|
1119
|
+
className: "BackgroundSyncQueueStore",
|
|
1120
|
+
funcName: "unshiftEntry",
|
|
1121
|
+
paramName: "entry"
|
|
1122
|
+
});
|
|
1123
|
+
finalAssertExports.isType(entry.requestData, "object", {
|
|
1124
|
+
moduleName: "serwist",
|
|
1125
|
+
className: "BackgroundSyncQueueStore",
|
|
1126
|
+
funcName: "unshiftEntry",
|
|
1127
|
+
paramName: "entry.requestData"
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
const firstId = await this._queueDb.getFirstEntryId();
|
|
1131
|
+
if (firstId) {
|
|
1132
|
+
entry.id = firstId - 1;
|
|
1133
|
+
} else {
|
|
1134
|
+
delete entry.id;
|
|
1135
|
+
}
|
|
1136
|
+
entry.queueName = this._queueName;
|
|
1137
|
+
await this._queueDb.addEntry(entry);
|
|
1138
|
+
}
|
|
1139
|
+
async popEntry() {
|
|
1140
|
+
return this._removeEntry(await this._queueDb.getLastEntryByQueueName(this._queueName));
|
|
1141
|
+
}
|
|
1142
|
+
async shiftEntry() {
|
|
1143
|
+
return this._removeEntry(await this._queueDb.getFirstEntryByQueueName(this._queueName));
|
|
1144
|
+
}
|
|
1145
|
+
async getAll() {
|
|
1146
|
+
return await this._queueDb.getAllEntriesByQueueName(this._queueName);
|
|
1147
|
+
}
|
|
1148
|
+
async size() {
|
|
1149
|
+
return await this._queueDb.getEntryCountByQueueName(this._queueName);
|
|
1150
|
+
}
|
|
1151
|
+
async deleteEntry(id) {
|
|
1152
|
+
await this._queueDb.deleteEntry(id);
|
|
1153
|
+
}
|
|
1154
|
+
async _removeEntry(entry) {
|
|
1155
|
+
if (entry) {
|
|
1156
|
+
await this.deleteEntry(entry.id);
|
|
1157
|
+
}
|
|
1158
|
+
return entry;
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
const serializableProperties = [
|
|
1163
|
+
"method",
|
|
1164
|
+
"referrer",
|
|
1165
|
+
"referrerPolicy",
|
|
1166
|
+
"mode",
|
|
1167
|
+
"credentials",
|
|
1168
|
+
"cache",
|
|
1169
|
+
"redirect",
|
|
1170
|
+
"integrity",
|
|
1171
|
+
"keepalive"
|
|
1172
|
+
];
|
|
1173
|
+
class StorableRequest {
|
|
1174
|
+
_requestData;
|
|
1175
|
+
static async fromRequest(request) {
|
|
1176
|
+
const requestData = {
|
|
1177
|
+
url: request.url,
|
|
1178
|
+
headers: {}
|
|
1179
|
+
};
|
|
1180
|
+
if (request.method !== "GET") {
|
|
1181
|
+
requestData.body = await request.clone().arrayBuffer();
|
|
1182
|
+
}
|
|
1183
|
+
request.headers.forEach((value, key)=>{
|
|
1184
|
+
requestData.headers[key] = value;
|
|
1185
|
+
});
|
|
1186
|
+
for (const prop of serializableProperties){
|
|
1187
|
+
if (request[prop] !== undefined) {
|
|
1188
|
+
requestData[prop] = request[prop];
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
return new StorableRequest(requestData);
|
|
1192
|
+
}
|
|
1193
|
+
constructor(requestData){
|
|
1194
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1195
|
+
finalAssertExports.isType(requestData, "object", {
|
|
1196
|
+
moduleName: "serwist",
|
|
1197
|
+
className: "StorableRequest",
|
|
1198
|
+
funcName: "constructor",
|
|
1199
|
+
paramName: "requestData"
|
|
1200
|
+
});
|
|
1201
|
+
finalAssertExports.isType(requestData.url, "string", {
|
|
1202
|
+
moduleName: "serwist",
|
|
1203
|
+
className: "StorableRequest",
|
|
1204
|
+
funcName: "constructor",
|
|
1205
|
+
paramName: "requestData.url"
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
if (requestData.mode === "navigate") {
|
|
1209
|
+
requestData.mode = "same-origin";
|
|
1210
|
+
}
|
|
1211
|
+
this._requestData = requestData;
|
|
1212
|
+
}
|
|
1213
|
+
toObject() {
|
|
1214
|
+
const requestData = Object.assign({}, this._requestData);
|
|
1215
|
+
requestData.headers = Object.assign({}, this._requestData.headers);
|
|
1216
|
+
if (requestData.body) {
|
|
1217
|
+
requestData.body = requestData.body.slice(0);
|
|
1218
|
+
}
|
|
1219
|
+
return requestData;
|
|
1220
|
+
}
|
|
1221
|
+
toRequest() {
|
|
1222
|
+
return new Request(this._requestData.url, this._requestData);
|
|
1223
|
+
}
|
|
1224
|
+
clone() {
|
|
1225
|
+
return new StorableRequest(this.toObject());
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
const TAG_PREFIX = "serwist-background-sync";
|
|
1230
|
+
const MAX_RETENTION_TIME$1 = 60 * 24 * 7;
|
|
1231
|
+
const queueNames = new Set();
|
|
1232
|
+
const convertEntry = (queueStoreEntry)=>{
|
|
1233
|
+
const queueEntry = {
|
|
1234
|
+
request: new StorableRequest(queueStoreEntry.requestData).toRequest(),
|
|
1235
|
+
timestamp: queueStoreEntry.timestamp
|
|
1236
|
+
};
|
|
1237
|
+
if (queueStoreEntry.metadata) {
|
|
1238
|
+
queueEntry.metadata = queueStoreEntry.metadata;
|
|
1239
|
+
}
|
|
1240
|
+
return queueEntry;
|
|
1241
|
+
};
|
|
1242
|
+
class BackgroundSyncQueue {
|
|
1243
|
+
_name;
|
|
1244
|
+
_onSync;
|
|
1245
|
+
_maxRetentionTime;
|
|
1246
|
+
_queueStore;
|
|
1247
|
+
_forceSyncFallback;
|
|
1248
|
+
_syncInProgress = false;
|
|
1249
|
+
_requestsAddedDuringSync = false;
|
|
1250
|
+
constructor(name, { forceSyncFallback, onSync, maxRetentionTime } = {}){
|
|
1251
|
+
if (queueNames.has(name)) {
|
|
1252
|
+
throw new SerwistError("duplicate-queue-name", {
|
|
1253
|
+
name
|
|
1254
|
+
});
|
|
1255
|
+
}
|
|
1256
|
+
queueNames.add(name);
|
|
1257
|
+
this._name = name;
|
|
1258
|
+
this._onSync = onSync || this.replayRequests;
|
|
1259
|
+
this._maxRetentionTime = maxRetentionTime || MAX_RETENTION_TIME$1;
|
|
1260
|
+
this._forceSyncFallback = Boolean(forceSyncFallback);
|
|
1261
|
+
this._queueStore = new BackgroundSyncQueueStore(this._name);
|
|
1262
|
+
this._addSyncListener();
|
|
1263
|
+
}
|
|
1264
|
+
get name() {
|
|
1265
|
+
return this._name;
|
|
1266
|
+
}
|
|
1267
|
+
async pushRequest(entry) {
|
|
1268
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1269
|
+
finalAssertExports.isType(entry, "object", {
|
|
1270
|
+
moduleName: "serwist",
|
|
1271
|
+
className: "BackgroundSyncQueue",
|
|
1272
|
+
funcName: "pushRequest",
|
|
1273
|
+
paramName: "entry"
|
|
1274
|
+
});
|
|
1275
|
+
finalAssertExports.isInstance(entry.request, Request, {
|
|
1276
|
+
moduleName: "serwist",
|
|
1277
|
+
className: "BackgroundSyncQueue",
|
|
1278
|
+
funcName: "pushRequest",
|
|
1279
|
+
paramName: "entry.request"
|
|
1280
|
+
});
|
|
1281
|
+
}
|
|
1282
|
+
await this._addRequest(entry, "push");
|
|
1283
|
+
}
|
|
1284
|
+
async unshiftRequest(entry) {
|
|
1285
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1286
|
+
finalAssertExports.isType(entry, "object", {
|
|
1287
|
+
moduleName: "serwist",
|
|
1288
|
+
className: "BackgroundSyncQueue",
|
|
1289
|
+
funcName: "unshiftRequest",
|
|
1290
|
+
paramName: "entry"
|
|
1291
|
+
});
|
|
1292
|
+
finalAssertExports.isInstance(entry.request, Request, {
|
|
1293
|
+
moduleName: "serwist",
|
|
1294
|
+
className: "BackgroundSyncQueue",
|
|
1295
|
+
funcName: "unshiftRequest",
|
|
1296
|
+
paramName: "entry.request"
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
await this._addRequest(entry, "unshift");
|
|
1300
|
+
}
|
|
1301
|
+
async popRequest() {
|
|
1302
|
+
return this._removeRequest("pop");
|
|
1303
|
+
}
|
|
1304
|
+
async shiftRequest() {
|
|
1305
|
+
return this._removeRequest("shift");
|
|
1306
|
+
}
|
|
1307
|
+
async getAll() {
|
|
1308
|
+
const allEntries = await this._queueStore.getAll();
|
|
1309
|
+
const now = Date.now();
|
|
1310
|
+
const unexpiredEntries = [];
|
|
1311
|
+
for (const entry of allEntries){
|
|
1312
|
+
const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000;
|
|
1313
|
+
if (now - entry.timestamp > maxRetentionTimeInMs) {
|
|
1314
|
+
await this._queueStore.deleteEntry(entry.id);
|
|
1315
|
+
} else {
|
|
1316
|
+
unexpiredEntries.push(convertEntry(entry));
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
return unexpiredEntries;
|
|
1320
|
+
}
|
|
1321
|
+
async size() {
|
|
1322
|
+
return await this._queueStore.size();
|
|
1323
|
+
}
|
|
1324
|
+
async _addRequest({ request, metadata, timestamp = Date.now() }, operation) {
|
|
1325
|
+
const storableRequest = await StorableRequest.fromRequest(request.clone());
|
|
1326
|
+
const entry = {
|
|
1327
|
+
requestData: storableRequest.toObject(),
|
|
1328
|
+
timestamp
|
|
1329
|
+
};
|
|
1330
|
+
if (metadata) {
|
|
1331
|
+
entry.metadata = metadata;
|
|
1332
|
+
}
|
|
1333
|
+
switch(operation){
|
|
1334
|
+
case "push":
|
|
1335
|
+
await this._queueStore.pushEntry(entry);
|
|
1336
|
+
break;
|
|
1337
|
+
case "unshift":
|
|
1338
|
+
await this._queueStore.unshiftEntry(entry);
|
|
1339
|
+
break;
|
|
1340
|
+
}
|
|
1341
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1342
|
+
logger.log(`Request for '${getFriendlyURL(request.url)}' has ` + `been added to background sync queue '${this._name}'.`);
|
|
1343
|
+
}
|
|
1344
|
+
if (this._syncInProgress) {
|
|
1345
|
+
this._requestsAddedDuringSync = true;
|
|
1346
|
+
} else {
|
|
1347
|
+
await this.registerSync();
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
async _removeRequest(operation) {
|
|
1351
|
+
const now = Date.now();
|
|
1352
|
+
let entry;
|
|
1353
|
+
switch(operation){
|
|
1354
|
+
case "pop":
|
|
1355
|
+
entry = await this._queueStore.popEntry();
|
|
1356
|
+
break;
|
|
1357
|
+
case "shift":
|
|
1358
|
+
entry = await this._queueStore.shiftEntry();
|
|
1359
|
+
break;
|
|
1360
|
+
}
|
|
1361
|
+
if (entry) {
|
|
1362
|
+
const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000;
|
|
1363
|
+
if (now - entry.timestamp > maxRetentionTimeInMs) {
|
|
1364
|
+
return this._removeRequest(operation);
|
|
1365
|
+
}
|
|
1366
|
+
return convertEntry(entry);
|
|
1367
|
+
}
|
|
1368
|
+
return undefined;
|
|
1369
|
+
}
|
|
1370
|
+
async replayRequests() {
|
|
1371
|
+
let entry = undefined;
|
|
1372
|
+
while(entry = await this.shiftRequest()){
|
|
1373
|
+
try {
|
|
1374
|
+
await fetch(entry.request.clone());
|
|
1375
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1376
|
+
logger.log(`Request for '${getFriendlyURL(entry.request.url)}' ` + `has been replayed in queue '${this._name}'`);
|
|
1377
|
+
}
|
|
1378
|
+
} catch (error) {
|
|
1379
|
+
await this.unshiftRequest(entry);
|
|
1380
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1381
|
+
logger.log(`Request for '${getFriendlyURL(entry.request.url)}' ` + `failed to replay, putting it back in queue '${this._name}'`);
|
|
1382
|
+
}
|
|
1383
|
+
throw new SerwistError("queue-replay-failed", {
|
|
1384
|
+
name: this._name
|
|
1385
|
+
});
|
|
1386
|
+
}
|
|
1387
|
+
}
|
|
1388
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1389
|
+
logger.log(`All requests in queue '${this.name}' have successfully replayed; the queue is now empty!`);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
async registerSync() {
|
|
1393
|
+
if ("sync" in self.registration && !this._forceSyncFallback) {
|
|
1394
|
+
try {
|
|
1395
|
+
await self.registration.sync.register(`${TAG_PREFIX}:${this._name}`);
|
|
1396
|
+
} catch (err) {
|
|
1397
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1398
|
+
logger.warn(`Unable to register sync event for '${this._name}'.`, err);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
_addSyncListener() {
|
|
1404
|
+
if ("sync" in self.registration && !this._forceSyncFallback) {
|
|
1405
|
+
self.addEventListener("sync", (event)=>{
|
|
1406
|
+
if (event.tag === `${TAG_PREFIX}:${this._name}`) {
|
|
1407
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1408
|
+
logger.log(`Background sync for tag '${event.tag}' has been received`);
|
|
1409
|
+
}
|
|
1410
|
+
const syncComplete = async ()=>{
|
|
1411
|
+
this._syncInProgress = true;
|
|
1412
|
+
let syncError = undefined;
|
|
1413
|
+
try {
|
|
1414
|
+
await this._onSync({
|
|
1415
|
+
queue: this
|
|
1416
|
+
});
|
|
1417
|
+
} catch (error) {
|
|
1418
|
+
if (error instanceof Error) {
|
|
1419
|
+
syncError = error;
|
|
1420
|
+
throw syncError;
|
|
1421
|
+
}
|
|
1422
|
+
} finally{
|
|
1423
|
+
if (this._requestsAddedDuringSync && !(syncError && !event.lastChance)) {
|
|
1424
|
+
await this.registerSync();
|
|
1425
|
+
}
|
|
1426
|
+
this._syncInProgress = false;
|
|
1427
|
+
this._requestsAddedDuringSync = false;
|
|
1428
|
+
}
|
|
1429
|
+
};
|
|
1430
|
+
event.waitUntil(syncComplete());
|
|
1431
|
+
}
|
|
1432
|
+
});
|
|
1433
|
+
} else {
|
|
1434
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1435
|
+
logger.log("Background sync replaying without background sync event");
|
|
23
1436
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
1437
|
+
void this._onSync({
|
|
1438
|
+
queue: this
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
static get _queueNames() {
|
|
1443
|
+
return queueNames;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
class BackgroundSyncPlugin {
|
|
1448
|
+
_queue;
|
|
1449
|
+
constructor(name, options){
|
|
1450
|
+
this._queue = new BackgroundSyncQueue(name, options);
|
|
1451
|
+
}
|
|
1452
|
+
async fetchDidFail({ request }) {
|
|
1453
|
+
await this._queue.pushRequest({
|
|
1454
|
+
request
|
|
1455
|
+
});
|
|
27
1456
|
}
|
|
28
1457
|
}
|
|
29
1458
|
|
|
@@ -131,30 +1560,216 @@ const initializeGoogleAnalytics = ({ serwist, cacheName, ...options })=>{
|
|
|
131
1560
|
}
|
|
132
1561
|
};
|
|
133
1562
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
1563
|
+
const isNavigationPreloadSupported = ()=>{
|
|
1564
|
+
return Boolean(self.registration?.navigationPreload);
|
|
1565
|
+
};
|
|
1566
|
+
const enableNavigationPreload = (headerValue)=>{
|
|
1567
|
+
if (isNavigationPreloadSupported()) {
|
|
1568
|
+
self.addEventListener("activate", (event)=>{
|
|
1569
|
+
event.waitUntil(self.registration.navigationPreload.enable().then(()=>{
|
|
1570
|
+
if (headerValue) {
|
|
1571
|
+
void self.registration.navigationPreload.setHeaderValue(headerValue);
|
|
1572
|
+
}
|
|
1573
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1574
|
+
logger.log("Navigation preloading is enabled.");
|
|
1575
|
+
}
|
|
1576
|
+
}));
|
|
1577
|
+
});
|
|
1578
|
+
} else {
|
|
1579
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1580
|
+
logger.log("Navigation preloading is not supported in this browser.");
|
|
1581
|
+
}
|
|
1582
|
+
}
|
|
1583
|
+
};
|
|
1584
|
+
const disableNavigationPreload = ()=>{
|
|
1585
|
+
if (isNavigationPreloadSupported()) {
|
|
1586
|
+
self.addEventListener("activate", (event)=>{
|
|
1587
|
+
event.waitUntil(self.registration.navigationPreload.disable().then(()=>{
|
|
1588
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1589
|
+
logger.log("Navigation preloading is disabled.");
|
|
1590
|
+
}
|
|
1591
|
+
}));
|
|
1592
|
+
});
|
|
1593
|
+
} else {
|
|
1594
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1595
|
+
logger.log("Navigation preloading is not supported in this browser.");
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
};
|
|
1599
|
+
|
|
1600
|
+
const setCacheNameDetails = (details)=>{
|
|
1601
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1602
|
+
for (const key of Object.keys(details)){
|
|
1603
|
+
finalAssertExports.isType(details[key], "string", {
|
|
1604
|
+
moduleName: "@serwist/core",
|
|
1605
|
+
funcName: "setCacheNameDetails",
|
|
1606
|
+
paramName: `details.${key}`
|
|
1607
|
+
});
|
|
1608
|
+
}
|
|
1609
|
+
if (details.precache?.length === 0) {
|
|
1610
|
+
throw new SerwistError("invalid-cache-name", {
|
|
1611
|
+
cacheNameId: "precache",
|
|
1612
|
+
value: details.precache
|
|
1613
|
+
});
|
|
1614
|
+
}
|
|
1615
|
+
if (details.runtime?.length === 0) {
|
|
1616
|
+
throw new SerwistError("invalid-cache-name", {
|
|
1617
|
+
cacheNameId: "runtime",
|
|
1618
|
+
value: details.runtime
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
if (details.googleAnalytics?.length === 0) {
|
|
1622
|
+
throw new SerwistError("invalid-cache-name", {
|
|
1623
|
+
cacheNameId: "googleAnalytics",
|
|
1624
|
+
value: details.googleAnalytics
|
|
1625
|
+
});
|
|
1626
|
+
}
|
|
1627
|
+
}
|
|
1628
|
+
cacheNames$1.updateDetails(details);
|
|
1629
|
+
};
|
|
1630
|
+
|
|
1631
|
+
const parseRoute = (capture, handler, method)=>{
|
|
1632
|
+
if (typeof capture === "string") {
|
|
1633
|
+
const captureUrl = new URL(capture, location.href);
|
|
1634
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1635
|
+
if (!(capture.startsWith("/") || capture.startsWith("http"))) {
|
|
1636
|
+
throw new SerwistError("invalid-string", {
|
|
1637
|
+
moduleName: "serwist",
|
|
1638
|
+
funcName: "parseRoute",
|
|
1639
|
+
paramName: "capture"
|
|
1640
|
+
});
|
|
1641
|
+
}
|
|
1642
|
+
const valueToCheck = capture.startsWith("http") ? captureUrl.pathname : capture;
|
|
1643
|
+
const wildcards = "[*:?+]";
|
|
1644
|
+
if (new RegExp(`${wildcards}`).exec(valueToCheck)) {
|
|
1645
|
+
logger.debug(`The '$capture' parameter contains an Express-style wildcard character (${wildcards}). Strings are now always interpreted as exact matches; use a RegExp for partial or wildcard matches.`);
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
const matchCallback = ({ url })=>{
|
|
1649
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1650
|
+
if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) {
|
|
1651
|
+
logger.debug(`${capture} only partially matches the cross-origin URL ${url.toString()}. This route will only handle cross-origin requests if they match the entire URL.`);
|
|
1652
|
+
}
|
|
1653
|
+
}
|
|
1654
|
+
return url.href === captureUrl.href;
|
|
1655
|
+
};
|
|
1656
|
+
return new Route(matchCallback, handler, method);
|
|
1657
|
+
}
|
|
1658
|
+
if (capture instanceof RegExp) {
|
|
1659
|
+
return new RegExpRoute(capture, handler, method);
|
|
1660
|
+
}
|
|
1661
|
+
if (typeof capture === "function") {
|
|
1662
|
+
return new Route(capture, handler, method);
|
|
1663
|
+
}
|
|
1664
|
+
if (capture instanceof Route) {
|
|
1665
|
+
return capture;
|
|
1666
|
+
}
|
|
1667
|
+
throw new SerwistError("unsupported-route-type", {
|
|
1668
|
+
moduleName: "serwist",
|
|
1669
|
+
funcName: "parseRoute",
|
|
1670
|
+
paramName: "capture"
|
|
1671
|
+
});
|
|
1672
|
+
};
|
|
1673
|
+
|
|
1674
|
+
const REVISION_SEARCH_PARAM = "__WB_REVISION__";
|
|
1675
|
+
const createCacheKey = (entry)=>{
|
|
1676
|
+
if (!entry) {
|
|
1677
|
+
throw new SerwistError("add-to-cache-list-unexpected-type", {
|
|
1678
|
+
entry
|
|
1679
|
+
});
|
|
1680
|
+
}
|
|
1681
|
+
if (typeof entry === "string") {
|
|
1682
|
+
const urlObject = new URL(entry, location.href);
|
|
1683
|
+
return {
|
|
1684
|
+
cacheKey: urlObject.href,
|
|
1685
|
+
url: urlObject.href
|
|
1686
|
+
};
|
|
1687
|
+
}
|
|
1688
|
+
const { revision, url } = entry;
|
|
1689
|
+
if (!url) {
|
|
1690
|
+
throw new SerwistError("add-to-cache-list-unexpected-type", {
|
|
1691
|
+
entry
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
if (!revision) {
|
|
1695
|
+
const urlObject = new URL(url, location.href);
|
|
1696
|
+
return {
|
|
1697
|
+
cacheKey: urlObject.href,
|
|
1698
|
+
url: urlObject.href
|
|
1699
|
+
};
|
|
1700
|
+
}
|
|
1701
|
+
const cacheKeyURL = new URL(url, location.href);
|
|
1702
|
+
const originalURL = new URL(url, location.href);
|
|
1703
|
+
cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision);
|
|
1704
|
+
return {
|
|
1705
|
+
cacheKey: cacheKeyURL.href,
|
|
1706
|
+
url: originalURL.href
|
|
1707
|
+
};
|
|
1708
|
+
};
|
|
1709
|
+
|
|
1710
|
+
class PrecacheInstallReportPlugin {
|
|
1711
|
+
updatedURLs = [];
|
|
1712
|
+
notUpdatedURLs = [];
|
|
1713
|
+
handlerWillStart = async ({ request, state })=>{
|
|
1714
|
+
if (state) {
|
|
1715
|
+
state.originalRequest = request;
|
|
1716
|
+
}
|
|
1717
|
+
};
|
|
1718
|
+
cachedResponseWillBeUsed = async ({ event, state, cachedResponse })=>{
|
|
1719
|
+
if (event.type === "install") {
|
|
1720
|
+
if (state?.originalRequest && state.originalRequest instanceof Request) {
|
|
1721
|
+
const url = state.originalRequest.url;
|
|
1722
|
+
if (cachedResponse) {
|
|
1723
|
+
this.notUpdatedURLs.push(url);
|
|
1724
|
+
} else {
|
|
1725
|
+
this.updatedURLs.push(url);
|
|
152
1726
|
}
|
|
153
1727
|
}
|
|
154
1728
|
}
|
|
155
|
-
return
|
|
1729
|
+
return cachedResponse;
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
function _nestedGroup(groupTitle, urls) {
|
|
1734
|
+
if (urls.length === 0) {
|
|
1735
|
+
return;
|
|
1736
|
+
}
|
|
1737
|
+
logger.groupCollapsed(groupTitle);
|
|
1738
|
+
for (const url of urls){
|
|
1739
|
+
logger.log(url);
|
|
156
1740
|
}
|
|
1741
|
+
logger.groupEnd();
|
|
157
1742
|
}
|
|
1743
|
+
const printInstallDetails = (urlsToPrecache, urlsAlreadyPrecached)=>{
|
|
1744
|
+
const precachedCount = urlsToPrecache.length;
|
|
1745
|
+
const alreadyPrecachedCount = urlsAlreadyPrecached.length;
|
|
1746
|
+
if (precachedCount || alreadyPrecachedCount) {
|
|
1747
|
+
let message = `Precaching ${precachedCount} file${precachedCount === 1 ? "" : "s"}.`;
|
|
1748
|
+
if (alreadyPrecachedCount > 0) {
|
|
1749
|
+
message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? " is" : "s are"} already cached.`;
|
|
1750
|
+
}
|
|
1751
|
+
logger.groupCollapsed(message);
|
|
1752
|
+
_nestedGroup("View newly precached URLs.", urlsToPrecache);
|
|
1753
|
+
_nestedGroup("View previously precached URLs.", urlsAlreadyPrecached);
|
|
1754
|
+
logger.groupEnd();
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
|
|
1758
|
+
const logGroup = (groupTitle, deletedURLs)=>{
|
|
1759
|
+
logger.groupCollapsed(groupTitle);
|
|
1760
|
+
for (const url of deletedURLs){
|
|
1761
|
+
logger.log(url);
|
|
1762
|
+
}
|
|
1763
|
+
logger.groupEnd();
|
|
1764
|
+
};
|
|
1765
|
+
const printCleanupDetails = (deletedURLs)=>{
|
|
1766
|
+
const deletionCount = deletedURLs.length;
|
|
1767
|
+
if (deletionCount > 0) {
|
|
1768
|
+
logger.groupCollapsed(`During precaching cleanup, ${deletionCount} cached request${deletionCount === 1 ? " was" : "s were"} deleted.`);
|
|
1769
|
+
logGroup("Deleted Cache Requests", deletedURLs);
|
|
1770
|
+
logger.groupEnd();
|
|
1771
|
+
}
|
|
1772
|
+
};
|
|
158
1773
|
|
|
159
1774
|
class PrecacheCacheKeyPlugin {
|
|
160
1775
|
_precacheController;
|
|
@@ -169,52 +1784,198 @@ class PrecacheCacheKeyPlugin {
|
|
|
169
1784
|
};
|
|
170
1785
|
}
|
|
171
1786
|
|
|
172
|
-
const parsePrecacheOptions = (
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
precacheStrategyOptions: {
|
|
176
|
-
cacheName: cacheNames$1.getPrecacheName(precacheCacheName),
|
|
1787
|
+
const parsePrecacheOptions = (controller, { cacheName, plugins, fetchOptions, matchOptions, fallbackToNetwork, directoryIndex, ignoreURLParametersMatching, cleanURLs, urlManipulation, cleanupOutdatedCaches, concurrency, navigateFallback, navigateFallbackAllowlist, navigateFallbackDenylist } = {})=>({
|
|
1788
|
+
strategyOptions: {
|
|
1789
|
+
cacheName: cacheNames$1.getPrecacheName(cacheName),
|
|
177
1790
|
plugins: [
|
|
178
|
-
...
|
|
1791
|
+
...plugins ?? [],
|
|
179
1792
|
new PrecacheCacheKeyPlugin({
|
|
180
|
-
precacheController:
|
|
1793
|
+
precacheController: controller
|
|
181
1794
|
})
|
|
182
1795
|
],
|
|
183
|
-
fetchOptions
|
|
184
|
-
matchOptions
|
|
185
|
-
fallbackToNetwork
|
|
1796
|
+
fetchOptions,
|
|
1797
|
+
matchOptions,
|
|
1798
|
+
fallbackToNetwork
|
|
186
1799
|
},
|
|
187
|
-
|
|
188
|
-
directoryIndex
|
|
189
|
-
ignoreURLParametersMatching
|
|
190
|
-
cleanURLs
|
|
191
|
-
urlManipulation
|
|
1800
|
+
routeOptions: {
|
|
1801
|
+
directoryIndex,
|
|
1802
|
+
ignoreURLParametersMatching,
|
|
1803
|
+
cleanURLs,
|
|
1804
|
+
urlManipulation
|
|
192
1805
|
},
|
|
193
|
-
|
|
1806
|
+
controllerOptions: {
|
|
194
1807
|
cleanupOutdatedCaches,
|
|
195
|
-
concurrency,
|
|
1808
|
+
concurrency: concurrency ?? 10,
|
|
196
1809
|
navigateFallback,
|
|
197
1810
|
navigateFallbackAllowlist,
|
|
198
1811
|
navigateFallbackDenylist
|
|
199
1812
|
}
|
|
200
|
-
};
|
|
201
|
-
};
|
|
1813
|
+
});
|
|
202
1814
|
|
|
203
|
-
class
|
|
1815
|
+
class PrecacheController {
|
|
204
1816
|
_urlsToCacheKeys = new Map();
|
|
205
1817
|
_urlsToCacheModes = new Map();
|
|
206
1818
|
_cacheKeysToIntegrities = new Map();
|
|
207
|
-
|
|
208
|
-
|
|
1819
|
+
_strategy;
|
|
1820
|
+
_options;
|
|
1821
|
+
_routeOptions;
|
|
1822
|
+
constructor(entries, precacheOptions){
|
|
1823
|
+
const { strategyOptions, routeOptions, controllerOptions } = parsePrecacheOptions(this, precacheOptions);
|
|
1824
|
+
this.addToCacheList(entries);
|
|
1825
|
+
this._strategy = new PrecacheStrategy(strategyOptions);
|
|
1826
|
+
this._options = controllerOptions;
|
|
1827
|
+
this._routeOptions = routeOptions;
|
|
1828
|
+
this.install = this.install.bind(this);
|
|
1829
|
+
this.activate = this.activate.bind(this);
|
|
1830
|
+
}
|
|
1831
|
+
get strategy() {
|
|
1832
|
+
return this._strategy;
|
|
1833
|
+
}
|
|
1834
|
+
addToCacheList(entries) {
|
|
1835
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1836
|
+
finalAssertExports.isArray(entries, {
|
|
1837
|
+
moduleName: "serwist",
|
|
1838
|
+
className: "PrecacheController",
|
|
1839
|
+
funcName: "addEntries",
|
|
1840
|
+
paramName: "entries"
|
|
1841
|
+
});
|
|
1842
|
+
}
|
|
1843
|
+
const urlsToWarnAbout = [];
|
|
1844
|
+
for (const entry of entries){
|
|
1845
|
+
if (typeof entry === "string") {
|
|
1846
|
+
urlsToWarnAbout.push(entry);
|
|
1847
|
+
} else if (entry && !entry.integrity && entry.revision === undefined) {
|
|
1848
|
+
urlsToWarnAbout.push(entry.url);
|
|
1849
|
+
}
|
|
1850
|
+
const { cacheKey, url } = createCacheKey(entry);
|
|
1851
|
+
const cacheMode = typeof entry !== "string" && entry.revision ? "reload" : "default";
|
|
1852
|
+
if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) {
|
|
1853
|
+
throw new SerwistError("add-to-cache-list-conflicting-entries", {
|
|
1854
|
+
firstEntry: this._urlsToCacheKeys.get(url),
|
|
1855
|
+
secondEntry: cacheKey
|
|
1856
|
+
});
|
|
1857
|
+
}
|
|
1858
|
+
if (typeof entry !== "string" && entry.integrity) {
|
|
1859
|
+
if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) {
|
|
1860
|
+
throw new SerwistError("add-to-cache-list-conflicting-integrities", {
|
|
1861
|
+
url
|
|
1862
|
+
});
|
|
1863
|
+
}
|
|
1864
|
+
this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);
|
|
1865
|
+
}
|
|
1866
|
+
this._urlsToCacheKeys.set(url, cacheKey);
|
|
1867
|
+
this._urlsToCacheModes.set(url, cacheMode);
|
|
1868
|
+
if (urlsToWarnAbout.length > 0) {
|
|
1869
|
+
const warningMessage = `Serwist is precaching URLs without revision info: ${urlsToWarnAbout.join(", ")}\nThis is generally NOT safe, as you risk serving outdated assets.`;
|
|
1870
|
+
if (process.env.NODE_ENV === "production") {
|
|
1871
|
+
console.warn(warningMessage);
|
|
1872
|
+
} else {
|
|
1873
|
+
logger.warn(warningMessage);
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
init(serwist) {
|
|
1879
|
+
serwist.registerRoute(new PrecacheRoute(this, this._routeOptions));
|
|
1880
|
+
if (this._options.navigateFallback) {
|
|
1881
|
+
serwist.registerRoute(new NavigationRoute(this.createHandlerBoundToUrl(this._options.navigateFallback), {
|
|
1882
|
+
allowlist: this._options.navigateFallbackAllowlist,
|
|
1883
|
+
denylist: this._options.navigateFallbackDenylist
|
|
1884
|
+
}));
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
async install(event) {
|
|
1888
|
+
const installReportPlugin = new PrecacheInstallReportPlugin();
|
|
1889
|
+
this._strategy.plugins.push(installReportPlugin);
|
|
1890
|
+
await parallel(this._options.concurrency, Array.from(this._urlsToCacheKeys.entries()), async ([url, cacheKey])=>{
|
|
1891
|
+
const integrity = this._cacheKeysToIntegrities.get(cacheKey);
|
|
1892
|
+
const cacheMode = this._urlsToCacheModes.get(url);
|
|
1893
|
+
const request = new Request(url, {
|
|
1894
|
+
integrity,
|
|
1895
|
+
cache: cacheMode,
|
|
1896
|
+
credentials: "same-origin"
|
|
1897
|
+
});
|
|
1898
|
+
await Promise.all(this._strategy.handleAll({
|
|
1899
|
+
event,
|
|
1900
|
+
request,
|
|
1901
|
+
url: new URL(request.url),
|
|
1902
|
+
params: {
|
|
1903
|
+
cacheKey
|
|
1904
|
+
}
|
|
1905
|
+
}));
|
|
1906
|
+
});
|
|
1907
|
+
const { updatedURLs, notUpdatedURLs } = installReportPlugin;
|
|
1908
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1909
|
+
printInstallDetails(updatedURLs, notUpdatedURLs);
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
async activate() {
|
|
1913
|
+
const cache = await self.caches.open(this._strategy.cacheName);
|
|
1914
|
+
const currentlyCachedRequests = await cache.keys();
|
|
1915
|
+
const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());
|
|
1916
|
+
const deletedCacheRequests = [];
|
|
1917
|
+
for (const request of currentlyCachedRequests){
|
|
1918
|
+
if (!expectedCacheKeys.has(request.url)) {
|
|
1919
|
+
await cache.delete(request);
|
|
1920
|
+
deletedCacheRequests.push(request.url);
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1924
|
+
printCleanupDetails(deletedCacheRequests);
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
getUrlsToPrecacheKeys() {
|
|
1928
|
+
return this._urlsToCacheKeys;
|
|
1929
|
+
}
|
|
1930
|
+
getPrecachedUrls() {
|
|
1931
|
+
return [
|
|
1932
|
+
...this._urlsToCacheKeys.keys()
|
|
1933
|
+
];
|
|
1934
|
+
}
|
|
1935
|
+
getPrecacheKeyForUrl(url) {
|
|
1936
|
+
const urlObject = new URL(url, location.href);
|
|
1937
|
+
return this._urlsToCacheKeys.get(urlObject.href);
|
|
1938
|
+
}
|
|
1939
|
+
getIntegrityForPrecacheKey(cacheKey) {
|
|
1940
|
+
return this._cacheKeysToIntegrities.get(cacheKey);
|
|
1941
|
+
}
|
|
1942
|
+
async matchPrecache(request) {
|
|
1943
|
+
const url = request instanceof Request ? request.url : request;
|
|
1944
|
+
const cacheKey = this.getPrecacheKeyForUrl(url);
|
|
1945
|
+
if (cacheKey) {
|
|
1946
|
+
const cache = await self.caches.open(this._strategy.cacheName);
|
|
1947
|
+
return cache.match(cacheKey);
|
|
1948
|
+
}
|
|
1949
|
+
return undefined;
|
|
1950
|
+
}
|
|
1951
|
+
createHandlerBoundToUrl(url) {
|
|
1952
|
+
const cacheKey = this.getPrecacheKeyForUrl(url);
|
|
1953
|
+
if (!cacheKey) {
|
|
1954
|
+
throw new SerwistError("non-precached-url", {
|
|
1955
|
+
url
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
return (options)=>{
|
|
1959
|
+
options.request = new Request(url);
|
|
1960
|
+
options.params = {
|
|
1961
|
+
cacheKey,
|
|
1962
|
+
...options.params
|
|
1963
|
+
};
|
|
1964
|
+
return this._strategy.handle(options);
|
|
1965
|
+
};
|
|
1966
|
+
}
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
class Serwist {
|
|
209
1970
|
_routes;
|
|
210
1971
|
_defaultHandlerMap;
|
|
1972
|
+
_precacheController;
|
|
1973
|
+
_controllers;
|
|
211
1974
|
_catchHandler;
|
|
212
|
-
constructor({ precacheEntries, precacheOptions, skipWaiting = false, importScripts, navigationPreload = false, cacheId, clientsClaim: clientsClaim$1 = false, runtimeCaching, offlineAnalyticsConfig, disableDevLogs: disableDevLogs$1 = false, fallbacks } = {}){
|
|
213
|
-
const { precacheStrategyOptions, precacheRouteOptions, precacheMiscOptions } = parsePrecacheOptions(this, precacheOptions);
|
|
214
|
-
this._concurrentPrecaching = precacheMiscOptions.concurrency;
|
|
215
|
-
this._precacheStrategy = new PrecacheStrategy(precacheStrategyOptions);
|
|
1975
|
+
constructor({ precacheEntries, precacheOptions, controllers = [], skipWaiting = false, importScripts, navigationPreload = false, cacheId, clientsClaim: clientsClaim$1 = false, runtimeCaching, offlineAnalyticsConfig, disableDevLogs: disableDevLogs$1 = false, fallbacks } = {}){
|
|
216
1976
|
this._routes = new Map();
|
|
217
1977
|
this._defaultHandlerMap = new Map();
|
|
1978
|
+
this._controllers = controllers;
|
|
218
1979
|
this.handleInstall = this.handleInstall.bind(this);
|
|
219
1980
|
this.handleActivate = this.handleActivate.bind(this);
|
|
220
1981
|
this.handleFetch = this.handleFetch.bind(this);
|
|
@@ -236,19 +1997,17 @@ class Serwist {
|
|
|
236
1997
|
});
|
|
237
1998
|
}
|
|
238
1999
|
if (clientsClaim$1) clientsClaim();
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
allowlist: precacheMiscOptions.navigateFallbackAllowlist,
|
|
249
|
-
denylist: precacheMiscOptions.navigateFallbackDenylist
|
|
250
|
-
}));
|
|
2000
|
+
this._precacheController = new PrecacheController(precacheEntries ?? [], precacheOptions);
|
|
2001
|
+
if (runtimeCaching) {
|
|
2002
|
+
if (!this._controllers?.some((controller)=>controller instanceof RuntimeCacheController)) {
|
|
2003
|
+
this._controllers.unshift(new RuntimeCacheController(runtimeCaching, {
|
|
2004
|
+
fallbacks
|
|
2005
|
+
}));
|
|
2006
|
+
} else if (process.env.NODE_ENV !== "production") {
|
|
2007
|
+
logger.warn("You have migrated to the Controller pattern, so setting `runtimeCaching` is a no-op.");
|
|
2008
|
+
}
|
|
251
2009
|
}
|
|
2010
|
+
this._controllers.unshift(this._precacheController);
|
|
252
2011
|
if (offlineAnalyticsConfig !== undefined) {
|
|
253
2012
|
if (typeof offlineAnalyticsConfig === "boolean") {
|
|
254
2013
|
offlineAnalyticsConfig && initializeGoogleAnalytics({
|
|
@@ -261,26 +2020,24 @@ class Serwist {
|
|
|
261
2020
|
});
|
|
262
2021
|
}
|
|
263
2022
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
const fallbackPlugin = new PrecacheFallbackPlugin({
|
|
267
|
-
fallbackUrls: fallbacks.entries,
|
|
268
|
-
serwist: this
|
|
269
|
-
});
|
|
270
|
-
runtimeCaching.forEach((cacheEntry)=>{
|
|
271
|
-
if (cacheEntry.handler instanceof Strategy && !cacheEntry.handler.plugins.some((plugin)=>"handlerDidError" in plugin)) {
|
|
272
|
-
cacheEntry.handler.plugins.push(fallbackPlugin);
|
|
273
|
-
}
|
|
274
|
-
});
|
|
275
|
-
}
|
|
276
|
-
for (const entry of runtimeCaching){
|
|
277
|
-
this.registerCapture(entry.matcher, entry.handler, entry.method);
|
|
278
|
-
}
|
|
2023
|
+
for (const callback of this.iterateControllers("init")){
|
|
2024
|
+
callback(this);
|
|
279
2025
|
}
|
|
280
2026
|
if (disableDevLogs$1) disableDevLogs();
|
|
281
2027
|
}
|
|
2028
|
+
*iterateControllers(name) {
|
|
2029
|
+
if (!this._controllers) return;
|
|
2030
|
+
for (const controller of this._controllers){
|
|
2031
|
+
if (typeof controller[name] === "function") {
|
|
2032
|
+
yield controller[name];
|
|
2033
|
+
}
|
|
2034
|
+
}
|
|
2035
|
+
}
|
|
2036
|
+
get precache() {
|
|
2037
|
+
return this._precacheController;
|
|
2038
|
+
}
|
|
282
2039
|
get precacheStrategy() {
|
|
283
|
-
return this.
|
|
2040
|
+
return this._precacheController.strategy;
|
|
284
2041
|
}
|
|
285
2042
|
get routes() {
|
|
286
2043
|
return this._routes;
|
|
@@ -291,99 +2048,18 @@ class Serwist {
|
|
|
291
2048
|
self.addEventListener("fetch", this.handleFetch);
|
|
292
2049
|
self.addEventListener("message", this.handleCache);
|
|
293
2050
|
}
|
|
294
|
-
addToPrecacheList(entries) {
|
|
295
|
-
if (process.env.NODE_ENV !== "production") {
|
|
296
|
-
finalAssertExports.isArray(entries, {
|
|
297
|
-
moduleName: "serwist",
|
|
298
|
-
className: "Serwist",
|
|
299
|
-
funcName: "addToCacheList",
|
|
300
|
-
paramName: "entries"
|
|
301
|
-
});
|
|
302
|
-
}
|
|
303
|
-
const urlsToWarnAbout = [];
|
|
304
|
-
for (const entry of entries){
|
|
305
|
-
if (typeof entry === "string") {
|
|
306
|
-
urlsToWarnAbout.push(entry);
|
|
307
|
-
} else if (entry && !entry.integrity && entry.revision === undefined) {
|
|
308
|
-
urlsToWarnAbout.push(entry.url);
|
|
309
|
-
}
|
|
310
|
-
const { cacheKey, url } = createCacheKey(entry);
|
|
311
|
-
const cacheMode = typeof entry !== "string" && entry.revision ? "reload" : "default";
|
|
312
|
-
if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) {
|
|
313
|
-
throw new SerwistError("add-to-cache-list-conflicting-entries", {
|
|
314
|
-
firstEntry: this._urlsToCacheKeys.get(url),
|
|
315
|
-
secondEntry: cacheKey
|
|
316
|
-
});
|
|
317
|
-
}
|
|
318
|
-
if (typeof entry !== "string" && entry.integrity) {
|
|
319
|
-
if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) {
|
|
320
|
-
throw new SerwistError("add-to-cache-list-conflicting-integrities", {
|
|
321
|
-
url
|
|
322
|
-
});
|
|
323
|
-
}
|
|
324
|
-
this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);
|
|
325
|
-
}
|
|
326
|
-
this._urlsToCacheKeys.set(url, cacheKey);
|
|
327
|
-
this._urlsToCacheModes.set(url, cacheMode);
|
|
328
|
-
if (urlsToWarnAbout.length > 0) {
|
|
329
|
-
const warningMessage = `Serwist is precaching URLs without revision info: ${urlsToWarnAbout.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;
|
|
330
|
-
if (process.env.NODE_ENV === "production") {
|
|
331
|
-
console.warn(warningMessage);
|
|
332
|
-
} else {
|
|
333
|
-
logger.warn(warningMessage);
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
2051
|
handleInstall(event) {
|
|
339
2052
|
return waitUntil(event, async ()=>{
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
await parallel(this._concurrentPrecaching, Array.from(this._urlsToCacheKeys.entries()), async ([url, cacheKey])=>{
|
|
343
|
-
const integrity = this._cacheKeysToIntegrities.get(cacheKey);
|
|
344
|
-
const cacheMode = this._urlsToCacheModes.get(url);
|
|
345
|
-
const request = new Request(url, {
|
|
346
|
-
integrity,
|
|
347
|
-
cache: cacheMode,
|
|
348
|
-
credentials: "same-origin"
|
|
349
|
-
});
|
|
350
|
-
await Promise.all(this.precacheStrategy.handleAll({
|
|
351
|
-
event,
|
|
352
|
-
request,
|
|
353
|
-
url: new URL(request.url),
|
|
354
|
-
params: {
|
|
355
|
-
cacheKey
|
|
356
|
-
}
|
|
357
|
-
}));
|
|
358
|
-
});
|
|
359
|
-
const { updatedURLs, notUpdatedURLs } = installReportPlugin;
|
|
360
|
-
if (process.env.NODE_ENV !== "production") {
|
|
361
|
-
printInstallDetails(updatedURLs, notUpdatedURLs);
|
|
2053
|
+
for (const callback of this.iterateControllers("install")){
|
|
2054
|
+
await callback(event, this);
|
|
362
2055
|
}
|
|
363
|
-
return {
|
|
364
|
-
updatedURLs,
|
|
365
|
-
notUpdatedURLs
|
|
366
|
-
};
|
|
367
2056
|
});
|
|
368
2057
|
}
|
|
369
2058
|
handleActivate(event) {
|
|
370
2059
|
return waitUntil(event, async ()=>{
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());
|
|
374
|
-
const deletedCacheRequests = [];
|
|
375
|
-
for (const request of currentlyCachedRequests){
|
|
376
|
-
if (!expectedCacheKeys.has(request.url)) {
|
|
377
|
-
await cache.delete(request);
|
|
378
|
-
deletedCacheRequests.push(request.url);
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
if (process.env.NODE_ENV !== "production") {
|
|
382
|
-
printCleanupDetails(deletedCacheRequests);
|
|
2060
|
+
for (const callback of this.iterateControllers("activate")){
|
|
2061
|
+
await callback(event, this);
|
|
383
2062
|
}
|
|
384
|
-
return {
|
|
385
|
-
deletedCacheRequests
|
|
386
|
-
};
|
|
387
2063
|
});
|
|
388
2064
|
}
|
|
389
2065
|
handleFetch(event) {
|
|
@@ -482,46 +2158,6 @@ class Serwist {
|
|
|
482
2158
|
throw new SerwistError("unregister-route-route-not-registered");
|
|
483
2159
|
}
|
|
484
2160
|
}
|
|
485
|
-
getUrlsToPrecacheKeys() {
|
|
486
|
-
return this._urlsToCacheKeys;
|
|
487
|
-
}
|
|
488
|
-
getPrecachedUrls() {
|
|
489
|
-
return [
|
|
490
|
-
...this._urlsToCacheKeys.keys()
|
|
491
|
-
];
|
|
492
|
-
}
|
|
493
|
-
getPrecacheKeyForUrl(url) {
|
|
494
|
-
const urlObject = new URL(url, location.href);
|
|
495
|
-
return this._urlsToCacheKeys.get(urlObject.href);
|
|
496
|
-
}
|
|
497
|
-
getIntegrityForPrecacheKey(cacheKey) {
|
|
498
|
-
return this._cacheKeysToIntegrities.get(cacheKey);
|
|
499
|
-
}
|
|
500
|
-
async matchPrecache(request) {
|
|
501
|
-
const url = request instanceof Request ? request.url : request;
|
|
502
|
-
const cacheKey = this.getPrecacheKeyForUrl(url);
|
|
503
|
-
if (cacheKey) {
|
|
504
|
-
const cache = await self.caches.open(this.precacheStrategy.cacheName);
|
|
505
|
-
return cache.match(cacheKey);
|
|
506
|
-
}
|
|
507
|
-
return undefined;
|
|
508
|
-
}
|
|
509
|
-
createHandlerBoundToUrl(url) {
|
|
510
|
-
const cacheKey = this.getPrecacheKeyForUrl(url);
|
|
511
|
-
if (!cacheKey) {
|
|
512
|
-
throw new SerwistError("non-precached-url", {
|
|
513
|
-
url
|
|
514
|
-
});
|
|
515
|
-
}
|
|
516
|
-
return (options)=>{
|
|
517
|
-
options.request = new Request(url);
|
|
518
|
-
options.params = {
|
|
519
|
-
cacheKey,
|
|
520
|
-
...options.params
|
|
521
|
-
};
|
|
522
|
-
return this.precacheStrategy.handle(options);
|
|
523
|
-
};
|
|
524
|
-
}
|
|
525
2161
|
handleRequest({ request, event }) {
|
|
526
2162
|
if (process.env.NODE_ENV !== "production") {
|
|
527
2163
|
finalAssertExports.isInstance(request, Request, {
|
|
@@ -669,6 +2305,27 @@ class Serwist {
|
|
|
669
2305
|
}
|
|
670
2306
|
return {};
|
|
671
2307
|
}
|
|
2308
|
+
addToPrecacheList(entries) {
|
|
2309
|
+
this._precacheController.addToCacheList(entries);
|
|
2310
|
+
}
|
|
2311
|
+
getUrlsToPrecacheKeys() {
|
|
2312
|
+
return this.precache.getUrlsToPrecacheKeys();
|
|
2313
|
+
}
|
|
2314
|
+
getPrecachedUrls() {
|
|
2315
|
+
return this.precache.getPrecachedUrls();
|
|
2316
|
+
}
|
|
2317
|
+
getPrecacheKeyForUrl(url) {
|
|
2318
|
+
return this.precache.getPrecacheKeyForUrl(url);
|
|
2319
|
+
}
|
|
2320
|
+
getIntegrityForPrecacheKey(cacheKey) {
|
|
2321
|
+
return this.precache.getIntegrityForPrecacheKey(cacheKey);
|
|
2322
|
+
}
|
|
2323
|
+
matchPrecache(request) {
|
|
2324
|
+
return this.precache.matchPrecache(request);
|
|
2325
|
+
}
|
|
2326
|
+
createHandlerBoundToUrl(url) {
|
|
2327
|
+
return this.precache.createHandlerBoundToUrl(url);
|
|
2328
|
+
}
|
|
672
2329
|
}
|
|
673
2330
|
|
|
674
2331
|
const cacheNames = {
|
|
@@ -1503,4 +3160,4 @@ class StaleWhileRevalidate extends Strategy {
|
|
|
1503
3160
|
}
|
|
1504
3161
|
}
|
|
1505
3162
|
|
|
1506
|
-
export { BROADCAST_UPDATE_DEFAULT_HEADERS, BackgroundSyncPlugin, BroadcastCacheUpdate, BroadcastUpdatePlugin, CacheExpiration, CacheFirst, CacheOnly, CacheableResponse, CacheableResponsePlugin, ExpirationPlugin, NavigationRoute, NetworkFirst, NetworkOnly, PrecacheFallbackPlugin, PrecacheRoute, PrecacheStrategy, RangeRequestsPlugin, Route, Serwist, StaleWhileRevalidate, Strategy, cacheNames, createPartialResponse, disableDevLogs, enableNavigationPreload, initializeGoogleAnalytics, registerQuotaErrorCallback, responsesAreSame, setCacheNameDetails };
|
|
3163
|
+
export { BROADCAST_UPDATE_DEFAULT_HEADERS, BackgroundSyncPlugin, BackgroundSyncQueue, BackgroundSyncQueueStore, BroadcastCacheUpdate, BroadcastUpdatePlugin, CacheExpiration, CacheFirst, CacheOnly, CacheableResponse, CacheableResponsePlugin, ExpirationPlugin, NavigationRoute, NetworkFirst, NetworkOnly, PrecacheFallbackPlugin, PrecacheRoute, PrecacheStrategy, RangeRequestsPlugin, RegExpRoute, Route, RuntimeCacheController, Serwist, StaleWhileRevalidate, StorableRequest, Strategy, StrategyHandler, cacheNames, copyResponse, createPartialResponse, disableDevLogs, disableNavigationPreload, enableNavigationPreload, initializeGoogleAnalytics, isNavigationPreloadSupported, registerQuotaErrorCallback, responsesAreSame, setCacheNameDetails };
|