serwist 9.0.0-preview.24 → 9.0.0-preview.26
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 +6 -7
- package/dist/NavigationRoute.d.ts.map +1 -1
- package/dist/PrecacheRoute.d.ts +2 -2
- package/dist/PrecacheRoute.d.ts.map +1 -1
- package/dist/RegExpRoute.d.ts +4 -4
- package/dist/RegExpRoute.d.ts.map +1 -1
- package/dist/Route.d.ts +10 -10
- package/dist/Route.d.ts.map +1 -1
- package/dist/Serwist.d.ts +39 -44
- package/dist/Serwist.d.ts.map +1 -1
- package/dist/chunks/printInstallDetails.js +1587 -0
- package/dist/chunks/resultingClientExists.js +1 -1
- package/dist/chunks/waitUntil.js +426 -1
- package/dist/copyResponse.d.ts +8 -10
- package/dist/copyResponse.d.ts.map +1 -1
- package/dist/disableDevLogs.d.ts +1 -1
- package/dist/index.d.ts +41 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.internal.d.ts +3 -1
- package/dist/index.internal.d.ts.map +1 -1
- package/dist/index.internal.js +1 -2
- package/dist/index.js +1464 -8
- package/dist/index.legacy.d.ts +4 -8
- package/dist/index.legacy.d.ts.map +1 -1
- package/dist/index.legacy.js +166 -16
- package/dist/legacy/PrecacheController.d.ts +1 -1
- package/dist/legacy/PrecacheController.d.ts.map +1 -1
- package/dist/legacy/PrecacheRoute.d.ts.map +1 -1
- package/dist/legacy/Router.d.ts +1 -1
- package/dist/legacy/Router.d.ts.map +1 -1
- package/dist/legacy/constants.d.ts.map +1 -0
- package/dist/legacy/fallbacks.d.ts +1 -1
- package/dist/legacy/fallbacks.d.ts.map +1 -1
- package/dist/legacy/handlePrecaching.d.ts.map +1 -1
- package/dist/legacy/initializeGoogleAnalytics.d.ts +38 -0
- package/dist/legacy/initializeGoogleAnalytics.d.ts.map +1 -0
- package/dist/legacy/installSerwist.d.ts +69 -3
- package/dist/legacy/installSerwist.d.ts.map +1 -1
- package/dist/legacy/registerRoute.d.ts +1 -1
- package/dist/legacy/registerRoute.d.ts.map +1 -1
- package/dist/{plugins → lib}/backgroundSync/BackgroundSyncPlugin.d.ts +3 -3
- package/dist/lib/backgroundSync/BackgroundSyncPlugin.d.ts.map +1 -0
- package/dist/{plugins/backgroundSync/Queue.d.ts → lib/backgroundSync/BackgroundSyncQueue.d.ts} +29 -13
- package/dist/lib/backgroundSync/BackgroundSyncQueue.d.ts.map +1 -0
- package/dist/{plugins/backgroundSync/QueueDb.d.ts → lib/backgroundSync/BackgroundSyncQueueDb.d.ts} +7 -7
- package/dist/lib/backgroundSync/BackgroundSyncQueueDb.d.ts.map +1 -0
- package/dist/{plugins/backgroundSync/QueueStore.d.ts → lib/backgroundSync/BackgroundSyncQueueStore.d.ts} +7 -7
- package/dist/lib/backgroundSync/BackgroundSyncQueueStore.d.ts.map +1 -0
- package/dist/lib/backgroundSync/StorableRequest.d.ts.map +1 -0
- package/dist/lib/broadcastUpdate/BroadcastCacheUpdate.d.ts.map +1 -0
- package/dist/{plugins → lib}/broadcastUpdate/BroadcastUpdatePlugin.d.ts +1 -1
- package/dist/lib/broadcastUpdate/BroadcastUpdatePlugin.d.ts.map +1 -0
- package/dist/lib/broadcastUpdate/constants.d.ts +5 -0
- package/dist/lib/broadcastUpdate/constants.d.ts.map +1 -0
- package/dist/lib/broadcastUpdate/responsesAreSame.d.ts.map +1 -0
- package/dist/{plugins → lib}/broadcastUpdate/types.d.ts +3 -3
- package/dist/lib/broadcastUpdate/types.d.ts.map +1 -0
- package/dist/lib/cacheableResponse/CacheableResponse.d.ts.map +1 -0
- package/dist/lib/cacheableResponse/CacheableResponsePlugin.d.ts.map +1 -0
- package/dist/{plugins → lib}/expiration/CacheExpiration.d.ts +1 -1
- package/dist/lib/expiration/CacheExpiration.d.ts.map +1 -0
- package/dist/{plugins → lib}/expiration/ExpirationPlugin.d.ts +1 -1
- package/dist/lib/expiration/ExpirationPlugin.d.ts.map +1 -0
- package/dist/lib/expiration/models/CacheTimestampsModel.d.ts.map +1 -0
- package/dist/lib/googleAnalytics/constants.d.ts +10 -0
- package/dist/lib/googleAnalytics/constants.d.ts.map +1 -0
- package/dist/{plugins/googleAnalytics/initialize.d.ts → lib/googleAnalytics/initializeGoogleAnalytics.d.ts} +2 -2
- package/dist/lib/googleAnalytics/initializeGoogleAnalytics.d.ts.map +1 -0
- package/dist/{plugins → lib}/precaching/PrecacheFallbackPlugin.d.ts +1 -1
- package/dist/lib/precaching/PrecacheFallbackPlugin.d.ts.map +1 -0
- package/dist/lib/rangeRequests/RangeRequestsPlugin.d.ts.map +1 -0
- package/dist/lib/rangeRequests/createPartialResponse.d.ts.map +1 -0
- package/dist/lib/rangeRequests/utils/calculateEffectiveBoundaries.d.ts.map +1 -0
- package/dist/lib/rangeRequests/utils/parseRangeHeader.d.ts.map +1 -0
- package/dist/{strategies → lib/strategies}/CacheFirst.d.ts +1 -1
- package/dist/lib/strategies/CacheFirst.d.ts.map +1 -0
- package/dist/{strategies → lib/strategies}/CacheOnly.d.ts +2 -2
- package/dist/lib/strategies/CacheOnly.d.ts.map +1 -0
- package/dist/{strategies → lib/strategies}/NetworkFirst.d.ts +2 -2
- package/dist/lib/strategies/NetworkFirst.d.ts.map +1 -0
- package/dist/{strategies → lib/strategies}/NetworkOnly.d.ts +2 -2
- package/dist/lib/strategies/NetworkOnly.d.ts.map +1 -0
- package/dist/{PrecacheStrategy.d.ts → lib/strategies/PrecacheStrategy.d.ts} +5 -5
- package/dist/lib/strategies/PrecacheStrategy.d.ts.map +1 -0
- package/dist/{strategies → lib/strategies}/StaleWhileRevalidate.d.ts +1 -1
- package/dist/lib/strategies/StaleWhileRevalidate.d.ts.map +1 -0
- package/dist/{strategies → lib/strategies}/Strategy.d.ts +15 -17
- package/dist/lib/strategies/Strategy.d.ts.map +1 -0
- package/dist/{strategies → lib/strategies}/StrategyHandler.d.ts +35 -41
- package/dist/lib/strategies/StrategyHandler.d.ts.map +1 -0
- package/dist/lib/strategies/plugins/cacheOkAndOpaquePlugin.d.ts +3 -0
- package/dist/lib/strategies/plugins/cacheOkAndOpaquePlugin.d.ts.map +1 -0
- package/dist/lib/strategies/utils/messages.d.ts.map +1 -0
- package/dist/navigationPreload.d.ts +3 -3
- package/dist/types.d.ts +12 -7
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/PrecacheCacheKeyPlugin.d.ts +1 -1
- package/dist/utils/PrecacheCacheKeyPlugin.d.ts.map +1 -1
- package/dist/utils/assert.d.ts.map +1 -1
- package/dist/utils/cleanupOutdatedCaches.d.ts.map +1 -0
- package/dist/utils/clientsClaim.d.ts.map +1 -0
- package/dist/{parseRoute.d.ts → utils/parseRoute.d.ts} +3 -3
- package/dist/utils/parseRoute.d.ts.map +1 -0
- package/package.json +7 -21
- package/src/NavigationRoute.ts +6 -7
- package/src/PrecacheRoute.ts +5 -5
- package/src/RegExpRoute.ts +5 -5
- package/src/Route.ts +11 -11
- package/src/Serwist.ts +73 -80
- package/src/copyResponse.ts +7 -9
- package/src/disableDevLogs.ts +1 -1
- package/src/index.internal.ts +4 -0
- package/src/index.legacy.ts +14 -18
- package/src/index.ts +105 -19
- package/src/legacy/PrecacheController.ts +8 -8
- package/src/legacy/PrecacheFallbackPlugin.ts +1 -1
- package/src/legacy/PrecacheRoute.ts +3 -3
- package/src/legacy/Router.ts +5 -5
- package/src/legacy/fallbacks.ts +3 -3
- package/src/legacy/handlePrecaching.ts +1 -1
- package/src/legacy/initializeGoogleAnalytics.ts +218 -0
- package/src/legacy/installSerwist.ts +156 -5
- package/src/legacy/registerRoute.ts +1 -1
- package/src/{plugins → lib}/backgroundSync/BackgroundSyncPlugin.ts +6 -6
- package/src/{plugins/backgroundSync/Queue.ts → lib/backgroundSync/BackgroundSyncQueue.ts} +47 -26
- package/src/{plugins/backgroundSync/QueueDb.ts → lib/backgroundSync/BackgroundSyncQueueDb.ts} +16 -16
- package/src/{plugins/backgroundSync/QueueStore.ts → lib/backgroundSync/BackgroundSyncQueueStore.ts} +13 -13
- package/src/{plugins → lib}/backgroundSync/StorableRequest.ts +2 -2
- package/src/{plugins → lib}/broadcastUpdate/BroadcastCacheUpdate.ts +14 -9
- package/src/{plugins → lib}/broadcastUpdate/BroadcastUpdatePlugin.ts +1 -1
- package/src/lib/broadcastUpdate/constants.ts +12 -0
- package/src/{plugins → lib}/broadcastUpdate/types.ts +3 -3
- package/src/{plugins → lib}/cacheableResponse/CacheableResponse.ts +5 -5
- package/src/{plugins → lib}/expiration/CacheExpiration.ts +7 -7
- package/src/{plugins → lib}/expiration/ExpirationPlugin.ts +10 -10
- package/src/lib/googleAnalytics/constants.ts +22 -0
- package/src/{plugins/googleAnalytics/initialize.ts → lib/googleAnalytics/initializeGoogleAnalytics.ts} +9 -9
- package/src/{strategies → lib/strategies}/CacheFirst.ts +5 -5
- package/src/{strategies → lib/strategies}/CacheOnly.ts +6 -6
- package/src/{strategies → lib/strategies}/NetworkFirst.ts +7 -7
- package/src/{strategies → lib/strategies}/NetworkOnly.ts +7 -7
- package/src/{PrecacheStrategy.ts → lib/strategies/PrecacheStrategy.ts} +14 -10
- package/src/{strategies → lib/strategies}/StaleWhileRevalidate.ts +5 -5
- package/src/{strategies → lib/strategies}/Strategy.ts +19 -21
- package/src/{strategies → lib/strategies}/StrategyHandler.ts +45 -51
- package/src/{strategies → lib/strategies}/plugins/cacheOkAndOpaquePlugin.ts +1 -1
- package/src/{strategies → lib/strategies}/utils/messages.ts +2 -2
- package/src/models/messages/messages.ts +1 -1
- package/src/navigationPreload.ts +3 -3
- package/src/registerQuotaErrorCallback.ts +1 -1
- package/src/types.ts +12 -7
- package/src/utils/PrecacheCacheKeyPlugin.ts +1 -1
- package/src/utils/assert.ts +1 -1
- package/src/{cleanupOutdatedCaches.ts → utils/cleanupOutdatedCaches.ts} +3 -4
- package/src/utils/executeQuotaErrorCallbacks.ts +1 -1
- package/src/{parseRoute.ts → utils/parseRoute.ts} +21 -23
- package/src/utils/welcome.ts +1 -1
- package/dist/PrecacheStrategy.d.ts.map +0 -1
- package/dist/chunks/NetworkOnly.js +0 -599
- package/dist/chunks/PrecacheFallbackPlugin.js +0 -634
- package/dist/chunks/Serwist.js +0 -1034
- package/dist/chunks/registerQuotaErrorCallback.js +0 -17
- package/dist/chunks/timeout.js +0 -400
- package/dist/cleanupOutdatedCaches.d.ts.map +0 -1
- package/dist/clientsClaim.d.ts.map +0 -1
- package/dist/index.plugins.d.ts +0 -41
- package/dist/index.plugins.d.ts.map +0 -1
- package/dist/index.plugins.js +0 -669
- package/dist/index.strategies.d.ts +0 -22
- package/dist/index.strategies.d.ts.map +0 -1
- package/dist/index.strategies.js +0 -144
- package/dist/parseRoute.d.ts.map +0 -1
- package/dist/plugins/backgroundSync/BackgroundSyncPlugin.d.ts.map +0 -1
- package/dist/plugins/backgroundSync/Queue.d.ts.map +0 -1
- package/dist/plugins/backgroundSync/QueueDb.d.ts.map +0 -1
- package/dist/plugins/backgroundSync/QueueStore.d.ts.map +0 -1
- package/dist/plugins/backgroundSync/StorableRequest.d.ts.map +0 -1
- package/dist/plugins/broadcastUpdate/BroadcastCacheUpdate.d.ts.map +0 -1
- package/dist/plugins/broadcastUpdate/BroadcastUpdatePlugin.d.ts.map +0 -1
- package/dist/plugins/broadcastUpdate/constants.d.ts +0 -5
- package/dist/plugins/broadcastUpdate/constants.d.ts.map +0 -1
- package/dist/plugins/broadcastUpdate/responsesAreSame.d.ts.map +0 -1
- package/dist/plugins/broadcastUpdate/types.d.ts.map +0 -1
- package/dist/plugins/cacheableResponse/CacheableResponse.d.ts.map +0 -1
- package/dist/plugins/cacheableResponse/CacheableResponsePlugin.d.ts.map +0 -1
- package/dist/plugins/expiration/CacheExpiration.d.ts.map +0 -1
- package/dist/plugins/expiration/ExpirationPlugin.d.ts.map +0 -1
- package/dist/plugins/expiration/models/CacheTimestampsModel.d.ts.map +0 -1
- package/dist/plugins/googleAnalytics/constants.d.ts.map +0 -1
- package/dist/plugins/googleAnalytics/initialize.d.ts.map +0 -1
- package/dist/plugins/precaching/PrecacheFallbackPlugin.d.ts.map +0 -1
- package/dist/plugins/rangeRequests/RangeRequestsPlugin.d.ts.map +0 -1
- package/dist/plugins/rangeRequests/createPartialResponse.d.ts.map +0 -1
- package/dist/plugins/rangeRequests/utils/calculateEffectiveBoundaries.d.ts.map +0 -1
- package/dist/plugins/rangeRequests/utils/parseRangeHeader.d.ts.map +0 -1
- package/dist/strategies/CacheFirst.d.ts.map +0 -1
- package/dist/strategies/CacheOnly.d.ts.map +0 -1
- package/dist/strategies/NetworkFirst.d.ts.map +0 -1
- package/dist/strategies/NetworkOnly.d.ts.map +0 -1
- package/dist/strategies/StaleWhileRevalidate.d.ts.map +0 -1
- package/dist/strategies/Strategy.d.ts.map +0 -1
- package/dist/strategies/StrategyHandler.d.ts.map +0 -1
- package/dist/strategies/plugins/cacheOkAndOpaquePlugin.d.ts +0 -3
- package/dist/strategies/plugins/cacheOkAndOpaquePlugin.d.ts.map +0 -1
- package/dist/strategies/utils/messages.d.ts.map +0 -1
- package/src/index.plugins.ts +0 -95
- package/src/index.strategies.ts +0 -26
- package/src/plugins/broadcastUpdate/constants.ts +0 -12
- package/dist/{plugins/googleAnalytics → legacy}/constants.d.ts +0 -0
- package/dist/{plugins → lib}/backgroundSync/StorableRequest.d.ts +0 -0
- package/dist/{plugins → lib}/broadcastUpdate/BroadcastCacheUpdate.d.ts +0 -0
- package/dist/{plugins → lib}/broadcastUpdate/responsesAreSame.d.ts +0 -0
- package/dist/{plugins → lib}/cacheableResponse/CacheableResponse.d.ts +0 -0
- package/dist/{plugins → lib}/cacheableResponse/CacheableResponsePlugin.d.ts +0 -0
- package/dist/{plugins → lib}/expiration/models/CacheTimestampsModel.d.ts +0 -0
- package/dist/{plugins → lib}/rangeRequests/RangeRequestsPlugin.d.ts +0 -0
- package/dist/{plugins → lib}/rangeRequests/createPartialResponse.d.ts +0 -0
- package/dist/{plugins → lib}/rangeRequests/utils/calculateEffectiveBoundaries.d.ts +0 -0
- package/dist/{plugins → lib}/rangeRequests/utils/parseRangeHeader.d.ts +0 -0
- package/dist/{strategies → lib/strategies}/utils/messages.d.ts +0 -0
- package/dist/{cleanupOutdatedCaches.d.ts → utils/cleanupOutdatedCaches.d.ts} +0 -0
- package/dist/{clientsClaim.d.ts → utils/clientsClaim.d.ts} +0 -0
- package/src/{plugins/googleAnalytics → legacy}/constants.ts +0 -0
- package/src/{plugins → lib}/broadcastUpdate/responsesAreSame.ts +0 -0
- package/src/{plugins → lib}/cacheableResponse/CacheableResponsePlugin.ts +0 -0
- package/src/{plugins → lib}/expiration/models/CacheTimestampsModel.ts +0 -0
- package/src/{plugins → lib}/precaching/PrecacheFallbackPlugin.ts +1 -1
- package/src/{plugins → lib}/rangeRequests/RangeRequestsPlugin.ts +0 -0
- package/src/{plugins → lib}/rangeRequests/createPartialResponse.ts +1 -1
- package/src/{plugins → lib}/rangeRequests/utils/calculateEffectiveBoundaries.ts +1 -1
- package/src/{plugins → lib}/rangeRequests/utils/parseRangeHeader.ts +1 -1
- /package/src/{clientsClaim.ts → utils/clientsClaim.ts} +0 -0
package/dist/index.js
CHANGED
|
@@ -1,10 +1,652 @@
|
|
|
1
|
-
import { c as
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import '
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import { R as Route, g as generateURLVariations, B as BackgroundSyncPlugin, N as NetworkOnly, a as NetworkFirst, P as PrecacheStrategy, e as enableNavigationPreload, s as setCacheNameDetails, b as NavigationRoute, S as Strategy, d as disableDevLogs, c as createCacheKey, p as parallel, f as printInstallDetails, h as printCleanupDetails, n as normalizeHandler, i as defaultMethod, j as parseRoute, k as PrecacheInstallReportPlugin, m as messages, l as cacheOkAndOpaquePlugin } from './chunks/printInstallDetails.js';
|
|
2
|
+
export { v as BackgroundSyncQueue, w as BackgroundSyncQueueStore, u as RegExpRoute, x as StorableRequest, t as StrategyHandler, o as copyResponse, q as disableNavigationPreload, r as isNavigationPreloadSupported } from './chunks/printInstallDetails.js';
|
|
3
|
+
import { l as logger, g as getFriendlyURL, c as cacheNames$1, a as clientsClaim, b as cleanupOutdatedCaches, f as finalAssertExports, S as SerwistError, w as waitUntil, t as timeout, q as quotaErrorCallbacks } from './chunks/waitUntil.js';
|
|
4
|
+
import { r as resultingClientExists } from './chunks/resultingClientExists.js';
|
|
5
|
+
import { deleteDB, openDB } from 'idb';
|
|
6
|
+
|
|
7
|
+
class PrecacheRoute extends Route {
|
|
8
|
+
constructor(serwist, options){
|
|
9
|
+
const match = ({ request })=>{
|
|
10
|
+
const urlsToCacheKeys = serwist.getUrlsToPrecacheKeys();
|
|
11
|
+
for (const possibleURL of generateURLVariations(request.url, options)){
|
|
12
|
+
const cacheKey = urlsToCacheKeys.get(possibleURL);
|
|
13
|
+
if (cacheKey) {
|
|
14
|
+
const integrity = serwist.getIntegrityForPrecacheKey(cacheKey);
|
|
15
|
+
return {
|
|
16
|
+
cacheKey,
|
|
17
|
+
integrity
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
if (process.env.NODE_ENV !== "production") {
|
|
22
|
+
logger.debug(`Precaching did not find a match for ${getFriendlyURL(request.url)}.`);
|
|
23
|
+
}
|
|
24
|
+
return;
|
|
25
|
+
};
|
|
26
|
+
super(match, serwist.precacheStrategy);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const QUEUE_NAME = "serwist-google-analytics";
|
|
31
|
+
const MAX_RETENTION_TIME = 60 * 48;
|
|
32
|
+
const GOOGLE_ANALYTICS_HOST = "www.google-analytics.com";
|
|
33
|
+
const GTM_HOST = "www.googletagmanager.com";
|
|
34
|
+
const ANALYTICS_JS_PATH = "/analytics.js";
|
|
35
|
+
const GTAG_JS_PATH = "/gtag/js";
|
|
36
|
+
const GTM_JS_PATH = "/gtm.js";
|
|
37
|
+
const COLLECT_PATHS_REGEX = /^\/(\w+\/)?collect/;
|
|
38
|
+
|
|
39
|
+
const createOnSyncCallback = (config)=>{
|
|
40
|
+
return async ({ queue })=>{
|
|
41
|
+
let entry = undefined;
|
|
42
|
+
while(entry = await queue.shiftRequest()){
|
|
43
|
+
const { request, timestamp } = entry;
|
|
44
|
+
const url = new URL(request.url);
|
|
45
|
+
try {
|
|
46
|
+
const params = request.method === "POST" ? new URLSearchParams(await request.clone().text()) : url.searchParams;
|
|
47
|
+
const originalHitTime = timestamp - (Number(params.get("qt")) || 0);
|
|
48
|
+
const queueTime = Date.now() - originalHitTime;
|
|
49
|
+
params.set("qt", String(queueTime));
|
|
50
|
+
if (config.parameterOverrides) {
|
|
51
|
+
for (const param of Object.keys(config.parameterOverrides)){
|
|
52
|
+
const value = config.parameterOverrides[param];
|
|
53
|
+
params.set(param, value);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (typeof config.hitFilter === "function") {
|
|
57
|
+
config.hitFilter.call(null, params);
|
|
58
|
+
}
|
|
59
|
+
await fetch(new Request(url.origin + url.pathname, {
|
|
60
|
+
body: params.toString(),
|
|
61
|
+
method: "POST",
|
|
62
|
+
mode: "cors",
|
|
63
|
+
credentials: "omit",
|
|
64
|
+
headers: {
|
|
65
|
+
"Content-Type": "text/plain"
|
|
66
|
+
}
|
|
67
|
+
}));
|
|
68
|
+
if (process.env.NODE_ENV !== "production") {
|
|
69
|
+
logger.log(`Request for '${getFriendlyURL(url.href)}' has been replayed`);
|
|
70
|
+
}
|
|
71
|
+
} catch (err) {
|
|
72
|
+
await queue.unshiftRequest(entry);
|
|
73
|
+
if (process.env.NODE_ENV !== "production") {
|
|
74
|
+
logger.log(`Request for '${getFriendlyURL(url.href)}' failed to replay, putting it back in the queue.`);
|
|
75
|
+
}
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (process.env.NODE_ENV !== "production") {
|
|
80
|
+
logger.log("All Google Analytics request successfully replayed; " + "the queue is now empty!");
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
const createCollectRoutes = (bgSyncPlugin)=>{
|
|
85
|
+
const match = ({ url })=>url.hostname === GOOGLE_ANALYTICS_HOST && COLLECT_PATHS_REGEX.test(url.pathname);
|
|
86
|
+
const handler = new NetworkOnly({
|
|
87
|
+
plugins: [
|
|
88
|
+
bgSyncPlugin
|
|
89
|
+
]
|
|
90
|
+
});
|
|
91
|
+
return [
|
|
92
|
+
new Route(match, handler, "GET"),
|
|
93
|
+
new Route(match, handler, "POST")
|
|
94
|
+
];
|
|
95
|
+
};
|
|
96
|
+
const createAnalyticsJsRoute = (cacheName)=>{
|
|
97
|
+
const match = ({ url })=>url.hostname === GOOGLE_ANALYTICS_HOST && url.pathname === ANALYTICS_JS_PATH;
|
|
98
|
+
const handler = new NetworkFirst({
|
|
99
|
+
cacheName
|
|
100
|
+
});
|
|
101
|
+
return new Route(match, handler, "GET");
|
|
102
|
+
};
|
|
103
|
+
const createGtagJsRoute = (cacheName)=>{
|
|
104
|
+
const match = ({ url })=>url.hostname === GTM_HOST && url.pathname === GTAG_JS_PATH;
|
|
105
|
+
const handler = new NetworkFirst({
|
|
106
|
+
cacheName
|
|
107
|
+
});
|
|
108
|
+
return new Route(match, handler, "GET");
|
|
109
|
+
};
|
|
110
|
+
const createGtmJsRoute = (cacheName)=>{
|
|
111
|
+
const match = ({ url })=>url.hostname === GTM_HOST && url.pathname === GTM_JS_PATH;
|
|
112
|
+
const handler = new NetworkFirst({
|
|
113
|
+
cacheName
|
|
114
|
+
});
|
|
115
|
+
return new Route(match, handler, "GET");
|
|
116
|
+
};
|
|
117
|
+
const initializeGoogleAnalytics = ({ serwist, cacheName, ...options })=>{
|
|
118
|
+
const resolvedCacheName = cacheNames$1.getGoogleAnalyticsName(cacheName);
|
|
119
|
+
const bgSyncPlugin = new BackgroundSyncPlugin(QUEUE_NAME, {
|
|
120
|
+
maxRetentionTime: MAX_RETENTION_TIME,
|
|
121
|
+
onSync: createOnSyncCallback(options)
|
|
122
|
+
});
|
|
123
|
+
const routes = [
|
|
124
|
+
createGtmJsRoute(resolvedCacheName),
|
|
125
|
+
createAnalyticsJsRoute(resolvedCacheName),
|
|
126
|
+
createGtagJsRoute(resolvedCacheName),
|
|
127
|
+
...createCollectRoutes(bgSyncPlugin)
|
|
128
|
+
];
|
|
129
|
+
for (const route of routes){
|
|
130
|
+
serwist.registerRoute(route);
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
class PrecacheFallbackPlugin {
|
|
135
|
+
_fallbackUrls;
|
|
136
|
+
_serwist;
|
|
137
|
+
constructor({ fallbackUrls, serwist }){
|
|
138
|
+
this._fallbackUrls = fallbackUrls;
|
|
139
|
+
this._serwist = serwist;
|
|
140
|
+
}
|
|
141
|
+
async handlerDidError(param) {
|
|
142
|
+
for (const fallback of this._fallbackUrls){
|
|
143
|
+
if (typeof fallback === "string") {
|
|
144
|
+
const fallbackResponse = await this._serwist.matchPrecache(fallback);
|
|
145
|
+
if (fallbackResponse !== undefined) {
|
|
146
|
+
return fallbackResponse;
|
|
147
|
+
}
|
|
148
|
+
} else if (fallback.matcher(param)) {
|
|
149
|
+
const fallbackResponse = await this._serwist.matchPrecache(fallback.url);
|
|
150
|
+
if (fallbackResponse !== undefined) {
|
|
151
|
+
return fallbackResponse;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
class PrecacheCacheKeyPlugin {
|
|
160
|
+
_precacheController;
|
|
161
|
+
constructor({ precacheController }){
|
|
162
|
+
this._precacheController = precacheController;
|
|
163
|
+
}
|
|
164
|
+
cacheKeyWillBeUsed = async ({ request, params })=>{
|
|
165
|
+
const cacheKey = params?.cacheKey || this._precacheController.getPrecacheKeyForUrl(request.url);
|
|
166
|
+
return cacheKey ? new Request(cacheKey, {
|
|
167
|
+
headers: request.headers
|
|
168
|
+
}) : request;
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
class Serwist {
|
|
173
|
+
_urlsToCacheKeys = new Map();
|
|
174
|
+
_urlsToCacheModes = new Map();
|
|
175
|
+
_cacheKeysToIntegrities = new Map();
|
|
176
|
+
_concurrentPrecaching;
|
|
177
|
+
_precacheStrategy;
|
|
178
|
+
_routes;
|
|
179
|
+
_defaultHandlerMap;
|
|
180
|
+
_catchHandler;
|
|
181
|
+
constructor({ precacheEntries, precacheOptions, skipWaiting = false, importScripts, navigationPreload = false, cacheId, clientsClaim: clientsClaim$1 = false, runtimeCaching, offlineAnalyticsConfig, disableDevLogs: disableDevLogs$1 = false, fallbacks } = {}){
|
|
182
|
+
this._concurrentPrecaching = precacheOptions?.concurrency ?? 10;
|
|
183
|
+
this._precacheStrategy = new PrecacheStrategy({
|
|
184
|
+
cacheName: cacheNames$1.getPrecacheName(precacheOptions?.cacheName),
|
|
185
|
+
plugins: [
|
|
186
|
+
...precacheOptions?.plugins ?? [],
|
|
187
|
+
new PrecacheCacheKeyPlugin({
|
|
188
|
+
precacheController: this
|
|
189
|
+
})
|
|
190
|
+
],
|
|
191
|
+
fallbackToNetwork: precacheOptions?.fallbackToNetwork
|
|
192
|
+
});
|
|
193
|
+
this._routes = new Map();
|
|
194
|
+
this._defaultHandlerMap = new Map();
|
|
195
|
+
this.handleInstall = this.handleInstall.bind(this);
|
|
196
|
+
this.handleActivate = this.handleActivate.bind(this);
|
|
197
|
+
this.handleFetch = this.handleFetch.bind(this);
|
|
198
|
+
this.handleCache = this.handleCache.bind(this);
|
|
199
|
+
if (!!importScripts && importScripts.length > 0) self.importScripts(...importScripts);
|
|
200
|
+
if (navigationPreload) enableNavigationPreload();
|
|
201
|
+
if (cacheId !== undefined) {
|
|
202
|
+
setCacheNameDetails({
|
|
203
|
+
prefix: cacheId
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
if (skipWaiting) {
|
|
207
|
+
self.skipWaiting();
|
|
208
|
+
} else {
|
|
209
|
+
self.addEventListener("message", (event)=>{
|
|
210
|
+
if (event.data && event.data.type === "SKIP_WAITING") {
|
|
211
|
+
self.skipWaiting();
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
if (clientsClaim$1) clientsClaim();
|
|
216
|
+
if (!!precacheEntries && precacheEntries.length > 0) {
|
|
217
|
+
this.addToPrecacheList(precacheEntries);
|
|
218
|
+
}
|
|
219
|
+
if (precacheOptions?.cleanupOutdatedCaches) {
|
|
220
|
+
cleanupOutdatedCaches(precacheOptions?.cacheName);
|
|
221
|
+
}
|
|
222
|
+
this.registerRoute(new PrecacheRoute(this, precacheOptions));
|
|
223
|
+
if (precacheOptions?.navigateFallback) {
|
|
224
|
+
this.registerRoute(new NavigationRoute(this.createHandlerBoundToUrl(precacheOptions?.navigateFallback), {
|
|
225
|
+
allowlist: precacheOptions?.navigateFallbackAllowlist,
|
|
226
|
+
denylist: precacheOptions?.navigateFallbackDenylist
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
if (offlineAnalyticsConfig !== undefined) {
|
|
230
|
+
if (typeof offlineAnalyticsConfig === "boolean") {
|
|
231
|
+
offlineAnalyticsConfig && initializeGoogleAnalytics({
|
|
232
|
+
serwist: this
|
|
233
|
+
});
|
|
234
|
+
} else {
|
|
235
|
+
initializeGoogleAnalytics({
|
|
236
|
+
...offlineAnalyticsConfig,
|
|
237
|
+
serwist: this
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
if (runtimeCaching !== undefined) {
|
|
242
|
+
if (fallbacks !== undefined) {
|
|
243
|
+
const fallbackPlugin = new PrecacheFallbackPlugin({
|
|
244
|
+
fallbackUrls: fallbacks.entries,
|
|
245
|
+
serwist: this
|
|
246
|
+
});
|
|
247
|
+
runtimeCaching.forEach((cacheEntry)=>{
|
|
248
|
+
if (cacheEntry.handler instanceof Strategy && !cacheEntry.handler.plugins.some((plugin)=>"handlerDidError" in plugin)) {
|
|
249
|
+
cacheEntry.handler.plugins.push(fallbackPlugin);
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
for (const entry of runtimeCaching){
|
|
254
|
+
this.registerCapture(entry.matcher, entry.handler, entry.method);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (disableDevLogs$1) disableDevLogs();
|
|
258
|
+
}
|
|
259
|
+
get precacheStrategy() {
|
|
260
|
+
return this._precacheStrategy;
|
|
261
|
+
}
|
|
262
|
+
get routes() {
|
|
263
|
+
return this._routes;
|
|
264
|
+
}
|
|
265
|
+
addEventListeners() {
|
|
266
|
+
self.addEventListener("install", this.handleInstall);
|
|
267
|
+
self.addEventListener("activate", this.handleActivate);
|
|
268
|
+
self.addEventListener("fetch", this.handleFetch);
|
|
269
|
+
self.addEventListener("message", this.handleCache);
|
|
270
|
+
}
|
|
271
|
+
addToPrecacheList(entries) {
|
|
272
|
+
if (process.env.NODE_ENV !== "production") {
|
|
273
|
+
finalAssertExports.isArray(entries, {
|
|
274
|
+
moduleName: "serwist",
|
|
275
|
+
className: "Serwist",
|
|
276
|
+
funcName: "addToCacheList",
|
|
277
|
+
paramName: "entries"
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
const urlsToWarnAbout = [];
|
|
281
|
+
for (const entry of entries){
|
|
282
|
+
if (typeof entry === "string") {
|
|
283
|
+
urlsToWarnAbout.push(entry);
|
|
284
|
+
} else if (entry && !entry.integrity && entry.revision === undefined) {
|
|
285
|
+
urlsToWarnAbout.push(entry.url);
|
|
286
|
+
}
|
|
287
|
+
const { cacheKey, url } = createCacheKey(entry);
|
|
288
|
+
const cacheMode = typeof entry !== "string" && entry.revision ? "reload" : "default";
|
|
289
|
+
if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) {
|
|
290
|
+
throw new SerwistError("add-to-cache-list-conflicting-entries", {
|
|
291
|
+
firstEntry: this._urlsToCacheKeys.get(url),
|
|
292
|
+
secondEntry: cacheKey
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
if (typeof entry !== "string" && entry.integrity) {
|
|
296
|
+
if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) {
|
|
297
|
+
throw new SerwistError("add-to-cache-list-conflicting-integrities", {
|
|
298
|
+
url
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);
|
|
302
|
+
}
|
|
303
|
+
this._urlsToCacheKeys.set(url, cacheKey);
|
|
304
|
+
this._urlsToCacheModes.set(url, cacheMode);
|
|
305
|
+
if (urlsToWarnAbout.length > 0) {
|
|
306
|
+
const warningMessage = `Serwist is precaching URLs without revision info: ${urlsToWarnAbout.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;
|
|
307
|
+
if (process.env.NODE_ENV === "production") {
|
|
308
|
+
console.warn(warningMessage);
|
|
309
|
+
} else {
|
|
310
|
+
logger.warn(warningMessage);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
handleInstall(event) {
|
|
316
|
+
return waitUntil(event, async ()=>{
|
|
317
|
+
const installReportPlugin = new PrecacheInstallReportPlugin();
|
|
318
|
+
this.precacheStrategy.plugins.push(installReportPlugin);
|
|
319
|
+
await parallel(this._concurrentPrecaching, Array.from(this._urlsToCacheKeys.entries()), async ([url, cacheKey])=>{
|
|
320
|
+
const integrity = this._cacheKeysToIntegrities.get(cacheKey);
|
|
321
|
+
const cacheMode = this._urlsToCacheModes.get(url);
|
|
322
|
+
const request = new Request(url, {
|
|
323
|
+
integrity,
|
|
324
|
+
cache: cacheMode,
|
|
325
|
+
credentials: "same-origin"
|
|
326
|
+
});
|
|
327
|
+
await Promise.all(this.precacheStrategy.handleAll({
|
|
328
|
+
event,
|
|
329
|
+
request,
|
|
330
|
+
url: new URL(request.url),
|
|
331
|
+
params: {
|
|
332
|
+
cacheKey
|
|
333
|
+
}
|
|
334
|
+
}));
|
|
335
|
+
});
|
|
336
|
+
const { updatedURLs, notUpdatedURLs } = installReportPlugin;
|
|
337
|
+
if (process.env.NODE_ENV !== "production") {
|
|
338
|
+
printInstallDetails(updatedURLs, notUpdatedURLs);
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
updatedURLs,
|
|
342
|
+
notUpdatedURLs
|
|
343
|
+
};
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
handleActivate(event) {
|
|
347
|
+
return waitUntil(event, async ()=>{
|
|
348
|
+
const cache = await self.caches.open(this.precacheStrategy.cacheName);
|
|
349
|
+
const currentlyCachedRequests = await cache.keys();
|
|
350
|
+
const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());
|
|
351
|
+
const deletedCacheRequests = [];
|
|
352
|
+
for (const request of currentlyCachedRequests){
|
|
353
|
+
if (!expectedCacheKeys.has(request.url)) {
|
|
354
|
+
await cache.delete(request);
|
|
355
|
+
deletedCacheRequests.push(request.url);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (process.env.NODE_ENV !== "production") {
|
|
359
|
+
printCleanupDetails(deletedCacheRequests);
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
deletedCacheRequests
|
|
363
|
+
};
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
handleFetch(event) {
|
|
367
|
+
const { request } = event;
|
|
368
|
+
const responsePromise = this.handleRequest({
|
|
369
|
+
request,
|
|
370
|
+
event
|
|
371
|
+
});
|
|
372
|
+
if (responsePromise) {
|
|
373
|
+
event.respondWith(responsePromise);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
handleCache(event) {
|
|
377
|
+
if (event.data && event.data.type === "CACHE_URLS") {
|
|
378
|
+
const { payload } = event.data;
|
|
379
|
+
if (process.env.NODE_ENV !== "production") {
|
|
380
|
+
logger.debug("Caching URLs from the window", payload.urlsToCache);
|
|
381
|
+
}
|
|
382
|
+
const requestPromises = Promise.all(payload.urlsToCache.map((entry)=>{
|
|
383
|
+
let request;
|
|
384
|
+
if (typeof entry === "string") {
|
|
385
|
+
request = new Request(entry);
|
|
386
|
+
} else {
|
|
387
|
+
request = new Request(...entry);
|
|
388
|
+
}
|
|
389
|
+
return this.handleRequest({
|
|
390
|
+
request,
|
|
391
|
+
event
|
|
392
|
+
});
|
|
393
|
+
}));
|
|
394
|
+
event.waitUntil(requestPromises);
|
|
395
|
+
if (event.ports?.[0]) {
|
|
396
|
+
void requestPromises.then(()=>event.ports[0].postMessage(true));
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
setDefaultHandler(handler, method = defaultMethod) {
|
|
401
|
+
this._defaultHandlerMap.set(method, normalizeHandler(handler));
|
|
402
|
+
}
|
|
403
|
+
setCatchHandler(handler) {
|
|
404
|
+
this._catchHandler = normalizeHandler(handler);
|
|
405
|
+
}
|
|
406
|
+
registerCapture(capture, handler, method) {
|
|
407
|
+
const route = parseRoute(capture, handler, method);
|
|
408
|
+
this.registerRoute(route);
|
|
409
|
+
return route;
|
|
410
|
+
}
|
|
411
|
+
registerRoute(route) {
|
|
412
|
+
if (process.env.NODE_ENV !== "production") {
|
|
413
|
+
finalAssertExports.isType(route, "object", {
|
|
414
|
+
moduleName: "serwist",
|
|
415
|
+
className: "Serwist",
|
|
416
|
+
funcName: "registerRoute",
|
|
417
|
+
paramName: "route"
|
|
418
|
+
});
|
|
419
|
+
finalAssertExports.hasMethod(route, "match", {
|
|
420
|
+
moduleName: "serwist",
|
|
421
|
+
className: "Serwist",
|
|
422
|
+
funcName: "registerRoute",
|
|
423
|
+
paramName: "route"
|
|
424
|
+
});
|
|
425
|
+
finalAssertExports.isType(route.handler, "object", {
|
|
426
|
+
moduleName: "serwist",
|
|
427
|
+
className: "Serwist",
|
|
428
|
+
funcName: "registerRoute",
|
|
429
|
+
paramName: "route"
|
|
430
|
+
});
|
|
431
|
+
finalAssertExports.hasMethod(route.handler, "handle", {
|
|
432
|
+
moduleName: "serwist",
|
|
433
|
+
className: "Serwist",
|
|
434
|
+
funcName: "registerRoute",
|
|
435
|
+
paramName: "route.handler"
|
|
436
|
+
});
|
|
437
|
+
finalAssertExports.isType(route.method, "string", {
|
|
438
|
+
moduleName: "serwist",
|
|
439
|
+
className: "Serwist",
|
|
440
|
+
funcName: "registerRoute",
|
|
441
|
+
paramName: "route.method"
|
|
442
|
+
});
|
|
443
|
+
}
|
|
444
|
+
if (!this._routes.has(route.method)) {
|
|
445
|
+
this._routes.set(route.method, []);
|
|
446
|
+
}
|
|
447
|
+
this._routes.get(route.method).push(route);
|
|
448
|
+
}
|
|
449
|
+
unregisterRoute(route) {
|
|
450
|
+
if (!this._routes.has(route.method)) {
|
|
451
|
+
throw new SerwistError("unregister-route-but-not-found-with-method", {
|
|
452
|
+
method: route.method
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
const routeIndex = this._routes.get(route.method).indexOf(route);
|
|
456
|
+
if (routeIndex > -1) {
|
|
457
|
+
this._routes.get(route.method).splice(routeIndex, 1);
|
|
458
|
+
} else {
|
|
459
|
+
throw new SerwistError("unregister-route-route-not-registered");
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
getUrlsToPrecacheKeys() {
|
|
463
|
+
return this._urlsToCacheKeys;
|
|
464
|
+
}
|
|
465
|
+
getPrecachedUrls() {
|
|
466
|
+
return [
|
|
467
|
+
...this._urlsToCacheKeys.keys()
|
|
468
|
+
];
|
|
469
|
+
}
|
|
470
|
+
getPrecacheKeyForUrl(url) {
|
|
471
|
+
const urlObject = new URL(url, location.href);
|
|
472
|
+
return this._urlsToCacheKeys.get(urlObject.href);
|
|
473
|
+
}
|
|
474
|
+
getIntegrityForPrecacheKey(cacheKey) {
|
|
475
|
+
return this._cacheKeysToIntegrities.get(cacheKey);
|
|
476
|
+
}
|
|
477
|
+
async matchPrecache(request) {
|
|
478
|
+
const url = request instanceof Request ? request.url : request;
|
|
479
|
+
const cacheKey = this.getPrecacheKeyForUrl(url);
|
|
480
|
+
if (cacheKey) {
|
|
481
|
+
const cache = await self.caches.open(this.precacheStrategy.cacheName);
|
|
482
|
+
return cache.match(cacheKey);
|
|
483
|
+
}
|
|
484
|
+
return undefined;
|
|
485
|
+
}
|
|
486
|
+
createHandlerBoundToUrl(url) {
|
|
487
|
+
const cacheKey = this.getPrecacheKeyForUrl(url);
|
|
488
|
+
if (!cacheKey) {
|
|
489
|
+
throw new SerwistError("non-precached-url", {
|
|
490
|
+
url
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
return (options)=>{
|
|
494
|
+
options.request = new Request(url);
|
|
495
|
+
options.params = {
|
|
496
|
+
cacheKey,
|
|
497
|
+
...options.params
|
|
498
|
+
};
|
|
499
|
+
return this.precacheStrategy.handle(options);
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
handleRequest({ request, event }) {
|
|
503
|
+
if (process.env.NODE_ENV !== "production") {
|
|
504
|
+
finalAssertExports.isInstance(request, Request, {
|
|
505
|
+
moduleName: "serwist",
|
|
506
|
+
className: "Serwist",
|
|
507
|
+
funcName: "handleRequest",
|
|
508
|
+
paramName: "options.request"
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
const url = new URL(request.url, location.href);
|
|
512
|
+
if (!url.protocol.startsWith("http")) {
|
|
513
|
+
if (process.env.NODE_ENV !== "production") {
|
|
514
|
+
logger.debug("Router only supports URLs that start with 'http'.");
|
|
515
|
+
}
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
const sameOrigin = url.origin === location.origin;
|
|
519
|
+
const { params, route } = this.findMatchingRoute({
|
|
520
|
+
event,
|
|
521
|
+
request,
|
|
522
|
+
sameOrigin,
|
|
523
|
+
url
|
|
524
|
+
});
|
|
525
|
+
let handler = route?.handler;
|
|
526
|
+
const debugMessages = [];
|
|
527
|
+
if (process.env.NODE_ENV !== "production") {
|
|
528
|
+
if (handler) {
|
|
529
|
+
debugMessages.push([
|
|
530
|
+
"Found a route to handle this request:",
|
|
531
|
+
route
|
|
532
|
+
]);
|
|
533
|
+
if (params) {
|
|
534
|
+
debugMessages.push([
|
|
535
|
+
`Passing the following params to the route's handler:`,
|
|
536
|
+
params
|
|
537
|
+
]);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
const method = request.method;
|
|
542
|
+
if (!handler && this._defaultHandlerMap.has(method)) {
|
|
543
|
+
if (process.env.NODE_ENV !== "production") {
|
|
544
|
+
debugMessages.push(`Failed to find a matching route. Falling back to the default handler for ${method}.`);
|
|
545
|
+
}
|
|
546
|
+
handler = this._defaultHandlerMap.get(method);
|
|
547
|
+
}
|
|
548
|
+
if (!handler) {
|
|
549
|
+
if (process.env.NODE_ENV !== "production") {
|
|
550
|
+
logger.debug(`No route found for: ${getFriendlyURL(url)}`);
|
|
551
|
+
}
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
554
|
+
if (process.env.NODE_ENV !== "production") {
|
|
555
|
+
logger.groupCollapsed(`Router is responding to: ${getFriendlyURL(url)}`);
|
|
556
|
+
for (const msg of debugMessages){
|
|
557
|
+
if (Array.isArray(msg)) {
|
|
558
|
+
logger.log(...msg);
|
|
559
|
+
} else {
|
|
560
|
+
logger.log(msg);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
logger.groupEnd();
|
|
564
|
+
}
|
|
565
|
+
let responsePromise;
|
|
566
|
+
try {
|
|
567
|
+
responsePromise = handler.handle({
|
|
568
|
+
url,
|
|
569
|
+
request,
|
|
570
|
+
event,
|
|
571
|
+
params
|
|
572
|
+
});
|
|
573
|
+
} catch (err) {
|
|
574
|
+
responsePromise = Promise.reject(err);
|
|
575
|
+
}
|
|
576
|
+
const catchHandler = route?.catchHandler;
|
|
577
|
+
if (responsePromise instanceof Promise && (this._catchHandler || catchHandler)) {
|
|
578
|
+
responsePromise = responsePromise.catch(async (err)=>{
|
|
579
|
+
if (catchHandler) {
|
|
580
|
+
if (process.env.NODE_ENV !== "production") {
|
|
581
|
+
logger.groupCollapsed(`Error thrown when responding to: ${getFriendlyURL(url)}. Falling back to route's Catch Handler.`);
|
|
582
|
+
logger.error("Error thrown by:", route);
|
|
583
|
+
logger.error(err);
|
|
584
|
+
logger.groupEnd();
|
|
585
|
+
}
|
|
586
|
+
try {
|
|
587
|
+
return await catchHandler.handle({
|
|
588
|
+
url,
|
|
589
|
+
request,
|
|
590
|
+
event,
|
|
591
|
+
params
|
|
592
|
+
});
|
|
593
|
+
} catch (catchErr) {
|
|
594
|
+
if (catchErr instanceof Error) {
|
|
595
|
+
err = catchErr;
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
if (this._catchHandler) {
|
|
600
|
+
if (process.env.NODE_ENV !== "production") {
|
|
601
|
+
logger.groupCollapsed(`Error thrown when responding to: ${getFriendlyURL(url)}. Falling back to global Catch Handler.`);
|
|
602
|
+
logger.error("Error thrown by:", route);
|
|
603
|
+
logger.error(err);
|
|
604
|
+
logger.groupEnd();
|
|
605
|
+
}
|
|
606
|
+
return this._catchHandler.handle({
|
|
607
|
+
url,
|
|
608
|
+
request,
|
|
609
|
+
event
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
throw err;
|
|
613
|
+
});
|
|
614
|
+
}
|
|
615
|
+
return responsePromise;
|
|
616
|
+
}
|
|
617
|
+
findMatchingRoute({ url, sameOrigin, request, event }) {
|
|
618
|
+
const routes = this._routes.get(request.method) || [];
|
|
619
|
+
for (const route of routes){
|
|
620
|
+
let params;
|
|
621
|
+
const matchResult = route.match({
|
|
622
|
+
url,
|
|
623
|
+
sameOrigin,
|
|
624
|
+
request,
|
|
625
|
+
event
|
|
626
|
+
});
|
|
627
|
+
if (matchResult) {
|
|
628
|
+
if (process.env.NODE_ENV !== "production") {
|
|
629
|
+
if (matchResult instanceof Promise) {
|
|
630
|
+
logger.warn(`While routing ${getFriendlyURL(url)}, an async matchCallback function was used. Please convert the following route to use a synchronous matchCallback function:`, route);
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
params = matchResult;
|
|
634
|
+
if (Array.isArray(params) && params.length === 0) {
|
|
635
|
+
params = undefined;
|
|
636
|
+
} else if (matchResult.constructor === Object && Object.keys(matchResult).length === 0) {
|
|
637
|
+
params = undefined;
|
|
638
|
+
} else if (typeof matchResult === "boolean") {
|
|
639
|
+
params = undefined;
|
|
640
|
+
}
|
|
641
|
+
return {
|
|
642
|
+
route,
|
|
643
|
+
params
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
return {};
|
|
648
|
+
}
|
|
649
|
+
}
|
|
8
650
|
|
|
9
651
|
const cacheNames = {
|
|
10
652
|
get googleAnalytics () {
|
|
@@ -24,4 +666,818 @@ const cacheNames = {
|
|
|
24
666
|
}
|
|
25
667
|
};
|
|
26
668
|
|
|
27
|
-
|
|
669
|
+
const BROADCAST_UPDATE_MESSAGE_TYPE = "CACHE_UPDATED";
|
|
670
|
+
const BROADCAST_UPDATE_MESSAGE_META = "serwist-broadcast-update";
|
|
671
|
+
const BROADCAST_UPDATE_DEFAULT_NOTIFY = true;
|
|
672
|
+
const BROADCAST_UPDATE_DEFAULT_HEADERS = [
|
|
673
|
+
"content-length",
|
|
674
|
+
"etag",
|
|
675
|
+
"last-modified"
|
|
676
|
+
];
|
|
677
|
+
|
|
678
|
+
const responsesAreSame = (firstResponse, secondResponse, headersToCheck)=>{
|
|
679
|
+
if (process.env.NODE_ENV !== "production") {
|
|
680
|
+
if (!(firstResponse instanceof Response && secondResponse instanceof Response)) {
|
|
681
|
+
throw new SerwistError("invalid-responses-are-same-args");
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
const atLeastOneHeaderAvailable = headersToCheck.some((header)=>{
|
|
685
|
+
return firstResponse.headers.has(header) && secondResponse.headers.has(header);
|
|
686
|
+
});
|
|
687
|
+
if (!atLeastOneHeaderAvailable) {
|
|
688
|
+
if (process.env.NODE_ENV !== "production") {
|
|
689
|
+
logger.warn("Unable to determine where the response has been updated because none of the headers that would be checked are present.");
|
|
690
|
+
logger.debug("Attempting to compare the following: ", firstResponse, secondResponse, headersToCheck);
|
|
691
|
+
}
|
|
692
|
+
return true;
|
|
693
|
+
}
|
|
694
|
+
return headersToCheck.every((header)=>{
|
|
695
|
+
const headerStateComparison = firstResponse.headers.has(header) === secondResponse.headers.has(header);
|
|
696
|
+
const headerValueComparison = firstResponse.headers.get(header) === secondResponse.headers.get(header);
|
|
697
|
+
return headerStateComparison && headerValueComparison;
|
|
698
|
+
});
|
|
699
|
+
};
|
|
700
|
+
|
|
701
|
+
const isSafari = typeof navigator !== "undefined" && /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
702
|
+
const defaultPayloadGenerator = (data)=>{
|
|
703
|
+
return {
|
|
704
|
+
cacheName: data.cacheName,
|
|
705
|
+
updatedURL: data.request.url
|
|
706
|
+
};
|
|
707
|
+
};
|
|
708
|
+
class BroadcastCacheUpdate {
|
|
709
|
+
_headersToCheck;
|
|
710
|
+
_generatePayload;
|
|
711
|
+
_notifyAllClients;
|
|
712
|
+
constructor({ generatePayload, headersToCheck, notifyAllClients } = {}){
|
|
713
|
+
this._headersToCheck = headersToCheck || BROADCAST_UPDATE_DEFAULT_HEADERS;
|
|
714
|
+
this._generatePayload = generatePayload || defaultPayloadGenerator;
|
|
715
|
+
this._notifyAllClients = notifyAllClients ?? BROADCAST_UPDATE_DEFAULT_NOTIFY;
|
|
716
|
+
}
|
|
717
|
+
async notifyIfUpdated(options) {
|
|
718
|
+
if (process.env.NODE_ENV !== "production") {
|
|
719
|
+
finalAssertExports.isType(options.cacheName, "string", {
|
|
720
|
+
moduleName: "serwist",
|
|
721
|
+
className: "BroadcastCacheUpdate",
|
|
722
|
+
funcName: "notifyIfUpdated",
|
|
723
|
+
paramName: "cacheName"
|
|
724
|
+
});
|
|
725
|
+
finalAssertExports.isInstance(options.newResponse, Response, {
|
|
726
|
+
moduleName: "serwist",
|
|
727
|
+
className: "BroadcastCacheUpdate",
|
|
728
|
+
funcName: "notifyIfUpdated",
|
|
729
|
+
paramName: "newResponse"
|
|
730
|
+
});
|
|
731
|
+
finalAssertExports.isInstance(options.request, Request, {
|
|
732
|
+
moduleName: "serwist",
|
|
733
|
+
className: "BroadcastCacheUpdate",
|
|
734
|
+
funcName: "notifyIfUpdated",
|
|
735
|
+
paramName: "request"
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
if (!options.oldResponse) {
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
if (!responsesAreSame(options.oldResponse, options.newResponse, this._headersToCheck)) {
|
|
742
|
+
if (process.env.NODE_ENV !== "production") {
|
|
743
|
+
logger.log("Newer response found (and cached) for:", options.request.url);
|
|
744
|
+
}
|
|
745
|
+
const messageData = {
|
|
746
|
+
type: BROADCAST_UPDATE_MESSAGE_TYPE,
|
|
747
|
+
meta: BROADCAST_UPDATE_MESSAGE_META,
|
|
748
|
+
payload: this._generatePayload(options)
|
|
749
|
+
};
|
|
750
|
+
if (options.request.mode === "navigate") {
|
|
751
|
+
let resultingClientId;
|
|
752
|
+
if (options.event instanceof FetchEvent) {
|
|
753
|
+
resultingClientId = options.event.resultingClientId;
|
|
754
|
+
}
|
|
755
|
+
const resultingWin = await resultingClientExists(resultingClientId);
|
|
756
|
+
if (!resultingWin || isSafari) {
|
|
757
|
+
await timeout(3500);
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
if (this._notifyAllClients) {
|
|
761
|
+
const windows = await self.clients.matchAll({
|
|
762
|
+
type: "window"
|
|
763
|
+
});
|
|
764
|
+
for (const win of windows){
|
|
765
|
+
win.postMessage(messageData);
|
|
766
|
+
}
|
|
767
|
+
} else {
|
|
768
|
+
if (options.event instanceof FetchEvent) {
|
|
769
|
+
const client = await self.clients.get(options.event.clientId);
|
|
770
|
+
client?.postMessage(messageData);
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
class BroadcastUpdatePlugin {
|
|
778
|
+
_broadcastUpdate;
|
|
779
|
+
constructor(options){
|
|
780
|
+
this._broadcastUpdate = new BroadcastCacheUpdate(options);
|
|
781
|
+
}
|
|
782
|
+
cacheDidUpdate(options) {
|
|
783
|
+
void this._broadcastUpdate.notifyIfUpdated(options);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
class CacheableResponse {
|
|
788
|
+
_statuses;
|
|
789
|
+
_headers;
|
|
790
|
+
constructor(config = {}){
|
|
791
|
+
if (process.env.NODE_ENV !== "production") {
|
|
792
|
+
if (!(config.statuses || config.headers)) {
|
|
793
|
+
throw new SerwistError("statuses-or-headers-required", {
|
|
794
|
+
moduleName: "serwist",
|
|
795
|
+
className: "CacheableResponse",
|
|
796
|
+
funcName: "constructor"
|
|
797
|
+
});
|
|
798
|
+
}
|
|
799
|
+
if (config.statuses) {
|
|
800
|
+
finalAssertExports.isArray(config.statuses, {
|
|
801
|
+
moduleName: "serwist",
|
|
802
|
+
className: "CacheableResponse",
|
|
803
|
+
funcName: "constructor",
|
|
804
|
+
paramName: "config.statuses"
|
|
805
|
+
});
|
|
806
|
+
}
|
|
807
|
+
if (config.headers) {
|
|
808
|
+
finalAssertExports.isType(config.headers, "object", {
|
|
809
|
+
moduleName: "serwist",
|
|
810
|
+
className: "CacheableResponse",
|
|
811
|
+
funcName: "constructor",
|
|
812
|
+
paramName: "config.headers"
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
this._statuses = config.statuses;
|
|
817
|
+
if (config.headers) {
|
|
818
|
+
this._headers = new Headers(config.headers);
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
isResponseCacheable(response) {
|
|
822
|
+
if (process.env.NODE_ENV !== "production") {
|
|
823
|
+
finalAssertExports.isInstance(response, Response, {
|
|
824
|
+
moduleName: "serwist",
|
|
825
|
+
className: "CacheableResponse",
|
|
826
|
+
funcName: "isResponseCacheable",
|
|
827
|
+
paramName: "response"
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
let cacheable = true;
|
|
831
|
+
if (this._statuses) {
|
|
832
|
+
cacheable = this._statuses.includes(response.status);
|
|
833
|
+
}
|
|
834
|
+
if (this._headers && cacheable) {
|
|
835
|
+
for (const [headerName, headerValue] of this._headers.entries()){
|
|
836
|
+
if (response.headers.get(headerName) !== headerValue) {
|
|
837
|
+
cacheable = false;
|
|
838
|
+
break;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
if (process.env.NODE_ENV !== "production") {
|
|
843
|
+
if (!cacheable) {
|
|
844
|
+
logger.groupCollapsed(`The request for '${getFriendlyURL(response.url)}' returned a response that does not meet the criteria for being cached.`);
|
|
845
|
+
logger.groupCollapsed("View cacheability criteria here.");
|
|
846
|
+
logger.log(`Cacheable statuses: ${JSON.stringify(this._statuses)}`);
|
|
847
|
+
logger.log(`Cacheable headers: ${JSON.stringify(this._headers, null, 2)}`);
|
|
848
|
+
logger.groupEnd();
|
|
849
|
+
const logFriendlyHeaders = {};
|
|
850
|
+
response.headers.forEach((value, key)=>{
|
|
851
|
+
logFriendlyHeaders[key] = value;
|
|
852
|
+
});
|
|
853
|
+
logger.groupCollapsed("View response status and headers here.");
|
|
854
|
+
logger.log(`Response status: ${response.status}`);
|
|
855
|
+
logger.log(`Response headers: ${JSON.stringify(logFriendlyHeaders, null, 2)}`);
|
|
856
|
+
logger.groupEnd();
|
|
857
|
+
logger.groupCollapsed("View full response details here.");
|
|
858
|
+
logger.log(response.headers);
|
|
859
|
+
logger.log(response);
|
|
860
|
+
logger.groupEnd();
|
|
861
|
+
logger.groupEnd();
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return cacheable;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
class CacheableResponsePlugin {
|
|
869
|
+
_cacheableResponse;
|
|
870
|
+
constructor(config){
|
|
871
|
+
this._cacheableResponse = new CacheableResponse(config);
|
|
872
|
+
}
|
|
873
|
+
cacheWillUpdate = async ({ response })=>{
|
|
874
|
+
if (this._cacheableResponse.isResponseCacheable(response)) {
|
|
875
|
+
return response;
|
|
876
|
+
}
|
|
877
|
+
return null;
|
|
878
|
+
};
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
const DB_NAME = "serwist-expiration";
|
|
882
|
+
const CACHE_OBJECT_STORE = "cache-entries";
|
|
883
|
+
const normalizeURL = (unNormalizedUrl)=>{
|
|
884
|
+
const url = new URL(unNormalizedUrl, location.href);
|
|
885
|
+
url.hash = "";
|
|
886
|
+
return url.href;
|
|
887
|
+
};
|
|
888
|
+
class CacheTimestampsModel {
|
|
889
|
+
_cacheName;
|
|
890
|
+
_db = null;
|
|
891
|
+
constructor(cacheName){
|
|
892
|
+
this._cacheName = cacheName;
|
|
893
|
+
}
|
|
894
|
+
_getId(url) {
|
|
895
|
+
return `${this._cacheName}|${normalizeURL(url)}`;
|
|
896
|
+
}
|
|
897
|
+
_upgradeDb(db) {
|
|
898
|
+
const objStore = db.createObjectStore(CACHE_OBJECT_STORE, {
|
|
899
|
+
keyPath: "id"
|
|
900
|
+
});
|
|
901
|
+
objStore.createIndex("cacheName", "cacheName", {
|
|
902
|
+
unique: false
|
|
903
|
+
});
|
|
904
|
+
objStore.createIndex("timestamp", "timestamp", {
|
|
905
|
+
unique: false
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
_upgradeDbAndDeleteOldDbs(db) {
|
|
909
|
+
this._upgradeDb(db);
|
|
910
|
+
if (this._cacheName) {
|
|
911
|
+
void deleteDB(this._cacheName);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
async setTimestamp(url, timestamp) {
|
|
915
|
+
url = normalizeURL(url);
|
|
916
|
+
const entry = {
|
|
917
|
+
id: this._getId(url),
|
|
918
|
+
cacheName: this._cacheName,
|
|
919
|
+
url,
|
|
920
|
+
timestamp
|
|
921
|
+
};
|
|
922
|
+
const db = await this.getDb();
|
|
923
|
+
const tx = db.transaction(CACHE_OBJECT_STORE, "readwrite", {
|
|
924
|
+
durability: "relaxed"
|
|
925
|
+
});
|
|
926
|
+
await tx.store.put(entry);
|
|
927
|
+
await tx.done;
|
|
928
|
+
}
|
|
929
|
+
async getTimestamp(url) {
|
|
930
|
+
const db = await this.getDb();
|
|
931
|
+
const entry = await db.get(CACHE_OBJECT_STORE, this._getId(url));
|
|
932
|
+
return entry?.timestamp;
|
|
933
|
+
}
|
|
934
|
+
async expireEntries(minTimestamp, maxCount) {
|
|
935
|
+
const db = await this.getDb();
|
|
936
|
+
let cursor = await db.transaction(CACHE_OBJECT_STORE, "readwrite").store.index("timestamp").openCursor(null, "prev");
|
|
937
|
+
const urlsDeleted = [];
|
|
938
|
+
let entriesNotDeletedCount = 0;
|
|
939
|
+
while(cursor){
|
|
940
|
+
const result = cursor.value;
|
|
941
|
+
if (result.cacheName === this._cacheName) {
|
|
942
|
+
if (minTimestamp && result.timestamp < minTimestamp || maxCount && entriesNotDeletedCount >= maxCount) {
|
|
943
|
+
cursor.delete();
|
|
944
|
+
urlsDeleted.push(result.url);
|
|
945
|
+
} else {
|
|
946
|
+
entriesNotDeletedCount++;
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
cursor = await cursor.continue();
|
|
950
|
+
}
|
|
951
|
+
return urlsDeleted;
|
|
952
|
+
}
|
|
953
|
+
async getDb() {
|
|
954
|
+
if (!this._db) {
|
|
955
|
+
this._db = await openDB(DB_NAME, 1, {
|
|
956
|
+
upgrade: this._upgradeDbAndDeleteOldDbs.bind(this)
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
return this._db;
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
class CacheExpiration {
|
|
964
|
+
_isRunning = false;
|
|
965
|
+
_rerunRequested = false;
|
|
966
|
+
_maxEntries;
|
|
967
|
+
_maxAgeSeconds;
|
|
968
|
+
_matchOptions;
|
|
969
|
+
_cacheName;
|
|
970
|
+
_timestampModel;
|
|
971
|
+
constructor(cacheName, config = {}){
|
|
972
|
+
if (process.env.NODE_ENV !== "production") {
|
|
973
|
+
finalAssertExports.isType(cacheName, "string", {
|
|
974
|
+
moduleName: "serwist",
|
|
975
|
+
className: "CacheExpiration",
|
|
976
|
+
funcName: "constructor",
|
|
977
|
+
paramName: "cacheName"
|
|
978
|
+
});
|
|
979
|
+
if (!(config.maxEntries || config.maxAgeSeconds)) {
|
|
980
|
+
throw new SerwistError("max-entries-or-age-required", {
|
|
981
|
+
moduleName: "serwist",
|
|
982
|
+
className: "CacheExpiration",
|
|
983
|
+
funcName: "constructor"
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
if (config.maxEntries) {
|
|
987
|
+
finalAssertExports.isType(config.maxEntries, "number", {
|
|
988
|
+
moduleName: "serwist",
|
|
989
|
+
className: "CacheExpiration",
|
|
990
|
+
funcName: "constructor",
|
|
991
|
+
paramName: "config.maxEntries"
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
if (config.maxAgeSeconds) {
|
|
995
|
+
finalAssertExports.isType(config.maxAgeSeconds, "number", {
|
|
996
|
+
moduleName: "serwist",
|
|
997
|
+
className: "CacheExpiration",
|
|
998
|
+
funcName: "constructor",
|
|
999
|
+
paramName: "config.maxAgeSeconds"
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
this._maxEntries = config.maxEntries;
|
|
1004
|
+
this._maxAgeSeconds = config.maxAgeSeconds;
|
|
1005
|
+
this._matchOptions = config.matchOptions;
|
|
1006
|
+
this._cacheName = cacheName;
|
|
1007
|
+
this._timestampModel = new CacheTimestampsModel(cacheName);
|
|
1008
|
+
}
|
|
1009
|
+
async expireEntries() {
|
|
1010
|
+
if (this._isRunning) {
|
|
1011
|
+
this._rerunRequested = true;
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
this._isRunning = true;
|
|
1015
|
+
const minTimestamp = this._maxAgeSeconds ? Date.now() - this._maxAgeSeconds * 1000 : 0;
|
|
1016
|
+
const urlsExpired = await this._timestampModel.expireEntries(minTimestamp, this._maxEntries);
|
|
1017
|
+
const cache = await self.caches.open(this._cacheName);
|
|
1018
|
+
for (const url of urlsExpired){
|
|
1019
|
+
await cache.delete(url, this._matchOptions);
|
|
1020
|
+
}
|
|
1021
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1022
|
+
if (urlsExpired.length > 0) {
|
|
1023
|
+
logger.groupCollapsed(`Expired ${urlsExpired.length} ` + `${urlsExpired.length === 1 ? "entry" : "entries"} and removed ` + `${urlsExpired.length === 1 ? "it" : "them"} from the ` + `'${this._cacheName}' cache.`);
|
|
1024
|
+
logger.log(`Expired the following ${urlsExpired.length === 1 ? "URL" : "URLs"}:`);
|
|
1025
|
+
for (const url of urlsExpired){
|
|
1026
|
+
logger.log(` ${url}`);
|
|
1027
|
+
}
|
|
1028
|
+
logger.groupEnd();
|
|
1029
|
+
} else {
|
|
1030
|
+
logger.debug("Cache expiration ran and found no entries to remove.");
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
this._isRunning = false;
|
|
1034
|
+
if (this._rerunRequested) {
|
|
1035
|
+
this._rerunRequested = false;
|
|
1036
|
+
void this.expireEntries();
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
async updateTimestamp(url) {
|
|
1040
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1041
|
+
finalAssertExports.isType(url, "string", {
|
|
1042
|
+
moduleName: "serwist",
|
|
1043
|
+
className: "CacheExpiration",
|
|
1044
|
+
funcName: "updateTimestamp",
|
|
1045
|
+
paramName: "url"
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
await this._timestampModel.setTimestamp(url, Date.now());
|
|
1049
|
+
}
|
|
1050
|
+
async isURLExpired(url) {
|
|
1051
|
+
if (!this._maxAgeSeconds) {
|
|
1052
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1053
|
+
throw new SerwistError("expired-test-without-max-age", {
|
|
1054
|
+
methodName: "isURLExpired",
|
|
1055
|
+
paramName: "maxAgeSeconds"
|
|
1056
|
+
});
|
|
1057
|
+
}
|
|
1058
|
+
return false;
|
|
1059
|
+
}
|
|
1060
|
+
const timestamp = await this._timestampModel.getTimestamp(url);
|
|
1061
|
+
const expireOlderThan = Date.now() - this._maxAgeSeconds * 1000;
|
|
1062
|
+
return timestamp !== undefined ? timestamp < expireOlderThan : true;
|
|
1063
|
+
}
|
|
1064
|
+
async delete() {
|
|
1065
|
+
this._rerunRequested = false;
|
|
1066
|
+
await this._timestampModel.expireEntries(Number.POSITIVE_INFINITY);
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
const registerQuotaErrorCallback = (callback)=>{
|
|
1071
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1072
|
+
finalAssertExports.isType(callback, "function", {
|
|
1073
|
+
moduleName: "@serwist/core",
|
|
1074
|
+
funcName: "register",
|
|
1075
|
+
paramName: "callback"
|
|
1076
|
+
});
|
|
1077
|
+
}
|
|
1078
|
+
quotaErrorCallbacks.add(callback);
|
|
1079
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1080
|
+
logger.log("Registered a callback to respond to quota errors.", callback);
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
class ExpirationPlugin {
|
|
1085
|
+
_config;
|
|
1086
|
+
_cacheExpirations;
|
|
1087
|
+
constructor(config = {}){
|
|
1088
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1089
|
+
if (!(config.maxEntries || config.maxAgeSeconds)) {
|
|
1090
|
+
throw new SerwistError("max-entries-or-age-required", {
|
|
1091
|
+
moduleName: "serwist",
|
|
1092
|
+
className: "ExpirationPlugin",
|
|
1093
|
+
funcName: "constructor"
|
|
1094
|
+
});
|
|
1095
|
+
}
|
|
1096
|
+
if (config.maxEntries) {
|
|
1097
|
+
finalAssertExports.isType(config.maxEntries, "number", {
|
|
1098
|
+
moduleName: "serwist",
|
|
1099
|
+
className: "ExpirationPlugin",
|
|
1100
|
+
funcName: "constructor",
|
|
1101
|
+
paramName: "config.maxEntries"
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
if (config.maxAgeSeconds) {
|
|
1105
|
+
finalAssertExports.isType(config.maxAgeSeconds, "number", {
|
|
1106
|
+
moduleName: "serwist",
|
|
1107
|
+
className: "ExpirationPlugin",
|
|
1108
|
+
funcName: "constructor",
|
|
1109
|
+
paramName: "config.maxAgeSeconds"
|
|
1110
|
+
});
|
|
1111
|
+
}
|
|
1112
|
+
if (config.maxAgeFrom) {
|
|
1113
|
+
finalAssertExports.isType(config.maxAgeFrom, "string", {
|
|
1114
|
+
moduleName: "serwist",
|
|
1115
|
+
className: "ExpirationPlugin",
|
|
1116
|
+
funcName: "constructor",
|
|
1117
|
+
paramName: "config.maxAgeFrom"
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
this._config = config;
|
|
1122
|
+
this._cacheExpirations = new Map();
|
|
1123
|
+
if (!this._config.maxAgeFrom) {
|
|
1124
|
+
this._config.maxAgeFrom = "last-fetched";
|
|
1125
|
+
}
|
|
1126
|
+
if (this._config.purgeOnQuotaError) {
|
|
1127
|
+
registerQuotaErrorCallback(()=>this.deleteCacheAndMetadata());
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
_getCacheExpiration(cacheName) {
|
|
1131
|
+
if (cacheName === cacheNames$1.getRuntimeName()) {
|
|
1132
|
+
throw new SerwistError("expire-custom-caches-only");
|
|
1133
|
+
}
|
|
1134
|
+
let cacheExpiration = this._cacheExpirations.get(cacheName);
|
|
1135
|
+
if (!cacheExpiration) {
|
|
1136
|
+
cacheExpiration = new CacheExpiration(cacheName, this._config);
|
|
1137
|
+
this._cacheExpirations.set(cacheName, cacheExpiration);
|
|
1138
|
+
}
|
|
1139
|
+
return cacheExpiration;
|
|
1140
|
+
}
|
|
1141
|
+
cachedResponseWillBeUsed({ event, cacheName, request, cachedResponse }) {
|
|
1142
|
+
if (!cachedResponse) {
|
|
1143
|
+
return null;
|
|
1144
|
+
}
|
|
1145
|
+
const isFresh = this._isResponseDateFresh(cachedResponse);
|
|
1146
|
+
const cacheExpiration = this._getCacheExpiration(cacheName);
|
|
1147
|
+
const isMaxAgeFromLastUsed = this._config.maxAgeFrom === "last-used";
|
|
1148
|
+
const done = (async ()=>{
|
|
1149
|
+
if (isMaxAgeFromLastUsed) {
|
|
1150
|
+
await cacheExpiration.updateTimestamp(request.url);
|
|
1151
|
+
}
|
|
1152
|
+
await cacheExpiration.expireEntries();
|
|
1153
|
+
})();
|
|
1154
|
+
try {
|
|
1155
|
+
event.waitUntil(done);
|
|
1156
|
+
} catch (error) {
|
|
1157
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1158
|
+
if (event instanceof FetchEvent) {
|
|
1159
|
+
logger.warn(`Unable to ensure service worker stays alive when updating cache entry for '${getFriendlyURL(event.request.url)}'.`);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
return isFresh ? cachedResponse : null;
|
|
1164
|
+
}
|
|
1165
|
+
_isResponseDateFresh(cachedResponse) {
|
|
1166
|
+
const isMaxAgeFromLastUsed = this._config.maxAgeFrom === "last-used";
|
|
1167
|
+
if (isMaxAgeFromLastUsed) {
|
|
1168
|
+
return true;
|
|
1169
|
+
}
|
|
1170
|
+
const now = Date.now();
|
|
1171
|
+
if (!this._config.maxAgeSeconds) {
|
|
1172
|
+
return true;
|
|
1173
|
+
}
|
|
1174
|
+
const dateHeaderTimestamp = this._getDateHeaderTimestamp(cachedResponse);
|
|
1175
|
+
if (dateHeaderTimestamp === null) {
|
|
1176
|
+
return true;
|
|
1177
|
+
}
|
|
1178
|
+
return dateHeaderTimestamp >= now - this._config.maxAgeSeconds * 1000;
|
|
1179
|
+
}
|
|
1180
|
+
_getDateHeaderTimestamp(cachedResponse) {
|
|
1181
|
+
if (!cachedResponse.headers.has("date")) {
|
|
1182
|
+
return null;
|
|
1183
|
+
}
|
|
1184
|
+
const dateHeader = cachedResponse.headers.get("date");
|
|
1185
|
+
const parsedDate = new Date(dateHeader);
|
|
1186
|
+
const headerTime = parsedDate.getTime();
|
|
1187
|
+
if (Number.isNaN(headerTime)) {
|
|
1188
|
+
return null;
|
|
1189
|
+
}
|
|
1190
|
+
return headerTime;
|
|
1191
|
+
}
|
|
1192
|
+
async cacheDidUpdate({ cacheName, request }) {
|
|
1193
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1194
|
+
finalAssertExports.isType(cacheName, "string", {
|
|
1195
|
+
moduleName: "serwist",
|
|
1196
|
+
className: "Plugin",
|
|
1197
|
+
funcName: "cacheDidUpdate",
|
|
1198
|
+
paramName: "cacheName"
|
|
1199
|
+
});
|
|
1200
|
+
finalAssertExports.isInstance(request, Request, {
|
|
1201
|
+
moduleName: "serwist",
|
|
1202
|
+
className: "Plugin",
|
|
1203
|
+
funcName: "cacheDidUpdate",
|
|
1204
|
+
paramName: "request"
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
1207
|
+
const cacheExpiration = this._getCacheExpiration(cacheName);
|
|
1208
|
+
await cacheExpiration.updateTimestamp(request.url);
|
|
1209
|
+
await cacheExpiration.expireEntries();
|
|
1210
|
+
}
|
|
1211
|
+
async deleteCacheAndMetadata() {
|
|
1212
|
+
for (const [cacheName, cacheExpiration] of this._cacheExpirations){
|
|
1213
|
+
await self.caches.delete(cacheName);
|
|
1214
|
+
await cacheExpiration.delete();
|
|
1215
|
+
}
|
|
1216
|
+
this._cacheExpirations = new Map();
|
|
1217
|
+
}
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
const calculateEffectiveBoundaries = (blob, start, end)=>{
|
|
1221
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1222
|
+
finalAssertExports.isInstance(blob, Blob, {
|
|
1223
|
+
moduleName: "@serwist/range-requests",
|
|
1224
|
+
funcName: "calculateEffectiveBoundaries",
|
|
1225
|
+
paramName: "blob"
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
const blobSize = blob.size;
|
|
1229
|
+
if (end && end > blobSize || start && start < 0) {
|
|
1230
|
+
throw new SerwistError("range-not-satisfiable", {
|
|
1231
|
+
size: blobSize,
|
|
1232
|
+
end,
|
|
1233
|
+
start
|
|
1234
|
+
});
|
|
1235
|
+
}
|
|
1236
|
+
let effectiveStart;
|
|
1237
|
+
let effectiveEnd;
|
|
1238
|
+
if (start !== undefined && end !== undefined) {
|
|
1239
|
+
effectiveStart = start;
|
|
1240
|
+
effectiveEnd = end + 1;
|
|
1241
|
+
} else if (start !== undefined && end === undefined) {
|
|
1242
|
+
effectiveStart = start;
|
|
1243
|
+
effectiveEnd = blobSize;
|
|
1244
|
+
} else if (end !== undefined && start === undefined) {
|
|
1245
|
+
effectiveStart = blobSize - end;
|
|
1246
|
+
effectiveEnd = blobSize;
|
|
1247
|
+
}
|
|
1248
|
+
return {
|
|
1249
|
+
start: effectiveStart,
|
|
1250
|
+
end: effectiveEnd
|
|
1251
|
+
};
|
|
1252
|
+
};
|
|
1253
|
+
|
|
1254
|
+
const parseRangeHeader = (rangeHeader)=>{
|
|
1255
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1256
|
+
finalAssertExports.isType(rangeHeader, "string", {
|
|
1257
|
+
moduleName: "@serwist/range-requests",
|
|
1258
|
+
funcName: "parseRangeHeader",
|
|
1259
|
+
paramName: "rangeHeader"
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
const normalizedRangeHeader = rangeHeader.trim().toLowerCase();
|
|
1263
|
+
if (!normalizedRangeHeader.startsWith("bytes=")) {
|
|
1264
|
+
throw new SerwistError("unit-must-be-bytes", {
|
|
1265
|
+
normalizedRangeHeader
|
|
1266
|
+
});
|
|
1267
|
+
}
|
|
1268
|
+
if (normalizedRangeHeader.includes(",")) {
|
|
1269
|
+
throw new SerwistError("single-range-only", {
|
|
1270
|
+
normalizedRangeHeader
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
const rangeParts = /(\d*)-(\d*)/.exec(normalizedRangeHeader);
|
|
1274
|
+
if (!rangeParts || !(rangeParts[1] || rangeParts[2])) {
|
|
1275
|
+
throw new SerwistError("invalid-range-values", {
|
|
1276
|
+
normalizedRangeHeader
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
return {
|
|
1280
|
+
start: rangeParts[1] === "" ? undefined : Number(rangeParts[1]),
|
|
1281
|
+
end: rangeParts[2] === "" ? undefined : Number(rangeParts[2])
|
|
1282
|
+
};
|
|
1283
|
+
};
|
|
1284
|
+
|
|
1285
|
+
const createPartialResponse = async (request, originalResponse)=>{
|
|
1286
|
+
try {
|
|
1287
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1288
|
+
finalAssertExports.isInstance(request, Request, {
|
|
1289
|
+
moduleName: "@serwist/range-requests",
|
|
1290
|
+
funcName: "createPartialResponse",
|
|
1291
|
+
paramName: "request"
|
|
1292
|
+
});
|
|
1293
|
+
finalAssertExports.isInstance(originalResponse, Response, {
|
|
1294
|
+
moduleName: "@serwist/range-requests",
|
|
1295
|
+
funcName: "createPartialResponse",
|
|
1296
|
+
paramName: "originalResponse"
|
|
1297
|
+
});
|
|
1298
|
+
}
|
|
1299
|
+
if (originalResponse.status === 206) {
|
|
1300
|
+
return originalResponse;
|
|
1301
|
+
}
|
|
1302
|
+
const rangeHeader = request.headers.get("range");
|
|
1303
|
+
if (!rangeHeader) {
|
|
1304
|
+
throw new SerwistError("no-range-header");
|
|
1305
|
+
}
|
|
1306
|
+
const boundaries = parseRangeHeader(rangeHeader);
|
|
1307
|
+
const originalBlob = await originalResponse.blob();
|
|
1308
|
+
const effectiveBoundaries = calculateEffectiveBoundaries(originalBlob, boundaries.start, boundaries.end);
|
|
1309
|
+
const slicedBlob = originalBlob.slice(effectiveBoundaries.start, effectiveBoundaries.end);
|
|
1310
|
+
const slicedBlobSize = slicedBlob.size;
|
|
1311
|
+
const slicedResponse = new Response(slicedBlob, {
|
|
1312
|
+
status: 206,
|
|
1313
|
+
statusText: "Partial Content",
|
|
1314
|
+
headers: originalResponse.headers
|
|
1315
|
+
});
|
|
1316
|
+
slicedResponse.headers.set("Content-Length", String(slicedBlobSize));
|
|
1317
|
+
slicedResponse.headers.set("Content-Range", `bytes ${effectiveBoundaries.start}-${effectiveBoundaries.end - 1}/` + `${originalBlob.size}`);
|
|
1318
|
+
return slicedResponse;
|
|
1319
|
+
} catch (error) {
|
|
1320
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1321
|
+
logger.warn("Unable to construct a partial response; returning a " + "416 Range Not Satisfiable response instead.");
|
|
1322
|
+
logger.groupCollapsed("View details here.");
|
|
1323
|
+
logger.log(error);
|
|
1324
|
+
logger.log(request);
|
|
1325
|
+
logger.log(originalResponse);
|
|
1326
|
+
logger.groupEnd();
|
|
1327
|
+
}
|
|
1328
|
+
return new Response("", {
|
|
1329
|
+
status: 416,
|
|
1330
|
+
statusText: "Range Not Satisfiable"
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
};
|
|
1334
|
+
|
|
1335
|
+
class RangeRequestsPlugin {
|
|
1336
|
+
cachedResponseWillBeUsed = async ({ request, cachedResponse })=>{
|
|
1337
|
+
if (cachedResponse && request.headers.has("range")) {
|
|
1338
|
+
return await createPartialResponse(request, cachedResponse);
|
|
1339
|
+
}
|
|
1340
|
+
return cachedResponse;
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
class CacheFirst extends Strategy {
|
|
1345
|
+
async _handle(request, handler) {
|
|
1346
|
+
const logs = [];
|
|
1347
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1348
|
+
finalAssertExports.isInstance(request, Request, {
|
|
1349
|
+
moduleName: "serwist",
|
|
1350
|
+
className: this.constructor.name,
|
|
1351
|
+
funcName: "makeRequest",
|
|
1352
|
+
paramName: "request"
|
|
1353
|
+
});
|
|
1354
|
+
}
|
|
1355
|
+
let response = await handler.cacheMatch(request);
|
|
1356
|
+
let error = undefined;
|
|
1357
|
+
if (!response) {
|
|
1358
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1359
|
+
logs.push(`No response found in the '${this.cacheName}' cache. Will respond with a network request.`);
|
|
1360
|
+
}
|
|
1361
|
+
try {
|
|
1362
|
+
response = await handler.fetchAndCachePut(request);
|
|
1363
|
+
} catch (err) {
|
|
1364
|
+
if (err instanceof Error) {
|
|
1365
|
+
error = err;
|
|
1366
|
+
}
|
|
1367
|
+
}
|
|
1368
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1369
|
+
if (response) {
|
|
1370
|
+
logs.push("Got response from network.");
|
|
1371
|
+
} else {
|
|
1372
|
+
logs.push("Unable to get a response from the network.");
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
} else {
|
|
1376
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1377
|
+
logs.push(`Found a cached response in the '${this.cacheName}' cache.`);
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1381
|
+
logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
|
|
1382
|
+
for (const log of logs){
|
|
1383
|
+
logger.log(log);
|
|
1384
|
+
}
|
|
1385
|
+
messages.printFinalResponse(response);
|
|
1386
|
+
logger.groupEnd();
|
|
1387
|
+
}
|
|
1388
|
+
if (!response) {
|
|
1389
|
+
throw new SerwistError("no-response", {
|
|
1390
|
+
url: request.url,
|
|
1391
|
+
error
|
|
1392
|
+
});
|
|
1393
|
+
}
|
|
1394
|
+
return response;
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
|
|
1398
|
+
class CacheOnly extends Strategy {
|
|
1399
|
+
async _handle(request, handler) {
|
|
1400
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1401
|
+
finalAssertExports.isInstance(request, Request, {
|
|
1402
|
+
moduleName: "serwist",
|
|
1403
|
+
className: this.constructor.name,
|
|
1404
|
+
funcName: "makeRequest",
|
|
1405
|
+
paramName: "request"
|
|
1406
|
+
});
|
|
1407
|
+
}
|
|
1408
|
+
const response = await handler.cacheMatch(request);
|
|
1409
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1410
|
+
logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
|
|
1411
|
+
if (response) {
|
|
1412
|
+
logger.log(`Found a cached response in the '${this.cacheName}' cache.`);
|
|
1413
|
+
messages.printFinalResponse(response);
|
|
1414
|
+
} else {
|
|
1415
|
+
logger.log(`No response found in the '${this.cacheName}' cache.`);
|
|
1416
|
+
}
|
|
1417
|
+
logger.groupEnd();
|
|
1418
|
+
}
|
|
1419
|
+
if (!response) {
|
|
1420
|
+
throw new SerwistError("no-response", {
|
|
1421
|
+
url: request.url
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
return response;
|
|
1425
|
+
}
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
class StaleWhileRevalidate extends Strategy {
|
|
1429
|
+
constructor(options = {}){
|
|
1430
|
+
super(options);
|
|
1431
|
+
if (!this.plugins.some((p)=>"cacheWillUpdate" in p)) {
|
|
1432
|
+
this.plugins.unshift(cacheOkAndOpaquePlugin);
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
async _handle(request, handler) {
|
|
1436
|
+
const logs = [];
|
|
1437
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1438
|
+
finalAssertExports.isInstance(request, Request, {
|
|
1439
|
+
moduleName: "serwist",
|
|
1440
|
+
className: this.constructor.name,
|
|
1441
|
+
funcName: "handle",
|
|
1442
|
+
paramName: "request"
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
const fetchAndCachePromise = handler.fetchAndCachePut(request).catch(()=>{});
|
|
1446
|
+
void handler.waitUntil(fetchAndCachePromise);
|
|
1447
|
+
let response = await handler.cacheMatch(request);
|
|
1448
|
+
let error = undefined;
|
|
1449
|
+
if (response) {
|
|
1450
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1451
|
+
logs.push(`Found a cached response in the '${this.cacheName}' cache. Will update with the network response in the background.`);
|
|
1452
|
+
}
|
|
1453
|
+
} else {
|
|
1454
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1455
|
+
logs.push(`No response found in the '${this.cacheName}' cache. Will wait for the network response.`);
|
|
1456
|
+
}
|
|
1457
|
+
try {
|
|
1458
|
+
response = await fetchAndCachePromise;
|
|
1459
|
+
} catch (err) {
|
|
1460
|
+
if (err instanceof Error) {
|
|
1461
|
+
error = err;
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1466
|
+
logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
|
|
1467
|
+
for (const log of logs){
|
|
1468
|
+
logger.log(log);
|
|
1469
|
+
}
|
|
1470
|
+
messages.printFinalResponse(response);
|
|
1471
|
+
logger.groupEnd();
|
|
1472
|
+
}
|
|
1473
|
+
if (!response) {
|
|
1474
|
+
throw new SerwistError("no-response", {
|
|
1475
|
+
url: request.url,
|
|
1476
|
+
error
|
|
1477
|
+
});
|
|
1478
|
+
}
|
|
1479
|
+
return response;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
export { BROADCAST_UPDATE_DEFAULT_HEADERS, BackgroundSyncPlugin, BroadcastCacheUpdate, BroadcastUpdatePlugin, CacheExpiration, CacheFirst, CacheOnly, CacheableResponse, CacheableResponsePlugin, ExpirationPlugin, NavigationRoute, NetworkFirst, NetworkOnly, PrecacheFallbackPlugin, PrecacheRoute, PrecacheStrategy, RangeRequestsPlugin, Route, Serwist, StaleWhileRevalidate, Strategy, cacheNames, createPartialResponse, disableDevLogs, enableNavigationPreload, initializeGoogleAnalytics, registerQuotaErrorCallback, responsesAreSame, setCacheNameDetails };
|