@serwist/sw 9.0.0-preview.2 → 9.0.0-preview.21
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/abstractions/Serwist.d.ts +82 -0
- package/dist/abstractions/Serwist.d.ts.map +1 -0
- package/dist/{disableDevLogs.d.ts → abstractions/disableDevLogs.d.ts} +2 -0
- package/dist/abstractions/disableDevLogs.d.ts.map +1 -0
- package/dist/{fallbacks.d.ts → abstractions/fallbacks.d.ts} +9 -20
- package/dist/abstractions/fallbacks.d.ts.map +1 -0
- package/dist/{handlePrecaching.d.ts → abstractions/handlePrecaching.d.ts} +8 -10
- package/dist/abstractions/handlePrecaching.d.ts.map +1 -0
- package/dist/abstractions/installSerwist.d.ts +15 -0
- package/dist/abstractions/installSerwist.d.ts.map +1 -0
- package/dist/abstractions/navigationPreload.d.ts +20 -0
- package/dist/abstractions/navigationPreload.d.ts.map +1 -0
- package/dist/{registerRuntimeCaching.d.ts → abstractions/registerRuntimeCaching.d.ts} +2 -0
- package/dist/abstractions/registerRuntimeCaching.d.ts.map +1 -0
- package/dist/abstractions/types.d.ts +29 -0
- package/dist/abstractions/types.d.ts.map +1 -0
- package/dist/chunks/NavigationRoute.js +54 -0
- package/dist/chunks/NetworkOnly.js +193 -0
- package/dist/chunks/PrecacheFallbackPlugin.js +573 -0
- package/dist/chunks/Strategy.js +410 -0
- package/dist/chunks/precacheAndRoute.js +113 -0
- package/dist/chunks/registerRoute.js +7 -0
- package/dist/chunks/singletonPrecacheController.js +433 -0
- package/dist/chunks/singletonRouter.js +435 -0
- package/dist/index.d.ts +11 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +121 -124
- package/dist/index.plugins.d.ts +41 -0
- package/dist/index.plugins.d.ts.map +1 -0
- package/dist/index.plugins.js +671 -0
- package/dist/index.precaching.d.ts +25 -0
- package/dist/index.precaching.d.ts.map +1 -0
- package/dist/index.precaching.js +24 -0
- package/dist/index.routing.d.ts +15 -0
- package/dist/index.routing.d.ts.map +1 -0
- package/dist/index.routing.js +19 -0
- package/dist/index.strategies.d.ts +22 -0
- package/dist/index.strategies.d.ts.map +1 -0
- package/dist/index.strategies.js +146 -0
- package/dist/plugins/backgroundSync/BackgroundSyncPlugin.d.ts +23 -0
- package/dist/plugins/backgroundSync/BackgroundSyncPlugin.d.ts.map +1 -0
- package/dist/plugins/backgroundSync/Queue.d.ts +166 -0
- package/dist/plugins/backgroundSync/Queue.d.ts.map +1 -0
- package/dist/plugins/backgroundSync/QueueDb.d.ts +90 -0
- package/dist/plugins/backgroundSync/QueueDb.d.ts.map +1 -0
- package/dist/plugins/backgroundSync/QueueStore.d.ts +75 -0
- package/dist/plugins/backgroundSync/QueueStore.d.ts.map +1 -0
- package/dist/plugins/backgroundSync/StorableRequest.d.ts +51 -0
- package/dist/plugins/backgroundSync/StorableRequest.d.ts.map +1 -0
- package/dist/plugins/broadcastUpdate/BroadcastCacheUpdate.d.ts +45 -0
- package/dist/plugins/broadcastUpdate/BroadcastCacheUpdate.d.ts.map +1 -0
- package/dist/plugins/broadcastUpdate/BroadcastUpdatePlugin.d.ts +27 -0
- package/dist/plugins/broadcastUpdate/BroadcastUpdatePlugin.d.ts.map +1 -0
- package/dist/plugins/broadcastUpdate/constants.d.ts +5 -0
- package/dist/plugins/broadcastUpdate/constants.d.ts.map +1 -0
- package/dist/plugins/broadcastUpdate/responsesAreSame.d.ts +11 -0
- package/dist/plugins/broadcastUpdate/responsesAreSame.d.ts.map +1 -0
- package/dist/plugins/broadcastUpdate/types.d.ts +34 -0
- package/dist/plugins/broadcastUpdate/types.d.ts.map +1 -0
- package/dist/plugins/cacheableResponse/CacheableResponse.d.ts +40 -0
- package/dist/plugins/cacheableResponse/CacheableResponse.d.ts.map +1 -0
- package/dist/plugins/cacheableResponse/CacheableResponsePlugin.d.ts +27 -0
- package/dist/plugins/cacheableResponse/CacheableResponsePlugin.d.ts.map +1 -0
- package/dist/plugins/expiration/CacheExpiration.d.ts +66 -0
- package/dist/plugins/expiration/CacheExpiration.d.ts.map +1 -0
- package/dist/plugins/expiration/ExpirationPlugin.d.ts +116 -0
- package/dist/plugins/expiration/ExpirationPlugin.d.ts.map +1 -0
- package/dist/plugins/expiration/models/CacheTimestampsModel.d.ts +73 -0
- package/dist/plugins/expiration/models/CacheTimestampsModel.d.ts.map +1 -0
- package/dist/plugins/googleAnalytics/constants.d.ts +10 -0
- package/dist/plugins/googleAnalytics/constants.d.ts.map +1 -0
- package/dist/plugins/googleAnalytics/initialize.d.ts +34 -0
- package/dist/plugins/googleAnalytics/initialize.d.ts.map +1 -0
- package/dist/plugins/precaching/PrecacheFallbackPlugin.d.ts +54 -0
- package/dist/plugins/precaching/PrecacheFallbackPlugin.d.ts.map +1 -0
- package/dist/plugins/rangeRequests/RangeRequestsPlugin.d.ts +19 -0
- package/dist/plugins/rangeRequests/RangeRequestsPlugin.d.ts.map +1 -0
- package/dist/plugins/rangeRequests/createPartialResponse.d.ts +18 -0
- package/dist/plugins/rangeRequests/createPartialResponse.d.ts.map +1 -0
- package/dist/plugins/rangeRequests/utils/calculateEffectiveBoundaries.d.ts +14 -0
- package/dist/plugins/rangeRequests/utils/calculateEffectiveBoundaries.d.ts.map +1 -0
- package/dist/plugins/rangeRequests/utils/parseRangeHeader.d.ts +12 -0
- package/dist/plugins/rangeRequests/utils/parseRangeHeader.d.ts.map +1 -0
- package/dist/precaching/PrecacheController.d.ts +145 -0
- package/dist/precaching/PrecacheController.d.ts.map +1 -0
- package/dist/precaching/PrecacheRoute.d.ts +20 -0
- package/dist/precaching/PrecacheRoute.d.ts.map +1 -0
- package/dist/precaching/PrecacheStrategy.d.ts +68 -0
- package/dist/precaching/PrecacheStrategy.d.ts.map +1 -0
- package/dist/precaching/addPlugins.d.ts +8 -0
- package/dist/precaching/addPlugins.d.ts.map +1 -0
- package/dist/precaching/addRoute.d.ts +15 -0
- package/dist/precaching/addRoute.d.ts.map +1 -0
- package/dist/precaching/cleanupOutdatedCaches.d.ts +6 -0
- package/dist/precaching/cleanupOutdatedCaches.d.ts.map +1 -0
- package/dist/precaching/createHandlerBoundToURL.d.ts +17 -0
- package/dist/precaching/createHandlerBoundToURL.d.ts.map +1 -0
- package/dist/precaching/getCacheKeyForURL.d.ts +20 -0
- package/dist/precaching/getCacheKeyForURL.d.ts.map +1 -0
- package/dist/precaching/matchPrecache.d.ts +14 -0
- package/dist/precaching/matchPrecache.d.ts.map +1 -0
- package/dist/precaching/precache.d.ts +19 -0
- package/dist/precaching/precache.d.ts.map +1 -0
- package/dist/precaching/precacheAndRoute.d.ts +14 -0
- package/dist/precaching/precacheAndRoute.d.ts.map +1 -0
- package/dist/precaching/singletonPrecacheController.d.ts +38 -0
- package/dist/precaching/singletonPrecacheController.d.ts.map +1 -0
- package/dist/precaching/types.d.ts +37 -0
- package/dist/precaching/types.d.ts.map +1 -0
- package/dist/precaching/utils/PrecacheCacheKeyPlugin.d.ts +17 -0
- package/dist/precaching/utils/PrecacheCacheKeyPlugin.d.ts.map +1 -0
- package/dist/precaching/utils/PrecacheInstallReportPlugin.d.ts +15 -0
- package/dist/precaching/utils/PrecacheInstallReportPlugin.d.ts.map +1 -0
- package/dist/precaching/utils/createCacheKey.d.ts +16 -0
- package/dist/precaching/utils/createCacheKey.d.ts.map +1 -0
- package/dist/precaching/utils/deleteOutdatedCaches.d.ts +18 -0
- package/dist/precaching/utils/deleteOutdatedCaches.d.ts.map +1 -0
- package/dist/precaching/utils/generateURLVariations.d.ts +12 -0
- package/dist/precaching/utils/generateURLVariations.d.ts.map +1 -0
- package/dist/precaching/utils/getCacheKeyForURL.d.ts +14 -0
- package/dist/precaching/utils/getCacheKeyForURL.d.ts.map +1 -0
- package/dist/precaching/utils/printCleanupDetails.d.ts +6 -0
- package/dist/precaching/utils/printCleanupDetails.d.ts.map +1 -0
- package/dist/precaching/utils/printInstallDetails.d.ts +7 -0
- package/dist/precaching/utils/printInstallDetails.d.ts.map +1 -0
- package/dist/precaching/utils/removeIgnoredSearchParams.d.ts +12 -0
- package/dist/precaching/utils/removeIgnoredSearchParams.d.ts.map +1 -0
- package/dist/routing/NavigationRoute.d.ts +57 -0
- package/dist/routing/NavigationRoute.d.ts.map +1 -0
- package/dist/routing/RegExpRoute.d.ts +24 -0
- package/dist/routing/RegExpRoute.d.ts.map +1 -0
- package/dist/routing/Route.d.ts +33 -0
- package/dist/routing/Route.d.ts.map +1 -0
- package/dist/routing/Router.d.ts +150 -0
- package/dist/routing/Router.d.ts.map +1 -0
- package/dist/routing/parseRoute.d.ts +16 -0
- package/dist/routing/parseRoute.d.ts.map +1 -0
- package/dist/routing/registerRoute.d.ts +15 -0
- package/dist/routing/registerRoute.d.ts.map +1 -0
- package/dist/routing/setCatchHandler.d.ts +9 -0
- package/dist/routing/setCatchHandler.d.ts.map +1 -0
- package/dist/routing/setDefaultHandler.d.ts +12 -0
- package/dist/routing/setDefaultHandler.d.ts.map +1 -0
- package/dist/routing/singletonRouter.d.ts +47 -0
- package/dist/routing/singletonRouter.d.ts.map +1 -0
- package/dist/routing/unregisterRoute.d.ts +8 -0
- package/dist/routing/unregisterRoute.d.ts.map +1 -0
- package/dist/routing/utils/constants.d.ts +15 -0
- package/dist/routing/utils/constants.d.ts.map +1 -0
- package/dist/routing/utils/normalizeHandler.d.ts +10 -0
- package/dist/routing/utils/normalizeHandler.d.ts.map +1 -0
- package/dist/strategies/CacheFirst.d.ts +23 -0
- package/dist/strategies/CacheFirst.d.ts.map +1 -0
- package/dist/strategies/CacheOnly.d.ts +20 -0
- package/dist/strategies/CacheOnly.d.ts.map +1 -0
- package/dist/strategies/NetworkFirst.d.ts +61 -0
- package/dist/strategies/NetworkFirst.d.ts.map +1 -0
- package/dist/strategies/NetworkOnly.d.ts +32 -0
- package/dist/strategies/NetworkOnly.d.ts.map +1 -0
- package/dist/strategies/StaleWhileRevalidate.d.ts +35 -0
- package/dist/strategies/StaleWhileRevalidate.d.ts.map +1 -0
- package/dist/strategies/Strategy.d.ts +83 -0
- package/dist/strategies/Strategy.d.ts.map +1 -0
- package/dist/strategies/StrategyHandler.d.ts +189 -0
- package/dist/strategies/StrategyHandler.d.ts.map +1 -0
- package/dist/strategies/plugins/cacheOkAndOpaquePlugin.d.ts +3 -0
- package/dist/strategies/plugins/cacheOkAndOpaquePlugin.d.ts.map +1 -0
- package/dist/strategies/utils/messages.d.ts +5 -0
- package/dist/strategies/utils/messages.d.ts.map +1 -0
- package/package.json +42 -19
- package/src/abstractions/Serwist.ts +177 -0
- package/src/abstractions/disableDevLogs.ts +10 -0
- package/src/abstractions/fallbacks.ts +65 -0
- package/src/abstractions/handlePrecaching.ts +65 -0
- package/src/abstractions/installSerwist.ts +28 -0
- package/src/abstractions/navigationPreload.ts +64 -0
- package/src/abstractions/registerRuntimeCaching.ts +17 -0
- package/src/abstractions/types.ts +29 -0
- package/src/index.plugins.ts +95 -0
- package/src/index.precaching.ts +41 -0
- package/src/index.routing.ts +28 -0
- package/src/index.strategies.ts +26 -0
- package/src/index.ts +31 -9
- package/src/plugins/backgroundSync/BackgroundSyncPlugin.ts +39 -0
- package/src/plugins/backgroundSync/Queue.ts +438 -0
- package/src/plugins/backgroundSync/QueueDb.ts +176 -0
- package/src/plugins/backgroundSync/QueueStore.ts +161 -0
- package/src/plugins/backgroundSync/StorableRequest.ts +142 -0
- package/src/plugins/broadcastUpdate/BroadcastCacheUpdate.ts +159 -0
- package/src/plugins/broadcastUpdate/BroadcastUpdatePlugin.ts +43 -0
- package/src/plugins/broadcastUpdate/constants.ts +12 -0
- package/src/plugins/broadcastUpdate/responsesAreSame.ts +48 -0
- package/src/plugins/broadcastUpdate/types.ts +37 -0
- package/src/plugins/cacheableResponse/CacheableResponse.ts +141 -0
- package/src/plugins/cacheableResponse/CacheableResponsePlugin.ts +46 -0
- package/src/plugins/expiration/CacheExpiration.ts +192 -0
- package/src/plugins/expiration/ExpirationPlugin.ts +297 -0
- package/src/plugins/expiration/models/CacheTimestampsModel.ts +184 -0
- package/src/plugins/googleAnalytics/constants.ts +22 -0
- package/src/plugins/googleAnalytics/initialize.ts +213 -0
- package/src/plugins/precaching/PrecacheFallbackPlugin.ts +86 -0
- package/src/plugins/rangeRequests/RangeRequestsPlugin.ts +39 -0
- package/src/plugins/rangeRequests/createPartialResponse.ts +92 -0
- package/src/plugins/rangeRequests/utils/calculateEffectiveBoundaries.ts +58 -0
- package/src/plugins/rangeRequests/utils/parseRangeHeader.ts +54 -0
- package/src/precaching/PrecacheController.ts +332 -0
- package/src/precaching/PrecacheRoute.ts +50 -0
- package/src/precaching/PrecacheStrategy.ts +238 -0
- package/src/precaching/addPlugins.ts +21 -0
- package/src/precaching/addRoute.ts +30 -0
- package/src/precaching/cleanupOutdatedCaches.ts +33 -0
- package/src/precaching/createHandlerBoundToURL.ts +30 -0
- package/src/precaching/getCacheKeyForURL.ts +33 -0
- package/src/precaching/matchPrecache.ts +25 -0
- package/src/precaching/precache.ts +31 -0
- package/src/precaching/precacheAndRoute.ts +27 -0
- package/src/precaching/singletonPrecacheController.ts +57 -0
- package/src/precaching/types.ts +46 -0
- package/src/precaching/utils/PrecacheCacheKeyPlugin.ts +36 -0
- package/src/precaching/utils/PrecacheInstallReportPlugin.ts +49 -0
- package/src/precaching/utils/createCacheKey.ts +68 -0
- package/src/precaching/utils/deleteOutdatedCaches.ts +40 -0
- package/src/precaching/utils/generateURLVariations.ts +55 -0
- package/src/precaching/utils/getCacheKeyForURL.ts +36 -0
- package/src/precaching/utils/printCleanupDetails.ts +38 -0
- package/src/precaching/utils/printInstallDetails.ts +53 -0
- package/src/precaching/utils/removeIgnoredSearchParams.ts +29 -0
- package/src/routing/NavigationRoute.ts +119 -0
- package/src/routing/RegExpRoute.ts +74 -0
- package/src/routing/Route.ts +68 -0
- package/src/routing/Router.ts +481 -0
- package/src/routing/parseRoute.ts +78 -0
- package/src/routing/registerRoute.ts +27 -0
- package/src/routing/setCatchHandler.ts +21 -0
- package/src/routing/setDefaultHandler.ts +24 -0
- package/src/routing/singletonRouter.ts +76 -0
- package/src/routing/unregisterRoute.ts +11 -0
- package/src/routing/utils/constants.ts +24 -0
- package/src/routing/utils/normalizeHandler.ts +40 -0
- package/src/strategies/CacheFirst.ts +87 -0
- package/src/strategies/CacheOnly.ts +58 -0
- package/src/strategies/NetworkFirst.ts +228 -0
- package/src/strategies/NetworkOnly.ts +96 -0
- package/src/strategies/StaleWhileRevalidate.ts +109 -0
- package/src/strategies/Strategy.ts +202 -0
- package/src/strategies/StrategyHandler.ts +557 -0
- package/src/strategies/plugins/cacheOkAndOpaquePlugin.ts +26 -0
- package/src/strategies/utils/messages.ts +20 -0
- package/dist/disableDevLogs.d.ts.map +0 -1
- package/dist/fallbacks.d.ts.map +0 -1
- package/dist/handlePrecaching.d.ts.map +0 -1
- package/dist/installSerwist.d.ts +0 -21
- package/dist/installSerwist.d.ts.map +0 -1
- package/dist/registerRuntimeCaching.d.ts.map +0 -1
- package/dist/types.d.ts +0 -110
- package/dist/types.d.ts.map +0 -1
- package/src/disableDevLogs.ts +0 -10
- package/src/fallbacks.ts +0 -83
- package/src/handlePrecaching.ts +0 -65
- package/src/installSerwist.ts +0 -106
- package/src/registerRuntimeCaching.ts +0 -84
- package/src/types.ts +0 -113
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
export { B as BackgroundSyncPlugin, Q as BackgroundSyncQueue, a as BackgroundSyncQueueStore, P as PrecacheFallbackPlugin, S as StorableRequest, i as initializeGoogleAnalytics } from './chunks/PrecacheFallbackPlugin.js';
|
|
2
|
+
import { SerwistError, logger, assert, resultingClientExists, timeout, getFriendlyURL, privateCacheNames } from '@serwist/core/internal';
|
|
3
|
+
import { deleteDB, openDB } from 'idb';
|
|
4
|
+
import { registerQuotaErrorCallback } from '@serwist/core';
|
|
5
|
+
import './chunks/singletonRouter.js';
|
|
6
|
+
import './chunks/NetworkOnly.js';
|
|
7
|
+
import './chunks/Strategy.js';
|
|
8
|
+
import './chunks/singletonPrecacheController.js';
|
|
9
|
+
|
|
10
|
+
const CACHE_UPDATED_MESSAGE_TYPE = "CACHE_UPDATED";
|
|
11
|
+
const CACHE_UPDATED_MESSAGE_META = "serwist-broadcast-update";
|
|
12
|
+
const defaultNotifyAllClients = true;
|
|
13
|
+
const defaultHeadersToCheck = [
|
|
14
|
+
"content-length",
|
|
15
|
+
"etag",
|
|
16
|
+
"last-modified"
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
const responsesAreSame = (firstResponse, secondResponse, headersToCheck)=>{
|
|
20
|
+
if (process.env.NODE_ENV !== "production") {
|
|
21
|
+
if (!(firstResponse instanceof Response && secondResponse instanceof Response)) {
|
|
22
|
+
throw new SerwistError("invalid-responses-are-same-args");
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const atLeastOneHeaderAvailable = headersToCheck.some((header)=>{
|
|
26
|
+
return firstResponse.headers.has(header) && secondResponse.headers.has(header);
|
|
27
|
+
});
|
|
28
|
+
if (!atLeastOneHeaderAvailable) {
|
|
29
|
+
if (process.env.NODE_ENV !== "production") {
|
|
30
|
+
logger.warn("Unable to determine where the response has been updated because none of the headers that would be checked are present.");
|
|
31
|
+
logger.debug("Attempting to compare the following: ", firstResponse, secondResponse, headersToCheck);
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
return headersToCheck.every((header)=>{
|
|
36
|
+
const headerStateComparison = firstResponse.headers.has(header) === secondResponse.headers.has(header);
|
|
37
|
+
const headerValueComparison = firstResponse.headers.get(header) === secondResponse.headers.get(header);
|
|
38
|
+
return headerStateComparison && headerValueComparison;
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const isSafari = typeof navigator !== "undefined" && /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
|
|
43
|
+
const defaultPayloadGenerator = (data)=>{
|
|
44
|
+
return {
|
|
45
|
+
cacheName: data.cacheName,
|
|
46
|
+
updatedURL: data.request.url
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
class BroadcastCacheUpdate {
|
|
50
|
+
_headersToCheck;
|
|
51
|
+
_generatePayload;
|
|
52
|
+
_notifyAllClients;
|
|
53
|
+
constructor({ generatePayload, headersToCheck, notifyAllClients } = {}){
|
|
54
|
+
this._headersToCheck = headersToCheck || defaultHeadersToCheck;
|
|
55
|
+
this._generatePayload = generatePayload || defaultPayloadGenerator;
|
|
56
|
+
this._notifyAllClients = notifyAllClients ?? defaultNotifyAllClients;
|
|
57
|
+
}
|
|
58
|
+
async notifyIfUpdated(options) {
|
|
59
|
+
if (process.env.NODE_ENV !== "production") {
|
|
60
|
+
assert.isType(options.cacheName, "string", {
|
|
61
|
+
moduleName: "@serwist/broadcast-update",
|
|
62
|
+
className: "BroadcastCacheUpdate",
|
|
63
|
+
funcName: "notifyIfUpdated",
|
|
64
|
+
paramName: "cacheName"
|
|
65
|
+
});
|
|
66
|
+
assert.isInstance(options.newResponse, Response, {
|
|
67
|
+
moduleName: "@serwist/broadcast-update",
|
|
68
|
+
className: "BroadcastCacheUpdate",
|
|
69
|
+
funcName: "notifyIfUpdated",
|
|
70
|
+
paramName: "newResponse"
|
|
71
|
+
});
|
|
72
|
+
assert.isInstance(options.request, Request, {
|
|
73
|
+
moduleName: "@serwist/broadcast-update",
|
|
74
|
+
className: "BroadcastCacheUpdate",
|
|
75
|
+
funcName: "notifyIfUpdated",
|
|
76
|
+
paramName: "request"
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
if (!options.oldResponse) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
if (!responsesAreSame(options.oldResponse, options.newResponse, this._headersToCheck)) {
|
|
83
|
+
if (process.env.NODE_ENV !== "production") {
|
|
84
|
+
logger.log("Newer response found (and cached) for:", options.request.url);
|
|
85
|
+
}
|
|
86
|
+
const messageData = {
|
|
87
|
+
type: CACHE_UPDATED_MESSAGE_TYPE,
|
|
88
|
+
meta: CACHE_UPDATED_MESSAGE_META,
|
|
89
|
+
payload: this._generatePayload(options)
|
|
90
|
+
};
|
|
91
|
+
if (options.request.mode === "navigate") {
|
|
92
|
+
let resultingClientId;
|
|
93
|
+
if (options.event instanceof FetchEvent) {
|
|
94
|
+
resultingClientId = options.event.resultingClientId;
|
|
95
|
+
}
|
|
96
|
+
const resultingWin = await resultingClientExists(resultingClientId);
|
|
97
|
+
if (!resultingWin || isSafari) {
|
|
98
|
+
await timeout(3500);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (this._notifyAllClients) {
|
|
102
|
+
const windows = await self.clients.matchAll({
|
|
103
|
+
type: "window"
|
|
104
|
+
});
|
|
105
|
+
for (const win of windows){
|
|
106
|
+
win.postMessage(messageData);
|
|
107
|
+
}
|
|
108
|
+
} else {
|
|
109
|
+
if (options.event instanceof FetchEvent) {
|
|
110
|
+
const client = await self.clients.get(options.event.clientId);
|
|
111
|
+
client?.postMessage(messageData);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
class BroadcastUpdatePlugin {
|
|
119
|
+
_broadcastUpdate;
|
|
120
|
+
constructor(options){
|
|
121
|
+
this._broadcastUpdate = new BroadcastCacheUpdate(options);
|
|
122
|
+
}
|
|
123
|
+
cacheDidUpdate(options) {
|
|
124
|
+
void this._broadcastUpdate.notifyIfUpdated(options);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
class CacheableResponse {
|
|
129
|
+
_statuses;
|
|
130
|
+
_headers;
|
|
131
|
+
constructor(config = {}){
|
|
132
|
+
if (process.env.NODE_ENV !== "production") {
|
|
133
|
+
if (!(config.statuses || config.headers)) {
|
|
134
|
+
throw new SerwistError("statuses-or-headers-required", {
|
|
135
|
+
moduleName: "@serwist/cacheable-response",
|
|
136
|
+
className: "CacheableResponse",
|
|
137
|
+
funcName: "constructor"
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (config.statuses) {
|
|
141
|
+
assert.isArray(config.statuses, {
|
|
142
|
+
moduleName: "@serwist/cacheable-response",
|
|
143
|
+
className: "CacheableResponse",
|
|
144
|
+
funcName: "constructor",
|
|
145
|
+
paramName: "config.statuses"
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
if (config.headers) {
|
|
149
|
+
assert.isType(config.headers, "object", {
|
|
150
|
+
moduleName: "@serwist/cacheable-response",
|
|
151
|
+
className: "CacheableResponse",
|
|
152
|
+
funcName: "constructor",
|
|
153
|
+
paramName: "config.headers"
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
this._statuses = config.statuses;
|
|
158
|
+
if (config.headers) {
|
|
159
|
+
this._headers = new Headers(config.headers);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
isResponseCacheable(response) {
|
|
163
|
+
if (process.env.NODE_ENV !== "production") {
|
|
164
|
+
assert.isInstance(response, Response, {
|
|
165
|
+
moduleName: "@serwist/cacheable-response",
|
|
166
|
+
className: "CacheableResponse",
|
|
167
|
+
funcName: "isResponseCacheable",
|
|
168
|
+
paramName: "response"
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
let cacheable = true;
|
|
172
|
+
if (this._statuses) {
|
|
173
|
+
cacheable = this._statuses.includes(response.status);
|
|
174
|
+
}
|
|
175
|
+
if (this._headers && cacheable) {
|
|
176
|
+
for (const [headerName, headerValue] of this._headers.entries()){
|
|
177
|
+
if (response.headers.get(headerName) !== headerValue) {
|
|
178
|
+
cacheable = false;
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (process.env.NODE_ENV !== "production") {
|
|
184
|
+
if (!cacheable) {
|
|
185
|
+
logger.groupCollapsed(`The request for '${getFriendlyURL(response.url)}' returned a response that does not meet the criteria for being cached.`);
|
|
186
|
+
logger.groupCollapsed("View cacheability criteria here.");
|
|
187
|
+
logger.log(`Cacheable statuses: ${JSON.stringify(this._statuses)}`);
|
|
188
|
+
logger.log(`Cacheable headers: ${JSON.stringify(this._headers, null, 2)}`);
|
|
189
|
+
logger.groupEnd();
|
|
190
|
+
const logFriendlyHeaders = {};
|
|
191
|
+
response.headers.forEach((value, key)=>{
|
|
192
|
+
logFriendlyHeaders[key] = value;
|
|
193
|
+
});
|
|
194
|
+
logger.groupCollapsed("View response status and headers here.");
|
|
195
|
+
logger.log(`Response status: ${response.status}`);
|
|
196
|
+
logger.log(`Response headers: ${JSON.stringify(logFriendlyHeaders, null, 2)}`);
|
|
197
|
+
logger.groupEnd();
|
|
198
|
+
logger.groupCollapsed("View full response details here.");
|
|
199
|
+
logger.log(response.headers);
|
|
200
|
+
logger.log(response);
|
|
201
|
+
logger.groupEnd();
|
|
202
|
+
logger.groupEnd();
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return cacheable;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
class CacheableResponsePlugin {
|
|
210
|
+
_cacheableResponse;
|
|
211
|
+
constructor(config){
|
|
212
|
+
this._cacheableResponse = new CacheableResponse(config);
|
|
213
|
+
}
|
|
214
|
+
cacheWillUpdate = async ({ response })=>{
|
|
215
|
+
if (this._cacheableResponse.isResponseCacheable(response)) {
|
|
216
|
+
return response;
|
|
217
|
+
}
|
|
218
|
+
return null;
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const DB_NAME = "serwist-expiration";
|
|
223
|
+
const CACHE_OBJECT_STORE = "cache-entries";
|
|
224
|
+
const normalizeURL = (unNormalizedUrl)=>{
|
|
225
|
+
const url = new URL(unNormalizedUrl, location.href);
|
|
226
|
+
url.hash = "";
|
|
227
|
+
return url.href;
|
|
228
|
+
};
|
|
229
|
+
class CacheTimestampsModel {
|
|
230
|
+
_cacheName;
|
|
231
|
+
_db = null;
|
|
232
|
+
constructor(cacheName){
|
|
233
|
+
this._cacheName = cacheName;
|
|
234
|
+
}
|
|
235
|
+
_getId(url) {
|
|
236
|
+
return `${this._cacheName}|${normalizeURL(url)}`;
|
|
237
|
+
}
|
|
238
|
+
_upgradeDb(db) {
|
|
239
|
+
const objStore = db.createObjectStore(CACHE_OBJECT_STORE, {
|
|
240
|
+
keyPath: "id"
|
|
241
|
+
});
|
|
242
|
+
objStore.createIndex("cacheName", "cacheName", {
|
|
243
|
+
unique: false
|
|
244
|
+
});
|
|
245
|
+
objStore.createIndex("timestamp", "timestamp", {
|
|
246
|
+
unique: false
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
_upgradeDbAndDeleteOldDbs(db) {
|
|
250
|
+
this._upgradeDb(db);
|
|
251
|
+
if (this._cacheName) {
|
|
252
|
+
void deleteDB(this._cacheName);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async setTimestamp(url, timestamp) {
|
|
256
|
+
url = normalizeURL(url);
|
|
257
|
+
const entry = {
|
|
258
|
+
id: this._getId(url),
|
|
259
|
+
cacheName: this._cacheName,
|
|
260
|
+
url,
|
|
261
|
+
timestamp
|
|
262
|
+
};
|
|
263
|
+
const db = await this.getDb();
|
|
264
|
+
const tx = db.transaction(CACHE_OBJECT_STORE, "readwrite", {
|
|
265
|
+
durability: "relaxed"
|
|
266
|
+
});
|
|
267
|
+
await tx.store.put(entry);
|
|
268
|
+
await tx.done;
|
|
269
|
+
}
|
|
270
|
+
async getTimestamp(url) {
|
|
271
|
+
const db = await this.getDb();
|
|
272
|
+
const entry = await db.get(CACHE_OBJECT_STORE, this._getId(url));
|
|
273
|
+
return entry?.timestamp;
|
|
274
|
+
}
|
|
275
|
+
async expireEntries(minTimestamp, maxCount) {
|
|
276
|
+
const db = await this.getDb();
|
|
277
|
+
let cursor = await db.transaction(CACHE_OBJECT_STORE, "readwrite").store.index("timestamp").openCursor(null, "prev");
|
|
278
|
+
const urlsDeleted = [];
|
|
279
|
+
let entriesNotDeletedCount = 0;
|
|
280
|
+
while(cursor){
|
|
281
|
+
const result = cursor.value;
|
|
282
|
+
if (result.cacheName === this._cacheName) {
|
|
283
|
+
if (minTimestamp && result.timestamp < minTimestamp || maxCount && entriesNotDeletedCount >= maxCount) {
|
|
284
|
+
cursor.delete();
|
|
285
|
+
urlsDeleted.push(result.url);
|
|
286
|
+
} else {
|
|
287
|
+
entriesNotDeletedCount++;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
cursor = await cursor.continue();
|
|
291
|
+
}
|
|
292
|
+
return urlsDeleted;
|
|
293
|
+
}
|
|
294
|
+
async getDb() {
|
|
295
|
+
if (!this._db) {
|
|
296
|
+
this._db = await openDB(DB_NAME, 1, {
|
|
297
|
+
upgrade: this._upgradeDbAndDeleteOldDbs.bind(this)
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
return this._db;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
class CacheExpiration {
|
|
305
|
+
_isRunning = false;
|
|
306
|
+
_rerunRequested = false;
|
|
307
|
+
_maxEntries;
|
|
308
|
+
_maxAgeSeconds;
|
|
309
|
+
_matchOptions;
|
|
310
|
+
_cacheName;
|
|
311
|
+
_timestampModel;
|
|
312
|
+
constructor(cacheName, config = {}){
|
|
313
|
+
if (process.env.NODE_ENV !== "production") {
|
|
314
|
+
assert.isType(cacheName, "string", {
|
|
315
|
+
moduleName: "@serwist/expiration",
|
|
316
|
+
className: "CacheExpiration",
|
|
317
|
+
funcName: "constructor",
|
|
318
|
+
paramName: "cacheName"
|
|
319
|
+
});
|
|
320
|
+
if (!(config.maxEntries || config.maxAgeSeconds)) {
|
|
321
|
+
throw new SerwistError("max-entries-or-age-required", {
|
|
322
|
+
moduleName: "@serwist/expiration",
|
|
323
|
+
className: "CacheExpiration",
|
|
324
|
+
funcName: "constructor"
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
if (config.maxEntries) {
|
|
328
|
+
assert.isType(config.maxEntries, "number", {
|
|
329
|
+
moduleName: "@serwist/expiration",
|
|
330
|
+
className: "CacheExpiration",
|
|
331
|
+
funcName: "constructor",
|
|
332
|
+
paramName: "config.maxEntries"
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
if (config.maxAgeSeconds) {
|
|
336
|
+
assert.isType(config.maxAgeSeconds, "number", {
|
|
337
|
+
moduleName: "@serwist/expiration",
|
|
338
|
+
className: "CacheExpiration",
|
|
339
|
+
funcName: "constructor",
|
|
340
|
+
paramName: "config.maxAgeSeconds"
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
this._maxEntries = config.maxEntries;
|
|
345
|
+
this._maxAgeSeconds = config.maxAgeSeconds;
|
|
346
|
+
this._matchOptions = config.matchOptions;
|
|
347
|
+
this._cacheName = cacheName;
|
|
348
|
+
this._timestampModel = new CacheTimestampsModel(cacheName);
|
|
349
|
+
}
|
|
350
|
+
async expireEntries() {
|
|
351
|
+
if (this._isRunning) {
|
|
352
|
+
this._rerunRequested = true;
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
this._isRunning = true;
|
|
356
|
+
const minTimestamp = this._maxAgeSeconds ? Date.now() - this._maxAgeSeconds * 1000 : 0;
|
|
357
|
+
const urlsExpired = await this._timestampModel.expireEntries(minTimestamp, this._maxEntries);
|
|
358
|
+
const cache = await self.caches.open(this._cacheName);
|
|
359
|
+
for (const url of urlsExpired){
|
|
360
|
+
await cache.delete(url, this._matchOptions);
|
|
361
|
+
}
|
|
362
|
+
if (process.env.NODE_ENV !== "production") {
|
|
363
|
+
if (urlsExpired.length > 0) {
|
|
364
|
+
logger.groupCollapsed(`Expired ${urlsExpired.length} ` + `${urlsExpired.length === 1 ? "entry" : "entries"} and removed ` + `${urlsExpired.length === 1 ? "it" : "them"} from the ` + `'${this._cacheName}' cache.`);
|
|
365
|
+
logger.log(`Expired the following ${urlsExpired.length === 1 ? "URL" : "URLs"}:`);
|
|
366
|
+
for (const url of urlsExpired){
|
|
367
|
+
logger.log(` ${url}`);
|
|
368
|
+
}
|
|
369
|
+
logger.groupEnd();
|
|
370
|
+
} else {
|
|
371
|
+
logger.debug("Cache expiration ran and found no entries to remove.");
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
this._isRunning = false;
|
|
375
|
+
if (this._rerunRequested) {
|
|
376
|
+
this._rerunRequested = false;
|
|
377
|
+
void this.expireEntries();
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
async updateTimestamp(url) {
|
|
381
|
+
if (process.env.NODE_ENV !== "production") {
|
|
382
|
+
assert.isType(url, "string", {
|
|
383
|
+
moduleName: "@serwist/expiration",
|
|
384
|
+
className: "CacheExpiration",
|
|
385
|
+
funcName: "updateTimestamp",
|
|
386
|
+
paramName: "url"
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
await this._timestampModel.setTimestamp(url, Date.now());
|
|
390
|
+
}
|
|
391
|
+
async isURLExpired(url) {
|
|
392
|
+
if (!this._maxAgeSeconds) {
|
|
393
|
+
if (process.env.NODE_ENV !== "production") {
|
|
394
|
+
throw new SerwistError("expired-test-without-max-age", {
|
|
395
|
+
methodName: "isURLExpired",
|
|
396
|
+
paramName: "maxAgeSeconds"
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
return false;
|
|
400
|
+
}
|
|
401
|
+
const timestamp = await this._timestampModel.getTimestamp(url);
|
|
402
|
+
const expireOlderThan = Date.now() - this._maxAgeSeconds * 1000;
|
|
403
|
+
return timestamp !== undefined ? timestamp < expireOlderThan : true;
|
|
404
|
+
}
|
|
405
|
+
async delete() {
|
|
406
|
+
this._rerunRequested = false;
|
|
407
|
+
await this._timestampModel.expireEntries(Number.POSITIVE_INFINITY);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
class ExpirationPlugin {
|
|
412
|
+
_config;
|
|
413
|
+
_cacheExpirations;
|
|
414
|
+
constructor(config = {}){
|
|
415
|
+
if (process.env.NODE_ENV !== "production") {
|
|
416
|
+
if (!(config.maxEntries || config.maxAgeSeconds)) {
|
|
417
|
+
throw new SerwistError("max-entries-or-age-required", {
|
|
418
|
+
moduleName: "@serwist/expiration",
|
|
419
|
+
className: "ExpirationPlugin",
|
|
420
|
+
funcName: "constructor"
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
if (config.maxEntries) {
|
|
424
|
+
assert.isType(config.maxEntries, "number", {
|
|
425
|
+
moduleName: "@serwist/expiration",
|
|
426
|
+
className: "ExpirationPlugin",
|
|
427
|
+
funcName: "constructor",
|
|
428
|
+
paramName: "config.maxEntries"
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
if (config.maxAgeSeconds) {
|
|
432
|
+
assert.isType(config.maxAgeSeconds, "number", {
|
|
433
|
+
moduleName: "@serwist/expiration",
|
|
434
|
+
className: "ExpirationPlugin",
|
|
435
|
+
funcName: "constructor",
|
|
436
|
+
paramName: "config.maxAgeSeconds"
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
if (config.maxAgeFrom) {
|
|
440
|
+
assert.isType(config.maxAgeFrom, "string", {
|
|
441
|
+
moduleName: "@serwist/expiration",
|
|
442
|
+
className: "ExpirationPlugin",
|
|
443
|
+
funcName: "constructor",
|
|
444
|
+
paramName: "config.maxAgeFrom"
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
this._config = config;
|
|
449
|
+
this._cacheExpirations = new Map();
|
|
450
|
+
if (!this._config.maxAgeFrom) {
|
|
451
|
+
this._config.maxAgeFrom = "last-fetched";
|
|
452
|
+
}
|
|
453
|
+
if (this._config.purgeOnQuotaError) {
|
|
454
|
+
registerQuotaErrorCallback(()=>this.deleteCacheAndMetadata());
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
_getCacheExpiration(cacheName) {
|
|
458
|
+
if (cacheName === privateCacheNames.getRuntimeName()) {
|
|
459
|
+
throw new SerwistError("expire-custom-caches-only");
|
|
460
|
+
}
|
|
461
|
+
let cacheExpiration = this._cacheExpirations.get(cacheName);
|
|
462
|
+
if (!cacheExpiration) {
|
|
463
|
+
cacheExpiration = new CacheExpiration(cacheName, this._config);
|
|
464
|
+
this._cacheExpirations.set(cacheName, cacheExpiration);
|
|
465
|
+
}
|
|
466
|
+
return cacheExpiration;
|
|
467
|
+
}
|
|
468
|
+
cachedResponseWillBeUsed({ event, cacheName, request, cachedResponse }) {
|
|
469
|
+
if (!cachedResponse) {
|
|
470
|
+
return null;
|
|
471
|
+
}
|
|
472
|
+
const isFresh = this._isResponseDateFresh(cachedResponse);
|
|
473
|
+
const cacheExpiration = this._getCacheExpiration(cacheName);
|
|
474
|
+
const isMaxAgeFromLastUsed = this._config.maxAgeFrom === "last-used";
|
|
475
|
+
const done = (async ()=>{
|
|
476
|
+
if (isMaxAgeFromLastUsed) {
|
|
477
|
+
await cacheExpiration.updateTimestamp(request.url);
|
|
478
|
+
}
|
|
479
|
+
await cacheExpiration.expireEntries();
|
|
480
|
+
})();
|
|
481
|
+
try {
|
|
482
|
+
event.waitUntil(done);
|
|
483
|
+
} catch (error) {
|
|
484
|
+
if (process.env.NODE_ENV !== "production") {
|
|
485
|
+
if (event instanceof FetchEvent) {
|
|
486
|
+
logger.warn(`Unable to ensure service worker stays alive when updating cache entry for '${getFriendlyURL(event.request.url)}'.`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return isFresh ? cachedResponse : null;
|
|
491
|
+
}
|
|
492
|
+
_isResponseDateFresh(cachedResponse) {
|
|
493
|
+
const isMaxAgeFromLastUsed = this._config.maxAgeFrom === "last-used";
|
|
494
|
+
if (isMaxAgeFromLastUsed) {
|
|
495
|
+
return true;
|
|
496
|
+
}
|
|
497
|
+
const now = Date.now();
|
|
498
|
+
if (!this._config.maxAgeSeconds) {
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
501
|
+
const dateHeaderTimestamp = this._getDateHeaderTimestamp(cachedResponse);
|
|
502
|
+
if (dateHeaderTimestamp === null) {
|
|
503
|
+
return true;
|
|
504
|
+
}
|
|
505
|
+
return dateHeaderTimestamp >= now - this._config.maxAgeSeconds * 1000;
|
|
506
|
+
}
|
|
507
|
+
_getDateHeaderTimestamp(cachedResponse) {
|
|
508
|
+
if (!cachedResponse.headers.has("date")) {
|
|
509
|
+
return null;
|
|
510
|
+
}
|
|
511
|
+
const dateHeader = cachedResponse.headers.get("date");
|
|
512
|
+
const parsedDate = new Date(dateHeader);
|
|
513
|
+
const headerTime = parsedDate.getTime();
|
|
514
|
+
if (Number.isNaN(headerTime)) {
|
|
515
|
+
return null;
|
|
516
|
+
}
|
|
517
|
+
return headerTime;
|
|
518
|
+
}
|
|
519
|
+
async cacheDidUpdate({ cacheName, request }) {
|
|
520
|
+
if (process.env.NODE_ENV !== "production") {
|
|
521
|
+
assert.isType(cacheName, "string", {
|
|
522
|
+
moduleName: "@serwist/expiration",
|
|
523
|
+
className: "Plugin",
|
|
524
|
+
funcName: "cacheDidUpdate",
|
|
525
|
+
paramName: "cacheName"
|
|
526
|
+
});
|
|
527
|
+
assert.isInstance(request, Request, {
|
|
528
|
+
moduleName: "@serwist/expiration",
|
|
529
|
+
className: "Plugin",
|
|
530
|
+
funcName: "cacheDidUpdate",
|
|
531
|
+
paramName: "request"
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
const cacheExpiration = this._getCacheExpiration(cacheName);
|
|
535
|
+
await cacheExpiration.updateTimestamp(request.url);
|
|
536
|
+
await cacheExpiration.expireEntries();
|
|
537
|
+
}
|
|
538
|
+
async deleteCacheAndMetadata() {
|
|
539
|
+
for (const [cacheName, cacheExpiration] of this._cacheExpirations){
|
|
540
|
+
await self.caches.delete(cacheName);
|
|
541
|
+
await cacheExpiration.delete();
|
|
542
|
+
}
|
|
543
|
+
this._cacheExpirations = new Map();
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const calculateEffectiveBoundaries = (blob, start, end)=>{
|
|
548
|
+
if (process.env.NODE_ENV !== "production") {
|
|
549
|
+
assert.isInstance(blob, Blob, {
|
|
550
|
+
moduleName: "@serwist/range-requests",
|
|
551
|
+
funcName: "calculateEffectiveBoundaries",
|
|
552
|
+
paramName: "blob"
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
const blobSize = blob.size;
|
|
556
|
+
if (end && end > blobSize || start && start < 0) {
|
|
557
|
+
throw new SerwistError("range-not-satisfiable", {
|
|
558
|
+
size: blobSize,
|
|
559
|
+
end,
|
|
560
|
+
start
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
let effectiveStart;
|
|
564
|
+
let effectiveEnd;
|
|
565
|
+
if (start !== undefined && end !== undefined) {
|
|
566
|
+
effectiveStart = start;
|
|
567
|
+
effectiveEnd = end + 1;
|
|
568
|
+
} else if (start !== undefined && end === undefined) {
|
|
569
|
+
effectiveStart = start;
|
|
570
|
+
effectiveEnd = blobSize;
|
|
571
|
+
} else if (end !== undefined && start === undefined) {
|
|
572
|
+
effectiveStart = blobSize - end;
|
|
573
|
+
effectiveEnd = blobSize;
|
|
574
|
+
}
|
|
575
|
+
return {
|
|
576
|
+
start: effectiveStart,
|
|
577
|
+
end: effectiveEnd
|
|
578
|
+
};
|
|
579
|
+
};
|
|
580
|
+
|
|
581
|
+
const parseRangeHeader = (rangeHeader)=>{
|
|
582
|
+
if (process.env.NODE_ENV !== "production") {
|
|
583
|
+
assert.isType(rangeHeader, "string", {
|
|
584
|
+
moduleName: "@serwist/range-requests",
|
|
585
|
+
funcName: "parseRangeHeader",
|
|
586
|
+
paramName: "rangeHeader"
|
|
587
|
+
});
|
|
588
|
+
}
|
|
589
|
+
const normalizedRangeHeader = rangeHeader.trim().toLowerCase();
|
|
590
|
+
if (!normalizedRangeHeader.startsWith("bytes=")) {
|
|
591
|
+
throw new SerwistError("unit-must-be-bytes", {
|
|
592
|
+
normalizedRangeHeader
|
|
593
|
+
});
|
|
594
|
+
}
|
|
595
|
+
if (normalizedRangeHeader.includes(",")) {
|
|
596
|
+
throw new SerwistError("single-range-only", {
|
|
597
|
+
normalizedRangeHeader
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
const rangeParts = /(\d*)-(\d*)/.exec(normalizedRangeHeader);
|
|
601
|
+
if (!rangeParts || !(rangeParts[1] || rangeParts[2])) {
|
|
602
|
+
throw new SerwistError("invalid-range-values", {
|
|
603
|
+
normalizedRangeHeader
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
return {
|
|
607
|
+
start: rangeParts[1] === "" ? undefined : Number(rangeParts[1]),
|
|
608
|
+
end: rangeParts[2] === "" ? undefined : Number(rangeParts[2])
|
|
609
|
+
};
|
|
610
|
+
};
|
|
611
|
+
|
|
612
|
+
const createPartialResponse = async (request, originalResponse)=>{
|
|
613
|
+
try {
|
|
614
|
+
if (process.env.NODE_ENV !== "production") {
|
|
615
|
+
assert.isInstance(request, Request, {
|
|
616
|
+
moduleName: "@serwist/range-requests",
|
|
617
|
+
funcName: "createPartialResponse",
|
|
618
|
+
paramName: "request"
|
|
619
|
+
});
|
|
620
|
+
assert.isInstance(originalResponse, Response, {
|
|
621
|
+
moduleName: "@serwist/range-requests",
|
|
622
|
+
funcName: "createPartialResponse",
|
|
623
|
+
paramName: "originalResponse"
|
|
624
|
+
});
|
|
625
|
+
}
|
|
626
|
+
if (originalResponse.status === 206) {
|
|
627
|
+
return originalResponse;
|
|
628
|
+
}
|
|
629
|
+
const rangeHeader = request.headers.get("range");
|
|
630
|
+
if (!rangeHeader) {
|
|
631
|
+
throw new SerwistError("no-range-header");
|
|
632
|
+
}
|
|
633
|
+
const boundaries = parseRangeHeader(rangeHeader);
|
|
634
|
+
const originalBlob = await originalResponse.blob();
|
|
635
|
+
const effectiveBoundaries = calculateEffectiveBoundaries(originalBlob, boundaries.start, boundaries.end);
|
|
636
|
+
const slicedBlob = originalBlob.slice(effectiveBoundaries.start, effectiveBoundaries.end);
|
|
637
|
+
const slicedBlobSize = slicedBlob.size;
|
|
638
|
+
const slicedResponse = new Response(slicedBlob, {
|
|
639
|
+
status: 206,
|
|
640
|
+
statusText: "Partial Content",
|
|
641
|
+
headers: originalResponse.headers
|
|
642
|
+
});
|
|
643
|
+
slicedResponse.headers.set("Content-Length", String(slicedBlobSize));
|
|
644
|
+
slicedResponse.headers.set("Content-Range", `bytes ${effectiveBoundaries.start}-${effectiveBoundaries.end - 1}/` + `${originalBlob.size}`);
|
|
645
|
+
return slicedResponse;
|
|
646
|
+
} catch (error) {
|
|
647
|
+
if (process.env.NODE_ENV !== "production") {
|
|
648
|
+
logger.warn("Unable to construct a partial response; returning a " + "416 Range Not Satisfiable response instead.");
|
|
649
|
+
logger.groupCollapsed("View details here.");
|
|
650
|
+
logger.log(error);
|
|
651
|
+
logger.log(request);
|
|
652
|
+
logger.log(originalResponse);
|
|
653
|
+
logger.groupEnd();
|
|
654
|
+
}
|
|
655
|
+
return new Response("", {
|
|
656
|
+
status: 416,
|
|
657
|
+
statusText: "Range Not Satisfiable"
|
|
658
|
+
});
|
|
659
|
+
}
|
|
660
|
+
};
|
|
661
|
+
|
|
662
|
+
class RangeRequestsPlugin {
|
|
663
|
+
cachedResponseWillBeUsed = async ({ request, cachedResponse })=>{
|
|
664
|
+
if (cachedResponse && request.headers.has("range")) {
|
|
665
|
+
return await createPartialResponse(request, cachedResponse);
|
|
666
|
+
}
|
|
667
|
+
return cachedResponse;
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
export { defaultHeadersToCheck as BROADCAST_UPDATE_DEFAULT_HEADERS, CACHE_UPDATED_MESSAGE_META as BROADCAST_UPDATE_MESSAGE_META, CACHE_UPDATED_MESSAGE_TYPE as BROADCAST_UPDATE_MESSAGE_TYPE, BroadcastCacheUpdate, BroadcastUpdatePlugin, CacheExpiration, CacheableResponse, CacheableResponsePlugin, ExpirationPlugin, RangeRequestsPlugin, createPartialResponse, responsesAreSame };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { PrecacheController } from "./precaching/PrecacheController.js";
|
|
2
|
+
import { PrecacheRoute } from "./precaching/PrecacheRoute.js";
|
|
3
|
+
import { PrecacheStrategy } from "./precaching/PrecacheStrategy.js";
|
|
4
|
+
import { addPlugins } from "./precaching/addPlugins.js";
|
|
5
|
+
import { addRoute } from "./precaching/addRoute.js";
|
|
6
|
+
import { cleanupOutdatedCaches } from "./precaching/cleanupOutdatedCaches.js";
|
|
7
|
+
import { createHandlerBoundToURL } from "./precaching/createHandlerBoundToURL.js";
|
|
8
|
+
import { getCacheKeyForURL } from "./precaching/getCacheKeyForURL.js";
|
|
9
|
+
import { matchPrecache } from "./precaching/matchPrecache.js";
|
|
10
|
+
import { precache } from "./precaching/precache.js";
|
|
11
|
+
import { precacheAndRoute } from "./precaching/precacheAndRoute.js";
|
|
12
|
+
import { getSingletonPrecacheController, setSingletonPrecacheController } from "./precaching/singletonPrecacheController.js";
|
|
13
|
+
/**
|
|
14
|
+
* Most consumers of this module will want to use the
|
|
15
|
+
* `@serwist/sw/precaching.precacheAndRoute`
|
|
16
|
+
* method to add assets to the cache and respond to network requests with these
|
|
17
|
+
* cached assets.
|
|
18
|
+
*
|
|
19
|
+
* If you require more control over caching and routing, you can use the
|
|
20
|
+
* `@serwist/precaching.PrecacheController`
|
|
21
|
+
* interface.
|
|
22
|
+
*/
|
|
23
|
+
export { addPlugins, addRoute, cleanupOutdatedCaches, createHandlerBoundToURL, getCacheKeyForURL, matchPrecache, precache, precacheAndRoute, PrecacheController, PrecacheRoute, PrecacheStrategy, getSingletonPrecacheController, setSingletonPrecacheController, };
|
|
24
|
+
export type * from "./precaching/types.js";
|
|
25
|
+
//# sourceMappingURL=index.precaching.d.ts.map
|