serwist 10.0.0-preview.1 → 10.0.0-preview.11
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/chunks/{resultingClientExists.js → waitUntil.js} +144 -144
- package/dist/index.d.ts +22 -50
- 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 +3 -3
- package/dist/index.js +1925 -1803
- package/dist/lib/{backgroundSync → background-sync}/BackgroundSyncPlugin.d.ts +1 -1
- package/dist/lib/background-sync/BackgroundSyncPlugin.d.ts.map +1 -0
- package/dist/lib/background-sync/BackgroundSyncQueue.d.ts.map +1 -0
- package/dist/lib/background-sync/BackgroundSyncQueueDb.d.ts.map +1 -0
- package/dist/lib/background-sync/BackgroundSyncQueueStore.d.ts.map +1 -0
- package/dist/lib/{backgroundSync → background-sync}/StorableRequest.d.ts +1 -1
- package/dist/lib/background-sync/StorableRequest.d.ts.map +1 -0
- package/dist/lib/background-sync/index.d.ts +6 -0
- package/dist/lib/background-sync/index.d.ts.map +1 -0
- package/dist/lib/{broadcastUpdate → broadcast-update}/BroadcastCacheUpdate.d.ts +1 -1
- package/dist/lib/broadcast-update/BroadcastCacheUpdate.d.ts.map +1 -0
- package/dist/lib/{broadcastUpdate → broadcast-update}/BroadcastUpdatePlugin.d.ts +1 -1
- package/dist/lib/broadcast-update/BroadcastUpdatePlugin.d.ts.map +1 -0
- package/dist/lib/broadcast-update/constants.d.ts.map +1 -0
- package/dist/lib/broadcast-update/index.d.ts +6 -0
- package/dist/lib/broadcast-update/index.d.ts.map +1 -0
- package/dist/lib/broadcast-update/responsesAreSame.d.ts.map +1 -0
- package/dist/lib/{broadcastUpdate → broadcast-update}/types.d.ts +1 -1
- package/dist/lib/broadcast-update/types.d.ts.map +1 -0
- package/dist/{cacheNames.d.ts → lib/cache-name.d.ts} +1 -1
- package/dist/lib/cache-name.d.ts.map +1 -0
- package/dist/lib/cacheable-response/CacheableResponse.d.ts.map +1 -0
- package/dist/lib/{cacheableResponse → cacheable-response}/CacheableResponsePlugin.d.ts +1 -1
- package/dist/lib/cacheable-response/CacheableResponsePlugin.d.ts.map +1 -0
- package/dist/lib/cacheable-response/index.d.ts +4 -0
- package/dist/lib/cacheable-response/index.d.ts.map +1 -0
- package/dist/{constants.d.ts → lib/constants.d.ts} +1 -1
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/core.d.ts +62 -0
- package/dist/lib/core.d.ts.map +1 -0
- package/dist/lib/expiration/ExpirationPlugin.d.ts +1 -1
- 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/extension.d.ts +30 -0
- package/dist/lib/extension.d.ts.map +1 -0
- package/dist/lib/extensions/google-analytics/constants.d.ts.map +1 -0
- package/dist/lib/extensions/google-analytics/extension.d.ts +79 -0
- package/dist/lib/extensions/google-analytics/extension.d.ts.map +1 -0
- package/dist/lib/extensions/google-analytics/index.d.ts +2 -0
- package/dist/lib/extensions/google-analytics/index.d.ts.map +1 -0
- package/dist/lib/extensions/google-analytics/initialize.d.ts +12 -0
- package/dist/lib/extensions/google-analytics/initialize.d.ts.map +1 -0
- package/dist/lib/extensions/index.d.ts +14 -0
- package/dist/lib/extensions/index.d.ts.map +1 -0
- package/dist/{controllers/PrecacheController/PrecacheController.d.ts → lib/extensions/precache/extension.d.ts} +19 -38
- package/dist/lib/extensions/precache/extension.d.ts.map +1 -0
- package/dist/{controllers/PrecacheController/parsePrecacheOptions.d.ts → lib/extensions/precache/options.d.ts} +6 -5
- package/dist/lib/extensions/precache/options.d.ts.map +1 -0
- package/dist/{controllers/PrecacheController/PrecacheCacheKeyPlugin.d.ts → lib/extensions/precache/plugin-cache-key.d.ts} +4 -4
- package/dist/lib/extensions/precache/plugin-cache-key.d.ts.map +1 -0
- package/dist/lib/{precaching/PrecacheFallbackPlugin.d.ts → extensions/precache/plugin-fallback.d.ts} +11 -11
- package/dist/lib/extensions/precache/plugin-fallback.d.ts.map +1 -0
- package/dist/{controllers/PrecacheController/PrecacheInstallReportPlugin.d.ts → lib/extensions/precache/plugin-install-report.d.ts} +2 -2
- package/dist/lib/extensions/precache/plugin-install-report.d.ts.map +1 -0
- package/dist/lib/extensions/precache/route.d.ts +42 -0
- package/dist/lib/extensions/precache/route.d.ts.map +1 -0
- package/dist/{controllers/PrecacheController/PrecacheStrategy.d.ts → lib/extensions/precache/strategy.d.ts} +5 -5
- package/dist/lib/extensions/precache/strategy.d.ts.map +1 -0
- package/dist/{controllers/RuntimeCacheController.d.ts → lib/extensions/runtime-cache.d.ts} +16 -10
- package/dist/lib/extensions/runtime-cache.d.ts.map +1 -0
- package/dist/lib/functions/handlers.d.ts +60 -0
- package/dist/lib/functions/handlers.d.ts.map +1 -0
- package/dist/lib/functions/router.d.ts +60 -0
- package/dist/lib/functions/router.d.ts.map +1 -0
- package/dist/{navigationPreload.d.ts → lib/navigation-preload.d.ts} +1 -1
- package/dist/lib/navigation-preload.d.ts.map +1 -0
- package/dist/lib/{rangeRequests → range-requests}/RangeRequestsPlugin.d.ts +1 -1
- package/dist/lib/range-requests/RangeRequestsPlugin.d.ts.map +1 -0
- package/dist/lib/range-requests/createPartialResponse.d.ts.map +1 -0
- package/dist/lib/range-requests/index.d.ts +3 -0
- package/dist/lib/range-requests/index.d.ts.map +1 -0
- package/dist/lib/range-requests/utils/calculateEffectiveBoundaries.d.ts.map +1 -0
- package/dist/lib/range-requests/utils/parseRangeHeader.d.ts.map +1 -0
- package/dist/lib/route.d.ts +106 -0
- package/dist/lib/route.d.ts.map +1 -0
- package/dist/{Serwist.d.ts → lib/serwist.d.ts} +28 -41
- package/dist/lib/serwist.d.ts.map +1 -0
- package/dist/lib/strategies/NetworkFirst.d.ts.map +1 -1
- package/dist/lib/strategies/StaleWhileRevalidate.d.ts.map +1 -1
- package/dist/lib/strategies/Strategy.d.ts +1 -1
- package/dist/lib/strategies/Strategy.d.ts.map +1 -1
- package/dist/lib/strategies/StrategyHandler.d.ts +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/lib/strategies/plugins/cacheOkAndOpaquePlugin.d.ts +1 -1
- package/dist/lib/strategies/plugins/cacheOkAndOpaquePlugin.d.ts.map +1 -1
- package/dist/{types.d.ts → lib/types.d.ts} +15 -24
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/{copyResponse.d.ts → lib/utils.d.ts} +22 -1
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/models/messages/messageGenerator.d.ts +1 -1
- package/dist/models/messages/messageGenerator.d.ts.map +1 -1
- package/dist/utils/SerwistError.d.ts +2 -2
- package/dist/utils/SerwistError.d.ts.map +1 -1
- package/dist/utils/assert.d.ts +1 -1
- package/dist/utils/assert.d.ts.map +1 -1
- package/dist/utils/cacheNames.d.ts.map +1 -1
- package/dist/utils/cleanupOutdatedCaches.d.ts.map +1 -1
- package/dist/utils/createCacheKey.d.ts +1 -1
- package/dist/utils/createCacheKey.d.ts.map +1 -1
- package/dist/utils/deleteOutdatedCaches.d.ts.map +1 -1
- package/dist/utils/generateURLVariations.d.ts +1 -1
- package/dist/utils/generateURLVariations.d.ts.map +1 -1
- package/dist/utils/getFriendlyURL.d.ts.map +1 -1
- package/dist/utils/logger.d.ts +1 -1
- package/dist/utils/logger.d.ts.map +1 -1
- package/dist/utils/normalizeHandler.d.ts +1 -1
- package/dist/utils/normalizeHandler.d.ts.map +1 -1
- package/dist/utils/parseRoute.d.ts +3 -3
- package/dist/utils/parseRoute.d.ts.map +1 -1
- package/dist/utils/pluginUtils.d.ts +1 -1
- package/dist/utils/pluginUtils.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 +30 -6
- package/src/index.internal.ts +16 -16
- package/src/index.ts +100 -110
- package/src/lib/{backgroundSync → background-sync}/BackgroundSyncPlugin.ts +1 -1
- package/src/lib/{backgroundSync → background-sync}/BackgroundSyncQueue.ts +7 -7
- package/src/lib/{backgroundSync → background-sync}/BackgroundSyncQueueDb.ts +1 -1
- package/src/lib/{backgroundSync → background-sync}/BackgroundSyncQueueStore.ts +1 -3
- package/src/lib/{backgroundSync → background-sync}/StorableRequest.ts +2 -2
- package/src/lib/background-sync/index.ts +5 -0
- package/src/lib/{broadcastUpdate → broadcast-update}/BroadcastCacheUpdate.ts +5 -5
- package/src/lib/{broadcastUpdate → broadcast-update}/BroadcastUpdatePlugin.ts +1 -1
- package/src/lib/broadcast-update/index.ts +5 -0
- package/src/lib/{broadcastUpdate → broadcast-update}/responsesAreSame.ts +2 -2
- package/src/lib/{broadcastUpdate → broadcast-update}/types.ts +1 -1
- package/src/{cacheNames.ts → lib/cache-name.ts} +1 -1
- package/src/lib/{cacheableResponse → cacheable-response}/CacheableResponse.ts +4 -4
- package/src/lib/{cacheableResponse → cacheable-response}/CacheableResponsePlugin.ts +1 -1
- package/src/lib/cacheable-response/index.ts +3 -0
- package/src/lib/core.ts +128 -0
- package/src/lib/expiration/CacheExpiration.ts +3 -3
- package/src/lib/expiration/ExpirationPlugin.ts +9 -10
- package/src/lib/expiration/index.ts +3 -0
- package/src/lib/extension.ts +37 -0
- package/src/lib/{googleAnalytics/initializeGoogleAnalytics.ts → extensions/google-analytics/extension.ts} +43 -49
- package/src/lib/extensions/google-analytics/index.ts +0 -0
- package/src/lib/extensions/google-analytics/initialize.ts +48 -0
- package/src/lib/extensions/index.ts +13 -0
- package/src/{controllers/PrecacheController/PrecacheController.ts → lib/extensions/precache/extension.ts} +37 -56
- package/src/{controllers/PrecacheController/parsePrecacheOptions.ts → lib/extensions/precache/options.ts} +11 -8
- package/src/{controllers/PrecacheController/PrecacheCacheKeyPlugin.ts → lib/extensions/precache/plugin-cache-key.ts} +4 -4
- package/src/lib/{precaching/PrecacheFallbackPlugin.ts → extensions/precache/plugin-fallback.ts} +26 -16
- package/src/{controllers/PrecacheController/PrecacheInstallReportPlugin.ts → lib/extensions/precache/plugin-install-report.ts} +1 -1
- package/src/lib/extensions/precache/route.ts +72 -0
- package/src/{controllers/PrecacheController/PrecacheStrategy.ts → lib/extensions/precache/strategy.ts} +11 -11
- package/src/{controllers/RuntimeCacheController.ts → lib/extensions/runtime-cache.ts} +24 -17
- package/src/lib/functions/handlers.ts +149 -0
- package/src/lib/functions/router.ts +314 -0
- package/src/{navigationPreload.ts → lib/navigation-preload.ts} +1 -1
- package/src/lib/{rangeRequests → range-requests}/RangeRequestsPlugin.ts +1 -1
- package/src/lib/{rangeRequests → range-requests}/createPartialResponse.ts +3 -3
- package/src/lib/range-requests/index.ts +2 -0
- package/src/lib/{rangeRequests → range-requests}/utils/calculateEffectiveBoundaries.ts +2 -2
- package/src/lib/{rangeRequests → range-requests}/utils/parseRangeHeader.ts +2 -2
- package/src/lib/route.ts +234 -0
- package/src/lib/serwist.ts +443 -0
- package/src/lib/strategies/CacheFirst.ts +4 -4
- package/src/lib/strategies/CacheOnly.ts +3 -3
- package/src/lib/strategies/NetworkFirst.ts +6 -6
- package/src/lib/strategies/NetworkOnly.ts +5 -5
- package/src/lib/strategies/StaleWhileRevalidate.ts +5 -5
- package/src/lib/strategies/Strategy.ts +9 -9
- package/src/lib/strategies/StrategyHandler.ts +11 -11
- package/src/lib/strategies/index.ts +10 -0
- package/src/lib/strategies/plugins/cacheOkAndOpaquePlugin.ts +1 -1
- package/src/lib/strategies/utils/messages.ts +2 -2
- package/src/{types.ts → lib/types.ts} +17 -26
- package/src/lib/utils.ts +137 -0
- package/src/models/messages/messageGenerator.ts +1 -1
- package/src/models/messages/messages.ts +3 -3
- package/src/utils/SerwistError.ts +3 -3
- package/src/utils/assert.ts +1 -2
- package/src/utils/cacheNames.ts +0 -2
- package/src/utils/canConstructReadableStream.ts +1 -1
- package/src/utils/canConstructResponseFromBodyStream.ts +1 -1
- package/src/utils/createCacheKey.ts +1 -2
- package/src/utils/executeQuotaErrorCallbacks.ts +1 -1
- package/src/utils/generateURLVariations.ts +1 -1
- package/src/utils/logger.ts +1 -1
- package/src/utils/normalizeHandler.ts +1 -1
- package/src/utils/parseRoute.ts +4 -5
- package/src/utils/pluginUtils.ts +1 -1
- package/src/utils/resultingClientExists.ts +1 -1
- package/dist/NavigationRoute.d.ts +0 -56
- package/dist/NavigationRoute.d.ts.map +0 -1
- package/dist/RegExpRoute.d.ts +0 -24
- package/dist/RegExpRoute.d.ts.map +0 -1
- package/dist/Route.d.ts +0 -33
- package/dist/Route.d.ts.map +0 -1
- package/dist/Serwist.d.ts.map +0 -1
- package/dist/cacheNames.d.ts.map +0 -1
- package/dist/constants.d.ts.map +0 -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 +0 -15
- 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/copyResponse.d.ts.map +0 -1
- package/dist/disableDevLogs.d.ts +0 -7
- package/dist/disableDevLogs.d.ts.map +0 -1
- package/dist/lib/backgroundSync/BackgroundSyncPlugin.d.ts.map +0 -1
- package/dist/lib/backgroundSync/BackgroundSyncQueue.d.ts.map +0 -1
- package/dist/lib/backgroundSync/BackgroundSyncQueueDb.d.ts.map +0 -1
- package/dist/lib/backgroundSync/BackgroundSyncQueueStore.d.ts.map +0 -1
- package/dist/lib/backgroundSync/StorableRequest.d.ts.map +0 -1
- package/dist/lib/broadcastUpdate/BroadcastCacheUpdate.d.ts.map +0 -1
- package/dist/lib/broadcastUpdate/BroadcastUpdatePlugin.d.ts.map +0 -1
- package/dist/lib/broadcastUpdate/constants.d.ts.map +0 -1
- package/dist/lib/broadcastUpdate/responsesAreSame.d.ts.map +0 -1
- package/dist/lib/broadcastUpdate/types.d.ts.map +0 -1
- package/dist/lib/cacheableResponse/CacheableResponse.d.ts.map +0 -1
- package/dist/lib/cacheableResponse/CacheableResponsePlugin.d.ts.map +0 -1
- package/dist/lib/googleAnalytics/constants.d.ts.map +0 -1
- package/dist/lib/googleAnalytics/initializeGoogleAnalytics.d.ts +0 -30
- package/dist/lib/googleAnalytics/initializeGoogleAnalytics.d.ts.map +0 -1
- package/dist/lib/precaching/PrecacheFallbackPlugin.d.ts.map +0 -1
- package/dist/lib/rangeRequests/RangeRequestsPlugin.d.ts.map +0 -1
- package/dist/lib/rangeRequests/createPartialResponse.d.ts.map +0 -1
- package/dist/lib/rangeRequests/utils/calculateEffectiveBoundaries.d.ts.map +0 -1
- package/dist/lib/rangeRequests/utils/parseRangeHeader.d.ts.map +0 -1
- package/dist/navigationPreload.d.ts.map +0 -1
- package/dist/registerQuotaErrorCallback.d.ts +0 -8
- package/dist/registerQuotaErrorCallback.d.ts.map +0 -1
- package/dist/setCacheNameDetails.d.ts +0 -9
- package/dist/setCacheNameDetails.d.ts.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/src/NavigationRoute.ts +0 -118
- package/src/RegExpRoute.ts +0 -74
- package/src/Route.ts +0 -67
- package/src/Serwist.ts +0 -762
- package/src/controllers/PrecacheController/PrecacheRoute.ts +0 -44
- package/src/copyResponse.ts +0 -58
- package/src/disableDevLogs.ts +0 -10
- package/src/registerQuotaErrorCallback.ts +0 -34
- package/src/setCacheNameDetails.ts +0 -53
- /package/dist/lib/{backgroundSync → background-sync}/BackgroundSyncQueue.d.ts +0 -0
- /package/dist/lib/{backgroundSync → background-sync}/BackgroundSyncQueueDb.d.ts +0 -0
- /package/dist/lib/{backgroundSync → background-sync}/BackgroundSyncQueueStore.d.ts +0 -0
- /package/dist/lib/{broadcastUpdate → broadcast-update}/constants.d.ts +0 -0
- /package/dist/lib/{broadcastUpdate → broadcast-update}/responsesAreSame.d.ts +0 -0
- /package/dist/lib/{cacheableResponse → cacheable-response}/CacheableResponse.d.ts +0 -0
- /package/dist/lib/{googleAnalytics → extensions/google-analytics}/constants.d.ts +0 -0
- /package/dist/lib/{rangeRequests → range-requests}/createPartialResponse.d.ts +0 -0
- /package/dist/lib/{rangeRequests → range-requests}/utils/calculateEffectiveBoundaries.d.ts +0 -0
- /package/dist/lib/{rangeRequests → range-requests}/utils/parseRangeHeader.d.ts +0 -0
- /package/src/lib/{broadcastUpdate → broadcast-update}/constants.ts +0 -0
- /package/src/{constants.ts → lib/constants.ts} +0 -0
- /package/src/lib/{googleAnalytics → extensions/google-analytics}/constants.ts +0 -0
package/dist/index.js
CHANGED
|
@@ -1,1458 +1,1487 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { parallel } from '@serwist/utils';
|
|
1
|
+
import { c as cacheNames$1, l as logger, f as finalAssertExports, S as SerwistError, g as getFriendlyURL, D as Deferred, t as timeout, a as cacheMatchIgnoreParams, e as executeQuotaErrorCallbacks, b as canConstructResponseFromBodyStream, q as quotaErrorCallbacks, w as waitUntil, d as clientsClaim, r as resultingClientExists } from './chunks/waitUntil.js';
|
|
3
2
|
import { openDB, deleteDB } from 'idb';
|
|
3
|
+
import { parallel } from '@serwist/utils';
|
|
4
4
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
const cacheNames = {
|
|
6
|
+
get googleAnalytics () {
|
|
7
|
+
return cacheNames$1.getGoogleAnalyticsName();
|
|
8
|
+
},
|
|
9
|
+
get precache () {
|
|
10
|
+
return cacheNames$1.getPrecacheName();
|
|
11
|
+
},
|
|
12
|
+
get prefix () {
|
|
13
|
+
return cacheNames$1.getPrefix();
|
|
14
|
+
},
|
|
15
|
+
get runtime () {
|
|
16
|
+
return cacheNames$1.getRuntimeName();
|
|
17
|
+
},
|
|
18
|
+
get suffix () {
|
|
19
|
+
return cacheNames$1.getSuffix();
|
|
10
20
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const isNavigationPreloadSupported = ()=>{
|
|
24
|
+
return Boolean(self.registration?.navigationPreload);
|
|
25
|
+
};
|
|
26
|
+
const enableNavigationPreload = (headerValue)=>{
|
|
27
|
+
if (isNavigationPreloadSupported()) {
|
|
28
|
+
self.addEventListener("activate", (event)=>{
|
|
29
|
+
event.waitUntil(self.registration.navigationPreload.enable().then(()=>{
|
|
30
|
+
if (headerValue) {
|
|
31
|
+
void self.registration.navigationPreload.setHeaderValue(headerValue);
|
|
32
|
+
}
|
|
33
|
+
if (process.env.NODE_ENV !== "production") {
|
|
34
|
+
logger.log("Navigation preloading is enabled.");
|
|
35
|
+
}
|
|
36
|
+
}));
|
|
14
37
|
});
|
|
38
|
+
} else {
|
|
39
|
+
if (process.env.NODE_ENV !== "production") {
|
|
40
|
+
logger.log("Navigation preloading is not supported in this browser.");
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
const disableNavigationPreload = ()=>{
|
|
45
|
+
if (isNavigationPreloadSupported()) {
|
|
46
|
+
self.addEventListener("activate", (event)=>{
|
|
47
|
+
event.waitUntil(self.registration.navigationPreload.disable().then(()=>{
|
|
48
|
+
if (process.env.NODE_ENV !== "production") {
|
|
49
|
+
logger.log("Navigation preloading is disabled.");
|
|
50
|
+
}
|
|
51
|
+
}));
|
|
52
|
+
});
|
|
53
|
+
} else {
|
|
54
|
+
if (process.env.NODE_ENV !== "production") {
|
|
55
|
+
logger.log("Navigation preloading is not supported in this browser.");
|
|
56
|
+
}
|
|
15
57
|
}
|
|
16
|
-
const clonedResponse = response.clone();
|
|
17
|
-
const responseInit = {
|
|
18
|
-
headers: new Headers(clonedResponse.headers),
|
|
19
|
-
status: clonedResponse.status,
|
|
20
|
-
statusText: clonedResponse.statusText
|
|
21
|
-
};
|
|
22
|
-
const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit;
|
|
23
|
-
const body = canConstructResponseFromBodyStream() ? clonedResponse.body : await clonedResponse.blob();
|
|
24
|
-
return new Response(body, modifiedResponseInit);
|
|
25
58
|
};
|
|
26
59
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
class StrategyHandler {
|
|
31
|
-
event;
|
|
32
|
-
request;
|
|
33
|
-
url;
|
|
34
|
-
params;
|
|
35
|
-
_cacheKeys = {};
|
|
36
|
-
_strategy;
|
|
37
|
-
_handlerDeferred;
|
|
38
|
-
_extendLifetimePromises;
|
|
39
|
-
_plugins;
|
|
40
|
-
_pluginStateMap;
|
|
41
|
-
constructor(strategy, options){
|
|
60
|
+
const normalizeHandler = (handler)=>{
|
|
61
|
+
if (handler && typeof handler === "object") {
|
|
42
62
|
if (process.env.NODE_ENV !== "production") {
|
|
43
|
-
finalAssertExports.
|
|
63
|
+
finalAssertExports.hasMethod(handler, "handle", {
|
|
44
64
|
moduleName: "serwist",
|
|
45
|
-
className: "
|
|
65
|
+
className: "Route",
|
|
46
66
|
funcName: "constructor",
|
|
47
|
-
paramName: "
|
|
67
|
+
paramName: "handler"
|
|
48
68
|
});
|
|
49
|
-
|
|
69
|
+
}
|
|
70
|
+
return handler;
|
|
71
|
+
}
|
|
72
|
+
if (process.env.NODE_ENV !== "production") {
|
|
73
|
+
finalAssertExports.isType(handler, "function", {
|
|
74
|
+
moduleName: "serwist",
|
|
75
|
+
className: "Route",
|
|
76
|
+
funcName: "constructor",
|
|
77
|
+
paramName: "handler"
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
handle: handler
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const defaultMethod = "GET";
|
|
86
|
+
const validMethods = [
|
|
87
|
+
"DELETE",
|
|
88
|
+
"GET",
|
|
89
|
+
"HEAD",
|
|
90
|
+
"PATCH",
|
|
91
|
+
"POST",
|
|
92
|
+
"PUT"
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
class Route {
|
|
96
|
+
handler;
|
|
97
|
+
match;
|
|
98
|
+
method;
|
|
99
|
+
catchHandler;
|
|
100
|
+
constructor(match, handler, method = defaultMethod){
|
|
101
|
+
if (process.env.NODE_ENV !== "production") {
|
|
102
|
+
finalAssertExports.isType(match, "function", {
|
|
50
103
|
moduleName: "serwist",
|
|
51
|
-
className: "
|
|
104
|
+
className: "Route",
|
|
52
105
|
funcName: "constructor",
|
|
53
|
-
paramName: "
|
|
106
|
+
paramName: "match"
|
|
54
107
|
});
|
|
108
|
+
if (method) {
|
|
109
|
+
finalAssertExports.isOneOf(method, validMethods, {
|
|
110
|
+
paramName: "method"
|
|
111
|
+
});
|
|
112
|
+
}
|
|
55
113
|
}
|
|
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);
|
|
114
|
+
this.handler = normalizeHandler(handler);
|
|
115
|
+
this.match = match;
|
|
116
|
+
this.method = method;
|
|
73
117
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
118
|
+
setCatchHandler(handler) {
|
|
119
|
+
this.catchHandler = normalizeHandler(handler);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
class RegExpRoute extends Route {
|
|
123
|
+
constructor(regExp, handler, method){
|
|
124
|
+
if (process.env.NODE_ENV !== "production") {
|
|
125
|
+
finalAssertExports.isInstance(regExp, RegExp, {
|
|
126
|
+
moduleName: "serwist",
|
|
127
|
+
className: "RegExpRoute",
|
|
128
|
+
funcName: "constructor",
|
|
129
|
+
paramName: "pattern"
|
|
130
|
+
});
|
|
80
131
|
}
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
request: request.clone(),
|
|
86
|
-
event
|
|
87
|
-
});
|
|
132
|
+
const match = ({ url })=>{
|
|
133
|
+
const result = regExp.exec(url.href);
|
|
134
|
+
if (!result) {
|
|
135
|
+
return;
|
|
88
136
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
137
|
+
if (url.origin !== location.origin && result.index !== 0) {
|
|
138
|
+
if (process.env.NODE_ENV !== "production") {
|
|
139
|
+
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.`);
|
|
140
|
+
}
|
|
141
|
+
return;
|
|
94
142
|
}
|
|
143
|
+
return result.slice(1);
|
|
144
|
+
};
|
|
145
|
+
super(match, handler, method);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
class NavigationRoute extends Route {
|
|
149
|
+
_allowlist;
|
|
150
|
+
_denylist;
|
|
151
|
+
constructor(handler, { allowlist = [
|
|
152
|
+
/./
|
|
153
|
+
], denylist = [] } = {}){
|
|
154
|
+
if (process.env.NODE_ENV !== "production") {
|
|
155
|
+
finalAssertExports.isArrayOfClass(allowlist, RegExp, {
|
|
156
|
+
moduleName: "serwist",
|
|
157
|
+
className: "NavigationRoute",
|
|
158
|
+
funcName: "constructor",
|
|
159
|
+
paramName: "options.allowlist"
|
|
160
|
+
});
|
|
161
|
+
finalAssertExports.isArrayOfClass(denylist, RegExp, {
|
|
162
|
+
moduleName: "serwist",
|
|
163
|
+
className: "NavigationRoute",
|
|
164
|
+
funcName: "constructor",
|
|
165
|
+
paramName: "options.denylist"
|
|
166
|
+
});
|
|
95
167
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
168
|
+
super((options)=>this._match(options), handler);
|
|
169
|
+
this._allowlist = allowlist;
|
|
170
|
+
this._denylist = denylist;
|
|
171
|
+
}
|
|
172
|
+
_match({ url, request }) {
|
|
173
|
+
if (request && request.mode !== "navigate") {
|
|
174
|
+
return false;
|
|
175
|
+
}
|
|
176
|
+
const pathnameAndSearch = url.pathname + url.search;
|
|
177
|
+
for (const regExp of this._denylist){
|
|
178
|
+
if (regExp.test(pathnameAndSearch)) {
|
|
179
|
+
if (process.env.NODE_ENV !== "production") {
|
|
180
|
+
logger.log(`The navigation route ${pathnameAndSearch} is not being used, since the URL matches this denylist pattern: ${regExp.toString()}`);
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
109
183
|
}
|
|
110
|
-
|
|
111
|
-
|
|
184
|
+
}
|
|
185
|
+
if (this._allowlist.some((regExp)=>regExp.test(pathnameAndSearch))) {
|
|
112
186
|
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
|
-
});
|
|
122
|
-
}
|
|
123
|
-
throw error;
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
async fetchAndCachePut(input) {
|
|
127
|
-
const response = await this.fetch(input);
|
|
128
|
-
const responseClone = response.clone();
|
|
129
|
-
void this.waitUntil(this.cachePut(input, responseClone));
|
|
130
|
-
return response;
|
|
131
|
-
}
|
|
132
|
-
async cacheMatch(key) {
|
|
133
|
-
const request = toRequest(key);
|
|
134
|
-
let cachedResponse;
|
|
135
|
-
const { cacheName, matchOptions } = this._strategy;
|
|
136
|
-
const effectiveRequest = await this.getCacheKey(request, "read");
|
|
137
|
-
const multiMatchOptions = {
|
|
138
|
-
...matchOptions,
|
|
139
|
-
...{
|
|
140
|
-
cacheName
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);
|
|
144
|
-
if (process.env.NODE_ENV !== "production") {
|
|
145
|
-
if (cachedResponse) {
|
|
146
|
-
logger.debug(`Found a cached response in '${cacheName}'.`);
|
|
147
|
-
} else {
|
|
148
|
-
logger.debug(`No cached response found in '${cacheName}'.`);
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
for (const callback of this.iterateCallbacks("cachedResponseWillBeUsed")){
|
|
152
|
-
cachedResponse = await callback({
|
|
153
|
-
cacheName,
|
|
154
|
-
matchOptions,
|
|
155
|
-
cachedResponse,
|
|
156
|
-
request: effectiveRequest,
|
|
157
|
-
event: this.event
|
|
158
|
-
}) || undefined;
|
|
159
|
-
}
|
|
160
|
-
return cachedResponse;
|
|
161
|
-
}
|
|
162
|
-
async cachePut(key, response) {
|
|
163
|
-
const request = toRequest(key);
|
|
164
|
-
await timeout(0);
|
|
165
|
-
const effectiveRequest = await this.getCacheKey(request, "write");
|
|
166
|
-
if (process.env.NODE_ENV !== "production") {
|
|
167
|
-
if (effectiveRequest.method && effectiveRequest.method !== "GET") {
|
|
168
|
-
throw new SerwistError("attempt-to-cache-non-get-request", {
|
|
169
|
-
url: getFriendlyURL(effectiveRequest.url),
|
|
170
|
-
method: effectiveRequest.method
|
|
171
|
-
});
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
if (!response) {
|
|
175
|
-
if (process.env.NODE_ENV !== "production") {
|
|
176
|
-
logger.error(`Cannot cache non-existent response for '${getFriendlyURL(effectiveRequest.url)}'.`);
|
|
177
|
-
}
|
|
178
|
-
throw new SerwistError("cache-put-with-no-response", {
|
|
179
|
-
url: getFriendlyURL(effectiveRequest.url)
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
const responseToCache = await this._ensureResponseSafeToCache(response);
|
|
183
|
-
if (!responseToCache) {
|
|
184
|
-
if (process.env.NODE_ENV !== "production") {
|
|
185
|
-
logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' will not be cached.`, responseToCache);
|
|
186
|
-
}
|
|
187
|
-
return false;
|
|
188
|
-
}
|
|
189
|
-
const { cacheName, matchOptions } = this._strategy;
|
|
190
|
-
const cache = await self.caches.open(cacheName);
|
|
191
|
-
if (process.env.NODE_ENV !== "production") {
|
|
192
|
-
const vary = response.headers.get("Vary");
|
|
193
|
-
if (vary && matchOptions?.ignoreVary !== true) {
|
|
194
|
-
logger.debug(`The response for ${getFriendlyURL(effectiveRequest.url)} has a 'Vary: ${vary}' header. Consider setting the {ignoreVary: true} option on your strategy to ensure cache matching and deletion works as expected.`);
|
|
187
|
+
logger.debug(`The navigation route ${pathnameAndSearch} is being used.`);
|
|
195
188
|
}
|
|
189
|
+
return true;
|
|
196
190
|
}
|
|
197
|
-
const hasCacheUpdateCallback = this.hasCallback("cacheDidUpdate");
|
|
198
|
-
const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams(cache, effectiveRequest.clone(), [
|
|
199
|
-
"__WB_REVISION__"
|
|
200
|
-
], matchOptions) : null;
|
|
201
191
|
if (process.env.NODE_ENV !== "production") {
|
|
202
|
-
logger.
|
|
203
|
-
}
|
|
204
|
-
try {
|
|
205
|
-
await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);
|
|
206
|
-
} catch (error) {
|
|
207
|
-
if (error instanceof Error) {
|
|
208
|
-
if (error.name === "QuotaExceededError") {
|
|
209
|
-
await executeQuotaErrorCallbacks();
|
|
210
|
-
}
|
|
211
|
-
throw error;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
for (const callback of this.iterateCallbacks("cacheDidUpdate")){
|
|
215
|
-
await callback({
|
|
216
|
-
cacheName,
|
|
217
|
-
oldResponse,
|
|
218
|
-
newResponse: responseToCache.clone(),
|
|
219
|
-
request: effectiveRequest,
|
|
220
|
-
event: this.event
|
|
221
|
-
});
|
|
222
|
-
}
|
|
223
|
-
return true;
|
|
224
|
-
}
|
|
225
|
-
async getCacheKey(request, mode) {
|
|
226
|
-
const key = `${request.url} | ${mode}`;
|
|
227
|
-
if (!this._cacheKeys[key]) {
|
|
228
|
-
let effectiveRequest = request;
|
|
229
|
-
for (const callback of this.iterateCallbacks("cacheKeyWillBeUsed")){
|
|
230
|
-
effectiveRequest = toRequest(await callback({
|
|
231
|
-
mode,
|
|
232
|
-
request: effectiveRequest,
|
|
233
|
-
event: this.event,
|
|
234
|
-
params: this.params
|
|
235
|
-
}));
|
|
236
|
-
}
|
|
237
|
-
this._cacheKeys[key] = effectiveRequest;
|
|
238
|
-
}
|
|
239
|
-
return this._cacheKeys[key];
|
|
240
|
-
}
|
|
241
|
-
hasCallback(name) {
|
|
242
|
-
for (const plugin of this._strategy.plugins){
|
|
243
|
-
if (name in plugin) {
|
|
244
|
-
return true;
|
|
245
|
-
}
|
|
192
|
+
logger.log(`The navigation route ${pathnameAndSearch} is not being used, since the URL being navigated to doesn't match the allowlist.`);
|
|
246
193
|
}
|
|
247
194
|
return false;
|
|
248
195
|
}
|
|
249
|
-
async runCallbacks(name, param) {
|
|
250
|
-
for (const callback of this.iterateCallbacks(name)){
|
|
251
|
-
await callback(param);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
*iterateCallbacks(name) {
|
|
255
|
-
for (const plugin of this._strategy.plugins){
|
|
256
|
-
if (typeof plugin[name] === "function") {
|
|
257
|
-
const state = this._pluginStateMap.get(plugin);
|
|
258
|
-
const statefulCallback = (param)=>{
|
|
259
|
-
const statefulParam = {
|
|
260
|
-
...param,
|
|
261
|
-
state
|
|
262
|
-
};
|
|
263
|
-
return plugin[name](statefulParam);
|
|
264
|
-
};
|
|
265
|
-
yield statefulCallback;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
waitUntil(promise) {
|
|
270
|
-
this._extendLifetimePromises.push(promise);
|
|
271
|
-
return promise;
|
|
272
|
-
}
|
|
273
|
-
async doneWaiting() {
|
|
274
|
-
let promise = undefined;
|
|
275
|
-
while(promise = this._extendLifetimePromises.shift()){
|
|
276
|
-
await promise;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
destroy() {
|
|
280
|
-
this._handlerDeferred.resolve(null);
|
|
281
|
-
}
|
|
282
|
-
async getPreloadResponse() {
|
|
283
|
-
if (this.event instanceof FetchEvent && this.event.request.mode === "navigate" && "preloadResponse" in this.event) {
|
|
284
|
-
try {
|
|
285
|
-
const possiblePreloadResponse = await this.event.preloadResponse;
|
|
286
|
-
if (possiblePreloadResponse) {
|
|
287
|
-
if (process.env.NODE_ENV !== "production") {
|
|
288
|
-
logger.log(`Using a preloaded navigation response for '${getFriendlyURL(this.event.request.url)}'`);
|
|
289
|
-
}
|
|
290
|
-
return possiblePreloadResponse;
|
|
291
|
-
}
|
|
292
|
-
} catch (error) {
|
|
293
|
-
if (process.env.NODE_ENV !== "production") {
|
|
294
|
-
logger.error(error);
|
|
295
|
-
}
|
|
296
|
-
return undefined;
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
return undefined;
|
|
300
|
-
}
|
|
301
|
-
async _ensureResponseSafeToCache(response) {
|
|
302
|
-
let responseToCache = response;
|
|
303
|
-
let pluginsUsed = false;
|
|
304
|
-
for (const callback of this.iterateCallbacks("cacheWillUpdate")){
|
|
305
|
-
responseToCache = await callback({
|
|
306
|
-
request: this.request,
|
|
307
|
-
response: responseToCache,
|
|
308
|
-
event: this.event
|
|
309
|
-
}) || undefined;
|
|
310
|
-
pluginsUsed = true;
|
|
311
|
-
if (!responseToCache) {
|
|
312
|
-
break;
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
if (!pluginsUsed) {
|
|
316
|
-
if (responseToCache && responseToCache.status !== 200) {
|
|
317
|
-
if (process.env.NODE_ENV !== "production") {
|
|
318
|
-
if (responseToCache.status === 0) {
|
|
319
|
-
logger.warn(`The response for '${this.request.url}' is an opaque response. The caching strategy that you're using will not cache opaque responses by default.`);
|
|
320
|
-
} else {
|
|
321
|
-
logger.debug(`The response for '${this.request.url}' returned a status code of '${response.status}' and won't be cached as a result.`);
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
responseToCache = undefined;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
return responseToCache;
|
|
328
|
-
}
|
|
329
196
|
}
|
|
330
197
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
}
|
|
342
|
-
handle(options) {
|
|
343
|
-
const [responseDone] = this.handleAll(options);
|
|
344
|
-
return responseDone;
|
|
345
|
-
}
|
|
346
|
-
handleAll(options) {
|
|
347
|
-
if (options instanceof FetchEvent) {
|
|
348
|
-
options = {
|
|
349
|
-
event: options,
|
|
350
|
-
request: options.request
|
|
351
|
-
};
|
|
352
|
-
}
|
|
353
|
-
const event = options.event;
|
|
354
|
-
const request = typeof options.request === "string" ? new Request(options.request) : options.request;
|
|
355
|
-
const handler = new StrategyHandler(this, options.url ? {
|
|
356
|
-
event,
|
|
357
|
-
request,
|
|
358
|
-
url: options.url,
|
|
359
|
-
params: options.params
|
|
360
|
-
} : {
|
|
361
|
-
event,
|
|
362
|
-
request
|
|
363
|
-
});
|
|
364
|
-
const responseDone = this._getResponse(handler, request, event);
|
|
365
|
-
const handlerDone = this._awaitComplete(responseDone, handler, request, event);
|
|
366
|
-
return [
|
|
367
|
-
responseDone,
|
|
368
|
-
handlerDone
|
|
369
|
-
];
|
|
370
|
-
}
|
|
371
|
-
async _getResponse(handler, request, event) {
|
|
372
|
-
await handler.runCallbacks("handlerWillStart", {
|
|
373
|
-
event,
|
|
374
|
-
request
|
|
198
|
+
const BACKGROUND_SYNC_DB_VERSION = 3;
|
|
199
|
+
const BACKGROUND_SYNC_DB_NAME = "serwist-background-sync";
|
|
200
|
+
const REQUEST_OBJECT_STORE_NAME = "requests";
|
|
201
|
+
const QUEUE_NAME_INDEX = "queueName";
|
|
202
|
+
class BackgroundSyncQueueDb {
|
|
203
|
+
_db = null;
|
|
204
|
+
async addEntry(entry) {
|
|
205
|
+
const db = await this.getDb();
|
|
206
|
+
const tx = db.transaction(REQUEST_OBJECT_STORE_NAME, "readwrite", {
|
|
207
|
+
durability: "relaxed"
|
|
375
208
|
});
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
response = await this._handle(request, handler);
|
|
379
|
-
if (response === undefined || response.type === "error") {
|
|
380
|
-
throw new SerwistError("no-response", {
|
|
381
|
-
url: request.url
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
} catch (error) {
|
|
385
|
-
if (error instanceof Error) {
|
|
386
|
-
for (const callback of handler.iterateCallbacks("handlerDidError")){
|
|
387
|
-
response = await callback({
|
|
388
|
-
error,
|
|
389
|
-
event,
|
|
390
|
-
request
|
|
391
|
-
});
|
|
392
|
-
if (response !== undefined) {
|
|
393
|
-
break;
|
|
394
|
-
}
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
if (!response) {
|
|
398
|
-
throw error;
|
|
399
|
-
}
|
|
400
|
-
if (process.env.NODE_ENV !== "production") {
|
|
401
|
-
throw logger.log(`While responding to '${getFriendlyURL(request.url)}', an ${error instanceof Error ? error.toString() : ""} error occurred. Using a fallback response provided by a handlerDidError plugin.`);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
for (const callback of handler.iterateCallbacks("handlerWillRespond")){
|
|
405
|
-
response = await callback({
|
|
406
|
-
event,
|
|
407
|
-
request,
|
|
408
|
-
response
|
|
409
|
-
});
|
|
410
|
-
}
|
|
411
|
-
return response;
|
|
209
|
+
await tx.store.add(entry);
|
|
210
|
+
await tx.done;
|
|
412
211
|
}
|
|
413
|
-
async
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
response = await responseDone;
|
|
418
|
-
} catch (error) {}
|
|
419
|
-
try {
|
|
420
|
-
await handler.runCallbacks("handlerDidRespond", {
|
|
421
|
-
event,
|
|
422
|
-
request,
|
|
423
|
-
response
|
|
424
|
-
});
|
|
425
|
-
await handler.doneWaiting();
|
|
426
|
-
} catch (waitUntilError) {
|
|
427
|
-
if (waitUntilError instanceof Error) {
|
|
428
|
-
error = waitUntilError;
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
await handler.runCallbacks("handlerDidComplete", {
|
|
432
|
-
event,
|
|
433
|
-
request,
|
|
434
|
-
response,
|
|
435
|
-
error
|
|
436
|
-
});
|
|
437
|
-
handler.destroy();
|
|
438
|
-
if (error) {
|
|
439
|
-
throw error;
|
|
440
|
-
}
|
|
212
|
+
async getFirstEntryId() {
|
|
213
|
+
const db = await this.getDb();
|
|
214
|
+
const cursor = await db.transaction(REQUEST_OBJECT_STORE_NAME).store.openCursor();
|
|
215
|
+
return cursor?.value.id;
|
|
441
216
|
}
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
static defaultPrecacheCacheabilityPlugin = {
|
|
447
|
-
async cacheWillUpdate ({ response }) {
|
|
448
|
-
if (!response || response.status >= 400) {
|
|
449
|
-
return null;
|
|
450
|
-
}
|
|
451
|
-
return response;
|
|
452
|
-
}
|
|
453
|
-
};
|
|
454
|
-
static copyRedirectedCacheableResponsesPlugin = {
|
|
455
|
-
async cacheWillUpdate ({ response }) {
|
|
456
|
-
return response.redirected ? await copyResponse(response) : response;
|
|
457
|
-
}
|
|
458
|
-
};
|
|
459
|
-
constructor(options = {}){
|
|
460
|
-
options.cacheName = cacheNames$1.getPrecacheName(options.cacheName);
|
|
461
|
-
super(options);
|
|
462
|
-
this._fallbackToNetwork = options.fallbackToNetwork !== false;
|
|
463
|
-
this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);
|
|
217
|
+
async getAllEntriesByQueueName(queueName) {
|
|
218
|
+
const db = await this.getDb();
|
|
219
|
+
const results = await db.getAllFromIndex(REQUEST_OBJECT_STORE_NAME, QUEUE_NAME_INDEX, IDBKeyRange.only(queueName));
|
|
220
|
+
return results ? results : [];
|
|
464
221
|
}
|
|
465
|
-
async
|
|
466
|
-
const
|
|
467
|
-
|
|
468
|
-
return preloadResponse;
|
|
469
|
-
}
|
|
470
|
-
const response = await handler.cacheMatch(request);
|
|
471
|
-
if (response) {
|
|
472
|
-
return response;
|
|
473
|
-
}
|
|
474
|
-
if (handler.event && handler.event.type === "install") {
|
|
475
|
-
return await this._handleInstall(request, handler);
|
|
476
|
-
}
|
|
477
|
-
return await this._handleFetch(request, handler);
|
|
222
|
+
async getEntryCountByQueueName(queueName) {
|
|
223
|
+
const db = await this.getDb();
|
|
224
|
+
return db.countFromIndex(REQUEST_OBJECT_STORE_NAME, QUEUE_NAME_INDEX, IDBKeyRange.only(queueName));
|
|
478
225
|
}
|
|
479
|
-
async
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
if (this._fallbackToNetwork) {
|
|
483
|
-
if (process.env.NODE_ENV !== "production") {
|
|
484
|
-
logger.warn(`The precached response for ${getFriendlyURL(request.url)} in ${this.cacheName} was not found. Falling back to the network.`);
|
|
485
|
-
}
|
|
486
|
-
const integrityInManifest = params.integrity;
|
|
487
|
-
const integrityInRequest = request.integrity;
|
|
488
|
-
const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest;
|
|
489
|
-
response = await handler.fetch(new Request(request, {
|
|
490
|
-
integrity: request.mode !== "no-cors" ? integrityInRequest || integrityInManifest : undefined
|
|
491
|
-
}));
|
|
492
|
-
if (integrityInManifest && noIntegrityConflict && request.mode !== "no-cors") {
|
|
493
|
-
this._useDefaultCacheabilityPluginIfNeeded();
|
|
494
|
-
const wasCached = await handler.cachePut(request, response.clone());
|
|
495
|
-
if (process.env.NODE_ENV !== "production") {
|
|
496
|
-
if (wasCached) {
|
|
497
|
-
logger.log(`A response for ${getFriendlyURL(request.url)} was used to "repair" the precache.`);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
} else {
|
|
502
|
-
throw new SerwistError("missing-precache-entry", {
|
|
503
|
-
cacheName: this.cacheName,
|
|
504
|
-
url: request.url
|
|
505
|
-
});
|
|
506
|
-
}
|
|
507
|
-
if (process.env.NODE_ENV !== "production") {
|
|
508
|
-
const cacheKey = params.cacheKey || await handler.getCacheKey(request, "read");
|
|
509
|
-
logger.groupCollapsed(`Precaching is responding to: ${getFriendlyURL(request.url)}`);
|
|
510
|
-
logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`);
|
|
511
|
-
logger.groupCollapsed("View request details here.");
|
|
512
|
-
logger.log(request);
|
|
513
|
-
logger.groupEnd();
|
|
514
|
-
logger.groupCollapsed("View response details here.");
|
|
515
|
-
logger.log(response);
|
|
516
|
-
logger.groupEnd();
|
|
517
|
-
logger.groupEnd();
|
|
518
|
-
}
|
|
519
|
-
return response;
|
|
226
|
+
async deleteEntry(id) {
|
|
227
|
+
const db = await this.getDb();
|
|
228
|
+
await db.delete(REQUEST_OBJECT_STORE_NAME, id);
|
|
520
229
|
}
|
|
521
|
-
async
|
|
522
|
-
this.
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
230
|
+
async getFirstEntryByQueueName(queueName) {
|
|
231
|
+
return await this.getEndEntryFromIndex(IDBKeyRange.only(queueName), "next");
|
|
232
|
+
}
|
|
233
|
+
async getLastEntryByQueueName(queueName) {
|
|
234
|
+
return await this.getEndEntryFromIndex(IDBKeyRange.only(queueName), "prev");
|
|
235
|
+
}
|
|
236
|
+
async getEndEntryFromIndex(query, direction) {
|
|
237
|
+
const db = await this.getDb();
|
|
238
|
+
const cursor = await db.transaction(REQUEST_OBJECT_STORE_NAME).store.index(QUEUE_NAME_INDEX).openCursor(query, direction);
|
|
239
|
+
return cursor?.value;
|
|
240
|
+
}
|
|
241
|
+
async getDb() {
|
|
242
|
+
if (!this._db) {
|
|
243
|
+
this._db = await openDB(BACKGROUND_SYNC_DB_NAME, BACKGROUND_SYNC_DB_VERSION, {
|
|
244
|
+
upgrade: this._upgradeDb
|
|
529
245
|
});
|
|
530
246
|
}
|
|
531
|
-
return
|
|
247
|
+
return this._db;
|
|
532
248
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) {
|
|
538
|
-
continue;
|
|
539
|
-
}
|
|
540
|
-
if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) {
|
|
541
|
-
defaultPluginIndex = index;
|
|
542
|
-
}
|
|
543
|
-
if (plugin.cacheWillUpdate) {
|
|
544
|
-
cacheWillUpdatePluginCount++;
|
|
249
|
+
_upgradeDb(db, oldVersion) {
|
|
250
|
+
if (oldVersion > 0 && oldVersion < BACKGROUND_SYNC_DB_VERSION) {
|
|
251
|
+
if (db.objectStoreNames.contains(REQUEST_OBJECT_STORE_NAME)) {
|
|
252
|
+
db.deleteObjectStore(REQUEST_OBJECT_STORE_NAME);
|
|
545
253
|
}
|
|
546
254
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
255
|
+
const objStore = db.createObjectStore(REQUEST_OBJECT_STORE_NAME, {
|
|
256
|
+
autoIncrement: true,
|
|
257
|
+
keyPath: "id"
|
|
258
|
+
});
|
|
259
|
+
objStore.createIndex(QUEUE_NAME_INDEX, QUEUE_NAME_INDEX, {
|
|
260
|
+
unique: false
|
|
261
|
+
});
|
|
552
262
|
}
|
|
553
263
|
}
|
|
554
264
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
];
|
|
564
|
-
|
|
565
|
-
const normalizeHandler = (handler)=>{
|
|
566
|
-
if (handler && typeof handler === "object") {
|
|
265
|
+
class BackgroundSyncQueueStore {
|
|
266
|
+
_queueName;
|
|
267
|
+
_queueDb;
|
|
268
|
+
constructor(queueName){
|
|
269
|
+
this._queueName = queueName;
|
|
270
|
+
this._queueDb = new BackgroundSyncQueueDb();
|
|
271
|
+
}
|
|
272
|
+
async pushEntry(entry) {
|
|
567
273
|
if (process.env.NODE_ENV !== "production") {
|
|
568
|
-
finalAssertExports.
|
|
274
|
+
finalAssertExports.isType(entry, "object", {
|
|
569
275
|
moduleName: "serwist",
|
|
570
|
-
className: "
|
|
571
|
-
funcName: "
|
|
572
|
-
paramName: "
|
|
276
|
+
className: "BackgroundSyncQueueStore",
|
|
277
|
+
funcName: "pushEntry",
|
|
278
|
+
paramName: "entry"
|
|
279
|
+
});
|
|
280
|
+
finalAssertExports.isType(entry.requestData, "object", {
|
|
281
|
+
moduleName: "serwist",
|
|
282
|
+
className: "BackgroundSyncQueueStore",
|
|
283
|
+
funcName: "pushEntry",
|
|
284
|
+
paramName: "entry.requestData"
|
|
573
285
|
});
|
|
574
286
|
}
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
finalAssertExports.isType(handler, "function", {
|
|
579
|
-
moduleName: "serwist",
|
|
580
|
-
className: "Route",
|
|
581
|
-
funcName: "constructor",
|
|
582
|
-
paramName: "handler"
|
|
583
|
-
});
|
|
287
|
+
delete entry.id;
|
|
288
|
+
entry.queueName = this._queueName;
|
|
289
|
+
await this._queueDb.addEntry(entry);
|
|
584
290
|
}
|
|
585
|
-
|
|
586
|
-
handle: handler
|
|
587
|
-
};
|
|
588
|
-
};
|
|
589
|
-
|
|
590
|
-
class Route {
|
|
591
|
-
handler;
|
|
592
|
-
match;
|
|
593
|
-
method;
|
|
594
|
-
catchHandler;
|
|
595
|
-
constructor(match, handler, method = defaultMethod){
|
|
291
|
+
async unshiftEntry(entry) {
|
|
596
292
|
if (process.env.NODE_ENV !== "production") {
|
|
597
|
-
finalAssertExports.isType(
|
|
293
|
+
finalAssertExports.isType(entry, "object", {
|
|
598
294
|
moduleName: "serwist",
|
|
599
|
-
className: "
|
|
600
|
-
funcName: "
|
|
601
|
-
paramName: "
|
|
295
|
+
className: "BackgroundSyncQueueStore",
|
|
296
|
+
funcName: "unshiftEntry",
|
|
297
|
+
paramName: "entry"
|
|
298
|
+
});
|
|
299
|
+
finalAssertExports.isType(entry.requestData, "object", {
|
|
300
|
+
moduleName: "serwist",
|
|
301
|
+
className: "BackgroundSyncQueueStore",
|
|
302
|
+
funcName: "unshiftEntry",
|
|
303
|
+
paramName: "entry.requestData"
|
|
602
304
|
});
|
|
603
|
-
if (method) {
|
|
604
|
-
finalAssertExports.isOneOf(method, validMethods, {
|
|
605
|
-
paramName: "method"
|
|
606
|
-
});
|
|
607
|
-
}
|
|
608
305
|
}
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
306
|
+
const firstId = await this._queueDb.getFirstEntryId();
|
|
307
|
+
if (firstId) {
|
|
308
|
+
entry.id = firstId - 1;
|
|
309
|
+
} else {
|
|
310
|
+
delete entry.id;
|
|
311
|
+
}
|
|
312
|
+
entry.queueName = this._queueName;
|
|
313
|
+
await this._queueDb.addEntry(entry);
|
|
612
314
|
}
|
|
613
|
-
|
|
614
|
-
this.
|
|
315
|
+
async popEntry() {
|
|
316
|
+
return this._removeEntry(await this._queueDb.getLastEntryByQueueName(this._queueName));
|
|
615
317
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
const removeIgnoredSearchParams = (urlObject, ignoreURLParametersMatching = [])=>{
|
|
619
|
-
for (const paramName of [
|
|
620
|
-
...urlObject.searchParams.keys()
|
|
621
|
-
]){
|
|
622
|
-
if (ignoreURLParametersMatching.some((regExp)=>regExp.test(paramName))) {
|
|
623
|
-
urlObject.searchParams.delete(paramName);
|
|
624
|
-
}
|
|
318
|
+
async shiftEntry() {
|
|
319
|
+
return this._removeEntry(await this._queueDb.getFirstEntryByQueueName(this._queueName));
|
|
625
320
|
}
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
function* generateURLVariations(url, { directoryIndex = "index.html", ignoreURLParametersMatching = [
|
|
630
|
-
/^utm_/,
|
|
631
|
-
/^fbclid$/
|
|
632
|
-
], cleanURLs = true, urlManipulation } = {}) {
|
|
633
|
-
const urlObject = new URL(url, location.href);
|
|
634
|
-
urlObject.hash = "";
|
|
635
|
-
yield urlObject.href;
|
|
636
|
-
const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching);
|
|
637
|
-
yield urlWithoutIgnoredParams.href;
|
|
638
|
-
if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith("/")) {
|
|
639
|
-
const directoryURL = new URL(urlWithoutIgnoredParams.href);
|
|
640
|
-
directoryURL.pathname += directoryIndex;
|
|
641
|
-
yield directoryURL.href;
|
|
321
|
+
async getAll() {
|
|
322
|
+
return await this._queueDb.getAllEntriesByQueueName(this._queueName);
|
|
642
323
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
cleanURL.pathname += ".html";
|
|
646
|
-
yield cleanURL.href;
|
|
324
|
+
async size() {
|
|
325
|
+
return await this._queueDb.getEntryCountByQueueName(this._queueName);
|
|
647
326
|
}
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
327
|
+
async deleteEntry(id) {
|
|
328
|
+
await this._queueDb.deleteEntry(id);
|
|
329
|
+
}
|
|
330
|
+
async _removeEntry(entry) {
|
|
331
|
+
if (entry) {
|
|
332
|
+
await this.deleteEntry(entry.id);
|
|
654
333
|
}
|
|
334
|
+
return entry;
|
|
655
335
|
}
|
|
656
336
|
}
|
|
657
337
|
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
}
|
|
675
|
-
return;
|
|
338
|
+
const serializableProperties = [
|
|
339
|
+
"method",
|
|
340
|
+
"referrer",
|
|
341
|
+
"referrerPolicy",
|
|
342
|
+
"mode",
|
|
343
|
+
"credentials",
|
|
344
|
+
"cache",
|
|
345
|
+
"redirect",
|
|
346
|
+
"integrity",
|
|
347
|
+
"keepalive"
|
|
348
|
+
];
|
|
349
|
+
class StorableRequest {
|
|
350
|
+
_requestData;
|
|
351
|
+
static async fromRequest(request) {
|
|
352
|
+
const requestData = {
|
|
353
|
+
url: request.url,
|
|
354
|
+
headers: {}
|
|
676
355
|
};
|
|
677
|
-
|
|
356
|
+
if (request.method !== "GET") {
|
|
357
|
+
requestData.body = await request.clone().arrayBuffer();
|
|
358
|
+
}
|
|
359
|
+
request.headers.forEach((value, key)=>{
|
|
360
|
+
requestData.headers[key] = value;
|
|
361
|
+
});
|
|
362
|
+
for (const prop of serializableProperties){
|
|
363
|
+
if (request[prop] !== undefined) {
|
|
364
|
+
requestData[prop] = request[prop];
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return new StorableRequest(requestData);
|
|
678
368
|
}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
369
|
+
constructor(requestData){
|
|
370
|
+
if (process.env.NODE_ENV !== "production") {
|
|
371
|
+
finalAssertExports.isType(requestData, "object", {
|
|
372
|
+
moduleName: "serwist",
|
|
373
|
+
className: "StorableRequest",
|
|
374
|
+
funcName: "constructor",
|
|
375
|
+
paramName: "requestData"
|
|
376
|
+
});
|
|
377
|
+
finalAssertExports.isType(requestData.url, "string", {
|
|
378
|
+
moduleName: "serwist",
|
|
379
|
+
className: "StorableRequest",
|
|
380
|
+
funcName: "constructor",
|
|
381
|
+
paramName: "requestData.url"
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
if (requestData.mode === "navigate") {
|
|
385
|
+
requestData.mode = "same-origin";
|
|
386
|
+
}
|
|
387
|
+
this._requestData = requestData;
|
|
687
388
|
}
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
return fallbackResponse;
|
|
694
|
-
}
|
|
695
|
-
} else if (fallback.matcher(param)) {
|
|
696
|
-
const fallbackResponse = await this._precacheController.matchPrecache(fallback.url);
|
|
697
|
-
if (fallbackResponse !== undefined) {
|
|
698
|
-
return fallbackResponse;
|
|
699
|
-
}
|
|
700
|
-
}
|
|
389
|
+
toObject() {
|
|
390
|
+
const requestData = Object.assign({}, this._requestData);
|
|
391
|
+
requestData.headers = Object.assign({}, this._requestData.headers);
|
|
392
|
+
if (requestData.body) {
|
|
393
|
+
requestData.body = requestData.body.slice(0);
|
|
701
394
|
}
|
|
702
|
-
return
|
|
395
|
+
return requestData;
|
|
396
|
+
}
|
|
397
|
+
toRequest() {
|
|
398
|
+
return new Request(this._requestData.url, this._requestData);
|
|
399
|
+
}
|
|
400
|
+
clone() {
|
|
401
|
+
return new StorableRequest(this.toObject());
|
|
703
402
|
}
|
|
704
403
|
}
|
|
705
404
|
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
405
|
+
const TAG_PREFIX = "serwist-background-sync";
|
|
406
|
+
const MAX_RETENTION_TIME$1 = 60 * 24 * 7;
|
|
407
|
+
const queueNames = new Set();
|
|
408
|
+
const convertEntry = (queueStoreEntry)=>{
|
|
409
|
+
const queueEntry = {
|
|
410
|
+
request: new StorableRequest(queueStoreEntry.requestData).toRequest(),
|
|
411
|
+
timestamp: queueStoreEntry.timestamp
|
|
412
|
+
};
|
|
413
|
+
if (queueStoreEntry.metadata) {
|
|
414
|
+
queueEntry.metadata = queueStoreEntry.metadata;
|
|
714
415
|
}
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
416
|
+
return queueEntry;
|
|
417
|
+
};
|
|
418
|
+
class BackgroundSyncQueue {
|
|
419
|
+
_name;
|
|
420
|
+
_onSync;
|
|
421
|
+
_maxRetentionTime;
|
|
422
|
+
_queueStore;
|
|
423
|
+
_forceSyncFallback;
|
|
424
|
+
_syncInProgress = false;
|
|
425
|
+
_requestsAddedDuringSync = false;
|
|
426
|
+
constructor(name, { forceSyncFallback, onSync, maxRetentionTime } = {}){
|
|
427
|
+
if (queueNames.has(name)) {
|
|
428
|
+
throw new SerwistError("duplicate-queue-name", {
|
|
429
|
+
name
|
|
725
430
|
});
|
|
726
431
|
}
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
432
|
+
queueNames.add(name);
|
|
433
|
+
this._name = name;
|
|
434
|
+
this._onSync = onSync || this.replayRequests;
|
|
435
|
+
this._maxRetentionTime = maxRetentionTime || MAX_RETENTION_TIME$1;
|
|
436
|
+
this._forceSyncFallback = Boolean(forceSyncFallback);
|
|
437
|
+
this._queueStore = new BackgroundSyncQueueStore(this._name);
|
|
438
|
+
this._addSyncListener();
|
|
730
439
|
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
440
|
+
get name() {
|
|
441
|
+
return this._name;
|
|
442
|
+
}
|
|
443
|
+
async pushRequest(entry) {
|
|
444
|
+
if (process.env.NODE_ENV !== "production") {
|
|
445
|
+
finalAssertExports.isType(entry, "object", {
|
|
446
|
+
moduleName: "serwist",
|
|
447
|
+
className: "BackgroundSyncQueue",
|
|
448
|
+
funcName: "pushRequest",
|
|
449
|
+
paramName: "entry"
|
|
450
|
+
});
|
|
451
|
+
finalAssertExports.isInstance(entry.request, Request, {
|
|
452
|
+
moduleName: "serwist",
|
|
453
|
+
className: "BackgroundSyncQueue",
|
|
454
|
+
funcName: "pushRequest",
|
|
455
|
+
paramName: "entry.request"
|
|
743
456
|
});
|
|
744
457
|
}
|
|
458
|
+
await this._addRequest(entry, "push");
|
|
745
459
|
}
|
|
746
|
-
|
|
747
|
-
if (!this._options.warmEntries) this._options.warmEntries = [];
|
|
748
|
-
this._options.warmEntries.push(...entries);
|
|
749
|
-
}
|
|
750
|
-
}
|
|
751
|
-
|
|
752
|
-
class NavigationRoute extends Route {
|
|
753
|
-
_allowlist;
|
|
754
|
-
_denylist;
|
|
755
|
-
constructor(handler, { allowlist = [
|
|
756
|
-
/./
|
|
757
|
-
], denylist = [] } = {}){
|
|
460
|
+
async unshiftRequest(entry) {
|
|
758
461
|
if (process.env.NODE_ENV !== "production") {
|
|
759
|
-
finalAssertExports.
|
|
462
|
+
finalAssertExports.isType(entry, "object", {
|
|
760
463
|
moduleName: "serwist",
|
|
761
|
-
className: "
|
|
762
|
-
funcName: "
|
|
763
|
-
paramName: "
|
|
464
|
+
className: "BackgroundSyncQueue",
|
|
465
|
+
funcName: "unshiftRequest",
|
|
466
|
+
paramName: "entry"
|
|
764
467
|
});
|
|
765
|
-
finalAssertExports.
|
|
468
|
+
finalAssertExports.isInstance(entry.request, Request, {
|
|
766
469
|
moduleName: "serwist",
|
|
767
|
-
className: "
|
|
768
|
-
funcName: "
|
|
769
|
-
paramName: "
|
|
470
|
+
className: "BackgroundSyncQueue",
|
|
471
|
+
funcName: "unshiftRequest",
|
|
472
|
+
paramName: "entry.request"
|
|
770
473
|
});
|
|
771
474
|
}
|
|
772
|
-
|
|
773
|
-
this._allowlist = allowlist;
|
|
774
|
-
this._denylist = denylist;
|
|
475
|
+
await this._addRequest(entry, "unshift");
|
|
775
476
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
477
|
+
async popRequest() {
|
|
478
|
+
return this._removeRequest("pop");
|
|
479
|
+
}
|
|
480
|
+
async shiftRequest() {
|
|
481
|
+
return this._removeRequest("shift");
|
|
482
|
+
}
|
|
483
|
+
async getAll() {
|
|
484
|
+
const allEntries = await this._queueStore.getAll();
|
|
485
|
+
const now = Date.now();
|
|
486
|
+
const unexpiredEntries = [];
|
|
487
|
+
for (const entry of allEntries){
|
|
488
|
+
const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000;
|
|
489
|
+
if (now - entry.timestamp > maxRetentionTimeInMs) {
|
|
490
|
+
await this._queueStore.deleteEntry(entry.id);
|
|
491
|
+
} else {
|
|
492
|
+
unexpiredEntries.push(convertEntry(entry));
|
|
493
|
+
}
|
|
779
494
|
}
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
495
|
+
return unexpiredEntries;
|
|
496
|
+
}
|
|
497
|
+
async size() {
|
|
498
|
+
return await this._queueStore.size();
|
|
499
|
+
}
|
|
500
|
+
async _addRequest({ request, metadata, timestamp = Date.now() }, operation) {
|
|
501
|
+
const storableRequest = await StorableRequest.fromRequest(request.clone());
|
|
502
|
+
const entry = {
|
|
503
|
+
requestData: storableRequest.toObject(),
|
|
504
|
+
timestamp
|
|
505
|
+
};
|
|
506
|
+
if (metadata) {
|
|
507
|
+
entry.metadata = metadata;
|
|
508
|
+
}
|
|
509
|
+
switch(operation){
|
|
510
|
+
case "push":
|
|
511
|
+
await this._queueStore.pushEntry(entry);
|
|
512
|
+
break;
|
|
513
|
+
case "unshift":
|
|
514
|
+
await this._queueStore.unshiftEntry(entry);
|
|
515
|
+
break;
|
|
516
|
+
}
|
|
517
|
+
if (process.env.NODE_ENV !== "production") {
|
|
518
|
+
logger.log(`Request for '${getFriendlyURL(request.url)}' has ` + `been added to background sync queue '${this._name}'.`);
|
|
519
|
+
}
|
|
520
|
+
if (this._syncInProgress) {
|
|
521
|
+
this._requestsAddedDuringSync = true;
|
|
522
|
+
} else {
|
|
523
|
+
await this.registerSync();
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
async _removeRequest(operation) {
|
|
527
|
+
const now = Date.now();
|
|
528
|
+
let entry;
|
|
529
|
+
switch(operation){
|
|
530
|
+
case "pop":
|
|
531
|
+
entry = await this._queueStore.popEntry();
|
|
532
|
+
break;
|
|
533
|
+
case "shift":
|
|
534
|
+
entry = await this._queueStore.shiftEntry();
|
|
535
|
+
break;
|
|
536
|
+
}
|
|
537
|
+
if (entry) {
|
|
538
|
+
const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000;
|
|
539
|
+
if (now - entry.timestamp > maxRetentionTimeInMs) {
|
|
540
|
+
return this._removeRequest(operation);
|
|
541
|
+
}
|
|
542
|
+
return convertEntry(entry);
|
|
543
|
+
}
|
|
544
|
+
return undefined;
|
|
545
|
+
}
|
|
546
|
+
async replayRequests() {
|
|
547
|
+
let entry;
|
|
548
|
+
while(entry = await this.shiftRequest()){
|
|
549
|
+
try {
|
|
550
|
+
await fetch(entry.request.clone());
|
|
783
551
|
if (process.env.NODE_ENV !== "production") {
|
|
784
|
-
logger.log(`
|
|
552
|
+
logger.log(`Request for '${getFriendlyURL(entry.request.url)}' ` + `has been replayed in queue '${this._name}'`);
|
|
553
|
+
}
|
|
554
|
+
} catch {
|
|
555
|
+
await this.unshiftRequest(entry);
|
|
556
|
+
if (process.env.NODE_ENV !== "production") {
|
|
557
|
+
logger.log(`Request for '${getFriendlyURL(entry.request.url)}' ` + `failed to replay, putting it back in queue '${this._name}'`);
|
|
558
|
+
}
|
|
559
|
+
throw new SerwistError("queue-replay-failed", {
|
|
560
|
+
name: this._name
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
if (process.env.NODE_ENV !== "production") {
|
|
565
|
+
logger.log(`All requests in queue '${this.name}' have successfully replayed; the queue is now empty!`);
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
async registerSync() {
|
|
569
|
+
if ("sync" in self.registration && !this._forceSyncFallback) {
|
|
570
|
+
try {
|
|
571
|
+
await self.registration.sync.register(`${TAG_PREFIX}:${this._name}`);
|
|
572
|
+
} catch (err) {
|
|
573
|
+
if (process.env.NODE_ENV !== "production") {
|
|
574
|
+
logger.warn(`Unable to register sync event for '${this._name}'.`, err);
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
_addSyncListener() {
|
|
580
|
+
if ("sync" in self.registration && !this._forceSyncFallback) {
|
|
581
|
+
self.addEventListener("sync", (event)=>{
|
|
582
|
+
if (event.tag === `${TAG_PREFIX}:${this._name}`) {
|
|
583
|
+
if (process.env.NODE_ENV !== "production") {
|
|
584
|
+
logger.log(`Background sync for tag '${event.tag}' has been received`);
|
|
585
|
+
}
|
|
586
|
+
const syncComplete = async ()=>{
|
|
587
|
+
this._syncInProgress = true;
|
|
588
|
+
let syncError;
|
|
589
|
+
try {
|
|
590
|
+
await this._onSync({
|
|
591
|
+
queue: this
|
|
592
|
+
});
|
|
593
|
+
} catch (error) {
|
|
594
|
+
if (error instanceof Error) {
|
|
595
|
+
syncError = error;
|
|
596
|
+
throw syncError;
|
|
597
|
+
}
|
|
598
|
+
} finally{
|
|
599
|
+
if (this._requestsAddedDuringSync && !(syncError && !event.lastChance)) {
|
|
600
|
+
await this.registerSync();
|
|
601
|
+
}
|
|
602
|
+
this._syncInProgress = false;
|
|
603
|
+
this._requestsAddedDuringSync = false;
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
event.waitUntil(syncComplete());
|
|
785
607
|
}
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
}
|
|
789
|
-
if (this._allowlist.some((regExp)=>regExp.test(pathnameAndSearch))) {
|
|
608
|
+
});
|
|
609
|
+
} else {
|
|
790
610
|
if (process.env.NODE_ENV !== "production") {
|
|
791
|
-
logger.
|
|
611
|
+
logger.log("Background sync replaying without background sync event");
|
|
792
612
|
}
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
logger.log(`The navigation route ${pathnameAndSearch} is not being used, since the URL being navigated to doesn't match the allowlist.`);
|
|
613
|
+
void this._onSync({
|
|
614
|
+
queue: this
|
|
615
|
+
});
|
|
797
616
|
}
|
|
798
|
-
|
|
617
|
+
}
|
|
618
|
+
static get _queueNames() {
|
|
619
|
+
return queueNames;
|
|
799
620
|
}
|
|
800
621
|
}
|
|
801
622
|
|
|
802
|
-
class
|
|
803
|
-
|
|
623
|
+
class BackgroundSyncPlugin {
|
|
624
|
+
_queue;
|
|
625
|
+
constructor(name, options){
|
|
626
|
+
this._queue = new BackgroundSyncQueue(name, options);
|
|
627
|
+
}
|
|
628
|
+
async fetchDidFail({ request }) {
|
|
629
|
+
await this._queue.pushRequest({
|
|
630
|
+
request
|
|
631
|
+
});
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const parseRoute = (capture, handler, method)=>{
|
|
636
|
+
if (typeof capture === "string") {
|
|
637
|
+
const captureUrl = new URL(capture, location.href);
|
|
804
638
|
if (process.env.NODE_ENV !== "production") {
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
}
|
|
812
|
-
const match = ({ url })=>{
|
|
813
|
-
const result = regExp.exec(url.href);
|
|
814
|
-
if (!result) {
|
|
815
|
-
return;
|
|
639
|
+
if (!(capture.startsWith("/") || capture.startsWith("http"))) {
|
|
640
|
+
throw new SerwistError("invalid-string", {
|
|
641
|
+
moduleName: "serwist",
|
|
642
|
+
funcName: "parseRoute",
|
|
643
|
+
paramName: "capture"
|
|
644
|
+
});
|
|
816
645
|
}
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
646
|
+
const valueToCheck = capture.startsWith("http") ? captureUrl.pathname : capture;
|
|
647
|
+
const wildcards = "[*:?+]";
|
|
648
|
+
if (new RegExp(`${wildcards}`).exec(valueToCheck)) {
|
|
649
|
+
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.`);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
const matchCallback = ({ url })=>{
|
|
653
|
+
if (process.env.NODE_ENV !== "production") {
|
|
654
|
+
if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) {
|
|
655
|
+
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.`);
|
|
820
656
|
}
|
|
821
|
-
return;
|
|
822
657
|
}
|
|
823
|
-
return
|
|
658
|
+
return url.href === captureUrl.href;
|
|
824
659
|
};
|
|
825
|
-
|
|
660
|
+
return new Route(matchCallback, handler, method);
|
|
826
661
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
662
|
+
if (capture instanceof RegExp) {
|
|
663
|
+
return new RegExpRoute(capture, handler, method);
|
|
664
|
+
}
|
|
665
|
+
if (typeof capture === "function") {
|
|
666
|
+
return new Route(capture, handler, method);
|
|
667
|
+
}
|
|
668
|
+
if (capture instanceof Route) {
|
|
669
|
+
return capture;
|
|
670
|
+
}
|
|
671
|
+
throw new SerwistError("unsupported-route-type", {
|
|
672
|
+
moduleName: "serwist",
|
|
673
|
+
funcName: "parseRoute",
|
|
674
|
+
paramName: "capture"
|
|
675
|
+
});
|
|
831
676
|
};
|
|
832
677
|
|
|
833
|
-
const
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
678
|
+
const registerCapture = (state, capture, handler, method)=>{
|
|
679
|
+
const route = parseRoute(capture, handler, method);
|
|
680
|
+
registerRoute(state, route);
|
|
681
|
+
return route;
|
|
682
|
+
};
|
|
683
|
+
const registerRoute = (state, route)=>{
|
|
684
|
+
if (process.env.NODE_ENV !== "production") {
|
|
685
|
+
finalAssertExports.isType(route, "object", {
|
|
686
|
+
moduleName: "serwist",
|
|
687
|
+
className: "Serwist",
|
|
688
|
+
funcName: "registerRoute",
|
|
689
|
+
paramName: "route"
|
|
690
|
+
});
|
|
691
|
+
finalAssertExports.hasMethod(route, "match", {
|
|
692
|
+
moduleName: "serwist",
|
|
693
|
+
className: "Serwist",
|
|
694
|
+
funcName: "registerRoute",
|
|
695
|
+
paramName: "route"
|
|
696
|
+
});
|
|
697
|
+
finalAssertExports.isType(route.handler, "object", {
|
|
698
|
+
moduleName: "serwist",
|
|
699
|
+
className: "Serwist",
|
|
700
|
+
funcName: "registerRoute",
|
|
701
|
+
paramName: "route"
|
|
702
|
+
});
|
|
703
|
+
finalAssertExports.hasMethod(route.handler, "handle", {
|
|
704
|
+
moduleName: "serwist",
|
|
705
|
+
className: "Serwist",
|
|
706
|
+
funcName: "registerRoute",
|
|
707
|
+
paramName: "route.handler"
|
|
708
|
+
});
|
|
709
|
+
finalAssertExports.isType(route.method, "string", {
|
|
710
|
+
moduleName: "serwist",
|
|
711
|
+
className: "Serwist",
|
|
712
|
+
funcName: "registerRoute",
|
|
713
|
+
paramName: "route.method"
|
|
714
|
+
});
|
|
715
|
+
}
|
|
716
|
+
if (!state.routes.has(route.method)) {
|
|
717
|
+
state.routes.set(route.method, []);
|
|
839
718
|
}
|
|
719
|
+
state.routes.get(route.method).push(route);
|
|
840
720
|
};
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
721
|
+
const unregisterRoute = (state, route)=>{
|
|
722
|
+
if (!state.routes.has(route.method)) {
|
|
723
|
+
throw new SerwistError("unregister-route-but-not-found-with-method", {
|
|
724
|
+
method: route.method
|
|
725
|
+
});
|
|
726
|
+
}
|
|
727
|
+
const routeIndex = state.routes.get(route.method).indexOf(route);
|
|
728
|
+
if (routeIndex > -1) {
|
|
729
|
+
state.routes.get(route.method).splice(routeIndex, 1);
|
|
730
|
+
} else {
|
|
731
|
+
throw new SerwistError("unregister-route-route-not-registered");
|
|
850
732
|
}
|
|
851
733
|
};
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
}
|
|
860
|
-
|
|
734
|
+
const handleRequest = (state, { request, event })=>{
|
|
735
|
+
if (process.env.NODE_ENV !== "production") {
|
|
736
|
+
finalAssertExports.isInstance(request, Request, {
|
|
737
|
+
moduleName: "serwist",
|
|
738
|
+
className: "Serwist",
|
|
739
|
+
funcName: "handleRequest",
|
|
740
|
+
paramName: "options.request"
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
const url = new URL(request.url, location.href);
|
|
744
|
+
if (!url.protocol.startsWith("http")) {
|
|
861
745
|
if (process.env.NODE_ENV !== "production") {
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
746
|
+
logger.debug("Router only supports URLs that start with 'http'.");
|
|
747
|
+
}
|
|
748
|
+
return;
|
|
749
|
+
}
|
|
750
|
+
const sameOrigin = url.origin === location.origin;
|
|
751
|
+
const { params, route } = findMatchingRoute(state, {
|
|
752
|
+
event,
|
|
753
|
+
request,
|
|
754
|
+
sameOrigin,
|
|
755
|
+
url
|
|
756
|
+
});
|
|
757
|
+
let handler = route?.handler;
|
|
758
|
+
const debugMessages = [];
|
|
759
|
+
if (process.env.NODE_ENV !== "production") {
|
|
760
|
+
if (handler) {
|
|
761
|
+
debugMessages.push([
|
|
762
|
+
"Found a route to handle this request:",
|
|
763
|
+
route
|
|
764
|
+
]);
|
|
765
|
+
if (params) {
|
|
766
|
+
debugMessages.push([
|
|
767
|
+
`Passing the following params to the route's handler:`,
|
|
768
|
+
params
|
|
769
|
+
]);
|
|
869
770
|
}
|
|
870
771
|
}
|
|
871
772
|
}
|
|
872
|
-
|
|
873
|
-
|
|
773
|
+
const method = request.method;
|
|
774
|
+
if (!handler && state.defaultHandlerMap.has(method)) {
|
|
874
775
|
if (process.env.NODE_ENV !== "production") {
|
|
875
|
-
|
|
876
|
-
moduleName: "serwist",
|
|
877
|
-
className: this.constructor.name,
|
|
878
|
-
funcName: "handle",
|
|
879
|
-
paramName: "makeRequest"
|
|
880
|
-
});
|
|
881
|
-
}
|
|
882
|
-
const promises = [];
|
|
883
|
-
let timeoutId;
|
|
884
|
-
if (this._networkTimeoutSeconds) {
|
|
885
|
-
const { id, promise } = this._getTimeoutPromise({
|
|
886
|
-
request,
|
|
887
|
-
logs,
|
|
888
|
-
handler
|
|
889
|
-
});
|
|
890
|
-
timeoutId = id;
|
|
891
|
-
promises.push(promise);
|
|
776
|
+
debugMessages.push(`Failed to find a matching route. Falling back to the default handler for ${method}.`);
|
|
892
777
|
}
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
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
|
-
})());
|
|
778
|
+
handler = state.defaultHandlerMap.get(method);
|
|
779
|
+
}
|
|
780
|
+
if (!handler) {
|
|
903
781
|
if (process.env.NODE_ENV !== "production") {
|
|
904
|
-
logger.
|
|
905
|
-
for (const log of logs){
|
|
906
|
-
logger.log(log);
|
|
907
|
-
}
|
|
908
|
-
messages.printFinalResponse(response);
|
|
909
|
-
logger.groupEnd();
|
|
782
|
+
logger.debug(`No route found for: ${getFriendlyURL(url)}`);
|
|
910
783
|
}
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
if (process.env.NODE_ENV !== "production") {
|
|
787
|
+
logger.groupCollapsed(`Router is responding to: ${getFriendlyURL(url)}`);
|
|
788
|
+
for (const msg of debugMessages){
|
|
789
|
+
if (Array.isArray(msg)) {
|
|
790
|
+
logger.log(...msg);
|
|
791
|
+
} else {
|
|
792
|
+
logger.log(msg);
|
|
793
|
+
}
|
|
915
794
|
}
|
|
916
|
-
|
|
795
|
+
logger.groupEnd();
|
|
917
796
|
}
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
resolve(await handler.cacheMatch(request));
|
|
926
|
-
};
|
|
927
|
-
timeoutId = setTimeout(onNetworkTimeout, this._networkTimeoutSeconds * 1000);
|
|
797
|
+
let responsePromise;
|
|
798
|
+
try {
|
|
799
|
+
responsePromise = handler.handle({
|
|
800
|
+
url,
|
|
801
|
+
request,
|
|
802
|
+
event,
|
|
803
|
+
params
|
|
928
804
|
});
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
id: timeoutId
|
|
932
|
-
};
|
|
805
|
+
} catch (err) {
|
|
806
|
+
responsePromise = Promise.reject(err);
|
|
933
807
|
}
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
808
|
+
const catchHandler = route?.catchHandler;
|
|
809
|
+
if (responsePromise instanceof Promise && (state.catchHandler || catchHandler)) {
|
|
810
|
+
responsePromise = responsePromise.catch(async (err)=>{
|
|
811
|
+
if (catchHandler) {
|
|
812
|
+
if (process.env.NODE_ENV !== "production") {
|
|
813
|
+
logger.groupCollapsed(`Error thrown when responding to: ${getFriendlyURL(url)}. Falling back to route's Catch Handler.`);
|
|
814
|
+
logger.error("Error thrown by:", route);
|
|
815
|
+
logger.error(err);
|
|
816
|
+
logger.groupEnd();
|
|
817
|
+
}
|
|
818
|
+
try {
|
|
819
|
+
return await catchHandler.handle({
|
|
820
|
+
url,
|
|
821
|
+
request,
|
|
822
|
+
event,
|
|
823
|
+
params
|
|
824
|
+
});
|
|
825
|
+
} catch (catchErr) {
|
|
826
|
+
if (catchErr instanceof Error) {
|
|
827
|
+
err = catchErr;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
942
830
|
}
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
831
|
+
if (state.catchHandler) {
|
|
832
|
+
if (process.env.NODE_ENV !== "production") {
|
|
833
|
+
logger.groupCollapsed(`Error thrown when responding to: ${getFriendlyURL(url)}. Falling back to global Catch Handler.`);
|
|
834
|
+
logger.error("Error thrown by:", route);
|
|
835
|
+
logger.error(err);
|
|
836
|
+
logger.groupEnd();
|
|
837
|
+
}
|
|
838
|
+
return state.catchHandler.handle({
|
|
839
|
+
url,
|
|
840
|
+
request,
|
|
841
|
+
event
|
|
842
|
+
});
|
|
952
843
|
}
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
844
|
+
throw err;
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
return responsePromise;
|
|
848
|
+
};
|
|
849
|
+
const findMatchingRoute = (state, { url, sameOrigin, request, event })=>{
|
|
850
|
+
const routes = state.routes.get(request.method) || [];
|
|
851
|
+
for (const route of routes){
|
|
852
|
+
let params;
|
|
853
|
+
const matchResult = route.match({
|
|
854
|
+
url,
|
|
855
|
+
sameOrigin,
|
|
856
|
+
request,
|
|
857
|
+
event
|
|
858
|
+
});
|
|
859
|
+
if (matchResult) {
|
|
956
860
|
if (process.env.NODE_ENV !== "production") {
|
|
957
|
-
if (
|
|
958
|
-
|
|
959
|
-
} else {
|
|
960
|
-
logs.push(`No response found in the '${this.cacheName}' cache.`);
|
|
861
|
+
if (matchResult instanceof Promise) {
|
|
862
|
+
logger.warn(`While routing ${getFriendlyURL(url)}, an async matchCallback function was used. Please convert the following route to use a synchronous matchCallback function:`, route);
|
|
961
863
|
}
|
|
962
864
|
}
|
|
865
|
+
params = matchResult;
|
|
866
|
+
if (Array.isArray(params) && params.length === 0) {
|
|
867
|
+
params = undefined;
|
|
868
|
+
} else if (matchResult.constructor === Object && Object.keys(matchResult).length === 0) {
|
|
869
|
+
params = undefined;
|
|
870
|
+
} else if (typeof matchResult === "boolean") {
|
|
871
|
+
params = undefined;
|
|
872
|
+
}
|
|
873
|
+
return {
|
|
874
|
+
route,
|
|
875
|
+
params
|
|
876
|
+
};
|
|
963
877
|
}
|
|
964
|
-
return response;
|
|
965
878
|
}
|
|
966
|
-
}
|
|
879
|
+
return {};
|
|
880
|
+
};
|
|
967
881
|
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
882
|
+
const cacheOkAndOpaquePlugin = {
|
|
883
|
+
cacheWillUpdate: async ({ response })=>{
|
|
884
|
+
if (response.status === 200 || response.status === 0) {
|
|
885
|
+
return response;
|
|
886
|
+
}
|
|
887
|
+
return null;
|
|
973
888
|
}
|
|
974
|
-
|
|
889
|
+
};
|
|
890
|
+
|
|
891
|
+
function toRequest(input) {
|
|
892
|
+
return typeof input === "string" ? new Request(input) : input;
|
|
893
|
+
}
|
|
894
|
+
class StrategyHandler {
|
|
895
|
+
event;
|
|
896
|
+
request;
|
|
897
|
+
url;
|
|
898
|
+
params;
|
|
899
|
+
_cacheKeys = {};
|
|
900
|
+
_strategy;
|
|
901
|
+
_handlerDeferred;
|
|
902
|
+
_extendLifetimePromises;
|
|
903
|
+
_plugins;
|
|
904
|
+
_pluginStateMap;
|
|
905
|
+
constructor(strategy, options){
|
|
975
906
|
if (process.env.NODE_ENV !== "production") {
|
|
976
|
-
finalAssertExports.isInstance(
|
|
907
|
+
finalAssertExports.isInstance(options.event, ExtendableEvent, {
|
|
977
908
|
moduleName: "serwist",
|
|
978
|
-
className:
|
|
979
|
-
funcName: "
|
|
980
|
-
paramName: "
|
|
909
|
+
className: "StrategyHandler",
|
|
910
|
+
funcName: "constructor",
|
|
911
|
+
paramName: "options.event"
|
|
912
|
+
});
|
|
913
|
+
finalAssertExports.isInstance(options.request, Request, {
|
|
914
|
+
moduleName: "serwist",
|
|
915
|
+
className: "StrategyHandler",
|
|
916
|
+
funcName: "constructor",
|
|
917
|
+
paramName: "options.request"
|
|
981
918
|
});
|
|
982
919
|
}
|
|
983
|
-
|
|
984
|
-
|
|
920
|
+
this.event = options.event;
|
|
921
|
+
this.request = options.request;
|
|
922
|
+
if (options.url) {
|
|
923
|
+
this.url = options.url;
|
|
924
|
+
this.params = options.params;
|
|
925
|
+
}
|
|
926
|
+
this._strategy = strategy;
|
|
927
|
+
this._handlerDeferred = new Deferred();
|
|
928
|
+
this._extendLifetimePromises = [];
|
|
929
|
+
this._plugins = [
|
|
930
|
+
...strategy.plugins
|
|
931
|
+
];
|
|
932
|
+
this._pluginStateMap = new Map();
|
|
933
|
+
for (const plugin of this._plugins){
|
|
934
|
+
this._pluginStateMap.set(plugin, {});
|
|
935
|
+
}
|
|
936
|
+
this.event.waitUntil(this._handlerDeferred.promise);
|
|
937
|
+
}
|
|
938
|
+
async fetch(input) {
|
|
939
|
+
const { event } = this;
|
|
940
|
+
let request = toRequest(input);
|
|
941
|
+
const preloadResponse = await this.getPreloadResponse();
|
|
942
|
+
if (preloadResponse) {
|
|
943
|
+
return preloadResponse;
|
|
944
|
+
}
|
|
945
|
+
const originalRequest = this.hasCallback("fetchDidFail") ? request.clone() : null;
|
|
985
946
|
try {
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
promises.push(timeoutPromise);
|
|
992
|
-
}
|
|
993
|
-
response = await Promise.race(promises);
|
|
994
|
-
if (!response) {
|
|
995
|
-
throw new Error(`Timed out the network response after ${this._networkTimeoutSeconds} seconds.`);
|
|
947
|
+
for (const cb of this.iterateCallbacks("requestWillFetch")){
|
|
948
|
+
request = await cb({
|
|
949
|
+
request: request.clone(),
|
|
950
|
+
event
|
|
951
|
+
});
|
|
996
952
|
}
|
|
997
953
|
} catch (err) {
|
|
998
954
|
if (err instanceof Error) {
|
|
999
|
-
error
|
|
955
|
+
throw new SerwistError("plugin-error-request-will-fetch", {
|
|
956
|
+
thrownErrorMessage: err.message
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
const pluginFilteredRequest = request.clone();
|
|
961
|
+
try {
|
|
962
|
+
let fetchResponse;
|
|
963
|
+
fetchResponse = await fetch(request, request.mode === "navigate" ? undefined : this._strategy.fetchOptions);
|
|
964
|
+
if (process.env.NODE_ENV !== "production") {
|
|
965
|
+
logger.debug(`Network request for '${getFriendlyURL(request.url)}' returned a response with status '${fetchResponse.status}'.`);
|
|
966
|
+
}
|
|
967
|
+
for (const callback of this.iterateCallbacks("fetchDidSucceed")){
|
|
968
|
+
fetchResponse = await callback({
|
|
969
|
+
event,
|
|
970
|
+
request: pluginFilteredRequest,
|
|
971
|
+
response: fetchResponse
|
|
972
|
+
});
|
|
973
|
+
}
|
|
974
|
+
return fetchResponse;
|
|
975
|
+
} catch (error) {
|
|
976
|
+
if (process.env.NODE_ENV !== "production") {
|
|
977
|
+
logger.log(`Network request for '${getFriendlyURL(request.url)}' threw an error.`, error);
|
|
978
|
+
}
|
|
979
|
+
if (originalRequest) {
|
|
980
|
+
await this.runCallbacks("fetchDidFail", {
|
|
981
|
+
error: error,
|
|
982
|
+
event,
|
|
983
|
+
originalRequest: originalRequest.clone(),
|
|
984
|
+
request: pluginFilteredRequest.clone()
|
|
985
|
+
});
|
|
1000
986
|
}
|
|
987
|
+
throw error;
|
|
1001
988
|
}
|
|
989
|
+
}
|
|
990
|
+
async fetchAndCachePut(input) {
|
|
991
|
+
const response = await this.fetch(input);
|
|
992
|
+
const responseClone = response.clone();
|
|
993
|
+
void this.waitUntil(this.cachePut(input, responseClone));
|
|
994
|
+
return response;
|
|
995
|
+
}
|
|
996
|
+
async cacheMatch(key) {
|
|
997
|
+
const request = toRequest(key);
|
|
998
|
+
let cachedResponse;
|
|
999
|
+
const { cacheName, matchOptions } = this._strategy;
|
|
1000
|
+
const effectiveRequest = await this.getCacheKey(request, "read");
|
|
1001
|
+
const multiMatchOptions = {
|
|
1002
|
+
...matchOptions,
|
|
1003
|
+
...{
|
|
1004
|
+
cacheName
|
|
1005
|
+
}
|
|
1006
|
+
};
|
|
1007
|
+
cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);
|
|
1002
1008
|
if (process.env.NODE_ENV !== "production") {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
logger.log("Got response from network.");
|
|
1009
|
+
if (cachedResponse) {
|
|
1010
|
+
logger.debug(`Found a cached response in '${cacheName}'.`);
|
|
1006
1011
|
} else {
|
|
1007
|
-
logger.
|
|
1012
|
+
logger.debug(`No cached response found in '${cacheName}'.`);
|
|
1008
1013
|
}
|
|
1009
|
-
messages.printFinalResponse(response);
|
|
1010
|
-
logger.groupEnd();
|
|
1011
1014
|
}
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1015
|
+
for (const callback of this.iterateCallbacks("cachedResponseWillBeUsed")){
|
|
1016
|
+
cachedResponse = await callback({
|
|
1017
|
+
cacheName,
|
|
1018
|
+
matchOptions,
|
|
1019
|
+
cachedResponse,
|
|
1020
|
+
request: effectiveRequest,
|
|
1021
|
+
event: this.event
|
|
1022
|
+
}) || undefined;
|
|
1017
1023
|
}
|
|
1018
|
-
return
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
|
|
1022
|
-
const BACKGROUND_SYNC_DB_VERSION = 3;
|
|
1023
|
-
const BACKGROUND_SYNC_DB_NAME = "serwist-background-sync";
|
|
1024
|
-
const REQUEST_OBJECT_STORE_NAME = "requests";
|
|
1025
|
-
const QUEUE_NAME_INDEX = "queueName";
|
|
1026
|
-
class BackgroundSyncQueueDb {
|
|
1027
|
-
_db = null;
|
|
1028
|
-
async addEntry(entry) {
|
|
1029
|
-
const db = await this.getDb();
|
|
1030
|
-
const tx = db.transaction(REQUEST_OBJECT_STORE_NAME, "readwrite", {
|
|
1031
|
-
durability: "relaxed"
|
|
1032
|
-
});
|
|
1033
|
-
await tx.store.add(entry);
|
|
1034
|
-
await tx.done;
|
|
1035
|
-
}
|
|
1036
|
-
async getFirstEntryId() {
|
|
1037
|
-
const db = await this.getDb();
|
|
1038
|
-
const cursor = await db.transaction(REQUEST_OBJECT_STORE_NAME).store.openCursor();
|
|
1039
|
-
return cursor?.value.id;
|
|
1040
|
-
}
|
|
1041
|
-
async getAllEntriesByQueueName(queueName) {
|
|
1042
|
-
const db = await this.getDb();
|
|
1043
|
-
const results = await db.getAllFromIndex(REQUEST_OBJECT_STORE_NAME, QUEUE_NAME_INDEX, IDBKeyRange.only(queueName));
|
|
1044
|
-
return results ? results : new Array();
|
|
1045
|
-
}
|
|
1046
|
-
async getEntryCountByQueueName(queueName) {
|
|
1047
|
-
const db = await this.getDb();
|
|
1048
|
-
return db.countFromIndex(REQUEST_OBJECT_STORE_NAME, QUEUE_NAME_INDEX, IDBKeyRange.only(queueName));
|
|
1049
|
-
}
|
|
1050
|
-
async deleteEntry(id) {
|
|
1051
|
-
const db = await this.getDb();
|
|
1052
|
-
await db.delete(REQUEST_OBJECT_STORE_NAME, id);
|
|
1053
|
-
}
|
|
1054
|
-
async getFirstEntryByQueueName(queueName) {
|
|
1055
|
-
return await this.getEndEntryFromIndex(IDBKeyRange.only(queueName), "next");
|
|
1056
|
-
}
|
|
1057
|
-
async getLastEntryByQueueName(queueName) {
|
|
1058
|
-
return await this.getEndEntryFromIndex(IDBKeyRange.only(queueName), "prev");
|
|
1059
|
-
}
|
|
1060
|
-
async getEndEntryFromIndex(query, direction) {
|
|
1061
|
-
const db = await this.getDb();
|
|
1062
|
-
const cursor = await db.transaction(REQUEST_OBJECT_STORE_NAME).store.index(QUEUE_NAME_INDEX).openCursor(query, direction);
|
|
1063
|
-
return cursor?.value;
|
|
1024
|
+
return cachedResponse;
|
|
1064
1025
|
}
|
|
1065
|
-
async
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1026
|
+
async cachePut(key, response) {
|
|
1027
|
+
const request = toRequest(key);
|
|
1028
|
+
await timeout(0);
|
|
1029
|
+
const effectiveRequest = await this.getCacheKey(request, "write");
|
|
1030
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1031
|
+
if (effectiveRequest.method && effectiveRequest.method !== "GET") {
|
|
1032
|
+
throw new SerwistError("attempt-to-cache-non-get-request", {
|
|
1033
|
+
url: getFriendlyURL(effectiveRequest.url),
|
|
1034
|
+
method: effectiveRequest.method
|
|
1035
|
+
});
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
if (!response) {
|
|
1039
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1040
|
+
logger.error(`Cannot cache non-existent response for '${getFriendlyURL(effectiveRequest.url)}'.`);
|
|
1041
|
+
}
|
|
1042
|
+
throw new SerwistError("cache-put-with-no-response", {
|
|
1043
|
+
url: getFriendlyURL(effectiveRequest.url)
|
|
1069
1044
|
});
|
|
1070
1045
|
}
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1046
|
+
const responseToCache = await this._ensureResponseSafeToCache(response);
|
|
1047
|
+
if (!responseToCache) {
|
|
1048
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1049
|
+
logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' will not be cached.`, responseToCache);
|
|
1050
|
+
}
|
|
1051
|
+
return false;
|
|
1052
|
+
}
|
|
1053
|
+
const { cacheName, matchOptions } = this._strategy;
|
|
1054
|
+
const cache = await self.caches.open(cacheName);
|
|
1055
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1056
|
+
const vary = response.headers.get("Vary");
|
|
1057
|
+
if (vary && matchOptions?.ignoreVary !== true) {
|
|
1058
|
+
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.`);
|
|
1077
1059
|
}
|
|
1078
1060
|
}
|
|
1079
|
-
const
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
objStore.createIndex(QUEUE_NAME_INDEX, QUEUE_NAME_INDEX, {
|
|
1084
|
-
unique: false
|
|
1085
|
-
});
|
|
1086
|
-
}
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
class BackgroundSyncQueueStore {
|
|
1090
|
-
_queueName;
|
|
1091
|
-
_queueDb;
|
|
1092
|
-
constructor(queueName){
|
|
1093
|
-
this._queueName = queueName;
|
|
1094
|
-
this._queueDb = new BackgroundSyncQueueDb();
|
|
1095
|
-
}
|
|
1096
|
-
async pushEntry(entry) {
|
|
1061
|
+
const hasCacheUpdateCallback = this.hasCallback("cacheDidUpdate");
|
|
1062
|
+
const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams(cache, effectiveRequest.clone(), [
|
|
1063
|
+
"__WB_REVISION__"
|
|
1064
|
+
], matchOptions) : null;
|
|
1097
1065
|
if (process.env.NODE_ENV !== "production") {
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1066
|
+
logger.debug(`Updating the '${cacheName}' cache with a new Response for ${getFriendlyURL(effectiveRequest.url)}.`);
|
|
1067
|
+
}
|
|
1068
|
+
try {
|
|
1069
|
+
await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);
|
|
1070
|
+
} catch (error) {
|
|
1071
|
+
if (error instanceof Error) {
|
|
1072
|
+
if (error.name === "QuotaExceededError") {
|
|
1073
|
+
await executeQuotaErrorCallbacks();
|
|
1074
|
+
}
|
|
1075
|
+
throw error;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
for (const callback of this.iterateCallbacks("cacheDidUpdate")){
|
|
1079
|
+
await callback({
|
|
1080
|
+
cacheName,
|
|
1081
|
+
oldResponse,
|
|
1082
|
+
newResponse: responseToCache.clone(),
|
|
1083
|
+
request: effectiveRequest,
|
|
1084
|
+
event: this.event
|
|
1109
1085
|
});
|
|
1110
1086
|
}
|
|
1111
|
-
|
|
1112
|
-
entry.queueName = this._queueName;
|
|
1113
|
-
await this._queueDb.addEntry(entry);
|
|
1087
|
+
return true;
|
|
1114
1088
|
}
|
|
1115
|
-
async
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
});
|
|
1089
|
+
async getCacheKey(request, mode) {
|
|
1090
|
+
const key = `${request.url} | ${mode}`;
|
|
1091
|
+
if (!this._cacheKeys[key]) {
|
|
1092
|
+
let effectiveRequest = request;
|
|
1093
|
+
for (const callback of this.iterateCallbacks("cacheKeyWillBeUsed")){
|
|
1094
|
+
effectiveRequest = toRequest(await callback({
|
|
1095
|
+
mode,
|
|
1096
|
+
request: effectiveRequest,
|
|
1097
|
+
event: this.event,
|
|
1098
|
+
params: this.params
|
|
1099
|
+
}));
|
|
1100
|
+
}
|
|
1101
|
+
this._cacheKeys[key] = effectiveRequest;
|
|
1129
1102
|
}
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1103
|
+
return this._cacheKeys[key];
|
|
1104
|
+
}
|
|
1105
|
+
hasCallback(name) {
|
|
1106
|
+
for (const plugin of this._strategy.plugins){
|
|
1107
|
+
if (name in plugin) {
|
|
1108
|
+
return true;
|
|
1109
|
+
}
|
|
1135
1110
|
}
|
|
1136
|
-
|
|
1137
|
-
await this._queueDb.addEntry(entry);
|
|
1111
|
+
return false;
|
|
1138
1112
|
}
|
|
1139
|
-
async
|
|
1140
|
-
|
|
1113
|
+
async runCallbacks(name, param) {
|
|
1114
|
+
for (const callback of this.iterateCallbacks(name)){
|
|
1115
|
+
await callback(param);
|
|
1116
|
+
}
|
|
1141
1117
|
}
|
|
1142
|
-
|
|
1143
|
-
|
|
1118
|
+
*iterateCallbacks(name) {
|
|
1119
|
+
for (const plugin of this._strategy.plugins){
|
|
1120
|
+
if (typeof plugin[name] === "function") {
|
|
1121
|
+
const state = this._pluginStateMap.get(plugin);
|
|
1122
|
+
const statefulCallback = (param)=>{
|
|
1123
|
+
const statefulParam = {
|
|
1124
|
+
...param,
|
|
1125
|
+
state
|
|
1126
|
+
};
|
|
1127
|
+
return plugin[name](statefulParam);
|
|
1128
|
+
};
|
|
1129
|
+
yield statefulCallback;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
1144
1132
|
}
|
|
1145
|
-
|
|
1146
|
-
|
|
1133
|
+
waitUntil(promise) {
|
|
1134
|
+
this._extendLifetimePromises.push(promise);
|
|
1135
|
+
return promise;
|
|
1147
1136
|
}
|
|
1148
|
-
async
|
|
1149
|
-
|
|
1137
|
+
async doneWaiting() {
|
|
1138
|
+
let promise;
|
|
1139
|
+
while(promise = this._extendLifetimePromises.shift()){
|
|
1140
|
+
await promise;
|
|
1141
|
+
}
|
|
1150
1142
|
}
|
|
1151
|
-
|
|
1152
|
-
|
|
1143
|
+
destroy() {
|
|
1144
|
+
this._handlerDeferred.resolve(null);
|
|
1153
1145
|
}
|
|
1154
|
-
async
|
|
1155
|
-
if (
|
|
1156
|
-
|
|
1146
|
+
async getPreloadResponse() {
|
|
1147
|
+
if (this.event instanceof FetchEvent && this.event.request.mode === "navigate" && "preloadResponse" in this.event) {
|
|
1148
|
+
try {
|
|
1149
|
+
const possiblePreloadResponse = await this.event.preloadResponse;
|
|
1150
|
+
if (possiblePreloadResponse) {
|
|
1151
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1152
|
+
logger.log(`Using a preloaded navigation response for '${getFriendlyURL(this.event.request.url)}'`);
|
|
1153
|
+
}
|
|
1154
|
+
return possiblePreloadResponse;
|
|
1155
|
+
}
|
|
1156
|
+
} catch (error) {
|
|
1157
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1158
|
+
logger.error(error);
|
|
1159
|
+
}
|
|
1160
|
+
return undefined;
|
|
1161
|
+
}
|
|
1157
1162
|
}
|
|
1158
|
-
return
|
|
1163
|
+
return undefined;
|
|
1164
|
+
}
|
|
1165
|
+
async _ensureResponseSafeToCache(response) {
|
|
1166
|
+
let responseToCache = response;
|
|
1167
|
+
let pluginsUsed = false;
|
|
1168
|
+
for (const callback of this.iterateCallbacks("cacheWillUpdate")){
|
|
1169
|
+
responseToCache = await callback({
|
|
1170
|
+
request: this.request,
|
|
1171
|
+
response: responseToCache,
|
|
1172
|
+
event: this.event
|
|
1173
|
+
}) || undefined;
|
|
1174
|
+
pluginsUsed = true;
|
|
1175
|
+
if (!responseToCache) {
|
|
1176
|
+
break;
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
if (!pluginsUsed) {
|
|
1180
|
+
if (responseToCache && responseToCache.status !== 200) {
|
|
1181
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1182
|
+
if (responseToCache.status === 0) {
|
|
1183
|
+
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.`);
|
|
1184
|
+
} else {
|
|
1185
|
+
logger.debug(`The response for '${this.request.url}' returned a status code of '${response.status}' and won't be cached as a result.`);
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
responseToCache = undefined;
|
|
1189
|
+
}
|
|
1190
|
+
}
|
|
1191
|
+
return responseToCache;
|
|
1159
1192
|
}
|
|
1160
1193
|
}
|
|
1161
1194
|
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1195
|
+
class Strategy {
|
|
1196
|
+
cacheName;
|
|
1197
|
+
plugins;
|
|
1198
|
+
fetchOptions;
|
|
1199
|
+
matchOptions;
|
|
1200
|
+
constructor(options = {}){
|
|
1201
|
+
this.cacheName = cacheNames$1.getRuntimeName(options.cacheName);
|
|
1202
|
+
this.plugins = options.plugins || [];
|
|
1203
|
+
this.fetchOptions = options.fetchOptions;
|
|
1204
|
+
this.matchOptions = options.matchOptions;
|
|
1205
|
+
}
|
|
1206
|
+
handle(options) {
|
|
1207
|
+
const [responseDone] = this.handleAll(options);
|
|
1208
|
+
return responseDone;
|
|
1209
|
+
}
|
|
1210
|
+
handleAll(options) {
|
|
1211
|
+
if (options instanceof FetchEvent) {
|
|
1212
|
+
options = {
|
|
1213
|
+
event: options,
|
|
1214
|
+
request: options.request
|
|
1215
|
+
};
|
|
1182
1216
|
}
|
|
1183
|
-
|
|
1184
|
-
|
|
1217
|
+
const event = options.event;
|
|
1218
|
+
const request = typeof options.request === "string" ? new Request(options.request) : options.request;
|
|
1219
|
+
const handler = new StrategyHandler(this, options.url ? {
|
|
1220
|
+
event,
|
|
1221
|
+
request,
|
|
1222
|
+
url: options.url,
|
|
1223
|
+
params: options.params
|
|
1224
|
+
} : {
|
|
1225
|
+
event,
|
|
1226
|
+
request
|
|
1227
|
+
});
|
|
1228
|
+
const responseDone = this._getResponse(handler, request, event);
|
|
1229
|
+
const handlerDone = this._awaitComplete(responseDone, handler, request, event);
|
|
1230
|
+
return [
|
|
1231
|
+
responseDone,
|
|
1232
|
+
handlerDone
|
|
1233
|
+
];
|
|
1234
|
+
}
|
|
1235
|
+
async _getResponse(handler, request, event) {
|
|
1236
|
+
await handler.runCallbacks("handlerWillStart", {
|
|
1237
|
+
event,
|
|
1238
|
+
request
|
|
1185
1239
|
});
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1240
|
+
let response;
|
|
1241
|
+
try {
|
|
1242
|
+
response = await this._handle(request, handler);
|
|
1243
|
+
if (response === undefined || response.type === "error") {
|
|
1244
|
+
throw new SerwistError("no-response", {
|
|
1245
|
+
url: request.url
|
|
1246
|
+
});
|
|
1247
|
+
}
|
|
1248
|
+
} catch (error) {
|
|
1249
|
+
if (error instanceof Error) {
|
|
1250
|
+
for (const callback of handler.iterateCallbacks("handlerDidError")){
|
|
1251
|
+
response = await callback({
|
|
1252
|
+
error,
|
|
1253
|
+
event,
|
|
1254
|
+
request
|
|
1255
|
+
});
|
|
1256
|
+
if (response !== undefined) {
|
|
1257
|
+
break;
|
|
1258
|
+
}
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
if (!response) {
|
|
1262
|
+
throw error;
|
|
1263
|
+
}
|
|
1264
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1265
|
+
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.`);
|
|
1189
1266
|
}
|
|
1190
1267
|
}
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
moduleName: "serwist",
|
|
1197
|
-
className: "StorableRequest",
|
|
1198
|
-
funcName: "constructor",
|
|
1199
|
-
paramName: "requestData"
|
|
1200
|
-
});
|
|
1201
|
-
finalAssertExports.isType(requestData.url, "string", {
|
|
1202
|
-
moduleName: "serwist",
|
|
1203
|
-
className: "StorableRequest",
|
|
1204
|
-
funcName: "constructor",
|
|
1205
|
-
paramName: "requestData.url"
|
|
1268
|
+
for (const callback of handler.iterateCallbacks("handlerWillRespond")){
|
|
1269
|
+
response = await callback({
|
|
1270
|
+
event,
|
|
1271
|
+
request,
|
|
1272
|
+
response
|
|
1206
1273
|
});
|
|
1207
1274
|
}
|
|
1208
|
-
|
|
1209
|
-
requestData.mode = "same-origin";
|
|
1210
|
-
}
|
|
1211
|
-
this._requestData = requestData;
|
|
1275
|
+
return response;
|
|
1212
1276
|
}
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1277
|
+
async _awaitComplete(responseDone, handler, request, event) {
|
|
1278
|
+
let response;
|
|
1279
|
+
let error;
|
|
1280
|
+
try {
|
|
1281
|
+
response = await responseDone;
|
|
1282
|
+
} catch {}
|
|
1283
|
+
try {
|
|
1284
|
+
await handler.runCallbacks("handlerDidRespond", {
|
|
1285
|
+
event,
|
|
1286
|
+
request,
|
|
1287
|
+
response
|
|
1288
|
+
});
|
|
1289
|
+
await handler.doneWaiting();
|
|
1290
|
+
} catch (waitUntilError) {
|
|
1291
|
+
if (waitUntilError instanceof Error) {
|
|
1292
|
+
error = waitUntilError;
|
|
1293
|
+
}
|
|
1294
|
+
}
|
|
1295
|
+
await handler.runCallbacks("handlerDidComplete", {
|
|
1296
|
+
event,
|
|
1297
|
+
request,
|
|
1298
|
+
response,
|
|
1299
|
+
error
|
|
1300
|
+
});
|
|
1301
|
+
handler.destroy();
|
|
1302
|
+
if (error) {
|
|
1303
|
+
throw error;
|
|
1218
1304
|
}
|
|
1219
|
-
return requestData;
|
|
1220
|
-
}
|
|
1221
|
-
toRequest() {
|
|
1222
|
-
return new Request(this._requestData.url, this._requestData);
|
|
1223
|
-
}
|
|
1224
|
-
clone() {
|
|
1225
|
-
return new StorableRequest(this.toObject());
|
|
1226
1305
|
}
|
|
1227
1306
|
}
|
|
1228
1307
|
|
|
1229
|
-
const
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
if (queueStoreEntry.metadata) {
|
|
1238
|
-
queueEntry.metadata = queueStoreEntry.metadata;
|
|
1308
|
+
const messages = {
|
|
1309
|
+
strategyStart: (strategyName, request)=>`Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`,
|
|
1310
|
+
printFinalResponse: (response)=>{
|
|
1311
|
+
if (response) {
|
|
1312
|
+
logger.groupCollapsed("View the final response here.");
|
|
1313
|
+
logger.log(response || "[No response returned]");
|
|
1314
|
+
logger.groupEnd();
|
|
1315
|
+
}
|
|
1239
1316
|
}
|
|
1240
|
-
return queueEntry;
|
|
1241
1317
|
};
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
_requestsAddedDuringSync = false;
|
|
1250
|
-
constructor(name, { forceSyncFallback, onSync, maxRetentionTime } = {}){
|
|
1251
|
-
if (queueNames.has(name)) {
|
|
1252
|
-
throw new SerwistError("duplicate-queue-name", {
|
|
1253
|
-
name
|
|
1254
|
-
});
|
|
1318
|
+
|
|
1319
|
+
class NetworkFirst extends Strategy {
|
|
1320
|
+
_networkTimeoutSeconds;
|
|
1321
|
+
constructor(options = {}){
|
|
1322
|
+
super(options);
|
|
1323
|
+
if (!this.plugins.some((p)=>"cacheWillUpdate" in p)) {
|
|
1324
|
+
this.plugins.unshift(cacheOkAndOpaquePlugin);
|
|
1255
1325
|
}
|
|
1256
|
-
|
|
1257
|
-
this._name = name;
|
|
1258
|
-
this._onSync = onSync || this.replayRequests;
|
|
1259
|
-
this._maxRetentionTime = maxRetentionTime || MAX_RETENTION_TIME$1;
|
|
1260
|
-
this._forceSyncFallback = Boolean(forceSyncFallback);
|
|
1261
|
-
this._queueStore = new BackgroundSyncQueueStore(this._name);
|
|
1262
|
-
this._addSyncListener();
|
|
1263
|
-
}
|
|
1264
|
-
get name() {
|
|
1265
|
-
return this._name;
|
|
1266
|
-
}
|
|
1267
|
-
async pushRequest(entry) {
|
|
1326
|
+
this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
|
|
1268
1327
|
if (process.env.NODE_ENV !== "production") {
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
className: "BackgroundSyncQueue",
|
|
1278
|
-
funcName: "pushRequest",
|
|
1279
|
-
paramName: "entry.request"
|
|
1280
|
-
});
|
|
1328
|
+
if (this._networkTimeoutSeconds) {
|
|
1329
|
+
finalAssertExports.isType(this._networkTimeoutSeconds, "number", {
|
|
1330
|
+
moduleName: "serwist",
|
|
1331
|
+
className: this.constructor.name,
|
|
1332
|
+
funcName: "constructor",
|
|
1333
|
+
paramName: "networkTimeoutSeconds"
|
|
1334
|
+
});
|
|
1335
|
+
}
|
|
1281
1336
|
}
|
|
1282
|
-
await this._addRequest(entry, "push");
|
|
1283
1337
|
}
|
|
1284
|
-
async
|
|
1338
|
+
async _handle(request, handler) {
|
|
1339
|
+
const logs = [];
|
|
1285
1340
|
if (process.env.NODE_ENV !== "production") {
|
|
1286
|
-
finalAssertExports.
|
|
1287
|
-
moduleName: "serwist",
|
|
1288
|
-
className: "BackgroundSyncQueue",
|
|
1289
|
-
funcName: "unshiftRequest",
|
|
1290
|
-
paramName: "entry"
|
|
1291
|
-
});
|
|
1292
|
-
finalAssertExports.isInstance(entry.request, Request, {
|
|
1341
|
+
finalAssertExports.isInstance(request, Request, {
|
|
1293
1342
|
moduleName: "serwist",
|
|
1294
|
-
className:
|
|
1295
|
-
funcName: "
|
|
1296
|
-
paramName: "
|
|
1343
|
+
className: this.constructor.name,
|
|
1344
|
+
funcName: "handle",
|
|
1345
|
+
paramName: "makeRequest"
|
|
1297
1346
|
});
|
|
1298
1347
|
}
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
const now = Date.now();
|
|
1310
|
-
const unexpiredEntries = [];
|
|
1311
|
-
for (const entry of allEntries){
|
|
1312
|
-
const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000;
|
|
1313
|
-
if (now - entry.timestamp > maxRetentionTimeInMs) {
|
|
1314
|
-
await this._queueStore.deleteEntry(entry.id);
|
|
1315
|
-
} else {
|
|
1316
|
-
unexpiredEntries.push(convertEntry(entry));
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
return unexpiredEntries;
|
|
1320
|
-
}
|
|
1321
|
-
async size() {
|
|
1322
|
-
return await this._queueStore.size();
|
|
1323
|
-
}
|
|
1324
|
-
async _addRequest({ request, metadata, timestamp = Date.now() }, operation) {
|
|
1325
|
-
const storableRequest = await StorableRequest.fromRequest(request.clone());
|
|
1326
|
-
const entry = {
|
|
1327
|
-
requestData: storableRequest.toObject(),
|
|
1328
|
-
timestamp
|
|
1329
|
-
};
|
|
1330
|
-
if (metadata) {
|
|
1331
|
-
entry.metadata = metadata;
|
|
1332
|
-
}
|
|
1333
|
-
switch(operation){
|
|
1334
|
-
case "push":
|
|
1335
|
-
await this._queueStore.pushEntry(entry);
|
|
1336
|
-
break;
|
|
1337
|
-
case "unshift":
|
|
1338
|
-
await this._queueStore.unshiftEntry(entry);
|
|
1339
|
-
break;
|
|
1340
|
-
}
|
|
1341
|
-
if (process.env.NODE_ENV !== "production") {
|
|
1342
|
-
logger.log(`Request for '${getFriendlyURL(request.url)}' has ` + `been added to background sync queue '${this._name}'.`);
|
|
1343
|
-
}
|
|
1344
|
-
if (this._syncInProgress) {
|
|
1345
|
-
this._requestsAddedDuringSync = true;
|
|
1346
|
-
} else {
|
|
1347
|
-
await this.registerSync();
|
|
1348
|
-
}
|
|
1349
|
-
}
|
|
1350
|
-
async _removeRequest(operation) {
|
|
1351
|
-
const now = Date.now();
|
|
1352
|
-
let entry;
|
|
1353
|
-
switch(operation){
|
|
1354
|
-
case "pop":
|
|
1355
|
-
entry = await this._queueStore.popEntry();
|
|
1356
|
-
break;
|
|
1357
|
-
case "shift":
|
|
1358
|
-
entry = await this._queueStore.shiftEntry();
|
|
1359
|
-
break;
|
|
1348
|
+
const promises = [];
|
|
1349
|
+
let timeoutId;
|
|
1350
|
+
if (this._networkTimeoutSeconds) {
|
|
1351
|
+
const { id, promise } = this._getTimeoutPromise({
|
|
1352
|
+
request,
|
|
1353
|
+
logs,
|
|
1354
|
+
handler
|
|
1355
|
+
});
|
|
1356
|
+
timeoutId = id;
|
|
1357
|
+
promises.push(promise);
|
|
1360
1358
|
}
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1359
|
+
const networkPromise = this._getNetworkPromise({
|
|
1360
|
+
timeoutId,
|
|
1361
|
+
request,
|
|
1362
|
+
logs,
|
|
1363
|
+
handler
|
|
1364
|
+
});
|
|
1365
|
+
promises.push(networkPromise);
|
|
1366
|
+
const response = await handler.waitUntil((async ()=>{
|
|
1367
|
+
return await handler.waitUntil(Promise.race(promises)) || await networkPromise;
|
|
1368
|
+
})());
|
|
1369
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1370
|
+
logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
|
|
1371
|
+
for (const log of logs){
|
|
1372
|
+
logger.log(log);
|
|
1365
1373
|
}
|
|
1366
|
-
|
|
1374
|
+
messages.printFinalResponse(response);
|
|
1375
|
+
logger.groupEnd();
|
|
1367
1376
|
}
|
|
1368
|
-
|
|
1377
|
+
if (!response) {
|
|
1378
|
+
throw new SerwistError("no-response", {
|
|
1379
|
+
url: request.url
|
|
1380
|
+
});
|
|
1381
|
+
}
|
|
1382
|
+
return response;
|
|
1369
1383
|
}
|
|
1370
|
-
|
|
1371
|
-
let
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
await fetch(entry.request.clone());
|
|
1375
|
-
if (process.env.NODE_ENV !== "production") {
|
|
1376
|
-
logger.log(`Request for '${getFriendlyURL(entry.request.url)}' ` + `has been replayed in queue '${this._name}'`);
|
|
1377
|
-
}
|
|
1378
|
-
} catch (error) {
|
|
1379
|
-
await this.unshiftRequest(entry);
|
|
1384
|
+
_getTimeoutPromise({ request, logs, handler }) {
|
|
1385
|
+
let timeoutId;
|
|
1386
|
+
const timeoutPromise = new Promise((resolve)=>{
|
|
1387
|
+
const onNetworkTimeout = async ()=>{
|
|
1380
1388
|
if (process.env.NODE_ENV !== "production") {
|
|
1381
|
-
|
|
1389
|
+
logs.push(`Timing out the network response at ${this._networkTimeoutSeconds} seconds.`);
|
|
1382
1390
|
}
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1391
|
+
resolve(await handler.cacheMatch(request));
|
|
1392
|
+
};
|
|
1393
|
+
timeoutId = setTimeout(onNetworkTimeout, this._networkTimeoutSeconds * 1000);
|
|
1394
|
+
});
|
|
1395
|
+
return {
|
|
1396
|
+
promise: timeoutPromise,
|
|
1397
|
+
id: timeoutId
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
async _getNetworkPromise({ timeoutId, request, logs, handler }) {
|
|
1401
|
+
let error;
|
|
1402
|
+
let response;
|
|
1403
|
+
try {
|
|
1404
|
+
response = await handler.fetchAndCachePut(request);
|
|
1405
|
+
} catch (fetchError) {
|
|
1406
|
+
if (fetchError instanceof Error) {
|
|
1407
|
+
error = fetchError;
|
|
1386
1408
|
}
|
|
1387
1409
|
}
|
|
1388
|
-
if (
|
|
1389
|
-
|
|
1410
|
+
if (timeoutId) {
|
|
1411
|
+
clearTimeout(timeoutId);
|
|
1390
1412
|
}
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
} catch (err) {
|
|
1397
|
-
if (process.env.NODE_ENV !== "production") {
|
|
1398
|
-
logger.warn(`Unable to register sync event for '${this._name}'.`, err);
|
|
1399
|
-
}
|
|
1413
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1414
|
+
if (response) {
|
|
1415
|
+
logs.push("Got response from network.");
|
|
1416
|
+
} else {
|
|
1417
|
+
logs.push("Unable to get a response from the network. Will respond " + "with a cached response.");
|
|
1400
1418
|
}
|
|
1401
1419
|
}
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
if ("sync" in self.registration && !this._forceSyncFallback) {
|
|
1405
|
-
self.addEventListener("sync", (event)=>{
|
|
1406
|
-
if (event.tag === `${TAG_PREFIX}:${this._name}`) {
|
|
1407
|
-
if (process.env.NODE_ENV !== "production") {
|
|
1408
|
-
logger.log(`Background sync for tag '${event.tag}' has been received`);
|
|
1409
|
-
}
|
|
1410
|
-
const syncComplete = async ()=>{
|
|
1411
|
-
this._syncInProgress = true;
|
|
1412
|
-
let syncError = undefined;
|
|
1413
|
-
try {
|
|
1414
|
-
await this._onSync({
|
|
1415
|
-
queue: this
|
|
1416
|
-
});
|
|
1417
|
-
} catch (error) {
|
|
1418
|
-
if (error instanceof Error) {
|
|
1419
|
-
syncError = error;
|
|
1420
|
-
throw syncError;
|
|
1421
|
-
}
|
|
1422
|
-
} finally{
|
|
1423
|
-
if (this._requestsAddedDuringSync && !(syncError && !event.lastChance)) {
|
|
1424
|
-
await this.registerSync();
|
|
1425
|
-
}
|
|
1426
|
-
this._syncInProgress = false;
|
|
1427
|
-
this._requestsAddedDuringSync = false;
|
|
1428
|
-
}
|
|
1429
|
-
};
|
|
1430
|
-
event.waitUntil(syncComplete());
|
|
1431
|
-
}
|
|
1432
|
-
});
|
|
1433
|
-
} else {
|
|
1420
|
+
if (error || !response) {
|
|
1421
|
+
response = await handler.cacheMatch(request);
|
|
1434
1422
|
if (process.env.NODE_ENV !== "production") {
|
|
1435
|
-
|
|
1423
|
+
if (response) {
|
|
1424
|
+
logs.push(`Found a cached response in the '${this.cacheName}' cache.`);
|
|
1425
|
+
} else {
|
|
1426
|
+
logs.push(`No response found in the '${this.cacheName}' cache.`);
|
|
1427
|
+
}
|
|
1436
1428
|
}
|
|
1437
|
-
void this._onSync({
|
|
1438
|
-
queue: this
|
|
1439
|
-
});
|
|
1440
1429
|
}
|
|
1441
|
-
|
|
1442
|
-
static get _queueNames() {
|
|
1443
|
-
return queueNames;
|
|
1430
|
+
return response;
|
|
1444
1431
|
}
|
|
1445
1432
|
}
|
|
1446
1433
|
|
|
1447
|
-
class
|
|
1448
|
-
|
|
1449
|
-
constructor(
|
|
1450
|
-
|
|
1434
|
+
class NetworkOnly extends Strategy {
|
|
1435
|
+
_networkTimeoutSeconds;
|
|
1436
|
+
constructor(options = {}){
|
|
1437
|
+
super(options);
|
|
1438
|
+
this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
|
|
1451
1439
|
}
|
|
1452
|
-
async
|
|
1453
|
-
|
|
1454
|
-
request
|
|
1455
|
-
|
|
1440
|
+
async _handle(request, handler) {
|
|
1441
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1442
|
+
finalAssertExports.isInstance(request, Request, {
|
|
1443
|
+
moduleName: "serwist",
|
|
1444
|
+
className: this.constructor.name,
|
|
1445
|
+
funcName: "_handle",
|
|
1446
|
+
paramName: "request"
|
|
1447
|
+
});
|
|
1448
|
+
}
|
|
1449
|
+
let error;
|
|
1450
|
+
let response;
|
|
1451
|
+
try {
|
|
1452
|
+
const promises = [
|
|
1453
|
+
handler.fetch(request)
|
|
1454
|
+
];
|
|
1455
|
+
if (this._networkTimeoutSeconds) {
|
|
1456
|
+
const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000);
|
|
1457
|
+
promises.push(timeoutPromise);
|
|
1458
|
+
}
|
|
1459
|
+
response = await Promise.race(promises);
|
|
1460
|
+
if (!response) {
|
|
1461
|
+
throw new Error(`Timed out the network response after ${this._networkTimeoutSeconds} seconds.`);
|
|
1462
|
+
}
|
|
1463
|
+
} catch (err) {
|
|
1464
|
+
if (err instanceof Error) {
|
|
1465
|
+
error = err;
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1469
|
+
logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
|
|
1470
|
+
if (response) {
|
|
1471
|
+
logger.log("Got response from network.");
|
|
1472
|
+
} else {
|
|
1473
|
+
logger.log("Unable to get a response from the network.");
|
|
1474
|
+
}
|
|
1475
|
+
messages.printFinalResponse(response);
|
|
1476
|
+
logger.groupEnd();
|
|
1477
|
+
}
|
|
1478
|
+
if (!response) {
|
|
1479
|
+
throw new SerwistError("no-response", {
|
|
1480
|
+
url: request.url,
|
|
1481
|
+
error
|
|
1482
|
+
});
|
|
1483
|
+
}
|
|
1484
|
+
return response;
|
|
1456
1485
|
}
|
|
1457
1486
|
}
|
|
1458
1487
|
|
|
@@ -1467,7 +1496,7 @@ const COLLECT_PATHS_REGEX = /^\/(\w+\/)?collect/;
|
|
|
1467
1496
|
|
|
1468
1497
|
const createOnSyncCallback = (config)=>{
|
|
1469
1498
|
return async ({ queue })=>{
|
|
1470
|
-
let entry
|
|
1499
|
+
let entry;
|
|
1471
1500
|
while(entry = await queue.shiftRequest()){
|
|
1472
1501
|
const { request, timestamp } = entry;
|
|
1473
1502
|
const url = new URL(request.url);
|
|
@@ -1543,60 +1572,254 @@ const createGtmJsRoute = (cacheName)=>{
|
|
|
1543
1572
|
});
|
|
1544
1573
|
return new Route(match, handler, "GET");
|
|
1545
1574
|
};
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
})
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1575
|
+
class GoogleAnalytics {
|
|
1576
|
+
_options;
|
|
1577
|
+
constructor(options = {}){
|
|
1578
|
+
this._options = options;
|
|
1579
|
+
}
|
|
1580
|
+
init({ serwist }) {
|
|
1581
|
+
const resolvedCacheName = cacheNames$1.getGoogleAnalyticsName(this._options.cacheName);
|
|
1582
|
+
const bgSyncPlugin = new BackgroundSyncPlugin(QUEUE_NAME, {
|
|
1583
|
+
maxRetentionTime: MAX_RETENTION_TIME,
|
|
1584
|
+
onSync: createOnSyncCallback(this._options)
|
|
1585
|
+
});
|
|
1586
|
+
const routes = [
|
|
1587
|
+
createGtmJsRoute(resolvedCacheName),
|
|
1588
|
+
createAnalyticsJsRoute(resolvedCacheName),
|
|
1589
|
+
createGtagJsRoute(resolvedCacheName),
|
|
1590
|
+
...createCollectRoutes(bgSyncPlugin)
|
|
1591
|
+
];
|
|
1592
|
+
for (const route of routes){
|
|
1593
|
+
registerRoute(serwist, route);
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
const initializeGoogleAnalytics = ({ serwist, cacheName, ...options })=>{
|
|
1599
|
+
const resolvedCacheName = cacheNames$1.getGoogleAnalyticsName(cacheName);
|
|
1600
|
+
const bgSyncPlugin = new BackgroundSyncPlugin(QUEUE_NAME, {
|
|
1601
|
+
maxRetentionTime: MAX_RETENTION_TIME,
|
|
1602
|
+
onSync: createOnSyncCallback(options)
|
|
1603
|
+
});
|
|
1604
|
+
const routes = [
|
|
1605
|
+
createGtmJsRoute(resolvedCacheName),
|
|
1606
|
+
createAnalyticsJsRoute(resolvedCacheName),
|
|
1607
|
+
createGtagJsRoute(resolvedCacheName),
|
|
1608
|
+
...createCollectRoutes(bgSyncPlugin)
|
|
1609
|
+
];
|
|
1610
|
+
for (const route of routes){
|
|
1611
|
+
serwist.registerRoute(route);
|
|
1612
|
+
}
|
|
1613
|
+
};
|
|
1614
|
+
|
|
1615
|
+
const REVISION_SEARCH_PARAM = "__WB_REVISION__";
|
|
1616
|
+
const createCacheKey = (entry)=>{
|
|
1617
|
+
if (!entry) {
|
|
1618
|
+
throw new SerwistError("add-to-cache-list-unexpected-type", {
|
|
1619
|
+
entry
|
|
1620
|
+
});
|
|
1621
|
+
}
|
|
1622
|
+
if (typeof entry === "string") {
|
|
1623
|
+
const urlObject = new URL(entry, location.href);
|
|
1624
|
+
return {
|
|
1625
|
+
cacheKey: urlObject.href,
|
|
1626
|
+
url: urlObject.href
|
|
1627
|
+
};
|
|
1628
|
+
}
|
|
1629
|
+
const { revision, url } = entry;
|
|
1630
|
+
if (!url) {
|
|
1631
|
+
throw new SerwistError("add-to-cache-list-unexpected-type", {
|
|
1632
|
+
entry
|
|
1633
|
+
});
|
|
1634
|
+
}
|
|
1635
|
+
if (!revision) {
|
|
1636
|
+
const urlObject = new URL(url, location.href);
|
|
1637
|
+
return {
|
|
1638
|
+
cacheKey: urlObject.href,
|
|
1639
|
+
url: urlObject.href
|
|
1640
|
+
};
|
|
1641
|
+
}
|
|
1642
|
+
const cacheKeyURL = new URL(url, location.href);
|
|
1643
|
+
const originalURL = new URL(url, location.href);
|
|
1644
|
+
cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision);
|
|
1645
|
+
return {
|
|
1646
|
+
cacheKey: cacheKeyURL.href,
|
|
1647
|
+
url: originalURL.href
|
|
1648
|
+
};
|
|
1649
|
+
};
|
|
1650
|
+
|
|
1651
|
+
const logGroup = (groupTitle, deletedURLs)=>{
|
|
1652
|
+
logger.groupCollapsed(groupTitle);
|
|
1653
|
+
for (const url of deletedURLs){
|
|
1654
|
+
logger.log(url);
|
|
1655
|
+
}
|
|
1656
|
+
logger.groupEnd();
|
|
1657
|
+
};
|
|
1658
|
+
const printCleanupDetails = (deletedURLs)=>{
|
|
1659
|
+
const deletionCount = deletedURLs.length;
|
|
1660
|
+
if (deletionCount > 0) {
|
|
1661
|
+
logger.groupCollapsed(`During precaching cleanup, ${deletionCount} cached request${deletionCount === 1 ? " was" : "s were"} deleted.`);
|
|
1662
|
+
logGroup("Deleted Cache Requests", deletedURLs);
|
|
1663
|
+
logger.groupEnd();
|
|
1560
1664
|
}
|
|
1561
1665
|
};
|
|
1562
1666
|
|
|
1563
|
-
|
|
1564
|
-
|
|
1667
|
+
function _nestedGroup(groupTitle, urls) {
|
|
1668
|
+
if (urls.length === 0) {
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
logger.groupCollapsed(groupTitle);
|
|
1672
|
+
for (const url of urls){
|
|
1673
|
+
logger.log(url);
|
|
1674
|
+
}
|
|
1675
|
+
logger.groupEnd();
|
|
1676
|
+
}
|
|
1677
|
+
const printInstallDetails = (urlsToPrecache, urlsAlreadyPrecached)=>{
|
|
1678
|
+
const precachedCount = urlsToPrecache.length;
|
|
1679
|
+
const alreadyPrecachedCount = urlsAlreadyPrecached.length;
|
|
1680
|
+
if (precachedCount || alreadyPrecachedCount) {
|
|
1681
|
+
let message = `Precaching ${precachedCount} file${precachedCount === 1 ? "" : "s"}.`;
|
|
1682
|
+
if (alreadyPrecachedCount > 0) {
|
|
1683
|
+
message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? " is" : "s are"} already cached.`;
|
|
1684
|
+
}
|
|
1685
|
+
logger.groupCollapsed(message);
|
|
1686
|
+
_nestedGroup("View newly precached URLs.", urlsToPrecache);
|
|
1687
|
+
_nestedGroup("View previously precached URLs.", urlsAlreadyPrecached);
|
|
1688
|
+
logger.groupEnd();
|
|
1689
|
+
}
|
|
1565
1690
|
};
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1691
|
+
|
|
1692
|
+
class PrecacheCacheKeyPlugin {
|
|
1693
|
+
_precacheController;
|
|
1694
|
+
constructor({ precacheController }){
|
|
1695
|
+
this._precacheController = precacheController;
|
|
1696
|
+
}
|
|
1697
|
+
cacheKeyWillBeUsed = async ({ request, params })=>{
|
|
1698
|
+
const cacheKey = params?.cacheKey || this._precacheController.getPrecacheKeyForUrl(request.url);
|
|
1699
|
+
return cacheKey ? new Request(cacheKey, {
|
|
1700
|
+
headers: request.headers
|
|
1701
|
+
}) : request;
|
|
1702
|
+
};
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
const parsePrecacheOptions = (controller, { entries, cacheName, plugins, fetchOptions, matchOptions, fallbackToNetwork, directoryIndex, ignoreURLParametersMatching, cleanURLs, urlManipulation, cleanupOutdatedCaches, concurrency, navigateFallback, navigateFallbackAllowlist, navigateFallbackDenylist })=>({
|
|
1706
|
+
entries: entries ?? [],
|
|
1707
|
+
strategyOptions: {
|
|
1708
|
+
cacheName: cacheNames$1.getPrecacheName(cacheName),
|
|
1709
|
+
plugins: [
|
|
1710
|
+
...plugins ?? [],
|
|
1711
|
+
new PrecacheCacheKeyPlugin({
|
|
1712
|
+
precacheController: controller
|
|
1713
|
+
})
|
|
1714
|
+
],
|
|
1715
|
+
fetchOptions,
|
|
1716
|
+
matchOptions,
|
|
1717
|
+
fallbackToNetwork
|
|
1718
|
+
},
|
|
1719
|
+
routeOptions: {
|
|
1720
|
+
directoryIndex,
|
|
1721
|
+
ignoreURLParametersMatching,
|
|
1722
|
+
cleanURLs,
|
|
1723
|
+
urlManipulation
|
|
1724
|
+
},
|
|
1725
|
+
extensionOptions: {
|
|
1726
|
+
cleanupOutdatedCaches,
|
|
1727
|
+
concurrency: concurrency ?? 10,
|
|
1728
|
+
navigateFallback,
|
|
1729
|
+
navigateFallbackAllowlist,
|
|
1730
|
+
navigateFallbackDenylist
|
|
1731
|
+
}
|
|
1732
|
+
});
|
|
1733
|
+
|
|
1734
|
+
class PrecacheInstallReportPlugin {
|
|
1735
|
+
updatedURLs = [];
|
|
1736
|
+
notUpdatedURLs = [];
|
|
1737
|
+
handlerWillStart = async ({ request, state })=>{
|
|
1738
|
+
if (state) {
|
|
1739
|
+
state.originalRequest = request;
|
|
1740
|
+
}
|
|
1741
|
+
};
|
|
1742
|
+
cachedResponseWillBeUsed = async ({ event, state, cachedResponse })=>{
|
|
1743
|
+
if (event.type === "install") {
|
|
1744
|
+
if (state?.originalRequest && state.originalRequest instanceof Request) {
|
|
1745
|
+
const url = state.originalRequest.url;
|
|
1746
|
+
if (cachedResponse) {
|
|
1747
|
+
this.notUpdatedURLs.push(url);
|
|
1748
|
+
} else {
|
|
1749
|
+
this.updatedURLs.push(url);
|
|
1575
1750
|
}
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1751
|
+
}
|
|
1752
|
+
}
|
|
1753
|
+
return cachedResponse;
|
|
1754
|
+
};
|
|
1755
|
+
}
|
|
1756
|
+
|
|
1757
|
+
const removeIgnoredSearchParams = (urlObject, ignoreURLParametersMatching = [])=>{
|
|
1758
|
+
for (const paramName of [
|
|
1759
|
+
...urlObject.searchParams.keys()
|
|
1760
|
+
]){
|
|
1761
|
+
if (ignoreURLParametersMatching.some((regExp)=>regExp.test(paramName))) {
|
|
1762
|
+
urlObject.searchParams.delete(paramName);
|
|
1581
1763
|
}
|
|
1582
1764
|
}
|
|
1765
|
+
return urlObject;
|
|
1583
1766
|
};
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1767
|
+
|
|
1768
|
+
function* generateURLVariations(url, { directoryIndex = "index.html", ignoreURLParametersMatching = [
|
|
1769
|
+
/^utm_/,
|
|
1770
|
+
/^fbclid$/
|
|
1771
|
+
], cleanURLs = true, urlManipulation } = {}) {
|
|
1772
|
+
const urlObject = new URL(url, location.href);
|
|
1773
|
+
urlObject.hash = "";
|
|
1774
|
+
yield urlObject.href;
|
|
1775
|
+
const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching);
|
|
1776
|
+
yield urlWithoutIgnoredParams.href;
|
|
1777
|
+
if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith("/")) {
|
|
1778
|
+
const directoryURL = new URL(urlWithoutIgnoredParams.href);
|
|
1779
|
+
directoryURL.pathname += directoryIndex;
|
|
1780
|
+
yield directoryURL.href;
|
|
1781
|
+
}
|
|
1782
|
+
if (cleanURLs) {
|
|
1783
|
+
const cleanURL = new URL(urlWithoutIgnoredParams.href);
|
|
1784
|
+
cleanURL.pathname += ".html";
|
|
1785
|
+
yield cleanURL.href;
|
|
1786
|
+
}
|
|
1787
|
+
if (urlManipulation) {
|
|
1788
|
+
const additionalURLs = urlManipulation({
|
|
1789
|
+
url: urlObject
|
|
1592
1790
|
});
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
logger.log("Navigation preloading is not supported in this browser.");
|
|
1791
|
+
for (const urlToAttempt of additionalURLs){
|
|
1792
|
+
yield urlToAttempt.href;
|
|
1596
1793
|
}
|
|
1597
1794
|
}
|
|
1598
|
-
}
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
class PrecacheRoute extends Route {
|
|
1798
|
+
constructor(precache, options){
|
|
1799
|
+
const match = ({ request })=>{
|
|
1800
|
+
const urlsToCacheKeys = precache.getUrlsToPrecacheKeys();
|
|
1801
|
+
for (const possibleURL of generateURLVariations(request.url, options)){
|
|
1802
|
+
const cacheKey = urlsToCacheKeys.get(possibleURL);
|
|
1803
|
+
if (cacheKey) {
|
|
1804
|
+
const integrity = precache.getIntegrityForPrecacheKey(cacheKey);
|
|
1805
|
+
return {
|
|
1806
|
+
cacheKey,
|
|
1807
|
+
integrity
|
|
1808
|
+
};
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1812
|
+
logger.debug(`Precaching did not find a match for ${getFriendlyURL(request.url)}.`);
|
|
1813
|
+
}
|
|
1814
|
+
return;
|
|
1815
|
+
};
|
|
1816
|
+
super(match, precache.strategy);
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1599
1819
|
|
|
1820
|
+
const disableDevLogs = ()=>{
|
|
1821
|
+
self.__WB_DISABLE_DEV_LOGS = true;
|
|
1822
|
+
};
|
|
1600
1823
|
const setCacheNameDetails = (details)=>{
|
|
1601
1824
|
if (process.env.NODE_ENV !== "production") {
|
|
1602
1825
|
for (const key of Object.keys(details)){
|
|
@@ -1627,206 +1850,165 @@ const setCacheNameDetails = (details)=>{
|
|
|
1627
1850
|
}
|
|
1628
1851
|
cacheNames$1.updateDetails(details);
|
|
1629
1852
|
};
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
moduleName: "serwist",
|
|
1638
|
-
funcName: "parseRoute",
|
|
1639
|
-
paramName: "capture"
|
|
1640
|
-
});
|
|
1641
|
-
}
|
|
1642
|
-
const valueToCheck = capture.startsWith("http") ? captureUrl.pathname : capture;
|
|
1643
|
-
const wildcards = "[*:?+]";
|
|
1644
|
-
if (new RegExp(`${wildcards}`).exec(valueToCheck)) {
|
|
1645
|
-
logger.debug(`The '$capture' parameter contains an Express-style wildcard character (${wildcards}). Strings are now always interpreted as exact matches; use a RegExp for partial or wildcard matches.`);
|
|
1646
|
-
}
|
|
1647
|
-
}
|
|
1648
|
-
const matchCallback = ({ url })=>{
|
|
1649
|
-
if (process.env.NODE_ENV !== "production") {
|
|
1650
|
-
if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) {
|
|
1651
|
-
logger.debug(`${capture} only partially matches the cross-origin URL ${url.toString()}. This route will only handle cross-origin requests if they match the entire URL.`);
|
|
1652
|
-
}
|
|
1653
|
-
}
|
|
1654
|
-
return url.href === captureUrl.href;
|
|
1655
|
-
};
|
|
1656
|
-
return new Route(matchCallback, handler, method);
|
|
1657
|
-
}
|
|
1658
|
-
if (capture instanceof RegExp) {
|
|
1659
|
-
return new RegExpRoute(capture, handler, method);
|
|
1660
|
-
}
|
|
1661
|
-
if (typeof capture === "function") {
|
|
1662
|
-
return new Route(capture, handler, method);
|
|
1853
|
+
const registerQuotaErrorCallback = (callback)=>{
|
|
1854
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1855
|
+
finalAssertExports.isType(callback, "function", {
|
|
1856
|
+
moduleName: "@serwist/core",
|
|
1857
|
+
funcName: "register",
|
|
1858
|
+
paramName: "callback"
|
|
1859
|
+
});
|
|
1663
1860
|
}
|
|
1664
|
-
|
|
1665
|
-
|
|
1861
|
+
quotaErrorCallbacks.add(callback);
|
|
1862
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1863
|
+
logger.log("Registered a callback to respond to quota errors.", callback);
|
|
1666
1864
|
}
|
|
1667
|
-
throw new SerwistError("unsupported-route-type", {
|
|
1668
|
-
moduleName: "serwist",
|
|
1669
|
-
funcName: "parseRoute",
|
|
1670
|
-
paramName: "capture"
|
|
1671
|
-
});
|
|
1672
1865
|
};
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
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
|
-
};
|
|
1866
|
+
const copyResponse = async (response, modifier)=>{
|
|
1867
|
+
let origin = null;
|
|
1868
|
+
if (response.url) {
|
|
1869
|
+
const responseURL = new URL(response.url);
|
|
1870
|
+
origin = responseURL.origin;
|
|
1687
1871
|
}
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
entry
|
|
1872
|
+
if (origin !== self.location.origin) {
|
|
1873
|
+
throw new SerwistError("cross-origin-copy-response", {
|
|
1874
|
+
origin
|
|
1692
1875
|
});
|
|
1693
1876
|
}
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
};
|
|
1700
|
-
}
|
|
1701
|
-
const cacheKeyURL = new URL(url, location.href);
|
|
1702
|
-
const originalURL = new URL(url, location.href);
|
|
1703
|
-
cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision);
|
|
1704
|
-
return {
|
|
1705
|
-
cacheKey: cacheKeyURL.href,
|
|
1706
|
-
url: originalURL.href
|
|
1877
|
+
const clonedResponse = response.clone();
|
|
1878
|
+
const responseInit = {
|
|
1879
|
+
headers: new Headers(clonedResponse.headers),
|
|
1880
|
+
status: clonedResponse.status,
|
|
1881
|
+
statusText: clonedResponse.statusText
|
|
1707
1882
|
};
|
|
1883
|
+
const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit;
|
|
1884
|
+
const body = canConstructResponseFromBodyStream() ? clonedResponse.body : await clonedResponse.blob();
|
|
1885
|
+
return new Response(body, modifiedResponseInit);
|
|
1708
1886
|
};
|
|
1709
1887
|
|
|
1710
|
-
class
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1888
|
+
class PrecacheStrategy extends Strategy {
|
|
1889
|
+
_fallbackToNetwork;
|
|
1890
|
+
static defaultPrecacheCacheabilityPlugin = {
|
|
1891
|
+
async cacheWillUpdate ({ response }) {
|
|
1892
|
+
if (!response || response.status >= 400) {
|
|
1893
|
+
return null;
|
|
1894
|
+
}
|
|
1895
|
+
return response;
|
|
1716
1896
|
}
|
|
1717
1897
|
};
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
const url = state.originalRequest.url;
|
|
1722
|
-
if (cachedResponse) {
|
|
1723
|
-
this.notUpdatedURLs.push(url);
|
|
1724
|
-
} else {
|
|
1725
|
-
this.updatedURLs.push(url);
|
|
1726
|
-
}
|
|
1727
|
-
}
|
|
1898
|
+
static copyRedirectedCacheableResponsesPlugin = {
|
|
1899
|
+
async cacheWillUpdate ({ response }) {
|
|
1900
|
+
return response.redirected ? await copyResponse(response) : response;
|
|
1728
1901
|
}
|
|
1729
|
-
return cachedResponse;
|
|
1730
1902
|
};
|
|
1731
|
-
}
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
}
|
|
1737
|
-
logger.groupCollapsed(groupTitle);
|
|
1738
|
-
for (const url of urls){
|
|
1739
|
-
logger.log(url);
|
|
1903
|
+
constructor(options = {}){
|
|
1904
|
+
options.cacheName = cacheNames$1.getPrecacheName(options.cacheName);
|
|
1905
|
+
super(options);
|
|
1906
|
+
this._fallbackToNetwork = options.fallbackToNetwork !== false;
|
|
1907
|
+
this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);
|
|
1740
1908
|
}
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
const alreadyPrecachedCount = urlsAlreadyPrecached.length;
|
|
1746
|
-
if (precachedCount || alreadyPrecachedCount) {
|
|
1747
|
-
let message = `Precaching ${precachedCount} file${precachedCount === 1 ? "" : "s"}.`;
|
|
1748
|
-
if (alreadyPrecachedCount > 0) {
|
|
1749
|
-
message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? " is" : "s are"} already cached.`;
|
|
1909
|
+
async _handle(request, handler) {
|
|
1910
|
+
const preloadResponse = await handler.getPreloadResponse();
|
|
1911
|
+
if (preloadResponse) {
|
|
1912
|
+
return preloadResponse;
|
|
1750
1913
|
}
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1914
|
+
const response = await handler.cacheMatch(request);
|
|
1915
|
+
if (response) {
|
|
1916
|
+
return response;
|
|
1917
|
+
}
|
|
1918
|
+
if (handler.event && handler.event.type === "install") {
|
|
1919
|
+
return await this._handleInstall(request, handler);
|
|
1920
|
+
}
|
|
1921
|
+
return await this._handleFetch(request, handler);
|
|
1755
1922
|
}
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
const
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1923
|
+
async _handleFetch(request, handler) {
|
|
1924
|
+
let response;
|
|
1925
|
+
const params = handler.params || {};
|
|
1926
|
+
if (this._fallbackToNetwork) {
|
|
1927
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1928
|
+
logger.warn(`The precached response for ${getFriendlyURL(request.url)} in ${this.cacheName} was not found. Falling back to the network.`);
|
|
1929
|
+
}
|
|
1930
|
+
const integrityInManifest = params.integrity;
|
|
1931
|
+
const integrityInRequest = request.integrity;
|
|
1932
|
+
const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest;
|
|
1933
|
+
response = await handler.fetch(new Request(request, {
|
|
1934
|
+
integrity: request.mode !== "no-cors" ? integrityInRequest || integrityInManifest : undefined
|
|
1935
|
+
}));
|
|
1936
|
+
if (integrityInManifest && noIntegrityConflict && request.mode !== "no-cors") {
|
|
1937
|
+
this._useDefaultCacheabilityPluginIfNeeded();
|
|
1938
|
+
const wasCached = await handler.cachePut(request, response.clone());
|
|
1939
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1940
|
+
if (wasCached) {
|
|
1941
|
+
logger.log(`A response for ${getFriendlyURL(request.url)} was used to "repair" the precache.`);
|
|
1942
|
+
}
|
|
1943
|
+
}
|
|
1944
|
+
}
|
|
1945
|
+
} else {
|
|
1946
|
+
throw new SerwistError("missing-precache-entry", {
|
|
1947
|
+
cacheName: this.cacheName,
|
|
1948
|
+
url: request.url
|
|
1949
|
+
});
|
|
1950
|
+
}
|
|
1951
|
+
if (process.env.NODE_ENV !== "production") {
|
|
1952
|
+
const cacheKey = params.cacheKey || await handler.getCacheKey(request, "read");
|
|
1953
|
+
logger.groupCollapsed(`Precaching is responding to: ${getFriendlyURL(request.url)}`);
|
|
1954
|
+
logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`);
|
|
1955
|
+
logger.groupCollapsed("View request details here.");
|
|
1956
|
+
logger.log(request);
|
|
1957
|
+
logger.groupEnd();
|
|
1958
|
+
logger.groupCollapsed("View response details here.");
|
|
1959
|
+
logger.log(response);
|
|
1960
|
+
logger.groupEnd();
|
|
1961
|
+
logger.groupEnd();
|
|
1962
|
+
}
|
|
1963
|
+
return response;
|
|
1762
1964
|
}
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
const
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1965
|
+
async _handleInstall(request, handler) {
|
|
1966
|
+
this._useDefaultCacheabilityPluginIfNeeded();
|
|
1967
|
+
const response = await handler.fetch(request);
|
|
1968
|
+
const wasCached = await handler.cachePut(request, response.clone());
|
|
1969
|
+
if (!wasCached) {
|
|
1970
|
+
throw new SerwistError("bad-precaching-response", {
|
|
1971
|
+
url: request.url,
|
|
1972
|
+
status: response.status
|
|
1973
|
+
});
|
|
1974
|
+
}
|
|
1975
|
+
return response;
|
|
1771
1976
|
}
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1977
|
+
_useDefaultCacheabilityPluginIfNeeded() {
|
|
1978
|
+
let defaultPluginIndex = null;
|
|
1979
|
+
let cacheWillUpdatePluginCount = 0;
|
|
1980
|
+
for (const [index, plugin] of this.plugins.entries()){
|
|
1981
|
+
if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) {
|
|
1982
|
+
continue;
|
|
1983
|
+
}
|
|
1984
|
+
if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) {
|
|
1985
|
+
defaultPluginIndex = index;
|
|
1986
|
+
}
|
|
1987
|
+
if (plugin.cacheWillUpdate) {
|
|
1988
|
+
cacheWillUpdatePluginCount++;
|
|
1989
|
+
}
|
|
1990
|
+
}
|
|
1991
|
+
if (cacheWillUpdatePluginCount === 0) {
|
|
1992
|
+
this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin);
|
|
1993
|
+
} else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) {
|
|
1994
|
+
this.plugins.splice(defaultPluginIndex, 1);
|
|
1995
|
+
}
|
|
1778
1996
|
}
|
|
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
1997
|
}
|
|
1786
1998
|
|
|
1787
|
-
|
|
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
|
-
}
|
|
1813
|
-
});
|
|
1814
|
-
|
|
1815
|
-
class PrecacheController {
|
|
1999
|
+
class Precache {
|
|
1816
2000
|
_urlsToCacheKeys = new Map();
|
|
1817
2001
|
_urlsToCacheModes = new Map();
|
|
1818
2002
|
_cacheKeysToIntegrities = new Map();
|
|
1819
2003
|
_strategy;
|
|
1820
2004
|
_options;
|
|
1821
2005
|
_routeOptions;
|
|
1822
|
-
constructor(
|
|
1823
|
-
const { strategyOptions, routeOptions, controllerOptions } = parsePrecacheOptions(this, precacheOptions);
|
|
2006
|
+
constructor(precacheOptions){
|
|
2007
|
+
const { entries, strategyOptions, routeOptions, extensionOptions: controllerOptions } = parsePrecacheOptions(this, precacheOptions);
|
|
1824
2008
|
this.addToCacheList(entries);
|
|
1825
2009
|
this._strategy = new PrecacheStrategy(strategyOptions);
|
|
1826
2010
|
this._options = controllerOptions;
|
|
1827
2011
|
this._routeOptions = routeOptions;
|
|
1828
|
-
this.install = this.install.bind(this);
|
|
1829
|
-
this.activate = this.activate.bind(this);
|
|
1830
2012
|
}
|
|
1831
2013
|
get strategy() {
|
|
1832
2014
|
return this._strategy;
|
|
@@ -1875,16 +2057,16 @@ class PrecacheController {
|
|
|
1875
2057
|
}
|
|
1876
2058
|
}
|
|
1877
2059
|
}
|
|
1878
|
-
init(serwist) {
|
|
1879
|
-
|
|
2060
|
+
init({ serwist }) {
|
|
2061
|
+
registerRoute(serwist, new PrecacheRoute(this, this._routeOptions));
|
|
1880
2062
|
if (this._options.navigateFallback) {
|
|
1881
|
-
|
|
2063
|
+
registerRoute(serwist, new NavigationRoute(this.createHandlerBoundToUrl(this._options.navigateFallback), {
|
|
1882
2064
|
allowlist: this._options.navigateFallbackAllowlist,
|
|
1883
2065
|
denylist: this._options.navigateFallbackDenylist
|
|
1884
2066
|
}));
|
|
1885
2067
|
}
|
|
1886
2068
|
}
|
|
1887
|
-
async install(event) {
|
|
2069
|
+
async install({ event }) {
|
|
1888
2070
|
const installReportPlugin = new PrecacheInstallReportPlugin();
|
|
1889
2071
|
this._strategy.plugins.push(installReportPlugin);
|
|
1890
2072
|
await parallel(this._options.concurrency, Array.from(this._urlsToCacheKeys.entries()), async ([url, cacheKey])=>{
|
|
@@ -1948,131 +2130,151 @@ class PrecacheController {
|
|
|
1948
2130
|
}
|
|
1949
2131
|
return undefined;
|
|
1950
2132
|
}
|
|
1951
|
-
createHandlerBoundToUrl(url) {
|
|
1952
|
-
const cacheKey = this.getPrecacheKeyForUrl(url);
|
|
1953
|
-
if (!cacheKey) {
|
|
1954
|
-
throw new SerwistError("non-precached-url", {
|
|
1955
|
-
url
|
|
1956
|
-
});
|
|
1957
|
-
}
|
|
1958
|
-
return (options)=>{
|
|
1959
|
-
options.request = new Request(url);
|
|
1960
|
-
options.params = {
|
|
1961
|
-
cacheKey,
|
|
1962
|
-
...options.params
|
|
1963
|
-
};
|
|
1964
|
-
return this._strategy.handle(options);
|
|
1965
|
-
};
|
|
1966
|
-
}
|
|
2133
|
+
createHandlerBoundToUrl(url) {
|
|
2134
|
+
const cacheKey = this.getPrecacheKeyForUrl(url);
|
|
2135
|
+
if (!cacheKey) {
|
|
2136
|
+
throw new SerwistError("non-precached-url", {
|
|
2137
|
+
url
|
|
2138
|
+
});
|
|
2139
|
+
}
|
|
2140
|
+
return (options)=>{
|
|
2141
|
+
options.request = new Request(url);
|
|
2142
|
+
options.params = {
|
|
2143
|
+
cacheKey,
|
|
2144
|
+
...options.params
|
|
2145
|
+
};
|
|
2146
|
+
return this._strategy.handle(options);
|
|
2147
|
+
};
|
|
2148
|
+
}
|
|
2149
|
+
}
|
|
2150
|
+
|
|
2151
|
+
class PrecacheFallbackPlugin {
|
|
2152
|
+
_fallbackUrls;
|
|
2153
|
+
_serwist;
|
|
2154
|
+
constructor({ fallbackUrls, serwist }){
|
|
2155
|
+
this._fallbackUrls = fallbackUrls;
|
|
2156
|
+
this._serwist = serwist;
|
|
2157
|
+
}
|
|
2158
|
+
async handlerDidError(param) {
|
|
2159
|
+
for (const fallback of this._fallbackUrls){
|
|
2160
|
+
if (typeof fallback === "string") {
|
|
2161
|
+
const fallbackResponse = handleRequest(this._serwist, {
|
|
2162
|
+
request: new Request(fallback, {
|
|
2163
|
+
credentials: "same-origin"
|
|
2164
|
+
}),
|
|
2165
|
+
event: param.event
|
|
2166
|
+
});
|
|
2167
|
+
if (fallbackResponse !== undefined) {
|
|
2168
|
+
return await fallbackResponse;
|
|
2169
|
+
}
|
|
2170
|
+
} else if (fallback.matcher(param)) {
|
|
2171
|
+
const fallbackResponse = handleRequest(this._serwist, {
|
|
2172
|
+
request: new Request(fallback.url, {
|
|
2173
|
+
credentials: "same-origin"
|
|
2174
|
+
}),
|
|
2175
|
+
event: param.event
|
|
2176
|
+
});
|
|
2177
|
+
if (fallbackResponse !== undefined) {
|
|
2178
|
+
return await fallbackResponse;
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
return undefined;
|
|
2183
|
+
}
|
|
1967
2184
|
}
|
|
1968
2185
|
|
|
1969
|
-
class
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
this.
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
this.
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
if (!!importScripts && importScripts.length > 0) self.importScripts(...importScripts);
|
|
1984
|
-
if (navigationPreload) enableNavigationPreload();
|
|
1985
|
-
if (cacheId !== undefined) {
|
|
1986
|
-
setCacheNameDetails({
|
|
1987
|
-
prefix: cacheId
|
|
2186
|
+
class RuntimeCache {
|
|
2187
|
+
_entries;
|
|
2188
|
+
_options;
|
|
2189
|
+
constructor(entries, options = {}){
|
|
2190
|
+
this._entries = entries;
|
|
2191
|
+
this._options = options;
|
|
2192
|
+
this.init = this.init.bind(this);
|
|
2193
|
+
this.install = this.install.bind(this);
|
|
2194
|
+
}
|
|
2195
|
+
init({ serwist }) {
|
|
2196
|
+
if (this._options.fallbacks !== undefined) {
|
|
2197
|
+
const fallbackPlugin = new PrecacheFallbackPlugin({
|
|
2198
|
+
fallbackUrls: this._options.fallbacks.entries,
|
|
2199
|
+
serwist
|
|
1988
2200
|
});
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
} else {
|
|
1993
|
-
self.addEventListener("message", (event)=>{
|
|
1994
|
-
if (event.data && event.data.type === "SKIP_WAITING") {
|
|
1995
|
-
self.skipWaiting();
|
|
2201
|
+
this._entries.forEach((cacheEntry)=>{
|
|
2202
|
+
if (cacheEntry.handler instanceof Strategy && !cacheEntry.handler.plugins.some((plugin)=>"handlerDidError" in plugin)) {
|
|
2203
|
+
cacheEntry.handler.plugins.push(fallbackPlugin);
|
|
1996
2204
|
}
|
|
1997
2205
|
});
|
|
1998
2206
|
}
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
if (runtimeCaching) {
|
|
2002
|
-
if (!this._controllers?.some((controller)=>controller instanceof RuntimeCacheController)) {
|
|
2003
|
-
this._controllers.unshift(new RuntimeCacheController(runtimeCaching, {
|
|
2004
|
-
fallbacks
|
|
2005
|
-
}));
|
|
2006
|
-
} else if (process.env.NODE_ENV !== "production") {
|
|
2007
|
-
logger.warn("You have migrated to the Controller pattern, so setting `runtimeCaching` is a no-op.");
|
|
2008
|
-
}
|
|
2207
|
+
for (const entry of this._entries){
|
|
2208
|
+
registerCapture(serwist, entry.matcher, entry.handler, entry.method);
|
|
2009
2209
|
}
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2210
|
+
}
|
|
2211
|
+
async install({ event, serwist }) {
|
|
2212
|
+
const concurrency = this._options.warmOptions?.concurrency ?? 10;
|
|
2213
|
+
if (this._options.warmEntries) {
|
|
2214
|
+
await parallel(concurrency, this._options.warmEntries, async (entry)=>{
|
|
2215
|
+
const request = entry instanceof Request ? entry : typeof entry === "string" ? new Request(entry, {
|
|
2216
|
+
credentials: "same-origin"
|
|
2217
|
+
}) : new Request(entry.url, {
|
|
2218
|
+
integrity: entry.integrity,
|
|
2219
|
+
credentials: "same-origin"
|
|
2015
2220
|
});
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
serwist: this
|
|
2221
|
+
await handleRequest(serwist, {
|
|
2222
|
+
request,
|
|
2223
|
+
event
|
|
2020
2224
|
});
|
|
2021
|
-
}
|
|
2022
|
-
}
|
|
2023
|
-
for (const callback of this.iterateControllers("init")){
|
|
2024
|
-
callback(this);
|
|
2025
|
-
}
|
|
2026
|
-
if (disableDevLogs$1) disableDevLogs();
|
|
2027
|
-
}
|
|
2028
|
-
*iterateControllers(name) {
|
|
2029
|
-
if (!this._controllers) return;
|
|
2030
|
-
for (const controller of this._controllers){
|
|
2031
|
-
if (typeof controller[name] === "function") {
|
|
2032
|
-
yield controller[name];
|
|
2033
|
-
}
|
|
2225
|
+
});
|
|
2034
2226
|
}
|
|
2035
2227
|
}
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
get precacheStrategy() {
|
|
2040
|
-
return this._precacheController.strategy;
|
|
2041
|
-
}
|
|
2042
|
-
get routes() {
|
|
2043
|
-
return this._routes;
|
|
2044
|
-
}
|
|
2045
|
-
addEventListeners() {
|
|
2046
|
-
self.addEventListener("install", this.handleInstall);
|
|
2047
|
-
self.addEventListener("activate", this.handleActivate);
|
|
2048
|
-
self.addEventListener("fetch", this.handleFetch);
|
|
2049
|
-
self.addEventListener("message", this.handleCache);
|
|
2228
|
+
warmRuntimeCache(entries) {
|
|
2229
|
+
if (!this._options.warmEntries) this._options.warmEntries = [];
|
|
2230
|
+
this._options.warmEntries.push(...entries);
|
|
2050
2231
|
}
|
|
2051
|
-
|
|
2232
|
+
}
|
|
2233
|
+
|
|
2234
|
+
const addEventListeners = (state)=>{
|
|
2235
|
+
self.addEventListener("install", createInstallHandler(state));
|
|
2236
|
+
self.addEventListener("activate", createActivateHandler(state));
|
|
2237
|
+
self.addEventListener("fetch", createFetchHandler(state));
|
|
2238
|
+
self.addEventListener("message", createCacheHandler(state));
|
|
2239
|
+
};
|
|
2240
|
+
const createInstallHandler = (state)=>{
|
|
2241
|
+
return (event)=>{
|
|
2052
2242
|
return waitUntil(event, async ()=>{
|
|
2053
|
-
for (const callback of
|
|
2054
|
-
await callback(
|
|
2243
|
+
for (const callback of iterateExtensions(state, "install")){
|
|
2244
|
+
await callback({
|
|
2245
|
+
event,
|
|
2246
|
+
serwist: state
|
|
2247
|
+
});
|
|
2055
2248
|
}
|
|
2056
2249
|
});
|
|
2057
|
-
}
|
|
2058
|
-
|
|
2250
|
+
};
|
|
2251
|
+
};
|
|
2252
|
+
const createActivateHandler = (state)=>{
|
|
2253
|
+
return (event)=>{
|
|
2059
2254
|
return waitUntil(event, async ()=>{
|
|
2060
|
-
for (const callback of
|
|
2061
|
-
await callback(
|
|
2255
|
+
for (const callback of iterateExtensions(state, "activate")){
|
|
2256
|
+
await callback({
|
|
2257
|
+
event,
|
|
2258
|
+
serwist: state
|
|
2259
|
+
});
|
|
2062
2260
|
}
|
|
2063
2261
|
});
|
|
2064
|
-
}
|
|
2065
|
-
|
|
2262
|
+
};
|
|
2263
|
+
};
|
|
2264
|
+
const createFetchHandler = (state)=>{
|
|
2265
|
+
return (event)=>{
|
|
2066
2266
|
const { request } = event;
|
|
2067
|
-
const responsePromise =
|
|
2267
|
+
const responsePromise = handleRequest(state, {
|
|
2068
2268
|
request,
|
|
2069
2269
|
event
|
|
2070
2270
|
});
|
|
2071
2271
|
if (responsePromise) {
|
|
2072
2272
|
event.respondWith(responsePromise);
|
|
2073
2273
|
}
|
|
2074
|
-
}
|
|
2075
|
-
|
|
2274
|
+
};
|
|
2275
|
+
};
|
|
2276
|
+
const createCacheHandler = (state)=>{
|
|
2277
|
+
return (event)=>{
|
|
2076
2278
|
if (event.data && event.data.type === "CACHE_URLS") {
|
|
2077
2279
|
const { payload } = event.data;
|
|
2078
2280
|
if (process.env.NODE_ENV !== "production") {
|
|
@@ -2085,7 +2287,7 @@ class Serwist {
|
|
|
2085
2287
|
} else {
|
|
2086
2288
|
request = new Request(...entry);
|
|
2087
2289
|
}
|
|
2088
|
-
return
|
|
2290
|
+
return handleRequest(state, {
|
|
2089
2291
|
request,
|
|
2090
2292
|
event
|
|
2091
2293
|
});
|
|
@@ -2095,218 +2297,170 @@ class Serwist {
|
|
|
2095
2297
|
void requestPromises.then(()=>event.ports[0].postMessage(true));
|
|
2096
2298
|
}
|
|
2097
2299
|
}
|
|
2300
|
+
};
|
|
2301
|
+
};
|
|
2302
|
+
const setDefaultHandler = (state, handler, method = defaultMethod)=>{
|
|
2303
|
+
state.defaultHandlerMap.set(method, normalizeHandler(handler));
|
|
2304
|
+
};
|
|
2305
|
+
const setCatchHandler = (state, handler)=>{
|
|
2306
|
+
state.catchHandler = normalizeHandler(handler);
|
|
2307
|
+
};
|
|
2308
|
+
function* iterateExtensions(state, name) {
|
|
2309
|
+
if (!state.extensions) return;
|
|
2310
|
+
for (const controller of state.extensions){
|
|
2311
|
+
if (typeof controller[name] === "function") {
|
|
2312
|
+
const callback = (param)=>{
|
|
2313
|
+
controller[name](param);
|
|
2314
|
+
};
|
|
2315
|
+
yield callback;
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
|
|
2320
|
+
const createSerwist = ({ precache, skipWaiting = false, importScripts, navigationPreload = false, cacheId, clientsClaim: clientsClaim$1 = false, disableDevLogs: disableDevLogs$1 = false, extensions = [] } = {})=>{
|
|
2321
|
+
if (importScripts?.length) self.importScripts(...importScripts);
|
|
2322
|
+
if (navigationPreload) enableNavigationPreload();
|
|
2323
|
+
if (cacheId) setCacheNameDetails({
|
|
2324
|
+
prefix: cacheId
|
|
2325
|
+
});
|
|
2326
|
+
if (skipWaiting) {
|
|
2327
|
+
self.skipWaiting();
|
|
2328
|
+
} else {
|
|
2329
|
+
self.addEventListener("message", (event)=>{
|
|
2330
|
+
if (event.data?.type === "SKIP_WAITING") {
|
|
2331
|
+
self.skipWaiting();
|
|
2332
|
+
}
|
|
2333
|
+
});
|
|
2334
|
+
}
|
|
2335
|
+
if (clientsClaim$1) clientsClaim();
|
|
2336
|
+
const precacheExtension = new Precache(precache ?? {
|
|
2337
|
+
entries: []
|
|
2338
|
+
});
|
|
2339
|
+
const routes = new Map();
|
|
2340
|
+
const defaultHandlerMap = new Map();
|
|
2341
|
+
const exts = [
|
|
2342
|
+
precacheExtension,
|
|
2343
|
+
...extensions
|
|
2344
|
+
];
|
|
2345
|
+
const state = {
|
|
2346
|
+
get routes () {
|
|
2347
|
+
return routes;
|
|
2348
|
+
},
|
|
2349
|
+
get defaultHandlerMap () {
|
|
2350
|
+
return defaultHandlerMap;
|
|
2351
|
+
},
|
|
2352
|
+
get precache () {
|
|
2353
|
+
return precacheExtension;
|
|
2354
|
+
},
|
|
2355
|
+
get extensions () {
|
|
2356
|
+
return exts;
|
|
2357
|
+
}
|
|
2358
|
+
};
|
|
2359
|
+
for (const callback of iterateExtensions(state, "init")){
|
|
2360
|
+
callback({
|
|
2361
|
+
serwist: state
|
|
2362
|
+
});
|
|
2363
|
+
}
|
|
2364
|
+
if (disableDevLogs$1) disableDevLogs();
|
|
2365
|
+
return state;
|
|
2366
|
+
};
|
|
2367
|
+
|
|
2368
|
+
class Serwist {
|
|
2369
|
+
_state;
|
|
2370
|
+
_installHandler;
|
|
2371
|
+
_activateHandler;
|
|
2372
|
+
_fetchHandler;
|
|
2373
|
+
_cacheHandler;
|
|
2374
|
+
constructor({ precacheEntries, precacheOptions, skipWaiting = false, importScripts, navigationPreload = false, cacheId, clientsClaim = false, runtimeCaching, offlineAnalyticsConfig, disableDevLogs = false, fallbacks, extensions } = {}){
|
|
2375
|
+
this.handleInstall = this.handleInstall.bind(this);
|
|
2376
|
+
this.handleActivate = this.handleActivate.bind(this);
|
|
2377
|
+
this.handleFetch = this.handleFetch.bind(this);
|
|
2378
|
+
this.handleCache = this.handleCache.bind(this);
|
|
2379
|
+
this._state = createSerwist({
|
|
2380
|
+
precache: {
|
|
2381
|
+
entries: precacheEntries ?? [],
|
|
2382
|
+
...precacheOptions
|
|
2383
|
+
},
|
|
2384
|
+
extensions: [
|
|
2385
|
+
!extensions?.some((ext)=>ext instanceof RuntimeCache) && runtimeCaching !== undefined ? new RuntimeCache(runtimeCaching, {
|
|
2386
|
+
fallbacks
|
|
2387
|
+
}) : undefined,
|
|
2388
|
+
!extensions?.some((ext)=>ext instanceof GoogleAnalytics) && offlineAnalyticsConfig !== undefined ? typeof offlineAnalyticsConfig === "boolean" ? offlineAnalyticsConfig ? new GoogleAnalytics() : undefined : new GoogleAnalytics(offlineAnalyticsConfig) : undefined,
|
|
2389
|
+
...extensions ?? []
|
|
2390
|
+
].filter((extension)=>extension !== undefined),
|
|
2391
|
+
skipWaiting,
|
|
2392
|
+
importScripts,
|
|
2393
|
+
navigationPreload,
|
|
2394
|
+
cacheId,
|
|
2395
|
+
clientsClaim,
|
|
2396
|
+
disableDevLogs
|
|
2397
|
+
});
|
|
2398
|
+
this._installHandler = createInstallHandler(this._state);
|
|
2399
|
+
this._activateHandler = createActivateHandler(this._state);
|
|
2400
|
+
this._fetchHandler = createFetchHandler(this._state);
|
|
2401
|
+
this._cacheHandler = createCacheHandler(this._state);
|
|
2402
|
+
}
|
|
2403
|
+
get precache() {
|
|
2404
|
+
return this._state.precache;
|
|
2405
|
+
}
|
|
2406
|
+
get precacheStrategy() {
|
|
2407
|
+
return this._state.precache.strategy;
|
|
2408
|
+
}
|
|
2409
|
+
get routes() {
|
|
2410
|
+
return this._state.routes;
|
|
2411
|
+
}
|
|
2412
|
+
get state() {
|
|
2413
|
+
return this._state;
|
|
2414
|
+
}
|
|
2415
|
+
addEventListeners() {
|
|
2416
|
+
self.addEventListener("install", this._installHandler);
|
|
2417
|
+
self.addEventListener("activate", this._activateHandler);
|
|
2418
|
+
self.addEventListener("fetch", this._fetchHandler);
|
|
2419
|
+
self.addEventListener("message", this._cacheHandler);
|
|
2098
2420
|
}
|
|
2099
|
-
|
|
2100
|
-
this.
|
|
2421
|
+
handleInstall(event) {
|
|
2422
|
+
return this._installHandler(event);
|
|
2423
|
+
}
|
|
2424
|
+
handleActivate(event) {
|
|
2425
|
+
return this._activateHandler(event);
|
|
2426
|
+
}
|
|
2427
|
+
handleFetch(event) {
|
|
2428
|
+
return this._fetchHandler(event);
|
|
2429
|
+
}
|
|
2430
|
+
handleCache(event) {
|
|
2431
|
+
return this._cacheHandler(event);
|
|
2432
|
+
}
|
|
2433
|
+
setDefaultHandler(handler, method) {
|
|
2434
|
+
setDefaultHandler(this._state, handler, method);
|
|
2101
2435
|
}
|
|
2102
2436
|
setCatchHandler(handler) {
|
|
2103
|
-
this.
|
|
2437
|
+
setCatchHandler(this._state, handler);
|
|
2104
2438
|
}
|
|
2105
2439
|
registerCapture(capture, handler, method) {
|
|
2106
|
-
|
|
2107
|
-
this.registerRoute(route);
|
|
2108
|
-
return route;
|
|
2440
|
+
return registerCapture(this._state, capture, handler, method);
|
|
2109
2441
|
}
|
|
2110
2442
|
registerRoute(route) {
|
|
2111
|
-
|
|
2112
|
-
finalAssertExports.isType(route, "object", {
|
|
2113
|
-
moduleName: "serwist",
|
|
2114
|
-
className: "Serwist",
|
|
2115
|
-
funcName: "registerRoute",
|
|
2116
|
-
paramName: "route"
|
|
2117
|
-
});
|
|
2118
|
-
finalAssertExports.hasMethod(route, "match", {
|
|
2119
|
-
moduleName: "serwist",
|
|
2120
|
-
className: "Serwist",
|
|
2121
|
-
funcName: "registerRoute",
|
|
2122
|
-
paramName: "route"
|
|
2123
|
-
});
|
|
2124
|
-
finalAssertExports.isType(route.handler, "object", {
|
|
2125
|
-
moduleName: "serwist",
|
|
2126
|
-
className: "Serwist",
|
|
2127
|
-
funcName: "registerRoute",
|
|
2128
|
-
paramName: "route"
|
|
2129
|
-
});
|
|
2130
|
-
finalAssertExports.hasMethod(route.handler, "handle", {
|
|
2131
|
-
moduleName: "serwist",
|
|
2132
|
-
className: "Serwist",
|
|
2133
|
-
funcName: "registerRoute",
|
|
2134
|
-
paramName: "route.handler"
|
|
2135
|
-
});
|
|
2136
|
-
finalAssertExports.isType(route.method, "string", {
|
|
2137
|
-
moduleName: "serwist",
|
|
2138
|
-
className: "Serwist",
|
|
2139
|
-
funcName: "registerRoute",
|
|
2140
|
-
paramName: "route.method"
|
|
2141
|
-
});
|
|
2142
|
-
}
|
|
2143
|
-
if (!this._routes.has(route.method)) {
|
|
2144
|
-
this._routes.set(route.method, []);
|
|
2145
|
-
}
|
|
2146
|
-
this._routes.get(route.method).push(route);
|
|
2443
|
+
registerRoute(this._state, route);
|
|
2147
2444
|
}
|
|
2148
2445
|
unregisterRoute(route) {
|
|
2149
|
-
|
|
2150
|
-
throw new SerwistError("unregister-route-but-not-found-with-method", {
|
|
2151
|
-
method: route.method
|
|
2152
|
-
});
|
|
2153
|
-
}
|
|
2154
|
-
const routeIndex = this._routes.get(route.method).indexOf(route);
|
|
2155
|
-
if (routeIndex > -1) {
|
|
2156
|
-
this._routes.get(route.method).splice(routeIndex, 1);
|
|
2157
|
-
} else {
|
|
2158
|
-
throw new SerwistError("unregister-route-route-not-registered");
|
|
2159
|
-
}
|
|
2446
|
+
unregisterRoute(this._state, route);
|
|
2160
2447
|
}
|
|
2161
2448
|
handleRequest({ request, event }) {
|
|
2162
|
-
|
|
2163
|
-
finalAssertExports.isInstance(request, Request, {
|
|
2164
|
-
moduleName: "serwist",
|
|
2165
|
-
className: "Serwist",
|
|
2166
|
-
funcName: "handleRequest",
|
|
2167
|
-
paramName: "options.request"
|
|
2168
|
-
});
|
|
2169
|
-
}
|
|
2170
|
-
const url = new URL(request.url, location.href);
|
|
2171
|
-
if (!url.protocol.startsWith("http")) {
|
|
2172
|
-
if (process.env.NODE_ENV !== "production") {
|
|
2173
|
-
logger.debug("Router only supports URLs that start with 'http'.");
|
|
2174
|
-
}
|
|
2175
|
-
return;
|
|
2176
|
-
}
|
|
2177
|
-
const sameOrigin = url.origin === location.origin;
|
|
2178
|
-
const { params, route } = this.findMatchingRoute({
|
|
2179
|
-
event,
|
|
2449
|
+
return handleRequest(this._state, {
|
|
2180
2450
|
request,
|
|
2181
|
-
|
|
2182
|
-
url
|
|
2451
|
+
event
|
|
2183
2452
|
});
|
|
2184
|
-
let handler = route?.handler;
|
|
2185
|
-
const debugMessages = [];
|
|
2186
|
-
if (process.env.NODE_ENV !== "production") {
|
|
2187
|
-
if (handler) {
|
|
2188
|
-
debugMessages.push([
|
|
2189
|
-
"Found a route to handle this request:",
|
|
2190
|
-
route
|
|
2191
|
-
]);
|
|
2192
|
-
if (params) {
|
|
2193
|
-
debugMessages.push([
|
|
2194
|
-
`Passing the following params to the route's handler:`,
|
|
2195
|
-
params
|
|
2196
|
-
]);
|
|
2197
|
-
}
|
|
2198
|
-
}
|
|
2199
|
-
}
|
|
2200
|
-
const method = request.method;
|
|
2201
|
-
if (!handler && this._defaultHandlerMap.has(method)) {
|
|
2202
|
-
if (process.env.NODE_ENV !== "production") {
|
|
2203
|
-
debugMessages.push(`Failed to find a matching route. Falling back to the default handler for ${method}.`);
|
|
2204
|
-
}
|
|
2205
|
-
handler = this._defaultHandlerMap.get(method);
|
|
2206
|
-
}
|
|
2207
|
-
if (!handler) {
|
|
2208
|
-
if (process.env.NODE_ENV !== "production") {
|
|
2209
|
-
logger.debug(`No route found for: ${getFriendlyURL(url)}`);
|
|
2210
|
-
}
|
|
2211
|
-
return;
|
|
2212
|
-
}
|
|
2213
|
-
if (process.env.NODE_ENV !== "production") {
|
|
2214
|
-
logger.groupCollapsed(`Router is responding to: ${getFriendlyURL(url)}`);
|
|
2215
|
-
for (const msg of debugMessages){
|
|
2216
|
-
if (Array.isArray(msg)) {
|
|
2217
|
-
logger.log(...msg);
|
|
2218
|
-
} else {
|
|
2219
|
-
logger.log(msg);
|
|
2220
|
-
}
|
|
2221
|
-
}
|
|
2222
|
-
logger.groupEnd();
|
|
2223
|
-
}
|
|
2224
|
-
let responsePromise;
|
|
2225
|
-
try {
|
|
2226
|
-
responsePromise = handler.handle({
|
|
2227
|
-
url,
|
|
2228
|
-
request,
|
|
2229
|
-
event,
|
|
2230
|
-
params
|
|
2231
|
-
});
|
|
2232
|
-
} catch (err) {
|
|
2233
|
-
responsePromise = Promise.reject(err);
|
|
2234
|
-
}
|
|
2235
|
-
const catchHandler = route?.catchHandler;
|
|
2236
|
-
if (responsePromise instanceof Promise && (this._catchHandler || catchHandler)) {
|
|
2237
|
-
responsePromise = responsePromise.catch(async (err)=>{
|
|
2238
|
-
if (catchHandler) {
|
|
2239
|
-
if (process.env.NODE_ENV !== "production") {
|
|
2240
|
-
logger.groupCollapsed(`Error thrown when responding to: ${getFriendlyURL(url)}. Falling back to route's Catch Handler.`);
|
|
2241
|
-
logger.error("Error thrown by:", route);
|
|
2242
|
-
logger.error(err);
|
|
2243
|
-
logger.groupEnd();
|
|
2244
|
-
}
|
|
2245
|
-
try {
|
|
2246
|
-
return await catchHandler.handle({
|
|
2247
|
-
url,
|
|
2248
|
-
request,
|
|
2249
|
-
event,
|
|
2250
|
-
params
|
|
2251
|
-
});
|
|
2252
|
-
} catch (catchErr) {
|
|
2253
|
-
if (catchErr instanceof Error) {
|
|
2254
|
-
err = catchErr;
|
|
2255
|
-
}
|
|
2256
|
-
}
|
|
2257
|
-
}
|
|
2258
|
-
if (this._catchHandler) {
|
|
2259
|
-
if (process.env.NODE_ENV !== "production") {
|
|
2260
|
-
logger.groupCollapsed(`Error thrown when responding to: ${getFriendlyURL(url)}. Falling back to global Catch Handler.`);
|
|
2261
|
-
logger.error("Error thrown by:", route);
|
|
2262
|
-
logger.error(err);
|
|
2263
|
-
logger.groupEnd();
|
|
2264
|
-
}
|
|
2265
|
-
return this._catchHandler.handle({
|
|
2266
|
-
url,
|
|
2267
|
-
request,
|
|
2268
|
-
event
|
|
2269
|
-
});
|
|
2270
|
-
}
|
|
2271
|
-
throw err;
|
|
2272
|
-
});
|
|
2273
|
-
}
|
|
2274
|
-
return responsePromise;
|
|
2275
2453
|
}
|
|
2276
2454
|
findMatchingRoute({ url, sameOrigin, request, event }) {
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
|
|
2283
|
-
request,
|
|
2284
|
-
event
|
|
2285
|
-
});
|
|
2286
|
-
if (matchResult) {
|
|
2287
|
-
if (process.env.NODE_ENV !== "production") {
|
|
2288
|
-
if (matchResult instanceof Promise) {
|
|
2289
|
-
logger.warn(`While routing ${getFriendlyURL(url)}, an async matchCallback function was used. Please convert the following route to use a synchronous matchCallback function:`, route);
|
|
2290
|
-
}
|
|
2291
|
-
}
|
|
2292
|
-
params = matchResult;
|
|
2293
|
-
if (Array.isArray(params) && params.length === 0) {
|
|
2294
|
-
params = undefined;
|
|
2295
|
-
} else if (matchResult.constructor === Object && Object.keys(matchResult).length === 0) {
|
|
2296
|
-
params = undefined;
|
|
2297
|
-
} else if (typeof matchResult === "boolean") {
|
|
2298
|
-
params = undefined;
|
|
2299
|
-
}
|
|
2300
|
-
return {
|
|
2301
|
-
route,
|
|
2302
|
-
params
|
|
2303
|
-
};
|
|
2304
|
-
}
|
|
2305
|
-
}
|
|
2306
|
-
return {};
|
|
2455
|
+
return findMatchingRoute(this._state, {
|
|
2456
|
+
url,
|
|
2457
|
+
sameOrigin,
|
|
2458
|
+
request,
|
|
2459
|
+
event
|
|
2460
|
+
});
|
|
2307
2461
|
}
|
|
2308
2462
|
addToPrecacheList(entries) {
|
|
2309
|
-
this.
|
|
2463
|
+
this.precache.addToCacheList(entries);
|
|
2310
2464
|
}
|
|
2311
2465
|
getUrlsToPrecacheKeys() {
|
|
2312
2466
|
return this.precache.getUrlsToPrecacheKeys();
|
|
@@ -2328,24 +2482,6 @@ class Serwist {
|
|
|
2328
2482
|
}
|
|
2329
2483
|
}
|
|
2330
2484
|
|
|
2331
|
-
const cacheNames = {
|
|
2332
|
-
get googleAnalytics () {
|
|
2333
|
-
return cacheNames$1.getGoogleAnalyticsName();
|
|
2334
|
-
},
|
|
2335
|
-
get precache () {
|
|
2336
|
-
return cacheNames$1.getPrecacheName();
|
|
2337
|
-
},
|
|
2338
|
-
get prefix () {
|
|
2339
|
-
return cacheNames$1.getPrefix();
|
|
2340
|
-
},
|
|
2341
|
-
get runtime () {
|
|
2342
|
-
return cacheNames$1.getRuntimeName();
|
|
2343
|
-
},
|
|
2344
|
-
get suffix () {
|
|
2345
|
-
return cacheNames$1.getSuffix();
|
|
2346
|
-
}
|
|
2347
|
-
};
|
|
2348
|
-
|
|
2349
2485
|
const BROADCAST_UPDATE_MESSAGE_TYPE = "CACHE_UPDATED";
|
|
2350
2486
|
const BROADCAST_UPDATE_MESSAGE_META = "serwist-broadcast-update";
|
|
2351
2487
|
const BROADCAST_UPDATE_DEFAULT_NOTIFY = true;
|
|
@@ -2747,20 +2883,6 @@ class CacheExpiration {
|
|
|
2747
2883
|
}
|
|
2748
2884
|
}
|
|
2749
2885
|
|
|
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
2886
|
class ExpirationPlugin {
|
|
2765
2887
|
_config;
|
|
2766
2888
|
_cacheExpirations;
|
|
@@ -2833,7 +2955,7 @@ class ExpirationPlugin {
|
|
|
2833
2955
|
})();
|
|
2834
2956
|
try {
|
|
2835
2957
|
event.waitUntil(done);
|
|
2836
|
-
} catch
|
|
2958
|
+
} catch {
|
|
2837
2959
|
if (process.env.NODE_ENV !== "production") {
|
|
2838
2960
|
if (event instanceof FetchEvent) {
|
|
2839
2961
|
logger.warn(`Unable to ensure service worker stays alive when updating cache entry for '${getFriendlyURL(event.request.url)}'.`);
|
|
@@ -3033,7 +3155,7 @@ class CacheFirst extends Strategy {
|
|
|
3033
3155
|
});
|
|
3034
3156
|
}
|
|
3035
3157
|
let response = await handler.cacheMatch(request);
|
|
3036
|
-
let error
|
|
3158
|
+
let error;
|
|
3037
3159
|
if (!response) {
|
|
3038
3160
|
if (process.env.NODE_ENV !== "production") {
|
|
3039
3161
|
logs.push(`No response found in the '${this.cacheName}' cache. Will respond with a network request.`);
|
|
@@ -3125,7 +3247,7 @@ class StaleWhileRevalidate extends Strategy {
|
|
|
3125
3247
|
const fetchAndCachePromise = handler.fetchAndCachePut(request).catch(()=>{});
|
|
3126
3248
|
void handler.waitUntil(fetchAndCachePromise);
|
|
3127
3249
|
let response = await handler.cacheMatch(request);
|
|
3128
|
-
let error
|
|
3250
|
+
let error;
|
|
3129
3251
|
if (response) {
|
|
3130
3252
|
if (process.env.NODE_ENV !== "production") {
|
|
3131
3253
|
logs.push(`Found a cached response in the '${this.cacheName}' cache. Will update with the network response in the background.`);
|
|
@@ -3160,4 +3282,4 @@ class StaleWhileRevalidate extends Strategy {
|
|
|
3160
3282
|
}
|
|
3161
3283
|
}
|
|
3162
3284
|
|
|
3163
|
-
export { BROADCAST_UPDATE_DEFAULT_HEADERS, BackgroundSyncPlugin, BackgroundSyncQueue, BackgroundSyncQueueStore, BroadcastCacheUpdate, BroadcastUpdatePlugin, CacheExpiration, CacheFirst, CacheOnly, CacheableResponse, CacheableResponsePlugin, ExpirationPlugin, NavigationRoute, NetworkFirst, NetworkOnly, PrecacheFallbackPlugin, PrecacheRoute, PrecacheStrategy, RangeRequestsPlugin, RegExpRoute, Route,
|
|
3285
|
+
export { BROADCAST_UPDATE_DEFAULT_HEADERS, BackgroundSyncPlugin, BackgroundSyncQueue, BackgroundSyncQueueStore, BroadcastCacheUpdate, BroadcastUpdatePlugin, CacheExpiration, CacheFirst, CacheOnly, CacheableResponse, CacheableResponsePlugin, ExpirationPlugin, GoogleAnalytics, NavigationRoute, NetworkFirst, NetworkOnly, PrecacheFallbackPlugin, PrecacheRoute, PrecacheStrategy, RangeRequestsPlugin, RegExpRoute, Route, RuntimeCache, Serwist, StaleWhileRevalidate, StorableRequest, Strategy, StrategyHandler, addEventListeners, cacheNames, copyResponse, createActivateHandler, createCacheHandler, createFetchHandler, createInstallHandler, createPartialResponse, createSerwist, disableDevLogs, disableNavigationPreload, enableNavigationPreload, findMatchingRoute, handleRequest, initializeGoogleAnalytics, isNavigationPreloadSupported, iterateExtensions, registerCapture, registerQuotaErrorCallback, registerRoute, responsesAreSame, setCacheNameDetails, setCatchHandler, setDefaultHandler, unregisterRoute };
|