UNPKG

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