source: frontend/node_modules/workbox-strategies/src/NetworkFirst.ts

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

Fix frontend appearance

  • Property mode set to 100644
File size: 7.9 KB
Line 
1/*
2 Copyright 2018 Google LLC
3
4 Use of this source code is governed by an MIT-style
5 license that can be found in the LICENSE file or at
6 https://opensource.org/licenses/MIT.
7*/
8
9import {assert} from 'workbox-core/_private/assert.js';
10import {logger} from 'workbox-core/_private/logger.js';
11import {WorkboxError} from 'workbox-core/_private/WorkboxError.js';
12
13import {cacheOkAndOpaquePlugin} from './plugins/cacheOkAndOpaquePlugin.js';
14import {Strategy, StrategyOptions} from './Strategy.js';
15import {StrategyHandler} from './StrategyHandler.js';
16import {messages} from './utils/messages.js';
17import './_version.js';
18
19export interface NetworkFirstOptions extends StrategyOptions {
20 networkTimeoutSeconds?: number;
21}
22
23/**
24 * An implementation of a
25 * [network first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-first-falling-back-to-cache)
26 * request strategy.
27 *
28 * By default, this strategy will cache responses with a 200 status code as
29 * well as [opaque responses](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
30 * Opaque responses are are cross-origin requests where the response doesn't
31 * support [CORS](https://enable-cors.org/).
32 *
33 * If the network request fails, and there is no cache match, this will throw
34 * a `WorkboxError` exception.
35 *
36 * @extends workbox-strategies.Strategy
37 * @memberof workbox-strategies
38 */
39class NetworkFirst extends Strategy {
40 private readonly _networkTimeoutSeconds: number;
41
42 /**
43 * @param {Object} [options]
44 * @param {string} [options.cacheName] Cache name to store and retrieve
45 * requests. Defaults to cache names provided by
46 * {@link workbox-core.cacheNames}.
47 * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
48 * to use in conjunction with this caching strategy.
49 * @param {Object} [options.fetchOptions] Values passed along to the
50 * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
51 * of [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
52 * `fetch()` requests made by this strategy.
53 * @param {Object} [options.matchOptions] [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions)
54 * @param {number} [options.networkTimeoutSeconds] If set, any network requests
55 * that fail to respond within the timeout will fallback to the cache.
56 *
57 * This option can be used to combat
58 * "[lie-fi]{@link https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi}"
59 * scenarios.
60 */
61 constructor(options: NetworkFirstOptions = {}) {
62 super(options);
63
64 // If this instance contains no plugins with a 'cacheWillUpdate' callback,
65 // prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.
66 if (!this.plugins.some((p) => 'cacheWillUpdate' in p)) {
67 this.plugins.unshift(cacheOkAndOpaquePlugin);
68 }
69
70 this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
71 if (process.env.NODE_ENV !== 'production') {
72 if (this._networkTimeoutSeconds) {
73 assert!.isType(this._networkTimeoutSeconds, 'number', {
74 moduleName: 'workbox-strategies',
75 className: this.constructor.name,
76 funcName: 'constructor',
77 paramName: 'networkTimeoutSeconds',
78 });
79 }
80 }
81 }
82
83 /**
84 * @private
85 * @param {Request|string} request A request to run this strategy for.
86 * @param {workbox-strategies.StrategyHandler} handler The event that
87 * triggered the request.
88 * @return {Promise<Response>}
89 */
90 async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
91 const logs: any[] = [];
92
93 if (process.env.NODE_ENV !== 'production') {
94 assert!.isInstance(request, Request, {
95 moduleName: 'workbox-strategies',
96 className: this.constructor.name,
97 funcName: 'handle',
98 paramName: 'makeRequest',
99 });
100 }
101
102 const promises: Promise<Response | undefined>[] = [];
103 let timeoutId: number | undefined;
104
105 if (this._networkTimeoutSeconds) {
106 const {id, promise} = this._getTimeoutPromise({request, logs, handler});
107 timeoutId = id;
108 promises.push(promise);
109 }
110
111 const networkPromise = this._getNetworkPromise({
112 timeoutId,
113 request,
114 logs,
115 handler,
116 });
117
118 promises.push(networkPromise);
119
120 const response = await handler.waitUntil(
121 (async () => {
122 // Promise.race() will resolve as soon as the first promise resolves.
123 return (
124 (await handler.waitUntil(Promise.race(promises))) ||
125 // If Promise.race() resolved with null, it might be due to a network
126 // timeout + a cache miss. If that were to happen, we'd rather wait until
127 // the networkPromise resolves instead of returning null.
128 // Note that it's fine to await an already-resolved promise, so we don't
129 // have to check to see if it's still "in flight".
130 (await networkPromise)
131 );
132 })(),
133 );
134
135 if (process.env.NODE_ENV !== 'production') {
136 logger.groupCollapsed(
137 messages.strategyStart(this.constructor.name, request),
138 );
139 for (const log of logs) {
140 logger.log(log);
141 }
142 messages.printFinalResponse(response);
143 logger.groupEnd();
144 }
145
146 if (!response) {
147 throw new WorkboxError('no-response', {url: request.url});
148 }
149 return response;
150 }
151
152 /**
153 * @param {Object} options
154 * @param {Request} options.request
155 * @param {Array} options.logs A reference to the logs array
156 * @param {Event} options.event
157 * @return {Promise<Response>}
158 *
159 * @private
160 */
161 private _getTimeoutPromise({
162 request,
163 logs,
164 handler,
165 }: {
166 request: Request;
167 logs: any[];
168 handler: StrategyHandler;
169 }): {promise: Promise<Response | undefined>; id?: number} {
170 let timeoutId;
171 const timeoutPromise: Promise<Response | undefined> = new Promise(
172 (resolve) => {
173 const onNetworkTimeout = async () => {
174 if (process.env.NODE_ENV !== 'production') {
175 logs.push(
176 `Timing out the network response at ` +
177 `${this._networkTimeoutSeconds} seconds.`,
178 );
179 }
180 resolve(await handler.cacheMatch(request));
181 };
182 timeoutId = setTimeout(
183 onNetworkTimeout,
184 this._networkTimeoutSeconds * 1000,
185 );
186 },
187 );
188
189 return {
190 promise: timeoutPromise,
191 id: timeoutId,
192 };
193 }
194
195 /**
196 * @param {Object} options
197 * @param {number|undefined} options.timeoutId
198 * @param {Request} options.request
199 * @param {Array} options.logs A reference to the logs Array.
200 * @param {Event} options.event
201 * @return {Promise<Response>}
202 *
203 * @private
204 */
205 async _getNetworkPromise({
206 timeoutId,
207 request,
208 logs,
209 handler,
210 }: {
211 request: Request;
212 logs: any[];
213 timeoutId?: number;
214 handler: StrategyHandler;
215 }): Promise<Response | undefined> {
216 let error;
217 let response;
218 try {
219 response = await handler.fetchAndCachePut(request);
220 } catch (fetchError) {
221 if (fetchError instanceof Error) {
222 error = fetchError;
223 }
224 }
225
226 if (timeoutId) {
227 clearTimeout(timeoutId);
228 }
229
230 if (process.env.NODE_ENV !== 'production') {
231 if (response) {
232 logs.push(`Got response from network.`);
233 } else {
234 logs.push(
235 `Unable to get a response from the network. Will respond ` +
236 `with a cached response.`,
237 );
238 }
239 }
240
241 if (error || !response) {
242 response = await handler.cacheMatch(request);
243
244 if (process.env.NODE_ENV !== 'production') {
245 if (response) {
246 logs.push(
247 `Found a cached response in the '${this.cacheName}'` + ` cache.`,
248 );
249 } else {
250 logs.push(`No response found in the '${this.cacheName}' cache.`);
251 }
252 }
253 }
254
255 return response;
256 }
257}
258
259export {NetworkFirst};
Note: See TracBrowser for help on using the repository browser.