serwist 9.5.7 → 9.5.8

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 (242) hide show
  1. package/dist/chunks/Route-wAAof_Tq.d.ts +36 -0
  2. package/dist/chunks/Route-wAAof_Tq.d.ts.map +1 -0
  3. package/dist/chunks/cacheNames-DZwJpopB.d.ts +35 -0
  4. package/dist/chunks/cacheNames-DZwJpopB.d.ts.map +1 -0
  5. package/dist/chunks/index.internal-Dxj9Ni9X.js +65 -0
  6. package/dist/chunks/index.internal-Dxj9Ni9X.js.map +1 -0
  7. package/dist/chunks/printInstallDetails-c9A08ZVZ.js +2026 -0
  8. package/dist/chunks/printInstallDetails-c9A08ZVZ.js.map +1 -0
  9. package/dist/chunks/types-CKER0KBv.d.ts +786 -0
  10. package/dist/chunks/types-CKER0KBv.d.ts.map +1 -0
  11. package/dist/chunks/waitUntil-BHDx3Rgo.js +486 -0
  12. package/dist/chunks/waitUntil-BHDx3Rgo.js.map +1 -0
  13. package/dist/index.d.mts +1525 -0
  14. package/dist/index.d.mts.map +1 -0
  15. package/dist/index.internal.d.mts +337 -0
  16. package/dist/index.internal.d.mts.map +1 -0
  17. package/dist/index.internal.mjs +3 -0
  18. package/dist/index.legacy.d.mts +892 -0
  19. package/dist/index.legacy.d.mts.map +1 -0
  20. package/dist/index.legacy.mjs +1162 -0
  21. package/dist/index.legacy.mjs.map +1 -0
  22. package/dist/index.mjs +1847 -0
  23. package/dist/index.mjs.map +1 -0
  24. package/package.json +16 -17
  25. package/dist/NavigationRoute.d.ts +0 -56
  26. package/dist/NavigationRoute.d.ts.map +0 -1
  27. package/dist/PrecacheRoute.d.ts +0 -16
  28. package/dist/PrecacheRoute.d.ts.map +0 -1
  29. package/dist/RegExpRoute.d.ts +0 -24
  30. package/dist/RegExpRoute.d.ts.map +0 -1
  31. package/dist/Route.d.ts +0 -33
  32. package/dist/Route.d.ts.map +0 -1
  33. package/dist/Serwist.d.ts +0 -299
  34. package/dist/Serwist.d.ts.map +0 -1
  35. package/dist/cacheNames.d.ts +0 -20
  36. package/dist/cacheNames.d.ts.map +0 -1
  37. package/dist/chunks/printInstallDetails.js +0 -1575
  38. package/dist/chunks/resultingClientExists.js +0 -32
  39. package/dist/chunks/waitUntil.js +0 -449
  40. package/dist/constants.d.ts +0 -15
  41. package/dist/constants.d.ts.map +0 -1
  42. package/dist/copyResponse.d.ts +0 -18
  43. package/dist/copyResponse.d.ts.map +0 -1
  44. package/dist/disableDevLogs.d.ts +0 -7
  45. package/dist/disableDevLogs.d.ts.map +0 -1
  46. package/dist/index.d.ts +0 -50
  47. package/dist/index.d.ts.map +0 -1
  48. package/dist/index.internal.d.ts +0 -18
  49. package/dist/index.internal.d.ts.map +0 -1
  50. package/dist/index.internal.js +0 -23
  51. package/dist/index.js +0 -1533
  52. package/dist/index.legacy.d.ts +0 -28
  53. package/dist/index.legacy.d.ts.map +0 -1
  54. package/dist/index.legacy.js +0 -790
  55. package/dist/legacy/PrecacheController.d.ts +0 -145
  56. package/dist/legacy/PrecacheController.d.ts.map +0 -1
  57. package/dist/legacy/PrecacheFallbackPlugin.d.ts +0 -61
  58. package/dist/legacy/PrecacheFallbackPlugin.d.ts.map +0 -1
  59. package/dist/legacy/PrecacheRoute.d.ts +0 -19
  60. package/dist/legacy/PrecacheRoute.d.ts.map +0 -1
  61. package/dist/legacy/Router.d.ts +0 -151
  62. package/dist/legacy/Router.d.ts.map +0 -1
  63. package/dist/legacy/addPlugins.d.ts +0 -9
  64. package/dist/legacy/addPlugins.d.ts.map +0 -1
  65. package/dist/legacy/addRoute.d.ts +0 -14
  66. package/dist/legacy/addRoute.d.ts.map +0 -1
  67. package/dist/legacy/constants.d.ts +0 -10
  68. package/dist/legacy/constants.d.ts.map +0 -1
  69. package/dist/legacy/createHandlerBoundToURL.d.ts +0 -17
  70. package/dist/legacy/createHandlerBoundToURL.d.ts.map +0 -1
  71. package/dist/legacy/fallbacks.d.ts +0 -59
  72. package/dist/legacy/fallbacks.d.ts.map +0 -1
  73. package/dist/legacy/getCacheKeyForURL.d.ts +0 -20
  74. package/dist/legacy/getCacheKeyForURL.d.ts.map +0 -1
  75. package/dist/legacy/handlePrecaching.d.ts +0 -54
  76. package/dist/legacy/handlePrecaching.d.ts.map +0 -1
  77. package/dist/legacy/initializeGoogleAnalytics.d.ts +0 -38
  78. package/dist/legacy/initializeGoogleAnalytics.d.ts.map +0 -1
  79. package/dist/legacy/installSerwist.d.ts +0 -81
  80. package/dist/legacy/installSerwist.d.ts.map +0 -1
  81. package/dist/legacy/matchPrecache.d.ts +0 -15
  82. package/dist/legacy/matchPrecache.d.ts.map +0 -1
  83. package/dist/legacy/precache.d.ts +0 -20
  84. package/dist/legacy/precache.d.ts.map +0 -1
  85. package/dist/legacy/precacheAndRoute.d.ts +0 -14
  86. package/dist/legacy/precacheAndRoute.d.ts.map +0 -1
  87. package/dist/legacy/registerRoute.d.ts +0 -16
  88. package/dist/legacy/registerRoute.d.ts.map +0 -1
  89. package/dist/legacy/registerRuntimeCaching.d.ts +0 -11
  90. package/dist/legacy/registerRuntimeCaching.d.ts.map +0 -1
  91. package/dist/legacy/setCatchHandler.d.ts +0 -10
  92. package/dist/legacy/setCatchHandler.d.ts.map +0 -1
  93. package/dist/legacy/setDefaultHandler.d.ts +0 -13
  94. package/dist/legacy/setDefaultHandler.d.ts.map +0 -1
  95. package/dist/legacy/singletonPrecacheController.d.ts +0 -34
  96. package/dist/legacy/singletonPrecacheController.d.ts.map +0 -1
  97. package/dist/legacy/singletonRouter.d.ts +0 -41
  98. package/dist/legacy/singletonRouter.d.ts.map +0 -1
  99. package/dist/legacy/unregisterRoute.d.ts +0 -9
  100. package/dist/legacy/unregisterRoute.d.ts.map +0 -1
  101. package/dist/legacy/utils/PrecacheCacheKeyPlugin.d.ts +0 -16
  102. package/dist/legacy/utils/PrecacheCacheKeyPlugin.d.ts.map +0 -1
  103. package/dist/legacy/utils/getCacheKeyForURL.d.ts +0 -14
  104. package/dist/legacy/utils/getCacheKeyForURL.d.ts.map +0 -1
  105. package/dist/lib/backgroundSync/BackgroundSyncPlugin.d.ts +0 -23
  106. package/dist/lib/backgroundSync/BackgroundSyncPlugin.d.ts.map +0 -1
  107. package/dist/lib/backgroundSync/BackgroundSyncQueue.d.ts +0 -182
  108. package/dist/lib/backgroundSync/BackgroundSyncQueue.d.ts.map +0 -1
  109. package/dist/lib/backgroundSync/BackgroundSyncQueueDb.d.ts +0 -90
  110. package/dist/lib/backgroundSync/BackgroundSyncQueueDb.d.ts.map +0 -1
  111. package/dist/lib/backgroundSync/BackgroundSyncQueueStore.d.ts +0 -75
  112. package/dist/lib/backgroundSync/BackgroundSyncQueueStore.d.ts.map +0 -1
  113. package/dist/lib/backgroundSync/StorableRequest.d.ts +0 -51
  114. package/dist/lib/backgroundSync/StorableRequest.d.ts.map +0 -1
  115. package/dist/lib/broadcastUpdate/BroadcastCacheUpdate.d.ts +0 -44
  116. package/dist/lib/broadcastUpdate/BroadcastCacheUpdate.d.ts.map +0 -1
  117. package/dist/lib/broadcastUpdate/BroadcastUpdatePlugin.d.ts +0 -24
  118. package/dist/lib/broadcastUpdate/BroadcastUpdatePlugin.d.ts.map +0 -1
  119. package/dist/lib/broadcastUpdate/constants.d.ts +0 -5
  120. package/dist/lib/broadcastUpdate/constants.d.ts.map +0 -1
  121. package/dist/lib/broadcastUpdate/responsesAreSame.d.ts +0 -11
  122. package/dist/lib/broadcastUpdate/responsesAreSame.d.ts.map +0 -1
  123. package/dist/lib/broadcastUpdate/types.d.ts +0 -35
  124. package/dist/lib/broadcastUpdate/types.d.ts.map +0 -1
  125. package/dist/lib/cacheableResponse/CacheableResponse.d.ts +0 -40
  126. package/dist/lib/cacheableResponse/CacheableResponse.d.ts.map +0 -1
  127. package/dist/lib/cacheableResponse/CacheableResponsePlugin.d.ts +0 -27
  128. package/dist/lib/cacheableResponse/CacheableResponsePlugin.d.ts.map +0 -1
  129. package/dist/lib/expiration/CacheExpiration.d.ts +0 -66
  130. package/dist/lib/expiration/CacheExpiration.d.ts.map +0 -1
  131. package/dist/lib/expiration/ExpirationPlugin.d.ts +0 -116
  132. package/dist/lib/expiration/ExpirationPlugin.d.ts.map +0 -1
  133. package/dist/lib/expiration/models/CacheTimestampsModel.d.ts +0 -73
  134. package/dist/lib/expiration/models/CacheTimestampsModel.d.ts.map +0 -1
  135. package/dist/lib/googleAnalytics/constants.d.ts +0 -10
  136. package/dist/lib/googleAnalytics/constants.d.ts.map +0 -1
  137. package/dist/lib/googleAnalytics/initializeGoogleAnalytics.d.ts +0 -30
  138. package/dist/lib/googleAnalytics/initializeGoogleAnalytics.d.ts.map +0 -1
  139. package/dist/lib/precaching/PrecacheFallbackPlugin.d.ts +0 -49
  140. package/dist/lib/precaching/PrecacheFallbackPlugin.d.ts.map +0 -1
  141. package/dist/lib/rangeRequests/RangeRequestsPlugin.d.ts +0 -18
  142. package/dist/lib/rangeRequests/RangeRequestsPlugin.d.ts.map +0 -1
  143. package/dist/lib/rangeRequests/createPartialResponse.d.ts +0 -18
  144. package/dist/lib/rangeRequests/createPartialResponse.d.ts.map +0 -1
  145. package/dist/lib/rangeRequests/utils/calculateEffectiveBoundaries.d.ts +0 -14
  146. package/dist/lib/rangeRequests/utils/calculateEffectiveBoundaries.d.ts.map +0 -1
  147. package/dist/lib/rangeRequests/utils/parseRangeHeader.d.ts +0 -12
  148. package/dist/lib/rangeRequests/utils/parseRangeHeader.d.ts.map +0 -1
  149. package/dist/lib/strategies/CacheFirst.d.ts +0 -23
  150. package/dist/lib/strategies/CacheFirst.d.ts.map +0 -1
  151. package/dist/lib/strategies/CacheOnly.d.ts +0 -20
  152. package/dist/lib/strategies/CacheOnly.d.ts.map +0 -1
  153. package/dist/lib/strategies/NetworkFirst.d.ts +0 -61
  154. package/dist/lib/strategies/NetworkFirst.d.ts.map +0 -1
  155. package/dist/lib/strategies/NetworkOnly.d.ts +0 -32
  156. package/dist/lib/strategies/NetworkOnly.d.ts.map +0 -1
  157. package/dist/lib/strategies/PrecacheStrategy.d.ts +0 -69
  158. package/dist/lib/strategies/PrecacheStrategy.d.ts.map +0 -1
  159. package/dist/lib/strategies/StaleWhileRevalidate.d.ts +0 -35
  160. package/dist/lib/strategies/StaleWhileRevalidate.d.ts.map +0 -1
  161. package/dist/lib/strategies/Strategy.d.ts +0 -81
  162. package/dist/lib/strategies/Strategy.d.ts.map +0 -1
  163. package/dist/lib/strategies/StrategyHandler.d.ts +0 -190
  164. package/dist/lib/strategies/StrategyHandler.d.ts.map +0 -1
  165. package/dist/lib/strategies/plugins/cacheOkAndOpaquePlugin.d.ts +0 -3
  166. package/dist/lib/strategies/plugins/cacheOkAndOpaquePlugin.d.ts.map +0 -1
  167. package/dist/lib/strategies/utils/messages.d.ts +0 -5
  168. package/dist/lib/strategies/utils/messages.d.ts.map +0 -1
  169. package/dist/models/messages/messageGenerator.d.ts +0 -4
  170. package/dist/models/messages/messageGenerator.d.ts.map +0 -1
  171. package/dist/models/messages/messages.d.ts +0 -44
  172. package/dist/models/messages/messages.d.ts.map +0 -1
  173. package/dist/models/pluginEvents.d.ts +0 -10
  174. package/dist/models/pluginEvents.d.ts.map +0 -1
  175. package/dist/models/quotaErrorCallbacks.d.ts +0 -3
  176. package/dist/models/quotaErrorCallbacks.d.ts.map +0 -1
  177. package/dist/navigationPreload.d.ts +0 -24
  178. package/dist/navigationPreload.d.ts.map +0 -1
  179. package/dist/registerQuotaErrorCallback.d.ts +0 -8
  180. package/dist/registerQuotaErrorCallback.d.ts.map +0 -1
  181. package/dist/setCacheNameDetails.d.ts +0 -9
  182. package/dist/setCacheNameDetails.d.ts.map +0 -1
  183. package/dist/types.d.ts +0 -445
  184. package/dist/types.d.ts.map +0 -1
  185. package/dist/utils/Deferred.d.ts +0 -19
  186. package/dist/utils/Deferred.d.ts.map +0 -1
  187. package/dist/utils/PrecacheCacheKeyPlugin.d.ts +0 -16
  188. package/dist/utils/PrecacheCacheKeyPlugin.d.ts.map +0 -1
  189. package/dist/utils/PrecacheInstallReportPlugin.d.ts +0 -14
  190. package/dist/utils/PrecacheInstallReportPlugin.d.ts.map +0 -1
  191. package/dist/utils/SerwistError.d.ts +0 -24
  192. package/dist/utils/SerwistError.d.ts.map +0 -1
  193. package/dist/utils/assert.d.ts +0 -11
  194. package/dist/utils/assert.d.ts.map +0 -1
  195. package/dist/utils/cacheMatchIgnoreParams.d.ts +0 -15
  196. package/dist/utils/cacheMatchIgnoreParams.d.ts.map +0 -1
  197. package/dist/utils/cacheNames.d.ts +0 -40
  198. package/dist/utils/cacheNames.d.ts.map +0 -1
  199. package/dist/utils/canConstructReadableStream.d.ts +0 -12
  200. package/dist/utils/canConstructReadableStream.d.ts.map +0 -1
  201. package/dist/utils/canConstructResponseFromBodyStream.d.ts +0 -11
  202. package/dist/utils/canConstructResponseFromBodyStream.d.ts.map +0 -1
  203. package/dist/utils/cleanupOutdatedCaches.d.ts +0 -6
  204. package/dist/utils/cleanupOutdatedCaches.d.ts.map +0 -1
  205. package/dist/utils/clientsClaim.d.ts +0 -6
  206. package/dist/utils/clientsClaim.d.ts.map +0 -1
  207. package/dist/utils/createCacheKey.d.ts +0 -16
  208. package/dist/utils/createCacheKey.d.ts.map +0 -1
  209. package/dist/utils/deleteOutdatedCaches.d.ts +0 -18
  210. package/dist/utils/deleteOutdatedCaches.d.ts.map +0 -1
  211. package/dist/utils/dontWaitFor.d.ts +0 -7
  212. package/dist/utils/dontWaitFor.d.ts.map +0 -1
  213. package/dist/utils/executeQuotaErrorCallbacks.d.ts +0 -8
  214. package/dist/utils/executeQuotaErrorCallbacks.d.ts.map +0 -1
  215. package/dist/utils/generateURLVariations.d.ts +0 -12
  216. package/dist/utils/generateURLVariations.d.ts.map +0 -1
  217. package/dist/utils/getFriendlyURL.d.ts +0 -3
  218. package/dist/utils/getFriendlyURL.d.ts.map +0 -1
  219. package/dist/utils/logger.d.ts +0 -24
  220. package/dist/utils/logger.d.ts.map +0 -1
  221. package/dist/utils/normalizeHandler.d.ts +0 -10
  222. package/dist/utils/normalizeHandler.d.ts.map +0 -1
  223. package/dist/utils/parsePrecacheOptions.d.ts +0 -26
  224. package/dist/utils/parsePrecacheOptions.d.ts.map +0 -1
  225. package/dist/utils/parseRoute.d.ts +0 -16
  226. package/dist/utils/parseRoute.d.ts.map +0 -1
  227. package/dist/utils/pluginUtils.d.ts +0 -5
  228. package/dist/utils/pluginUtils.d.ts.map +0 -1
  229. package/dist/utils/printCleanupDetails.d.ts +0 -6
  230. package/dist/utils/printCleanupDetails.d.ts.map +0 -1
  231. package/dist/utils/printInstallDetails.d.ts +0 -7
  232. package/dist/utils/printInstallDetails.d.ts.map +0 -1
  233. package/dist/utils/removeIgnoredSearchParams.d.ts +0 -12
  234. package/dist/utils/removeIgnoredSearchParams.d.ts.map +0 -1
  235. package/dist/utils/resultingClientExists.d.ts +0 -12
  236. package/dist/utils/resultingClientExists.d.ts.map +0 -1
  237. package/dist/utils/timeout.d.ts +0 -10
  238. package/dist/utils/timeout.d.ts.map +0 -1
  239. package/dist/utils/waitUntil.d.ts +0 -11
  240. package/dist/utils/waitUntil.d.ts.map +0 -1
  241. package/dist/utils/welcome.d.ts +0 -2
  242. package/dist/utils/welcome.d.ts.map +0 -1
