@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.
Files changed (262) hide show
  1. package/dist/abstractions/Serwist.d.ts +82 -0
  2. package/dist/abstractions/Serwist.d.ts.map +1 -0
  3. package/dist/{disableDevLogs.d.ts → abstractions/disableDevLogs.d.ts} +2 -0
  4. package/dist/abstractions/disableDevLogs.d.ts.map +1 -0
  5. package/dist/{fallbacks.d.ts → abstractions/fallbacks.d.ts} +9 -20
  6. package/dist/abstractions/fallbacks.d.ts.map +1 -0
  7. package/dist/{handlePrecaching.d.ts → abstractions/handlePrecaching.d.ts} +8 -10
  8. package/dist/abstractions/handlePrecaching.d.ts.map +1 -0
  9. package/dist/abstractions/installSerwist.d.ts +15 -0
  10. package/dist/abstractions/installSerwist.d.ts.map +1 -0
  11. package/dist/abstractions/navigationPreload.d.ts +20 -0
  12. package/dist/abstractions/navigationPreload.d.ts.map +1 -0
  13. package/dist/{registerRuntimeCaching.d.ts → abstractions/registerRuntimeCaching.d.ts} +2 -0
  14. package/dist/abstractions/registerRuntimeCaching.d.ts.map +1 -0
  15. package/dist/abstractions/types.d.ts +29 -0
  16. package/dist/abstractions/types.d.ts.map +1 -0
  17. package/dist/chunks/NavigationRoute.js +54 -0
  18. package/dist/chunks/NetworkOnly.js +193 -0
  19. package/dist/chunks/PrecacheFallbackPlugin.js +573 -0
  20. package/dist/chunks/Strategy.js +410 -0
  21. package/dist/chunks/precacheAndRoute.js +113 -0
  22. package/dist/chunks/registerRoute.js +7 -0
  23. package/dist/chunks/singletonPrecacheController.js +433 -0
  24. package/dist/chunks/singletonRouter.js +435 -0
  25. package/dist/index.d.ts +11 -9
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +121 -124
  28. package/dist/index.plugins.d.ts +41 -0
  29. package/dist/index.plugins.d.ts.map +1 -0
  30. package/dist/index.plugins.js +671 -0
  31. package/dist/index.precaching.d.ts +25 -0
  32. package/dist/index.precaching.d.ts.map +1 -0
  33. package/dist/index.precaching.js +24 -0
  34. package/dist/index.routing.d.ts +15 -0
  35. package/dist/index.routing.d.ts.map +1 -0
  36. package/dist/index.routing.js +19 -0
  37. package/dist/index.strategies.d.ts +22 -0
  38. package/dist/index.strategies.d.ts.map +1 -0
  39. package/dist/index.strategies.js +146 -0
  40. package/dist/plugins/backgroundSync/BackgroundSyncPlugin.d.ts +23 -0
  41. package/dist/plugins/backgroundSync/BackgroundSyncPlugin.d.ts.map +1 -0
  42. package/dist/plugins/backgroundSync/Queue.d.ts +166 -0
  43. package/dist/plugins/backgroundSync/Queue.d.ts.map +1 -0
  44. package/dist/plugins/backgroundSync/QueueDb.d.ts +90 -0
  45. package/dist/plugins/backgroundSync/QueueDb.d.ts.map +1 -0
  46. package/dist/plugins/backgroundSync/QueueStore.d.ts +75 -0
  47. package/dist/plugins/backgroundSync/QueueStore.d.ts.map +1 -0
  48. package/dist/plugins/backgroundSync/StorableRequest.d.ts +51 -0
  49. package/dist/plugins/backgroundSync/StorableRequest.d.ts.map +1 -0
  50. package/dist/plugins/broadcastUpdate/BroadcastCacheUpdate.d.ts +45 -0
  51. package/dist/plugins/broadcastUpdate/BroadcastCacheUpdate.d.ts.map +1 -0
  52. package/dist/plugins/broadcastUpdate/BroadcastUpdatePlugin.d.ts +27 -0
  53. package/dist/plugins/broadcastUpdate/BroadcastUpdatePlugin.d.ts.map +1 -0
  54. package/dist/plugins/broadcastUpdate/constants.d.ts +5 -0
  55. package/dist/plugins/broadcastUpdate/constants.d.ts.map +1 -0
  56. package/dist/plugins/broadcastUpdate/responsesAreSame.d.ts +11 -0
  57. package/dist/plugins/broadcastUpdate/responsesAreSame.d.ts.map +1 -0
  58. package/dist/plugins/broadcastUpdate/types.d.ts +34 -0
  59. package/dist/plugins/broadcastUpdate/types.d.ts.map +1 -0
  60. package/dist/plugins/cacheableResponse/CacheableResponse.d.ts +40 -0
  61. package/dist/plugins/cacheableResponse/CacheableResponse.d.ts.map +1 -0
  62. package/dist/plugins/cacheableResponse/CacheableResponsePlugin.d.ts +27 -0
  63. package/dist/plugins/cacheableResponse/CacheableResponsePlugin.d.ts.map +1 -0
  64. package/dist/plugins/expiration/CacheExpiration.d.ts +66 -0
  65. package/dist/plugins/expiration/CacheExpiration.d.ts.map +1 -0
  66. package/dist/plugins/expiration/ExpirationPlugin.d.ts +116 -0
  67. package/dist/plugins/expiration/ExpirationPlugin.d.ts.map +1 -0
  68. package/dist/plugins/expiration/models/CacheTimestampsModel.d.ts +73 -0
  69. package/dist/plugins/expiration/models/CacheTimestampsModel.d.ts.map +1 -0
  70. package/dist/plugins/googleAnalytics/constants.d.ts +10 -0
  71. package/dist/plugins/googleAnalytics/constants.d.ts.map +1 -0
  72. package/dist/plugins/googleAnalytics/initialize.d.ts +34 -0
  73. package/dist/plugins/googleAnalytics/initialize.d.ts.map +1 -0
  74. package/dist/plugins/precaching/PrecacheFallbackPlugin.d.ts +54 -0
  75. package/dist/plugins/precaching/PrecacheFallbackPlugin.d.ts.map +1 -0
  76. package/dist/plugins/rangeRequests/RangeRequestsPlugin.d.ts +19 -0
  77. package/dist/plugins/rangeRequests/RangeRequestsPlugin.d.ts.map +1 -0
  78. package/dist/plugins/rangeRequests/createPartialResponse.d.ts +18 -0
  79. package/dist/plugins/rangeRequests/createPartialResponse.d.ts.map +1 -0
  80. package/dist/plugins/rangeRequests/utils/calculateEffectiveBoundaries.d.ts +14 -0
  81. package/dist/plugins/rangeRequests/utils/calculateEffectiveBoundaries.d.ts.map +1 -0
  82. package/dist/plugins/rangeRequests/utils/parseRangeHeader.d.ts +12 -0
  83. package/dist/plugins/rangeRequests/utils/parseRangeHeader.d.ts.map +1 -0
  84. package/dist/precaching/PrecacheController.d.ts +145 -0
  85. package/dist/precaching/PrecacheController.d.ts.map +1 -0
  86. package/dist/precaching/PrecacheRoute.d.ts +20 -0
  87. package/dist/precaching/PrecacheRoute.d.ts.map +1 -0
  88. package/dist/precaching/PrecacheStrategy.d.ts +68 -0
  89. package/dist/precaching/PrecacheStrategy.d.ts.map +1 -0
  90. package/dist/precaching/addPlugins.d.ts +8 -0
  91. package/dist/precaching/addPlugins.d.ts.map +1 -0
  92. package/dist/precaching/addRoute.d.ts +15 -0
  93. package/dist/precaching/addRoute.d.ts.map +1 -0
  94. package/dist/precaching/cleanupOutdatedCaches.d.ts +6 -0
  95. package/dist/precaching/cleanupOutdatedCaches.d.ts.map +1 -0
  96. package/dist/precaching/createHandlerBoundToURL.d.ts +17 -0
  97. package/dist/precaching/createHandlerBoundToURL.d.ts.map +1 -0
  98. package/dist/precaching/getCacheKeyForURL.d.ts +20 -0
  99. package/dist/precaching/getCacheKeyForURL.d.ts.map +1 -0
  100. package/dist/precaching/matchPrecache.d.ts +14 -0
  101. package/dist/precaching/matchPrecache.d.ts.map +1 -0
  102. package/dist/precaching/precache.d.ts +19 -0
  103. package/dist/precaching/precache.d.ts.map +1 -0
  104. package/dist/precaching/precacheAndRoute.d.ts +14 -0
  105. package/dist/precaching/precacheAndRoute.d.ts.map +1 -0
  106. package/dist/precaching/singletonPrecacheController.d.ts +38 -0
  107. package/dist/precaching/singletonPrecacheController.d.ts.map +1 -0
  108. package/dist/precaching/types.d.ts +37 -0
  109. package/dist/precaching/types.d.ts.map +1 -0
  110. package/dist/precaching/utils/PrecacheCacheKeyPlugin.d.ts +17 -0
  111. package/dist/precaching/utils/PrecacheCacheKeyPlugin.d.ts.map +1 -0
  112. package/dist/precaching/utils/PrecacheInstallReportPlugin.d.ts +15 -0
  113. package/dist/precaching/utils/PrecacheInstallReportPlugin.d.ts.map +1 -0
  114. package/dist/precaching/utils/createCacheKey.d.ts +16 -0
  115. package/dist/precaching/utils/createCacheKey.d.ts.map +1 -0
  116. package/dist/precaching/utils/deleteOutdatedCaches.d.ts +18 -0
  117. package/dist/precaching/utils/deleteOutdatedCaches.d.ts.map +1 -0
  118. package/dist/precaching/utils/generateURLVariations.d.ts +12 -0
  119. package/dist/precaching/utils/generateURLVariations.d.ts.map +1 -0
  120. package/dist/precaching/utils/getCacheKeyForURL.d.ts +14 -0
  121. package/dist/precaching/utils/getCacheKeyForURL.d.ts.map +1 -0
  122. package/dist/precaching/utils/printCleanupDetails.d.ts +6 -0
  123. package/dist/precaching/utils/printCleanupDetails.d.ts.map +1 -0
  124. package/dist/precaching/utils/printInstallDetails.d.ts +7 -0
  125. package/dist/precaching/utils/printInstallDetails.d.ts.map +1 -0
  126. package/dist/precaching/utils/removeIgnoredSearchParams.d.ts +12 -0
  127. package/dist/precaching/utils/removeIgnoredSearchParams.d.ts.map +1 -0
  128. package/dist/routing/NavigationRoute.d.ts +57 -0
  129. package/dist/routing/NavigationRoute.d.ts.map +1 -0
  130. package/dist/routing/RegExpRoute.d.ts +24 -0
  131. package/dist/routing/RegExpRoute.d.ts.map +1 -0
  132. package/dist/routing/Route.d.ts +33 -0
  133. package/dist/routing/Route.d.ts.map +1 -0
  134. package/dist/routing/Router.d.ts +150 -0
  135. package/dist/routing/Router.d.ts.map +1 -0
  136. package/dist/routing/parseRoute.d.ts +16 -0
  137. package/dist/routing/parseRoute.d.ts.map +1 -0
  138. package/dist/routing/registerRoute.d.ts +15 -0
  139. package/dist/routing/registerRoute.d.ts.map +1 -0
  140. package/dist/routing/setCatchHandler.d.ts +9 -0
  141. package/dist/routing/setCatchHandler.d.ts.map +1 -0
  142. package/dist/routing/setDefaultHandler.d.ts +12 -0
  143. package/dist/routing/setDefaultHandler.d.ts.map +1 -0
  144. package/dist/routing/singletonRouter.d.ts +47 -0
  145. package/dist/routing/singletonRouter.d.ts.map +1 -0
  146. package/dist/routing/unregisterRoute.d.ts +8 -0
  147. package/dist/routing/unregisterRoute.d.ts.map +1 -0
  148. package/dist/routing/utils/constants.d.ts +15 -0
  149. package/dist/routing/utils/constants.d.ts.map +1 -0
  150. package/dist/routing/utils/normalizeHandler.d.ts +10 -0
  151. package/dist/routing/utils/normalizeHandler.d.ts.map +1 -0
  152. package/dist/strategies/CacheFirst.d.ts +23 -0
  153. package/dist/strategies/CacheFirst.d.ts.map +1 -0
  154. package/dist/strategies/CacheOnly.d.ts +20 -0
  155. package/dist/strategies/CacheOnly.d.ts.map +1 -0
  156. package/dist/strategies/NetworkFirst.d.ts +61 -0
  157. package/dist/strategies/NetworkFirst.d.ts.map +1 -0
  158. package/dist/strategies/NetworkOnly.d.ts +32 -0
  159. package/dist/strategies/NetworkOnly.d.ts.map +1 -0
  160. package/dist/strategies/StaleWhileRevalidate.d.ts +35 -0
  161. package/dist/strategies/StaleWhileRevalidate.d.ts.map +1 -0
  162. package/dist/strategies/Strategy.d.ts +83 -0
  163. package/dist/strategies/Strategy.d.ts.map +1 -0
  164. package/dist/strategies/StrategyHandler.d.ts +189 -0
  165. package/dist/strategies/StrategyHandler.d.ts.map +1 -0
  166. package/dist/strategies/plugins/cacheOkAndOpaquePlugin.d.ts +3 -0
  167. package/dist/strategies/plugins/cacheOkAndOpaquePlugin.d.ts.map +1 -0
  168. package/dist/strategies/utils/messages.d.ts +5 -0
  169. package/dist/strategies/utils/messages.d.ts.map +1 -0
  170. package/package.json +42 -19
  171. package/src/abstractions/Serwist.ts +177 -0
  172. package/src/abstractions/disableDevLogs.ts +10 -0
  173. package/src/abstractions/fallbacks.ts +65 -0
  174. package/src/abstractions/handlePrecaching.ts +65 -0
  175. package/src/abstractions/installSerwist.ts +28 -0
  176. package/src/abstractions/navigationPreload.ts +64 -0
  177. package/src/abstractions/registerRuntimeCaching.ts +17 -0
  178. package/src/abstractions/types.ts +29 -0
  179. package/src/index.plugins.ts +95 -0
  180. package/src/index.precaching.ts +41 -0
  181. package/src/index.routing.ts +28 -0
  182. package/src/index.strategies.ts +26 -0
  183. package/src/index.ts +31 -9
  184. package/src/plugins/backgroundSync/BackgroundSyncPlugin.ts +39 -0
  185. package/src/plugins/backgroundSync/Queue.ts +438 -0
  186. package/src/plugins/backgroundSync/QueueDb.ts +176 -0
  187. package/src/plugins/backgroundSync/QueueStore.ts +161 -0
  188. package/src/plugins/backgroundSync/StorableRequest.ts +142 -0
  189. package/src/plugins/broadcastUpdate/BroadcastCacheUpdate.ts +159 -0
  190. package/src/plugins/broadcastUpdate/BroadcastUpdatePlugin.ts +43 -0
  191. package/src/plugins/broadcastUpdate/constants.ts +12 -0
  192. package/src/plugins/broadcastUpdate/responsesAreSame.ts +48 -0
  193. package/src/plugins/broadcastUpdate/types.ts +37 -0
  194. package/src/plugins/cacheableResponse/CacheableResponse.ts +141 -0
  195. package/src/plugins/cacheableResponse/CacheableResponsePlugin.ts +46 -0
  196. package/src/plugins/expiration/CacheExpiration.ts +192 -0
  197. package/src/plugins/expiration/ExpirationPlugin.ts +297 -0
  198. package/src/plugins/expiration/models/CacheTimestampsModel.ts +184 -0
  199. package/src/plugins/googleAnalytics/constants.ts +22 -0
  200. package/src/plugins/googleAnalytics/initialize.ts +213 -0
  201. package/src/plugins/precaching/PrecacheFallbackPlugin.ts +86 -0
  202. package/src/plugins/rangeRequests/RangeRequestsPlugin.ts +39 -0
  203. package/src/plugins/rangeRequests/createPartialResponse.ts +92 -0
  204. package/src/plugins/rangeRequests/utils/calculateEffectiveBoundaries.ts +58 -0
  205. package/src/plugins/rangeRequests/utils/parseRangeHeader.ts +54 -0
  206. package/src/precaching/PrecacheController.ts +332 -0
  207. package/src/precaching/PrecacheRoute.ts +50 -0
  208. package/src/precaching/PrecacheStrategy.ts +238 -0
  209. package/src/precaching/addPlugins.ts +21 -0
  210. package/src/precaching/addRoute.ts +30 -0
  211. package/src/precaching/cleanupOutdatedCaches.ts +33 -0
  212. package/src/precaching/createHandlerBoundToURL.ts +30 -0
  213. package/src/precaching/getCacheKeyForURL.ts +33 -0
  214. package/src/precaching/matchPrecache.ts +25 -0
  215. package/src/precaching/precache.ts +31 -0
  216. package/src/precaching/precacheAndRoute.ts +27 -0
  217. package/src/precaching/singletonPrecacheController.ts +57 -0
  218. package/src/precaching/types.ts +46 -0
  219. package/src/precaching/utils/PrecacheCacheKeyPlugin.ts +36 -0
  220. package/src/precaching/utils/PrecacheInstallReportPlugin.ts +49 -0
  221. package/src/precaching/utils/createCacheKey.ts +68 -0
  222. package/src/precaching/utils/deleteOutdatedCaches.ts +40 -0
  223. package/src/precaching/utils/generateURLVariations.ts +55 -0
  224. package/src/precaching/utils/getCacheKeyForURL.ts +36 -0
  225. package/src/precaching/utils/printCleanupDetails.ts +38 -0
  226. package/src/precaching/utils/printInstallDetails.ts +53 -0
  227. package/src/precaching/utils/removeIgnoredSearchParams.ts +29 -0
  228. package/src/routing/NavigationRoute.ts +119 -0
  229. package/src/routing/RegExpRoute.ts +74 -0
  230. package/src/routing/Route.ts +68 -0
  231. package/src/routing/Router.ts +481 -0
  232. package/src/routing/parseRoute.ts +78 -0
  233. package/src/routing/registerRoute.ts +27 -0
  234. package/src/routing/setCatchHandler.ts +21 -0
  235. package/src/routing/setDefaultHandler.ts +24 -0
  236. package/src/routing/singletonRouter.ts +76 -0
  237. package/src/routing/unregisterRoute.ts +11 -0
  238. package/src/routing/utils/constants.ts +24 -0
  239. package/src/routing/utils/normalizeHandler.ts +40 -0
  240. package/src/strategies/CacheFirst.ts +87 -0
  241. package/src/strategies/CacheOnly.ts +58 -0
  242. package/src/strategies/NetworkFirst.ts +228 -0
  243. package/src/strategies/NetworkOnly.ts +96 -0
  244. package/src/strategies/StaleWhileRevalidate.ts +109 -0
  245. package/src/strategies/Strategy.ts +202 -0
  246. package/src/strategies/StrategyHandler.ts +557 -0
  247. package/src/strategies/plugins/cacheOkAndOpaquePlugin.ts +26 -0
  248. package/src/strategies/utils/messages.ts +20 -0
  249. package/dist/disableDevLogs.d.ts.map +0 -1
  250. package/dist/fallbacks.d.ts.map +0 -1
  251. package/dist/handlePrecaching.d.ts.map +0 -1
  252. package/dist/installSerwist.d.ts +0 -21
  253. package/dist/installSerwist.d.ts.map +0 -1
  254. package/dist/registerRuntimeCaching.d.ts.map +0 -1
  255. package/dist/types.d.ts +0 -110
  256. package/dist/types.d.ts.map +0 -1
  257. package/src/disableDevLogs.ts +0 -10
  258. package/src/fallbacks.ts +0 -83
  259. package/src/handlePrecaching.ts +0 -65
  260. package/src/installSerwist.ts +0 -106
  261. package/src/registerRuntimeCaching.ts +0 -84
  262. package/src/types.ts +0 -113
