UNPKG

38.7 kBJavaScriptView Raw
1this.workbox = this.workbox || {};
2this.workbox.precaching = (function (DBWrapper_mjs,logger_mjs,cacheNames_mjs,WorkboxError_mjs,fetchWrapper_mjs,cacheWrapper_mjs,assert_mjs,getFriendlyURL_mjs) {
3 'use strict';
4
5 try {
6 self.workbox.v['workbox:precaching:3.6.3'] = 1;
7 } catch (e) {} // eslint-disable-line
8
9 /*
10 Copyright 2017 Google Inc.
11
12 Licensed under the Apache License, Version 2.0 (the "License");
13 you may not use this file except in compliance with the License.
14 You may obtain a copy of the License at
15
16 https://www.apache.org/licenses/LICENSE-2.0
17
18 Unless required by applicable law or agreed to in writing, software
19 distributed under the License is distributed on an "AS IS" BASIS,
20 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 See the License for the specific language governing permissions and
22 limitations under the License.
23 */
24
25 /**
26 * Used as a consistent way of referencing a URL to precache.
27 *
28 * @private
29 * @memberof module:workbox-precaching
30 */
31 class PrecacheEntry {
32 /**
33 * This class ensures all cache list entries are consistent and
34 * adds cache busting if required.
35 *
36 * @param {*} originalInput
37 * @param {string} url
38 * @param {string} revision
39 * @param {boolean} shouldCacheBust
40 */
41 constructor(originalInput, url, revision, shouldCacheBust) {
42 this._originalInput = originalInput;
43 this._entryId = url;
44 this._revision = revision;
45 const requestAsCacheKey = new Request(url, { credentials: 'same-origin' });
46 this._cacheRequest = requestAsCacheKey;
47 this._networkRequest = shouldCacheBust ? this._cacheBustRequest(requestAsCacheKey) : requestAsCacheKey;
48 }
49
50 /**
51 * This method will either use Request.cache option OR append a cache
52 * busting parameter to the URL.
53 *
54 * @param {Request} request The request to cache bust
55 * @return {Request} A cachebusted Request
56 *
57 * @private
58 */
59 _cacheBustRequest(request) {
60 let url = request.url;
61 const requestOptions = {
62 credentials: 'same-origin'
63 };
64 if ('cache' in Request.prototype) {
65 // Make use of the Request cache mode where we can.
66 // Reload skips the HTTP cache for outgoing requests and updates
67 // the cache with the returned response.
68 requestOptions.cache = 'reload';
69 } else {
70 const parsedURL = new URL(url, location);
71
72 // This is done so the minifier can mangle 'global.encodeURIComponent'
73 const _encodeURIComponent = encodeURIComponent;
74
75 parsedURL.search += (parsedURL.search ? '&' : '') + _encodeURIComponent(`_workbox-cache-bust`) + '=' + _encodeURIComponent(this._revision);
76 url = parsedURL.toString();
77 }
78
79 return new Request(url, requestOptions);
80 }
81 }
82
83 /*
84 Copyright 2017 Google Inc.
85
86 Licensed under the Apache License, Version 2.0 (the "License");
87 you may not use this file except in compliance with the License.
88 You may obtain a copy of the License at
89
90 https://www.apache.org/licenses/LICENSE-2.0
91
92 Unless required by applicable law or agreed to in writing, software
93 distributed under the License is distributed on an "AS IS" BASIS,
94 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
95 See the License for the specific language governing permissions and
96 limitations under the License.
97 */
98
99 // Allows minifier to mangle this name
100 const REVISON_IDB_FIELD = 'revision';
101 const URL_IDB_FIELD = 'url';
102 const DB_STORE_NAME = 'precached-details-models';
103 /**
104 * This model will track the relevant information of entries that
105 * are cached and their matching revision details.
106 *
107 * @private
108 */
109 class PrecachedDetailsModel {
110 /**
111 * Construct a new model for a specific cache.
112 *
113 * @param {string} dbName
114 * @private
115 */
116 constructor(dbName) {
117 // This ensures the db name contains only letters, numbers, '-', '_' and '$'
118 const filteredDBName = dbName.replace(/[^\w-]/g, '_');
119 this._db = new DBWrapper_mjs.DBWrapper(filteredDBName, 2, {
120 onupgradeneeded: this._handleUpgrade
121 });
122 }
123
124 /**
125 * Should perform an upgrade of indexedDB.
126 *
127 * @param {Event} evt
128 *
129 * @private
130 */
131 _handleUpgrade(evt) {
132 const db = evt.target.result;
133 if (evt.oldVersion < 2) {
134 // IndexedDB version 1 used both 'workbox-precaching' and
135 // 'precached-details-model' before upgrading to version 2.
136 // Delete them and create a new store with latest schema.
137 if (db.objectStoreNames.contains('workbox-precaching')) {
138 db.deleteObjectStore('workbox-precaching');
139 }
140 if (db.objectStoreNames.contains(DB_STORE_NAME)) {
141 db.deleteObjectStore(DB_STORE_NAME);
142 }
143 }
144 db.createObjectStore(DB_STORE_NAME);
145 }
146
147 /**
148 * Check if an entry is already cached. Returns false if
149 * the entry isn't cached or the revision has changed.
150 *
151 * @param {string} cacheName
152 * @param {PrecacheEntry} precacheEntry
153 * @return {boolean}
154 *
155 * @private
156 */
157 _isEntryCached(cacheName, precacheEntry) {
158 var _this = this;
159
160 return babelHelpers.asyncToGenerator(function* () {
161 const revisionDetails = yield _this._getRevision(precacheEntry._entryId);
162 if (revisionDetails !== precacheEntry._revision) {
163 return false;
164 }
165
166 const openCache = yield caches.open(cacheName);
167 const cachedResponse = yield openCache.match(precacheEntry._cacheRequest);
168 return !!cachedResponse;
169 })();
170 }
171
172 /**
173 * @return {Promise<Array>}
174 *
175 * @private
176 */
177 _getAllEntries() {
178 var _this2 = this;
179
180 return babelHelpers.asyncToGenerator(function* () {
181 return yield _this2._db.getAllMatching(DB_STORE_NAME, {
182 includeKeys: true
183 });
184 })();
185 }
186
187 /**
188 * Get the current revision details.
189 *
190 * @param {Object} entryId
191 * @return {Promise<string|null>}
192 *
193 * @private
194 */
195 _getRevision(entryId) {
196 var _this3 = this;
197
198 return babelHelpers.asyncToGenerator(function* () {
199 const data = yield _this3._db.get(DB_STORE_NAME, entryId);
200 return data ? data[REVISON_IDB_FIELD] : null;
201 })();
202 }
203
204 /**
205 * Add an entry to the details model.
206 *
207 * @param {PrecacheEntry} precacheEntry
208 *
209 * @private
210 */
211 _addEntry(precacheEntry) {
212 var _this4 = this;
213
214 return babelHelpers.asyncToGenerator(function* () {
215 yield _this4._db.put(DB_STORE_NAME, {
216 [REVISON_IDB_FIELD]: precacheEntry._revision,
217 [URL_IDB_FIELD]: precacheEntry._cacheRequest.url
218 }, precacheEntry._entryId);
219 })();
220 }
221
222 /**
223 * Delete entry from details model.
224 *
225 * @param {string} entryId
226 *
227 * @private
228 */
229 _deleteEntry(entryId) {
230 var _this5 = this;
231
232 return babelHelpers.asyncToGenerator(function* () {
233 yield _this5._db.delete(DB_STORE_NAME, entryId);
234 })();
235 }
236 }
237
238 /*
239 Copyright 2017 Google Inc.
240
241 Licensed under the Apache License, Version 2.0 (the "License");
242 you may not use this file except in compliance with the License.
243 You may obtain a copy of the License at
244
245 https://www.apache.org/licenses/LICENSE-2.0
246
247 Unless required by applicable law or agreed to in writing, software
248 distributed under the License is distributed on an "AS IS" BASIS,
249 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
250 See the License for the specific language governing permissions and
251 limitations under the License.
252 */
253
254 /**
255 * This method will print out a warning if a precache entry doesn't have
256 * a `revision` value.
257 *
258 * This is common if the asset if revisioned in the url like `index.1234.css`.
259 *
260 * @param {Map} entriesMap
261 *
262 * @private
263 * @memberof module:workbox-preaching
264 */
265 var showWarningsIfNeeded = (entriesMap => {
266 const urlOnlyEntries = [];
267 entriesMap.forEach(entry => {
268 if (typeof entry === 'string' || !entry._originalInput.revision) {
269 urlOnlyEntries.push(entry._originalInput);
270 }
271 });
272
273 if (urlOnlyEntries.length === 0) {
274 // No warnings needed.
275 return;
276 }
277
278 logger_mjs.logger.groupCollapsed('Are your precached assets revisioned?');
279
280 const urlsList = urlOnlyEntries.map(urlOnlyEntry => {
281 return ` - ${JSON.stringify(urlOnlyEntry)}`;
282 }).join(`\n`);
283
284 logger_mjs.logger.warn(`The following precache entries might not be revisioned:` + `\n\n` + urlsList + `\n\n`);
285
286 logger_mjs.logger.unprefixed.warn(`You can learn more about why this might be a ` + `problem here: https://developers.google.com/web/tools/workbox/modules/workbox-precaching`);
287
288 logger_mjs.logger.groupEnd();
289 });
290
291 /*
292 Copyright 2017 Google Inc.
293
294 Licensed under the Apache License, Version 2.0 (the "License");
295 you may not use this file except in compliance with the License.
296 You may obtain a copy of the License at
297
298 https://www.apache.org/licenses/LICENSE-2.0
299
300 Unless required by applicable law or agreed to in writing, software
301 distributed under the License is distributed on an "AS IS" BASIS,
302 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
303 See the License for the specific language governing permissions and
304 limitations under the License.
305 */
306
307 /**
308 * @param {string} groupTitle
309 * @param {Array<PrecacheEntry>} entries
310 *
311 * @private
312 */
313 const _nestedGroup = (groupTitle, entries) => {
314 if (entries.length === 0) {
315 return;
316 }
317
318 logger_mjs.logger.groupCollapsed(groupTitle);
319
320 entries.forEach(entry => {
321 logger_mjs.logger.log(entry._originalInput);
322 });
323
324 logger_mjs.logger.groupEnd();
325 };
326
327 /**
328 * @param {Array<Object>} entriesToPrecache
329 * @param {Array<Object>} alreadyPrecachedEntries
330 *
331 * @private
332 * @memberof module:workbox-precachig
333 */
334 var printInstallDetails = ((entriesToPrecache, alreadyPrecachedEntries) => {
335 // Goal is to print the message:
336 // Precaching X files.
337 // Or:
338 // Precaching X files. Y files were cached and up-to-date.
339
340 const precachedCount = entriesToPrecache.length;
341 const alreadyPrecachedCount = alreadyPrecachedEntries.length;
342 let printText = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`;
343 if (alreadyPrecachedCount > 0) {
344 printText += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`;
345 }
346
347 logger_mjs.logger.groupCollapsed(printText);
348
349 _nestedGroup(`View precached URLs.`, entriesToPrecache);
350 _nestedGroup(`View URLs that were already precached.`, alreadyPrecachedEntries);
351 logger_mjs.logger.groupEnd();
352 });
353
354 /*
355 Copyright 2017 Google Inc.
356
357 Licensed under the Apache License, Version 2.0 (the "License");
358 you may not use this file except in compliance with the License.
359 You may obtain a copy of the License at
360
361 https://www.apache.org/licenses/LICENSE-2.0
362
363 Unless required by applicable law or agreed to in writing, software
364 distributed under the License is distributed on an "AS IS" BASIS,
365 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
366 See the License for the specific language governing permissions and
367 limitations under the License.
368 */
369
370 const logGroup = (groupTitle, urls) => {
371 logger_mjs.logger.groupCollapsed(groupTitle);
372
373 urls.forEach(url => {
374 logger_mjs.logger.log(url);
375 });
376
377 logger_mjs.logger.groupEnd();
378 };
379
380 /**
381 * @param {Array<string>} deletedCacheRequests
382 * @param {Array<string>} deletedRevisionDetails
383 *
384 * @private
385 * @memberof module:workbox-precachig
386 */
387 var printCleanupDetails = ((deletedCacheRequests, deletedRevisionDetails) => {
388 if (deletedCacheRequests.length === 0 && deletedRevisionDetails.length === 0) {
389 return;
390 }
391
392 const cacheDeleteCount = deletedCacheRequests.length;
393 const revisionDeleteCount = deletedRevisionDetails.length;
394
395 const cacheDeleteText = `${cacheDeleteCount} cached ` + `request${cacheDeleteCount === 1 ? ' was' : 's were'} deleted`;
396 const revisionDeleteText = `${revisionDeleteCount} ` + `${revisionDeleteCount === 1 ? 'entry' : 'entries'} ` + `${revisionDeleteCount === 1 ? 'was' : 'were'} deleted from IndexedDB.`;
397
398 logger_mjs.logger.groupCollapsed(`During precaching cleanup, ${cacheDeleteText} and ${revisionDeleteText}`);
399
400 logGroup('Deleted Cache Requests', deletedCacheRequests);
401 logGroup('Revision Details Deleted from DB', deletedRevisionDetails);
402
403 logger_mjs.logger.groupEnd();
404 });
405
406 /*
407 Copyright 2017 Google Inc.
408
409 Licensed under the Apache License, Version 2.0 (the "License");
410 you may not use this file except in compliance with the License.
411 You may obtain a copy of the License at
412
413 https://www.apache.org/licenses/LICENSE-2.0
414
415 Unless required by applicable law or agreed to in writing, software
416 distributed under the License is distributed on an "AS IS" BASIS,
417 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
418 See the License for the specific language governing permissions and
419 limitations under the License.
420 */
421
422 /**
423 * @param {Response} response
424 * @return {Response}
425 *
426 * @private
427 * @memberof module:workbox-precaching
428 */
429 const cleanRedirect = (() => {
430 var _ref = babelHelpers.asyncToGenerator(function* (response) {
431 const clonedResponse = response.clone();
432
433 // Not all browsers support the Response.body stream, so fall back
434 // to reading the entire body into memory as a blob.
435 const bodyPromise = 'body' in clonedResponse ? Promise.resolve(clonedResponse.body) : clonedResponse.blob();
436
437 const body = yield bodyPromise;
438
439 // new Response() is happy when passed either a stream or a Blob.
440 return new Response(body, {
441 headers: clonedResponse.headers,
442 status: clonedResponse.status,
443 statusText: clonedResponse.statusText
444 });
445 });
446
447 return function cleanRedirect(_x) {
448 return _ref.apply(this, arguments);
449 };
450 })();
451
452 /*
453 Copyright 2017 Google Inc.
454
455 Licensed under the Apache License, Version 2.0 (the "License");
456 you may not use this file except in compliance with the License.
457 You may obtain a copy of the License at
458
459 https://www.apache.org/licenses/LICENSE-2.0
460
461 Unless required by applicable law or agreed to in writing, software
462 distributed under the License is distributed on an "AS IS" BASIS,
463 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
464 See the License for the specific language governing permissions and
465 limitations under the License.
466 */
467
468 /**
469 * Performs efficient precaching of assets.
470 *
471 * @memberof workbox.precaching
472 */
473 class PrecacheController {
474 /**
475 * Create a new PrecacheController.
476 *
477 * @param {string} cacheName
478 */
479 constructor(cacheName) {
480 this._cacheName = cacheNames_mjs.cacheNames.getPrecacheName(cacheName);
481 this._entriesToCacheMap = new Map();
482 this._precacheDetailsModel = new PrecachedDetailsModel(this._cacheName);
483 }
484
485 /**
486 * This method will add items to the precache list, removing duplicates
487 * and ensuring the information is valid.
488 *
489 * @param {
490 * Array<module:workbox-precaching.PrecacheController.PrecacheEntry|string>
491 * } entries Array of entries to
492 * precache.
493 */
494 addToCacheList(entries) {
495 {
496 assert_mjs.assert.isArray(entries, {
497 moduleName: 'workbox-precaching',
498 className: 'PrecacheController',
499 funcName: 'addToCacheList',
500 paramName: 'entries'
501 });
502 }
503
504 entries.map(userEntry => {
505 this._addEntryToCacheList(this._parseEntry(userEntry));
506 });
507 }
508
509 /**
510 * This method returns a precache entry.
511 *
512 * @private
513 * @param {string|Object} input
514 * @return {PrecacheEntry}
515 */
516 _parseEntry(input) {
517 switch (typeof input) {
518 case 'string':
519 {
520 {
521 if (input.length === 0) {
522 throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-unexpected-type', {
523 entry: input
524 });
525 }
526 }
527
528 return new PrecacheEntry(input, input, input);
529 }
530 case 'object':
531 {
532 {
533 if (!input || !input.url) {
534 throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-unexpected-type', {
535 entry: input
536 });
537 }
538 }
539
540 return new PrecacheEntry(input, input.url, input.revision || input.url, !!input.revision);
541 }
542 default:
543 throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-unexpected-type', {
544 entry: input
545 });
546 }
547 }
548
549 /**
550 * Adds an entry to the precache list, accounting for possible duplicates.
551 *
552 * @private
553 * @param {PrecacheEntry} entryToAdd
554 */
555 _addEntryToCacheList(entryToAdd) {
556 // Check if the entry is already part of the map
557 const existingEntry = this._entriesToCacheMap.get(entryToAdd._entryId);
558 if (!existingEntry) {
559 this._entriesToCacheMap.set(entryToAdd._entryId, entryToAdd);
560 return;
561 }
562
563 // Duplicates are fine, but make sure the revision information
564 // is the same.
565 if (existingEntry._revision !== entryToAdd._revision) {
566 throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-conflicting-entries', {
567 firstEntry: existingEntry._originalInput,
568 secondEntry: entryToAdd._originalInput
569 });
570 }
571 }
572
573 /**
574 * Call this method from a service work install event to start
575 * precaching assets.
576 *
577 * @param {Object} options
578 * @param {boolean} [options.suppressWarnings] Suppress warning messages.
579 * @param {Event} [options.event] The install event (if needed).
580 * @param {Array<Object>} [options.plugins] Plugins to be used for fetching
581 * and caching during install.
582 * @return {Promise<workbox.precaching.InstallResult>}
583 */
584 install({ suppressWarnings = false, event, plugins } = {}) {
585 var _this = this;
586
587 return babelHelpers.asyncToGenerator(function* () {
588 {
589 if (suppressWarnings !== true) {
590 showWarningsIfNeeded(_this._entriesToCacheMap);
591 }
592
593 if (plugins) {
594 assert_mjs.assert.isArray(plugins, {
595 moduleName: 'workbox-precaching',
596 className: 'PrecacheController',
597 funcName: 'install',
598 paramName: 'plugins'
599 });
600 }
601 }
602
603 // Empty the temporary cache.
604 // NOTE: We remove all entries instead of deleting the cache as the cache
605 // may be marked for deletion but still exist until a later stage
606 // resulting in unexpected behavior of being deletect when all references
607 // are dropped.
608 // https://github.com/GoogleChrome/workbox/issues/1368
609 const tempCache = yield caches.open(_this._getTempCacheName());
610 const requests = yield tempCache.keys();
611 yield Promise.all(requests.map(function (request) {
612 return tempCache.delete(request);
613 }));
614
615 const entriesToPrecache = [];
616 const entriesAlreadyPrecached = [];
617
618 for (const precacheEntry of _this._entriesToCacheMap.values()) {
619 if (yield _this._precacheDetailsModel._isEntryCached(_this._cacheName, precacheEntry)) {
620 entriesAlreadyPrecached.push(precacheEntry);
621 } else {
622 entriesToPrecache.push(precacheEntry);
623 }
624 }
625
626 // Wait for all requests to be cached.
627 yield Promise.all(entriesToPrecache.map(function (precacheEntry) {
628 return _this._cacheEntryInTemp({ event, plugins, precacheEntry });
629 }));
630
631 {
632 printInstallDetails(entriesToPrecache, entriesAlreadyPrecached);
633 }
634
635 return {
636 updatedEntries: entriesToPrecache,
637 notUpdatedEntries: entriesAlreadyPrecached
638 };
639 })();
640 }
641
642 /**
643 * Takes the current set of temporary files and moves them to the final
644 * cache, deleting the temporary cache once copying is complete.
645 *
646 * @param {Object} options
647 * @param {Array<Object>} options.plugins Plugins to be used for fetching
648 * and caching during install.
649 * @return {
650 * Promise<workbox.precaching.CleanupResult>}
651 * Resolves with an object containing details of the deleted cache requests
652 * and precache revision details.
653 */
654 activate(options = {}) {
655 var _this2 = this;
656
657 return babelHelpers.asyncToGenerator(function* () {
658 const tempCache = yield caches.open(_this2._getTempCacheName());
659
660 const requests = yield tempCache.keys();
661 // Process each request/response one at a time, deleting the temporary entry
662 // when done, to help avoid triggering quota errors.
663 for (const request of requests) {
664 const response = yield tempCache.match(request);
665 yield cacheWrapper_mjs.cacheWrapper.put({
666 cacheName: _this2._cacheName,
667 request,
668 response,
669 plugins: options.plugins
670 });
671 yield tempCache.delete(request);
672 }
673
674 return _this2._cleanup();
675 })();
676 }
677
678 /**
679 * Returns the name of the temporary cache.
680 *
681 * @return {string}
682 *
683 * @private
684 */
685 _getTempCacheName() {
686 return `${this._cacheName}-temp`;
687 }
688
689 /**
690 * Requests the entry and saves it to the cache if the response
691 * is valid.
692 *
693 * @private
694 * @param {Object} options
695 * @param {BaseCacheEntry} options.precacheEntry The entry to fetch and cache.
696 * @param {Event} [options.event] The install event (if passed).
697 * @param {Array<Object>} [options.plugins] An array of plugins to apply to
698 * fetch and caching.
699 * @return {Promise<boolean>} Returns a promise that resolves once the entry
700 * has been fetched and cached or skipped if no update is needed. The
701 * promise resolves with true if the entry was cached / updated and
702 * false if the entry is already cached and up-to-date.
703 */
704 _cacheEntryInTemp({ precacheEntry, event, plugins }) {
705 var _this3 = this;
706
707 return babelHelpers.asyncToGenerator(function* () {
708 let response = yield fetchWrapper_mjs.fetchWrapper.fetch({
709 request: precacheEntry._networkRequest,
710 event,
711 fetchOptions: null,
712 plugins
713 });
714
715 if (response.redirected) {
716 response = yield cleanRedirect(response);
717 }
718
719 yield cacheWrapper_mjs.cacheWrapper.put({
720 cacheName: _this3._getTempCacheName(),
721 request: precacheEntry._cacheRequest,
722 response,
723 event,
724 plugins
725 });
726
727 yield _this3._precacheDetailsModel._addEntry(precacheEntry);
728
729 return true;
730 })();
731 }
732
733 /**
734 * Compare the URLs and determines which assets are no longer required
735 * in the cache.
736 *
737 * This should be called in the service worker activate event.
738 *
739 * @return {
740 * Promise<workbox.precaching.CleanupResult>}
741 * Resolves with an object containing details of the deleted cache requests
742 * and precache revision details.
743 *
744 * @private
745 */
746 _cleanup() {
747 var _this4 = this;
748
749 return babelHelpers.asyncToGenerator(function* () {
750 const expectedCacheUrls = [];
751 _this4._entriesToCacheMap.forEach(function (entry) {
752 const fullUrl = new URL(entry._cacheRequest.url, location).toString();
753 expectedCacheUrls.push(fullUrl);
754 });
755
756 const [deletedCacheRequests, deletedRevisionDetails] = yield Promise.all([_this4._cleanupCache(expectedCacheUrls), _this4._cleanupDetailsModel(expectedCacheUrls)]);
757
758 {
759 printCleanupDetails(deletedCacheRequests, deletedRevisionDetails);
760 }
761
762 return {
763 deletedCacheRequests,
764 deletedRevisionDetails
765 };
766 })();
767 }
768
769 /**
770 * Goes through all the cache entries and removes any that are
771 * outdated.
772 *
773 * @private
774 * @param {Array<string>} expectedCacheUrls Array of URLs that are
775 * expected to be cached.
776 * @return {Promise<Array<string>>} Resolves to an array of URLs
777 * of cached requests that were deleted.
778 */
779 _cleanupCache(expectedCacheUrls) {
780 var _this5 = this;
781
782 return babelHelpers.asyncToGenerator(function* () {
783 if (!(yield caches.has(_this5._cacheName))) {
784 // Cache doesn't exist, so nothing to delete
785 return [];
786 }
787
788 const cache = yield caches.open(_this5._cacheName);
789 const cachedRequests = yield cache.keys();
790 const cachedRequestsToDelete = cachedRequests.filter(function (cachedRequest) {
791 return !expectedCacheUrls.includes(new URL(cachedRequest.url, location).toString());
792 });
793
794 yield Promise.all(cachedRequestsToDelete.map(function (cacheUrl) {
795 return cache.delete(cacheUrl);
796 }));
797
798 return cachedRequestsToDelete.map(function (request) {
799 return request.url;
800 });
801 })();
802 }
803
804 /**
805 * Goes through all entries in indexedDB and removes any that are outdated.
806 *
807 * @private
808 * @param {Array<string>} expectedCacheUrls Array of URLs that are
809 * expected to be cached.
810 * @return {Promise<Array<string>>} Resolves to an array of URLs removed
811 * from indexedDB.
812 */
813 _cleanupDetailsModel(expectedCacheUrls) {
814 var _this6 = this;
815
816 return babelHelpers.asyncToGenerator(function* () {
817 const revisionedEntries = yield _this6._precacheDetailsModel._getAllEntries();
818 const detailsToDelete = revisionedEntries.filter(function (entry) {
819 const fullUrl = new URL(entry.value.url, location).toString();
820 return !expectedCacheUrls.includes(fullUrl);
821 });
822
823 yield Promise.all(detailsToDelete.map(function (entry) {
824 return _this6._precacheDetailsModel._deleteEntry(entry.primaryKey);
825 }));
826 return detailsToDelete.map(function (entry) {
827 return entry.value.url;
828 });
829 })();
830 }
831
832 /**
833 * Returns an array of fully qualified URL's that will be precached.
834 *
835 * @return {Array<string>} An array of URLs.
836 */
837 getCachedUrls() {
838 return Array.from(this._entriesToCacheMap.keys()).map(url => new URL(url, location).href);
839 }
840 }
841
842 /*
843 Copyright 2017 Google Inc.
844
845 Licensed under the Apache License, Version 2.0 (the "License");
846 you may not use this file except in compliance with the License.
847 You may obtain a copy of the License at
848
849 https://www.apache.org/licenses/LICENSE-2.0
850
851 Unless required by applicable law or agreed to in writing, software
852 distributed under the License is distributed on an "AS IS" BASIS,
853 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
854 See the License for the specific language governing permissions and
855 limitations under the License.
856 */
857
858 var publicAPI = /*#__PURE__*/Object.freeze({
859 PrecacheController: PrecacheController
860 });
861
862 /*
863 Copyright 2017 Google Inc.
864
865 Licensed under the Apache License, Version 2.0 (the "License");
866 you may not use this file except in compliance with the License.
867 You may obtain a copy of the License at
868
869 https://www.apache.org/licenses/LICENSE-2.0
870
871 Unless required by applicable law or agreed to in writing, software
872 distributed under the License is distributed on an "AS IS" BASIS,
873 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
874 See the License for the specific language governing permissions and
875 limitations under the License.
876 */
877
878 {
879 assert_mjs.assert.isSwEnv('workbox-precaching');
880 }
881
882 let installActivateListenersAdded = false;
883 let fetchListenersAdded = false;
884 let suppressWarnings = false;
885 let plugins = [];
886
887 const cacheName = cacheNames_mjs.cacheNames.getPrecacheName();
888 const precacheController = new PrecacheController(cacheName);
889
890 const _removeIgnoreUrlParams = (origUrlObject, ignoreUrlParametersMatching) => {
891 // Exclude initial '?'
892 const searchString = origUrlObject.search.slice(1);
893
894 // Split into an array of 'key=value' strings
895 const keyValueStrings = searchString.split('&');
896 const keyValuePairs = keyValueStrings.map(keyValueString => {
897 // Split each 'key=value' string into a [key, value] array
898 return keyValueString.split('=');
899 });
900 const filteredKeyValuesPairs = keyValuePairs.filter(keyValuePair => {
901 return ignoreUrlParametersMatching.every(ignoredRegex => {
902 // Return true iff the key doesn't match any of the regexes.
903 return !ignoredRegex.test(keyValuePair[0]);
904 });
905 });
906 const filteredStrings = filteredKeyValuesPairs.map(keyValuePair => {
907 // Join each [key, value] array into a 'key=value' string
908 return keyValuePair.join('=');
909 });
910
911 // Join the array of 'key=value' strings into a string with '&' in
912 // between each
913 const urlClone = new URL(origUrlObject);
914 urlClone.search = filteredStrings.join('&');
915 return urlClone;
916 };
917
918 /**
919 * This function will take the request URL and manipulate it based on the
920 * configuration options.
921 *
922 * @param {string} url
923 * @param {Object} options
924 * @return {string|null} Returns the URL in the cache that matches the request
925 * if available, other null.
926 *
927 * @private
928 */
929 const _getPrecachedUrl = (url, {
930 ignoreUrlParametersMatching = [/^utm_/],
931 directoryIndex = 'index.html',
932 cleanUrls = true,
933 urlManipulation = null
934 } = {}) => {
935 const urlObject = new URL(url, location);
936
937 // Change '/some-url#123' => '/some-url'
938 urlObject.hash = '';
939
940 const urlWithoutIgnoredParams = _removeIgnoreUrlParams(urlObject, ignoreUrlParametersMatching);
941
942 let urlsToAttempt = [
943 // Test the URL that was fetched
944 urlObject,
945 // Test the URL without search params
946 urlWithoutIgnoredParams];
947
948 // Test the URL with a directory index
949 if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) {
950 const directoryUrl = new URL(urlWithoutIgnoredParams);
951 directoryUrl.pathname += directoryIndex;
952 urlsToAttempt.push(directoryUrl);
953 }
954
955 // Test the URL with a '.html' extension
956 if (cleanUrls) {
957 const cleanUrl = new URL(urlWithoutIgnoredParams);
958 cleanUrl.pathname += '.html';
959 urlsToAttempt.push(cleanUrl);
960 }
961
962 if (urlManipulation) {
963 const additionalUrls = urlManipulation({ url: urlObject });
964 urlsToAttempt = urlsToAttempt.concat(additionalUrls);
965 }
966
967 const cachedUrls = precacheController.getCachedUrls();
968 for (const possibleUrl of urlsToAttempt) {
969 if (cachedUrls.indexOf(possibleUrl.href) !== -1) {
970 // It's a perfect match
971 {
972 logger_mjs.logger.debug(`Precaching found a match for ` + getFriendlyURL_mjs.getFriendlyURL(possibleUrl.toString()));
973 }
974 return possibleUrl.href;
975 }
976 }
977
978 return null;
979 };
980
981 const moduleExports = {};
982
983 /**
984 * Add items to the precache list, removing any duplicates and
985 * store the files in the
986 * ["precache cache"]{@link module:workbox-core.cacheNames} when the service
987 * worker installs.
988 *
989 * This method can be called multiple times.
990 *
991 * Please note: This method **will not** serve any of the cached files for you,
992 * it only precaches files. To respond to a network request you call
993 * [addRoute()]{@link module:workbox-precaching.addRoute}.
994 *
995 * If you have a single array of files to precache, you can just call
996 * [precacheAndRoute()]{@link module:workbox-precaching.precacheAndRoute}.
997 *
998 * @param {Array<Object|string>} entries Array of entries to precache.
999 *
1000 * @alias workbox.precaching.precache
1001 */
1002 moduleExports.precache = entries => {
1003 precacheController.addToCacheList(entries);
1004
1005 if (installActivateListenersAdded || entries.length <= 0) {
1006 return;
1007 }
1008
1009 installActivateListenersAdded = true;
1010 self.addEventListener('install', event => {
1011 event.waitUntil(precacheController.install({
1012 event,
1013 plugins,
1014 suppressWarnings
1015 }));
1016 });
1017 self.addEventListener('activate', event => {
1018 event.waitUntil(precacheController.activate({
1019 event,
1020 plugins
1021 }));
1022 });
1023 };
1024
1025 /**
1026 * Add a `fetch` listener to the service worker that will
1027 * respond to
1028 * [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests}
1029 * with precached assets.
1030 *
1031 * Requests for assets that aren't precached, the `FetchEvent` will not be
1032 * responded to, allowing the event to fall through to other `fetch` event
1033 * listeners.
1034 *
1035 * @param {Object} options
1036 * @param {string} [options.directoryIndex=index.html] The `directoryIndex` will
1037 * check cache entries for a URLs ending with '/' to see if there is a hit when
1038 * appending the `directoryIndex` value.
1039 * @param {Array<RegExp>} [options.ignoreUrlParametersMatching=[/^utm_/]] An
1040 * array of regex's to remove search params when looking for a cache match.
1041 * @param {boolean} [options.cleanUrls=true] The `cleanUrls` option will
1042 * check the cache for the URL with a `.html` added to the end of the end.
1043 * @param {workbox.precaching~urlManipulation} [options.urlManipulation]
1044 * This is a function that should take a URL and return an array of
1045 * alternative URL's that should be checked for precache matches.
1046 *
1047 * @alias workbox.precaching.addRoute
1048 */
1049 moduleExports.addRoute = options => {
1050 if (fetchListenersAdded) {
1051 // TODO: Throw error here.
1052 return;
1053 }
1054
1055 fetchListenersAdded = true;
1056 self.addEventListener('fetch', event => {
1057 const precachedUrl = _getPrecachedUrl(event.request.url, options);
1058 if (!precachedUrl) {
1059 {
1060 logger_mjs.logger.debug(`Precaching found no match for ` + getFriendlyURL_mjs.getFriendlyURL(event.request.url));
1061 }
1062 return;
1063 }
1064
1065 let responsePromise = caches.open(cacheName).then(cache => {
1066 return cache.match(precachedUrl);
1067 }).then(cachedResponse => {
1068 if (cachedResponse) {
1069 return cachedResponse;
1070 }
1071
1072 // Fall back to the network if we don't have a cached response (perhaps
1073 // due to manual cache cleanup).
1074 {
1075 logger_mjs.logger.debug(`The precached response for ` + `${getFriendlyURL_mjs.getFriendlyURL(precachedUrl)} in ${cacheName} was not found. ` + `Falling back to the network instead.`);
1076 }
1077
1078 return fetch(precachedUrl);
1079 });
1080
1081 {
1082 responsePromise = responsePromise.then(response => {
1083 // Workbox is going to handle the route.
1084 // print the routing details to the console.
1085 logger_mjs.logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL_mjs.getFriendlyURL(event.request.url));
1086 logger_mjs.logger.log(`Serving the precached url: ${precachedUrl}`);
1087
1088 // The Request and Response objects contains a great deal of
1089 // information, hide it under a group in case developers want to see it.
1090 logger_mjs.logger.groupCollapsed(`View request details here.`);
1091 logger_mjs.logger.unprefixed.log(event.request);
1092 logger_mjs.logger.groupEnd();
1093
1094 logger_mjs.logger.groupCollapsed(`View response details here.`);
1095 logger_mjs.logger.unprefixed.log(response);
1096 logger_mjs.logger.groupEnd();
1097
1098 logger_mjs.logger.groupEnd();
1099 return response;
1100 });
1101 }
1102 event.respondWith(responsePromise);
1103 });
1104 };
1105
1106 /**
1107 * This method will add entries to the precache list and add a route to
1108 * respond to fetch events.
1109 *
1110 * This is a convenience method that will call
1111 * [precache()]{@link module:workbox-precaching.precache} and
1112 * [addRoute()]{@link module:workbox-precaching.addRoute} in a single call.
1113 *
1114 * @param {Array<Object|string>} entries Array of entries to precache.
1115 * @param {Object} options See
1116 * [addRoute() options]{@link module:workbox-precaching.addRoute}.
1117 *
1118 * @alias workbox.precaching.precacheAndRoute
1119 */
1120 moduleExports.precacheAndRoute = (entries, options) => {
1121 moduleExports.precache(entries);
1122 moduleExports.addRoute(options);
1123 };
1124
1125 /**
1126 * Warnings will be logged if any of the precached assets are entered without
1127 * a `revision` property. This is extremely dangerous if the URL's aren't
1128 * revisioned. However, the warnings can be supressed with this method.
1129 *
1130 * @param {boolean} suppress
1131 *
1132 * @alias workbox.precaching.suppressWarnings
1133 */
1134 moduleExports.suppressWarnings = suppress => {
1135 suppressWarnings = suppress;
1136 };
1137
1138 /**
1139 * Add plugins to precaching.
1140 *
1141 * @param {Array<Object>} newPlugins
1142 *
1143 * @alias workbox.precaching.addPlugins
1144 */
1145 moduleExports.addPlugins = newPlugins => {
1146 plugins = plugins.concat(newPlugins);
1147 };
1148
1149 /*
1150 Copyright 2017 Google Inc.
1151
1152 Licensed under the Apache License, Version 2.0 (the "License");
1153 you may not use this file except in compliance with the License.
1154 You may obtain a copy of the License at
1155
1156 https://www.apache.org/licenses/LICENSE-2.0
1157
1158 Unless required by applicable law or agreed to in writing, software
1159 distributed under the License is distributed on an "AS IS" BASIS,
1160 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1161 See the License for the specific language governing permissions and
1162 limitations under the License.
1163 */
1164
1165 const finalExport = Object.assign(moduleExports, publicAPI);
1166
1167 return finalExport;
1168
1169}(workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private));
1170
1171//# sourceMappingURL=workbox-precaching.dev.js.map