serwist 9.1.0-preview.0 → 10.0.0-preview.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/dist/NavigationRoute.d.ts.map +1 -1
  2. package/dist/RegExpRoute.d.ts.map +1 -1
  3. package/dist/Route.d.ts.map +1 -1
  4. package/dist/Serwist.d.ts +75 -53
  5. package/dist/Serwist.d.ts.map +1 -1
  6. package/dist/cacheNames.d.ts.map +1 -1
  7. package/dist/chunks/waitUntil.js +49 -42
  8. package/dist/copyResponse.d.ts.map +1 -1
  9. package/dist/index.d.ts +11 -36
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.internal.d.ts +16 -16
  12. package/dist/index.internal.d.ts.map +1 -1
  13. package/dist/index.internal.js +25 -3
  14. package/dist/index.js +1918 -248
  15. package/dist/lib/backgroundSync/BackgroundSyncPlugin.d.ts +2 -2
  16. package/dist/lib/backgroundSync/BackgroundSyncPlugin.d.ts.map +1 -1
  17. package/dist/lib/backgroundSync/StorableRequest.d.ts.map +1 -1
  18. package/dist/lib/backgroundSync/index.d.ts +6 -0
  19. package/dist/lib/backgroundSync/index.d.ts.map +1 -0
  20. package/dist/lib/broadcastUpdate/BroadcastCacheUpdate.d.ts.map +1 -1
  21. package/dist/lib/broadcastUpdate/BroadcastUpdatePlugin.d.ts +2 -2
  22. package/dist/lib/broadcastUpdate/BroadcastUpdatePlugin.d.ts.map +1 -1
  23. package/dist/lib/broadcastUpdate/index.d.ts +6 -0
  24. package/dist/lib/broadcastUpdate/index.d.ts.map +1 -0
  25. package/dist/lib/broadcastUpdate/responsesAreSame.d.ts.map +1 -1
  26. package/dist/lib/cacheableResponse/CacheableResponsePlugin.d.ts +3 -3
  27. package/dist/lib/cacheableResponse/CacheableResponsePlugin.d.ts.map +1 -1
  28. package/dist/lib/cacheableResponse/index.d.ts +4 -0
  29. package/dist/lib/cacheableResponse/index.d.ts.map +1 -0
  30. package/dist/{legacy/utils → lib/controllers/PrecacheController}/PrecacheCacheKeyPlugin.d.ts +4 -4
  31. package/dist/lib/controllers/PrecacheController/PrecacheCacheKeyPlugin.d.ts.map +1 -0
  32. package/dist/{legacy → lib/controllers/PrecacheController}/PrecacheController.d.ts +77 -64
  33. package/dist/lib/controllers/PrecacheController/PrecacheController.d.ts.map +1 -0
  34. package/dist/lib/controllers/PrecacheController/PrecacheInstallReportPlugin.d.ts +14 -0
  35. package/dist/lib/controllers/PrecacheController/PrecacheInstallReportPlugin.d.ts.map +1 -0
  36. package/dist/{PrecacheRoute.d.ts → lib/controllers/PrecacheController/PrecacheRoute.d.ts} +4 -5
  37. package/dist/lib/controllers/PrecacheController/PrecacheRoute.d.ts.map +1 -0
  38. package/dist/lib/{strategies → controllers/PrecacheController}/PrecacheStrategy.d.ts +6 -6
  39. package/dist/lib/controllers/PrecacheController/PrecacheStrategy.d.ts.map +1 -0
  40. package/dist/lib/controllers/PrecacheController/parsePrecacheOptions.d.ts +25 -0
  41. package/dist/lib/controllers/PrecacheController/parsePrecacheOptions.d.ts.map +1 -0
  42. package/dist/lib/controllers/RuntimeCacheController.d.ts +70 -0
  43. package/dist/lib/controllers/RuntimeCacheController.d.ts.map +1 -0
  44. package/dist/lib/controllers/index.d.ts +4 -0
  45. package/dist/lib/controllers/index.d.ts.map +1 -0
  46. package/dist/lib/expiration/ExpirationPlugin.d.ts +2 -2
  47. package/dist/lib/expiration/ExpirationPlugin.d.ts.map +1 -1
  48. package/dist/lib/expiration/index.d.ts +4 -0
  49. package/dist/lib/expiration/index.d.ts.map +1 -0
  50. package/dist/lib/googleAnalytics/index.d.ts +3 -0
  51. package/dist/lib/googleAnalytics/index.d.ts.map +1 -0
  52. package/dist/lib/googleAnalytics/initializeGoogleAnalytics.d.ts.map +1 -1
  53. package/dist/lib/precaching/PrecacheFallbackPlugin.d.ts +15 -9
  54. package/dist/lib/precaching/PrecacheFallbackPlugin.d.ts.map +1 -1
  55. package/dist/lib/precaching/index.d.ts +3 -0
  56. package/dist/lib/precaching/index.d.ts.map +1 -0
  57. package/dist/lib/rangeRequests/RangeRequestsPlugin.d.ts +3 -3
  58. package/dist/lib/rangeRequests/RangeRequestsPlugin.d.ts.map +1 -1
  59. package/dist/lib/rangeRequests/createPartialResponse.d.ts.map +1 -1
  60. package/dist/lib/rangeRequests/index.d.ts +3 -0
  61. package/dist/lib/rangeRequests/index.d.ts.map +1 -0
  62. package/dist/lib/rangeRequests/utils/calculateEffectiveBoundaries.d.ts.map +1 -1
  63. package/dist/lib/rangeRequests/utils/parseRangeHeader.d.ts.map +1 -1
  64. package/dist/lib/strategies/Strategy.d.ts +3 -3
  65. package/dist/lib/strategies/Strategy.d.ts.map +1 -1
  66. package/dist/lib/strategies/StrategyHandler.d.ts +4 -4
  67. package/dist/lib/strategies/StrategyHandler.d.ts.map +1 -1
  68. package/dist/lib/strategies/index.d.ts +11 -0
  69. package/dist/lib/strategies/index.d.ts.map +1 -0
  70. package/dist/lib/strategies/plugins/cacheOkAndOpaquePlugin.d.ts +2 -2
  71. package/dist/lib/strategies/plugins/cacheOkAndOpaquePlugin.d.ts.map +1 -1
  72. package/dist/navigationPreload.d.ts.map +1 -1
  73. package/dist/registerQuotaErrorCallback.d.ts.map +1 -1
  74. package/dist/setCacheNameDetails.d.ts +1 -1
  75. package/dist/setCacheNameDetails.d.ts.map +1 -1
  76. package/dist/types.d.ts +35 -64
  77. package/dist/types.d.ts.map +1 -1
  78. package/dist/utils/SerwistError.d.ts +1 -1
  79. package/dist/utils/SerwistError.d.ts.map +1 -1
  80. package/dist/utils/cleanupOutdatedCaches.d.ts.map +1 -1
  81. package/dist/utils/createCacheKey.d.ts.map +1 -1
  82. package/dist/utils/deleteOutdatedCaches.d.ts.map +1 -1
  83. package/dist/utils/getFriendlyURL.d.ts.map +1 -1
  84. package/dist/utils/normalizeHandler.d.ts.map +1 -1
  85. package/dist/utils/parseRoute.d.ts.map +1 -1
  86. package/dist/utils/pluginUtils.d.ts +2 -2
  87. package/dist/utils/pluginUtils.d.ts.map +1 -1
  88. package/dist/utils/printCleanupDetails.d.ts.map +1 -1
  89. package/dist/utils/printInstallDetails.d.ts.map +1 -1
  90. package/dist/utils/removeIgnoredSearchParams.d.ts.map +1 -1
  91. package/dist/utils/waitUntil.d.ts.map +1 -1
  92. package/package.json +29 -13
  93. package/src/NavigationRoute.ts +2 -2
  94. package/src/RegExpRoute.ts +2 -2
  95. package/src/Route.ts +2 -2
  96. package/src/Serwist.ts +170 -271
  97. package/src/cacheNames.ts +1 -1
  98. package/src/copyResponse.ts +2 -2
  99. package/src/index.internal.ts +16 -16
  100. package/src/index.ts +68 -93
  101. package/src/lib/backgroundSync/BackgroundSyncPlugin.ts +2 -2
  102. package/src/lib/backgroundSync/BackgroundSyncQueue.ts +4 -4
  103. package/src/lib/backgroundSync/BackgroundSyncQueueStore.ts +1 -1
  104. package/src/lib/backgroundSync/StorableRequest.ts +1 -1
  105. package/src/lib/backgroundSync/index.ts +5 -0
  106. package/src/lib/broadcastUpdate/BroadcastCacheUpdate.ts +4 -4
  107. package/src/lib/broadcastUpdate/BroadcastUpdatePlugin.ts +2 -2
  108. package/src/lib/broadcastUpdate/index.ts +5 -0
  109. package/src/lib/broadcastUpdate/responsesAreSame.ts +2 -2
  110. package/src/lib/cacheableResponse/CacheableResponse.ts +4 -4
  111. package/src/lib/cacheableResponse/CacheableResponsePlugin.ts +3 -3
  112. package/src/lib/cacheableResponse/index.ts +3 -0
  113. package/src/{legacy/utils → lib/controllers/PrecacheController}/PrecacheCacheKeyPlugin.ts +5 -6
  114. package/src/lib/controllers/PrecacheController/PrecacheController.ts +328 -0
  115. package/src/{utils → lib/controllers/PrecacheController}/PrecacheInstallReportPlugin.ts +5 -5
  116. package/src/{PrecacheRoute.ts → lib/controllers/PrecacheController/PrecacheRoute.ts} +11 -12
  117. package/src/lib/{strategies → controllers/PrecacheController}/PrecacheStrategy.ts +13 -13
  118. package/src/lib/controllers/PrecacheController/parsePrecacheOptions.ts +46 -0
  119. package/src/lib/controllers/RuntimeCacheController.ts +119 -0
  120. package/src/lib/controllers/index.ts +3 -0
  121. package/src/lib/expiration/CacheExpiration.ts +3 -3
  122. package/src/lib/expiration/ExpirationPlugin.ts +8 -8
  123. package/src/lib/expiration/index.ts +3 -0
  124. package/src/lib/googleAnalytics/index.ts +2 -0
  125. package/src/lib/googleAnalytics/initializeGoogleAnalytics.ts +5 -5
  126. package/src/lib/precaching/PrecacheFallbackPlugin.ts +23 -12
  127. package/src/lib/precaching/index.ts +2 -0
  128. package/src/lib/rangeRequests/RangeRequestsPlugin.ts +3 -3
  129. package/src/lib/rangeRequests/createPartialResponse.ts +3 -3
  130. package/src/lib/rangeRequests/index.ts +2 -0
  131. package/src/lib/rangeRequests/utils/calculateEffectiveBoundaries.ts +2 -2
  132. package/src/lib/rangeRequests/utils/parseRangeHeader.ts +2 -2
  133. package/src/lib/strategies/CacheFirst.ts +3 -3
  134. package/src/lib/strategies/CacheOnly.ts +3 -3
  135. package/src/lib/strategies/NetworkFirst.ts +3 -3
  136. package/src/lib/strategies/NetworkOnly.ts +4 -4
  137. package/src/lib/strategies/StaleWhileRevalidate.ts +3 -3
  138. package/src/lib/strategies/Strategy.ts +7 -7
  139. package/src/lib/strategies/StrategyHandler.ts +18 -18
  140. package/src/lib/strategies/index.ts +10 -0
  141. package/src/lib/strategies/plugins/cacheOkAndOpaquePlugin.ts +2 -2
  142. package/src/lib/strategies/utils/messages.ts +2 -2
  143. package/src/models/messages/messages.ts +3 -3
  144. package/src/navigationPreload.ts +1 -1
  145. package/src/registerQuotaErrorCallback.ts +2 -2
  146. package/src/setCacheNameDetails.ts +4 -4
  147. package/src/types.ts +56 -68
  148. package/src/utils/SerwistError.ts +2 -2
  149. package/src/utils/cacheNames.ts +1 -1
  150. package/src/utils/executeQuotaErrorCallbacks.ts +1 -1
  151. package/src/utils/pluginUtils.ts +2 -2
  152. package/dist/PrecacheRoute.d.ts.map +0 -1
  153. package/dist/chunks/printInstallDetails.js +0 -1601
  154. package/dist/chunks/resultingClientExists.js +0 -32
  155. package/dist/index.legacy.d.ts +0 -28
  156. package/dist/index.legacy.d.ts.map +0 -1
  157. package/dist/index.legacy.js +0 -790
  158. package/dist/legacy/PrecacheController.d.ts.map +0 -1
  159. package/dist/legacy/PrecacheFallbackPlugin.d.ts +0 -61
  160. package/dist/legacy/PrecacheFallbackPlugin.d.ts.map +0 -1
  161. package/dist/legacy/PrecacheRoute.d.ts +0 -19
  162. package/dist/legacy/PrecacheRoute.d.ts.map +0 -1
  163. package/dist/legacy/Router.d.ts +0 -151
  164. package/dist/legacy/Router.d.ts.map +0 -1
  165. package/dist/legacy/addPlugins.d.ts +0 -9
  166. package/dist/legacy/addPlugins.d.ts.map +0 -1
  167. package/dist/legacy/addRoute.d.ts +0 -14
  168. package/dist/legacy/addRoute.d.ts.map +0 -1
  169. package/dist/legacy/constants.d.ts +0 -10
  170. package/dist/legacy/constants.d.ts.map +0 -1
  171. package/dist/legacy/createHandlerBoundToURL.d.ts +0 -17
  172. package/dist/legacy/createHandlerBoundToURL.d.ts.map +0 -1
  173. package/dist/legacy/fallbacks.d.ts +0 -59
  174. package/dist/legacy/fallbacks.d.ts.map +0 -1
  175. package/dist/legacy/getCacheKeyForURL.d.ts +0 -20
  176. package/dist/legacy/getCacheKeyForURL.d.ts.map +0 -1
  177. package/dist/legacy/handlePrecaching.d.ts +0 -54
  178. package/dist/legacy/handlePrecaching.d.ts.map +0 -1
  179. package/dist/legacy/initializeGoogleAnalytics.d.ts +0 -38
  180. package/dist/legacy/initializeGoogleAnalytics.d.ts.map +0 -1
  181. package/dist/legacy/installSerwist.d.ts +0 -81
  182. package/dist/legacy/installSerwist.d.ts.map +0 -1
  183. package/dist/legacy/matchPrecache.d.ts +0 -15
  184. package/dist/legacy/matchPrecache.d.ts.map +0 -1
  185. package/dist/legacy/precache.d.ts +0 -20
  186. package/dist/legacy/precache.d.ts.map +0 -1
  187. package/dist/legacy/precacheAndRoute.d.ts +0 -14
  188. package/dist/legacy/precacheAndRoute.d.ts.map +0 -1
  189. package/dist/legacy/registerRoute.d.ts +0 -16
  190. package/dist/legacy/registerRoute.d.ts.map +0 -1
  191. package/dist/legacy/registerRuntimeCaching.d.ts +0 -11
  192. package/dist/legacy/registerRuntimeCaching.d.ts.map +0 -1
  193. package/dist/legacy/setCatchHandler.d.ts +0 -10
  194. package/dist/legacy/setCatchHandler.d.ts.map +0 -1
  195. package/dist/legacy/setDefaultHandler.d.ts +0 -13
  196. package/dist/legacy/setDefaultHandler.d.ts.map +0 -1
  197. package/dist/legacy/singletonPrecacheController.d.ts +0 -34
  198. package/dist/legacy/singletonPrecacheController.d.ts.map +0 -1
  199. package/dist/legacy/singletonRouter.d.ts +0 -41
  200. package/dist/legacy/singletonRouter.d.ts.map +0 -1
  201. package/dist/legacy/unregisterRoute.d.ts +0 -9
  202. package/dist/legacy/unregisterRoute.d.ts.map +0 -1
  203. package/dist/legacy/utils/PrecacheCacheKeyPlugin.d.ts.map +0 -1
  204. package/dist/legacy/utils/getCacheKeyForURL.d.ts +0 -14
  205. package/dist/legacy/utils/getCacheKeyForURL.d.ts.map +0 -1
  206. package/dist/lib/strategies/PrecacheStrategy.d.ts.map +0 -1
  207. package/dist/utils/PrecacheCacheKeyPlugin.d.ts +0 -16
  208. package/dist/utils/PrecacheCacheKeyPlugin.d.ts.map +0 -1
  209. package/dist/utils/PrecacheInstallReportPlugin.d.ts +0 -14
  210. package/dist/utils/PrecacheInstallReportPlugin.d.ts.map +0 -1
  211. package/dist/utils/parsePrecacheOptions.d.ts +0 -26
  212. package/dist/utils/parsePrecacheOptions.d.ts.map +0 -1
  213. package/src/index.legacy.ts +0 -62
  214. package/src/legacy/PrecacheController.ts +0 -337
  215. package/src/legacy/PrecacheFallbackPlugin.ts +0 -92
  216. package/src/legacy/PrecacheRoute.ts +0 -48
  217. package/src/legacy/Router.ts +0 -484
  218. package/src/legacy/addPlugins.ts +0 -21
  219. package/src/legacy/addRoute.ts +0 -27
  220. package/src/legacy/constants.ts +0 -22
  221. package/src/legacy/createHandlerBoundToURL.ts +0 -30
  222. package/src/legacy/fallbacks.ts +0 -94
  223. package/src/legacy/getCacheKeyForURL.ts +0 -32
  224. package/src/legacy/handlePrecaching.ts +0 -86
  225. package/src/legacy/initializeGoogleAnalytics.ts +0 -218
  226. package/src/legacy/installSerwist.ts +0 -170
  227. package/src/legacy/matchPrecache.ts +0 -27
  228. package/src/legacy/precache.ts +0 -33
  229. package/src/legacy/precacheAndRoute.ts +0 -27
  230. package/src/legacy/registerRoute.ts +0 -28
  231. package/src/legacy/registerRuntimeCaching.ts +0 -17
  232. package/src/legacy/setCatchHandler.ts +0 -21
  233. package/src/legacy/setDefaultHandler.ts +0 -24
  234. package/src/legacy/singletonPrecacheController.ts +0 -53
  235. package/src/legacy/singletonRouter.ts +0 -70
  236. package/src/legacy/unregisterRoute.ts +0 -13
  237. package/src/legacy/utils/getCacheKeyForURL.ts +0 -36
  238. package/src/utils/PrecacheCacheKeyPlugin.ts +0 -33
  239. package/src/utils/parsePrecacheOptions.ts +0 -47
