jekyll-pwa-workbox 5.1.4 → 5.1.41

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/lib/jekyll-pwa-workbox.rb +1 -1
  3. data/lib/vendor/workbox-v5.1.4/workbox-background-sync.dev.js +818 -818
  4. data/lib/vendor/workbox-v5.1.4/workbox-background-sync.prod.js +2 -2
  5. data/lib/vendor/workbox-v5.1.4/workbox-broadcast-update.dev.js +288 -288
  6. data/lib/vendor/workbox-v5.1.4/workbox-broadcast-update.prod.js +2 -2
  7. data/lib/vendor/workbox-v5.1.4/workbox-cacheable-response.dev.js +191 -191
  8. data/lib/vendor/workbox-v5.1.4/workbox-cacheable-response.prod.js +2 -2
  9. data/lib/vendor/workbox-v5.1.4/workbox-core.dev.js +1858 -1858
  10. data/lib/vendor/workbox-v5.1.4/workbox-core.prod.js +2 -2
  11. data/lib/vendor/workbox-v5.1.4/workbox-expiration.dev.js +649 -649
  12. data/lib/vendor/workbox-v5.1.4/workbox-expiration.prod.js +2 -2
  13. data/lib/vendor/workbox-v5.1.4/workbox-navigation-preload.dev.js +102 -102
  14. data/lib/vendor/workbox-v5.1.4/workbox-navigation-preload.prod.js +2 -2
  15. data/lib/vendor/workbox-v5.1.4/workbox-offline-ga.dev.js +235 -235
  16. data/lib/vendor/workbox-v5.1.4/workbox-offline-ga.prod.js +2 -2
  17. data/lib/vendor/workbox-v5.1.4/workbox-precaching.dev.js +1210 -1210
  18. data/lib/vendor/workbox-v5.1.4/workbox-precaching.prod.js +2 -2
  19. data/lib/vendor/workbox-v5.1.4/workbox-range-requests.dev.js +262 -262
  20. data/lib/vendor/workbox-v5.1.4/workbox-range-requests.prod.js +2 -2
  21. data/lib/vendor/workbox-v5.1.4/workbox-routing.dev.js +923 -923
  22. data/lib/vendor/workbox-v5.1.4/workbox-routing.prod.js +2 -2
  23. data/lib/vendor/workbox-v5.1.4/workbox-strategies.dev.js +923 -923
  24. data/lib/vendor/workbox-v5.1.4/workbox-strategies.prod.js +2 -2
  25. data/lib/vendor/workbox-v5.1.4/workbox-streams.dev.js +318 -318
  26. data/lib/vendor/workbox-v5.1.4/workbox-streams.prod.js +2 -2
  27. data/lib/vendor/workbox-v5.1.4/workbox-sw.js +2 -2
  28. data/lib/vendor/workbox-v5.1.4/workbox-window.dev.es5.mjs +1125 -1125
  29. data/lib/vendor/workbox-v5.1.4/workbox-window.dev.mjs +943 -943
  30. data/lib/vendor/workbox-v5.1.4/workbox-window.dev.umd.js +1136 -1136
  31. data/lib/vendor/workbox-v5.1.4/workbox-window.prod.es5.mjs +2 -2
  32. data/lib/vendor/workbox-v5.1.4/workbox-window.prod.mjs +2 -2
  33. data/lib/vendor/workbox-v5.1.4/workbox-window.prod.umd.js +2 -2
  34. metadata +2 -2
