You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2399 lines
79KB

  1. define("./workbox-32092201.js",['exports'], function (exports) { 'use strict';
  2. try {
  3. self['workbox:core:6.0.2'] && _();
  4. } catch (e) {}
  5. /*
  6. Copyright 2019 Google LLC
  7. Use of this source code is governed by an MIT-style
  8. license that can be found in the LICENSE file or at
  9. https://opensource.org/licenses/MIT.
  10. */
  11. const logger = (() => {
  12. // Don't overwrite this value if it's already set.
  13. // See https://github.com/GoogleChrome/workbox/pull/2284#issuecomment-560470923
  14. if (!('__WB_DISABLE_DEV_LOGS' in self)) {
  15. self.__WB_DISABLE_DEV_LOGS = false;
  16. }
  17. let inGroup = false;
  18. const methodToColorMap = {
  19. debug: `#7f8c8d`,
  20. log: `#2ecc71`,
  21. warn: `#f39c12`,
  22. error: `#c0392b`,
  23. groupCollapsed: `#3498db`,
  24. groupEnd: null
  25. };
  26. const print = function (method, args) {
  27. if (self.__WB_DISABLE_DEV_LOGS) {
  28. return;
  29. }
  30. if (method === 'groupCollapsed') {
  31. // Safari doesn't print all console.groupCollapsed() arguments:
  32. // https://bugs.webkit.org/show_bug.cgi?id=182754
  33. if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
  34. console[method](...args);
  35. return;
  36. }
  37. }
  38. const styles = [`background: ${methodToColorMap[method]}`, `border-radius: 0.5em`, `color: white`, `font-weight: bold`, `padding: 2px 0.5em`]; // When in a group, the workbox prefix is not displayed.
  39. const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')];
  40. console[method](...logPrefix, ...args);
  41. if (method === 'groupCollapsed') {
  42. inGroup = true;
  43. }
  44. if (method === 'groupEnd') {
  45. inGroup = false;
  46. }
  47. };
  48. const api = {};
  49. const loggerMethods = Object.keys(methodToColorMap);
  50. for (const key of loggerMethods) {
  51. const method = key;
  52. api[method] = (...args) => {
  53. print(method, args);
  54. };
  55. }
  56. return api;
  57. })();
  58. /*
  59. Copyright 2018 Google LLC
  60. Use of this source code is governed by an MIT-style
  61. license that can be found in the LICENSE file or at
  62. https://opensource.org/licenses/MIT.
  63. */
  64. const messages = {
  65. 'invalid-value': ({
  66. paramName,
  67. validValueDescription,
  68. value
  69. }) => {
  70. if (!paramName || !validValueDescription) {
  71. throw new Error(`Unexpected input to 'invalid-value' error.`);
  72. }
  73. return `The '${paramName}' parameter was given a value with an ` + `unexpected value. ${validValueDescription} Received a value of ` + `${JSON.stringify(value)}.`;
  74. },
  75. 'not-an-array': ({
  76. moduleName,
  77. className,
  78. funcName,
  79. paramName
  80. }) => {
  81. if (!moduleName || !className || !funcName || !paramName) {
  82. throw new Error(`Unexpected input to 'not-an-array' error.`);
  83. }
  84. return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className}.${funcName}()' must be an array.`;
  85. },
  86. 'incorrect-type': ({
  87. expectedType,
  88. paramName,
  89. moduleName,
  90. className,
  91. funcName
  92. }) => {
  93. if (!expectedType || !paramName || !moduleName || !funcName) {
  94. throw new Error(`Unexpected input to 'incorrect-type' error.`);
  95. }
  96. return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className ? className + '.' : ''}` + `${funcName}()' must be of type ${expectedType}.`;
  97. },
  98. 'incorrect-class': ({
  99. expectedClass,
  100. paramName,
  101. moduleName,
  102. className,
  103. funcName,
  104. isReturnValueProblem
  105. }) => {
  106. if (!expectedClass || !moduleName || !funcName) {
  107. throw new Error(`Unexpected input to 'incorrect-class' error.`);
  108. }
  109. if (isReturnValueProblem) {
  110. return `The return value from ` + `'${moduleName}.${className ? className + '.' : ''}${funcName}()' ` + `must be an instance of class ${expectedClass.name}.`;
  111. }
  112. return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className ? className + '.' : ''}${funcName}()' ` + `must be an instance of class ${expectedClass.name}.`;
  113. },
  114. 'missing-a-method': ({
  115. expectedMethod,
  116. paramName,
  117. moduleName,
  118. className,
  119. funcName
  120. }) => {
  121. if (!expectedMethod || !paramName || !moduleName || !className || !funcName) {
  122. throw new Error(`Unexpected input to 'missing-a-method' error.`);
  123. }
  124. return `${moduleName}.${className}.${funcName}() expected the ` + `'${paramName}' parameter to expose a '${expectedMethod}' method.`;
  125. },
  126. 'add-to-cache-list-unexpected-type': ({
  127. entry
  128. }) => {
  129. return `An unexpected entry was passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' The entry ` + `'${JSON.stringify(entry)}' isn't supported. You must supply an array of ` + `strings with one or more characters, objects with a url property or ` + `Request objects.`;
  130. },
  131. 'add-to-cache-list-conflicting-entries': ({
  132. firstEntry,
  133. secondEntry
  134. }) => {
  135. if (!firstEntry || !secondEntry) {
  136. throw new Error(`Unexpected input to ` + `'add-to-cache-list-duplicate-entries' error.`);
  137. }
  138. return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${firstEntry._entryId} but different revision details. Workbox is ` + `unable to cache and version the asset correctly. Please remove one ` + `of the entries.`;
  139. },
  140. 'plugin-error-request-will-fetch': ({
  141. thrownError
  142. }) => {
  143. if (!thrownError) {
  144. throw new Error(`Unexpected input to ` + `'plugin-error-request-will-fetch', error.`);
  145. }
  146. return `An error was thrown by a plugins 'requestWillFetch()' method. ` + `The thrown error message was: '${thrownError.message}'.`;
  147. },
  148. 'invalid-cache-name': ({
  149. cacheNameId,
  150. value
  151. }) => {
  152. if (!cacheNameId) {
  153. throw new Error(`Expected a 'cacheNameId' for error 'invalid-cache-name'`);
  154. }
  155. return `You must provide a name containing at least one character for ` + `setCacheDetails({${cacheNameId}: '...'}). Received a value of ` + `'${JSON.stringify(value)}'`;
  156. },
  157. 'unregister-route-but-not-found-with-method': ({
  158. method
  159. }) => {
  160. if (!method) {
  161. throw new Error(`Unexpected input to ` + `'unregister-route-but-not-found-with-method' error.`);
  162. }
  163. return `The route you're trying to unregister was not previously ` + `registered for the method type '${method}'.`;
  164. },
  165. 'unregister-route-route-not-registered': () => {
  166. return `The route you're trying to unregister was not previously ` + `registered.`;
  167. },
  168. 'queue-replay-failed': ({
  169. name
  170. }) => {
  171. return `Replaying the background sync queue '${name}' failed.`;
  172. },
  173. 'duplicate-queue-name': ({
  174. name
  175. }) => {
  176. return `The Queue name '${name}' is already being used. ` + `All instances of backgroundSync.Queue must be given unique names.`;
  177. },
  178. 'expired-test-without-max-age': ({
  179. methodName,
  180. paramName
  181. }) => {
  182. return `The '${methodName}()' method can only be used when the ` + `'${paramName}' is used in the constructor.`;
  183. },
  184. 'unsupported-route-type': ({
  185. moduleName,
  186. className,
  187. funcName,
  188. paramName
  189. }) => {
  190. return `The supplied '${paramName}' parameter was an unsupported type. ` + `Please check the docs for ${moduleName}.${className}.${funcName} for ` + `valid input types.`;
  191. },
  192. 'not-array-of-class': ({
  193. value,
  194. expectedClass,
  195. moduleName,
  196. className,
  197. funcName,
  198. paramName
  199. }) => {
  200. return `The supplied '${paramName}' parameter must be an array of ` + `'${expectedClass}' objects. Received '${JSON.stringify(value)},'. ` + `Please check the call to ${moduleName}.${className}.${funcName}() ` + `to fix the issue.`;
  201. },
  202. 'max-entries-or-age-required': ({
  203. moduleName,
  204. className,
  205. funcName
  206. }) => {
  207. return `You must define either config.maxEntries or config.maxAgeSeconds` + `in ${moduleName}.${className}.${funcName}`;
  208. },
  209. 'statuses-or-headers-required': ({
  210. moduleName,
  211. className,
  212. funcName
  213. }) => {
  214. return `You must define either config.statuses or config.headers` + `in ${moduleName}.${className}.${funcName}`;
  215. },
  216. 'invalid-string': ({
  217. moduleName,
  218. funcName,
  219. paramName
  220. }) => {
  221. if (!paramName || !moduleName || !funcName) {
  222. throw new Error(`Unexpected input to 'invalid-string' error.`);
  223. }
  224. return `When using strings, the '${paramName}' parameter must start with ` + `'http' (for cross-origin matches) or '/' (for same-origin matches). ` + `Please see the docs for ${moduleName}.${funcName}() for ` + `more info.`;
  225. },
  226. 'channel-name-required': () => {
  227. return `You must provide a channelName to construct a ` + `BroadcastCacheUpdate instance.`;
  228. },
  229. 'invalid-responses-are-same-args': () => {
  230. return `The arguments passed into responsesAreSame() appear to be ` + `invalid. Please ensure valid Responses are used.`;
  231. },
  232. 'expire-custom-caches-only': () => {
  233. return `You must provide a 'cacheName' property when using the ` + `expiration plugin with a runtime caching strategy.`;
  234. },
  235. 'unit-must-be-bytes': ({
  236. normalizedRangeHeader
  237. }) => {
  238. if (!normalizedRangeHeader) {
  239. throw new Error(`Unexpected input to 'unit-must-be-bytes' error.`);
  240. }
  241. return `The 'unit' portion of the Range header must be set to 'bytes'. ` + `The Range header provided was "${normalizedRangeHeader}"`;
  242. },
  243. 'single-range-only': ({
  244. normalizedRangeHeader
  245. }) => {
  246. if (!normalizedRangeHeader) {
  247. throw new Error(`Unexpected input to 'single-range-only' error.`);
  248. }
  249. return `Multiple ranges are not supported. Please use a single start ` + `value, and optional end value. The Range header provided was ` + `"${normalizedRangeHeader}"`;
  250. },
  251. 'invalid-range-values': ({
  252. normalizedRangeHeader
  253. }) => {
  254. if (!normalizedRangeHeader) {
  255. throw new Error(`Unexpected input to 'invalid-range-values' error.`);
  256. }
  257. return `The Range header is missing both start and end values. At least ` + `one of those values is needed. The Range header provided was ` + `"${normalizedRangeHeader}"`;
  258. },
  259. 'no-range-header': () => {
  260. return `No Range header was found in the Request provided.`;
  261. },
  262. 'range-not-satisfiable': ({
  263. size,
  264. start,
  265. end
  266. }) => {
  267. return `The start (${start}) and end (${end}) values in the Range are ` + `not satisfiable by the cached response, which is ${size} bytes.`;
  268. },
  269. 'attempt-to-cache-non-get-request': ({
  270. url,
  271. method
  272. }) => {
  273. return `Unable to cache '${url}' because it is a '${method}' request and ` + `only 'GET' requests can be cached.`;
  274. },
  275. 'cache-put-with-no-response': ({
  276. url
  277. }) => {
  278. return `There was an attempt to cache '${url}' but the response was not ` + `defined.`;
  279. },
  280. 'no-response': ({
  281. url,
  282. error
  283. }) => {
  284. let message = `The strategy could not generate a response for '${url}'.`;
  285. if (error) {
  286. message += ` The underlying error is ${error}.`;
  287. }
  288. return message;
  289. },
  290. 'bad-precaching-response': ({
  291. url,
  292. status
  293. }) => {
  294. return `The precaching request for '${url}' failed` + (status ? ` with an HTTP status of ${status}.` : `.`);
  295. },
  296. 'non-precached-url': ({
  297. url
  298. }) => {
  299. return `createHandlerBoundToURL('${url}') was called, but that URL is ` + `not precached. Please pass in a URL that is precached instead.`;
  300. },
  301. 'add-to-cache-list-conflicting-integrities': ({
  302. url
  303. }) => {
  304. return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${url} with different integrity values. Please remove one of them.`;
  305. },
  306. 'missing-precache-entry': ({
  307. cacheName,
  308. url
  309. }) => {
  310. return `Unable to find a precached response in ${cacheName} for ${url}.`;
  311. },
  312. 'cross-origin-copy-response': ({
  313. origin
  314. }) => {
  315. return `workbox-core.copyResponse() can only be used with same-origin ` + `responses. It was passed a response with origin ${origin}.`;
  316. }
  317. };
  318. /*
  319. Copyright 2018 Google LLC
  320. Use of this source code is governed by an MIT-style
  321. license that can be found in the LICENSE file or at
  322. https://opensource.org/licenses/MIT.
  323. */
  324. const generatorFunction = (code, details = {}) => {
  325. const message = messages[code];
  326. if (!message) {
  327. throw new Error(`Unable to find message for code '${code}'.`);
  328. }
  329. return message(details);
  330. };
  331. const messageGenerator = generatorFunction;
  332. /*
  333. Copyright 2018 Google LLC
  334. Use of this source code is governed by an MIT-style
  335. license that can be found in the LICENSE file or at
  336. https://opensource.org/licenses/MIT.
  337. */
  338. /**
  339. * Workbox errors should be thrown with this class.
  340. * This allows use to ensure the type easily in tests,
  341. * helps developers identify errors from workbox
  342. * easily and allows use to optimise error
  343. * messages correctly.
  344. *
  345. * @private
  346. */
  347. class WorkboxError extends Error {
  348. /**
  349. *
  350. * @param {string} errorCode The error code that
  351. * identifies this particular error.
  352. * @param {Object=} details Any relevant arguments
  353. * that will help developers identify issues should
  354. * be added as a key on the context object.
  355. */
  356. constructor(errorCode, details) {
  357. const message = messageGenerator(errorCode, details);
  358. super(message);
  359. this.name = errorCode;
  360. this.details = details;
  361. }
  362. }
  363. /*
  364. Copyright 2018 Google LLC
  365. Use of this source code is governed by an MIT-style
  366. license that can be found in the LICENSE file or at
  367. https://opensource.org/licenses/MIT.
  368. */
  369. /*
  370. * This method throws if the supplied value is not an array.
  371. * The destructed values are required to produce a meaningful error for users.
  372. * The destructed and restructured object is so it's clear what is
  373. * needed.
  374. */
  375. const isArray = (value, details) => {
  376. if (!Array.isArray(value)) {
  377. throw new WorkboxError('not-an-array', details);
  378. }
  379. };
  380. const hasMethod = (object, expectedMethod, details) => {
  381. const type = typeof object[expectedMethod];
  382. if (type !== 'function') {
  383. details['expectedMethod'] = expectedMethod;
  384. throw new WorkboxError('missing-a-method', details);
  385. }
  386. };
  387. const isType = (object, expectedType, details) => {
  388. if (typeof object !== expectedType) {
  389. details['expectedType'] = expectedType;
  390. throw new WorkboxError('incorrect-type', details);
  391. }
  392. };
  393. const isInstance = (object, expectedClass, details) => {
  394. if (!(object instanceof expectedClass)) {
  395. details['expectedClass'] = expectedClass;
  396. throw new WorkboxError('incorrect-class', details);
  397. }
  398. };
  399. const isOneOf = (value, validValues, details) => {
  400. if (!validValues.includes(value)) {
  401. details['validValueDescription'] = `Valid values are ${JSON.stringify(validValues)}.`;
  402. throw new WorkboxError('invalid-value', details);
  403. }
  404. };
  405. const isArrayOfClass = (value, expectedClass, details) => {
  406. const error = new WorkboxError('not-array-of-class', details);
  407. if (!Array.isArray(value)) {
  408. throw error;
  409. }
  410. for (const item of value) {
  411. if (!(item instanceof expectedClass)) {
  412. throw error;
  413. }
  414. }
  415. };
  416. const finalAssertExports = {
  417. hasMethod,
  418. isArray,
  419. isInstance,
  420. isOneOf,
  421. isType,
  422. isArrayOfClass
  423. };
  424. try {
  425. self['workbox:routing:6.0.2'] && _();
  426. } catch (e) {}
  427. /*
  428. Copyright 2018 Google LLC
  429. Use of this source code is governed by an MIT-style
  430. license that can be found in the LICENSE file or at
  431. https://opensource.org/licenses/MIT.
  432. */
  433. /**
  434. * The default HTTP method, 'GET', used when there's no specific method
  435. * configured for a route.
  436. *
  437. * @type {string}
  438. *
  439. * @private
  440. */
  441. const defaultMethod = 'GET';
  442. /**
  443. * The list of valid HTTP methods associated with requests that could be routed.
  444. *
  445. * @type {Array<string>}
  446. *
  447. * @private
  448. */
  449. const validMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT'];
  450. /*
  451. Copyright 2018 Google LLC
  452. Use of this source code is governed by an MIT-style
  453. license that can be found in the LICENSE file or at
  454. https://opensource.org/licenses/MIT.
  455. */
  456. /**
  457. * @param {function()|Object} handler Either a function, or an object with a
  458. * 'handle' method.
  459. * @return {Object} An object with a handle method.
  460. *
  461. * @private
  462. */
  463. const normalizeHandler = handler => {
  464. if (handler && typeof handler === 'object') {
  465. {
  466. finalAssertExports.hasMethod(handler, 'handle', {
  467. moduleName: 'workbox-routing',
  468. className: 'Route',
  469. funcName: 'constructor',
  470. paramName: 'handler'
  471. });
  472. }
  473. return handler;
  474. } else {
  475. {
  476. finalAssertExports.isType(handler, 'function', {
  477. moduleName: 'workbox-routing',
  478. className: 'Route',
  479. funcName: 'constructor',
  480. paramName: 'handler'
  481. });
  482. }
  483. return {
  484. handle: handler
  485. };
  486. }
  487. };
  488. /*
  489. Copyright 2018 Google LLC
  490. Use of this source code is governed by an MIT-style
  491. license that can be found in the LICENSE file or at
  492. https://opensource.org/licenses/MIT.
  493. */
  494. /**
  495. * A `Route` consists of a pair of callback functions, "match" and "handler".
  496. * The "match" callback determine if a route should be used to "handle" a
  497. * request by returning a non-falsy value if it can. The "handler" callback
  498. * is called when there is a match and should return a Promise that resolves
  499. * to a `Response`.
  500. *
  501. * @memberof module:workbox-routing
  502. */
  503. class Route {
  504. /**
  505. * Constructor for Route class.
  506. *
  507. * @param {module:workbox-routing~matchCallback} match
  508. * A callback function that determines whether the route matches a given
  509. * `fetch` event by returning a non-falsy value.
  510. * @param {module:workbox-routing~handlerCallback} handler A callback
  511. * function that returns a Promise resolving to a Response.
  512. * @param {string} [method='GET'] The HTTP method to match the Route
  513. * against.
  514. */
  515. constructor(match, handler, method = defaultMethod) {
  516. {
  517. finalAssertExports.isType(match, 'function', {
  518. moduleName: 'workbox-routing',
  519. className: 'Route',
  520. funcName: 'constructor',
  521. paramName: 'match'
  522. });
  523. if (method) {
  524. finalAssertExports.isOneOf(method, validMethods, {
  525. paramName: 'method'
  526. });
  527. }
  528. } // These values are referenced directly by Router so cannot be
  529. // altered by minificaton.
  530. this.handler = normalizeHandler(handler);
  531. this.match = match;
  532. this.method = method;
  533. }
  534. }
  535. /*
  536. Copyright 2018 Google LLC
  537. Use of this source code is governed by an MIT-style
  538. license that can be found in the LICENSE file or at
  539. https://opensource.org/licenses/MIT.
  540. */
  541. /**
  542. * RegExpRoute makes it easy to create a regular expression based
  543. * [Route]{@link module:workbox-routing.Route}.
  544. *
  545. * For same-origin requests the RegExp only needs to match part of the URL. For
  546. * requests against third-party servers, you must define a RegExp that matches
  547. * the start of the URL.
  548. *
  549. * [See the module docs for info.]{@link https://developers.google.com/web/tools/workbox/modules/workbox-routing}
  550. *
  551. * @memberof module:workbox-routing
  552. * @extends module:workbox-routing.Route
  553. */
  554. class RegExpRoute extends Route {
  555. /**
  556. * If the regular expression contains
  557. * [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references},
  558. * the captured values will be passed to the
  559. * [handler's]{@link module:workbox-routing~handlerCallback} `params`
  560. * argument.
  561. *
  562. * @param {RegExp} regExp The regular expression to match against URLs.
  563. * @param {module:workbox-routing~handlerCallback} handler A callback
  564. * function that returns a Promise resulting in a Response.
  565. * @param {string} [method='GET'] The HTTP method to match the Route
  566. * against.
  567. */
  568. constructor(regExp, handler, method) {
  569. {
  570. finalAssertExports.isInstance(regExp, RegExp, {
  571. moduleName: 'workbox-routing',
  572. className: 'RegExpRoute',
  573. funcName: 'constructor',
  574. paramName: 'pattern'
  575. });
  576. }
  577. const match = ({
  578. url
  579. }) => {
  580. const result = regExp.exec(url.href); // Return immediately if there's no match.
  581. if (!result) {
  582. return;
  583. } // Require that the match start at the first character in the URL string
  584. // if it's a cross-origin request.
  585. // See https://github.com/GoogleChrome/workbox/issues/281 for the context
  586. // behind this behavior.
  587. if (url.origin !== location.origin && result.index !== 0) {
  588. {
  589. logger.debug(`The regular expression '${regExp}' only partially matched ` + `against the cross-origin URL '${url}'. RegExpRoute's will only ` + `handle cross-origin requests if they match the entire URL.`);
  590. }
  591. return;
  592. } // If the route matches, but there aren't any capture groups defined, then
  593. // this will return [], which is truthy and therefore sufficient to
  594. // indicate a match.
  595. // If there are capture groups, then it will return their values.
  596. return result.slice(1);
  597. };
  598. super(match, handler, method);
  599. }
  600. }
  601. /*
  602. Copyright 2018 Google LLC
  603. Use of this source code is governed by an MIT-style
  604. license that can be found in the LICENSE file or at
  605. https://opensource.org/licenses/MIT.
  606. */
  607. const getFriendlyURL = url => {
  608. const urlObj = new URL(String(url), location.href); // See https://github.com/GoogleChrome/workbox/issues/2323
  609. // We want to include everything, except for the origin if it's same-origin.
  610. return urlObj.href.replace(new RegExp(`^${location.origin}`), '');
  611. };
  612. /*
  613. Copyright 2018 Google LLC
  614. Use of this source code is governed by an MIT-style
  615. license that can be found in the LICENSE file or at
  616. https://opensource.org/licenses/MIT.
  617. */
  618. /**
  619. * The Router can be used to process a FetchEvent through one or more
  620. * [Routes]{@link module:workbox-routing.Route} responding with a Request if
  621. * a matching route exists.
  622. *
  623. * If no route matches a given a request, the Router will use a "default"
  624. * handler if one is defined.
  625. *
  626. * Should the matching Route throw an error, the Router will use a "catch"
  627. * handler if one is defined to gracefully deal with issues and respond with a
  628. * Request.
  629. *
  630. * If a request matches multiple routes, the **earliest** registered route will
  631. * be used to respond to the request.
  632. *
  633. * @memberof module:workbox-routing
  634. */
  635. class Router {
  636. /**
  637. * Initializes a new Router.
  638. */
  639. constructor() {
  640. this._routes = new Map();
  641. this._defaultHandlerMap = new Map();
  642. }
  643. /**
  644. * @return {Map<string, Array<module:workbox-routing.Route>>} routes A `Map` of HTTP
  645. * method name ('GET', etc.) to an array of all the corresponding `Route`
  646. * instances that are registered.
  647. */
  648. get routes() {
  649. return this._routes;
  650. }
  651. /**
  652. * Adds a fetch event listener to respond to events when a route matches
  653. * the event's request.
  654. */
  655. addFetchListener() {
  656. // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705
  657. self.addEventListener('fetch', event => {
  658. const {
  659. request
  660. } = event;
  661. const responsePromise = this.handleRequest({
  662. request,
  663. event
  664. });
  665. if (responsePromise) {
  666. event.respondWith(responsePromise);
  667. }
  668. });
  669. }
  670. /**
  671. * Adds a message event listener for URLs to cache from the window.
  672. * This is useful to cache resources loaded on the page prior to when the
  673. * service worker started controlling it.
  674. *
  675. * The format of the message data sent from the window should be as follows.
  676. * Where the `urlsToCache` array may consist of URL strings or an array of
  677. * URL string + `requestInit` object (the same as you'd pass to `fetch()`).
  678. *
  679. * ```
  680. * {
  681. * type: 'CACHE_URLS',
  682. * payload: {
  683. * urlsToCache: [
  684. * './script1.js',
  685. * './script2.js',
  686. * ['./script3.js', {mode: 'no-cors'}],
  687. * ],
  688. * },
  689. * }
  690. * ```
  691. */
  692. addCacheListener() {
  693. // See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705
  694. self.addEventListener('message', event => {
  695. if (event.data && event.data.type === 'CACHE_URLS') {
  696. const {
  697. payload
  698. } = event.data;
  699. {
  700. logger.debug(`Caching URLs from the window`, payload.urlsToCache);
  701. }
  702. const requestPromises = Promise.all(payload.urlsToCache.map(entry => {
  703. if (typeof entry === 'string') {
  704. entry = [entry];
  705. }
  706. const request = new Request(...entry);
  707. return this.handleRequest({
  708. request,
  709. event
  710. }); // TODO(philipwalton): TypeScript errors without this typecast for
  711. // some reason (probably a bug). The real type here should work but
  712. // doesn't: `Array<Promise<Response> | undefined>`.
  713. })); // TypeScript
  714. event.waitUntil(requestPromises); // If a MessageChannel was used, reply to the message on success.
  715. if (event.ports && event.ports[0]) {
  716. requestPromises.then(() => event.ports[0].postMessage(true));
  717. }
  718. }
  719. });
  720. }
  721. /**
  722. * Apply the routing rules to a FetchEvent object to get a Response from an
  723. * appropriate Route's handler.
  724. *
  725. * @param {Object} options
  726. * @param {Request} options.request The request to handle.
  727. * @param {ExtendableEvent} options.event The event that triggered the
  728. * request.
  729. * @return {Promise<Response>|undefined} A promise is returned if a
  730. * registered route can handle the request. If there is no matching
  731. * route and there's no `defaultHandler`, `undefined` is returned.
  732. */
  733. handleRequest({
  734. request,
  735. event
  736. }) {
  737. {
  738. finalAssertExports.isInstance(request, Request, {
  739. moduleName: 'workbox-routing',
  740. className: 'Router',
  741. funcName: 'handleRequest',
  742. paramName: 'options.request'
  743. });
  744. }
  745. const url = new URL(request.url, location.href);
  746. if (!url.protocol.startsWith('http')) {
  747. {
  748. logger.debug(`Workbox Router only supports URLs that start with 'http'.`);
  749. }
  750. return;
  751. }
  752. const sameOrigin = url.origin === location.origin;
  753. const {
  754. params,
  755. route
  756. } = this.findMatchingRoute({
  757. event,
  758. request,
  759. sameOrigin,
  760. url
  761. });
  762. let handler = route && route.handler;
  763. const debugMessages = [];
  764. {
  765. if (handler) {
  766. debugMessages.push([`Found a route to handle this request:`, route]);
  767. if (params) {
  768. debugMessages.push([`Passing the following params to the route's handler:`, params]);
  769. }
  770. }
  771. } // If we don't have a handler because there was no matching route, then
  772. // fall back to defaultHandler if that's defined.
  773. const method = request.method;
  774. if (!handler && this._defaultHandlerMap.has(method)) {
  775. {
  776. debugMessages.push(`Failed to find a matching route. Falling ` + `back to the default handler for ${method}.`);
  777. }
  778. handler = this._defaultHandlerMap.get(method);
  779. }
  780. if (!handler) {
  781. {
  782. // No handler so Workbox will do nothing. If logs is set of debug
  783. // i.e. verbose, we should print out this information.
  784. logger.debug(`No route found for: ${getFriendlyURL(url)}`);
  785. }
  786. return;
  787. }
  788. {
  789. // We have a handler, meaning Workbox is going to handle the route.
  790. // print the routing details to the console.
  791. logger.groupCollapsed(`Router is responding to: ${getFriendlyURL(url)}`);
  792. debugMessages.forEach(msg => {
  793. if (Array.isArray(msg)) {
  794. logger.log(...msg);
  795. } else {
  796. logger.log(msg);
  797. }
  798. });
  799. logger.groupEnd();
  800. } // Wrap in try and catch in case the handle method throws a synchronous
  801. // error. It should still callback to the catch handler.
  802. let responsePromise;
  803. try {
  804. responsePromise = handler.handle({
  805. url,
  806. request,
  807. event,
  808. params
  809. });
  810. } catch (err) {
  811. responsePromise = Promise.reject(err);
  812. }
  813. if (responsePromise instanceof Promise && this._catchHandler) {
  814. responsePromise = responsePromise.catch(err => {
  815. {
  816. // Still include URL here as it will be async from the console group
  817. // and may not make sense without the URL
  818. logger.groupCollapsed(`Error thrown when responding to: ` + ` ${getFriendlyURL(url)}. Falling back to Catch Handler.`);
  819. logger.error(`Error thrown by:`, route);
  820. logger.error(err);
  821. logger.groupEnd();
  822. }
  823. return this._catchHandler.handle({
  824. url,
  825. request,
  826. event
  827. });
  828. });
  829. }
  830. return responsePromise;
  831. }
  832. /**
  833. * Checks a request and URL (and optionally an event) against the list of
  834. * registered routes, and if there's a match, returns the corresponding
  835. * route along with any params generated by the match.
  836. *
  837. * @param {Object} options
  838. * @param {URL} options.url
  839. * @param {Request} options.request The request to match.
  840. * @param {Event} options.event The corresponding event.
  841. * @return {Object} An object with `route` and `params` properties.
  842. * They are populated if a matching route was found or `undefined`
  843. * otherwise.
  844. */
  845. findMatchingRoute({
  846. url,
  847. sameOrigin,
  848. request,
  849. event
  850. }) {
  851. const routes = this._routes.get(request.method) || [];
  852. for (const route of routes) {
  853. let params;
  854. const matchResult = route.match({
  855. url,
  856. sameOrigin,
  857. request,
  858. event
  859. });
  860. if (matchResult) {
  861. {
  862. // Warn developers that using an async matchCallback is almost always
  863. // not the right thing to do.
  864. if (matchResult instanceof Promise) {
  865. logger.warn(`While routing ${getFriendlyURL(url)}, an async ` + `matchCallback function was used. Please convert the ` + `following route to use a synchronous matchCallback function:`, route);
  866. }
  867. } // See https://github.com/GoogleChrome/workbox/issues/2079
  868. params = matchResult;
  869. if (Array.isArray(matchResult) && matchResult.length === 0) {
  870. // Instead of passing an empty array in as params, use undefined.
  871. params = undefined;
  872. } else if (matchResult.constructor === Object && Object.keys(matchResult).length === 0) {
  873. // Instead of passing an empty object in as params, use undefined.
  874. params = undefined;
  875. } else if (typeof matchResult === 'boolean') {
  876. // For the boolean value true (rather than just something truth-y),
  877. // don't set params.
  878. // See https://github.com/GoogleChrome/workbox/pull/2134#issuecomment-513924353
  879. params = undefined;
  880. } // Return early if have a match.
  881. return {
  882. route,
  883. params
  884. };
  885. }
  886. } // If no match was found above, return and empty object.
  887. return {};
  888. }
  889. /**
  890. * Define a default `handler` that's called when no routes explicitly
  891. * match the incoming request.
  892. *
  893. * Each HTTP method ('GET', 'POST', etc.) gets its own default handler.
  894. *
  895. * Without a default handler, unmatched requests will go against the
  896. * network as if there were no service worker present.
  897. *
  898. * @param {module:workbox-routing~handlerCallback} handler A callback
  899. * function that returns a Promise resulting in a Response.
  900. * @param {string} [method='GET'] The HTTP method to associate with this
  901. * default handler. Each method has its own default.
  902. */
  903. setDefaultHandler(handler, method = defaultMethod) {
  904. this._defaultHandlerMap.set(method, normalizeHandler(handler));
  905. }
  906. /**
  907. * If a Route throws an error while handling a request, this `handler`
  908. * will be called and given a chance to provide a response.
  909. *
  910. * @param {module:workbox-routing~handlerCallback} handler A callback
  911. * function that returns a Promise resulting in a Response.
  912. */
  913. setCatchHandler(handler) {
  914. this._catchHandler = normalizeHandler(handler);
  915. }
  916. /**
  917. * Registers a route with the router.
  918. *
  919. * @param {module:workbox-routing.Route} route The route to register.
  920. */
  921. registerRoute(route) {
  922. {
  923. finalAssertExports.isType(route, 'object', {
  924. moduleName: 'workbox-routing',
  925. className: 'Router',
  926. funcName: 'registerRoute',
  927. paramName: 'route'
  928. });
  929. finalAssertExports.hasMethod(route, 'match', {
  930. moduleName: 'workbox-routing',
  931. className: 'Router',
  932. funcName: 'registerRoute',
  933. paramName: 'route'
  934. });
  935. finalAssertExports.isType(route.handler, 'object', {
  936. moduleName: 'workbox-routing',
  937. className: 'Router',
  938. funcName: 'registerRoute',
  939. paramName: 'route'
  940. });
  941. finalAssertExports.hasMethod(route.handler, 'handle', {
  942. moduleName: 'workbox-routing',
  943. className: 'Router',
  944. funcName: 'registerRoute',
  945. paramName: 'route.handler'
  946. });
  947. finalAssertExports.isType(route.method, 'string', {
  948. moduleName: 'workbox-routing',
  949. className: 'Router',
  950. funcName: 'registerRoute',
  951. paramName: 'route.method'
  952. });
  953. }
  954. if (!this._routes.has(route.method)) {
  955. this._routes.set(route.method, []);
  956. } // Give precedence to all of the earlier routes by adding this additional
  957. // route to the end of the array.
  958. this._routes.get(route.method).push(route);
  959. }
  960. /**
  961. * Unregisters a route with the router.
  962. *
  963. * @param {module:workbox-routing.Route} route The route to unregister.
  964. */
  965. unregisterRoute(route) {
  966. if (!this._routes.has(route.method)) {
  967. throw new WorkboxError('unregister-route-but-not-found-with-method', {
  968. method: route.method
  969. });
  970. }
  971. const routeIndex = this._routes.get(route.method).indexOf(route);
  972. if (routeIndex > -1) {
  973. this._routes.get(route.method).splice(routeIndex, 1);
  974. } else {
  975. throw new WorkboxError('unregister-route-route-not-registered');
  976. }
  977. }
  978. }
  979. /*
  980. Copyright 2019 Google LLC
  981. Use of this source code is governed by an MIT-style
  982. license that can be found in the LICENSE file or at
  983. https://opensource.org/licenses/MIT.
  984. */
  985. let defaultRouter;
  986. /**
  987. * Creates a new, singleton Router instance if one does not exist. If one
  988. * does already exist, that instance is returned.
  989. *
  990. * @private
  991. * @return {Router}
  992. */
  993. const getOrCreateDefaultRouter = () => {
  994. if (!defaultRouter) {
  995. defaultRouter = new Router(); // The helpers that use the default Router assume these listeners exist.
  996. defaultRouter.addFetchListener();
  997. defaultRouter.addCacheListener();
  998. }
  999. return defaultRouter;
  1000. };
  1001. /*
  1002. Copyright 2019 Google LLC
  1003. Use of this source code is governed by an MIT-style
  1004. license that can be found in the LICENSE file or at
  1005. https://opensource.org/licenses/MIT.
  1006. */
  1007. /**
  1008. * Easily register a RegExp, string, or function with a caching
  1009. * strategy to a singleton Router instance.
  1010. *
  1011. * This method will generate a Route for you if needed and
  1012. * call [registerRoute()]{@link module:workbox-routing.Router#registerRoute}.
  1013. *
  1014. * @param {RegExp|string|module:workbox-routing.Route~matchCallback|module:workbox-routing.Route} capture
  1015. * If the capture param is a `Route`, all other arguments will be ignored.
  1016. * @param {module:workbox-routing~handlerCallback} [handler] A callback
  1017. * function that returns a Promise resulting in a Response. This parameter
  1018. * is required if `capture` is not a `Route` object.
  1019. * @param {string} [method='GET'] The HTTP method to match the Route
  1020. * against.
  1021. * @return {module:workbox-routing.Route} The generated `Route`(Useful for
  1022. * unregistering).
  1023. *
  1024. * @memberof module:workbox-routing
  1025. */
  1026. function registerRoute(capture, handler, method) {
  1027. let route;
  1028. if (typeof capture === 'string') {
  1029. const captureUrl = new URL(capture, location.href);
  1030. {
  1031. if (!(capture.startsWith('/') || capture.startsWith('http'))) {
  1032. throw new WorkboxError('invalid-string', {
  1033. moduleName: 'workbox-routing',
  1034. funcName: 'registerRoute',
  1035. paramName: 'capture'
  1036. });
  1037. } // We want to check if Express-style wildcards are in the pathname only.
  1038. // TODO: Remove this log message in v4.
  1039. const valueToCheck = capture.startsWith('http') ? captureUrl.pathname : capture; // See https://github.com/pillarjs/path-to-regexp#parameters
  1040. const wildcards = '[*:?+]';
  1041. if (new RegExp(`${wildcards}`).exec(valueToCheck)) {
  1042. 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.`);
  1043. }
  1044. }
  1045. const matchCallback = ({
  1046. url
  1047. }) => {
  1048. {
  1049. if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) {
  1050. logger.debug(`${capture} only partially matches the cross-origin URL ` + `${url}. This route will only handle cross-origin requests ` + `if they match the entire URL.`);
  1051. }
  1052. }
  1053. return url.href === captureUrl.href;
  1054. }; // If `capture` is a string then `handler` and `method` must be present.
  1055. route = new Route(matchCallback, handler, method);
  1056. } else if (capture instanceof RegExp) {
  1057. // If `capture` is a `RegExp` then `handler` and `method` must be present.
  1058. route = new RegExpRoute(capture, handler, method);
  1059. } else if (typeof capture === 'function') {
  1060. // If `capture` is a function then `handler` and `method` must be present.
  1061. route = new Route(capture, handler, method);
  1062. } else if (capture instanceof Route) {
  1063. route = capture;
  1064. } else {
  1065. throw new WorkboxError('unsupported-route-type', {
  1066. moduleName: 'workbox-routing',
  1067. funcName: 'registerRoute',
  1068. paramName: 'capture'
  1069. });
  1070. }
  1071. const defaultRouter = getOrCreateDefaultRouter();
  1072. defaultRouter.registerRoute(route);
  1073. return route;
  1074. }
  1075. /*
  1076. Copyright 2019 Google LLC
  1077. Use of this source code is governed by an MIT-style
  1078. license that can be found in the LICENSE file or at
  1079. https://opensource.org/licenses/MIT.
  1080. */
  1081. /**
  1082. * Returns a promise that resolves and the passed number of milliseconds.
  1083. * This utility is an async/await-friendly version of `setTimeout`.
  1084. *
  1085. * @param {number} ms
  1086. * @return {Promise}
  1087. * @private
  1088. */
  1089. function timeout(ms) {
  1090. return new Promise(resolve => setTimeout(resolve, ms));
  1091. }
  1092. /*
  1093. Copyright 2018 Google LLC
  1094. Use of this source code is governed by an MIT-style
  1095. license that can be found in the LICENSE file or at
  1096. https://opensource.org/licenses/MIT.
  1097. */
  1098. const _cacheNameDetails = {
  1099. googleAnalytics: 'googleAnalytics',
  1100. precache: 'precache-v2',
  1101. prefix: 'workbox',
  1102. runtime: 'runtime',
  1103. suffix: typeof registration !== 'undefined' ? registration.scope : ''
  1104. };
  1105. const _createCacheName = cacheName => {
  1106. return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix].filter(value => value && value.length > 0).join('-');
  1107. };
  1108. const eachCacheNameDetail = fn => {
  1109. for (const key of Object.keys(_cacheNameDetails)) {
  1110. fn(key);
  1111. }
  1112. };
  1113. const cacheNames = {
  1114. updateDetails: details => {
  1115. eachCacheNameDetail(key => {
  1116. if (typeof details[key] === 'string') {
  1117. _cacheNameDetails[key] = details[key];
  1118. }
  1119. });
  1120. },
  1121. getGoogleAnalyticsName: userCacheName => {
  1122. return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics);
  1123. },
  1124. getPrecacheName: userCacheName => {
  1125. return userCacheName || _createCacheName(_cacheNameDetails.precache);
  1126. },
  1127. getPrefix: () => {
  1128. return _cacheNameDetails.prefix;
  1129. },
  1130. getRuntimeName: userCacheName => {
  1131. return userCacheName || _createCacheName(_cacheNameDetails.runtime);
  1132. },
  1133. getSuffix: () => {
  1134. return _cacheNameDetails.suffix;
  1135. }
  1136. };
  1137. function _extends() {
  1138. _extends = Object.assign || function (target) {
  1139. for (var i = 1; i < arguments.length; i++) {
  1140. var source = arguments[i];
  1141. for (var key in source) {
  1142. if (Object.prototype.hasOwnProperty.call(source, key)) {
  1143. target[key] = source[key];
  1144. }
  1145. }
  1146. }
  1147. return target;
  1148. };
  1149. return _extends.apply(this, arguments);
  1150. }
  1151. function stripParams(fullURL, ignoreParams) {
  1152. const strippedURL = new URL(fullURL);
  1153. for (const param of ignoreParams) {
  1154. strippedURL.searchParams.delete(param);
  1155. }
  1156. return strippedURL.href;
  1157. }
  1158. /**
  1159. * Matches an item in the cache, ignoring specific URL params. This is similar
  1160. * to the `ignoreSearch` option, but it allows you to ignore just specific
  1161. * params (while continuing to match on the others).
  1162. *
  1163. * @private
  1164. * @param {Cache} cache
  1165. * @param {Request} request
  1166. * @param {Object} matchOptions
  1167. * @param {Array<string>} ignoreParams
  1168. * @return {Promise<Response|undefined>}
  1169. */
  1170. async function cacheMatchIgnoreParams(cache, request, ignoreParams, matchOptions) {
  1171. const strippedRequestURL = stripParams(request.url, ignoreParams); // If the request doesn't include any ignored params, match as normal.
  1172. if (request.url === strippedRequestURL) {
  1173. return cache.match(request, matchOptions);
  1174. } // Otherwise, match by comparing keys
  1175. const keysOptions = _extends({}, matchOptions, {
  1176. ignoreSearch: true
  1177. });
  1178. const cacheKeys = await cache.keys(request, keysOptions);
  1179. for (const cacheKey of cacheKeys) {
  1180. const strippedCacheKeyURL = stripParams(cacheKey.url, ignoreParams);
  1181. if (strippedRequestURL === strippedCacheKeyURL) {
  1182. return cache.match(cacheKey, matchOptions);
  1183. }
  1184. }
  1185. return;
  1186. }
  1187. /*
  1188. Copyright 2018 Google LLC
  1189. Use of this source code is governed by an MIT-style
  1190. license that can be found in the LICENSE file or at
  1191. https://opensource.org/licenses/MIT.
  1192. */
  1193. /**
  1194. * The Deferred class composes Promises in a way that allows for them to be
  1195. * resolved or rejected from outside the constructor. In most cases promises
  1196. * should be used directly, but Deferreds can be necessary when the logic to
  1197. * resolve a promise must be separate.
  1198. *
  1199. * @private
  1200. */
  1201. class Deferred {
  1202. /**
  1203. * Creates a promise and exposes its resolve and reject functions as methods.
  1204. */
  1205. constructor() {
  1206. this.promise = new Promise((resolve, reject) => {
  1207. this.resolve = resolve;
  1208. this.reject = reject;
  1209. });
  1210. }
  1211. }
  1212. /*
  1213. Copyright 2018 Google LLC
  1214. Use of this source code is governed by an MIT-style
  1215. license that can be found in the LICENSE file or at
  1216. https://opensource.org/licenses/MIT.
  1217. */
  1218. const quotaErrorCallbacks = new Set();
  1219. /*
  1220. Copyright 2018 Google LLC
  1221. Use of this source code is governed by an MIT-style
  1222. license that can be found in the LICENSE file or at
  1223. https://opensource.org/licenses/MIT.
  1224. */
  1225. /**
  1226. * Runs all of the callback functions, one at a time sequentially, in the order
  1227. * in which they were registered.
  1228. *
  1229. * @memberof module:workbox-core
  1230. * @private
  1231. */
  1232. async function executeQuotaErrorCallbacks() {
  1233. {
  1234. logger.log(`About to run ${quotaErrorCallbacks.size} ` + `callbacks to clean up caches.`);
  1235. }
  1236. for (const callback of quotaErrorCallbacks) {
  1237. await callback();
  1238. {
  1239. logger.log(callback, 'is complete.');
  1240. }
  1241. }
  1242. {
  1243. logger.log('Finished running callbacks.');
  1244. }
  1245. }
  1246. try {
  1247. self['workbox:strategies:6.0.2'] && _();
  1248. } catch (e) {}
  1249. function toRequest(input) {
  1250. return typeof input === 'string' ? new Request(input) : input;
  1251. }
  1252. /**
  1253. * A class created every time a Strategy instance instance calls
  1254. * [handle()]{@link module:workbox-strategies.Strategy~handle} or
  1255. * [handleAll()]{@link module:workbox-strategies.Strategy~handleAll} that wraps all fetch and
  1256. * cache actions around plugin callbacks and keeps track of when the strategy
  1257. * is "done" (i.e. all added `event.waitUntil()` promises have resolved).
  1258. *
  1259. * @memberof module:workbox-strategies
  1260. */
  1261. class StrategyHandler {
  1262. /**
  1263. * Creates a new instance associated with the passed strategy and event
  1264. * that's handling the request.
  1265. *
  1266. * The constructor also initializes the state that will be passed to each of
  1267. * the plugins handling this request.
  1268. *
  1269. * @param {module:workbox-strategies.Strategy} strategy
  1270. * @param {Object} options
  1271. * @param {Request|string} options.request A request to run this strategy for.
  1272. * @param {ExtendableEvent} options.event The event associated with the
  1273. * request.
  1274. * @param {URL} [options.url]
  1275. * @param {*} [options.params]
  1276. * [match callback]{@link module:workbox-routing~matchCallback},
  1277. * (if applicable).
  1278. */
  1279. constructor(strategy, options) {
  1280. this._cacheKeys = {};
  1281. /**
  1282. * The request the strategy is performing (passed to the strategy's
  1283. * `handle()` or `handleAll()` method).
  1284. * @name request
  1285. * @instance
  1286. * @type {Request}
  1287. * @memberof module:workbox-strategies.StrategyHandler
  1288. */
  1289. /**
  1290. * The event associated with this request.
  1291. * @name event
  1292. * @instance
  1293. * @type {ExtendableEvent}
  1294. * @memberof module:workbox-strategies.StrategyHandler
  1295. */
  1296. /**
  1297. * A `URL` instance of `request.url` (if passed to the strategy's
  1298. * `handle()` or `handleAll()` method).
  1299. * Note: the `url` param will be present if the strategy was invoked
  1300. * from a workbox `Route` object.
  1301. * @name url
  1302. * @instance
  1303. * @type {URL|undefined}
  1304. * @memberof module:workbox-strategies.StrategyHandler
  1305. */
  1306. /**
  1307. * A `param` value (if passed to the strategy's
  1308. * `handle()` or `handleAll()` method).
  1309. * Note: the `param` param will be present if the strategy was invoked
  1310. * from a workbox `Route` object and the
  1311. * [match callback]{@link module:workbox-routing~matchCallback} returned
  1312. * a truthy value (it will be that value).
  1313. * @name params
  1314. * @instance
  1315. * @type {*|undefined}
  1316. * @memberof module:workbox-strategies.StrategyHandler
  1317. */
  1318. {
  1319. finalAssertExports.isInstance(options.event, ExtendableEvent, {
  1320. moduleName: 'workbox-strategies',
  1321. className: 'StrategyHandler',
  1322. funcName: 'constructor',
  1323. paramName: 'options.event'
  1324. });
  1325. }
  1326. Object.assign(this, options);
  1327. this.event = options.event;
  1328. this._strategy = strategy;
  1329. this._handlerDeferred = new Deferred();
  1330. this._extendLifetimePromises = []; // Copy the plugins list (since it's mutable on the strategy),
  1331. // so any mutations don't affect this handler instance.
  1332. this._plugins = [...strategy.plugins];
  1333. this._pluginStateMap = new Map();
  1334. for (const plugin of this._plugins) {
  1335. this._pluginStateMap.set(plugin, {});
  1336. }
  1337. this.event.waitUntil(this._handlerDeferred.promise);
  1338. }
  1339. /**
  1340. * Fetches a given request (and invokes any applicable plugin callback
  1341. * methods) using the `fetchOptions` and `plugins` defined on the strategy
  1342. * object.
  1343. *
  1344. * The following plugin lifecycle methods are invoked when using this method:
  1345. * - `requestWillFetch()`
  1346. * - `fetchDidSucceed()`
  1347. * - `fetchDidFail()`
  1348. *
  1349. * @param {Request|string} input The URL or request to fetch.
  1350. * @return {Promise<Response>}
  1351. */
  1352. fetch(input) {
  1353. return this.waitUntil((async () => {
  1354. const {
  1355. event
  1356. } = this;
  1357. let request = toRequest(input);
  1358. if (request.mode === 'navigate' && event instanceof FetchEvent && event.preloadResponse) {
  1359. const possiblePreloadResponse = await event.preloadResponse;
  1360. if (possiblePreloadResponse) {
  1361. {
  1362. logger.log(`Using a preloaded navigation response for ` + `'${getFriendlyURL(request.url)}'`);
  1363. }
  1364. return possiblePreloadResponse;
  1365. }
  1366. } // If there is a fetchDidFail plugin, we need to save a clone of the
  1367. // original request before it's either modified by a requestWillFetch
  1368. // plugin or before the original request's body is consumed via fetch().
  1369. const originalRequest = this.hasCallback('fetchDidFail') ? request.clone() : null;
  1370. try {
  1371. for (const cb of this.iterateCallbacks('requestWillFetch')) {
  1372. request = await cb({
  1373. request: request.clone(),
  1374. event
  1375. });
  1376. }
  1377. } catch (err) {
  1378. throw new WorkboxError('plugin-error-request-will-fetch', {
  1379. thrownError: err
  1380. });
  1381. } // The request can be altered by plugins with `requestWillFetch` making
  1382. // the original request (most likely from a `fetch` event) different
  1383. // from the Request we make. Pass both to `fetchDidFail` to aid debugging.
  1384. const pluginFilteredRequest = request.clone();
  1385. try {
  1386. let fetchResponse; // See https://github.com/GoogleChrome/workbox/issues/1796
  1387. fetchResponse = await fetch(request, request.mode === 'navigate' ? undefined : this._strategy.fetchOptions);
  1388. if ("development" !== 'production') {
  1389. logger.debug(`Network request for ` + `'${getFriendlyURL(request.url)}' returned a response with ` + `status '${fetchResponse.status}'.`);
  1390. }
  1391. for (const callback of this.iterateCallbacks('fetchDidSucceed')) {
  1392. fetchResponse = await callback({
  1393. event,
  1394. request: pluginFilteredRequest,
  1395. response: fetchResponse
  1396. });
  1397. }
  1398. return fetchResponse;
  1399. } catch (error) {
  1400. {
  1401. logger.error(`Network request for ` + `'${getFriendlyURL(request.url)}' threw an error.`, error);
  1402. } // `originalRequest` will only exist if a `fetchDidFail` callback
  1403. // is being used (see above).
  1404. if (originalRequest) {
  1405. await this.runCallbacks('fetchDidFail', {
  1406. error,
  1407. event,
  1408. originalRequest: originalRequest.clone(),
  1409. request: pluginFilteredRequest.clone()
  1410. });
  1411. }
  1412. throw error;
  1413. }
  1414. })());
  1415. }
  1416. /**
  1417. * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on
  1418. * the response generated by `this.fetch()`.
  1419. *
  1420. * The call to `this.cachePut()` automatically invokes `this.waitUntil()`,
  1421. * so you do not have to manually call `waitUntil()` on the event.
  1422. *
  1423. * @param {Request|string} input The request or URL to fetch and cache.
  1424. * @return {Promise<Response>}
  1425. */
  1426. async fetchAndCachePut(input) {
  1427. const response = await this.fetch(input);
  1428. const responseClone = response.clone();
  1429. this.waitUntil(this.cachePut(input, responseClone));
  1430. return response;
  1431. }
  1432. /**
  1433. * Matches a request from the cache (and invokes any applicable plugin
  1434. * callback methods) using the `cacheName`, `matchOptions`, and `plugins`
  1435. * defined on the strategy object.
  1436. *
  1437. * The following plugin lifecycle methods are invoked when using this method:
  1438. * - cacheKeyWillByUsed()
  1439. * - cachedResponseWillByUsed()
  1440. *
  1441. * @param {Request|string} key The Request or URL to use as the cache key.
  1442. * @return {Promise<Response|undefined>} A matching response, if found.
  1443. */
  1444. cacheMatch(key) {
  1445. return this.waitUntil((async () => {
  1446. const request = toRequest(key);
  1447. let cachedResponse;
  1448. const {
  1449. cacheName,
  1450. matchOptions
  1451. } = this._strategy;
  1452. const effectiveRequest = await this.getCacheKey(request, 'read');
  1453. const multiMatchOptions = _extends({}, matchOptions, {
  1454. cacheName
  1455. });
  1456. cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);
  1457. {
  1458. if (cachedResponse) {
  1459. logger.debug(`Found a cached response in '${cacheName}'.`);
  1460. } else {
  1461. logger.debug(`No cached response found in '${cacheName}'.`);
  1462. }
  1463. }
  1464. for (const callback of this.iterateCallbacks('cachedResponseWillBeUsed')) {
  1465. cachedResponse = (await callback({
  1466. cacheName,
  1467. matchOptions,
  1468. cachedResponse,
  1469. request: effectiveRequest,
  1470. event: this.event
  1471. })) || undefined;
  1472. }
  1473. return cachedResponse;
  1474. })());
  1475. }
  1476. /**
  1477. * Puts a request/response pair in the cache (and invokes any applicable
  1478. * plugin callback methods) using the `cacheName` and `plugins` defined on
  1479. * the strategy object.
  1480. *
  1481. * The following plugin lifecycle methods are invoked when using this method:
  1482. * - cacheKeyWillByUsed()
  1483. * - cacheWillUpdate()
  1484. * - cacheDidUpdate()
  1485. *
  1486. * @param {Request|string} key The request or URL to use as the cache key.
  1487. * @param {Promise<void>} response The response to cache.
  1488. */
  1489. async cachePut(key, response) {
  1490. const request = toRequest(key); // Run in the next task to avoid blocking other cache reads.
  1491. // https://github.com/w3c/ServiceWorker/issues/1397
  1492. await timeout(0);
  1493. const effectiveRequest = await this.getCacheKey(request, 'write');
  1494. {
  1495. if (effectiveRequest.method && effectiveRequest.method !== 'GET') {
  1496. throw new WorkboxError('attempt-to-cache-non-get-request', {
  1497. url: getFriendlyURL(effectiveRequest.url),
  1498. method: effectiveRequest.method
  1499. });
  1500. }
  1501. }
  1502. if (!response) {
  1503. {
  1504. logger.error(`Cannot cache non-existent response for ` + `'${getFriendlyURL(effectiveRequest.url)}'.`);
  1505. }
  1506. throw new WorkboxError('cache-put-with-no-response', {
  1507. url: getFriendlyURL(effectiveRequest.url)
  1508. });
  1509. }
  1510. const responseToCache = await this._ensureResponseSafeToCache(response);
  1511. if (!responseToCache) {
  1512. {
  1513. logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' ` + `will not be cached.`, responseToCache);
  1514. }
  1515. return;
  1516. }
  1517. const {
  1518. cacheName,
  1519. matchOptions
  1520. } = this._strategy;
  1521. const cache = await self.caches.open(cacheName);
  1522. const hasCacheUpdateCallback = this.hasCallback('cacheDidUpdate');
  1523. const oldResponse = hasCacheUpdateCallback ? await cacheMatchIgnoreParams( // TODO(philipwalton): the `__WB_REVISION__` param is a precaching
  1524. // feature. Consider into ways to only add this behavior if using
  1525. // precaching.
  1526. cache, effectiveRequest.clone(), ['__WB_REVISION__'], matchOptions) : null;
  1527. {
  1528. logger.debug(`Updating the '${cacheName}' cache with a new Response ` + `for ${getFriendlyURL(effectiveRequest.url)}.`);
  1529. }
  1530. try {
  1531. await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);
  1532. } catch (error) {
  1533. // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError
  1534. if (error.name === 'QuotaExceededError') {
  1535. await executeQuotaErrorCallbacks();
  1536. }
  1537. throw error;
  1538. }
  1539. for (const callback of this.iterateCallbacks('cacheDidUpdate')) {
  1540. await callback({
  1541. cacheName,
  1542. oldResponse,
  1543. newResponse: responseToCache.clone(),
  1544. request: effectiveRequest,
  1545. event: this.event
  1546. });
  1547. }
  1548. }
  1549. /**
  1550. * Checks the list of plugins for the `cacheKeyWillBeUsed` callback, and
  1551. * executes any of those callbacks found in sequence. The final `Request`
  1552. * object returned by the last plugin is treated as the cache key for cache
  1553. * reads and/or writes. If no `cacheKeyWillBeUsed` plugin callbacks have
  1554. * been registered, the passed request is returned unmodified
  1555. *
  1556. * @param {Request} request
  1557. * @param {string} mode
  1558. * @return {Promise<Request>}
  1559. */
  1560. async getCacheKey(request, mode) {
  1561. if (!this._cacheKeys[mode]) {
  1562. let effectiveRequest = request;
  1563. for (const callback of this.iterateCallbacks('cacheKeyWillBeUsed')) {
  1564. effectiveRequest = toRequest(await callback({
  1565. mode,
  1566. request: effectiveRequest,
  1567. event: this.event,
  1568. params: this.params
  1569. }));
  1570. }
  1571. this._cacheKeys[mode] = effectiveRequest;
  1572. }
  1573. return this._cacheKeys[mode];
  1574. }
  1575. /**
  1576. * Returns true if the strategy has at least one plugin with the given
  1577. * callback.
  1578. *
  1579. * @param {string} name The name of the callback to check for.
  1580. * @return {boolean}
  1581. */
  1582. hasCallback(name) {
  1583. for (const plugin of this._strategy.plugins) {
  1584. if (name in plugin) {
  1585. return true;
  1586. }
  1587. }
  1588. return false;
  1589. }
  1590. /**
  1591. * Runs all plugin callbacks matching the given name, in order, passing the
  1592. * given param object (merged ith the current plugin state) as the only
  1593. * argument.
  1594. *
  1595. * Note: since this method runs all plugins, it's not suitable for cases
  1596. * where the return value of a callback needs to be applied prior to calling
  1597. * the next callback. See
  1598. * [`iterateCallbacks()`]{@link module:workbox-strategies.StrategyHandler#iterateCallbacks}
  1599. * below for how to handle that case.
  1600. *
  1601. * @param {string} name The name of the callback to run within each plugin.
  1602. * @param {Object} param The object to pass as the first (and only) param
  1603. * when executing each callback. This object will be merged with the
  1604. * current plugin state prior to callback execution.
  1605. */
  1606. async runCallbacks(name, param) {
  1607. for (const callback of this.iterateCallbacks(name)) {
  1608. // TODO(philipwalton): not sure why `any` is needed. It seems like
  1609. // this should work with `as WorkboxPluginCallbackParam[C]`.
  1610. await callback(param);
  1611. }
  1612. }
  1613. /**
  1614. * Accepts a callback and returns an iterable of matching plugin callbacks,
  1615. * where each callback is wrapped with the current handler state (i.e. when
  1616. * you call each callback, whatever object parameter you pass it will
  1617. * be merged with the plugin's current state).
  1618. *
  1619. * @param {string} name The name fo the callback to run
  1620. * @return {Array<Function>}
  1621. */
  1622. *iterateCallbacks(name) {
  1623. for (const plugin of this._strategy.plugins) {
  1624. if (typeof plugin[name] === 'function') {
  1625. const state = this._pluginStateMap.get(plugin);
  1626. const statefulCallback = param => {
  1627. const statefulParam = _extends({}, param, {
  1628. state
  1629. }); // TODO(philipwalton): not sure why `any` is needed. It seems like
  1630. // this should work with `as WorkboxPluginCallbackParam[C]`.
  1631. return plugin[name](statefulParam);
  1632. };
  1633. yield statefulCallback;
  1634. }
  1635. }
  1636. }
  1637. /**
  1638. * Adds a promise to the
  1639. * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises}
  1640. * of the event event associated with the request being handled (usually a
  1641. * `FetchEvent`).
  1642. *
  1643. * Note: you can await
  1644. * [`doneWaiting()`]{@link module:workbox-strategies.StrategyHandler~doneWaiting}
  1645. * to know when all added promises have settled.
  1646. *
  1647. * @param {Promise} promise A promise to add to the extend lifetime promises
  1648. * of the event that triggered the request.
  1649. */
  1650. waitUntil(promise) {
  1651. this._extendLifetimePromises.push(promise);
  1652. return promise;
  1653. }
  1654. /**
  1655. * Returns a promise that resolves once all promises passed to
  1656. * [`waitUntil()`]{@link module:workbox-strategies.StrategyHandler~waitUntil}
  1657. * have settled.
  1658. *
  1659. * Note: any work done after `doneWaiting()` settles should be manually
  1660. * passed to an event's `waitUntil()` method (not this handler's
  1661. * `waitUntil()` method), otherwise the service worker thread my be killed
  1662. * prior to your work completing.
  1663. */
  1664. async doneWaiting() {
  1665. let promise;
  1666. while (promise = this._extendLifetimePromises.shift()) {
  1667. await promise;
  1668. }
  1669. }
  1670. /**
  1671. * Stops running the strategy and immediately resolves any pending
  1672. * `waitUntil()` promises.
  1673. */
  1674. destroy() {
  1675. this._handlerDeferred.resolve();
  1676. }
  1677. /**
  1678. * This method will call cacheWillUpdate on the available plugins (or use
  1679. * status === 200) to determine if the Response is safe and valid to cache.
  1680. *
  1681. * @param {Request} options.request
  1682. * @param {Response} options.response
  1683. * @return {Promise<Response|undefined>}
  1684. *
  1685. * @private
  1686. */
  1687. async _ensureResponseSafeToCache(response) {
  1688. let responseToCache = response;
  1689. let pluginsUsed = false;
  1690. for (const callback of this.iterateCallbacks('cacheWillUpdate')) {
  1691. responseToCache = (await callback({
  1692. request: this.request,
  1693. response: responseToCache,
  1694. event: this.event
  1695. })) || undefined;
  1696. pluginsUsed = true;
  1697. if (!responseToCache) {
  1698. break;
  1699. }
  1700. }
  1701. if (!pluginsUsed) {
  1702. if (responseToCache && responseToCache.status !== 200) {
  1703. responseToCache = undefined;
  1704. }
  1705. {
  1706. if (responseToCache) {
  1707. if (responseToCache.status !== 200) {
  1708. if (responseToCache.status === 0) {
  1709. 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.`);
  1710. } else {
  1711. logger.debug(`The response for '${this.request.url}' ` + `returned a status code of '${response.status}' and won't ` + `be cached as a result.`);
  1712. }
  1713. }
  1714. }
  1715. }
  1716. }
  1717. return responseToCache;
  1718. }
  1719. }
  1720. /*
  1721. Copyright 2020 Google LLC
  1722. Use of this source code is governed by an MIT-style
  1723. license that can be found in the LICENSE file or at
  1724. https://opensource.org/licenses/MIT.
  1725. */
  1726. /**
  1727. * An abstract base class that all other strategy classes must extend from:
  1728. *
  1729. * @memberof module:workbox-strategies
  1730. */
  1731. class Strategy {
  1732. /**
  1733. * Creates a new instance of the strategy and sets all documented option
  1734. * properties as public instance properties.
  1735. *
  1736. * Note: if a custom strategy class extends the base Strategy class and does
  1737. * not need more than these properties, it does not need to define its own
  1738. * constructor.
  1739. *
  1740. * @param {Object} [options]
  1741. * @param {string} [options.cacheName] Cache name to store and retrieve
  1742. * requests. Defaults to the cache names provided by
  1743. * [workbox-core]{@link module:workbox-core.cacheNames}.
  1744. * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
  1745. * to use in conjunction with this caching strategy.
  1746. * @param {Object} [options.fetchOptions] Values passed along to the
  1747. * [`init`]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters}
  1748. * of all fetch() requests made by this strategy.
  1749. * @param {Object} [options.matchOptions] The
  1750. * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}
  1751. * for any `cache.match()` or `cache.put()` calls made by this strategy.
  1752. */
  1753. constructor(options = {}) {
  1754. /**
  1755. * Cache name to store and retrieve
  1756. * requests. Defaults to the cache names provided by
  1757. * [workbox-core]{@link module:workbox-core.cacheNames}.
  1758. *
  1759. * @type {string}
  1760. */
  1761. this.cacheName = cacheNames.getRuntimeName(options.cacheName);
  1762. /**
  1763. * The list
  1764. * [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
  1765. * used by this strategy.
  1766. *
  1767. * @type {Array<Object>}
  1768. */
  1769. this.plugins = options.plugins || [];
  1770. /**
  1771. * Values passed along to the
  1772. * [`init`]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters}
  1773. * of all fetch() requests made by this strategy.
  1774. *
  1775. * @type {Object}
  1776. */
  1777. this.fetchOptions = options.fetchOptions;
  1778. /**
  1779. * The
  1780. * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}
  1781. * for any `cache.match()` or `cache.put()` calls made by this strategy.
  1782. *
  1783. * @type {Object}
  1784. */
  1785. this.matchOptions = options.matchOptions;
  1786. }
  1787. /**
  1788. * Perform a request strategy and returns a `Promise` that will resolve with
  1789. * a `Response`, invoking all relevant plugin callbacks.
  1790. *
  1791. * When a strategy instance is registered with a Workbox
  1792. * [route]{@link module:workbox-routing.Route}, this method is automatically
  1793. * called when the route matches.
  1794. *
  1795. * Alternatively, this method can be used in a standalone `FetchEvent`
  1796. * listener by passing it to `event.respondWith()`.
  1797. *
  1798. * @param {FetchEvent|Object} options A `FetchEvent` or an object with the
  1799. * properties listed below.
  1800. * @param {Request|string} options.request A request to run this strategy for.
  1801. * @param {ExtendableEvent} options.event The event associated with the
  1802. * request.
  1803. * @param {URL} [options.url]
  1804. * @param {*} [options.params]
  1805. */
  1806. handle(options) {
  1807. const [responseDone] = this.handleAll(options);
  1808. return responseDone;
  1809. }
  1810. /**
  1811. * Similar to [`handle()`]{@link module:workbox-strategies.Strategy~handle}, but
  1812. * instead of just returning a `Promise` that resolves to a `Response` it
  1813. * it will return an tuple of [response, done] promises, where the former
  1814. * (`response`) is equivalent to what `handle()` returns, and the latter is a
  1815. * Promise that will resolve once any promises that were added to
  1816. * `event.waitUntil()` as part of performing the strategy have completed.
  1817. *
  1818. * You can await the `done` promise to ensure any extra work performed by
  1819. * the strategy (usually caching responses) completes successfully.
  1820. *
  1821. * @param {FetchEvent|Object} options A `FetchEvent` or an object with the
  1822. * properties listed below.
  1823. * @param {Request|string} options.request A request to run this strategy for.
  1824. * @param {ExtendableEvent} options.event The event associated with the
  1825. * request.
  1826. * @param {URL} [options.url]
  1827. * @param {*} [options.params]
  1828. * @return {Array<Promise>} A tuple of [response, done]
  1829. * promises that can be used to determine when the response resolves as
  1830. * well as when the handler has completed all its work.
  1831. */
  1832. handleAll(options) {
  1833. // Allow for flexible options to be passed.
  1834. if (options instanceof FetchEvent) {
  1835. options = {
  1836. event: options,
  1837. request: options.request
  1838. };
  1839. }
  1840. const event = options.event;
  1841. const request = typeof options.request === 'string' ? new Request(options.request) : options.request;
  1842. const params = 'params' in options ? options.params : undefined;
  1843. const handler = new StrategyHandler(this, {
  1844. event,
  1845. request,
  1846. params
  1847. });
  1848. const responseDone = this._getResponse(handler, request, event);
  1849. const handlerDone = this._awaitComplete(responseDone, handler, request, event); // Return an array of promises, suitable for use with Promise.all().
  1850. return [responseDone, handlerDone];
  1851. }
  1852. async _getResponse(handler, request, event) {
  1853. await handler.runCallbacks('handlerWillStart', {
  1854. event,
  1855. request
  1856. });
  1857. let response = undefined;
  1858. try {
  1859. response = await this._handle(request, handler); // The "official" Strategy subclasses all throw this error automatically,
  1860. // but in case a third-party Strategy doesn't, ensure that we have a
  1861. // consistent failure when there's no response or an error response.
  1862. if (!response || response.type === 'error') {
  1863. throw new WorkboxError('no-response', {
  1864. url: request.url
  1865. });
  1866. }
  1867. } catch (error) {
  1868. for (const callback of handler.iterateCallbacks('handlerDidError')) {
  1869. response = await callback({
  1870. error,
  1871. event,
  1872. request
  1873. });
  1874. if (response) {
  1875. break;
  1876. }
  1877. }
  1878. if (!response) {
  1879. throw error;
  1880. } else {
  1881. logger.log(`While responding to '${getFriendlyURL(request.url)}', ` + `an ${error} error occurred. Using a fallback response provided by ` + `a handlerDidError plugin.`);
  1882. }
  1883. }
  1884. for (const callback of handler.iterateCallbacks('handlerWillRespond')) {
  1885. response = await callback({
  1886. event,
  1887. request,
  1888. response
  1889. });
  1890. }
  1891. return response;
  1892. }
  1893. async _awaitComplete(responseDone, handler, request, event) {
  1894. let response;
  1895. let error;
  1896. try {
  1897. response = await responseDone;
  1898. } catch (error) {// Ignore errors, as response errors should be caught via the `response`
  1899. // promise above. The `done` promise will only throw for errors in
  1900. // promises passed to `handler.waitUntil()`.
  1901. }
  1902. try {
  1903. await handler.runCallbacks('handlerDidRespond', {
  1904. event,
  1905. request,
  1906. response
  1907. });
  1908. await handler.doneWaiting();
  1909. } catch (waitUntilError) {
  1910. error = waitUntilError;
  1911. }
  1912. await handler.runCallbacks('handlerDidComplete', {
  1913. event,
  1914. request,
  1915. response,
  1916. error
  1917. });
  1918. handler.destroy();
  1919. if (error) {
  1920. throw error;
  1921. }
  1922. }
  1923. }
  1924. /**
  1925. * Classes extending the `Strategy` based class should implement this method,
  1926. * and leverage the [`handler`]{@link module:workbox-strategies.StrategyHandler}
  1927. * arg to perform all fetching and cache logic, which will ensure all relevant
  1928. * cache, cache options, fetch options and plugins are used (per the current
  1929. * strategy instance).
  1930. *
  1931. * @name _handle
  1932. * @instance
  1933. * @abstract
  1934. * @function
  1935. * @param {Request} request
  1936. * @param {module:workbox-strategies.StrategyHandler} handler
  1937. * @return {Promise<Response>}
  1938. *
  1939. * @memberof module:workbox-strategies.Strategy
  1940. */
  1941. /*
  1942. Copyright 2018 Google LLC
  1943. Use of this source code is governed by an MIT-style
  1944. license that can be found in the LICENSE file or at
  1945. https://opensource.org/licenses/MIT.
  1946. */
  1947. const messages$1 = {
  1948. strategyStart: (strategyName, request) => `Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`,
  1949. printFinalResponse: response => {
  1950. if (response) {
  1951. logger.groupCollapsed(`View the final response here.`);
  1952. logger.log(response || '[No response returned]');
  1953. logger.groupEnd();
  1954. }
  1955. }
  1956. };
  1957. /*
  1958. Copyright 2018 Google LLC
  1959. Use of this source code is governed by an MIT-style
  1960. license that can be found in the LICENSE file or at
  1961. https://opensource.org/licenses/MIT.
  1962. */
  1963. /**
  1964. * An implementation of a
  1965. * [network-only]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-only}
  1966. * request strategy.
  1967. *
  1968. * This class is useful if you want to take advantage of any
  1969. * [Workbox plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}.
  1970. *
  1971. * If the network request fails, this will throw a `WorkboxError` exception.
  1972. *
  1973. * @extends module:workbox-strategies.Strategy
  1974. * @memberof module:workbox-strategies
  1975. */
  1976. class NetworkOnly extends Strategy {
  1977. /**
  1978. * @param {Object} [options]
  1979. * @param {Array<Object>} [options.plugins] [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
  1980. * to use in conjunction with this caching strategy.
  1981. * @param {Object} [options.fetchOptions] Values passed along to the
  1982. * [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters)
  1983. * of all fetch() requests made by this strategy.
  1984. * @param {number} [options.networkTimeoutSeconds] If set, any network requests
  1985. * that fail to respond within the timeout will result in a network error.
  1986. */
  1987. constructor(options = {}) {
  1988. super(options);
  1989. this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
  1990. }
  1991. /**
  1992. * @private
  1993. * @param {Request|string} request A request to run this strategy for.
  1994. * @param {module:workbox-strategies.StrategyHandler} handler The event that
  1995. * triggered the request.
  1996. * @return {Promise<Response>}
  1997. */
  1998. async _handle(request, handler) {
  1999. {
  2000. finalAssertExports.isInstance(request, Request, {
  2001. moduleName: 'workbox-strategies',
  2002. className: this.constructor.name,
  2003. funcName: '_handle',
  2004. paramName: 'request'
  2005. });
  2006. }
  2007. let error = undefined;
  2008. let response;
  2009. try {
  2010. const promises = [handler.fetch(request)];
  2011. if (this._networkTimeoutSeconds) {
  2012. const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000);
  2013. promises.push(timeoutPromise);
  2014. }
  2015. response = await Promise.race(promises);
  2016. if (!response) {
  2017. throw new Error(`Timed out the network response after ` + `${this._networkTimeoutSeconds} seconds.`);
  2018. }
  2019. } catch (err) {
  2020. error = err;
  2021. }
  2022. {
  2023. logger.groupCollapsed(messages$1.strategyStart(this.constructor.name, request));
  2024. if (response) {
  2025. logger.log(`Got response from network.`);
  2026. } else {
  2027. logger.log(`Unable to get a response from the network.`);
  2028. }
  2029. messages$1.printFinalResponse(response);
  2030. logger.groupEnd();
  2031. }
  2032. if (!response) {
  2033. throw new WorkboxError('no-response', {
  2034. url: request.url,
  2035. error
  2036. });
  2037. }
  2038. return response;
  2039. }
  2040. }
  2041. /*
  2042. Copyright 2019 Google LLC
  2043. Use of this source code is governed by an MIT-style
  2044. license that can be found in the LICENSE file or at
  2045. https://opensource.org/licenses/MIT.
  2046. */
  2047. /**
  2048. * Claim any currently available clients once the service worker
  2049. * becomes active. This is normally used in conjunction with `skipWaiting()`.
  2050. *
  2051. * @memberof module:workbox-core
  2052. */
  2053. function clientsClaim() {
  2054. self.addEventListener('activate', () => self.clients.claim());
  2055. }
  2056. exports.NetworkOnly = NetworkOnly;
  2057. exports.clientsClaim = clientsClaim;
  2058. exports.registerRoute = registerRoute;
  2059. });
  2060. //# sourceMappingURL=workbox-32092201.js.map