source: frontend/node_modules/workbox-precaching/src/PrecacheStrategy.ts

Last change on this file was 9af201e, checked in by MBK <marija.karapandzova@…>, 12 days ago

Fix frontend appearance

  • Property mode set to 100644
File size: 10.4 KB
Line 
1/*
2 Copyright 2020 Google LLC
3
4 Use of this source code is governed by an MIT-style
5 license that can be found in the LICENSE file or at
6 https://opensource.org/licenses/MIT.
7*/
8
9import {copyResponse} from 'workbox-core/copyResponse.js';
10import {cacheNames} from 'workbox-core/_private/cacheNames.js';
11import {getFriendlyURL} from 'workbox-core/_private/getFriendlyURL.js';
12import {logger} from 'workbox-core/_private/logger.js';
13import {WorkboxError} from 'workbox-core/_private/WorkboxError.js';
14import {WorkboxPlugin} from 'workbox-core/types.js';
15import {Strategy, StrategyOptions} from 'workbox-strategies/Strategy.js';
16import {StrategyHandler} from 'workbox-strategies/StrategyHandler.js';
17
18import './_version.js';
19
20interface PrecacheStrategyOptions extends StrategyOptions {
21 fallbackToNetwork?: boolean;
22}
23
24/**
25 * A {@link workbox-strategies.Strategy} implementation
26 * specifically designed to work with
27 * {@link workbox-precaching.PrecacheController}
28 * to both cache and fetch precached assets.
29 *
30 * Note: an instance of this class is created automatically when creating a
31 * `PrecacheController`; it's generally not necessary to create this yourself.
32 *
33 * @extends workbox-strategies.Strategy
34 * @memberof workbox-precaching
35 */
36class PrecacheStrategy extends Strategy {
37 private readonly _fallbackToNetwork: boolean;
38
39 static readonly defaultPrecacheCacheabilityPlugin: WorkboxPlugin = {
40 async cacheWillUpdate({response}) {
41 if (!response || response.status >= 400) {
42 return null;
43 }
44
45 return response;
46 },
47 };
48
49 static readonly copyRedirectedCacheableResponsesPlugin: WorkboxPlugin = {
50 async cacheWillUpdate({response}) {
51 return response.redirected ? await copyResponse(response) : response;
52 },
53 };
54
55 /**
56 *
57 * @param {Object} [options]
58 * @param {string} [options.cacheName] Cache name to store and retrieve
59 * requests. Defaults to the cache names provided by
60 * {@link workbox-core.cacheNames}.
61 * @param {Array<Object>} [options.plugins] {@link https://developers.google.com/web/tools/workbox/guides/using-plugins|Plugins}
62 * to use in conjunction with this caching strategy.
63 * @param {Object} [options.fetchOptions] Values passed along to the
64 * {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters|init}
65 * of all fetch() requests made by this strategy.
66 * @param {Object} [options.matchOptions] The
67 * {@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions|CacheQueryOptions}
68 * for any `cache.match()` or `cache.put()` calls made by this strategy.
69 * @param {boolean} [options.fallbackToNetwork=true] Whether to attempt to
70 * get the response from the network if there's a precache miss.
71 */
72 constructor(options: PrecacheStrategyOptions = {}) {
73 options.cacheName = cacheNames.getPrecacheName(options.cacheName);
74 super(options);
75
76 this._fallbackToNetwork =
77 options.fallbackToNetwork === false ? false : true;
78
79 // Redirected responses cannot be used to satisfy a navigation request, so
80 // any redirected response must be "copied" rather than cloned, so the new
81 // response doesn't contain the `redirected` flag. See:
82 // https://bugs.chromium.org/p/chromium/issues/detail?id=669363&desc=2#c1
83 this.plugins.push(PrecacheStrategy.copyRedirectedCacheableResponsesPlugin);
84 }
85
86 /**
87 * @private
88 * @param {Request|string} request A request to run this strategy for.
89 * @param {workbox-strategies.StrategyHandler} handler The event that
90 * triggered the request.
91 * @return {Promise<Response>}
92 */
93 async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
94 const response = await handler.cacheMatch(request);
95 if (response) {
96 return response;
97 }
98
99 // If this is an `install` event for an entry that isn't already cached,
100 // then populate the cache.
101 if (handler.event && handler.event.type === 'install') {
102 return await this._handleInstall(request, handler);
103 }
104
105 // Getting here means something went wrong. An entry that should have been
106 // precached wasn't found in the cache.
107 return await this._handleFetch(request, handler);
108 }
109
110 async _handleFetch(
111 request: Request,
112 handler: StrategyHandler,
113 ): Promise<Response> {
114 let response;
115 const params = (handler.params || {}) as {
116 cacheKey?: string;
117 integrity?: string;
118 };
119
120 // Fall back to the network if we're configured to do so.
121 if (this._fallbackToNetwork) {
122 if (process.env.NODE_ENV !== 'production') {
123 logger.warn(
124 `The precached response for ` +
125 `${getFriendlyURL(request.url)} in ${this.cacheName} was not ` +
126 `found. Falling back to the network.`,
127 );
128 }
129
130 const integrityInManifest = params.integrity;
131 const integrityInRequest = request.integrity;
132 const noIntegrityConflict =
133 !integrityInRequest || integrityInRequest === integrityInManifest;
134
135 // Do not add integrity if the original request is no-cors
136 // See https://github.com/GoogleChrome/workbox/issues/3096
137 response = await handler.fetch(
138 new Request(request, {
139 integrity:
140 request.mode !== 'no-cors'
141 ? integrityInRequest || integrityInManifest
142 : undefined,
143 }),
144 );
145
146 // It's only "safe" to repair the cache if we're using SRI to guarantee
147 // that the response matches the precache manifest's expectations,
148 // and there's either a) no integrity property in the incoming request
149 // or b) there is an integrity, and it matches the precache manifest.
150 // See https://github.com/GoogleChrome/workbox/issues/2858
151 // Also if the original request users no-cors we don't use integrity.
152 // See https://github.com/GoogleChrome/workbox/issues/3096
153 if (
154 integrityInManifest &&
155 noIntegrityConflict &&
156 request.mode !== 'no-cors'
157 ) {
158 this._useDefaultCacheabilityPluginIfNeeded();
159 const wasCached = await handler.cachePut(request, response.clone());
160 if (process.env.NODE_ENV !== 'production') {
161 if (wasCached) {
162 logger.log(
163 `A response for ${getFriendlyURL(request.url)} ` +
164 `was used to "repair" the precache.`,
165 );
166 }
167 }
168 }
169 } else {
170 // This shouldn't normally happen, but there are edge cases:
171 // https://github.com/GoogleChrome/workbox/issues/1441
172 throw new WorkboxError('missing-precache-entry', {
173 cacheName: this.cacheName,
174 url: request.url,
175 });
176 }
177
178 if (process.env.NODE_ENV !== 'production') {
179 const cacheKey =
180 params.cacheKey || (await handler.getCacheKey(request, 'read'));
181
182 // Workbox is going to handle the route.
183 // print the routing details to the console.
184 logger.groupCollapsed(
185 `Precaching is responding to: ` + getFriendlyURL(request.url),
186 );
187 logger.log(
188 `Serving the precached url: ${getFriendlyURL(
189 cacheKey instanceof Request ? cacheKey.url : cacheKey,
190 )}`,
191 );
192
193 logger.groupCollapsed(`View request details here.`);
194 logger.log(request);
195 logger.groupEnd();
196
197 logger.groupCollapsed(`View response details here.`);
198 logger.log(response);
199 logger.groupEnd();
200
201 logger.groupEnd();
202 }
203
204 return response;
205 }
206
207 async _handleInstall(
208 request: Request,
209 handler: StrategyHandler,
210 ): Promise<Response> {
211 this._useDefaultCacheabilityPluginIfNeeded();
212
213 const response = await handler.fetch(request);
214
215 // Make sure we defer cachePut() until after we know the response
216 // should be cached; see https://github.com/GoogleChrome/workbox/issues/2737
217 const wasCached = await handler.cachePut(request, response.clone());
218 if (!wasCached) {
219 // Throwing here will lead to the `install` handler failing, which
220 // we want to do if *any* of the responses aren't safe to cache.
221 throw new WorkboxError('bad-precaching-response', {
222 url: request.url,
223 status: response.status,
224 });
225 }
226
227 return response;
228 }
229
230 /**
231 * This method is complex, as there a number of things to account for:
232 *
233 * The `plugins` array can be set at construction, and/or it might be added to
234 * to at any time before the strategy is used.
235 *
236 * At the time the strategy is used (i.e. during an `install` event), there
237 * needs to be at least one plugin that implements `cacheWillUpdate` in the
238 * array, other than `copyRedirectedCacheableResponsesPlugin`.
239 *
240 * - If this method is called and there are no suitable `cacheWillUpdate`
241 * plugins, we need to add `defaultPrecacheCacheabilityPlugin`.
242 *
243 * - If this method is called and there is exactly one `cacheWillUpdate`, then
244 * we don't have to do anything (this might be a previously added
245 * `defaultPrecacheCacheabilityPlugin`, or it might be a custom plugin).
246 *
247 * - If this method is called and there is more than one `cacheWillUpdate`,
248 * then we need to check if one is `defaultPrecacheCacheabilityPlugin`. If so,
249 * we need to remove it. (This situation is unlikely, but it could happen if
250 * the strategy is used multiple times, the first without a `cacheWillUpdate`,
251 * and then later on after manually adding a custom `cacheWillUpdate`.)
252 *
253 * See https://github.com/GoogleChrome/workbox/issues/2737 for more context.
254 *
255 * @private
256 */
257 _useDefaultCacheabilityPluginIfNeeded(): void {
258 let defaultPluginIndex: number | null = null;
259 let cacheWillUpdatePluginCount = 0;
260
261 for (const [index, plugin] of this.plugins.entries()) {
262 // Ignore the copy redirected plugin when determining what to do.
263 if (plugin === PrecacheStrategy.copyRedirectedCacheableResponsesPlugin) {
264 continue;
265 }
266
267 // Save the default plugin's index, in case it needs to be removed.
268 if (plugin === PrecacheStrategy.defaultPrecacheCacheabilityPlugin) {
269 defaultPluginIndex = index;
270 }
271
272 if (plugin.cacheWillUpdate) {
273 cacheWillUpdatePluginCount++;
274 }
275 }
276
277 if (cacheWillUpdatePluginCount === 0) {
278 this.plugins.push(PrecacheStrategy.defaultPrecacheCacheabilityPlugin);
279 } else if (cacheWillUpdatePluginCount > 1 && defaultPluginIndex !== null) {
280 // Only remove the default plugin; multiple custom plugins are allowed.
281 this.plugins.splice(defaultPluginIndex, 1);
282 }
283 // Nothing needs to be done if cacheWillUpdatePluginCount is 1
284 }
285}
286
287export {PrecacheStrategy};
Note: See TracBrowser for help on using the repository browser.