@@ -1,2 +1,2 @@
1
- this.workbox=this.workbox||{},this.workbox.core=function(e){"use strict";try{self["workbox:core:5.1.4"]&&_()}catch(e){}const t=(e,...t)=>{let n=e;return t.length>0&&(n+=" :: "+JSON.stringify(t)),n};class n extends Error{constructor(e,n){super(t(e,n)),this.name=e,this.details=n}}const s=new Set;const r={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},i=e=>[r.prefix,e,r.suffix].filter(e=>e&&e.length>0).join("-"),o={updateDetails:e=>{(e=>{for(const t of Object.keys(r))e(t)})(t=>{"string"==typeof e[t]&&(r[t]=e[t])})},getGoogleAnalyticsName:e=>e||i(r.googleAnalytics),getPrecacheName:e=>e||i(r.precache),getPrefix:()=>r.prefix,getRuntimeName:e=>e||i(r.runtime),getSuffix:()=>r.suffix};async function a(){for(const e of s)await e()}const c=e=>new URL(String(e),location.href).href.replace(new RegExp("^"+location.origin),""),u=(e,t)=>e.filter(e=>t in e),l=async({request:e,mode:t,plugins:n=[]})=>{const s=u(n,"cacheKeyWillBeUsed");let r=e;for(const e of s)r=await e.cacheKeyWillBeUsed.call(e,{mode:t,request:r}),"string"==typeof r&&(r=new Request(r));return r},f=async({cacheName:e,request:t,event:n,matchOptions:s,plugins:r=[]})=>{const i=await self.caches.open(e),o=await l({plugins:r,request:t,mode:"read"});let a=await i.match(o,s);for(const t of r)if("cachedResponseWillBeUsed"in t){const r=t.cachedResponseWillBeUsed;a=await r.call(t,{cacheName:e,event:n,matchOptions:s,cachedResponse:a,request:o})}return a},h={put:async({cacheName:e,request:t,response:s,event:r,plugins:i=[],matchOptions:o})=>{const h=await l({plugins:i,request:t,mode:"write"});if(!s)throw new n("cache-put-with-no-response",{url:c(h.url)});const w=await(async({request:e,response:t,event:n,plugins:s=[]})=>{let r=t,i=!1;for(const t of s)if("cacheWillUpdate"in t){i=!0;const s=t.cacheWillUpdate;if(r=await s.call(t,{request:e,response:r,event:n}),!r)break}return i||(r=r&&200===r.status?r:void 0),r||null})({event:r,plugins:i,response:s,request:h});if(!w)return;const p=await self.caches.open(e),d=u(i,"cacheDidUpdate"),g=d.length>0?await f({cacheName:e,matchOptions:o,request:h}):null;try{await p.put(h,w)}catch(e){throw"QuotaExceededError"===e.name&&await a(),e}for(const t of d)await t.cacheDidUpdate.call(t,{cacheName:e,event:r,oldResponse:g,newResponse:w,request:h})},match:f};let w,p;function d(){if(void 0===p){const e=new Response("");if("body"in e)try{new Response(e.body),p=!0}catch(e){p=!1}p=!1}return p}class g{constructor(e,t,{onupgradeneeded:n,onversionchange:s}={}){this.t=null,this.s=e,this.i=t,this.o=n,this.u=s||(()=>this.close())}get db(){return this.t}async open(){if(!this.t)return this.t=await new Promise((e,t)=>{let n=!1;setTimeout(()=>{n=!0,t(new Error("The open request was blocked and timed out"))},this.OPEN_TIMEOUT);const s=indexedDB.open(this.s,this.i);s.onerror=()=>t(s.error),s.onupgradeneeded=e=>{n?(s.transaction.abort(),s.result.close()):"function"==typeof this.o&&this.o(e)},s.onsuccess=()=>{const t=s.result;n?t.close():(t.onversionchange=this.u.bind(this),e(t))}}),this}async getKey(e,t){return(await this.getAllKeys(e,t,1))[0]}async getAll(e,t,n){return await this.getAllMatching(e,{query:t,count:n})}async getAllKeys(e,t,n){return(await this.getAllMatching(e,{query:t,count:n,includeKeys:!0})).map(e=>e.key)}async getAllMatching(e,{index:t,query:n=null,direction:s="next",count:r,includeKeys:i=!1}={}){return await this.transaction([e],"readonly",(o,a)=>{const c=o.objectStore(e),u=t?c.index(t):c,l=[],f=u.openCursor(n,s);f.onsuccess=()=>{const e=f.result;e?(l.push(i?e:e.value),r&&l.length>=r?a(l):e.continue()):a(l)}})}async transaction(e,t,n){return await this.open(),await new Promise((s,r)=>{const i=this.t.transaction(e,t);i.onabort=()=>r(i.error),i.oncomplete=()=>s(),n(i,e=>s(e))})}async l(e,t,n,...s){return await this.transaction([t],n,(n,r)=>{const i=n.objectStore(t),o=i[e].apply(i,s);o.onsuccess=()=>r(o.result)})}close(){this.t&&(this.t.close(),this.t=null)}}g.prototype.OPEN_TIMEOUT=2e3;const y={readonly:["get","count","getKey","getAll","getAllKeys"],readwrite:["add","put","clear","delete"]};for(const[e,t]of Object.entries(y))for(const n of t)n in IDBObjectStore.prototype&&(g.prototype[n]=async function(t,...s){return await this.l(n,t,e,...s)});const m={fetch:async({request:e,fetchOptions:t,event:s,plugins:r=[]})=>{if("string"==typeof e&&(e=new Request(e)),s instanceof FetchEvent&&s.preloadResponse){const e=await s.preloadResponse;if(e)return e}const i=u(r,"fetchDidFail"),o=i.length>0?e.clone():null;try{for(const t of r)if("requestWillFetch"in t){const n=t.requestWillFetch,r=e.clone();e=await n.call(t,{request:r,event:s})}}catch(e){throw new n("plugin-error-request-will-fetch",{thrownError:e})}const a=e.clone();try{let n;n="navigate"===e.mode?await fetch(e):await fetch(e,t);for(const e of r)"fetchDidSucceed"in e&&(n=await e.fetchDidSucceed.call(e,{event:s,request:a,response:n}));return n}catch(e){for(const t of i)await t.fetchDidFail.call(t,{error:e,event:s,originalRequest:o.clone(),request:a.clone()});throw e}}};function q(e){return new Promise(t=>setTimeout(t,e))}var v=Object.freeze({__proto__:null,assert:null,cacheNames:o,cacheWrapper:h,canConstructReadableStream:function(){if(void 0===w)try{new ReadableStream({start(){}}),w=!0}catch(e){w=!1}return w},canConstructResponseFromBodyStream:d,dontWaitFor:function(e){e.then(()=>{})},DBWrapper:g,Deferred:class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},deleteDatabase:async e=>{await new Promise((t,n)=>{const s=indexedDB.deleteDatabase(e);s.onerror=()=>{n(s.error)},s.onblocked=()=>{n(new Error("Delete blocked"))},s.onsuccess=()=>{t()}})},executeQuotaErrorCallbacks:a,fetchWrapper:m,getFriendlyURL:c,logger:null,resultingClientExists:async function(e){if(!e)return;let t=await self.clients.matchAll({type:"window"});const n=new Set(t.map(e=>e.id));let s;const r=performance.now();for(;performance.now()-r<2e3&&(t=await self.clients.matchAll({type:"window"}),s=t.find(t=>e?t.id===e:!n.has(t.id)),!s);)await q(100);return s},timeout:q,WorkboxError:n});const x={get googleAnalytics(){return o.getGoogleAnalyticsName()},get precache(){return o.getPrecacheName()},get prefix(){return o.getPrefix()},get runtime(){return o.getRuntimeName()},get suffix(){return o.getSuffix()}};return e._private=v,e.cacheNames=x,e.clientsClaim=function(){self.addEventListener("activate",()=>self.clients.claim())},e.copyResponse=async function(e,t){const n=e.clone(),s={headers:new Headers(n.headers),status:n.status,statusText:n.statusText},r=t?t(s):s,i=d()?n.body:await n.blob();return new Response(i,r)},e.registerQuotaErrorCallback=function(e){s.add(e)},e.setCacheNameDetails=function(e){o.updateDetails(e)},e.skipWaiting=function(){self.addEventListener("install",()=>self.skipWaiting())},e}({});
2
- //# sourceMappingURL=workbox-core.prod.js.map
1
+ this.workbox=this.workbox||{},this.workbox.core=function(e){"use strict";try{self["workbox:core:5.1.4"]&&_()}catch(e){}const t=(e,...t)=>{let n=e;return t.length>0&&(n+=" :: "+JSON.stringify(t)),n};class n extends Error{constructor(e,n){super(t(e,n)),this.name=e,this.details=n}}const s=new Set;const r={googleAnalytics:"googleAnalytics",precache:"precache-v2",prefix:"workbox",runtime:"runtime",suffix:"undefined"!=typeof registration?registration.scope:""},i=e=>[r.prefix,e,r.suffix].filter(e=>e&&e.length>0).join("-"),o={updateDetails:e=>{(e=>{for(const t of Object.keys(r))e(t)})(t=>{"string"==typeof e[t]&&(r[t]=e[t])})},getGoogleAnalyticsName:e=>e||i(r.googleAnalytics),getPrecacheName:e=>e||i(r.precache),getPrefix:()=>r.prefix,getRuntimeName:e=>e||i(r.runtime),getSuffix:()=>r.suffix};async function a(){for(const e of s)await e()}const c=e=>new URL(String(e),location.href).href.replace(new RegExp("^"+location.origin),""),u=(e,t)=>e.filter(e=>t in e),l=async({request:e,mode:t,plugins:n=[]})=>{const s=u(n,"cacheKeyWillBeUsed");let r=e;for(const e of s)r=await e.cacheKeyWillBeUsed.call(e,{mode:t,request:r}),"string"==typeof r&&(r=new Request(r));return r},f=async({cacheName:e,request:t,event:n,matchOptions:s,plugins:r=[]})=>{const i=await self.caches.open(e),o=await l({plugins:r,request:t,mode:"read"});let a=await i.match(o,s);for(const t of r)if("cachedResponseWillBeUsed"in t){const r=t.cachedResponseWillBeUsed;a=await r.call(t,{cacheName:e,event:n,matchOptions:s,cachedResponse:a,request:o})}return a},h={put:async({cacheName:e,request:t,response:s,event:r,plugins:i=[],matchOptions:o})=>{const h=await l({plugins:i,request:t,mode:"write"});if(!s)throw new n("cache-put-with-no-response",{url:c(h.url)});const w=await(async({request:e,response:t,event:n,plugins:s=[]})=>{let r=t,i=!1;for(const t of s)if("cacheWillUpdate"in t){i=!0;const s=t.cacheWillUpdate;if(r=await s.call(t,{request:e,response:r,event:n}),!r)break}return i||(r=r&&200===r.status?r:void 0),r||null})({event:r,plugins:i,response:s,request:h});if(!w)return;const p=await self.caches.open(e),d=u(i,"cacheDidUpdate"),g=d.length>0?await f({cacheName:e,matchOptions:o,request:h}):null;try{await p.put(h,w)}catch(e){throw"QuotaExceededError"===e.name&&await a(),e}for(const t of d)await t.cacheDidUpdate.call(t,{cacheName:e,event:r,oldResponse:g,newResponse:w,request:h})},match:f};let w,p;function d(){if(void 0===p){const e=new Response("");if("body"in e)try{new Response(e.body),p=!0}catch(e){p=!1}p=!1}return p}class g{constructor(e,t,{onupgradeneeded:n,onversionchange:s}={}){this.t=null,this.s=e,this.i=t,this.o=n,this.u=s||(()=>this.close())}get db(){return this.t}async open(){if(!this.t)return this.t=await new Promise((e,t)=>{let n=!1;setTimeout(()=>{n=!0,t(new Error("The open request was blocked and timed out"))},this.OPEN_TIMEOUT);const s=indexedDB.open(this.s,this.i);s.onerror=()=>t(s.error),s.onupgradeneeded=e=>{n?(s.transaction.abort(),s.result.close()):"function"==typeof this.o&&this.o(e)},s.onsuccess=()=>{const t=s.result;n?t.close():(t.onversionchange=this.u.bind(this),e(t))}}),this}async getKey(e,t){return(await this.getAllKeys(e,t,1))[0]}async getAll(e,t,n){return await this.getAllMatching(e,{query:t,count:n})}async getAllKeys(e,t,n){return(await this.getAllMatching(e,{query:t,count:n,includeKeys:!0})).map(e=>e.key)}async getAllMatching(e,{index:t,query:n=null,direction:s="next",count:r,includeKeys:i=!1}={}){return await this.transaction([e],"readonly",(o,a)=>{const c=o.objectStore(e),u=t?c.index(t):c,l=[],f=u.openCursor(n,s);f.onsuccess=()=>{const e=f.result;e?(l.push(i?e:e.value),r&&l.length>=r?a(l):e.continue()):a(l)}})}async transaction(e,t,n){return await this.open(),await new Promise((s,r)=>{const i=this.t.transaction(e,t);i.onabort=()=>r(i.error),i.oncomplete=()=>s(),n(i,e=>s(e))})}async l(e,t,n,...s){return await this.transaction([t],n,(n,r)=>{const i=n.objectStore(t),o=i[e].apply(i,s);o.onsuccess=()=>r(o.result)})}close(){this.t&&(this.t.close(),this.t=null)}}g.prototype.OPEN_TIMEOUT=2e3;const y={readonly:["get","count","getKey","getAll","getAllKeys"],readwrite:["add","put","clear","delete"]};for(const[e,t]of Object.entries(y))for(const n of t)n in IDBObjectStore.prototype&&(g.prototype[n]=async function(t,...s){return await this.l(n,t,e,...s)});const m={fetch:async({request:e,fetchOptions:t,event:s,plugins:r=[]})=>{if("string"==typeof e&&(e=new Request(e)),s instanceof FetchEvent&&s.preloadResponse){const e=await s.preloadResponse;if(e)return e}const i=u(r,"fetchDidFail"),o=i.length>0?e.clone():null;try{for(const t of r)if("requestWillFetch"in t){const n=t.requestWillFetch,r=e.clone();e=await n.call(t,{request:r,event:s})}}catch(e){throw new n("plugin-error-request-will-fetch",{thrownError:e})}const a=e.clone();try{let n;n="navigate"===e.mode?await fetch(e):await fetch(e,t);for(const e of r)"fetchDidSucceed"in e&&(n=await e.fetchDidSucceed.call(e,{event:s,request:a,response:n}));return n}catch(e){for(const t of i)await t.fetchDidFail.call(t,{error:e,event:s,originalRequest:o.clone(),request:a.clone()});throw e}}};function q(e){return new Promise(t=>setTimeout(t,e))}var v=Object.freeze({__proto__:null,assert:null,cacheNames:o,cacheWrapper:h,canConstructReadableStream:function(){if(void 0===w)try{new ReadableStream({start(){}}),w=!0}catch(e){w=!1}return w},canConstructResponseFromBodyStream:d,dontWaitFor:function(e){e.then(()=>{})},DBWrapper:g,Deferred:class{constructor(){this.promise=new Promise((e,t)=>{this.resolve=e,this.reject=t})}},deleteDatabase:async e=>{await new Promise((t,n)=>{const s=indexedDB.deleteDatabase(e);s.onerror=()=>{n(s.error)},s.onblocked=()=>{n(new Error("Delete blocked"))},s.onsuccess=()=>{t()}})},executeQuotaErrorCallbacks:a,fetchWrapper:m,getFriendlyURL:c,logger:null,resultingClientExists:async function(e){if(!e)return;let t=await self.clients.matchAll({type:"window"});const n=new Set(t.map(e=>e.id));let s;const r=performance.now();for(;performance.now()-r<2e3&&(t=await self.clients.matchAll({type:"window"}),s=t.find(t=>e?t.id===e:!n.has(t.id)),!s);)await q(100);return s},timeout:q,WorkboxError:n});const x={get googleAnalytics(){return o.getGoogleAnalyticsName()},get precache(){return o.getPrecacheName()},get prefix(){return o.getPrefix()},get runtime(){return o.getRuntimeName()},get suffix(){return o.getSuffix()}};return e._private=v,e.cacheNames=x,e.clientsClaim=function(){self.addEventListener("activate",()=>self.clients.claim())},e.copyResponse=async function(e,t){const n=e.clone(),s={headers:new Headers(n.headers),status:n.status,statusText:n.statusText},r=t?t(s):s,i=d()?n.body:await n.blob();return new Response(i,r)},e.registerQuotaErrorCallback=function(e){s.add(e)},e.setCacheNameDetails=function(e){o.updateDetails(e)},e.skipWaiting=function(){self.addEventListener("install",()=>self.skipWaiting())},e}({});
2
+ //# sourceMappingURL=workbox-core.prod.js.map
@@ -1,649 +1,649 @@
1
- this.workbox = this.workbox || {};
2
- this.workbox.expiration = (function (exports, assert_js, dontWaitFor_js, logger_js, WorkboxError_js, DBWrapper_js, deleteDatabase_js, cacheNames_js, getFriendlyURL_js, registerQuotaErrorCallback_js) {
3
- 'use strict';
4
-
5
- try {
6
- self['workbox:expiration:5.1.4'] && _();
7
- } catch (e) {}
8
-
9
- /*
10
- Copyright 2018 Google LLC
11
-
12
- Use of this source code is governed by an MIT-style
13
- license that can be found in the LICENSE file or at
14
- https://opensource.org/licenses/MIT.
15
- */
16
- const DB_NAME = 'workbox-expiration';
17
- const OBJECT_STORE_NAME = 'cache-entries';
18
-
19
- const normalizeURL = unNormalizedUrl => {
20
- const url = new URL(unNormalizedUrl, location.href);
21
- url.hash = '';
22
- return url.href;
23
- };
24
- /**
25
- * Returns the timestamp model.
26
- *
27
- * @private
28
- */
29
-
30
-
31
- class CacheTimestampsModel {
32
- /**
33
- *
34
- * @param {string} cacheName
35
- *
36
- * @private
37
- */
38
- constructor(cacheName) {
39
- this._cacheName = cacheName;
40
- this._db = new DBWrapper_js.DBWrapper(DB_NAME, 1, {
41
- onupgradeneeded: event => this._handleUpgrade(event)
42
- });
43
- }
44
- /**
45
- * Should perform an upgrade of indexedDB.
46
- *
47
- * @param {Event} event
48
- *
49
- * @private
50
- */
51
-
52
-
53
- _handleUpgrade(event) {
54
- const db = event.target.result; // TODO(philipwalton): EdgeHTML doesn't support arrays as a keyPath, so we
55
- // have to use the `id` keyPath here and create our own values (a
56
- // concatenation of `url + cacheName`) instead of simply using
57
- // `keyPath: ['url', 'cacheName']`, which is supported in other browsers.
58
-
59
- const objStore = db.createObjectStore(OBJECT_STORE_NAME, {
60
- keyPath: 'id'
61
- }); // TODO(philipwalton): once we don't have to support EdgeHTML, we can
62
- // create a single index with the keyPath `['cacheName', 'timestamp']`
63
- // instead of doing both these indexes.
64
-
65
- objStore.createIndex('cacheName', 'cacheName', {
66
- unique: false
67
- });
68
- objStore.createIndex('timestamp', 'timestamp', {
69
- unique: false
70
- }); // Previous versions of `workbox-expiration` used `this._cacheName`
71
- // as the IDBDatabase name.
72
-
73
- deleteDatabase_js.deleteDatabase(this._cacheName);
74
- }
75
- /**
76
- * @param {string} url
77
- * @param {number} timestamp
78
- *
79
- * @private
80
- */
81
-
82
-
83
- async setTimestamp(url, timestamp) {
84
- url = normalizeURL(url);
85
- const entry = {
86
- url,
87
- timestamp,
88
- cacheName: this._cacheName,
89
- // Creating an ID from the URL and cache name won't be necessary once
90
- // Edge switches to Chromium and all browsers we support work with
91
- // array keyPaths.
92
- id: this._getId(url)
93
- };
94
- await this._db.put(OBJECT_STORE_NAME, entry);
95
- }
96
- /**
97
- * Returns the timestamp stored for a given URL.
98
- *
99
- * @param {string} url
100
- * @return {number}
101
- *
102
- * @private
103
- */
104
-
105
-
106
- async getTimestamp(url) {
107
- const entry = await this._db.get(OBJECT_STORE_NAME, this._getId(url));
108
- return entry.timestamp;
109
- }
110
- /**
111
- * Iterates through all the entries in the object store (from newest to
112
- * oldest) and removes entries once either `maxCount` is reached or the
113
- * entry's timestamp is less than `minTimestamp`.
114
- *
115
- * @param {number} minTimestamp
116
- * @param {number} maxCount
117
- * @return {Array<string>}
118
- *
119
- * @private
120
- */
121
-
122
-
123
- async expireEntries(minTimestamp, maxCount) {
124
- const entriesToDelete = await this._db.transaction(OBJECT_STORE_NAME, 'readwrite', (txn, done) => {
125
- const store = txn.objectStore(OBJECT_STORE_NAME);
126
- const request = store.index('timestamp').openCursor(null, 'prev');
127
- const entriesToDelete = [];
128
- let entriesNotDeletedCount = 0;
129
-
130
- request.onsuccess = () => {
131
- const cursor = request.result;
132
-
133
- if (cursor) {
134
- const result = cursor.value; // TODO(philipwalton): once we can use a multi-key index, we
135
- // won't have to check `cacheName` here.
136
-
137
- if (result.cacheName === this._cacheName) {
138
- // Delete an entry if it's older than the max age or
139
- // if we already have the max number allowed.
140
- if (minTimestamp && result.timestamp < minTimestamp || maxCount && entriesNotDeletedCount >= maxCount) {
141
- // TODO(philipwalton): we should be able to delete the
142
- // entry right here, but doing so causes an iteration
143
- // bug in Safari stable (fixed in TP). Instead we can
144
- // store the keys of the entries to delete, and then
145
- // delete the separate transactions.
146
- // https://github.com/GoogleChrome/workbox/issues/1978
147
- // cursor.delete();
148
- // We only need to return the URL, not the whole entry.
149
- entriesToDelete.push(cursor.value);
150
- } else {
151
- entriesNotDeletedCount++;
152
- }
153
- }
154
-
155
- cursor.continue();
156
- } else {
157
- done(entriesToDelete);
158
- }
159
- };
160
- }); // TODO(philipwalton): once the Safari bug in the following issue is fixed,
161
- // we should be able to remove this loop and do the entry deletion in the
162
- // cursor loop above:
163
- // https://github.com/GoogleChrome/workbox/issues/1978
164
-
165
- const urlsDeleted = [];
166
-
167
- for (const entry of entriesToDelete) {
168
- await this._db.delete(OBJECT_STORE_NAME, entry.id);
169
- urlsDeleted.push(entry.url);
170
- }
171
-
172
- return urlsDeleted;
173
- }
174
- /**
175
- * Takes a URL and returns an ID that will be unique in the object store.
176
- *
177
- * @param {string} url
178
- * @return {string}
179
- *
180
- * @private
181
- */
182
-
183
-
184
- _getId(url) {
185
- // Creating an ID from the URL and cache name won't be necessary once
186
- // Edge switches to Chromium and all browsers we support work with
187
- // array keyPaths.
188
- return this._cacheName + '|' + normalizeURL(url);
189
- }
190
-
191
- }
192
-
193
- /*
194
- Copyright 2018 Google LLC
195
-
196
- Use of this source code is governed by an MIT-style
197
- license that can be found in the LICENSE file or at
198
- https://opensource.org/licenses/MIT.
199
- */
200
- /**
201
- * The `CacheExpiration` class allows you define an expiration and / or
202
- * limit on the number of responses stored in a
203
- * [`Cache`](https://developer.mozilla.org/en-US/docs/Web/API/Cache).
204
- *
205
- * @memberof module:workbox-expiration
206
- */
207
-
208
- class CacheExpiration {
209
- /**
210
- * To construct a new CacheExpiration instance you must provide at least
211
- * one of the `config` properties.
212
- *
213
- * @param {string} cacheName Name of the cache to apply restrictions to.
214
- * @param {Object} config
215
- * @param {number} [config.maxEntries] The maximum number of entries to cache.
216
- * Entries used the least will be removed as the maximum is reached.
217
- * @param {number} [config.maxAgeSeconds] The maximum age of an entry before
218
- * it's treated as stale and removed.
219
- */
220
- constructor(cacheName, config = {}) {
221
- this._isRunning = false;
222
- this._rerunRequested = false;
223
-
224
- {
225
- assert_js.assert.isType(cacheName, 'string', {
226
- moduleName: 'workbox-expiration',
227
- className: 'CacheExpiration',
228
- funcName: 'constructor',
229
- paramName: 'cacheName'
230
- });
231
-
232
- if (!(config.maxEntries || config.maxAgeSeconds)) {
233
- throw new WorkboxError_js.WorkboxError('max-entries-or-age-required', {
234
- moduleName: 'workbox-expiration',
235
- className: 'CacheExpiration',
236
- funcName: 'constructor'
237
- });
238
- }
239
-
240
- if (config.maxEntries) {
241
- assert_js.assert.isType(config.maxEntries, 'number', {
242
- moduleName: 'workbox-expiration',
243
- className: 'CacheExpiration',
244
- funcName: 'constructor',
245
- paramName: 'config.maxEntries'
246
- }); // TODO: Assert is positive
247
- }
248
-
249
- if (config.maxAgeSeconds) {
250
- assert_js.assert.isType(config.maxAgeSeconds, 'number', {
251
- moduleName: 'workbox-expiration',
252
- className: 'CacheExpiration',
253
- funcName: 'constructor',
254
- paramName: 'config.maxAgeSeconds'
255
- }); // TODO: Assert is positive
256
- }
257
- }
258
-
259
- this._maxEntries = config.maxEntries;
260
- this._maxAgeSeconds = config.maxAgeSeconds;
261
- this._cacheName = cacheName;
262
- this._timestampModel = new CacheTimestampsModel(cacheName);
263
- }
264
- /**
265
- * Expires entries for the given cache and given criteria.
266
- */
267
-
268
-
269
- async expireEntries() {
270
- if (this._isRunning) {
271
- this._rerunRequested = true;
272
- return;
273
- }
274
-
275
- this._isRunning = true;
276
- const minTimestamp = this._maxAgeSeconds ? Date.now() - this._maxAgeSeconds * 1000 : 0;
277
- const urlsExpired = await this._timestampModel.expireEntries(minTimestamp, this._maxEntries); // Delete URLs from the cache
278
-
279
- const cache = await self.caches.open(this._cacheName);
280
-
281
- for (const url of urlsExpired) {
282
- await cache.delete(url);
283
- }
284
-
285
- {
286
- if (urlsExpired.length > 0) {
287
- logger_js.logger.groupCollapsed(`Expired ${urlsExpired.length} ` + `${urlsExpired.length === 1 ? 'entry' : 'entries'} and removed ` + `${urlsExpired.length === 1 ? 'it' : 'them'} from the ` + `'${this._cacheName}' cache.`);
288
- logger_js.logger.log(`Expired the following ${urlsExpired.length === 1 ? 'URL' : 'URLs'}:`);
289
- urlsExpired.forEach(url => logger_js.logger.log(` ${url}`));
290
- logger_js.logger.groupEnd();
291
- } else {
292
- logger_js.logger.debug(`Cache expiration ran and found no entries to remove.`);
293
- }
294
- }
295
-
296
- this._isRunning = false;
297
-
298
- if (this._rerunRequested) {
299
- this._rerunRequested = false;
300
- dontWaitFor_js.dontWaitFor(this.expireEntries());
301
- }
302
- }
303
- /**
304
- * Update the timestamp for the given URL. This ensures the when
305
- * removing entries based on maximum entries, most recently used
306
- * is accurate or when expiring, the timestamp is up-to-date.
307
- *
308
- * @param {string} url
309
- */
310
-
311
-
312
- async updateTimestamp(url) {
313
- {
314
- assert_js.assert.isType(url, 'string', {
315
- moduleName: 'workbox-expiration',
316
- className: 'CacheExpiration',
317
- funcName: 'updateTimestamp',
318
- paramName: 'url'
319
- });
320
- }
321
-
322
- await this._timestampModel.setTimestamp(url, Date.now());
323
- }
324
- /**
325
- * Can be used to check if a URL has expired or not before it's used.
326
- *
327
- * This requires a look up from IndexedDB, so can be slow.
328
- *
329
- * Note: This method will not remove the cached entry, call
330
- * `expireEntries()` to remove indexedDB and Cache entries.
331
- *
332
- * @param {string} url
333
- * @return {boolean}
334
- */
335
-
336
-
337
- async isURLExpired(url) {
338
- if (!this._maxAgeSeconds) {
339
- {
340
- throw new WorkboxError_js.WorkboxError(`expired-test-without-max-age`, {
341
- methodName: 'isURLExpired',
342
- paramName: 'maxAgeSeconds'
343
- });
344
- }
345
- } else {
346
- const timestamp = await this._timestampModel.getTimestamp(url);
347
- const expireOlderThan = Date.now() - this._maxAgeSeconds * 1000;
348
- return timestamp < expireOlderThan;
349
- }
350
- }
351
- /**
352
- * Removes the IndexedDB object store used to keep track of cache expiration
353
- * metadata.
354
- */
355
-
356
-
357
- async delete() {
358
- // Make sure we don't attempt another rerun if we're called in the middle of
359
- // a cache expiration.
360
- this._rerunRequested = false;
361
- await this._timestampModel.expireEntries(Infinity); // Expires all.
362
- }
363
-
364
- }
365
-
366
- /*
367
- Copyright 2018 Google LLC
368
-
369
- Use of this source code is governed by an MIT-style
370
- license that can be found in the LICENSE file or at
371
- https://opensource.org/licenses/MIT.
372
- */
373
- /**
374
- * This plugin can be used in the Workbox APIs to regularly enforce a
375
- * limit on the age and / or the number of cached requests.
376
- *
377
- * Whenever a cached request is used or updated, this plugin will look
378
- * at the used Cache and remove any old or extra requests.
379
- *
380
- * When using `maxAgeSeconds`, requests may be used *once* after expiring
381
- * because the expiration clean up will not have occurred until *after* the
382
- * cached request has been used. If the request has a "Date" header, then
383
- * a light weight expiration check is performed and the request will not be
384
- * used immediately.
385
- *
386
- * When using `maxEntries`, the entry least-recently requested will be removed
387
- * from the cache first.
388
- *
389
- * @memberof module:workbox-expiration
390
- */
391
-
392
- class ExpirationPlugin {
393
- /**
394
- * @param {Object} config
395
- * @param {number} [config.maxEntries] The maximum number of entries to cache.
396
- * Entries used the least will be removed as the maximum is reached.
397
- * @param {number} [config.maxAgeSeconds] The maximum age of an entry before
398
- * it's treated as stale and removed.
399
- * @param {boolean} [config.purgeOnQuotaError] Whether to opt this cache in to
400
- * automatic deletion if the available storage quota has been exceeded.
401
- */
402
- constructor(config = {}) {
403
- /**
404
- * A "lifecycle" callback that will be triggered automatically by the
405
- * `workbox-strategies` handlers when a `Response` is about to be returned
406
- * from a [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache) to
407
- * the handler. It allows the `Response` to be inspected for freshness and
408
- * prevents it from being used if the `Response`'s `Date` header value is
409
- * older than the configured `maxAgeSeconds`.
410
- *
411
- * @param {Object} options
412
- * @param {string} options.cacheName Name of the cache the response is in.
413
- * @param {Response} options.cachedResponse The `Response` object that's been
414
- * read from a cache and whose freshness should be checked.
415
- * @return {Response} Either the `cachedResponse`, if it's
416
- * fresh, or `null` if the `Response` is older than `maxAgeSeconds`.
417
- *
418
- * @private
419
- */
420
- this.cachedResponseWillBeUsed = async ({
421
- event,
422
- request,
423
- cacheName,
424
- cachedResponse
425
- }) => {
426
- if (!cachedResponse) {
427
- return null;
428
- }
429
-
430
- const isFresh = this._isResponseDateFresh(cachedResponse); // Expire entries to ensure that even if the expiration date has
431
- // expired, it'll only be used once.
432
-
433
-
434
- const cacheExpiration = this._getCacheExpiration(cacheName);
435
-
436
- dontWaitFor_js.dontWaitFor(cacheExpiration.expireEntries()); // Update the metadata for the request URL to the current timestamp,
437
- // but don't `await` it as we don't want to block the response.
438
-
439
- const updateTimestampDone = cacheExpiration.updateTimestamp(request.url);
440
-
441
- if (event) {
442
- try {
443
- event.waitUntil(updateTimestampDone);
444
- } catch (error) {
445
- {
446
- // The event may not be a fetch event; only log the URL if it is.
447
- if ('request' in event) {
448
- logger_js.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache entry for ` + `'${getFriendlyURL_js.getFriendlyURL(event.request.url)}'.`);
449
- }
450
- }
451
- }
452
- }
453
-
454
- return isFresh ? cachedResponse : null;
455
- };
456
- /**
457
- * A "lifecycle" callback that will be triggered automatically by the
458
- * `workbox-strategies` handlers when an entry is added to a cache.
459
- *
460
- * @param {Object} options
461
- * @param {string} options.cacheName Name of the cache that was updated.
462
- * @param {string} options.request The Request for the cached entry.
463
- *
464
- * @private
465
- */
466
-
467
-
468
- this.cacheDidUpdate = async ({
469
- cacheName,
470
- request
471
- }) => {
472
- {
473
- assert_js.assert.isType(cacheName, 'string', {
474
- moduleName: 'workbox-expiration',
475
- className: 'Plugin',
476
- funcName: 'cacheDidUpdate',
477
- paramName: 'cacheName'
478
- });
479
- assert_js.assert.isInstance(request, Request, {
480
- moduleName: 'workbox-expiration',
481
- className: 'Plugin',
482
- funcName: 'cacheDidUpdate',
483
- paramName: 'request'
484
- });
485
- }
486
-
487
- const cacheExpiration = this._getCacheExpiration(cacheName);
488
-
489
- await cacheExpiration.updateTimestamp(request.url);
490
- await cacheExpiration.expireEntries();
491
- };
492
-
493
- {
494
- if (!(config.maxEntries || config.maxAgeSeconds)) {
495
- throw new WorkboxError_js.WorkboxError('max-entries-or-age-required', {
496
- moduleName: 'workbox-expiration',
497
- className: 'Plugin',
498
- funcName: 'constructor'
499
- });
500
- }
501
-
502
- if (config.maxEntries) {
503
- assert_js.assert.isType(config.maxEntries, 'number', {
504
- moduleName: 'workbox-expiration',
505
- className: 'Plugin',
506
- funcName: 'constructor',
507
- paramName: 'config.maxEntries'
508
- });
509
- }
510
-
511
- if (config.maxAgeSeconds) {
512
- assert_js.assert.isType(config.maxAgeSeconds, 'number', {
513
- moduleName: 'workbox-expiration',
514
- className: 'Plugin',
515
- funcName: 'constructor',
516
- paramName: 'config.maxAgeSeconds'
517
- });
518
- }
519
- }
520
-
521
- this._config = config;
522
- this._maxAgeSeconds = config.maxAgeSeconds;
523
- this._cacheExpirations = new Map();
524
-
525
- if (config.purgeOnQuotaError) {
526
- registerQuotaErrorCallback_js.registerQuotaErrorCallback(() => this.deleteCacheAndMetadata());
527
- }
528
- }
529
- /**
530
- * A simple helper method to return a CacheExpiration instance for a given
531
- * cache name.
532
- *
533
- * @param {string} cacheName
534
- * @return {CacheExpiration}
535
- *
536
- * @private
537
- */
538
-
539
-
540
- _getCacheExpiration(cacheName) {
541
- if (cacheName === cacheNames_js.cacheNames.getRuntimeName()) {
542
- throw new WorkboxError_js.WorkboxError('expire-custom-caches-only');
543
- }
544
-
545
- let cacheExpiration = this._cacheExpirations.get(cacheName);
546
-
547
- if (!cacheExpiration) {
548
- cacheExpiration = new CacheExpiration(cacheName, this._config);
549
-
550
- this._cacheExpirations.set(cacheName, cacheExpiration);
551
- }
552
-
553
- return cacheExpiration;
554
- }
555
- /**
556
- * @param {Response} cachedResponse
557
- * @return {boolean}
558
- *
559
- * @private
560
- */
561
-
562
-
563
- _isResponseDateFresh(cachedResponse) {
564
- if (!this._maxAgeSeconds) {
565
- // We aren't expiring by age, so return true, it's fresh
566
- return true;
567
- } // Check if the 'date' header will suffice a quick expiration check.
568
- // See https://github.com/GoogleChromeLabs/sw-toolbox/issues/164 for
569
- // discussion.
570
-
571
-
572
- const dateHeaderTimestamp = this._getDateHeaderTimestamp(cachedResponse);
573
-
574
- if (dateHeaderTimestamp === null) {
575
- // Unable to parse date, so assume it's fresh.
576
- return true;
577
- } // If we have a valid headerTime, then our response is fresh iff the
578
- // headerTime plus maxAgeSeconds is greater than the current time.
579
-
580
-
581
- const now = Date.now();
582
- return dateHeaderTimestamp >= now - this._maxAgeSeconds * 1000;
583
- }
584
- /**
585
- * This method will extract the data header and parse it into a useful
586
- * value.
587
- *
588
- * @param {Response} cachedResponse
589
- * @return {number|null}
590
- *
591
- * @private
592
- */
593
-
594
-
595
- _getDateHeaderTimestamp(cachedResponse) {
596
- if (!cachedResponse.headers.has('date')) {
597
- return null;
598
- }
599
-
600
- const dateHeader = cachedResponse.headers.get('date');
601
- const parsedDate = new Date(dateHeader);
602
- const headerTime = parsedDate.getTime(); // If the Date header was invalid for some reason, parsedDate.getTime()
603
- // will return NaN.
604
-
605
- if (isNaN(headerTime)) {
606
- return null;
607
- }
608
-
609
- return headerTime;
610
- }
611
- /**
612
- * This is a helper method that performs two operations:
613
- *
614
- * - Deletes *all* the underlying Cache instances associated with this plugin
615
- * instance, by calling caches.delete() on your behalf.
616
- * - Deletes the metadata from IndexedDB used to keep track of expiration
617
- * details for each Cache instance.
618
- *
619
- * When using cache expiration, calling this method is preferable to calling
620
- * `caches.delete()` directly, since this will ensure that the IndexedDB
621
- * metadata is also cleanly removed and open IndexedDB instances are deleted.
622
- *
623
- * Note that if you're *not* using cache expiration for a given cache, calling
624
- * `caches.delete()` and passing in the cache's name should be sufficient.
625
- * There is no Workbox-specific method needed for cleanup in that case.
626
- */
627
-
628
-
629
- async deleteCacheAndMetadata() {
630
- // Do this one at a time instead of all at once via `Promise.all()` to
631
- // reduce the chance of inconsistency if a promise rejects.
632
- for (const [cacheName, cacheExpiration] of this._cacheExpirations) {
633
- await self.caches.delete(cacheName);
634
- await cacheExpiration.delete();
635
- } // Reset this._cacheExpirations to its initial state.
636
-
637
-
638
- this._cacheExpirations = new Map();
639
- }
640
-
641
- }
642
-
643
- exports.CacheExpiration = CacheExpiration;
644
- exports.ExpirationPlugin = ExpirationPlugin;
645
-
646
- return exports;
647
-
648
- }({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core));
649
- //# sourceMappingURL=workbox-expiration.dev.js.map
1
+ this.workbox = this.workbox || {};
2
+ this.workbox.expiration = (function (exports, assert_js, dontWaitFor_js, logger_js, WorkboxError_js, DBWrapper_js, deleteDatabase_js, cacheNames_js, getFriendlyURL_js, registerQuotaErrorCallback_js) {
3
+ 'use strict';
4
+
5
+ try {
6
+ self['workbox:expiration:5.1.4'] && _();
7
+ } catch (e) {}
8
+
9
+ /*
10
+ Copyright 2018 Google LLC
11
+
12
+ Use of this source code is governed by an MIT-style
13
+ license that can be found in the LICENSE file or at
14
+ https://opensource.org/licenses/MIT.
15
+ */
16
+ const DB_NAME = 'workbox-expiration';
17
+ const OBJECT_STORE_NAME = 'cache-entries';
18
+
19
+ const normalizeURL = unNormalizedUrl => {
20
+ const url = new URL(unNormalizedUrl, location.href);
21
+ url.hash = '';
22
+ return url.href;
23
+ };
24
+ /**
25
+ * Returns the timestamp model.
26
+ *
27
+ * @private
28
+ */
29
+
30
+
31
+ class CacheTimestampsModel {
32
+ /**
33
+ *
34
+ * @param {string} cacheName
35
+ *
36
+ * @private
37
+ */
38
+ constructor(cacheName) {
39
+ this._cacheName = cacheName;
40
+ this._db = new DBWrapper_js.DBWrapper(DB_NAME, 1, {
41
+ onupgradeneeded: event => this._handleUpgrade(event)
42
+ });
43
+ }
44
+ /**
45
+ * Should perform an upgrade of indexedDB.
46
+ *
47
+ * @param {Event} event
48
+ *
49
+ * @private
50
+ */
51
+
52
+
53
+ _handleUpgrade(event) {
54
+ const db = event.target.result; // TODO(philipwalton): EdgeHTML doesn't support arrays as a keyPath, so we
55
+ // have to use the `id` keyPath here and create our own values (a
56
+ // concatenation of `url + cacheName`) instead of simply using
57
+ // `keyPath: ['url', 'cacheName']`, which is supported in other browsers.
58
+
59
+ const objStore = db.createObjectStore(OBJECT_STORE_NAME, {
60
+ keyPath: 'id'
61
+ }); // TODO(philipwalton): once we don't have to support EdgeHTML, we can
62
+ // create a single index with the keyPath `['cacheName', 'timestamp']`
63
+ // instead of doing both these indexes.
64
+
65
+ objStore.createIndex('cacheName', 'cacheName', {
66
+ unique: false
67
+ });
68
+ objStore.createIndex('timestamp', 'timestamp', {
69
+ unique: false
70
+ }); // Previous versions of `workbox-expiration` used `this._cacheName`
71
+ // as the IDBDatabase name.
72
+
73
+ deleteDatabase_js.deleteDatabase(this._cacheName);
74
+ }
75
+ /**
76
+ * @param {string} url
77
+ * @param {number} timestamp
78
+ *
79
+ * @private
80
+ */
81
+
82
+
83
+ async setTimestamp(url, timestamp) {
84
+ url = normalizeURL(url);
85
+ const entry = {
86
+ url,
87
+ timestamp,
88
+ cacheName: this._cacheName,
89
+ // Creating an ID from the URL and cache name won't be necessary once
90
+ // Edge switches to Chromium and all browsers we support work with
91
+ // array keyPaths.
92
+ id: this._getId(url)
93
+ };
94
+ await this._db.put(OBJECT_STORE_NAME, entry);
95
+ }
96
+ /**
97
+ * Returns the timestamp stored for a given URL.
98
+ *
99
+ * @param {string} url
100
+ * @return {number}
101
+ *
102
+ * @private
103
+ */
104
+
105
+
106
+ async getTimestamp(url) {
107
+ const entry = await this._db.get(OBJECT_STORE_NAME, this._getId(url));
108
+ return entry.timestamp;
109
+ }
110
+ /**
111
+ * Iterates through all the entries in the object store (from newest to
112
+ * oldest) and removes entries once either `maxCount` is reached or the
113
+ * entry's timestamp is less than `minTimestamp`.
114
+ *
115
+ * @param {number} minTimestamp
116
+ * @param {number} maxCount
117
+ * @return {Array<string>}
118
+ *
119
+ * @private
120
+ */
121
+
122
+
123
+ async expireEntries(minTimestamp, maxCount) {
124
+ const entriesToDelete = await this._db.transaction(OBJECT_STORE_NAME, 'readwrite', (txn, done) => {
125
+ const store = txn.objectStore(OBJECT_STORE_NAME);
126
+ const request = store.index('timestamp').openCursor(null, 'prev');
127
+ const entriesToDelete = [];
128
+ let entriesNotDeletedCount = 0;
129
+
130
+ request.onsuccess = () => {
131
+ const cursor = request.result;
132
+
133
+ if (cursor) {
134
+ const result = cursor.value; // TODO(philipwalton): once we can use a multi-key index, we
135
+ // won't have to check `cacheName` here.
136
+
137
+ if (result.cacheName === this._cacheName) {
138
+ // Delete an entry if it's older than the max age or
139
+ // if we already have the max number allowed.
140
+ if (minTimestamp && result.timestamp < minTimestamp || maxCount && entriesNotDeletedCount >= maxCount) {
141
+ // TODO(philipwalton): we should be able to delete the
142
+ // entry right here, but doing so causes an iteration
143
+ // bug in Safari stable (fixed in TP). Instead we can
144
+ // store the keys of the entries to delete, and then
145
+ // delete the separate transactions.
146
+ // https://github.com/GoogleChrome/workbox/issues/1978
147
+ // cursor.delete();
148
+ // We only need to return the URL, not the whole entry.
149
+ entriesToDelete.push(cursor.value);
150
+ } else {
151
+ entriesNotDeletedCount++;
152
+ }
153
+ }
154
+
155
+ cursor.continue();
156
+ } else {
157
+ done(entriesToDelete);
158
+ }
159
+ };
160
+ }); // TODO(philipwalton): once the Safari bug in the following issue is fixed,
161
+ // we should be able to remove this loop and do the entry deletion in the
162
+ // cursor loop above:
163
+ // https://github.com/GoogleChrome/workbox/issues/1978
164
+
165
+ const urlsDeleted = [];
166
+
167
+ for (const entry of entriesToDelete) {
168
+ await this._db.delete(OBJECT_STORE_NAME, entry.id);
169
+ urlsDeleted.push(entry.url);
170
+ }
171
+
172
+ return urlsDeleted;
173
+ }
174
+ /**
175
+ * Takes a URL and returns an ID that will be unique in the object store.
176
+ *
177
+ * @param {string} url
178
+ * @return {string}
179
+ *
180
+ * @private
181
+ */
182
+
183
+
184
+ _getId(url) {
185
+ // Creating an ID from the URL and cache name won't be necessary once
186
+ // Edge switches to Chromium and all browsers we support work with
187
+ // array keyPaths.
188
+ return this._cacheName + '|' + normalizeURL(url);
189
+ }
190
+
191
+ }
192
+
193
+ /*
194
+ Copyright 2018 Google LLC
195
+
196
+ Use of this source code is governed by an MIT-style
197
+ license that can be found in the LICENSE file or at
198
+ https://opensource.org/licenses/MIT.
199
+ */
200
+ /**
201
+ * The `CacheExpiration` class allows you define an expiration and / or
202
+ * limit on the number of responses stored in a
203
+ * [`Cache`](https://developer.mozilla.org/en-US/docs/Web/API/Cache).
204
+ *
205
+ * @memberof module:workbox-expiration
206
+ */
207
+
208
+ class CacheExpiration {
209
+ /**
210
+ * To construct a new CacheExpiration instance you must provide at least
211
+ * one of the `config` properties.
212
+ *
213
+ * @param {string} cacheName Name of the cache to apply restrictions to.
214
+ * @param {Object} config
215
+ * @param {number} [config.maxEntries] The maximum number of entries to cache.
216
+ * Entries used the least will be removed as the maximum is reached.
217
+ * @param {number} [config.maxAgeSeconds] The maximum age of an entry before
218
+ * it's treated as stale and removed.
219
+ */
220
+ constructor(cacheName, config = {}) {
221
+ this._isRunning = false;
222
+ this._rerunRequested = false;
223
+
224
+ {
225
+ assert_js.assert.isType(cacheName, 'string', {
226
+ moduleName: 'workbox-expiration',
227
+ className: 'CacheExpiration',
228
+ funcName: 'constructor',
229
+ paramName: 'cacheName'
230
+ });
231
+
232
+ if (!(config.maxEntries || config.maxAgeSeconds)) {
233
+ throw new WorkboxError_js.WorkboxError('max-entries-or-age-required', {
234
+ moduleName: 'workbox-expiration',
235
+ className: 'CacheExpiration',
236
+ funcName: 'constructor'
237
+ });
238
+ }
239
+
240
+ if (config.maxEntries) {
241
+ assert_js.assert.isType(config.maxEntries, 'number', {
242
+ moduleName: 'workbox-expiration',
243
+ className: 'CacheExpiration',
244
+ funcName: 'constructor',
245
+ paramName: 'config.maxEntries'
246
+ }); // TODO: Assert is positive
247
+ }
248
+
249
+ if (config.maxAgeSeconds) {
250
+ assert_js.assert.isType(config.maxAgeSeconds, 'number', {
251
+ moduleName: 'workbox-expiration',
252
+ className: 'CacheExpiration',
253
+ funcName: 'constructor',
254
+ paramName: 'config.maxAgeSeconds'
255
+ }); // TODO: Assert is positive
256
+ }
257
+ }
258
+
259
+ this._maxEntries = config.maxEntries;
260
+ this._maxAgeSeconds = config.maxAgeSeconds;
261
+ this._cacheName = cacheName;
262
+ this._timestampModel = new CacheTimestampsModel(cacheName);
263
+ }
264
+ /**
265
+ * Expires entries for the given cache and given criteria.
266
+ */
267
+
268
+
269
+ async expireEntries() {
270
+ if (this._isRunning) {
271
+ this._rerunRequested = true;
272
+ return;
273
+ }
274
+
275
+ this._isRunning = true;
276
+ const minTimestamp = this._maxAgeSeconds ? Date.now() - this._maxAgeSeconds * 1000 : 0;
277
+ const urlsExpired = await this._timestampModel.expireEntries(minTimestamp, this._maxEntries); // Delete URLs from the cache
278
+
279
+ const cache = await self.caches.open(this._cacheName);
280
+
281
+ for (const url of urlsExpired) {
282
+ await cache.delete(url);
283
+ }
284
+
285
+ {
286
+ if (urlsExpired.length > 0) {
287
+ logger_js.logger.groupCollapsed(`Expired ${urlsExpired.length} ` + `${urlsExpired.length === 1 ? 'entry' : 'entries'} and removed ` + `${urlsExpired.length === 1 ? 'it' : 'them'} from the ` + `'${this._cacheName}' cache.`);
288
+ logger_js.logger.log(`Expired the following ${urlsExpired.length === 1 ? 'URL' : 'URLs'}:`);
289
+ urlsExpired.forEach(url => logger_js.logger.log(` ${url}`));
290
+ logger_js.logger.groupEnd();
291
+ } else {
292
+ logger_js.logger.debug(`Cache expiration ran and found no entries to remove.`);
293
+ }
294
+ }
295
+
296
+ this._isRunning = false;
297
+
298
+ if (this._rerunRequested) {
299
+ this._rerunRequested = false;
300
+ dontWaitFor_js.dontWaitFor(this.expireEntries());
301
+ }
302
+ }
303
+ /**
304
+ * Update the timestamp for the given URL. This ensures the when
305
+ * removing entries based on maximum entries, most recently used
306
+ * is accurate or when expiring, the timestamp is up-to-date.
307
+ *
308
+ * @param {string} url
309
+ */
310
+
311
+
312
+ async updateTimestamp(url) {
313
+ {
314
+ assert_js.assert.isType(url, 'string', {
315
+ moduleName: 'workbox-expiration',
316
+ className: 'CacheExpiration',
317
+ funcName: 'updateTimestamp',
318
+ paramName: 'url'
319
+ });
320
+ }
321
+
322
+ await this._timestampModel.setTimestamp(url, Date.now());
323
+ }
324
+ /**
325
+ * Can be used to check if a URL has expired or not before it's used.
326
+ *
327
+ * This requires a look up from IndexedDB, so can be slow.
328
+ *
329
+ * Note: This method will not remove the cached entry, call
330
+ * `expireEntries()` to remove indexedDB and Cache entries.
331
+ *
332
+ * @param {string} url
333
+ * @return {boolean}
334
+ */
335
+
336
+
337
+ async isURLExpired(url) {
338
+ if (!this._maxAgeSeconds) {
339
+ {
340
+ throw new WorkboxError_js.WorkboxError(`expired-test-without-max-age`, {
341
+ methodName: 'isURLExpired',
342
+ paramName: 'maxAgeSeconds'
343
+ });
344
+ }
345
+ } else {
346
+ const timestamp = await this._timestampModel.getTimestamp(url);
347
+ const expireOlderThan = Date.now() - this._maxAgeSeconds * 1000;
348
+ return timestamp < expireOlderThan;
349
+ }
350
+ }
351
+ /**
352
+ * Removes the IndexedDB object store used to keep track of cache expiration
353
+ * metadata.
354
+ */
355
+
356
+
357
+ async delete() {
358
+ // Make sure we don't attempt another rerun if we're called in the middle of
359
+ // a cache expiration.
360
+ this._rerunRequested = false;
361
+ await this._timestampModel.expireEntries(Infinity); // Expires all.
362
+ }
363
+
364
+ }
365
+
366
+ /*
367
+ Copyright 2018 Google LLC
368
+
369
+ Use of this source code is governed by an MIT-style
370
+ license that can be found in the LICENSE file or at
371
+ https://opensource.org/licenses/MIT.
372
+ */
373
+ /**
374
+ * This plugin can be used in the Workbox APIs to regularly enforce a
375
+ * limit on the age and / or the number of cached requests.
376
+ *
377
+ * Whenever a cached request is used or updated, this plugin will look
378
+ * at the used Cache and remove any old or extra requests.
379
+ *
380
+ * When using `maxAgeSeconds`, requests may be used *once* after expiring
381
+ * because the expiration clean up will not have occurred until *after* the
382
+ * cached request has been used. If the request has a "Date" header, then
383
+ * a light weight expiration check is performed and the request will not be
384
+ * used immediately.
385
+ *
386
+ * When using `maxEntries`, the entry least-recently requested will be removed
387
+ * from the cache first.
388
+ *
389
+ * @memberof module:workbox-expiration
390
+ */
391
+
392
+ class ExpirationPlugin {
393
+ /**
394
+ * @param {Object} config
395
+ * @param {number} [config.maxEntries] The maximum number of entries to cache.
396
+ * Entries used the least will be removed as the maximum is reached.
397
+ * @param {number} [config.maxAgeSeconds] The maximum age of an entry before
398
+ * it's treated as stale and removed.
399
+ * @param {boolean} [config.purgeOnQuotaError] Whether to opt this cache in to
400
+ * automatic deletion if the available storage quota has been exceeded.
401
+ */
402
+ constructor(config = {}) {
403
+ /**
404
+ * A "lifecycle" callback that will be triggered automatically by the
405
+ * `workbox-strategies` handlers when a `Response` is about to be returned
406
+ * from a [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache) to
407
+ * the handler. It allows the `Response` to be inspected for freshness and
408
+ * prevents it from being used if the `Response`'s `Date` header value is
409
+ * older than the configured `maxAgeSeconds`.
410
+ *
411
+ * @param {Object} options
412
+ * @param {string} options.cacheName Name of the cache the response is in.
413
+ * @param {Response} options.cachedResponse The `Response` object that's been
414
+ * read from a cache and whose freshness should be checked.
415
+ * @return {Response} Either the `cachedResponse`, if it's
416
+ * fresh, or `null` if the `Response` is older than `maxAgeSeconds`.
417
+ *
418
+ * @private
419
+ */
420
+ this.cachedResponseWillBeUsed = async ({
421
+ event,
422
+ request,
423
+ cacheName,
424
+ cachedResponse
425
+ }) => {
426
+ if (!cachedResponse) {
427
+ return null;
428
+ }
429
+
430
+ const isFresh = this._isResponseDateFresh(cachedResponse); // Expire entries to ensure that even if the expiration date has
431
+ // expired, it'll only be used once.
432
+
433
+
434
+ const cacheExpiration = this._getCacheExpiration(cacheName);
435
+
436
+ dontWaitFor_js.dontWaitFor(cacheExpiration.expireEntries()); // Update the metadata for the request URL to the current timestamp,
437
+ // but don't `await` it as we don't want to block the response.
438
+
439
+ const updateTimestampDone = cacheExpiration.updateTimestamp(request.url);
440
+
441
+ if (event) {
442
+ try {
443
+ event.waitUntil(updateTimestampDone);
444
+ } catch (error) {
445
+ {
446
+ // The event may not be a fetch event; only log the URL if it is.
447
+ if ('request' in event) {
448
+ logger_js.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache entry for ` + `'${getFriendlyURL_js.getFriendlyURL(event.request.url)}'.`);
449
+ }
450
+ }
451
+ }
452
+ }
453
+
454
+ return isFresh ? cachedResponse : null;
455
+ };
456
+ /**
457
+ * A "lifecycle" callback that will be triggered automatically by the
458
+ * `workbox-strategies` handlers when an entry is added to a cache.
459
+ *
460
+ * @param {Object} options
461
+ * @param {string} options.cacheName Name of the cache that was updated.
462
+ * @param {string} options.request The Request for the cached entry.
463
+ *
464
+ * @private
465
+ */
466
+
467
+
468
+ this.cacheDidUpdate = async ({
469
+ cacheName,
470
+ request
471
+ }) => {
472
+ {
473
+ assert_js.assert.isType(cacheName, 'string', {
474
+ moduleName: 'workbox-expiration',
475
+ className: 'Plugin',
476
+ funcName: 'cacheDidUpdate',
477
+ paramName: 'cacheName'
478
+ });
479
+ assert_js.assert.isInstance(request, Request, {
480
+ moduleName: 'workbox-expiration',
481
+ className: 'Plugin',
482
+ funcName: 'cacheDidUpdate',
483
+ paramName: 'request'
484
+ });
485
+ }
486
+
487
+ const cacheExpiration = this._getCacheExpiration(cacheName);
488
+
489
+ await cacheExpiration.updateTimestamp(request.url);
490
+ await cacheExpiration.expireEntries();
491
+ };
492
+
493
+ {
494
+ if (!(config.maxEntries || config.maxAgeSeconds)) {
495
+ throw new WorkboxError_js.WorkboxError('max-entries-or-age-required', {
496
+ moduleName: 'workbox-expiration',
497
+ className: 'Plugin',
498
+ funcName: 'constructor'
499
+ });
500
+ }
501
+
502
+ if (config.maxEntries) {
503
+ assert_js.assert.isType(config.maxEntries, 'number', {
504
+ moduleName: 'workbox-expiration',
505
+ className: 'Plugin',
506
+ funcName: 'constructor',
507
+ paramName: 'config.maxEntries'
508
+ });
509
+ }
510
+
511
+ if (config.maxAgeSeconds) {
512
+ assert_js.assert.isType(config.maxAgeSeconds, 'number', {
513
+ moduleName: 'workbox-expiration',
514
+ className: 'Plugin',
515
+ funcName: 'constructor',
516
+ paramName: 'config.maxAgeSeconds'
517
+ });
518
+ }
519
+ }
520
+
521
+ this._config = config;
522
+ this._maxAgeSeconds = config.maxAgeSeconds;
523
+ this._cacheExpirations = new Map();
524
+
525
+ if (config.purgeOnQuotaError) {
526
+ registerQuotaErrorCallback_js.registerQuotaErrorCallback(() => this.deleteCacheAndMetadata());
527
+ }
528
+ }
529
+ /**
530
+ * A simple helper method to return a CacheExpiration instance for a given
531
+ * cache name.
532
+ *
533
+ * @param {string} cacheName
534
+ * @return {CacheExpiration}
535
+ *
536
+ * @private
537
+ */
538
+
539
+
540
+ _getCacheExpiration(cacheName) {
541
+ if (cacheName === cacheNames_js.cacheNames.getRuntimeName()) {
542
+ throw new WorkboxError_js.WorkboxError('expire-custom-caches-only');
543
+ }
544
+
545
+ let cacheExpiration = this._cacheExpirations.get(cacheName);
546
+
547
+ if (!cacheExpiration) {
548
+ cacheExpiration = new CacheExpiration(cacheName, this._config);
549
+
550
+ this._cacheExpirations.set(cacheName, cacheExpiration);
551
+ }
552
+
553
+ return cacheExpiration;
554
+ }
555
+ /**
556
+ * @param {Response} cachedResponse
557
+ * @return {boolean}
558
+ *
559
+ * @private
560
+ */
561
+
562
+
563
+ _isResponseDateFresh(cachedResponse) {
564
+ if (!this._maxAgeSeconds) {
565
+ // We aren't expiring by age, so return true, it's fresh
566
+ return true;
567
+ } // Check if the 'date' header will suffice a quick expiration check.
568
+ // See https://github.com/GoogleChromeLabs/sw-toolbox/issues/164 for
569
+ // discussion.
570
+
571
+
572
+ const dateHeaderTimestamp = this._getDateHeaderTimestamp(cachedResponse);
573
+
574
+ if (dateHeaderTimestamp === null) {
575
+ // Unable to parse date, so assume it's fresh.
576
+ return true;
577
+ } // If we have a valid headerTime, then our response is fresh iff the
578
+ // headerTime plus maxAgeSeconds is greater than the current time.
579
+
580
+
581
+ const now = Date.now();
582
+ return dateHeaderTimestamp >= now - this._maxAgeSeconds * 1000;
583
+ }
584
+ /**
585
+ * This method will extract the data header and parse it into a useful
586
+ * value.
587
+ *
588
+ * @param {Response} cachedResponse
589
+ * @return {number|null}
590
+ *
591
+ * @private
592
+ */
593
+
594
+
595
+ _getDateHeaderTimestamp(cachedResponse) {
596
+ if (!cachedResponse.headers.has('date')) {
597
+ return null;
598
+ }
599
+
600
+ const dateHeader = cachedResponse.headers.get('date');
601
+ const parsedDate = new Date(dateHeader);
602
+ const headerTime = parsedDate.getTime(); // If the Date header was invalid for some reason, parsedDate.getTime()
603
+ // will return NaN.
604
+
605
+ if (isNaN(headerTime)) {
606
+ return null;
607
+ }
608
+
609
+ return headerTime;
610
+ }
611
+ /**
612
+ * This is a helper method that performs two operations:
613
+ *
614
+ * - Deletes *all* the underlying Cache instances associated with this plugin
615
+ * instance, by calling caches.delete() on your behalf.
616
+ * - Deletes the metadata from IndexedDB used to keep track of expiration
617
+ * details for each Cache instance.
618
+ *
619
+ * When using cache expiration, calling this method is preferable to calling
620
+ * `caches.delete()` directly, since this will ensure that the IndexedDB
621
+ * metadata is also cleanly removed and open IndexedDB instances are deleted.
622
+ *
623
+ * Note that if you're *not* using cache expiration for a given cache, calling
624
+ * `caches.delete()` and passing in the cache's name should be sufficient.
625
+ * There is no Workbox-specific method needed for cleanup in that case.
626
+ */
627
+
628
+
629
+ async deleteCacheAndMetadata() {
630
+ // Do this one at a time instead of all at once via `Promise.all()` to
631
+ // reduce the chance of inconsistency if a promise rejects.
632
+ for (const [cacheName, cacheExpiration] of this._cacheExpirations) {
633
+ await self.caches.delete(cacheName);
634
+ await cacheExpiration.delete();
635
+ } // Reset this._cacheExpirations to its initial state.
636
+
637
+
638
+ this._cacheExpirations = new Map();
639
+ }
640
+
641
+ }
642
+
643
+ exports.CacheExpiration = CacheExpiration;
644
+ exports.ExpirationPlugin = ExpirationPlugin;
645
+
646
+ return exports;
647
+
648
+ }({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core));
649
+ //# sourceMappingURL=workbox-expiration.dev.js.map