UNPKG

59.1 kBJavaScriptView Raw
1this.workbox = this.workbox || {};
2this.workbox.core = (function (exports) {
3 'use strict';
4
5 try {
6 self['workbox:core:5.1.4'] && _();
7 } catch (e) {}
8
9 /*
10 Copyright 2019 Google LLC
11 Use of this source code is governed by an MIT-style
12 license that can be found in the LICENSE file or at
13 https://opensource.org/licenses/MIT.
14 */
15 const logger = (() => {
16 // Don't overwrite this value if it's already set.
17 // See https://github.com/GoogleChrome/workbox/pull/2284#issuecomment-560470923
18 if (!('__WB_DISABLE_DEV_LOGS' in self)) {
19 self.__WB_DISABLE_DEV_LOGS = false;
20 }
21
22 let inGroup = false;
23 const methodToColorMap = {
24 debug: `#7f8c8d`,
25 log: `#2ecc71`,
26 warn: `#f39c12`,
27 error: `#c0392b`,
28 groupCollapsed: `#3498db`,
29 groupEnd: null
30 };
31
32 const print = function (method, args) {
33 if (self.__WB_DISABLE_DEV_LOGS) {
34 return;
35 }
36
37 if (method === 'groupCollapsed') {
38 // Safari doesn't print all console.groupCollapsed() arguments:
39 // https://bugs.webkit.org/show_bug.cgi?id=182754
40 if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
41 console[method](...args);
42 return;
43 }
44 }
45
46 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.
47
48 const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')];
49 console[method](...logPrefix, ...args);
50
51 if (method === 'groupCollapsed') {
52 inGroup = true;
53 }
54
55 if (method === 'groupEnd') {
56 inGroup = false;
57 }
58 };
59
60 const api = {};
61 const loggerMethods = Object.keys(methodToColorMap);
62
63 for (const key of loggerMethods) {
64 const method = key;
65
66 api[method] = (...args) => {
67 print(method, args);
68 };
69 }
70
71 return api;
72 })();
73
74 /*
75 Copyright 2018 Google LLC
76
77 Use of this source code is governed by an MIT-style
78 license that can be found in the LICENSE file or at
79 https://opensource.org/licenses/MIT.
80 */
81 const messages = {
82 'invalid-value': ({
83 paramName,
84 validValueDescription,
85 value
86 }) => {
87 if (!paramName || !validValueDescription) {
88 throw new Error(`Unexpected input to 'invalid-value' error.`);
89 }
90
91 return `The '${paramName}' parameter was given a value with an ` + `unexpected value. ${validValueDescription} Received a value of ` + `${JSON.stringify(value)}.`;
92 },
93 'not-an-array': ({
94 moduleName,
95 className,
96 funcName,
97 paramName
98 }) => {
99 if (!moduleName || !className || !funcName || !paramName) {
100 throw new Error(`Unexpected input to 'not-an-array' error.`);
101 }
102
103 return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className}.${funcName}()' must be an array.`;
104 },
105 'incorrect-type': ({
106 expectedType,
107 paramName,
108 moduleName,
109 className,
110 funcName
111 }) => {
112 if (!expectedType || !paramName || !moduleName || !funcName) {
113 throw new Error(`Unexpected input to 'incorrect-type' error.`);
114 }
115
116 return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className ? className + '.' : ''}` + `${funcName}()' must be of type ${expectedType}.`;
117 },
118 'incorrect-class': ({
119 expectedClass,
120 paramName,
121 moduleName,
122 className,
123 funcName,
124 isReturnValueProblem
125 }) => {
126 if (!expectedClass || !moduleName || !funcName) {
127 throw new Error(`Unexpected input to 'incorrect-class' error.`);
128 }
129
130 if (isReturnValueProblem) {
131 return `The return value from ` + `'${moduleName}.${className ? className + '.' : ''}${funcName}()' ` + `must be an instance of class ${expectedClass.name}.`;
132 }
133
134 return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className ? className + '.' : ''}${funcName}()' ` + `must be an instance of class ${expectedClass.name}.`;
135 },
136 'missing-a-method': ({
137 expectedMethod,
138 paramName,
139 moduleName,
140 className,
141 funcName
142 }) => {
143 if (!expectedMethod || !paramName || !moduleName || !className || !funcName) {
144 throw new Error(`Unexpected input to 'missing-a-method' error.`);
145 }
146
147 return `${moduleName}.${className}.${funcName}() expected the ` + `'${paramName}' parameter to expose a '${expectedMethod}' method.`;
148 },
149 'add-to-cache-list-unexpected-type': ({
150 entry
151 }) => {
152 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.`;
153 },
154 'add-to-cache-list-conflicting-entries': ({
155 firstEntry,
156 secondEntry
157 }) => {
158 if (!firstEntry || !secondEntry) {
159 throw new Error(`Unexpected input to ` + `'add-to-cache-list-duplicate-entries' error.`);
160 }
161
162 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.`;
163 },
164 'plugin-error-request-will-fetch': ({
165 thrownError
166 }) => {
167 if (!thrownError) {
168 throw new Error(`Unexpected input to ` + `'plugin-error-request-will-fetch', error.`);
169 }
170
171 return `An error was thrown by a plugins 'requestWillFetch()' method. ` + `The thrown error message was: '${thrownError.message}'.`;
172 },
173 'invalid-cache-name': ({
174 cacheNameId,
175 value
176 }) => {
177 if (!cacheNameId) {
178 throw new Error(`Expected a 'cacheNameId' for error 'invalid-cache-name'`);
179 }
180
181 return `You must provide a name containing at least one character for ` + `setCacheDetails({${cacheNameId}: '...'}). Received a value of ` + `'${JSON.stringify(value)}'`;
182 },
183 'unregister-route-but-not-found-with-method': ({
184 method
185 }) => {
186 if (!method) {
187 throw new Error(`Unexpected input to ` + `'unregister-route-but-not-found-with-method' error.`);
188 }
189
190 return `The route you're trying to unregister was not previously ` + `registered for the method type '${method}'.`;
191 },
192 'unregister-route-route-not-registered': () => {
193 return `The route you're trying to unregister was not previously ` + `registered.`;
194 },
195 'queue-replay-failed': ({
196 name
197 }) => {
198 return `Replaying the background sync queue '${name}' failed.`;
199 },
200 'duplicate-queue-name': ({
201 name
202 }) => {
203 return `The Queue name '${name}' is already being used. ` + `All instances of backgroundSync.Queue must be given unique names.`;
204 },
205 'expired-test-without-max-age': ({
206 methodName,
207 paramName
208 }) => {
209 return `The '${methodName}()' method can only be used when the ` + `'${paramName}' is used in the constructor.`;
210 },
211 'unsupported-route-type': ({
212 moduleName,
213 className,
214 funcName,
215 paramName
216 }) => {
217 return `The supplied '${paramName}' parameter was an unsupported type. ` + `Please check the docs for ${moduleName}.${className}.${funcName} for ` + `valid input types.`;
218 },
219 'not-array-of-class': ({
220 value,
221 expectedClass,
222 moduleName,
223 className,
224 funcName,
225 paramName
226 }) => {
227 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.`;
228 },
229 'max-entries-or-age-required': ({
230 moduleName,
231 className,
232 funcName
233 }) => {
234 return `You must define either config.maxEntries or config.maxAgeSeconds` + `in ${moduleName}.${className}.${funcName}`;
235 },
236 'statuses-or-headers-required': ({
237 moduleName,
238 className,
239 funcName
240 }) => {
241 return `You must define either config.statuses or config.headers` + `in ${moduleName}.${className}.${funcName}`;
242 },
243 'invalid-string': ({
244 moduleName,
245 funcName,
246 paramName
247 }) => {
248 if (!paramName || !moduleName || !funcName) {
249 throw new Error(`Unexpected input to 'invalid-string' error.`);
250 }
251
252 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.`;
253 },
254 'channel-name-required': () => {
255 return `You must provide a channelName to construct a ` + `BroadcastCacheUpdate instance.`;
256 },
257 'invalid-responses-are-same-args': () => {
258 return `The arguments passed into responsesAreSame() appear to be ` + `invalid. Please ensure valid Responses are used.`;
259 },
260 'expire-custom-caches-only': () => {
261 return `You must provide a 'cacheName' property when using the ` + `expiration plugin with a runtime caching strategy.`;
262 },
263 'unit-must-be-bytes': ({
264 normalizedRangeHeader
265 }) => {
266 if (!normalizedRangeHeader) {
267 throw new Error(`Unexpected input to 'unit-must-be-bytes' error.`);
268 }
269
270 return `The 'unit' portion of the Range header must be set to 'bytes'. ` + `The Range header provided was "${normalizedRangeHeader}"`;
271 },
272 'single-range-only': ({
273 normalizedRangeHeader
274 }) => {
275 if (!normalizedRangeHeader) {
276 throw new Error(`Unexpected input to 'single-range-only' error.`);
277 }
278
279 return `Multiple ranges are not supported. Please use a single start ` + `value, and optional end value. The Range header provided was ` + `"${normalizedRangeHeader}"`;
280 },
281 'invalid-range-values': ({
282 normalizedRangeHeader
283 }) => {
284 if (!normalizedRangeHeader) {
285 throw new Error(`Unexpected input to 'invalid-range-values' error.`);
286 }
287
288 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}"`;
289 },
290 'no-range-header': () => {
291 return `No Range header was found in the Request provided.`;
292 },
293 'range-not-satisfiable': ({
294 size,
295 start,
296 end
297 }) => {
298 return `The start (${start}) and end (${end}) values in the Range are ` + `not satisfiable by the cached response, which is ${size} bytes.`;
299 },
300 'attempt-to-cache-non-get-request': ({
301 url,
302 method
303 }) => {
304 return `Unable to cache '${url}' because it is a '${method}' request and ` + `only 'GET' requests can be cached.`;
305 },
306 'cache-put-with-no-response': ({
307 url
308 }) => {
309 return `There was an attempt to cache '${url}' but the response was not ` + `defined.`;
310 },
311 'no-response': ({
312 url,
313 error
314 }) => {
315 let message = `The strategy could not generate a response for '${url}'.`;
316
317 if (error) {
318 message += ` The underlying error is ${error}.`;
319 }
320
321 return message;
322 },
323 'bad-precaching-response': ({
324 url,
325 status
326 }) => {
327 return `The precaching request for '${url}' failed with an HTTP ` + `status of ${status}.`;
328 },
329 'non-precached-url': ({
330 url
331 }) => {
332 return `createHandlerBoundToURL('${url}') was called, but that URL is ` + `not precached. Please pass in a URL that is precached instead.`;
333 },
334 'add-to-cache-list-conflicting-integrities': ({
335 url
336 }) => {
337 return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${url} with different integrity values. Please remove one of them.`;
338 },
339 'missing-precache-entry': ({
340 cacheName,
341 url
342 }) => {
343 return `Unable to find a precached response in ${cacheName} for ${url}.`;
344 }
345 };
346
347 /*
348 Copyright 2018 Google LLC
349
350 Use of this source code is governed by an MIT-style
351 license that can be found in the LICENSE file or at
352 https://opensource.org/licenses/MIT.
353 */
354
355 const generatorFunction = (code, details = {}) => {
356 const message = messages[code];
357
358 if (!message) {
359 throw new Error(`Unable to find message for code '${code}'.`);
360 }
361
362 return message(details);
363 };
364
365 const messageGenerator = generatorFunction;
366
367 /*
368 Copyright 2018 Google LLC
369
370 Use of this source code is governed by an MIT-style
371 license that can be found in the LICENSE file or at
372 https://opensource.org/licenses/MIT.
373 */
374 /**
375 * Workbox errors should be thrown with this class.
376 * This allows use to ensure the type easily in tests,
377 * helps developers identify errors from workbox
378 * easily and allows use to optimise error
379 * messages correctly.
380 *
381 * @private
382 */
383
384 class WorkboxError extends Error {
385 /**
386 *
387 * @param {string} errorCode The error code that
388 * identifies this particular error.
389 * @param {Object=} details Any relevant arguments
390 * that will help developers identify issues should
391 * be added as a key on the context object.
392 */
393 constructor(errorCode, details) {
394 const message = messageGenerator(errorCode, details);
395 super(message);
396 this.name = errorCode;
397 this.details = details;
398 }
399
400 }
401
402 /*
403 Copyright 2018 Google LLC
404
405 Use of this source code is governed by an MIT-style
406 license that can be found in the LICENSE file or at
407 https://opensource.org/licenses/MIT.
408 */
409 /*
410 * This method throws if the supplied value is not an array.
411 * The destructed values are required to produce a meaningful error for users.
412 * The destructed and restructured object is so it's clear what is
413 * needed.
414 */
415
416 const isArray = (value, details) => {
417 if (!Array.isArray(value)) {
418 throw new WorkboxError('not-an-array', details);
419 }
420 };
421
422 const hasMethod = (object, expectedMethod, details) => {
423 const type = typeof object[expectedMethod];
424
425 if (type !== 'function') {
426 details['expectedMethod'] = expectedMethod;
427 throw new WorkboxError('missing-a-method', details);
428 }
429 };
430
431 const isType = (object, expectedType, details) => {
432 if (typeof object !== expectedType) {
433 details['expectedType'] = expectedType;
434 throw new WorkboxError('incorrect-type', details);
435 }
436 };
437
438 const isInstance = (object, expectedClass, details) => {
439 if (!(object instanceof expectedClass)) {
440 details['expectedClass'] = expectedClass;
441 throw new WorkboxError('incorrect-class', details);
442 }
443 };
444
445 const isOneOf = (value, validValues, details) => {
446 if (!validValues.includes(value)) {
447 details['validValueDescription'] = `Valid values are ${JSON.stringify(validValues)}.`;
448 throw new WorkboxError('invalid-value', details);
449 }
450 };
451
452 const isArrayOfClass = (value, expectedClass, details) => {
453 const error = new WorkboxError('not-array-of-class', details);
454
455 if (!Array.isArray(value)) {
456 throw error;
457 }
458
459 for (const item of value) {
460 if (!(item instanceof expectedClass)) {
461 throw error;
462 }
463 }
464 };
465
466 const finalAssertExports = {
467 hasMethod,
468 isArray,
469 isInstance,
470 isOneOf,
471 isType,
472 isArrayOfClass
473 };
474
475 /*
476 Copyright 2018 Google LLC
477
478 Use of this source code is governed by an MIT-style
479 license that can be found in the LICENSE file or at
480 https://opensource.org/licenses/MIT.
481 */
482
483 const quotaErrorCallbacks = new Set();
484
485 /*
486 Copyright 2019 Google LLC
487
488 Use of this source code is governed by an MIT-style
489 license that can be found in the LICENSE file or at
490 https://opensource.org/licenses/MIT.
491 */
492 /**
493 * Adds a function to the set of quotaErrorCallbacks that will be executed if
494 * there's a quota error.
495 *
496 * @param {Function} callback
497 * @memberof module:workbox-core
498 */
499
500 function registerQuotaErrorCallback(callback) {
501 {
502 finalAssertExports.isType(callback, 'function', {
503 moduleName: 'workbox-core',
504 funcName: 'register',
505 paramName: 'callback'
506 });
507 }
508
509 quotaErrorCallbacks.add(callback);
510
511 {
512 logger.log('Registered a callback to respond to quota errors.', callback);
513 }
514 }
515
516 /*
517 Copyright 2018 Google LLC
518
519 Use of this source code is governed by an MIT-style
520 license that can be found in the LICENSE file or at
521 https://opensource.org/licenses/MIT.
522 */
523 const _cacheNameDetails = {
524 googleAnalytics: 'googleAnalytics',
525 precache: 'precache-v2',
526 prefix: 'workbox',
527 runtime: 'runtime',
528 suffix: typeof registration !== 'undefined' ? registration.scope : ''
529 };
530
531 const _createCacheName = cacheName => {
532 return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix].filter(value => value && value.length > 0).join('-');
533 };
534
535 const eachCacheNameDetail = fn => {
536 for (const key of Object.keys(_cacheNameDetails)) {
537 fn(key);
538 }
539 };
540
541 const cacheNames = {
542 updateDetails: details => {
543 eachCacheNameDetail(key => {
544 if (typeof details[key] === 'string') {
545 _cacheNameDetails[key] = details[key];
546 }
547 });
548 },
549 getGoogleAnalyticsName: userCacheName => {
550 return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics);
551 },
552 getPrecacheName: userCacheName => {
553 return userCacheName || _createCacheName(_cacheNameDetails.precache);
554 },
555 getPrefix: () => {
556 return _cacheNameDetails.prefix;
557 },
558 getRuntimeName: userCacheName => {
559 return userCacheName || _createCacheName(_cacheNameDetails.runtime);
560 },
561 getSuffix: () => {
562 return _cacheNameDetails.suffix;
563 }
564 };
565
566 /*
567 Copyright 2018 Google LLC
568
569 Use of this source code is governed by an MIT-style
570 license that can be found in the LICENSE file or at
571 https://opensource.org/licenses/MIT.
572 */
573 /**
574 * Runs all of the callback functions, one at a time sequentially, in the order
575 * in which they were registered.
576 *
577 * @memberof module:workbox-core
578 * @private
579 */
580
581 async function executeQuotaErrorCallbacks() {
582 {
583 logger.log(`About to run ${quotaErrorCallbacks.size} ` + `callbacks to clean up caches.`);
584 }
585
586 for (const callback of quotaErrorCallbacks) {
587 await callback();
588
589 {
590 logger.log(callback, 'is complete.');
591 }
592 }
593
594 {
595 logger.log('Finished running callbacks.');
596 }
597 }
598
599 /*
600 Copyright 2018 Google LLC
601
602 Use of this source code is governed by an MIT-style
603 license that can be found in the LICENSE file or at
604 https://opensource.org/licenses/MIT.
605 */
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
611 return urlObj.href.replace(new RegExp(`^${location.origin}`), '');
612 };
613
614 /*
615 Copyright 2018 Google LLC
616
617 Use of this source code is governed by an MIT-style
618 license that can be found in the LICENSE file or at
619 https://opensource.org/licenses/MIT.
620 */
621 const pluginUtils = {
622 filter: (plugins, callbackName) => {
623 return plugins.filter(plugin => callbackName in plugin);
624 }
625 };
626
627 /*
628 Copyright 2018 Google LLC
629
630 Use of this source code is governed by an MIT-style
631 license that can be found in the LICENSE file or at
632 https://opensource.org/licenses/MIT.
633 */
634 /**
635 * Checks the list of plugins for the cacheKeyWillBeUsed callback, and
636 * executes any of those callbacks found in sequence. The final `Request` object
637 * returned by the last plugin is treated as the cache key for cache reads
638 * and/or writes.
639 *
640 * @param {Object} options
641 * @param {Request} options.request
642 * @param {string} options.mode
643 * @param {Array<Object>} [options.plugins=[]]
644 * @return {Promise<Request>}
645 *
646 * @private
647 * @memberof module:workbox-core
648 */
649
650 const _getEffectiveRequest = async ({
651 request,
652 mode,
653 plugins = []
654 }) => {
655 const cacheKeyWillBeUsedPlugins = pluginUtils.filter(plugins, "cacheKeyWillBeUsed"
656 /* CACHE_KEY_WILL_BE_USED */
657 );
658 let effectiveRequest = request;
659
660 for (const plugin of cacheKeyWillBeUsedPlugins) {
661 effectiveRequest = await plugin["cacheKeyWillBeUsed"
662 /* CACHE_KEY_WILL_BE_USED */
663 ].call(plugin, {
664 mode,
665 request: effectiveRequest
666 });
667
668 if (typeof effectiveRequest === 'string') {
669 effectiveRequest = new Request(effectiveRequest);
670 }
671
672 {
673 finalAssertExports.isInstance(effectiveRequest, Request, {
674 moduleName: 'Plugin',
675 funcName: "cacheKeyWillBeUsed"
676 /* CACHE_KEY_WILL_BE_USED */
677 ,
678 isReturnValueProblem: true
679 });
680 }
681 }
682
683 return effectiveRequest;
684 };
685 /**
686 * This method will call cacheWillUpdate on the available plugins (or use
687 * status === 200) to determine if the Response is safe and valid to cache.
688 *
689 * @param {Object} options
690 * @param {Request} options.request
691 * @param {Response} options.response
692 * @param {Event} [options.event]
693 * @param {Array<Object>} [options.plugins=[]]
694 * @return {Promise<Response>}
695 *
696 * @private
697 * @memberof module:workbox-core
698 */
699
700
701 const _isResponseSafeToCache = async ({
702 request,
703 response,
704 event,
705 plugins = []
706 }) => {
707 let responseToCache = response;
708 let pluginsUsed = false;
709
710 for (const plugin of plugins) {
711 if ("cacheWillUpdate"
712 /* CACHE_WILL_UPDATE */
713 in plugin) {
714 pluginsUsed = true;
715 const pluginMethod = plugin["cacheWillUpdate"
716 /* CACHE_WILL_UPDATE */
717 ];
718 responseToCache = await pluginMethod.call(plugin, {
719 request,
720 response: responseToCache,
721 event
722 });
723
724 {
725 if (responseToCache) {
726 finalAssertExports.isInstance(responseToCache, Response, {
727 moduleName: 'Plugin',
728 funcName: "cacheWillUpdate"
729 /* CACHE_WILL_UPDATE */
730 ,
731 isReturnValueProblem: true
732 });
733 }
734 }
735
736 if (!responseToCache) {
737 break;
738 }
739 }
740 }
741
742 if (!pluginsUsed) {
743 {
744 if (responseToCache) {
745 if (responseToCache.status !== 200) {
746 if (responseToCache.status === 0) {
747 logger.warn(`The response for '${request.url}' is an opaque ` + `response. The caching strategy that you're using will not ` + `cache opaque responses by default.`);
748 } else {
749 logger.debug(`The response for '${request.url}' returned ` + `a status code of '${response.status}' and won't be cached as a ` + `result.`);
750 }
751 }
752 }
753 }
754
755 responseToCache = responseToCache && responseToCache.status === 200 ? responseToCache : undefined;
756 }
757
758 return responseToCache ? responseToCache : null;
759 };
760 /**
761 * This is a wrapper around cache.match().
762 *
763 * @param {Object} options
764 * @param {string} options.cacheName Name of the cache to match against.
765 * @param {Request} options.request The Request that will be used to look up
766 * cache entries.
767 * @param {Event} [options.event] The event that prompted the action.
768 * @param {Object} [options.matchOptions] Options passed to cache.match().
769 * @param {Array<Object>} [options.plugins=[]] Array of plugins.
770 * @return {Response} A cached response if available.
771 *
772 * @private
773 * @memberof module:workbox-core
774 */
775
776
777 const matchWrapper = async ({
778 cacheName,
779 request,
780 event,
781 matchOptions,
782 plugins = []
783 }) => {
784 const cache = await self.caches.open(cacheName);
785 const effectiveRequest = await _getEffectiveRequest({
786 plugins,
787 request,
788 mode: 'read'
789 });
790 let cachedResponse = await cache.match(effectiveRequest, matchOptions);
791
792 {
793 if (cachedResponse) {
794 logger.debug(`Found a cached response in '${cacheName}'.`);
795 } else {
796 logger.debug(`No cached response found in '${cacheName}'.`);
797 }
798 }
799
800 for (const plugin of plugins) {
801 if ("cachedResponseWillBeUsed"
802 /* CACHED_RESPONSE_WILL_BE_USED */
803 in plugin) {
804 const pluginMethod = plugin["cachedResponseWillBeUsed"
805 /* CACHED_RESPONSE_WILL_BE_USED */
806 ];
807 cachedResponse = await pluginMethod.call(plugin, {
808 cacheName,
809 event,
810 matchOptions,
811 cachedResponse,
812 request: effectiveRequest
813 });
814
815 {
816 if (cachedResponse) {
817 finalAssertExports.isInstance(cachedResponse, Response, {
818 moduleName: 'Plugin',
819 funcName: "cachedResponseWillBeUsed"
820 /* CACHED_RESPONSE_WILL_BE_USED */
821 ,
822 isReturnValueProblem: true
823 });
824 }
825 }
826 }
827 }
828
829 return cachedResponse;
830 };
831 /**
832 * Wrapper around cache.put().
833 *
834 * Will call `cacheDidUpdate` on plugins if the cache was updated, using
835 * `matchOptions` when determining what the old entry is.
836 *
837 * @param {Object} options
838 * @param {string} options.cacheName
839 * @param {Request} options.request
840 * @param {Response} options.response
841 * @param {Event} [options.event]
842 * @param {Array<Object>} [options.plugins=[]]
843 * @param {Object} [options.matchOptions]
844 *
845 * @private
846 * @memberof module:workbox-core
847 */
848
849
850 const putWrapper = async ({
851 cacheName,
852 request,
853 response,
854 event,
855 plugins = [],
856 matchOptions
857 }) => {
858 {
859 if (request.method && request.method !== 'GET') {
860 throw new WorkboxError('attempt-to-cache-non-get-request', {
861 url: getFriendlyURL(request.url),
862 method: request.method
863 });
864 }
865 }
866
867 const effectiveRequest = await _getEffectiveRequest({
868 plugins,
869 request,
870 mode: 'write'
871 });
872
873 if (!response) {
874 {
875 logger.error(`Cannot cache non-existent response for ` + `'${getFriendlyURL(effectiveRequest.url)}'.`);
876 }
877
878 throw new WorkboxError('cache-put-with-no-response', {
879 url: getFriendlyURL(effectiveRequest.url)
880 });
881 }
882
883 const responseToCache = await _isResponseSafeToCache({
884 event,
885 plugins,
886 response,
887 request: effectiveRequest
888 });
889
890 if (!responseToCache) {
891 {
892 logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' will ` + `not be cached.`, responseToCache);
893 }
894
895 return;
896 }
897
898 const cache = await self.caches.open(cacheName);
899 const updatePlugins = pluginUtils.filter(plugins, "cacheDidUpdate"
900 /* CACHE_DID_UPDATE */
901 );
902 const oldResponse = updatePlugins.length > 0 ? await matchWrapper({
903 cacheName,
904 matchOptions,
905 request: effectiveRequest
906 }) : null;
907
908 {
909 logger.debug(`Updating the '${cacheName}' cache with a new Response for ` + `${getFriendlyURL(effectiveRequest.url)}.`);
910 }
911
912 try {
913 await cache.put(effectiveRequest, responseToCache);
914 } catch (error) {
915 // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError
916 if (error.name === 'QuotaExceededError') {
917 await executeQuotaErrorCallbacks();
918 }
919
920 throw error;
921 }
922
923 for (const plugin of updatePlugins) {
924 await plugin["cacheDidUpdate"
925 /* CACHE_DID_UPDATE */
926 ].call(plugin, {
927 cacheName,
928 event,
929 oldResponse,
930 newResponse: responseToCache,
931 request: effectiveRequest
932 });
933 }
934 };
935
936 const cacheWrapper = {
937 put: putWrapper,
938 match: matchWrapper
939 };
940
941 /*
942 Copyright 2019 Google LLC
943
944 Use of this source code is governed by an MIT-style
945 license that can be found in the LICENSE file or at
946 https://opensource.org/licenses/MIT.
947 */
948 let supportStatus;
949 /**
950 * A utility function that determines whether the current browser supports
951 * constructing a [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
952 * object.
953 *
954 * @return {boolean} `true`, if the current browser can successfully
955 * construct a `ReadableStream`, `false` otherwise.
956 *
957 * @private
958 */
959
960 function canConstructReadableStream() {
961 if (supportStatus === undefined) {
962 // See https://github.com/GoogleChrome/workbox/issues/1473
963 try {
964 new ReadableStream({
965 start() {}
966
967 });
968 supportStatus = true;
969 } catch (error) {
970 supportStatus = false;
971 }
972 }
973
974 return supportStatus;
975 }
976
977 /*
978 Copyright 2019 Google LLC
979
980 Use of this source code is governed by an MIT-style
981 license that can be found in the LICENSE file or at
982 https://opensource.org/licenses/MIT.
983 */
984 let supportStatus$1;
985 /**
986 * A utility function that determines whether the current browser supports
987 * constructing a new `Response` from a `response.body` stream.
988 *
989 * @return {boolean} `true`, if the current browser can successfully
990 * construct a `Response` from a `response.body` stream, `false` otherwise.
991 *
992 * @private
993 */
994
995 function canConstructResponseFromBodyStream() {
996 if (supportStatus$1 === undefined) {
997 const testResponse = new Response('');
998
999 if ('body' in testResponse) {
1000 try {
1001 new Response(testResponse.body);
1002 supportStatus$1 = true;
1003 } catch (error) {
1004 supportStatus$1 = false;
1005 }
1006 }
1007
1008 supportStatus$1 = false;
1009 }
1010
1011 return supportStatus$1;
1012 }
1013
1014 /*
1015 Copyright 2019 Google LLC
1016 Use of this source code is governed by an MIT-style
1017 license that can be found in the LICENSE file or at
1018 https://opensource.org/licenses/MIT.
1019 */
1020 /**
1021 * A helper function that prevents a promise from being flagged as unused.
1022 *
1023 * @private
1024 **/
1025
1026 function dontWaitFor(promise) {
1027 // Effective no-op.
1028 promise.then(() => {});
1029 }
1030
1031 /*
1032 Copyright 2018 Google LLC
1033
1034 Use of this source code is governed by an MIT-style
1035 license that can be found in the LICENSE file or at
1036 https://opensource.org/licenses/MIT.
1037 */
1038 /**
1039 * A class that wraps common IndexedDB functionality in a promise-based API.
1040 * It exposes all the underlying power and functionality of IndexedDB, but
1041 * wraps the most commonly used features in a way that's much simpler to use.
1042 *
1043 * @private
1044 */
1045
1046 class DBWrapper {
1047 /**
1048 * @param {string} name
1049 * @param {number} version
1050 * @param {Object=} [callback]
1051 * @param {!Function} [callbacks.onupgradeneeded]
1052 * @param {!Function} [callbacks.onversionchange] Defaults to
1053 * DBWrapper.prototype._onversionchange when not specified.
1054 * @private
1055 */
1056 constructor(name, version, {
1057 onupgradeneeded,
1058 onversionchange
1059 } = {}) {
1060 this._db = null;
1061 this._name = name;
1062 this._version = version;
1063 this._onupgradeneeded = onupgradeneeded;
1064
1065 this._onversionchange = onversionchange || (() => this.close());
1066 }
1067 /**
1068 * Returns the IDBDatabase instance (not normally needed).
1069 * @return {IDBDatabase|undefined}
1070 *
1071 * @private
1072 */
1073
1074
1075 get db() {
1076 return this._db;
1077 }
1078 /**
1079 * Opens a connected to an IDBDatabase, invokes any onupgradedneeded
1080 * callback, and added an onversionchange callback to the database.
1081 *
1082 * @return {IDBDatabase}
1083 * @private
1084 */
1085
1086
1087 async open() {
1088 if (this._db) return;
1089 this._db = await new Promise((resolve, reject) => {
1090 // This flag is flipped to true if the timeout callback runs prior
1091 // to the request failing or succeeding. Note: we use a timeout instead
1092 // of an onblocked handler since there are cases where onblocked will
1093 // never never run. A timeout better handles all possible scenarios:
1094 // https://github.com/w3c/IndexedDB/issues/223
1095 let openRequestTimedOut = false;
1096 setTimeout(() => {
1097 openRequestTimedOut = true;
1098 reject(new Error('The open request was blocked and timed out'));
1099 }, this.OPEN_TIMEOUT);
1100 const openRequest = indexedDB.open(this._name, this._version);
1101
1102 openRequest.onerror = () => reject(openRequest.error);
1103
1104 openRequest.onupgradeneeded = evt => {
1105 if (openRequestTimedOut) {
1106 openRequest.transaction.abort();
1107 openRequest.result.close();
1108 } else if (typeof this._onupgradeneeded === 'function') {
1109 this._onupgradeneeded(evt);
1110 }
1111 };
1112
1113 openRequest.onsuccess = () => {
1114 const db = openRequest.result;
1115
1116 if (openRequestTimedOut) {
1117 db.close();
1118 } else {
1119 db.onversionchange = this._onversionchange.bind(this);
1120 resolve(db);
1121 }
1122 };
1123 });
1124 return this;
1125 }
1126 /**
1127 * Polyfills the native `getKey()` method. Note, this is overridden at
1128 * runtime if the browser supports the native method.
1129 *
1130 * @param {string} storeName
1131 * @param {*} query
1132 * @return {Array}
1133 * @private
1134 */
1135
1136
1137 async getKey(storeName, query) {
1138 return (await this.getAllKeys(storeName, query, 1))[0];
1139 }
1140 /**
1141 * Polyfills the native `getAll()` method. Note, this is overridden at
1142 * runtime if the browser supports the native method.
1143 *
1144 * @param {string} storeName
1145 * @param {*} query
1146 * @param {number} count
1147 * @return {Array}
1148 * @private
1149 */
1150
1151
1152 async getAll(storeName, query, count) {
1153 return await this.getAllMatching(storeName, {
1154 query,
1155 count
1156 });
1157 }
1158 /**
1159 * Polyfills the native `getAllKeys()` method. Note, this is overridden at
1160 * runtime if the browser supports the native method.
1161 *
1162 * @param {string} storeName
1163 * @param {*} query
1164 * @param {number} count
1165 * @return {Array}
1166 * @private
1167 */
1168
1169
1170 async getAllKeys(storeName, query, count) {
1171 const entries = await this.getAllMatching(storeName, {
1172 query,
1173 count,
1174 includeKeys: true
1175 });
1176 return entries.map(entry => entry.key);
1177 }
1178 /**
1179 * Supports flexible lookup in an object store by specifying an index,
1180 * query, direction, and count. This method returns an array of objects
1181 * with the signature .
1182 *
1183 * @param {string} storeName
1184 * @param {Object} [opts]
1185 * @param {string} [opts.index] The index to use (if specified).
1186 * @param {*} [opts.query]
1187 * @param {IDBCursorDirection} [opts.direction]
1188 * @param {number} [opts.count] The max number of results to return.
1189 * @param {boolean} [opts.includeKeys] When true, the structure of the
1190 * returned objects is changed from an array of values to an array of
1191 * objects in the form {key, primaryKey, value}.
1192 * @return {Array}
1193 * @private
1194 */
1195
1196
1197 async getAllMatching(storeName, {
1198 index,
1199 query = null,
1200 // IE/Edge errors if query === `undefined`.
1201 direction = 'next',
1202 count,
1203 includeKeys = false
1204 } = {}) {
1205 return await this.transaction([storeName], 'readonly', (txn, done) => {
1206 const store = txn.objectStore(storeName);
1207 const target = index ? store.index(index) : store;
1208 const results = [];
1209 const request = target.openCursor(query, direction);
1210
1211 request.onsuccess = () => {
1212 const cursor = request.result;
1213
1214 if (cursor) {
1215 results.push(includeKeys ? cursor : cursor.value);
1216
1217 if (count && results.length >= count) {
1218 done(results);
1219 } else {
1220 cursor.continue();
1221 }
1222 } else {
1223 done(results);
1224 }
1225 };
1226 });
1227 }
1228 /**
1229 * Accepts a list of stores, a transaction type, and a callback and
1230 * performs a transaction. A promise is returned that resolves to whatever
1231 * value the callback chooses. The callback holds all the transaction logic
1232 * and is invoked with two arguments:
1233 * 1. The IDBTransaction object
1234 * 2. A `done` function, that's used to resolve the promise when
1235 * when the transaction is done, if passed a value, the promise is
1236 * resolved to that value.
1237 *
1238 * @param {Array<string>} storeNames An array of object store names
1239 * involved in the transaction.
1240 * @param {string} type Can be `readonly` or `readwrite`.
1241 * @param {!Function} callback
1242 * @return {*} The result of the transaction ran by the callback.
1243 * @private
1244 */
1245
1246
1247 async transaction(storeNames, type, callback) {
1248 await this.open();
1249 return await new Promise((resolve, reject) => {
1250 const txn = this._db.transaction(storeNames, type);
1251
1252 txn.onabort = () => reject(txn.error);
1253
1254 txn.oncomplete = () => resolve();
1255
1256 callback(txn, value => resolve(value));
1257 });
1258 }
1259 /**
1260 * Delegates async to a native IDBObjectStore method.
1261 *
1262 * @param {string} method The method name.
1263 * @param {string} storeName The object store name.
1264 * @param {string} type Can be `readonly` or `readwrite`.
1265 * @param {...*} args The list of args to pass to the native method.
1266 * @return {*} The result of the transaction.
1267 * @private
1268 */
1269
1270
1271 async _call(method, storeName, type, ...args) {
1272 const callback = (txn, done) => {
1273 const objStore = txn.objectStore(storeName); // TODO(philipwalton): Fix this underlying TS2684 error.
1274 // @ts-ignore
1275
1276 const request = objStore[method].apply(objStore, args);
1277
1278 request.onsuccess = () => done(request.result);
1279 };
1280
1281 return await this.transaction([storeName], type, callback);
1282 }
1283 /**
1284 * Closes the connection opened by `DBWrapper.open()`. Generally this method
1285 * doesn't need to be called since:
1286 * 1. It's usually better to keep a connection open since opening
1287 * a new connection is somewhat slow.
1288 * 2. Connections are automatically closed when the reference is
1289 * garbage collected.
1290 * The primary use case for needing to close a connection is when another
1291 * reference (typically in another tab) needs to upgrade it and would be
1292 * blocked by the current, open connection.
1293 *
1294 * @private
1295 */
1296
1297
1298 close() {
1299 if (this._db) {
1300 this._db.close();
1301
1302 this._db = null;
1303 }
1304 }
1305
1306 } // Exposed on the prototype to let users modify the default timeout on a
1307 // per-instance or global basis.
1308
1309 DBWrapper.prototype.OPEN_TIMEOUT = 2000; // Wrap native IDBObjectStore methods according to their mode.
1310
1311 const methodsToWrap = {
1312 readonly: ['get', 'count', 'getKey', 'getAll', 'getAllKeys'],
1313 readwrite: ['add', 'put', 'clear', 'delete']
1314 };
1315
1316 for (const [mode, methods] of Object.entries(methodsToWrap)) {
1317 for (const method of methods) {
1318 if (method in IDBObjectStore.prototype) {
1319 // Don't use arrow functions here since we're outside of the class.
1320 DBWrapper.prototype[method] = async function (storeName, ...args) {
1321 return await this._call(method, storeName, mode, ...args);
1322 };
1323 }
1324 }
1325 }
1326
1327 /*
1328 Copyright 2018 Google LLC
1329
1330 Use of this source code is governed by an MIT-style
1331 license that can be found in the LICENSE file or at
1332 https://opensource.org/licenses/MIT.
1333 */
1334 /**
1335 * The Deferred class composes Promises in a way that allows for them to be
1336 * resolved or rejected from outside the constructor. In most cases promises
1337 * should be used directly, but Deferreds can be necessary when the logic to
1338 * resolve a promise must be separate.
1339 *
1340 * @private
1341 */
1342
1343 class Deferred {
1344 /**
1345 * Creates a promise and exposes its resolve and reject functions as methods.
1346 */
1347 constructor() {
1348 this.promise = new Promise((resolve, reject) => {
1349 this.resolve = resolve;
1350 this.reject = reject;
1351 });
1352 }
1353
1354 }
1355
1356 /*
1357 Copyright 2018 Google LLC
1358
1359 Use of this source code is governed by an MIT-style
1360 license that can be found in the LICENSE file or at
1361 https://opensource.org/licenses/MIT.
1362 */
1363 /**
1364 * Deletes the database.
1365 * Note: this is exported separately from the DBWrapper module because most
1366 * usages of IndexedDB in workbox dont need deleting, and this way it can be
1367 * reused in tests to delete databases without creating DBWrapper instances.
1368 *
1369 * @param {string} name The database name.
1370 * @private
1371 */
1372
1373 const deleteDatabase = async name => {
1374 await new Promise((resolve, reject) => {
1375 const request = indexedDB.deleteDatabase(name);
1376
1377 request.onerror = () => {
1378 reject(request.error);
1379 };
1380
1381 request.onblocked = () => {
1382 reject(new Error('Delete blocked'));
1383 };
1384
1385 request.onsuccess = () => {
1386 resolve();
1387 };
1388 });
1389 };
1390
1391 /*
1392 Copyright 2018 Google LLC
1393
1394 Use of this source code is governed by an MIT-style
1395 license that can be found in the LICENSE file or at
1396 https://opensource.org/licenses/MIT.
1397 */
1398 /**
1399 * Wrapper around the fetch API.
1400 *
1401 * Will call requestWillFetch on available plugins.
1402 *
1403 * @param {Object} options
1404 * @param {Request|string} options.request
1405 * @param {Object} [options.fetchOptions]
1406 * @param {ExtendableEvent} [options.event]
1407 * @param {Array<Object>} [options.plugins=[]]
1408 * @return {Promise<Response>}
1409 *
1410 * @private
1411 * @memberof module:workbox-core
1412 */
1413
1414 const wrappedFetch = async ({
1415 request,
1416 fetchOptions,
1417 event,
1418 plugins = []
1419 }) => {
1420 if (typeof request === 'string') {
1421 request = new Request(request);
1422 } // We *should* be able to call `await event.preloadResponse` even if it's
1423 // undefined, but for some reason, doing so leads to errors in our Node unit
1424 // tests. To work around that, explicitly check preloadResponse's value first.
1425
1426
1427 if (event instanceof FetchEvent && event.preloadResponse) {
1428 const possiblePreloadResponse = await event.preloadResponse;
1429
1430 if (possiblePreloadResponse) {
1431 {
1432 logger.log(`Using a preloaded navigation response for ` + `'${getFriendlyURL(request.url)}'`);
1433 }
1434
1435 return possiblePreloadResponse;
1436 }
1437 }
1438
1439 {
1440 finalAssertExports.isInstance(request, Request, {
1441 paramName: 'request',
1442 expectedClass: Request,
1443 moduleName: 'workbox-core',
1444 className: 'fetchWrapper',
1445 funcName: 'wrappedFetch'
1446 });
1447 }
1448
1449 const failedFetchPlugins = pluginUtils.filter(plugins, "fetchDidFail"
1450 /* FETCH_DID_FAIL */
1451 ); // If there is a fetchDidFail plugin, we need to save a clone of the
1452 // original request before it's either modified by a requestWillFetch
1453 // plugin or before the original request's body is consumed via fetch().
1454
1455 const originalRequest = failedFetchPlugins.length > 0 ? request.clone() : null;
1456
1457 try {
1458 for (const plugin of plugins) {
1459 if ("requestWillFetch"
1460 /* REQUEST_WILL_FETCH */
1461 in plugin) {
1462 const pluginMethod = plugin["requestWillFetch"
1463 /* REQUEST_WILL_FETCH */
1464 ];
1465 const requestClone = request.clone();
1466 request = await pluginMethod.call(plugin, {
1467 request: requestClone,
1468 event
1469 });
1470
1471 if ("dev" !== 'production') {
1472 if (request) {
1473 finalAssertExports.isInstance(request, Request, {
1474 moduleName: 'Plugin',
1475 funcName: "cachedResponseWillBeUsed"
1476 /* CACHED_RESPONSE_WILL_BE_USED */
1477 ,
1478 isReturnValueProblem: true
1479 });
1480 }
1481 }
1482 }
1483 }
1484 } catch (err) {
1485 throw new WorkboxError('plugin-error-request-will-fetch', {
1486 thrownError: err
1487 });
1488 } // The request can be altered by plugins with `requestWillFetch` making
1489 // the original request (Most likely from a `fetch` event) to be different
1490 // to the Request we make. Pass both to `fetchDidFail` to aid debugging.
1491
1492
1493 const pluginFilteredRequest = request.clone();
1494
1495 try {
1496 let fetchResponse; // See https://github.com/GoogleChrome/workbox/issues/1796
1497
1498 if (request.mode === 'navigate') {
1499 fetchResponse = await fetch(request);
1500 } else {
1501 fetchResponse = await fetch(request, fetchOptions);
1502 }
1503
1504 if ("dev" !== 'production') {
1505 logger.debug(`Network request for ` + `'${getFriendlyURL(request.url)}' returned a response with ` + `status '${fetchResponse.status}'.`);
1506 }
1507
1508 for (const plugin of plugins) {
1509 if ("fetchDidSucceed"
1510 /* FETCH_DID_SUCCEED */
1511 in plugin) {
1512 fetchResponse = await plugin["fetchDidSucceed"
1513 /* FETCH_DID_SUCCEED */
1514 ].call(plugin, {
1515 event,
1516 request: pluginFilteredRequest,
1517 response: fetchResponse
1518 });
1519
1520 if ("dev" !== 'production') {
1521 if (fetchResponse) {
1522 finalAssertExports.isInstance(fetchResponse, Response, {
1523 moduleName: 'Plugin',
1524 funcName: "fetchDidSucceed"
1525 /* FETCH_DID_SUCCEED */
1526 ,
1527 isReturnValueProblem: true
1528 });
1529 }
1530 }
1531 }
1532 }
1533
1534 return fetchResponse;
1535 } catch (error) {
1536 {
1537 logger.error(`Network request for ` + `'${getFriendlyURL(request.url)}' threw an error.`, error);
1538 }
1539
1540 for (const plugin of failedFetchPlugins) {
1541 await plugin["fetchDidFail"
1542 /* FETCH_DID_FAIL */
1543 ].call(plugin, {
1544 error,
1545 event,
1546 originalRequest: originalRequest.clone(),
1547 request: pluginFilteredRequest.clone()
1548 });
1549 }
1550
1551 throw error;
1552 }
1553 };
1554
1555 const fetchWrapper = {
1556 fetch: wrappedFetch
1557 };
1558
1559 /*
1560 Copyright 2019 Google LLC
1561 Use of this source code is governed by an MIT-style
1562 license that can be found in the LICENSE file or at
1563 https://opensource.org/licenses/MIT.
1564 */
1565 /**
1566 * Returns a promise that resolves and the passed number of milliseconds.
1567 * This utility is an async/await-friendly version of `setTimeout`.
1568 *
1569 * @param {number} ms
1570 * @return {Promise}
1571 * @private
1572 */
1573
1574 function timeout(ms) {
1575 return new Promise(resolve => setTimeout(resolve, ms));
1576 }
1577
1578 /*
1579 Copyright 2019 Google LLC
1580 Use of this source code is governed by an MIT-style
1581 license that can be found in the LICENSE file or at
1582 https://opensource.org/licenses/MIT.
1583 */
1584 const MAX_RETRY_TIME = 2000;
1585 /**
1586 * Returns a promise that resolves to a window client matching the passed
1587 * `resultingClientId`. For browsers that don't support `resultingClientId`
1588 * or if waiting for the resulting client to apper takes too long, resolve to
1589 * `undefined`.
1590 *
1591 * @param {string} [resultingClientId]
1592 * @return {Promise<Client|undefined>}
1593 * @private
1594 */
1595
1596 async function resultingClientExists(resultingClientId) {
1597 if (!resultingClientId) {
1598 return;
1599 }
1600
1601 let existingWindows = await self.clients.matchAll({
1602 type: 'window'
1603 });
1604 const existingWindowIds = new Set(existingWindows.map(w => w.id));
1605 let resultingWindow;
1606 const startTime = performance.now(); // Only wait up to `MAX_RETRY_TIME` to find a matching client.
1607
1608 while (performance.now() - startTime < MAX_RETRY_TIME) {
1609 existingWindows = await self.clients.matchAll({
1610 type: 'window'
1611 });
1612 resultingWindow = existingWindows.find(w => {
1613 if (resultingClientId) {
1614 // If we have a `resultingClientId`, we can match on that.
1615 return w.id === resultingClientId;
1616 } else {
1617 // Otherwise match on finding a window not in `existingWindowIds`.
1618 return !existingWindowIds.has(w.id);
1619 }
1620 });
1621
1622 if (resultingWindow) {
1623 break;
1624 } // Sleep for 100ms and retry.
1625
1626
1627 await timeout(100);
1628 }
1629
1630 return resultingWindow;
1631 }
1632
1633 /*
1634 Copyright 2018 Google LLC
1635
1636 Use of this source code is governed by an MIT-style
1637 license that can be found in the LICENSE file or at
1638 https://opensource.org/licenses/MIT.
1639 */
1640
1641 var _private = /*#__PURE__*/Object.freeze({
1642 __proto__: null,
1643 assert: finalAssertExports,
1644 cacheNames: cacheNames,
1645 cacheWrapper: cacheWrapper,
1646 canConstructReadableStream: canConstructReadableStream,
1647 canConstructResponseFromBodyStream: canConstructResponseFromBodyStream,
1648 dontWaitFor: dontWaitFor,
1649 DBWrapper: DBWrapper,
1650 Deferred: Deferred,
1651 deleteDatabase: deleteDatabase,
1652 executeQuotaErrorCallbacks: executeQuotaErrorCallbacks,
1653 fetchWrapper: fetchWrapper,
1654 getFriendlyURL: getFriendlyURL,
1655 logger: logger,
1656 resultingClientExists: resultingClientExists,
1657 timeout: timeout,
1658 WorkboxError: WorkboxError
1659 });
1660
1661 /*
1662 Copyright 2019 Google LLC
1663
1664 Use of this source code is governed by an MIT-style
1665 license that can be found in the LICENSE file or at
1666 https://opensource.org/licenses/MIT.
1667 */
1668 /**
1669 * Get the current cache names and prefix/suffix used by Workbox.
1670 *
1671 * `cacheNames.precache` is used for precached assets,
1672 * `cacheNames.googleAnalytics` is used by `workbox-google-analytics` to
1673 * store `analytics.js`, and `cacheNames.runtime` is used for everything else.
1674 *
1675 * `cacheNames.prefix` can be used to retrieve just the current prefix value.
1676 * `cacheNames.suffix` can be used to retrieve just the current suffix value.
1677 *
1678 * @return {Object} An object with `precache`, `runtime`, `prefix`, and
1679 * `googleAnalytics` properties.
1680 *
1681 * @memberof module:workbox-core
1682 */
1683
1684 const cacheNames$1 = {
1685 get googleAnalytics() {
1686 return cacheNames.getGoogleAnalyticsName();
1687 },
1688
1689 get precache() {
1690 return cacheNames.getPrecacheName();
1691 },
1692
1693 get prefix() {
1694 return cacheNames.getPrefix();
1695 },
1696
1697 get runtime() {
1698 return cacheNames.getRuntimeName();
1699 },
1700
1701 get suffix() {
1702 return cacheNames.getSuffix();
1703 }
1704
1705 };
1706
1707 /*
1708 Copyright 2019 Google LLC
1709
1710 Use of this source code is governed by an MIT-style
1711 license that can be found in the LICENSE file or at
1712 https://opensource.org/licenses/MIT.
1713 */
1714 /**
1715 * Allows developers to copy a response and modify its `headers`, `status`,
1716 * or `statusText` values (the values settable via a
1717 * [`ResponseInit`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Response/Response#Syntax}
1718 * object in the constructor).
1719 * To modify these values, pass a function as the second argument. That
1720 * function will be invoked with a single object with the response properties
1721 * `{headers, status, statusText}`. The return value of this function will
1722 * be used as the `ResponseInit` for the new `Response`. To change the values
1723 * either modify the passed parameter(s) and return it, or return a totally
1724 * new object.
1725 *
1726 * @param {Response} response
1727 * @param {Function} modifier
1728 * @memberof module:workbox-core
1729 */
1730
1731 async function copyResponse(response, modifier) {
1732 const clonedResponse = response.clone(); // Create a fresh `ResponseInit` object by cloning the headers.
1733
1734 const responseInit = {
1735 headers: new Headers(clonedResponse.headers),
1736 status: clonedResponse.status,
1737 statusText: clonedResponse.statusText
1738 }; // Apply any user modifications.
1739
1740 const modifiedResponseInit = modifier ? modifier(responseInit) : responseInit; // Create the new response from the body stream and `ResponseInit`
1741 // modifications. Note: not all browsers support the Response.body stream,
1742 // so fall back to reading the entire body into memory as a blob.
1743
1744 const body = canConstructResponseFromBodyStream() ? clonedResponse.body : await clonedResponse.blob();
1745 return new Response(body, modifiedResponseInit);
1746 }
1747
1748 /*
1749 Copyright 2019 Google LLC
1750
1751 Use of this source code is governed by an MIT-style
1752 license that can be found in the LICENSE file or at
1753 https://opensource.org/licenses/MIT.
1754 */
1755 /**
1756 * Claim any currently available clients once the service worker
1757 * becomes active. This is normally used in conjunction with `skipWaiting()`.
1758 *
1759 * @memberof module:workbox-core
1760 */
1761
1762 function clientsClaim() {
1763 self.addEventListener('activate', () => self.clients.claim());
1764 }
1765
1766 /*
1767 Copyright 2019 Google LLC
1768
1769 Use of this source code is governed by an MIT-style
1770 license that can be found in the LICENSE file or at
1771 https://opensource.org/licenses/MIT.
1772 */
1773 /**
1774 * Modifies the default cache names used by the Workbox packages.
1775 * Cache names are generated as `<prefix>-<Cache Name>-<suffix>`.
1776 *
1777 * @param {Object} details
1778 * @param {Object} [details.prefix] The string to add to the beginning of
1779 * the precache and runtime cache names.
1780 * @param {Object} [details.suffix] The string to add to the end of
1781 * the precache and runtime cache names.
1782 * @param {Object} [details.precache] The cache name to use for precache
1783 * caching.
1784 * @param {Object} [details.runtime] The cache name to use for runtime caching.
1785 * @param {Object} [details.googleAnalytics] The cache name to use for
1786 * `workbox-google-analytics` caching.
1787 *
1788 * @memberof module:workbox-core
1789 */
1790
1791 function setCacheNameDetails(details) {
1792 {
1793 Object.keys(details).forEach(key => {
1794 finalAssertExports.isType(details[key], 'string', {
1795 moduleName: 'workbox-core',
1796 funcName: 'setCacheNameDetails',
1797 paramName: `details.${key}`
1798 });
1799 });
1800
1801 if ('precache' in details && details['precache'].length === 0) {
1802 throw new WorkboxError('invalid-cache-name', {
1803 cacheNameId: 'precache',
1804 value: details['precache']
1805 });
1806 }
1807
1808 if ('runtime' in details && details['runtime'].length === 0) {
1809 throw new WorkboxError('invalid-cache-name', {
1810 cacheNameId: 'runtime',
1811 value: details['runtime']
1812 });
1813 }
1814
1815 if ('googleAnalytics' in details && details['googleAnalytics'].length === 0) {
1816 throw new WorkboxError('invalid-cache-name', {
1817 cacheNameId: 'googleAnalytics',
1818 value: details['googleAnalytics']
1819 });
1820 }
1821 }
1822
1823 cacheNames.updateDetails(details);
1824 }
1825
1826 /*
1827 Copyright 2019 Google LLC
1828
1829 Use of this source code is governed by an MIT-style
1830 license that can be found in the LICENSE file or at
1831 https://opensource.org/licenses/MIT.
1832 */
1833 /**
1834 * Force a service worker to activate immediately, instead of
1835 * [waiting](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#waiting)
1836 * for existing clients to close.
1837 *
1838 * @memberof module:workbox-core
1839 */
1840
1841 function skipWaiting() {
1842 // We need to explicitly call `self.skipWaiting()` here because we're
1843 // shadowing `skipWaiting` with this local function.
1844 self.addEventListener('install', () => self.skipWaiting());
1845 }
1846
1847 exports._private = _private;
1848 exports.cacheNames = cacheNames$1;
1849 exports.clientsClaim = clientsClaim;
1850 exports.copyResponse = copyResponse;
1851 exports.registerQuotaErrorCallback = registerQuotaErrorCallback;
1852 exports.setCacheNameDetails = setCacheNameDetails;
1853 exports.skipWaiting = skipWaiting;
1854
1855 return exports;
1856
1857}({}));
1858