workbox-strategies.dev.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  1. this.workbox = this.workbox || {};
  2. this.workbox.strategies = (function (exports, assert_js, cacheNames_js, cacheWrapper_js, fetchWrapper_js, getFriendlyURL_js, logger_js, WorkboxError_js) {
  3. 'use strict';
  4. try {
  5. self['workbox:strategies:5.1.4'] && _();
  6. } catch (e) {}
  7. /*
  8. Copyright 2018 Google LLC
  9. Use of this source code is governed by an MIT-style
  10. license that can be found in the LICENSE file or at
  11. https://opensource.org/licenses/MIT.
  12. */
  13. const messages = {
  14. strategyStart: (strategyName, request) => `Using ${strategyName} to respond to '${getFriendlyURL_js.getFriendlyURL(request.url)}'`,
  15. printFinalResponse: response => {
  16. if (response) {
  17. logger_js.logger.groupCollapsed(`View the final response here.`);
  18. logger_js.logger.log(response || '[No response returned]');
  19. logger_js.logger.groupEnd();
  20. }
  21. }
  22. };
  23. /*
  24. Copyright 2018 Google LLC
  25. Use of this source code is governed by an MIT-style
  26. license that can be found in the LICENSE file or at
  27. https://opensource.org/licenses/MIT.
  28. */
  29. /**
  30. * An implementation of a [cache-first]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#cache-falling-back-to-network}
  31. * request strategy.
  32. *
  33. * A cache first strategy is useful for assets that have been revisioned,
  34. * such as URLs like `/styles/example.a8f5f1.css`, since they
  35. * can be cached for long periods of time.
  36. *
  37. * If the network request fails, and there is no cache match, this will throw
  38. * a `WorkboxError` exception.
  39. *
  40. * @memberof module:workbox-strategies
  41. */
  42. class CacheFirst {
  43. /**
  44. * @param {Object} options
  45. * @param {string} options.cacheName Cache name to store and retrieve
  46. * requests. Defaults to cache names provided by
  47. * [workbox-core]{@link module:workbox-core.cacheNames}.
  48. * @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
  49. * to use in conjunction with this caching strategy.
  50. * @param {Object} options.fetchOptions Values passed along to the
  51. * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
  52. * of all fetch() requests made by this strategy.
  53. * @param {Object} options.matchOptions [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions)
  54. */
  55. constructor(options = {}) {
  56. this._cacheName = cacheNames_js.cacheNames.getRuntimeName(options.cacheName);
  57. this._plugins = options.plugins || [];
  58. this._fetchOptions = options.fetchOptions;
  59. this._matchOptions = options.matchOptions;
  60. }
  61. /**
  62. * This method will perform a request strategy and follows an API that
  63. * will work with the
  64. * [Workbox Router]{@link module:workbox-routing.Router}.
  65. *
  66. * @param {Object} options
  67. * @param {Request|string} options.request A request to run this strategy for.
  68. * @param {Event} [options.event] The event that triggered the request.
  69. * @return {Promise<Response>}
  70. */
  71. async handle({
  72. event,
  73. request
  74. }) {
  75. const logs = [];
  76. if (typeof request === 'string') {
  77. request = new Request(request);
  78. }
  79. {
  80. assert_js.assert.isInstance(request, Request, {
  81. moduleName: 'workbox-strategies',
  82. className: 'CacheFirst',
  83. funcName: 'makeRequest',
  84. paramName: 'request'
  85. });
  86. }
  87. let response = await cacheWrapper_js.cacheWrapper.match({
  88. cacheName: this._cacheName,
  89. request,
  90. event,
  91. matchOptions: this._matchOptions,
  92. plugins: this._plugins
  93. });
  94. let error;
  95. if (!response) {
  96. {
  97. logs.push(`No response found in the '${this._cacheName}' cache. ` + `Will respond with a network request.`);
  98. }
  99. try {
  100. response = await this._getFromNetwork(request, event);
  101. } catch (err) {
  102. error = err;
  103. }
  104. {
  105. if (response) {
  106. logs.push(`Got response from network.`);
  107. } else {
  108. logs.push(`Unable to get a response from the network.`);
  109. }
  110. }
  111. } else {
  112. {
  113. logs.push(`Found a cached response in the '${this._cacheName}' cache.`);
  114. }
  115. }
  116. {
  117. logger_js.logger.groupCollapsed(messages.strategyStart('CacheFirst', request));
  118. for (const log of logs) {
  119. logger_js.logger.log(log);
  120. }
  121. messages.printFinalResponse(response);
  122. logger_js.logger.groupEnd();
  123. }
  124. if (!response) {
  125. throw new WorkboxError_js.WorkboxError('no-response', {
  126. url: request.url,
  127. error
  128. });
  129. }
  130. return response;
  131. }
  132. /**
  133. * Handles the network and cache part of CacheFirst.
  134. *
  135. * @param {Request} request
  136. * @param {Event} [event]
  137. * @return {Promise<Response>}
  138. *
  139. * @private
  140. */
  141. async _getFromNetwork(request, event) {
  142. const response = await fetchWrapper_js.fetchWrapper.fetch({
  143. request,
  144. event,
  145. fetchOptions: this._fetchOptions,
  146. plugins: this._plugins
  147. }); // Keep the service worker while we put the request to the cache
  148. const responseClone = response.clone();
  149. const cachePutPromise = cacheWrapper_js.cacheWrapper.put({
  150. cacheName: this._cacheName,
  151. request,
  152. response: responseClone,
  153. event,
  154. plugins: this._plugins
  155. });
  156. if (event) {
  157. try {
  158. event.waitUntil(cachePutPromise);
  159. } catch (error) {
  160. {
  161. logger_js.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache for '${getFriendlyURL_js.getFriendlyURL(request.url)}'.`);
  162. }
  163. }
  164. }
  165. return response;
  166. }
  167. }
  168. /*
  169. Copyright 2018 Google LLC
  170. Use of this source code is governed by an MIT-style
  171. license that can be found in the LICENSE file or at
  172. https://opensource.org/licenses/MIT.
  173. */
  174. /**
  175. * An implementation of a
  176. * [cache-only]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#cache-only}
  177. * request strategy.
  178. *
  179. * This class is useful if you want to take advantage of any
  180. * [Workbox plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}.
  181. *
  182. * If there is no cache match, this will throw a `WorkboxError` exception.
  183. *
  184. * @memberof module:workbox-strategies
  185. */
  186. class CacheOnly {
  187. /**
  188. * @param {Object} options
  189. * @param {string} options.cacheName Cache name to store and retrieve
  190. * requests. Defaults to cache names provided by
  191. * [workbox-core]{@link module:workbox-core.cacheNames}.
  192. * @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
  193. * to use in conjunction with this caching strategy.
  194. * @param {Object} options.matchOptions [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions)
  195. */
  196. constructor(options = {}) {
  197. this._cacheName = cacheNames_js.cacheNames.getRuntimeName(options.cacheName);
  198. this._plugins = options.plugins || [];
  199. this._matchOptions = options.matchOptions;
  200. }
  201. /**
  202. * This method will perform a request strategy and follows an API that
  203. * will work with the
  204. * [Workbox Router]{@link module:workbox-routing.Router}.
  205. *
  206. * @param {Object} options
  207. * @param {Request|string} options.request A request to run this strategy for.
  208. * @param {Event} [options.event] The event that triggered the request.
  209. * @return {Promise<Response>}
  210. */
  211. async handle({
  212. event,
  213. request
  214. }) {
  215. if (typeof request === 'string') {
  216. request = new Request(request);
  217. }
  218. {
  219. assert_js.assert.isInstance(request, Request, {
  220. moduleName: 'workbox-strategies',
  221. className: 'CacheOnly',
  222. funcName: 'makeRequest',
  223. paramName: 'request'
  224. });
  225. }
  226. const response = await cacheWrapper_js.cacheWrapper.match({
  227. cacheName: this._cacheName,
  228. request,
  229. event,
  230. matchOptions: this._matchOptions,
  231. plugins: this._plugins
  232. });
  233. {
  234. logger_js.logger.groupCollapsed(messages.strategyStart('CacheOnly', request));
  235. if (response) {
  236. logger_js.logger.log(`Found a cached response in the '${this._cacheName}'` + ` cache.`);
  237. messages.printFinalResponse(response);
  238. } else {
  239. logger_js.logger.log(`No response found in the '${this._cacheName}' cache.`);
  240. }
  241. logger_js.logger.groupEnd();
  242. }
  243. if (!response) {
  244. throw new WorkboxError_js.WorkboxError('no-response', {
  245. url: request.url
  246. });
  247. }
  248. return response;
  249. }
  250. }
  251. /*
  252. Copyright 2018 Google LLC
  253. Use of this source code is governed by an MIT-style
  254. license that can be found in the LICENSE file or at
  255. https://opensource.org/licenses/MIT.
  256. */
  257. const cacheOkAndOpaquePlugin = {
  258. /**
  259. * Returns a valid response (to allow caching) if the status is 200 (OK) or
  260. * 0 (opaque).
  261. *
  262. * @param {Object} options
  263. * @param {Response} options.response
  264. * @return {Response|null}
  265. *
  266. * @private
  267. */
  268. cacheWillUpdate: async ({
  269. response
  270. }) => {
  271. if (response.status === 200 || response.status === 0) {
  272. return response;
  273. }
  274. return null;
  275. }
  276. };
  277. /*
  278. Copyright 2018 Google LLC
  279. Use of this source code is governed by an MIT-style
  280. license that can be found in the LICENSE file or at
  281. https://opensource.org/licenses/MIT.
  282. */
  283. /**
  284. * An implementation of a
  285. * [network first]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-falling-back-to-cache}
  286. * request strategy.
  287. *
  288. * By default, this strategy will cache responses with a 200 status code as
  289. * well as [opaque responses]{@link https://developers.google.com/web/tools/workbox/guides/handle-third-party-requests}.
  290. * Opaque responses are are cross-origin requests where the response doesn't
  291. * support [CORS]{@link https://enable-cors.org/}.
  292. *
  293. * If the network request fails, and there is no cache match, this will throw
  294. * a `WorkboxError` exception.
  295. *
  296. * @memberof module:workbox-strategies
  297. */
  298. class NetworkFirst {
  299. /**
  300. * @param {Object} options
  301. * @param {string} options.cacheName Cache name to store and retrieve
  302. * requests. Defaults to cache names provided by
  303. * [workbox-core]{@link module:workbox-core.cacheNames}.
  304. * @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
  305. * to use in conjunction with this caching strategy.
  306. * @param {Object} options.fetchOptions Values passed along to the
  307. * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
  308. * of all fetch() requests made by this strategy.
  309. * @param {Object} options.matchOptions [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions)
  310. * @param {number} options.networkTimeoutSeconds If set, any network requests
  311. * that fail to respond within the timeout will fallback to the cache.
  312. *
  313. * This option can be used to combat
  314. * "[lie-fi]{@link https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi}"
  315. * scenarios.
  316. */
  317. constructor(options = {}) {
  318. this._cacheName = cacheNames_js.cacheNames.getRuntimeName(options.cacheName);
  319. if (options.plugins) {
  320. const isUsingCacheWillUpdate = options.plugins.some(plugin => !!plugin.cacheWillUpdate);
  321. this._plugins = isUsingCacheWillUpdate ? options.plugins : [cacheOkAndOpaquePlugin, ...options.plugins];
  322. } else {
  323. // No plugins passed in, use the default plugin.
  324. this._plugins = [cacheOkAndOpaquePlugin];
  325. }
  326. this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
  327. {
  328. if (this._networkTimeoutSeconds) {
  329. assert_js.assert.isType(this._networkTimeoutSeconds, 'number', {
  330. moduleName: 'workbox-strategies',
  331. className: 'NetworkFirst',
  332. funcName: 'constructor',
  333. paramName: 'networkTimeoutSeconds'
  334. });
  335. }
  336. }
  337. this._fetchOptions = options.fetchOptions;
  338. this._matchOptions = options.matchOptions;
  339. }
  340. /**
  341. * This method will perform a request strategy and follows an API that
  342. * will work with the
  343. * [Workbox Router]{@link module:workbox-routing.Router}.
  344. *
  345. * @param {Object} options
  346. * @param {Request|string} options.request A request to run this strategy for.
  347. * @param {Event} [options.event] The event that triggered the request.
  348. * @return {Promise<Response>}
  349. */
  350. async handle({
  351. event,
  352. request
  353. }) {
  354. const logs = [];
  355. if (typeof request === 'string') {
  356. request = new Request(request);
  357. }
  358. {
  359. assert_js.assert.isInstance(request, Request, {
  360. moduleName: 'workbox-strategies',
  361. className: 'NetworkFirst',
  362. funcName: 'handle',
  363. paramName: 'makeRequest'
  364. });
  365. }
  366. const promises = [];
  367. let timeoutId;
  368. if (this._networkTimeoutSeconds) {
  369. const {
  370. id,
  371. promise
  372. } = this._getTimeoutPromise({
  373. request,
  374. event,
  375. logs
  376. });
  377. timeoutId = id;
  378. promises.push(promise);
  379. }
  380. const networkPromise = this._getNetworkPromise({
  381. timeoutId,
  382. request,
  383. event,
  384. logs
  385. });
  386. promises.push(networkPromise); // Promise.race() will resolve as soon as the first promise resolves.
  387. let response = await Promise.race(promises); // If Promise.race() resolved with null, it might be due to a network
  388. // timeout + a cache miss. If that were to happen, we'd rather wait until
  389. // the networkPromise resolves instead of returning null.
  390. // Note that it's fine to await an already-resolved promise, so we don't
  391. // have to check to see if it's still "in flight".
  392. if (!response) {
  393. response = await networkPromise;
  394. }
  395. {
  396. logger_js.logger.groupCollapsed(messages.strategyStart('NetworkFirst', request));
  397. for (const log of logs) {
  398. logger_js.logger.log(log);
  399. }
  400. messages.printFinalResponse(response);
  401. logger_js.logger.groupEnd();
  402. }
  403. if (!response) {
  404. throw new WorkboxError_js.WorkboxError('no-response', {
  405. url: request.url
  406. });
  407. }
  408. return response;
  409. }
  410. /**
  411. * @param {Object} options
  412. * @param {Request} options.request
  413. * @param {Array} options.logs A reference to the logs array
  414. * @param {Event} [options.event]
  415. * @return {Promise<Response>}
  416. *
  417. * @private
  418. */
  419. _getTimeoutPromise({
  420. request,
  421. logs,
  422. event
  423. }) {
  424. let timeoutId;
  425. const timeoutPromise = new Promise(resolve => {
  426. const onNetworkTimeout = async () => {
  427. {
  428. logs.push(`Timing out the network response at ` + `${this._networkTimeoutSeconds} seconds.`);
  429. }
  430. resolve(await this._respondFromCache({
  431. request,
  432. event
  433. }));
  434. };
  435. timeoutId = setTimeout(onNetworkTimeout, this._networkTimeoutSeconds * 1000);
  436. });
  437. return {
  438. promise: timeoutPromise,
  439. id: timeoutId
  440. };
  441. }
  442. /**
  443. * @param {Object} options
  444. * @param {number|undefined} options.timeoutId
  445. * @param {Request} options.request
  446. * @param {Array} options.logs A reference to the logs Array.
  447. * @param {Event} [options.event]
  448. * @return {Promise<Response>}
  449. *
  450. * @private
  451. */
  452. async _getNetworkPromise({
  453. timeoutId,
  454. request,
  455. logs,
  456. event
  457. }) {
  458. let error;
  459. let response;
  460. try {
  461. response = await fetchWrapper_js.fetchWrapper.fetch({
  462. request,
  463. event,
  464. fetchOptions: this._fetchOptions,
  465. plugins: this._plugins
  466. });
  467. } catch (err) {
  468. error = err;
  469. }
  470. if (timeoutId) {
  471. clearTimeout(timeoutId);
  472. }
  473. {
  474. if (response) {
  475. logs.push(`Got response from network.`);
  476. } else {
  477. logs.push(`Unable to get a response from the network. Will respond ` + `with a cached response.`);
  478. }
  479. }
  480. if (error || !response) {
  481. response = await this._respondFromCache({
  482. request,
  483. event
  484. });
  485. {
  486. if (response) {
  487. logs.push(`Found a cached response in the '${this._cacheName}'` + ` cache.`);
  488. } else {
  489. logs.push(`No response found in the '${this._cacheName}' cache.`);
  490. }
  491. }
  492. } else {
  493. // Keep the service worker alive while we put the request in the cache
  494. const responseClone = response.clone();
  495. const cachePut = cacheWrapper_js.cacheWrapper.put({
  496. cacheName: this._cacheName,
  497. request,
  498. response: responseClone,
  499. event,
  500. plugins: this._plugins
  501. });
  502. if (event) {
  503. try {
  504. // The event has been responded to so we can keep the SW alive to
  505. // respond to the request
  506. event.waitUntil(cachePut);
  507. } catch (err) {
  508. {
  509. logger_js.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache for '${getFriendlyURL_js.getFriendlyURL(request.url)}'.`);
  510. }
  511. }
  512. }
  513. }
  514. return response;
  515. }
  516. /**
  517. * Used if the network timeouts or fails to make the request.
  518. *
  519. * @param {Object} options
  520. * @param {Request} request The request to match in the cache
  521. * @param {Event} [options.event]
  522. * @return {Promise<Object>}
  523. *
  524. * @private
  525. */
  526. _respondFromCache({
  527. event,
  528. request
  529. }) {
  530. return cacheWrapper_js.cacheWrapper.match({
  531. cacheName: this._cacheName,
  532. request,
  533. event,
  534. matchOptions: this._matchOptions,
  535. plugins: this._plugins
  536. });
  537. }
  538. }
  539. /*
  540. Copyright 2018 Google LLC
  541. Use of this source code is governed by an MIT-style
  542. license that can be found in the LICENSE file or at
  543. https://opensource.org/licenses/MIT.
  544. */
  545. /**
  546. * An implementation of a
  547. * [network-only]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-only}
  548. * request strategy.
  549. *
  550. * This class is useful if you want to take advantage of any
  551. * [Workbox plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}.
  552. *
  553. * If the network request fails, this will throw a `WorkboxError` exception.
  554. *
  555. * @memberof module:workbox-strategies
  556. */
  557. class NetworkOnly {
  558. /**
  559. * @param {Object} options
  560. * @param {string} options.cacheName Cache name to store and retrieve
  561. * requests. Defaults to cache names provided by
  562. * [workbox-core]{@link module:workbox-core.cacheNames}.
  563. * @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
  564. * to use in conjunction with this caching strategy.
  565. * @param {Object} options.fetchOptions Values passed along to the
  566. * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
  567. * of all fetch() requests made by this strategy.
  568. */
  569. constructor(options = {}) {
  570. this._plugins = options.plugins || [];
  571. this._fetchOptions = options.fetchOptions;
  572. }
  573. /**
  574. * This method will perform a request strategy and follows an API that
  575. * will work with the
  576. * [Workbox Router]{@link module:workbox-routing.Router}.
  577. *
  578. * @param {Object} options
  579. * @param {Request|string} options.request The request to run this strategy for.
  580. * @param {Event} [options.event] The event that triggered the request.
  581. * @return {Promise<Response>}
  582. */
  583. async handle({
  584. event,
  585. request
  586. }) {
  587. if (typeof request === 'string') {
  588. request = new Request(request);
  589. }
  590. {
  591. assert_js.assert.isInstance(request, Request, {
  592. moduleName: 'workbox-strategies',
  593. className: 'NetworkOnly',
  594. funcName: 'handle',
  595. paramName: 'request'
  596. });
  597. }
  598. let error;
  599. let response;
  600. try {
  601. response = await fetchWrapper_js.fetchWrapper.fetch({
  602. request,
  603. event,
  604. fetchOptions: this._fetchOptions,
  605. plugins: this._plugins
  606. });
  607. } catch (err) {
  608. error = err;
  609. }
  610. {
  611. logger_js.logger.groupCollapsed(messages.strategyStart('NetworkOnly', request));
  612. if (response) {
  613. logger_js.logger.log(`Got response from network.`);
  614. } else {
  615. logger_js.logger.log(`Unable to get a response from the network.`);
  616. }
  617. messages.printFinalResponse(response);
  618. logger_js.logger.groupEnd();
  619. }
  620. if (!response) {
  621. throw new WorkboxError_js.WorkboxError('no-response', {
  622. url: request.url,
  623. error
  624. });
  625. }
  626. return response;
  627. }
  628. }
  629. /*
  630. Copyright 2018 Google LLC
  631. Use of this source code is governed by an MIT-style
  632. license that can be found in the LICENSE file or at
  633. https://opensource.org/licenses/MIT.
  634. */
  635. /**
  636. * An implementation of a
  637. * [stale-while-revalidate]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate}
  638. * request strategy.
  639. *
  640. * Resources are requested from both the cache and the network in parallel.
  641. * The strategy will respond with the cached version if available, otherwise
  642. * wait for the network response. The cache is updated with the network response
  643. * with each successful request.
  644. *
  645. * By default, this strategy will cache responses with a 200 status code as
  646. * well as [opaque responses]{@link https://developers.google.com/web/tools/workbox/guides/handle-third-party-requests}.
  647. * Opaque responses are cross-origin requests where the response doesn't
  648. * support [CORS]{@link https://enable-cors.org/}.
  649. *
  650. * If the network request fails, and there is no cache match, this will throw
  651. * a `WorkboxError` exception.
  652. *
  653. * @memberof module:workbox-strategies
  654. */
  655. class StaleWhileRevalidate {
  656. /**
  657. * @param {Object} options
  658. * @param {string} options.cacheName Cache name to store and retrieve
  659. * requests. Defaults to cache names provided by
  660. * [workbox-core]{@link module:workbox-core.cacheNames}.
  661. * @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
  662. * to use in conjunction with this caching strategy.
  663. * @param {Object} options.fetchOptions Values passed along to the
  664. * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
  665. * of all fetch() requests made by this strategy.
  666. * @param {Object} options.matchOptions [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions)
  667. */
  668. constructor(options = {}) {
  669. this._cacheName = cacheNames_js.cacheNames.getRuntimeName(options.cacheName);
  670. this._plugins = options.plugins || [];
  671. if (options.plugins) {
  672. const isUsingCacheWillUpdate = options.plugins.some(plugin => !!plugin.cacheWillUpdate);
  673. this._plugins = isUsingCacheWillUpdate ? options.plugins : [cacheOkAndOpaquePlugin, ...options.plugins];
  674. } else {
  675. // No plugins passed in, use the default plugin.
  676. this._plugins = [cacheOkAndOpaquePlugin];
  677. }
  678. this._fetchOptions = options.fetchOptions;
  679. this._matchOptions = options.matchOptions;
  680. }
  681. /**
  682. * This method will perform a request strategy and follows an API that
  683. * will work with the
  684. * [Workbox Router]{@link module:workbox-routing.Router}.
  685. *
  686. * @param {Object} options
  687. * @param {Request|string} options.request A request to run this strategy for.
  688. * @param {Event} [options.event] The event that triggered the request.
  689. * @return {Promise<Response>}
  690. */
  691. async handle({
  692. event,
  693. request
  694. }) {
  695. const logs = [];
  696. if (typeof request === 'string') {
  697. request = new Request(request);
  698. }
  699. {
  700. assert_js.assert.isInstance(request, Request, {
  701. moduleName: 'workbox-strategies',
  702. className: 'StaleWhileRevalidate',
  703. funcName: 'handle',
  704. paramName: 'request'
  705. });
  706. }
  707. const fetchAndCachePromise = this._getFromNetwork({
  708. request,
  709. event
  710. });
  711. let response = await cacheWrapper_js.cacheWrapper.match({
  712. cacheName: this._cacheName,
  713. request,
  714. event,
  715. matchOptions: this._matchOptions,
  716. plugins: this._plugins
  717. });
  718. let error;
  719. if (response) {
  720. {
  721. logs.push(`Found a cached response in the '${this._cacheName}'` + ` cache. Will update with the network response in the background.`);
  722. }
  723. if (event) {
  724. try {
  725. event.waitUntil(fetchAndCachePromise);
  726. } catch (error) {
  727. {
  728. logger_js.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache for '${getFriendlyURL_js.getFriendlyURL(request.url)}'.`);
  729. }
  730. }
  731. }
  732. } else {
  733. {
  734. logs.push(`No response found in the '${this._cacheName}' cache. ` + `Will wait for the network response.`);
  735. }
  736. try {
  737. response = await fetchAndCachePromise;
  738. } catch (err) {
  739. error = err;
  740. }
  741. }
  742. {
  743. logger_js.logger.groupCollapsed(messages.strategyStart('StaleWhileRevalidate', request));
  744. for (const log of logs) {
  745. logger_js.logger.log(log);
  746. }
  747. messages.printFinalResponse(response);
  748. logger_js.logger.groupEnd();
  749. }
  750. if (!response) {
  751. throw new WorkboxError_js.WorkboxError('no-response', {
  752. url: request.url,
  753. error
  754. });
  755. }
  756. return response;
  757. }
  758. /**
  759. * @param {Object} options
  760. * @param {Request} options.request
  761. * @param {Event} [options.event]
  762. * @return {Promise<Response>}
  763. *
  764. * @private
  765. */
  766. async _getFromNetwork({
  767. request,
  768. event
  769. }) {
  770. const response = await fetchWrapper_js.fetchWrapper.fetch({
  771. request,
  772. event,
  773. fetchOptions: this._fetchOptions,
  774. plugins: this._plugins
  775. });
  776. const cachePutPromise = cacheWrapper_js.cacheWrapper.put({
  777. cacheName: this._cacheName,
  778. request,
  779. response: response.clone(),
  780. event,
  781. plugins: this._plugins
  782. });
  783. if (event) {
  784. try {
  785. event.waitUntil(cachePutPromise);
  786. } catch (error) {
  787. {
  788. logger_js.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache for '${getFriendlyURL_js.getFriendlyURL(request.url)}'.`);
  789. }
  790. }
  791. }
  792. return response;
  793. }
  794. }
  795. exports.CacheFirst = CacheFirst;
  796. exports.CacheOnly = CacheOnly;
  797. exports.NetworkFirst = NetworkFirst;
  798. exports.NetworkOnly = NetworkOnly;
  799. exports.StaleWhileRevalidate = StaleWhileRevalidate;
  800. return exports;
  801. }({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private));
  802. //# sourceMappingURL=workbox-strategies.dev.js.map