@@ -0,0 +1,54 @@
1
+ /*
2
+ Copyright 2018 Google LLC
3
+
4
+ Use of this source code is governed by an MIT-style
5
+ license that can be found in the LICENSE file or at
6
+ https://opensource.org/licenses/MIT.
7
+ */
8
+
9
+ import { assert, SerwistError } from "@serwist/core/internal";
10
+
11
+ /**
12
+ * @param rangeHeader A Range: header value.
13
+ * @returns An object with `start` and `end` properties, reflecting
14
+ * the parsed value of the Range: header. If either the `start` or `end` are
15
+ * omitted, then `null` will be returned.
16
+ * @private
17
+ */
18
+ export const parseRangeHeader = (
19
+ rangeHeader: string,
20
+ ): {
21
+ start?: number;
22
+ end?: number;
23
+ } => {
24
+ if (process.env.NODE_ENV !== "production") {
25
+ assert!.isType(rangeHeader, "string", {
26
+ moduleName: "@serwist/range-requests",
27
+ funcName: "parseRangeHeader",
28
+ paramName: "rangeHeader",
29
+ });
30
+ }
31
+
32
+ const normalizedRangeHeader = rangeHeader.trim().toLowerCase();
33
+ if (!normalizedRangeHeader.startsWith("bytes=")) {
34
+ throw new SerwistError("unit-must-be-bytes", { normalizedRangeHeader });
35
+ }
36
+
37
+ // Specifying multiple ranges separate by commas is valid syntax, but this
38
+ // library only attempts to handle a single, contiguous sequence of bytes.
39
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range#Syntax
40
+ if (normalizedRangeHeader.includes(",")) {
41
+ throw new SerwistError("single-range-only", { normalizedRangeHeader });
42
+ }
43
+
44
+ const rangeParts = /(\d*)-(\d*)/.exec(normalizedRangeHeader);
45
+ // We need either at least one of the start or end values.
46
+ if (!rangeParts || !(rangeParts[1] || rangeParts[2])) {
47
+ throw new SerwistError("invalid-range-values", { normalizedRangeHeader });
48
+ }
49
+
50
+ return {
51
+ start: rangeParts[1] === "" ? undefined : Number(rangeParts[1]),
52
+ end: rangeParts[2] === "" ? undefined : Number(rangeParts[2]),
53
+ };
54
+ };
@@ -0,0 +1,332 @@
1
+ /*
2
+ Copyright 2019 Google LLC
3
+
4
+ Use of this source code is governed by an MIT-style
5
+ license that can be found in the LICENSE file or at
6
+ https://opensource.org/licenses/MIT.
7
+ */
8
+
9
+ import type { RouteHandlerCallback, SerwistPlugin } from "@serwist/core";
10
+ import { assert, SerwistError, logger, privateCacheNames, waitUntil } from "@serwist/core/internal";
11
+ import { parallel } from "@serwist/utils";
12
+ import type { Strategy } from "../strategies/Strategy.js";
13
+ import { PrecacheStrategy } from "./PrecacheStrategy.js";
14
+ import type { CleanupResult, InstallResult, PrecacheEntry } from "./types.js";
15
+ import { PrecacheCacheKeyPlugin } from "./utils/PrecacheCacheKeyPlugin.js";
16
+ import { PrecacheInstallReportPlugin } from "./utils/PrecacheInstallReportPlugin.js";
17
+ import { createCacheKey } from "./utils/createCacheKey.js";
18
+ import { printCleanupDetails } from "./utils/printCleanupDetails.js";
19
+ import { printInstallDetails } from "./utils/printInstallDetails.js";
20
+
21
+ // Give TypeScript the correct global.
22
+ declare const self: ServiceWorkerGlobalScope;
23
+
24
+ interface PrecacheControllerOptions {
25
+ /**
26
+ * The cache to use for precaching.
27
+ */
28
+ cacheName?: string;
29
+ /**
30
+ * Plugins to use when precaching as well as responding to fetch
31
+ * events for precached assets.
32
+ */
33
+ plugins?: SerwistPlugin[];
34
+ /**
35
+ * Whether to attempt to get the response from the network if there's
36
+ * a precache miss.
37
+ */
38
+ fallbackToNetwork?: boolean;
39
+ /**
40
+ * A number of how many precache requests should be made concurrently.
41
+ *
42
+ * @default 1
43
+ */
44
+ concurrentPrecaching?: number;
45
+ }
46
+
47
+ /**
48
+ * Performs efficient precaching of assets.
49
+ */
50
+ export class PrecacheController {
51
+ private _installAndActiveListenersAdded?: boolean;
52
+ private _concurrentPrecaching: number;
53
+ private readonly _strategy: Strategy;
54
+ private readonly _urlsToCacheKeys: Map<string, string> = new Map();
55
+ private readonly _urlsToCacheModes: Map<string, "reload" | "default" | "no-store" | "no-cache" | "force-cache" | "only-if-cached"> = new Map();
56
+ private readonly _cacheKeysToIntegrities: Map<string, string> = new Map();
57
+
58
+ /**
59
+ * Create a new PrecacheController.
60
+ *
61
+ * @param options
62
+ */
63
+ constructor({ cacheName, plugins = [], fallbackToNetwork = true, concurrentPrecaching = 1 }: PrecacheControllerOptions = {}) {
64
+ this._concurrentPrecaching = concurrentPrecaching;
65
+ this._strategy = new PrecacheStrategy({
66
+ cacheName: privateCacheNames.getPrecacheName(cacheName),
67
+ plugins: [...plugins, new PrecacheCacheKeyPlugin({ precacheController: this })],
68
+ fallbackToNetwork,
69
+ });
70
+ // Bind the install and activate methods to the instance.
71
+ this.install = this.install.bind(this);
72
+ this.activate = this.activate.bind(this);
73
+ }
74
+
75
+ /**
76
+ * The strategy created by this controller and
77
+ * used to cache assets and respond to fetch events.
78
+ */
79
+ get strategy(): Strategy {
80
+ return this._strategy;
81
+ }
82
+
83
+ /**
84
+ * Adds items to the precache list, removing any duplicates and
85
+ * stores the files in the precache cache when the service
86
+ * worker installs.
87
+ *
88
+ * This method can be called multiple times.
89
+ *
90
+ * @param entries Array of entries to precache.
91
+ */
92
+ precache(entries: (PrecacheEntry | string)[]): void {
93
+ this.addToCacheList(entries);
94
+
95
+ if (!this._installAndActiveListenersAdded) {
96
+ self.addEventListener("install", this.install);
97
+ self.addEventListener("activate", this.activate);
98
+ this._installAndActiveListenersAdded = true;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * This method will add items to the precache list, removing duplicates
104
+ * and ensuring the information is valid.
105
+ *
106
+ * @param entries Array of entries to precache.
107
+ */
108
+ addToCacheList(entries: (PrecacheEntry | string)[]): void {
109
+ if (process.env.NODE_ENV !== "production") {
110
+ assert!.isArray(entries, {
111
+ moduleName: "@serwist/precaching",
112
+ className: "PrecacheController",
113
+ funcName: "addToCacheList",
114
+ paramName: "entries",
115
+ });
116
+ }
117
+
118
+ const urlsToWarnAbout: string[] = [];
119
+ for (const entry of entries) {
120
+ // See https://github.com/GoogleChrome/workbox/issues/2259
121
+ if (typeof entry === "string") {
122
+ urlsToWarnAbout.push(entry);
123
+ } else if (entry && !entry.integrity && entry.revision === undefined) {
124
+ urlsToWarnAbout.push(entry.url);
125
+ }
126
+
127
+ const { cacheKey, url } = createCacheKey(entry);
128
+ const cacheMode = typeof entry !== "string" && entry.revision ? "reload" : "default";
129
+
130
+ if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) {
131
+ throw new SerwistError("add-to-cache-list-conflicting-entries", {
132
+ firstEntry: this._urlsToCacheKeys.get(url),
133
+ secondEntry: cacheKey,
134
+ });
135
+ }
136
+
137
+ if (typeof entry !== "string" && entry.integrity) {
138
+ if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) {
139
+ throw new SerwistError("add-to-cache-list-conflicting-integrities", {
140
+ url,
141
+ });
142
+ }
143
+ this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);
144
+ }
145
+
146
+ this._urlsToCacheKeys.set(url, cacheKey);
147
+ this._urlsToCacheModes.set(url, cacheMode);
148
+
149
+ if (urlsToWarnAbout.length > 0) {
150
+ const warningMessage = `Serwist is precaching URLs without revision info: ${urlsToWarnAbout.join(
151
+ ", ",
152
+ )}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;
153
+ if (process.env.NODE_ENV === "production") {
154
+ // Use console directly to display this warning without bloating
155
+ // bundle sizes by pulling in all of the logger codebase in prod.
156
+ console.warn(warningMessage);
157
+ } else {
158
+ logger.warn(warningMessage);
159
+ }
160
+ }
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Precaches new and updated assets. Call this method from the service worker
166
+ * install event.
167
+ *
168
+ * Note: this method calls `event.waitUntil()` for you, so you do not need
169
+ * to call it yourself in your event handlers.
170
+ *
171
+ * @param event
172
+ * @returns
173
+ */
174
+ install(event: ExtendableEvent): Promise<InstallResult> {
175
+ return waitUntil<InstallResult>(event, async () => {
176
+ const installReportPlugin = new PrecacheInstallReportPlugin();
177
+ this.strategy.plugins.push(installReportPlugin);
178
+
179
+ await parallel(this._concurrentPrecaching, Array.from(this._urlsToCacheKeys.entries()), async ([url, cacheKey]): Promise<void> => {
180
+ const integrity = this._cacheKeysToIntegrities.get(cacheKey);
181
+ const cacheMode = this._urlsToCacheModes.get(url);
182
+
183
+ const request = new Request(url, {
184
+ integrity,
185
+ cache: cacheMode,
186
+ credentials: "same-origin",
187
+ });
188
+
189
+ await Promise.all(
190
+ this.strategy.handleAll({
191
+ event,
192
+ request,
193
+ url: new URL(request.url),
194
+ params: { cacheKey },
195
+ }),
196
+ );
197
+ });
198
+
199
+ const { updatedURLs, notUpdatedURLs } = installReportPlugin;
200
+
201
+ if (process.env.NODE_ENV !== "production") {
202
+ printInstallDetails(updatedURLs, notUpdatedURLs);
203
+ }
204
+
205
+ return { updatedURLs, notUpdatedURLs };
206
+ });
207
+ }
208
+
209
+ /**
210
+ * Deletes assets that are no longer present in the current precache manifest.
211
+ * Call this method from the service worker activate event.
212
+ *
213
+ * Note: this method calls `event.waitUntil()` for you, so you do not need
214
+ * to call it yourself in your event handlers.
215
+ *
216
+ * @param event
217
+ * @returns
218
+ */
219
+ activate(event: ExtendableEvent): Promise<CleanupResult> {
220
+ return waitUntil<CleanupResult>(event, async () => {
221
+ const cache = await self.caches.open(this.strategy.cacheName);
222
+ const currentlyCachedRequests = await cache.keys();
223
+ const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());
224
+
225
+ const deletedCacheRequests: string[] = [];
226
+
227
+ for (const request of currentlyCachedRequests) {
228
+ if (!expectedCacheKeys.has(request.url)) {
229
+ await cache.delete(request);
230
+ deletedCacheRequests.push(request.url);
231
+ }
232
+ }
233
+
234
+ if (process.env.NODE_ENV !== "production") {
235
+ printCleanupDetails(deletedCacheRequests);
236
+ }
237
+
238
+ return { deletedCacheRequests };
239
+ });
240
+ }
241
+
242
+ /**
243
+ * Returns a mapping of a precached URL to the corresponding cache key, taking
244
+ * into account the revision information for the URL.
245
+ *
246
+ * @returns A URL to cache key mapping.
247
+ */
248
+ getURLsToCacheKeys(): Map<string, string> {
249
+ return this._urlsToCacheKeys;
250
+ }
251
+
252
+ /**
253
+ * Returns a list of all the URLs that have been precached by the current
254
+ * service worker.
255
+ *
256
+ * @returns The precached URLs.
257
+ */
258
+ getCachedURLs(): string[] {
259
+ return [...this._urlsToCacheKeys.keys()];
260
+ }
261
+
262
+ /**
263
+ * Returns the cache key used for storing a given URL. If that URL is
264
+ * unversioned, like `/index.html', then the cache key will be the original
265
+ * URL with a search parameter appended to it.
266
+ *
267
+ * @param url A URL whose cache key you want to look up.
268
+ * @returns The versioned URL that corresponds to a cache key
269
+ * for the original URL, or undefined if that URL isn't precached.
270
+ */
271
+ getCacheKeyForURL(url: string): string | undefined {
272
+ const urlObject = new URL(url, location.href);
273
+ return this._urlsToCacheKeys.get(urlObject.href);
274
+ }
275
+
276
+ /**
277
+ * @param url A cache key whose SRI you want to look up.
278
+ * @returns The subresource integrity associated with the cache key,
279
+ * or undefined if it's not set.
280
+ */
281
+ getIntegrityForCacheKey(cacheKey: string): string | undefined {
282
+ return this._cacheKeysToIntegrities.get(cacheKey);
283
+ }
284
+
285
+ /**
286
+ * This acts as a drop-in replacement for
287
+ * [`cache.match()`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match)
288
+ * with the following differences:
289
+ *
290
+ * - It knows what the name of the precache is, and only checks in that cache.
291
+ * - It allows you to pass in an "original" URL without versioning parameters,
292
+ * and it will automatically look up the correct cache key for the currently
293
+ * active revision of that URL.
294
+ *
295
+ * E.g., `matchPrecache('index.html')` will find the correct precached
296
+ * response for the currently active service worker, even if the actual cache
297
+ * key is `'/index.html?__WB_REVISION__=1234abcd'`.
298
+ *
299
+ * @param request The key (without revisioning parameters)
300
+ * to look up in the precache.
301
+ * @returns
302
+ */
303
+ async matchPrecache(request: string | Request): Promise<Response | undefined> {
304
+ const url = request instanceof Request ? request.url : request;
305
+ const cacheKey = this.getCacheKeyForURL(url);
306
+ if (cacheKey) {
307
+ const cache = await self.caches.open(this.strategy.cacheName);
308
+ return cache.match(cacheKey);
309
+ }
310
+ return undefined;
311
+ }
312
+
313
+ /**
314
+ * Returns a function that looks up `url` in the precache (taking into
315
+ * account revision information), and returns the corresponding `Response`.
316
+ *
317
+ * @param url The precached URL which will be used to lookup the response.
318
+ * @return
319
+ */
320
+ createHandlerBoundToURL(url: string): RouteHandlerCallback {
321
+ const cacheKey = this.getCacheKeyForURL(url);
322
+ if (!cacheKey) {
323
+ throw new SerwistError("non-precached-url", { url });
324
+ }
325
+ return (options) => {
326
+ options.request = new Request(url);
327
+ options.params = { cacheKey, ...options.params };
328
+
329
+ return this.strategy.handle(options);
330
+ };
331
+ }
332
+ }
@@ -0,0 +1,50 @@
1
+ /*
2
+ Copyright 2020 Google LLC
3
+
4
+ Use of this source code is governed by an MIT-style
5
+ license that can be found in the LICENSE file or at
6
+ https://opensource.org/licenses/MIT.
7
+ */
8
+
9
+ import type { RouteMatchCallback, RouteMatchCallbackOptions } from "@serwist/core";
10
+ import { getFriendlyURL, logger } from "@serwist/core/internal";
11
+ import { Route } from "../routing/Route.js";
12
+
13
+ import type { PrecacheController } from "./PrecacheController.js";
14
+ import type { PrecacheRouteOptions } from "./types.js";
15
+ import { generateURLVariations } from "./utils/generateURLVariations.js";
16
+
17
+ /**
18
+ * A subclass of `@serwist/routing.Route` that takes a
19
+ * `@serwist/precaching.PrecacheController`
20
+ * instance and uses it to match incoming requests and handle fetching
21
+ * responses from the precache.
22
+ */
23
+ class PrecacheRoute extends Route {
24
+ /**
25
+ * @param precacheController A `PrecacheController`
26
+ * instance used to both match requests and respond to fetch events.
27
+ * @param options Options to control how requests are matched
28
+ * against the list of precached URLs.
29
+ */
30
+ constructor(precacheController: PrecacheController, options?: PrecacheRouteOptions) {
31
+ const match: RouteMatchCallback = ({ request }: RouteMatchCallbackOptions) => {
32
+ const urlsToCacheKeys = precacheController.getURLsToCacheKeys();
33
+ for (const possibleURL of generateURLVariations(request.url, options)) {
34
+ const cacheKey = urlsToCacheKeys.get(possibleURL);
35
+ if (cacheKey) {
36
+ const integrity = precacheController.getIntegrityForCacheKey(cacheKey);
37
+ return { cacheKey, integrity };
38
+ }
39
+ }
40
+ if (process.env.NODE_ENV !== "production") {
41
+ logger.debug(`Precaching did not find a match for ${getFriendlyURL(request.url)}`);
42
+ }
43
+ return;
44
+ };
45
+
46
+ super(match, precacheController.strategy);
47
+ }
48
+ }
49
+
50
+ export { PrecacheRoute };
@@ -0,0 +1,238 @@
1
+ /*
2
+ Copyright 2020 Google LLC
3
+
4
+ Use of this source code is governed by an MIT-style
5
+ license that can be found in the LICENSE file or at
6
+ https://opensource.org/licenses/MIT.
7
+ */
8
+
9
+ import type { SerwistPlugin } from "@serwist/core";
10
+ import { copyResponse } from "@serwist/core";
11
+ import { SerwistError, getFriendlyURL, logger, privateCacheNames } from "@serwist/core/internal";
12
+ import type { StrategyOptions } from "../strategies/Strategy.js";
13
+ import { Strategy } from "../strategies/Strategy.js";
14
+ import type { StrategyHandler } from "../strategies/StrategyHandler.js";
15
+
16
+ interface PrecacheStrategyOptions extends StrategyOptions {
17
+ /**
18
+ * Whether to attempt to get the response from the network
19
+ * if there's a precache miss.
20
+ */
21
+ fallbackToNetwork?: boolean;
22
+ }
23
+
24
+ /**
25
+ * A `@serwist/strategies.Strategy` implementation
26
+ * specifically designed to work with
27
+ * `@serwist/precaching.PrecacheController`
28
+ * to both cache and fetch precached assets.
29
+ *
30
+ * Note: an instance of this class is created automatically when creating a
31
+ * `PrecacheController`; it's generally not necessary to create this yourself.
32
+ */
33
+ export class PrecacheStrategy extends Strategy {
34
+ private readonly _fallbackToNetwork: boolean;
35
+
36
+ static readonly defaultPrecacheCacheabilityPlugin: SerwistPlugin = {
37
+ async cacheWillUpdate({ response }) {
38
+ if (!response || response.status >= 400) {
39
+ return null;
40
+ }
41
+
42
+ return response;
43
+ },
44
+ };
45
+
46
+ static readonly copyRedirectedCacheableResponsesPlugin: SerwistPlugin = {
47
+ async cacheWillUpdate({ response }) {
48
+ return response.redirected ? await copyResponse(response) : response;
49
+ },
50
+ };
51
+
52
+ /**
53
+ * @param options
54
+ */
55
+ constructor(options: PrecacheStrategyOptions = {}) {
56
+ options.cacheName = privateCacheNames.getPrecacheName(options.cacheName);
57
+ super(options);
58
+
59
+ this._fallbackToNetwork = options.fallbackToNetwork === false ? false : true;
60
+
61
+ // Redirected responses cannot be used to satisfy a navigation request, so
62
+ // any redirected response must be "copied" rather than cloned, so the new
63
+ // response doesn't contain the `redirected` flag. See:
64
+ // https://bugs.chromium.org/p/chromium/issues/detail?id=669363&desc=2#c1
65
+ this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);
66
+ }
67
+
68
+ /**
69
+ * @private
70
+ * @param request A request to run this strategy for.
71
+ * @param handler The event that triggered the request.
72
+ * @returns
73
+ */
74
+ async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
75
+ const response = await handler.cacheMatch(request);
76
+ if (response) {
77
+ return response;
78
+ }
79
+
80
+ // If this is an `install` event for an entry that isn't already cached,
81
+ // then populate the cache.
82
+ if (handler.event && handler.event.type === "install") {
83
+ return await this._handleInstall(request, handler);
84
+ }
85
+
86
+ // Getting here means something went wrong. An entry that should have been
87
+ // precached wasn't found in the cache.
88
+ return await this._handleFetch(request, handler);
89
+ }
90
+
91
+ async _handleFetch(request: Request, handler: StrategyHandler): Promise<Response> {
92
+ let response: Response | undefined = undefined;
93
+ const params = (handler.params || {}) as {
94
+ cacheKey?: string;
95
+ integrity?: string;
96
+ };
97
+
98
+ // Fallback to the network if we're configured to do so.
99
+ if (this._fallbackToNetwork) {
100
+ if (process.env.NODE_ENV !== "production") {
101
+ logger.warn(`The precached response for ${getFriendlyURL(request.url)} in ${this.cacheName} was not found. Falling back to the network.`);
102
+ }
103
+
104
+ const integrityInManifest = params.integrity;
105
+ const integrityInRequest = request.integrity;
106
+ const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest;
107
+
108
+ // Do not add integrity if the original request is no-cors
109
+ // See https://github.com/GoogleChrome/workbox/issues/3096
110
+ response = await handler.fetch(
111
+ new Request(request, {
112
+ integrity: request.mode !== "no-cors" ? integrityInRequest || integrityInManifest : undefined,
113
+ }),
114
+ );
115
+
116
+ // It's only "safe" to repair the cache if we're using SRI to guarantee
117
+ // that the response matches the precache manifest's expectations,
118
+ // and there's either a) no integrity property in the incoming request
119
+ // or b) there is an integrity, and it matches the precache manifest.
120
+ // See https://github.com/GoogleChrome/workbox/issues/2858
121
+ // Also if the original request users no-cors we don't use integrity.
122
+ // See https://github.com/GoogleChrome/workbox/issues/3096
123
+ if (integrityInManifest && noIntegrityConflict && request.mode !== "no-cors") {
124
+ this._useDefaultCacheabilityPluginIfNeeded();
125
+ const wasCached = await handler.cachePut(request, response.clone());
126
+ if (process.env.NODE_ENV !== "production") {
127
+ if (wasCached) {
128
+ logger.log(`A response for ${getFriendlyURL(request.url)} was used to "repair" the precache.`);
129
+ }
130
+ }
131
+ }
132
+ } else {
133
+ // This shouldn't normally happen, but there are edge cases:
134
+ // https://github.com/GoogleChrome/workbox/issues/1441
135
+ throw new SerwistError("missing-precache-entry", {
136
+ cacheName: this.cacheName,
137
+ url: request.url,
138
+ });
139
+ }
140
+
141
+ if (process.env.NODE_ENV !== "production") {
142
+ const cacheKey = params.cacheKey || (await handler.getCacheKey(request, "read"));
143
+
144
+ // Serwist is going to handle the route.
145
+ // print the routing details to the console.
146
+ logger.groupCollapsed(`Precaching is responding to: ${getFriendlyURL(request.url)}`);
147
+ logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`);
148
+
149
+ logger.groupCollapsed("View request details here.");
150
+ logger.log(request);
151
+ logger.groupEnd();
152
+
153
+ logger.groupCollapsed("View response details here.");
154
+ logger.log(response);
155
+ logger.groupEnd();
156
+
157
+ logger.groupEnd();
158
+ }
159
+
160
+ return response;
161
+ }
162
+
163
+ async _handleInstall(request: Request, handler: StrategyHandler): Promise<Response> {
164
+ this._useDefaultCacheabilityPluginIfNeeded();
165
+
166
+ const response = await handler.fetch(request);
167
+
168
+ // Make sure we defer cachePut() until after we know the response
169
+ // should be cached; see https://github.com/GoogleChrome/workbox/issues/2737
170
+ const wasCached = await handler.cachePut(request, response.clone());
171
+ if (!wasCached) {
172
+ // Throwing here will lead to the `install` handler failing, which
173
+ // we want to do if *any* of the responses aren't safe to cache.
174
+ throw new SerwistError("bad-precaching-response", {
175
+ url: request.url,
176
+ status: response.status,
177
+ });
178
+ }
179
+
180
+ return response;
181
+ }
182
+
183
+ /**
184
+ * This method is complex, as there a number of things to account for:
185
+ *
186
+ * The `plugins` array can be set at construction, and/or it might be added to
187
+ * to at any time before the strategy is used.
188
+ *
189
+ * At the time the strategy is used (i.e. during an `install` event), there
190
+ * needs to be at least one plugin that implements `cacheWillUpdate` in the
191
+ * array, other than `copyRedirectedCacheableResponsesPlugin`.
192
+ *
193
+ * - If this method is called and there are no suitable `cacheWillUpdate`
194
+ * plugins, we need to add `defaultPrecacheCacheabilityPlugin`.
195
+ *
196
+ * - If this method is called and there is exactly one `cacheWillUpdate`, then
197
+ * we don't have to do anything (this might be a previously added
198
+ * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin).
199
+ *
200
+ * - If this method is called and there is more than one `cacheWillUpdate`,
201
+ * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so,
202
+ * we need to remove it. (This situation is unlikely, but it could happen if
203
+ * the strategy is used multiple times, the first without a `cacheWillUpdate`,
204
+ * and then later on after manually adding a custom `cacheWillUpdate`.)
205
+ *
206
+ * See https://github.com/GoogleChrome/workbox/issues/2737 for more context.
207
+ *
208
+ * @private
209
+ */
210
+ _useDefaultCacheabilityPluginIfNeeded(): void {
211
+ let defaultPluginIndex: number | null = null;
212
+ let cacheWillUpdatePluginCount = 0;
213
+
214
+ for (const [index, plugin] of this.plugins.entries()) {
215
+ // Ignore the copy redirected plugin when determining what to do.
216
+ if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) {
217
+ continue;
218
+ }
219
+
220
+ // Save the default plugin's index, in case it needs to be removed.
221
+ if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) {
222
+ defaultPluginIndex = index;
223
+ }
224
+
225
+ if (plugin.cacheWillUpdate) {
226
+ cacheWillUpdatePluginCount++;
227
+ }
228
+ }
229
+
230
+ if (cacheWillUpdatePluginCount === 0) {
231
+ this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin);
232
+ } else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) {
233
+ // Only remove the default plugin; multiple custom plugins are allowed.
234
+ this.plugins.splice(defaultPluginIndex, 1);
235
+ }
236
+ // Nothing needs to be done if cacheWillUpdatePluginCount is 1
237
+ }
238
+ }
@@ -0,0 +1,21 @@
1
+ /*
2
+ Copyright 2019 Google LLC
3
+
4
+ Use of this source code is governed by an MIT-style
5
+ license that can be found in the LICENSE file or at
6
+ https://opensource.org/licenses/MIT.
7
+ */
8
+
9
+ import type { SerwistPlugin } from "@serwist/core";
10
+
11
+ import { getSingletonPrecacheController } from "./singletonPrecacheController.js";
12
+
13
+ /**
14
+ * Adds plugins to the precaching strategy.
15
+ *
16
+ * @param plugins
17
+ */
18
+ export const addPlugins = (plugins: SerwistPlugin[]): void => {
19
+ const precacheController = getSingletonPrecacheController();
20
+ precacheController.strategy.plugins.push(...plugins);
21
+ };