serwist 10.0.0-preview.1 → 10.0.0-preview.2
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/NavigationRoute.d.ts.map +1 -1
- package/dist/RegExpRoute.d.ts.map +1 -1
- package/dist/Route.d.ts.map +1 -1
- package/dist/Serwist.d.ts +5 -5
- package/dist/Serwist.d.ts.map +1 -1
- package/dist/chunks/{resultingClientExists.js → waitUntil.js} +117 -117
- package/dist/copyResponse.d.ts.map +1 -1
- package/dist/index.d.ts +11 -37
- package/dist/index.d.ts.map +1 -1
- package/dist/index.internal.d.ts +16 -16
- package/dist/index.internal.d.ts.map +1 -1
- package/dist/index.internal.js +2 -2
- package/dist/index.js +1400 -1387
- package/dist/lib/backgroundSync/StorableRequest.d.ts.map +1 -1
- package/dist/lib/backgroundSync/index.d.ts +6 -0
- package/dist/lib/backgroundSync/index.d.ts.map +1 -0
- package/dist/lib/broadcastUpdate/BroadcastCacheUpdate.d.ts.map +1 -1
- package/dist/lib/broadcastUpdate/index.d.ts +6 -0
- package/dist/lib/broadcastUpdate/index.d.ts.map +1 -0
- package/dist/lib/broadcastUpdate/responsesAreSame.d.ts.map +1 -1
- package/dist/lib/cacheableResponse/index.d.ts +4 -0
- package/dist/lib/cacheableResponse/index.d.ts.map +1 -0
- package/dist/{controllers → lib/controllers}/PrecacheController/PrecacheCacheKeyPlugin.d.ts +1 -1
- package/dist/lib/controllers/PrecacheController/PrecacheCacheKeyPlugin.d.ts.map +1 -0
- package/dist/{controllers → lib/controllers}/PrecacheController/PrecacheController.d.ts +9 -5
- package/dist/lib/controllers/PrecacheController/PrecacheController.d.ts.map +1 -0
- package/dist/{controllers → lib/controllers}/PrecacheController/PrecacheInstallReportPlugin.d.ts +1 -1
- package/dist/lib/controllers/PrecacheController/PrecacheInstallReportPlugin.d.ts.map +1 -0
- package/dist/{controllers → lib/controllers}/PrecacheController/PrecacheRoute.d.ts +1 -1
- package/dist/lib/controllers/PrecacheController/PrecacheRoute.d.ts.map +1 -0
- package/dist/{controllers → lib/controllers}/PrecacheController/PrecacheStrategy.d.ts +4 -4
- package/dist/lib/controllers/PrecacheController/PrecacheStrategy.d.ts.map +1 -0
- package/dist/{controllers → lib/controllers}/PrecacheController/parsePrecacheOptions.d.ts +3 -3
- package/dist/lib/controllers/PrecacheController/parsePrecacheOptions.d.ts.map +1 -0
- package/dist/{controllers → lib/controllers}/RuntimeCacheController.d.ts +10 -5
- package/dist/lib/controllers/RuntimeCacheController.d.ts.map +1 -0
- package/dist/lib/controllers/index.d.ts +4 -0
- package/dist/lib/controllers/index.d.ts.map +1 -0
- package/dist/lib/expiration/ExpirationPlugin.d.ts.map +1 -1
- package/dist/lib/expiration/index.d.ts +4 -0
- package/dist/lib/expiration/index.d.ts.map +1 -0
- package/dist/lib/googleAnalytics/index.d.ts +3 -0
- package/dist/lib/googleAnalytics/index.d.ts.map +1 -0
- package/dist/lib/googleAnalytics/initializeGoogleAnalytics.d.ts.map +1 -1
- package/dist/lib/precaching/PrecacheFallbackPlugin.d.ts +12 -6
- package/dist/lib/precaching/PrecacheFallbackPlugin.d.ts.map +1 -1
- package/dist/lib/precaching/index.d.ts +3 -0
- package/dist/lib/precaching/index.d.ts.map +1 -0
- package/dist/lib/rangeRequests/createPartialResponse.d.ts.map +1 -1
- package/dist/lib/rangeRequests/index.d.ts +3 -0
- package/dist/lib/rangeRequests/index.d.ts.map +1 -0
- package/dist/lib/rangeRequests/utils/calculateEffectiveBoundaries.d.ts.map +1 -1
- package/dist/lib/rangeRequests/utils/parseRangeHeader.d.ts.map +1 -1
- package/dist/lib/strategies/Strategy.d.ts.map +1 -1
- package/dist/lib/strategies/StrategyHandler.d.ts.map +1 -1
- package/dist/lib/strategies/index.d.ts +11 -0
- package/dist/lib/strategies/index.d.ts.map +1 -0
- package/dist/navigationPreload.d.ts.map +1 -1
- package/dist/registerQuotaErrorCallback.d.ts.map +1 -1
- package/dist/setCacheNameDetails.d.ts +1 -1
- package/dist/setCacheNameDetails.d.ts.map +1 -1
- package/dist/types.d.ts +24 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/SerwistError.d.ts +1 -1
- package/dist/utils/SerwistError.d.ts.map +1 -1
- package/dist/utils/cleanupOutdatedCaches.d.ts.map +1 -1
- package/dist/utils/createCacheKey.d.ts.map +1 -1
- package/dist/utils/deleteOutdatedCaches.d.ts.map +1 -1
- package/dist/utils/getFriendlyURL.d.ts.map +1 -1
- package/dist/utils/normalizeHandler.d.ts.map +1 -1
- package/dist/utils/parseRoute.d.ts.map +1 -1
- package/dist/utils/printCleanupDetails.d.ts.map +1 -1
- package/dist/utils/printInstallDetails.d.ts.map +1 -1
- package/dist/utils/removeIgnoredSearchParams.d.ts.map +1 -1
- package/dist/utils/waitUntil.d.ts.map +1 -1
- package/package.json +28 -5
- package/src/NavigationRoute.ts +2 -2
- package/src/RegExpRoute.ts +2 -2
- package/src/Route.ts +2 -2
- package/src/Serwist.ts +23 -19
- package/src/cacheNames.ts +1 -1
- package/src/copyResponse.ts +2 -2
- package/src/index.internal.ts +16 -16
- package/src/index.ts +68 -96
- package/src/lib/backgroundSync/BackgroundSyncQueue.ts +4 -4
- package/src/lib/backgroundSync/BackgroundSyncQueueStore.ts +1 -1
- package/src/lib/backgroundSync/StorableRequest.ts +1 -1
- package/src/lib/backgroundSync/index.ts +5 -0
- package/src/lib/broadcastUpdate/BroadcastCacheUpdate.ts +4 -4
- package/src/lib/broadcastUpdate/index.ts +5 -0
- package/src/lib/broadcastUpdate/responsesAreSame.ts +2 -2
- package/src/lib/cacheableResponse/CacheableResponse.ts +4 -4
- package/src/lib/cacheableResponse/index.ts +3 -0
- package/src/{controllers → lib/controllers}/PrecacheController/PrecacheCacheKeyPlugin.ts +1 -1
- package/src/{controllers → lib/controllers}/PrecacheController/PrecacheController.ts +19 -16
- package/src/{controllers → lib/controllers}/PrecacheController/PrecacheInstallReportPlugin.ts +1 -1
- package/src/{controllers → lib/controllers}/PrecacheController/PrecacheRoute.ts +5 -5
- package/src/{controllers → lib/controllers}/PrecacheController/PrecacheStrategy.ts +10 -10
- package/src/{controllers → lib/controllers}/PrecacheController/parsePrecacheOptions.ts +3 -3
- package/src/{controllers → lib/controllers}/RuntimeCacheController.ts +6 -6
- package/src/lib/controllers/index.ts +3 -0
- package/src/lib/expiration/CacheExpiration.ts +3 -3
- package/src/lib/expiration/ExpirationPlugin.ts +6 -6
- package/src/lib/expiration/index.ts +3 -0
- package/src/lib/googleAnalytics/index.ts +2 -0
- package/src/lib/googleAnalytics/initializeGoogleAnalytics.ts +5 -5
- package/src/lib/precaching/PrecacheFallbackPlugin.ts +18 -7
- package/src/lib/precaching/index.ts +2 -0
- package/src/lib/rangeRequests/createPartialResponse.ts +3 -3
- package/src/lib/rangeRequests/index.ts +2 -0
- package/src/lib/rangeRequests/utils/calculateEffectiveBoundaries.ts +2 -2
- package/src/lib/rangeRequests/utils/parseRangeHeader.ts +2 -2
- package/src/lib/strategies/CacheFirst.ts +3 -3
- package/src/lib/strategies/CacheOnly.ts +3 -3
- package/src/lib/strategies/NetworkFirst.ts +3 -3
- package/src/lib/strategies/NetworkOnly.ts +4 -4
- package/src/lib/strategies/StaleWhileRevalidate.ts +3 -3
- package/src/lib/strategies/Strategy.ts +4 -4
- package/src/lib/strategies/StrategyHandler.ts +9 -9
- package/src/lib/strategies/index.ts +10 -0
- package/src/lib/strategies/utils/messages.ts +2 -2
- package/src/models/messages/messages.ts +3 -3
- package/src/navigationPreload.ts +1 -1
- package/src/registerQuotaErrorCallback.ts +2 -2
- package/src/setCacheNameDetails.ts +4 -4
- package/src/types.ts +36 -5
- package/src/utils/SerwistError.ts +2 -2
- package/src/utils/cacheNames.ts +1 -1
- package/src/utils/executeQuotaErrorCallbacks.ts +1 -1
- package/dist/controllers/PrecacheController/PrecacheCacheKeyPlugin.d.ts.map +0 -1
- package/dist/controllers/PrecacheController/PrecacheController.d.ts.map +0 -1
- package/dist/controllers/PrecacheController/PrecacheInstallReportPlugin.d.ts.map +0 -1
- package/dist/controllers/PrecacheController/PrecacheRoute.d.ts.map +0 -1
- package/dist/controllers/PrecacheController/PrecacheStrategy.d.ts.map +0 -1
- package/dist/controllers/PrecacheController/parsePrecacheOptions.d.ts.map +0 -1
- package/dist/controllers/RuntimeCacheController.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,1094 +1,1198 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { f as finalAssertExports, l as logger, S as SerwistError, g as getFriendlyURL, c as canConstructResponseFromBodyStream, D as Deferred, t as timeout, a as cacheMatchIgnoreParams, e as executeQuotaErrorCallbacks, b as cacheNames$1, d as clientsClaim, w as waitUntil, q as quotaErrorCallbacks, r as resultingClientExists } from './chunks/waitUntil.js';
|
|
2
2
|
import { parallel } from '@serwist/utils';
|
|
3
3
|
import { openDB, deleteDB } from 'idb';
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const normalizeHandler = (handler)=>{
|
|
6
|
+
if (handler && typeof handler === "object") {
|
|
7
|
+
if (process.env.NODE_ENV !== "production") {
|
|
8
|
+
finalAssertExports.hasMethod(handler, "handle", {
|
|
9
|
+
moduleName: "serwist",
|
|
10
|
+
className: "Route",
|
|
11
|
+
funcName: "constructor",
|
|
12
|
+
paramName: "handler"
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
return handler;
|
|
10
16
|
}
|
|
11
|
-
if (
|
|
12
|
-
|
|
13
|
-
|
|
17
|
+
if (process.env.NODE_ENV !== "production") {
|
|
18
|
+
finalAssertExports.isType(handler, "function", {
|
|
19
|
+
moduleName: "serwist",
|
|
20
|
+
className: "Route",
|
|
21
|
+
funcName: "constructor",
|
|
22
|
+
paramName: "handler"
|
|
14
23
|
});
|
|
15
24
|
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
headers: new Headers(clonedResponse.headers),
|
|
19
|
-
status: clonedResponse.status,
|
|
20
|
-
statusText: clonedResponse.statusText
|
|
25
|
+
return {
|
|
26
|
+
handle: handler
|
|
21
27
|
};
|
|
22
|
-
const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit;
|
|
23
|
-
const body = canConstructResponseFromBodyStream() ? clonedResponse.body : await clonedResponse.blob();
|
|
24
|
-
return new Response(body, modifiedResponseInit);
|
|
25
28
|
};
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
const defaultMethod = "GET";
|
|
31
|
+
const validMethods = [
|
|
32
|
+
"DELETE",
|
|
33
|
+
"GET",
|
|
34
|
+
"HEAD",
|
|
35
|
+
"PATCH",
|
|
36
|
+
"POST",
|
|
37
|
+
"PUT"
|
|
38
|
+
];
|
|
39
|
+
|
|
40
|
+
class Route {
|
|
41
|
+
handler;
|
|
42
|
+
match;
|
|
43
|
+
method;
|
|
44
|
+
catchHandler;
|
|
45
|
+
constructor(match, handler, method = defaultMethod){
|
|
46
|
+
if (process.env.NODE_ENV !== "production") {
|
|
47
|
+
finalAssertExports.isType(match, "function", {
|
|
48
|
+
moduleName: "serwist",
|
|
49
|
+
className: "Route",
|
|
50
|
+
funcName: "constructor",
|
|
51
|
+
paramName: "match"
|
|
52
|
+
});
|
|
53
|
+
if (method) {
|
|
54
|
+
finalAssertExports.isOneOf(method, validMethods, {
|
|
55
|
+
paramName: "method"
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
this.handler = normalizeHandler(handler);
|
|
60
|
+
this.match = match;
|
|
61
|
+
this.method = method;
|
|
62
|
+
}
|
|
63
|
+
setCatchHandler(handler) {
|
|
64
|
+
this.catchHandler = normalizeHandler(handler);
|
|
65
|
+
}
|
|
29
66
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
_handlerDeferred;
|
|
38
|
-
_extendLifetimePromises;
|
|
39
|
-
_plugins;
|
|
40
|
-
_pluginStateMap;
|
|
41
|
-
constructor(strategy, options){
|
|
67
|
+
|
|
68
|
+
class NavigationRoute extends Route {
|
|
69
|
+
_allowlist;
|
|
70
|
+
_denylist;
|
|
71
|
+
constructor(handler, { allowlist = [
|
|
72
|
+
/./
|
|
73
|
+
], denylist = [] } = {}){
|
|
42
74
|
if (process.env.NODE_ENV !== "production") {
|
|
43
|
-
finalAssertExports.
|
|
75
|
+
finalAssertExports.isArrayOfClass(allowlist, RegExp, {
|
|
44
76
|
moduleName: "serwist",
|
|
45
|
-
className: "
|
|
77
|
+
className: "NavigationRoute",
|
|
46
78
|
funcName: "constructor",
|
|
47
|
-
paramName: "options.
|
|
79
|
+
paramName: "options.allowlist"
|
|
48
80
|
});
|
|
49
|
-
finalAssertExports.
|
|
81
|
+
finalAssertExports.isArrayOfClass(denylist, RegExp, {
|
|
50
82
|
moduleName: "serwist",
|
|
51
|
-
className: "
|
|
83
|
+
className: "NavigationRoute",
|
|
52
84
|
funcName: "constructor",
|
|
53
|
-
paramName: "options.
|
|
85
|
+
paramName: "options.denylist"
|
|
54
86
|
});
|
|
55
87
|
}
|
|
56
|
-
this.
|
|
57
|
-
this.
|
|
58
|
-
|
|
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);
|
|
88
|
+
super((options)=>this._match(options), handler);
|
|
89
|
+
this._allowlist = allowlist;
|
|
90
|
+
this._denylist = denylist;
|
|
73
91
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
const preloadResponse = await this.getPreloadResponse();
|
|
78
|
-
if (preloadResponse) {
|
|
79
|
-
return preloadResponse;
|
|
92
|
+
_match({ url, request }) {
|
|
93
|
+
if (request && request.mode !== "navigate") {
|
|
94
|
+
return false;
|
|
80
95
|
}
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
} catch (err) {
|
|
90
|
-
if (err instanceof Error) {
|
|
91
|
-
throw new SerwistError("plugin-error-request-will-fetch", {
|
|
92
|
-
thrownErrorMessage: err.message
|
|
93
|
-
});
|
|
96
|
+
const pathnameAndSearch = url.pathname + url.search;
|
|
97
|
+
for (const regExp of this._denylist){
|
|
98
|
+
if (regExp.test(pathnameAndSearch)) {
|
|
99
|
+
if (process.env.NODE_ENV !== "production") {
|
|
100
|
+
logger.log(`The navigation route ${pathnameAndSearch} is not being used, since the URL matches this denylist pattern: ${regExp.toString()}`);
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
94
103
|
}
|
|
95
104
|
}
|
|
96
|
-
|
|
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) {
|
|
105
|
+
if (this._allowlist.some((regExp)=>regExp.test(pathnameAndSearch))) {
|
|
112
106
|
if (process.env.NODE_ENV !== "production") {
|
|
113
|
-
logger.
|
|
114
|
-
}
|
|
115
|
-
if (originalRequest) {
|
|
116
|
-
await this.runCallbacks("fetchDidFail", {
|
|
117
|
-
error: error,
|
|
118
|
-
event,
|
|
119
|
-
originalRequest: originalRequest.clone(),
|
|
120
|
-
request: pluginFilteredRequest.clone()
|
|
121
|
-
});
|
|
107
|
+
logger.debug(`The navigation route ${pathnameAndSearch} is being used.`);
|
|
122
108
|
}
|
|
123
|
-
|
|
109
|
+
return true;
|
|
124
110
|
}
|
|
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
111
|
if (process.env.NODE_ENV !== "production") {
|
|
145
|
-
|
|
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;
|
|
112
|
+
logger.log(`The navigation route ${pathnameAndSearch} is not being used, since the URL being navigated to doesn't match the allowlist.`);
|
|
159
113
|
}
|
|
160
|
-
return
|
|
114
|
+
return false;
|
|
161
115
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
class RegExpRoute extends Route {
|
|
119
|
+
constructor(regExp, handler, method){
|
|
166
120
|
if (process.env.NODE_ENV !== "production") {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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)
|
|
121
|
+
finalAssertExports.isInstance(regExp, RegExp, {
|
|
122
|
+
moduleName: "serwist",
|
|
123
|
+
className: "RegExpRoute",
|
|
124
|
+
funcName: "constructor",
|
|
125
|
+
paramName: "pattern"
|
|
180
126
|
});
|
|
181
127
|
}
|
|
182
|
-
const
|
|
183
|
-
|
|
184
|
-
if (
|
|
185
|
-
|
|
128
|
+
const match = ({ url })=>{
|
|
129
|
+
const result = regExp.exec(url.href);
|
|
130
|
+
if (!result) {
|
|
131
|
+
return;
|
|
186
132
|
}
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
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();
|
|
133
|
+
if (url.origin !== location.origin && result.index !== 0) {
|
|
134
|
+
if (process.env.NODE_ENV !== "production") {
|
|
135
|
+
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.`);
|
|
210
136
|
}
|
|
211
|
-
|
|
137
|
+
return;
|
|
212
138
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
cacheName,
|
|
217
|
-
oldResponse,
|
|
218
|
-
newResponse: responseToCache.clone(),
|
|
219
|
-
request: effectiveRequest,
|
|
220
|
-
event: this.event
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
return true;
|
|
139
|
+
return result.slice(1);
|
|
140
|
+
};
|
|
141
|
+
super(match, handler, method);
|
|
224
142
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
event: this.event,
|
|
234
|
-
params: this.params
|
|
235
|
-
}));
|
|
236
|
-
}
|
|
237
|
-
this._cacheKeys[key] = effectiveRequest;
|
|
238
|
-
}
|
|
239
|
-
return this._cacheKeys[key];
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const REVISION_SEARCH_PARAM = "__WB_REVISION__";
|
|
146
|
+
const createCacheKey = (entry)=>{
|
|
147
|
+
if (!entry) {
|
|
148
|
+
throw new SerwistError("add-to-cache-list-unexpected-type", {
|
|
149
|
+
entry
|
|
150
|
+
});
|
|
240
151
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
return false;
|
|
152
|
+
if (typeof entry === "string") {
|
|
153
|
+
const urlObject = new URL(entry, location.href);
|
|
154
|
+
return {
|
|
155
|
+
cacheKey: urlObject.href,
|
|
156
|
+
url: urlObject.href
|
|
157
|
+
};
|
|
248
158
|
}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
159
|
+
const { revision, url } = entry;
|
|
160
|
+
if (!url) {
|
|
161
|
+
throw new SerwistError("add-to-cache-list-unexpected-type", {
|
|
162
|
+
entry
|
|
163
|
+
});
|
|
253
164
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
...param,
|
|
261
|
-
state
|
|
262
|
-
};
|
|
263
|
-
return plugin[name](statefulParam);
|
|
264
|
-
};
|
|
265
|
-
yield statefulCallback;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
165
|
+
if (!revision) {
|
|
166
|
+
const urlObject = new URL(url, location.href);
|
|
167
|
+
return {
|
|
168
|
+
cacheKey: urlObject.href,
|
|
169
|
+
url: urlObject.href
|
|
170
|
+
};
|
|
268
171
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
172
|
+
const cacheKeyURL = new URL(url, location.href);
|
|
173
|
+
const originalURL = new URL(url, location.href);
|
|
174
|
+
cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision);
|
|
175
|
+
return {
|
|
176
|
+
cacheKey: cacheKeyURL.href,
|
|
177
|
+
url: originalURL.href
|
|
178
|
+
};
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const logGroup = (groupTitle, deletedURLs)=>{
|
|
182
|
+
logger.groupCollapsed(groupTitle);
|
|
183
|
+
for (const url of deletedURLs){
|
|
184
|
+
logger.log(url);
|
|
272
185
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
186
|
+
logger.groupEnd();
|
|
187
|
+
};
|
|
188
|
+
const printCleanupDetails = (deletedURLs)=>{
|
|
189
|
+
const deletionCount = deletedURLs.length;
|
|
190
|
+
if (deletionCount > 0) {
|
|
191
|
+
logger.groupCollapsed(`During precaching cleanup, ${deletionCount} cached request${deletionCount === 1 ? " was" : "s were"} deleted.`);
|
|
192
|
+
logGroup("Deleted Cache Requests", deletedURLs);
|
|
193
|
+
logger.groupEnd();
|
|
278
194
|
}
|
|
279
|
-
|
|
280
|
-
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
function _nestedGroup(groupTitle, urls) {
|
|
198
|
+
if (urls.length === 0) {
|
|
199
|
+
return;
|
|
281
200
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
}
|
|
296
|
-
return undefined;
|
|
297
|
-
}
|
|
201
|
+
logger.groupCollapsed(groupTitle);
|
|
202
|
+
for (const url of urls){
|
|
203
|
+
logger.log(url);
|
|
204
|
+
}
|
|
205
|
+
logger.groupEnd();
|
|
206
|
+
}
|
|
207
|
+
const printInstallDetails = (urlsToPrecache, urlsAlreadyPrecached)=>{
|
|
208
|
+
const precachedCount = urlsToPrecache.length;
|
|
209
|
+
const alreadyPrecachedCount = urlsAlreadyPrecached.length;
|
|
210
|
+
if (precachedCount || alreadyPrecachedCount) {
|
|
211
|
+
let message = `Precaching ${precachedCount} file${precachedCount === 1 ? "" : "s"}.`;
|
|
212
|
+
if (alreadyPrecachedCount > 0) {
|
|
213
|
+
message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? " is" : "s are"} already cached.`;
|
|
298
214
|
}
|
|
299
|
-
|
|
215
|
+
logger.groupCollapsed(message);
|
|
216
|
+
_nestedGroup("View newly precached URLs.", urlsToPrecache);
|
|
217
|
+
_nestedGroup("View previously precached URLs.", urlsAlreadyPrecached);
|
|
218
|
+
logger.groupEnd();
|
|
300
219
|
}
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
}) || undefined;
|
|
310
|
-
pluginsUsed = true;
|
|
311
|
-
if (!responseToCache) {
|
|
312
|
-
break;
|
|
313
|
-
}
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
class PrecacheInstallReportPlugin {
|
|
223
|
+
updatedURLs = [];
|
|
224
|
+
notUpdatedURLs = [];
|
|
225
|
+
handlerWillStart = async ({ request, state })=>{
|
|
226
|
+
if (state) {
|
|
227
|
+
state.originalRequest = request;
|
|
314
228
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
229
|
+
};
|
|
230
|
+
cachedResponseWillBeUsed = async ({ event, state, cachedResponse })=>{
|
|
231
|
+
if (event.type === "install") {
|
|
232
|
+
if (state?.originalRequest && state.originalRequest instanceof Request) {
|
|
233
|
+
const url = state.originalRequest.url;
|
|
234
|
+
if (cachedResponse) {
|
|
235
|
+
this.notUpdatedURLs.push(url);
|
|
236
|
+
} else {
|
|
237
|
+
this.updatedURLs.push(url);
|
|
323
238
|
}
|
|
324
|
-
responseToCache = undefined;
|
|
325
239
|
}
|
|
326
240
|
}
|
|
327
|
-
return
|
|
328
|
-
}
|
|
241
|
+
return cachedResponse;
|
|
242
|
+
};
|
|
329
243
|
}
|
|
330
244
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
this.plugins = options.plugins || [];
|
|
339
|
-
this.fetchOptions = options.fetchOptions;
|
|
340
|
-
this.matchOptions = options.matchOptions;
|
|
245
|
+
const removeIgnoredSearchParams = (urlObject, ignoreURLParametersMatching = [])=>{
|
|
246
|
+
for (const paramName of [
|
|
247
|
+
...urlObject.searchParams.keys()
|
|
248
|
+
]){
|
|
249
|
+
if (ignoreURLParametersMatching.some((regExp)=>regExp.test(paramName))) {
|
|
250
|
+
urlObject.searchParams.delete(paramName);
|
|
251
|
+
}
|
|
341
252
|
}
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
253
|
+
return urlObject;
|
|
254
|
+
};
|
|
255
|
+
|
|
256
|
+
function* generateURLVariations(url, { directoryIndex = "index.html", ignoreURLParametersMatching = [
|
|
257
|
+
/^utm_/,
|
|
258
|
+
/^fbclid$/
|
|
259
|
+
], cleanURLs = true, urlManipulation } = {}) {
|
|
260
|
+
const urlObject = new URL(url, location.href);
|
|
261
|
+
urlObject.hash = "";
|
|
262
|
+
yield urlObject.href;
|
|
263
|
+
const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching);
|
|
264
|
+
yield urlWithoutIgnoredParams.href;
|
|
265
|
+
if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith("/")) {
|
|
266
|
+
const directoryURL = new URL(urlWithoutIgnoredParams.href);
|
|
267
|
+
directoryURL.pathname += directoryIndex;
|
|
268
|
+
yield directoryURL.href;
|
|
345
269
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
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
|
-
];
|
|
270
|
+
if (cleanURLs) {
|
|
271
|
+
const cleanURL = new URL(urlWithoutIgnoredParams.href);
|
|
272
|
+
cleanURL.pathname += ".html";
|
|
273
|
+
yield cleanURL.href;
|
|
370
274
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
request
|
|
275
|
+
if (urlManipulation) {
|
|
276
|
+
const additionalURLs = urlManipulation({
|
|
277
|
+
url: urlObject
|
|
375
278
|
});
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
}
|
|
279
|
+
for (const urlToAttempt of additionalURLs){
|
|
280
|
+
yield urlToAttempt.href;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
class PrecacheRoute extends Route {
|
|
286
|
+
constructor(controller, options){
|
|
287
|
+
const match = ({ request })=>{
|
|
288
|
+
const urlsToCacheKeys = controller.getUrlsToPrecacheKeys();
|
|
289
|
+
for (const possibleURL of generateURLVariations(request.url, options)){
|
|
290
|
+
const cacheKey = urlsToCacheKeys.get(possibleURL);
|
|
291
|
+
if (cacheKey) {
|
|
292
|
+
const integrity = controller.getIntegrityForPrecacheKey(cacheKey);
|
|
293
|
+
return {
|
|
294
|
+
cacheKey,
|
|
295
|
+
integrity
|
|
296
|
+
};
|
|
395
297
|
}
|
|
396
298
|
}
|
|
397
|
-
if (!response) {
|
|
398
|
-
throw error;
|
|
399
|
-
}
|
|
400
299
|
if (process.env.NODE_ENV !== "production") {
|
|
401
|
-
|
|
300
|
+
logger.debug(`Precaching did not find a match for ${getFriendlyURL(request.url)}.`);
|
|
402
301
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
event,
|
|
407
|
-
request,
|
|
408
|
-
response
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
return response;
|
|
302
|
+
return;
|
|
303
|
+
};
|
|
304
|
+
super(match, controller.strategy);
|
|
412
305
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const copyResponse = async (response, modifier)=>{
|
|
309
|
+
let origin = null;
|
|
310
|
+
if (response.url) {
|
|
311
|
+
const responseURL = new URL(response.url);
|
|
312
|
+
origin = responseURL.origin;
|
|
313
|
+
}
|
|
314
|
+
if (origin !== self.location.origin) {
|
|
315
|
+
throw new SerwistError("cross-origin-copy-response", {
|
|
316
|
+
origin
|
|
436
317
|
});
|
|
437
|
-
handler.destroy();
|
|
438
|
-
if (error) {
|
|
439
|
-
throw error;
|
|
440
|
-
}
|
|
441
318
|
}
|
|
442
|
-
|
|
319
|
+
const clonedResponse = response.clone();
|
|
320
|
+
const responseInit = {
|
|
321
|
+
headers: new Headers(clonedResponse.headers),
|
|
322
|
+
status: clonedResponse.status,
|
|
323
|
+
statusText: clonedResponse.statusText
|
|
324
|
+
};
|
|
325
|
+
const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit;
|
|
326
|
+
const body = canConstructResponseFromBodyStream() ? clonedResponse.body : await clonedResponse.blob();
|
|
327
|
+
return new Response(body, modifiedResponseInit);
|
|
328
|
+
};
|
|
443
329
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
330
|
+
function toRequest(input) {
|
|
331
|
+
return typeof input === "string" ? new Request(input) : input;
|
|
332
|
+
}
|
|
333
|
+
class StrategyHandler {
|
|
334
|
+
event;
|
|
335
|
+
request;
|
|
336
|
+
url;
|
|
337
|
+
params;
|
|
338
|
+
_cacheKeys = {};
|
|
339
|
+
_strategy;
|
|
340
|
+
_handlerDeferred;
|
|
341
|
+
_extendLifetimePromises;
|
|
342
|
+
_plugins;
|
|
343
|
+
_pluginStateMap;
|
|
344
|
+
constructor(strategy, options){
|
|
345
|
+
if (process.env.NODE_ENV !== "production") {
|
|
346
|
+
finalAssertExports.isInstance(options.event, ExtendableEvent, {
|
|
347
|
+
moduleName: "serwist",
|
|
348
|
+
className: "StrategyHandler",
|
|
349
|
+
funcName: "constructor",
|
|
350
|
+
paramName: "options.event"
|
|
351
|
+
});
|
|
352
|
+
finalAssertExports.isInstance(options.request, Request, {
|
|
353
|
+
moduleName: "serwist",
|
|
354
|
+
className: "StrategyHandler",
|
|
355
|
+
funcName: "constructor",
|
|
356
|
+
paramName: "options.request"
|
|
357
|
+
});
|
|
452
358
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
359
|
+
this.event = options.event;
|
|
360
|
+
this.request = options.request;
|
|
361
|
+
if (options.url) {
|
|
362
|
+
this.url = options.url;
|
|
363
|
+
this.params = options.params;
|
|
457
364
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
365
|
+
this._strategy = strategy;
|
|
366
|
+
this._handlerDeferred = new Deferred();
|
|
367
|
+
this._extendLifetimePromises = [];
|
|
368
|
+
this._plugins = [
|
|
369
|
+
...strategy.plugins
|
|
370
|
+
];
|
|
371
|
+
this._pluginStateMap = new Map();
|
|
372
|
+
for (const plugin of this._plugins){
|
|
373
|
+
this._pluginStateMap.set(plugin, {});
|
|
374
|
+
}
|
|
375
|
+
this.event.waitUntil(this._handlerDeferred.promise);
|
|
464
376
|
}
|
|
465
|
-
async
|
|
466
|
-
const
|
|
377
|
+
async fetch(input) {
|
|
378
|
+
const { event } = this;
|
|
379
|
+
let request = toRequest(input);
|
|
380
|
+
const preloadResponse = await this.getPreloadResponse();
|
|
467
381
|
if (preloadResponse) {
|
|
468
382
|
return preloadResponse;
|
|
469
383
|
}
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
384
|
+
const originalRequest = this.hasCallback("fetchDidFail") ? request.clone() : null;
|
|
385
|
+
try {
|
|
386
|
+
for (const cb of this.iterateCallbacks("requestWillFetch")){
|
|
387
|
+
request = await cb({
|
|
388
|
+
request: request.clone(),
|
|
389
|
+
event
|
|
390
|
+
});
|
|
391
|
+
}
|
|
392
|
+
} catch (err) {
|
|
393
|
+
if (err instanceof Error) {
|
|
394
|
+
throw new SerwistError("plugin-error-request-will-fetch", {
|
|
395
|
+
thrownErrorMessage: err.message
|
|
396
|
+
});
|
|
397
|
+
}
|
|
476
398
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
const params = handler.params || {};
|
|
482
|
-
if (this._fallbackToNetwork) {
|
|
399
|
+
const pluginFilteredRequest = request.clone();
|
|
400
|
+
try {
|
|
401
|
+
let fetchResponse;
|
|
402
|
+
fetchResponse = await fetch(request, request.mode === "navigate" ? undefined : this._strategy.fetchOptions);
|
|
483
403
|
if (process.env.NODE_ENV !== "production") {
|
|
484
|
-
logger.
|
|
404
|
+
logger.debug(`Network request for '${getFriendlyURL(request.url)}' returned a response with status '${fetchResponse.status}'.`);
|
|
485
405
|
}
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
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
|
-
}
|
|
406
|
+
for (const callback of this.iterateCallbacks("fetchDidSucceed")){
|
|
407
|
+
fetchResponse = await callback({
|
|
408
|
+
event,
|
|
409
|
+
request: pluginFilteredRequest,
|
|
410
|
+
response: fetchResponse
|
|
411
|
+
});
|
|
500
412
|
}
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
logger.log(response);
|
|
516
|
-
logger.groupEnd();
|
|
517
|
-
logger.groupEnd();
|
|
413
|
+
return fetchResponse;
|
|
414
|
+
} catch (error) {
|
|
415
|
+
if (process.env.NODE_ENV !== "production") {
|
|
416
|
+
logger.log(`Network request for '${getFriendlyURL(request.url)}' threw an error.`, error);
|
|
417
|
+
}
|
|
418
|
+
if (originalRequest) {
|
|
419
|
+
await this.runCallbacks("fetchDidFail", {
|
|
420
|
+
error: error,
|
|
421
|
+
event,
|
|
422
|
+
originalRequest: originalRequest.clone(),
|
|
423
|
+
request: pluginFilteredRequest.clone()
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
throw error;
|
|
518
427
|
}
|
|
519
|
-
return response;
|
|
520
428
|
}
|
|
521
|
-
async
|
|
522
|
-
this.
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
if (!wasCached) {
|
|
526
|
-
throw new SerwistError("bad-precaching-response", {
|
|
527
|
-
url: request.url,
|
|
528
|
-
status: response.status
|
|
529
|
-
});
|
|
530
|
-
}
|
|
429
|
+
async fetchAndCachePut(input) {
|
|
430
|
+
const response = await this.fetch(input);
|
|
431
|
+
const responseClone = response.clone();
|
|
432
|
+
void this.waitUntil(this.cachePut(input, responseClone));
|
|
531
433
|
return response;
|
|
532
434
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
let
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
435
|
+
async cacheMatch(key) {
|
|
436
|
+
const request = toRequest(key);
|
|
437
|
+
let cachedResponse;
|
|
438
|
+
const { cacheName, matchOptions } = this._strategy;
|
|
439
|
+
const effectiveRequest = await this.getCacheKey(request, "read");
|
|
440
|
+
const multiMatchOptions = {
|
|
441
|
+
...matchOptions,
|
|
442
|
+
...{
|
|
443
|
+
cacheName
|
|
542
444
|
}
|
|
543
|
-
|
|
544
|
-
|
|
445
|
+
};
|
|
446
|
+
cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);
|
|
447
|
+
if (process.env.NODE_ENV !== "production") {
|
|
448
|
+
if (cachedResponse) {
|
|
449
|
+
logger.debug(`Found a cached response in '${cacheName}'.`);
|
|
450
|
+
} else {
|
|
451
|
+
logger.debug(`No cached response found in '${cacheName}'.`);
|
|
545
452
|
}
|
|
546
453
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
454
|
+
for (const callback of this.iterateCallbacks("cachedResponseWillBeUsed")){
|
|
455
|
+
cachedResponse = await callback({
|
|
456
|
+
cacheName,
|
|
457
|
+
matchOptions,
|
|
458
|
+
cachedResponse,
|
|
459
|
+
request: effectiveRequest,
|
|
460
|
+
event: this.event
|
|
461
|
+
}) || undefined;
|
|
551
462
|
}
|
|
463
|
+
return cachedResponse;
|
|
552
464
|
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
const
|
|
557
|
-
"DELETE",
|
|
558
|
-
"GET",
|
|
559
|
-
"HEAD",
|
|
560
|
-
"PATCH",
|
|
561
|
-
"POST",
|
|
562
|
-
"PUT"
|
|
563
|
-
];
|
|
564
|
-
|
|
565
|
-
const normalizeHandler = (handler)=>{
|
|
566
|
-
if (handler && typeof handler === "object") {
|
|
465
|
+
async cachePut(key, response) {
|
|
466
|
+
const request = toRequest(key);
|
|
467
|
+
await timeout(0);
|
|
468
|
+
const effectiveRequest = await this.getCacheKey(request, "write");
|
|
567
469
|
if (process.env.NODE_ENV !== "production") {
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
470
|
+
if (effectiveRequest.method && effectiveRequest.method !== "GET") {
|
|
471
|
+
throw new SerwistError("attempt-to-cache-non-get-request", {
|
|
472
|
+
url: getFriendlyURL(effectiveRequest.url),
|
|
473
|
+
method: effectiveRequest.method
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
if (!response) {
|
|
478
|
+
if (process.env.NODE_ENV !== "production") {
|
|
479
|
+
logger.error(`Cannot cache non-existent response for '${getFriendlyURL(effectiveRequest.url)}'.`);
|
|
480
|
+
}
|
|
481
|
+
throw new SerwistError("cache-put-with-no-response", {
|
|
482
|
+
url: getFriendlyURL(effectiveRequest.url)
|
|
573
483
|
});
|
|
574
484
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
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){
|
|
485
|
+
const responseToCache = await this._ensureResponseSafeToCache(response);
|
|
486
|
+
if (!responseToCache) {
|
|
487
|
+
if (process.env.NODE_ENV !== "production") {
|
|
488
|
+
logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' will not be cached.`, responseToCache);
|
|
489
|
+
}
|
|
490
|
+
return false;
|
|
491
|
+
}
|
|
492
|
+
const { cacheName, matchOptions } = this._strategy;
|
|
493
|
+
const cache = await self.caches.open(cacheName);
|
|
596
494
|
if (process.env.NODE_ENV !== "production") {
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
funcName: "constructor",
|
|
601
|
-
paramName: "match"
|
|
602
|
-
});
|
|
603
|
-
if (method) {
|
|
604
|
-
finalAssertExports.isOneOf(method, validMethods, {
|
|
605
|
-
paramName: "method"
|
|
606
|
-
});
|
|
495
|
+
const vary = response.headers.get("Vary");
|
|
496
|
+
if (vary && matchOptions?.ignoreVary !== true) {
|
|
497
|
+
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.`);
|
|
607
498
|
}
|
|
608
499
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
500
|
+
const hasCacheUpdateCallback = this.hasCallback("cacheDidUpdate");
|
|
501
|
+
const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams(cache, effectiveRequest.clone(), [
|
|
502
|
+
"__WB_REVISION__"
|
|
503
|
+
], matchOptions) : null;
|
|
504
|
+
if (process.env.NODE_ENV !== "production") {
|
|
505
|
+
logger.debug(`Updating the '${cacheName}' cache with a new Response for ${getFriendlyURL(effectiveRequest.url)}.`);
|
|
506
|
+
}
|
|
507
|
+
try {
|
|
508
|
+
await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);
|
|
509
|
+
} catch (error) {
|
|
510
|
+
if (error instanceof Error) {
|
|
511
|
+
if (error.name === "QuotaExceededError") {
|
|
512
|
+
await executeQuotaErrorCallbacks();
|
|
513
|
+
}
|
|
514
|
+
throw error;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
for (const callback of this.iterateCallbacks("cacheDidUpdate")){
|
|
518
|
+
await callback({
|
|
519
|
+
cacheName,
|
|
520
|
+
oldResponse,
|
|
521
|
+
newResponse: responseToCache.clone(),
|
|
522
|
+
request: effectiveRequest,
|
|
523
|
+
event: this.event
|
|
524
|
+
});
|
|
624
525
|
}
|
|
526
|
+
return true;
|
|
625
527
|
}
|
|
626
|
-
|
|
627
|
-
}
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
yield directoryURL.href;
|
|
528
|
+
async getCacheKey(request, mode) {
|
|
529
|
+
const key = `${request.url} | ${mode}`;
|
|
530
|
+
if (!this._cacheKeys[key]) {
|
|
531
|
+
let effectiveRequest = request;
|
|
532
|
+
for (const callback of this.iterateCallbacks("cacheKeyWillBeUsed")){
|
|
533
|
+
effectiveRequest = toRequest(await callback({
|
|
534
|
+
mode,
|
|
535
|
+
request: effectiveRequest,
|
|
536
|
+
event: this.event,
|
|
537
|
+
params: this.params
|
|
538
|
+
}));
|
|
539
|
+
}
|
|
540
|
+
this._cacheKeys[key] = effectiveRequest;
|
|
541
|
+
}
|
|
542
|
+
return this._cacheKeys[key];
|
|
642
543
|
}
|
|
643
|
-
|
|
644
|
-
const
|
|
645
|
-
|
|
646
|
-
|
|
544
|
+
hasCallback(name) {
|
|
545
|
+
for (const plugin of this._strategy.plugins){
|
|
546
|
+
if (name in plugin) {
|
|
547
|
+
return true;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
return false;
|
|
647
551
|
}
|
|
648
|
-
|
|
649
|
-
const
|
|
650
|
-
|
|
651
|
-
});
|
|
652
|
-
for (const urlToAttempt of additionalURLs){
|
|
653
|
-
yield urlToAttempt.href;
|
|
552
|
+
async runCallbacks(name, param) {
|
|
553
|
+
for (const callback of this.iterateCallbacks(name)){
|
|
554
|
+
await callback(param);
|
|
654
555
|
}
|
|
655
556
|
}
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
if (cacheKey) {
|
|
665
|
-
const integrity = controller.getIntegrityForPrecacheKey(cacheKey);
|
|
666
|
-
return {
|
|
667
|
-
cacheKey,
|
|
668
|
-
integrity
|
|
557
|
+
*iterateCallbacks(name) {
|
|
558
|
+
for (const plugin of this._strategy.plugins){
|
|
559
|
+
if (typeof plugin[name] === "function") {
|
|
560
|
+
const state = this._pluginStateMap.get(plugin);
|
|
561
|
+
const statefulCallback = (param)=>{
|
|
562
|
+
const statefulParam = {
|
|
563
|
+
...param,
|
|
564
|
+
state
|
|
669
565
|
};
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
logger.debug(`Precaching did not find a match for ${getFriendlyURL(request.url)}.`);
|
|
566
|
+
return plugin[name](statefulParam);
|
|
567
|
+
};
|
|
568
|
+
yield statefulCallback;
|
|
674
569
|
}
|
|
675
|
-
|
|
676
|
-
};
|
|
677
|
-
super(match, controller.strategy);
|
|
570
|
+
}
|
|
678
571
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
_fallbackUrls;
|
|
683
|
-
_precacheController;
|
|
684
|
-
constructor({ fallbackUrls, precacheController }){
|
|
685
|
-
this._fallbackUrls = fallbackUrls;
|
|
686
|
-
this._precacheController = precacheController;
|
|
572
|
+
waitUntil(promise) {
|
|
573
|
+
this._extendLifetimePromises.push(promise);
|
|
574
|
+
return promise;
|
|
687
575
|
}
|
|
688
|
-
async
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
576
|
+
async doneWaiting() {
|
|
577
|
+
let promise = undefined;
|
|
578
|
+
while(promise = this._extendLifetimePromises.shift()){
|
|
579
|
+
await promise;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
destroy() {
|
|
583
|
+
this._handlerDeferred.resolve(null);
|
|
584
|
+
}
|
|
585
|
+
async getPreloadResponse() {
|
|
586
|
+
if (this.event instanceof FetchEvent && this.event.request.mode === "navigate" && "preloadResponse" in this.event) {
|
|
587
|
+
try {
|
|
588
|
+
const possiblePreloadResponse = await this.event.preloadResponse;
|
|
589
|
+
if (possiblePreloadResponse) {
|
|
590
|
+
if (process.env.NODE_ENV !== "production") {
|
|
591
|
+
logger.log(`Using a preloaded navigation response for '${getFriendlyURL(this.event.request.url)}'`);
|
|
592
|
+
}
|
|
593
|
+
return possiblePreloadResponse;
|
|
694
594
|
}
|
|
695
|
-
}
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
return fallbackResponse;
|
|
595
|
+
} catch (error) {
|
|
596
|
+
if (process.env.NODE_ENV !== "production") {
|
|
597
|
+
logger.error(error);
|
|
699
598
|
}
|
|
599
|
+
return undefined;
|
|
700
600
|
}
|
|
701
601
|
}
|
|
702
602
|
return undefined;
|
|
703
603
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
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);
|
|
604
|
+
async _ensureResponseSafeToCache(response) {
|
|
605
|
+
let responseToCache = response;
|
|
606
|
+
let pluginsUsed = false;
|
|
607
|
+
for (const callback of this.iterateCallbacks("cacheWillUpdate")){
|
|
608
|
+
responseToCache = await callback({
|
|
609
|
+
request: this.request,
|
|
610
|
+
response: responseToCache,
|
|
611
|
+
event: this.event
|
|
612
|
+
}) || undefined;
|
|
613
|
+
pluginsUsed = true;
|
|
614
|
+
if (!responseToCache) {
|
|
615
|
+
break;
|
|
616
|
+
}
|
|
729
617
|
}
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
}
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
event
|
|
742
|
-
});
|
|
743
|
-
});
|
|
618
|
+
if (!pluginsUsed) {
|
|
619
|
+
if (responseToCache && responseToCache.status !== 200) {
|
|
620
|
+
if (process.env.NODE_ENV !== "production") {
|
|
621
|
+
if (responseToCache.status === 0) {
|
|
622
|
+
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.`);
|
|
623
|
+
} else {
|
|
624
|
+
logger.debug(`The response for '${this.request.url}' returned a status code of '${response.status}' and won't be cached as a result.`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
responseToCache = undefined;
|
|
628
|
+
}
|
|
744
629
|
}
|
|
745
|
-
|
|
746
|
-
warmRuntimeCache(entries) {
|
|
747
|
-
if (!this._options.warmEntries) this._options.warmEntries = [];
|
|
748
|
-
this._options.warmEntries.push(...entries);
|
|
630
|
+
return responseToCache;
|
|
749
631
|
}
|
|
750
632
|
}
|
|
751
633
|
|
|
752
|
-
class
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
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;
|
|
634
|
+
class Strategy {
|
|
635
|
+
cacheName;
|
|
636
|
+
plugins;
|
|
637
|
+
fetchOptions;
|
|
638
|
+
matchOptions;
|
|
639
|
+
constructor(options = {}){
|
|
640
|
+
this.cacheName = cacheNames$1.getRuntimeName(options.cacheName);
|
|
641
|
+
this.plugins = options.plugins || [];
|
|
642
|
+
this.fetchOptions = options.fetchOptions;
|
|
643
|
+
this.matchOptions = options.matchOptions;
|
|
775
644
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
645
|
+
handle(options) {
|
|
646
|
+
const [responseDone] = this.handleAll(options);
|
|
647
|
+
return responseDone;
|
|
648
|
+
}
|
|
649
|
+
handleAll(options) {
|
|
650
|
+
if (options instanceof FetchEvent) {
|
|
651
|
+
options = {
|
|
652
|
+
event: options,
|
|
653
|
+
request: options.request
|
|
654
|
+
};
|
|
779
655
|
}
|
|
780
|
-
const
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
656
|
+
const event = options.event;
|
|
657
|
+
const request = typeof options.request === "string" ? new Request(options.request) : options.request;
|
|
658
|
+
const handler = new StrategyHandler(this, options.url ? {
|
|
659
|
+
event,
|
|
660
|
+
request,
|
|
661
|
+
url: options.url,
|
|
662
|
+
params: options.params
|
|
663
|
+
} : {
|
|
664
|
+
event,
|
|
665
|
+
request
|
|
666
|
+
});
|
|
667
|
+
const responseDone = this._getResponse(handler, request, event);
|
|
668
|
+
const handlerDone = this._awaitComplete(responseDone, handler, request, event);
|
|
669
|
+
return [
|
|
670
|
+
responseDone,
|
|
671
|
+
handlerDone
|
|
672
|
+
];
|
|
673
|
+
}
|
|
674
|
+
async _getResponse(handler, request, event) {
|
|
675
|
+
await handler.runCallbacks("handlerWillStart", {
|
|
676
|
+
event,
|
|
677
|
+
request
|
|
678
|
+
});
|
|
679
|
+
let response = undefined;
|
|
680
|
+
try {
|
|
681
|
+
response = await this._handle(request, handler);
|
|
682
|
+
if (response === undefined || response.type === "error") {
|
|
683
|
+
throw new SerwistError("no-response", {
|
|
684
|
+
url: request.url
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
} catch (error) {
|
|
688
|
+
if (error instanceof Error) {
|
|
689
|
+
for (const callback of handler.iterateCallbacks("handlerDidError")){
|
|
690
|
+
response = await callback({
|
|
691
|
+
error,
|
|
692
|
+
event,
|
|
693
|
+
request
|
|
694
|
+
});
|
|
695
|
+
if (response !== undefined) {
|
|
696
|
+
break;
|
|
697
|
+
}
|
|
785
698
|
}
|
|
786
|
-
return false;
|
|
787
699
|
}
|
|
788
|
-
|
|
789
|
-
|
|
700
|
+
if (!response) {
|
|
701
|
+
throw error;
|
|
702
|
+
}
|
|
790
703
|
if (process.env.NODE_ENV !== "production") {
|
|
791
|
-
logger.
|
|
704
|
+
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.`);
|
|
792
705
|
}
|
|
793
|
-
return true;
|
|
794
706
|
}
|
|
795
|
-
|
|
796
|
-
|
|
707
|
+
for (const callback of handler.iterateCallbacks("handlerWillRespond")){
|
|
708
|
+
response = await callback({
|
|
709
|
+
event,
|
|
710
|
+
request,
|
|
711
|
+
response
|
|
712
|
+
});
|
|
797
713
|
}
|
|
798
|
-
return
|
|
714
|
+
return response;
|
|
799
715
|
}
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
716
|
+
async _awaitComplete(responseDone, handler, request, event) {
|
|
717
|
+
let response = undefined;
|
|
718
|
+
let error = undefined;
|
|
719
|
+
try {
|
|
720
|
+
response = await responseDone;
|
|
721
|
+
} catch (error) {}
|
|
722
|
+
try {
|
|
723
|
+
await handler.runCallbacks("handlerDidRespond", {
|
|
724
|
+
event,
|
|
725
|
+
request,
|
|
726
|
+
response
|
|
810
727
|
});
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
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;
|
|
728
|
+
await handler.doneWaiting();
|
|
729
|
+
} catch (waitUntilError) {
|
|
730
|
+
if (waitUntilError instanceof Error) {
|
|
731
|
+
error = waitUntilError;
|
|
822
732
|
}
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
733
|
+
}
|
|
734
|
+
await handler.runCallbacks("handlerDidComplete", {
|
|
735
|
+
event,
|
|
736
|
+
request,
|
|
737
|
+
response,
|
|
738
|
+
error
|
|
739
|
+
});
|
|
740
|
+
handler.destroy();
|
|
741
|
+
if (error) {
|
|
742
|
+
throw error;
|
|
743
|
+
}
|
|
826
744
|
}
|
|
827
745
|
}
|
|
828
746
|
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
747
|
+
class PrecacheStrategy extends Strategy {
|
|
748
|
+
_fallbackToNetwork;
|
|
749
|
+
static defaultPrecacheCacheabilityPlugin = {
|
|
750
|
+
async cacheWillUpdate ({ response }) {
|
|
751
|
+
if (!response || response.status >= 400) {
|
|
752
|
+
return null;
|
|
753
|
+
}
|
|
836
754
|
return response;
|
|
837
755
|
}
|
|
838
|
-
|
|
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();
|
|
756
|
+
};
|
|
757
|
+
static copyRedirectedCacheableResponsesPlugin = {
|
|
758
|
+
async cacheWillUpdate ({ response }) {
|
|
759
|
+
return response.redirected ? await copyResponse(response) : response;
|
|
849
760
|
}
|
|
850
|
-
}
|
|
851
|
-
};
|
|
852
|
-
|
|
853
|
-
class NetworkFirst extends Strategy {
|
|
854
|
-
_networkTimeoutSeconds;
|
|
761
|
+
};
|
|
855
762
|
constructor(options = {}){
|
|
763
|
+
options.cacheName = cacheNames$1.getPrecacheName(options.cacheName);
|
|
856
764
|
super(options);
|
|
857
|
-
|
|
858
|
-
|
|
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
|
-
}
|
|
765
|
+
this._fallbackToNetwork = options.fallbackToNetwork !== false;
|
|
766
|
+
this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);
|
|
871
767
|
}
|
|
872
768
|
async _handle(request, handler) {
|
|
873
|
-
const
|
|
874
|
-
if (
|
|
875
|
-
|
|
876
|
-
moduleName: "serwist",
|
|
877
|
-
className: this.constructor.name,
|
|
878
|
-
funcName: "handle",
|
|
879
|
-
paramName: "makeRequest"
|
|
880
|
-
});
|
|
769
|
+
const preloadResponse = await handler.getPreloadResponse();
|
|
770
|
+
if (preloadResponse) {
|
|
771
|
+
return preloadResponse;
|
|
881
772
|
}
|
|
882
|
-
const
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
773
|
+
const response = await handler.cacheMatch(request);
|
|
774
|
+
if (response) {
|
|
775
|
+
return response;
|
|
776
|
+
}
|
|
777
|
+
if (handler.event && handler.event.type === "install") {
|
|
778
|
+
return await this._handleInstall(request, handler);
|
|
779
|
+
}
|
|
780
|
+
return await this._handleFetch(request, handler);
|
|
781
|
+
}
|
|
782
|
+
async _handleFetch(request, handler) {
|
|
783
|
+
let response = undefined;
|
|
784
|
+
const params = handler.params || {};
|
|
785
|
+
if (this._fallbackToNetwork) {
|
|
786
|
+
if (process.env.NODE_ENV !== "production") {
|
|
787
|
+
logger.warn(`The precached response for ${getFriendlyURL(request.url)} in ${this.cacheName} was not found. Falling back to the network.`);
|
|
788
|
+
}
|
|
789
|
+
const integrityInManifest = params.integrity;
|
|
790
|
+
const integrityInRequest = request.integrity;
|
|
791
|
+
const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest;
|
|
792
|
+
response = await handler.fetch(new Request(request, {
|
|
793
|
+
integrity: request.mode !== "no-cors" ? integrityInRequest || integrityInManifest : undefined
|
|
794
|
+
}));
|
|
795
|
+
if (integrityInManifest && noIntegrityConflict && request.mode !== "no-cors") {
|
|
796
|
+
this._useDefaultCacheabilityPluginIfNeeded();
|
|
797
|
+
const wasCached = await handler.cachePut(request, response.clone());
|
|
798
|
+
if (process.env.NODE_ENV !== "production") {
|
|
799
|
+
if (wasCached) {
|
|
800
|
+
logger.log(`A response for ${getFriendlyURL(request.url)} was used to "repair" the precache.`);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
} else {
|
|
805
|
+
throw new SerwistError("missing-precache-entry", {
|
|
806
|
+
cacheName: this.cacheName,
|
|
807
|
+
url: request.url
|
|
889
808
|
});
|
|
890
|
-
timeoutId = id;
|
|
891
|
-
promises.push(promise);
|
|
892
809
|
}
|
|
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
810
|
if (process.env.NODE_ENV !== "production") {
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
811
|
+
const cacheKey = params.cacheKey || await handler.getCacheKey(request, "read");
|
|
812
|
+
logger.groupCollapsed(`Precaching is responding to: ${getFriendlyURL(request.url)}`);
|
|
813
|
+
logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`);
|
|
814
|
+
logger.groupCollapsed("View request details here.");
|
|
815
|
+
logger.log(request);
|
|
816
|
+
logger.groupEnd();
|
|
817
|
+
logger.groupCollapsed("View response details here.");
|
|
818
|
+
logger.log(response);
|
|
819
|
+
logger.groupEnd();
|
|
909
820
|
logger.groupEnd();
|
|
910
821
|
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
822
|
+
return response;
|
|
823
|
+
}
|
|
824
|
+
async _handleInstall(request, handler) {
|
|
825
|
+
this._useDefaultCacheabilityPluginIfNeeded();
|
|
826
|
+
const response = await handler.fetch(request);
|
|
827
|
+
const wasCached = await handler.cachePut(request, response.clone());
|
|
828
|
+
if (!wasCached) {
|
|
829
|
+
throw new SerwistError("bad-precaching-response", {
|
|
830
|
+
url: request.url,
|
|
831
|
+
status: response.status
|
|
914
832
|
});
|
|
915
833
|
}
|
|
916
834
|
return response;
|
|
917
835
|
}
|
|
918
|
-
|
|
919
|
-
let
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
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;
|
|
836
|
+
_useDefaultCacheabilityPluginIfNeeded() {
|
|
837
|
+
let defaultPluginIndex = null;
|
|
838
|
+
let cacheWillUpdatePluginCount = 0;
|
|
839
|
+
for (const [index, plugin] of this.plugins.entries()){
|
|
840
|
+
if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) {
|
|
841
|
+
continue;
|
|
942
842
|
}
|
|
943
|
-
|
|
944
|
-
|
|
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.");
|
|
843
|
+
if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) {
|
|
844
|
+
defaultPluginIndex = index;
|
|
952
845
|
}
|
|
953
|
-
|
|
954
|
-
|
|
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
|
-
}
|
|
846
|
+
if (plugin.cacheWillUpdate) {
|
|
847
|
+
cacheWillUpdatePluginCount++;
|
|
962
848
|
}
|
|
963
849
|
}
|
|
964
|
-
|
|
850
|
+
if (cacheWillUpdatePluginCount === 0) {
|
|
851
|
+
this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin);
|
|
852
|
+
} else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) {
|
|
853
|
+
this.plugins.splice(defaultPluginIndex, 1);
|
|
854
|
+
}
|
|
965
855
|
}
|
|
966
856
|
}
|
|
967
857
|
|
|
968
|
-
class
|
|
969
|
-
|
|
970
|
-
constructor(
|
|
971
|
-
|
|
972
|
-
this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
|
|
858
|
+
class PrecacheCacheKeyPlugin {
|
|
859
|
+
_precacheController;
|
|
860
|
+
constructor({ precacheController }){
|
|
861
|
+
this._precacheController = precacheController;
|
|
973
862
|
}
|
|
974
|
-
async
|
|
863
|
+
cacheKeyWillBeUsed = async ({ request, params })=>{
|
|
864
|
+
const cacheKey = params?.cacheKey || this._precacheController.getPrecacheKeyForUrl(request.url);
|
|
865
|
+
return cacheKey ? new Request(cacheKey, {
|
|
866
|
+
headers: request.headers
|
|
867
|
+
}) : request;
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
const parsePrecacheOptions = (controller, { cacheName, plugins, fetchOptions, matchOptions, fallbackToNetwork, directoryIndex, ignoreURLParametersMatching, cleanURLs, urlManipulation, cleanupOutdatedCaches, concurrency, navigateFallback, navigateFallbackAllowlist, navigateFallbackDenylist } = {})=>({
|
|
872
|
+
strategyOptions: {
|
|
873
|
+
cacheName: cacheNames$1.getPrecacheName(cacheName),
|
|
874
|
+
plugins: [
|
|
875
|
+
...plugins ?? [],
|
|
876
|
+
new PrecacheCacheKeyPlugin({
|
|
877
|
+
precacheController: controller
|
|
878
|
+
})
|
|
879
|
+
],
|
|
880
|
+
fetchOptions,
|
|
881
|
+
matchOptions,
|
|
882
|
+
fallbackToNetwork
|
|
883
|
+
},
|
|
884
|
+
routeOptions: {
|
|
885
|
+
directoryIndex,
|
|
886
|
+
ignoreURLParametersMatching,
|
|
887
|
+
cleanURLs,
|
|
888
|
+
urlManipulation
|
|
889
|
+
},
|
|
890
|
+
controllerOptions: {
|
|
891
|
+
cleanupOutdatedCaches,
|
|
892
|
+
concurrency: concurrency ?? 10,
|
|
893
|
+
navigateFallback,
|
|
894
|
+
navigateFallbackAllowlist,
|
|
895
|
+
navigateFallbackDenylist
|
|
896
|
+
}
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
class PrecacheController {
|
|
900
|
+
_urlsToCacheKeys = new Map();
|
|
901
|
+
_urlsToCacheModes = new Map();
|
|
902
|
+
_cacheKeysToIntegrities = new Map();
|
|
903
|
+
_strategy;
|
|
904
|
+
_options;
|
|
905
|
+
_routeOptions;
|
|
906
|
+
constructor(entries, precacheOptions){
|
|
907
|
+
const { strategyOptions, routeOptions, controllerOptions } = parsePrecacheOptions(this, precacheOptions);
|
|
908
|
+
this.addToCacheList(entries);
|
|
909
|
+
this._strategy = new PrecacheStrategy(strategyOptions);
|
|
910
|
+
this._options = controllerOptions;
|
|
911
|
+
this._routeOptions = routeOptions;
|
|
912
|
+
}
|
|
913
|
+
get strategy() {
|
|
914
|
+
return this._strategy;
|
|
915
|
+
}
|
|
916
|
+
addToCacheList(entries) {
|
|
975
917
|
if (process.env.NODE_ENV !== "production") {
|
|
976
|
-
finalAssertExports.
|
|
918
|
+
finalAssertExports.isArray(entries, {
|
|
977
919
|
moduleName: "serwist",
|
|
978
|
-
className:
|
|
979
|
-
funcName: "
|
|
980
|
-
paramName: "
|
|
920
|
+
className: "PrecacheController",
|
|
921
|
+
funcName: "addEntries",
|
|
922
|
+
paramName: "entries"
|
|
981
923
|
});
|
|
982
924
|
}
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
if (this._networkTimeoutSeconds) {
|
|
990
|
-
const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000);
|
|
991
|
-
promises.push(timeoutPromise);
|
|
925
|
+
const urlsToWarnAbout = [];
|
|
926
|
+
for (const entry of entries){
|
|
927
|
+
if (typeof entry === "string") {
|
|
928
|
+
urlsToWarnAbout.push(entry);
|
|
929
|
+
} else if (entry && !entry.integrity && entry.revision === undefined) {
|
|
930
|
+
urlsToWarnAbout.push(entry.url);
|
|
992
931
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
932
|
+
const { cacheKey, url } = createCacheKey(entry);
|
|
933
|
+
const cacheMode = typeof entry !== "string" && entry.revision ? "reload" : "default";
|
|
934
|
+
if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) {
|
|
935
|
+
throw new SerwistError("add-to-cache-list-conflicting-entries", {
|
|
936
|
+
firstEntry: this._urlsToCacheKeys.get(url),
|
|
937
|
+
secondEntry: cacheKey
|
|
938
|
+
});
|
|
996
939
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
940
|
+
if (typeof entry !== "string" && entry.integrity) {
|
|
941
|
+
if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) {
|
|
942
|
+
throw new SerwistError("add-to-cache-list-conflicting-integrities", {
|
|
943
|
+
url
|
|
944
|
+
});
|
|
945
|
+
}
|
|
946
|
+
this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);
|
|
1000
947
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
948
|
+
this._urlsToCacheKeys.set(url, cacheKey);
|
|
949
|
+
this._urlsToCacheModes.set(url, cacheMode);
|
|
950
|
+
if (urlsToWarnAbout.length > 0) {
|
|
951
|
+
const warningMessage = `Serwist is precaching URLs without revision info: ${urlsToWarnAbout.join(", ")}\nThis is generally NOT safe, as you risk serving outdated assets.`;
|
|
952
|
+
if (process.env.NODE_ENV === "production") {
|
|
953
|
+
console.warn(warningMessage);
|
|
954
|
+
} else {
|
|
955
|
+
logger.warn(warningMessage);
|
|
956
|
+
}
|
|
1008
957
|
}
|
|
1009
|
-
messages.printFinalResponse(response);
|
|
1010
|
-
logger.groupEnd();
|
|
1011
958
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
959
|
+
}
|
|
960
|
+
init({ serwist }) {
|
|
961
|
+
serwist.registerRoute(new PrecacheRoute(this, this._routeOptions));
|
|
962
|
+
if (this._options.navigateFallback) {
|
|
963
|
+
serwist.registerRoute(new NavigationRoute(this.createHandlerBoundToUrl(this._options.navigateFallback), {
|
|
964
|
+
allowlist: this._options.navigateFallbackAllowlist,
|
|
965
|
+
denylist: this._options.navigateFallbackDenylist
|
|
966
|
+
}));
|
|
1017
967
|
}
|
|
1018
|
-
return response;
|
|
1019
968
|
}
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
const
|
|
1025
|
-
const
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
969
|
+
async install({ event }) {
|
|
970
|
+
const installReportPlugin = new PrecacheInstallReportPlugin();
|
|
971
|
+
this._strategy.plugins.push(installReportPlugin);
|
|
972
|
+
await parallel(this._options.concurrency, Array.from(this._urlsToCacheKeys.entries()), async ([url, cacheKey])=>{
|
|
973
|
+
const integrity = this._cacheKeysToIntegrities.get(cacheKey);
|
|
974
|
+
const cacheMode = this._urlsToCacheModes.get(url);
|
|
975
|
+
const request = new Request(url, {
|
|
976
|
+
integrity,
|
|
977
|
+
cache: cacheMode,
|
|
978
|
+
credentials: "same-origin"
|
|
979
|
+
});
|
|
980
|
+
await Promise.all(this._strategy.handleAll({
|
|
981
|
+
event,
|
|
982
|
+
request,
|
|
983
|
+
url: new URL(request.url),
|
|
984
|
+
params: {
|
|
985
|
+
cacheKey
|
|
986
|
+
}
|
|
987
|
+
}));
|
|
1032
988
|
});
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
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();
|
|
989
|
+
const { updatedURLs, notUpdatedURLs } = installReportPlugin;
|
|
990
|
+
if (process.env.NODE_ENV !== "production") {
|
|
991
|
+
printInstallDetails(updatedURLs, notUpdatedURLs);
|
|
992
|
+
}
|
|
1045
993
|
}
|
|
1046
|
-
async
|
|
1047
|
-
const
|
|
1048
|
-
|
|
994
|
+
async activate() {
|
|
995
|
+
const cache = await self.caches.open(this._strategy.cacheName);
|
|
996
|
+
const currentlyCachedRequests = await cache.keys();
|
|
997
|
+
const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());
|
|
998
|
+
const deletedCacheRequests = [];
|
|
999
|
+
for (const request of currentlyCachedRequests){
|
|
1000
|
+
if (!expectedCacheKeys.has(request.url)) {
|
|
1001
|
+
await cache.delete(request);
|
|
1002
|
+
deletedCacheRequests.push(request.url);
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1006
|
+
printCleanupDetails(deletedCacheRequests);
|
|
1007
|
+
}
|
|
1049
1008
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
await db.delete(REQUEST_OBJECT_STORE_NAME, id);
|
|
1009
|
+
getUrlsToPrecacheKeys() {
|
|
1010
|
+
return this._urlsToCacheKeys;
|
|
1053
1011
|
}
|
|
1054
|
-
|
|
1055
|
-
return
|
|
1012
|
+
getPrecachedUrls() {
|
|
1013
|
+
return [
|
|
1014
|
+
...this._urlsToCacheKeys.keys()
|
|
1015
|
+
];
|
|
1056
1016
|
}
|
|
1057
|
-
|
|
1058
|
-
|
|
1017
|
+
getPrecacheKeyForUrl(url) {
|
|
1018
|
+
const urlObject = new URL(url, location.href);
|
|
1019
|
+
return this._urlsToCacheKeys.get(urlObject.href);
|
|
1059
1020
|
}
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
const cursor = await db.transaction(REQUEST_OBJECT_STORE_NAME).store.index(QUEUE_NAME_INDEX).openCursor(query, direction);
|
|
1063
|
-
return cursor?.value;
|
|
1021
|
+
getIntegrityForPrecacheKey(cacheKey) {
|
|
1022
|
+
return this._cacheKeysToIntegrities.get(cacheKey);
|
|
1064
1023
|
}
|
|
1065
|
-
async
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1024
|
+
async matchPrecache(request) {
|
|
1025
|
+
const url = request instanceof Request ? request.url : request;
|
|
1026
|
+
const cacheKey = this.getPrecacheKeyForUrl(url);
|
|
1027
|
+
if (cacheKey) {
|
|
1028
|
+
const cache = await self.caches.open(this._strategy.cacheName);
|
|
1029
|
+
return cache.match(cacheKey);
|
|
1070
1030
|
}
|
|
1071
|
-
return
|
|
1031
|
+
return undefined;
|
|
1072
1032
|
}
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1033
|
+
createHandlerBoundToUrl(url) {
|
|
1034
|
+
const cacheKey = this.getPrecacheKeyForUrl(url);
|
|
1035
|
+
if (!cacheKey) {
|
|
1036
|
+
throw new SerwistError("non-precached-url", {
|
|
1037
|
+
url
|
|
1038
|
+
});
|
|
1078
1039
|
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1040
|
+
return (options)=>{
|
|
1041
|
+
options.request = new Request(url);
|
|
1042
|
+
options.params = {
|
|
1043
|
+
cacheKey,
|
|
1044
|
+
...options.params
|
|
1045
|
+
};
|
|
1046
|
+
return this._strategy.handle(options);
|
|
1047
|
+
};
|
|
1086
1048
|
}
|
|
1087
1049
|
}
|
|
1088
1050
|
|
|
1089
|
-
class
|
|
1090
|
-
|
|
1091
|
-
|
|
1051
|
+
class PrecacheFallbackPlugin {
|
|
1052
|
+
_fallbackUrls;
|
|
1053
|
+
_precacheController;
|
|
1054
|
+
constructor({ fallbackUrls, precacheController, serwist }){
|
|
1055
|
+
this._fallbackUrls = fallbackUrls;
|
|
1056
|
+
if (!serwist) {
|
|
1057
|
+
this._precacheController = precacheController;
|
|
1058
|
+
} else {
|
|
1059
|
+
this._precacheController = serwist.precache;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
async handlerDidError(param) {
|
|
1063
|
+
for (const fallback of this._fallbackUrls){
|
|
1064
|
+
if (typeof fallback === "string") {
|
|
1065
|
+
const fallbackResponse = await this._precacheController.matchPrecache(fallback);
|
|
1066
|
+
if (fallbackResponse !== undefined) {
|
|
1067
|
+
return fallbackResponse;
|
|
1068
|
+
}
|
|
1069
|
+
} else if (fallback.matcher(param)) {
|
|
1070
|
+
const fallbackResponse = await this._precacheController.matchPrecache(fallback.url);
|
|
1071
|
+
if (fallbackResponse !== undefined) {
|
|
1072
|
+
return fallbackResponse;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
return undefined;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
class RuntimeCacheController {
|
|
1081
|
+
_entries;
|
|
1082
|
+
_options;
|
|
1083
|
+
constructor(entries, options = {}){
|
|
1084
|
+
this._entries = entries;
|
|
1085
|
+
this._options = options;
|
|
1086
|
+
this.init = this.init.bind(this);
|
|
1087
|
+
this.install = this.install.bind(this);
|
|
1088
|
+
}
|
|
1089
|
+
init({ serwist }) {
|
|
1090
|
+
if (this._options.fallbacks !== undefined) {
|
|
1091
|
+
const fallbackPlugin = new PrecacheFallbackPlugin({
|
|
1092
|
+
fallbackUrls: this._options.fallbacks.entries,
|
|
1093
|
+
precacheController: serwist.precache
|
|
1094
|
+
});
|
|
1095
|
+
this._entries.forEach((cacheEntry)=>{
|
|
1096
|
+
if (cacheEntry.handler instanceof Strategy && !cacheEntry.handler.plugins.some((plugin)=>"handlerDidError" in plugin)) {
|
|
1097
|
+
cacheEntry.handler.plugins.push(fallbackPlugin);
|
|
1098
|
+
}
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
for (const entry of this._entries){
|
|
1102
|
+
serwist.registerCapture(entry.matcher, entry.handler, entry.method);
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
async install({ event, serwist }) {
|
|
1106
|
+
const concurrency = this._options.warmOptions?.concurrency ?? 10;
|
|
1107
|
+
if (this._options.warmEntries) {
|
|
1108
|
+
await parallel(concurrency, this._options.warmEntries, async (entry)=>{
|
|
1109
|
+
const request = entry instanceof Request ? entry : new Request(typeof entry === "string" ? entry : entry.url, {
|
|
1110
|
+
integrity: typeof entry !== "string" ? entry.integrity : undefined,
|
|
1111
|
+
credentials: "same-origin"
|
|
1112
|
+
});
|
|
1113
|
+
await serwist.handleRequest({
|
|
1114
|
+
request,
|
|
1115
|
+
event
|
|
1116
|
+
});
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
warmRuntimeCache(entries) {
|
|
1121
|
+
if (!this._options.warmEntries) this._options.warmEntries = [];
|
|
1122
|
+
this._options.warmEntries.push(...entries);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
const BACKGROUND_SYNC_DB_VERSION = 3;
|
|
1127
|
+
const BACKGROUND_SYNC_DB_NAME = "serwist-background-sync";
|
|
1128
|
+
const REQUEST_OBJECT_STORE_NAME = "requests";
|
|
1129
|
+
const QUEUE_NAME_INDEX = "queueName";
|
|
1130
|
+
class BackgroundSyncQueueDb {
|
|
1131
|
+
_db = null;
|
|
1132
|
+
async addEntry(entry) {
|
|
1133
|
+
const db = await this.getDb();
|
|
1134
|
+
const tx = db.transaction(REQUEST_OBJECT_STORE_NAME, "readwrite", {
|
|
1135
|
+
durability: "relaxed"
|
|
1136
|
+
});
|
|
1137
|
+
await tx.store.add(entry);
|
|
1138
|
+
await tx.done;
|
|
1139
|
+
}
|
|
1140
|
+
async getFirstEntryId() {
|
|
1141
|
+
const db = await this.getDb();
|
|
1142
|
+
const cursor = await db.transaction(REQUEST_OBJECT_STORE_NAME).store.openCursor();
|
|
1143
|
+
return cursor?.value.id;
|
|
1144
|
+
}
|
|
1145
|
+
async getAllEntriesByQueueName(queueName) {
|
|
1146
|
+
const db = await this.getDb();
|
|
1147
|
+
const results = await db.getAllFromIndex(REQUEST_OBJECT_STORE_NAME, QUEUE_NAME_INDEX, IDBKeyRange.only(queueName));
|
|
1148
|
+
return results ? results : new Array();
|
|
1149
|
+
}
|
|
1150
|
+
async getEntryCountByQueueName(queueName) {
|
|
1151
|
+
const db = await this.getDb();
|
|
1152
|
+
return db.countFromIndex(REQUEST_OBJECT_STORE_NAME, QUEUE_NAME_INDEX, IDBKeyRange.only(queueName));
|
|
1153
|
+
}
|
|
1154
|
+
async deleteEntry(id) {
|
|
1155
|
+
const db = await this.getDb();
|
|
1156
|
+
await db.delete(REQUEST_OBJECT_STORE_NAME, id);
|
|
1157
|
+
}
|
|
1158
|
+
async getFirstEntryByQueueName(queueName) {
|
|
1159
|
+
return await this.getEndEntryFromIndex(IDBKeyRange.only(queueName), "next");
|
|
1160
|
+
}
|
|
1161
|
+
async getLastEntryByQueueName(queueName) {
|
|
1162
|
+
return await this.getEndEntryFromIndex(IDBKeyRange.only(queueName), "prev");
|
|
1163
|
+
}
|
|
1164
|
+
async getEndEntryFromIndex(query, direction) {
|
|
1165
|
+
const db = await this.getDb();
|
|
1166
|
+
const cursor = await db.transaction(REQUEST_OBJECT_STORE_NAME).store.index(QUEUE_NAME_INDEX).openCursor(query, direction);
|
|
1167
|
+
return cursor?.value;
|
|
1168
|
+
}
|
|
1169
|
+
async getDb() {
|
|
1170
|
+
if (!this._db) {
|
|
1171
|
+
this._db = await openDB(BACKGROUND_SYNC_DB_NAME, BACKGROUND_SYNC_DB_VERSION, {
|
|
1172
|
+
upgrade: this._upgradeDb
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
return this._db;
|
|
1176
|
+
}
|
|
1177
|
+
_upgradeDb(db, oldVersion) {
|
|
1178
|
+
if (oldVersion > 0 && oldVersion < BACKGROUND_SYNC_DB_VERSION) {
|
|
1179
|
+
if (db.objectStoreNames.contains(REQUEST_OBJECT_STORE_NAME)) {
|
|
1180
|
+
db.deleteObjectStore(REQUEST_OBJECT_STORE_NAME);
|
|
1181
|
+
}
|
|
1182
|
+
}
|
|
1183
|
+
const objStore = db.createObjectStore(REQUEST_OBJECT_STORE_NAME, {
|
|
1184
|
+
autoIncrement: true,
|
|
1185
|
+
keyPath: "id"
|
|
1186
|
+
});
|
|
1187
|
+
objStore.createIndex(QUEUE_NAME_INDEX, QUEUE_NAME_INDEX, {
|
|
1188
|
+
unique: false
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
class BackgroundSyncQueueStore {
|
|
1194
|
+
_queueName;
|
|
1195
|
+
_queueDb;
|
|
1092
1196
|
constructor(queueName){
|
|
1093
1197
|
this._queueName = queueName;
|
|
1094
1198
|
this._queueDb = new BackgroundSyncQueueDb();
|
|
@@ -1456,515 +1560,413 @@ class BackgroundSyncPlugin {
|
|
|
1456
1560
|
}
|
|
1457
1561
|
}
|
|
1458
1562
|
|
|
1459
|
-
const
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
const ANALYTICS_JS_PATH = "/analytics.js";
|
|
1464
|
-
const GTAG_JS_PATH = "/gtag/js";
|
|
1465
|
-
const GTM_JS_PATH = "/gtm.js";
|
|
1466
|
-
const COLLECT_PATHS_REGEX = /^\/(\w+\/)?collect/;
|
|
1467
|
-
|
|
1468
|
-
const createOnSyncCallback = (config)=>{
|
|
1469
|
-
return async ({ queue })=>{
|
|
1470
|
-
let entry = undefined;
|
|
1471
|
-
while(entry = await queue.shiftRequest()){
|
|
1472
|
-
const { request, timestamp } = entry;
|
|
1473
|
-
const url = new URL(request.url);
|
|
1474
|
-
try {
|
|
1475
|
-
const params = request.method === "POST" ? new URLSearchParams(await request.clone().text()) : url.searchParams;
|
|
1476
|
-
const originalHitTime = timestamp - (Number(params.get("qt")) || 0);
|
|
1477
|
-
const queueTime = Date.now() - originalHitTime;
|
|
1478
|
-
params.set("qt", String(queueTime));
|
|
1479
|
-
if (config.parameterOverrides) {
|
|
1480
|
-
for (const param of Object.keys(config.parameterOverrides)){
|
|
1481
|
-
const value = config.parameterOverrides[param];
|
|
1482
|
-
params.set(param, value);
|
|
1483
|
-
}
|
|
1484
|
-
}
|
|
1485
|
-
if (typeof config.hitFilter === "function") {
|
|
1486
|
-
config.hitFilter.call(null, params);
|
|
1487
|
-
}
|
|
1488
|
-
await fetch(new Request(url.origin + url.pathname, {
|
|
1489
|
-
body: params.toString(),
|
|
1490
|
-
method: "POST",
|
|
1491
|
-
mode: "cors",
|
|
1492
|
-
credentials: "omit",
|
|
1493
|
-
headers: {
|
|
1494
|
-
"Content-Type": "text/plain"
|
|
1495
|
-
}
|
|
1496
|
-
}));
|
|
1497
|
-
if (process.env.NODE_ENV !== "production") {
|
|
1498
|
-
logger.log(`Request for '${getFriendlyURL(url.href)}' has been replayed`);
|
|
1499
|
-
}
|
|
1500
|
-
} catch (err) {
|
|
1501
|
-
await queue.unshiftRequest(entry);
|
|
1502
|
-
if (process.env.NODE_ENV !== "production") {
|
|
1503
|
-
logger.log(`Request for '${getFriendlyURL(url.href)}' failed to replay, putting it back in the queue.`);
|
|
1504
|
-
}
|
|
1505
|
-
throw err;
|
|
1506
|
-
}
|
|
1507
|
-
}
|
|
1508
|
-
if (process.env.NODE_ENV !== "production") {
|
|
1509
|
-
logger.log("All Google Analytics request successfully replayed; " + "the queue is now empty!");
|
|
1563
|
+
const cacheOkAndOpaquePlugin = {
|
|
1564
|
+
cacheWillUpdate: async ({ response })=>{
|
|
1565
|
+
if (response.status === 200 || response.status === 0) {
|
|
1566
|
+
return response;
|
|
1510
1567
|
}
|
|
1511
|
-
|
|
1512
|
-
};
|
|
1513
|
-
const createCollectRoutes = (bgSyncPlugin)=>{
|
|
1514
|
-
const match = ({ url })=>url.hostname === GOOGLE_ANALYTICS_HOST && COLLECT_PATHS_REGEX.test(url.pathname);
|
|
1515
|
-
const handler = new NetworkOnly({
|
|
1516
|
-
plugins: [
|
|
1517
|
-
bgSyncPlugin
|
|
1518
|
-
]
|
|
1519
|
-
});
|
|
1520
|
-
return [
|
|
1521
|
-
new Route(match, handler, "GET"),
|
|
1522
|
-
new Route(match, handler, "POST")
|
|
1523
|
-
];
|
|
1524
|
-
};
|
|
1525
|
-
const createAnalyticsJsRoute = (cacheName)=>{
|
|
1526
|
-
const match = ({ url })=>url.hostname === GOOGLE_ANALYTICS_HOST && url.pathname === ANALYTICS_JS_PATH;
|
|
1527
|
-
const handler = new NetworkFirst({
|
|
1528
|
-
cacheName
|
|
1529
|
-
});
|
|
1530
|
-
return new Route(match, handler, "GET");
|
|
1531
|
-
};
|
|
1532
|
-
const createGtagJsRoute = (cacheName)=>{
|
|
1533
|
-
const match = ({ url })=>url.hostname === GTM_HOST && url.pathname === GTAG_JS_PATH;
|
|
1534
|
-
const handler = new NetworkFirst({
|
|
1535
|
-
cacheName
|
|
1536
|
-
});
|
|
1537
|
-
return new Route(match, handler, "GET");
|
|
1538
|
-
};
|
|
1539
|
-
const createGtmJsRoute = (cacheName)=>{
|
|
1540
|
-
const match = ({ url })=>url.hostname === GTM_HOST && url.pathname === GTM_JS_PATH;
|
|
1541
|
-
const handler = new NetworkFirst({
|
|
1542
|
-
cacheName
|
|
1543
|
-
});
|
|
1544
|
-
return new Route(match, handler, "GET");
|
|
1545
|
-
};
|
|
1546
|
-
const initializeGoogleAnalytics = ({ serwist, cacheName, ...options })=>{
|
|
1547
|
-
const resolvedCacheName = cacheNames$1.getGoogleAnalyticsName(cacheName);
|
|
1548
|
-
const bgSyncPlugin = new BackgroundSyncPlugin(QUEUE_NAME, {
|
|
1549
|
-
maxRetentionTime: MAX_RETENTION_TIME,
|
|
1550
|
-
onSync: createOnSyncCallback(options)
|
|
1551
|
-
});
|
|
1552
|
-
const routes = [
|
|
1553
|
-
createGtmJsRoute(resolvedCacheName),
|
|
1554
|
-
createAnalyticsJsRoute(resolvedCacheName),
|
|
1555
|
-
createGtagJsRoute(resolvedCacheName),
|
|
1556
|
-
...createCollectRoutes(bgSyncPlugin)
|
|
1557
|
-
];
|
|
1558
|
-
for (const route of routes){
|
|
1559
|
-
serwist.registerRoute(route);
|
|
1568
|
+
return null;
|
|
1560
1569
|
}
|
|
1561
1570
|
};
|
|
1562
1571
|
|
|
1563
|
-
const
|
|
1564
|
-
|
|
1572
|
+
const messages = {
|
|
1573
|
+
strategyStart: (strategyName, request)=>`Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`,
|
|
1574
|
+
printFinalResponse: (response)=>{
|
|
1575
|
+
if (response) {
|
|
1576
|
+
logger.groupCollapsed("View the final response here.");
|
|
1577
|
+
logger.log(response || "[No response returned]");
|
|
1578
|
+
logger.groupEnd();
|
|
1579
|
+
}
|
|
1580
|
+
}
|
|
1565
1581
|
};
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
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.");
|
|
1582
|
+
|
|
1583
|
+
class NetworkFirst extends Strategy {
|
|
1584
|
+
_networkTimeoutSeconds;
|
|
1585
|
+
constructor(options = {}){
|
|
1586
|
+
super(options);
|
|
1587
|
+
if (!this.plugins.some((p)=>"cacheWillUpdate" in p)) {
|
|
1588
|
+
this.plugins.unshift(cacheOkAndOpaquePlugin);
|
|
1581
1589
|
}
|
|
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 {
|
|
1590
|
+
this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
|
|
1594
1591
|
if (process.env.NODE_ENV !== "production") {
|
|
1595
|
-
|
|
1592
|
+
if (this._networkTimeoutSeconds) {
|
|
1593
|
+
finalAssertExports.isType(this._networkTimeoutSeconds, "number", {
|
|
1594
|
+
moduleName: "serwist",
|
|
1595
|
+
className: this.constructor.name,
|
|
1596
|
+
funcName: "constructor",
|
|
1597
|
+
paramName: "networkTimeoutSeconds"
|
|
1598
|
+
});
|
|
1599
|
+
}
|
|
1596
1600
|
}
|
|
1597
1601
|
}
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
paramName: `details.${key}`
|
|
1602
|
+
async _handle(request, handler) {
|
|
1603
|
+
const logs = [];
|
|
1604
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1605
|
+
finalAssertExports.isInstance(request, Request, {
|
|
1606
|
+
moduleName: "serwist",
|
|
1607
|
+
className: this.constructor.name,
|
|
1608
|
+
funcName: "handle",
|
|
1609
|
+
paramName: "makeRequest"
|
|
1607
1610
|
});
|
|
1608
1611
|
}
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1612
|
+
const promises = [];
|
|
1613
|
+
let timeoutId;
|
|
1614
|
+
if (this._networkTimeoutSeconds) {
|
|
1615
|
+
const { id, promise } = this._getTimeoutPromise({
|
|
1616
|
+
request,
|
|
1617
|
+
logs,
|
|
1618
|
+
handler
|
|
1613
1619
|
});
|
|
1620
|
+
timeoutId = id;
|
|
1621
|
+
promises.push(promise);
|
|
1614
1622
|
}
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1623
|
+
const networkPromise = this._getNetworkPromise({
|
|
1624
|
+
timeoutId,
|
|
1625
|
+
request,
|
|
1626
|
+
logs,
|
|
1627
|
+
handler
|
|
1628
|
+
});
|
|
1629
|
+
promises.push(networkPromise);
|
|
1630
|
+
const response = await handler.waitUntil((async ()=>{
|
|
1631
|
+
return await handler.waitUntil(Promise.race(promises)) || await networkPromise;
|
|
1632
|
+
})());
|
|
1633
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1634
|
+
logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
|
|
1635
|
+
for (const log of logs){
|
|
1636
|
+
logger.log(log);
|
|
1637
|
+
}
|
|
1638
|
+
messages.printFinalResponse(response);
|
|
1639
|
+
logger.groupEnd();
|
|
1620
1640
|
}
|
|
1621
|
-
if (
|
|
1622
|
-
throw new SerwistError("
|
|
1623
|
-
|
|
1624
|
-
value: details.googleAnalytics
|
|
1641
|
+
if (!response) {
|
|
1642
|
+
throw new SerwistError("no-response", {
|
|
1643
|
+
url: request.url
|
|
1625
1644
|
});
|
|
1626
1645
|
}
|
|
1646
|
+
return response;
|
|
1627
1647
|
}
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
const
|
|
1632
|
-
|
|
1633
|
-
|
|
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.`);
|
|
1648
|
+
_getTimeoutPromise({ request, logs, handler }) {
|
|
1649
|
+
let timeoutId;
|
|
1650
|
+
const timeoutPromise = new Promise((resolve)=>{
|
|
1651
|
+
const onNetworkTimeout = async ()=>{
|
|
1652
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1653
|
+
logs.push(`Timing out the network response at ${this._networkTimeoutSeconds} seconds.`);
|
|
1652
1654
|
}
|
|
1653
|
-
|
|
1654
|
-
|
|
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
|
|
1655
|
+
resolve(await handler.cacheMatch(request));
|
|
1656
|
+
};
|
|
1657
|
+
timeoutId = setTimeout(onNetworkTimeout, this._networkTimeoutSeconds * 1000);
|
|
1692
1658
|
});
|
|
1693
|
-
}
|
|
1694
|
-
if (!revision) {
|
|
1695
|
-
const urlObject = new URL(url, location.href);
|
|
1696
1659
|
return {
|
|
1697
|
-
|
|
1698
|
-
|
|
1660
|
+
promise: timeoutPromise,
|
|
1661
|
+
id: timeoutId
|
|
1699
1662
|
};
|
|
1700
1663
|
}
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
class PrecacheInstallReportPlugin {
|
|
1711
|
-
updatedURLs = [];
|
|
1712
|
-
notUpdatedURLs = [];
|
|
1713
|
-
handlerWillStart = async ({ request, state })=>{
|
|
1714
|
-
if (state) {
|
|
1715
|
-
state.originalRequest = request;
|
|
1664
|
+
async _getNetworkPromise({ timeoutId, request, logs, handler }) {
|
|
1665
|
+
let error = undefined;
|
|
1666
|
+
let response = undefined;
|
|
1667
|
+
try {
|
|
1668
|
+
response = await handler.fetchAndCachePut(request);
|
|
1669
|
+
} catch (fetchError) {
|
|
1670
|
+
if (fetchError instanceof Error) {
|
|
1671
|
+
error = fetchError;
|
|
1672
|
+
}
|
|
1716
1673
|
}
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1674
|
+
if (timeoutId) {
|
|
1675
|
+
clearTimeout(timeoutId);
|
|
1676
|
+
}
|
|
1677
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1678
|
+
if (response) {
|
|
1679
|
+
logs.push("Got response from network.");
|
|
1680
|
+
} else {
|
|
1681
|
+
logs.push("Unable to get a response from the network. Will respond " + "with a cached response.");
|
|
1682
|
+
}
|
|
1683
|
+
}
|
|
1684
|
+
if (error || !response) {
|
|
1685
|
+
response = await handler.cacheMatch(request);
|
|
1686
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1687
|
+
if (response) {
|
|
1688
|
+
logs.push(`Found a cached response in the '${this.cacheName}' cache.`);
|
|
1724
1689
|
} else {
|
|
1725
|
-
|
|
1690
|
+
logs.push(`No response found in the '${this.cacheName}' cache.`);
|
|
1726
1691
|
}
|
|
1727
1692
|
}
|
|
1728
1693
|
}
|
|
1729
|
-
return
|
|
1730
|
-
}
|
|
1694
|
+
return response;
|
|
1695
|
+
}
|
|
1731
1696
|
}
|
|
1732
1697
|
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1698
|
+
class NetworkOnly extends Strategy {
|
|
1699
|
+
_networkTimeoutSeconds;
|
|
1700
|
+
constructor(options = {}){
|
|
1701
|
+
super(options);
|
|
1702
|
+
this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
|
|
1736
1703
|
}
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1704
|
+
async _handle(request, handler) {
|
|
1705
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1706
|
+
finalAssertExports.isInstance(request, Request, {
|
|
1707
|
+
moduleName: "serwist",
|
|
1708
|
+
className: this.constructor.name,
|
|
1709
|
+
funcName: "_handle",
|
|
1710
|
+
paramName: "request"
|
|
1711
|
+
});
|
|
1712
|
+
}
|
|
1713
|
+
let error = undefined;
|
|
1714
|
+
let response;
|
|
1715
|
+
try {
|
|
1716
|
+
const promises = [
|
|
1717
|
+
handler.fetch(request)
|
|
1718
|
+
];
|
|
1719
|
+
if (this._networkTimeoutSeconds) {
|
|
1720
|
+
const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000);
|
|
1721
|
+
promises.push(timeoutPromise);
|
|
1722
|
+
}
|
|
1723
|
+
response = await Promise.race(promises);
|
|
1724
|
+
if (!response) {
|
|
1725
|
+
throw new Error(`Timed out the network response after ${this._networkTimeoutSeconds} seconds.`);
|
|
1726
|
+
}
|
|
1727
|
+
} catch (err) {
|
|
1728
|
+
if (err instanceof Error) {
|
|
1729
|
+
error = err;
|
|
1730
|
+
}
|
|
1731
|
+
}
|
|
1732
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1733
|
+
logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
|
|
1734
|
+
if (response) {
|
|
1735
|
+
logger.log("Got response from network.");
|
|
1736
|
+
} else {
|
|
1737
|
+
logger.log("Unable to get a response from the network.");
|
|
1738
|
+
}
|
|
1739
|
+
messages.printFinalResponse(response);
|
|
1740
|
+
logger.groupEnd();
|
|
1741
|
+
}
|
|
1742
|
+
if (!response) {
|
|
1743
|
+
throw new SerwistError("no-response", {
|
|
1744
|
+
url: request.url,
|
|
1745
|
+
error
|
|
1746
|
+
});
|
|
1747
|
+
}
|
|
1748
|
+
return response;
|
|
1740
1749
|
}
|
|
1741
|
-
logger.groupEnd();
|
|
1742
1750
|
}
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1751
|
+
|
|
1752
|
+
const QUEUE_NAME = "serwist-google-analytics";
|
|
1753
|
+
const MAX_RETENTION_TIME = 60 * 48;
|
|
1754
|
+
const GOOGLE_ANALYTICS_HOST = "www.google-analytics.com";
|
|
1755
|
+
const GTM_HOST = "www.googletagmanager.com";
|
|
1756
|
+
const ANALYTICS_JS_PATH = "/analytics.js";
|
|
1757
|
+
const GTAG_JS_PATH = "/gtag/js";
|
|
1758
|
+
const GTM_JS_PATH = "/gtm.js";
|
|
1759
|
+
const COLLECT_PATHS_REGEX = /^\/(\w+\/)?collect/;
|
|
1760
|
+
|
|
1761
|
+
const createOnSyncCallback = (config)=>{
|
|
1762
|
+
return async ({ queue })=>{
|
|
1763
|
+
let entry = undefined;
|
|
1764
|
+
while(entry = await queue.shiftRequest()){
|
|
1765
|
+
const { request, timestamp } = entry;
|
|
1766
|
+
const url = new URL(request.url);
|
|
1767
|
+
try {
|
|
1768
|
+
const params = request.method === "POST" ? new URLSearchParams(await request.clone().text()) : url.searchParams;
|
|
1769
|
+
const originalHitTime = timestamp - (Number(params.get("qt")) || 0);
|
|
1770
|
+
const queueTime = Date.now() - originalHitTime;
|
|
1771
|
+
params.set("qt", String(queueTime));
|
|
1772
|
+
if (config.parameterOverrides) {
|
|
1773
|
+
for (const param of Object.keys(config.parameterOverrides)){
|
|
1774
|
+
const value = config.parameterOverrides[param];
|
|
1775
|
+
params.set(param, value);
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
if (typeof config.hitFilter === "function") {
|
|
1779
|
+
config.hitFilter.call(null, params);
|
|
1780
|
+
}
|
|
1781
|
+
await fetch(new Request(url.origin + url.pathname, {
|
|
1782
|
+
body: params.toString(),
|
|
1783
|
+
method: "POST",
|
|
1784
|
+
mode: "cors",
|
|
1785
|
+
credentials: "omit",
|
|
1786
|
+
headers: {
|
|
1787
|
+
"Content-Type": "text/plain"
|
|
1788
|
+
}
|
|
1789
|
+
}));
|
|
1790
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1791
|
+
logger.log(`Request for '${getFriendlyURL(url.href)}' has been replayed`);
|
|
1792
|
+
}
|
|
1793
|
+
} catch (err) {
|
|
1794
|
+
await queue.unshiftRequest(entry);
|
|
1795
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1796
|
+
logger.log(`Request for '${getFriendlyURL(url.href)}' failed to replay, putting it back in the queue.`);
|
|
1797
|
+
}
|
|
1798
|
+
throw err;
|
|
1799
|
+
}
|
|
1750
1800
|
}
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
}
|
|
1801
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1802
|
+
logger.log("All Google Analytics request successfully replayed; " + "the queue is now empty!");
|
|
1803
|
+
}
|
|
1804
|
+
};
|
|
1756
1805
|
};
|
|
1757
|
-
|
|
1758
|
-
const
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1806
|
+
const createCollectRoutes = (bgSyncPlugin)=>{
|
|
1807
|
+
const match = ({ url })=>url.hostname === GOOGLE_ANALYTICS_HOST && COLLECT_PATHS_REGEX.test(url.pathname);
|
|
1808
|
+
const handler = new NetworkOnly({
|
|
1809
|
+
plugins: [
|
|
1810
|
+
bgSyncPlugin
|
|
1811
|
+
]
|
|
1812
|
+
});
|
|
1813
|
+
return [
|
|
1814
|
+
new Route(match, handler, "GET"),
|
|
1815
|
+
new Route(match, handler, "POST")
|
|
1816
|
+
];
|
|
1764
1817
|
};
|
|
1765
|
-
const
|
|
1766
|
-
const
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
}
|
|
1818
|
+
const createAnalyticsJsRoute = (cacheName)=>{
|
|
1819
|
+
const match = ({ url })=>url.hostname === GOOGLE_ANALYTICS_HOST && url.pathname === ANALYTICS_JS_PATH;
|
|
1820
|
+
const handler = new NetworkFirst({
|
|
1821
|
+
cacheName
|
|
1822
|
+
});
|
|
1823
|
+
return new Route(match, handler, "GET");
|
|
1772
1824
|
};
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
this._precacheController = precacheController;
|
|
1778
|
-
}
|
|
1779
|
-
cacheKeyWillBeUsed = async ({ request, params })=>{
|
|
1780
|
-
const cacheKey = params?.cacheKey || this._precacheController.getPrecacheKeyForUrl(request.url);
|
|
1781
|
-
return cacheKey ? new Request(cacheKey, {
|
|
1782
|
-
headers: request.headers
|
|
1783
|
-
}) : request;
|
|
1784
|
-
};
|
|
1785
|
-
}
|
|
1786
|
-
|
|
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),
|
|
1790
|
-
plugins: [
|
|
1791
|
-
...plugins ?? [],
|
|
1792
|
-
new PrecacheCacheKeyPlugin({
|
|
1793
|
-
precacheController: controller
|
|
1794
|
-
})
|
|
1795
|
-
],
|
|
1796
|
-
fetchOptions,
|
|
1797
|
-
matchOptions,
|
|
1798
|
-
fallbackToNetwork
|
|
1799
|
-
},
|
|
1800
|
-
routeOptions: {
|
|
1801
|
-
directoryIndex,
|
|
1802
|
-
ignoreURLParametersMatching,
|
|
1803
|
-
cleanURLs,
|
|
1804
|
-
urlManipulation
|
|
1805
|
-
},
|
|
1806
|
-
controllerOptions: {
|
|
1807
|
-
cleanupOutdatedCaches,
|
|
1808
|
-
concurrency: concurrency ?? 10,
|
|
1809
|
-
navigateFallback,
|
|
1810
|
-
navigateFallbackAllowlist,
|
|
1811
|
-
navigateFallbackDenylist
|
|
1812
|
-
}
|
|
1825
|
+
const createGtagJsRoute = (cacheName)=>{
|
|
1826
|
+
const match = ({ url })=>url.hostname === GTM_HOST && url.pathname === GTAG_JS_PATH;
|
|
1827
|
+
const handler = new NetworkFirst({
|
|
1828
|
+
cacheName
|
|
1813
1829
|
});
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1830
|
+
return new Route(match, handler, "GET");
|
|
1831
|
+
};
|
|
1832
|
+
const createGtmJsRoute = (cacheName)=>{
|
|
1833
|
+
const match = ({ url })=>url.hostname === GTM_HOST && url.pathname === GTM_JS_PATH;
|
|
1834
|
+
const handler = new NetworkFirst({
|
|
1835
|
+
cacheName
|
|
1836
|
+
});
|
|
1837
|
+
return new Route(match, handler, "GET");
|
|
1838
|
+
};
|
|
1839
|
+
const initializeGoogleAnalytics = ({ serwist, cacheName, ...options })=>{
|
|
1840
|
+
const resolvedCacheName = cacheNames$1.getGoogleAnalyticsName(cacheName);
|
|
1841
|
+
const bgSyncPlugin = new BackgroundSyncPlugin(QUEUE_NAME, {
|
|
1842
|
+
maxRetentionTime: MAX_RETENTION_TIME,
|
|
1843
|
+
onSync: createOnSyncCallback(options)
|
|
1844
|
+
});
|
|
1845
|
+
const routes = [
|
|
1846
|
+
createGtmJsRoute(resolvedCacheName),
|
|
1847
|
+
createAnalyticsJsRoute(resolvedCacheName),
|
|
1848
|
+
createGtagJsRoute(resolvedCacheName),
|
|
1849
|
+
...createCollectRoutes(bgSyncPlugin)
|
|
1850
|
+
];
|
|
1851
|
+
for (const route of routes){
|
|
1852
|
+
serwist.registerRoute(route);
|
|
1833
1853
|
}
|
|
1834
|
-
|
|
1854
|
+
};
|
|
1855
|
+
|
|
1856
|
+
const parseRoute = (capture, handler, method)=>{
|
|
1857
|
+
if (typeof capture === "string") {
|
|
1858
|
+
const captureUrl = new URL(capture, location.href);
|
|
1835
1859
|
if (process.env.NODE_ENV !== "production") {
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
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
|
|
1860
|
+
if (!(capture.startsWith("/") || capture.startsWith("http"))) {
|
|
1861
|
+
throw new SerwistError("invalid-string", {
|
|
1862
|
+
moduleName: "serwist",
|
|
1863
|
+
funcName: "parseRoute",
|
|
1864
|
+
paramName: "capture"
|
|
1856
1865
|
});
|
|
1857
1866
|
}
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
});
|
|
1863
|
-
}
|
|
1864
|
-
this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);
|
|
1867
|
+
const valueToCheck = capture.startsWith("http") ? captureUrl.pathname : capture;
|
|
1868
|
+
const wildcards = "[*:?+]";
|
|
1869
|
+
if (new RegExp(`${wildcards}`).exec(valueToCheck)) {
|
|
1870
|
+
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.`);
|
|
1865
1871
|
}
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
if (
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
console.warn(warningMessage);
|
|
1872
|
-
} else {
|
|
1873
|
-
logger.warn(warningMessage);
|
|
1872
|
+
}
|
|
1873
|
+
const matchCallback = ({ url })=>{
|
|
1874
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1875
|
+
if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) {
|
|
1876
|
+
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.`);
|
|
1874
1877
|
}
|
|
1875
1878
|
}
|
|
1876
|
-
|
|
1879
|
+
return url.href === captureUrl.href;
|
|
1880
|
+
};
|
|
1881
|
+
return new Route(matchCallback, handler, method);
|
|
1877
1882
|
}
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1883
|
+
if (capture instanceof RegExp) {
|
|
1884
|
+
return new RegExpRoute(capture, handler, method);
|
|
1885
|
+
}
|
|
1886
|
+
if (typeof capture === "function") {
|
|
1887
|
+
return new Route(capture, handler, method);
|
|
1888
|
+
}
|
|
1889
|
+
if (capture instanceof Route) {
|
|
1890
|
+
return capture;
|
|
1891
|
+
}
|
|
1892
|
+
throw new SerwistError("unsupported-route-type", {
|
|
1893
|
+
moduleName: "serwist",
|
|
1894
|
+
funcName: "parseRoute",
|
|
1895
|
+
paramName: "capture"
|
|
1896
|
+
});
|
|
1897
|
+
};
|
|
1898
|
+
|
|
1899
|
+
const disableDevLogs = ()=>{
|
|
1900
|
+
self.__WB_DISABLE_DEV_LOGS = true;
|
|
1901
|
+
};
|
|
1902
|
+
|
|
1903
|
+
const isNavigationPreloadSupported = ()=>{
|
|
1904
|
+
return Boolean(self.registration?.navigationPreload);
|
|
1905
|
+
};
|
|
1906
|
+
const enableNavigationPreload = (headerValue)=>{
|
|
1907
|
+
if (isNavigationPreloadSupported()) {
|
|
1908
|
+
self.addEventListener("activate", (event)=>{
|
|
1909
|
+
event.waitUntil(self.registration.navigationPreload.enable().then(()=>{
|
|
1910
|
+
if (headerValue) {
|
|
1911
|
+
void self.registration.navigationPreload.setHeaderValue(headerValue);
|
|
1912
|
+
}
|
|
1913
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1914
|
+
logger.log("Navigation preloading is enabled.");
|
|
1915
|
+
}
|
|
1884
1916
|
}));
|
|
1917
|
+
});
|
|
1918
|
+
} else {
|
|
1919
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1920
|
+
logger.log("Navigation preloading is not supported in this browser.");
|
|
1885
1921
|
}
|
|
1886
1922
|
}
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
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
|
|
1923
|
+
};
|
|
1924
|
+
const disableNavigationPreload = ()=>{
|
|
1925
|
+
if (isNavigationPreloadSupported()) {
|
|
1926
|
+
self.addEventListener("activate", (event)=>{
|
|
1927
|
+
event.waitUntil(self.registration.navigationPreload.disable().then(()=>{
|
|
1928
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1929
|
+
logger.log("Navigation preloading is disabled.");
|
|
1904
1930
|
}
|
|
1905
1931
|
}));
|
|
1906
1932
|
});
|
|
1907
|
-
|
|
1933
|
+
} else {
|
|
1908
1934
|
if (process.env.NODE_ENV !== "production") {
|
|
1909
|
-
|
|
1935
|
+
logger.log("Navigation preloading is not supported in this browser.");
|
|
1910
1936
|
}
|
|
1911
1937
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
const
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
}
|
|
1938
|
+
};
|
|
1939
|
+
|
|
1940
|
+
const setCacheNameDetails = (details)=>{
|
|
1941
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1942
|
+
for (const key of Object.keys(details)){
|
|
1943
|
+
finalAssertExports.isType(details[key], "string", {
|
|
1944
|
+
moduleName: "@serwist/core",
|
|
1945
|
+
funcName: "setCacheNameDetails",
|
|
1946
|
+
paramName: `details.${key}`
|
|
1947
|
+
});
|
|
1922
1948
|
}
|
|
1923
|
-
if (
|
|
1924
|
-
|
|
1949
|
+
if (details.precache?.length === 0) {
|
|
1950
|
+
throw new SerwistError("invalid-cache-name", {
|
|
1951
|
+
cacheNameId: "precache",
|
|
1952
|
+
value: details.precache
|
|
1953
|
+
});
|
|
1925
1954
|
}
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
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);
|
|
1955
|
+
if (details.runtime?.length === 0) {
|
|
1956
|
+
throw new SerwistError("invalid-cache-name", {
|
|
1957
|
+
cacheNameId: "runtime",
|
|
1958
|
+
value: details.runtime
|
|
1959
|
+
});
|
|
1948
1960
|
}
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
if (!cacheKey) {
|
|
1954
|
-
throw new SerwistError("non-precached-url", {
|
|
1955
|
-
url
|
|
1961
|
+
if (details.googleAnalytics?.length === 0) {
|
|
1962
|
+
throw new SerwistError("invalid-cache-name", {
|
|
1963
|
+
cacheNameId: "googleAnalytics",
|
|
1964
|
+
value: details.googleAnalytics
|
|
1956
1965
|
});
|
|
1957
1966
|
}
|
|
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
|
}
|
|
1967
|
-
|
|
1968
|
+
cacheNames$1.updateDetails(details);
|
|
1969
|
+
};
|
|
1968
1970
|
|
|
1969
1971
|
class Serwist {
|
|
1970
1972
|
_routes;
|
|
@@ -2021,7 +2023,9 @@ class Serwist {
|
|
|
2021
2023
|
}
|
|
2022
2024
|
}
|
|
2023
2025
|
for (const callback of this.iterateControllers("init")){
|
|
2024
|
-
callback(
|
|
2026
|
+
callback({
|
|
2027
|
+
serwist: this
|
|
2028
|
+
});
|
|
2025
2029
|
}
|
|
2026
2030
|
if (disableDevLogs$1) disableDevLogs();
|
|
2027
2031
|
}
|
|
@@ -2029,7 +2033,10 @@ class Serwist {
|
|
|
2029
2033
|
if (!this._controllers) return;
|
|
2030
2034
|
for (const controller of this._controllers){
|
|
2031
2035
|
if (typeof controller[name] === "function") {
|
|
2032
|
-
|
|
2036
|
+
const callback = (param)=>{
|
|
2037
|
+
controller[name](param);
|
|
2038
|
+
};
|
|
2039
|
+
yield callback;
|
|
2033
2040
|
}
|
|
2034
2041
|
}
|
|
2035
2042
|
}
|
|
@@ -2051,14 +2058,20 @@ class Serwist {
|
|
|
2051
2058
|
handleInstall(event) {
|
|
2052
2059
|
return waitUntil(event, async ()=>{
|
|
2053
2060
|
for (const callback of this.iterateControllers("install")){
|
|
2054
|
-
await callback(
|
|
2061
|
+
await callback({
|
|
2062
|
+
event,
|
|
2063
|
+
serwist: this
|
|
2064
|
+
});
|
|
2055
2065
|
}
|
|
2056
2066
|
});
|
|
2057
2067
|
}
|
|
2058
2068
|
handleActivate(event) {
|
|
2059
2069
|
return waitUntil(event, async ()=>{
|
|
2060
2070
|
for (const callback of this.iterateControllers("activate")){
|
|
2061
|
-
await callback(
|
|
2071
|
+
await callback({
|
|
2072
|
+
event,
|
|
2073
|
+
serwist: this
|
|
2074
|
+
});
|
|
2062
2075
|
}
|
|
2063
2076
|
});
|
|
2064
2077
|
}
|
|
@@ -2346,6 +2359,20 @@ const cacheNames = {
|
|
|
2346
2359
|
}
|
|
2347
2360
|
};
|
|
2348
2361
|
|
|
2362
|
+
const registerQuotaErrorCallback = (callback)=>{
|
|
2363
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2364
|
+
finalAssertExports.isType(callback, "function", {
|
|
2365
|
+
moduleName: "@serwist/core",
|
|
2366
|
+
funcName: "register",
|
|
2367
|
+
paramName: "callback"
|
|
2368
|
+
});
|
|
2369
|
+
}
|
|
2370
|
+
quotaErrorCallbacks.add(callback);
|
|
2371
|
+
if (process.env.NODE_ENV !== "production") {
|
|
2372
|
+
logger.log("Registered a callback to respond to quota errors.", callback);
|
|
2373
|
+
}
|
|
2374
|
+
};
|
|
2375
|
+
|
|
2349
2376
|
const BROADCAST_UPDATE_MESSAGE_TYPE = "CACHE_UPDATED";
|
|
2350
2377
|
const BROADCAST_UPDATE_MESSAGE_META = "serwist-broadcast-update";
|
|
2351
2378
|
const BROADCAST_UPDATE_DEFAULT_NOTIFY = true;
|
|
@@ -2747,20 +2774,6 @@ class CacheExpiration {
|
|
|
2747
2774
|
}
|
|
2748
2775
|
}
|
|
2749
2776
|
|
|
2750
|
-
const registerQuotaErrorCallback = (callback)=>{
|
|
2751
|
-
if (process.env.NODE_ENV !== "production") {
|
|
2752
|
-
finalAssertExports.isType(callback, "function", {
|
|
2753
|
-
moduleName: "@serwist/core",
|
|
2754
|
-
funcName: "register",
|
|
2755
|
-
paramName: "callback"
|
|
2756
|
-
});
|
|
2757
|
-
}
|
|
2758
|
-
quotaErrorCallbacks.add(callback);
|
|
2759
|
-
if (process.env.NODE_ENV !== "production") {
|
|
2760
|
-
logger.log("Registered a callback to respond to quota errors.", callback);
|
|
2761
|
-
}
|
|
2762
|
-
};
|
|
2763
|
-
|
|
2764
2777
|
class ExpirationPlugin {
|
|
2765
2778
|
_config;
|
|
2766
2779
|
_cacheExpirations;
|