package/dist/index.js CHANGED
@@ -1,29 +1,1751 @@
1
- import { R as Route, g as generateURLVariations, B as BackgroundSyncPlugin, N as NetworkOnly, a as NetworkFirst, P as PrecacheStrategy, e as enableNavigationPreload, s as setCacheNameDetails, b as NavigationRoute, S as Strategy, d as disableDevLogs, c as createCacheKey, p as parallel, f as printInstallDetails, h as printCleanupDetails, n as normalizeHandler, i as defaultMethod, j as parseRoute, k as PrecacheInstallReportPlugin, m as messages, l as cacheOkAndOpaquePlugin } from './chunks/printInstallDetails.js';
2
- export { v as BackgroundSyncQueue, w as BackgroundSyncQueueStore, u as RegExpRoute, x as StorableRequest, t as StrategyHandler, o as copyResponse, q as disableNavigationPreload, r as isNavigationPreloadSupported } from './chunks/printInstallDetails.js';
3
- import { l as logger, g as getFriendlyURL, c as cacheNames$1, a as clientsClaim, b as cleanupOutdatedCaches, f as finalAssertExports, S as SerwistError, w as waitUntil, t as timeout, q as quotaErrorCallbacks } from './chunks/waitUntil.js';
4
- import { r as resultingClientExists } from './chunks/resultingClientExists.js';
5
- import { deleteDB, openDB } from 'idb';
1
+ import { f as finalAssertExports, l as logger, S as SerwistError, g as getFriendlyURL, c as canConstructResponseFromBodyStream, D as Deferred, t as timeout, a as cacheMatchIgnoreParams, e as executeQuotaErrorCallbacks, b as cacheNames$1, d as clientsClaim, w as waitUntil, q as quotaErrorCallbacks, r as resultingClientExists } from './chunks/waitUntil.js';
2
+ import { parallel } from '@serwist/utils';
3
+ import { openDB, deleteDB } from 'idb';
4
+
5
+ const normalizeHandler = (handler)=>{
6
+ if (handler && typeof handler === "object") {
7
+ if (process.env.NODE_ENV !== "production") {
8
+ finalAssertExports.hasMethod(handler, "handle", {
9
+ moduleName: "serwist",
10
+ className: "Route",
11
+ funcName: "constructor",
12
+ paramName: "handler"
13
+ });
14
+ }
15
+ return handler;
16
+ }
17
+ if (process.env.NODE_ENV !== "production") {
18
+ finalAssertExports.isType(handler, "function", {
19
+ moduleName: "serwist",
20
+ className: "Route",
21
+ funcName: "constructor",
22
+ paramName: "handler"
23
+ });
24
+ }
25
+ return {
26
+ handle: handler
27
+ };
28
+ };
29
+
30
+ const defaultMethod = "GET";
31
+ const validMethods = [
32
+ "DELETE",
33
+ "GET",
34
+ "HEAD",
35
+ "PATCH",
36
+ "POST",
37
+ "PUT"
38
+ ];
39
+
40
+ class Route {
41
+ handler;
42
+ match;
43
+ method;
44
+ catchHandler;
45
+ constructor(match, handler, method = defaultMethod){
46
+ if (process.env.NODE_ENV !== "production") {
47
+ finalAssertExports.isType(match, "function", {
48
+ moduleName: "serwist",
49
+ className: "Route",
50
+ funcName: "constructor",
51
+ paramName: "match"
52
+ });
53
+ if (method) {
54
+ finalAssertExports.isOneOf(method, validMethods, {
55
+ paramName: "method"
56
+ });
57
+ }
58
+ }
59
+ this.handler = normalizeHandler(handler);
60
+ this.match = match;
61
+ this.method = method;
62
+ }
63
+ setCatchHandler(handler) {
64
+ this.catchHandler = normalizeHandler(handler);
65
+ }
66
+ }
67
+
68
+ class NavigationRoute extends Route {
69
+ _allowlist;
70
+ _denylist;
71
+ constructor(handler, { allowlist = [
72
+ /./
73
+ ], denylist = [] } = {}){
74
+ if (process.env.NODE_ENV !== "production") {
75
+ finalAssertExports.isArrayOfClass(allowlist, RegExp, {
76
+ moduleName: "serwist",
77
+ className: "NavigationRoute",
78
+ funcName: "constructor",
79
+ paramName: "options.allowlist"
80
+ });
81
+ finalAssertExports.isArrayOfClass(denylist, RegExp, {
82
+ moduleName: "serwist",
83
+ className: "NavigationRoute",
84
+ funcName: "constructor",
85
+ paramName: "options.denylist"
86
+ });
87
+ }
88
+ super((options)=>this._match(options), handler);
89
+ this._allowlist = allowlist;
90
+ this._denylist = denylist;
91
+ }
92
+ _match({ url, request }) {
93
+ if (request && request.mode !== "navigate") {
94
+ return false;
95
+ }
96
+ const pathnameAndSearch = url.pathname + url.search;
97
+ for (const regExp of this._denylist){
98
+ if (regExp.test(pathnameAndSearch)) {
99
+ if (process.env.NODE_ENV !== "production") {
100
+ logger.log(`The navigation route ${pathnameAndSearch} is not being used, since the URL matches this denylist pattern: ${regExp.toString()}`);
101
+ }
102
+ return false;
103
+ }
104
+ }
105
+ if (this._allowlist.some((regExp)=>regExp.test(pathnameAndSearch))) {
106
+ if (process.env.NODE_ENV !== "production") {
107
+ logger.debug(`The navigation route ${pathnameAndSearch} is being used.`);
108
+ }
109
+ return true;
110
+ }
111
+ if (process.env.NODE_ENV !== "production") {
112
+ logger.log(`The navigation route ${pathnameAndSearch} is not being used, since the URL being navigated to doesn't match the allowlist.`);
113
+ }
114
+ return false;
115
+ }
116
+ }
117
+
118
+ class RegExpRoute extends Route {
119
+ constructor(regExp, handler, method){
120
+ if (process.env.NODE_ENV !== "production") {
121
+ finalAssertExports.isInstance(regExp, RegExp, {
122
+ moduleName: "serwist",
123
+ className: "RegExpRoute",
124
+ funcName: "constructor",
125
+ paramName: "pattern"
126
+ });
127
+ }
128
+ const match = ({ url })=>{
129
+ const result = regExp.exec(url.href);
130
+ if (!result) {
131
+ return;
132
+ }
133
+ if (url.origin !== location.origin && result.index !== 0) {
134
+ if (process.env.NODE_ENV !== "production") {
135
+ logger.debug(`The regular expression '${regExp.toString()}' only partially matched against the cross-origin URL '${url.toString()}'. RegExpRoute's will only handle cross-origin requests if they match the entire URL.`);
136
+ }
137
+ return;
138
+ }
139
+ return result.slice(1);
140
+ };
141
+ super(match, handler, method);
142
+ }
143
+ }
144
+
145
+ const REVISION_SEARCH_PARAM = "__WB_REVISION__";
146
+ const createCacheKey = (entry)=>{
147
+ if (!entry) {
148
+ throw new SerwistError("add-to-cache-list-unexpected-type", {
149
+ entry
150
+ });
151
+ }
152
+ if (typeof entry === "string") {
153
+ const urlObject = new URL(entry, location.href);
154
+ return {
155
+ cacheKey: urlObject.href,
156
+ url: urlObject.href
157
+ };
158
+ }
159
+ const { revision, url } = entry;
160
+ if (!url) {
161
+ throw new SerwistError("add-to-cache-list-unexpected-type", {
162
+ entry
163
+ });
164
+ }
165
+ if (!revision) {
166
+ const urlObject = new URL(url, location.href);
167
+ return {
168
+ cacheKey: urlObject.href,
169
+ url: urlObject.href
170
+ };
171
+ }
172
+ const cacheKeyURL = new URL(url, location.href);
173
+ const originalURL = new URL(url, location.href);
174
+ cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision);
175
+ return {
176
+ cacheKey: cacheKeyURL.href,
177
+ url: originalURL.href
178
+ };
179
+ };
180
+
181
+ const logGroup = (groupTitle, deletedURLs)=>{
182
+ logger.groupCollapsed(groupTitle);
183
+ for (const url of deletedURLs){
184
+ logger.log(url);
185
+ }
186
+ logger.groupEnd();
187
+ };
188
+ const printCleanupDetails = (deletedURLs)=>{
189
+ const deletionCount = deletedURLs.length;
190
+ if (deletionCount > 0) {
191
+ logger.groupCollapsed(`During precaching cleanup, ${deletionCount} cached request${deletionCount === 1 ? " was" : "s were"} deleted.`);
192
+ logGroup("Deleted Cache Requests", deletedURLs);
193
+ logger.groupEnd();
194
+ }
195
+ };
196
+
197
+ function _nestedGroup(groupTitle, urls) {
198
+ if (urls.length === 0) {
199
+ return;
200
+ }
201
+ logger.groupCollapsed(groupTitle);
202
+ for (const url of urls){
203
+ logger.log(url);
204
+ }
205
+ logger.groupEnd();
206
+ }
207
+ const printInstallDetails = (urlsToPrecache, urlsAlreadyPrecached)=>{
208
+ const precachedCount = urlsToPrecache.length;
209
+ const alreadyPrecachedCount = urlsAlreadyPrecached.length;
210
+ if (precachedCount || alreadyPrecachedCount) {
211
+ let message = `Precaching ${precachedCount} file${precachedCount === 1 ? "" : "s"}.`;
212
+ if (alreadyPrecachedCount > 0) {
213
+ message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? " is" : "s are"} already cached.`;
214
+ }
215
+ logger.groupCollapsed(message);
216
+ _nestedGroup("View newly precached URLs.", urlsToPrecache);
217
+ _nestedGroup("View previously precached URLs.", urlsAlreadyPrecached);
218
+ logger.groupEnd();
219
+ }
220
+ };
221
+
222
+ class PrecacheInstallReportPlugin {
223
+ updatedURLs = [];
224
+ notUpdatedURLs = [];
225
+ handlerWillStart = async ({ request, state })=>{
226
+ if (state) {
227
+ state.originalRequest = request;
228
+ }
229
+ };
230
+ cachedResponseWillBeUsed = async ({ event, state, cachedResponse })=>{
231
+ if (event.type === "install") {
232
+ if (state?.originalRequest && state.originalRequest instanceof Request) {
233
+ const url = state.originalRequest.url;
234
+ if (cachedResponse) {
235
+ this.notUpdatedURLs.push(url);
236
+ } else {
237
+ this.updatedURLs.push(url);
238
+ }
239
+ }
240
+ }
241
+ return cachedResponse;
242
+ };
243
+ }
244
+
245
+ const removeIgnoredSearchParams = (urlObject, ignoreURLParametersMatching = [])=>{
246
+ for (const paramName of [
247
+ ...urlObject.searchParams.keys()
248
+ ]){
249
+ if (ignoreURLParametersMatching.some((regExp)=>regExp.test(paramName))) {
250
+ urlObject.searchParams.delete(paramName);
251
+ }
252
+ }
253
+ return urlObject;
254
+ };
255
+
256
+ function* generateURLVariations(url, { directoryIndex = "index.html", ignoreURLParametersMatching = [
257
+ /^utm_/,
258
+ /^fbclid$/
259
+ ], cleanURLs = true, urlManipulation } = {}) {
260
+ const urlObject = new URL(url, location.href);
261
+ urlObject.hash = "";
262
+ yield urlObject.href;
263
+ const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching);
264
+ yield urlWithoutIgnoredParams.href;
265
+ if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith("/")) {
266
+ const directoryURL = new URL(urlWithoutIgnoredParams.href);
267
+ directoryURL.pathname += directoryIndex;
268
+ yield directoryURL.href;
269
+ }
270
+ if (cleanURLs) {
271
+ const cleanURL = new URL(urlWithoutIgnoredParams.href);
272
+ cleanURL.pathname += ".html";
273
+ yield cleanURL.href;
274
+ }
275
+ if (urlManipulation) {
276
+ const additionalURLs = urlManipulation({
277
+ url: urlObject
278
+ });
279
+ for (const urlToAttempt of additionalURLs){
280
+ yield urlToAttempt.href;
281
+ }
282
+ }
283
+ }
6
284
 
7
285
  class PrecacheRoute extends Route {
8
- constructor(serwist, options){
286
+ constructor(controller, options){
9
287
  const match = ({ request })=>{
10
- const urlsToCacheKeys = serwist.getUrlsToPrecacheKeys();
288
+ const urlsToCacheKeys = controller.getUrlsToPrecacheKeys();
11
289
  for (const possibleURL of generateURLVariations(request.url, options)){
12
290
  const cacheKey = urlsToCacheKeys.get(possibleURL);
13
291
  if (cacheKey) {
14
- const integrity = serwist.getIntegrityForPrecacheKey(cacheKey);
292
+ const integrity = controller.getIntegrityForPrecacheKey(cacheKey);
15
293
  return {
16
294
  cacheKey,
17
295
  integrity
18
296
  };
19
297
  }
20
298
  }
21
- if (process.env.NODE_ENV !== "production") {
22
- logger.debug(`Precaching did not find a match for ${getFriendlyURL(request.url)}.`);
299
+ if (process.env.NODE_ENV !== "production") {
300
+ logger.debug(`Precaching did not find a match for ${getFriendlyURL(request.url)}.`);
301
+ }
302
+ return;
303
+ };
304
+ super(match, controller.strategy);
305
+ }
306
+ }
307
+
308
+ const copyResponse = async (response, modifier)=>{
309
+ let origin = null;
310
+ if (response.url) {
311
+ const responseURL = new URL(response.url);
312
+ origin = responseURL.origin;
313
+ }
314
+ if (origin !== self.location.origin) {
315
+ throw new SerwistError("cross-origin-copy-response", {
316
+ origin
317
+ });
318
+ }
319
+ const clonedResponse = response.clone();
320
+ const responseInit = {
321
+ headers: new Headers(clonedResponse.headers),
322
+ status: clonedResponse.status,
323
+ statusText: clonedResponse.statusText
324
+ };
325
+ const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit;
326
+ const body = canConstructResponseFromBodyStream() ? clonedResponse.body : await clonedResponse.blob();
327
+ return new Response(body, modifiedResponseInit);
328
+ };
329
+
330
+ function toRequest(input) {
331
+ return typeof input === "string" ? new Request(input) : input;
332
+ }
333
+ class StrategyHandler {
334
+ event;
335
+ request;
336
+ url;
337
+ params;
338
+ _cacheKeys = {};
339
+ _strategy;
340
+ _handlerDeferred;
341
+ _extendLifetimePromises;
342
+ _plugins;
343
+ _pluginStateMap;
344
+ constructor(strategy, options){
345
+ if (process.env.NODE_ENV !== "production") {
346
+ finalAssertExports.isInstance(options.event, ExtendableEvent, {
347
+ moduleName: "serwist",
348
+ className: "StrategyHandler",
349
+ funcName: "constructor",
350
+ paramName: "options.event"
351
+ });
352
+ finalAssertExports.isInstance(options.request, Request, {
353
+ moduleName: "serwist",
354
+ className: "StrategyHandler",
355
+ funcName: "constructor",
356
+ paramName: "options.request"
357
+ });
358
+ }
359
+ this.event = options.event;
360
+ this.request = options.request;
361
+ if (options.url) {
362
+ this.url = options.url;
363
+ this.params = options.params;
364
+ }
365
+ this._strategy = strategy;
366
+ this._handlerDeferred = new Deferred();
367
+ this._extendLifetimePromises = [];
368
+ this._plugins = [
369
+ ...strategy.plugins
370
+ ];
371
+ this._pluginStateMap = new Map();
372
+ for (const plugin of this._plugins){
373
+ this._pluginStateMap.set(plugin, {});
374
+ }
375
+ this.event.waitUntil(this._handlerDeferred.promise);
376
+ }
377
+ async fetch(input) {
378
+ const { event } = this;
379
+ let request = toRequest(input);
380
+ const preloadResponse = await this.getPreloadResponse();
381
+ if (preloadResponse) {
382
+ return preloadResponse;
383
+ }
384
+ const originalRequest = this.hasCallback("fetchDidFail") ? request.clone() : null;
385
+ try {
386
+ for (const cb of this.iterateCallbacks("requestWillFetch")){
387
+ request = await cb({
388
+ request: request.clone(),
389
+ event
390
+ });
391
+ }
392
+ } catch (err) {
393
+ if (err instanceof Error) {
394
+ throw new SerwistError("plugin-error-request-will-fetch", {
395
+ thrownErrorMessage: err.message
396
+ });
397
+ }
398
+ }
399
+ const pluginFilteredRequest = request.clone();
400
+ try {
401
+ let fetchResponse;
402
+ fetchResponse = await fetch(request, request.mode === "navigate" ? undefined : this._strategy.fetchOptions);
403
+ if (process.env.NODE_ENV !== "production") {
404
+ logger.debug(`Network request for '${getFriendlyURL(request.url)}' returned a response with status '${fetchResponse.status}'.`);
405
+ }
406
+ for (const callback of this.iterateCallbacks("fetchDidSucceed")){
407
+ fetchResponse = await callback({
408
+ event,
409
+ request: pluginFilteredRequest,
410
+ response: fetchResponse
411
+ });
412
+ }
413
+ return fetchResponse;
414
+ } catch (error) {
415
+ if (process.env.NODE_ENV !== "production") {
416
+ logger.log(`Network request for '${getFriendlyURL(request.url)}' threw an error.`, error);
417
+ }
418
+ if (originalRequest) {
419
+ await this.runCallbacks("fetchDidFail", {
420
+ error: error,
421
+ event,
422
+ originalRequest: originalRequest.clone(),
423
+ request: pluginFilteredRequest.clone()
424
+ });
425
+ }
426
+ throw error;
427
+ }
428
+ }
429
+ async fetchAndCachePut(input) {
430
+ const response = await this.fetch(input);
431
+ const responseClone = response.clone();
432
+ void this.waitUntil(this.cachePut(input, responseClone));
433
+ return response;
434
+ }
435
+ async cacheMatch(key) {
436
+ const request = toRequest(key);
437
+ let cachedResponse;
438
+ const { cacheName, matchOptions } = this._strategy;
439
+ const effectiveRequest = await this.getCacheKey(request, "read");
440
+ const multiMatchOptions = {
441
+ ...matchOptions,
442
+ ...{
443
+ cacheName
444
+ }
445
+ };
446
+ cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);
447
+ if (process.env.NODE_ENV !== "production") {
448
+ if (cachedResponse) {
449
+ logger.debug(`Found a cached response in '${cacheName}'.`);
450
+ } else {
451
+ logger.debug(`No cached response found in '${cacheName}'.`);
452
+ }
453
+ }
454
+ for (const callback of this.iterateCallbacks("cachedResponseWillBeUsed")){
455
+ cachedResponse = await callback({
456
+ cacheName,
457
+ matchOptions,
458
+ cachedResponse,
459
+ request: effectiveRequest,
460
+ event: this.event
461
+ }) || undefined;
462
+ }
463
+ return cachedResponse;
464
+ }
465
+ async cachePut(key, response) {
466
+ const request = toRequest(key);
467
+ await timeout(0);
468
+ const effectiveRequest = await this.getCacheKey(request, "write");
469
+ if (process.env.NODE_ENV !== "production") {
470
+ if (effectiveRequest.method && effectiveRequest.method !== "GET") {
471
+ throw new SerwistError("attempt-to-cache-non-get-request", {
472
+ url: getFriendlyURL(effectiveRequest.url),
473
+ method: effectiveRequest.method
474
+ });
475
+ }
476
+ }
477
+ if (!response) {
478
+ if (process.env.NODE_ENV !== "production") {
479
+ logger.error(`Cannot cache non-existent response for '${getFriendlyURL(effectiveRequest.url)}'.`);
480
+ }
481
+ throw new SerwistError("cache-put-with-no-response", {
482
+ url: getFriendlyURL(effectiveRequest.url)
483
+ });
484
+ }
485
+ const responseToCache = await this._ensureResponseSafeToCache(response);
486
+ if (!responseToCache) {
487
+ if (process.env.NODE_ENV !== "production") {
488
+ logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' will not be cached.`, responseToCache);
489
+ }
490
+ return false;
491
+ }
492
+ const { cacheName, matchOptions } = this._strategy;
493
+ const cache = await self.caches.open(cacheName);
494
+ if (process.env.NODE_ENV !== "production") {
495
+ const vary = response.headers.get("Vary");
496
+ if (vary && matchOptions?.ignoreVary !== true) {
497
+ logger.debug(`The response for ${getFriendlyURL(effectiveRequest.url)} has a 'Vary: ${vary}' header. Consider setting the {ignoreVary: true} option on your strategy to ensure cache matching and deletion works as expected.`);
498
+ }
499
+ }
500
+ const hasCacheUpdateCallback = this.hasCallback("cacheDidUpdate");
501
+ const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams(cache, effectiveRequest.clone(), [
502
+ "__WB_REVISION__"
503
+ ], matchOptions) : null;
504
+ if (process.env.NODE_ENV !== "production") {
505
+ logger.debug(`Updating the '${cacheName}' cache with a new Response for ${getFriendlyURL(effectiveRequest.url)}.`);
506
+ }
507
+ try {
508
+ await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);
509
+ } catch (error) {
510
+ if (error instanceof Error) {
511
+ if (error.name === "QuotaExceededError") {
512
+ await executeQuotaErrorCallbacks();
513
+ }
514
+ throw error;
515
+ }
516
+ }
517
+ for (const callback of this.iterateCallbacks("cacheDidUpdate")){
518
+ await callback({
519
+ cacheName,
520
+ oldResponse,
521
+ newResponse: responseToCache.clone(),
522
+ request: effectiveRequest,
523
+ event: this.event
524
+ });
525
+ }
526
+ return true;
527
+ }
528
+ async getCacheKey(request, mode) {
529
+ const key = `${request.url} | ${mode}`;
530
+ if (!this._cacheKeys[key]) {
531
+ let effectiveRequest = request;
532
+ for (const callback of this.iterateCallbacks("cacheKeyWillBeUsed")){
533
+ effectiveRequest = toRequest(await callback({
534
+ mode,
535
+ request: effectiveRequest,
536
+ event: this.event,
537
+ params: this.params
538
+ }));
539
+ }
540
+ this._cacheKeys[key] = effectiveRequest;
541
+ }
542
+ return this._cacheKeys[key];
543
+ }
544
+ hasCallback(name) {
545
+ for (const plugin of this._strategy.plugins){
546
+ if (name in plugin) {
547
+ return true;
548
+ }
549
+ }
550
+ return false;
551
+ }
552
+ async runCallbacks(name, param) {
553
+ for (const callback of this.iterateCallbacks(name)){
554
+ await callback(param);
555
+ }
556
+ }
557
+ *iterateCallbacks(name) {
558
+ for (const plugin of this._strategy.plugins){
559
+ if (typeof plugin[name] === "function") {
560
+ const state = this._pluginStateMap.get(plugin);
561
+ const statefulCallback = (param)=>{
562
+ const statefulParam = {
563
+ ...param,
564
+ state
565
+ };
566
+ return plugin[name](statefulParam);
567
+ };
568
+ yield statefulCallback;
569
+ }
570
+ }
571
+ }
572
+ waitUntil(promise) {
573
+ this._extendLifetimePromises.push(promise);
574
+ return promise;
575
+ }
576
+ async doneWaiting() {
577
+ let promise = undefined;
578
+ while(promise = this._extendLifetimePromises.shift()){
579
+ await promise;
580
+ }
581
+ }
582
+ destroy() {
583
+ this._handlerDeferred.resolve(null);
584
+ }
585
+ async getPreloadResponse() {
586
+ if (this.event instanceof FetchEvent && this.event.request.mode === "navigate" && "preloadResponse" in this.event) {
587
+ try {
588
+ const possiblePreloadResponse = await this.event.preloadResponse;
589
+ if (possiblePreloadResponse) {
590
+ if (process.env.NODE_ENV !== "production") {
591
+ logger.log(`Using a preloaded navigation response for '${getFriendlyURL(this.event.request.url)}'`);
592
+ }
593
+ return possiblePreloadResponse;
594
+ }
595
+ } catch (error) {
596
+ if (process.env.NODE_ENV !== "production") {
597
+ logger.error(error);
598
+ }
599
+ return undefined;
600
+ }
601
+ }
602
+ return undefined;
603
+ }
604
+ async _ensureResponseSafeToCache(response) {
605
+ let responseToCache = response;
606
+ let pluginsUsed = false;
607
+ for (const callback of this.iterateCallbacks("cacheWillUpdate")){
608
+ responseToCache = await callback({
609
+ request: this.request,
610
+ response: responseToCache,
611
+ event: this.event
612
+ }) || undefined;
613
+ pluginsUsed = true;
614
+ if (!responseToCache) {
615
+ break;
616
+ }
617
+ }
618
+ if (!pluginsUsed) {
619
+ if (responseToCache && responseToCache.status !== 200) {
620
+ if (process.env.NODE_ENV !== "production") {
621
+ if (responseToCache.status === 0) {
622
+ logger.warn(`The response for '${this.request.url}' is an opaque response. The caching strategy that you're using will not cache opaque responses by default.`);
623
+ } else {
624
+ logger.debug(`The response for '${this.request.url}' returned a status code of '${response.status}' and won't be cached as a result.`);
625
+ }
626
+ }
627
+ responseToCache = undefined;
628
+ }
629
+ }
630
+ return responseToCache;
631
+ }
632
+ }
633
+
634
+ class Strategy {
635
+ cacheName;
636
+ plugins;
637
+ fetchOptions;
638
+ matchOptions;
639
+ constructor(options = {}){
640
+ this.cacheName = cacheNames$1.getRuntimeName(options.cacheName);
641
+ this.plugins = options.plugins || [];
642
+ this.fetchOptions = options.fetchOptions;
643
+ this.matchOptions = options.matchOptions;
644
+ }
645
+ handle(options) {
646
+ const [responseDone] = this.handleAll(options);
647
+ return responseDone;
648
+ }
649
+ handleAll(options) {
650
+ if (options instanceof FetchEvent) {
651
+ options = {
652
+ event: options,
653
+ request: options.request
654
+ };
655
+ }
656
+ const event = options.event;
657
+ const request = typeof options.request === "string" ? new Request(options.request) : options.request;
658
+ const handler = new StrategyHandler(this, options.url ? {
659
+ event,
660
+ request,
661
+ url: options.url,
662
+ params: options.params
663
+ } : {
664
+ event,
665
+ request
666
+ });
667
+ const responseDone = this._getResponse(handler, request, event);
668
+ const handlerDone = this._awaitComplete(responseDone, handler, request, event);
669
+ return [
670
+ responseDone,
671
+ handlerDone
672
+ ];
673
+ }
674
+ async _getResponse(handler, request, event) {
675
+ await handler.runCallbacks("handlerWillStart", {
676
+ event,
677
+ request
678
+ });
679
+ let response = undefined;
680
+ try {
681
+ response = await this._handle(request, handler);
682
+ if (response === undefined || response.type === "error") {
683
+ throw new SerwistError("no-response", {
684
+ url: request.url
685
+ });
686
+ }
687
+ } catch (error) {
688
+ if (error instanceof Error) {
689
+ for (const callback of handler.iterateCallbacks("handlerDidError")){
690
+ response = await callback({
691
+ error,
692
+ event,
693
+ request
694
+ });
695
+ if (response !== undefined) {
696
+ break;
697
+ }
698
+ }
699
+ }
700
+ if (!response) {
701
+ throw error;
702
+ }
703
+ if (process.env.NODE_ENV !== "production") {
704
+ throw logger.log(`While responding to '${getFriendlyURL(request.url)}', an ${error instanceof Error ? error.toString() : ""} error occurred. Using a fallback response provided by a handlerDidError plugin.`);
705
+ }
706
+ }
707
+ for (const callback of handler.iterateCallbacks("handlerWillRespond")){
708
+ response = await callback({
709
+ event,
710
+ request,
711
+ response
712
+ });
713
+ }
714
+ return response;
715
+ }
716
+ async _awaitComplete(responseDone, handler, request, event) {
717
+ let response = undefined;
718
+ let error = undefined;
719
+ try {
720
+ response = await responseDone;
721
+ } catch (error) {}
722
+ try {
723
+ await handler.runCallbacks("handlerDidRespond", {
724
+ event,
725
+ request,
726
+ response
727
+ });
728
+ await handler.doneWaiting();
729
+ } catch (waitUntilError) {
730
+ if (waitUntilError instanceof Error) {
731
+ error = waitUntilError;
732
+ }
733
+ }
734
+ await handler.runCallbacks("handlerDidComplete", {
735
+ event,
736
+ request,
737
+ response,
738
+ error
739
+ });
740
+ handler.destroy();
741
+ if (error) {
742
+ throw error;
743
+ }
744
+ }
745
+ }
746
+
747
+ class PrecacheStrategy extends Strategy {
748
+ _fallbackToNetwork;
749
+ static defaultPrecacheCacheabilityPlugin = {
750
+ async cacheWillUpdate ({ response }) {
751
+ if (!response || response.status >= 400) {
752
+ return null;
753
+ }
754
+ return response;
755
+ }
756
+ };
757
+ static copyRedirectedCacheableResponsesPlugin = {
758
+ async cacheWillUpdate ({ response }) {
759
+ return response.redirected ? await copyResponse(response) : response;
760
+ }
761
+ };
762
+ constructor(options = {}){
763
+ options.cacheName = cacheNames$1.getPrecacheName(options.cacheName);
764
+ super(options);
765
+ this._fallbackToNetwork = options.fallbackToNetwork !== false;
766
+ this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);
767
+ }
768
+ async _handle(request, handler) {
769
+ const preloadResponse = await handler.getPreloadResponse();
770
+ if (preloadResponse) {
771
+ return preloadResponse;
772
+ }
773
+ const response = await handler.cacheMatch(request);
774
+ if (response) {
775
+ return response;
776
+ }
777
+ if (handler.event && handler.event.type === "install") {
778
+ return await this._handleInstall(request, handler);
779
+ }
780
+ return await this._handleFetch(request, handler);
781
+ }
782
+ async _handleFetch(request, handler) {
783
+ let response = undefined;
784
+ const params = handler.params || {};
785
+ if (this._fallbackToNetwork) {
786
+ if (process.env.NODE_ENV !== "production") {
787
+ logger.warn(`The precached response for ${getFriendlyURL(request.url)} in ${this.cacheName} was not found. Falling back to the network.`);
788
+ }
789
+ const integrityInManifest = params.integrity;
790
+ const integrityInRequest = request.integrity;
791
+ const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest;
792
+ response = await handler.fetch(new Request(request, {
793
+ integrity: request.mode !== "no-cors" ? integrityInRequest || integrityInManifest : undefined
794
+ }));
795
+ if (integrityInManifest && noIntegrityConflict && request.mode !== "no-cors") {
796
+ this._useDefaultCacheabilityPluginIfNeeded();
797
+ const wasCached = await handler.cachePut(request, response.clone());
798
+ if (process.env.NODE_ENV !== "production") {
799
+ if (wasCached) {
800
+ logger.log(`A response for ${getFriendlyURL(request.url)} was used to "repair" the precache.`);
801
+ }
802
+ }
803
+ }
804
+ } else {
805
+ throw new SerwistError("missing-precache-entry", {
806
+ cacheName: this.cacheName,
807
+ url: request.url
808
+ });
809
+ }
810
+ if (process.env.NODE_ENV !== "production") {
811
+ const cacheKey = params.cacheKey || await handler.getCacheKey(request, "read");
812
+ logger.groupCollapsed(`Precaching is responding to: ${getFriendlyURL(request.url)}`);
813
+ logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`);
814
+ logger.groupCollapsed("View request details here.");
815
+ logger.log(request);
816
+ logger.groupEnd();
817
+ logger.groupCollapsed("View response details here.");
818
+ logger.log(response);
819
+ logger.groupEnd();
820
+ logger.groupEnd();
821
+ }
822
+ return response;
823
+ }
824
+ async _handleInstall(request, handler) {
825
+ this._useDefaultCacheabilityPluginIfNeeded();
826
+ const response = await handler.fetch(request);
827
+ const wasCached = await handler.cachePut(request, response.clone());
828
+ if (!wasCached) {
829
+ throw new SerwistError("bad-precaching-response", {
830
+ url: request.url,
831
+ status: response.status
832
+ });
833
+ }
834
+ return response;
835
+ }
836
+ _useDefaultCacheabilityPluginIfNeeded() {
837
+ let defaultPluginIndex = null;
838
+ let cacheWillUpdatePluginCount = 0;
839
+ for (const [index, plugin] of this.plugins.entries()){
840
+ if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) {
841
+ continue;
842
+ }
843
+ if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) {
844
+ defaultPluginIndex = index;
845
+ }
846
+ if (plugin.cacheWillUpdate) {
847
+ cacheWillUpdatePluginCount++;
848
+ }
849
+ }
850
+ if (cacheWillUpdatePluginCount === 0) {
851
+ this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin);
852
+ } else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) {
853
+ this.plugins.splice(defaultPluginIndex, 1);
854
+ }
855
+ }
856
+ }
857
+
858
+ class PrecacheCacheKeyPlugin {
859
+ _precacheController;
860
+ constructor({ precacheController }){
861
+ this._precacheController = precacheController;
862
+ }
863
+ cacheKeyWillBeUsed = async ({ request, params })=>{
864
+ const cacheKey = params?.cacheKey || this._precacheController.getPrecacheKeyForUrl(request.url);
865
+ return cacheKey ? new Request(cacheKey, {
866
+ headers: request.headers
867
+ }) : request;
868
+ };
869
+ }
870
+
871
+ const parsePrecacheOptions = (controller, { cacheName, plugins, fetchOptions, matchOptions, fallbackToNetwork, directoryIndex, ignoreURLParametersMatching, cleanURLs, urlManipulation, cleanupOutdatedCaches, concurrency, navigateFallback, navigateFallbackAllowlist, navigateFallbackDenylist } = {})=>({
872
+ strategyOptions: {
873
+ cacheName: cacheNames$1.getPrecacheName(cacheName),
874
+ plugins: [
875
+ ...plugins ?? [],
876
+ new PrecacheCacheKeyPlugin({
877
+ precacheController: controller
878
+ })
879
+ ],
880
+ fetchOptions,
881
+ matchOptions,
882
+ fallbackToNetwork
883
+ },
884
+ routeOptions: {
885
+ directoryIndex,
886
+ ignoreURLParametersMatching,
887
+ cleanURLs,
888
+ urlManipulation
889
+ },
890
+ controllerOptions: {
891
+ cleanupOutdatedCaches,
892
+ concurrency: concurrency ?? 10,
893
+ navigateFallback,
894
+ navigateFallbackAllowlist,
895
+ navigateFallbackDenylist
896
+ }
897
+ });
898
+
899
+ class PrecacheController {
900
+ _urlsToCacheKeys = new Map();
901
+ _urlsToCacheModes = new Map();
902
+ _cacheKeysToIntegrities = new Map();
903
+ _strategy;
904
+ _options;
905
+ _routeOptions;
906
+ constructor(entries, precacheOptions){
907
+ const { strategyOptions, routeOptions, controllerOptions } = parsePrecacheOptions(this, precacheOptions);
908
+ this.addToCacheList(entries);
909
+ this._strategy = new PrecacheStrategy(strategyOptions);
910
+ this._options = controllerOptions;
911
+ this._routeOptions = routeOptions;
912
+ }
913
+ get strategy() {
914
+ return this._strategy;
915
+ }
916
+ addToCacheList(entries) {
917
+ if (process.env.NODE_ENV !== "production") {
918
+ finalAssertExports.isArray(entries, {
919
+ moduleName: "serwist",
920
+ className: "PrecacheController",
921
+ funcName: "addEntries",
922
+ paramName: "entries"
923
+ });
924
+ }
925
+ const urlsToWarnAbout = [];
926
+ for (const entry of entries){
927
+ if (typeof entry === "string") {
928
+ urlsToWarnAbout.push(entry);
929
+ } else if (entry && !entry.integrity && entry.revision === undefined) {
930
+ urlsToWarnAbout.push(entry.url);
931
+ }
932
+ const { cacheKey, url } = createCacheKey(entry);
933
+ const cacheMode = typeof entry !== "string" && entry.revision ? "reload" : "default";
934
+ if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) {
935
+ throw new SerwistError("add-to-cache-list-conflicting-entries", {
936
+ firstEntry: this._urlsToCacheKeys.get(url),
937
+ secondEntry: cacheKey
938
+ });
939
+ }
940
+ if (typeof entry !== "string" && entry.integrity) {
941
+ if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) {
942
+ throw new SerwistError("add-to-cache-list-conflicting-integrities", {
943
+ url
944
+ });
945
+ }
946
+ this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);
947
+ }
948
+ this._urlsToCacheKeys.set(url, cacheKey);
949
+ this._urlsToCacheModes.set(url, cacheMode);
950
+ if (urlsToWarnAbout.length > 0) {
951
+ const warningMessage = `Serwist is precaching URLs without revision info: ${urlsToWarnAbout.join(", ")}\nThis is generally NOT safe, as you risk serving outdated assets.`;
952
+ if (process.env.NODE_ENV === "production") {
953
+ console.warn(warningMessage);
954
+ } else {
955
+ logger.warn(warningMessage);
956
+ }
957
+ }
958
+ }
959
+ }
960
+ init({ serwist }) {
961
+ serwist.registerRoute(new PrecacheRoute(this, this._routeOptions));
962
+ if (this._options.navigateFallback) {
963
+ serwist.registerRoute(new NavigationRoute(this.createHandlerBoundToUrl(this._options.navigateFallback), {
964
+ allowlist: this._options.navigateFallbackAllowlist,
965
+ denylist: this._options.navigateFallbackDenylist
966
+ }));
967
+ }
968
+ }
969
+ async install({ event }) {
970
+ const installReportPlugin = new PrecacheInstallReportPlugin();
971
+ this._strategy.plugins.push(installReportPlugin);
972
+ await parallel(this._options.concurrency, Array.from(this._urlsToCacheKeys.entries()), async ([url, cacheKey])=>{
973
+ const integrity = this._cacheKeysToIntegrities.get(cacheKey);
974
+ const cacheMode = this._urlsToCacheModes.get(url);
975
+ const request = new Request(url, {
976
+ integrity,
977
+ cache: cacheMode,
978
+ credentials: "same-origin"
979
+ });
980
+ await Promise.all(this._strategy.handleAll({
981
+ event,
982
+ request,
983
+ url: new URL(request.url),
984
+ params: {
985
+ cacheKey
986
+ }
987
+ }));
988
+ });
989
+ const { updatedURLs, notUpdatedURLs } = installReportPlugin;
990
+ if (process.env.NODE_ENV !== "production") {
991
+ printInstallDetails(updatedURLs, notUpdatedURLs);
992
+ }
993
+ }
994
+ async activate() {
995
+ const cache = await self.caches.open(this._strategy.cacheName);
996
+ const currentlyCachedRequests = await cache.keys();
997
+ const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());
998
+ const deletedCacheRequests = [];
999
+ for (const request of currentlyCachedRequests){
1000
+ if (!expectedCacheKeys.has(request.url)) {
1001
+ await cache.delete(request);
1002
+ deletedCacheRequests.push(request.url);
1003
+ }
1004
+ }
1005
+ if (process.env.NODE_ENV !== "production") {
1006
+ printCleanupDetails(deletedCacheRequests);
1007
+ }
1008
+ }
1009
+ getUrlsToPrecacheKeys() {
1010
+ return this._urlsToCacheKeys;
1011
+ }
1012
+ getPrecachedUrls() {
1013
+ return [
1014
+ ...this._urlsToCacheKeys.keys()
1015
+ ];
1016
+ }
1017
+ getPrecacheKeyForUrl(url) {
1018
+ const urlObject = new URL(url, location.href);
1019
+ return this._urlsToCacheKeys.get(urlObject.href);
1020
+ }
1021
+ getIntegrityForPrecacheKey(cacheKey) {
1022
+ return this._cacheKeysToIntegrities.get(cacheKey);
1023
+ }
1024
+ async matchPrecache(request) {
1025
+ const url = request instanceof Request ? request.url : request;
1026
+ const cacheKey = this.getPrecacheKeyForUrl(url);
1027
+ if (cacheKey) {
1028
+ const cache = await self.caches.open(this._strategy.cacheName);
1029
+ return cache.match(cacheKey);
1030
+ }
1031
+ return undefined;
1032
+ }
1033
+ createHandlerBoundToUrl(url) {
1034
+ const cacheKey = this.getPrecacheKeyForUrl(url);
1035
+ if (!cacheKey) {
1036
+ throw new SerwistError("non-precached-url", {
1037
+ url
1038
+ });
1039
+ }
1040
+ return (options)=>{
1041
+ options.request = new Request(url);
1042
+ options.params = {
1043
+ cacheKey,
1044
+ ...options.params
1045
+ };
1046
+ return this._strategy.handle(options);
1047
+ };
1048
+ }
1049
+ }
1050
+
1051
+ class PrecacheFallbackPlugin {
1052
+ _fallbackUrls;
1053
+ _precacheController;
1054
+ constructor({ fallbackUrls, precacheController, serwist }){
1055
+ this._fallbackUrls = fallbackUrls;
1056
+ if (!serwist) {
1057
+ this._precacheController = precacheController;
1058
+ } else {
1059
+ this._precacheController = serwist.precache;
1060
+ }
1061
+ }
1062
+ async handlerDidError(param) {
1063
+ for (const fallback of this._fallbackUrls){
1064
+ if (typeof fallback === "string") {
1065
+ const fallbackResponse = await this._precacheController.matchPrecache(fallback);
1066
+ if (fallbackResponse !== undefined) {
1067
+ return fallbackResponse;
1068
+ }
1069
+ } else if (fallback.matcher(param)) {
1070
+ const fallbackResponse = await this._precacheController.matchPrecache(fallback.url);
1071
+ if (fallbackResponse !== undefined) {
1072
+ return fallbackResponse;
1073
+ }
1074
+ }
1075
+ }
1076
+ return undefined;
1077
+ }
1078
+ }
1079
+
1080
+ class RuntimeCacheController {
1081
+ _entries;
1082
+ _options;
1083
+ constructor(entries, options = {}){
1084
+ this._entries = entries;
1085
+ this._options = options;
1086
+ this.init = this.init.bind(this);
1087
+ this.install = this.install.bind(this);
1088
+ }
1089
+ init({ serwist }) {
1090
+ if (this._options.fallbacks !== undefined) {
1091
+ const fallbackPlugin = new PrecacheFallbackPlugin({
1092
+ fallbackUrls: this._options.fallbacks.entries,
1093
+ precacheController: serwist.precache
1094
+ });
1095
+ this._entries.forEach((cacheEntry)=>{
1096
+ if (cacheEntry.handler instanceof Strategy && !cacheEntry.handler.plugins.some((plugin)=>"handlerDidError" in plugin)) {
1097
+ cacheEntry.handler.plugins.push(fallbackPlugin);
1098
+ }
1099
+ });
1100
+ }
1101
+ for (const entry of this._entries){
1102
+ serwist.registerCapture(entry.matcher, entry.handler, entry.method);
1103
+ }
1104
+ }
1105
+ async install({ event, serwist }) {
1106
+ const concurrency = this._options.warmOptions?.concurrency ?? 10;
1107
+ if (this._options.warmEntries) {
1108
+ await parallel(concurrency, this._options.warmEntries, async (entry)=>{
1109
+ const request = entry instanceof Request ? entry : new Request(typeof entry === "string" ? entry : entry.url, {
1110
+ integrity: typeof entry !== "string" ? entry.integrity : undefined,
1111
+ credentials: "same-origin"
1112
+ });
1113
+ await serwist.handleRequest({
1114
+ request,
1115
+ event
1116
+ });
1117
+ });
1118
+ }
1119
+ }
1120
+ warmRuntimeCache(entries) {
1121
+ if (!this._options.warmEntries) this._options.warmEntries = [];
1122
+ this._options.warmEntries.push(...entries);
1123
+ }
1124
+ }
1125
+
1126
+ const BACKGROUND_SYNC_DB_VERSION = 3;
1127
+ const BACKGROUND_SYNC_DB_NAME = "serwist-background-sync";
1128
+ const REQUEST_OBJECT_STORE_NAME = "requests";
1129
+ const QUEUE_NAME_INDEX = "queueName";
1130
+ class BackgroundSyncQueueDb {
1131
+ _db = null;
1132
+ async addEntry(entry) {
1133
+ const db = await this.getDb();
1134
+ const tx = db.transaction(REQUEST_OBJECT_STORE_NAME, "readwrite", {
1135
+ durability: "relaxed"
1136
+ });
1137
+ await tx.store.add(entry);
1138
+ await tx.done;
1139
+ }
1140
+ async getFirstEntryId() {
1141
+ const db = await this.getDb();
1142
+ const cursor = await db.transaction(REQUEST_OBJECT_STORE_NAME).store.openCursor();
1143
+ return cursor?.value.id;
1144
+ }
1145
+ async getAllEntriesByQueueName(queueName) {
1146
+ const db = await this.getDb();
1147
+ const results = await db.getAllFromIndex(REQUEST_OBJECT_STORE_NAME, QUEUE_NAME_INDEX, IDBKeyRange.only(queueName));
1148
+ return results ? results : new Array();
1149
+ }
1150
+ async getEntryCountByQueueName(queueName) {
1151
+ const db = await this.getDb();
1152
+ return db.countFromIndex(REQUEST_OBJECT_STORE_NAME, QUEUE_NAME_INDEX, IDBKeyRange.only(queueName));
1153
+ }
1154
+ async deleteEntry(id) {
1155
+ const db = await this.getDb();
1156
+ await db.delete(REQUEST_OBJECT_STORE_NAME, id);
1157
+ }
1158
+ async getFirstEntryByQueueName(queueName) {
1159
+ return await this.getEndEntryFromIndex(IDBKeyRange.only(queueName), "next");
1160
+ }
1161
+ async getLastEntryByQueueName(queueName) {
1162
+ return await this.getEndEntryFromIndex(IDBKeyRange.only(queueName), "prev");
1163
+ }
1164
+ async getEndEntryFromIndex(query, direction) {
1165
+ const db = await this.getDb();
1166
+ const cursor = await db.transaction(REQUEST_OBJECT_STORE_NAME).store.index(QUEUE_NAME_INDEX).openCursor(query, direction);
1167
+ return cursor?.value;
1168
+ }
1169
+ async getDb() {
1170
+ if (!this._db) {
1171
+ this._db = await openDB(BACKGROUND_SYNC_DB_NAME, BACKGROUND_SYNC_DB_VERSION, {
1172
+ upgrade: this._upgradeDb
1173
+ });
1174
+ }
1175
+ return this._db;
1176
+ }
1177
+ _upgradeDb(db, oldVersion) {
1178
+ if (oldVersion > 0 && oldVersion < BACKGROUND_SYNC_DB_VERSION) {
1179
+ if (db.objectStoreNames.contains(REQUEST_OBJECT_STORE_NAME)) {
1180
+ db.deleteObjectStore(REQUEST_OBJECT_STORE_NAME);
1181
+ }
1182
+ }
1183
+ const objStore = db.createObjectStore(REQUEST_OBJECT_STORE_NAME, {
1184
+ autoIncrement: true,
1185
+ keyPath: "id"
1186
+ });
1187
+ objStore.createIndex(QUEUE_NAME_INDEX, QUEUE_NAME_INDEX, {
1188
+ unique: false
1189
+ });
1190
+ }
1191
+ }
1192
+
1193
+ class BackgroundSyncQueueStore {
1194
+ _queueName;
1195
+ _queueDb;
1196
+ constructor(queueName){
1197
+ this._queueName = queueName;
1198
+ this._queueDb = new BackgroundSyncQueueDb();
1199
+ }
1200
+ async pushEntry(entry) {
1201
+ if (process.env.NODE_ENV !== "production") {
1202
+ finalAssertExports.isType(entry, "object", {
1203
+ moduleName: "serwist",
1204
+ className: "BackgroundSyncQueueStore",
1205
+ funcName: "pushEntry",
1206
+ paramName: "entry"
1207
+ });
1208
+ finalAssertExports.isType(entry.requestData, "object", {
1209
+ moduleName: "serwist",
1210
+ className: "BackgroundSyncQueueStore",
1211
+ funcName: "pushEntry",
1212
+ paramName: "entry.requestData"
1213
+ });
1214
+ }
1215
+ delete entry.id;
1216
+ entry.queueName = this._queueName;
1217
+ await this._queueDb.addEntry(entry);
1218
+ }
1219
+ async unshiftEntry(entry) {
1220
+ if (process.env.NODE_ENV !== "production") {
1221
+ finalAssertExports.isType(entry, "object", {
1222
+ moduleName: "serwist",
1223
+ className: "BackgroundSyncQueueStore",
1224
+ funcName: "unshiftEntry",
1225
+ paramName: "entry"
1226
+ });
1227
+ finalAssertExports.isType(entry.requestData, "object", {
1228
+ moduleName: "serwist",
1229
+ className: "BackgroundSyncQueueStore",
1230
+ funcName: "unshiftEntry",
1231
+ paramName: "entry.requestData"
1232
+ });
1233
+ }
1234
+ const firstId = await this._queueDb.getFirstEntryId();
1235
+ if (firstId) {
1236
+ entry.id = firstId - 1;
1237
+ } else {
1238
+ delete entry.id;
1239
+ }
1240
+ entry.queueName = this._queueName;
1241
+ await this._queueDb.addEntry(entry);
1242
+ }
1243
+ async popEntry() {
1244
+ return this._removeEntry(await this._queueDb.getLastEntryByQueueName(this._queueName));
1245
+ }
1246
+ async shiftEntry() {
1247
+ return this._removeEntry(await this._queueDb.getFirstEntryByQueueName(this._queueName));
1248
+ }
1249
+ async getAll() {
1250
+ return await this._queueDb.getAllEntriesByQueueName(this._queueName);
1251
+ }
1252
+ async size() {
1253
+ return await this._queueDb.getEntryCountByQueueName(this._queueName);
1254
+ }
1255
+ async deleteEntry(id) {
1256
+ await this._queueDb.deleteEntry(id);
1257
+ }
1258
+ async _removeEntry(entry) {
1259
+ if (entry) {
1260
+ await this.deleteEntry(entry.id);
1261
+ }
1262
+ return entry;
1263
+ }
1264
+ }
1265
+
1266
+ const serializableProperties = [
1267
+ "method",
1268
+ "referrer",
1269
+ "referrerPolicy",
1270
+ "mode",
1271
+ "credentials",
1272
+ "cache",
1273
+ "redirect",
1274
+ "integrity",
1275
+ "keepalive"
1276
+ ];
1277
+ class StorableRequest {
1278
+ _requestData;
1279
+ static async fromRequest(request) {
1280
+ const requestData = {
1281
+ url: request.url,
1282
+ headers: {}
1283
+ };
1284
+ if (request.method !== "GET") {
1285
+ requestData.body = await request.clone().arrayBuffer();
1286
+ }
1287
+ request.headers.forEach((value, key)=>{
1288
+ requestData.headers[key] = value;
1289
+ });
1290
+ for (const prop of serializableProperties){
1291
+ if (request[prop] !== undefined) {
1292
+ requestData[prop] = request[prop];
1293
+ }
1294
+ }
1295
+ return new StorableRequest(requestData);
1296
+ }
1297
+ constructor(requestData){
1298
+ if (process.env.NODE_ENV !== "production") {
1299
+ finalAssertExports.isType(requestData, "object", {
1300
+ moduleName: "serwist",
1301
+ className: "StorableRequest",
1302
+ funcName: "constructor",
1303
+ paramName: "requestData"
1304
+ });
1305
+ finalAssertExports.isType(requestData.url, "string", {
1306
+ moduleName: "serwist",
1307
+ className: "StorableRequest",
1308
+ funcName: "constructor",
1309
+ paramName: "requestData.url"
1310
+ });
1311
+ }
1312
+ if (requestData.mode === "navigate") {
1313
+ requestData.mode = "same-origin";
1314
+ }
1315
+ this._requestData = requestData;
1316
+ }
1317
+ toObject() {
1318
+ const requestData = Object.assign({}, this._requestData);
1319
+ requestData.headers = Object.assign({}, this._requestData.headers);
1320
+ if (requestData.body) {
1321
+ requestData.body = requestData.body.slice(0);
1322
+ }
1323
+ return requestData;
1324
+ }
1325
+ toRequest() {
1326
+ return new Request(this._requestData.url, this._requestData);
1327
+ }
1328
+ clone() {
1329
+ return new StorableRequest(this.toObject());
1330
+ }
1331
+ }
1332
+
1333
+ const TAG_PREFIX = "serwist-background-sync";
1334
+ const MAX_RETENTION_TIME$1 = 60 * 24 * 7;
1335
+ const queueNames = new Set();
1336
+ const convertEntry = (queueStoreEntry)=>{
1337
+ const queueEntry = {
1338
+ request: new StorableRequest(queueStoreEntry.requestData).toRequest(),
1339
+ timestamp: queueStoreEntry.timestamp
1340
+ };
1341
+ if (queueStoreEntry.metadata) {
1342
+ queueEntry.metadata = queueStoreEntry.metadata;
1343
+ }
1344
+ return queueEntry;
1345
+ };
1346
+ class BackgroundSyncQueue {
1347
+ _name;
1348
+ _onSync;
1349
+ _maxRetentionTime;
1350
+ _queueStore;
1351
+ _forceSyncFallback;
1352
+ _syncInProgress = false;
1353
+ _requestsAddedDuringSync = false;
1354
+ constructor(name, { forceSyncFallback, onSync, maxRetentionTime } = {}){
1355
+ if (queueNames.has(name)) {
1356
+ throw new SerwistError("duplicate-queue-name", {
1357
+ name
1358
+ });
1359
+ }
1360
+ queueNames.add(name);
1361
+ this._name = name;
1362
+ this._onSync = onSync || this.replayRequests;
1363
+ this._maxRetentionTime = maxRetentionTime || MAX_RETENTION_TIME$1;
1364
+ this._forceSyncFallback = Boolean(forceSyncFallback);
1365
+ this._queueStore = new BackgroundSyncQueueStore(this._name);
1366
+ this._addSyncListener();
1367
+ }
1368
+ get name() {
1369
+ return this._name;
1370
+ }
1371
+ async pushRequest(entry) {
1372
+ if (process.env.NODE_ENV !== "production") {
1373
+ finalAssertExports.isType(entry, "object", {
1374
+ moduleName: "serwist",
1375
+ className: "BackgroundSyncQueue",
1376
+ funcName: "pushRequest",
1377
+ paramName: "entry"
1378
+ });
1379
+ finalAssertExports.isInstance(entry.request, Request, {
1380
+ moduleName: "serwist",
1381
+ className: "BackgroundSyncQueue",
1382
+ funcName: "pushRequest",
1383
+ paramName: "entry.request"
1384
+ });
1385
+ }
1386
+ await this._addRequest(entry, "push");
1387
+ }
1388
+ async unshiftRequest(entry) {
1389
+ if (process.env.NODE_ENV !== "production") {
1390
+ finalAssertExports.isType(entry, "object", {
1391
+ moduleName: "serwist",
1392
+ className: "BackgroundSyncQueue",
1393
+ funcName: "unshiftRequest",
1394
+ paramName: "entry"
1395
+ });
1396
+ finalAssertExports.isInstance(entry.request, Request, {
1397
+ moduleName: "serwist",
1398
+ className: "BackgroundSyncQueue",
1399
+ funcName: "unshiftRequest",
1400
+ paramName: "entry.request"
1401
+ });
1402
+ }
1403
+ await this._addRequest(entry, "unshift");
1404
+ }
1405
+ async popRequest() {
1406
+ return this._removeRequest("pop");
1407
+ }
1408
+ async shiftRequest() {
1409
+ return this._removeRequest("shift");
1410
+ }
1411
+ async getAll() {
1412
+ const allEntries = await this._queueStore.getAll();
1413
+ const now = Date.now();
1414
+ const unexpiredEntries = [];
1415
+ for (const entry of allEntries){
1416
+ const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000;
1417
+ if (now - entry.timestamp > maxRetentionTimeInMs) {
1418
+ await this._queueStore.deleteEntry(entry.id);
1419
+ } else {
1420
+ unexpiredEntries.push(convertEntry(entry));
1421
+ }
1422
+ }
1423
+ return unexpiredEntries;
1424
+ }
1425
+ async size() {
1426
+ return await this._queueStore.size();
1427
+ }
1428
+ async _addRequest({ request, metadata, timestamp = Date.now() }, operation) {
1429
+ const storableRequest = await StorableRequest.fromRequest(request.clone());
1430
+ const entry = {
1431
+ requestData: storableRequest.toObject(),
1432
+ timestamp
1433
+ };
1434
+ if (metadata) {
1435
+ entry.metadata = metadata;
1436
+ }
1437
+ switch(operation){
1438
+ case "push":
1439
+ await this._queueStore.pushEntry(entry);
1440
+ break;
1441
+ case "unshift":
1442
+ await this._queueStore.unshiftEntry(entry);
1443
+ break;
1444
+ }
1445
+ if (process.env.NODE_ENV !== "production") {
1446
+ logger.log(`Request for '${getFriendlyURL(request.url)}' has ` + `been added to background sync queue '${this._name}'.`);
1447
+ }
1448
+ if (this._syncInProgress) {
1449
+ this._requestsAddedDuringSync = true;
1450
+ } else {
1451
+ await this.registerSync();
1452
+ }
1453
+ }
1454
+ async _removeRequest(operation) {
1455
+ const now = Date.now();
1456
+ let entry;
1457
+ switch(operation){
1458
+ case "pop":
1459
+ entry = await this._queueStore.popEntry();
1460
+ break;
1461
+ case "shift":
1462
+ entry = await this._queueStore.shiftEntry();
1463
+ break;
1464
+ }
1465
+ if (entry) {
1466
+ const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000;
1467
+ if (now - entry.timestamp > maxRetentionTimeInMs) {
1468
+ return this._removeRequest(operation);
1469
+ }
1470
+ return convertEntry(entry);
1471
+ }
1472
+ return undefined;
1473
+ }
1474
+ async replayRequests() {
1475
+ let entry = undefined;
1476
+ while(entry = await this.shiftRequest()){
1477
+ try {
1478
+ await fetch(entry.request.clone());
1479
+ if (process.env.NODE_ENV !== "production") {
1480
+ logger.log(`Request for '${getFriendlyURL(entry.request.url)}' ` + `has been replayed in queue '${this._name}'`);
1481
+ }
1482
+ } catch (error) {
1483
+ await this.unshiftRequest(entry);
1484
+ if (process.env.NODE_ENV !== "production") {
1485
+ logger.log(`Request for '${getFriendlyURL(entry.request.url)}' ` + `failed to replay, putting it back in queue '${this._name}'`);
1486
+ }
1487
+ throw new SerwistError("queue-replay-failed", {
1488
+ name: this._name
1489
+ });
1490
+ }
1491
+ }
1492
+ if (process.env.NODE_ENV !== "production") {
1493
+ logger.log(`All requests in queue '${this.name}' have successfully replayed; the queue is now empty!`);
1494
+ }
1495
+ }
1496
+ async registerSync() {
1497
+ if ("sync" in self.registration && !this._forceSyncFallback) {
1498
+ try {
1499
+ await self.registration.sync.register(`${TAG_PREFIX}:${this._name}`);
1500
+ } catch (err) {
1501
+ if (process.env.NODE_ENV !== "production") {
1502
+ logger.warn(`Unable to register sync event for '${this._name}'.`, err);
1503
+ }
1504
+ }
1505
+ }
1506
+ }
1507
+ _addSyncListener() {
1508
+ if ("sync" in self.registration && !this._forceSyncFallback) {
1509
+ self.addEventListener("sync", (event)=>{
1510
+ if (event.tag === `${TAG_PREFIX}:${this._name}`) {
1511
+ if (process.env.NODE_ENV !== "production") {
1512
+ logger.log(`Background sync for tag '${event.tag}' has been received`);
1513
+ }
1514
+ const syncComplete = async ()=>{
1515
+ this._syncInProgress = true;
1516
+ let syncError = undefined;
1517
+ try {
1518
+ await this._onSync({
1519
+ queue: this
1520
+ });
1521
+ } catch (error) {
1522
+ if (error instanceof Error) {
1523
+ syncError = error;
1524
+ throw syncError;
1525
+ }
1526
+ } finally{
1527
+ if (this._requestsAddedDuringSync && !(syncError && !event.lastChance)) {
1528
+ await this.registerSync();
1529
+ }
1530
+ this._syncInProgress = false;
1531
+ this._requestsAddedDuringSync = false;
1532
+ }
1533
+ };
1534
+ event.waitUntil(syncComplete());
1535
+ }
1536
+ });
1537
+ } else {
1538
+ if (process.env.NODE_ENV !== "production") {
1539
+ logger.log("Background sync replaying without background sync event");
1540
+ }
1541
+ void this._onSync({
1542
+ queue: this
1543
+ });
1544
+ }
1545
+ }
1546
+ static get _queueNames() {
1547
+ return queueNames;
1548
+ }
1549
+ }
1550
+
1551
+ class BackgroundSyncPlugin {
1552
+ _queue;
1553
+ constructor(name, options){
1554
+ this._queue = new BackgroundSyncQueue(name, options);
1555
+ }
1556
+ async fetchDidFail({ request }) {
1557
+ await this._queue.pushRequest({
1558
+ request
1559
+ });
1560
+ }
1561
+ }
1562
+
1563
+ const cacheOkAndOpaquePlugin = {
1564
+ cacheWillUpdate: async ({ response })=>{
1565
+ if (response.status === 200 || response.status === 0) {
1566
+ return response;
1567
+ }
1568
+ return null;
1569
+ }
1570
+ };
1571
+
1572
+ const messages = {
1573
+ strategyStart: (strategyName, request)=>`Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`,
1574
+ printFinalResponse: (response)=>{
1575
+ if (response) {
1576
+ logger.groupCollapsed("View the final response here.");
1577
+ logger.log(response || "[No response returned]");
1578
+ logger.groupEnd();
1579
+ }
1580
+ }
1581
+ };
1582
+
1583
+ class NetworkFirst extends Strategy {
1584
+ _networkTimeoutSeconds;
1585
+ constructor(options = {}){
1586
+ super(options);
1587
+ if (!this.plugins.some((p)=>"cacheWillUpdate" in p)) {
1588
+ this.plugins.unshift(cacheOkAndOpaquePlugin);
1589
+ }
1590
+ this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
1591
+ if (process.env.NODE_ENV !== "production") {
1592
+ if (this._networkTimeoutSeconds) {
1593
+ finalAssertExports.isType(this._networkTimeoutSeconds, "number", {
1594
+ moduleName: "serwist",
1595
+ className: this.constructor.name,
1596
+ funcName: "constructor",
1597
+ paramName: "networkTimeoutSeconds"
1598
+ });
1599
+ }
1600
+ }
1601
+ }
1602
+ async _handle(request, handler) {
1603
+ const logs = [];
1604
+ if (process.env.NODE_ENV !== "production") {
1605
+ finalAssertExports.isInstance(request, Request, {
1606
+ moduleName: "serwist",
1607
+ className: this.constructor.name,
1608
+ funcName: "handle",
1609
+ paramName: "makeRequest"
1610
+ });
1611
+ }
1612
+ const promises = [];
1613
+ let timeoutId;
1614
+ if (this._networkTimeoutSeconds) {
1615
+ const { id, promise } = this._getTimeoutPromise({
1616
+ request,
1617
+ logs,
1618
+ handler
1619
+ });
1620
+ timeoutId = id;
1621
+ promises.push(promise);
1622
+ }
1623
+ const networkPromise = this._getNetworkPromise({
1624
+ timeoutId,
1625
+ request,
1626
+ logs,
1627
+ handler
1628
+ });
1629
+ promises.push(networkPromise);
1630
+ const response = await handler.waitUntil((async ()=>{
1631
+ return await handler.waitUntil(Promise.race(promises)) || await networkPromise;
1632
+ })());
1633
+ if (process.env.NODE_ENV !== "production") {
1634
+ logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
1635
+ for (const log of logs){
1636
+ logger.log(log);
1637
+ }
1638
+ messages.printFinalResponse(response);
1639
+ logger.groupEnd();
1640
+ }
1641
+ if (!response) {
1642
+ throw new SerwistError("no-response", {
1643
+ url: request.url
1644
+ });
1645
+ }
1646
+ return response;
1647
+ }
1648
+ _getTimeoutPromise({ request, logs, handler }) {
1649
+ let timeoutId;
1650
+ const timeoutPromise = new Promise((resolve)=>{
1651
+ const onNetworkTimeout = async ()=>{
1652
+ if (process.env.NODE_ENV !== "production") {
1653
+ logs.push(`Timing out the network response at ${this._networkTimeoutSeconds} seconds.`);
1654
+ }
1655
+ resolve(await handler.cacheMatch(request));
1656
+ };
1657
+ timeoutId = setTimeout(onNetworkTimeout, this._networkTimeoutSeconds * 1000);
1658
+ });
1659
+ return {
1660
+ promise: timeoutPromise,
1661
+ id: timeoutId
1662
+ };
1663
+ }
1664
+ async _getNetworkPromise({ timeoutId, request, logs, handler }) {
1665
+ let error = undefined;
1666
+ let response = undefined;
1667
+ try {
1668
+ response = await handler.fetchAndCachePut(request);
1669
+ } catch (fetchError) {
1670
+ if (fetchError instanceof Error) {
1671
+ error = fetchError;
1672
+ }
1673
+ }
1674
+ if (timeoutId) {
1675
+ clearTimeout(timeoutId);
1676
+ }
1677
+ if (process.env.NODE_ENV !== "production") {
1678
+ if (response) {
1679
+ logs.push("Got response from network.");
1680
+ } else {
1681
+ logs.push("Unable to get a response from the network. Will respond " + "with a cached response.");
1682
+ }
1683
+ }
1684
+ if (error || !response) {
1685
+ response = await handler.cacheMatch(request);
1686
+ if (process.env.NODE_ENV !== "production") {
1687
+ if (response) {
1688
+ logs.push(`Found a cached response in the '${this.cacheName}' cache.`);
1689
+ } else {
1690
+ logs.push(`No response found in the '${this.cacheName}' cache.`);
1691
+ }
1692
+ }
1693
+ }
1694
+ return response;
1695
+ }
1696
+ }
1697
+
1698
+ class NetworkOnly extends Strategy {
1699
+ _networkTimeoutSeconds;
1700
+ constructor(options = {}){
1701
+ super(options);
1702
+ this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
1703
+ }
1704
+ async _handle(request, handler) {
1705
+ if (process.env.NODE_ENV !== "production") {
1706
+ finalAssertExports.isInstance(request, Request, {
1707
+ moduleName: "serwist",
1708
+ className: this.constructor.name,
1709
+ funcName: "_handle",
1710
+ paramName: "request"
1711
+ });
1712
+ }
1713
+ let error = undefined;
1714
+ let response;
1715
+ try {
1716
+ const promises = [
1717
+ handler.fetch(request)
1718
+ ];
1719
+ if (this._networkTimeoutSeconds) {
1720
+ const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000);
1721
+ promises.push(timeoutPromise);
1722
+ }
1723
+ response = await Promise.race(promises);
1724
+ if (!response) {
1725
+ throw new Error(`Timed out the network response after ${this._networkTimeoutSeconds} seconds.`);
1726
+ }
1727
+ } catch (err) {
1728
+ if (err instanceof Error) {
1729
+ error = err;
1730
+ }
1731
+ }
1732
+ if (process.env.NODE_ENV !== "production") {
1733
+ logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
1734
+ if (response) {
1735
+ logger.log("Got response from network.");
1736
+ } else {
1737
+ logger.log("Unable to get a response from the network.");
23
1738
  }
24
- return;
25
- };
26
- super(match, serwist.precacheStrategy);
1739
+ messages.printFinalResponse(response);
1740
+ logger.groupEnd();
1741
+ }
1742
+ if (!response) {
1743
+ throw new SerwistError("no-response", {
1744
+ url: request.url,
1745
+ error
1746
+ });
1747
+ }
1748
+ return response;
27
1749
  }
28
1750
  }
29
1751
 
@@ -131,90 +1853,131 @@ const initializeGoogleAnalytics = ({ serwist, cacheName, ...options })=>{
131
1853
  }
132
1854
  };
133
1855
 
134
- class PrecacheFallbackPlugin {
135
- _fallbackUrls;
136
- _serwist;
137
- constructor({ fallbackUrls, serwist }){
138
- this._fallbackUrls = fallbackUrls;
139
- this._serwist = serwist;
1856
+ const parseRoute = (capture, handler, method)=>{
1857
+ if (typeof capture === "string") {
1858
+ const captureUrl = new URL(capture, location.href);
1859
+ if (process.env.NODE_ENV !== "production") {
1860
+ if (!(capture.startsWith("/") || capture.startsWith("http"))) {
1861
+ throw new SerwistError("invalid-string", {
1862
+ moduleName: "serwist",
1863
+ funcName: "parseRoute",
1864
+ paramName: "capture"
1865
+ });
1866
+ }
1867
+ const valueToCheck = capture.startsWith("http") ? captureUrl.pathname : capture;
1868
+ const wildcards = "[*:?+]";
1869
+ if (new RegExp(`${wildcards}`).exec(valueToCheck)) {
1870
+ logger.debug(`The '$capture' parameter contains an Express-style wildcard character (${wildcards}). Strings are now always interpreted as exact matches; use a RegExp for partial or wildcard matches.`);
1871
+ }
1872
+ }
1873
+ const matchCallback = ({ url })=>{
1874
+ if (process.env.NODE_ENV !== "production") {
1875
+ if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) {
1876
+ logger.debug(`${capture} only partially matches the cross-origin URL ${url.toString()}. This route will only handle cross-origin requests if they match the entire URL.`);
1877
+ }
1878
+ }
1879
+ return url.href === captureUrl.href;
1880
+ };
1881
+ return new Route(matchCallback, handler, method);
140
1882
  }
141
- async handlerDidError(param) {
142
- for (const fallback of this._fallbackUrls){
143
- if (typeof fallback === "string") {
144
- const fallbackResponse = await this._serwist.matchPrecache(fallback);
145
- if (fallbackResponse !== undefined) {
146
- return fallbackResponse;
1883
+ if (capture instanceof RegExp) {
1884
+ return new RegExpRoute(capture, handler, method);
1885
+ }
1886
+ if (typeof capture === "function") {
1887
+ return new Route(capture, handler, method);
1888
+ }
1889
+ if (capture instanceof Route) {
1890
+ return capture;
1891
+ }
1892
+ throw new SerwistError("unsupported-route-type", {
1893
+ moduleName: "serwist",
1894
+ funcName: "parseRoute",
1895
+ paramName: "capture"
1896
+ });
1897
+ };
1898
+
1899
+ const disableDevLogs = ()=>{
1900
+ self.__WB_DISABLE_DEV_LOGS = true;
1901
+ };
1902
+
1903
+ const isNavigationPreloadSupported = ()=>{
1904
+ return Boolean(self.registration?.navigationPreload);
1905
+ };
1906
+ const enableNavigationPreload = (headerValue)=>{
1907
+ if (isNavigationPreloadSupported()) {
1908
+ self.addEventListener("activate", (event)=>{
1909
+ event.waitUntil(self.registration.navigationPreload.enable().then(()=>{
1910
+ if (headerValue) {
1911
+ void self.registration.navigationPreload.setHeaderValue(headerValue);
147
1912
  }
148
- } else if (fallback.matcher(param)) {
149
- const fallbackResponse = await this._serwist.matchPrecache(fallback.url);
150
- if (fallbackResponse !== undefined) {
151
- return fallbackResponse;
1913
+ if (process.env.NODE_ENV !== "production") {
1914
+ logger.log("Navigation preloading is enabled.");
152
1915
  }
153
- }
1916
+ }));
1917
+ });
1918
+ } else {
1919
+ if (process.env.NODE_ENV !== "production") {
1920
+ logger.log("Navigation preloading is not supported in this browser.");
154
1921
  }
155
- return undefined;
156
1922
  }
157
- }
158
-
159
- class PrecacheCacheKeyPlugin {
160
- _precacheController;
161
- constructor({ precacheController }){
162
- this._precacheController = precacheController;
1923
+ };
1924
+ const disableNavigationPreload = ()=>{
1925
+ if (isNavigationPreloadSupported()) {
1926
+ self.addEventListener("activate", (event)=>{
1927
+ event.waitUntil(self.registration.navigationPreload.disable().then(()=>{
1928
+ if (process.env.NODE_ENV !== "production") {
1929
+ logger.log("Navigation preloading is disabled.");
1930
+ }
1931
+ }));
1932
+ });
1933
+ } else {
1934
+ if (process.env.NODE_ENV !== "production") {
1935
+ logger.log("Navigation preloading is not supported in this browser.");
1936
+ }
163
1937
  }
164
- cacheKeyWillBeUsed = async ({ request, params })=>{
165
- const cacheKey = params?.cacheKey || this._precacheController.getPrecacheKeyForUrl(request.url);
166
- return cacheKey ? new Request(cacheKey, {
167
- headers: request.headers
168
- }) : request;
169
- };
170
- }
1938
+ };
171
1939
 
172
- const parsePrecacheOptions = (serwist, precacheOptions = {})=>{
173
- const { cacheName: precacheCacheName, plugins: precachePlugins = [], fetchOptions: precacheFetchOptions, matchOptions: precacheMatchOptions, fallbackToNetwork: precacheFallbackToNetwork, directoryIndex: precacheDirectoryIndex, ignoreURLParametersMatching: precacheIgnoreUrls, cleanURLs: precacheCleanUrls, urlManipulation: precacheUrlManipulation, cleanupOutdatedCaches, concurrency = 10, navigateFallback, navigateFallbackAllowlist, navigateFallbackDenylist } = precacheOptions ?? {};
174
- return {
175
- precacheStrategyOptions: {
176
- cacheName: cacheNames$1.getPrecacheName(precacheCacheName),
177
- plugins: [
178
- ...precachePlugins,
179
- new PrecacheCacheKeyPlugin({
180
- precacheController: serwist
181
- })
182
- ],
183
- fetchOptions: precacheFetchOptions,
184
- matchOptions: precacheMatchOptions,
185
- fallbackToNetwork: precacheFallbackToNetwork
186
- },
187
- precacheRouteOptions: {
188
- directoryIndex: precacheDirectoryIndex,
189
- ignoreURLParametersMatching: precacheIgnoreUrls,
190
- cleanURLs: precacheCleanUrls,
191
- urlManipulation: precacheUrlManipulation
192
- },
193
- precacheMiscOptions: {
194
- cleanupOutdatedCaches,
195
- concurrency,
196
- navigateFallback,
197
- navigateFallbackAllowlist,
198
- navigateFallbackDenylist
1940
+ const setCacheNameDetails = (details)=>{
1941
+ if (process.env.NODE_ENV !== "production") {
1942
+ for (const key of Object.keys(details)){
1943
+ finalAssertExports.isType(details[key], "string", {
1944
+ moduleName: "@serwist/core",
1945
+ funcName: "setCacheNameDetails",
1946
+ paramName: `details.${key}`
1947
+ });
199
1948
  }
200
- };
1949
+ if (details.precache?.length === 0) {
1950
+ throw new SerwistError("invalid-cache-name", {
1951
+ cacheNameId: "precache",
1952
+ value: details.precache
1953
+ });
1954
+ }
1955
+ if (details.runtime?.length === 0) {
1956
+ throw new SerwistError("invalid-cache-name", {
1957
+ cacheNameId: "runtime",
1958
+ value: details.runtime
1959
+ });
1960
+ }
1961
+ if (details.googleAnalytics?.length === 0) {
1962
+ throw new SerwistError("invalid-cache-name", {
1963
+ cacheNameId: "googleAnalytics",
1964
+ value: details.googleAnalytics
1965
+ });
1966
+ }
1967
+ }
1968
+ cacheNames$1.updateDetails(details);
201
1969
  };
202
1970
 
203
1971
  class Serwist {
204
- _urlsToCacheKeys = new Map();
205
- _urlsToCacheModes = new Map();
206
- _cacheKeysToIntegrities = new Map();
207
- _concurrentPrecaching;
208
- _precacheStrategy;
209
1972
  _routes;
210
1973
  _defaultHandlerMap;
1974
+ _precacheController;
1975
+ _controllers;
211
1976
  _catchHandler;
212
- constructor({ precacheEntries, precacheOptions, skipWaiting = false, importScripts, navigationPreload = false, cacheId, clientsClaim: clientsClaim$1 = false, runtimeCaching, offlineAnalyticsConfig, disableDevLogs: disableDevLogs$1 = false, fallbacks } = {}){
213
- const { precacheStrategyOptions, precacheRouteOptions, precacheMiscOptions } = parsePrecacheOptions(this, precacheOptions);
214
- this._concurrentPrecaching = precacheMiscOptions.concurrency;
215
- this._precacheStrategy = new PrecacheStrategy(precacheStrategyOptions);
1977
+ constructor({ precacheEntries, precacheOptions, controllers = [], skipWaiting = false, importScripts, navigationPreload = false, cacheId, clientsClaim: clientsClaim$1 = false, runtimeCaching, offlineAnalyticsConfig, disableDevLogs: disableDevLogs$1 = false, fallbacks } = {}){
216
1978
  this._routes = new Map();
217
1979
  this._defaultHandlerMap = new Map();
1980
+ this._controllers = controllers;
218
1981
  this.handleInstall = this.handleInstall.bind(this);
219
1982
  this.handleActivate = this.handleActivate.bind(this);
220
1983
  this.handleFetch = this.handleFetch.bind(this);
@@ -236,19 +1999,17 @@ class Serwist {
236
1999
  });
237
2000
  }
238
2001
  if (clientsClaim$1) clientsClaim();
239
- if (!!precacheEntries && precacheEntries.length > 0) {
240
- this.addToPrecacheList(precacheEntries);
241
- }
242
- if (precacheMiscOptions.cleanupOutdatedCaches) {
243
- cleanupOutdatedCaches(precacheStrategyOptions.cacheName);
244
- }
245
- this.registerRoute(new PrecacheRoute(this, precacheRouteOptions));
246
- if (precacheMiscOptions.navigateFallback) {
247
- this.registerRoute(new NavigationRoute(this.createHandlerBoundToUrl(precacheMiscOptions.navigateFallback), {
248
- allowlist: precacheMiscOptions.navigateFallbackAllowlist,
249
- denylist: precacheMiscOptions.navigateFallbackDenylist
250
- }));
2002
+ this._precacheController = new PrecacheController(precacheEntries ?? [], precacheOptions);
2003
+ if (runtimeCaching) {
2004
+ if (!this._controllers?.some((controller)=>controller instanceof RuntimeCacheController)) {
2005
+ this._controllers.unshift(new RuntimeCacheController(runtimeCaching, {
2006
+ fallbacks
2007
+ }));
2008
+ } else if (process.env.NODE_ENV !== "production") {
2009
+ logger.warn("You have migrated to the Controller pattern, so setting `runtimeCaching` is a no-op.");
2010
+ }
251
2011
  }
2012
+ this._controllers.unshift(this._precacheController);
252
2013
  if (offlineAnalyticsConfig !== undefined) {
253
2014
  if (typeof offlineAnalyticsConfig === "boolean") {
254
2015
  offlineAnalyticsConfig && initializeGoogleAnalytics({
@@ -261,26 +2022,29 @@ class Serwist {
261
2022
  });
262
2023
  }
263
2024
  }
264
- if (runtimeCaching !== undefined) {
265
- if (fallbacks !== undefined) {
266
- const fallbackPlugin = new PrecacheFallbackPlugin({
267
- fallbackUrls: fallbacks.entries,
268
- serwist: this
269
- });
270
- runtimeCaching.forEach((cacheEntry)=>{
271
- if (cacheEntry.handler instanceof Strategy && !cacheEntry.handler.plugins.some((plugin)=>"handlerDidError" in plugin)) {
272
- cacheEntry.handler.plugins.push(fallbackPlugin);
273
- }
274
- });
275
- }
276
- for (const entry of runtimeCaching){
277
- this.registerCapture(entry.matcher, entry.handler, entry.method);
278
- }
2025
+ for (const callback of this.iterateControllers("init")){
2026
+ callback({
2027
+ serwist: this
2028
+ });
279
2029
  }
280
2030
  if (disableDevLogs$1) disableDevLogs();
281
2031
  }
2032
+ *iterateControllers(name) {
2033
+ if (!this._controllers) return;
2034
+ for (const controller of this._controllers){
2035
+ if (typeof controller[name] === "function") {
2036
+ const callback = (param)=>{
2037
+ controller[name](param);
2038
+ };
2039
+ yield callback;
2040
+ }
2041
+ }
2042
+ }
2043
+ get precache() {
2044
+ return this._precacheController;
2045
+ }
282
2046
  get precacheStrategy() {
283
- return this._precacheStrategy;
2047
+ return this._precacheController.strategy;
284
2048
  }
285
2049
  get routes() {
286
2050
  return this._routes;
@@ -291,99 +2055,24 @@ class Serwist {
291
2055
  self.addEventListener("fetch", this.handleFetch);
292
2056
  self.addEventListener("message", this.handleCache);
293
2057
  }
294
- addToPrecacheList(entries) {
295
- if (process.env.NODE_ENV !== "production") {
296
- finalAssertExports.isArray(entries, {
297
- moduleName: "serwist",
298
- className: "Serwist",
299
- funcName: "addToCacheList",
300
- paramName: "entries"
301
- });
302
- }
303
- const urlsToWarnAbout = [];
304
- for (const entry of entries){
305
- if (typeof entry === "string") {
306
- urlsToWarnAbout.push(entry);
307
- } else if (entry && !entry.integrity && entry.revision === undefined) {
308
- urlsToWarnAbout.push(entry.url);
309
- }
310
- const { cacheKey, url } = createCacheKey(entry);
311
- const cacheMode = typeof entry !== "string" && entry.revision ? "reload" : "default";
312
- if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) {
313
- throw new SerwistError("add-to-cache-list-conflicting-entries", {
314
- firstEntry: this._urlsToCacheKeys.get(url),
315
- secondEntry: cacheKey
316
- });
317
- }
318
- if (typeof entry !== "string" && entry.integrity) {
319
- if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) {
320
- throw new SerwistError("add-to-cache-list-conflicting-integrities", {
321
- url
322
- });
323
- }
324
- this._cacheKeysToIntegrities.set(cacheKey, entry.integrity);
325
- }
326
- this._urlsToCacheKeys.set(url, cacheKey);
327
- this._urlsToCacheModes.set(url, cacheMode);
328
- if (urlsToWarnAbout.length > 0) {
329
- const warningMessage = `Serwist is precaching URLs without revision info: ${urlsToWarnAbout.join(", ")}\nThis is generally NOT safe. Learn more at https://bit.ly/wb-precache`;
330
- if (process.env.NODE_ENV === "production") {
331
- console.warn(warningMessage);
332
- } else {
333
- logger.warn(warningMessage);
334
- }
335
- }
336
- }
337
- }
338
2058
  handleInstall(event) {
339
2059
  return waitUntil(event, async ()=>{
340
- const installReportPlugin = new PrecacheInstallReportPlugin();
341
- this.precacheStrategy.plugins.push(installReportPlugin);
342
- await parallel(this._concurrentPrecaching, Array.from(this._urlsToCacheKeys.entries()), async ([url, cacheKey])=>{
343
- const integrity = this._cacheKeysToIntegrities.get(cacheKey);
344
- const cacheMode = this._urlsToCacheModes.get(url);
345
- const request = new Request(url, {
346
- integrity,
347
- cache: cacheMode,
348
- credentials: "same-origin"
349
- });
350
- await Promise.all(this.precacheStrategy.handleAll({
2060
+ for (const callback of this.iterateControllers("install")){
2061
+ await callback({
351
2062
  event,
352
- request,
353
- url: new URL(request.url),
354
- params: {
355
- cacheKey
356
- }
357
- }));
358
- });
359
- const { updatedURLs, notUpdatedURLs } = installReportPlugin;
360
- if (process.env.NODE_ENV !== "production") {
361
- printInstallDetails(updatedURLs, notUpdatedURLs);
2063
+ serwist: this
2064
+ });
362
2065
  }
363
- return {
364
- updatedURLs,
365
- notUpdatedURLs
366
- };
367
2066
  });
368
2067
  }
369
2068
  handleActivate(event) {
370
2069
  return waitUntil(event, async ()=>{
371
- const cache = await self.caches.open(this.precacheStrategy.cacheName);
372
- const currentlyCachedRequests = await cache.keys();
373
- const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());
374
- const deletedCacheRequests = [];
375
- for (const request of currentlyCachedRequests){
376
- if (!expectedCacheKeys.has(request.url)) {
377
- await cache.delete(request);
378
- deletedCacheRequests.push(request.url);
379
- }
380
- }
381
- if (process.env.NODE_ENV !== "production") {
382
- printCleanupDetails(deletedCacheRequests);
2070
+ for (const callback of this.iterateControllers("activate")){
2071
+ await callback({
2072
+ event,
2073
+ serwist: this
2074
+ });
383
2075
  }
384
- return {
385
- deletedCacheRequests
386
- };
387
2076
  });
388
2077
  }
389
2078
  handleFetch(event) {
@@ -482,46 +2171,6 @@ class Serwist {
482
2171
  throw new SerwistError("unregister-route-route-not-registered");
483
2172
  }
484
2173
  }
485
- getUrlsToPrecacheKeys() {
486
- return this._urlsToCacheKeys;
487
- }
488
- getPrecachedUrls() {
489
- return [
490
- ...this._urlsToCacheKeys.keys()
491
- ];
492
- }
493
- getPrecacheKeyForUrl(url) {
494
- const urlObject = new URL(url, location.href);
495
- return this._urlsToCacheKeys.get(urlObject.href);
496
- }
497
- getIntegrityForPrecacheKey(cacheKey) {
498
- return this._cacheKeysToIntegrities.get(cacheKey);
499
- }
500
- async matchPrecache(request) {
501
- const url = request instanceof Request ? request.url : request;
502
- const cacheKey = this.getPrecacheKeyForUrl(url);
503
- if (cacheKey) {
504
- const cache = await self.caches.open(this.precacheStrategy.cacheName);
505
- return cache.match(cacheKey);
506
- }
507
- return undefined;
508
- }
509
- createHandlerBoundToUrl(url) {
510
- const cacheKey = this.getPrecacheKeyForUrl(url);
511
- if (!cacheKey) {
512
- throw new SerwistError("non-precached-url", {
513
- url
514
- });
515
- }
516
- return (options)=>{
517
- options.request = new Request(url);
518
- options.params = {
519
- cacheKey,
520
- ...options.params
521
- };
522
- return this.precacheStrategy.handle(options);
523
- };
524
- }
525
2174
  handleRequest({ request, event }) {
526
2175
  if (process.env.NODE_ENV !== "production") {
527
2176
  finalAssertExports.isInstance(request, Request, {
@@ -669,6 +2318,27 @@ class Serwist {
669
2318
  }
670
2319
  return {};
671
2320
  }
2321
+ addToPrecacheList(entries) {
2322
+ this._precacheController.addToCacheList(entries);
2323
+ }
2324
+ getUrlsToPrecacheKeys() {
2325
+ return this.precache.getUrlsToPrecacheKeys();
2326
+ }
2327
+ getPrecachedUrls() {
2328
+ return this.precache.getPrecachedUrls();
2329
+ }
2330
+ getPrecacheKeyForUrl(url) {
2331
+ return this.precache.getPrecacheKeyForUrl(url);
2332
+ }
2333
+ getIntegrityForPrecacheKey(cacheKey) {
2334
+ return this.precache.getIntegrityForPrecacheKey(cacheKey);
2335
+ }
2336
+ matchPrecache(request) {
2337
+ return this.precache.matchPrecache(request);
2338
+ }
2339
+ createHandlerBoundToUrl(url) {
2340
+ return this.precache.createHandlerBoundToUrl(url);
2341
+ }
672
2342
  }
673
2343
 
674
2344
  const cacheNames = {
@@ -689,6 +2359,20 @@ const cacheNames = {
689
2359
  }
690
2360
  };
691
2361
 
2362
+ const registerQuotaErrorCallback = (callback)=>{
2363
+ if (process.env.NODE_ENV !== "production") {
2364
+ finalAssertExports.isType(callback, "function", {
2365
+ moduleName: "@serwist/core",
2366
+ funcName: "register",
2367
+ paramName: "callback"
2368
+ });
2369
+ }
2370
+ quotaErrorCallbacks.add(callback);
2371
+ if (process.env.NODE_ENV !== "production") {
2372
+ logger.log("Registered a callback to respond to quota errors.", callback);
2373
+ }
2374
+ };
2375
+
692
2376
  const BROADCAST_UPDATE_MESSAGE_TYPE = "CACHE_UPDATED";
693
2377
  const BROADCAST_UPDATE_MESSAGE_META = "serwist-broadcast-update";
694
2378
  const BROADCAST_UPDATE_DEFAULT_NOTIFY = true;
@@ -1090,20 +2774,6 @@ class CacheExpiration {
1090
2774
  }
1091
2775
  }
1092
2776
 
1093
- const registerQuotaErrorCallback = (callback)=>{
1094
- if (process.env.NODE_ENV !== "production") {
1095
- finalAssertExports.isType(callback, "function", {
1096
- moduleName: "@serwist/core",
1097
- funcName: "register",
1098
- paramName: "callback"
1099
- });
1100
- }
1101
- quotaErrorCallbacks.add(callback);
1102
- if (process.env.NODE_ENV !== "production") {
1103
- logger.log("Registered a callback to respond to quota errors.", callback);
1104
- }
1105
- };
1106
-
1107
2777
  class ExpirationPlugin {
1108
2778
  _config;
1109
2779
  _cacheExpirations;
@@ -1503,4 +3173,4 @@ class StaleWhileRevalidate extends Strategy {
1503
3173
  }
1504
3174
  }
1505
3175
 
1506
- export { BROADCAST_UPDATE_DEFAULT_HEADERS, BackgroundSyncPlugin, BroadcastCacheUpdate, BroadcastUpdatePlugin, CacheExpiration, CacheFirst, CacheOnly, CacheableResponse, CacheableResponsePlugin, ExpirationPlugin, NavigationRoute, NetworkFirst, NetworkOnly, PrecacheFallbackPlugin, PrecacheRoute, PrecacheStrategy, RangeRequestsPlugin, Route, Serwist, StaleWhileRevalidate, Strategy, cacheNames, createPartialResponse, disableDevLogs, enableNavigationPreload, initializeGoogleAnalytics, registerQuotaErrorCallback, responsesAreSame, setCacheNameDetails };
3176
+ 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, RuntimeCacheController, Serwist, StaleWhileRevalidate, StorableRequest, Strategy, StrategyHandler, cacheNames, copyResponse, createPartialResponse, disableDevLogs, disableNavigationPreload, enableNavigationPreload, initializeGoogleAnalytics, isNavigationPreloadSupported, registerQuotaErrorCallback, responsesAreSame, setCacheNameDetails };