@@ -0,0 +1,2026 @@
1
+ import { a as Deferred, c as timeout, d as finalAssertExports, f as SerwistError, i as executeQuotaErrorCallbacks, l as logger, m as cacheNames, o as cacheMatchIgnoreParams, p as canConstructResponseFromBodyStream, u as getFriendlyURL } from "./waitUntil-BHDx3Rgo.js";
2
+ import { openDB } from "idb";
3
+ //#region src/copyResponse.ts
4
+ /**
5
+ * Allows developers to copy a response and modify its `headers`, `status`,
6
+ * or `statusText` values (the [valid options](https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#options)
7
+ * when constructing a `Response` object).
8
+ * To modify these values, pass a function as the second argument. That
9
+ * function will be invoked with the options of the initial `Response` object.
10
+ * The return value of this function will be used as the options for the new `Response` object.
11
+ * To change the values either modify the passed parameter(s) and return it or return a totally
12
+ * new object.
13
+ *
14
+ * This method is intentionally limited to same-origin responses, regardless of
15
+ * whether CORS was used or not.
16
+ *
17
+ * @param response The initial response.
18
+ * @param modifier The function used to modify the options of the `Response` object.
19
+ */
20
+ const copyResponse = async (response, modifier) => {
21
+ let origin = null;
22
+ if (response.url) origin = new URL(response.url).origin;
23
+ if (origin !== self.location.origin) throw new SerwistError("cross-origin-copy-response", { origin });
24
+ const clonedResponse = response.clone();
25
+ const responseInit = {
26
+ headers: new Headers(clonedResponse.headers),
27
+ status: clonedResponse.status,
28
+ statusText: clonedResponse.statusText
29
+ };
30
+ const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit;
31
+ const body = canConstructResponseFromBodyStream() ? clonedResponse.body : await clonedResponse.blob();
32
+ return new Response(body, modifiedResponseInit);
33
+ };
34
+ //#endregion
35
+ //#region src/disableDevLogs.ts
36
+ /**
37
+ * Disables Serwist's logging in development mode.
38
+ *
39
+ * @see https://serwist.pages.dev/docs/serwist/core/disable-dev-logs
40
+ */
41
+ const disableDevLogs = () => {
42
+ self.__WB_DISABLE_DEV_LOGS = true;
43
+ };
44
+ //#endregion
45
+ //#region src/lib/backgroundSync/BackgroundSyncQueueDb.ts
46
+ const BACKGROUND_SYNC_DB_VERSION = 3;
47
+ const BACKGROUND_SYNC_DB_NAME = "serwist-background-sync";
48
+ const REQUEST_OBJECT_STORE_NAME = "requests";
49
+ const QUEUE_NAME_INDEX = "queueName";
50
+ /**
51
+ * A class to interact directly an IndexedDB created specifically to save and
52
+ * retrieve QueueStoreEntries. This class encapsulates all the schema details
53
+ * to store the representation of a Queue.
54
+ *
55
+ * @private
56
+ */
57
+ var BackgroundSyncQueueDb = class {
58
+ _db = null;
59
+ /**
60
+ * Add QueueStoreEntry to underlying db.
61
+ *
62
+ * @param entry
63
+ */
64
+ async addEntry(entry) {
65
+ const tx = (await this.getDb()).transaction(REQUEST_OBJECT_STORE_NAME, "readwrite", { durability: "relaxed" });
66
+ await tx.store.add(entry);
67
+ await tx.done;
68
+ }
69
+ /**
70
+ * Returns the first entry id in the ObjectStore.
71
+ *
72
+ * @returns
73
+ */
74
+ async getFirstEntryId() {
75
+ return (await (await this.getDb()).transaction(REQUEST_OBJECT_STORE_NAME).store.openCursor())?.value.id;
76
+ }
77
+ /**
78
+ * Get all the entries filtered by index
79
+ *
80
+ * @param queueName
81
+ * @returns
82
+ */
83
+ async getAllEntriesByQueueName(queueName) {
84
+ const results = await (await this.getDb()).getAllFromIndex(REQUEST_OBJECT_STORE_NAME, QUEUE_NAME_INDEX, IDBKeyRange.only(queueName));
85
+ return results ? results : [];
86
+ }
87
+ /**
88
+ * Returns the number of entries filtered by index
89
+ *
90
+ * @param queueName
91
+ * @returns
92
+ */
93
+ async getEntryCountByQueueName(queueName) {
94
+ return (await this.getDb()).countFromIndex(REQUEST_OBJECT_STORE_NAME, QUEUE_NAME_INDEX, IDBKeyRange.only(queueName));
95
+ }
96
+ /**
97
+ * Deletes a single entry by id.
98
+ *
99
+ * @param id the id of the entry to be deleted
100
+ */
101
+ async deleteEntry(id) {
102
+ await (await this.getDb()).delete(REQUEST_OBJECT_STORE_NAME, id);
103
+ }
104
+ /**
105
+ *
106
+ * @param queueName
107
+ * @returns
108
+ */
109
+ async getFirstEntryByQueueName(queueName) {
110
+ return await this.getEndEntryFromIndex(IDBKeyRange.only(queueName), "next");
111
+ }
112
+ /**
113
+ *
114
+ * @param queueName
115
+ * @returns
116
+ */
117
+ async getLastEntryByQueueName(queueName) {
118
+ return await this.getEndEntryFromIndex(IDBKeyRange.only(queueName), "prev");
119
+ }
120
+ /**
121
+ * Returns either the first or the last entries, depending on direction.
122
+ * Filtered by index.
123
+ *
124
+ * @param direction
125
+ * @param query
126
+ * @returns
127
+ * @private
128
+ */
129
+ async getEndEntryFromIndex(query, direction) {
130
+ return (await (await this.getDb()).transaction(REQUEST_OBJECT_STORE_NAME).store.index(QUEUE_NAME_INDEX).openCursor(query, direction))?.value;
131
+ }
132
+ /**
133
+ * Returns an open connection to the database.
134
+ *
135
+ * @private
136
+ */
137
+ async getDb() {
138
+ if (!this._db) this._db = await openDB(BACKGROUND_SYNC_DB_NAME, BACKGROUND_SYNC_DB_VERSION, { upgrade: this._upgradeDb });
139
+ return this._db;
140
+ }
141
+ /**
142
+ * Upgrades QueueDB
143
+ *
144
+ * @param db
145
+ * @param oldVersion
146
+ * @private
147
+ */
148
+ _upgradeDb(db, oldVersion) {
149
+ if (oldVersion > 0 && oldVersion < BACKGROUND_SYNC_DB_VERSION) {
150
+ if (db.objectStoreNames.contains(REQUEST_OBJECT_STORE_NAME)) db.deleteObjectStore(REQUEST_OBJECT_STORE_NAME);
151
+ }
152
+ db.createObjectStore(REQUEST_OBJECT_STORE_NAME, {
153
+ autoIncrement: true,
154
+ keyPath: "id"
155
+ }).createIndex(QUEUE_NAME_INDEX, QUEUE_NAME_INDEX, { unique: false });
156
+ }
157
+ };
158
+ //#endregion
159
+ //#region src/lib/backgroundSync/BackgroundSyncQueueStore.ts
160
+ /**
161
+ * A class to manage storing requests from a Queue in IndexedDB,
162
+ * indexed by their queue name for easier access.
163
+ *
164
+ * Most developers will not need to access this class directly;
165
+ * it is exposed for advanced use cases.
166
+ */
167
+ var BackgroundSyncQueueStore = class {
168
+ _queueName;
169
+ _queueDb;
170
+ /**
171
+ * Associates this instance with a Queue instance, so entries added can be
172
+ * identified by their queue name.
173
+ *
174
+ * @param queueName
175
+ */
176
+ constructor(queueName) {
177
+ this._queueName = queueName;
178
+ this._queueDb = new BackgroundSyncQueueDb();
179
+ }
180
+ /**
181
+ * Append an entry last in the queue.
182
+ *
183
+ * @param entry
184
+ */
185
+ async pushEntry(entry) {
186
+ if (process.env.NODE_ENV !== "production") {
187
+ finalAssertExports.isType(entry, "object", {
188
+ moduleName: "serwist",
189
+ className: "BackgroundSyncQueueStore",
190
+ funcName: "pushEntry",
191
+ paramName: "entry"
192
+ });
193
+ finalAssertExports.isType(entry.requestData, "object", {
194
+ moduleName: "serwist",
195
+ className: "BackgroundSyncQueueStore",
196
+ funcName: "pushEntry",
197
+ paramName: "entry.requestData"
198
+ });
199
+ }
200
+ delete entry.id;
201
+ entry.queueName = this._queueName;
202
+ await this._queueDb.addEntry(entry);
203
+ }
204
+ /**
205
+ * Prepend an entry first in the queue.
206
+ *
207
+ * @param entry
208
+ */
209
+ async unshiftEntry(entry) {
210
+ if (process.env.NODE_ENV !== "production") {
211
+ finalAssertExports.isType(entry, "object", {
212
+ moduleName: "serwist",
213
+ className: "BackgroundSyncQueueStore",
214
+ funcName: "unshiftEntry",
215
+ paramName: "entry"
216
+ });
217
+ finalAssertExports.isType(entry.requestData, "object", {
218
+ moduleName: "serwist",
219
+ className: "BackgroundSyncQueueStore",
220
+ funcName: "unshiftEntry",
221
+ paramName: "entry.requestData"
222
+ });
223
+ }
224
+ const firstId = await this._queueDb.getFirstEntryId();
225
+ if (firstId) entry.id = firstId - 1;
226
+ else delete entry.id;
227
+ entry.queueName = this._queueName;
228
+ await this._queueDb.addEntry(entry);
229
+ }
230
+ /**
231
+ * Removes and returns the last entry in the queue matching the `queueName`.
232
+ *
233
+ * @returns
234
+ */
235
+ async popEntry() {
236
+ return this._removeEntry(await this._queueDb.getLastEntryByQueueName(this._queueName));
237
+ }
238
+ /**
239
+ * Removes and returns the first entry in the queue matching the `queueName`.
240
+ *
241
+ * @returns
242
+ */
243
+ async shiftEntry() {
244
+ return this._removeEntry(await this._queueDb.getFirstEntryByQueueName(this._queueName));
245
+ }
246
+ /**
247
+ * Returns all entries in the store matching the `queueName`.
248
+ *
249
+ * @returns
250
+ */
251
+ async getAll() {
252
+ return await this._queueDb.getAllEntriesByQueueName(this._queueName);
253
+ }
254
+ /**
255
+ * Returns the number of entries in the store matching the `queueName`.
256
+ *
257
+ * @returns
258
+ */
259
+ async size() {
260
+ return await this._queueDb.getEntryCountByQueueName(this._queueName);
261
+ }
262
+ /**
263
+ * Deletes the entry for the given ID.
264
+ *
265
+ * WARNING: this method does not ensure the deleted entry belongs to this
266
+ * queue (i.e. matches the `queueName`). But this limitation is acceptable
267
+ * as this class is not publicly exposed. An additional check would make
268
+ * this method slower than it needs to be.
269
+ *
270
+ * @param id
271
+ */
272
+ async deleteEntry(id) {
273
+ await this._queueDb.deleteEntry(id);
274
+ }
275
+ /**
276
+ * Removes and returns the first or last entry in the queue (based on the
277
+ * `direction` argument) matching the `queueName`.
278
+ *
279
+ * @returns
280
+ * @private
281
+ */
282
+ async _removeEntry(entry) {
283
+ if (entry) await this.deleteEntry(entry.id);
284
+ return entry;
285
+ }
286
+ };
287
+ //#endregion
288
+ //#region src/lib/backgroundSync/StorableRequest.ts
289
+ const serializableProperties = [
290
+ "method",
291
+ "referrer",
292
+ "referrerPolicy",
293
+ "mode",
294
+ "credentials",
295
+ "cache",
296
+ "redirect",
297
+ "integrity",
298
+ "keepalive"
299
+ ];
300
+ /**
301
+ * A class to make it easier to serialize and de-serialize requests so they
302
+ * can be stored in IndexedDB.
303
+ *
304
+ * Most developers will not need to access this class directly;
305
+ * it is exposed for advanced use cases.
306
+ */
307
+ var StorableRequest = class StorableRequest {
308
+ _requestData;
309
+ /**
310
+ * Converts a Request object to a plain object that can be structured
311
+ * cloned or stringified to JSON.
312
+ *
313
+ * @param request
314
+ * @returns
315
+ */
316
+ static async fromRequest(request) {
317
+ const requestData = {
318
+ url: request.url,
319
+ headers: {}
320
+ };
321
+ if (request.method !== "GET") requestData.body = await request.clone().arrayBuffer();
322
+ request.headers.forEach((value, key) => {
323
+ requestData.headers[key] = value;
324
+ });
325
+ for (const prop of serializableProperties) if (request[prop] !== void 0) requestData[prop] = request[prop];
326
+ return new StorableRequest(requestData);
327
+ }
328
+ /**
329
+ * Accepts an object of request data that can be used to construct a
330
+ * `Request` object but can also be stored in IndexedDB.
331
+ *
332
+ * @param requestData An object of request data that includes the `url` plus any relevant property of
333
+ * [`requestInit`](https://fetch.spec.whatwg.org/#requestinit).
334
+ */
335
+ constructor(requestData) {
336
+ if (process.env.NODE_ENV !== "production") {
337
+ finalAssertExports.isType(requestData, "object", {
338
+ moduleName: "serwist",
339
+ className: "StorableRequest",
340
+ funcName: "constructor",
341
+ paramName: "requestData"
342
+ });
343
+ finalAssertExports.isType(requestData.url, "string", {
344
+ moduleName: "serwist",
345
+ className: "StorableRequest",
346
+ funcName: "constructor",
347
+ paramName: "requestData.url"
348
+ });
349
+ }
350
+ if (requestData.mode === "navigate") requestData.mode = "same-origin";
351
+ this._requestData = requestData;
352
+ }
353
+ /**
354
+ * Returns a deep clone of the instance's `requestData` object.
355
+ *
356
+ * @returns
357
+ */
358
+ toObject() {
359
+ const requestData = Object.assign({}, this._requestData);
360
+ requestData.headers = Object.assign({}, this._requestData.headers);
361
+ if (requestData.body) requestData.body = requestData.body.slice(0);
362
+ return requestData;
363
+ }
364
+ /**
365
+ * Converts this instance to a Request.
366
+ *
367
+ * @returns
368
+ */
369
+ toRequest() {
370
+ return new Request(this._requestData.url, this._requestData);
371
+ }
372
+ /**
373
+ * Creates and returns a deep clone of the instance.
374
+ *
375
+ * @returns
376
+ */
377
+ clone() {
378
+ return new StorableRequest(this.toObject());
379
+ }
380
+ };
381
+ //#endregion
382
+ //#region src/lib/backgroundSync/BackgroundSyncQueue.ts
383
+ const TAG_PREFIX = "serwist-background-sync";
384
+ const MAX_RETENTION_TIME = 1440 * 7;
385
+ const queueNames = /* @__PURE__ */ new Set();
386
+ /**
387
+ * Converts a QueueStore entry into the format exposed by Queue. This entails
388
+ * converting the request data into a real request and omitting the `id` and
389
+ * `queueName` properties.
390
+ *
391
+ * @param queueStoreEntry
392
+ * @returns
393
+ * @private
394
+ */
395
+ const convertEntry = (queueStoreEntry) => {
396
+ const queueEntry = {
397
+ request: new StorableRequest(queueStoreEntry.requestData).toRequest(),
398
+ timestamp: queueStoreEntry.timestamp
399
+ };
400
+ if (queueStoreEntry.metadata) queueEntry.metadata = queueStoreEntry.metadata;
401
+ return queueEntry;
402
+ };
403
+ /**
404
+ * A class to manage storing failed requests in IndexedDB and retrying them
405
+ * later. All parts of the storing and replaying process are observable via
406
+ * callbacks.
407
+ */
408
+ var BackgroundSyncQueue = class {
409
+ _name;
410
+ _onSync;
411
+ _maxRetentionTime;
412
+ _queueStore;
413
+ _forceSyncFallback;
414
+ _syncInProgress = false;
415
+ _requestsAddedDuringSync = false;
416
+ /**
417
+ * Creates an instance of Queue with the given options
418
+ *
419
+ * @param name The unique name for this queue. This name must be
420
+ * unique as it's used to register sync events and store requests
421
+ * in IndexedDB specific to this instance. An error will be thrown if
422
+ * a duplicate name is detected.
423
+ * @param options
424
+ */
425
+ constructor(name, { forceSyncFallback, onSync, maxRetentionTime } = {}) {
426
+ if (queueNames.has(name)) throw new SerwistError("duplicate-queue-name", { name });
427
+ queueNames.add(name);
428
+ this._name = name;
429
+ this._onSync = onSync || this.replayRequests;
430
+ this._maxRetentionTime = maxRetentionTime || MAX_RETENTION_TIME;
431
+ this._forceSyncFallback = Boolean(forceSyncFallback);
432
+ this._queueStore = new BackgroundSyncQueueStore(this._name);
433
+ this._addSyncListener();
434
+ }
435
+ /**
436
+ * @returns
437
+ */
438
+ get name() {
439
+ return this._name;
440
+ }
441
+ /**
442
+ * Stores the passed request in IndexedDB (with its timestamp and any
443
+ * metadata) at the end of the queue.
444
+ *
445
+ * @param entry
446
+ */
447
+ async pushRequest(entry) {
448
+ if (process.env.NODE_ENV !== "production") {
449
+ finalAssertExports.isType(entry, "object", {
450
+ moduleName: "serwist",
451
+ className: "BackgroundSyncQueue",
452
+ funcName: "pushRequest",
453
+ paramName: "entry"
454
+ });
455
+ finalAssertExports.isInstance(entry.request, Request, {
456
+ moduleName: "serwist",
457
+ className: "BackgroundSyncQueue",
458
+ funcName: "pushRequest",
459
+ paramName: "entry.request"
460
+ });
461
+ }
462
+ await this._addRequest(entry, "push");
463
+ }
464
+ /**
465
+ * Stores the passed request in IndexedDB (with its timestamp and any
466
+ * metadata) at the beginning of the queue.
467
+ *
468
+ * @param entry
469
+ */
470
+ async unshiftRequest(entry) {
471
+ if (process.env.NODE_ENV !== "production") {
472
+ finalAssertExports.isType(entry, "object", {
473
+ moduleName: "serwist",
474
+ className: "BackgroundSyncQueue",
475
+ funcName: "unshiftRequest",
476
+ paramName: "entry"
477
+ });
478
+ finalAssertExports.isInstance(entry.request, Request, {
479
+ moduleName: "serwist",
480
+ className: "BackgroundSyncQueue",
481
+ funcName: "unshiftRequest",
482
+ paramName: "entry.request"
483
+ });
484
+ }
485
+ await this._addRequest(entry, "unshift");
486
+ }
487
+ /**
488
+ * Removes and returns the last request in the queue (along with its
489
+ * timestamp and any metadata).
490
+ *
491
+ * @returns
492
+ */
493
+ async popRequest() {
494
+ return this._removeRequest("pop");
495
+ }
496
+ /**
497
+ * Removes and returns the first request in the queue (along with its
498
+ * timestamp and any metadata).
499
+ *
500
+ * @returns
501
+ */
502
+ async shiftRequest() {
503
+ return this._removeRequest("shift");
504
+ }
505
+ /**
506
+ * Returns all the entries that have not expired (per `maxRetentionTime`).
507
+ * Any expired entries are removed from the queue.
508
+ *
509
+ * @returns
510
+ */
511
+ async getAll() {
512
+ const allEntries = await this._queueStore.getAll();
513
+ const now = Date.now();
514
+ const unexpiredEntries = [];
515
+ for (const entry of allEntries) {
516
+ const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1e3;
517
+ if (now - entry.timestamp > maxRetentionTimeInMs) await this._queueStore.deleteEntry(entry.id);
518
+ else unexpiredEntries.push(convertEntry(entry));
519
+ }
520
+ return unexpiredEntries;
521
+ }
522
+ /**
523
+ * Returns the number of entries present in the queue.
524
+ * Note that expired entries (per `maxRetentionTime`) are also included in this count.
525
+ *
526
+ * @returns
527
+ */
528
+ async size() {
529
+ return await this._queueStore.size();
530
+ }
531
+ /**
532
+ * Adds the entry to the QueueStore and registers for a sync event.
533
+ *
534
+ * @param entry
535
+ * @param operation
536
+ * @private
537
+ */
538
+ async _addRequest({ request, metadata, timestamp = Date.now() }, operation) {
539
+ const entry = {
540
+ requestData: (await StorableRequest.fromRequest(request.clone())).toObject(),
541
+ timestamp
542
+ };
543
+ if (metadata) entry.metadata = metadata;
544
+ switch (operation) {
545
+ case "push":
546
+ await this._queueStore.pushEntry(entry);
547
+ break;
548
+ case "unshift":
549
+ await this._queueStore.unshiftEntry(entry);
550
+ break;
551
+ }
552
+ if (process.env.NODE_ENV !== "production") logger.log(`Request for '${getFriendlyURL(request.url)}' has been added to background sync queue '${this._name}'.`);
553
+ if (this._syncInProgress) this._requestsAddedDuringSync = true;
554
+ else await this.registerSync();
555
+ }
556
+ /**
557
+ * Removes and returns the first or last (depending on `operation`) entry
558
+ * from the {@linkcode BackgroundSyncQueueStore} that's not older than the `maxRetentionTime`.
559
+ *
560
+ * @param operation
561
+ * @returns
562
+ * @private
563
+ */
564
+ async _removeRequest(operation) {
565
+ const now = Date.now();
566
+ let entry;
567
+ switch (operation) {
568
+ case "pop":
569
+ entry = await this._queueStore.popEntry();
570
+ break;
571
+ case "shift":
572
+ entry = await this._queueStore.shiftEntry();
573
+ break;
574
+ }
575
+ if (entry) {
576
+ const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1e3;
577
+ if (now - entry.timestamp > maxRetentionTimeInMs) return this._removeRequest(operation);
578
+ return convertEntry(entry);
579
+ }
580
+ }
581
+ /**
582
+ * Loops through each request in the queue and attempts to re-fetch it.
583
+ * If any request fails to re-fetch, it's put back in the same position in
584
+ * the queue (which registers a retry for the next sync event).
585
+ */
586
+ async replayRequests() {
587
+ let entry;
588
+ while (entry = await this.shiftRequest()) try {
589
+ await fetch(entry.request.clone());
590
+ if (process.env.NODE_ENV !== "production") logger.log(`Request for '${getFriendlyURL(entry.request.url)}' has been replayed in queue '${this._name}'`);
591
+ } catch {
592
+ await this.unshiftRequest(entry);
593
+ if (process.env.NODE_ENV !== "production") logger.log(`Request for '${getFriendlyURL(entry.request.url)}' failed to replay, putting it back in queue '${this._name}'`);
594
+ throw new SerwistError("queue-replay-failed", { name: this._name });
595
+ }
596
+ if (process.env.NODE_ENV !== "production") logger.log(`All requests in queue '${this.name}' have successfully replayed; the queue is now empty!`);
597
+ }
598
+ /**
599
+ * Registers a sync event with a tag unique to this instance.
600
+ */
601
+ async registerSync() {
602
+ if ("sync" in self.registration && !this._forceSyncFallback) try {
603
+ await self.registration.sync.register(`${TAG_PREFIX}:${this._name}`);
604
+ } catch (err) {
605
+ if (process.env.NODE_ENV !== "production") logger.warn(`Unable to register sync event for '${this._name}'.`, err);
606
+ }
607
+ }
608
+ /**
609
+ * In sync-supporting browsers, this adds a listener for the sync event.
610
+ * In non-sync-supporting browsers, or if _forceSyncFallback is true, this
611
+ * will retry the queue on service worker startup.
612
+ *
613
+ * @private
614
+ */
615
+ _addSyncListener() {
616
+ if ("sync" in self.registration && !this._forceSyncFallback) self.addEventListener("sync", (event) => {
617
+ if (event.tag === `${TAG_PREFIX}:${this._name}`) {
618
+ if (process.env.NODE_ENV !== "production") logger.log(`Background sync for tag '${event.tag}' has been received`);
619
+ const syncComplete = async () => {
620
+ this._syncInProgress = true;
621
+ let syncError;
622
+ try {
623
+ await this._onSync({ queue: this });
624
+ } catch (error) {
625
+ if (error instanceof Error) {
626
+ syncError = error;
627
+ throw syncError;
628
+ }
629
+ } finally {
630
+ if (this._requestsAddedDuringSync && !(syncError && !event.lastChance)) await this.registerSync();
631
+ this._syncInProgress = false;
632
+ this._requestsAddedDuringSync = false;
633
+ }
634
+ };
635
+ event.waitUntil(syncComplete());
636
+ }
637
+ });
638
+ else {
639
+ if (process.env.NODE_ENV !== "production") logger.log("Background sync replaying without background sync event");
640
+ this._onSync({ queue: this });
641
+ }
642
+ }
643
+ /**
644
+ * Returns the set of queue names. This is primarily used to reset the list
645
+ * of queue names in tests.
646
+ *
647
+ * @returns
648
+ * @private
649
+ */
650
+ static get _queueNames() {
651
+ return queueNames;
652
+ }
653
+ };
654
+ //#endregion
655
+ //#region src/lib/backgroundSync/BackgroundSyncPlugin.ts
656
+ /**
657
+ * A class implementing the `fetchDidFail` lifecycle callback. This makes it
658
+ * easier to add failed requests to a {@linkcode BackgroundSyncQueue}.
659
+ */
660
+ var BackgroundSyncPlugin = class {
661
+ _queue;
662
+ /**
663
+ * @param name See the {@linkcode BackgroundSyncQueue}
664
+ * documentation for parameter details.
665
+ * @param options See the {@linkcode BackgroundSyncQueue}
666
+ * documentation for parameter details.
667
+ * @see https://serwist.pages.dev/docs/serwist/core/background-sync-queue
668
+ */
669
+ constructor(name, options) {
670
+ this._queue = new BackgroundSyncQueue(name, options);
671
+ }
672
+ /**
673
+ * @param options
674
+ * @private
675
+ */
676
+ async fetchDidFail({ request }) {
677
+ await this._queue.pushRequest({ request });
678
+ }
679
+ };
680
+ //#endregion
681
+ //#region src/lib/strategies/plugins/cacheOkAndOpaquePlugin.ts
682
+ const cacheOkAndOpaquePlugin = {
683
+ /**
684
+ * Returns a valid response (to allow caching) if the status is 200 (OK) or
685
+ * 0 (opaque).
686
+ *
687
+ * @param options
688
+ * @returns
689
+ * @private
690
+ */
691
+ cacheWillUpdate: async ({ response }) => {
692
+ if (response.status === 200 || response.status === 0) return response;
693
+ return null;
694
+ } };
695
+ //#endregion
696
+ //#region src/lib/strategies/StrategyHandler.ts
697
+ function toRequest(input) {
698
+ return typeof input === "string" ? new Request(input) : input;
699
+ }
700
+ /**
701
+ * A class created every time a {@linkcode Strategy} instance calls {@linkcode Strategy.handle} or
702
+ * {@linkcode Strategy.handleAll} that wraps all fetch and cache actions around plugin callbacks
703
+ * and keeps track of when the strategy is "done" (i.e. when all added `event.waitUntil()` promises
704
+ * have resolved).
705
+ */
706
+ var StrategyHandler = class {
707
+ /**
708
+ * The event associated with this request.
709
+ */
710
+ event;
711
+ /**
712
+ * The request the strategy is processing (passed to the strategy's
713
+ * `handle()` or `handleAll()` method).
714
+ */
715
+ request;
716
+ /**
717
+ * A `URL` instance of `request.url` (if passed to the strategy's
718
+ * `handle()` or `handleAll()` method).
719
+ * Note: the `url` param will be present if the strategy is invoked
720
+ * from a {@linkcode Route} object.
721
+ */
722
+ url;
723
+ /**
724
+ * Some additional params (if passed to the strategy's
725
+ * `handle()` or `handleAll()` method).
726
+ *
727
+ * Note: the `params` param will be present if the strategy is invoked
728
+ * from a {@linkcode Route} object and that route's matcher returned a truthy
729
+ * value (it will be that value).
730
+ */
731
+ params;
732
+ _cacheKeys = {};
733
+ _strategy;
734
+ _handlerDeferred;
735
+ _extendLifetimePromises;
736
+ _plugins;
737
+ _pluginStateMap;
738
+ /**
739
+ * Creates a new instance associated with the passed strategy and event
740
+ * that's handling the request.
741
+ *
742
+ * The constructor also initializes the state that will be passed to each of
743
+ * the plugins handling this request.
744
+ *
745
+ * @param strategy
746
+ * @param options
747
+ */
748
+ constructor(strategy, options) {
749
+ if (process.env.NODE_ENV !== "production") {
750
+ finalAssertExports.isInstance(options.event, ExtendableEvent, {
751
+ moduleName: "serwist",
752
+ className: "StrategyHandler",
753
+ funcName: "constructor",
754
+ paramName: "options.event"
755
+ });
756
+ finalAssertExports.isInstance(options.request, Request, {
757
+ moduleName: "serwist",
758
+ className: "StrategyHandler",
759
+ funcName: "constructor",
760
+ paramName: "options.request"
761
+ });
762
+ }
763
+ this.event = options.event;
764
+ this.request = options.request;
765
+ if (options.url) {
766
+ this.url = options.url;
767
+ this.params = options.params;
768
+ }
769
+ this._strategy = strategy;
770
+ this._handlerDeferred = new Deferred();
771
+ this._extendLifetimePromises = [];
772
+ this._plugins = [...strategy.plugins];
773
+ this._pluginStateMap = /* @__PURE__ */ new Map();
774
+ for (const plugin of this._plugins) this._pluginStateMap.set(plugin, {});
775
+ this.event.waitUntil(this._handlerDeferred.promise);
776
+ }
777
+ /**
778
+ * Fetches a given request (and invokes any applicable plugin callback
779
+ * methods), taking the `fetchOptions` (for non-navigation requests) and
780
+ * `plugins` provided to the {@linkcode Strategy} object into account.
781
+ *
782
+ * The following plugin lifecycle methods are invoked when using this method:
783
+ * - `requestWillFetch()`
784
+ * - `fetchDidSucceed()`
785
+ * - `fetchDidFail()`
786
+ *
787
+ * @param input The URL or request to fetch.
788
+ * @returns
789
+ */
790
+ async fetch(input) {
791
+ const { event } = this;
792
+ let request = toRequest(input);
793
+ const preloadResponse = await this.getPreloadResponse();
794
+ if (preloadResponse) return preloadResponse;
795
+ const originalRequest = this.hasCallback("fetchDidFail") ? request.clone() : null;
796
+ try {
797
+ for (const cb of this.iterateCallbacks("requestWillFetch")) request = await cb({
798
+ request: request.clone(),
799
+ event
800
+ });
801
+ } catch (err) {
802
+ if (err instanceof Error) throw new SerwistError("plugin-error-request-will-fetch", { thrownErrorMessage: err.message });
803
+ }
804
+ const pluginFilteredRequest = request.clone();
805
+ try {
806
+ let fetchResponse;
807
+ fetchResponse = await fetch(request, request.mode === "navigate" ? void 0 : this._strategy.fetchOptions);
808
+ if (process.env.NODE_ENV !== "production") logger.debug(`Network request for '${getFriendlyURL(request.url)}' returned a response with status '${fetchResponse.status}'.`);
809
+ for (const callback of this.iterateCallbacks("fetchDidSucceed")) fetchResponse = await callback({
810
+ event,
811
+ request: pluginFilteredRequest,
812
+ response: fetchResponse
813
+ });
814
+ return fetchResponse;
815
+ } catch (error) {
816
+ if (process.env.NODE_ENV !== "production") logger.log(`Network request for '${getFriendlyURL(request.url)}' threw an error.`, error);
817
+ if (originalRequest) await this.runCallbacks("fetchDidFail", {
818
+ error,
819
+ event,
820
+ originalRequest: originalRequest.clone(),
821
+ request: pluginFilteredRequest.clone()
822
+ });
823
+ throw error;
824
+ }
825
+ }
826
+ /**
827
+ * Calls `this.fetch()` and (in the background) caches the generated response.
828
+ *
829
+ * The call to `this.cachePut()` automatically invokes `this.waitUntil()`,
830
+ * so you do not have to call `waitUntil()` yourself.
831
+ *
832
+ * @param input The request or URL to fetch and cache.
833
+ * @returns
834
+ */
835
+ async fetchAndCachePut(input) {
836
+ const response = await this.fetch(input);
837
+ const responseClone = response.clone();
838
+ this.waitUntil(this.cachePut(input, responseClone));
839
+ return response;
840
+ }
841
+ /**
842
+ * Matches a request from the cache (and invokes any applicable plugin
843
+ * callback method) using the `cacheName`, `matchOptions`, and `plugins`
844
+ * provided to the `Strategy` object.
845
+ *
846
+ * The following lifecycle methods are invoked when using this method:
847
+ * - `cacheKeyWillBeUsed`
848
+ * - `cachedResponseWillBeUsed`
849
+ *
850
+ * @param key The `Request` or `URL` object to use as the cache key.
851
+ * @returns A matching response, if found.
852
+ */
853
+ async cacheMatch(key) {
854
+ const request = toRequest(key);
855
+ let cachedResponse;
856
+ const { cacheName, matchOptions } = this._strategy;
857
+ const effectiveRequest = await this.getCacheKey(request, "read");
858
+ const multiMatchOptions = {
859
+ ...matchOptions,
860
+ cacheName
861
+ };
862
+ cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);
863
+ if (process.env.NODE_ENV !== "production") if (cachedResponse) logger.debug(`Found a cached response in '${cacheName}'.`);
864
+ else logger.debug(`No cached response found in '${cacheName}'.`);
865
+ for (const callback of this.iterateCallbacks("cachedResponseWillBeUsed")) cachedResponse = await callback({
866
+ cacheName,
867
+ matchOptions,
868
+ cachedResponse,
869
+ request: effectiveRequest,
870
+ event: this.event
871
+ }) || void 0;
872
+ return cachedResponse;
873
+ }
874
+ /**
875
+ * Puts a request/response pair into the cache (and invokes any applicable
876
+ * plugin callback method) using the `cacheName` and `plugins` provided to
877
+ * the {@linkcode Strategy} object.
878
+ *
879
+ * The following plugin lifecycle methods are invoked when using this method:
880
+ * - `cacheKeyWillBeUsed`
881
+ * - `cacheWillUpdate`
882
+ * - `cacheDidUpdate`
883
+ *
884
+ * @param key The request or URL to use as the cache key.
885
+ * @param response The response to cache.
886
+ * @returns `false` if a `cacheWillUpdate` caused the response to
887
+ * not be cached, and `true` otherwise.
888
+ */
889
+ async cachePut(key, response) {
890
+ const request = toRequest(key);
891
+ await timeout(0);
892
+ const effectiveRequest = await this.getCacheKey(request, "write");
893
+ if (process.env.NODE_ENV !== "production") {
894
+ if (effectiveRequest.method && effectiveRequest.method !== "GET") throw new SerwistError("attempt-to-cache-non-get-request", {
895
+ url: getFriendlyURL(effectiveRequest.url),
896
+ method: effectiveRequest.method
897
+ });
898
+ }
899
+ if (!response) {
900
+ if (process.env.NODE_ENV !== "production") logger.error(`Cannot cache non-existent response for '${getFriendlyURL(effectiveRequest.url)}'.`);
901
+ throw new SerwistError("cache-put-with-no-response", { url: getFriendlyURL(effectiveRequest.url) });
902
+ }
903
+ const responseToCache = await this._ensureResponseSafeToCache(response);
904
+ if (!responseToCache) {
905
+ if (process.env.NODE_ENV !== "production") logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' will not be cached.`, responseToCache);
906
+ return false;
907
+ }
908
+ const { cacheName, matchOptions } = this._strategy;
909
+ const cache = await self.caches.open(cacheName);
910
+ if (process.env.NODE_ENV !== "production") {
911
+ const vary = response.headers.get("Vary");
912
+ if (vary && matchOptions?.ignoreVary !== true) 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.`);
913
+ }
914
+ const hasCacheUpdateCallback = this.hasCallback("cacheDidUpdate");
915
+ const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams(cache, effectiveRequest.clone(), ["__WB_REVISION__"], matchOptions) : null;
916
+ if (process.env.NODE_ENV !== "production") logger.debug(`Updating the '${cacheName}' cache with a new Response for ${getFriendlyURL(effectiveRequest.url)}.`);
917
+ try {
918
+ await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);
919
+ } catch (error) {
920
+ if (error instanceof Error) {
921
+ if (error.name === "QuotaExceededError") await executeQuotaErrorCallbacks();
922
+ throw error;
923
+ }
924
+ }
925
+ for (const callback of this.iterateCallbacks("cacheDidUpdate")) await callback({
926
+ cacheName,
927
+ oldResponse,
928
+ newResponse: responseToCache.clone(),
929
+ request: effectiveRequest,
930
+ event: this.event
931
+ });
932
+ return true;
933
+ }
934
+ /**
935
+ * Checks the `plugins` provided to the {@linkcode Strategy} object for `cacheKeyWillBeUsed`
936
+ * callbacks and executes found callbacks in sequence. The final `Request`
937
+ * object returned by the last plugin is treated as the cache key for cache
938
+ * reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have
939
+ * been registered, the passed request is returned unmodified.
940
+ *
941
+ * @param request
942
+ * @param mode
943
+ * @returns
944
+ */
945
+ async getCacheKey(request, mode) {
946
+ const key = `${request.url} | ${mode}`;
947
+ if (!this._cacheKeys[key]) {
948
+ let effectiveRequest = request;
949
+ for (const callback of this.iterateCallbacks("cacheKeyWillBeUsed")) effectiveRequest = toRequest(await callback({
950
+ mode,
951
+ request: effectiveRequest,
952
+ event: this.event,
953
+ params: this.params
954
+ }));
955
+ this._cacheKeys[key] = effectiveRequest;
956
+ }
957
+ return this._cacheKeys[key];
958
+ }
959
+ /**
960
+ * Returns `true` if the strategy has at least one plugin with the given
961
+ * callback.
962
+ *
963
+ * @param name The name of the callback to check for.
964
+ * @returns
965
+ */
966
+ hasCallback(name) {
967
+ for (const plugin of this._strategy.plugins) if (name in plugin) return true;
968
+ return false;
969
+ }
970
+ /**
971
+ * Runs all plugin callbacks matching the given name, in order, passing the
972
+ * given param object as the only argument.
973
+ *
974
+ * Note: since this method runs all plugins, it's not suitable for cases
975
+ * where the return value of a callback needs to be applied prior to calling
976
+ * the next callback. See {@linkcode StrategyHandler.iterateCallbacks} for how to handle that case.
977
+ *
978
+ * @param name The name of the callback to run within each plugin.
979
+ * @param param The object to pass as the first (and only) param when executing each callback. This object will be merged with the
980
+ * current plugin state prior to callback execution.
981
+ */
982
+ async runCallbacks(name, param) {
983
+ for (const callback of this.iterateCallbacks(name)) await callback(param);
984
+ }
985
+ /**
986
+ * Accepts a callback name and returns an iterable of matching plugin callbacks.
987
+ *
988
+ * @param name The name fo the callback to run
989
+ * @returns
990
+ */
991
+ *iterateCallbacks(name) {
992
+ for (const plugin of this._strategy.plugins) if (typeof plugin[name] === "function") {
993
+ const state = this._pluginStateMap.get(plugin);
994
+ const statefulCallback = (param) => {
995
+ const statefulParam = {
996
+ ...param,
997
+ state
998
+ };
999
+ return plugin[name](statefulParam);
1000
+ };
1001
+ yield statefulCallback;
1002
+ }
1003
+ }
1004
+ /**
1005
+ * Adds a promise to the
1006
+ * [extend lifetime promises](https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises)
1007
+ * of the event event associated with the request being handled (usually a `FetchEvent`).
1008
+ *
1009
+ * Note: you can await {@linkcode StrategyHandler.doneWaiting} to know when all added promises have settled.
1010
+ *
1011
+ * @param promise A promise to add to the extend lifetime promises of
1012
+ * the event that triggered the request.
1013
+ */
1014
+ waitUntil(promise) {
1015
+ this._extendLifetimePromises.push(promise);
1016
+ return promise;
1017
+ }
1018
+ /**
1019
+ * Returns a promise that resolves once all promises passed to
1020
+ * `this.waitUntil()` have settled.
1021
+ *
1022
+ * Note: any work done after `doneWaiting()` settles should be manually
1023
+ * passed to an event's `waitUntil()` method (not `this.waitUntil()`), otherwise
1024
+ * the service worker thread may be killed prior to your work completing.
1025
+ */
1026
+ async doneWaiting() {
1027
+ let promise;
1028
+ while (promise = this._extendLifetimePromises.shift()) await promise;
1029
+ }
1030
+ /**
1031
+ * Stops running the strategy and immediately resolves any pending
1032
+ * `waitUntil()` promise.
1033
+ */
1034
+ destroy() {
1035
+ this._handlerDeferred.resolve(null);
1036
+ }
1037
+ /**
1038
+ * This method checks if the navigation preload `Response` is available.
1039
+ *
1040
+ * @param request
1041
+ * @param event
1042
+ * @returns
1043
+ */
1044
+ async getPreloadResponse() {
1045
+ if (this.event instanceof FetchEvent && this.event.request.mode === "navigate" && "preloadResponse" in this.event) try {
1046
+ const possiblePreloadResponse = await this.event.preloadResponse;
1047
+ if (possiblePreloadResponse) {
1048
+ if (process.env.NODE_ENV !== "production") logger.log(`Using a preloaded navigation response for '${getFriendlyURL(this.event.request.url)}'`);
1049
+ return possiblePreloadResponse;
1050
+ }
1051
+ } catch (error) {
1052
+ if (process.env.NODE_ENV !== "production") logger.error(error);
1053
+ return;
1054
+ }
1055
+ }
1056
+ /**
1057
+ * This method will call `cacheWillUpdate` on the available plugins (or use
1058
+ * status === 200) to determine if the response is safe and valid to cache.
1059
+ *
1060
+ * @param response
1061
+ * @returns
1062
+ * @private
1063
+ */
1064
+ async _ensureResponseSafeToCache(response) {
1065
+ let responseToCache = response;
1066
+ let pluginsUsed = false;
1067
+ for (const callback of this.iterateCallbacks("cacheWillUpdate")) {
1068
+ responseToCache = await callback({
1069
+ request: this.request,
1070
+ response: responseToCache,
1071
+ event: this.event
1072
+ }) || void 0;
1073
+ pluginsUsed = true;
1074
+ if (!responseToCache) break;
1075
+ }
1076
+ if (!pluginsUsed) {
1077
+ if (responseToCache && responseToCache.status !== 200) {
1078
+ if (process.env.NODE_ENV !== "production") if (responseToCache.status === 0) 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.`);
1079
+ else logger.debug(`The response for '${this.request.url}' returned a status code of '${response.status}' and won't be cached as a result.`);
1080
+ responseToCache = void 0;
1081
+ }
1082
+ }
1083
+ return responseToCache;
1084
+ }
1085
+ };
1086
+ //#endregion
1087
+ //#region src/lib/strategies/Strategy.ts
1088
+ /**
1089
+ * Abstract class for implementing runtime caching strategies.
1090
+ *
1091
+ * Custom strategies should extend this class and leverage `StrategyHandler`, which will ensure all relevant cache options,
1092
+ * fetch options, and plugins are used (per the current strategy instance), to perform all fetching and caching logic.
1093
+ */
1094
+ var Strategy = class {
1095
+ cacheName;
1096
+ plugins;
1097
+ fetchOptions;
1098
+ matchOptions;
1099
+ /**
1100
+ * Creates a new instance of the strategy and sets all documented option
1101
+ * properties as public instance properties.
1102
+ *
1103
+ * Note: if a custom strategy class extends the base Strategy class and does
1104
+ * not need more than these properties, it does not need to define its own
1105
+ * constructor.
1106
+ *
1107
+ * @param options
1108
+ */
1109
+ constructor(options = {}) {
1110
+ this.cacheName = cacheNames.getRuntimeName(options.cacheName);
1111
+ this.plugins = options.plugins || [];
1112
+ this.fetchOptions = options.fetchOptions;
1113
+ this.matchOptions = options.matchOptions;
1114
+ }
1115
+ /**
1116
+ * Performs a request strategy and returns a promise that will resolve to
1117
+ * a response, invoking all relevant plugin callbacks.
1118
+ *
1119
+ * When a strategy instance is registered with a route, this method is automatically
1120
+ * called when the route matches.
1121
+ *
1122
+ * Alternatively, this method can be used in a standalone `fetch` event
1123
+ * listener by passing it to `event.respondWith()`.
1124
+ *
1125
+ * @param options A `FetchEvent` or an object with the properties listed below.
1126
+ * @param options.request A request to run this strategy for.
1127
+ * @param options.event The event associated with the request.
1128
+ * @param options.url
1129
+ * @param options.params
1130
+ */
1131
+ handle(options) {
1132
+ const [responseDone] = this.handleAll(options);
1133
+ return responseDone;
1134
+ }
1135
+ /**
1136
+ * Similar to `handle()`, but instead of just returning a promise that
1137
+ * resolves to a response, it will return an tuple of `[response, done]` promises,
1138
+ * where `response` is equivalent to what `handle()` returns, and `done` is a
1139
+ * promise that will resolve once all promises added to `event.waitUntil()` as a part
1140
+ * of performing the strategy have completed.
1141
+ *
1142
+ * You can await the `done` promise to ensure any extra work performed by
1143
+ * the strategy (usually caching responses) completes successfully.
1144
+ *
1145
+ * @param options A `FetchEvent` or `HandlerCallbackOptions` object.
1146
+ * @returns A tuple of [response, done] promises that can be used to determine when the response resolves as
1147
+ * well as when the handler has completed all its work.
1148
+ */
1149
+ handleAll(options) {
1150
+ if (options instanceof FetchEvent) options = {
1151
+ event: options,
1152
+ request: options.request
1153
+ };
1154
+ const event = options.event;
1155
+ const request = typeof options.request === "string" ? new Request(options.request) : options.request;
1156
+ const handler = new StrategyHandler(this, options.url ? {
1157
+ event,
1158
+ request,
1159
+ url: options.url,
1160
+ params: options.params
1161
+ } : {
1162
+ event,
1163
+ request
1164
+ });
1165
+ const responseDone = this._getResponse(handler, request, event);
1166
+ return [responseDone, this._awaitComplete(responseDone, handler, request, event)];
1167
+ }
1168
+ async _getResponse(handler, request, event) {
1169
+ await handler.runCallbacks("handlerWillStart", {
1170
+ event,
1171
+ request
1172
+ });
1173
+ let response;
1174
+ try {
1175
+ response = await this._handle(request, handler);
1176
+ if (response === void 0 || response.type === "error") throw new SerwistError("no-response", { url: request.url });
1177
+ } catch (error) {
1178
+ if (error instanceof Error) for (const callback of handler.iterateCallbacks("handlerDidError")) {
1179
+ response = await callback({
1180
+ error,
1181
+ event,
1182
+ request
1183
+ });
1184
+ if (response !== void 0) break;
1185
+ }
1186
+ if (!response) throw error;
1187
+ if (process.env.NODE_ENV !== "production") 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.`);
1188
+ }
1189
+ for (const callback of handler.iterateCallbacks("handlerWillRespond")) response = await callback({
1190
+ event,
1191
+ request,
1192
+ response
1193
+ });
1194
+ return response;
1195
+ }
1196
+ async _awaitComplete(responseDone, handler, request, event) {
1197
+ let response;
1198
+ let error;
1199
+ try {
1200
+ response = await responseDone;
1201
+ } catch {}
1202
+ try {
1203
+ await handler.runCallbacks("handlerDidRespond", {
1204
+ event,
1205
+ request,
1206
+ response
1207
+ });
1208
+ await handler.doneWaiting();
1209
+ } catch (waitUntilError) {
1210
+ if (waitUntilError instanceof Error) error = waitUntilError;
1211
+ }
1212
+ await handler.runCallbacks("handlerDidComplete", {
1213
+ event,
1214
+ request,
1215
+ response,
1216
+ error
1217
+ });
1218
+ handler.destroy();
1219
+ if (error) throw error;
1220
+ }
1221
+ };
1222
+ //#endregion
1223
+ //#region src/lib/strategies/utils/messages.ts
1224
+ const messages = {
1225
+ strategyStart: (strategyName, request) => `Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`,
1226
+ printFinalResponse: (response) => {
1227
+ if (response) {
1228
+ logger.groupCollapsed("View the final response here.");
1229
+ logger.log(response || "[No response returned]");
1230
+ logger.groupEnd();
1231
+ }
1232
+ }
1233
+ };
1234
+ //#endregion
1235
+ //#region src/lib/strategies/NetworkFirst.ts
1236
+ /**
1237
+ * An implementation of the [network first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network_first_falling_back_to_cache)
1238
+ * request strategy.
1239
+ *
1240
+ * By default, this strategy will cache responses with a 200 status code as
1241
+ * well as [opaque responses](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque_responses).
1242
+ * Opaque responses are are cross-origin requests where the response doesn't
1243
+ * support [CORS](https://enable-cors.org/).
1244
+ *
1245
+ * If the network request fails, and there is no cache match, this will throw
1246
+ * a {@linkcode SerwistError} exception.
1247
+ */
1248
+ var NetworkFirst = class extends Strategy {
1249
+ _networkTimeoutSeconds;
1250
+ /**
1251
+ * @param options
1252
+ * This option can be used to combat
1253
+ * "[lie-fi](https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi)"
1254
+ * scenarios.
1255
+ */
1256
+ constructor(options = {}) {
1257
+ super(options);
1258
+ if (!this.plugins.some((p) => "cacheWillUpdate" in p)) this.plugins.unshift(cacheOkAndOpaquePlugin);
1259
+ this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
1260
+ if (process.env.NODE_ENV !== "production") {
1261
+ if (this._networkTimeoutSeconds) finalAssertExports.isType(this._networkTimeoutSeconds, "number", {
1262
+ moduleName: "serwist",
1263
+ className: this.constructor.name,
1264
+ funcName: "constructor",
1265
+ paramName: "networkTimeoutSeconds"
1266
+ });
1267
+ }
1268
+ }
1269
+ /**
1270
+ * @private
1271
+ * @param request A request to run this strategy for.
1272
+ * @param handler The event that triggered the request.
1273
+ * @returns
1274
+ */
1275
+ async _handle(request, handler) {
1276
+ const logs = [];
1277
+ if (process.env.NODE_ENV !== "production") finalAssertExports.isInstance(request, Request, {
1278
+ moduleName: "serwist",
1279
+ className: this.constructor.name,
1280
+ funcName: "handle",
1281
+ paramName: "makeRequest"
1282
+ });
1283
+ const promises = [];
1284
+ let timeoutId;
1285
+ if (this._networkTimeoutSeconds) {
1286
+ const { id, promise } = this._getTimeoutPromise({
1287
+ request,
1288
+ logs,
1289
+ handler
1290
+ });
1291
+ timeoutId = id;
1292
+ promises.push(promise);
1293
+ }
1294
+ const networkPromise = this._getNetworkPromise({
1295
+ timeoutId,
1296
+ request,
1297
+ logs,
1298
+ handler
1299
+ });
1300
+ promises.push(networkPromise);
1301
+ const response = await handler.waitUntil((async () => {
1302
+ return await handler.waitUntil(Promise.race(promises)) || await networkPromise;
1303
+ })());
1304
+ if (process.env.NODE_ENV !== "production") {
1305
+ logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
1306
+ for (const log of logs) logger.log(log);
1307
+ messages.printFinalResponse(response);
1308
+ logger.groupEnd();
1309
+ }
1310
+ if (!response) throw new SerwistError("no-response", { url: request.url });
1311
+ return response;
1312
+ }
1313
+ /**
1314
+ * @param options
1315
+ * @returns
1316
+ * @private
1317
+ */
1318
+ _getTimeoutPromise({ request, logs, handler }) {
1319
+ let timeoutId;
1320
+ return {
1321
+ promise: new Promise((resolve) => {
1322
+ const onNetworkTimeout = async () => {
1323
+ if (process.env.NODE_ENV !== "production") logs.push(`Timing out the network response at ${this._networkTimeoutSeconds} seconds.`);
1324
+ resolve(await handler.cacheMatch(request));
1325
+ };
1326
+ timeoutId = setTimeout(onNetworkTimeout, this._networkTimeoutSeconds * 1e3);
1327
+ }),
1328
+ id: timeoutId
1329
+ };
1330
+ }
1331
+ /**
1332
+ * @param options
1333
+ * @param options.timeoutId
1334
+ * @param options.request
1335
+ * @param options.logs A reference to the logs Array.
1336
+ * @param options.event
1337
+ * @returns
1338
+ *
1339
+ * @private
1340
+ */
1341
+ async _getNetworkPromise({ timeoutId, request, logs, handler }) {
1342
+ let error;
1343
+ let response;
1344
+ try {
1345
+ response = await handler.fetchAndCachePut(request);
1346
+ } catch (fetchError) {
1347
+ if (fetchError instanceof Error) error = fetchError;
1348
+ }
1349
+ if (timeoutId) clearTimeout(timeoutId);
1350
+ if (process.env.NODE_ENV !== "production") if (response) logs.push("Got response from network.");
1351
+ else logs.push("Unable to get a response from the network. Will respond with a cached response.");
1352
+ if (error || !response) {
1353
+ response = await handler.cacheMatch(request);
1354
+ if (process.env.NODE_ENV !== "production") if (response) logs.push(`Found a cached response in the '${this.cacheName}' cache.`);
1355
+ else logs.push(`No response found in the '${this.cacheName}' cache.`);
1356
+ }
1357
+ return response;
1358
+ }
1359
+ };
1360
+ //#endregion
1361
+ //#region src/lib/strategies/NetworkOnly.ts
1362
+ /**
1363
+ * An implementation of the [network only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network_only)
1364
+ * request strategy.
1365
+ *
1366
+ * This class is useful if you require specific requests to only be fulfilled from the network.
1367
+ *
1368
+ * If the network request fails, this will throw a {@linkcode SerwistError} exception.
1369
+ */
1370
+ var NetworkOnly = class extends Strategy {
1371
+ _networkTimeoutSeconds;
1372
+ /**
1373
+ * @param options
1374
+ */
1375
+ constructor(options = {}) {
1376
+ super(options);
1377
+ this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
1378
+ }
1379
+ /**
1380
+ * @private
1381
+ * @param request A request to run this strategy for.
1382
+ * @param handler The event that triggered the request.
1383
+ * @returns
1384
+ */
1385
+ async _handle(request, handler) {
1386
+ if (process.env.NODE_ENV !== "production") finalAssertExports.isInstance(request, Request, {
1387
+ moduleName: "serwist",
1388
+ className: this.constructor.name,
1389
+ funcName: "_handle",
1390
+ paramName: "request"
1391
+ });
1392
+ let error;
1393
+ let response;
1394
+ try {
1395
+ const promises = [handler.fetch(request)];
1396
+ if (this._networkTimeoutSeconds) {
1397
+ const timeoutPromise = timeout(this._networkTimeoutSeconds * 1e3);
1398
+ promises.push(timeoutPromise);
1399
+ }
1400
+ response = await Promise.race(promises);
1401
+ if (!response) throw new Error(`Timed out the network response after ${this._networkTimeoutSeconds} seconds.`);
1402
+ } catch (err) {
1403
+ if (err instanceof Error) error = err;
1404
+ }
1405
+ if (process.env.NODE_ENV !== "production") {
1406
+ logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
1407
+ if (response) logger.log("Got response from network.");
1408
+ else logger.log("Unable to get a response from the network.");
1409
+ messages.printFinalResponse(response);
1410
+ logger.groupEnd();
1411
+ }
1412
+ if (!response) throw new SerwistError("no-response", {
1413
+ url: request.url,
1414
+ error
1415
+ });
1416
+ return response;
1417
+ }
1418
+ };
1419
+ //#endregion
1420
+ //#region src/constants.ts
1421
+ /**
1422
+ * The list of valid HTTP methods associated with requests that could be routed.
1423
+ *
1424
+ * @private
1425
+ */
1426
+ const validMethods = [
1427
+ "DELETE",
1428
+ "GET",
1429
+ "HEAD",
1430
+ "PATCH",
1431
+ "POST",
1432
+ "PUT"
1433
+ ];
1434
+ //#endregion
1435
+ //#region src/utils/normalizeHandler.ts
1436
+ /**
1437
+ * @param handler Either a function, or an object with a
1438
+ * 'handle' method.
1439
+ * @returns An object with a handle method.
1440
+ *
1441
+ * @private
1442
+ */
1443
+ const normalizeHandler = (handler) => {
1444
+ if (handler && typeof handler === "object") {
1445
+ if (process.env.NODE_ENV !== "production") finalAssertExports.hasMethod(handler, "handle", {
1446
+ moduleName: "serwist",
1447
+ className: "Route",
1448
+ funcName: "constructor",
1449
+ paramName: "handler"
1450
+ });
1451
+ return handler;
1452
+ }
1453
+ if (process.env.NODE_ENV !== "production") finalAssertExports.isType(handler, "function", {
1454
+ moduleName: "serwist",
1455
+ className: "Route",
1456
+ funcName: "constructor",
1457
+ paramName: "handler"
1458
+ });
1459
+ return { handle: handler };
1460
+ };
1461
+ //#endregion
1462
+ //#region src/Route.ts
1463
+ /**
1464
+ * A `Route` consists of a pair of callback functions, `match` and `handler`.
1465
+ * The `match` callback determines if a route should be used to handle a
1466
+ * request by returning a truthy value if it can. The `handler` callback
1467
+ * is called when the route matches and should return a promise that resolves
1468
+ * to a response.
1469
+ */
1470
+ var Route = class {
1471
+ handler;
1472
+ match;
1473
+ method;
1474
+ catchHandler;
1475
+ /**
1476
+ * Constructor for Route class.
1477
+ *
1478
+ * @param match A callback function that determines whether the
1479
+ * route matches a given `fetch` event by returning a truthy value.
1480
+ * @param handler A callback function that returns a `Promise` resolving
1481
+ * to a `Response`.
1482
+ * @param method The HTTP method to match the route against. Defaults
1483
+ * to `GET`.
1484
+ */
1485
+ constructor(match, handler, method = "GET") {
1486
+ if (process.env.NODE_ENV !== "production") {
1487
+ finalAssertExports.isType(match, "function", {
1488
+ moduleName: "serwist",
1489
+ className: "Route",
1490
+ funcName: "constructor",
1491
+ paramName: "match"
1492
+ });
1493
+ if (method) finalAssertExports.isOneOf(method, validMethods, { paramName: "method" });
1494
+ }
1495
+ this.handler = normalizeHandler(handler);
1496
+ this.match = match;
1497
+ this.method = method;
1498
+ }
1499
+ /**
1500
+ *
1501
+ * @param handler A callback function that returns a Promise resolving
1502
+ * to a Response.
1503
+ */
1504
+ setCatchHandler(handler) {
1505
+ this.catchHandler = normalizeHandler(handler);
1506
+ }
1507
+ };
1508
+ //#endregion
1509
+ //#region src/lib/strategies/PrecacheStrategy.ts
1510
+ /**
1511
+ * A {@linkcode Strategy} implementation specifically designed to both cache
1512
+ * and fetch precached assets.
1513
+ *
1514
+ * Note: an instance of this class is created automatically when creating a
1515
+ * {@linkcode Serwist} instance; it's generally not necessary to create this yourself.
1516
+ */
1517
+ var PrecacheStrategy = class PrecacheStrategy extends Strategy {
1518
+ _fallbackToNetwork;
1519
+ static defaultPrecacheCacheabilityPlugin = { async cacheWillUpdate({ response }) {
1520
+ if (!response || response.status >= 400) return null;
1521
+ return response;
1522
+ } };
1523
+ static copyRedirectedCacheableResponsesPlugin = { async cacheWillUpdate({ response }) {
1524
+ return response.redirected ? await copyResponse(response) : response;
1525
+ } };
1526
+ /**
1527
+ * @param options
1528
+ */
1529
+ constructor(options = {}) {
1530
+ options.cacheName = cacheNames.getPrecacheName(options.cacheName);
1531
+ super(options);
1532
+ this._fallbackToNetwork = options.fallbackToNetwork !== false;
1533
+ this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);
1534
+ }
1535
+ /**
1536
+ * @private
1537
+ * @param request A request to run this strategy for.
1538
+ * @param handler The event that triggered the request.
1539
+ * @returns
1540
+ */
1541
+ async _handle(request, handler) {
1542
+ const preloadResponse = await handler.getPreloadResponse();
1543
+ if (preloadResponse) return preloadResponse;
1544
+ const response = await handler.cacheMatch(request);
1545
+ if (response) return response;
1546
+ if (handler.event && handler.event.type === "install") return await this._handleInstall(request, handler);
1547
+ return await this._handleFetch(request, handler);
1548
+ }
1549
+ async _handleFetch(request, handler) {
1550
+ let response;
1551
+ const params = handler.params || {};
1552
+ if (this._fallbackToNetwork) {
1553
+ if (process.env.NODE_ENV !== "production") logger.warn(`The precached response for ${getFriendlyURL(request.url)} in ${this.cacheName} was not found. Falling back to the network.`);
1554
+ const integrityInManifest = params.integrity;
1555
+ const integrityInRequest = request.integrity;
1556
+ const noIntegrityConflict = !integrityInRequest || integrityInRequest === integrityInManifest;
1557
+ response = await handler.fetch(new Request(request, { integrity: request.mode !== "no-cors" ? integrityInRequest || integrityInManifest : void 0 }));
1558
+ if (integrityInManifest && noIntegrityConflict && request.mode !== "no-cors") {
1559
+ this._useDefaultCacheabilityPluginIfNeeded();
1560
+ const wasCached = await handler.cachePut(request, response.clone());
1561
+ if (process.env.NODE_ENV !== "production") {
1562
+ if (wasCached) logger.log(`A response for ${getFriendlyURL(request.url)} was used to "repair" the precache.`);
1563
+ }
1564
+ }
1565
+ } else throw new SerwistError("missing-precache-entry", {
1566
+ cacheName: this.cacheName,
1567
+ url: request.url
1568
+ });
1569
+ if (process.env.NODE_ENV !== "production") {
1570
+ const cacheKey = params.cacheKey || await handler.getCacheKey(request, "read");
1571
+ logger.groupCollapsed(`Precaching is responding to: ${getFriendlyURL(request.url)}`);
1572
+ logger.log(`Serving the precached url: ${getFriendlyURL(cacheKey instanceof Request ? cacheKey.url : cacheKey)}`);
1573
+ logger.groupCollapsed("View request details here.");
1574
+ logger.log(request);
1575
+ logger.groupEnd();
1576
+ logger.groupCollapsed("View response details here.");
1577
+ logger.log(response);
1578
+ logger.groupEnd();
1579
+ logger.groupEnd();
1580
+ }
1581
+ return response;
1582
+ }
1583
+ async _handleInstall(request, handler) {
1584
+ this._useDefaultCacheabilityPluginIfNeeded();
1585
+ const response = await handler.fetch(request);
1586
+ if (!await handler.cachePut(request, response.clone())) throw new SerwistError("bad-precaching-response", {
1587
+ url: request.url,
1588
+ status: response.status
1589
+ });
1590
+ return response;
1591
+ }
1592
+ /**
1593
+ * This method is complex, as there a number of things to account for:
1594
+ *
1595
+ * The `plugins` array can be set at construction, and/or it might be added to
1596
+ * to at any time before the strategy is used.
1597
+ *
1598
+ * At the time the strategy is used (i.e. during an `install` event), there
1599
+ * needs to be at least one plugin that implements `cacheWillUpdate` in the
1600
+ * array, other than `copyRedirectedCacheableResponsesPlugin`.
1601
+ *
1602
+ * - If this method is called and there are no suitable `cacheWillUpdate`
1603
+ * plugins, we need to add `defaultPrecacheCacheabilityPlugin`.
1604
+ *
1605
+ * - If this method is called and there is exactly one `cacheWillUpdate`, then
1606
+ * we don't have to do anything (this might be a previously added
1607
+ * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin).
1608
+ *
1609
+ * - If this method is called and there is more than one `cacheWillUpdate`,
1610
+ * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so,
1611
+ * we need to remove it. (This situation is unlikely, but it could happen if
1612
+ * the strategy is used multiple times, the first without a `cacheWillUpdate`,
1613
+ * and then later on after manually adding a custom `cacheWillUpdate`.)
1614
+ *
1615
+ * See https://github.com/GoogleChrome/workbox/issues/2737 for more context.
1616
+ *
1617
+ * @private
1618
+ */
1619
+ _useDefaultCacheabilityPluginIfNeeded() {
1620
+ let defaultPluginIndex = null;
1621
+ let cacheWillUpdatePluginCount = 0;
1622
+ for (const [index, plugin] of this.plugins.entries()) {
1623
+ if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) continue;
1624
+ if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) defaultPluginIndex = index;
1625
+ if (plugin.cacheWillUpdate) cacheWillUpdatePluginCount++;
1626
+ }
1627
+ if (cacheWillUpdatePluginCount === 0) this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin);
1628
+ else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) this.plugins.splice(defaultPluginIndex, 1);
1629
+ }
1630
+ };
1631
+ //#endregion
1632
+ //#region src/NavigationRoute.ts
1633
+ /**
1634
+ * A class that makes it easy to create a {@linkcode Route} object that matches navigation requests.
1635
+ *
1636
+ * It will only match incoming requests whose [`mode`](https://fetch.spec.whatwg.org/#concept-request-mode) is set to `"navigate"`.
1637
+ *
1638
+ * You can optionally only apply this route to a subset of navigation requests
1639
+ * by using one or both of the `denylist` and `allowlist` parameters.
1640
+ */
1641
+ var NavigationRoute = class extends Route {
1642
+ _allowlist;
1643
+ _denylist;
1644
+ /**
1645
+ * If both `denylist` and `allowlist` are provided, `denylist` will
1646
+ * take precedence.
1647
+ *
1648
+ * The regular expressions in `allowlist` and `denylist`
1649
+ * are matched against the concatenated
1650
+ * [`pathname`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname)
1651
+ * and [`search`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search)
1652
+ * portions of the requested URL.
1653
+ *
1654
+ * *Note*: These RegExps may be evaluated against every destination URL during
1655
+ * a navigation. Avoid using
1656
+ * [complex RegExps](https://github.com/GoogleChrome/workbox/issues/3077),
1657
+ * or else your users may see delays when navigating your site.
1658
+ *
1659
+ * @param handler A callback function that returns a `Promise` resulting in a `Response`.
1660
+ * @param options
1661
+ */
1662
+ constructor(handler, { allowlist = [/./], denylist = [] } = {}) {
1663
+ if (process.env.NODE_ENV !== "production") {
1664
+ finalAssertExports.isArrayOfClass(allowlist, RegExp, {
1665
+ moduleName: "serwist",
1666
+ className: "NavigationRoute",
1667
+ funcName: "constructor",
1668
+ paramName: "options.allowlist"
1669
+ });
1670
+ finalAssertExports.isArrayOfClass(denylist, RegExp, {
1671
+ moduleName: "serwist",
1672
+ className: "NavigationRoute",
1673
+ funcName: "constructor",
1674
+ paramName: "options.denylist"
1675
+ });
1676
+ }
1677
+ super((options) => this._match(options), handler);
1678
+ this._allowlist = allowlist;
1679
+ this._denylist = denylist;
1680
+ }
1681
+ /**
1682
+ * Routes match handler.
1683
+ *
1684
+ * @param options
1685
+ * @returns
1686
+ * @private
1687
+ */
1688
+ _match({ url, request }) {
1689
+ if (request && request.mode !== "navigate") return false;
1690
+ const pathnameAndSearch = url.pathname + url.search;
1691
+ for (const regExp of this._denylist) if (regExp.test(pathnameAndSearch)) {
1692
+ if (process.env.NODE_ENV !== "production") logger.log(`The navigation route ${pathnameAndSearch} is not being used, since the URL matches this denylist pattern: ${regExp.toString()}`);
1693
+ return false;
1694
+ }
1695
+ if (this._allowlist.some((regExp) => regExp.test(pathnameAndSearch))) {
1696
+ if (process.env.NODE_ENV !== "production") logger.debug(`The navigation route ${pathnameAndSearch} is being used.`);
1697
+ return true;
1698
+ }
1699
+ if (process.env.NODE_ENV !== "production") logger.log(`The navigation route ${pathnameAndSearch} is not being used, since the URL being navigated to doesn't match the allowlist.`);
1700
+ return false;
1701
+ }
1702
+ };
1703
+ //#endregion
1704
+ //#region src/navigationPreload.ts
1705
+ /**
1706
+ * Checks whether the current browser supports
1707
+ * navigation preloading.
1708
+ *
1709
+ * @returns
1710
+ * @see https://serwist.pages.dev/docs/serwist/core/is-navigation-preload-supported
1711
+ */
1712
+ const isNavigationPreloadSupported = () => {
1713
+ return Boolean(self.registration?.navigationPreload);
1714
+ };
1715
+ /**
1716
+ * If the browser supports navigation preloading, then this will enable it.
1717
+ *
1718
+ * @param headerValue Optional. Allows developers to override the value of
1719
+ * the `Service-Worker-Navigation-Preload` header which will be sent to the
1720
+ * server when making the navigation request.
1721
+ * @see https://serwist.pages.dev/docs/serwist/core/enable-navigation-preload
1722
+ */
1723
+ const enableNavigationPreload = (headerValue) => {
1724
+ if (isNavigationPreloadSupported()) self.addEventListener("activate", (event) => {
1725
+ event.waitUntil(self.registration.navigationPreload.enable().then(() => {
1726
+ if (headerValue) self.registration.navigationPreload.setHeaderValue(headerValue);
1727
+ if (process.env.NODE_ENV !== "production") logger.log("Navigation preloading is enabled.");
1728
+ }));
1729
+ });
1730
+ else if (process.env.NODE_ENV !== "production") logger.log("Navigation preloading is not supported in this browser.");
1731
+ };
1732
+ /**
1733
+ * If the browser supports navigation preloading, then this will disable it.
1734
+ *
1735
+ * @see https://serwist.pages.dev/docs/serwist/core/disable-navigation-preload
1736
+ */
1737
+ const disableNavigationPreload = () => {
1738
+ if (isNavigationPreloadSupported()) self.addEventListener("activate", (event) => {
1739
+ event.waitUntil(self.registration.navigationPreload.disable().then(() => {
1740
+ if (process.env.NODE_ENV !== "production") logger.log("Navigation preloading is disabled.");
1741
+ }));
1742
+ });
1743
+ else if (process.env.NODE_ENV !== "production") logger.log("Navigation preloading is not supported in this browser.");
1744
+ };
1745
+ //#endregion
1746
+ //#region src/utils/removeIgnoredSearchParams.ts
1747
+ /**
1748
+ * Removes any URL search parameters that should be ignored.
1749
+ *
1750
+ * @param urlObject The original URL.
1751
+ * @param ignoreURLParametersMatching RegExps to test against
1752
+ * each search parameter name. Matches mean that the search parameter should be
1753
+ * ignored.
1754
+ * @returns The URL with any ignored search parameters removed.
1755
+ * @private
1756
+ */
1757
+ const removeIgnoredSearchParams = (urlObject, ignoreURLParametersMatching = []) => {
1758
+ for (const paramName of [...urlObject.searchParams.keys()]) if (ignoreURLParametersMatching.some((regExp) => regExp.test(paramName))) urlObject.searchParams.delete(paramName);
1759
+ return urlObject;
1760
+ };
1761
+ //#endregion
1762
+ //#region src/utils/generateURLVariations.ts
1763
+ /**
1764
+ * Generator function that yields possible variations on the original URL to
1765
+ * check, one at a time.
1766
+ *
1767
+ * @param url
1768
+ * @param options
1769
+ *
1770
+ * @private
1771
+ */
1772
+ function* generateURLVariations(url, { directoryIndex = "index.html", ignoreURLParametersMatching = [/^utm_/, /^fbclid$/], cleanURLs = true, urlManipulation } = {}) {
1773
+ const urlObject = new URL(url, location.href);
1774
+ urlObject.hash = "";
1775
+ yield urlObject.href;
1776
+ const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching);
1777
+ yield urlWithoutIgnoredParams.href;
1778
+ if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith("/")) {
1779
+ const directoryURL = new URL(urlWithoutIgnoredParams.href);
1780
+ directoryURL.pathname += directoryIndex;
1781
+ yield directoryURL.href;
1782
+ }
1783
+ if (cleanURLs) {
1784
+ const cleanURL = new URL(urlWithoutIgnoredParams.href);
1785
+ cleanURL.pathname += ".html";
1786
+ yield cleanURL.href;
1787
+ }
1788
+ if (urlManipulation) {
1789
+ const additionalURLs = urlManipulation({ url: urlObject });
1790
+ for (const urlToAttempt of additionalURLs) yield urlToAttempt.href;
1791
+ }
1792
+ }
1793
+ //#endregion
1794
+ //#region src/RegExpRoute.ts
1795
+ /**
1796
+ * A class that makes it easy to create a {@linkcode Route} object with a regular expression.
1797
+ *
1798
+ * For same-origin requests the `RegExp` only needs to match part of the URL. For
1799
+ * requests against third-party servers, you must define a `RegExp` that matches
1800
+ * the start of the URL.
1801
+ */
1802
+ var RegExpRoute = class extends Route {
1803
+ /**
1804
+ * If the regular expression contains
1805
+ * [capture groups](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references),
1806
+ * the captured values will be passed to the `params` argument.
1807
+ *
1808
+ * @param regExp The regular expression to match against URLs.
1809
+ * @param handler A callback function that returns a `Promise` resulting in a `Response`.
1810
+ * @param method The HTTP method to match the {@linkcode Route} against. Defaults to `GET`.
1811
+ * against.
1812
+ */
1813
+ constructor(regExp, handler, method) {
1814
+ if (process.env.NODE_ENV !== "production") finalAssertExports.isInstance(regExp, RegExp, {
1815
+ moduleName: "serwist",
1816
+ className: "RegExpRoute",
1817
+ funcName: "constructor",
1818
+ paramName: "pattern"
1819
+ });
1820
+ const match = ({ url }) => {
1821
+ const result = regExp.exec(url.href);
1822
+ if (!result) return;
1823
+ if (url.origin !== location.origin && result.index !== 0) {
1824
+ if (process.env.NODE_ENV !== "production") 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.`);
1825
+ return;
1826
+ }
1827
+ return result.slice(1);
1828
+ };
1829
+ super(match, handler, method);
1830
+ }
1831
+ };
1832
+ //#endregion
1833
+ //#region src/setCacheNameDetails.ts
1834
+ /**
1835
+ * Modifies the default cache names used by Serwist packages.
1836
+ * Cache names are generated as `<prefix>-<Cache Name>-<suffix>`.
1837
+ *
1838
+ * @param details
1839
+ */
1840
+ const setCacheNameDetails = (details) => {
1841
+ if (process.env.NODE_ENV !== "production") {
1842
+ for (const key of Object.keys(details)) finalAssertExports.isType(details[key], "string", {
1843
+ moduleName: "@serwist/core",
1844
+ funcName: "setCacheNameDetails",
1845
+ paramName: `details.${key}`
1846
+ });
1847
+ if (details.precache?.length === 0) throw new SerwistError("invalid-cache-name", {
1848
+ cacheNameId: "precache",
1849
+ value: details.precache
1850
+ });
1851
+ if (details.runtime?.length === 0) throw new SerwistError("invalid-cache-name", {
1852
+ cacheNameId: "runtime",
1853
+ value: details.runtime
1854
+ });
1855
+ if (details.googleAnalytics?.length === 0) throw new SerwistError("invalid-cache-name", {
1856
+ cacheNameId: "googleAnalytics",
1857
+ value: details.googleAnalytics
1858
+ });
1859
+ }
1860
+ cacheNames.updateDetails(details);
1861
+ };
1862
+ //#endregion
1863
+ //#region src/utils/createCacheKey.ts
1864
+ const REVISION_SEARCH_PARAM = "__WB_REVISION__";
1865
+ /**
1866
+ * Converts a manifest entry into a versioned URL suitable for precaching.
1867
+ *
1868
+ * @param entry
1869
+ * @returns A URL with versioning info.
1870
+ *
1871
+ * @private
1872
+ */
1873
+ const createCacheKey = (entry) => {
1874
+ if (!entry) throw new SerwistError("add-to-cache-list-unexpected-type", { entry });
1875
+ if (typeof entry === "string") {
1876
+ const urlObject = new URL(entry, location.href);
1877
+ return {
1878
+ cacheKey: urlObject.href,
1879
+ url: urlObject.href
1880
+ };
1881
+ }
1882
+ const { revision, url } = entry;
1883
+ if (!url) throw new SerwistError("add-to-cache-list-unexpected-type", { entry });
1884
+ if (!revision) {
1885
+ const urlObject = new URL(url, location.href);
1886
+ return {
1887
+ cacheKey: urlObject.href,
1888
+ url: urlObject.href
1889
+ };
1890
+ }
1891
+ const cacheKeyURL = new URL(url, location.href);
1892
+ const originalURL = new URL(url, location.href);
1893
+ cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision);
1894
+ return {
1895
+ cacheKey: cacheKeyURL.href,
1896
+ url: originalURL.href
1897
+ };
1898
+ };
1899
+ //#endregion
1900
+ //#region src/utils/PrecacheInstallReportPlugin.ts
1901
+ /**
1902
+ * A plugin designed to determine the number of assets that were updated (or not updated)
1903
+ * during the `install` event.
1904
+ *
1905
+ * @private
1906
+ */
1907
+ var PrecacheInstallReportPlugin = class {
1908
+ updatedURLs = [];
1909
+ notUpdatedURLs = [];
1910
+ handlerWillStart = async ({ request, state }) => {
1911
+ if (state) state.originalRequest = request;
1912
+ };
1913
+ cachedResponseWillBeUsed = async ({ event, state, cachedResponse }) => {
1914
+ if (event.type === "install") {
1915
+ if (state?.originalRequest && state.originalRequest instanceof Request) {
1916
+ const url = state.originalRequest.url;
1917
+ if (cachedResponse) this.notUpdatedURLs.push(url);
1918
+ else this.updatedURLs.push(url);
1919
+ }
1920
+ }
1921
+ return cachedResponse;
1922
+ };
1923
+ };
1924
+ //#endregion
1925
+ //#region src/utils/parseRoute.ts
1926
+ /**
1927
+ * Parses a `RegExp`, string, or function with a caching strategy into a {@linkcode Route}. This is for
1928
+ * when you want to create a {@linkcode Route}, but you don't want to register it just yet: sometimes
1929
+ * you want to call {@linkcode Route.setCatchHandler} first, for example.
1930
+ *
1931
+ * @param capture If the capture param is a {@linkcode Route} object, all other arguments will be ignored.
1932
+ * @param handler A callback function that returns a promise resulting in a response.
1933
+ * This parameter is required if `capture` is not a {@linkcode Route} object.
1934
+ * @param method The HTTP method to match the route against. Defaults to `'GET'`.
1935
+ * @returns The generated {@linkcode Route}.
1936
+ */
1937
+ const parseRoute = (capture, handler, method) => {
1938
+ if (typeof capture === "string") {
1939
+ const captureUrl = new URL(capture, location.href);
1940
+ if (process.env.NODE_ENV !== "production") {
1941
+ if (!(capture.startsWith("/") || capture.startsWith("http"))) throw new SerwistError("invalid-string", {
1942
+ moduleName: "serwist",
1943
+ funcName: "parseRoute",
1944
+ paramName: "capture"
1945
+ });
1946
+ const valueToCheck = capture.startsWith("http") ? captureUrl.pathname : capture;
1947
+ const wildcards = "[*:?+]";
1948
+ if (new RegExp(`${wildcards}`).exec(valueToCheck)) 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.`);
1949
+ }
1950
+ const matchCallback = ({ url }) => {
1951
+ if (process.env.NODE_ENV !== "production") {
1952
+ if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) 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.`);
1953
+ }
1954
+ return url.href === captureUrl.href;
1955
+ };
1956
+ return new Route(matchCallback, handler, method);
1957
+ }
1958
+ if (capture instanceof RegExp) return new RegExpRoute(capture, handler, method);
1959
+ if (typeof capture === "function") return new Route(capture, handler, method);
1960
+ if (capture instanceof Route) return capture;
1961
+ throw new SerwistError("unsupported-route-type", {
1962
+ moduleName: "serwist",
1963
+ funcName: "parseRoute",
1964
+ paramName: "capture"
1965
+ });
1966
+ };
1967
+ //#endregion
1968
+ //#region src/utils/printCleanupDetails.ts
1969
+ /**
1970
+ * @param groupTitle
1971
+ * @param deletedURLs
1972
+ *
1973
+ * @private
1974
+ */
1975
+ const logGroup = (groupTitle, deletedURLs) => {
1976
+ logger.groupCollapsed(groupTitle);
1977
+ for (const url of deletedURLs) logger.log(url);
1978
+ logger.groupEnd();
1979
+ };
1980
+ /**
1981
+ * @param deletedURLs
1982
+ * @private
1983
+ */
1984
+ const printCleanupDetails = (deletedURLs) => {
1985
+ const deletionCount = deletedURLs.length;
1986
+ if (deletionCount > 0) {
1987
+ logger.groupCollapsed(`During precaching cleanup, ${deletionCount} cached request${deletionCount === 1 ? " was" : "s were"} deleted.`);
1988
+ logGroup("Deleted Cache Requests", deletedURLs);
1989
+ logger.groupEnd();
1990
+ }
1991
+ };
1992
+ //#endregion
1993
+ //#region src/utils/printInstallDetails.ts
1994
+ /**
1995
+ * @param groupTitle
1996
+ * @param urls
1997
+ *
1998
+ * @private
1999
+ */
2000
+ function _nestedGroup(groupTitle, urls) {
2001
+ if (urls.length === 0) return;
2002
+ logger.groupCollapsed(groupTitle);
2003
+ for (const url of urls) logger.log(url);
2004
+ logger.groupEnd();
2005
+ }
2006
+ /**
2007
+ * @param urlsToPrecache
2008
+ * @param urlsAlreadyPrecached
2009
+ * @private
2010
+ */
2011
+ const printInstallDetails = (urlsToPrecache, urlsAlreadyPrecached) => {
2012
+ const precachedCount = urlsToPrecache.length;
2013
+ const alreadyPrecachedCount = urlsAlreadyPrecached.length;
2014
+ if (precachedCount || alreadyPrecachedCount) {
2015
+ let message = `Precaching ${precachedCount} file${precachedCount === 1 ? "" : "s"}.`;
2016
+ if (alreadyPrecachedCount > 0) message += ` ${alreadyPrecachedCount} file${alreadyPrecachedCount === 1 ? " is" : "s are"} already cached.`;
2017
+ logger.groupCollapsed(message);
2018
+ _nestedGroup("View newly precached URLs.", urlsToPrecache);
2019
+ _nestedGroup("View previously precached URLs.", urlsAlreadyPrecached);
2020
+ logger.groupEnd();
2021
+ }
2022
+ };
2023
+ //#endregion
2024
+ export { BackgroundSyncQueue as C, copyResponse as D, disableDevLogs as E, BackgroundSyncPlugin as S, BackgroundSyncQueueStore as T, NetworkFirst as _, createCacheKey as a, StrategyHandler as b, generateURLVariations as c, isNavigationPreloadSupported as d, NavigationRoute as f, NetworkOnly as g, normalizeHandler as h, PrecacheInstallReportPlugin as i, disableNavigationPreload as l, Route as m, printCleanupDetails as n, setCacheNameDetails as o, PrecacheStrategy as p, parseRoute as r, RegExpRoute as s, printInstallDetails as t, enableNavigationPreload as u, messages as v, StorableRequest as w, cacheOkAndOpaquePlugin as x, Strategy as y };
2025
+
2026
+ //# sourceMappingURL=printInstallDetails-c9A08ZVZ.js.map