workbox-cdn
Advanced tools
Comparing version 4.3.1 to 5.0.0-alpha.2.1
{ | ||
"name": "workbox-cdn", | ||
"version": "4.3.1", | ||
"version": "5.0.0-alpha.2.1", | ||
"description": "Workbox Unofficial CDN", | ||
@@ -16,3 +16,3 @@ "repository": "nuxt-community/workbox-cdn", | ||
"devDependencies": { | ||
"workbox-cli": "^4.3.1" | ||
"workbox-cli": "5.0.0-alpha.2" | ||
}, | ||
@@ -19,0 +19,0 @@ "contributors": [ |
this.workbox = this.workbox || {}; | ||
this.workbox.backgroundSync = (function (exports, WorkboxError_mjs, logger_mjs, assert_mjs, getFriendlyURL_mjs, DBWrapper_mjs) { | ||
'use strict'; | ||
this.workbox.backgroundSync = (function (exports, WorkboxError_js, logger_js, assert_js, getFriendlyURL_js, DBWrapper_js) { | ||
'use strict'; | ||
try { | ||
self['workbox:background-sync:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
// @ts-ignore | ||
try { | ||
self['workbox:background-sync:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const DB_VERSION = 3; | ||
const DB_NAME = 'workbox-background-sync'; | ||
const OBJECT_STORE_NAME = 'requests'; | ||
const INDEXED_PROP = 'queueName'; | ||
/** | ||
* A class to manage storing requests from a Queue in IndexedbDB, | ||
* indexed by their queue name for easier access. | ||
* | ||
* @private | ||
*/ | ||
class QueueStore { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const DB_VERSION = 3; | ||
const DB_NAME = 'workbox-background-sync'; | ||
const OBJECT_STORE_NAME = 'requests'; | ||
const INDEXED_PROP = 'queueName'; | ||
/** | ||
* Associates this instance with a Queue instance, so entries added can be | ||
* identified by their queue name. | ||
* A class to manage storing requests from a Queue in IndexedbDB, | ||
* indexed by their queue name for easier access. | ||
* | ||
* @param {string} queueName | ||
* @private | ||
*/ | ||
constructor(queueName) { | ||
this._queueName = queueName; | ||
this._db = new DBWrapper_mjs.DBWrapper(DB_NAME, DB_VERSION, { | ||
onupgradeneeded: this._upgradeDb | ||
}); | ||
} | ||
/** | ||
* Append an entry last in the queue. | ||
* | ||
* @param {Object} entry | ||
* @param {Object} entry.requestData | ||
* @param {number} [entry.timestamp] | ||
* @param {Object} [entry.metadata] | ||
* @private | ||
*/ | ||
async pushEntry(entry) { | ||
{ | ||
assert_mjs.assert.isType(entry, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'QueueStore', | ||
funcName: 'pushEntry', | ||
paramName: 'entry' | ||
class QueueStore { | ||
/** | ||
* Associates this instance with a Queue instance, so entries added can be | ||
* identified by their queue name. | ||
* | ||
* @param {string} queueName | ||
* @private | ||
*/ | ||
constructor(queueName) { | ||
this._queueName = queueName; | ||
this._db = new DBWrapper_js.DBWrapper(DB_NAME, DB_VERSION, { | ||
onupgradeneeded: this._upgradeDb | ||
}); | ||
assert_mjs.assert.isType(entry.requestData, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'QueueStore', | ||
funcName: 'pushEntry', | ||
paramName: 'entry.requestData' | ||
}); | ||
} // Don't specify an ID since one is automatically generated. | ||
} | ||
/** | ||
* Append an entry last in the queue. | ||
* | ||
* @param {Object} entry | ||
* @param {Object} entry.requestData | ||
* @param {number} [entry.timestamp] | ||
* @param {Object} [entry.metadata] | ||
* @private | ||
*/ | ||
delete entry.id; | ||
entry.queueName = this._queueName; | ||
await this._db.add(OBJECT_STORE_NAME, entry); | ||
} | ||
/** | ||
* Preppend an entry first in the queue. | ||
* | ||
* @param {Object} entry | ||
* @param {Object} entry.requestData | ||
* @param {number} [entry.timestamp] | ||
* @param {Object} [entry.metadata] | ||
* @private | ||
*/ | ||
async pushEntry(entry) { | ||
{ | ||
assert_js.assert.isType(entry, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'QueueStore', | ||
funcName: 'pushEntry', | ||
paramName: 'entry' | ||
}); | ||
assert_js.assert.isType(entry.requestData, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'QueueStore', | ||
funcName: 'pushEntry', | ||
paramName: 'entry.requestData' | ||
}); | ||
} // Don't specify an ID since one is automatically generated. | ||
async unshiftEntry(entry) { | ||
{ | ||
assert_mjs.assert.isType(entry, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'QueueStore', | ||
funcName: 'unshiftEntry', | ||
paramName: 'entry' | ||
}); | ||
assert_mjs.assert.isType(entry.requestData, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'QueueStore', | ||
funcName: 'unshiftEntry', | ||
paramName: 'entry.requestData' | ||
}); | ||
delete entry.id; | ||
entry.queueName = this._queueName; | ||
await this._db.add(OBJECT_STORE_NAME, entry); | ||
} | ||
/** | ||
* Preppend an entry first in the queue. | ||
* | ||
* @param {Object} entry | ||
* @param {Object} entry.requestData | ||
* @param {number} [entry.timestamp] | ||
* @param {Object} [entry.metadata] | ||
* @private | ||
*/ | ||
const [firstEntry] = await this._db.getAllMatching(OBJECT_STORE_NAME, { | ||
count: 1 | ||
}); | ||
if (firstEntry) { | ||
// Pick an ID one less than the lowest ID in the object store. | ||
entry.id = firstEntry.id - 1; | ||
} else { | ||
// Otherwise let the auto-incrementor assign the ID. | ||
delete entry.id; | ||
async unshiftEntry(entry) { | ||
{ | ||
assert_js.assert.isType(entry, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'QueueStore', | ||
funcName: 'unshiftEntry', | ||
paramName: 'entry' | ||
}); | ||
assert_js.assert.isType(entry.requestData, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'QueueStore', | ||
funcName: 'unshiftEntry', | ||
paramName: 'entry.requestData' | ||
}); | ||
} | ||
const [firstEntry] = await this._db.getAllMatching(OBJECT_STORE_NAME, { | ||
count: 1 | ||
}); | ||
if (firstEntry) { | ||
// Pick an ID one less than the lowest ID in the object store. | ||
entry.id = firstEntry.id - 1; | ||
} else { | ||
// Otherwise let the auto-incrementor assign the ID. | ||
delete entry.id; | ||
} | ||
entry.queueName = this._queueName; | ||
await this._db.add(OBJECT_STORE_NAME, entry); | ||
} | ||
/** | ||
* Removes and returns the last entry in the queue matching the `queueName`. | ||
* | ||
* @return {Promise<Object>} | ||
* @private | ||
*/ | ||
entry.queueName = this._queueName; | ||
await this._db.add(OBJECT_STORE_NAME, entry); | ||
} | ||
/** | ||
* Removes and returns the last entry in the queue matching the `queueName`. | ||
* | ||
* @return {Promise<Object>} | ||
* @private | ||
*/ | ||
async popEntry() { | ||
return this._removeEntry({ | ||
direction: 'prev' | ||
}); | ||
} | ||
/** | ||
* Removes and returns the first entry in the queue matching the `queueName`. | ||
* | ||
* @return {Promise<Object>} | ||
* @private | ||
*/ | ||
async popEntry() { | ||
return this._removeEntry({ | ||
direction: 'prev' | ||
}); | ||
} | ||
/** | ||
* Removes and returns the first entry in the queue matching the `queueName`. | ||
* | ||
* @return {Promise<Object>} | ||
* @private | ||
*/ | ||
async shiftEntry() { | ||
return this._removeEntry({ | ||
direction: 'next' | ||
}); | ||
} | ||
/** | ||
* Returns all entries in the store matching the `queueName`. | ||
* | ||
* @param {Object} options See workbox.backgroundSync.Queue~getAll} | ||
* @return {Promise<Array<Object>>} | ||
* @private | ||
*/ | ||
async shiftEntry() { | ||
return this._removeEntry({ | ||
direction: 'next' | ||
}); | ||
} | ||
/** | ||
* Returns all entries in the store matching the `queueName`. | ||
* | ||
* @param {Object} options See workbox.backgroundSync.Queue~getAll} | ||
* @return {Promise<Array<Object>>} | ||
* @private | ||
*/ | ||
async getAll() { | ||
return await this._db.getAllMatching(OBJECT_STORE_NAME, { | ||
index: INDEXED_PROP, | ||
query: IDBKeyRange.only(this._queueName) | ||
}); | ||
} | ||
/** | ||
* Deletes the entry for the given ID. | ||
* | ||
* WARNING: this method does not ensure the deleted enry belongs to this | ||
* queue (i.e. matches the `queueName`). But this limitation is acceptable | ||
* as this class is not publicly exposed. An additional check would make | ||
* this method slower than it needs to be. | ||
* | ||
* @private | ||
* @param {number} id | ||
*/ | ||
async getAll() { | ||
return await this._db.getAllMatching(OBJECT_STORE_NAME, { | ||
index: INDEXED_PROP, | ||
query: IDBKeyRange.only(this._queueName) | ||
}); | ||
} | ||
/** | ||
* Deletes the entry for the given ID. | ||
* | ||
* WARNING: this method does not ensure the deleted enry belongs to this | ||
* queue (i.e. matches the `queueName`). But this limitation is acceptable | ||
* as this class is not publicly exposed. An additional check would make | ||
* this method slower than it needs to be. | ||
* | ||
* @private | ||
* @param {number} id | ||
*/ | ||
async deleteEntry(id) { | ||
await this._db.delete(OBJECT_STORE_NAME, id); | ||
} | ||
/** | ||
* Removes and returns the first or last entry in the queue (based on the | ||
* `direction` argument) matching the `queueName`. | ||
* | ||
* @return {Promise<Object>} | ||
* @private | ||
*/ | ||
async deleteEntry(id) { | ||
await this._db.delete(OBJECT_STORE_NAME, id); | ||
} | ||
/** | ||
* Removes and returns the first or last entry in the queue (based on the | ||
* `direction` argument) matching the `queueName`. | ||
* | ||
* @return {Promise<Object>} | ||
* @private | ||
*/ | ||
async _removeEntry({ | ||
direction | ||
}) { | ||
const [entry] = await this._db.getAllMatching(OBJECT_STORE_NAME, { | ||
direction, | ||
index: INDEXED_PROP, | ||
query: IDBKeyRange.only(this._queueName), | ||
count: 1 | ||
}); | ||
async _removeEntry({ | ||
direction | ||
}) { | ||
const [entry] = await this._db.getAllMatching(OBJECT_STORE_NAME, { | ||
direction, | ||
index: INDEXED_PROP, | ||
query: IDBKeyRange.only(this._queueName), | ||
count: 1 | ||
}); | ||
if (entry) { | ||
await this.deleteEntry(entry.id); | ||
return entry; | ||
if (entry) { | ||
await this.deleteEntry(entry.id); | ||
return entry; | ||
} | ||
} | ||
} | ||
/** | ||
* Upgrades the database given an `upgradeneeded` event. | ||
* | ||
* @param {Event} event | ||
* @private | ||
*/ | ||
/** | ||
* Upgrades the database given an `upgradeneeded` event. | ||
* | ||
* @param {Event} event | ||
* @private | ||
*/ | ||
_upgradeDb(event) { | ||
const db = event.target.result; | ||
_upgradeDb(event) { | ||
const db = event.target.result; | ||
if (event.oldVersion > 0 && event.oldVersion < DB_VERSION) { | ||
if (db.objectStoreNames.contains(OBJECT_STORE_NAME)) { | ||
db.deleteObjectStore(OBJECT_STORE_NAME); | ||
if (event.oldVersion > 0 && event.oldVersion < DB_VERSION) { | ||
if (db.objectStoreNames.contains(OBJECT_STORE_NAME)) { | ||
db.deleteObjectStore(OBJECT_STORE_NAME); | ||
} | ||
} | ||
const objStore = db.createObjectStore(OBJECT_STORE_NAME, { | ||
autoIncrement: true, | ||
keyPath: 'id' | ||
}); | ||
objStore.createIndex(INDEXED_PROP, INDEXED_PROP, { | ||
unique: false | ||
}); | ||
} | ||
const objStore = db.createObjectStore(OBJECT_STORE_NAME, { | ||
autoIncrement: true, | ||
keyPath: 'id' | ||
}); | ||
objStore.createIndex(INDEXED_PROP, INDEXED_PROP, { | ||
unique: false | ||
}); | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const serializableProperties = ['method', 'referrer', 'referrerPolicy', 'mode', 'credentials', 'cache', 'redirect', 'integrity', 'keepalive']; | ||
/** | ||
* A class to make it easier to serialize and de-serialize requests so they | ||
* can be stored in IndexedDB. | ||
* | ||
* @private | ||
*/ | ||
class StorableRequest { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const serializableProperties = ['method', 'referrer', 'referrerPolicy', 'mode', 'credentials', 'cache', 'redirect', 'integrity', 'keepalive']; | ||
/** | ||
* Converts a Request object to a plain object that can be structured | ||
* cloned or JSON-stringified. | ||
* A class to make it easier to serialize and de-serialize requests so they | ||
* can be stored in IndexedDB. | ||
* | ||
* @param {Request} request | ||
* @return {Promise<StorableRequest>} | ||
* | ||
* @private | ||
*/ | ||
static async fromRequest(request) { | ||
const requestData = { | ||
url: request.url, | ||
headers: {} | ||
}; // Set the body if present. | ||
if (request.method !== 'GET') { | ||
// Use ArrayBuffer to support non-text request bodies. | ||
// NOTE: we can't use Blobs becuse Safari doesn't support storing | ||
// Blobs in IndexedDB in some cases: | ||
// https://github.com/dfahlander/Dexie.js/issues/618#issuecomment-398348457 | ||
requestData.body = await request.clone().arrayBuffer(); | ||
} // Convert the headers from an iterable to an object. | ||
class StorableRequest { | ||
/** | ||
* Converts a Request object to a plain object that can be structured | ||
* cloned or JSON-stringified. | ||
* | ||
* @param {Request} request | ||
* @return {Promise<StorableRequest>} | ||
* | ||
* @private | ||
*/ | ||
static async fromRequest(request) { | ||
const requestData = { | ||
url: request.url, | ||
headers: {} | ||
}; // Set the body if present. | ||
if (request.method !== 'GET') { | ||
// Use ArrayBuffer to support non-text request bodies. | ||
// NOTE: we can't use Blobs becuse Safari doesn't support storing | ||
// Blobs in IndexedDB in some cases: | ||
// https://github.com/dfahlander/Dexie.js/issues/618#issuecomment-398348457 | ||
requestData.body = await request.clone().arrayBuffer(); | ||
} // Convert the headers from an iterable to an object. | ||
for (const [key, value] of request.headers.entries()) { | ||
requestData.headers[key] = value; | ||
} // Add all other serializable request properties | ||
for (const [key, value] of request.headers.entries()) { | ||
requestData.headers[key] = value; | ||
} // Add all other serializable request properties | ||
for (const prop of serializableProperties) { | ||
if (request[prop] !== undefined) { | ||
requestData[prop] = request[prop]; | ||
for (const prop of serializableProperties) { | ||
if (request[prop] !== undefined) { | ||
requestData[prop] = request[prop]; | ||
} | ||
} | ||
return new StorableRequest(requestData); | ||
} | ||
/** | ||
* Accepts an object of request data that can be used to construct a | ||
* `Request` but can also be stored in IndexedDB. | ||
* | ||
* @param {Object} requestData An object of request data that includes the | ||
* `url` plus any relevant properties of | ||
* [requestInit]{@link https://fetch.spec.whatwg.org/#requestinit}. | ||
* @private | ||
*/ | ||
return new StorableRequest(requestData); | ||
} | ||
/** | ||
* Accepts an object of request data that can be used to construct a | ||
* `Request` but can also be stored in IndexedDB. | ||
* | ||
* @param {Object} requestData An object of request data that includes the | ||
* `url` plus any relevant properties of | ||
* [requestInit]{@link https://fetch.spec.whatwg.org/#requestinit}. | ||
* @private | ||
*/ | ||
constructor(requestData) { | ||
{ | ||
assert_js.assert.isType(requestData, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'StorableRequest', | ||
funcName: 'constructor', | ||
paramName: 'requestData' | ||
}); | ||
assert_js.assert.isType(requestData.url, 'string', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'StorableRequest', | ||
funcName: 'constructor', | ||
paramName: 'requestData.url' | ||
}); | ||
} // If the request's mode is `navigate`, convert it to `same-origin` since | ||
// navigation requests can't be constructed via script. | ||
constructor(requestData) { | ||
{ | ||
assert_mjs.assert.isType(requestData, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'StorableRequest', | ||
funcName: 'constructor', | ||
paramName: 'requestData' | ||
}); | ||
assert_mjs.assert.isType(requestData.url, 'string', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'StorableRequest', | ||
funcName: 'constructor', | ||
paramName: 'requestData.url' | ||
}); | ||
} // If the request's mode is `navigate`, convert it to `same-origin` since | ||
// navigation requests can't be constructed via script. | ||
if (requestData.mode === 'navigate') { | ||
requestData.mode = 'same-origin'; | ||
} | ||
if (requestData.mode === 'navigate') { | ||
requestData.mode = 'same-origin'; | ||
this._requestData = requestData; | ||
} | ||
/** | ||
* Returns a deep clone of the instances `_requestData` object. | ||
* | ||
* @return {Object} | ||
* | ||
* @private | ||
*/ | ||
this._requestData = requestData; | ||
} | ||
/** | ||
* Returns a deep clone of the instances `_requestData` object. | ||
* | ||
* @return {Object} | ||
* | ||
* @private | ||
*/ | ||
toObject() { | ||
const requestData = Object.assign({}, this._requestData); | ||
requestData.headers = Object.assign({}, this._requestData.headers); | ||
toObject() { | ||
const requestData = Object.assign({}, this._requestData); | ||
requestData.headers = Object.assign({}, this._requestData.headers); | ||
if (requestData.body) { | ||
requestData.body = requestData.body.slice(0); | ||
} | ||
if (requestData.body) { | ||
requestData.body = requestData.body.slice(0); | ||
return requestData; | ||
} | ||
/** | ||
* Converts this instance to a Request. | ||
* | ||
* @return {Request} | ||
* | ||
* @private | ||
*/ | ||
return requestData; | ||
} | ||
/** | ||
* Converts this instance to a Request. | ||
* | ||
* @return {Request} | ||
* | ||
* @private | ||
*/ | ||
toRequest() { | ||
return new Request(this._requestData.url, this._requestData); | ||
} | ||
/** | ||
* Creates and returns a deep clone of the instance. | ||
* | ||
* @return {StorableRequest} | ||
* | ||
* @private | ||
*/ | ||
toRequest() { | ||
return new Request(this._requestData.url, this._requestData); | ||
} | ||
/** | ||
* Creates and returns a deep clone of the instance. | ||
* | ||
* @return {StorableRequest} | ||
* | ||
* @private | ||
*/ | ||
clone() { | ||
return new StorableRequest(this.toObject()); | ||
} | ||
clone() { | ||
return new StorableRequest(this.toObject()); | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const TAG_PREFIX = 'workbox-background-sync'; | ||
const MAX_RETENTION_TIME = 60 * 24 * 7; // 7 days in minutes | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const TAG_PREFIX = 'workbox-background-sync'; | ||
const MAX_RETENTION_TIME = 60 * 24 * 7; // 7 days in minutes | ||
const queueNames = new Set(); | ||
/** | ||
* A class to manage storing failed requests in IndexedDB and retrying them | ||
* later. All parts of the storing and replaying process are observable via | ||
* callbacks. | ||
* | ||
* @memberof workbox.backgroundSync | ||
*/ | ||
class Queue { | ||
const queueNames = new Set(); | ||
/** | ||
* Creates an instance of Queue with the given options | ||
* A class to manage storing failed requests in IndexedDB and retrying them | ||
* later. All parts of the storing and replaying process are observable via | ||
* callbacks. | ||
* | ||
* @param {string} name The unique name for this queue. This name must be | ||
* unique as it's used to register sync events and store requests | ||
* in IndexedDB specific to this instance. An error will be thrown if | ||
* a duplicate name is detected. | ||
* @param {Object} [options] | ||
* @param {Function} [options.onSync] A function that gets invoked whenever | ||
* the 'sync' event fires. The function is invoked with an object | ||
* containing the `queue` property (referencing this instance), and you | ||
* can use the callback to customize the replay behavior of the queue. | ||
* When not set the `replayRequests()` method is called. | ||
* Note: if the replay fails after a sync event, make sure you throw an | ||
* error, so the browser knows to retry the sync event later. | ||
* @param {number} [options.maxRetentionTime=7 days] The amount of time (in | ||
* minutes) a request may be retried. After this amount of time has | ||
* passed, the request will be deleted from the queue. | ||
* @memberof workbox.backgroundSync | ||
*/ | ||
constructor(name, { | ||
onSync, | ||
maxRetentionTime | ||
} = {}) { | ||
// Ensure the store name is not already being used | ||
if (queueNames.has(name)) { | ||
throw new WorkboxError_mjs.WorkboxError('duplicate-queue-name', { | ||
name | ||
}); | ||
} else { | ||
queueNames.add(name); | ||
} | ||
this._name = name; | ||
this._onSync = onSync || this.replayRequests; | ||
this._maxRetentionTime = maxRetentionTime || MAX_RETENTION_TIME; | ||
this._queueStore = new QueueStore(this._name); | ||
class Queue { | ||
/** | ||
* Creates an instance of Queue with the given options | ||
* | ||
* @param {string} name The unique name for this queue. This name must be | ||
* unique as it's used to register sync events and store requests | ||
* in IndexedDB specific to this instance. An error will be thrown if | ||
* a duplicate name is detected. | ||
* @param {Object} [options] | ||
* @param {Function} [options.onSync] A function that gets invoked whenever | ||
* the 'sync' event fires. The function is invoked with an object | ||
* containing the `queue` property (referencing this instance), and you | ||
* can use the callback to customize the replay behavior of the queue. | ||
* When not set the `replayRequests()` method is called. | ||
* Note: if the replay fails after a sync event, make sure you throw an | ||
* error, so the browser knows to retry the sync event later. | ||
* @param {number} [options.maxRetentionTime=7 days] The amount of time (in | ||
* minutes) a request may be retried. After this amount of time has | ||
* passed, the request will be deleted from the queue. | ||
*/ | ||
constructor(name, { | ||
onSync, | ||
maxRetentionTime | ||
} = {}) { | ||
this._syncInProgress = false; | ||
this._requestsAddedDuringSync = false; // Ensure the store name is not already being used | ||
this._addSyncListener(); | ||
} | ||
/** | ||
* @return {string} | ||
*/ | ||
if (queueNames.has(name)) { | ||
throw new WorkboxError_js.WorkboxError('duplicate-queue-name', { | ||
name | ||
}); | ||
} else { | ||
queueNames.add(name); | ||
} | ||
this._name = name; | ||
this._onSync = onSync || this.replayRequests; | ||
this._maxRetentionTime = maxRetentionTime || MAX_RETENTION_TIME; | ||
this._queueStore = new QueueStore(this._name); | ||
get name() { | ||
return this._name; | ||
} | ||
/** | ||
* Stores the passed request in IndexedDB (with its timestamp and any | ||
* metadata) at the end of the queue. | ||
* | ||
* @param {Object} entry | ||
* @param {Request} entry.request The request to store in the queue. | ||
* @param {Object} [entry.metadata] Any metadata you want associated with the | ||
* stored request. When requests are replayed you'll have access to this | ||
* metadata object in case you need to modify the request beforehand. | ||
* @param {number} [entry.timestamp] The timestamp (Epoch time in | ||
* milliseconds) when the request was first added to the queue. This is | ||
* used along with `maxRetentionTime` to remove outdated requests. In | ||
* general you don't need to set this value, as it's automatically set | ||
* for you (defaulting to `Date.now()`), but you can update it if you | ||
* don't want particular requests to expire. | ||
*/ | ||
this._addSyncListener(); | ||
} | ||
/** | ||
* @return {string} | ||
*/ | ||
async pushRequest(entry) { | ||
{ | ||
assert_mjs.assert.isType(entry, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'Queue', | ||
funcName: 'pushRequest', | ||
paramName: 'entry' | ||
}); | ||
assert_mjs.assert.isInstance(entry.request, Request, { | ||
moduleName: 'workbox-background-sync', | ||
className: 'Queue', | ||
funcName: 'pushRequest', | ||
paramName: 'entry.request' | ||
}); | ||
get name() { | ||
return this._name; | ||
} | ||
/** | ||
* Stores the passed request in IndexedDB (with its timestamp and any | ||
* metadata) at the end of the queue. | ||
* | ||
* @param {Object} entry | ||
* @param {Request} entry.request The request to store in the queue. | ||
* @param {Object} [entry.metadata] Any metadata you want associated with the | ||
* stored request. When requests are replayed you'll have access to this | ||
* metadata object in case you need to modify the request beforehand. | ||
* @param {number} [entry.timestamp] The timestamp (Epoch time in | ||
* milliseconds) when the request was first added to the queue. This is | ||
* used along with `maxRetentionTime` to remove outdated requests. In | ||
* general you don't need to set this value, as it's automatically set | ||
* for you (defaulting to `Date.now()`), but you can update it if you | ||
* don't want particular requests to expire. | ||
*/ | ||
await this._addRequest(entry, 'push'); | ||
} | ||
/** | ||
* Stores the passed request in IndexedDB (with its timestamp and any | ||
* metadata) at the beginning of the queue. | ||
* | ||
* @param {Object} entry | ||
* @param {Request} entry.request The request to store in the queue. | ||
* @param {Object} [entry.metadata] Any metadata you want associated with the | ||
* stored request. When requests are replayed you'll have access to this | ||
* metadata object in case you need to modify the request beforehand. | ||
* @param {number} [entry.timestamp] The timestamp (Epoch time in | ||
* milliseconds) when the request was first added to the queue. This is | ||
* used along with `maxRetentionTime` to remove outdated requests. In | ||
* general you don't need to set this value, as it's automatically set | ||
* for you (defaulting to `Date.now()`), but you can update it if you | ||
* don't want particular requests to expire. | ||
*/ | ||
async pushRequest(entry) { | ||
{ | ||
assert_js.assert.isType(entry, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'Queue', | ||
funcName: 'pushRequest', | ||
paramName: 'entry' | ||
}); | ||
assert_js.assert.isInstance(entry.request, Request, { | ||
moduleName: 'workbox-background-sync', | ||
className: 'Queue', | ||
funcName: 'pushRequest', | ||
paramName: 'entry.request' | ||
}); | ||
} | ||
async unshiftRequest(entry) { | ||
{ | ||
assert_mjs.assert.isType(entry, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'Queue', | ||
funcName: 'unshiftRequest', | ||
paramName: 'entry' | ||
}); | ||
assert_mjs.assert.isInstance(entry.request, Request, { | ||
moduleName: 'workbox-background-sync', | ||
className: 'Queue', | ||
funcName: 'unshiftRequest', | ||
paramName: 'entry.request' | ||
}); | ||
await this._addRequest(entry, 'push'); | ||
} | ||
/** | ||
* Stores the passed request in IndexedDB (with its timestamp and any | ||
* metadata) at the beginning of the queue. | ||
* | ||
* @param {Object} entry | ||
* @param {Request} entry.request The request to store in the queue. | ||
* @param {Object} [entry.metadata] Any metadata you want associated with the | ||
* stored request. When requests are replayed you'll have access to this | ||
* metadata object in case you need to modify the request beforehand. | ||
* @param {number} [entry.timestamp] The timestamp (Epoch time in | ||
* milliseconds) when the request was first added to the queue. This is | ||
* used along with `maxRetentionTime` to remove outdated requests. In | ||
* general you don't need to set this value, as it's automatically set | ||
* for you (defaulting to `Date.now()`), but you can update it if you | ||
* don't want particular requests to expire. | ||
*/ | ||
await this._addRequest(entry, 'unshift'); | ||
} | ||
/** | ||
* Removes and returns the last request in the queue (along with its | ||
* timestamp and any metadata). The returned object takes the form: | ||
* `{request, timestamp, metadata}`. | ||
* | ||
* @return {Promise<Object>} | ||
*/ | ||
async unshiftRequest(entry) { | ||
{ | ||
assert_js.assert.isType(entry, 'object', { | ||
moduleName: 'workbox-background-sync', | ||
className: 'Queue', | ||
funcName: 'unshiftRequest', | ||
paramName: 'entry' | ||
}); | ||
assert_js.assert.isInstance(entry.request, Request, { | ||
moduleName: 'workbox-background-sync', | ||
className: 'Queue', | ||
funcName: 'unshiftRequest', | ||
paramName: 'entry.request' | ||
}); | ||
} | ||
async popRequest() { | ||
return this._removeRequest('pop'); | ||
} | ||
/** | ||
* Removes and returns the first request in the queue (along with its | ||
* timestamp and any metadata). The returned object takes the form: | ||
* `{request, timestamp, metadata}`. | ||
* | ||
* @return {Promise<Object>} | ||
*/ | ||
await this._addRequest(entry, 'unshift'); | ||
} | ||
/** | ||
* Removes and returns the last request in the queue (along with its | ||
* timestamp and any metadata). The returned object takes the form: | ||
* `{request, timestamp, metadata}`. | ||
* | ||
* @return {Promise<Object>} | ||
*/ | ||
async shiftRequest() { | ||
return this._removeRequest('shift'); | ||
} | ||
/** | ||
* Returns all the entries that have not expired (per `maxRetentionTime`). | ||
* Any expired entries are removed from the queue. | ||
* | ||
* @return {Promise<Array<Object>>} | ||
*/ | ||
async popRequest() { | ||
return this._removeRequest('pop'); | ||
} | ||
/** | ||
* Removes and returns the first request in the queue (along with its | ||
* timestamp and any metadata). The returned object takes the form: | ||
* `{request, timestamp, metadata}`. | ||
* | ||
* @return {Promise<Object>} | ||
*/ | ||
async getAll() { | ||
const allEntries = await this._queueStore.getAll(); | ||
const now = Date.now(); | ||
const unexpiredEntries = []; | ||
async shiftRequest() { | ||
return this._removeRequest('shift'); | ||
} | ||
/** | ||
* Returns all the entries that have not expired (per `maxRetentionTime`). | ||
* Any expired entries are removed from the queue. | ||
* | ||
* @return {Promise<Array<Object>>} | ||
*/ | ||
for (const entry of allEntries) { | ||
// Ignore requests older than maxRetentionTime. Call this function | ||
// recursively until an unexpired request is found. | ||
const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000; | ||
if (now - entry.timestamp > maxRetentionTimeInMs) { | ||
await this._queueStore.deleteEntry(entry.id); | ||
} else { | ||
unexpiredEntries.push(convertEntry(entry)); | ||
async getAll() { | ||
const allEntries = await this._queueStore.getAll(); | ||
const now = Date.now(); | ||
const unexpiredEntries = []; | ||
for (const entry of allEntries) { | ||
// Ignore requests older than maxRetentionTime. Call this function | ||
// recursively until an unexpired request is found. | ||
const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000; | ||
if (now - entry.timestamp > maxRetentionTimeInMs) { | ||
await this._queueStore.deleteEntry(entry.id); | ||
} else { | ||
unexpiredEntries.push(convertEntry(entry)); | ||
} | ||
} | ||
return unexpiredEntries; | ||
} | ||
/** | ||
* Adds the entry to the QueueStore and registers for a sync event. | ||
* | ||
* @param {Object} entry | ||
* @param {Request} entry.request | ||
* @param {Object} [entry.metadata] | ||
* @param {number} [entry.timestamp=Date.now()] | ||
* @param {string} operation ('push' or 'unshift') | ||
* @private | ||
*/ | ||
return unexpiredEntries; | ||
} | ||
/** | ||
* Adds the entry to the QueueStore and registers for a sync event. | ||
* | ||
* @param {Object} entry | ||
* @param {Request} entry.request | ||
* @param {Object} [entry.metadata] | ||
* @param {number} [entry.timestamp=Date.now()] | ||
* @param {string} operation ('push' or 'unshift') | ||
* @private | ||
*/ | ||
async _addRequest({ | ||
request, | ||
metadata, | ||
timestamp = Date.now() | ||
}, operation) { | ||
const storableRequest = await StorableRequest.fromRequest(request.clone()); | ||
const entry = { | ||
requestData: storableRequest.toObject(), | ||
timestamp | ||
}; // Only include metadata if it's present. | ||
async _addRequest({ | ||
request, | ||
metadata, | ||
timestamp = Date.now() | ||
}, operation) { | ||
const storableRequest = await StorableRequest.fromRequest(request.clone()); | ||
const entry = { | ||
requestData: storableRequest.toObject(), | ||
timestamp | ||
}; // Only include metadata if it's present. | ||
if (metadata) { | ||
entry.metadata = metadata; | ||
} | ||
if (metadata) { | ||
entry.metadata = metadata; | ||
} | ||
await this._queueStore[`${operation}Entry`](entry); | ||
await this._queueStore[`${operation}Entry`](entry); | ||
{ | ||
logger_js.logger.log(`Request for '${getFriendlyURL_js.getFriendlyURL(request.url)}' has ` + `been added to background sync queue '${this._name}'.`); | ||
} // Don't register for a sync if we're in the middle of a sync. Instead, | ||
// we wait until the sync is complete and call register if | ||
// `this._requestsAddedDuringSync` is true. | ||
{ | ||
logger_mjs.logger.log(`Request for '${getFriendlyURL_mjs.getFriendlyURL(request.url)}' has ` + `been added to background sync queue '${this._name}'.`); | ||
} // Don't register for a sync if we're in the middle of a sync. Instead, | ||
// we wait until the sync is complete and call register if | ||
// `this._requestsAddedDuringSync` is true. | ||
if (this._syncInProgress) { | ||
this._requestsAddedDuringSync = true; | ||
} else { | ||
await this.registerSync(); | ||
if (this._syncInProgress) { | ||
this._requestsAddedDuringSync = true; | ||
} else { | ||
await this.registerSync(); | ||
} | ||
} | ||
} | ||
/** | ||
* Removes and returns the first or last (depending on `operation`) entry | ||
* from the QueueStore that's not older than the `maxRetentionTime`. | ||
* | ||
* @param {string} operation ('pop' or 'shift') | ||
* @return {Object|undefined} | ||
* @private | ||
*/ | ||
/** | ||
* Removes and returns the first or last (depending on `operation`) entry | ||
* from the QueueStore that's not older than the `maxRetentionTime`. | ||
* | ||
* @param {string} operation ('pop' or 'shift') | ||
* @return {Object|undefined} | ||
* @private | ||
*/ | ||
async _removeRequest(operation) { | ||
const now = Date.now(); | ||
const entry = await this._queueStore[`${operation}Entry`](); | ||
async _removeRequest(operation) { | ||
const now = Date.now(); | ||
const entry = await this._queueStore[`${operation}Entry`](); | ||
if (entry) { | ||
// Ignore requests older than maxRetentionTime. Call this function | ||
// recursively until an unexpired request is found. | ||
const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000; | ||
if (entry) { | ||
// Ignore requests older than maxRetentionTime. Call this function | ||
// recursively until an unexpired request is found. | ||
const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000; | ||
if (now - entry.timestamp > maxRetentionTimeInMs) { | ||
return this._removeRequest(operation); | ||
if (now - entry.timestamp > maxRetentionTimeInMs) { | ||
return this._removeRequest(operation); | ||
} | ||
return convertEntry(entry); | ||
} else { | ||
return undefined; | ||
} | ||
return convertEntry(entry); | ||
} | ||
} | ||
/** | ||
* Loops through each request in the queue and attempts to re-fetch it. | ||
* If any request fails to re-fetch, it's put back in the same position in | ||
* the queue (which registers a retry for the next sync event). | ||
*/ | ||
/** | ||
* Loops through each request in the queue and attempts to re-fetch it. | ||
* If any request fails to re-fetch, it's put back in the same position in | ||
* the queue (which registers a retry for the next sync event). | ||
*/ | ||
async replayRequests() { | ||
let entry; | ||
async replayRequests() { | ||
let entry; | ||
while (entry = await this.shiftRequest()) { | ||
try { | ||
await fetch(entry.request.clone()); | ||
while (entry = await this.shiftRequest()) { | ||
try { | ||
await fetch(entry.request.clone()); | ||
{ | ||
logger_mjs.logger.log(`Request for '${getFriendlyURL_mjs.getFriendlyURL(entry.request.url)}'` + `has been replayed in queue '${this._name}'`); | ||
} | ||
} catch (error) { | ||
await this.unshiftRequest(entry); | ||
if ("dev" !== 'production') { | ||
logger_js.logger.log(`Request for '${getFriendlyURL_js.getFriendlyURL(entry.request.url)}'` + `has been replayed in queue '${this._name}'`); | ||
} | ||
} catch (error) { | ||
await this.unshiftRequest(entry); | ||
{ | ||
logger_mjs.logger.log(`Request for '${getFriendlyURL_mjs.getFriendlyURL(entry.request.url)}'` + `failed to replay, putting it back in queue '${this._name}'`); | ||
{ | ||
logger_js.logger.log(`Request for '${getFriendlyURL_js.getFriendlyURL(entry.request.url)}'` + `failed to replay, putting it back in queue '${this._name}'`); | ||
} | ||
throw new WorkboxError_js.WorkboxError('queue-replay-failed', { | ||
name: this._name | ||
}); | ||
} | ||
} | ||
throw new WorkboxError_mjs.WorkboxError('queue-replay-failed', { | ||
name: this._name | ||
}); | ||
{ | ||
logger_js.logger.log(`All requests in queue '${this.name}' have successfully ` + `replayed; the queue is now empty!`); | ||
} | ||
} | ||
/** | ||
* Registers a sync event with a tag unique to this instance. | ||
*/ | ||
{ | ||
logger_mjs.logger.log(`All requests in queue '${this.name}' have successfully ` + `replayed; the queue is now empty!`); | ||
} | ||
} | ||
/** | ||
* Registers a sync event with a tag unique to this instance. | ||
*/ | ||
async registerSync() { | ||
if ('sync' in registration) { | ||
try { | ||
await registration.sync.register(`${TAG_PREFIX}:${this._name}`); | ||
} catch (err) { | ||
// This means the registration failed for some reason, possibly due to | ||
// the user disabling it. | ||
{ | ||
logger_mjs.logger.warn(`Unable to register sync event for '${this._name}'.`, err); | ||
async registerSync() { | ||
if ('sync' in self.registration) { | ||
try { | ||
await self.registration.sync.register(`${TAG_PREFIX}:${this._name}`); | ||
} catch (err) { | ||
// This means the registration failed for some reason, possibly due to | ||
// the user disabling it. | ||
{ | ||
logger_js.logger.warn(`Unable to register sync event for '${this._name}'.`, err); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
/** | ||
* In sync-supporting browsers, this adds a listener for the sync event. | ||
* In non-sync-supporting browsers, this will retry the queue on service | ||
* worker startup. | ||
* | ||
* @private | ||
*/ | ||
/** | ||
* In sync-supporting browsers, this adds a listener for the sync event. | ||
* In non-sync-supporting browsers, this will retry the queue on service | ||
* worker startup. | ||
* | ||
* @private | ||
*/ | ||
_addSyncListener() { | ||
if ('sync' in registration) { | ||
self.addEventListener('sync', event => { | ||
if (event.tag === `${TAG_PREFIX}:${this._name}`) { | ||
{ | ||
logger_mjs.logger.log(`Background sync for tag '${event.tag}'` + `has been received`); | ||
} | ||
_addSyncListener() { | ||
if ('sync' in self.registration) { | ||
self.addEventListener('sync', event => { | ||
if (event.tag === `${TAG_PREFIX}:${this._name}`) { | ||
{ | ||
logger_js.logger.log(`Background sync for tag '${event.tag}'` + `has been received`); | ||
} | ||
const syncComplete = async () => { | ||
this._syncInProgress = true; | ||
let syncError; | ||
const syncComplete = async () => { | ||
this._syncInProgress = true; | ||
let syncError; | ||
try { | ||
await this._onSync({ | ||
queue: this | ||
}); | ||
} catch (error) { | ||
syncError = error; // Rethrow the error. Note: the logic in the finally clause | ||
// will run before this gets rethrown. | ||
try { | ||
await this._onSync({ | ||
queue: this | ||
}); | ||
} catch (error) { | ||
syncError = error; // Rethrow the error. Note: the logic in the finally clause | ||
// will run before this gets rethrown. | ||
throw syncError; | ||
} finally { | ||
// New items may have been added to the queue during the sync, | ||
// so we need to register for a new sync if that's happened... | ||
// Unless there was an error during the sync, in which | ||
// case the browser will automatically retry later, as long | ||
// as `event.lastChance` is not true. | ||
if (this._requestsAddedDuringSync && !(syncError && !event.lastChance)) { | ||
await this.registerSync(); | ||
throw syncError; | ||
} finally { | ||
// New items may have been added to the queue during the sync, | ||
// so we need to register for a new sync if that's happened... | ||
// Unless there was an error during the sync, in which | ||
// case the browser will automatically retry later, as long | ||
// as `event.lastChance` is not true. | ||
if (this._requestsAddedDuringSync && !(syncError && !event.lastChance)) { | ||
await this.registerSync(); | ||
} | ||
this._syncInProgress = false; | ||
this._requestsAddedDuringSync = false; | ||
} | ||
}; | ||
this._syncInProgress = false; | ||
this._requestsAddedDuringSync = false; | ||
} | ||
}; | ||
event.waitUntil(syncComplete()); | ||
} | ||
}); | ||
} else { | ||
{ | ||
logger_js.logger.log(`Background sync replaying without background sync event`); | ||
} // If the browser doesn't support background sync, retry | ||
// every time the service worker starts up as a fallback. | ||
event.waitUntil(syncComplete()); | ||
} | ||
}); | ||
} else { | ||
{ | ||
logger_mjs.logger.log(`Background sync replaying without background sync event`); | ||
} // If the browser doesn't support background sync, retry | ||
// every time the service worker starts up as a fallback. | ||
this._onSync({ | ||
queue: this | ||
}); | ||
} | ||
} | ||
/** | ||
* Returns the set of queue names. This is primarily used to reset the list | ||
* of queue names in tests. | ||
* | ||
* @return {Set} | ||
* | ||
* @private | ||
*/ | ||
this._onSync({ | ||
queue: this | ||
}); | ||
static get _queueNames() { | ||
return queueNames; | ||
} | ||
} | ||
/** | ||
* Returns the set of queue names. This is primarily used to reset the list | ||
* of queue names in tests. | ||
* Converts a QueueStore entry into the format exposed by Queue. This entails | ||
* converting the request data into a real request and omitting the `id` and | ||
* `queueName` properties. | ||
* | ||
* @return {Set} | ||
* | ||
* @param {Object} queueStoreEntry | ||
* @return {Object} | ||
* @private | ||
@@ -738,86 +759,62 @@ */ | ||
static get _queueNames() { | ||
return queueNames; | ||
} | ||
const convertEntry = queueStoreEntry => { | ||
const queueEntry = { | ||
request: new StorableRequest(queueStoreEntry.requestData).toRequest(), | ||
timestamp: queueStoreEntry.timestamp | ||
}; | ||
} | ||
/** | ||
* Converts a QueueStore entry into the format exposed by Queue. This entails | ||
* converting the request data into a real request and omitting the `id` and | ||
* `queueName` properties. | ||
* | ||
* @param {Object} queueStoreEntry | ||
* @return {Object} | ||
* @private | ||
*/ | ||
if (queueStoreEntry.metadata) { | ||
queueEntry.metadata = queueStoreEntry.metadata; | ||
} | ||
const convertEntry = queueStoreEntry => { | ||
const queueEntry = { | ||
request: new StorableRequest(queueStoreEntry.requestData).toRequest(), | ||
timestamp: queueStoreEntry.timestamp | ||
return queueEntry; | ||
}; | ||
if (queueStoreEntry.metadata) { | ||
queueEntry.metadata = queueStoreEntry.metadata; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
return queueEntry; | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* A class implementing the `fetchDidFail` lifecycle callback. This makes it | ||
* easier to add failed requests to a background sync Queue. | ||
* | ||
* @memberof workbox.backgroundSync | ||
*/ | ||
class Plugin { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {...*} queueArgs Args to forward to the composed Queue instance. | ||
* See the [Queue]{@link workbox.backgroundSync.Queue} documentation for | ||
* parameter details. | ||
* A class implementing the `fetchDidFail` lifecycle callback. This makes it | ||
* easier to add failed requests to a background sync Queue. | ||
* | ||
* @memberof workbox.backgroundSync | ||
*/ | ||
constructor(...queueArgs) { | ||
this._queue = new Queue(...queueArgs); | ||
this.fetchDidFail = this.fetchDidFail.bind(this); | ||
} | ||
/** | ||
* @param {Object} options | ||
* @param {Request} options.request | ||
* @private | ||
*/ | ||
class Plugin { | ||
/** | ||
* @param {string} name See the [Queue]{@link workbox.backgroundSync.Queue} | ||
* documentation for parameter details. | ||
* @param {Object} [options] See the | ||
* [Queue]{@link workbox.backgroundSync.Queue} documentation for | ||
* parameter details. | ||
*/ | ||
constructor(name, options) { | ||
/** | ||
* @param {Object} options | ||
* @param {Request} options.request | ||
* @private | ||
*/ | ||
this.fetchDidFail = async ({ | ||
request | ||
}) => { | ||
await this._queue.pushRequest({ | ||
request | ||
}); | ||
}; | ||
async fetchDidFail({ | ||
request | ||
}) { | ||
await this._queue.pushRequest({ | ||
request | ||
}); | ||
this._queue = new Queue(name, options); | ||
} | ||
} | ||
} | ||
exports.Plugin = Plugin; | ||
exports.Queue = Queue; | ||
/* | ||
Copyright 2018 Google LLC | ||
return exports; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
exports.Queue = Queue; | ||
exports.Plugin = Plugin; | ||
return exports; | ||
}({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private)); | ||
@@ -1,2 +0,2 @@ | ||
this.workbox=this.workbox||{},this.workbox.backgroundSync=function(t,e,s){"use strict";try{self["workbox:background-sync:4.3.1"]&&_()}catch(t){}const i=3,n="workbox-background-sync",a="requests",r="queueName";class c{constructor(t){this.t=t,this.s=new s.DBWrapper(n,i,{onupgradeneeded:this.i})}async pushEntry(t){delete t.id,t.queueName=this.t,await this.s.add(a,t)}async unshiftEntry(t){const[e]=await this.s.getAllMatching(a,{count:1});e?t.id=e.id-1:delete t.id,t.queueName=this.t,await this.s.add(a,t)}async popEntry(){return this.h({direction:"prev"})}async shiftEntry(){return this.h({direction:"next"})}async getAll(){return await this.s.getAllMatching(a,{index:r,query:IDBKeyRange.only(this.t)})}async deleteEntry(t){await this.s.delete(a,t)}async h({direction:t}){const[e]=await this.s.getAllMatching(a,{direction:t,index:r,query:IDBKeyRange.only(this.t),count:1});if(e)return await this.deleteEntry(e.id),e}i(t){const e=t.target.result;t.oldVersion>0&&t.oldVersion<i&&e.objectStoreNames.contains(a)&&e.deleteObjectStore(a),e.createObjectStore(a,{autoIncrement:!0,keyPath:"id"}).createIndex(r,r,{unique:!1})}}const h=["method","referrer","referrerPolicy","mode","credentials","cache","redirect","integrity","keepalive"];class o{static async fromRequest(t){const e={url:t.url,headers:{}};"GET"!==t.method&&(e.body=await t.clone().arrayBuffer());for(const[s,i]of t.headers.entries())e.headers[s]=i;for(const s of h)void 0!==t[s]&&(e[s]=t[s]);return new o(e)}constructor(t){"navigate"===t.mode&&(t.mode="same-origin"),this.o=t}toObject(){const t=Object.assign({},this.o);return t.headers=Object.assign({},this.o.headers),t.body&&(t.body=t.body.slice(0)),t}toRequest(){return new Request(this.o.url,this.o)}clone(){return new o(this.toObject())}}const u="workbox-background-sync",y=10080,w=new Set;class d{constructor(t,{onSync:s,maxRetentionTime:i}={}){if(w.has(t))throw new e.WorkboxError("duplicate-queue-name",{name:t});w.add(t),this.u=t,this.l=s||this.replayRequests,this.q=i||y,this.m=new c(this.u),this.p()}get name(){return this.u}async pushRequest(t){await this.g(t,"push")}async unshiftRequest(t){await this.g(t,"unshift")}async popRequest(){return this.R("pop")}async shiftRequest(){return this.R("shift")}async getAll(){const t=await this.m.getAll(),e=Date.now(),s=[];for(const i of t){const t=60*this.q*1e3;e-i.timestamp>t?await this.m.deleteEntry(i.id):s.push(f(i))}return s}async g({request:t,metadata:e,timestamp:s=Date.now()},i){const n={requestData:(await o.fromRequest(t.clone())).toObject(),timestamp:s};e&&(n.metadata=e),await this.m[`${i}Entry`](n),this.k?this.D=!0:await this.registerSync()}async R(t){const e=Date.now(),s=await this.m[`${t}Entry`]();if(s){const i=60*this.q*1e3;return e-s.timestamp>i?this.R(t):f(s)}}async replayRequests(){let t;for(;t=await this.shiftRequest();)try{await fetch(t.request.clone())}catch(s){throw await this.unshiftRequest(t),new e.WorkboxError("queue-replay-failed",{name:this.u})}}async registerSync(){if("sync"in registration)try{await registration.sync.register(`${u}:${this.u}`)}catch(t){}}p(){"sync"in registration?self.addEventListener("sync",t=>{if(t.tag===`${u}:${this.u}`){const e=async()=>{let e;this.k=!0;try{await this.l({queue:this})}catch(t){throw e=t}finally{!this.D||e&&!t.lastChance||await this.registerSync(),this.k=!1,this.D=!1}};t.waitUntil(e())}}):this.l({queue:this})}static get _(){return w}}const f=t=>{const e={request:new o(t.requestData).toRequest(),timestamp:t.timestamp};return t.metadata&&(e.metadata=t.metadata),e};return t.Queue=d,t.Plugin=class{constructor(...t){this.v=new d(...t),this.fetchDidFail=this.fetchDidFail.bind(this)}async fetchDidFail({request:t}){await this.v.pushRequest({request:t})}},t}({},workbox.core._private,workbox.core._private); | ||
this.workbox=this.workbox||{},this.workbox.backgroundSync=function(t,e,s,i,n){"use strict";try{self["workbox:background-sync:5.0.0-alpha.2"]&&_()}catch(t){}const a=3,r="workbox-background-sync",c="requests",h="queueName";class o{constructor(t){this.t=t,this.s=new n.DBWrapper(r,a,{onupgradeneeded:this.i})}async pushEntry(t){delete t.id,t.queueName=this.t,await this.s.add(c,t)}async unshiftEntry(t){const[e]=await this.s.getAllMatching(c,{count:1});e?t.id=e.id-1:delete t.id,t.queueName=this.t,await this.s.add(c,t)}async popEntry(){return this.h({direction:"prev"})}async shiftEntry(){return this.h({direction:"next"})}async getAll(){return await this.s.getAllMatching(c,{index:h,query:IDBKeyRange.only(this.t)})}async deleteEntry(t){await this.s.delete(c,t)}async h({direction:t}){const[e]=await this.s.getAllMatching(c,{direction:t,index:h,query:IDBKeyRange.only(this.t),count:1});if(e)return await this.deleteEntry(e.id),e}i(t){const e=t.target.result;t.oldVersion>0&&t.oldVersion<a&&e.objectStoreNames.contains(c)&&e.deleteObjectStore(c),e.createObjectStore(c,{autoIncrement:!0,keyPath:"id"}).createIndex(h,h,{unique:!1})}}const u=["method","referrer","referrerPolicy","mode","credentials","cache","redirect","integrity","keepalive"];class y{static async fromRequest(t){const e={url:t.url,headers:{}};"GET"!==t.method&&(e.body=await t.clone().arrayBuffer());for(const[s,i]of t.headers.entries())e.headers[s]=i;for(const s of u)void 0!==t[s]&&(e[s]=t[s]);return new y(e)}constructor(t){"navigate"===t.mode&&(t.mode="same-origin"),this.o=t}toObject(){const t=Object.assign({},this.o);return t.headers=Object.assign({},this.o.headers),t.body&&(t.body=t.body.slice(0)),t}toRequest(){return new Request(this.o.url,this.o)}clone(){return new y(this.toObject())}}const w="workbox-background-sync",f=10080,d=new Set;class l{constructor(t,{onSync:s,maxRetentionTime:i}={}){if(this.u=!1,this.l=!1,d.has(t))throw new e.WorkboxError("duplicate-queue-name",{name:t});d.add(t),this.q=t,this.m=s||this.replayRequests,this.p=i||f,this.g=new o(this.q),this.R()}get name(){return this.q}async pushRequest(t){await this.k(t,"push")}async unshiftRequest(t){await this.k(t,"unshift")}async popRequest(){return this.D("pop")}async shiftRequest(){return this.D("shift")}async getAll(){const t=await this.g.getAll(),e=Date.now(),s=[];for(const i of t){const t=60*this.p*1e3;e-i.timestamp>t?await this.g.deleteEntry(i.id):s.push(q(i))}return s}async k({request:t,metadata:e,timestamp:s=Date.now()},i){const n={requestData:(await y.fromRequest(t.clone())).toObject(),timestamp:s};e&&(n.metadata=e),await this.g[`${i}Entry`](n),this.u?this.l=!0:await this.registerSync()}async D(t){const e=Date.now(),s=await this.g[`${t}Entry`]();if(s){const i=60*this.p*1e3;return e-s.timestamp>i?this.D(t):q(s)}}async replayRequests(){let t;for(;t=await this.shiftRequest();)try{await fetch(t.request.clone())}catch(s){throw await this.unshiftRequest(t),new e.WorkboxError("queue-replay-failed",{name:this.q})}}async registerSync(){if("sync"in self.registration)try{await self.registration.sync.register(`${w}:${this.q}`)}catch(t){}}R(){"sync"in self.registration?self.addEventListener("sync",t=>{if(t.tag===`${w}:${this.q}`){const e=async()=>{let e;this.u=!0;try{await this.m({queue:this})}catch(t){throw e=t}finally{!this.l||e&&!t.lastChance||await this.registerSync(),this.u=!1,this.l=!1}};t.waitUntil(e())}}):this.m({queue:this})}static get _(){return d}}const q=t=>{const e={request:new y(t.requestData).toRequest(),timestamp:t.timestamp};return t.metadata&&(e.metadata=t.metadata),e};return t.Plugin=class{constructor(t,e){this.fetchDidFail=(async({request:t})=>{await this.v.pushRequest({request:t})}),this.v=new l(t,e)}},t.Queue=l,t}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private); | ||
this.workbox = this.workbox || {}; | ||
this.workbox.broadcastUpdate = (function (exports, assert_mjs, getFriendlyURL_mjs, logger_mjs, Deferred_mjs, WorkboxError_mjs) { | ||
'use strict'; | ||
this.workbox.broadcastUpdate = (function (exports, assert_js, getFriendlyURL_js, logger_js, Deferred_js, WorkboxError_js) { | ||
'use strict'; | ||
try { | ||
self['workbox:broadcast-update:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
// @ts-ignore | ||
try { | ||
self['workbox:broadcast-update:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Given two `Response's`, compares several header values to see if they are | ||
* the same or not. | ||
* | ||
* @param {Response} firstResponse | ||
* @param {Response} secondResponse | ||
* @param {Array<string>} headersToCheck | ||
* @return {boolean} | ||
* | ||
* @memberof workbox.broadcastUpdate | ||
* @private | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Given two `Response's`, compares several header values to see if they are | ||
* the same or not. | ||
* | ||
* @param {Response} firstResponse | ||
* @param {Response} secondResponse | ||
* @param {Array<string>} headersToCheck | ||
* @return {boolean} | ||
* | ||
* @memberof workbox.broadcastUpdate | ||
* @private | ||
*/ | ||
const responsesAreSame = (firstResponse, secondResponse, headersToCheck) => { | ||
{ | ||
if (!(firstResponse instanceof Response && secondResponse instanceof Response)) { | ||
throw new WorkboxError_mjs.WorkboxError('invalid-responses-are-same-args'); | ||
const responsesAreSame = (firstResponse, secondResponse, headersToCheck) => { | ||
{ | ||
if (!(firstResponse instanceof Response && secondResponse instanceof Response)) { | ||
throw new WorkboxError_js.WorkboxError('invalid-responses-are-same-args'); | ||
} | ||
} | ||
} | ||
const atLeastOneHeaderAvailable = headersToCheck.some(header => { | ||
return firstResponse.headers.has(header) && secondResponse.headers.has(header); | ||
}); | ||
const atLeastOneHeaderAvailable = headersToCheck.some(header => { | ||
return firstResponse.headers.has(header) && secondResponse.headers.has(header); | ||
}); | ||
if (!atLeastOneHeaderAvailable) { | ||
{ | ||
logger_mjs.logger.warn(`Unable to determine where the response has been updated ` + `because none of the headers that would be checked are present.`); | ||
logger_mjs.logger.debug(`Attempting to compare the following: `, firstResponse, secondResponse, headersToCheck); | ||
} // Just return true, indicating the that responses are the same, since we | ||
// can't determine otherwise. | ||
if (!atLeastOneHeaderAvailable) { | ||
{ | ||
logger_js.logger.warn(`Unable to determine where the response has been updated ` + `because none of the headers that would be checked are present.`); | ||
logger_js.logger.debug(`Attempting to compare the following: `, firstResponse, secondResponse, headersToCheck); | ||
} // Just return true, indicating the that responses are the same, since we | ||
// can't determine otherwise. | ||
return true; | ||
} | ||
return true; | ||
} | ||
return headersToCheck.every(header => { | ||
const headerStateComparison = firstResponse.headers.has(header) === secondResponse.headers.has(header); | ||
const headerValueComparison = firstResponse.headers.get(header) === secondResponse.headers.get(header); | ||
return headerStateComparison && headerValueComparison; | ||
}); | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const CACHE_UPDATED_MESSAGE_TYPE = 'CACHE_UPDATED'; | ||
const CACHE_UPDATED_MESSAGE_META = 'workbox-broadcast-update'; | ||
const DEFAULT_BROADCAST_CHANNEL_NAME = 'workbox'; | ||
const DEFAULT_DEFER_NOTIFICATION_TIMEOUT = 10000; | ||
const DEFAULT_HEADERS_TO_CHECK = ['content-length', 'etag', 'last-modified']; | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* You would not normally call this method directly; it's called automatically | ||
* by an instance of the {@link BroadcastCacheUpdate} class. It's exposed here | ||
* for the benefit of developers who would rather not use the full | ||
* `BroadcastCacheUpdate` implementation. | ||
* | ||
* Calling this will dispatch a message on the provided | ||
* {@link https://developers.google.com/web/updates/2016/09/broadcastchannel|Broadcast Channel} | ||
* to notify interested subscribers about a change to a cached resource. | ||
* | ||
* The message that's posted has a formation inspired by the | ||
* [Flux standard action](https://github.com/acdlite/flux-standard-action#introduction) | ||
* format like so: | ||
* | ||
* ``` | ||
* { | ||
* type: 'CACHE_UPDATED', | ||
* meta: 'workbox-broadcast-update', | ||
* payload: { | ||
* cacheName: 'the-cache-name', | ||
* updatedURL: 'https://example.com/' | ||
* } | ||
* } | ||
* ``` | ||
* | ||
* (Usage of [Flux](https://facebook.github.io/flux/) itself is not at | ||
* all required.) | ||
* | ||
* @param {Object} options | ||
* @param {string} options.cacheName The name of the cache in which the updated | ||
* `Response` was stored. | ||
* @param {string} options.url The URL associated with the updated `Response`. | ||
* @param {BroadcastChannel} [options.channel] The `BroadcastChannel` to use. | ||
* If no channel is set or the browser doesn't support the BroadcastChannel | ||
* api, then an attempt will be made to `postMessage` each window client. | ||
* | ||
* @memberof workbox.broadcastUpdate | ||
*/ | ||
const broadcastUpdate = async ({ | ||
channel, | ||
cacheName, | ||
url | ||
}) => { | ||
{ | ||
assert_mjs.assert.isType(cacheName, 'string', { | ||
moduleName: 'workbox-broadcast-update', | ||
className: '~', | ||
funcName: 'broadcastUpdate', | ||
paramName: 'cacheName' | ||
return headersToCheck.every(header => { | ||
const headerStateComparison = firstResponse.headers.has(header) === secondResponse.headers.has(header); | ||
const headerValueComparison = firstResponse.headers.get(header) === secondResponse.headers.get(header); | ||
return headerStateComparison && headerValueComparison; | ||
}); | ||
assert_mjs.assert.isType(url, 'string', { | ||
moduleName: 'workbox-broadcast-update', | ||
className: '~', | ||
funcName: 'broadcastUpdate', | ||
paramName: 'url' | ||
}); | ||
} | ||
const data = { | ||
type: CACHE_UPDATED_MESSAGE_TYPE, | ||
meta: CACHE_UPDATED_MESSAGE_META, | ||
payload: { | ||
cacheName: cacheName, | ||
updatedURL: url | ||
} | ||
}; | ||
if (channel) { | ||
channel.postMessage(data); | ||
} else { | ||
const windows = await clients.matchAll({ | ||
type: 'window' | ||
}); | ||
/* | ||
Copyright 2018 Google LLC | ||
for (const win of windows) { | ||
win.postMessage(data); | ||
} | ||
} | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const CACHE_UPDATED_MESSAGE_TYPE = 'CACHE_UPDATED'; | ||
const CACHE_UPDATED_MESSAGE_META = 'workbox-broadcast-update'; | ||
const DEFAULT_BROADCAST_CHANNEL_NAME = 'workbox'; | ||
const DEFAULT_DEFER_NOTIFICATION_TIMEOUT = 10000; | ||
const DEFAULT_HEADERS_TO_CHECK = ['content-length', 'etag', 'last-modified']; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Uses the [Broadcast Channel API]{@link https://developers.google.com/web/updates/2016/09/broadcastchannel} | ||
* to notify interested parties when a cached response has been updated. | ||
* In browsers that do not support the Broadcast Channel API, the instance | ||
* falls back to sending the update via `postMessage()` to all window clients. | ||
* | ||
* For efficiency's sake, the underlying response bodies are not compared; | ||
* only specific response headers are checked. | ||
* | ||
* @memberof workbox.broadcastUpdate | ||
*/ | ||
class BroadcastCacheUpdate { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Construct a BroadcastCacheUpdate instance with a specific `channelName` to | ||
* broadcast messages on | ||
* You would not normally call this method directly; it's called automatically | ||
* by an instance of the {@link BroadcastCacheUpdate} class. It's exposed here | ||
* for the benefit of developers who would rather not use the full | ||
* `BroadcastCacheUpdate` implementation. | ||
* | ||
* Calling this will dispatch a message on the provided | ||
* {@link https://developers.google.com/web/updates/2016/09/broadcastchannel|Broadcast Channel} | ||
* to notify interested subscribers about a change to a cached resource. | ||
* | ||
* The message that's posted has a formation inspired by the | ||
* [Flux standard action](https://github.com/acdlite/flux-standard-action#introduction) | ||
* format like so: | ||
* | ||
* ``` | ||
* { | ||
* type: 'CACHE_UPDATED', | ||
* meta: 'workbox-broadcast-update', | ||
* payload: { | ||
* cacheName: 'the-cache-name', | ||
* updatedURL: 'https://example.com/' | ||
* } | ||
* } | ||
* ``` | ||
* | ||
* (Usage of [Flux](https://facebook.github.io/flux/) itself is not at | ||
* all required.) | ||
* | ||
* @param {Object} options | ||
* @param {Array<string>} | ||
* [options.headersToCheck=['content-length', 'etag', 'last-modified']] | ||
* A list of headers that will be used to determine whether the responses | ||
* differ. | ||
* @param {string} [options.channelName='workbox'] The name that will be used | ||
*. when creating the `BroadcastChannel`, which defaults to 'workbox' (the | ||
* channel name used by the `workbox-window` package). | ||
* @param {string} [options.deferNoticationTimeout=10000] The amount of time | ||
* to wait for a ready message from the window on navigation requests | ||
* before sending the update. | ||
* @param {string} options.cacheName The name of the cache in which the updated | ||
* `Response` was stored. | ||
* @param {string} options.url The URL associated with the updated `Response`. | ||
* @param {BroadcastChannel} [options.channel] The `BroadcastChannel` to use. | ||
* If no channel is set or the browser doesn't support the BroadcastChannel | ||
* api, then an attempt will be made to `postMessage` each window client. | ||
* | ||
* @memberof workbox.broadcastUpdate | ||
*/ | ||
constructor({ | ||
headersToCheck, | ||
channelName, | ||
deferNoticationTimeout | ||
} = {}) { | ||
this._headersToCheck = headersToCheck || DEFAULT_HEADERS_TO_CHECK; | ||
this._channelName = channelName || DEFAULT_BROADCAST_CHANNEL_NAME; | ||
this._deferNoticationTimeout = deferNoticationTimeout || DEFAULT_DEFER_NOTIFICATION_TIMEOUT; | ||
const broadcastUpdate = async ({ | ||
channel, | ||
cacheName, | ||
url | ||
}) => { | ||
{ | ||
assert_mjs.assert.isType(this._channelName, 'string', { | ||
assert_js.assert.isType(cacheName, 'string', { | ||
moduleName: 'workbox-broadcast-update', | ||
className: 'BroadcastCacheUpdate', | ||
funcName: 'constructor', | ||
paramName: 'channelName' | ||
className: '~', | ||
funcName: 'broadcastUpdate', | ||
paramName: 'cacheName' | ||
}); | ||
assert_mjs.assert.isArray(this._headersToCheck, { | ||
assert_js.assert.isType(url, 'string', { | ||
moduleName: 'workbox-broadcast-update', | ||
className: 'BroadcastCacheUpdate', | ||
funcName: 'constructor', | ||
paramName: 'headersToCheck' | ||
className: '~', | ||
funcName: 'broadcastUpdate', | ||
paramName: 'url' | ||
}); | ||
} | ||
this._initWindowReadyDeferreds(); | ||
} | ||
const data = { | ||
type: CACHE_UPDATED_MESSAGE_TYPE, | ||
meta: CACHE_UPDATED_MESSAGE_META, | ||
payload: { | ||
cacheName: cacheName, | ||
updatedURL: url | ||
} | ||
}; | ||
if (channel) { | ||
channel.postMessage(data); | ||
} else { | ||
const windows = await self.clients.matchAll({ | ||
type: 'window' | ||
}); | ||
for (const win of windows) { | ||
win.postMessage(data); | ||
} | ||
} | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Compare two [Responses](https://developer.mozilla.org/en-US/docs/Web/API/Response) | ||
* and send a message via the | ||
* {@link https://developers.google.com/web/updates/2016/09/broadcastchannel|Broadcast Channel API} | ||
* if they differ. | ||
* Uses the [Broadcast Channel API]{@link https://developers.google.com/web/updates/2016/09/broadcastchannel} | ||
* to notify interested parties when a cached response has been updated. | ||
* In browsers that do not support the Broadcast Channel API, the instance | ||
* falls back to sending the update via `postMessage()` to all window clients. | ||
* | ||
* Neither of the Responses can be {@link http://stackoverflow.com/questions/39109789|opaque}. | ||
* For efficiency's sake, the underlying response bodies are not compared; | ||
* only specific response headers are checked. | ||
* | ||
* @param {Object} options | ||
* @param {Response} options.oldResponse Cached response to compare. | ||
* @param {Response} options.newResponse Possibly updated response to compare. | ||
* @param {string} options.url The URL of the request. | ||
* @param {string} options.cacheName Name of the cache the responses belong | ||
* to. This is included in the broadcast message. | ||
* @param {Event} [options.event] event An optional event that triggered | ||
* this possible cache update. | ||
* @return {Promise} Resolves once the update is sent. | ||
* @memberof workbox.broadcastUpdate | ||
*/ | ||
class BroadcastCacheUpdate { | ||
/** | ||
* Construct a BroadcastCacheUpdate instance with a specific `channelName` to | ||
* broadcast messages on | ||
* | ||
* @param {Object} options | ||
* @param {Array<string>} | ||
* [options.headersToCheck=['content-length', 'etag', 'last-modified']] | ||
* A list of headers that will be used to determine whether the responses | ||
* differ. | ||
* @param {string} [options.channelName='workbox'] The name that will be used | ||
*. when creating the `BroadcastChannel`, which defaults to 'workbox' (the | ||
* channel name used by the `workbox-window` package). | ||
* @param {string} [options.deferNoticationTimeout=10000] The amount of time | ||
* to wait for a ready message from the window on navigation requests | ||
* before sending the update. | ||
*/ | ||
constructor({ | ||
headersToCheck, | ||
channelName, | ||
deferNoticationTimeout | ||
} = {}) { | ||
this._headersToCheck = headersToCheck || DEFAULT_HEADERS_TO_CHECK; | ||
this._channelName = channelName || DEFAULT_BROADCAST_CHANNEL_NAME; | ||
this._deferNoticationTimeout = deferNoticationTimeout || DEFAULT_DEFER_NOTIFICATION_TIMEOUT; | ||
notifyIfUpdated({ | ||
oldResponse, | ||
newResponse, | ||
url, | ||
cacheName, | ||
event | ||
}) { | ||
if (!responsesAreSame(oldResponse, newResponse, this._headersToCheck)) { | ||
{ | ||
logger_mjs.logger.log(`Newer response found (and cached) for:`, url); | ||
assert_js.assert.isType(this._channelName, 'string', { | ||
moduleName: 'workbox-broadcast-update', | ||
className: 'BroadcastCacheUpdate', | ||
funcName: 'constructor', | ||
paramName: 'channelName' | ||
}); | ||
assert_js.assert.isArray(this._headersToCheck, { | ||
moduleName: 'workbox-broadcast-update', | ||
className: 'BroadcastCacheUpdate', | ||
funcName: 'constructor', | ||
paramName: 'headersToCheck' | ||
}); | ||
} | ||
const sendUpdate = async () => { | ||
// In the case of a navigation request, the requesting page will likely | ||
// not have loaded its JavaScript in time to recevied the update | ||
// notification, so we defer it until ready (or we timeout waiting). | ||
if (event && event.request && event.request.mode === 'navigate') { | ||
{ | ||
logger_mjs.logger.debug(`Original request was a navigation request, ` + `waiting for a ready message from the window`, event.request); | ||
} | ||
this._initWindowReadyDeferreds(); | ||
} | ||
/** | ||
* Compare two [Responses](https://developer.mozilla.org/en-US/docs/Web/API/Response) | ||
* and send a message via the | ||
* {@link https://developers.google.com/web/updates/2016/09/broadcastchannel|Broadcast Channel API} | ||
* if they differ. | ||
* | ||
* Neither of the Responses can be {@link http://stackoverflow.com/questions/39109789|opaque}. | ||
* | ||
* @param {Object} options | ||
* @param {Response} options.oldResponse Cached response to compare. | ||
* @param {Response} options.newResponse Possibly updated response to compare. | ||
* @param {string} options.url The URL of the request. | ||
* @param {string} options.cacheName Name of the cache the responses belong | ||
* to. This is included in the broadcast message. | ||
* @param {Event} [options.event] event An optional event that triggered | ||
* this possible cache update. | ||
* @return {Promise} Resolves once the update is sent. | ||
*/ | ||
await this._windowReadyOrTimeout(event); | ||
notifyIfUpdated({ | ||
oldResponse, | ||
newResponse, | ||
url, | ||
cacheName, | ||
event | ||
}) { | ||
if (!responsesAreSame(oldResponse, newResponse, this._headersToCheck)) { | ||
{ | ||
logger_js.logger.log(`Newer response found (and cached) for:`, url); | ||
} | ||
await this._broadcastUpdate({ | ||
channel: this._getChannel(), | ||
cacheName, | ||
url | ||
}); | ||
}; // Send the update and ensure the SW stays alive until it's sent. | ||
const sendUpdate = async () => { | ||
// In the case of a navigation request, the requesting page will likely | ||
// not have loaded its JavaScript in time to recevied the update | ||
// notification, so we defer it until ready (or we timeout waiting). | ||
if (event && event.request && event.request.mode === 'navigate') { | ||
{ | ||
logger_js.logger.debug(`Original request was a navigation request, ` + `waiting for a ready message from the window`, event.request); | ||
} | ||
await this._windowReadyOrTimeout(event); | ||
} | ||
const done = sendUpdate(); | ||
await this._broadcastUpdate({ | ||
channel: this._getChannel(), | ||
cacheName, | ||
url | ||
}); | ||
}; // Send the update and ensure the SW stays alive until it's sent. | ||
if (event) { | ||
try { | ||
event.waitUntil(done); | ||
} catch (error) { | ||
{ | ||
logger_mjs.logger.warn(`Unable to ensure service worker stays alive ` + `when broadcasting cache update for ` + `${getFriendlyURL_mjs.getFriendlyURL(event.request.url)}'.`); | ||
const done = sendUpdate(); | ||
if (event) { | ||
try { | ||
event.waitUntil(done); | ||
} catch (error) { | ||
{ | ||
logger_js.logger.warn(`Unable to ensure service worker stays alive ` + `when broadcasting cache update for ` + `${getFriendlyURL_js.getFriendlyURL(event.request.url)}'.`); | ||
} | ||
} | ||
} | ||
return done; | ||
} | ||
} | ||
/** | ||
* NOTE: this is exposed on the instance primarily so it can be spied on | ||
* in tests. | ||
* | ||
* @param {Object} opts | ||
* @private | ||
*/ | ||
return done; | ||
async _broadcastUpdate(opts) { | ||
await broadcastUpdate(opts); | ||
} | ||
} | ||
/** | ||
* NOTE: this is exposed on the instance primarily so it can be spied on | ||
* in tests. | ||
* | ||
* @param {Object} opts | ||
* @private | ||
*/ | ||
/** | ||
* @return {BroadcastChannel|undefined} The BroadcastChannel instance used for | ||
* broadcasting updates, or undefined if the browser doesn't support the | ||
* Broadcast Channel API. | ||
* | ||
* @private | ||
*/ | ||
async _broadcastUpdate(opts) { | ||
await broadcastUpdate(opts); | ||
} | ||
/** | ||
* @return {BroadcastChannel|undefined} The BroadcastChannel instance used for | ||
* broadcasting updates, or undefined if the browser doesn't support the | ||
* Broadcast Channel API. | ||
* | ||
* @private | ||
*/ | ||
_getChannel() { | ||
if ('BroadcastChannel' in self && !this._channel) { | ||
this._channel = new BroadcastChannel(this._channelName); | ||
} | ||
_getChannel() { | ||
if ('BroadcastChannel' in self && !this._channel) { | ||
this._channel = new BroadcastChannel(this._channelName); | ||
return this._channel; | ||
} | ||
/** | ||
* Waits for a message from the window indicating that it's capable of | ||
* receiving broadcasts. By default, this will only wait for the amount of | ||
* time specified via the `deferNoticationTimeout` option. | ||
* | ||
* @param {Event} event The navigation fetch event. | ||
* @return {Promise} | ||
* @private | ||
*/ | ||
return this._channel; | ||
} | ||
/** | ||
* Waits for a message from the window indicating that it's capable of | ||
* receiving broadcasts. By default, this will only wait for the amount of | ||
* time specified via the `deferNoticationTimeout` option. | ||
* | ||
* @param {Event} event The navigation fetch event. | ||
* @return {Promise} | ||
* @private | ||
*/ | ||
_windowReadyOrTimeout(event) { | ||
if (!this._navigationEventsDeferreds.has(event)) { | ||
const deferred = new Deferred_js.Deferred(); // Set the deferred on the `_navigationEventsDeferreds` map so it will | ||
// be resolved when the next ready message event comes. | ||
_windowReadyOrTimeout(event) { | ||
if (!this._navigationEventsDeferreds.has(event)) { | ||
const deferred = new Deferred_mjs.Deferred(); // Set the deferred on the `_navigationEventsDeferreds` map so it will | ||
// be resolved when the next ready message event comes. | ||
this._navigationEventsDeferreds.set(event, deferred); // But don't wait too long for the message since it may never come. | ||
this._navigationEventsDeferreds.set(event, deferred); // But don't wait too long for the message since it may never come. | ||
const timeout = setTimeout(() => { | ||
{ | ||
logger_js.logger.debug(`Timed out after ${this._deferNoticationTimeout}` + `ms waiting for message from window`); | ||
} | ||
const timeout = setTimeout(() => { | ||
{ | ||
logger_mjs.logger.debug(`Timed out after ${this._deferNoticationTimeout}` + `ms waiting for message from window`); | ||
} | ||
deferred.resolve(); | ||
}, this._deferNoticationTimeout); // Ensure the timeout is cleared if the deferred promise is resolved. | ||
deferred.resolve(); | ||
}, this._deferNoticationTimeout); // Ensure the timeout is cleared if the deferred promise is resolved. | ||
deferred.promise.then(() => clearTimeout(timeout)); | ||
} | ||
deferred.promise.then(() => clearTimeout(timeout)); | ||
return this._navigationEventsDeferreds.get(event).promise; | ||
} | ||
/** | ||
* Creates a mapping between navigation fetch events and deferreds, and adds | ||
* a listener for message events from the window. When message events arrive, | ||
* all deferreds in the mapping are resolved. | ||
* | ||
* Note: it would be easier if we could only resolve the deferred of | ||
* navigation fetch event whose client ID matched the source ID of the | ||
* message event, but currently client IDs are not exposed on navigation | ||
* fetch events: https://www.chromestatus.com/feature/4846038800138240 | ||
* | ||
* @private | ||
*/ | ||
return this._navigationEventsDeferreds.get(event).promise; | ||
} | ||
/** | ||
* Creates a mapping between navigation fetch events and deferreds, and adds | ||
* a listener for message events from the window. When message events arrive, | ||
* all deferreds in the mapping are resolved. | ||
* | ||
* Note: it would be easier if we could only resolve the deferred of | ||
* navigation fetch event whose client ID matched the source ID of the | ||
* message event, but currently client IDs are not exposed on navigation | ||
* fetch events: https://www.chromestatus.com/feature/4846038800138240 | ||
* | ||
* @private | ||
*/ | ||
_initWindowReadyDeferreds() { | ||
// A mapping between navigation events and their deferreds. | ||
this._navigationEventsDeferreds = new Map(); // The message listener needs to be added in the initial run of the | ||
// service worker, but since we don't actually need to be listening for | ||
// messages until the cache updates, we only invoke the callback if set. | ||
_initWindowReadyDeferreds() { | ||
// A mapping between navigation events and their deferreds. | ||
this._navigationEventsDeferreds = new Map(); // The message listener needs to be added in the initial run of the | ||
// service worker, but since we don't actually need to be listening for | ||
// messages until the cache updates, we only invoke the callback if set. | ||
self.addEventListener('message', event => { | ||
if (event.data.type === 'WINDOW_READY' && event.data.meta === 'workbox-window' && this._navigationEventsDeferreds.size > 0) { | ||
{ | ||
logger_js.logger.debug(`Received WINDOW_READY event: `, event); | ||
} // Resolve any pending deferreds. | ||
self.addEventListener('message', event => { | ||
if (event.data.type === 'WINDOW_READY' && event.data.meta === 'workbox-window' && this._navigationEventsDeferreds.size > 0) { | ||
{ | ||
logger_mjs.logger.debug(`Received WINDOW_READY event: `, event); | ||
} // Resolve any pending deferreds. | ||
for (const deferred of this._navigationEventsDeferreds.values()) { | ||
deferred.resolve(); | ||
} | ||
for (const deferred of this._navigationEventsDeferreds.values()) { | ||
deferred.resolve(); | ||
this._navigationEventsDeferreds.clear(); | ||
} | ||
}); | ||
} | ||
this._navigationEventsDeferreds.clear(); | ||
} | ||
}); | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* This plugin will automatically broadcast a message whenever a cached response | ||
* is updated. | ||
* | ||
* @memberof workbox.broadcastUpdate | ||
*/ | ||
class Plugin { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Construct a BroadcastCacheUpdate instance with the passed options and | ||
* calls its `notifyIfUpdated()` method whenever the plugin's | ||
* `cacheDidUpdate` callback is invoked. | ||
* This plugin will automatically broadcast a message whenever a cached response | ||
* is updated. | ||
* | ||
* @param {Object} options | ||
* @param {Array<string>} | ||
* [options.headersToCheck=['content-length', 'etag', 'last-modified']] | ||
* A list of headers that will be used to determine whether the responses | ||
* differ. | ||
* @param {string} [options.channelName='workbox'] The name that will be used | ||
*. when creating the `BroadcastChannel`, which defaults to 'workbox' (the | ||
* channel name used by the `workbox-window` package). | ||
* @param {string} [options.deferNoticationTimeout=10000] The amount of time | ||
* to wait for a ready message from the window on navigation requests | ||
* before sending the update. | ||
* @memberof workbox.broadcastUpdate | ||
*/ | ||
constructor(options) { | ||
this._broadcastUpdate = new BroadcastCacheUpdate(options); | ||
} | ||
/** | ||
* A "lifecycle" callback that will be triggered automatically by the | ||
* `workbox-sw` and `workbox-runtime-caching` handlers when an entry is | ||
* added to a cache. | ||
* | ||
* @private | ||
* @param {Object} options The input object to this function. | ||
* @param {string} options.cacheName Name of the cache being updated. | ||
* @param {Response} [options.oldResponse] The previous cached value, if any. | ||
* @param {Response} options.newResponse The new value in the cache. | ||
* @param {Request} options.request The request that triggered the udpate. | ||
* @param {Request} [options.event] The event that triggered the update. | ||
*/ | ||
class Plugin { | ||
/** | ||
* Construct a BroadcastCacheUpdate instance with the passed options and | ||
* calls its `notifyIfUpdated()` method whenever the plugin's | ||
* `cacheDidUpdate` callback is invoked. | ||
* | ||
* @param {Object} options | ||
* @param {Array<string>} | ||
* [options.headersToCheck=['content-length', 'etag', 'last-modified']] | ||
* A list of headers that will be used to determine whether the responses | ||
* differ. | ||
* @param {string} [options.channelName='workbox'] The name that will be used | ||
*. when creating the `BroadcastChannel`, which defaults to 'workbox' (the | ||
* channel name used by the `workbox-window` package). | ||
* @param {string} [options.deferNoticationTimeout=10000] The amount of time | ||
* to wait for a ready message from the window on navigation requests | ||
* before sending the update. | ||
*/ | ||
constructor(options) { | ||
/** | ||
* A "lifecycle" callback that will be triggered automatically by the | ||
* `workbox-sw` and `workbox-runtime-caching` handlers when an entry is | ||
* added to a cache. | ||
* | ||
* @private | ||
* @param {Object} options The input object to this function. | ||
* @param {string} options.cacheName Name of the cache being updated. | ||
* @param {Response} [options.oldResponse] The previous cached value, if any. | ||
* @param {Response} options.newResponse The new value in the cache. | ||
* @param {Request} options.request The request that triggered the update. | ||
* @param {Request} [options.event] The event that triggered the update. | ||
*/ | ||
this.cacheDidUpdate = async ({ | ||
cacheName, | ||
oldResponse, | ||
newResponse, | ||
request, | ||
event | ||
}) => { | ||
{ | ||
assert_js.assert.isType(cacheName, 'string', { | ||
moduleName: 'workbox-broadcast-update', | ||
className: 'Plugin', | ||
funcName: 'cacheDidUpdate', | ||
paramName: 'cacheName' | ||
}); | ||
assert_js.assert.isInstance(newResponse, Response, { | ||
moduleName: 'workbox-broadcast-update', | ||
className: 'Plugin', | ||
funcName: 'cacheDidUpdate', | ||
paramName: 'newResponse' | ||
}); | ||
assert_js.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-broadcast-update', | ||
className: 'Plugin', | ||
funcName: 'cacheDidUpdate', | ||
paramName: 'request' | ||
}); | ||
} // Without a two responses there is nothing to compare. | ||
cacheDidUpdate({ | ||
cacheName, | ||
oldResponse, | ||
newResponse, | ||
request, | ||
event | ||
}) { | ||
{ | ||
assert_mjs.assert.isType(cacheName, 'string', { | ||
moduleName: 'workbox-broadcast-update', | ||
className: 'Plugin', | ||
funcName: 'cacheDidUpdate', | ||
paramName: 'cacheName' | ||
}); | ||
assert_mjs.assert.isInstance(newResponse, Response, { | ||
moduleName: 'workbox-broadcast-update', | ||
className: 'Plugin', | ||
funcName: 'cacheDidUpdate', | ||
paramName: 'newResponse' | ||
}); | ||
assert_mjs.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-broadcast-update', | ||
className: 'Plugin', | ||
funcName: 'cacheDidUpdate', | ||
paramName: 'request' | ||
}); | ||
} | ||
if (!oldResponse) { | ||
// Without a two responses there is nothing to compare. | ||
return; | ||
if (oldResponse) { | ||
this._broadcastUpdate.notifyIfUpdated({ | ||
cacheName, | ||
oldResponse, | ||
newResponse, | ||
event, | ||
url: request.url | ||
}); | ||
} | ||
}; | ||
this._broadcastUpdate = new BroadcastCacheUpdate(options); | ||
} | ||
this._broadcastUpdate.notifyIfUpdated({ | ||
cacheName, | ||
oldResponse, | ||
newResponse, | ||
event, | ||
url: request.url | ||
}); | ||
} | ||
} | ||
exports.BroadcastCacheUpdate = BroadcastCacheUpdate; | ||
exports.Plugin = Plugin; | ||
exports.broadcastUpdate = broadcastUpdate; | ||
exports.responsesAreSame = responsesAreSame; | ||
/* | ||
Copyright 2018 Google LLC | ||
return exports; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
exports.BroadcastCacheUpdate = BroadcastCacheUpdate; | ||
exports.Plugin = Plugin; | ||
exports.broadcastUpdate = broadcastUpdate; | ||
exports.responsesAreSame = responsesAreSame; | ||
return exports; | ||
}({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private)); | ||
@@ -1,2 +0,2 @@ | ||
this.workbox=this.workbox||{},this.workbox.broadcastUpdate=function(e,t){"use strict";try{self["workbox:broadcast-update:4.3.1"]&&_()}catch(e){}const s=(e,t,s)=>{return!s.some(s=>e.headers.has(s)&&t.headers.has(s))||s.every(s=>{const n=e.headers.has(s)===t.headers.has(s),a=e.headers.get(s)===t.headers.get(s);return n&&a})},n="workbox",a=1e4,i=["content-length","etag","last-modified"],o=async({channel:e,cacheName:t,url:s})=>{const n={type:"CACHE_UPDATED",meta:"workbox-broadcast-update",payload:{cacheName:t,updatedURL:s}};if(e)e.postMessage(n);else{const e=await clients.matchAll({type:"window"});for(const t of e)t.postMessage(n)}};class c{constructor({headersToCheck:e,channelName:t,deferNoticationTimeout:s}={}){this.t=e||i,this.s=t||n,this.i=s||a,this.o()}notifyIfUpdated({oldResponse:e,newResponse:t,url:n,cacheName:a,event:i}){if(!s(e,t,this.t)){const e=(async()=>{i&&i.request&&"navigate"===i.request.mode&&await this.h(i),await this.l({channel:this.u(),cacheName:a,url:n})})();if(i)try{i.waitUntil(e)}catch(e){}return e}}async l(e){await o(e)}u(){return"BroadcastChannel"in self&&!this.p&&(this.p=new BroadcastChannel(this.s)),this.p}h(e){if(!this.m.has(e)){const s=new t.Deferred;this.m.set(e,s);const n=setTimeout(()=>{s.resolve()},this.i);s.promise.then(()=>clearTimeout(n))}return this.m.get(e).promise}o(){this.m=new Map,self.addEventListener("message",e=>{if("WINDOW_READY"===e.data.type&&"workbox-window"===e.data.meta&&this.m.size>0){for(const e of this.m.values())e.resolve();this.m.clear()}})}}return e.BroadcastCacheUpdate=c,e.Plugin=class{constructor(e){this.l=new c(e)}cacheDidUpdate({cacheName:e,oldResponse:t,newResponse:s,request:n,event:a}){t&&this.l.notifyIfUpdated({cacheName:e,oldResponse:t,newResponse:s,event:a,url:n.url})}},e.broadcastUpdate=o,e.responsesAreSame=s,e}({},workbox.core._private); | ||
this.workbox=this.workbox||{},this.workbox.broadcastUpdate=function(e,t){"use strict";try{self["workbox:broadcast-update:5.0.0-alpha.2"]&&_()}catch(e){}const s=(e,t,s)=>{return!s.some(s=>e.headers.has(s)&&t.headers.has(s))||s.every(s=>{const n=e.headers.has(s)===t.headers.has(s),a=e.headers.get(s)===t.headers.get(s);return n&&a})},n="workbox",a=1e4,i=["content-length","etag","last-modified"],o=async({channel:e,cacheName:t,url:s})=>{const n={type:"CACHE_UPDATED",meta:"workbox-broadcast-update",payload:{cacheName:t,updatedURL:s}};if(e)e.postMessage(n);else{const e=await self.clients.matchAll({type:"window"});for(const t of e)t.postMessage(n)}};class c{constructor({headersToCheck:e,channelName:t,deferNoticationTimeout:s}={}){this.t=e||i,this.s=t||n,this.i=s||a,this.o()}notifyIfUpdated({oldResponse:e,newResponse:t,url:n,cacheName:a,event:i}){if(!s(e,t,this.t)){const e=(async()=>{i&&i.request&&"navigate"===i.request.mode&&await this.h(i),await this.l({channel:this.u(),cacheName:a,url:n})})();if(i)try{i.waitUntil(e)}catch(e){}return e}}async l(e){await o(e)}u(){return"BroadcastChannel"in self&&!this.m&&(this.m=new BroadcastChannel(this.s)),this.m}h(e){if(!this.p.has(e)){const s=new t.Deferred;this.p.set(e,s);const n=setTimeout(()=>{s.resolve()},this.i);s.promise.then(()=>clearTimeout(n))}return this.p.get(e).promise}o(){this.p=new Map,self.addEventListener("message",e=>{if("WINDOW_READY"===e.data.type&&"workbox-window"===e.data.meta&&this.p.size>0){for(const e of this.p.values())e.resolve();this.p.clear()}})}}return e.BroadcastCacheUpdate=c,e.Plugin=class{constructor(e){this.cacheDidUpdate=(async({cacheName:e,oldResponse:t,newResponse:s,request:n,event:a})=>{t&&this.l.notifyIfUpdated({cacheName:e,oldResponse:t,newResponse:s,event:a,url:n.url})}),this.l=new c(e)}},e.broadcastUpdate=o,e.responsesAreSame=s,e}({},workbox.core._private); | ||
this.workbox = this.workbox || {}; | ||
this.workbox.cacheableResponse = (function (exports, WorkboxError_mjs, assert_mjs, getFriendlyURL_mjs, logger_mjs) { | ||
'use strict'; | ||
this.workbox.cacheableResponse = (function (exports, assert_js, WorkboxError_js, getFriendlyURL_js, logger_js) { | ||
'use strict'; | ||
try { | ||
self['workbox:cacheable-response:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
// @ts-ignore | ||
try { | ||
self['workbox:cacheable-response:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* This class allows you to set up rules determining what | ||
* status codes and/or headers need to be present in order for a | ||
* [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) | ||
* to be considered cacheable. | ||
* | ||
* @memberof workbox.cacheableResponse | ||
*/ | ||
class CacheableResponse { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* To construct a new CacheableResponse instance you must provide at least | ||
* one of the `config` properties. | ||
* This class allows you to set up rules determining what | ||
* status codes and/or headers need to be present in order for a | ||
* [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response) | ||
* to be considered cacheable. | ||
* | ||
* If both `statuses` and `headers` are specified, then both conditions must | ||
* be met for the `Response` to be considered cacheable. | ||
* | ||
* @param {Object} config | ||
* @param {Array<number>} [config.statuses] One or more status codes that a | ||
* `Response` can have and be considered cacheable. | ||
* @param {Object<string,string>} [config.headers] A mapping of header names | ||
* and expected values that a `Response` can have and be considered cacheable. | ||
* If multiple headers are provided, only one needs to be present. | ||
* @memberof workbox.cacheableResponse | ||
*/ | ||
constructor(config = {}) { | ||
{ | ||
if (!(config.statuses || config.headers)) { | ||
throw new WorkboxError_mjs.WorkboxError('statuses-or-headers-required', { | ||
moduleName: 'workbox-cacheable-response', | ||
className: 'CacheableResponse', | ||
funcName: 'constructor' | ||
}); | ||
} | ||
if (config.statuses) { | ||
assert_mjs.assert.isArray(config.statuses, { | ||
moduleName: 'workbox-cacheable-response', | ||
className: 'CacheableResponse', | ||
funcName: 'constructor', | ||
paramName: 'config.statuses' | ||
}); | ||
class CacheableResponse { | ||
/** | ||
* To construct a new CacheableResponse instance you must provide at least | ||
* one of the `config` properties. | ||
* | ||
* If both `statuses` and `headers` are specified, then both conditions must | ||
* be met for the `Response` to be considered cacheable. | ||
* | ||
* @param {Object} config | ||
* @param {Array<number>} [config.statuses] One or more status codes that a | ||
* `Response` can have and be considered cacheable. | ||
* @param {Object<string,string>} [config.headers] A mapping of header names | ||
* and expected values that a `Response` can have and be considered cacheable. | ||
* If multiple headers are provided, only one needs to be present. | ||
*/ | ||
constructor(config = {}) { | ||
{ | ||
if (!(config.statuses || config.headers)) { | ||
throw new WorkboxError_js.WorkboxError('statuses-or-headers-required', { | ||
moduleName: 'workbox-cacheable-response', | ||
className: 'CacheableResponse', | ||
funcName: 'constructor' | ||
}); | ||
} | ||
if (config.statuses) { | ||
assert_js.assert.isArray(config.statuses, { | ||
moduleName: 'workbox-cacheable-response', | ||
className: 'CacheableResponse', | ||
funcName: 'constructor', | ||
paramName: 'config.statuses' | ||
}); | ||
} | ||
if (config.headers) { | ||
assert_js.assert.isType(config.headers, 'object', { | ||
moduleName: 'workbox-cacheable-response', | ||
className: 'CacheableResponse', | ||
funcName: 'constructor', | ||
paramName: 'config.headers' | ||
}); | ||
} | ||
} | ||
if (config.headers) { | ||
assert_mjs.assert.isType(config.headers, 'object', { | ||
this._statuses = config.statuses; | ||
this._headers = config.headers; | ||
} | ||
/** | ||
* Checks a response to see whether it's cacheable or not, based on this | ||
* object's configuration. | ||
* | ||
* @param {Response} response The response whose cacheability is being | ||
* checked. | ||
* @return {boolean} `true` if the `Response` is cacheable, and `false` | ||
* otherwise. | ||
*/ | ||
isResponseCacheable(response) { | ||
{ | ||
assert_js.assert.isInstance(response, Response, { | ||
moduleName: 'workbox-cacheable-response', | ||
className: 'CacheableResponse', | ||
funcName: 'constructor', | ||
paramName: 'config.headers' | ||
funcName: 'isResponseCacheable', | ||
paramName: 'response' | ||
}); | ||
} | ||
} | ||
this._statuses = config.statuses; | ||
this._headers = config.headers; | ||
} | ||
/** | ||
* Checks a response to see whether it's cacheable or not, based on this | ||
* object's configuration. | ||
* | ||
* @param {Response} response The response whose cacheability is being | ||
* checked. | ||
* @return {boolean} `true` if the `Response` is cacheable, and `false` | ||
* otherwise. | ||
*/ | ||
let cacheable = true; | ||
if (this._statuses) { | ||
cacheable = this._statuses.includes(response.status); | ||
} | ||
isResponseCacheable(response) { | ||
{ | ||
assert_mjs.assert.isInstance(response, Response, { | ||
moduleName: 'workbox-cacheable-response', | ||
className: 'CacheableResponse', | ||
funcName: 'isResponseCacheable', | ||
paramName: 'response' | ||
}); | ||
} | ||
if (this._headers && cacheable) { | ||
cacheable = Object.keys(this._headers).some(headerName => { | ||
return response.headers.get(headerName) === this._headers[headerName]; | ||
}); | ||
} | ||
let cacheable = true; | ||
{ | ||
if (!cacheable) { | ||
logger_js.logger.groupCollapsed(`The request for ` + `'${getFriendlyURL_js.getFriendlyURL(response.url)}' returned a response that does ` + `not meet the criteria for being cached.`); | ||
logger_js.logger.groupCollapsed(`View cacheability criteria here.`); | ||
logger_js.logger.log(`Cacheable statuses: ` + JSON.stringify(this._statuses)); | ||
logger_js.logger.log(`Cacheable headers: ` + JSON.stringify(this._headers, null, 2)); | ||
logger_js.logger.groupEnd(); | ||
const logFriendlyHeaders = {}; | ||
response.headers.forEach((value, key) => { | ||
logFriendlyHeaders[key] = value; | ||
}); | ||
logger_js.logger.groupCollapsed(`View response status and headers here.`); | ||
logger_js.logger.log(`Response status: ` + response.status); | ||
logger_js.logger.log(`Response headers: ` + JSON.stringify(logFriendlyHeaders, null, 2)); | ||
logger_js.logger.groupEnd(); | ||
logger_js.logger.groupCollapsed(`View full response details here.`); | ||
logger_js.logger.log(response.headers); | ||
logger_js.logger.log(response); | ||
logger_js.logger.groupEnd(); | ||
logger_js.logger.groupEnd(); | ||
} | ||
} | ||
if (this._statuses) { | ||
cacheable = this._statuses.includes(response.status); | ||
return cacheable; | ||
} | ||
if (this._headers && cacheable) { | ||
cacheable = Object.keys(this._headers).some(headerName => { | ||
return response.headers.get(headerName) === this._headers[headerName]; | ||
}); | ||
} | ||
{ | ||
if (!cacheable) { | ||
logger_mjs.logger.groupCollapsed(`The request for ` + `'${getFriendlyURL_mjs.getFriendlyURL(response.url)}' returned a response that does ` + `not meet the criteria for being cached.`); | ||
logger_mjs.logger.groupCollapsed(`View cacheability criteria here.`); | ||
logger_mjs.logger.log(`Cacheable statuses: ` + JSON.stringify(this._statuses)); | ||
logger_mjs.logger.log(`Cacheable headers: ` + JSON.stringify(this._headers, null, 2)); | ||
logger_mjs.logger.groupEnd(); | ||
const logFriendlyHeaders = {}; | ||
response.headers.forEach((value, key) => { | ||
logFriendlyHeaders[key] = value; | ||
}); | ||
logger_mjs.logger.groupCollapsed(`View response status and headers here.`); | ||
logger_mjs.logger.log(`Response status: ` + response.status); | ||
logger_mjs.logger.log(`Response headers: ` + JSON.stringify(logFriendlyHeaders, null, 2)); | ||
logger_mjs.logger.groupEnd(); | ||
logger_mjs.logger.groupCollapsed(`View full response details here.`); | ||
logger_mjs.logger.log(response.headers); | ||
logger_mjs.logger.log(response); | ||
logger_mjs.logger.groupEnd(); | ||
logger_mjs.logger.groupEnd(); | ||
} | ||
} | ||
return cacheable; | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* A class implementing the `cacheWillUpdate` lifecycle callback. This makes it | ||
* easier to add in cacheability checks to requests made via Workbox's built-in | ||
* strategies. | ||
* | ||
* @memberof workbox.cacheableResponse | ||
*/ | ||
class Plugin { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* To construct a new cacheable response Plugin instance you must provide at | ||
* least one of the `config` properties. | ||
* A class implementing the `cacheWillUpdate` lifecycle callback. This makes it | ||
* easier to add in cacheability checks to requests made via Workbox's built-in | ||
* strategies. | ||
* | ||
* If both `statuses` and `headers` are specified, then both conditions must | ||
* be met for the `Response` to be considered cacheable. | ||
* | ||
* @param {Object} config | ||
* @param {Array<number>} [config.statuses] One or more status codes that a | ||
* `Response` can have and be considered cacheable. | ||
* @param {Object<string,string>} [config.headers] A mapping of header names | ||
* and expected values that a `Response` can have and be considered cacheable. | ||
* If multiple headers are provided, only one needs to be present. | ||
* @memberof workbox.cacheableResponse | ||
*/ | ||
constructor(config) { | ||
this._cacheableResponse = new CacheableResponse(config); | ||
} | ||
/** | ||
* @param {Object} options | ||
* @param {Response} options.response | ||
* @return {boolean} | ||
* @private | ||
*/ | ||
class Plugin { | ||
/** | ||
* To construct a new cacheable response Plugin instance you must provide at | ||
* least one of the `config` properties. | ||
* | ||
* If both `statuses` and `headers` are specified, then both conditions must | ||
* be met for the `Response` to be considered cacheable. | ||
* | ||
* @param {Object} config | ||
* @param {Array<number>} [config.statuses] One or more status codes that a | ||
* `Response` can have and be considered cacheable. | ||
* @param {Object<string,string>} [config.headers] A mapping of header names | ||
* and expected values that a `Response` can have and be considered cacheable. | ||
* If multiple headers are provided, only one needs to be present. | ||
*/ | ||
constructor(config) { | ||
/** | ||
* @param {Object} options | ||
* @param {Response} options.response | ||
* @return {Response|null} | ||
* @private | ||
*/ | ||
this.cacheWillUpdate = async ({ | ||
response | ||
}) => { | ||
if (this._cacheableResponse.isResponseCacheable(response)) { | ||
return response; | ||
} | ||
cacheWillUpdate({ | ||
response | ||
}) { | ||
if (this._cacheableResponse.isResponseCacheable(response)) { | ||
return response; | ||
return null; | ||
}; | ||
this._cacheableResponse = new CacheableResponse(config); | ||
} | ||
return null; | ||
} | ||
} | ||
exports.CacheableResponse = CacheableResponse; | ||
exports.Plugin = Plugin; | ||
/* | ||
Copyright 2018 Google LLC | ||
return exports; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
exports.CacheableResponse = CacheableResponse; | ||
exports.Plugin = Plugin; | ||
return exports; | ||
}({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private)); | ||
@@ -1,2 +0,2 @@ | ||
this.workbox=this.workbox||{},this.workbox.cacheableResponse=function(t){"use strict";try{self["workbox:cacheable-response:4.3.1"]&&_()}catch(t){}class s{constructor(t={}){this.t=t.statuses,this.s=t.headers}isResponseCacheable(t){let s=!0;return this.t&&(s=this.t.includes(t.status)),this.s&&s&&(s=Object.keys(this.s).some(s=>t.headers.get(s)===this.s[s])),s}}return t.CacheableResponse=s,t.Plugin=class{constructor(t){this.i=new s(t)}cacheWillUpdate({response:t}){return this.i.isResponseCacheable(t)?t:null}},t}({}); | ||
this.workbox=this.workbox||{},this.workbox.cacheableResponse=function(s){"use strict";try{self["workbox:cacheable-response:5.0.0-alpha.2"]&&_()}catch(s){}class t{constructor(s={}){this.s=s.statuses,this.t=s.headers}isResponseCacheable(s){let t=!0;return this.s&&(t=this.s.includes(s.status)),this.t&&t&&(t=Object.keys(this.t).some(t=>s.headers.get(t)===this.t[t])),t}}return s.CacheableResponse=t,s.Plugin=class{constructor(s){this.cacheWillUpdate=(async({response:s})=>this.i.isResponseCacheable(s)?s:null),this.i=new t(s)}},s}({}); | ||
this.workbox = this.workbox || {}; | ||
this.workbox.core = (function (exports) { | ||
'use strict'; | ||
'use strict'; | ||
try { | ||
self['workbox:core:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
// @ts-ignore | ||
try { | ||
self['workbox:core:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const logger = (() => { | ||
let inGroup = false; | ||
const methodToColorMap = { | ||
debug: `#7f8c8d`, | ||
// Gray | ||
log: `#2ecc71`, | ||
// Green | ||
warn: `#f39c12`, | ||
// Yellow | ||
error: `#c0392b`, | ||
// Red | ||
groupCollapsed: `#3498db`, | ||
// Blue | ||
groupEnd: null // No colored prefix on groupEnd | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const logger = (() => { | ||
let inGroup = false; | ||
const methodToColorMap = { | ||
debug: `#7f8c8d`, | ||
log: `#2ecc71`, | ||
warn: `#f39c12`, | ||
error: `#c0392b`, | ||
groupCollapsed: `#3498db`, | ||
groupEnd: null | ||
}; | ||
}; | ||
const print = function (method, args) { | ||
if (method === 'groupCollapsed') { | ||
// Safari doesn't print all console.groupCollapsed() arguments: | ||
// https://bugs.webkit.org/show_bug.cgi?id=182754 | ||
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { | ||
console[method](...args); | ||
return; | ||
} | ||
} | ||
const print = function (method, args) { | ||
if (method === 'groupCollapsed') { | ||
// Safari doesn't print all console.groupCollapsed() arguments: | ||
// https://bugs.webkit.org/show_bug.cgi?id=182754 | ||
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { | ||
console[method](...args); | ||
return; | ||
const styles = [`background: ${methodToColorMap[method]}`, `border-radius: 0.5em`, `color: white`, `font-weight: bold`, `padding: 2px 0.5em`]; // When in a group, the workbox prefix is not displayed. | ||
const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')]; | ||
console[method](...logPrefix, ...args); | ||
if (method === 'groupCollapsed') { | ||
inGroup = true; | ||
} | ||
} | ||
const styles = [`background: ${methodToColorMap[method]}`, `border-radius: 0.5em`, `color: white`, `font-weight: bold`, `padding: 2px 0.5em`]; // When in a group, the workbox prefix is not displayed. | ||
if (method === 'groupEnd') { | ||
inGroup = false; | ||
} | ||
}; | ||
const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')]; | ||
console[method](...logPrefix, ...args); | ||
const api = {}; | ||
const loggerMethods = Object.keys(methodToColorMap); | ||
if (method === 'groupCollapsed') { | ||
inGroup = true; | ||
} | ||
for (const key of loggerMethods) { | ||
const method = key; | ||
if (method === 'groupEnd') { | ||
inGroup = false; | ||
api[method] = (...args) => { | ||
print(method, args); | ||
}; | ||
} | ||
}; | ||
const api = {}; | ||
return api; | ||
})(); | ||
for (const method of Object.keys(methodToColorMap)) { | ||
api[method] = (...args) => { | ||
print(method, args); | ||
}; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
return api; | ||
})(); | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const messages = { | ||
'invalid-value': ({ | ||
paramName, | ||
validValueDescription, | ||
value | ||
}) => { | ||
if (!paramName || !validValueDescription) { | ||
throw new Error(`Unexpected input to 'invalid-value' error.`); | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
return `The '${paramName}' parameter was given a value with an ` + `unexpected value. ${validValueDescription} Received a value of ` + `${JSON.stringify(value)}.`; | ||
}, | ||
'not-in-sw': ({ | ||
moduleName | ||
}) => { | ||
if (!moduleName) { | ||
throw new Error(`Unexpected input to 'not-in-sw' error.`); | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const messages = { | ||
'invalid-value': ({ | ||
paramName, | ||
validValueDescription, | ||
value | ||
}) => { | ||
if (!paramName || !validValueDescription) { | ||
throw new Error(`Unexpected input to 'invalid-value' error.`); | ||
} | ||
return `The '${moduleName}' must be used in a service worker.`; | ||
}, | ||
'not-an-array': ({ | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName | ||
}) => { | ||
if (!moduleName || !className || !funcName || !paramName) { | ||
throw new Error(`Unexpected input to 'not-an-array' error.`); | ||
} | ||
return `The '${paramName}' parameter was given a value with an ` + `unexpected value. ${validValueDescription} Received a value of ` + `${JSON.stringify(value)}.`; | ||
}, | ||
'not-in-sw': ({ | ||
moduleName | ||
}) => { | ||
if (!moduleName) { | ||
throw new Error(`Unexpected input to 'not-in-sw' error.`); | ||
} | ||
return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className}.${funcName}()' must be an array.`; | ||
}, | ||
'incorrect-type': ({ | ||
expectedType, | ||
paramName, | ||
moduleName, | ||
className, | ||
funcName | ||
}) => { | ||
if (!expectedType || !paramName || !moduleName || !funcName) { | ||
throw new Error(`Unexpected input to 'incorrect-type' error.`); | ||
} | ||
return `The '${moduleName}' must be used in a service worker.`; | ||
}, | ||
'not-an-array': ({ | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName | ||
}) => { | ||
if (!moduleName || !className || !funcName || !paramName) { | ||
throw new Error(`Unexpected input to 'not-an-array' error.`); | ||
} | ||
return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className ? className + '.' : ''}` + `${funcName}()' must be of type ${expectedType}.`; | ||
}, | ||
'incorrect-class': ({ | ||
expectedClass, | ||
paramName, | ||
moduleName, | ||
className, | ||
funcName, | ||
isReturnValueProblem | ||
}) => { | ||
if (!expectedClass || !moduleName || !funcName) { | ||
throw new Error(`Unexpected input to 'incorrect-class' error.`); | ||
} | ||
return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className}.${funcName}()' must be an array.`; | ||
}, | ||
'incorrect-type': ({ | ||
expectedType, | ||
paramName, | ||
moduleName, | ||
className, | ||
funcName | ||
}) => { | ||
if (!expectedType || !paramName || !moduleName || !funcName) { | ||
throw new Error(`Unexpected input to 'incorrect-type' error.`); | ||
} | ||
if (isReturnValueProblem) { | ||
return `The return value from ` + `'${moduleName}.${className ? className + '.' : ''}${funcName}()' ` + `must be an instance of class ${expectedClass.name}.`; | ||
} | ||
return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className ? className + '.' : ''}` + `${funcName}()' must be of type ${expectedType}.`; | ||
}, | ||
'incorrect-class': ({ | ||
expectedClass, | ||
paramName, | ||
moduleName, | ||
className, | ||
funcName, | ||
isReturnValueProblem | ||
}) => { | ||
if (!expectedClass || !moduleName || !funcName) { | ||
throw new Error(`Unexpected input to 'incorrect-class' error.`); | ||
} | ||
return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className ? className + '.' : ''}${funcName}()' ` + `must be an instance of class ${expectedClass.name}.`; | ||
}, | ||
'missing-a-method': ({ | ||
expectedMethod, | ||
paramName, | ||
moduleName, | ||
className, | ||
funcName | ||
}) => { | ||
if (!expectedMethod || !paramName || !moduleName || !className || !funcName) { | ||
throw new Error(`Unexpected input to 'missing-a-method' error.`); | ||
} | ||
if (isReturnValueProblem) { | ||
return `The return value from ` + `'${moduleName}.${className ? className + '.' : ''}${funcName}()' ` + `must be an instance of class ${expectedClass.name}.`; | ||
} | ||
return `${moduleName}.${className}.${funcName}() expected the ` + `'${paramName}' parameter to expose a '${expectedMethod}' method.`; | ||
}, | ||
'add-to-cache-list-unexpected-type': ({ | ||
entry | ||
}) => { | ||
return `An unexpected entry was passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' The entry ` + `'${JSON.stringify(entry)}' isn't supported. You must supply an array of ` + `strings with one or more characters, objects with a url property or ` + `Request objects.`; | ||
}, | ||
'add-to-cache-list-conflicting-entries': ({ | ||
firstEntry, | ||
secondEntry | ||
}) => { | ||
if (!firstEntry || !secondEntry) { | ||
throw new Error(`Unexpected input to ` + `'add-to-cache-list-duplicate-entries' error.`); | ||
} | ||
return `The parameter '${paramName}' passed into ` + `'${moduleName}.${className ? className + '.' : ''}${funcName}()' ` + `must be an instance of class ${expectedClass.name}.`; | ||
}, | ||
'missing-a-method': ({ | ||
expectedMethod, | ||
paramName, | ||
moduleName, | ||
className, | ||
funcName | ||
}) => { | ||
if (!expectedMethod || !paramName || !moduleName || !className || !funcName) { | ||
throw new Error(`Unexpected input to 'missing-a-method' error.`); | ||
} | ||
return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${firstEntry._entryId} but different revision details. Workbox is ` + `is unable to cache and version the asset correctly. Please remove one ` + `of the entries.`; | ||
}, | ||
'plugin-error-request-will-fetch': ({ | ||
thrownError | ||
}) => { | ||
if (!thrownError) { | ||
throw new Error(`Unexpected input to ` + `'plugin-error-request-will-fetch', error.`); | ||
} | ||
return `${moduleName}.${className}.${funcName}() expected the ` + `'${paramName}' parameter to expose a '${expectedMethod}' method.`; | ||
}, | ||
'add-to-cache-list-unexpected-type': ({ | ||
entry | ||
}) => { | ||
return `An unexpected entry was passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' The entry ` + `'${JSON.stringify(entry)}' isn't supported. You must supply an array of ` + `strings with one or more characters, objects with a url property or ` + `Request objects.`; | ||
}, | ||
'add-to-cache-list-conflicting-entries': ({ | ||
firstEntry, | ||
secondEntry | ||
}) => { | ||
if (!firstEntry || !secondEntry) { | ||
throw new Error(`Unexpected input to ` + `'add-to-cache-list-duplicate-entries' error.`); | ||
} | ||
return `An error was thrown by a plugins 'requestWillFetch()' method. ` + `The thrown error message was: '${thrownError.message}'.`; | ||
}, | ||
'invalid-cache-name': ({ | ||
cacheNameId, | ||
value | ||
}) => { | ||
if (!cacheNameId) { | ||
throw new Error(`Expected a 'cacheNameId' for error 'invalid-cache-name'`); | ||
} | ||
return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${firstEntry._entryId} but different revision details. Workbox is ` + `is unable to cache and version the asset correctly. Please remove one ` + `of the entries.`; | ||
}, | ||
'plugin-error-request-will-fetch': ({ | ||
thrownError | ||
}) => { | ||
if (!thrownError) { | ||
throw new Error(`Unexpected input to ` + `'plugin-error-request-will-fetch', error.`); | ||
} | ||
return `You must provide a name containing at least one character for ` + `setCacheDetails({${cacheNameId}: '...'}). Received a value of ` + `'${JSON.stringify(value)}'`; | ||
}, | ||
'unregister-route-but-not-found-with-method': ({ | ||
method | ||
}) => { | ||
if (!method) { | ||
throw new Error(`Unexpected input to ` + `'unregister-route-but-not-found-with-method' error.`); | ||
} | ||
return `An error was thrown by a plugins 'requestWillFetch()' method. ` + `The thrown error message was: '${thrownError.message}'.`; | ||
}, | ||
'invalid-cache-name': ({ | ||
cacheNameId, | ||
value | ||
}) => { | ||
if (!cacheNameId) { | ||
throw new Error(`Expected a 'cacheNameId' for error 'invalid-cache-name'`); | ||
} | ||
return `The route you're trying to unregister was not previously ` + `registered for the method type '${method}'.`; | ||
}, | ||
'unregister-route-route-not-registered': () => { | ||
return `The route you're trying to unregister was not previously ` + `registered.`; | ||
}, | ||
'queue-replay-failed': ({ | ||
name | ||
}) => { | ||
return `Replaying the background sync queue '${name}' failed.`; | ||
}, | ||
'duplicate-queue-name': ({ | ||
name | ||
}) => { | ||
return `The Queue name '${name}' is already being used. ` + `All instances of backgroundSync.Queue must be given unique names.`; | ||
}, | ||
'expired-test-without-max-age': ({ | ||
methodName, | ||
paramName | ||
}) => { | ||
return `The '${methodName}()' method can only be used when the ` + `'${paramName}' is used in the constructor.`; | ||
}, | ||
'unsupported-route-type': ({ | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName | ||
}) => { | ||
return `The supplied '${paramName}' parameter was an unsupported type. ` + `Please check the docs for ${moduleName}.${className}.${funcName} for ` + `valid input types.`; | ||
}, | ||
'not-array-of-class': ({ | ||
value, | ||
expectedClass, | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName | ||
}) => { | ||
return `The supplied '${paramName}' parameter must be an array of ` + `'${expectedClass}' objects. Received '${JSON.stringify(value)},'. ` + `Please check the call to ${moduleName}.${className}.${funcName}() ` + `to fix the issue.`; | ||
}, | ||
'max-entries-or-age-required': ({ | ||
moduleName, | ||
className, | ||
funcName | ||
}) => { | ||
return `You must define either config.maxEntries or config.maxAgeSeconds` + `in ${moduleName}.${className}.${funcName}`; | ||
}, | ||
'statuses-or-headers-required': ({ | ||
moduleName, | ||
className, | ||
funcName | ||
}) => { | ||
return `You must define either config.statuses or config.headers` + `in ${moduleName}.${className}.${funcName}`; | ||
}, | ||
'invalid-string': ({ | ||
moduleName, | ||
funcName, | ||
paramName | ||
}) => { | ||
if (!paramName || !moduleName || !funcName) { | ||
throw new Error(`Unexpected input to 'invalid-string' error.`); | ||
} | ||
return `You must provide a name containing at least one character for ` + `setCacheDeatils({${cacheNameId}: '...'}). Received a value of ` + `'${JSON.stringify(value)}'`; | ||
}, | ||
'unregister-route-but-not-found-with-method': ({ | ||
method | ||
}) => { | ||
if (!method) { | ||
throw new Error(`Unexpected input to ` + `'unregister-route-but-not-found-with-method' error.`); | ||
} | ||
return `When using strings, the '${paramName}' parameter must start with ` + `'http' (for cross-origin matches) or '/' (for same-origin matches). ` + `Please see the docs for ${moduleName}.${funcName}() for ` + `more info.`; | ||
}, | ||
'channel-name-required': () => { | ||
return `You must provide a channelName to construct a ` + `BroadcastCacheUpdate instance.`; | ||
}, | ||
'invalid-responses-are-same-args': () => { | ||
return `The arguments passed into responsesAreSame() appear to be ` + `invalid. Please ensure valid Responses are used.`; | ||
}, | ||
'expire-custom-caches-only': () => { | ||
return `You must provide a 'cacheName' property when using the ` + `expiration plugin with a runtime caching strategy.`; | ||
}, | ||
'unit-must-be-bytes': ({ | ||
normalizedRangeHeader | ||
}) => { | ||
if (!normalizedRangeHeader) { | ||
throw new Error(`Unexpected input to 'unit-must-be-bytes' error.`); | ||
} | ||
return `The route you're trying to unregister was not previously ` + `registered for the method type '${method}'.`; | ||
}, | ||
'unregister-route-route-not-registered': () => { | ||
return `The route you're trying to unregister was not previously ` + `registered.`; | ||
}, | ||
'queue-replay-failed': ({ | ||
name | ||
}) => { | ||
return `Replaying the background sync queue '${name}' failed.`; | ||
}, | ||
'duplicate-queue-name': ({ | ||
name | ||
}) => { | ||
return `The Queue name '${name}' is already being used. ` + `All instances of backgroundSync.Queue must be given unique names.`; | ||
}, | ||
'expired-test-without-max-age': ({ | ||
methodName, | ||
paramName | ||
}) => { | ||
return `The '${methodName}()' method can only be used when the ` + `'${paramName}' is used in the constructor.`; | ||
}, | ||
'unsupported-route-type': ({ | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName | ||
}) => { | ||
return `The supplied '${paramName}' parameter was an unsupported type. ` + `Please check the docs for ${moduleName}.${className}.${funcName} for ` + `valid input types.`; | ||
}, | ||
'not-array-of-class': ({ | ||
value, | ||
expectedClass, | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName | ||
}) => { | ||
return `The supplied '${paramName}' parameter must be an array of ` + `'${expectedClass}' objects. Received '${JSON.stringify(value)},'. ` + `Please check the call to ${moduleName}.${className}.${funcName}() ` + `to fix the issue.`; | ||
}, | ||
'max-entries-or-age-required': ({ | ||
moduleName, | ||
className, | ||
funcName | ||
}) => { | ||
return `You must define either config.maxEntries or config.maxAgeSeconds` + `in ${moduleName}.${className}.${funcName}`; | ||
}, | ||
'statuses-or-headers-required': ({ | ||
moduleName, | ||
className, | ||
funcName | ||
}) => { | ||
return `You must define either config.statuses or config.headers` + `in ${moduleName}.${className}.${funcName}`; | ||
}, | ||
'invalid-string': ({ | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName | ||
}) => { | ||
if (!paramName || !moduleName || !funcName) { | ||
throw new Error(`Unexpected input to 'invalid-string' error.`); | ||
} | ||
return `The 'unit' portion of the Range header must be set to 'bytes'. ` + `The Range header provided was "${normalizedRangeHeader}"`; | ||
}, | ||
'single-range-only': ({ | ||
normalizedRangeHeader | ||
}) => { | ||
if (!normalizedRangeHeader) { | ||
throw new Error(`Unexpected input to 'single-range-only' error.`); | ||
} | ||
return `When using strings, the '${paramName}' parameter must start with ` + `'http' (for cross-origin matches) or '/' (for same-origin matches). ` + `Please see the docs for ${moduleName}.${funcName}() for ` + `more info.`; | ||
}, | ||
'channel-name-required': () => { | ||
return `You must provide a channelName to construct a ` + `BroadcastCacheUpdate instance.`; | ||
}, | ||
'invalid-responses-are-same-args': () => { | ||
return `The arguments passed into responsesAreSame() appear to be ` + `invalid. Please ensure valid Responses are used.`; | ||
}, | ||
'expire-custom-caches-only': () => { | ||
return `You must provide a 'cacheName' property when using the ` + `expiration plugin with a runtime caching strategy.`; | ||
}, | ||
'unit-must-be-bytes': ({ | ||
normalizedRangeHeader | ||
}) => { | ||
if (!normalizedRangeHeader) { | ||
throw new Error(`Unexpected input to 'unit-must-be-bytes' error.`); | ||
} | ||
return `Multiple ranges are not supported. Please use a single start ` + `value, and optional end value. The Range header provided was ` + `"${normalizedRangeHeader}"`; | ||
}, | ||
'invalid-range-values': ({ | ||
normalizedRangeHeader | ||
}) => { | ||
if (!normalizedRangeHeader) { | ||
throw new Error(`Unexpected input to 'invalid-range-values' error.`); | ||
} | ||
return `The 'unit' portion of the Range header must be set to 'bytes'. ` + `The Range header provided was "${normalizedRangeHeader}"`; | ||
}, | ||
'single-range-only': ({ | ||
normalizedRangeHeader | ||
}) => { | ||
if (!normalizedRangeHeader) { | ||
throw new Error(`Unexpected input to 'single-range-only' error.`); | ||
} | ||
return `The Range header is missing both start and end values. At least ` + `one of those values is needed. The Range header provided was ` + `"${normalizedRangeHeader}"`; | ||
}, | ||
'no-range-header': () => { | ||
return `No Range header was found in the Request provided.`; | ||
}, | ||
'range-not-satisfiable': ({ | ||
size, | ||
start, | ||
end | ||
}) => { | ||
return `The start (${start}) and end (${end}) values in the Range are ` + `not satisfiable by the cached response, which is ${size} bytes.`; | ||
}, | ||
'attempt-to-cache-non-get-request': ({ | ||
url, | ||
method | ||
}) => { | ||
return `Unable to cache '${url}' because it is a '${method}' request and ` + `only 'GET' requests can be cached.`; | ||
}, | ||
'cache-put-with-no-response': ({ | ||
url | ||
}) => { | ||
return `There was an attempt to cache '${url}' but the response was not ` + `defined.`; | ||
}, | ||
'no-response': ({ | ||
url, | ||
error | ||
}) => { | ||
let message = `The strategy could not generate a response for '${url}'.`; | ||
return `Multiple ranges are not supported. Please use a single start ` + `value, and optional end value. The Range header provided was ` + `"${normalizedRangeHeader}"`; | ||
}, | ||
'invalid-range-values': ({ | ||
normalizedRangeHeader | ||
}) => { | ||
if (!normalizedRangeHeader) { | ||
throw new Error(`Unexpected input to 'invalid-range-values' error.`); | ||
} | ||
if (error) { | ||
message += ` The underlying error is ${error}.`; | ||
} | ||
return `The Range header is missing both start and end values. At least ` + `one of those values is needed. The Range header provided was ` + `"${normalizedRangeHeader}"`; | ||
}, | ||
'no-range-header': () => { | ||
return `No Range header was found in the Request provided.`; | ||
}, | ||
'range-not-satisfiable': ({ | ||
size, | ||
start, | ||
end | ||
}) => { | ||
return `The start (${start}) and end (${end}) values in the Range are ` + `not satisfiable by the cached response, which is ${size} bytes.`; | ||
}, | ||
'attempt-to-cache-non-get-request': ({ | ||
url, | ||
method | ||
}) => { | ||
return `Unable to cache '${url}' because it is a '${method}' request and ` + `only 'GET' requests can be cached.`; | ||
}, | ||
'cache-put-with-no-response': ({ | ||
url | ||
}) => { | ||
return `There was an attempt to cache '${url}' but the response was not ` + `defined.`; | ||
}, | ||
'no-response': ({ | ||
url, | ||
error | ||
}) => { | ||
let message = `The strategy could not generate a response for '${url}'.`; | ||
if (error) { | ||
message += ` The underlying error is ${error}.`; | ||
return message; | ||
}, | ||
'bad-precaching-response': ({ | ||
url, | ||
status | ||
}) => { | ||
return `The precaching request for '${url}' failed with an HTTP ` + `status of ${status}.`; | ||
}, | ||
'non-precached-url': ({ | ||
url | ||
}) => { | ||
return `createHandlerForURL('${url}') was called, but that URL is not ` + `precached. Please pass in a URL that is precached instead.`; | ||
}, | ||
'add-to-cache-list-conflicting-integrities': ({ | ||
url | ||
}) => { | ||
return `Two of the entries passed to ` + `'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + `${url} with different integrity values. Please remove one of them.`; | ||
} | ||
}; | ||
return message; | ||
}, | ||
'bad-precaching-response': ({ | ||
url, | ||
status | ||
}) => { | ||
return `The precaching request for '${url}' failed with an HTTP ` + `status of ${status}.`; | ||
} | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const generatorFunction = (code, details = {}) => { | ||
const message = messages[code]; | ||
const generatorFunction = (code, ...args) => { | ||
const message = messages[code]; | ||
if (!message) { | ||
throw new Error(`Unable to find message for code '${code}'.`); | ||
} | ||
if (!message) { | ||
throw new Error(`Unable to find message for code '${code}'.`); | ||
} | ||
return message(details); | ||
}; | ||
return message(...args); | ||
}; | ||
const messageGenerator = generatorFunction; | ||
const messageGenerator = generatorFunction; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Workbox errors should be thrown with this class. | ||
* This allows use to ensure the type easily in tests, | ||
* helps developers identify errors from workbox | ||
* easily and allows use to optimise error | ||
* messages correctly. | ||
* | ||
* @private | ||
*/ | ||
class WorkboxError extends Error { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Workbox errors should be thrown with this class. | ||
* This allows use to ensure the type easily in tests, | ||
* helps developers identify errors from workbox | ||
* easily and allows use to optimise error | ||
* messages correctly. | ||
* | ||
* @param {string} errorCode The error code that | ||
* identifies this particular error. | ||
* @param {Object=} details Any relevant arguments | ||
* that will help developers identify issues should | ||
* be added as a key on the context object. | ||
* @private | ||
*/ | ||
constructor(errorCode, details) { | ||
let message = messageGenerator(errorCode, details); | ||
super(message); | ||
this.name = errorCode; | ||
this.details = details; | ||
} | ||
} | ||
class WorkboxError extends Error { | ||
/** | ||
* | ||
* @param {string} errorCode The error code that | ||
* identifies this particular error. | ||
* @param {Object=} details Any relevant arguments | ||
* that will help developers identify issues should | ||
* be added as a key on the context object. | ||
*/ | ||
constructor(errorCode, details) { | ||
let message = messageGenerator(errorCode, details); | ||
super(message); | ||
this.name = errorCode; | ||
this.details = details; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/* | ||
* This method returns true if the current context is a service worker. | ||
*/ | ||
/* | ||
Copyright 2018 Google LLC | ||
const isSWEnv = moduleName => { | ||
if (!('ServiceWorkerGlobalScope' in self)) { | ||
throw new WorkboxError('not-in-sw', { | ||
moduleName | ||
}); | ||
} | ||
}; | ||
/* | ||
* This method throws if the supplied value is not an array. | ||
* The destructed values are required to produce a meaningful error for users. | ||
* The destructed and restructured object is so it's clear what is | ||
* needed. | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/* | ||
* This method returns true if the current context is a service worker. | ||
*/ | ||
const isSWEnv = moduleName => { | ||
if (!('ServiceWorkerGlobalScope' in self)) { | ||
throw new WorkboxError('not-in-sw', { | ||
moduleName | ||
}); | ||
} | ||
}; | ||
/* | ||
* This method throws if the supplied value is not an array. | ||
* The destructed values are required to produce a meaningful error for users. | ||
* The destructed and restructured object is so it's clear what is | ||
* needed. | ||
*/ | ||
const isArray = (value, { | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName | ||
}) => { | ||
if (!Array.isArray(value)) { | ||
throw new WorkboxError('not-an-array', { | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName | ||
}); | ||
} | ||
}; | ||
const hasMethod = (object, expectedMethod, { | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName | ||
}) => { | ||
const type = typeof object[expectedMethod]; | ||
const isArray = (value, details) => { | ||
if (!Array.isArray(value)) { | ||
throw new WorkboxError('not-an-array', details); | ||
} | ||
}; | ||
if (type !== 'function') { | ||
throw new WorkboxError('missing-a-method', { | ||
paramName, | ||
expectedMethod, | ||
moduleName, | ||
className, | ||
funcName | ||
}); | ||
} | ||
}; | ||
const hasMethod = (object, expectedMethod, details) => { | ||
const type = typeof object[expectedMethod]; | ||
const isType = (object, expectedType, { | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName | ||
}) => { | ||
if (typeof object !== expectedType) { | ||
throw new WorkboxError('incorrect-type', { | ||
paramName, | ||
expectedType, | ||
moduleName, | ||
className, | ||
funcName | ||
}); | ||
} | ||
}; | ||
if (type !== 'function') { | ||
details.expectedMethod = expectedMethod; | ||
throw new WorkboxError('missing-a-method', details); | ||
} | ||
}; | ||
const isInstance = (object, expectedClass, { | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName, | ||
isReturnValueProblem | ||
}) => { | ||
if (!(object instanceof expectedClass)) { | ||
throw new WorkboxError('incorrect-class', { | ||
paramName, | ||
expectedClass, | ||
moduleName, | ||
className, | ||
funcName, | ||
isReturnValueProblem | ||
}); | ||
} | ||
}; | ||
const isType = (object, expectedType, details) => { | ||
if (typeof object !== expectedType) { | ||
details.expectedType = expectedType; | ||
throw new WorkboxError('incorrect-type', details); | ||
} | ||
}; | ||
const isOneOf = (value, validValues, { | ||
paramName | ||
}) => { | ||
if (!validValues.includes(value)) { | ||
throw new WorkboxError('invalid-value', { | ||
paramName, | ||
value, | ||
validValueDescription: `Valid values are ${JSON.stringify(validValues)}.` | ||
}); | ||
} | ||
}; | ||
const isInstance = (object, expectedClass, details) => { | ||
if (!(object instanceof expectedClass)) { | ||
details.expectedClass = expectedClass; | ||
throw new WorkboxError('incorrect-class', details); | ||
} | ||
}; | ||
const isArrayOfClass = (value, expectedClass, { | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName | ||
}) => { | ||
const error = new WorkboxError('not-array-of-class', { | ||
value, | ||
expectedClass, | ||
moduleName, | ||
className, | ||
funcName, | ||
paramName | ||
}); | ||
const isOneOf = (value, validValues, details) => { | ||
if (!validValues.includes(value)) { | ||
details.validValueDescription = `Valid values are ${JSON.stringify(validValues)}.`; | ||
throw new WorkboxError('invalid-value', details); | ||
} | ||
}; | ||
if (!Array.isArray(value)) { | ||
throw error; | ||
} | ||
const isArrayOfClass = (value, expectedClass, details) => { | ||
const error = new WorkboxError('not-array-of-class', details); | ||
for (let item of value) { | ||
if (!(item instanceof expectedClass)) { | ||
if (!Array.isArray(value)) { | ||
throw error; | ||
} | ||
} | ||
}; | ||
const finalAssertExports = { | ||
hasMethod, | ||
isArray, | ||
isInstance, | ||
isOneOf, | ||
isSWEnv, | ||
isType, | ||
isArrayOfClass | ||
}; | ||
for (let item of value) { | ||
if (!(item instanceof expectedClass)) { | ||
throw error; | ||
} | ||
} | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
const finalAssertExports = { | ||
hasMethod, | ||
isArray, | ||
isInstance, | ||
isOneOf, | ||
isSWEnv, | ||
isType, | ||
isArrayOfClass | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/* | ||
Copyright 2018 Google LLC | ||
const quotaErrorCallbacks = new Set(); | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/* | ||
Copyright 2019 Google LLC | ||
const quotaErrorCallbacks = new Set(); | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Adds a function to the set of quotaErrorCallbacks that will be executed if | ||
* there's a quota error. | ||
* | ||
* @param {Function} callback | ||
* @memberof workbox.core | ||
*/ | ||
/* | ||
Copyright 2019 Google LLC | ||
function registerQuotaErrorCallback(callback) { | ||
{ | ||
finalAssertExports.isType(callback, 'function', { | ||
moduleName: 'workbox-core', | ||
funcName: 'register', | ||
paramName: 'callback' | ||
}); | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Adds a function to the set of quotaErrorCallbacks that will be executed if | ||
* there's a quota error. | ||
* | ||
* @param {Function} callback | ||
* @memberof workbox.core | ||
*/ | ||
quotaErrorCallbacks.add(callback); | ||
function registerQuotaErrorCallback(callback) { | ||
{ | ||
finalAssertExports.isType(callback, 'function', { | ||
moduleName: 'workbox-core', | ||
funcName: 'register', | ||
paramName: 'callback' | ||
}); | ||
} | ||
{ | ||
logger.log('Registered a callback to respond to quota errors.', callback); | ||
quotaErrorCallbacks.add(callback); | ||
{ | ||
logger.log('Registered a callback to respond to quota errors.', callback); | ||
} | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const _cacheNameDetails = { | ||
googleAnalytics: 'googleAnalytics', | ||
precache: 'precache-v2', | ||
prefix: 'workbox', | ||
runtime: 'runtime', | ||
suffix: self.registration.scope | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const _cacheNameDetails = { | ||
googleAnalytics: 'googleAnalytics', | ||
precache: 'precache-v2', | ||
prefix: 'workbox', | ||
runtime: 'runtime', | ||
suffix: registration.scope | ||
}; | ||
const _createCacheName = cacheName => { | ||
return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix].filter(value => value.length > 0).join('-'); | ||
}; | ||
const _createCacheName = cacheName => { | ||
return [_cacheNameDetails.prefix, cacheName, _cacheNameDetails.suffix].filter(value => value && value.length > 0).join('-'); | ||
}; | ||
const cacheNames = { | ||
updateDetails: details => { | ||
Object.keys(_cacheNameDetails).forEach(key => { | ||
if (typeof details[key] !== 'undefined') { | ||
_cacheNameDetails[key] = details[key]; | ||
} | ||
}); | ||
}, | ||
getGoogleAnalyticsName: userCacheName => { | ||
return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics); | ||
}, | ||
getPrecacheName: userCacheName => { | ||
return userCacheName || _createCacheName(_cacheNameDetails.precache); | ||
}, | ||
getPrefix: () => { | ||
return _cacheNameDetails.prefix; | ||
}, | ||
getRuntimeName: userCacheName => { | ||
return userCacheName || _createCacheName(_cacheNameDetails.runtime); | ||
}, | ||
getSuffix: () => { | ||
return _cacheNameDetails.suffix; | ||
} | ||
}; | ||
const eachCacheNameDetail = fn => { | ||
for (const key of Object.keys(_cacheNameDetails)) { | ||
fn(key); | ||
} | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
const cacheNames = { | ||
updateDetails: details => { | ||
eachCacheNameDetail(key => { | ||
if (typeof details[key] === 'string') { | ||
_cacheNameDetails[key] = details[key]; | ||
} | ||
}); | ||
}, | ||
getGoogleAnalyticsName: userCacheName => { | ||
return userCacheName || _createCacheName(_cacheNameDetails.googleAnalytics); | ||
}, | ||
getPrecacheName: userCacheName => { | ||
return userCacheName || _createCacheName(_cacheNameDetails.precache); | ||
}, | ||
getPrefix: () => { | ||
return _cacheNameDetails.prefix; | ||
}, | ||
getRuntimeName: userCacheName => { | ||
return userCacheName || _createCacheName(_cacheNameDetails.runtime); | ||
}, | ||
getSuffix: () => { | ||
return _cacheNameDetails.suffix; | ||
} | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/* | ||
Copyright 2018 Google LLC | ||
const getFriendlyURL = url => { | ||
const urlObj = new URL(url, location); | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
if (urlObj.origin === location.origin) { | ||
return urlObj.pathname; | ||
} | ||
const getFriendlyURL = url => { | ||
const urlObj = new URL(String(url), location.href); | ||
return urlObj.href; | ||
}; | ||
if (urlObj.origin === location.origin) { | ||
return urlObj.pathname; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
return urlObj.href; | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Runs all of the callback functions, one at a time sequentially, in the order | ||
* in which they were registered. | ||
* | ||
* @memberof workbox.core | ||
* @private | ||
*/ | ||
/* | ||
Copyright 2018 Google LLC | ||
async function executeQuotaErrorCallbacks() { | ||
{ | ||
logger.log(`About to run ${quotaErrorCallbacks.size} ` + `callbacks to clean up caches.`); | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Runs all of the callback functions, one at a time sequentially, in the order | ||
* in which they were registered. | ||
* | ||
* @memberof workbox.core | ||
* @private | ||
*/ | ||
for (const callback of quotaErrorCallbacks) { | ||
await callback(); | ||
async function executeQuotaErrorCallbacks() { | ||
{ | ||
logger.log(callback, 'is complete.'); | ||
logger.log(`About to run ${quotaErrorCallbacks.size} ` + `callbacks to clean up caches.`); | ||
} | ||
} | ||
{ | ||
logger.log('Finished running callbacks.'); | ||
} | ||
} | ||
for (const callback of quotaErrorCallbacks) { | ||
await callback(); | ||
/* | ||
Copyright 2018 Google LLC | ||
{ | ||
logger.log(callback, 'is complete.'); | ||
} | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const pluginEvents = { | ||
CACHE_DID_UPDATE: 'cacheDidUpdate', | ||
CACHE_KEY_WILL_BE_USED: 'cacheKeyWillBeUsed', | ||
CACHE_WILL_UPDATE: 'cacheWillUpdate', | ||
CACHED_RESPONSE_WILL_BE_USED: 'cachedResponseWillBeUsed', | ||
FETCH_DID_FAIL: 'fetchDidFail', | ||
FETCH_DID_SUCCEED: 'fetchDidSucceed', | ||
REQUEST_WILL_FETCH: 'requestWillFetch' | ||
}; | ||
{ | ||
logger.log('Finished running callbacks.'); | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const pluginUtils = { | ||
filter: (plugins, callbackName) => { | ||
return plugins.filter(plugin => callbackName in plugin); | ||
} | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const pluginUtils = { | ||
filter: (plugins, callbackName) => { | ||
return plugins.filter(plugin => callbackName in plugin); | ||
} | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Wrapper around cache.put(). | ||
* | ||
* Will call `cacheDidUpdate` on plugins if the cache was updated, using | ||
* `matchOptions` when determining what the old entry is. | ||
* | ||
* @param {Object} options | ||
* @param {string} options.cacheName | ||
* @param {Request} options.request | ||
* @param {Response} options.response | ||
* @param {Event} [options.event] | ||
* @param {Array<Object>} [options.plugins=[]] | ||
* @param {Object} [options.matchOptions] | ||
* | ||
* @private | ||
* @memberof module:workbox-core | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Wrapper around cache.put(). | ||
* | ||
* Will call `cacheDidUpdate` on plugins if the cache was updated, using | ||
* `matchOptions` when determining what the old entry is. | ||
* | ||
* @param {Object} options | ||
* @param {string} options.cacheName | ||
* @param {Request} options.request | ||
* @param {Response} options.response | ||
* @param {Event} [options.event] | ||
* @param {Array<Object>} [options.plugins=[]] | ||
* @param {Object} [options.matchOptions] | ||
* | ||
* @private | ||
* @memberof module:workbox-core | ||
*/ | ||
const putWrapper = async ({ | ||
cacheName, | ||
request, | ||
response, | ||
event, | ||
plugins = [], | ||
matchOptions | ||
} = {}) => { | ||
{ | ||
if (request.method && request.method !== 'GET') { | ||
throw new WorkboxError('attempt-to-cache-non-get-request', { | ||
url: getFriendlyURL(request.url), | ||
method: request.method | ||
}); | ||
} | ||
} | ||
const effectiveRequest = await _getEffectiveRequest({ | ||
plugins, | ||
const putWrapper = async ({ | ||
cacheName, | ||
request, | ||
mode: 'write' | ||
}); | ||
if (!response) { | ||
response, | ||
event, | ||
plugins = [], | ||
matchOptions | ||
}) => { | ||
{ | ||
logger.error(`Cannot cache non-existent response for ` + `'${getFriendlyURL(effectiveRequest.url)}'.`); | ||
if (request.method && request.method !== 'GET') { | ||
throw new WorkboxError('attempt-to-cache-non-get-request', { | ||
url: getFriendlyURL(request.url), | ||
method: request.method | ||
}); | ||
} | ||
} | ||
throw new WorkboxError('cache-put-with-no-response', { | ||
url: getFriendlyURL(effectiveRequest.url) | ||
const effectiveRequest = await _getEffectiveRequest({ | ||
plugins, | ||
request, | ||
mode: 'write' | ||
}); | ||
} | ||
let responseToCache = await _isResponseSafeToCache({ | ||
event, | ||
plugins, | ||
response, | ||
request: effectiveRequest | ||
}); | ||
if (!response) { | ||
{ | ||
logger.error(`Cannot cache non-existent response for ` + `'${getFriendlyURL(effectiveRequest.url)}'.`); | ||
} | ||
if (!responseToCache) { | ||
{ | ||
logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' will ` + `not be cached.`, responseToCache); | ||
throw new WorkboxError('cache-put-with-no-response', { | ||
url: getFriendlyURL(effectiveRequest.url) | ||
}); | ||
} | ||
return; | ||
} | ||
let responseToCache = await _isResponseSafeToCache({ | ||
event, | ||
plugins, | ||
response, | ||
request: effectiveRequest | ||
}); | ||
const cache = await caches.open(cacheName); | ||
const updatePlugins = pluginUtils.filter(plugins, pluginEvents.CACHE_DID_UPDATE); | ||
let oldResponse = updatePlugins.length > 0 ? await matchWrapper({ | ||
cacheName, | ||
matchOptions, | ||
request: effectiveRequest | ||
}) : null; | ||
if (!responseToCache) { | ||
{ | ||
logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' will ` + `not be cached.`, responseToCache); | ||
} | ||
{ | ||
logger.debug(`Updating the '${cacheName}' cache with a new Response for ` + `${getFriendlyURL(effectiveRequest.url)}.`); | ||
} | ||
try { | ||
await cache.put(effectiveRequest, responseToCache); | ||
} catch (error) { | ||
// See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError | ||
if (error.name === 'QuotaExceededError') { | ||
await executeQuotaErrorCallbacks(); | ||
return; | ||
} | ||
throw error; | ||
} | ||
for (let plugin of updatePlugins) { | ||
await plugin[pluginEvents.CACHE_DID_UPDATE].call(plugin, { | ||
const cache = await caches.open(cacheName); | ||
const updatePlugins = pluginUtils.filter(plugins, "cacheDidUpdate" | ||
/* CACHE_DID_UPDATE */ | ||
); | ||
let oldResponse = updatePlugins.length > 0 ? await matchWrapper({ | ||
cacheName, | ||
event, | ||
oldResponse, | ||
newResponse: responseToCache, | ||
matchOptions, | ||
request: effectiveRequest | ||
}); | ||
} | ||
}; | ||
/** | ||
* This is a wrapper around cache.match(). | ||
* | ||
* @param {Object} options | ||
* @param {string} options.cacheName Name of the cache to match against. | ||
* @param {Request} options.request The Request that will be used to look up | ||
* cache entries. | ||
* @param {Event} [options.event] The event that propted the action. | ||
* @param {Object} [options.matchOptions] Options passed to cache.match(). | ||
* @param {Array<Object>} [options.plugins=[]] Array of plugins. | ||
* @return {Response} A cached response if available. | ||
* | ||
* @private | ||
* @memberof module:workbox-core | ||
*/ | ||
}) : null; | ||
{ | ||
logger.debug(`Updating the '${cacheName}' cache with a new Response for ` + `${getFriendlyURL(effectiveRequest.url)}.`); | ||
} | ||
const matchWrapper = async ({ | ||
cacheName, | ||
request, | ||
event, | ||
matchOptions, | ||
plugins = [] | ||
}) => { | ||
const cache = await caches.open(cacheName); | ||
const effectiveRequest = await _getEffectiveRequest({ | ||
plugins, | ||
request, | ||
mode: 'read' | ||
}); | ||
let cachedResponse = await cache.match(effectiveRequest, matchOptions); | ||
try { | ||
await cache.put(effectiveRequest, responseToCache); | ||
} catch (error) { | ||
// See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError | ||
if (error.name === 'QuotaExceededError') { | ||
await executeQuotaErrorCallbacks(); | ||
} | ||
{ | ||
if (cachedResponse) { | ||
logger.debug(`Found a cached response in '${cacheName}'.`); | ||
} else { | ||
logger.debug(`No cached response found in '${cacheName}'.`); | ||
throw error; | ||
} | ||
} | ||
for (const plugin of plugins) { | ||
if (pluginEvents.CACHED_RESPONSE_WILL_BE_USED in plugin) { | ||
cachedResponse = await plugin[pluginEvents.CACHED_RESPONSE_WILL_BE_USED].call(plugin, { | ||
for (let plugin of updatePlugins) { | ||
await plugin["cacheDidUpdate" | ||
/* CACHE_DID_UPDATE */ | ||
].call(plugin, { | ||
cacheName, | ||
event, | ||
matchOptions, | ||
cachedResponse, | ||
oldResponse, | ||
newResponse: responseToCache, | ||
request: effectiveRequest | ||
}); | ||
} | ||
}; | ||
/** | ||
* This is a wrapper around cache.match(). | ||
* | ||
* @param {Object} options | ||
* @param {string} options.cacheName Name of the cache to match against. | ||
* @param {Request} options.request The Request that will be used to look up | ||
* cache entries. | ||
* @param {Event} [options.event] The event that propted the action. | ||
* @param {Object} [options.matchOptions] Options passed to cache.match(). | ||
* @param {Array<Object>} [options.plugins=[]] Array of plugins. | ||
* @return {Response} A cached response if available. | ||
* | ||
* @private | ||
* @memberof module:workbox-core | ||
*/ | ||
{ | ||
if (cachedResponse) { | ||
finalAssertExports.isInstance(cachedResponse, Response, { | ||
moduleName: 'Plugin', | ||
funcName: pluginEvents.CACHED_RESPONSE_WILL_BE_USED, | ||
isReturnValueProblem: true | ||
}); | ||
const matchWrapper = async ({ | ||
cacheName, | ||
request, | ||
event, | ||
matchOptions, | ||
plugins = [] | ||
}) => { | ||
const cache = await caches.open(cacheName); | ||
const effectiveRequest = await _getEffectiveRequest({ | ||
plugins, | ||
request, | ||
mode: 'read' | ||
}); | ||
let cachedResponse = await cache.match(effectiveRequest, matchOptions); | ||
{ | ||
if (cachedResponse) { | ||
logger.debug(`Found a cached response in '${cacheName}'.`); | ||
} else { | ||
logger.debug(`No cached response found in '${cacheName}'.`); | ||
} | ||
} | ||
for (const plugin of plugins) { | ||
if ("cachedResponseWillBeUsed" | ||
/* CACHED_RESPONSE_WILL_BE_USED */ | ||
in plugin) { | ||
const pluginMethod = plugin["cachedResponseWillBeUsed" | ||
/* CACHED_RESPONSE_WILL_BE_USED */ | ||
]; | ||
cachedResponse = await pluginMethod.call(plugin, { | ||
cacheName, | ||
event, | ||
matchOptions, | ||
cachedResponse, | ||
request: effectiveRequest | ||
}); | ||
{ | ||
if (cachedResponse) { | ||
finalAssertExports.isInstance(cachedResponse, Response, { | ||
moduleName: 'Plugin', | ||
funcName: "cachedResponseWillBeUsed" | ||
/* CACHED_RESPONSE_WILL_BE_USED */ | ||
, | ||
isReturnValueProblem: true | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return cachedResponse; | ||
}; | ||
/** | ||
* This method will call cacheWillUpdate on the available plugins (or use | ||
* status === 200) to determine if the Response is safe and valid to cache. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request | ||
* @param {Response} options.response | ||
* @param {Event} [options.event] | ||
* @param {Array<Object>} [options.plugins=[]] | ||
* @return {Promise<Response>} | ||
* | ||
* @private | ||
* @memberof module:workbox-core | ||
*/ | ||
return cachedResponse; | ||
}; | ||
/** | ||
* This method will call cacheWillUpdate on the available plugins (or use | ||
* status === 200) to determine if the Response is safe and valid to cache. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request | ||
* @param {Response} options.response | ||
* @param {Event} [options.event] | ||
* @param {Array<Object>} [options.plugins=[]] | ||
* @return {Promise<Response>} | ||
* | ||
* @private | ||
* @memberof module:workbox-core | ||
*/ | ||
const _isResponseSafeToCache = async ({ | ||
request, | ||
response, | ||
event, | ||
plugins | ||
}) => { | ||
let responseToCache = response; | ||
let pluginsUsed = false; | ||
const _isResponseSafeToCache = async ({ | ||
request, | ||
response, | ||
event, | ||
plugins = [] | ||
}) => { | ||
let responseToCache = response; | ||
let pluginsUsed = false; | ||
for (let plugin of plugins) { | ||
if (pluginEvents.CACHE_WILL_UPDATE in plugin) { | ||
pluginsUsed = true; | ||
responseToCache = await plugin[pluginEvents.CACHE_WILL_UPDATE].call(plugin, { | ||
request, | ||
response: responseToCache, | ||
event | ||
}); | ||
for (let plugin of plugins) { | ||
if ("cacheWillUpdate" | ||
/* CACHE_WILL_UPDATE */ | ||
in plugin) { | ||
pluginsUsed = true; | ||
const pluginMethod = plugin["cacheWillUpdate" | ||
/* CACHE_WILL_UPDATE */ | ||
]; | ||
responseToCache = await pluginMethod.call(plugin, { | ||
request, | ||
response: responseToCache, | ||
event | ||
}); | ||
{ | ||
if (responseToCache) { | ||
finalAssertExports.isInstance(responseToCache, Response, { | ||
moduleName: 'Plugin', | ||
funcName: pluginEvents.CACHE_WILL_UPDATE, | ||
isReturnValueProblem: true | ||
}); | ||
{ | ||
if (responseToCache) { | ||
finalAssertExports.isInstance(responseToCache, Response, { | ||
moduleName: 'Plugin', | ||
funcName: "cacheWillUpdate" | ||
/* CACHE_WILL_UPDATE */ | ||
, | ||
isReturnValueProblem: true | ||
}); | ||
} | ||
} | ||
} | ||
if (!responseToCache) { | ||
break; | ||
if (!responseToCache) { | ||
break; | ||
} | ||
} | ||
} | ||
} | ||
if (!pluginsUsed) { | ||
{ | ||
if (!responseToCache.status === 200) { | ||
if (responseToCache.status === 0) { | ||
logger.warn(`The response for '${request.url}' is an opaque ` + `response. The caching strategy that you're using will not ` + `cache opaque responses by default.`); | ||
} else { | ||
logger.debug(`The response for '${request.url}' returned ` + `a status code of '${response.status}' and won't be cached as a ` + `result.`); | ||
if (!pluginsUsed) { | ||
{ | ||
if (responseToCache) { | ||
if (responseToCache.status !== 200) { | ||
if (responseToCache.status === 0) { | ||
logger.warn(`The response for '${request.url}' is an opaque ` + `response. The caching strategy that you're using will not ` + `cache opaque responses by default.`); | ||
} else { | ||
logger.debug(`The response for '${request.url}' returned ` + `a status code of '${response.status}' and won't be cached as a ` + `result.`); | ||
} | ||
} | ||
} | ||
} | ||
responseToCache = responseToCache && responseToCache.status === 200 ? responseToCache : undefined; | ||
} | ||
responseToCache = responseToCache.status === 200 ? responseToCache : null; | ||
} | ||
return responseToCache ? responseToCache : null; | ||
}; | ||
/** | ||
* Checks the list of plugins for the cacheKeyWillBeUsed callback, and | ||
* executes any of those callbacks found in sequence. The final `Request` object | ||
* returned by the last plugin is treated as the cache key for cache reads | ||
* and/or writes. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request | ||
* @param {string} options.mode | ||
* @param {Array<Object>} [options.plugins=[]] | ||
* @return {Promise<Request>} | ||
* | ||
* @private | ||
* @memberof module:workbox-core | ||
*/ | ||
return responseToCache ? responseToCache : null; | ||
}; | ||
/** | ||
* Checks the list of plugins for the cacheKeyWillBeUsed callback, and | ||
* executes any of those callbacks found in sequence. The final `Request` object | ||
* returned by the last plugin is treated as the cache key for cache reads | ||
* and/or writes. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request | ||
* @param {string} options.mode | ||
* @param {Array<Object>} [options.plugins=[]] | ||
* @return {Promise<Request>} | ||
* | ||
* @private | ||
* @memberof module:workbox-core | ||
*/ | ||
const _getEffectiveRequest = async ({ | ||
request, | ||
mode, | ||
plugins = [] | ||
}) => { | ||
const cacheKeyWillBeUsedPlugins = pluginUtils.filter(plugins, "cacheKeyWillBeUsed" | ||
/* CACHE_KEY_WILL_BE_USED */ | ||
); | ||
let effectiveRequest = request; | ||
const _getEffectiveRequest = async ({ | ||
request, | ||
mode, | ||
plugins | ||
}) => { | ||
const cacheKeyWillBeUsedPlugins = pluginUtils.filter(plugins, pluginEvents.CACHE_KEY_WILL_BE_USED); | ||
let effectiveRequest = request; | ||
for (const plugin of cacheKeyWillBeUsedPlugins) { | ||
effectiveRequest = await plugin["cacheKeyWillBeUsed" | ||
/* CACHE_KEY_WILL_BE_USED */ | ||
].call(plugin, { | ||
mode, | ||
request: effectiveRequest | ||
}); | ||
for (const plugin of cacheKeyWillBeUsedPlugins) { | ||
effectiveRequest = await plugin[pluginEvents.CACHE_KEY_WILL_BE_USED].call(plugin, { | ||
mode, | ||
request: effectiveRequest | ||
}); | ||
if (typeof effectiveRequest === 'string') { | ||
effectiveRequest = new Request(effectiveRequest); | ||
} | ||
if (typeof effectiveRequest === 'string') { | ||
effectiveRequest = new Request(effectiveRequest); | ||
{ | ||
finalAssertExports.isInstance(effectiveRequest, Request, { | ||
moduleName: 'Plugin', | ||
funcName: "cacheKeyWillBeUsed" | ||
/* CACHE_KEY_WILL_BE_USED */ | ||
, | ||
isReturnValueProblem: true | ||
}); | ||
} | ||
} | ||
{ | ||
finalAssertExports.isInstance(effectiveRequest, Request, { | ||
moduleName: 'Plugin', | ||
funcName: pluginEvents.CACHE_KEY_WILL_BE_USED, | ||
isReturnValueProblem: true | ||
}); | ||
} | ||
} | ||
return effectiveRequest; | ||
}; | ||
return effectiveRequest; | ||
}; | ||
const cacheWrapper = { | ||
put: putWrapper, | ||
match: matchWrapper | ||
}; | ||
const cacheWrapper = { | ||
put: putWrapper, | ||
match: matchWrapper | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* A class that wraps common IndexedDB functionality in a promise-based API. | ||
* It exposes all the underlying power and functionality of IndexedDB, but | ||
* wraps the most commonly used features in a way that's much simpler to use. | ||
* | ||
* @private | ||
*/ | ||
class DBWrapper { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {string} name | ||
* @param {number} version | ||
* @param {Object=} [callback] | ||
* @param {!Function} [callbacks.onupgradeneeded] | ||
* @param {!Function} [callbacks.onversionchange] Defaults to | ||
* DBWrapper.prototype._onversionchange when not specified. | ||
* @private | ||
*/ | ||
constructor(name, version, { | ||
onupgradeneeded, | ||
onversionchange = this._onversionchange | ||
} = {}) { | ||
this._name = name; | ||
this._version = version; | ||
this._onupgradeneeded = onupgradeneeded; | ||
this._onversionchange = onversionchange; // If this is null, it means the database isn't open. | ||
this._db = null; | ||
} | ||
/** | ||
* Returns the IDBDatabase instance (not normally needed). | ||
* A class that wraps common IndexedDB functionality in a promise-based API. | ||
* It exposes all the underlying power and functionality of IndexedDB, but | ||
* wraps the most commonly used features in a way that's much simpler to use. | ||
* | ||
@@ -1030,684 +966,689 @@ * @private | ||
class DBWrapper { | ||
/** | ||
* @param {string} name | ||
* @param {number} version | ||
* @param {Object=} [callback] | ||
* @param {!Function} [callbacks.onupgradeneeded] | ||
* @param {!Function} [callbacks.onversionchange] Defaults to | ||
* DBWrapper.prototype._onversionchange when not specified. | ||
* @private | ||
*/ | ||
constructor(name, version, { | ||
onupgradeneeded, | ||
onversionchange | ||
} = {}) { | ||
this._db = null; | ||
this._name = name; | ||
this._version = version; | ||
this._onupgradeneeded = onupgradeneeded; | ||
get db() { | ||
return this._db; | ||
} | ||
/** | ||
* Opens a connected to an IDBDatabase, invokes any onupgradedneeded | ||
* callback, and added an onversionchange callback to the database. | ||
* | ||
* @return {IDBDatabase} | ||
* @private | ||
*/ | ||
this._onversionchange = onversionchange || (() => this.close()); | ||
} | ||
/** | ||
* Returns the IDBDatabase instance (not normally needed). | ||
* @return {IDBDatabase|undefined} | ||
* | ||
* @private | ||
*/ | ||
async open() { | ||
if (this._db) return; | ||
this._db = await new Promise((resolve, reject) => { | ||
// This flag is flipped to true if the timeout callback runs prior | ||
// to the request failing or succeeding. Note: we use a timeout instead | ||
// of an onblocked handler since there are cases where onblocked will | ||
// never never run. A timeout better handles all possible scenarios: | ||
// https://github.com/w3c/IndexedDB/issues/223 | ||
let openRequestTimedOut = false; | ||
setTimeout(() => { | ||
openRequestTimedOut = true; | ||
reject(new Error('The open request was blocked and timed out')); | ||
}, this.OPEN_TIMEOUT); | ||
const openRequest = indexedDB.open(this._name, this._version); | ||
get db() { | ||
return this._db; | ||
} | ||
/** | ||
* Opens a connected to an IDBDatabase, invokes any onupgradedneeded | ||
* callback, and added an onversionchange callback to the database. | ||
* | ||
* @return {IDBDatabase} | ||
* @private | ||
*/ | ||
openRequest.onerror = () => reject(openRequest.error); | ||
openRequest.onupgradeneeded = evt => { | ||
if (openRequestTimedOut) { | ||
openRequest.transaction.abort(); | ||
evt.target.result.close(); | ||
} else if (this._onupgradeneeded) { | ||
this._onupgradeneeded(evt); | ||
} | ||
}; | ||
async open() { | ||
if (this._db) return; | ||
this._db = await new Promise((resolve, reject) => { | ||
// This flag is flipped to true if the timeout callback runs prior | ||
// to the request failing or succeeding. Note: we use a timeout instead | ||
// of an onblocked handler since there are cases where onblocked will | ||
// never never run. A timeout better handles all possible scenarios: | ||
// https://github.com/w3c/IndexedDB/issues/223 | ||
let openRequestTimedOut = false; | ||
setTimeout(() => { | ||
openRequestTimedOut = true; | ||
reject(new Error('The open request was blocked and timed out')); | ||
}, this.OPEN_TIMEOUT); | ||
const openRequest = indexedDB.open(this._name, this._version); | ||
openRequest.onsuccess = ({ | ||
target | ||
}) => { | ||
const db = target.result; | ||
openRequest.onerror = () => reject(openRequest.error); | ||
if (openRequestTimedOut) { | ||
db.close(); | ||
} else { | ||
db.onversionchange = this._onversionchange.bind(this); | ||
resolve(db); | ||
} | ||
}; | ||
}); | ||
return this; | ||
} | ||
/** | ||
* Polyfills the native `getKey()` method. Note, this is overridden at | ||
* runtime if the browser supports the native method. | ||
* | ||
* @param {string} storeName | ||
* @param {*} query | ||
* @return {Array} | ||
* @private | ||
*/ | ||
openRequest.onupgradeneeded = evt => { | ||
if (openRequestTimedOut) { | ||
openRequest.transaction.abort(); | ||
openRequest.result.close(); | ||
} else if (typeof this._onupgradeneeded === 'function') { | ||
this._onupgradeneeded(evt); | ||
} | ||
}; | ||
openRequest.onsuccess = () => { | ||
const db = openRequest.result; | ||
async getKey(storeName, query) { | ||
return (await this.getAllKeys(storeName, query, 1))[0]; | ||
} | ||
/** | ||
* Polyfills the native `getAll()` method. Note, this is overridden at | ||
* runtime if the browser supports the native method. | ||
* | ||
* @param {string} storeName | ||
* @param {*} query | ||
* @param {number} count | ||
* @return {Array} | ||
* @private | ||
*/ | ||
if (openRequestTimedOut) { | ||
db.close(); | ||
} else { | ||
db.onversionchange = this._onversionchange.bind(this); | ||
resolve(db); | ||
} | ||
}; | ||
}); | ||
return this; | ||
} | ||
/** | ||
* Polyfills the native `getKey()` method. Note, this is overridden at | ||
* runtime if the browser supports the native method. | ||
* | ||
* @param {string} storeName | ||
* @param {*} query | ||
* @return {Array} | ||
* @private | ||
*/ | ||
async getAll(storeName, query, count) { | ||
return await this.getAllMatching(storeName, { | ||
query, | ||
count | ||
}); | ||
} | ||
/** | ||
* Polyfills the native `getAllKeys()` method. Note, this is overridden at | ||
* runtime if the browser supports the native method. | ||
* | ||
* @param {string} storeName | ||
* @param {*} query | ||
* @param {number} count | ||
* @return {Array} | ||
* @private | ||
*/ | ||
async getKey(storeName, query) { | ||
return (await this.getAllKeys(storeName, query, 1))[0]; | ||
} | ||
/** | ||
* Polyfills the native `getAll()` method. Note, this is overridden at | ||
* runtime if the browser supports the native method. | ||
* | ||
* @param {string} storeName | ||
* @param {*} query | ||
* @param {number} count | ||
* @return {Array} | ||
* @private | ||
*/ | ||
async getAllKeys(storeName, query, count) { | ||
return (await this.getAllMatching(storeName, { | ||
query, | ||
count, | ||
includeKeys: true | ||
})).map(({ | ||
key | ||
}) => key); | ||
} | ||
/** | ||
* Supports flexible lookup in an object store by specifying an index, | ||
* query, direction, and count. This method returns an array of objects | ||
* with the signature . | ||
* | ||
* @param {string} storeName | ||
* @param {Object} [opts] | ||
* @param {string} [opts.index] The index to use (if specified). | ||
* @param {*} [opts.query] | ||
* @param {IDBCursorDirection} [opts.direction] | ||
* @param {number} [opts.count] The max number of results to return. | ||
* @param {boolean} [opts.includeKeys] When true, the structure of the | ||
* returned objects is changed from an array of values to an array of | ||
* objects in the form {key, primaryKey, value}. | ||
* @return {Array} | ||
* @private | ||
*/ | ||
async getAll(storeName, query, count) { | ||
return await this.getAllMatching(storeName, { | ||
query, | ||
count | ||
}); | ||
} | ||
/** | ||
* Polyfills the native `getAllKeys()` method. Note, this is overridden at | ||
* runtime if the browser supports the native method. | ||
* | ||
* @param {string} storeName | ||
* @param {*} query | ||
* @param {number} count | ||
* @return {Array} | ||
* @private | ||
*/ | ||
async getAllMatching(storeName, { | ||
index, | ||
query = null, | ||
// IE errors if query === `undefined`. | ||
direction = 'next', | ||
count, | ||
includeKeys | ||
} = {}) { | ||
return await this.transaction([storeName], 'readonly', (txn, done) => { | ||
const store = txn.objectStore(storeName); | ||
const target = index ? store.index(index) : store; | ||
const results = []; | ||
async getAllKeys(storeName, query, count) { | ||
const entries = await this.getAllMatching(storeName, { | ||
query, | ||
count, | ||
includeKeys: true | ||
}); | ||
return entries.map(entry => entry.key); | ||
} | ||
/** | ||
* Supports flexible lookup in an object store by specifying an index, | ||
* query, direction, and count. This method returns an array of objects | ||
* with the signature . | ||
* | ||
* @param {string} storeName | ||
* @param {Object} [opts] | ||
* @param {string} [opts.index] The index to use (if specified). | ||
* @param {*} [opts.query] | ||
* @param {IDBCursorDirection} [opts.direction] | ||
* @param {number} [opts.count] The max number of results to return. | ||
* @param {boolean} [opts.includeKeys] When true, the structure of the | ||
* returned objects is changed from an array of values to an array of | ||
* objects in the form {key, primaryKey, value}. | ||
* @return {Array} | ||
* @private | ||
*/ | ||
target.openCursor(query, direction).onsuccess = ({ | ||
target | ||
}) => { | ||
const cursor = target.result; | ||
if (cursor) { | ||
const { | ||
primaryKey, | ||
key, | ||
value | ||
} = cursor; | ||
results.push(includeKeys ? { | ||
primaryKey, | ||
key, | ||
value | ||
} : value); | ||
async getAllMatching(storeName, { | ||
index, | ||
query = null, | ||
// IE/Edge errors if query === `undefined`. | ||
direction = 'next', | ||
count, | ||
includeKeys = false | ||
} = {}) { | ||
return await this.transaction([storeName], 'readonly', (txn, done) => { | ||
const store = txn.objectStore(storeName); | ||
const target = index ? store.index(index) : store; | ||
const results = []; | ||
const request = target.openCursor(query, direction); | ||
if (count && results.length >= count) { | ||
request.onsuccess = () => { | ||
const cursor = request.result; | ||
if (cursor) { | ||
results.push(includeKeys ? cursor : cursor.value); | ||
if (count && results.length >= count) { | ||
done(results); | ||
} else { | ||
cursor.continue(); | ||
} | ||
} else { | ||
done(results); | ||
} else { | ||
cursor.continue(); | ||
} | ||
} else { | ||
done(results); | ||
} | ||
}; | ||
}); | ||
} | ||
/** | ||
* Accepts a list of stores, a transaction type, and a callback and | ||
* performs a transaction. A promise is returned that resolves to whatever | ||
* value the callback chooses. The callback holds all the transaction logic | ||
* and is invoked with two arguments: | ||
* 1. The IDBTransaction object | ||
* 2. A `done` function, that's used to resolve the promise when | ||
* when the transaction is done, if passed a value, the promise is | ||
* resolved to that value. | ||
* | ||
* @param {Array<string>} storeNames An array of object store names | ||
* involved in the transaction. | ||
* @param {string} type Can be `readonly` or `readwrite`. | ||
* @param {!Function} callback | ||
* @return {*} The result of the transaction ran by the callback. | ||
* @private | ||
*/ | ||
}; | ||
}); | ||
} | ||
/** | ||
* Accepts a list of stores, a transaction type, and a callback and | ||
* performs a transaction. A promise is returned that resolves to whatever | ||
* value the callback chooses. The callback holds all the transaction logic | ||
* and is invoked with two arguments: | ||
* 1. The IDBTransaction object | ||
* 2. A `done` function, that's used to resolve the promise when | ||
* when the transaction is done, if passed a value, the promise is | ||
* resolved to that value. | ||
* | ||
* @param {Array<string>} storeNames An array of object store names | ||
* involved in the transaction. | ||
* @param {string} type Can be `readonly` or `readwrite`. | ||
* @param {!Function} callback | ||
* @return {*} The result of the transaction ran by the callback. | ||
* @private | ||
*/ | ||
async transaction(storeNames, type, callback) { | ||
await this.open(); | ||
return await new Promise((resolve, reject) => { | ||
const txn = this._db.transaction(storeNames, type); | ||
async transaction(storeNames, type, callback) { | ||
await this.open(); | ||
return await new Promise((resolve, reject) => { | ||
const txn = this._db.transaction(storeNames, type); | ||
txn.onabort = ({ | ||
target | ||
}) => reject(target.error); | ||
txn.onabort = () => reject(txn.error); | ||
txn.oncomplete = () => resolve(); | ||
txn.oncomplete = () => resolve(); | ||
callback(txn, value => resolve(value)); | ||
}); | ||
} | ||
/** | ||
* Delegates async to a native IDBObjectStore method. | ||
* | ||
* @param {string} method The method name. | ||
* @param {string} storeName The object store name. | ||
* @param {string} type Can be `readonly` or `readwrite`. | ||
* @param {...*} args The list of args to pass to the native method. | ||
* @return {*} The result of the transaction. | ||
* @private | ||
*/ | ||
callback(txn, value => resolve(value)); | ||
}); | ||
} | ||
/** | ||
* Delegates async to a native IDBObjectStore method. | ||
* | ||
* @param {string} method The method name. | ||
* @param {string} storeName The object store name. | ||
* @param {string} type Can be `readonly` or `readwrite`. | ||
* @param {...*} args The list of args to pass to the native method. | ||
* @return {*} The result of the transaction. | ||
* @private | ||
*/ | ||
async _call(method, storeName, type, ...args) { | ||
const callback = (txn, done) => { | ||
txn.objectStore(storeName)[method](...args).onsuccess = ({ | ||
target | ||
}) => { | ||
done(target.result); | ||
async _call(method, storeName, type, ...args) { | ||
const callback = (txn, done) => { | ||
const objStore = txn.objectStore(storeName); | ||
const request = objStore[method].apply(objStore, args); | ||
request.onsuccess = () => done(request.result); | ||
}; | ||
}; | ||
return await this.transaction([storeName], type, callback); | ||
} | ||
/** | ||
* The default onversionchange handler, which closes the database so other | ||
* connections can open without being blocked. | ||
* | ||
* @private | ||
*/ | ||
return await this.transaction([storeName], type, callback); | ||
} | ||
/** | ||
* Closes the connection opened by `DBWrapper.open()`. Generally this method | ||
* doesn't need to be called since: | ||
* 1. It's usually better to keep a connection open since opening | ||
* a new connection is somewhat slow. | ||
* 2. Connections are automatically closed when the reference is | ||
* garbage collected. | ||
* The primary use case for needing to close a connection is when another | ||
* reference (typically in another tab) needs to upgrade it and would be | ||
* blocked by the current, open connection. | ||
* | ||
* @private | ||
*/ | ||
_onversionchange() { | ||
this.close(); | ||
} | ||
/** | ||
* Closes the connection opened by `DBWrapper.open()`. Generally this method | ||
* doesn't need to be called since: | ||
* 1. It's usually better to keep a connection open since opening | ||
* a new connection is somewhat slow. | ||
* 2. Connections are automatically closed when the reference is | ||
* garbage collected. | ||
* The primary use case for needing to close a connection is when another | ||
* reference (typically in another tab) needs to upgrade it and would be | ||
* blocked by the current, open connection. | ||
* | ||
* @private | ||
*/ | ||
close() { | ||
if (this._db) { | ||
this._db.close(); | ||
this._db = null; | ||
} | ||
} | ||
close() { | ||
if (this._db) { | ||
this._db.close(); | ||
} // Exposed on the prototype to let users modify the default timeout on a | ||
// per-instance or global basis. | ||
this._db = null; | ||
DBWrapper.prototype.OPEN_TIMEOUT = 2000; // Wrap native IDBObjectStore methods according to their mode. | ||
const methodsToWrap = { | ||
readonly: ['get', 'count', 'getKey', 'getAll', 'getAllKeys'], | ||
readwrite: ['add', 'put', 'clear', 'delete'] | ||
}; | ||
for (const [mode, methods] of Object.entries(methodsToWrap)) { | ||
for (const method of methods) { | ||
if (method in IDBObjectStore.prototype) { | ||
// Don't use arrow functions here since we're outside of the class. | ||
DBWrapper.prototype[method] = async function (storeName, ...args) { | ||
return await this._call(method, storeName, mode, ...args); | ||
}; | ||
} | ||
} | ||
} | ||
} // Exposed to let users modify the default timeout on a per-instance | ||
// or global basis. | ||
/* | ||
Copyright 2018 Google LLC | ||
DBWrapper.prototype.OPEN_TIMEOUT = 2000; // Wrap native IDBObjectStore methods according to their mode. | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* The Deferred class composes Promises in a way that allows for them to be | ||
* resolved or rejected from outside the constructor. In most cases promises | ||
* should be used directly, but Deferreds can be necessary when the logic to | ||
* resolve a promise must be separate. | ||
* | ||
* @private | ||
*/ | ||
const methodsToWrap = { | ||
'readonly': ['get', 'count', 'getKey', 'getAll', 'getAllKeys'], | ||
'readwrite': ['add', 'put', 'clear', 'delete'] | ||
}; | ||
class Deferred { | ||
/** | ||
* Creates a promise and exposes its resolve and reject functions as methods. | ||
*/ | ||
constructor() { | ||
this.promise = new Promise((resolve, reject) => { | ||
this.resolve = resolve; | ||
this.reject = reject; | ||
}); | ||
} | ||
for (const [mode, methods] of Object.entries(methodsToWrap)) { | ||
for (const method of methods) { | ||
if (method in IDBObjectStore.prototype) { | ||
// Don't use arrow functions here since we're outside of the class. | ||
DBWrapper.prototype[method] = async function (storeName, ...args) { | ||
return await this._call(method, storeName, mode, ...args); | ||
}; | ||
} | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* The Deferred class composes Promises in a way that allows for them to be | ||
* resolved or rejected from outside the constructor. In most cases promises | ||
* should be used directly, but Deferreds can be necessary when the logic to | ||
* resolve a promise must be separate. | ||
* | ||
* @private | ||
*/ | ||
class Deferred { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Creates a promise and exposes its resolve and reject functions as methods. | ||
* Deletes the database. | ||
* Note: this is exported separately from the DBWrapper module because most | ||
* usages of IndexedDB in workbox dont need deleting, and this way it can be | ||
* reused in tests to delete databases without creating DBWrapper instances. | ||
* | ||
* @param {string} name The database name. | ||
* @private | ||
*/ | ||
constructor() { | ||
this.promise = new Promise((resolve, reject) => { | ||
this.resolve = resolve; | ||
this.reject = reject; | ||
}); | ||
} | ||
} | ||
const deleteDatabase = async name => { | ||
await new Promise((resolve, reject) => { | ||
const request = indexedDB.deleteDatabase(name); | ||
/* | ||
Copyright 2018 Google LLC | ||
request.onerror = () => { | ||
reject(request.error); | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Deletes the database. | ||
* Note: this is exported separately from the DBWrapper module because most | ||
* usages of IndexedDB in workbox dont need deleting, and this way it can be | ||
* reused in tests to delete databases without creating DBWrapper instances. | ||
* | ||
* @param {string} name The database name. | ||
* @private | ||
*/ | ||
request.onblocked = () => { | ||
reject(new Error('Delete blocked')); | ||
}; | ||
const deleteDatabase = async name => { | ||
await new Promise((resolve, reject) => { | ||
const request = indexedDB.deleteDatabase(name); | ||
request.onsuccess = () => { | ||
resolve(); | ||
}; | ||
}); | ||
}; | ||
request.onerror = ({ | ||
target | ||
}) => { | ||
reject(target.error); | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
request.onblocked = () => { | ||
reject(new Error('Delete blocked')); | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Wrapper around the fetch API. | ||
* | ||
* Will call requestWillFetch on available plugins. | ||
* | ||
* @param {Object} options | ||
* @param {Request|string} options.request | ||
* @param {Object} [options.fetchOptions] | ||
* @param {Event} [options.event] | ||
* @param {Array<Object>} [options.plugins=[]] | ||
* @return {Promise<Response>} | ||
* | ||
* @private | ||
* @memberof module:workbox-core | ||
*/ | ||
request.onsuccess = () => { | ||
resolve(); | ||
}; | ||
}); | ||
}; | ||
const wrappedFetch = async ({ | ||
request, | ||
fetchOptions, | ||
event, | ||
plugins = [] | ||
}) => { | ||
if (typeof request === 'string') { | ||
request = new Request(request); | ||
} // We *should* be able to call `await event.preloadResponse` even if it's | ||
// undefined, but for some reason, doing so leads to errors in our Node unit | ||
// tests. To work around that, explicitly check preloadResponse's value first. | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Wrapper around the fetch API. | ||
* | ||
* Will call requestWillFetch on available plugins. | ||
* | ||
* @param {Object} options | ||
* @param {Request|string} options.request | ||
* @param {Object} [options.fetchOptions] | ||
* @param {Event} [options.event] | ||
* @param {Array<Object>} [options.plugins=[]] | ||
* @return {Promise<Response>} | ||
* | ||
* @private | ||
* @memberof module:workbox-core | ||
*/ | ||
if (event instanceof FetchEvent && event.preloadResponse) { | ||
const possiblePreloadResponse = await event.preloadResponse; | ||
const wrappedFetch = async ({ | ||
request, | ||
fetchOptions, | ||
event, | ||
plugins = [] | ||
}) => { | ||
// We *should* be able to call `await event.preloadResponse` even if it's | ||
// undefined, but for some reason, doing so leads to errors in our Node unit | ||
// tests. To work around that, explicitly check preloadResponse's value first. | ||
if (event && event.preloadResponse) { | ||
const possiblePreloadResponse = await event.preloadResponse; | ||
if (possiblePreloadResponse) { | ||
{ | ||
logger.log(`Using a preloaded navigation response for ` + `'${getFriendlyURL(request.url)}'`); | ||
} | ||
if (possiblePreloadResponse) { | ||
{ | ||
logger.log(`Using a preloaded navigation response for ` + `'${getFriendlyURL(request.url)}'`); | ||
return possiblePreloadResponse; | ||
} | ||
} | ||
return possiblePreloadResponse; | ||
{ | ||
finalAssertExports.isInstance(request, Request, { | ||
paramName: 'request', | ||
expectedClass: Request, | ||
moduleName: 'workbox-core', | ||
className: 'fetchWrapper', | ||
funcName: 'wrappedFetch' | ||
}); | ||
} | ||
} | ||
if (typeof request === 'string') { | ||
request = new Request(request); | ||
} | ||
const failedFetchPlugins = pluginUtils.filter(plugins, "fetchDidFail" | ||
/* FETCH_DID_FAIL */ | ||
); // If there is a fetchDidFail plugin, we need to save a clone of the | ||
// original request before it's either modified by a requestWillFetch | ||
// plugin or before the original request's body is consumed via fetch(). | ||
{ | ||
finalAssertExports.isInstance(request, Request, { | ||
paramName: request, | ||
expectedClass: 'Request', | ||
moduleName: 'workbox-core', | ||
className: 'fetchWrapper', | ||
funcName: 'wrappedFetch' | ||
}); | ||
} | ||
const originalRequest = failedFetchPlugins.length > 0 ? request.clone() : null; | ||
const failedFetchPlugins = pluginUtils.filter(plugins, pluginEvents.FETCH_DID_FAIL); // If there is a fetchDidFail plugin, we need to save a clone of the | ||
// original request before it's either modified by a requestWillFetch | ||
// plugin or before the original request's body is consumed via fetch(). | ||
try { | ||
for (let plugin of plugins) { | ||
if ("requestWillFetch" | ||
/* REQUEST_WILL_FETCH */ | ||
in plugin) { | ||
const pluginMethod = plugin["requestWillFetch" | ||
/* REQUEST_WILL_FETCH */ | ||
]; | ||
const requestClone = request.clone(); | ||
request = await pluginMethod.call(plugin, { | ||
request: requestClone, | ||
event | ||
}); | ||
const originalRequest = failedFetchPlugins.length > 0 ? request.clone() : null; | ||
try { | ||
for (let plugin of plugins) { | ||
if (pluginEvents.REQUEST_WILL_FETCH in plugin) { | ||
request = await plugin[pluginEvents.REQUEST_WILL_FETCH].call(plugin, { | ||
request: request.clone(), | ||
event | ||
}); | ||
{ | ||
if (request) { | ||
finalAssertExports.isInstance(request, Request, { | ||
moduleName: 'Plugin', | ||
funcName: pluginEvents.CACHED_RESPONSE_WILL_BE_USED, | ||
isReturnValueProblem: true | ||
}); | ||
if ("dev" !== 'production') { | ||
if (request) { | ||
finalAssertExports.isInstance(request, Request, { | ||
moduleName: 'Plugin', | ||
funcName: "cachedResponseWillBeUsed" | ||
/* CACHED_RESPONSE_WILL_BE_USED */ | ||
, | ||
isReturnValueProblem: true | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
} catch (err) { | ||
throw new WorkboxError('plugin-error-request-will-fetch', { | ||
thrownError: err | ||
}); | ||
} // The request can be altered by plugins with `requestWillFetch` making | ||
// the original request (Most likely from a `fetch` event) to be different | ||
// to the Request we make. Pass both to `fetchDidFail` to aid debugging. | ||
} catch (err) { | ||
throw new WorkboxError('plugin-error-request-will-fetch', { | ||
thrownError: err | ||
}); | ||
} // The request can be altered by plugins with `requestWillFetch` making | ||
// the original request (Most likely from a `fetch` event) to be different | ||
// to the Request we make. Pass both to `fetchDidFail` to aid debugging. | ||
let pluginFilteredRequest = request.clone(); | ||
let pluginFilteredRequest = request.clone(); | ||
try { | ||
let fetchResponse; // See https://github.com/GoogleChrome/workbox/issues/1796 | ||
try { | ||
let fetchResponse; // See https://github.com/GoogleChrome/workbox/issues/1796 | ||
if (request.mode === 'navigate') { | ||
fetchResponse = await fetch(request); | ||
} else { | ||
fetchResponse = await fetch(request, fetchOptions); | ||
} | ||
if (request.mode === 'navigate') { | ||
fetchResponse = await fetch(request); | ||
} else { | ||
fetchResponse = await fetch(request, fetchOptions); | ||
} | ||
{ | ||
logger.debug(`Network request for ` + `'${getFriendlyURL(request.url)}' returned a response with ` + `status '${fetchResponse.status}'.`); | ||
} | ||
if ("dev" !== 'production') { | ||
logger.debug(`Network request for ` + `'${getFriendlyURL(request.url)}' returned a response with ` + `status '${fetchResponse.status}'.`); | ||
} | ||
for (const plugin of plugins) { | ||
if (pluginEvents.FETCH_DID_SUCCEED in plugin) { | ||
fetchResponse = await plugin[pluginEvents.FETCH_DID_SUCCEED].call(plugin, { | ||
event, | ||
request: pluginFilteredRequest, | ||
response: fetchResponse | ||
}); | ||
for (const plugin of plugins) { | ||
if ("fetchDidSucceed" | ||
/* FETCH_DID_SUCCEED */ | ||
in plugin) { | ||
fetchResponse = await plugin["fetchDidSucceed" | ||
/* FETCH_DID_SUCCEED */ | ||
].call(plugin, { | ||
event, | ||
request: pluginFilteredRequest, | ||
response: fetchResponse | ||
}); | ||
{ | ||
if (fetchResponse) { | ||
finalAssertExports.isInstance(fetchResponse, Response, { | ||
moduleName: 'Plugin', | ||
funcName: pluginEvents.FETCH_DID_SUCCEED, | ||
isReturnValueProblem: true | ||
}); | ||
if ("dev" !== 'production') { | ||
if (fetchResponse) { | ||
finalAssertExports.isInstance(fetchResponse, Response, { | ||
moduleName: 'Plugin', | ||
funcName: "fetchDidSucceed" | ||
/* FETCH_DID_SUCCEED */ | ||
, | ||
isReturnValueProblem: true | ||
}); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
return fetchResponse; | ||
} catch (error) { | ||
{ | ||
logger.error(`Network request for ` + `'${getFriendlyURL(request.url)}' threw an error.`, error); | ||
} | ||
return fetchResponse; | ||
} catch (error) { | ||
{ | ||
logger.error(`Network request for ` + `'${getFriendlyURL(request.url)}' threw an error.`, error); | ||
} | ||
for (const plugin of failedFetchPlugins) { | ||
await plugin[pluginEvents.FETCH_DID_FAIL].call(plugin, { | ||
error, | ||
event, | ||
originalRequest: originalRequest.clone(), | ||
request: pluginFilteredRequest.clone() | ||
}); | ||
for (const plugin of failedFetchPlugins) { | ||
await plugin["fetchDidFail" | ||
/* FETCH_DID_FAIL */ | ||
].call(plugin, { | ||
error, | ||
event, | ||
originalRequest: originalRequest.clone(), | ||
request: pluginFilteredRequest.clone() | ||
}); | ||
} | ||
throw error; | ||
} | ||
}; | ||
throw error; | ||
} | ||
}; | ||
const fetchWrapper = { | ||
fetch: wrappedFetch | ||
}; | ||
const fetchWrapper = { | ||
fetch: wrappedFetch | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
var _private = /*#__PURE__*/Object.freeze({ | ||
assert: finalAssertExports, | ||
cacheNames: cacheNames, | ||
cacheWrapper: cacheWrapper, | ||
DBWrapper: DBWrapper, | ||
Deferred: Deferred, | ||
deleteDatabase: deleteDatabase, | ||
executeQuotaErrorCallbacks: executeQuotaErrorCallbacks, | ||
fetchWrapper: fetchWrapper, | ||
getFriendlyURL: getFriendlyURL, | ||
logger: logger, | ||
WorkboxError: WorkboxError | ||
}); | ||
var _private = /*#__PURE__*/Object.freeze({ | ||
assert: finalAssertExports, | ||
cacheNames: cacheNames, | ||
cacheWrapper: cacheWrapper, | ||
DBWrapper: DBWrapper, | ||
Deferred: Deferred, | ||
deleteDatabase: deleteDatabase, | ||
executeQuotaErrorCallbacks: executeQuotaErrorCallbacks, | ||
fetchWrapper: fetchWrapper, | ||
getFriendlyURL: getFriendlyURL, | ||
logger: logger, | ||
WorkboxError: WorkboxError | ||
}); | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Claim any currently available clients once the service worker | ||
* becomes active. This is normally used in conjunction with `skipWaiting()`. | ||
* | ||
* @alias workbox.core.clientsClaim | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Claim any currently available clients once the service worker | ||
* becomes active. This is normally used in conjunction with `skipWaiting()`. | ||
* | ||
* @alias workbox.core.clientsClaim | ||
*/ | ||
const clientsClaim = () => { | ||
addEventListener('activate', () => self.clients.claim()); | ||
}; | ||
const clientsClaim = () => { | ||
addEventListener('activate', () => clients.claim()); | ||
}; | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Get the current cache names and prefix/suffix used by Workbox. | ||
* | ||
* `cacheNames.precache` is used for precached assets, | ||
* `cacheNames.googleAnalytics` is used by `workbox-google-analytics` to | ||
* store `analytics.js`, and `cacheNames.runtime` is used for everything else. | ||
* | ||
* `cacheNames.prefix` can be used to retrieve just the current prefix value. | ||
* `cacheNames.suffix` can be used to retrieve just the current suffix value. | ||
* | ||
* @return {Object} An object with `precache`, `runtime`, `prefix`, and | ||
* `googleAnalytics` properties. | ||
* | ||
* @alias workbox.core.cacheNames | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Get the current cache names and prefix/suffix used by Workbox. | ||
* | ||
* `cacheNames.precache` is used for precached assets, | ||
* `cacheNames.googleAnalytics` is used by `workbox-google-analytics` to | ||
* store `analytics.js`, and `cacheNames.runtime` is used for everything else. | ||
* | ||
* `cacheNames.prefix` can be used to retrieve just the current prefix value. | ||
* `cacheNames.suffix` can be used to retrieve just the current suffix value. | ||
* | ||
* @return {Object} An object with `precache`, `runtime`, `prefix`, and | ||
* `googleAnalytics` properties. | ||
* | ||
* @alias workbox.core.cacheNames | ||
*/ | ||
const cacheNames$1 = { | ||
get googleAnalytics() { | ||
return cacheNames.getGoogleAnalyticsName(); | ||
}, | ||
const cacheNames$1 = { | ||
get googleAnalytics() { | ||
return cacheNames.getGoogleAnalyticsName(); | ||
}, | ||
get precache() { | ||
return cacheNames.getPrecacheName(); | ||
}, | ||
get precache() { | ||
return cacheNames.getPrecacheName(); | ||
}, | ||
get prefix() { | ||
return cacheNames.getPrefix(); | ||
}, | ||
get prefix() { | ||
return cacheNames.getPrefix(); | ||
}, | ||
get runtime() { | ||
return cacheNames.getRuntimeName(); | ||
}, | ||
get runtime() { | ||
return cacheNames.getRuntimeName(); | ||
}, | ||
get suffix() { | ||
return cacheNames.getSuffix(); | ||
} | ||
get suffix() { | ||
return cacheNames.getSuffix(); | ||
} | ||
}; | ||
}; | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Modifies the default cache names used by the Workbox packages. | ||
* Cache names are generated as `<prefix>-<Cache Name>-<suffix>`. | ||
* | ||
* @param {Object} details | ||
* @param {Object} [details.prefix] The string to add to the beginning of | ||
* the precache and runtime cache names. | ||
* @param {Object} [details.suffix] The string to add to the end of | ||
* the precache and runtime cache names. | ||
* @param {Object} [details.precache] The cache name to use for precache | ||
* caching. | ||
* @param {Object} [details.runtime] The cache name to use for runtime caching. | ||
* @param {Object} [details.googleAnalytics] The cache name to use for | ||
* `workbox-google-analytics` caching. | ||
* | ||
* @alias workbox.core.setCacheNameDetails | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Modifies the default cache names used by the Workbox packages. | ||
* Cache names are generated as `<prefix>-<Cache Name>-<suffix>`. | ||
* | ||
* @param {Object} details | ||
* @param {Object} [details.prefix] The string to add to the beginning of | ||
* the precache and runtime cache names. | ||
* @param {Object} [details.suffix] The string to add to the end of | ||
* the precache and runtime cache names. | ||
* @param {Object} [details.precache] The cache name to use for precache | ||
* caching. | ||
* @param {Object} [details.runtime] The cache name to use for runtime caching. | ||
* @param {Object} [details.googleAnalytics] The cache name to use for | ||
* `workbox-google-analytics` caching. | ||
* | ||
* @alias workbox.core.setCacheNameDetails | ||
*/ | ||
const setCacheNameDetails = details => { | ||
{ | ||
Object.keys(details).forEach(key => { | ||
finalAssertExports.isType(details[key], 'string', { | ||
moduleName: 'workbox-core', | ||
funcName: 'setCacheNameDetails', | ||
paramName: `details.${key}` | ||
const setCacheNameDetails = details => { | ||
{ | ||
Object.keys(details).forEach(key => { | ||
finalAssertExports.isType(details[key], 'string', { | ||
moduleName: 'workbox-core', | ||
funcName: 'setCacheNameDetails', | ||
paramName: `details.${key}` | ||
}); | ||
}); | ||
}); | ||
if ('precache' in details && details.precache.length === 0) { | ||
throw new WorkboxError('invalid-cache-name', { | ||
cacheNameId: 'precache', | ||
value: details.precache | ||
}); | ||
} | ||
if ('precache' in details && details.precache.length === 0) { | ||
throw new WorkboxError('invalid-cache-name', { | ||
cacheNameId: 'precache', | ||
value: details.precache | ||
}); | ||
} | ||
if ('runtime' in details && details.runtime.length === 0) { | ||
throw new WorkboxError('invalid-cache-name', { | ||
cacheNameId: 'runtime', | ||
value: details.runtime | ||
}); | ||
} | ||
if ('runtime' in details && details.runtime.length === 0) { | ||
throw new WorkboxError('invalid-cache-name', { | ||
cacheNameId: 'runtime', | ||
value: details.runtime | ||
}); | ||
} | ||
if ('googleAnalytics' in details && details.googleAnalytics.length === 0) { | ||
throw new WorkboxError('invalid-cache-name', { | ||
cacheNameId: 'googleAnalytics', | ||
value: details.googleAnalytics | ||
}); | ||
if ('googleAnalytics' in details && details.googleAnalytics.length === 0) { | ||
throw new WorkboxError('invalid-cache-name', { | ||
cacheNameId: 'googleAnalytics', | ||
value: details.googleAnalytics | ||
}); | ||
} | ||
} | ||
} | ||
cacheNames.updateDetails(details); | ||
}; | ||
cacheNames.updateDetails(details); | ||
}; | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Force a service worker to become active, instead of waiting. This is | ||
* normally used in conjunction with `clientsClaim()`. | ||
* | ||
* @alias workbox.core.skipWaiting | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Force a service worker to become active, instead of waiting. This is | ||
* normally used in conjunction with `clientsClaim()`. | ||
* | ||
* @alias workbox.core.skipWaiting | ||
*/ | ||
const skipWaiting = () => { | ||
// We need to explicitly call `self.skipWaiting()` here because we're | ||
// shadowing `skipWaiting` with this local function. | ||
addEventListener('install', () => self.skipWaiting()); | ||
}; | ||
const skipWaiting = () => { | ||
// We need to explicitly call `self.skipWaiting()` here because we're | ||
// shadowing `skipWaiting` with this local function. | ||
addEventListener('install', () => self.skipWaiting()); | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
exports._private = _private; | ||
exports.cacheNames = cacheNames$1; | ||
exports.clientsClaim = clientsClaim; | ||
exports.registerQuotaErrorCallback = registerQuotaErrorCallback; | ||
exports.setCacheNameDetails = setCacheNameDetails; | ||
exports.skipWaiting = skipWaiting; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
return exports; | ||
try { | ||
self.workbox.v = self.workbox.v || {}; | ||
} catch (errer) {} // NOOP | ||
exports._private = _private; | ||
exports.clientsClaim = clientsClaim; | ||
exports.cacheNames = cacheNames$1; | ||
exports.registerQuotaErrorCallback = registerQuotaErrorCallback; | ||
exports.setCacheNameDetails = setCacheNameDetails; | ||
exports.skipWaiting = skipWaiting; | ||
return exports; | ||
}({})); | ||
@@ -1,2 +0,2 @@ | ||
this.workbox=this.workbox||{},this.workbox.core=function(e){"use strict";try{self["workbox:core:4.3.1"]&&_()}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:self.registration.scope},a=e=>[r.prefix,e,r.suffix].filter(e=>e.length>0).join("-"),i={updateDetails:e=>{Object.keys(r).forEach(t=>{void 0!==e[t]&&(r[t]=e[t])})},getGoogleAnalyticsName:e=>e||a(r.googleAnalytics),getPrecacheName:e=>e||a(r.precache),getPrefix:()=>r.prefix,getRuntimeName:e=>e||a(r.runtime),getSuffix:()=>r.suffix},c=e=>{const t=new URL(e,location);return t.origin===location.origin?t.pathname:t.href};async function o(){for(const e of s)await e()}const l="cacheDidUpdate",u="cacheKeyWillBeUsed",h="cacheWillUpdate",f="cachedResponseWillBeUsed",w="fetchDidFail",g="fetchDidSucceed",d="requestWillFetch",p=(e,t)=>e.filter(e=>t in e),y=async({cacheName:e,request:t,event:n,matchOptions:s,plugins:r=[]})=>{const a=await caches.open(e),i=await q({plugins:r,request:t,mode:"read"});let c=await a.match(i,s);for(const t of r)f in t&&(c=await t[f].call(t,{cacheName:e,event:n,matchOptions:s,cachedResponse:c,request:i}));return c},m=async({request:e,response:t,event:n,plugins:s})=>{let r=t,a=!1;for(let t of s)if(h in t&&(a=!0,!(r=await t[h].call(t,{request:e,response:r,event:n}))))break;return a||(r=200===r.status?r:null),r||null},q=async({request:e,mode:t,plugins:n})=>{const s=p(n,u);let r=e;for(const e of s)"string"==typeof(r=await e[u].call(e,{mode:t,request:r}))&&(r=new Request(r));return r},v={put:async({cacheName:e,request:t,response:s,event:r,plugins:a=[],matchOptions:i}={})=>{const u=await q({plugins:a,request:t,mode:"write"});if(!s)throw new n("cache-put-with-no-response",{url:c(u.url)});let h=await m({event:r,plugins:a,response:s,request:u});if(!h)return;const f=await caches.open(e),w=p(a,l);let g=w.length>0?await y({cacheName:e,matchOptions:i,request:u}):null;try{await f.put(u,h)}catch(e){throw"QuotaExceededError"===e.name&&await o(),e}for(let t of w)await t[l].call(t,{cacheName:e,event:r,oldResponse:g,newResponse:h,request:u})},match:y};class x{constructor(e,t,{onupgradeneeded:n,onversionchange:s=this.t}={}){this.s=e,this.i=t,this.o=n,this.t=s,this.l=null}get db(){return this.l}async open(){if(!this.l)return this.l=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(),e.target.result.close()):this.o&&this.o(e)}),s.onsuccess=(({target:t})=>{const s=t.result;n?s.close():(s.onversionchange=this.t.bind(this),e(s))})}),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(({key:e})=>e)}async getAllMatching(e,{index:t,query:n=null,direction:s="next",count:r,includeKeys:a}={}){return await this.transaction([e],"readonly",(i,c)=>{const o=i.objectStore(e),l=t?o.index(t):o,u=[];l.openCursor(n,s).onsuccess=(({target:e})=>{const t=e.result;if(t){const{primaryKey:e,key:n,value:s}=t;u.push(a?{primaryKey:e,key:n,value:s}:s),r&&u.length>=r?c(u):t.continue()}else c(u)})})}async transaction(e,t,n){return await this.open(),await new Promise((s,r)=>{const a=this.l.transaction(e,t);a.onabort=(({target:e})=>r(e.error)),a.oncomplete=(()=>s()),n(a,e=>s(e))})}async u(e,t,n,...s){return await this.transaction([t],n,(n,r)=>{n.objectStore(t)[e](...s).onsuccess=(({target:e})=>{r(e.result)})})}t(){this.close()}close(){this.l&&(this.l.close(),this.l=null)}}x.prototype.OPEN_TIMEOUT=2e3;const b={readonly:["get","count","getKey","getAll","getAllKeys"],readwrite:["add","put","clear","delete"]};for(const[e,t]of Object.entries(b))for(const n of t)n in IDBObjectStore.prototype&&(x.prototype[n]=async function(t,...s){return await this.u(n,t,e,...s)});const D={fetch:async({request:e,fetchOptions:t,event:s,plugins:r=[]})=>{if(s&&s.preloadResponse){const e=await s.preloadResponse;if(e)return e}"string"==typeof e&&(e=new Request(e));const a=p(r,w),i=a.length>0?e.clone():null;try{for(let t of r)d in t&&(e=await t[d].call(t,{request:e.clone(),event:s}))}catch(e){throw new n("plugin-error-request-will-fetch",{thrownError:e})}let c=e.clone();try{let n;n="navigate"===e.mode?await fetch(e):await fetch(e,t);for(const e of r)g in e&&(n=await e[g].call(e,{event:s,request:c,response:n}));return n}catch(e){for(const t of a)await t[w].call(t,{error:e,event:s,originalRequest:i.clone(),request:c.clone()});throw e}}};var E=Object.freeze({assert:null,cacheNames:i,cacheWrapper:v,DBWrapper:x,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=(({target:e})=>{n(e.error)}),s.onblocked=(()=>{n(new Error("Delete blocked"))}),s.onsuccess=(()=>{t()})})},executeQuotaErrorCallbacks:o,fetchWrapper:D,getFriendlyURL:c,logger:null,WorkboxError:n});const N={get googleAnalytics(){return i.getGoogleAnalyticsName()},get precache(){return i.getPrecacheName()},get prefix(){return i.getPrefix()},get runtime(){return i.getRuntimeName()},get suffix(){return i.getSuffix()}};try{self.workbox.v=self.workbox.v||{}}catch(e){}return e._private=E,e.clientsClaim=(()=>{addEventListener("activate",()=>clients.claim())}),e.cacheNames=N,e.registerQuotaErrorCallback=function(e){s.add(e)},e.setCacheNameDetails=(e=>{i.updateDetails(e)}),e.skipWaiting=(()=>{addEventListener("install",()=>self.skipWaiting())}),e}({}); | ||
this.workbox=this.workbox||{},this.workbox.core=function(e){"use strict";try{self["workbox:core:5.0.0-alpha.2"]&&_()}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:registration.scope},a=e=>[r.prefix,e,r.suffix].filter(e=>e&&e.length>0).join("-"),i={updateDetails:e=>{(e=>{for(const t of Object.keys(r))e(t)})(t=>{"string"==typeof e[t]&&(r[t]=e[t])})},getGoogleAnalyticsName:e=>e||a(r.googleAnalytics),getPrecacheName:e=>e||a(r.precache),getPrefix:()=>r.prefix,getRuntimeName:e=>e||a(r.runtime),getSuffix:()=>r.suffix},c=e=>{const t=new URL(String(e),location.href);return t.origin===location.origin?t.pathname:t.href};async function o(){for(const e of s)await e()}const u=(e,t)=>e.filter(e=>t in e),l=async({cacheName:e,request:t,event:n,matchOptions:s,plugins:r=[]})=>{const a=await caches.open(e),i=await f({plugins:r,request:t,mode:"read"});let c=await a.match(i,s);for(const t of r)if("cachedResponseWillBeUsed"in t){const r=t.cachedResponseWillBeUsed;c=await r.call(t,{cacheName:e,event:n,matchOptions:s,cachedResponse:c,request:i})}return c},h=async({request:e,response:t,event:n,plugins:s=[]})=>{let r=t,a=!1;for(let t of s)if("cacheWillUpdate"in t){a=!0;const s=t.cacheWillUpdate;if(!(r=await s.call(t,{request:e,response:r,event:n})))break}return a||(r=r&&200===r.status?r:void 0),r||null},f=async({request:e,mode:t,plugins:n=[]})=>{const s=u(n,"cacheKeyWillBeUsed");let r=e;for(const e of s)"string"==typeof(r=await e.cacheKeyWillBeUsed.call(e,{mode:t,request:r}))&&(r=new Request(r));return r},w={put:async({cacheName:e,request:t,response:s,event:r,plugins:a=[],matchOptions:i})=>{const w=await f({plugins:a,request:t,mode:"write"});if(!s)throw new n("cache-put-with-no-response",{url:c(w.url)});let d=await h({event:r,plugins:a,response:s,request:w});if(!d)return;const p=await caches.open(e),g=u(a,"cacheDidUpdate");let y=g.length>0?await l({cacheName:e,matchOptions:i,request:w}):null;try{await p.put(w,d)}catch(e){throw"QuotaExceededError"===e.name&&await o(),e}for(let t of g)await t.cacheDidUpdate.call(t,{cacheName:e,event:r,oldResponse:y,newResponse:d,request:w})},match:l};class d{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:a=!1}={}){return await this.transaction([e],"readonly",(i,c)=>{const o=i.objectStore(e),u=t?o.index(t):o,l=[],h=u.openCursor(n,s);h.onsuccess=(()=>{const e=h.result;e?(l.push(a?e:e.value),r&&l.length>=r?c(l):e.continue()):c(l)})})}async transaction(e,t,n){return await this.open(),await new Promise((s,r)=>{const a=this.t.transaction(e,t);a.onabort=(()=>r(a.error)),a.oncomplete=(()=>s()),n(a,e=>s(e))})}async l(e,t,n,...s){return await this.transaction([t],n,(n,r)=>{const a=n.objectStore(t),i=a[e].apply(a,s);i.onsuccess=(()=>r(i.result))})}close(){this.t&&(this.t.close(),this.t=null)}}d.prototype.OPEN_TIMEOUT=2e3;const p={readonly:["get","count","getKey","getAll","getAllKeys"],readwrite:["add","put","clear","delete"]};for(const[e,t]of Object.entries(p))for(const n of t)n in IDBObjectStore.prototype&&(d.prototype[n]=async function(t,...s){return await this.l(n,t,e,...s)});const g={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 a=u(r,"fetchDidFail"),i=a.length>0?e.clone():null;try{for(let 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})}let c=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:c,response:n}));return n}catch(e){for(const t of a)await t.fetchDidFail.call(t,{error:e,event:s,originalRequest:i.clone(),request:c.clone()});throw e}}};var y=Object.freeze({assert:null,cacheNames:i,cacheWrapper:w,DBWrapper:d,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:o,fetchWrapper:g,getFriendlyURL:c,logger:null,WorkboxError:n});const m={get googleAnalytics(){return i.getGoogleAnalyticsName()},get precache(){return i.getPrecacheName()},get prefix(){return i.getPrefix()},get runtime(){return i.getRuntimeName()},get suffix(){return i.getSuffix()}};return e._private=y,e.cacheNames=m,e.clientsClaim=(()=>{addEventListener("activate",()=>self.clients.claim())}),e.registerQuotaErrorCallback=function(e){s.add(e)},e.setCacheNameDetails=(e=>{i.updateDetails(e)}),e.skipWaiting=(()=>{addEventListener("install",()=>self.skipWaiting())}),e}({}); | ||
this.workbox = this.workbox || {}; | ||
this.workbox.expiration = (function (exports, DBWrapper_mjs, deleteDatabase_mjs, WorkboxError_mjs, assert_mjs, logger_mjs, cacheNames_mjs, getFriendlyURL_mjs, registerQuotaErrorCallback_mjs) { | ||
'use strict'; | ||
this.workbox.expiration = (function (exports, DBWrapper_js, deleteDatabase_js, WorkboxError_js, assert_js, logger_js, cacheNames_js, getFriendlyURL_js, registerQuotaErrorCallback_js) { | ||
'use strict'; | ||
try { | ||
self['workbox:expiration:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
// @ts-ignore | ||
try { | ||
self['workbox:expiration:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const DB_NAME = 'workbox-expiration'; | ||
const OBJECT_STORE_NAME = 'cache-entries'; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const DB_NAME = 'workbox-expiration'; | ||
const OBJECT_STORE_NAME = 'cache-entries'; | ||
const normalizeURL = unNormalizedUrl => { | ||
const url = new URL(unNormalizedUrl, location); | ||
url.hash = ''; | ||
return url.href; | ||
}; | ||
/** | ||
* Returns the timestamp model. | ||
* | ||
* @private | ||
*/ | ||
class CacheTimestampsModel { | ||
const normalizeURL = unNormalizedUrl => { | ||
const url = new URL(unNormalizedUrl, location.href); | ||
url.hash = ''; | ||
return url.href; | ||
}; | ||
/** | ||
* Returns the timestamp model. | ||
* | ||
* @param {string} cacheName | ||
* | ||
* @private | ||
*/ | ||
constructor(cacheName) { | ||
this._cacheName = cacheName; | ||
this._db = new DBWrapper_mjs.DBWrapper(DB_NAME, 1, { | ||
onupgradeneeded: event => this._handleUpgrade(event) | ||
}); | ||
} | ||
/** | ||
* Should perform an upgrade of indexedDB. | ||
* | ||
* @param {Event} event | ||
* | ||
* @private | ||
*/ | ||
_handleUpgrade(event) { | ||
const db = event.target.result; // TODO(philipwalton): EdgeHTML doesn't support arrays as a keyPath, so we | ||
// have to use the `id` keyPath here and create our own values (a | ||
// concatenation of `url + cacheName`) instead of simply using | ||
// `keyPath: ['url', 'cacheName']`, which is supported in other browsers. | ||
class CacheTimestampsModel { | ||
/** | ||
* | ||
* @param {string} cacheName | ||
* | ||
* @private | ||
*/ | ||
constructor(cacheName) { | ||
this._cacheName = cacheName; | ||
this._db = new DBWrapper_js.DBWrapper(DB_NAME, 1, { | ||
onupgradeneeded: event => this._handleUpgrade(event) | ||
}); | ||
} | ||
/** | ||
* Should perform an upgrade of indexedDB. | ||
* | ||
* @param {Event} event | ||
* | ||
* @private | ||
*/ | ||
const objStore = db.createObjectStore(OBJECT_STORE_NAME, { | ||
keyPath: 'id' | ||
}); // TODO(philipwalton): once we don't have to support EdgeHTML, we can | ||
// create a single index with the keyPath `['cacheName', 'timestamp']` | ||
// instead of doing both these indexes. | ||
objStore.createIndex('cacheName', 'cacheName', { | ||
unique: false | ||
}); | ||
objStore.createIndex('timestamp', 'timestamp', { | ||
unique: false | ||
}); // Previous versions of `workbox-expiration` used `this._cacheName` | ||
// as the IDBDatabase name. | ||
_handleUpgrade(event) { | ||
const db = event.target.result; // TODO(philipwalton): EdgeHTML doesn't support arrays as a keyPath, so we | ||
// have to use the `id` keyPath here and create our own values (a | ||
// concatenation of `url + cacheName`) instead of simply using | ||
// `keyPath: ['url', 'cacheName']`, which is supported in other browsers. | ||
deleteDatabase_mjs.deleteDatabase(this._cacheName); | ||
} | ||
/** | ||
* @param {string} url | ||
* @param {number} timestamp | ||
* | ||
* @private | ||
*/ | ||
const objStore = db.createObjectStore(OBJECT_STORE_NAME, { | ||
keyPath: 'id' | ||
}); // TODO(philipwalton): once we don't have to support EdgeHTML, we can | ||
// create a single index with the keyPath `['cacheName', 'timestamp']` | ||
// instead of doing both these indexes. | ||
objStore.createIndex('cacheName', 'cacheName', { | ||
unique: false | ||
}); | ||
objStore.createIndex('timestamp', 'timestamp', { | ||
unique: false | ||
}); // Previous versions of `workbox-expiration` used `this._cacheName` | ||
// as the IDBDatabase name. | ||
async setTimestamp(url, timestamp) { | ||
url = normalizeURL(url); | ||
await this._db.put(OBJECT_STORE_NAME, { | ||
url, | ||
timestamp, | ||
cacheName: this._cacheName, | ||
// Creating an ID from the URL and cache name won't be necessary once | ||
// Edge switches to Chromium and all browsers we support work with | ||
// array keyPaths. | ||
id: this._getId(url) | ||
}); | ||
} | ||
/** | ||
* Returns the timestamp stored for a given URL. | ||
* | ||
* @param {string} url | ||
* @return {number} | ||
* | ||
* @private | ||
*/ | ||
deleteDatabase_js.deleteDatabase(this._cacheName); | ||
} | ||
/** | ||
* @param {string} url | ||
* @param {number} timestamp | ||
* | ||
* @private | ||
*/ | ||
async getTimestamp(url) { | ||
const entry = await this._db.get(OBJECT_STORE_NAME, this._getId(url)); | ||
return entry.timestamp; | ||
} | ||
/** | ||
* Iterates through all the entries in the object store (from newest to | ||
* oldest) and removes entries once either `maxCount` is reached or the | ||
* entry's timestamp is less than `minTimestamp`. | ||
* | ||
* @param {number} minTimestamp | ||
* @param {number} maxCount | ||
* | ||
* @private | ||
*/ | ||
async setTimestamp(url, timestamp) { | ||
url = normalizeURL(url); | ||
const entry = { | ||
url, | ||
timestamp, | ||
cacheName: this._cacheName, | ||
// Creating an ID from the URL and cache name won't be necessary once | ||
// Edge switches to Chromium and all browsers we support work with | ||
// array keyPaths. | ||
id: this._getId(url) | ||
}; | ||
await this._db.put(OBJECT_STORE_NAME, entry); | ||
} | ||
/** | ||
* Returns the timestamp stored for a given URL. | ||
* | ||
* @param {string} url | ||
* @return {number} | ||
* | ||
* @private | ||
*/ | ||
async expireEntries(minTimestamp, maxCount) { | ||
const entriesToDelete = await this._db.transaction(OBJECT_STORE_NAME, 'readwrite', (txn, done) => { | ||
const store = txn.objectStore(OBJECT_STORE_NAME); | ||
const entriesToDelete = []; | ||
let entriesNotDeletedCount = 0; | ||
async getTimestamp(url) { | ||
const entry = await this._db.get(OBJECT_STORE_NAME, this._getId(url)); | ||
return entry.timestamp; | ||
} | ||
/** | ||
* Iterates through all the entries in the object store (from newest to | ||
* oldest) and removes entries once either `maxCount` is reached or the | ||
* entry's timestamp is less than `minTimestamp`. | ||
* | ||
* @param {number} minTimestamp | ||
* @param {number} maxCount | ||
* @return {Array<string>} | ||
* | ||
* @private | ||
*/ | ||
store.index('timestamp').openCursor(null, 'prev').onsuccess = ({ | ||
target | ||
}) => { | ||
const cursor = target.result; | ||
if (cursor) { | ||
const result = cursor.value; // TODO(philipwalton): once we can use a multi-key index, we | ||
// won't have to check `cacheName` here. | ||
async expireEntries(minTimestamp, maxCount) { | ||
const entriesToDelete = await this._db.transaction(OBJECT_STORE_NAME, 'readwrite', (txn, done) => { | ||
const store = txn.objectStore(OBJECT_STORE_NAME); | ||
const request = store.index('timestamp').openCursor(null, 'prev'); | ||
const entriesToDelete = []; | ||
let entriesNotDeletedCount = 0; | ||
if (result.cacheName === this._cacheName) { | ||
// Delete an entry if it's older than the max age or | ||
// if we already have the max number allowed. | ||
if (minTimestamp && result.timestamp < minTimestamp || maxCount && entriesNotDeletedCount >= maxCount) { | ||
// TODO(philipwalton): we should be able to delete the | ||
// entry right here, but doing so causes an iteration | ||
// bug in Safari stable (fixed in TP). Instead we can | ||
// store the keys of the entries to delete, and then | ||
// delete the separate transactions. | ||
// https://github.com/GoogleChrome/workbox/issues/1978 | ||
// cursor.delete(); | ||
// We only need to return the URL, not the whole entry. | ||
entriesToDelete.push(cursor.value); | ||
} else { | ||
entriesNotDeletedCount++; | ||
request.onsuccess = () => { | ||
const cursor = request.result; | ||
if (cursor) { | ||
const result = cursor.value; // TODO(philipwalton): once we can use a multi-key index, we | ||
// won't have to check `cacheName` here. | ||
if (result.cacheName === this._cacheName) { | ||
// Delete an entry if it's older than the max age or | ||
// if we already have the max number allowed. | ||
if (minTimestamp && result.timestamp < minTimestamp || maxCount && entriesNotDeletedCount >= maxCount) { | ||
// TODO(philipwalton): we should be able to delete the | ||
// entry right here, but doing so causes an iteration | ||
// bug in Safari stable (fixed in TP). Instead we can | ||
// store the keys of the entries to delete, and then | ||
// delete the separate transactions. | ||
// https://github.com/GoogleChrome/workbox/issues/1978 | ||
// cursor.delete(); | ||
// We only need to return the URL, not the whole entry. | ||
entriesToDelete.push(cursor.value); | ||
} else { | ||
entriesNotDeletedCount++; | ||
} | ||
} | ||
cursor.continue(); | ||
} else { | ||
done(entriesToDelete); | ||
} | ||
}; | ||
}); // TODO(philipwalton): once the Safari bug in the following issue is fixed, | ||
// we should be able to remove this loop and do the entry deletion in the | ||
// cursor loop above: | ||
// https://github.com/GoogleChrome/workbox/issues/1978 | ||
cursor.continue(); | ||
} else { | ||
done(entriesToDelete); | ||
} | ||
}; | ||
}); // TODO(philipwalton): once the Safari bug in the following issue is fixed, | ||
// we should be able to remove this loop and do the entry deletion in the | ||
// cursor loop above: | ||
// https://github.com/GoogleChrome/workbox/issues/1978 | ||
const urlsDeleted = []; | ||
const urlsDeleted = []; | ||
for (const entry of entriesToDelete) { | ||
await this._db.delete(OBJECT_STORE_NAME, entry.id); | ||
urlsDeleted.push(entry.url); | ||
} | ||
for (const entry of entriesToDelete) { | ||
await this._db.delete(OBJECT_STORE_NAME, entry.id); | ||
urlsDeleted.push(entry.url); | ||
return urlsDeleted; | ||
} | ||
/** | ||
* Takes a URL and returns an ID that will be unique in the object store. | ||
* | ||
* @param {string} url | ||
* @return {string} | ||
* | ||
* @private | ||
*/ | ||
return urlsDeleted; | ||
} | ||
/** | ||
* Takes a URL and returns an ID that will be unique in the object store. | ||
* | ||
* @param {string} url | ||
* @return {string} | ||
* | ||
* @private | ||
*/ | ||
_getId(url) { | ||
// Creating an ID from the URL and cache name won't be necessary once | ||
// Edge switches to Chromium and all browsers we support work with | ||
// array keyPaths. | ||
return this._cacheName + '|' + normalizeURL(url); | ||
} | ||
_getId(url) { | ||
// Creating an ID from the URL and cache name won't be necessary once | ||
// Edge switches to Chromium and all browsers we support work with | ||
// array keyPaths. | ||
return this._cacheName + '|' + normalizeURL(url); | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* The `CacheExpiration` class allows you define an expiration and / or | ||
* limit on the number of responses stored in a | ||
* [`Cache`](https://developer.mozilla.org/en-US/docs/Web/API/Cache). | ||
* | ||
* @memberof workbox.expiration | ||
*/ | ||
class CacheExpiration { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* To construct a new CacheExpiration instance you must provide at least | ||
* one of the `config` properties. | ||
* The `CacheExpiration` class allows you define an expiration and / or | ||
* limit on the number of responses stored in a | ||
* [`Cache`](https://developer.mozilla.org/en-US/docs/Web/API/Cache). | ||
* | ||
* @param {string} cacheName Name of the cache to apply restrictions to. | ||
* @param {Object} config | ||
* @param {number} [config.maxEntries] The maximum number of entries to cache. | ||
* Entries used the least will be removed as the maximum is reached. | ||
* @param {number} [config.maxAgeSeconds] The maximum age of an entry before | ||
* it's treated as stale and removed. | ||
* @memberof workbox.expiration | ||
*/ | ||
constructor(cacheName, config = {}) { | ||
{ | ||
assert_mjs.assert.isType(cacheName, 'string', { | ||
moduleName: 'workbox-expiration', | ||
className: 'CacheExpiration', | ||
funcName: 'constructor', | ||
paramName: 'cacheName' | ||
}); | ||
if (!(config.maxEntries || config.maxAgeSeconds)) { | ||
throw new WorkboxError_mjs.WorkboxError('max-entries-or-age-required', { | ||
moduleName: 'workbox-expiration', | ||
className: 'CacheExpiration', | ||
funcName: 'constructor' | ||
}); | ||
} | ||
class CacheExpiration { | ||
/** | ||
* To construct a new CacheExpiration instance you must provide at least | ||
* one of the `config` properties. | ||
* | ||
* @param {string} cacheName Name of the cache to apply restrictions to. | ||
* @param {Object} config | ||
* @param {number} [config.maxEntries] The maximum number of entries to cache. | ||
* Entries used the least will be removed as the maximum is reached. | ||
* @param {number} [config.maxAgeSeconds] The maximum age of an entry before | ||
* it's treated as stale and removed. | ||
*/ | ||
constructor(cacheName, config = {}) { | ||
this._isRunning = false; | ||
this._rerunRequested = false; | ||
if (config.maxEntries) { | ||
assert_mjs.assert.isType(config.maxEntries, 'number', { | ||
{ | ||
assert_js.assert.isType(cacheName, 'string', { | ||
moduleName: 'workbox-expiration', | ||
className: 'CacheExpiration', | ||
funcName: 'constructor', | ||
paramName: 'config.maxEntries' | ||
}); // TODO: Assert is positive | ||
} | ||
paramName: 'cacheName' | ||
}); | ||
if (config.maxAgeSeconds) { | ||
assert_mjs.assert.isType(config.maxAgeSeconds, 'number', { | ||
moduleName: 'workbox-expiration', | ||
className: 'CacheExpiration', | ||
funcName: 'constructor', | ||
paramName: 'config.maxAgeSeconds' | ||
}); // TODO: Assert is positive | ||
} | ||
} | ||
if (!(config.maxEntries || config.maxAgeSeconds)) { | ||
throw new WorkboxError_js.WorkboxError('max-entries-or-age-required', { | ||
moduleName: 'workbox-expiration', | ||
className: 'CacheExpiration', | ||
funcName: 'constructor' | ||
}); | ||
} | ||
this._isRunning = false; | ||
this._rerunRequested = false; | ||
this._maxEntries = config.maxEntries; | ||
this._maxAgeSeconds = config.maxAgeSeconds; | ||
this._cacheName = cacheName; | ||
this._timestampModel = new CacheTimestampsModel(cacheName); | ||
} | ||
/** | ||
* Expires entries for the given cache and given criteria. | ||
*/ | ||
if (config.maxEntries) { | ||
assert_js.assert.isType(config.maxEntries, 'number', { | ||
moduleName: 'workbox-expiration', | ||
className: 'CacheExpiration', | ||
funcName: 'constructor', | ||
paramName: 'config.maxEntries' | ||
}); // TODO: Assert is positive | ||
} | ||
if (config.maxAgeSeconds) { | ||
assert_js.assert.isType(config.maxAgeSeconds, 'number', { | ||
moduleName: 'workbox-expiration', | ||
className: 'CacheExpiration', | ||
funcName: 'constructor', | ||
paramName: 'config.maxAgeSeconds' | ||
}); // TODO: Assert is positive | ||
} | ||
} | ||
async expireEntries() { | ||
if (this._isRunning) { | ||
this._rerunRequested = true; | ||
return; | ||
this._maxEntries = config.maxEntries; | ||
this._maxAgeSeconds = config.maxAgeSeconds; | ||
this._cacheName = cacheName; | ||
this._timestampModel = new CacheTimestampsModel(cacheName); | ||
} | ||
/** | ||
* Expires entries for the given cache and given criteria. | ||
*/ | ||
this._isRunning = true; | ||
const minTimestamp = this._maxAgeSeconds ? Date.now() - this._maxAgeSeconds * 1000 : undefined; | ||
const urlsExpired = await this._timestampModel.expireEntries(minTimestamp, this._maxEntries); // Delete URLs from the cache | ||
const cache = await caches.open(this._cacheName); | ||
for (const url of urlsExpired) { | ||
await cache.delete(url); | ||
} | ||
{ | ||
if (urlsExpired.length > 0) { | ||
logger_mjs.logger.groupCollapsed(`Expired ${urlsExpired.length} ` + `${urlsExpired.length === 1 ? 'entry' : 'entries'} and removed ` + `${urlsExpired.length === 1 ? 'it' : 'them'} from the ` + `'${this._cacheName}' cache.`); | ||
logger_mjs.logger.log(`Expired the following ${urlsExpired.length === 1 ? 'URL' : 'URLs'}:`); | ||
urlsExpired.forEach(url => logger_mjs.logger.log(` ${url}`)); | ||
logger_mjs.logger.groupEnd(); | ||
} else { | ||
logger_mjs.logger.debug(`Cache expiration ran and found no entries to remove.`); | ||
async expireEntries() { | ||
if (this._isRunning) { | ||
this._rerunRequested = true; | ||
return; | ||
} | ||
} | ||
this._isRunning = false; | ||
this._isRunning = true; | ||
const minTimestamp = this._maxAgeSeconds ? Date.now() - this._maxAgeSeconds * 1000 : 0; | ||
const urlsExpired = await this._timestampModel.expireEntries(minTimestamp, this._maxEntries); // Delete URLs from the cache | ||
if (this._rerunRequested) { | ||
this._rerunRequested = false; | ||
this.expireEntries(); | ||
} | ||
} | ||
/** | ||
* Update the timestamp for the given URL. This ensures the when | ||
* removing entries based on maximum entries, most recently used | ||
* is accurate or when expiring, the timestamp is up-to-date. | ||
* | ||
* @param {string} url | ||
*/ | ||
const cache = await caches.open(this._cacheName); | ||
for (const url of urlsExpired) { | ||
await cache.delete(url); | ||
} | ||
async updateTimestamp(url) { | ||
{ | ||
assert_mjs.assert.isType(url, 'string', { | ||
moduleName: 'workbox-expiration', | ||
className: 'CacheExpiration', | ||
funcName: 'updateTimestamp', | ||
paramName: 'url' | ||
}); | ||
} | ||
{ | ||
if (urlsExpired.length > 0) { | ||
logger_js.logger.groupCollapsed(`Expired ${urlsExpired.length} ` + `${urlsExpired.length === 1 ? 'entry' : 'entries'} and removed ` + `${urlsExpired.length === 1 ? 'it' : 'them'} from the ` + `'${this._cacheName}' cache.`); | ||
logger_js.logger.log(`Expired the following ${urlsExpired.length === 1 ? 'URL' : 'URLs'}:`); | ||
urlsExpired.forEach(url => logger_js.logger.log(` ${url}`)); | ||
logger_js.logger.groupEnd(); | ||
} else { | ||
logger_js.logger.debug(`Cache expiration ran and found no entries to remove.`); | ||
} | ||
} | ||
await this._timestampModel.setTimestamp(url, Date.now()); | ||
} | ||
/** | ||
* Can be used to check if a URL has expired or not before it's used. | ||
* | ||
* This requires a look up from IndexedDB, so can be slow. | ||
* | ||
* Note: This method will not remove the cached entry, call | ||
* `expireEntries()` to remove indexedDB and Cache entries. | ||
* | ||
* @param {string} url | ||
* @return {boolean} | ||
*/ | ||
this._isRunning = false; | ||
async isURLExpired(url) { | ||
{ | ||
if (!this._maxAgeSeconds) { | ||
throw new WorkboxError_mjs.WorkboxError(`expired-test-without-max-age`, { | ||
methodName: 'isURLExpired', | ||
paramName: 'maxAgeSeconds' | ||
}); | ||
if (this._rerunRequested) { | ||
this._rerunRequested = false; | ||
this.expireEntries(); | ||
} | ||
} | ||
/** | ||
* Update the timestamp for the given URL. This ensures the when | ||
* removing entries based on maximum entries, most recently used | ||
* is accurate or when expiring, the timestamp is up-to-date. | ||
* | ||
* @param {string} url | ||
*/ | ||
const timestamp = await this._timestampModel.getTimestamp(url); | ||
const expireOlderThan = Date.now() - this._maxAgeSeconds * 1000; | ||
return timestamp < expireOlderThan; | ||
} | ||
/** | ||
* Removes the IndexedDB object store used to keep track of cache expiration | ||
* metadata. | ||
*/ | ||
async delete() { | ||
// Make sure we don't attempt another rerun if we're called in the middle of | ||
// a cache expiration. | ||
this._rerunRequested = false; | ||
await this._timestampModel.expireEntries(Infinity); // Expires all. | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* This plugin can be used in the Workbox APIs to regularly enforce a | ||
* limit on the age and / or the number of cached requests. | ||
* | ||
* Whenever a cached request is used or updated, this plugin will look | ||
* at the used Cache and remove any old or extra requests. | ||
* | ||
* When using `maxAgeSeconds`, requests may be used *once* after expiring | ||
* because the expiration clean up will not have occurred until *after* the | ||
* cached request has been used. If the request has a "Date" header, then | ||
* a light weight expiration check is performed and the request will not be | ||
* used immediately. | ||
* | ||
* When using `maxEntries`, the entry least-recently requested will be removed from the cache first. | ||
* | ||
* @memberof workbox.expiration | ||
*/ | ||
class Plugin { | ||
/** | ||
* @param {Object} config | ||
* @param {number} [config.maxEntries] The maximum number of entries to cache. | ||
* Entries used the least will be removed as the maximum is reached. | ||
* @param {number} [config.maxAgeSeconds] The maximum age of an entry before | ||
* it's treated as stale and removed. | ||
* @param {boolean} [config.purgeOnQuotaError] Whether to opt this cache in to | ||
* automatic deletion if the available storage quota has been exceeded. | ||
*/ | ||
constructor(config = {}) { | ||
{ | ||
if (!(config.maxEntries || config.maxAgeSeconds)) { | ||
throw new WorkboxError_mjs.WorkboxError('max-entries-or-age-required', { | ||
async updateTimestamp(url) { | ||
{ | ||
assert_js.assert.isType(url, 'string', { | ||
moduleName: 'workbox-expiration', | ||
className: 'Plugin', | ||
funcName: 'constructor' | ||
className: 'CacheExpiration', | ||
funcName: 'updateTimestamp', | ||
paramName: 'url' | ||
}); | ||
} | ||
if (config.maxEntries) { | ||
assert_mjs.assert.isType(config.maxEntries, 'number', { | ||
moduleName: 'workbox-expiration', | ||
className: 'Plugin', | ||
funcName: 'constructor', | ||
paramName: 'config.maxEntries' | ||
}); | ||
} | ||
await this._timestampModel.setTimestamp(url, Date.now()); | ||
} | ||
/** | ||
* Can be used to check if a URL has expired or not before it's used. | ||
* | ||
* This requires a look up from IndexedDB, so can be slow. | ||
* | ||
* Note: This method will not remove the cached entry, call | ||
* `expireEntries()` to remove indexedDB and Cache entries. | ||
* | ||
* @param {string} url | ||
* @return {boolean} | ||
*/ | ||
if (config.maxAgeSeconds) { | ||
assert_mjs.assert.isType(config.maxAgeSeconds, 'number', { | ||
moduleName: 'workbox-expiration', | ||
className: 'Plugin', | ||
funcName: 'constructor', | ||
paramName: 'config.maxAgeSeconds' | ||
}); | ||
async isURLExpired(url) { | ||
if (!this._maxAgeSeconds) { | ||
{ | ||
throw new WorkboxError_js.WorkboxError(`expired-test-without-max-age`, { | ||
methodName: 'isURLExpired', | ||
paramName: 'maxAgeSeconds' | ||
}); | ||
} | ||
return false; | ||
} else { | ||
const timestamp = await this._timestampModel.getTimestamp(url); | ||
const expireOlderThan = Date.now() - this._maxAgeSeconds * 1000; | ||
return timestamp < expireOlderThan; | ||
} | ||
} | ||
/** | ||
* Removes the IndexedDB object store used to keep track of cache expiration | ||
* metadata. | ||
*/ | ||
this._config = config; | ||
this._maxAgeSeconds = config.maxAgeSeconds; | ||
this._cacheExpirations = new Map(); | ||
if (config.purgeOnQuotaError) { | ||
registerQuotaErrorCallback_mjs.registerQuotaErrorCallback(() => this.deleteCacheAndMetadata()); | ||
async delete() { | ||
// Make sure we don't attempt another rerun if we're called in the middle of | ||
// a cache expiration. | ||
this._rerunRequested = false; | ||
await this._timestampModel.expireEntries(Infinity); // Expires all. | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* A simple helper method to return a CacheExpiration instance for a given | ||
* cache name. | ||
* This plugin can be used in the Workbox APIs to regularly enforce a | ||
* limit on the age and / or the number of cached requests. | ||
* | ||
* @param {string} cacheName | ||
* @return {CacheExpiration} | ||
* Whenever a cached request is used or updated, this plugin will look | ||
* at the used Cache and remove any old or extra requests. | ||
* | ||
* @private | ||
* When using `maxAgeSeconds`, requests may be used *once* after expiring | ||
* because the expiration clean up will not have occurred until *after* the | ||
* cached request has been used. If the request has a "Date" header, then | ||
* a light weight expiration check is performed and the request will not be | ||
* used immediately. | ||
* | ||
* When using `maxEntries`, the entry least-recently requested will be removed | ||
* from the cache first. | ||
* | ||
* @memberof workbox.expiration | ||
*/ | ||
class Plugin { | ||
/** | ||
* @param {Object} config | ||
* @param {number} [config.maxEntries] The maximum number of entries to cache. | ||
* Entries used the least will be removed as the maximum is reached. | ||
* @param {number} [config.maxAgeSeconds] The maximum age of an entry before | ||
* it's treated as stale and removed. | ||
* @param {boolean} [config.purgeOnQuotaError] Whether to opt this cache in to | ||
* automatic deletion if the available storage quota has been exceeded. | ||
*/ | ||
constructor(config = {}) { | ||
/** | ||
* A "lifecycle" callback that will be triggered automatically by the | ||
* `workbox.strategies` handlers when a `Response` is about to be returned | ||
* from a [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache) to | ||
* the handler. It allows the `Response` to be inspected for freshness and | ||
* prevents it from being used if the `Response`'s `Date` header value is | ||
* older than the configured `maxAgeSeconds`. | ||
* | ||
* @param {Object} options | ||
* @param {string} options.cacheName Name of the cache the response is in. | ||
* @param {Response} options.cachedResponse The `Response` object that's been | ||
* read from a cache and whose freshness should be checked. | ||
* @return {Response} Either the `cachedResponse`, if it's | ||
* fresh, or `null` if the `Response` is older than `maxAgeSeconds`. | ||
* | ||
* @private | ||
*/ | ||
this.cachedResponseWillBeUsed = async ({ | ||
event, | ||
request, | ||
cacheName, | ||
cachedResponse | ||
}) => { | ||
if (!cachedResponse) { | ||
return null; | ||
} | ||
_getCacheExpiration(cacheName) { | ||
if (cacheName === cacheNames_mjs.cacheNames.getRuntimeName()) { | ||
throw new WorkboxError_mjs.WorkboxError('expire-custom-caches-only'); | ||
} | ||
let isFresh = this._isResponseDateFresh(cachedResponse); // Expire entries to ensure that even if the expiration date has | ||
// expired, it'll only be used once. | ||
let cacheExpiration = this._cacheExpirations.get(cacheName); | ||
if (!cacheExpiration) { | ||
cacheExpiration = new CacheExpiration(cacheName, this._config); | ||
const cacheExpiration = this._getCacheExpiration(cacheName); | ||
this._cacheExpirations.set(cacheName, cacheExpiration); | ||
} | ||
cacheExpiration.expireEntries(); // Update the metadata for the request URL to the current timestamp, | ||
// but don't `await` it as we don't want to block the response. | ||
return cacheExpiration; | ||
} | ||
/** | ||
* A "lifecycle" callback that will be triggered automatically by the | ||
* `workbox.strategies` handlers when a `Response` is about to be returned | ||
* from a [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache) to | ||
* the handler. It allows the `Response` to be inspected for freshness and | ||
* prevents it from being used if the `Response`'s `Date` header value is | ||
* older than the configured `maxAgeSeconds`. | ||
* | ||
* @param {Object} options | ||
* @param {string} options.cacheName Name of the cache the response is in. | ||
* @param {Response} options.cachedResponse The `Response` object that's been | ||
* read from a cache and whose freshness should be checked. | ||
* @return {Response} Either the `cachedResponse`, if it's | ||
* fresh, or `null` if the `Response` is older than `maxAgeSeconds`. | ||
* | ||
* @private | ||
*/ | ||
const updateTimestampDone = cacheExpiration.updateTimestamp(request.url); | ||
if (event) { | ||
try { | ||
event.waitUntil(updateTimestampDone); | ||
} catch (error) { | ||
{ | ||
// The event may not be a fetch event; only log the URL if it is. | ||
if ('request' in event) { | ||
logger_js.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache entry for ` + `'${getFriendlyURL_js.getFriendlyURL(event.request.url)}'.`); | ||
} | ||
} | ||
} | ||
} | ||
cachedResponseWillBeUsed({ | ||
event, | ||
request, | ||
cacheName, | ||
cachedResponse | ||
}) { | ||
if (!cachedResponse) { | ||
return null; | ||
} | ||
return isFresh ? cachedResponse : null; | ||
}; | ||
/** | ||
* A "lifecycle" callback that will be triggered automatically by the | ||
* `workbox.strategies` handlers when an entry is added to a cache. | ||
* | ||
* @param {Object} options | ||
* @param {string} options.cacheName Name of the cache that was updated. | ||
* @param {string} options.request The Request for the cached entry. | ||
* | ||
* @private | ||
*/ | ||
let isFresh = this._isResponseDateFresh(cachedResponse); // Expire entries to ensure that even if the expiration date has | ||
// expired, it'll only be used once. | ||
this.cacheDidUpdate = async ({ | ||
cacheName, | ||
request | ||
}) => { | ||
{ | ||
assert_js.assert.isType(cacheName, 'string', { | ||
moduleName: 'workbox-expiration', | ||
className: 'Plugin', | ||
funcName: 'cacheDidUpdate', | ||
paramName: 'cacheName' | ||
}); | ||
assert_js.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-expiration', | ||
className: 'Plugin', | ||
funcName: 'cacheDidUpdate', | ||
paramName: 'request' | ||
}); | ||
} | ||
const cacheExpiration = this._getCacheExpiration(cacheName); | ||
const cacheExpiration = this._getCacheExpiration(cacheName); | ||
cacheExpiration.expireEntries(); // Update the metadata for the request URL to the current timestamp, | ||
// but don't `await` it as we don't want to block the response. | ||
await cacheExpiration.updateTimestamp(request.url); | ||
await cacheExpiration.expireEntries(); | ||
}; | ||
const updateTimestampDone = cacheExpiration.updateTimestamp(request.url); | ||
{ | ||
if (!(config.maxEntries || config.maxAgeSeconds)) { | ||
throw new WorkboxError_js.WorkboxError('max-entries-or-age-required', { | ||
moduleName: 'workbox-expiration', | ||
className: 'Plugin', | ||
funcName: 'constructor' | ||
}); | ||
} | ||
if (event) { | ||
try { | ||
event.waitUntil(updateTimestampDone); | ||
} catch (error) { | ||
{ | ||
logger_mjs.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache entry for '${getFriendlyURL_mjs.getFriendlyURL(event.request.url)}'.`); | ||
if (config.maxEntries) { | ||
assert_js.assert.isType(config.maxEntries, 'number', { | ||
moduleName: 'workbox-expiration', | ||
className: 'Plugin', | ||
funcName: 'constructor', | ||
paramName: 'config.maxEntries' | ||
}); | ||
} | ||
if (config.maxAgeSeconds) { | ||
assert_js.assert.isType(config.maxAgeSeconds, 'number', { | ||
moduleName: 'workbox-expiration', | ||
className: 'Plugin', | ||
funcName: 'constructor', | ||
paramName: 'config.maxAgeSeconds' | ||
}); | ||
} | ||
} | ||
} | ||
return isFresh ? cachedResponse : null; | ||
} | ||
/** | ||
* @param {Response} cachedResponse | ||
* @return {boolean} | ||
* | ||
* @private | ||
*/ | ||
this._config = config; | ||
this._maxAgeSeconds = config.maxAgeSeconds; | ||
this._cacheExpirations = new Map(); | ||
if (config.purgeOnQuotaError) { | ||
registerQuotaErrorCallback_js.registerQuotaErrorCallback(() => this.deleteCacheAndMetadata()); | ||
} | ||
} | ||
/** | ||
* A simple helper method to return a CacheExpiration instance for a given | ||
* cache name. | ||
* | ||
* @param {string} cacheName | ||
* @return {CacheExpiration} | ||
* | ||
* @private | ||
*/ | ||
_isResponseDateFresh(cachedResponse) { | ||
if (!this._maxAgeSeconds) { | ||
// We aren't expiring by age, so return true, it's fresh | ||
return true; | ||
} // Check if the 'date' header will suffice a quick expiration check. | ||
// See https://github.com/GoogleChromeLabs/sw-toolbox/issues/164 for | ||
// discussion. | ||
_getCacheExpiration(cacheName) { | ||
if (cacheName === cacheNames_js.cacheNames.getRuntimeName()) { | ||
throw new WorkboxError_js.WorkboxError('expire-custom-caches-only'); | ||
} | ||
const dateHeaderTimestamp = this._getDateHeaderTimestamp(cachedResponse); | ||
let cacheExpiration = this._cacheExpirations.get(cacheName); | ||
if (dateHeaderTimestamp === null) { | ||
// Unable to parse date, so assume it's fresh. | ||
return true; | ||
} // If we have a valid headerTime, then our response is fresh iff the | ||
// headerTime plus maxAgeSeconds is greater than the current time. | ||
if (!cacheExpiration) { | ||
cacheExpiration = new CacheExpiration(cacheName, this._config); | ||
this._cacheExpirations.set(cacheName, cacheExpiration); | ||
} | ||
const now = Date.now(); | ||
return dateHeaderTimestamp >= now - this._maxAgeSeconds * 1000; | ||
} | ||
/** | ||
* This method will extract the data header and parse it into a useful | ||
* value. | ||
* | ||
* @param {Response} cachedResponse | ||
* @return {number} | ||
* | ||
* @private | ||
*/ | ||
return cacheExpiration; | ||
} | ||
/** | ||
* @param {Response} cachedResponse | ||
* @return {boolean} | ||
* | ||
* @private | ||
*/ | ||
_getDateHeaderTimestamp(cachedResponse) { | ||
if (!cachedResponse.headers.has('date')) { | ||
return null; | ||
} | ||
_isResponseDateFresh(cachedResponse) { | ||
if (!this._maxAgeSeconds) { | ||
// We aren't expiring by age, so return true, it's fresh | ||
return true; | ||
} // Check if the 'date' header will suffice a quick expiration check. | ||
// See https://github.com/GoogleChromeLabs/sw-toolbox/issues/164 for | ||
// discussion. | ||
const dateHeader = cachedResponse.headers.get('date'); | ||
const parsedDate = new Date(dateHeader); | ||
const headerTime = parsedDate.getTime(); // If the Date header was invalid for some reason, parsedDate.getTime() | ||
// will return NaN. | ||
if (isNaN(headerTime)) { | ||
return null; | ||
} | ||
const dateHeaderTimestamp = this._getDateHeaderTimestamp(cachedResponse); | ||
return headerTime; | ||
} | ||
/** | ||
* A "lifecycle" callback that will be triggered automatically by the | ||
* `workbox.strategies` handlers when an entry is added to a cache. | ||
* | ||
* @param {Object} options | ||
* @param {string} options.cacheName Name of the cache that was updated. | ||
* @param {string} options.request The Request for the cached entry. | ||
* | ||
* @private | ||
*/ | ||
if (dateHeaderTimestamp === null) { | ||
// Unable to parse date, so assume it's fresh. | ||
return true; | ||
} // If we have a valid headerTime, then our response is fresh iff the | ||
// headerTime plus maxAgeSeconds is greater than the current time. | ||
async cacheDidUpdate({ | ||
cacheName, | ||
request | ||
}) { | ||
{ | ||
assert_mjs.assert.isType(cacheName, 'string', { | ||
moduleName: 'workbox-expiration', | ||
className: 'Plugin', | ||
funcName: 'cacheDidUpdate', | ||
paramName: 'cacheName' | ||
}); | ||
assert_mjs.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-expiration', | ||
className: 'Plugin', | ||
funcName: 'cacheDidUpdate', | ||
paramName: 'request' | ||
}); | ||
const now = Date.now(); | ||
return dateHeaderTimestamp >= now - this._maxAgeSeconds * 1000; | ||
} | ||
/** | ||
* This method will extract the data header and parse it into a useful | ||
* value. | ||
* | ||
* @param {Response} cachedResponse | ||
* @return {number|null} | ||
* | ||
* @private | ||
*/ | ||
const cacheExpiration = this._getCacheExpiration(cacheName); | ||
await cacheExpiration.updateTimestamp(request.url); | ||
await cacheExpiration.expireEntries(); | ||
} | ||
/** | ||
* This is a helper method that performs two operations: | ||
* | ||
* - Deletes *all* the underlying Cache instances associated with this plugin | ||
* instance, by calling caches.delete() on your behalf. | ||
* - Deletes the metadata from IndexedDB used to keep track of expiration | ||
* details for each Cache instance. | ||
* | ||
* When using cache expiration, calling this method is preferable to calling | ||
* `caches.delete()` directly, since this will ensure that the IndexedDB | ||
* metadata is also cleanly removed and open IndexedDB instances are deleted. | ||
* | ||
* Note that if you're *not* using cache expiration for a given cache, calling | ||
* `caches.delete()` and passing in the cache's name should be sufficient. | ||
* There is no Workbox-specific method needed for cleanup in that case. | ||
*/ | ||
_getDateHeaderTimestamp(cachedResponse) { | ||
if (!cachedResponse.headers.has('date')) { | ||
return null; | ||
} | ||
const dateHeader = cachedResponse.headers.get('date'); | ||
const parsedDate = new Date(dateHeader); | ||
const headerTime = parsedDate.getTime(); // If the Date header was invalid for some reason, parsedDate.getTime() | ||
// will return NaN. | ||
async deleteCacheAndMetadata() { | ||
// Do this one at a time instead of all at once via `Promise.all()` to | ||
// reduce the chance of inconsistency if a promise rejects. | ||
for (const [cacheName, cacheExpiration] of this._cacheExpirations) { | ||
await caches.delete(cacheName); | ||
await cacheExpiration.delete(); | ||
} // Reset this._cacheExpirations to its initial state. | ||
if (isNaN(headerTime)) { | ||
return null; | ||
} | ||
return headerTime; | ||
} | ||
/** | ||
* This is a helper method that performs two operations: | ||
* | ||
* - Deletes *all* the underlying Cache instances associated with this plugin | ||
* instance, by calling caches.delete() on your behalf. | ||
* - Deletes the metadata from IndexedDB used to keep track of expiration | ||
* details for each Cache instance. | ||
* | ||
* When using cache expiration, calling this method is preferable to calling | ||
* `caches.delete()` directly, since this will ensure that the IndexedDB | ||
* metadata is also cleanly removed and open IndexedDB instances are deleted. | ||
* | ||
* Note that if you're *not* using cache expiration for a given cache, calling | ||
* `caches.delete()` and passing in the cache's name should be sufficient. | ||
* There is no Workbox-specific method needed for cleanup in that case. | ||
*/ | ||
this._cacheExpirations = new Map(); | ||
} | ||
} | ||
async deleteCacheAndMetadata() { | ||
// Do this one at a time instead of all at once via `Promise.all()` to | ||
// reduce the chance of inconsistency if a promise rejects. | ||
for (const [cacheName, cacheExpiration] of this._cacheExpirations) { | ||
await caches.delete(cacheName); | ||
await cacheExpiration.delete(); | ||
} // Reset this._cacheExpirations to its initial state. | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
this._cacheExpirations = new Map(); | ||
} | ||
exports.CacheExpiration = CacheExpiration; | ||
exports.Plugin = Plugin; | ||
} | ||
return exports; | ||
exports.CacheExpiration = CacheExpiration; | ||
exports.Plugin = Plugin; | ||
return exports; | ||
}({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core)); | ||
@@ -1,2 +0,2 @@ | ||
this.workbox=this.workbox||{},this.workbox.expiration=function(t,e,s,i,a,n){"use strict";try{self["workbox:expiration:4.3.1"]&&_()}catch(t){}const h="workbox-expiration",c="cache-entries",r=t=>{const e=new URL(t,location);return e.hash="",e.href};class o{constructor(t){this.t=t,this.s=new e.DBWrapper(h,1,{onupgradeneeded:t=>this.i(t)})}i(t){const e=t.target.result.createObjectStore(c,{keyPath:"id"});e.createIndex("cacheName","cacheName",{unique:!1}),e.createIndex("timestamp","timestamp",{unique:!1}),s.deleteDatabase(this.t)}async setTimestamp(t,e){t=r(t),await this.s.put(c,{url:t,timestamp:e,cacheName:this.t,id:this.h(t)})}async getTimestamp(t){return(await this.s.get(c,this.h(t))).timestamp}async expireEntries(t,e){const s=await this.s.transaction(c,"readwrite",(s,i)=>{const a=s.objectStore(c),n=[];let h=0;a.index("timestamp").openCursor(null,"prev").onsuccess=(({target:s})=>{const a=s.result;if(a){const s=a.value;s.cacheName===this.t&&(t&&s.timestamp<t||e&&h>=e?n.push(a.value):h++),a.continue()}else i(n)})}),i=[];for(const t of s)await this.s.delete(c,t.id),i.push(t.url);return i}h(t){return this.t+"|"+r(t)}}class u{constructor(t,e={}){this.o=!1,this.u=!1,this.l=e.maxEntries,this.p=e.maxAgeSeconds,this.t=t,this.m=new o(t)}async expireEntries(){if(this.o)return void(this.u=!0);this.o=!0;const t=this.p?Date.now()-1e3*this.p:void 0,e=await this.m.expireEntries(t,this.l),s=await caches.open(this.t);for(const t of e)await s.delete(t);this.o=!1,this.u&&(this.u=!1,this.expireEntries())}async updateTimestamp(t){await this.m.setTimestamp(t,Date.now())}async isURLExpired(t){return await this.m.getTimestamp(t)<Date.now()-1e3*this.p}async delete(){this.u=!1,await this.m.expireEntries(1/0)}}return t.CacheExpiration=u,t.Plugin=class{constructor(t={}){this.D=t,this.p=t.maxAgeSeconds,this.g=new Map,t.purgeOnQuotaError&&n.registerQuotaErrorCallback(()=>this.deleteCacheAndMetadata())}k(t){if(t===a.cacheNames.getRuntimeName())throw new i.WorkboxError("expire-custom-caches-only");let e=this.g.get(t);return e||(e=new u(t,this.D),this.g.set(t,e)),e}cachedResponseWillBeUsed({event:t,request:e,cacheName:s,cachedResponse:i}){if(!i)return null;let a=this.N(i);const n=this.k(s);n.expireEntries();const h=n.updateTimestamp(e.url);if(t)try{t.waitUntil(h)}catch(t){}return a?i:null}N(t){if(!this.p)return!0;const e=this._(t);return null===e||e>=Date.now()-1e3*this.p}_(t){if(!t.headers.has("date"))return null;const e=t.headers.get("date"),s=new Date(e).getTime();return isNaN(s)?null:s}async cacheDidUpdate({cacheName:t,request:e}){const s=this.k(t);await s.updateTimestamp(e.url),await s.expireEntries()}async deleteCacheAndMetadata(){for(const[t,e]of this.g)await caches.delete(t),await e.delete();this.g=new Map}},t}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core); | ||
this.workbox=this.workbox||{},this.workbox.expiration=function(t,s,e,i,a,n){"use strict";try{self["workbox:expiration:5.0.0-alpha.2"]&&_()}catch(t){}const h="workbox-expiration",r="cache-entries",c=t=>{const s=new URL(t,location.href);return s.hash="",s.href};class o{constructor(t){this.t=t,this.s=new s.DBWrapper(h,1,{onupgradeneeded:t=>this.i(t)})}i(t){const s=t.target.result.createObjectStore(r,{keyPath:"id"});s.createIndex("cacheName","cacheName",{unique:!1}),s.createIndex("timestamp","timestamp",{unique:!1}),e.deleteDatabase(this.t)}async setTimestamp(t,s){const e={url:t=c(t),timestamp:s,cacheName:this.t,id:this.h(t)};await this.s.put(r,e)}async getTimestamp(t){return(await this.s.get(r,this.h(t))).timestamp}async expireEntries(t,s){const e=await this.s.transaction(r,"readwrite",(e,i)=>{const a=e.objectStore(r).index("timestamp").openCursor(null,"prev"),n=[];let h=0;a.onsuccess=(()=>{const e=a.result;if(e){const i=e.value;i.cacheName===this.t&&(t&&i.timestamp<t||s&&h>=s?n.push(e.value):h++),e.continue()}else i(n)})}),i=[];for(const t of e)await this.s.delete(r,t.id),i.push(t.url);return i}h(t){return this.t+"|"+c(t)}}class u{constructor(t,s={}){this.o=!1,this.u=!1,this.l=s.maxEntries,this.m=s.maxAgeSeconds,this.t=t,this.p=new o(t)}async expireEntries(){if(this.o)return void(this.u=!0);this.o=!0;const t=this.m?Date.now()-1e3*this.m:0,s=await this.p.expireEntries(t,this.l),e=await caches.open(this.t);for(const t of s)await e.delete(t);this.o=!1,this.u&&(this.u=!1,this.expireEntries())}async updateTimestamp(t){await this.p.setTimestamp(t,Date.now())}async isURLExpired(t){if(this.m){return await this.p.getTimestamp(t)<Date.now()-1e3*this.m}return!1}async delete(){this.u=!1,await this.p.expireEntries(1/0)}}return t.CacheExpiration=u,t.Plugin=class{constructor(t={}){this.cachedResponseWillBeUsed=(async({event:t,request:s,cacheName:e,cachedResponse:i})=>{if(!i)return null;let a=this.k(i);const n=this.D(e);n.expireEntries();const h=n.updateTimestamp(s.url);if(t)try{t.waitUntil(h)}catch(t){}return a?i:null}),this.cacheDidUpdate=(async({cacheName:t,request:s})=>{const e=this.D(t);await e.updateTimestamp(s.url),await e.expireEntries()}),this.N=t,this.m=t.maxAgeSeconds,this.g=new Map,t.purgeOnQuotaError&&n.registerQuotaErrorCallback(()=>this.deleteCacheAndMetadata())}D(t){if(t===a.cacheNames.getRuntimeName())throw new i.WorkboxError("expire-custom-caches-only");let s=this.g.get(t);return s||(s=new u(t,this.N),this.g.set(t,s)),s}k(t){if(!this.m)return!0;const s=this._(t);return null===s||s>=Date.now()-1e3*this.m}_(t){if(!t.headers.has("date"))return null;const s=t.headers.get("date"),e=new Date(s).getTime();return isNaN(e)?null:e}async deleteCacheAndMetadata(){for(const[t,s]of this.g)await caches.delete(t),await s.delete();this.g=new Map}},t}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core); | ||
this.workbox = this.workbox || {}; | ||
this.workbox.navigationPreload = (function (exports, logger_mjs) { | ||
'use strict'; | ||
this.workbox.navigationPreload = (function (exports, logger_js) { | ||
'use strict'; | ||
try { | ||
self['workbox:navigation-preload:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
// @ts-ignore | ||
try { | ||
self['workbox:navigation-preload:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @return {boolean} Whether or not the current browser supports enabling | ||
* navigation preload. | ||
* | ||
* @memberof workbox.navigationPreload | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @return {boolean} Whether or not the current browser supports enabling | ||
* navigation preload. | ||
* | ||
* @memberof workbox.navigationPreload | ||
*/ | ||
function isSupported() { | ||
return Boolean(self.registration && self.registration.navigationPreload); | ||
} | ||
function isSupported() { | ||
return Boolean(self.registration && self.registration.navigationPreload); | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* If the browser supports Navigation Preload, then this will disable it. | ||
* | ||
* @memberof workbox.navigationPreload | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* If the browser supports Navigation Preload, then this will disable it. | ||
* | ||
* @memberof workbox.navigationPreload | ||
*/ | ||
function disable() { | ||
if (isSupported()) { | ||
self.addEventListener('activate', event => { | ||
event.waitUntil(self.registration.navigationPreload.disable().then(() => { | ||
{ | ||
logger_mjs.logger.log(`Navigation preload is disabled.`); | ||
} | ||
})); | ||
}); | ||
} else { | ||
{ | ||
logger_mjs.logger.log(`Navigation preload is not supported in this browser.`); | ||
function disable() { | ||
if (isSupported()) { | ||
self.addEventListener('activate', event => { | ||
event.waitUntil(self.registration.navigationPreload.disable().then(() => { | ||
{ | ||
logger_js.logger.log(`Navigation preload is disabled.`); | ||
} | ||
})); | ||
}); | ||
} else { | ||
{ | ||
logger_js.logger.log(`Navigation preload is not supported in this browser.`); | ||
} | ||
} | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* If the browser supports Navigation Preload, then this will enable it. | ||
* | ||
* @param {string} [headerValue] Optionally, allows developers to | ||
* [override](https://developers.google.com/web/updates/2017/02/navigation-preload#changing_the_header) | ||
* the value of the `Service-Worker-Navigation-Preload` header which will be | ||
* sent to the server when making the navigation request. | ||
* | ||
* @memberof workbox.navigationPreload | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* If the browser supports Navigation Preload, then this will enable it. | ||
* | ||
* @param {string} [headerValue] Optionally, allows developers to | ||
* [override](https://developers.google.com/web/updates/2017/02/navigation-preload#changing_the_header) | ||
* the value of the `Service-Worker-Navigation-Preload` header which will be | ||
* sent to the server when making the navigation request. | ||
* | ||
* @memberof workbox.navigationPreload | ||
*/ | ||
function enable(headerValue) { | ||
if (isSupported()) { | ||
self.addEventListener('activate', event => { | ||
event.waitUntil(self.registration.navigationPreload.enable().then(() => { | ||
// Defaults to Service-Worker-Navigation-Preload: true if not set. | ||
if (headerValue) { | ||
self.registration.navigationPreload.setHeaderValue(headerValue); | ||
} | ||
function enable(headerValue) { | ||
if (isSupported()) { | ||
self.addEventListener('activate', event => { | ||
event.waitUntil(self.registration.navigationPreload.enable().then(() => { | ||
// Defaults to Service-Worker-Navigation-Preload: true if not set. | ||
if (headerValue) { | ||
self.registration.navigationPreload.setHeaderValue(headerValue); | ||
} | ||
{ | ||
logger_mjs.logger.log(`Navigation preload is enabled.`); | ||
} | ||
})); | ||
}); | ||
} else { | ||
{ | ||
logger_mjs.logger.log(`Navigation preload is not supported in this browser.`); | ||
{ | ||
logger_js.logger.log(`Navigation preload is enabled.`); | ||
} | ||
})); | ||
}); | ||
} else { | ||
{ | ||
logger_js.logger.log(`Navigation preload is not supported in this browser.`); | ||
} | ||
} | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
exports.disable = disable; | ||
exports.enable = enable; | ||
exports.isSupported = isSupported; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
return exports; | ||
exports.disable = disable; | ||
exports.enable = enable; | ||
exports.isSupported = isSupported; | ||
return exports; | ||
}({}, workbox.core._private)); | ||
@@ -1,2 +0,2 @@ | ||
this.workbox=this.workbox||{},this.workbox.navigationPreload=function(t){"use strict";try{self["workbox:navigation-preload:4.3.1"]&&_()}catch(t){}function e(){return Boolean(self.registration&&self.registration.navigationPreload)}return t.disable=function(){e()&&self.addEventListener("activate",t=>{t.waitUntil(self.registration.navigationPreload.disable().then(()=>{}))})},t.enable=function(t){e()&&self.addEventListener("activate",e=>{e.waitUntil(self.registration.navigationPreload.enable().then(()=>{t&&self.registration.navigationPreload.setHeaderValue(t)}))})},t.isSupported=e,t}({}); | ||
this.workbox=this.workbox||{},this.workbox.navigationPreload=function(t){"use strict";try{self["workbox:navigation-preload:5.0.0-alpha.2"]&&_()}catch(t){}function e(){return Boolean(self.registration&&self.registration.navigationPreload)}return t.disable=function(){e()&&self.addEventListener("activate",t=>{t.waitUntil(self.registration.navigationPreload.disable().then(()=>{}))})},t.enable=function(t){e()&&self.addEventListener("activate",e=>{e.waitUntil(self.registration.navigationPreload.enable().then(()=>{t&&self.registration.navigationPreload.setHeaderValue(t)}))})},t.isSupported=e,t}({}); | ||
this.workbox = this.workbox || {}; | ||
this.workbox.googleAnalytics = (function (exports, Plugin_mjs, cacheNames_mjs, getFriendlyURL_mjs, logger_mjs, Route_mjs, Router_mjs, NetworkFirst_mjs, NetworkOnly_mjs) { | ||
'use strict'; | ||
this.workbox.googleAnalytics = (function (exports, Plugin_js, cacheNames_js, getFriendlyURL_js, logger_js, Route_js, Router_js, NetworkFirst_js, NetworkOnly_js) { | ||
'use strict'; | ||
try { | ||
self['workbox:google-analytics:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
// @ts-ignore | ||
try { | ||
self['workbox:google-analytics:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const QUEUE_NAME = 'workbox-google-analytics'; | ||
const MAX_RETENTION_TIME = 60 * 48; // Two days in minutes | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const QUEUE_NAME = 'workbox-google-analytics'; | ||
const MAX_RETENTION_TIME = 60 * 48; // Two days in minutes | ||
const GOOGLE_ANALYTICS_HOST = 'www.google-analytics.com'; | ||
const GTM_HOST = 'www.googletagmanager.com'; | ||
const ANALYTICS_JS_PATH = '/analytics.js'; | ||
const GTAG_JS_PATH = '/gtag/js'; | ||
const GTM_JS_PATH = '/gtm.js'; | ||
// endpoints. Most of the time the default path (/collect) is used, but | ||
// occasionally an experimental endpoint is used when testing new features, | ||
// (e.g. /r/collect or /j/collect) | ||
const GOOGLE_ANALYTICS_HOST = 'www.google-analytics.com'; | ||
const GTM_HOST = 'www.googletagmanager.com'; | ||
const ANALYTICS_JS_PATH = '/analytics.js'; | ||
const GTAG_JS_PATH = '/gtag/js'; | ||
const GTM_JS_PATH = '/gtm.js'; | ||
// endpoints. Most of the time the default path (/collect) is used, but | ||
// occasionally an experimental endpoint is used when testing new features, | ||
// (e.g. /r/collect or /j/collect) | ||
const COLLECT_PATHS_REGEX = /^\/(\w+\/)?collect/; | ||
const COLLECT_PATHS_REGEX = /^\/(\w+\/)?collect/; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Creates the requestWillDequeue callback to be used with the background | ||
* sync queue plugin. The callback takes the failed request and adds the | ||
* `qt` param based on the current time, as well as applies any other | ||
* user-defined hit modifications. | ||
* | ||
* @param {Object} config See workbox.googleAnalytics.initialize. | ||
* @return {Function} The requestWillDequeu callback function. | ||
* | ||
* @private | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Creates the requestWillDequeue callback to be used with the background | ||
* sync queue plugin. The callback takes the failed request and adds the | ||
* `qt` param based on the current time, as well as applies any other | ||
* user-defined hit modifications. | ||
* | ||
* @param {Object} config See workbox.googleAnalytics.initialize. | ||
* @return {Function} The requestWillDequeu callback function. | ||
* | ||
* @private | ||
*/ | ||
const createOnSyncCallback = config => { | ||
return async ({ | ||
queue | ||
}) => { | ||
let entry; | ||
const createOnSyncCallback = config => { | ||
return async ({ | ||
queue | ||
}) => { | ||
let entry; | ||
while (entry = await queue.shiftRequest()) { | ||
const { | ||
request, | ||
timestamp | ||
} = entry; | ||
const url = new URL(request.url); | ||
while (entry = await queue.shiftRequest()) { | ||
const { | ||
request, | ||
timestamp | ||
} = entry; | ||
const url = new URL(request.url); | ||
try { | ||
// Measurement protocol requests can set their payload parameters in | ||
// either the URL query string (for GET requests) or the POST body. | ||
const params = request.method === 'POST' ? new URLSearchParams((await request.clone().text())) : url.searchParams; // Calculate the qt param, accounting for the fact that an existing | ||
// qt param may be present and should be updated rather than replaced. | ||
try { | ||
// Measurement protocol requests can set their payload parameters in | ||
// either the URL query string (for GET requests) or the POST body. | ||
const params = request.method === 'POST' ? new URLSearchParams((await request.clone().text())) : url.searchParams; // Calculate the qt param, accounting for the fact that an existing | ||
// qt param may be present and should be updated rather than replaced. | ||
const originalHitTime = timestamp - (Number(params.get('qt')) || 0); | ||
const queueTime = Date.now() - originalHitTime; // Set the qt param prior to applying hitFilter or parameterOverrides. | ||
const originalHitTime = timestamp - (Number(params.get('qt')) || 0); | ||
const queueTime = Date.now() - originalHitTime; // Set the qt param prior to applying hitFilter or parameterOverrides. | ||
params.set('qt', queueTime); // Apply `paramterOverrideds`, if set. | ||
params.set('qt', String(queueTime)); // Apply `parameterOverrides`, if set. | ||
if (config.parameterOverrides) { | ||
for (const param of Object.keys(config.parameterOverrides)) { | ||
const value = config.parameterOverrides[param]; | ||
params.set(param, value); | ||
} | ||
} // Apply `hitFilter`, if set. | ||
if (config.parameterOverrides) { | ||
for (const param of Object.keys(config.parameterOverrides)) { | ||
const value = config.parameterOverrides[param]; | ||
params.set(param, value); | ||
} | ||
} // Apply `hitFilter`, if set. | ||
if (typeof config.hitFilter === 'function') { | ||
config.hitFilter.call(null, params); | ||
} // Retry the fetch. Ignore URL search params from the URL as they're | ||
// now in the post body. | ||
if (typeof config.hitFilter === 'function') { | ||
config.hitFilter.call(null, params); | ||
} // Retry the fetch. Ignore URL search params from the URL as they're | ||
// now in the post body. | ||
await fetch(new Request(url.origin + url.pathname, { | ||
body: params.toString(), | ||
method: 'POST', | ||
mode: 'cors', | ||
credentials: 'omit', | ||
headers: { | ||
'Content-Type': 'text/plain' | ||
await fetch(new Request(url.origin + url.pathname, { | ||
body: params.toString(), | ||
method: 'POST', | ||
mode: 'cors', | ||
credentials: 'omit', | ||
headers: { | ||
'Content-Type': 'text/plain' | ||
} | ||
})); | ||
if ("dev" !== 'production') { | ||
logger_js.logger.log(`Request for '${getFriendlyURL_js.getFriendlyURL(url.href)}'` + `has been replayed`); | ||
} | ||
})); | ||
} catch (err) { | ||
await queue.unshiftRequest(entry); | ||
{ | ||
logger_mjs.logger.log(`Request for '${getFriendlyURL_mjs.getFriendlyURL(url.href)}'` + `has been replayed`); | ||
} | ||
} catch (err) { | ||
await queue.unshiftRequest(entry); | ||
{ | ||
logger_js.logger.log(`Request for '${getFriendlyURL_js.getFriendlyURL(url.href)}'` + `failed to replay, putting it back in the queue.`); | ||
} | ||
{ | ||
logger_mjs.logger.log(`Request for '${getFriendlyURL_mjs.getFriendlyURL(url.href)}'` + `failed to replay, putting it back in the queue.`); | ||
throw err; | ||
} | ||
} | ||
throw err; | ||
{ | ||
logger_js.logger.log(`All Google Analytics request successfully replayed; ` + `the queue is now empty!`); | ||
} | ||
} | ||
{ | ||
logger_mjs.logger.log(`All Google Analytics request successfully replayed; ` + `the queue is now empty!`); | ||
} | ||
}; | ||
}; | ||
}; | ||
/** | ||
* Creates GET and POST routes to catch failed Measurement Protocol hits. | ||
* | ||
* @param {Plugin} queuePlugin | ||
* @return {Array<Route>} The created routes. | ||
* | ||
* @private | ||
*/ | ||
/** | ||
* Creates GET and POST routes to catch failed Measurement Protocol hits. | ||
* | ||
* @param {Plugin} queuePlugin | ||
* @return {Array<Route>} The created routes. | ||
* | ||
* @private | ||
*/ | ||
const createCollectRoutes = queuePlugin => { | ||
const match = ({ | ||
url | ||
}) => url.hostname === GOOGLE_ANALYTICS_HOST && COLLECT_PATHS_REGEX.test(url.pathname); | ||
const createCollectRoutes = queuePlugin => { | ||
const match = ({ | ||
url | ||
}) => url.hostname === GOOGLE_ANALYTICS_HOST && COLLECT_PATHS_REGEX.test(url.pathname); | ||
const handler = new NetworkOnly_mjs.NetworkOnly({ | ||
plugins: [queuePlugin] | ||
}); | ||
return [new Route_mjs.Route(match, handler, 'GET'), new Route_mjs.Route(match, handler, 'POST')]; | ||
}; | ||
/** | ||
* Creates a route with a network first strategy for the analytics.js script. | ||
* | ||
* @param {string} cacheName | ||
* @return {Route} The created route. | ||
* | ||
* @private | ||
*/ | ||
const handler = new NetworkOnly_js.NetworkOnly({ | ||
plugins: [queuePlugin] | ||
}); | ||
return [new Route_js.Route(match, handler, 'GET'), new Route_js.Route(match, handler, 'POST')]; | ||
}; | ||
/** | ||
* Creates a route with a network first strategy for the analytics.js script. | ||
* | ||
* @param {string} cacheName | ||
* @return {Route} The created route. | ||
* | ||
* @private | ||
*/ | ||
const createAnalyticsJsRoute = cacheName => { | ||
const match = ({ | ||
url | ||
}) => url.hostname === GOOGLE_ANALYTICS_HOST && url.pathname === ANALYTICS_JS_PATH; | ||
const createAnalyticsJsRoute = cacheName => { | ||
const match = ({ | ||
url | ||
}) => url.hostname === GOOGLE_ANALYTICS_HOST && url.pathname === ANALYTICS_JS_PATH; | ||
const handler = new NetworkFirst_mjs.NetworkFirst({ | ||
cacheName | ||
}); | ||
return new Route_mjs.Route(match, handler, 'GET'); | ||
}; | ||
/** | ||
* Creates a route with a network first strategy for the gtag.js script. | ||
* | ||
* @param {string} cacheName | ||
* @return {Route} The created route. | ||
* | ||
* @private | ||
*/ | ||
const handler = new NetworkFirst_js.NetworkFirst({ | ||
cacheName | ||
}); | ||
return new Route_js.Route(match, handler, 'GET'); | ||
}; | ||
/** | ||
* Creates a route with a network first strategy for the gtag.js script. | ||
* | ||
* @param {string} cacheName | ||
* @return {Route} The created route. | ||
* | ||
* @private | ||
*/ | ||
const createGtagJsRoute = cacheName => { | ||
const match = ({ | ||
url | ||
}) => url.hostname === GTM_HOST && url.pathname === GTAG_JS_PATH; | ||
const createGtagJsRoute = cacheName => { | ||
const match = ({ | ||
url | ||
}) => url.hostname === GTM_HOST && url.pathname === GTAG_JS_PATH; | ||
const handler = new NetworkFirst_mjs.NetworkFirst({ | ||
cacheName | ||
}); | ||
return new Route_mjs.Route(match, handler, 'GET'); | ||
}; | ||
/** | ||
* Creates a route with a network first strategy for the gtm.js script. | ||
* | ||
* @param {string} cacheName | ||
* @return {Route} The created route. | ||
* | ||
* @private | ||
*/ | ||
const handler = new NetworkFirst_js.NetworkFirst({ | ||
cacheName | ||
}); | ||
return new Route_js.Route(match, handler, 'GET'); | ||
}; | ||
/** | ||
* Creates a route with a network first strategy for the gtm.js script. | ||
* | ||
* @param {string} cacheName | ||
* @return {Route} The created route. | ||
* | ||
* @private | ||
*/ | ||
const createGtmJsRoute = cacheName => { | ||
const match = ({ | ||
url | ||
}) => url.hostname === GTM_HOST && url.pathname === GTM_JS_PATH; | ||
const createGtmJsRoute = cacheName => { | ||
const match = ({ | ||
url | ||
}) => url.hostname === GTM_HOST && url.pathname === GTM_JS_PATH; | ||
const handler = new NetworkFirst_mjs.NetworkFirst({ | ||
cacheName | ||
}); | ||
return new Route_mjs.Route(match, handler, 'GET'); | ||
}; | ||
/** | ||
* @param {Object=} [options] | ||
* @param {Object} [options.cacheName] The cache name to store and retrieve | ||
* analytics.js. Defaults to the cache names provided by `workbox-core`. | ||
* @param {Object} [options.parameterOverrides] | ||
* [Measurement Protocol parameters](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters), | ||
* expressed as key/value pairs, to be added to replayed Google Analytics | ||
* requests. This can be used to, e.g., set a custom dimension indicating | ||
* that the request was replayed. | ||
* @param {Function} [options.hitFilter] A function that allows you to modify | ||
* the hit parameters prior to replaying | ||
* the hit. The function is invoked with the original hit's URLSearchParams | ||
* object as its only argument. | ||
* | ||
* @memberof workbox.googleAnalytics | ||
*/ | ||
const handler = new NetworkFirst_js.NetworkFirst({ | ||
cacheName | ||
}); | ||
return new Route_js.Route(match, handler, 'GET'); | ||
}; | ||
/** | ||
* @param {Object=} [options] | ||
* @param {Object} [options.cacheName] The cache name to store and retrieve | ||
* analytics.js. Defaults to the cache names provided by `workbox-core`. | ||
* @param {Object} [options.parameterOverrides] | ||
* [Measurement Protocol parameters](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters), | ||
* expressed as key/value pairs, to be added to replayed Google Analytics | ||
* requests. This can be used to, e.g., set a custom dimension indicating | ||
* that the request was replayed. | ||
* @param {Function} [options.hitFilter] A function that allows you to modify | ||
* the hit parameters prior to replaying | ||
* the hit. The function is invoked with the original hit's URLSearchParams | ||
* object as its only argument. | ||
* | ||
* @memberof workbox.googleAnalytics | ||
*/ | ||
const initialize = (options = {}) => { | ||
const cacheName = cacheNames_mjs.cacheNames.getGoogleAnalyticsName(options.cacheName); | ||
const queuePlugin = new Plugin_mjs.Plugin(QUEUE_NAME, { | ||
maxRetentionTime: MAX_RETENTION_TIME, | ||
onSync: createOnSyncCallback(options) | ||
}); | ||
const routes = [createGtmJsRoute(cacheName), createAnalyticsJsRoute(cacheName), createGtagJsRoute(cacheName), ...createCollectRoutes(queuePlugin)]; | ||
const router = new Router_mjs.Router(); | ||
const initialize = (options = {}) => { | ||
const cacheName = cacheNames_js.cacheNames.getGoogleAnalyticsName(options.cacheName); | ||
const queuePlugin = new Plugin_js.Plugin(QUEUE_NAME, { | ||
maxRetentionTime: MAX_RETENTION_TIME, | ||
onSync: createOnSyncCallback(options) | ||
}); | ||
const routes = [createGtmJsRoute(cacheName), createAnalyticsJsRoute(cacheName), createGtagJsRoute(cacheName), ...createCollectRoutes(queuePlugin)]; | ||
const router = new Router_js.Router(); | ||
for (const route of routes) { | ||
router.registerRoute(route); | ||
} | ||
for (const route of routes) { | ||
router.registerRoute(route); | ||
} | ||
router.addFetchListener(); | ||
}; | ||
router.addFetchListener(); | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
exports.initialize = initialize; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
return exports; | ||
exports.initialize = initialize; | ||
return exports; | ||
}({}, workbox.backgroundSync, workbox.core._private, workbox.core._private, workbox.core._private, workbox.routing, workbox.routing, workbox.strategies, workbox.strategies)); | ||
@@ -1,2 +0,2 @@ | ||
this.workbox=this.workbox||{},this.workbox.googleAnalytics=function(e,t,o,n,a,c,w){"use strict";try{self["workbox:google-analytics:4.3.1"]&&_()}catch(e){}const r=/^\/(\w+\/)?collect/,s=e=>async({queue:t})=>{let o;for(;o=await t.shiftRequest();){const{request:n,timestamp:a}=o,c=new URL(n.url);try{const w="POST"===n.method?new URLSearchParams(await n.clone().text()):c.searchParams,r=a-(Number(w.get("qt"))||0),s=Date.now()-r;if(w.set("qt",s),e.parameterOverrides)for(const t of Object.keys(e.parameterOverrides)){const o=e.parameterOverrides[t];w.set(t,o)}"function"==typeof e.hitFilter&&e.hitFilter.call(null,w),await fetch(new Request(c.origin+c.pathname,{body:w.toString(),method:"POST",mode:"cors",credentials:"omit",headers:{"Content-Type":"text/plain"}}))}catch(e){throw await t.unshiftRequest(o),e}}},i=e=>{const t=({url:e})=>"www.google-analytics.com"===e.hostname&&r.test(e.pathname),o=new w.NetworkOnly({plugins:[e]});return[new n.Route(t,o,"GET"),new n.Route(t,o,"POST")]},l=e=>{const t=new c.NetworkFirst({cacheName:e});return new n.Route(({url:e})=>"www.google-analytics.com"===e.hostname&&"/analytics.js"===e.pathname,t,"GET")},m=e=>{const t=new c.NetworkFirst({cacheName:e});return new n.Route(({url:e})=>"www.googletagmanager.com"===e.hostname&&"/gtag/js"===e.pathname,t,"GET")},u=e=>{const t=new c.NetworkFirst({cacheName:e});return new n.Route(({url:e})=>"www.googletagmanager.com"===e.hostname&&"/gtm.js"===e.pathname,t,"GET")};return e.initialize=((e={})=>{const n=o.cacheNames.getGoogleAnalyticsName(e.cacheName),c=new t.Plugin("workbox-google-analytics",{maxRetentionTime:2880,onSync:s(e)}),w=[u(n),l(n),m(n),...i(c)],r=new a.Router;for(const e of w)r.registerRoute(e);r.addFetchListener()}),e}({},workbox.backgroundSync,workbox.core._private,workbox.routing,workbox.routing,workbox.strategies,workbox.strategies); | ||
this.workbox=this.workbox||{},this.workbox.googleAnalytics=function(t,e,o,n,a,c,w,r,s){"use strict";try{self["workbox:google-analytics:5.0.0-alpha.2"]&&_()}catch(t){}const i=/^\/(\w+\/)?collect/,l=t=>async({queue:e})=>{let o;for(;o=await e.shiftRequest();){const{request:n,timestamp:a}=o,c=new URL(n.url);try{const w="POST"===n.method?new URLSearchParams(await n.clone().text()):c.searchParams,r=a-(Number(w.get("qt"))||0),s=Date.now()-r;if(w.set("qt",String(s)),t.parameterOverrides)for(const e of Object.keys(t.parameterOverrides)){const o=t.parameterOverrides[e];w.set(e,o)}"function"==typeof t.hitFilter&&t.hitFilter.call(null,w),await fetch(new Request(c.origin+c.pathname,{body:w.toString(),method:"POST",mode:"cors",credentials:"omit",headers:{"Content-Type":"text/plain"}}))}catch(t){throw await e.unshiftRequest(o),t}}},g=t=>{const e=({url:t})=>"www.google-analytics.com"===t.hostname&&i.test(t.pathname),o=new s.NetworkOnly({plugins:[t]});return[new c.Route(e,o,"GET"),new c.Route(e,o,"POST")]},m=t=>{const e=new r.NetworkFirst({cacheName:t});return new c.Route(({url:t})=>"www.google-analytics.com"===t.hostname&&"/analytics.js"===t.pathname,e,"GET")},u=t=>{const e=new r.NetworkFirst({cacheName:t});return new c.Route(({url:t})=>"www.googletagmanager.com"===t.hostname&&"/gtag/js"===t.pathname,e,"GET")},h=t=>{const e=new r.NetworkFirst({cacheName:t});return new c.Route(({url:t})=>"www.googletagmanager.com"===t.hostname&&"/gtm.js"===t.pathname,e,"GET")};return t.initialize=((t={})=>{const n=o.cacheNames.getGoogleAnalyticsName(t.cacheName),a=new e.Plugin("workbox-google-analytics",{maxRetentionTime:2880,onSync:l(t)}),c=[h(n),m(n),u(n),...g(a)],r=new w.Router;for(const t of c)r.registerRoute(t);r.addFetchListener()}),t}({},workbox.backgroundSync,workbox.core._private,workbox.core._private,workbox.core._private,workbox.routing,workbox.routing,workbox.strategies,workbox.strategies); | ||
this.workbox = this.workbox || {}; | ||
this.workbox.precaching = (function (exports, assert_mjs, cacheNames_mjs, getFriendlyURL_mjs, logger_mjs, cacheWrapper_mjs, fetchWrapper_mjs, WorkboxError_mjs) { | ||
'use strict'; | ||
this.workbox.precaching = (function (exports, assert_js, cacheNames_js, getFriendlyURL_js, logger_js, cacheWrapper_js, fetchWrapper_js, WorkboxError_js) { | ||
'use strict'; | ||
try { | ||
self['workbox:precaching:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
// @ts-ignore | ||
try { | ||
self['workbox:precaching:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const plugins = []; | ||
const precachePlugins = { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const plugins = []; | ||
const precachePlugins = { | ||
/* | ||
* @return {Array} | ||
* @private | ||
*/ | ||
get() { | ||
return plugins; | ||
}, | ||
/* | ||
* @param {Array} newPlugins | ||
* @private | ||
*/ | ||
add(newPlugins) { | ||
plugins.push(...newPlugins); | ||
} | ||
}; | ||
/* | ||
* @return {Array} | ||
* @private | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Adds plugins to precaching. | ||
* | ||
* @param {Array<Object>} newPlugins | ||
* | ||
* @alias workbox.precaching.addPlugins | ||
*/ | ||
get() { | ||
return plugins; | ||
}, | ||
const addPlugins = newPlugins => { | ||
precachePlugins.add(newPlugins); | ||
}; | ||
/* | ||
* @param {Array} newPlugins | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {Response} response | ||
* @return {Response} | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
add(newPlugins) { | ||
plugins.push(...newPlugins); | ||
} | ||
}; | ||
async function cleanRedirect(response) { | ||
const clonedResponse = response.clone(); // Not all browsers support the Response.body stream, so fall back | ||
// to reading the entire body into memory as a blob. | ||
/* | ||
Copyright 2019 Google LLC | ||
const bodyPromise = 'body' in clonedResponse ? Promise.resolve(clonedResponse.body) : clonedResponse.blob(); | ||
const body = await bodyPromise; // new Response() is happy when passed either a stream or a Blob. | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Adds plugins to precaching. | ||
* | ||
* @param {Array<Object>} newPlugins | ||
* | ||
* @alias workbox.precaching.addPlugins | ||
*/ | ||
return new Response(body, { | ||
headers: clonedResponse.headers, | ||
status: clonedResponse.status, | ||
statusText: clonedResponse.statusText | ||
}); | ||
} | ||
const addPlugins = newPlugins => { | ||
precachePlugins.add(newPlugins); | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {Response} response | ||
* @return {Response} | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
const REVISION_SEARCH_PARAM = '__WB_REVISION__'; | ||
/** | ||
* Converts a manifest entry into a versioned URL suitable for precaching. | ||
* | ||
* @param {Object|string} entry | ||
* @return {string} A URL with versioning info. | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
async function cleanRedirect(response) { | ||
const clonedResponse = response.clone(); // Not all browsers support the Response.body stream, so fall back | ||
// to reading the entire body into memory as a blob. | ||
function createCacheKey(entry) { | ||
if (!entry) { | ||
throw new WorkboxError_js.WorkboxError('add-to-cache-list-unexpected-type', { | ||
entry | ||
}); | ||
} // If a precache manifest entry is a string, it's assumed to be a versioned | ||
// URL, like '/app.abcd1234.js'. Return as-is. | ||
const bodyPromise = 'body' in clonedResponse ? Promise.resolve(clonedResponse.body) : clonedResponse.blob(); | ||
const body = await bodyPromise; // new Response() is happy when passed either a stream or a Blob. | ||
return new Response(body, { | ||
headers: clonedResponse.headers, | ||
status: clonedResponse.status, | ||
statusText: clonedResponse.statusText | ||
}); | ||
} | ||
if (typeof entry === 'string') { | ||
const urlObject = new URL(entry, location.href); | ||
return { | ||
cacheKey: urlObject.href, | ||
url: urlObject.href | ||
}; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
const { | ||
revision, | ||
url | ||
} = entry; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
if (!url) { | ||
throw new WorkboxError_js.WorkboxError('add-to-cache-list-unexpected-type', { | ||
entry | ||
}); | ||
} // If there's just a URL and no revision, then it's also assumed to be a | ||
// versioned URL. | ||
const REVISION_SEARCH_PARAM = '__WB_REVISION__'; | ||
/** | ||
* Converts a manifest entry into a versioned URL suitable for precaching. | ||
* | ||
* @param {Object} entry | ||
* @return {string} A URL with versioning info. | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
function createCacheKey(entry) { | ||
if (!entry) { | ||
throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-unexpected-type', { | ||
entry | ||
}); | ||
} // If a precache manifest entry is a string, it's assumed to be a versioned | ||
// URL, like '/app.abcd1234.js'. Return as-is. | ||
if (!revision) { | ||
const urlObject = new URL(url, location.href); | ||
return { | ||
cacheKey: urlObject.href, | ||
url: urlObject.href | ||
}; | ||
} // Otherwise, construct a properly versioned URL using the custom Workbox | ||
// search parameter along with the revision info. | ||
if (typeof entry === 'string') { | ||
const urlObject = new URL(entry, location); | ||
const cacheKeyURL = new URL(url, location.href); | ||
const originalURL = new URL(url, location.href); | ||
cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision); | ||
return { | ||
cacheKey: urlObject.href, | ||
url: urlObject.href | ||
cacheKey: cacheKeyURL.href, | ||
url: originalURL.href | ||
}; | ||
} | ||
const { | ||
revision, | ||
url | ||
} = entry; | ||
/* | ||
Copyright 2018 Google LLC | ||
if (!url) { | ||
throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-unexpected-type', { | ||
entry | ||
}); | ||
} // If there's just a URL and no revision, then it's also assumed to be a | ||
// versioned URL. | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {string} groupTitle | ||
* @param {Array<string>} deletedURLs | ||
* | ||
* @private | ||
*/ | ||
const logGroup = (groupTitle, deletedURLs) => { | ||
logger_js.logger.groupCollapsed(groupTitle); | ||
if (!revision) { | ||
const urlObject = new URL(url, location); | ||
return { | ||
cacheKey: urlObject.href, | ||
url: urlObject.href | ||
}; | ||
} // Otherwise, construct a properly versioned URL using the custom Workbox | ||
// search parameter along with the revision info. | ||
for (const url of deletedURLs) { | ||
logger_js.logger.log(url); | ||
} | ||
const originalURL = new URL(url, location); | ||
const cacheKeyURL = new URL(url, location); | ||
cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision); | ||
return { | ||
cacheKey: cacheKeyURL.href, | ||
url: originalURL.href | ||
logger_js.logger.groupEnd(); | ||
}; | ||
} | ||
/** | ||
* @param {Array<string>} deletedURLs | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
function printCleanupDetails(deletedURLs) { | ||
const deletionCount = deletedURLs.length; | ||
const logGroup = (groupTitle, deletedURLs) => { | ||
logger_mjs.logger.groupCollapsed(groupTitle); | ||
for (const url of deletedURLs) { | ||
logger_mjs.logger.log(url); | ||
if (deletionCount > 0) { | ||
logger_js.logger.groupCollapsed(`During precaching cleanup, ` + `${deletionCount} cached ` + `request${deletionCount === 1 ? ' was' : 's were'} deleted.`); | ||
logGroup('Deleted Cache Requests', deletedURLs); | ||
logger_js.logger.groupEnd(); | ||
} | ||
} | ||
logger_mjs.logger.groupEnd(); | ||
}; | ||
/** | ||
* @param {Array<string>} deletedURLs | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {string} groupTitle | ||
* @param {Array<string>} urls | ||
* | ||
* @private | ||
*/ | ||
function printCleanupDetails(deletedURLs) { | ||
const deletionCount = deletedURLs.length; | ||
function _nestedGroup(groupTitle, urls) { | ||
if (urls.length === 0) { | ||
return; | ||
} | ||
if (deletionCount > 0) { | ||
logger_mjs.logger.groupCollapsed(`During precaching cleanup, ` + `${deletionCount} cached ` + `request${deletionCount === 1 ? ' was' : 's were'} deleted.`); | ||
logGroup('Deleted Cache Requests', deletedURLs); | ||
logger_mjs.logger.groupEnd(); | ||
} | ||
} | ||
logger_js.logger.groupCollapsed(groupTitle); | ||
/* | ||
Copyright 2018 Google LLC | ||
for (const url of urls) { | ||
logger_js.logger.log(url); | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {string} groupTitle | ||
* @param {Array<string>} urls | ||
* | ||
* @private | ||
*/ | ||
function _nestedGroup(groupTitle, urls) { | ||
if (urls.length === 0) { | ||
return; | ||
logger_js.logger.groupEnd(); | ||
} | ||
/** | ||
* @param {Array<string>} urlsToPrecache | ||
* @param {Array<string>} urlsAlreadyPrecached | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
logger_mjs.logger.groupCollapsed(groupTitle); | ||
for (const url of urls) { | ||
logger_mjs.logger.log(url); | ||
} | ||
function printInstallDetails(urlsToPrecache, urlsAlreadyPrecached) { | ||
const precachedCount = urlsToPrecache.length; | ||
const alreadyPrecachedCount = urlsAlreadyPrecached.length; | ||
logger_mjs.logger.groupEnd(); | ||
} | ||
/** | ||
* @param {Array<string>} urlsToPrecache | ||
* @param {Array<string>} urlsAlreadyPrecached | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
if (precachedCount || alreadyPrecachedCount) { | ||
let message = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`; | ||
if (alreadyPrecachedCount > 0) { | ||
message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`; | ||
} | ||
function printInstallDetails(urlsToPrecache, urlsAlreadyPrecached) { | ||
const precachedCount = urlsToPrecache.length; | ||
const alreadyPrecachedCount = urlsAlreadyPrecached.length; | ||
logger_js.logger.groupCollapsed(message); | ||
if (precachedCount || alreadyPrecachedCount) { | ||
let message = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`; | ||
_nestedGroup(`View newly precached URLs.`, urlsToPrecache); | ||
if (alreadyPrecachedCount > 0) { | ||
message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`; | ||
_nestedGroup(`View previously precached URLs.`, urlsAlreadyPrecached); | ||
logger_js.logger.groupEnd(); | ||
} | ||
logger_mjs.logger.groupCollapsed(message); | ||
_nestedGroup(`View newly precached URLs.`, urlsToPrecache); | ||
_nestedGroup(`View previously precached URLs.`, urlsAlreadyPrecached); | ||
logger_mjs.logger.groupEnd(); | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Performs efficient precaching of assets. | ||
* | ||
* @memberof module:workbox-precaching | ||
*/ | ||
class PrecacheController { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Create a new PrecacheController. | ||
* Performs efficient precaching of assets. | ||
* | ||
* @param {string} [cacheName] An optional name for the cache, to override | ||
* the default precache name. | ||
* @memberof module:workbox-precaching | ||
*/ | ||
constructor(cacheName) { | ||
this._cacheName = cacheNames_mjs.cacheNames.getPrecacheName(cacheName); | ||
this._urlsToCacheKeys = new Map(); | ||
} | ||
/** | ||
* This method will add items to the precache list, removing duplicates | ||
* and ensuring the information is valid. | ||
* | ||
* @param { | ||
* Array<module:workbox-precaching.PrecacheController.PrecacheEntry|string> | ||
* } entries Array of entries to precache. | ||
*/ | ||
addToCacheList(entries) { | ||
{ | ||
assert_mjs.assert.isArray(entries, { | ||
moduleName: 'workbox-precaching', | ||
className: 'PrecacheController', | ||
funcName: 'addToCacheList', | ||
paramName: 'entries' | ||
}); | ||
class PrecacheController { | ||
/** | ||
* Create a new PrecacheController. | ||
* | ||
* @param {string} [cacheName] An optional name for the cache, to override | ||
* the default precache name. | ||
*/ | ||
constructor(cacheName) { | ||
this._cacheName = cacheNames_js.cacheNames.getPrecacheName(cacheName); | ||
this._urlsToCacheKeys = new Map(); | ||
this._cacheKeysToIntegrities = new Map(); | ||
} | ||
/** | ||
* This method will add items to the precache list, removing duplicates | ||
* and ensuring the information is valid. | ||
* | ||
* @param { | ||
* Array<module:workbox-precaching.PrecacheController.PrecacheEntry|string> | ||
* } entries Array of entries to precache. | ||
*/ | ||
for (const entry of entries) { | ||
const { | ||
cacheKey, | ||
url | ||
} = createCacheKey(entry); | ||
if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) { | ||
throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-conflicting-entries', { | ||
firstEntry: this._urlsToCacheKeys.get(url), | ||
secondEntry: cacheKey | ||
addToCacheList(entries) { | ||
{ | ||
assert_js.assert.isArray(entries, { | ||
moduleName: 'workbox-precaching', | ||
className: 'PrecacheController', | ||
funcName: 'addToCacheList', | ||
paramName: 'entries' | ||
}); | ||
} | ||
this._urlsToCacheKeys.set(url, cacheKey); | ||
} | ||
} | ||
/** | ||
* Precaches new and updated assets. Call this method from the service worker | ||
* install event. | ||
* | ||
* @param {Object} options | ||
* @param {Event} [options.event] The install event (if needed). | ||
* @param {Array<Object>} [options.plugins] Plugins to be used for fetching | ||
* and caching during install. | ||
* @return {Promise<workbox.precaching.InstallResult>} | ||
*/ | ||
for (const entry of entries) { | ||
const { | ||
cacheKey, | ||
url | ||
} = createCacheKey(entry); | ||
if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) { | ||
throw new WorkboxError_js.WorkboxError('add-to-cache-list-conflicting-entries', { | ||
firstEntry: this._urlsToCacheKeys.get(url), | ||
secondEntry: cacheKey | ||
}); | ||
} | ||
async install({ | ||
event, | ||
plugins | ||
} = {}) { | ||
{ | ||
if (plugins) { | ||
assert_mjs.assert.isArray(plugins, { | ||
moduleName: 'workbox-precaching', | ||
className: 'PrecacheController', | ||
funcName: 'install', | ||
paramName: 'plugins' | ||
}); | ||
if (typeof entry !== 'string' && entry.integrity) { | ||
if (this._cacheKeysToIntegrities.has(cacheKey) && this._cacheKeysToIntegrities.get(cacheKey) !== entry.integrity) { | ||
throw new WorkboxError_js.WorkboxError('add-to-cache-list-conflicting-integrities', { | ||
url | ||
}); | ||
} | ||
this._cacheKeysToIntegrities.set(cacheKey, entry.integrity); | ||
} | ||
this._urlsToCacheKeys.set(url, cacheKey); | ||
} | ||
} | ||
/** | ||
* Precaches new and updated assets. Call this method from the service worker | ||
* install event. | ||
* | ||
* @param {Object} options | ||
* @param {Event} [options.event] The install event (if needed). | ||
* @param {Array<Object>} [options.plugins] Plugins to be used for fetching | ||
* and caching during install. | ||
* @return {Promise<workbox.precaching.InstallResult>} | ||
*/ | ||
const urlsToPrecache = []; | ||
const urlsAlreadyPrecached = []; | ||
const cache = await caches.open(this._cacheName); | ||
const alreadyCachedRequests = await cache.keys(); | ||
const alreadyCachedURLs = new Set(alreadyCachedRequests.map(request => request.url)); | ||
for (const cacheKey of this._urlsToCacheKeys.values()) { | ||
if (alreadyCachedURLs.has(cacheKey)) { | ||
urlsAlreadyPrecached.push(cacheKey); | ||
} else { | ||
urlsToPrecache.push(cacheKey); | ||
async install({ | ||
event, | ||
plugins | ||
} = {}) { | ||
{ | ||
if (plugins) { | ||
assert_js.assert.isArray(plugins, { | ||
moduleName: 'workbox-precaching', | ||
className: 'PrecacheController', | ||
funcName: 'install', | ||
paramName: 'plugins' | ||
}); | ||
} | ||
} | ||
} | ||
const precacheRequests = urlsToPrecache.map(url => { | ||
return this._addURLToCache({ | ||
event, | ||
plugins, | ||
url | ||
const urlsToPrecache = []; | ||
const urlsAlreadyPrecached = []; | ||
const cache = await caches.open(this._cacheName); | ||
const alreadyCachedRequests = await cache.keys(); | ||
const alreadyCachedURLs = new Set(alreadyCachedRequests.map(request => request.url)); | ||
for (const cacheKey of this._urlsToCacheKeys.values()) { | ||
if (alreadyCachedURLs.has(cacheKey)) { | ||
urlsAlreadyPrecached.push(cacheKey); | ||
} else { | ||
urlsToPrecache.push(cacheKey); | ||
} | ||
} | ||
const precacheRequests = urlsToPrecache.map(url => { | ||
const integrity = this._cacheKeysToIntegrities.get(url); | ||
return this._addURLToCache({ | ||
event, | ||
plugins, | ||
url, | ||
integrity | ||
}); | ||
}); | ||
}); | ||
await Promise.all(precacheRequests); | ||
await Promise.all(precacheRequests); | ||
{ | ||
printInstallDetails(urlsToPrecache, urlsAlreadyPrecached); | ||
{ | ||
printInstallDetails(urlsToPrecache, urlsAlreadyPrecached); | ||
} | ||
return { | ||
updatedURLs: urlsToPrecache, | ||
notUpdatedURLs: urlsAlreadyPrecached | ||
}; | ||
} | ||
/** | ||
* Deletes assets that are no longer present in the current precache manifest. | ||
* Call this method from the service worker activate event. | ||
* | ||
* @return {Promise<workbox.precaching.CleanupResult>} | ||
*/ | ||
return { | ||
updatedURLs: urlsToPrecache, | ||
notUpdatedURLs: urlsAlreadyPrecached | ||
}; | ||
} | ||
/** | ||
* Deletes assets that are no longer present in the current precache manifest. | ||
* Call this method from the service worker activate event. | ||
* | ||
* @return {Promise<workbox.precaching.CleanupResult>} | ||
*/ | ||
async activate() { | ||
const cache = await caches.open(this._cacheName); | ||
const currentlyCachedRequests = await cache.keys(); | ||
const expectedCacheKeys = new Set(this._urlsToCacheKeys.values()); | ||
const deletedURLs = []; | ||
async activate() { | ||
const cache = await caches.open(this._cacheName); | ||
const currentlyCachedRequests = await cache.keys(); | ||
const expectedCacheKeys = new Set(this._urlsToCacheKeys.values()); | ||
const deletedURLs = []; | ||
for (const request of currentlyCachedRequests) { | ||
if (!expectedCacheKeys.has(request.url)) { | ||
await cache.delete(request); | ||
deletedURLs.push(request.url); | ||
} | ||
} | ||
for (const request of currentlyCachedRequests) { | ||
if (!expectedCacheKeys.has(request.url)) { | ||
await cache.delete(request); | ||
deletedURLs.push(request.url); | ||
{ | ||
printCleanupDetails(deletedURLs); | ||
} | ||
} | ||
{ | ||
printCleanupDetails(deletedURLs); | ||
return { | ||
deletedURLs | ||
}; | ||
} | ||
/** | ||
* Requests the entry and saves it to the cache if the response is valid. | ||
* By default, any response with a status code of less than 400 (including | ||
* opaque responses) is considered valid. | ||
* | ||
* If you need to use custom criteria to determine what's valid and what | ||
* isn't, then pass in an item in `options.plugins` that implements the | ||
* `cacheWillUpdate()` lifecycle event. | ||
* | ||
* @private | ||
* @param {Object} options | ||
* @param {string} options.url The URL to fetch and cache. | ||
* @param {Event} [options.event] The install event (if passed). | ||
* @param {Array<Object>} [options.plugins] An array of plugins to apply to | ||
* fetch and caching. | ||
*/ | ||
return { | ||
deletedURLs | ||
}; | ||
} | ||
/** | ||
* Requests the entry and saves it to the cache if the response is valid. | ||
* By default, any response with a status code of less than 400 (including | ||
* opaque responses) is considered valid. | ||
* | ||
* If you need to use custom criteria to determine what's valid and what | ||
* isn't, then pass in an item in `options.plugins` that implements the | ||
* `cacheWillUpdate()` lifecycle event. | ||
* | ||
* @private | ||
* @param {Object} options | ||
* @param {string} options.url The URL to fetch and cache. | ||
* @param {Event} [options.event] The install event (if passed). | ||
* @param {Array<Object>} [options.plugins] An array of plugins to apply to | ||
* fetch and caching. | ||
*/ | ||
async _addURLToCache({ | ||
url, | ||
event, | ||
plugins | ||
}) { | ||
const request = new Request(url, { | ||
credentials: 'same-origin' | ||
}); | ||
let response = await fetchWrapper_mjs.fetchWrapper.fetch({ | ||
async _addURLToCache({ | ||
url, | ||
event, | ||
plugins, | ||
request | ||
}); // Allow developers to override the default logic about what is and isn't | ||
// valid by passing in a plugin implementing cacheWillUpdate(), e.g. | ||
// a workbox.cacheableResponse.Plugin instance. | ||
integrity | ||
}) { | ||
const request = new Request(url, { | ||
integrity, | ||
credentials: 'same-origin' | ||
}); | ||
let response = await fetchWrapper_js.fetchWrapper.fetch({ | ||
event, | ||
plugins, | ||
request | ||
}); // Allow developers to override the default logic about what is and isn't | ||
// valid by passing in a plugin implementing cacheWillUpdate(), e.g. | ||
// a workbox.cacheableResponse.Plugin instance. | ||
let cacheWillUpdateCallback; | ||
let cacheWillUpdatePlugin; | ||
for (const plugin of plugins || []) { | ||
if ('cacheWillUpdate' in plugin) { | ||
cacheWillUpdateCallback = plugin.cacheWillUpdate.bind(plugin); | ||
for (const plugin of plugins || []) { | ||
if ('cacheWillUpdate' in plugin) { | ||
cacheWillUpdatePlugin = plugin; | ||
} | ||
} | ||
} | ||
const isValidResponse = cacheWillUpdateCallback ? // Use a callback if provided. It returns a truthy value if valid. | ||
cacheWillUpdateCallback({ | ||
event, | ||
request, | ||
response | ||
}) : // Otherwise, default to considering any response status under 400 valid. | ||
// This includes, by default, considering opaque responses valid. | ||
response.status < 400; // Consider this a failure, leading to the `install` handler failing, if | ||
// we get back an invalid response. | ||
const isValidResponse = cacheWillUpdatePlugin ? // Use a callback if provided. It returns a truthy value if valid. | ||
// NOTE: invoke the method on the plugin instance so the `this` context | ||
// is correct. | ||
cacheWillUpdatePlugin.cacheWillUpdate({ | ||
event, | ||
request, | ||
response | ||
}) : // Otherwise, default to considering any response status under 400 valid. | ||
// This includes, by default, considering opaque responses valid. | ||
response.status < 400; // Consider this a failure, leading to the `install` handler failing, if | ||
// we get back an invalid response. | ||
if (!isValidResponse) { | ||
throw new WorkboxError_mjs.WorkboxError('bad-precaching-response', { | ||
url, | ||
status: response.status | ||
if (!isValidResponse) { | ||
throw new WorkboxError_js.WorkboxError('bad-precaching-response', { | ||
url, | ||
status: response.status | ||
}); | ||
} | ||
if (response.redirected) { | ||
response = await cleanRedirect(response); | ||
} | ||
await cacheWrapper_js.cacheWrapper.put({ | ||
event, | ||
plugins, | ||
request, | ||
response, | ||
cacheName: this._cacheName, | ||
matchOptions: { | ||
ignoreSearch: true | ||
} | ||
}); | ||
} | ||
/** | ||
* Returns a mapping of a precached URL to the corresponding cache key, taking | ||
* into account the revision information for the URL. | ||
* | ||
* @return {Map<string, string>} A URL to cache key mapping. | ||
*/ | ||
if (response.redirected) { | ||
response = await cleanRedirect(response); | ||
getURLsToCacheKeys() { | ||
return this._urlsToCacheKeys; | ||
} | ||
/** | ||
* Returns a list of all the URLs that have been precached by the current | ||
* service worker. | ||
* | ||
* @return {Array<string>} The precached URLs. | ||
*/ | ||
await cacheWrapper_mjs.cacheWrapper.put({ | ||
event, | ||
plugins, | ||
request, | ||
response, | ||
cacheName: this._cacheName, | ||
matchOptions: { | ||
ignoreSearch: true | ||
} | ||
}); | ||
} | ||
/** | ||
* Returns a mapping of a precached URL to the corresponding cache key, taking | ||
* into account the revision information for the URL. | ||
* | ||
* @return {Map<string, string>} A URL to cache key mapping. | ||
*/ | ||
getCachedURLs() { | ||
return [...this._urlsToCacheKeys.keys()]; | ||
} | ||
/** | ||
* Returns the cache key used for storing a given URL. If that URL is | ||
* unversioned, like `/index.html', then the cache key will be the original | ||
* URL with a search parameter appended to it. | ||
* | ||
* @param {string} url A URL whose cache key you want to look up. | ||
* @return {string} The versioned URL that corresponds to a cache key | ||
* for the original URL, or undefined if that URL isn't precached. | ||
*/ | ||
getURLsToCacheKeys() { | ||
return this._urlsToCacheKeys; | ||
} | ||
/** | ||
* Returns a list of all the URLs that have been precached by the current | ||
* service worker. | ||
* | ||
* @return {Array<string>} The precached URLs. | ||
*/ | ||
getCacheKeyForURL(url) { | ||
const urlObject = new URL(url, location.href); | ||
return this._urlsToCacheKeys.get(urlObject.href); | ||
} | ||
/** | ||
* Returns a function that looks up `url` in the precache (taking into | ||
* account revision information), and returns the corresponding `Response`. | ||
* | ||
* If for an unexpected reason there is a cache miss when looking up `url`, | ||
* this will fall back to retrieving the `Response` via `fetch()`. | ||
* | ||
* @param {string} url The precached URL which will be used to lookup the | ||
* `Response`. | ||
* @return {workbox.routing.Route~handlerCallback} | ||
*/ | ||
getCachedURLs() { | ||
return [...this._urlsToCacheKeys.keys()]; | ||
} | ||
/** | ||
* Returns the cache key used for storing a given URL. If that URL is | ||
* unversioned, like `/index.html', then the cache key will be the original | ||
* URL with a search parameter appended to it. | ||
* | ||
* @param {string} url A URL whose cache key you want to look up. | ||
* @return {string} The versioned URL that corresponds to a cache key | ||
* for the original URL, or undefined if that URL isn't precached. | ||
*/ | ||
createHandlerForURL(url) { | ||
{ | ||
assert_js.assert.isType(url, 'string', { | ||
moduleName: 'workbox-precaching', | ||
funcName: 'createHandlerForURL', | ||
paramName: 'url' | ||
}); | ||
} | ||
getCacheKeyForURL(url) { | ||
const urlObject = new URL(url, location); | ||
return this._urlsToCacheKeys.get(urlObject.href); | ||
} | ||
const cacheKey = this.getCacheKeyForURL(url); | ||
} | ||
if (!cacheKey) { | ||
throw new WorkboxError_js.WorkboxError('non-precached-url', { | ||
url | ||
}); | ||
} | ||
/* | ||
Copyright 2019 Google LLC | ||
return async () => { | ||
try { | ||
const cache = await caches.open(this._cacheName); | ||
const response = await cache.match(cacheKey); | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
let precacheController; | ||
/** | ||
* @return {PrecacheController} | ||
* @private | ||
*/ | ||
if (response) { | ||
return response; | ||
} // This shouldn't normally happen, but there are edge cases: | ||
// https://github.com/GoogleChrome/workbox/issues/1441 | ||
const getOrCreatePrecacheController = () => { | ||
if (!precacheController) { | ||
precacheController = new PrecacheController(); | ||
} | ||
return precacheController; | ||
}; | ||
throw new Error(`The cache ${this._cacheName} did not have an entry ` + `for ${cacheKey}.`); | ||
} catch (error) { | ||
// If there's either a cache miss, or the caches.match() call threw | ||
// an exception, then attempt to fulfill the navigation request with | ||
// a response from the network rather than leaving the user with a | ||
// failed navigation. | ||
{ | ||
logger_js.logger.debug(`Unable to respond to navigation request with ` + `cached response. Falling back to network.`, error); | ||
} // This might still fail if the browser is offline... | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Removes any URL search parameters that should be ignored. | ||
* | ||
* @param {URL} urlObject The original URL. | ||
* @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against | ||
* each search parameter name. Matches mean that the search parameter should be | ||
* ignored. | ||
* @return {URL} The URL with any ignored search parameters removed. | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
return fetch(cacheKey); | ||
} | ||
}; | ||
} | ||
function removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching) { | ||
// Convert the iterable into an array at the start of the loop to make sure | ||
// deletion doesn't mess up iteration. | ||
for (const paramName of [...urlObject.searchParams.keys()]) { | ||
if (ignoreURLParametersMatching.some(regExp => regExp.test(paramName))) { | ||
urlObject.searchParams.delete(paramName); | ||
} | ||
} | ||
return urlObject; | ||
} | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
let precacheController; | ||
/** | ||
* @return {PrecacheController} | ||
* @private | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Generator function that yields possible variations on the original URL to | ||
* check, one at a time. | ||
* | ||
* @param {string} url | ||
* @param {Object} options | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
const getOrCreatePrecacheController = () => { | ||
if (!precacheController) { | ||
precacheController = new PrecacheController(); | ||
} | ||
function* generateURLVariations(url, { | ||
ignoreURLParametersMatching, | ||
directoryIndex, | ||
cleanURLs, | ||
urlManipulation | ||
} = {}) { | ||
const urlObject = new URL(url, location); | ||
urlObject.hash = ''; | ||
yield urlObject.href; | ||
const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching); | ||
yield urlWithoutIgnoredParams.href; | ||
return precacheController; | ||
}; | ||
if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) { | ||
const directoryURL = new URL(urlWithoutIgnoredParams); | ||
directoryURL.pathname += directoryIndex; | ||
yield directoryURL.href; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
if (cleanURLs) { | ||
const cleanURL = new URL(urlWithoutIgnoredParams); | ||
cleanURL.pathname += '.html'; | ||
yield cleanURL.href; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Removes any URL search parameters that should be ignored. | ||
* | ||
* @param {URL} urlObject The original URL. | ||
* @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against | ||
* each search parameter name. Matches mean that the search parameter should be | ||
* ignored. | ||
* @return {URL} The URL with any ignored search parameters removed. | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
function removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching = []) { | ||
// Convert the iterable into an array at the start of the loop to make sure | ||
// deletion doesn't mess up iteration. | ||
for (const paramName of [...urlObject.searchParams.keys()]) { | ||
if (ignoreURLParametersMatching.some(regExp => regExp.test(paramName))) { | ||
urlObject.searchParams.delete(paramName); | ||
} | ||
} | ||
return urlObject; | ||
} | ||
if (urlManipulation) { | ||
const additionalURLs = urlManipulation({ | ||
url: urlObject | ||
}); | ||
/* | ||
Copyright 2019 Google LLC | ||
for (const urlToAttempt of additionalURLs) { | ||
yield urlToAttempt.href; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Generator function that yields possible variations on the original URL to | ||
* check, one at a time. | ||
* | ||
* @param {string} url | ||
* @param {Object} options | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
function* generateURLVariations(url, { | ||
ignoreURLParametersMatching, | ||
directoryIndex, | ||
cleanURLs, | ||
urlManipulation | ||
} = {}) { | ||
const urlObject = new URL(url, location.href); | ||
urlObject.hash = ''; | ||
yield urlObject.href; | ||
const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching); | ||
yield urlWithoutIgnoredParams.href; | ||
if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) { | ||
const directoryURL = new URL(urlWithoutIgnoredParams.href); | ||
directoryURL.pathname += directoryIndex; | ||
yield directoryURL.href; | ||
} | ||
if (cleanURLs) { | ||
const cleanURL = new URL(urlWithoutIgnoredParams.href); | ||
cleanURL.pathname += '.html'; | ||
yield cleanURL.href; | ||
} | ||
if (urlManipulation) { | ||
const additionalURLs = urlManipulation({ | ||
url: urlObject | ||
}); | ||
for (const urlToAttempt of additionalURLs) { | ||
yield urlToAttempt.href; | ||
} | ||
} | ||
} | ||
} | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* This function will take the request URL and manipulate it based on the | ||
* configuration options. | ||
* | ||
* @param {string} url | ||
* @param {Object} options | ||
* @return {string} Returns the URL in the cache that matches the request, | ||
* if possible. | ||
* | ||
* @private | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* This function will take the request URL and manipulate it based on the | ||
* configuration options. | ||
* | ||
* @param {string} url | ||
* @param {Object} options | ||
* @return {string} Returns the URL in the cache that matches the request, | ||
* if possible. | ||
* | ||
* @private | ||
*/ | ||
const getCacheKeyForURL = (url, options) => { | ||
const precacheController = getOrCreatePrecacheController(); | ||
const urlsToCacheKeys = precacheController.getURLsToCacheKeys(); | ||
const getCacheKeyForURL = (url, options) => { | ||
const precacheController = getOrCreatePrecacheController(); | ||
const urlsToCacheKeys = precacheController.getURLsToCacheKeys(); | ||
for (const possibleURL of generateURLVariations(url, options)) { | ||
const possibleCacheKey = urlsToCacheKeys.get(possibleURL); | ||
for (const possibleURL of generateURLVariations(url, options)) { | ||
const possibleCacheKey = urlsToCacheKeys.get(possibleURL); | ||
if (possibleCacheKey) { | ||
return possibleCacheKey; | ||
if (possibleCacheKey) { | ||
return possibleCacheKey; | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Adds a `fetch` listener to the service worker that will | ||
* respond to | ||
* [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests} | ||
* with precached assets. | ||
* | ||
* Requests for assets that aren't precached, the `FetchEvent` will not be | ||
* responded to, allowing the event to fall through to other `fetch` event | ||
* listeners. | ||
* | ||
* NOTE: when called more than once this method will replace the previously set | ||
* configuration options. Calling it more than once is not recommended outside | ||
* of tests. | ||
* | ||
* @private | ||
* @param {Object} options | ||
* @param {string} [options.directoryIndex=index.html] The `directoryIndex` will | ||
* check cache entries for a URLs ending with '/' to see if there is a hit when | ||
* appending the `directoryIndex` value. | ||
* @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/]] An | ||
* array of regex's to remove search params when looking for a cache match. | ||
* @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will | ||
* check the cache for the URL with a `.html` added to the end of the end. | ||
* @param {workbox.precaching~urlManipulation} [options.urlManipulation] | ||
* This is a function that should take a URL and return an array of | ||
* alternative URL's that should be checked for precache matches. | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Adds a `fetch` listener to the service worker that will | ||
* respond to | ||
* [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests} | ||
* with precached assets. | ||
* | ||
* Requests for assets that aren't precached, the `FetchEvent` will not be | ||
* responded to, allowing the event to fall through to other `fetch` event | ||
* listeners. | ||
* | ||
* NOTE: when called more than once this method will replace the previously set | ||
* configuration options. Calling it more than once is not recommended outside | ||
* of tests. | ||
* | ||
* @private | ||
* @param {Object} [options] | ||
* @param {string} [options.directoryIndex=index.html] The `directoryIndex` will | ||
* check cache entries for a URLs ending with '/' to see if there is a hit when | ||
* appending the `directoryIndex` value. | ||
* @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/]] An | ||
* array of regex's to remove search params when looking for a cache match. | ||
* @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will | ||
* check the cache for the URL with a `.html` added to the end of the end. | ||
* @param {workbox.precaching~urlManipulation} [options.urlManipulation] | ||
* This is a function that should take a URL and return an array of | ||
* alternative URLs that should be checked for precache matches. | ||
*/ | ||
const addFetchListener = ({ | ||
ignoreURLParametersMatching = [/^utm_/], | ||
directoryIndex = 'index.html', | ||
cleanURLs = true, | ||
urlManipulation = null | ||
} = {}) => { | ||
const cacheName = cacheNames_mjs.cacheNames.getPrecacheName(); | ||
addEventListener('fetch', event => { | ||
const precachedURL = getCacheKeyForURL(event.request.url, { | ||
cleanURLs, | ||
directoryIndex, | ||
ignoreURLParametersMatching, | ||
urlManipulation | ||
}); | ||
const addFetchListener = ({ | ||
ignoreURLParametersMatching = [/^utm_/], | ||
directoryIndex = 'index.html', | ||
cleanURLs = true, | ||
urlManipulation | ||
} = {}) => { | ||
const cacheName = cacheNames_js.cacheNames.getPrecacheName(); | ||
addEventListener('fetch', event => { | ||
const precachedURL = getCacheKeyForURL(event.request.url, { | ||
cleanURLs, | ||
directoryIndex, | ||
ignoreURLParametersMatching, | ||
urlManipulation | ||
}); | ||
if (!precachedURL) { | ||
{ | ||
logger_mjs.logger.debug(`Precaching did not find a match for ` + getFriendlyURL_mjs.getFriendlyURL(event.request.url)); | ||
if (!precachedURL) { | ||
{ | ||
logger_js.logger.debug(`Precaching did not find a match for ` + getFriendlyURL_js.getFriendlyURL(event.request.url)); | ||
} | ||
return; | ||
} | ||
return; | ||
} | ||
let responsePromise = caches.open(cacheName).then(cache => { | ||
return cache.match(precachedURL); | ||
}).then(cachedResponse => { | ||
if (cachedResponse) { | ||
return cachedResponse; | ||
} // Fall back to the network if we don't have a cached response | ||
// (perhaps due to manual cache cleanup). | ||
let responsePromise = caches.open(cacheName).then(cache => { | ||
return cache.match(precachedURL); | ||
}).then(cachedResponse => { | ||
if (cachedResponse) { | ||
return cachedResponse; | ||
} // Fall back to the network if we don't have a cached response | ||
// (perhaps due to manual cache cleanup). | ||
{ | ||
logger_js.logger.warn(`The precached response for ` + `${getFriendlyURL_js.getFriendlyURL(precachedURL)} in ${cacheName} was not found. ` + `Falling back to the network instead.`); | ||
} | ||
return fetch(precachedURL); | ||
}); | ||
{ | ||
logger_mjs.logger.warn(`The precached response for ` + `${getFriendlyURL_mjs.getFriendlyURL(precachedURL)} in ${cacheName} was not found. ` + `Falling back to the network instead.`); | ||
responsePromise = responsePromise.then(response => { | ||
// Workbox is going to handle the route. | ||
// print the routing details to the console. | ||
logger_js.logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL_js.getFriendlyURL(event.request.url)); | ||
logger_js.logger.log(`Serving the precached url: ${precachedURL}`); | ||
logger_js.logger.groupCollapsed(`View request details here.`); | ||
logger_js.logger.log(event.request); | ||
logger_js.logger.groupEnd(); | ||
logger_js.logger.groupCollapsed(`View response details here.`); | ||
logger_js.logger.log(response); | ||
logger_js.logger.groupEnd(); | ||
logger_js.logger.groupEnd(); | ||
return response; | ||
}); | ||
} | ||
return fetch(precachedURL); | ||
event.respondWith(responsePromise); | ||
}); | ||
}; | ||
{ | ||
responsePromise = responsePromise.then(response => { | ||
// Workbox is going to handle the route. | ||
// print the routing details to the console. | ||
logger_mjs.logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL_mjs.getFriendlyURL(event.request.url)); | ||
logger_mjs.logger.log(`Serving the precached url: ${precachedURL}`); | ||
logger_mjs.logger.groupCollapsed(`View request details here.`); | ||
logger_mjs.logger.log(event.request); | ||
logger_mjs.logger.groupEnd(); | ||
logger_mjs.logger.groupCollapsed(`View response details here.`); | ||
logger_mjs.logger.log(response); | ||
logger_mjs.logger.groupEnd(); | ||
logger_mjs.logger.groupEnd(); | ||
return response; | ||
}); | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
let listenerAdded = false; | ||
/** | ||
* Add a `fetch` listener to the service worker that will | ||
* respond to | ||
* [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests} | ||
* with precached assets. | ||
* | ||
* Requests for assets that aren't precached, the `FetchEvent` will not be | ||
* responded to, allowing the event to fall through to other `fetch` event | ||
* listeners. | ||
* | ||
* @param {Object} [options] | ||
* @param {string} [options.directoryIndex=index.html] The `directoryIndex` will | ||
* check cache entries for a URLs ending with '/' to see if there is a hit when | ||
* appending the `directoryIndex` value. | ||
* @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/]] An | ||
* array of regex's to remove search params when looking for a cache match. | ||
* @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will | ||
* check the cache for the URL with a `.html` added to the end of the end. | ||
* @param {workbox.precaching~urlManipulation} [options.urlManipulation] | ||
* This is a function that should take a URL and return an array of | ||
* alternative URLs that should be checked for precache matches. | ||
* | ||
* @alias workbox.precaching.addRoute | ||
*/ | ||
const addRoute = options => { | ||
if (!listenerAdded) { | ||
addFetchListener(options); | ||
listenerAdded = true; | ||
} | ||
}; | ||
event.respondWith(responsePromise); | ||
}); | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
let listenerAdded = false; | ||
/** | ||
* Add a `fetch` listener to the service worker that will | ||
* respond to | ||
* [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests} | ||
* with precached assets. | ||
* | ||
* Requests for assets that aren't precached, the `FetchEvent` will not be | ||
* responded to, allowing the event to fall through to other `fetch` event | ||
* listeners. | ||
* | ||
* @param {Object} options | ||
* @param {string} [options.directoryIndex=index.html] The `directoryIndex` will | ||
* check cache entries for a URLs ending with '/' to see if there is a hit when | ||
* appending the `directoryIndex` value. | ||
* @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/]] An | ||
* array of regex's to remove search params when looking for a cache match. | ||
* @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will | ||
* check the cache for the URL with a `.html` added to the end of the end. | ||
* @param {workbox.precaching~urlManipulation} [options.urlManipulation] | ||
* This is a function that should take a URL and return an array of | ||
* alternative URL's that should be checked for precache matches. | ||
* | ||
* @alias workbox.precaching.addRoute | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const SUBSTRING_TO_FIND = '-precache-'; | ||
/** | ||
* Cleans up incompatible precaches that were created by older versions of | ||
* Workbox, by a service worker registered under the current scope. | ||
* | ||
* This is meant to be called as part of the `activate` event. | ||
* | ||
* This should be safe to use as long as you don't include `substringToFind` | ||
* (defaulting to `-precache-`) in your non-precache cache names. | ||
* | ||
* @param {string} currentPrecacheName The cache name currently in use for | ||
* precaching. This cache won't be deleted. | ||
* @param {string} [substringToFind='-precache-'] Cache names which include this | ||
* substring will be deleted (excluding `currentPrecacheName`). | ||
* @return {Array<string>} A list of all the cache names that were deleted. | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
const addRoute = options => { | ||
if (!listenerAdded) { | ||
addFetchListener(options); | ||
listenerAdded = true; | ||
} | ||
}; | ||
const deleteOutdatedCaches = async (currentPrecacheName, substringToFind = SUBSTRING_TO_FIND) => { | ||
const cacheNames = await caches.keys(); | ||
const cacheNamesToDelete = cacheNames.filter(cacheName => { | ||
return cacheName.includes(substringToFind) && cacheName.includes(self.registration.scope) && cacheName !== currentPrecacheName; | ||
}); | ||
await Promise.all(cacheNamesToDelete.map(cacheName => caches.delete(cacheName))); | ||
return cacheNamesToDelete; | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const SUBSTRING_TO_FIND = '-precache-'; | ||
/** | ||
* Cleans up incompatible precaches that were created by older versions of | ||
* Workbox, by a service worker registered under the current scope. | ||
* | ||
* This is meant to be called as part of the `activate` event. | ||
* | ||
* This should be safe to use as long as you don't include `substringToFind` | ||
* (defaulting to `-precache-`) in your non-precache cache names. | ||
* | ||
* @param {string} currentPrecacheName The cache name currently in use for | ||
* precaching. This cache won't be deleted. | ||
* @param {string} [substringToFind='-precache-'] Cache names which include this | ||
* substring will be deleted (excluding `currentPrecacheName`). | ||
* @return {Array<string>} A list of all the cache names that were deleted. | ||
* | ||
* @private | ||
* @memberof module:workbox-precaching | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Adds an `activate` event listener which will clean up incompatible | ||
* precaches that were created by older versions of Workbox. | ||
* | ||
* @alias workbox.precaching.cleanupOutdatedCaches | ||
*/ | ||
const deleteOutdatedCaches = async (currentPrecacheName, substringToFind = SUBSTRING_TO_FIND) => { | ||
const cacheNames = await caches.keys(); | ||
const cacheNamesToDelete = cacheNames.filter(cacheName => { | ||
return cacheName.includes(substringToFind) && cacheName.includes(self.registration.scope) && cacheName !== currentPrecacheName; | ||
}); | ||
await Promise.all(cacheNamesToDelete.map(cacheName => caches.delete(cacheName))); | ||
return cacheNamesToDelete; | ||
}; | ||
const cleanupOutdatedCaches = () => { | ||
addEventListener('activate', event => { | ||
const cacheName = cacheNames_js.cacheNames.getPrecacheName(); | ||
event.waitUntil(deleteOutdatedCaches(cacheName).then(cachesDeleted => { | ||
{ | ||
if (cachesDeleted.length > 0) { | ||
logger_js.logger.log(`The following out-of-date precaches were cleaned up ` + `automatically:`, cachesDeleted); | ||
} | ||
} | ||
})); | ||
}); | ||
}; | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Adds an `activate` event listener which will clean up incompatible | ||
* precaches that were created by older versions of Workbox. | ||
* | ||
* @alias workbox.precaching.cleanupOutdatedCaches | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Helper function that calls | ||
* {@link PrecacheController#createHandlerForURL} on the default | ||
* {@link PrecacheController} instance. | ||
* | ||
* If you are creating your own {@link PrecacheController}, then call the | ||
* {@link PrecacheController#createHandlerForURL} on that instance, | ||
* instead of using this function. | ||
* | ||
* @param {string} url The precached URL which will be used to lookup the | ||
* `Response`. | ||
* @return {workbox.routing.Route~handlerCallback} | ||
* | ||
* @alias workbox.precaching.createHandlerForURL | ||
*/ | ||
const cleanupOutdatedCaches = () => { | ||
addEventListener('activate', event => { | ||
const cacheName = cacheNames_mjs.cacheNames.getPrecacheName(); | ||
event.waitUntil(deleteOutdatedCaches(cacheName).then(cachesDeleted => { | ||
{ | ||
if (cachesDeleted.length > 0) { | ||
logger_mjs.logger.log(`The following out-of-date precaches were cleaned up ` + `automatically:`, cachesDeleted); | ||
} | ||
} | ||
})); | ||
}); | ||
}; | ||
const createHandlerForURL = url => { | ||
const precacheController = getOrCreatePrecacheController(); | ||
return precacheController.createHandlerForURL(url); | ||
}; | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Takes in a URL, and returns the corresponding URL that could be used to | ||
* lookup the entry in the precache. | ||
* | ||
* If a relative URL is provided, the location of the service worker file will | ||
* be used as the base. | ||
* | ||
* For precached entries without revision information, the cache key will be the | ||
* same as the original URL. | ||
* | ||
* For precached entries with revision information, the cache key will be the | ||
* original URL with the addition of a query parameter used for keeping track of | ||
* the revision info. | ||
* | ||
* @param {string} url The URL whose cache key to look up. | ||
* @return {string} The cache key that corresponds to that URL. | ||
* | ||
* @alias workbox.precaching.getCacheKeyForURL | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Takes in a URL, and returns the corresponding URL that could be used to | ||
* lookup the entry in the precache. | ||
* | ||
* If a relative URL is provided, the location of the service worker file will | ||
* be used as the base. | ||
* | ||
* For precached entries without revision information, the cache key will be the | ||
* same as the original URL. | ||
* | ||
* For precached entries with revision information, the cache key will be the | ||
* original URL with the addition of a query parameter used for keeping track of | ||
* the revision info. | ||
* | ||
* @param {string} url The URL whose cache key to look up. | ||
* @return {string} The cache key that corresponds to that URL. | ||
* | ||
* @alias workbox.precaching.getCacheKeyForURL | ||
*/ | ||
const getCacheKeyForURL$1 = url => { | ||
const precacheController = getOrCreatePrecacheController(); | ||
return precacheController.getCacheKeyForURL(url); | ||
}; | ||
const getCacheKeyForURL$1 = url => { | ||
const precacheController = getOrCreatePrecacheController(); | ||
return precacheController.getCacheKeyForURL(url); | ||
}; | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const installListener = event => { | ||
const precacheController = getOrCreatePrecacheController(); | ||
const plugins = precachePlugins.get(); | ||
event.waitUntil(precacheController.install({ | ||
event, | ||
plugins | ||
}).catch(error => { | ||
{ | ||
logger_mjs.logger.error(`Service worker installation failed. It will ` + `be retried automatically during the next navigation.`); | ||
} // Re-throw the error to ensure installation fails. | ||
const installListener = event => { | ||
const precacheController = getOrCreatePrecacheController(); | ||
const plugins = precachePlugins.get(); | ||
event.waitUntil(precacheController.install({ | ||
event, | ||
plugins | ||
}).catch(error => { | ||
{ | ||
logger_js.logger.error(`Service worker installation failed. It will ` + `be retried automatically during the next navigation.`); | ||
} // Re-throw the error to ensure installation fails. | ||
throw error; | ||
})); | ||
}; | ||
throw error; | ||
})); | ||
}; | ||
const activateListener = event => { | ||
const precacheController = getOrCreatePrecacheController(); | ||
const plugins = precachePlugins.get(); | ||
event.waitUntil(precacheController.activate({ | ||
event, | ||
plugins | ||
})); | ||
}; | ||
/** | ||
* Adds items to the precache list, removing any duplicates and | ||
* stores the files in the | ||
* ["precache cache"]{@link module:workbox-core.cacheNames} when the service | ||
* worker installs. | ||
* | ||
* This method can be called multiple times. | ||
* | ||
* Please note: This method **will not** serve any of the cached files for you. | ||
* It only precaches files. To respond to a network request you call | ||
* [addRoute()]{@link module:workbox-precaching.addRoute}. | ||
* | ||
* If you have a single array of files to precache, you can just call | ||
* [precacheAndRoute()]{@link module:workbox-precaching.precacheAndRoute}. | ||
* | ||
* @param {Array<Object|string>} entries Array of entries to precache. | ||
* | ||
* @alias workbox.precaching.precache | ||
*/ | ||
const activateListener = event => { | ||
const precacheController = getOrCreatePrecacheController(); | ||
event.waitUntil(precacheController.activate()); | ||
}; | ||
/** | ||
* Adds items to the precache list, removing any duplicates and | ||
* stores the files in the | ||
* ["precache cache"]{@link module:workbox-core.cacheNames} when the service | ||
* worker installs. | ||
* | ||
* This method can be called multiple times. | ||
* | ||
* Please note: This method **will not** serve any of the cached files for you. | ||
* It only precaches files. To respond to a network request you call | ||
* [addRoute()]{@link module:workbox-precaching.addRoute}. | ||
* | ||
* If you have a single array of files to precache, you can just call | ||
* [precacheAndRoute()]{@link module:workbox-precaching.precacheAndRoute}. | ||
* | ||
* @param {Array<Object|string>} [entries=[]] Array of entries to precache. | ||
* | ||
* @alias workbox.precaching.precache | ||
*/ | ||
const precache = entries => { | ||
const precacheController = getOrCreatePrecacheController(); | ||
precacheController.addToCacheList(entries); | ||
const precache = entries => { | ||
const precacheController = getOrCreatePrecacheController(); | ||
precacheController.addToCacheList(entries); | ||
if (entries.length > 0) { | ||
// NOTE: these listeners will only be added once (even if the `precache()` | ||
// method is called multiple times) because event listeners are implemented | ||
// as a set, where each listener must be unique. | ||
addEventListener('install', installListener); | ||
addEventListener('activate', activateListener); | ||
} | ||
}; | ||
if (entries.length > 0) { | ||
// NOTE: these listeners will only be added once (even if the `precache()` | ||
// method is called multiple times) because event listeners are implemented | ||
// as a set, where each listener must be unique. | ||
addEventListener('install', installListener); | ||
addEventListener('activate', activateListener); | ||
} | ||
}; | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* This method will add entries to the precache list and add a route to | ||
* respond to fetch events. | ||
* | ||
* This is a convenience method that will call | ||
* [precache()]{@link module:workbox-precaching.precache} and | ||
* [addRoute()]{@link module:workbox-precaching.addRoute} in a single call. | ||
* | ||
* @param {Array<Object|string>} entries Array of entries to precache. | ||
* @param {Object} options See | ||
* [addRoute() options]{@link module:workbox-precaching.addRoute}. | ||
* | ||
* @alias workbox.precaching.precacheAndRoute | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* This method will add entries to the precache list and add a route to | ||
* respond to fetch events. | ||
* | ||
* This is a convenience method that will call | ||
* [precache()]{@link module:workbox-precaching.precache} and | ||
* [addRoute()]{@link module:workbox-precaching.addRoute} in a single call. | ||
* | ||
* @param {Array<Object|string>} entries Array of entries to precache. | ||
* @param {Object} [options] See | ||
* [addRoute() options]{@link module:workbox-precaching.addRoute}. | ||
* | ||
* @alias workbox.precaching.precacheAndRoute | ||
*/ | ||
const precacheAndRoute = (entries, options) => { | ||
precache(entries); | ||
addRoute(options); | ||
}; | ||
const precacheAndRoute = (entries, options) => { | ||
precache(entries); | ||
addRoute(options); | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
{ | ||
assert_mjs.assert.isSWEnv('workbox-precaching'); | ||
} | ||
{ | ||
assert_js.assert.isSWEnv('workbox-precaching'); | ||
} | ||
exports.addPlugins = addPlugins; | ||
exports.addRoute = addRoute; | ||
exports.cleanupOutdatedCaches = cleanupOutdatedCaches; | ||
exports.getCacheKeyForURL = getCacheKeyForURL$1; | ||
exports.precache = precache; | ||
exports.precacheAndRoute = precacheAndRoute; | ||
exports.PrecacheController = PrecacheController; | ||
exports.PrecacheController = PrecacheController; | ||
exports.addPlugins = addPlugins; | ||
exports.addRoute = addRoute; | ||
exports.cleanupOutdatedCaches = cleanupOutdatedCaches; | ||
exports.createHandlerForURL = createHandlerForURL; | ||
exports.getCacheKeyForURL = getCacheKeyForURL$1; | ||
exports.precache = precache; | ||
exports.precacheAndRoute = precacheAndRoute; | ||
return exports; | ||
return exports; | ||
}({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private)); | ||
@@ -1,2 +0,2 @@ | ||
this.workbox=this.workbox||{},this.workbox.precaching=function(t,e,n,s,c){"use strict";try{self["workbox:precaching:4.3.1"]&&_()}catch(t){}const o=[],i={get:()=>o,add(t){o.push(...t)}};const a="__WB_REVISION__";function r(t){if(!t)throw new c.WorkboxError("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new c.WorkboxError("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location);return{cacheKey:t.href,url:t.href}}const s=new URL(n,location),o=new URL(n,location);return o.searchParams.set(a,e),{cacheKey:o.href,url:s.href}}class l{constructor(t){this.t=e.cacheNames.getPrecacheName(t),this.s=new Map}addToCacheList(t){for(const e of t){const{cacheKey:t,url:n}=r(e);if(this.s.has(n)&&this.s.get(n)!==t)throw new c.WorkboxError("add-to-cache-list-conflicting-entries",{firstEntry:this.s.get(n),secondEntry:t});this.s.set(n,t)}}async install({event:t,plugins:e}={}){const n=[],s=[],c=await caches.open(this.t),o=await c.keys(),i=new Set(o.map(t=>t.url));for(const t of this.s.values())i.has(t)?s.push(t):n.push(t);const a=n.map(n=>this.o({event:t,plugins:e,url:n}));return await Promise.all(a),{updatedURLs:n,notUpdatedURLs:s}}async activate(){const t=await caches.open(this.t),e=await t.keys(),n=new Set(this.s.values()),s=[];for(const c of e)n.has(c.url)||(await t.delete(c),s.push(c.url));return{deletedURLs:s}}async o({url:t,event:e,plugins:o}){const i=new Request(t,{credentials:"same-origin"});let a,r=await s.fetchWrapper.fetch({event:e,plugins:o,request:i});for(const t of o||[])"cacheWillUpdate"in t&&(a=t.cacheWillUpdate.bind(t));if(!(a?a({event:e,request:i,response:r}):r.status<400))throw new c.WorkboxError("bad-precaching-response",{url:t,status:r.status});r.redirected&&(r=await async function(t){const e=t.clone(),n="body"in e?Promise.resolve(e.body):e.blob(),s=await n;return new Response(s,{headers:e.headers,status:e.status,statusText:e.statusText})}(r)),await n.cacheWrapper.put({event:e,plugins:o,request:i,response:r,cacheName:this.t,matchOptions:{ignoreSearch:!0}})}getURLsToCacheKeys(){return this.s}getCachedURLs(){return[...this.s.keys()]}getCacheKeyForURL(t){const e=new URL(t,location);return this.s.get(e.href)}}let u;const h=()=>(u||(u=new l),u);const d=(t,e)=>{const n=h().getURLsToCacheKeys();for(const s of function*(t,{ignoreURLParametersMatching:e,directoryIndex:n,cleanURLs:s,urlManipulation:c}={}){const o=new URL(t,location);o.hash="",yield o.href;const i=function(t,e){for(const n of[...t.searchParams.keys()])e.some(t=>t.test(n))&&t.searchParams.delete(n);return t}(o,e);if(yield i.href,n&&i.pathname.endsWith("/")){const t=new URL(i);t.pathname+=n,yield t.href}if(s){const t=new URL(i);t.pathname+=".html",yield t.href}if(c){const t=c({url:o});for(const e of t)yield e.href}}(t,e)){const t=n.get(s);if(t)return t}};let w=!1;const f=t=>{w||((({ignoreURLParametersMatching:t=[/^utm_/],directoryIndex:n="index.html",cleanURLs:s=!0,urlManipulation:c=null}={})=>{const o=e.cacheNames.getPrecacheName();addEventListener("fetch",e=>{const i=d(e.request.url,{cleanURLs:s,directoryIndex:n,ignoreURLParametersMatching:t,urlManipulation:c});if(!i)return;let a=caches.open(o).then(t=>t.match(i)).then(t=>t||fetch(i));e.respondWith(a)})})(t),w=!0)},y=t=>{const e=h(),n=i.get();t.waitUntil(e.install({event:t,plugins:n}).catch(t=>{throw t}))},p=t=>{const e=h(),n=i.get();t.waitUntil(e.activate({event:t,plugins:n}))},L=t=>{h().addToCacheList(t),t.length>0&&(addEventListener("install",y),addEventListener("activate",p))};return t.addPlugins=(t=>{i.add(t)}),t.addRoute=f,t.cleanupOutdatedCaches=(()=>{addEventListener("activate",t=>{const n=e.cacheNames.getPrecacheName();t.waitUntil((async(t,e="-precache-")=>{const n=(await caches.keys()).filter(n=>n.includes(e)&&n.includes(self.registration.scope)&&n!==t);return await Promise.all(n.map(t=>caches.delete(t))),n})(n).then(t=>{}))})}),t.getCacheKeyForURL=(t=>{return h().getCacheKeyForURL(t)}),t.precache=L,t.precacheAndRoute=((t,e)=>{L(t),f(e)}),t.PrecacheController=l,t}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private); | ||
this.workbox=this.workbox||{},this.workbox.precaching=function(t,e,n,s,i){"use strict";try{self["workbox:precaching:5.0.0-alpha.2"]&&_()}catch(t){}const r=[],c={get:()=>r,add(t){r.push(...t)}};const o="__WB_REVISION__";function a(t){if(!t)throw new i.WorkboxError("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location.href);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new i.WorkboxError("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location.href);return{cacheKey:t.href,url:t.href}}const s=new URL(n,location.href),r=new URL(n,location.href);return s.searchParams.set(o,e),{cacheKey:s.href,url:r.href}}class h{constructor(t){this.t=e.cacheNames.getPrecacheName(t),this.s=new Map,this.i=new Map}addToCacheList(t){for(const e of t){const{cacheKey:t,url:n}=a(e);if(this.s.has(n)&&this.s.get(n)!==t)throw new i.WorkboxError("add-to-cache-list-conflicting-entries",{firstEntry:this.s.get(n),secondEntry:t});if("string"!=typeof e&&e.integrity){if(this.i.has(t)&&this.i.get(t)!==e.integrity)throw new i.WorkboxError("add-to-cache-list-conflicting-integrities",{url:n});this.i.set(t,e.integrity)}this.s.set(n,t)}}async install({event:t,plugins:e}={}){const n=[],s=[],i=await caches.open(this.t),r=await i.keys(),c=new Set(r.map(t=>t.url));for(const t of this.s.values())c.has(t)?s.push(t):n.push(t);const o=n.map(n=>{const s=this.i.get(n);return this.o({event:t,plugins:e,url:n,integrity:s})});return await Promise.all(o),{updatedURLs:n,notUpdatedURLs:s}}async activate(){const t=await caches.open(this.t),e=await t.keys(),n=new Set(this.s.values()),s=[];for(const i of e)n.has(i.url)||(await t.delete(i),s.push(i.url));return{deletedURLs:s}}async o({url:t,event:e,plugins:r,integrity:c}){const o=new Request(t,{integrity:c,credentials:"same-origin"});let a,h=await s.fetchWrapper.fetch({event:e,plugins:r,request:o});for(const t of r||[])"cacheWillUpdate"in t&&(a=t);if(!(a?a.cacheWillUpdate({event:e,request:o,response:h}):h.status<400))throw new i.WorkboxError("bad-precaching-response",{url:t,status:h.status});h.redirected&&(h=await async function(t){const e=t.clone(),n="body"in e?Promise.resolve(e.body):e.blob(),s=await n;return new Response(s,{headers:e.headers,status:e.status,statusText:e.statusText})}(h)),await n.cacheWrapper.put({event:e,plugins:r,request:o,response:h,cacheName:this.t,matchOptions:{ignoreSearch:!0}})}getURLsToCacheKeys(){return this.s}getCachedURLs(){return[...this.s.keys()]}getCacheKeyForURL(t){const e=new URL(t,location.href);return this.s.get(e.href)}createHandlerForURL(t){const e=this.getCacheKeyForURL(t);if(!e)throw new i.WorkboxError("non-precached-url",{url:t});return async()=>{try{const t=await caches.open(this.t),n=await t.match(e);if(n)return n;throw new Error(`The cache ${this.t} did not have an entry `+`for ${e}.`)}catch(t){return fetch(e)}}}}let l;const u=()=>(l||(l=new h),l);const d=(t,e)=>{const n=u().getURLsToCacheKeys();for(const s of function*(t,{ignoreURLParametersMatching:e,directoryIndex:n,cleanURLs:s,urlManipulation:i}={}){const r=new URL(t,location.href);r.hash="",yield r.href;const c=function(t,e=[]){for(const n of[...t.searchParams.keys()])e.some(t=>t.test(n))&&t.searchParams.delete(n);return t}(r,e);if(yield c.href,n&&c.pathname.endsWith("/")){const t=new URL(c.href);t.pathname+=n,yield t.href}if(s){const t=new URL(c.href);t.pathname+=".html",yield t.href}if(i){const t=i({url:r});for(const e of t)yield e.href}}(t,e)){const t=n.get(s);if(t)return t}};let w=!1;const f=t=>{w||((({ignoreURLParametersMatching:t=[/^utm_/],directoryIndex:n="index.html",cleanURLs:s=!0,urlManipulation:i}={})=>{const r=e.cacheNames.getPrecacheName();addEventListener("fetch",e=>{const c=d(e.request.url,{cleanURLs:s,directoryIndex:n,ignoreURLParametersMatching:t,urlManipulation:i});if(!c)return;let o=caches.open(r).then(t=>t.match(c)).then(t=>t||fetch(c));e.respondWith(o)})})(t),w=!0)},y=t=>{const e=u(),n=c.get();t.waitUntil(e.install({event:t,plugins:n}).catch(t=>{throw t}))},p=t=>{const e=u();t.waitUntil(e.activate())},g=t=>{u().addToCacheList(t),t.length>0&&(addEventListener("install",y),addEventListener("activate",p))};return t.PrecacheController=h,t.addPlugins=(t=>{c.add(t)}),t.addRoute=f,t.cleanupOutdatedCaches=(()=>{addEventListener("activate",t=>{const n=e.cacheNames.getPrecacheName();t.waitUntil((async(t,e="-precache-")=>{const n=(await caches.keys()).filter(n=>n.includes(e)&&n.includes(self.registration.scope)&&n!==t);return await Promise.all(n.map(t=>caches.delete(t))),n})(n).then(t=>{}))})}),t.createHandlerForURL=(t=>{return u().createHandlerForURL(t)}),t.getCacheKeyForURL=(t=>{return u().getCacheKeyForURL(t)}),t.precache=g,t.precacheAndRoute=((t,e)=>{g(t),f(e)}),t}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private); | ||
this.workbox = this.workbox || {}; | ||
this.workbox.rangeRequests = (function (exports, WorkboxError_mjs, assert_mjs, logger_mjs) { | ||
'use strict'; | ||
this.workbox.rangeRequests = (function (exports, WorkboxError_js, assert_js, logger_js) { | ||
'use strict'; | ||
try { | ||
self['workbox:range-requests:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
// @ts-ignore | ||
try { | ||
self['workbox:range-requests:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {Blob} blob A source blob. | ||
* @param {number|null} start The offset to use as the start of the | ||
* slice. | ||
* @param {number|null} end The offset to use as the end of the slice. | ||
* @return {Object} An object with `start` and `end` properties, reflecting | ||
* the effective boundaries to use given the size of the blob. | ||
* | ||
* @private | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {Blob} blob A source blob. | ||
* @param {number} [start] The offset to use as the start of the | ||
* slice. | ||
* @param {number} [end] The offset to use as the end of the slice. | ||
* @return {Object} An object with `start` and `end` properties, reflecting | ||
* the effective boundaries to use given the size of the blob. | ||
* | ||
* @private | ||
*/ | ||
function calculateEffectiveBoundaries(blob, start, end) { | ||
{ | ||
assert_mjs.assert.isInstance(blob, Blob, { | ||
moduleName: 'workbox-range-requests', | ||
funcName: 'calculateEffectiveBoundaries', | ||
paramName: 'blob' | ||
}); | ||
} | ||
function calculateEffectiveBoundaries(blob, start, end) { | ||
{ | ||
assert_js.assert.isInstance(blob, Blob, { | ||
moduleName: 'workbox-range-requests', | ||
funcName: 'calculateEffectiveBoundaries', | ||
paramName: 'blob' | ||
}); | ||
} | ||
const blobSize = blob.size; | ||
const blobSize = blob.size; | ||
if (end > blobSize || start < 0) { | ||
throw new WorkboxError_mjs.WorkboxError('range-not-satisfiable', { | ||
size: blobSize, | ||
end, | ||
start | ||
}); | ||
} | ||
if (end && end > blobSize || start && start < 0) { | ||
throw new WorkboxError_js.WorkboxError('range-not-satisfiable', { | ||
size: blobSize, | ||
end, | ||
start | ||
}); | ||
} | ||
let effectiveStart; | ||
let effectiveEnd; | ||
let effectiveStart; | ||
let effectiveEnd; | ||
if (start === null) { | ||
effectiveStart = blobSize - end; | ||
effectiveEnd = blobSize; | ||
} else if (end === null) { | ||
effectiveStart = start; | ||
effectiveEnd = blobSize; | ||
} else { | ||
effectiveStart = start; // Range values are inclusive, so add 1 to the value. | ||
if (start && end) { | ||
effectiveStart = start; // Range values are inclusive, so add 1 to the value. | ||
effectiveEnd = end + 1; | ||
effectiveEnd = end + 1; | ||
} else if (start && !end) { | ||
effectiveStart = start; | ||
effectiveEnd = blobSize; | ||
} else if (end && !start) { | ||
effectiveStart = blobSize - end; | ||
effectiveEnd = blobSize; | ||
} | ||
return { | ||
start: effectiveStart, | ||
end: effectiveEnd | ||
}; | ||
} | ||
return { | ||
start: effectiveStart, | ||
end: effectiveEnd | ||
}; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {string} rangeHeader A Range: header value. | ||
* @return {Object} An object with `start` and `end` properties, reflecting | ||
* the parsed value of the Range: header. If either the `start` or `end` are | ||
* omitted, then `null` will be returned. | ||
* | ||
* @private | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {string} rangeHeader A Range: header value. | ||
* @return {Object} An object with `start` and `end` properties, reflecting | ||
* the parsed value of the Range: header. If either the `start` or `end` are | ||
* omitted, then `null` will be returned. | ||
* | ||
* @private | ||
*/ | ||
function parseRangeHeader(rangeHeader) { | ||
{ | ||
assert_js.assert.isType(rangeHeader, 'string', { | ||
moduleName: 'workbox-range-requests', | ||
funcName: 'parseRangeHeader', | ||
paramName: 'rangeHeader' | ||
}); | ||
} | ||
function parseRangeHeader(rangeHeader) { | ||
{ | ||
assert_mjs.assert.isType(rangeHeader, 'string', { | ||
moduleName: 'workbox-range-requests', | ||
funcName: 'parseRangeHeader', | ||
paramName: 'rangeHeader' | ||
}); | ||
} | ||
const normalizedRangeHeader = rangeHeader.trim().toLowerCase(); | ||
const normalizedRangeHeader = rangeHeader.trim().toLowerCase(); | ||
if (!normalizedRangeHeader.startsWith('bytes=')) { | ||
throw new WorkboxError_js.WorkboxError('unit-must-be-bytes', { | ||
normalizedRangeHeader | ||
}); | ||
} // Specifying multiple ranges separate by commas is valid syntax, but this | ||
// library only attempts to handle a single, contiguous sequence of bytes. | ||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range#Syntax | ||
if (!normalizedRangeHeader.startsWith('bytes=')) { | ||
throw new WorkboxError_mjs.WorkboxError('unit-must-be-bytes', { | ||
normalizedRangeHeader | ||
}); | ||
} // Specifying multiple ranges separate by commas is valid syntax, but this | ||
// library only attempts to handle a single, contiguous sequence of bytes. | ||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range#Syntax | ||
if (normalizedRangeHeader.includes(',')) { | ||
throw new WorkboxError_js.WorkboxError('single-range-only', { | ||
normalizedRangeHeader | ||
}); | ||
} | ||
if (normalizedRangeHeader.includes(',')) { | ||
throw new WorkboxError_mjs.WorkboxError('single-range-only', { | ||
normalizedRangeHeader | ||
}); | ||
} | ||
const rangeParts = /(\d*)-(\d*)/.exec(normalizedRangeHeader); // We need either at least one of the start or end values. | ||
const rangeParts = /(\d*)-(\d*)/.exec(normalizedRangeHeader); // We need either at least one of the start or end values. | ||
if (!rangeParts || !(rangeParts[1] || rangeParts[2])) { | ||
throw new WorkboxError_js.WorkboxError('invalid-range-values', { | ||
normalizedRangeHeader | ||
}); | ||
} | ||
if (rangeParts === null || !(rangeParts[1] || rangeParts[2])) { | ||
throw new WorkboxError_mjs.WorkboxError('invalid-range-values', { | ||
normalizedRangeHeader | ||
}); | ||
return { | ||
start: rangeParts[1] === '' ? undefined : Number(rangeParts[1]), | ||
end: rangeParts[2] === '' ? undefined : Number(rangeParts[2]) | ||
}; | ||
} | ||
return { | ||
start: rangeParts[1] === '' ? null : Number(rangeParts[1]), | ||
end: rangeParts[2] === '' ? null : Number(rangeParts[2]) | ||
}; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Given a `Request` and `Response` objects as input, this will return a | ||
* promise for a new `Response`. | ||
* | ||
* If the original `Response` already contains partial content (i.e. it has | ||
* a status of 206), then this assumes it already fulfills the `Range:` | ||
* requirements, and will return it as-is. | ||
* | ||
* @param {Request} request A request, which should contain a Range: | ||
* header. | ||
* @param {Response} originalResponse A response. | ||
* @return {Promise<Response>} Either a `206 Partial Content` response, with | ||
* the response body set to the slice of content specified by the request's | ||
* `Range:` header, or a `416 Range Not Satisfiable` response if the | ||
* conditions of the `Range:` header can't be met. | ||
* | ||
* @memberof workbox.rangeRequests | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Given a `Request` and `Response` objects as input, this will return a | ||
* promise for a new `Response`. | ||
* | ||
* If the original `Response` already contains partial content (i.e. it has | ||
* a status of 206), then this assumes it already fulfills the `Range:` | ||
* requirements, and will return it as-is. | ||
* | ||
* @param {Request} request A request, which should contain a Range: | ||
* header. | ||
* @param {Response} originalResponse A response. | ||
* @return {Promise<Response>} Either a `206 Partial Content` response, with | ||
* the response body set to the slice of content specified by the request's | ||
* `Range:` header, or a `416 Range Not Satisfiable` response if the | ||
* conditions of the `Range:` header can't be met. | ||
* | ||
* @memberof workbox.rangeRequests | ||
*/ | ||
async function createPartialResponse(request, originalResponse) { | ||
try { | ||
if ("dev" !== 'production') { | ||
assert_js.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-range-requests', | ||
funcName: 'createPartialResponse', | ||
paramName: 'request' | ||
}); | ||
assert_js.assert.isInstance(originalResponse, Response, { | ||
moduleName: 'workbox-range-requests', | ||
funcName: 'createPartialResponse', | ||
paramName: 'originalResponse' | ||
}); | ||
} | ||
async function createPartialResponse(request, originalResponse) { | ||
try { | ||
{ | ||
assert_mjs.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-range-requests', | ||
funcName: 'createPartialResponse', | ||
paramName: 'request' | ||
}); | ||
assert_mjs.assert.isInstance(originalResponse, Response, { | ||
moduleName: 'workbox-range-requests', | ||
funcName: 'createPartialResponse', | ||
paramName: 'originalResponse' | ||
}); | ||
} | ||
if (originalResponse.status === 206) { | ||
// If we already have a 206, then just pass it through as-is; | ||
// see https://github.com/GoogleChrome/workbox/issues/1720 | ||
return originalResponse; | ||
} | ||
if (originalResponse.status === 206) { | ||
// If we already have a 206, then just pass it through as-is; | ||
// see https://github.com/GoogleChrome/workbox/issues/1720 | ||
return originalResponse; | ||
} | ||
const rangeHeader = request.headers.get('range'); | ||
const rangeHeader = request.headers.get('range'); | ||
if (!rangeHeader) { | ||
throw new WorkboxError_js.WorkboxError('no-range-header'); | ||
} | ||
if (!rangeHeader) { | ||
throw new WorkboxError_mjs.WorkboxError('no-range-header'); | ||
} | ||
const boundaries = parseRangeHeader(rangeHeader); | ||
const originalBlob = await originalResponse.blob(); | ||
const effectiveBoundaries = calculateEffectiveBoundaries(originalBlob, boundaries.start, boundaries.end); | ||
const slicedBlob = originalBlob.slice(effectiveBoundaries.start, effectiveBoundaries.end); | ||
const slicedBlobSize = slicedBlob.size; | ||
const slicedResponse = new Response(slicedBlob, { | ||
// Status code 206 is for a Partial Content response. | ||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206 | ||
status: 206, | ||
statusText: 'Partial Content', | ||
headers: originalResponse.headers | ||
}); | ||
slicedResponse.headers.set('Content-Length', String(slicedBlobSize)); | ||
slicedResponse.headers.set('Content-Range', `bytes ${effectiveBoundaries.start}-${effectiveBoundaries.end - 1}/` + originalBlob.size); | ||
return slicedResponse; | ||
} catch (error) { | ||
{ | ||
logger_js.logger.warn(`Unable to construct a partial response; returning a ` + `416 Range Not Satisfiable response instead.`); | ||
logger_js.logger.groupCollapsed(`View details here.`); | ||
logger_js.logger.log(error); | ||
logger_js.logger.log(request); | ||
logger_js.logger.log(originalResponse); | ||
logger_js.logger.groupEnd(); | ||
} | ||
const boundaries = parseRangeHeader(rangeHeader); | ||
const originalBlob = await originalResponse.blob(); | ||
const effectiveBoundaries = calculateEffectiveBoundaries(originalBlob, boundaries.start, boundaries.end); | ||
const slicedBlob = originalBlob.slice(effectiveBoundaries.start, effectiveBoundaries.end); | ||
const slicedBlobSize = slicedBlob.size; | ||
const slicedResponse = new Response(slicedBlob, { | ||
// Status code 206 is for a Partial Content response. | ||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206 | ||
status: 206, | ||
statusText: 'Partial Content', | ||
headers: originalResponse.headers | ||
}); | ||
slicedResponse.headers.set('Content-Length', slicedBlobSize); | ||
slicedResponse.headers.set('Content-Range', `bytes ${effectiveBoundaries.start}-${effectiveBoundaries.end - 1}/` + originalBlob.size); | ||
return slicedResponse; | ||
} catch (error) { | ||
{ | ||
logger_mjs.logger.warn(`Unable to construct a partial response; returning a ` + `416 Range Not Satisfiable response instead.`); | ||
logger_mjs.logger.groupCollapsed(`View details here.`); | ||
logger_mjs.logger.log(error); | ||
logger_mjs.logger.log(request); | ||
logger_mjs.logger.log(originalResponse); | ||
logger_mjs.logger.groupEnd(); | ||
return new Response('', { | ||
status: 416, | ||
statusText: 'Range Not Satisfiable' | ||
}); | ||
} | ||
return new Response('', { | ||
status: 416, | ||
statusText: 'Range Not Satisfiable' | ||
}); | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* The range request plugin makes it easy for a request with a 'Range' header to | ||
* be fulfilled by a cached response. | ||
* | ||
* It does this by intercepting the `cachedResponseWillBeUsed` plugin callback | ||
* and returning the appropriate subset of the cached response body. | ||
* | ||
* @memberof workbox.rangeRequests | ||
*/ | ||
class Plugin { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {Object} options | ||
* @param {Request} options.request The original request, which may or may not | ||
* contain a Range: header. | ||
* @param {Response} options.cachedResponse The complete cached response. | ||
* @return {Promise<Response>} If request contains a 'Range' header, then a | ||
* new response with status 206 whose body is a subset of `cachedResponse` is | ||
* returned. Otherwise, `cachedResponse` is returned as-is. | ||
* The range request plugin makes it easy for a request with a 'Range' header to | ||
* be fulfilled by a cached response. | ||
* | ||
* @private | ||
* It does this by intercepting the `cachedResponseWillBeUsed` plugin callback | ||
* and returning the appropriate subset of the cached response body. | ||
* | ||
* @memberof workbox.rangeRequests | ||
*/ | ||
async cachedResponseWillBeUsed({ | ||
request, | ||
cachedResponse | ||
}) { | ||
// Only return a sliced response if there's something valid in the cache, | ||
// and there's a Range: header in the request. | ||
if (cachedResponse && request.headers.has('range')) { | ||
return await createPartialResponse(request, cachedResponse); | ||
} // If there was no Range: header, or if cachedResponse wasn't valid, just | ||
// pass it through as-is. | ||
class Plugin { | ||
constructor() { | ||
/** | ||
* @param {Object} options | ||
* @param {Request} options.request The original request, which may or may not | ||
* contain a Range: header. | ||
* @param {Response} options.cachedResponse The complete cached response. | ||
* @return {Promise<Response>} If request contains a 'Range' header, then a | ||
* new response with status 206 whose body is a subset of `cachedResponse` is | ||
* returned. Otherwise, `cachedResponse` is returned as-is. | ||
* | ||
* @private | ||
*/ | ||
this.cachedResponseWillBeUsed = async ({ | ||
request, | ||
cachedResponse | ||
}) => { | ||
// Only return a sliced response if there's something valid in the cache, | ||
// and there's a Range: header in the request. | ||
if (cachedResponse && request.headers.has('range')) { | ||
return await createPartialResponse(request, cachedResponse); | ||
} // If there was no Range: header, or if cachedResponse wasn't valid, just | ||
// pass it through as-is. | ||
return cachedResponse; | ||
} | ||
} | ||
return cachedResponse; | ||
}; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
exports.Plugin = Plugin; | ||
exports.createPartialResponse = createPartialResponse; | ||
exports.createPartialResponse = createPartialResponse; | ||
exports.Plugin = Plugin; | ||
return exports; | ||
return exports; | ||
}({}, workbox.core._private, workbox.core._private, workbox.core._private)); | ||
@@ -1,2 +0,2 @@ | ||
this.workbox=this.workbox||{},this.workbox.rangeRequests=function(e,n){"use strict";try{self["workbox:range-requests:4.3.1"]&&_()}catch(e){}async function t(e,t){try{if(206===t.status)return t;const s=e.headers.get("range");if(!s)throw new n.WorkboxError("no-range-header");const a=function(e){const t=e.trim().toLowerCase();if(!t.startsWith("bytes="))throw new n.WorkboxError("unit-must-be-bytes",{normalizedRangeHeader:t});if(t.includes(","))throw new n.WorkboxError("single-range-only",{normalizedRangeHeader:t});const s=/(\d*)-(\d*)/.exec(t);if(null===s||!s[1]&&!s[2])throw new n.WorkboxError("invalid-range-values",{normalizedRangeHeader:t});return{start:""===s[1]?null:Number(s[1]),end:""===s[2]?null:Number(s[2])}}(s),r=await t.blob(),i=function(e,t,s){const a=e.size;if(s>a||t<0)throw new n.WorkboxError("range-not-satisfiable",{size:a,end:s,start:t});let r,i;return null===t?(r=a-s,i=a):null===s?(r=t,i=a):(r=t,i=s+1),{start:r,end:i}}(r,a.start,a.end),o=r.slice(i.start,i.end),u=o.size,l=new Response(o,{status:206,statusText:"Partial Content",headers:t.headers});return l.headers.set("Content-Length",u),l.headers.set("Content-Range",`bytes ${i.start}-${i.end-1}/`+r.size),l}catch(e){return new Response("",{status:416,statusText:"Range Not Satisfiable"})}}return e.createPartialResponse=t,e.Plugin=class{async cachedResponseWillBeUsed({request:e,cachedResponse:n}){return n&&e.headers.has("range")?await t(e,n):n}},e}({},workbox.core._private); | ||
this.workbox=this.workbox||{},this.workbox.rangeRequests=function(t,e,n){"use strict";try{self["workbox:range-requests:5.0.0-alpha.2"]&&_()}catch(t){}async function r(t,n){try{if(206===n.status)return n;const r=t.headers.get("range");if(!r)throw new e.WorkboxError("no-range-header");const s=function(t){const n=t.trim().toLowerCase();if(!n.startsWith("bytes="))throw new e.WorkboxError("unit-must-be-bytes",{normalizedRangeHeader:n});if(n.includes(","))throw new e.WorkboxError("single-range-only",{normalizedRangeHeader:n});const r=/(\d*)-(\d*)/.exec(n);if(!r||!r[1]&&!r[2])throw new e.WorkboxError("invalid-range-values",{normalizedRangeHeader:n});return{start:""===r[1]?void 0:Number(r[1]),end:""===r[2]?void 0:Number(r[2])}}(r),a=await n.blob(),o=function(t,n,r){const s=t.size;if(r&&r>s||n&&n<0)throw new e.WorkboxError("range-not-satisfiable",{size:s,end:r,start:n});let a,o;return n&&r?(a=n,o=r+1):n&&!r?(a=n,o=s):r&&!n&&(a=s-r,o=s),{start:a,end:o}}(a,s.start,s.end),i=a.slice(o.start,o.end),u=i.size,c=new Response(i,{status:206,statusText:"Partial Content",headers:n.headers});return c.headers.set("Content-Length",String(u)),c.headers.set("Content-Range",`bytes ${o.start}-${o.end-1}/`+a.size),c}catch(t){return new Response("",{status:416,statusText:"Range Not Satisfiable"})}}return t.Plugin=class{constructor(){this.cachedResponseWillBeUsed=(async({request:t,cachedResponse:e})=>e&&t.headers.has("range")?await r(t,e):e)}},t.createPartialResponse=r,t}({},workbox.core._private,workbox.core._private); | ||
this.workbox = this.workbox || {}; | ||
this.workbox.routing = (function (exports, assert_mjs, logger_mjs, cacheNames_mjs, WorkboxError_mjs, getFriendlyURL_mjs) { | ||
'use strict'; | ||
this.workbox.routing = (function (exports, assert_js, logger_js, WorkboxError_js, getFriendlyURL_js) { | ||
'use strict'; | ||
try { | ||
self['workbox:routing:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
// @ts-ignore | ||
try { | ||
self['workbox:routing:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* The default HTTP method, 'GET', used when there's no specific method | ||
* configured for a route. | ||
* | ||
* @type {string} | ||
* | ||
* @private | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* The default HTTP method, 'GET', used when there's no specific method | ||
* configured for a route. | ||
* | ||
* @type {string} | ||
* | ||
* @private | ||
*/ | ||
const defaultMethod = 'GET'; | ||
/** | ||
* The list of valid HTTP methods associated with requests that could be routed. | ||
* | ||
* @type {Array<string>} | ||
* | ||
* @private | ||
*/ | ||
const defaultMethod = 'GET'; | ||
/** | ||
* The list of valid HTTP methods associated with requests that could be routed. | ||
* | ||
* @type {Array<string>} | ||
* | ||
* @private | ||
*/ | ||
const validMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT']; | ||
const validMethods = ['DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT']; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {function()|Object} handler Either a function, or an object with a | ||
* 'handle' method. | ||
* @return {Object} An object with a handle method. | ||
* | ||
* @private | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {function()|Object} handler Either a function, or an object with a | ||
* 'handle' method. | ||
* @return {Object} An object with a handle method. | ||
* | ||
* @private | ||
*/ | ||
const normalizeHandler = handler => { | ||
if (handler && typeof handler === 'object') { | ||
{ | ||
assert_mjs.assert.hasMethod(handler, 'handle', { | ||
moduleName: 'workbox-routing', | ||
className: 'Route', | ||
funcName: 'constructor', | ||
paramName: 'handler' | ||
}); | ||
} | ||
const normalizeHandler = handler => { | ||
if (handler && typeof handler === 'object') { | ||
{ | ||
assert_js.assert.hasMethod(handler, 'handle', { | ||
moduleName: 'workbox-routing', | ||
className: 'Route', | ||
funcName: 'constructor', | ||
paramName: 'handler' | ||
}); | ||
} | ||
return handler; | ||
} else { | ||
{ | ||
assert_mjs.assert.isType(handler, 'function', { | ||
moduleName: 'workbox-routing', | ||
className: 'Route', | ||
funcName: 'constructor', | ||
paramName: 'handler' | ||
}); | ||
return handler; | ||
} else { | ||
{ | ||
assert_js.assert.isType(handler, 'function', { | ||
moduleName: 'workbox-routing', | ||
className: 'Route', | ||
funcName: 'constructor', | ||
paramName: 'handler' | ||
}); | ||
} | ||
return { | ||
handle: handler | ||
}; | ||
} | ||
}; | ||
return { | ||
handle: handler | ||
}; | ||
} | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* A `Route` consists of a pair of callback functions, "match" and "handler". | ||
* The "match" callback determine if a route should be used to "handle" a | ||
* request by returning a non-falsy value if it can. The "handler" callback | ||
* is called when there is a match and should return a Promise that resolves | ||
* to a `Response`. | ||
* | ||
* @memberof workbox.routing | ||
*/ | ||
class Route { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Constructor for Route class. | ||
* A `Route` consists of a pair of callback functions, "match" and "handler". | ||
* The "match" callback determine if a route should be used to "handle" a | ||
* request by returning a non-falsy value if it can. The "handler" callback | ||
* is called when there is a match and should return a Promise that resolves | ||
* to a `Response`. | ||
* | ||
* @param {workbox.routing.Route~matchCallback} match | ||
* A callback function that determines whether the route matches a given | ||
* `fetch` event by returning a non-falsy value. | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resolving to a Response. | ||
* @param {string} [method='GET'] The HTTP method to match the Route | ||
* against. | ||
* @memberof workbox.routing | ||
*/ | ||
constructor(match, handler, method) { | ||
{ | ||
assert_mjs.assert.isType(match, 'function', { | ||
moduleName: 'workbox-routing', | ||
className: 'Route', | ||
funcName: 'constructor', | ||
paramName: 'match' | ||
}); | ||
if (method) { | ||
assert_mjs.assert.isOneOf(method, validMethods, { | ||
paramName: 'method' | ||
class Route { | ||
/** | ||
* Constructor for Route class. | ||
* | ||
* @param {workbox.routing.Route~matchCallback} match | ||
* A callback function that determines whether the route matches a given | ||
* `fetch` event by returning a non-falsy value. | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resolving to a Response. | ||
* @param {string} [method='GET'] The HTTP method to match the Route | ||
* against. | ||
*/ | ||
constructor(match, handler, method = defaultMethod) { | ||
{ | ||
assert_js.assert.isType(match, 'function', { | ||
moduleName: 'workbox-routing', | ||
className: 'Route', | ||
funcName: 'constructor', | ||
paramName: 'match' | ||
}); | ||
} | ||
} // These values are referenced directly by Router so cannot be | ||
// altered by minifification. | ||
if (method) { | ||
assert_js.assert.isOneOf(method, validMethods, { | ||
paramName: 'method' | ||
}); | ||
} | ||
} // These values are referenced directly by Router so cannot be | ||
// altered by minifification. | ||
this.handler = normalizeHandler(handler); | ||
this.match = match; | ||
this.method = method || defaultMethod; | ||
} | ||
} | ||
this.handler = normalizeHandler(handler); | ||
this.match = match; | ||
this.method = method; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* NavigationRoute makes it easy to create a [Route]{@link | ||
* workbox.routing.Route} that matches for browser | ||
* [navigation requests]{@link https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests}. | ||
* | ||
* It will only match incoming Requests whose | ||
* [`mode`]{@link https://fetch.spec.whatwg.org/#concept-request-mode} | ||
* is set to `navigate`. | ||
* | ||
* You can optionally only apply this route to a subset of navigation requests | ||
* by using one or both of the `blacklist` and `whitelist` parameters. | ||
* | ||
* @memberof workbox.routing | ||
* @extends workbox.routing.Route | ||
*/ | ||
/* | ||
Copyright 2018 Google LLC | ||
class NavigationRoute extends Route { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* If both `blacklist` and `whiltelist` are provided, the `blacklist` will | ||
* take precedence and the request will not match this route. | ||
* NavigationRoute makes it easy to create a [Route]{@link | ||
* workbox.routing.Route} that matches for browser | ||
* [navigation requests]{@link https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests}. | ||
* | ||
* The regular expressions in `whitelist` and `blacklist` | ||
* are matched against the concatenated | ||
* [`pathname`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname} | ||
* and [`search`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search} | ||
* portions of the requested URL. | ||
* It will only match incoming Requests whose | ||
* [`mode`]{@link https://fetch.spec.whatwg.org/#concept-request-mode} | ||
* is set to `navigate`. | ||
* | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resulting in a Response. | ||
* @param {Object} options | ||
* @param {Array<RegExp>} [options.blacklist] If any of these patterns match, | ||
* the route will not handle the request (even if a whitelist RegExp matches). | ||
* @param {Array<RegExp>} [options.whitelist=[/./]] If any of these patterns | ||
* match the URL's pathname and search parameter, the route will handle the | ||
* request (assuming the blacklist doesn't match). | ||
*/ | ||
constructor(handler, { | ||
whitelist = [/./], | ||
blacklist = [] | ||
} = {}) { | ||
{ | ||
assert_mjs.assert.isArrayOfClass(whitelist, RegExp, { | ||
moduleName: 'workbox-routing', | ||
className: 'NavigationRoute', | ||
funcName: 'constructor', | ||
paramName: 'options.whitelist' | ||
}); | ||
assert_mjs.assert.isArrayOfClass(blacklist, RegExp, { | ||
moduleName: 'workbox-routing', | ||
className: 'NavigationRoute', | ||
funcName: 'constructor', | ||
paramName: 'options.blacklist' | ||
}); | ||
} | ||
super(options => this._match(options), handler); | ||
this._whitelist = whitelist; | ||
this._blacklist = blacklist; | ||
} | ||
/** | ||
* Routes match handler. | ||
* You can optionally only apply this route to a subset of navigation requests | ||
* by using one or both of the `blacklist` and `whitelist` parameters. | ||
* | ||
* @param {Object} options | ||
* @param {URL} options.url | ||
* @param {Request} options.request | ||
* @return {boolean} | ||
* | ||
* @private | ||
* @memberof workbox.routing | ||
* @extends workbox.routing.Route | ||
*/ | ||
class NavigationRoute extends Route { | ||
/** | ||
* If both `blacklist` and `whiltelist` are provided, the `blacklist` will | ||
* take precedence and the request will not match this route. | ||
* | ||
* The regular expressions in `whitelist` and `blacklist` | ||
* are matched against the concatenated | ||
* [`pathname`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname} | ||
* and [`search`]{@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/search} | ||
* portions of the requested URL. | ||
* | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resulting in a Response. | ||
* @param {Object} options | ||
* @param {Array<RegExp>} [options.blacklist] If any of these patterns match, | ||
* the route will not handle the request (even if a whitelist RegExp matches). | ||
* @param {Array<RegExp>} [options.whitelist=[/./]] If any of these patterns | ||
* match the URL's pathname and search parameter, the route will handle the | ||
* request (assuming the blacklist doesn't match). | ||
*/ | ||
constructor(handler, { | ||
whitelist = [/./], | ||
blacklist = [] | ||
} = {}) { | ||
{ | ||
assert_js.assert.isArrayOfClass(whitelist, RegExp, { | ||
moduleName: 'workbox-routing', | ||
className: 'NavigationRoute', | ||
funcName: 'constructor', | ||
paramName: 'options.whitelist' | ||
}); | ||
assert_js.assert.isArrayOfClass(blacklist, RegExp, { | ||
moduleName: 'workbox-routing', | ||
className: 'NavigationRoute', | ||
funcName: 'constructor', | ||
paramName: 'options.blacklist' | ||
}); | ||
} | ||
_match({ | ||
url, | ||
request | ||
}) { | ||
if (request.mode !== 'navigate') { | ||
return false; | ||
super(options => this._match(options), handler); | ||
this._whitelist = whitelist; | ||
this._blacklist = blacklist; | ||
} | ||
/** | ||
* Routes match handler. | ||
* | ||
* @param {Object} options | ||
* @param {URL} options.url | ||
* @param {Request} options.request | ||
* @return {boolean} | ||
* | ||
* @private | ||
*/ | ||
const pathnameAndSearch = url.pathname + url.search; | ||
for (const regExp of this._blacklist) { | ||
if (regExp.test(pathnameAndSearch)) { | ||
_match({ | ||
url, | ||
request | ||
}) { | ||
if (request && request.mode !== 'navigate') { | ||
return false; | ||
} | ||
const pathnameAndSearch = url.pathname + url.search; | ||
for (const regExp of this._blacklist) { | ||
if (regExp.test(pathnameAndSearch)) { | ||
{ | ||
logger_js.logger.log(`The navigation route ${pathnameAndSearch} is not ` + `being used, since the URL matches this blacklist pattern: ` + `${regExp}`); | ||
} | ||
return false; | ||
} | ||
} | ||
if (this._whitelist.some(regExp => regExp.test(pathnameAndSearch))) { | ||
{ | ||
logger_mjs.logger.log(`The navigation route is not being used, since the ` + `URL matches this blacklist pattern: ${regExp}`); | ||
logger_js.logger.debug(`The navigation route ${pathnameAndSearch} ` + `is being used.`); | ||
} | ||
return false; | ||
return true; | ||
} | ||
} | ||
if (this._whitelist.some(regExp => regExp.test(pathnameAndSearch))) { | ||
{ | ||
logger_mjs.logger.debug(`The navigation route is being used.`); | ||
logger_js.logger.log(`The navigation route ${pathnameAndSearch} is not ` + `being used, since the URL being navigated to doesn't ` + `match the whitelist.`); | ||
} | ||
return true; | ||
return false; | ||
} | ||
{ | ||
logger_mjs.logger.log(`The navigation route is not being used, since the URL ` + `being navigated to doesn't match the whitelist.`); | ||
} | ||
return false; | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* RegExpRoute makes it easy to create a regular expression based | ||
* [Route]{@link workbox.routing.Route}. | ||
* | ||
* For same-origin requests the RegExp only needs to match part of the URL. For | ||
* requests against third-party servers, you must define a RegExp that matches | ||
* the start of the URL. | ||
* | ||
* [See the module docs for info.]{@link https://developers.google.com/web/tools/workbox/modules/workbox-routing} | ||
* | ||
* @memberof workbox.routing | ||
* @extends workbox.routing.Route | ||
*/ | ||
class RegExpRoute extends Route { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* If the regulard expression contains | ||
* [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references}, | ||
* th ecaptured values will be passed to the | ||
* [handler's]{@link workbox.routing.Route~handlerCallback} `params` | ||
* argument. | ||
* RegExpRoute makes it easy to create a regular expression based | ||
* [Route]{@link workbox.routing.Route}. | ||
* | ||
* @param {RegExp} regExp The regular expression to match against URLs. | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resulting in a Response. | ||
* @param {string} [method='GET'] The HTTP method to match the Route | ||
* against. | ||
* For same-origin requests the RegExp only needs to match part of the URL. For | ||
* requests against third-party servers, you must define a RegExp that matches | ||
* the start of the URL. | ||
* | ||
* [See the module docs for info.]{@link https://developers.google.com/web/tools/workbox/modules/workbox-routing} | ||
* | ||
* @memberof workbox.routing | ||
* @extends workbox.routing.Route | ||
*/ | ||
constructor(regExp, handler, method) { | ||
{ | ||
assert_mjs.assert.isInstance(regExp, RegExp, { | ||
moduleName: 'workbox-routing', | ||
className: 'RegExpRoute', | ||
funcName: 'constructor', | ||
paramName: 'pattern' | ||
}); | ||
} | ||
const match = ({ | ||
url | ||
}) => { | ||
const result = regExp.exec(url.href); // Return null immediately if there's no match. | ||
class RegExpRoute extends Route { | ||
/** | ||
* If the regulard expression contains | ||
* [capture groups]{@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references}, | ||
* th ecaptured values will be passed to the | ||
* [handler's]{@link workbox.routing.Route~handlerCallback} `params` | ||
* argument. | ||
* | ||
* @param {RegExp} regExp The regular expression to match against URLs. | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resulting in a Response. | ||
* @param {string} [method='GET'] The HTTP method to match the Route | ||
* against. | ||
*/ | ||
constructor(regExp, handler, method) { | ||
{ | ||
assert_js.assert.isInstance(regExp, RegExp, { | ||
moduleName: 'workbox-routing', | ||
className: 'RegExpRoute', | ||
funcName: 'constructor', | ||
paramName: 'pattern' | ||
}); | ||
} | ||
if (!result) { | ||
return null; | ||
} // Require that the match start at the first character in the URL string | ||
// if it's a cross-origin request. | ||
// See https://github.com/GoogleChrome/workbox/issues/281 for the context | ||
// behind this behavior. | ||
const match = ({ | ||
url | ||
}) => { | ||
const result = regExp.exec(url.href); // Return immediately if there's no match. | ||
if (!result) { | ||
return; | ||
} // Require that the match start at the first character in the URL string | ||
// if it's a cross-origin request. | ||
// See https://github.com/GoogleChrome/workbox/issues/281 for the context | ||
// behind this behavior. | ||
if (url.origin !== location.origin && result.index !== 0) { | ||
{ | ||
logger_mjs.logger.debug(`The regular expression '${regExp}' only partially matched ` + `against the cross-origin URL '${url}'. RegExpRoute's will only ` + `handle cross-origin requests if they match the entire URL.`); | ||
} | ||
return null; | ||
} // If the route matches, but there aren't any capture groups defined, then | ||
// this will return [], which is truthy and therefore sufficient to | ||
// indicate a match. | ||
// If there are capture groups, then it will return their values. | ||
if (url.origin !== location.origin && result.index !== 0) { | ||
{ | ||
logger_js.logger.debug(`The regular expression '${regExp}' only partially matched ` + `against the cross-origin URL '${url}'. RegExpRoute's will only ` + `handle cross-origin requests if they match the entire URL.`); | ||
} | ||
return; | ||
} // If the route matches, but there aren't any capture groups defined, then | ||
// this will return [], which is truthy and therefore sufficient to | ||
// indicate a match. | ||
// If there are capture groups, then it will return their values. | ||
return result.slice(1); | ||
}; | ||
super(match, handler, method); | ||
} | ||
return result.slice(1); | ||
}; | ||
} | ||
super(match, handler, method); | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* The Router can be used to process a FetchEvent through one or more | ||
* [Routes]{@link workbox.routing.Route} responding with a Request if | ||
* a matching route exists. | ||
* | ||
* If no route matches a given a request, the Router will use a "default" | ||
* handler if one is defined. | ||
* | ||
* Should the matching Route throw an error, the Router will use a "catch" | ||
* handler if one is defined to gracefully deal with issues and respond with a | ||
* Request. | ||
* | ||
* If a request matches multiple routes, the **earliest** registered route will | ||
* be used to respond to the request. | ||
* | ||
* @memberof workbox.routing | ||
*/ | ||
class Router { | ||
/** | ||
* Initializes a new Router. | ||
*/ | ||
constructor() { | ||
this._routes = new Map(); | ||
} | ||
/** | ||
* @return {Map<string, Array<workbox.routing.Route>>} routes A `Map` of HTTP | ||
* method name ('GET', etc.) to an array of all the corresponding `Route` | ||
* instances that are registered. | ||
*/ | ||
/* | ||
Copyright 2018 Google LLC | ||
get routes() { | ||
return this._routes; | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Adds a fetch event listener to respond to events when a route matches | ||
* the event's request. | ||
* The Router can be used to process a FetchEvent through one or more | ||
* [Routes]{@link workbox.routing.Route} responding with a Request if | ||
* a matching route exists. | ||
* | ||
* If no route matches a given a request, the Router will use a "default" | ||
* handler if one is defined. | ||
* | ||
* Should the matching Route throw an error, the Router will use a "catch" | ||
* handler if one is defined to gracefully deal with issues and respond with a | ||
* Request. | ||
* | ||
* If a request matches multiple routes, the **earliest** registered route will | ||
* be used to respond to the request. | ||
* | ||
* @memberof workbox.routing | ||
*/ | ||
class Router { | ||
/** | ||
* Initializes a new Router. | ||
*/ | ||
constructor() { | ||
this._routes = new Map(); | ||
} | ||
/** | ||
* @return {Map<string, Array<workbox.routing.Route>>} routes A `Map` of HTTP | ||
* method name ('GET', etc.) to an array of all the corresponding `Route` | ||
* instances that are registered. | ||
*/ | ||
addFetchListener() { | ||
self.addEventListener('fetch', event => { | ||
const { | ||
request | ||
} = event; | ||
const responsePromise = this.handleRequest({ | ||
request, | ||
event | ||
}); | ||
if (responsePromise) { | ||
event.respondWith(responsePromise); | ||
} | ||
}); | ||
} | ||
/** | ||
* Adds a message event listener for URLs to cache from the window. | ||
* This is useful to cache resources loaded on the page prior to when the | ||
* service worker started controlling it. | ||
* | ||
* The format of the message data sent from the window should be as follows. | ||
* Where the `urlsToCache` array may consist of URL strings or an array of | ||
* URL string + `requestInit` object (the same as you'd pass to `fetch()`). | ||
* | ||
* ``` | ||
* { | ||
* type: 'CACHE_URLS', | ||
* payload: { | ||
* urlsToCache: [ | ||
* './script1.js', | ||
* './script2.js', | ||
* ['./script3.js', {mode: 'no-cors'}], | ||
* ], | ||
* }, | ||
* } | ||
* ``` | ||
*/ | ||
get routes() { | ||
return this._routes; | ||
} | ||
/** | ||
* Adds a fetch event listener to respond to events when a route matches | ||
* the event's request. | ||
*/ | ||
addCacheListener() { | ||
self.addEventListener('message', async event => { | ||
if (event.data && event.data.type === 'CACHE_URLS') { | ||
addFetchListener() { | ||
self.addEventListener('fetch', event => { | ||
const { | ||
payload | ||
} = event.data; | ||
request | ||
} = event; | ||
const responsePromise = this.handleRequest({ | ||
request, | ||
event | ||
}); | ||
{ | ||
logger_mjs.logger.debug(`Caching URLs from the window`, payload.urlsToCache); | ||
if (responsePromise) { | ||
event.respondWith(responsePromise); | ||
} | ||
}); | ||
} | ||
/** | ||
* Adds a message event listener for URLs to cache from the window. | ||
* This is useful to cache resources loaded on the page prior to when the | ||
* service worker started controlling it. | ||
* | ||
* The format of the message data sent from the window should be as follows. | ||
* Where the `urlsToCache` array may consist of URL strings or an array of | ||
* URL string + `requestInit` object (the same as you'd pass to `fetch()`). | ||
* | ||
* ``` | ||
* { | ||
* type: 'CACHE_URLS', | ||
* payload: { | ||
* urlsToCache: [ | ||
* './script1.js', | ||
* './script2.js', | ||
* ['./script3.js', {mode: 'no-cors'}], | ||
* ], | ||
* }, | ||
* } | ||
* ``` | ||
*/ | ||
const requestPromises = Promise.all(payload.urlsToCache.map(entry => { | ||
if (typeof entry === 'string') { | ||
entry = [entry]; | ||
addCacheListener() { | ||
self.addEventListener('message', async event => { | ||
if (event.data && event.data.type === 'CACHE_URLS') { | ||
const { | ||
payload | ||
} = event.data; | ||
{ | ||
logger_js.logger.debug(`Caching URLs from the window`, payload.urlsToCache); | ||
} | ||
const request = new Request(...entry); | ||
return this.handleRequest({ | ||
request | ||
}); | ||
})); | ||
event.waitUntil(requestPromises); // If a MessageChannel was used, reply to the message on success. | ||
const requestPromises = Promise.all(payload.urlsToCache.map(entry => { | ||
if (typeof entry === 'string') { | ||
entry = [entry]; | ||
} | ||
if (event.ports && event.ports[0]) { | ||
await requestPromises; | ||
event.ports[0].postMessage(true); | ||
} | ||
} | ||
}); | ||
} | ||
/** | ||
* Apply the routing rules to a FetchEvent object to get a Response from an | ||
* appropriate Route's handler. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request The request to handle (this is usually | ||
* from a fetch event, but it does not have to be). | ||
* @param {FetchEvent} [options.event] The event that triggered the request, | ||
* if applicable. | ||
* @return {Promise<Response>|undefined} A promise is returned if a | ||
* registered route can handle the request. If there is no matching | ||
* route and there's no `defaultHandler`, `undefined` is returned. | ||
*/ | ||
const request = new Request(...entry); | ||
return this.handleRequest({ | ||
request | ||
}); // TODO(philipwalton): TypeScript errors without this typecast for | ||
// some reason (probably a bug). The real type here should work but | ||
// doesn't: `Array<Promise<Response> | undefined>`. | ||
})); // TypeScript | ||
event.waitUntil(requestPromises); // If a MessageChannel was used, reply to the message on success. | ||
handleRequest({ | ||
request, | ||
event | ||
}) { | ||
{ | ||
assert_mjs.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'handleRequest', | ||
paramName: 'options.request' | ||
if (event.ports && event.ports[0]) { | ||
await requestPromises; | ||
event.ports[0].postMessage(true); | ||
} | ||
} | ||
}); | ||
} | ||
/** | ||
* Apply the routing rules to a FetchEvent object to get a Response from an | ||
* appropriate Route's handler. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request The request to handle (this is usually | ||
* from a fetch event, but it does not have to be). | ||
* @param {FetchEvent} [options.event] The event that triggered the request, | ||
* if applicable. | ||
* @return {Promise<Response>|undefined} A promise is returned if a | ||
* registered route can handle the request. If there is no matching | ||
* route and there's no `defaultHandler`, `undefined` is returned. | ||
*/ | ||
const url = new URL(request.url, location); | ||
if (!url.protocol.startsWith('http')) { | ||
handleRequest({ | ||
request, | ||
event | ||
}) { | ||
{ | ||
logger_mjs.logger.debug(`Workbox Router only supports URLs that start with 'http'.`); | ||
assert_js.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'handleRequest', | ||
paramName: 'options.request' | ||
}); | ||
} | ||
return; | ||
} | ||
const url = new URL(request.url, location.href); | ||
let { | ||
params, | ||
route | ||
} = this.findMatchingRoute({ | ||
url, | ||
request, | ||
event | ||
}); | ||
let handler = route && route.handler; | ||
let debugMessages = []; | ||
if (!url.protocol.startsWith('http')) { | ||
{ | ||
logger_js.logger.debug(`Workbox Router only supports URLs that start with 'http'.`); | ||
} | ||
{ | ||
if (handler) { | ||
debugMessages.push([`Found a route to handle this request:`, route]); | ||
if (params) { | ||
debugMessages.push([`Passing the following params to the route's handler:`, params]); | ||
} | ||
return; | ||
} | ||
} // If we don't have a handler because there was no matching route, then | ||
// fall back to defaultHandler if that's defined. | ||
let { | ||
params, | ||
route | ||
} = this.findMatchingRoute({ | ||
url, | ||
request, | ||
event | ||
}); | ||
let handler = route && route.handler; | ||
let debugMessages = []; | ||
if (!handler && this._defaultHandler) { | ||
{ | ||
debugMessages.push(`Failed to find a matching route. Falling ` + `back to the default handler.`); // This is used for debugging in logs in the case of an error. | ||
if (handler) { | ||
debugMessages.push([`Found a route to handle this request:`, route]); | ||
route = '[Default Handler]'; | ||
} | ||
if (params) { | ||
debugMessages.push([`Passing the following params to the route's handler:`, params]); | ||
} | ||
} | ||
} // If we don't have a handler because there was no matching route, then | ||
// fall back to defaultHandler if that's defined. | ||
handler = this._defaultHandler; | ||
} | ||
if (!handler) { | ||
{ | ||
// No handler so Workbox will do nothing. If logs is set of debug | ||
// i.e. verbose, we should print out this information. | ||
logger_mjs.logger.debug(`No route found for: ${getFriendlyURL_mjs.getFriendlyURL(url)}`); | ||
if (!handler && this._defaultHandler) { | ||
{ | ||
debugMessages.push(`Failed to find a matching route. Falling ` + `back to the default handler.`); | ||
} | ||
handler = this._defaultHandler; | ||
} | ||
return; | ||
} | ||
{ | ||
// We have a handler, meaning Workbox is going to handle the route. | ||
// print the routing details to the console. | ||
logger_mjs.logger.groupCollapsed(`Router is responding to: ${getFriendlyURL_mjs.getFriendlyURL(url)}`); | ||
debugMessages.forEach(msg => { | ||
if (Array.isArray(msg)) { | ||
logger_mjs.logger.log(...msg); | ||
} else { | ||
logger_mjs.logger.log(msg); | ||
if (!handler) { | ||
{ | ||
// No handler so Workbox will do nothing. If logs is set of debug | ||
// i.e. verbose, we should print out this information. | ||
logger_js.logger.debug(`No route found for: ${getFriendlyURL_js.getFriendlyURL(url)}`); | ||
} | ||
}); // The Request and Response objects contains a great deal of information, | ||
// hide it under a group in case developers want to see it. | ||
logger_mjs.logger.groupCollapsed(`View request details here.`); | ||
logger_mjs.logger.log(request); | ||
logger_mjs.logger.groupEnd(); | ||
logger_mjs.logger.groupEnd(); | ||
} // Wrap in try and catch in case the handle method throws a synchronous | ||
// error. It should still callback to the catch handler. | ||
return; | ||
} | ||
{ | ||
// We have a handler, meaning Workbox is going to handle the route. | ||
// print the routing details to the console. | ||
logger_js.logger.groupCollapsed(`Router is responding to: ${getFriendlyURL_js.getFriendlyURL(url)}`); | ||
debugMessages.forEach(msg => { | ||
if (Array.isArray(msg)) { | ||
logger_js.logger.log(...msg); | ||
} else { | ||
logger_js.logger.log(msg); | ||
} | ||
}); // The Request and Response objects contains a great deal of information, | ||
// hide it under a group in case developers want to see it. | ||
let responsePromise; | ||
logger_js.logger.groupCollapsed(`View request details here.`); | ||
logger_js.logger.log(request); | ||
logger_js.logger.groupEnd(); | ||
logger_js.logger.groupEnd(); | ||
} // Wrap in try and catch in case the handle method throws a synchronous | ||
// error. It should still callback to the catch handler. | ||
try { | ||
responsePromise = handler.handle({ | ||
url, | ||
request, | ||
event, | ||
params | ||
}); | ||
} catch (err) { | ||
responsePromise = Promise.reject(err); | ||
} | ||
if (responsePromise && this._catchHandler) { | ||
responsePromise = responsePromise.catch(err => { | ||
{ | ||
// Still include URL here as it will be async from the console group | ||
// and may not make sense without the URL | ||
logger_mjs.logger.groupCollapsed(`Error thrown when responding to: ` + ` ${getFriendlyURL_mjs.getFriendlyURL(url)}. Falling back to Catch Handler.`); | ||
logger_mjs.logger.error(`Error thrown by:`, route); | ||
logger_mjs.logger.error(err); | ||
logger_mjs.logger.groupEnd(); | ||
} | ||
let responsePromise; | ||
return this._catchHandler.handle({ | ||
try { | ||
responsePromise = handler.handle({ | ||
url, | ||
request, | ||
event, | ||
err | ||
params | ||
}); | ||
}); | ||
} | ||
} catch (err) { | ||
responsePromise = Promise.reject(err); | ||
} | ||
return responsePromise; | ||
} | ||
/** | ||
* Checks a request and URL (and optionally an event) against the list of | ||
* registered routes, and if there's a match, returns the corresponding | ||
* route along with any params generated by the match. | ||
* | ||
* @param {Object} options | ||
* @param {URL} options.url | ||
* @param {Request} options.request The request to match. | ||
* @param {FetchEvent} [options.event] The corresponding event (unless N/A). | ||
* @return {Object} An object with `route` and `params` properties. | ||
* They are populated if a matching route was found or `undefined` | ||
* otherwise. | ||
*/ | ||
if (responsePromise && this._catchHandler) { | ||
responsePromise = responsePromise.catch(err => { | ||
{ | ||
// Still include URL here as it will be async from the console group | ||
// and may not make sense without the URL | ||
logger_js.logger.groupCollapsed(`Error thrown when responding to: ` + ` ${getFriendlyURL_js.getFriendlyURL(url)}. Falling back to Catch Handler.`); | ||
logger_js.logger.error(`Error thrown by:`, route); | ||
logger_js.logger.error(err); | ||
logger_js.logger.groupEnd(); | ||
} | ||
return this._catchHandler.handle({ | ||
url, | ||
request, | ||
event | ||
}); | ||
}); | ||
} | ||
findMatchingRoute({ | ||
url, | ||
request, | ||
event | ||
}) { | ||
{ | ||
assert_mjs.assert.isInstance(url, URL, { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'findMatchingRoute', | ||
paramName: 'options.url' | ||
}); | ||
assert_mjs.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'findMatchingRoute', | ||
paramName: 'options.request' | ||
}); | ||
return responsePromise; | ||
} | ||
/** | ||
* Checks a request and URL (and optionally an event) against the list of | ||
* registered routes, and if there's a match, returns the corresponding | ||
* route along with any params generated by the match. | ||
* | ||
* @param {Object} options | ||
* @param {URL} options.url | ||
* @param {Request} options.request The request to match. | ||
* @param {Event} [options.event] The corresponding event (unless N/A). | ||
* @return {Object} An object with `route` and `params` properties. | ||
* They are populated if a matching route was found or `undefined` | ||
* otherwise. | ||
*/ | ||
const routes = this._routes.get(request.method) || []; | ||
for (const route of routes) { | ||
let params; | ||
let matchResult = route.match({ | ||
url, | ||
request, | ||
event | ||
}); | ||
if (matchResult) { | ||
if (Array.isArray(matchResult) && matchResult.length > 0) { | ||
// Instead of passing an empty array in as params, use undefined. | ||
params = matchResult; | ||
} else if (matchResult.constructor === Object && Object.keys(matchResult).length > 0) { | ||
// Instead of passing an empty object in as params, use undefined. | ||
params = matchResult; | ||
} // Return early if have a match. | ||
return { | ||
route, | ||
params | ||
}; | ||
findMatchingRoute({ | ||
url, | ||
request, | ||
event | ||
}) { | ||
{ | ||
assert_js.assert.isInstance(url, URL, { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'findMatchingRoute', | ||
paramName: 'options.url' | ||
}); | ||
assert_js.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'findMatchingRoute', | ||
paramName: 'options.request' | ||
}); | ||
} | ||
} // If no match was found above, return and empty object. | ||
const routes = this._routes.get(request.method) || []; | ||
return {}; | ||
} | ||
/** | ||
* Define a default `handler` that's called when no routes explicitly | ||
* match the incoming request. | ||
* | ||
* Without a default handler, unmatched requests will go against the | ||
* network as if there were no service worker present. | ||
* | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resulting in a Response. | ||
*/ | ||
for (const route of routes) { | ||
let params; | ||
let matchResult = route.match({ | ||
url, | ||
request, | ||
event | ||
}); | ||
if (matchResult) { | ||
// See https://github.com/GoogleChrome/workbox/issues/2079 | ||
params = matchResult; | ||
setDefaultHandler(handler) { | ||
this._defaultHandler = normalizeHandler(handler); | ||
} | ||
/** | ||
* If a Route throws an error while handling a request, this `handler` | ||
* will be called and given a chance to provide a response. | ||
* | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resulting in a Response. | ||
*/ | ||
if (Array.isArray(matchResult) && matchResult.length === 0) { | ||
// Instead of passing an empty array in as params, use undefined. | ||
params = undefined; | ||
} else if (matchResult.constructor === Object && Object.keys(matchResult).length === 0) { | ||
// Instead of passing an empty object in as params, use undefined. | ||
params = undefined; | ||
} else if (typeof matchResult === 'boolean') { | ||
// For the boolean value true (rather than just something truth-y), | ||
// don't set params. | ||
// See https://github.com/GoogleChrome/workbox/pull/2134#issuecomment-513924353 | ||
params = undefined; | ||
} // Return early if have a match. | ||
setCatchHandler(handler) { | ||
this._catchHandler = normalizeHandler(handler); | ||
} | ||
/** | ||
* Registers a route with the router. | ||
* | ||
* @param {workbox.routing.Route} route The route to register. | ||
*/ | ||
return { | ||
route, | ||
params | ||
}; | ||
} | ||
} // If no match was found above, return and empty object. | ||
registerRoute(route) { | ||
{ | ||
assert_mjs.assert.isType(route, 'object', { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'registerRoute', | ||
paramName: 'route' | ||
}); | ||
assert_mjs.assert.hasMethod(route, 'match', { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'registerRoute', | ||
paramName: 'route' | ||
}); | ||
assert_mjs.assert.isType(route.handler, 'object', { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'registerRoute', | ||
paramName: 'route' | ||
}); | ||
assert_mjs.assert.hasMethod(route.handler, 'handle', { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'registerRoute', | ||
paramName: 'route.handler' | ||
}); | ||
assert_mjs.assert.isType(route.method, 'string', { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'registerRoute', | ||
paramName: 'route.method' | ||
}); | ||
return {}; | ||
} | ||
/** | ||
* Define a default `handler` that's called when no routes explicitly | ||
* match the incoming request. | ||
* | ||
* Without a default handler, unmatched requests will go against the | ||
* network as if there were no service worker present. | ||
* | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resulting in a Response. | ||
*/ | ||
if (!this._routes.has(route.method)) { | ||
this._routes.set(route.method, []); | ||
} // Give precedence to all of the earlier routes by adding this additional | ||
// route to the end of the array. | ||
setDefaultHandler(handler) { | ||
this._defaultHandler = normalizeHandler(handler); | ||
} | ||
/** | ||
* If a Route throws an error while handling a request, this `handler` | ||
* will be called and given a chance to provide a response. | ||
* | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resulting in a Response. | ||
*/ | ||
this._routes.get(route.method).push(route); | ||
} | ||
/** | ||
* Unregisters a route with the router. | ||
* | ||
* @param {workbox.routing.Route} route The route to unregister. | ||
*/ | ||
unregisterRoute(route) { | ||
if (!this._routes.has(route.method)) { | ||
throw new WorkboxError_mjs.WorkboxError('unregister-route-but-not-found-with-method', { | ||
method: route.method | ||
}); | ||
setCatchHandler(handler) { | ||
this._catchHandler = normalizeHandler(handler); | ||
} | ||
/** | ||
* Registers a route with the router. | ||
* | ||
* @param {workbox.routing.Route} route The route to register. | ||
*/ | ||
const routeIndex = this._routes.get(route.method).indexOf(route); | ||
if (routeIndex > -1) { | ||
this._routes.get(route.method).splice(routeIndex, 1); | ||
} else { | ||
throw new WorkboxError_mjs.WorkboxError('unregister-route-route-not-registered'); | ||
} | ||
} | ||
registerRoute(route) { | ||
{ | ||
assert_js.assert.isType(route, 'object', { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'registerRoute', | ||
paramName: 'route' | ||
}); | ||
assert_js.assert.hasMethod(route, 'match', { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'registerRoute', | ||
paramName: 'route' | ||
}); | ||
assert_js.assert.isType(route.handler, 'object', { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'registerRoute', | ||
paramName: 'route' | ||
}); | ||
assert_js.assert.hasMethod(route.handler, 'handle', { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'registerRoute', | ||
paramName: 'route.handler' | ||
}); | ||
assert_js.assert.isType(route.method, 'string', { | ||
moduleName: 'workbox-routing', | ||
className: 'Router', | ||
funcName: 'registerRoute', | ||
paramName: 'route.method' | ||
}); | ||
} | ||
} | ||
if (!this._routes.has(route.method)) { | ||
this._routes.set(route.method, []); | ||
} // Give precedence to all of the earlier routes by adding this additional | ||
// route to the end of the array. | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
let defaultRouter; | ||
/** | ||
* Creates a new, singleton Router instance if one does not exist. If one | ||
* does already exist, that instance is returned. | ||
* | ||
* @private | ||
* @return {Router} | ||
*/ | ||
this._routes.get(route.method).push(route); | ||
} | ||
/** | ||
* Unregisters a route with the router. | ||
* | ||
* @param {workbox.routing.Route} route The route to unregister. | ||
*/ | ||
const getOrCreateDefaultRouter = () => { | ||
if (!defaultRouter) { | ||
defaultRouter = new Router(); // The helpers that use the default Router assume these listeners exist. | ||
defaultRouter.addFetchListener(); | ||
defaultRouter.addCacheListener(); | ||
} | ||
unregisterRoute(route) { | ||
if (!this._routes.has(route.method)) { | ||
throw new WorkboxError_js.WorkboxError('unregister-route-but-not-found-with-method', { | ||
method: route.method | ||
}); | ||
} | ||
return defaultRouter; | ||
}; | ||
const routeIndex = this._routes.get(route.method).indexOf(route); | ||
/* | ||
Copyright 2019 Google LLC | ||
if (routeIndex > -1) { | ||
this._routes.get(route.method).splice(routeIndex, 1); | ||
} else { | ||
throw new WorkboxError_js.WorkboxError('unregister-route-route-not-registered'); | ||
} | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Registers a route that will return a precached file for a navigation | ||
* request. This is useful for the | ||
* [application shell pattern]{@link https://developers.google.com/web/fundamentals/architecture/app-shell}. | ||
* | ||
* When determining the URL of the precached HTML document, you will likely need | ||
* to call `workbox.precaching.getCacheKeyForURL(originalUrl)`, to account for | ||
* the fact that Workbox's precaching naming conventions often results in URL | ||
* cache keys that contain extra revisioning info. | ||
* | ||
* This method will generate a | ||
* [NavigationRoute]{@link workbox.routing.NavigationRoute} | ||
* and call | ||
* [Router.registerRoute()]{@link workbox.routing.Router#registerRoute} on a | ||
* singleton Router instance. | ||
* | ||
* @param {string} cachedAssetUrl The cache key to use for the HTML file. | ||
* @param {Object} [options] | ||
* @param {string} [options.cacheName] Cache name to store and retrieve | ||
* requests. Defaults to precache cache name provided by | ||
* [workbox-core.cacheNames]{@link workbox.core.cacheNames}. | ||
* @param {Array<RegExp>} [options.blacklist=[]] If any of these patterns | ||
* match, the route will not handle the request (even if a whitelist entry | ||
* matches). | ||
* @param {Array<RegExp>} [options.whitelist=[/./]] If any of these patterns | ||
* match the URL's pathname and search parameter, the route will handle the | ||
* request (assuming the blacklist doesn't match). | ||
* @return {workbox.routing.NavigationRoute} Returns the generated | ||
* Route. | ||
* | ||
* @alias workbox.routing.registerNavigationRoute | ||
*/ | ||
const registerNavigationRoute = (cachedAssetUrl, options = {}) => { | ||
{ | ||
assert_mjs.assert.isType(cachedAssetUrl, 'string', { | ||
moduleName: 'workbox-routing', | ||
funcName: 'registerNavigationRoute', | ||
paramName: 'cachedAssetUrl' | ||
}); | ||
} | ||
const cacheName = cacheNames_mjs.cacheNames.getPrecacheName(options.cacheName); | ||
/* | ||
Copyright 2019 Google LLC | ||
const handler = async () => { | ||
try { | ||
const response = await caches.match(cachedAssetUrl, { | ||
cacheName | ||
}); | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
let defaultRouter; | ||
/** | ||
* Creates a new, singleton Router instance if one does not exist. If one | ||
* does already exist, that instance is returned. | ||
* | ||
* @private | ||
* @return {Router} | ||
*/ | ||
if (response) { | ||
return response; | ||
} // This shouldn't normally happen, but there are edge cases: | ||
// https://github.com/GoogleChrome/workbox/issues/1441 | ||
const getOrCreateDefaultRouter = () => { | ||
if (!defaultRouter) { | ||
defaultRouter = new Router(); // The helpers that use the default Router assume these listeners exist. | ||
defaultRouter.addFetchListener(); | ||
defaultRouter.addCacheListener(); | ||
} | ||
throw new Error(`The cache ${cacheName} did not have an entry for ` + `${cachedAssetUrl}.`); | ||
} catch (error) { | ||
// If there's either a cache miss, or the caches.match() call threw | ||
// an exception, then attempt to fulfill the navigation request with | ||
// a response from the network rather than leaving the user with a | ||
// failed navigation. | ||
{ | ||
logger_mjs.logger.debug(`Unable to respond to navigation request with ` + `cached response. Falling back to network.`, error); | ||
} // This might still fail if the browser is offline... | ||
return fetch(cachedAssetUrl); | ||
} | ||
return defaultRouter; | ||
}; | ||
const route = new NavigationRoute(handler, { | ||
whitelist: options.whitelist, | ||
blacklist: options.blacklist | ||
}); | ||
const defaultRouter = getOrCreateDefaultRouter(); | ||
defaultRouter.registerRoute(route); | ||
return route; | ||
}; | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Easily register a RegExp, string, or function with a caching | ||
* strategy to a singleton Router instance. | ||
* | ||
* This method will generate a Route for you if needed and | ||
* call [Router.registerRoute()]{@link | ||
* workbox.routing.Router#registerRoute}. | ||
* | ||
* @param { | ||
* RegExp| | ||
* string| | ||
* workbox.routing.Route~matchCallback| | ||
* workbox.routing.Route | ||
* } capture | ||
* If the capture param is a `Route`, all other arguments will be ignored. | ||
* @param {workbox.routing.Route~handlerCallback} [handler] A callback | ||
* function that returns a Promise resulting in a Response. This parameter | ||
* is required if `capture` is not a `Route` object. | ||
* @param {string} [method='GET'] The HTTP method to match the Route | ||
* against. | ||
* @return {workbox.routing.Route} The generated `Route`(Useful for | ||
* unregistering). | ||
* | ||
* @alias workbox.routing.registerRoute | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Easily register a RegExp, string, or function with a caching | ||
* strategy to a singleton Router instance. | ||
* | ||
* This method will generate a Route for you if needed and | ||
* call [Router.registerRoute()]{@link | ||
* workbox.routing.Router#registerRoute}. | ||
* | ||
* @param { | ||
* RegExp| | ||
* string| | ||
* workbox.routing.Route~matchCallback| | ||
* workbox.routing.Route | ||
* } capture | ||
* If the capture param is a `Route`, all other arguments will be ignored. | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resulting in a Response. | ||
* @param {string} [method='GET'] The HTTP method to match the Route | ||
* against. | ||
* @return {workbox.routing.Route} The generated `Route`(Useful for | ||
* unregistering). | ||
* | ||
* @alias workbox.routing.registerRoute | ||
*/ | ||
const registerRoute = (capture, handler, method) => { | ||
let route; | ||
const registerRoute = (capture, handler, method = 'GET') => { | ||
let route; | ||
if (typeof capture === 'string') { | ||
const captureUrl = new URL(capture, location.href); | ||
if (typeof capture === 'string') { | ||
const captureUrl = new URL(capture, location); | ||
{ | ||
if (!(capture.startsWith('/') || capture.startsWith('http'))) { | ||
throw new WorkboxError_js.WorkboxError('invalid-string', { | ||
moduleName: 'workbox-routing', | ||
funcName: 'registerRoute', | ||
paramName: 'capture' | ||
}); | ||
} // We want to check if Express-style wildcards are in the pathname only. | ||
// TODO: Remove this log message in v4. | ||
{ | ||
if (!(capture.startsWith('/') || capture.startsWith('http'))) { | ||
throw new WorkboxError_mjs.WorkboxError('invalid-string', { | ||
moduleName: 'workbox-routing', | ||
funcName: 'registerRoute', | ||
paramName: 'capture' | ||
}); | ||
} // We want to check if Express-style wildcards are in the pathname only. | ||
// TODO: Remove this log message in v4. | ||
const valueToCheck = capture.startsWith('http') ? captureUrl.pathname : capture; // See https://github.com/pillarjs/path-to-regexp#parameters | ||
const valueToCheck = capture.startsWith('http') ? captureUrl.pathname : capture; // See https://github.com/pillarjs/path-to-regexp#parameters | ||
const wildcards = '[*:?+]'; | ||
const wildcards = '[*:?+]'; | ||
if (valueToCheck.match(new RegExp(`${wildcards}`))) { | ||
logger_mjs.logger.debug(`The '$capture' parameter contains an Express-style wildcard ` + `character (${wildcards}). Strings are now always interpreted as ` + `exact matches; use a RegExp for partial or wildcard matches.`); | ||
if (valueToCheck.match(new RegExp(`${wildcards}`))) { | ||
logger_js.logger.debug(`The '$capture' parameter contains an Express-style wildcard ` + `character (${wildcards}). Strings are now always interpreted as ` + `exact matches; use a RegExp for partial or wildcard matches.`); | ||
} | ||
} | ||
} | ||
const matchCallback = ({ | ||
url | ||
}) => { | ||
{ | ||
if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) { | ||
logger_mjs.logger.debug(`${capture} only partially matches the cross-origin URL ` + `${url}. This route will only handle cross-origin requests ` + `if they match the entire URL.`); | ||
const matchCallback = ({ | ||
url | ||
}) => { | ||
{ | ||
if (url.pathname === captureUrl.pathname && url.origin !== captureUrl.origin) { | ||
logger_js.logger.debug(`${capture} only partially matches the cross-origin URL ` + `${url}. This route will only handle cross-origin requests ` + `if they match the entire URL.`); | ||
} | ||
} | ||
} | ||
return url.href === captureUrl.href; | ||
}; | ||
return url.href === captureUrl.href; | ||
}; // If `capture` is a string then `handler` and `method` must be present. | ||
route = new Route(matchCallback, handler, method); | ||
} else if (capture instanceof RegExp) { | ||
route = new RegExpRoute(capture, handler, method); | ||
} else if (typeof capture === 'function') { | ||
route = new Route(capture, handler, method); | ||
} else if (capture instanceof Route) { | ||
route = capture; | ||
} else { | ||
throw new WorkboxError_mjs.WorkboxError('unsupported-route-type', { | ||
moduleName: 'workbox-routing', | ||
funcName: 'registerRoute', | ||
paramName: 'capture' | ||
}); | ||
} | ||
const defaultRouter = getOrCreateDefaultRouter(); | ||
defaultRouter.registerRoute(route); | ||
return route; | ||
}; | ||
route = new Route(matchCallback, handler, method); | ||
} else if (capture instanceof RegExp) { | ||
// If `capture` is a `RegExp` then `handler` and `method` must be present. | ||
route = new RegExpRoute(capture, handler, method); | ||
} else if (typeof capture === 'function') { | ||
// If `capture` is a function then `handler` and `method` must be present. | ||
route = new Route(capture, handler, method); | ||
} else if (capture instanceof Route) { | ||
route = capture; | ||
} else { | ||
throw new WorkboxError_js.WorkboxError('unsupported-route-type', { | ||
moduleName: 'workbox-routing', | ||
funcName: 'registerRoute', | ||
paramName: 'capture' | ||
}); | ||
} | ||
/* | ||
Copyright 2019 Google LLC | ||
const defaultRouter = getOrCreateDefaultRouter(); | ||
defaultRouter.registerRoute(route); | ||
return route; | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* If a Route throws an error while handling a request, this `handler` | ||
* will be called and given a chance to provide a response. | ||
* | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resulting in a Response. | ||
* | ||
* @alias workbox.routing.setCatchHandler | ||
*/ | ||
/* | ||
Copyright 2019 Google LLC | ||
const setCatchHandler = handler => { | ||
const defaultRouter = getOrCreateDefaultRouter(); | ||
defaultRouter.setCatchHandler(handler); | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* If a Route throws an error while handling a request, this `handler` | ||
* will be called and given a chance to provide a response. | ||
* | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resulting in a Response. | ||
* | ||
* @alias workbox.routing.setCatchHandler | ||
*/ | ||
/* | ||
Copyright 2019 Google LLC | ||
const setCatchHandler = handler => { | ||
const defaultRouter = getOrCreateDefaultRouter(); | ||
defaultRouter.setCatchHandler(handler); | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Define a default `handler` that's called when no routes explicitly | ||
* match the incoming request. | ||
* | ||
* Without a default handler, unmatched requests will go against the | ||
* network as if there were no service worker present. | ||
* | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resulting in a Response. | ||
* | ||
* @alias workbox.routing.setDefaultHandler | ||
*/ | ||
/* | ||
Copyright 2019 Google LLC | ||
const setDefaultHandler = handler => { | ||
const defaultRouter = getOrCreateDefaultRouter(); | ||
defaultRouter.setDefaultHandler(handler); | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Define a default `handler` that's called when no routes explicitly | ||
* match the incoming request. | ||
* | ||
* Without a default handler, unmatched requests will go against the | ||
* network as if there were no service worker present. | ||
* | ||
* @param {workbox.routing.Route~handlerCallback} handler A callback | ||
* function that returns a Promise resulting in a Response. | ||
* | ||
* @alias workbox.routing.setDefaultHandler | ||
*/ | ||
/* | ||
Copyright 2018 Google LLC | ||
const setDefaultHandler = handler => { | ||
const defaultRouter = getOrCreateDefaultRouter(); | ||
defaultRouter.setDefaultHandler(handler); | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/* | ||
Copyright 2018 Google LLC | ||
{ | ||
assert_mjs.assert.isSWEnv('workbox-routing'); | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
exports.NavigationRoute = NavigationRoute; | ||
exports.RegExpRoute = RegExpRoute; | ||
exports.registerNavigationRoute = registerNavigationRoute; | ||
exports.registerRoute = registerRoute; | ||
exports.Route = Route; | ||
exports.Router = Router; | ||
exports.setCatchHandler = setCatchHandler; | ||
exports.setDefaultHandler = setDefaultHandler; | ||
{ | ||
assert_js.assert.isSWEnv('workbox-routing'); | ||
} | ||
return exports; | ||
exports.NavigationRoute = NavigationRoute; | ||
exports.RegExpRoute = RegExpRoute; | ||
exports.Route = Route; | ||
exports.Router = Router; | ||
exports.registerRoute = registerRoute; | ||
exports.setCatchHandler = setCatchHandler; | ||
exports.setDefaultHandler = setDefaultHandler; | ||
}({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private)); | ||
return exports; | ||
}({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private)); | ||
@@ -1,2 +0,2 @@ | ||
this.workbox=this.workbox||{},this.workbox.routing=function(t,e,r){"use strict";try{self["workbox:routing:4.3.1"]&&_()}catch(t){}const s="GET",n=t=>t&&"object"==typeof t?t:{handle:t};class o{constructor(t,e,r){this.handler=n(e),this.match=t,this.method=r||s}}class i extends o{constructor(t,{whitelist:e=[/./],blacklist:r=[]}={}){super(t=>this.t(t),t),this.s=e,this.o=r}t({url:t,request:e}){if("navigate"!==e.mode)return!1;const r=t.pathname+t.search;for(const t of this.o)if(t.test(r))return!1;return!!this.s.some(t=>t.test(r))}}class u extends o{constructor(t,e,r){super(({url:e})=>{const r=t.exec(e.href);return r?e.origin!==location.origin&&0!==r.index?null:r.slice(1):null},e,r)}}class c{constructor(){this.i=new Map}get routes(){return this.i}addFetchListener(){self.addEventListener("fetch",t=>{const{request:e}=t,r=this.handleRequest({request:e,event:t});r&&t.respondWith(r)})}addCacheListener(){self.addEventListener("message",async t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,r=Promise.all(e.urlsToCache.map(t=>{"string"==typeof t&&(t=[t]);const e=new Request(...t);return this.handleRequest({request:e})}));t.waitUntil(r),t.ports&&t.ports[0]&&(await r,t.ports[0].postMessage(!0))}})}handleRequest({request:t,event:e}){const r=new URL(t.url,location);if(!r.protocol.startsWith("http"))return;let s,{params:n,route:o}=this.findMatchingRoute({url:r,request:t,event:e}),i=o&&o.handler;if(!i&&this.u&&(i=this.u),i){try{s=i.handle({url:r,request:t,event:e,params:n})}catch(t){s=Promise.reject(t)}return s&&this.h&&(s=s.catch(t=>this.h.handle({url:r,event:e,err:t}))),s}}findMatchingRoute({url:t,request:e,event:r}){const s=this.i.get(e.method)||[];for(const n of s){let s,o=n.match({url:t,request:e,event:r});if(o)return Array.isArray(o)&&o.length>0?s=o:o.constructor===Object&&Object.keys(o).length>0&&(s=o),{route:n,params:s}}return{}}setDefaultHandler(t){this.u=n(t)}setCatchHandler(t){this.h=n(t)}registerRoute(t){this.i.has(t.method)||this.i.set(t.method,[]),this.i.get(t.method).push(t)}unregisterRoute(t){if(!this.i.has(t.method))throw new r.WorkboxError("unregister-route-but-not-found-with-method",{method:t.method});const e=this.i.get(t.method).indexOf(t);if(!(e>-1))throw new r.WorkboxError("unregister-route-route-not-registered");this.i.get(t.method).splice(e,1)}}let a;const h=()=>(a||((a=new c).addFetchListener(),a.addCacheListener()),a);return t.NavigationRoute=i,t.RegExpRoute=u,t.registerNavigationRoute=((t,r={})=>{const s=e.cacheNames.getPrecacheName(r.cacheName),n=new i(async()=>{try{const e=await caches.match(t,{cacheName:s});if(e)return e;throw new Error(`The cache ${s} did not have an entry for `+`${t}.`)}catch(e){return fetch(t)}},{whitelist:r.whitelist,blacklist:r.blacklist});return h().registerRoute(n),n}),t.registerRoute=((t,e,s="GET")=>{let n;if("string"==typeof t){const r=new URL(t,location);n=new o(({url:t})=>t.href===r.href,e,s)}else if(t instanceof RegExp)n=new u(t,e,s);else if("function"==typeof t)n=new o(t,e,s);else{if(!(t instanceof o))throw new r.WorkboxError("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});n=t}return h().registerRoute(n),n}),t.Route=o,t.Router=c,t.setCatchHandler=(t=>{h().setCatchHandler(t)}),t.setDefaultHandler=(t=>{h().setDefaultHandler(t)}),t}({},workbox.core._private,workbox.core._private); | ||
this.workbox=this.workbox||{},this.workbox.routing=function(t,e){"use strict";try{self["workbox:routing:5.0.0-alpha.2"]&&_()}catch(t){}const s="GET",r=t=>t&&"object"==typeof t?t:{handle:t};class n{constructor(t,e,n=s){this.handler=r(e),this.match=t,this.method=n}}class o extends n{constructor(t,e,s){super(({url:e})=>{const s=t.exec(e.href);if(s&&(e.origin===location.origin||0===s.index))return s.slice(1)},e,s)}}class i{constructor(){this.t=new Map}get routes(){return this.t}addFetchListener(){self.addEventListener("fetch",t=>{const{request:e}=t,s=this.handleRequest({request:e,event:t});s&&t.respondWith(s)})}addCacheListener(){self.addEventListener("message",async t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,s=Promise.all(e.urlsToCache.map(t=>{"string"==typeof t&&(t=[t]);const e=new Request(...t);return this.handleRequest({request:e})}));t.waitUntil(s),t.ports&&t.ports[0]&&(await s,t.ports[0].postMessage(!0))}})}handleRequest({request:t,event:e}){const s=new URL(t.url,location.href);if(!s.protocol.startsWith("http"))return;let r,{params:n,route:o}=this.findMatchingRoute({url:s,request:t,event:e}),i=o&&o.handler;if(!i&&this.s&&(i=this.s),i){try{r=i.handle({url:s,request:t,event:e,params:n})}catch(t){r=Promise.reject(t)}return r&&this.o&&(r=r.catch(r=>this.o.handle({url:s,request:t,event:e}))),r}}findMatchingRoute({url:t,request:e,event:s}){const r=this.t.get(e.method)||[];for(const n of r){let r,o=n.match({url:t,request:e,event:s});if(o)return r=o,Array.isArray(o)&&0===o.length?r=void 0:o.constructor===Object&&0===Object.keys(o).length?r=void 0:"boolean"==typeof o&&(r=void 0),{route:n,params:r}}return{}}setDefaultHandler(t){this.s=r(t)}setCatchHandler(t){this.o=r(t)}registerRoute(t){this.t.has(t.method)||this.t.set(t.method,[]),this.t.get(t.method).push(t)}unregisterRoute(t){if(!this.t.has(t.method))throw new e.WorkboxError("unregister-route-but-not-found-with-method",{method:t.method});const s=this.t.get(t.method).indexOf(t);if(!(s>-1))throw new e.WorkboxError("unregister-route-route-not-registered");this.t.get(t.method).splice(s,1)}}let u;const c=()=>(u||((u=new i).addFetchListener(),u.addCacheListener()),u);return t.NavigationRoute=class extends n{constructor(t,{whitelist:e=[/./],blacklist:s=[]}={}){super(t=>this.i(t),t),this.u=e,this.h=s}i({url:t,request:e}){if(e&&"navigate"!==e.mode)return!1;const s=t.pathname+t.search;for(const t of this.h)if(t.test(s))return!1;return!!this.u.some(t=>t.test(s))}},t.RegExpRoute=o,t.Route=n,t.Router=i,t.registerRoute=((t,s,r)=>{let i;if("string"==typeof t){const e=new URL(t,location.href);i=new n(({url:t})=>t.href===e.href,s,r)}else if(t instanceof RegExp)i=new o(t,s,r);else if("function"==typeof t)i=new n(t,s,r);else{if(!(t instanceof n))throw new e.WorkboxError("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});i=t}return c().registerRoute(i),i}),t.setCatchHandler=(t=>{c().setCatchHandler(t)}),t.setDefaultHandler=(t=>{c().setDefaultHandler(t)}),t}({},workbox.core._private); | ||
this.workbox = this.workbox || {}; | ||
this.workbox.strategies = (function (exports, logger_mjs, assert_mjs, cacheNames_mjs, cacheWrapper_mjs, fetchWrapper_mjs, getFriendlyURL_mjs, WorkboxError_mjs) { | ||
'use strict'; | ||
this.workbox.strategies = (function (exports, logger_js, assert_js, cacheNames_js, cacheWrapper_js, fetchWrapper_js, getFriendlyURL_js, WorkboxError_js) { | ||
'use strict'; | ||
try { | ||
self['workbox:strategies:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
// @ts-ignore | ||
try { | ||
self['workbox:strategies:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const getFriendlyURL = url => { | ||
const urlObj = new URL(url, location); | ||
if (urlObj.origin === location.origin) { | ||
return urlObj.pathname; | ||
} | ||
return urlObj.href; | ||
}; | ||
const messages = { | ||
strategyStart: (strategyName, request) => `Using ${strategyName} to ` + `respond to '${getFriendlyURL(request.url)}'`, | ||
printFinalResponse: response => { | ||
if (response) { | ||
logger_mjs.logger.groupCollapsed(`View the final response here.`); | ||
logger_mjs.logger.log(response); | ||
logger_mjs.logger.groupEnd(); | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const messages = { | ||
strategyStart: (strategyName, request) => `Using ${strategyName} to respond to '${getFriendlyURL_js.getFriendlyURL(request.url)}'`, | ||
printFinalResponse: response => { | ||
if (response) { | ||
logger_js.logger.groupCollapsed(`View the final response here.`); | ||
logger_js.logger.log(response || '[No response returned]'); | ||
logger_js.logger.groupEnd(); | ||
} | ||
} | ||
} | ||
}; | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* An implementation of a [cache-first]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#cache-falling-back-to-network} | ||
* request strategy. | ||
* | ||
* A cache first strategy is useful for assets that have been revisioned, | ||
* such as URLs like `/styles/example.a8f5f1.css`, since they | ||
* can be cached for long periods of time. | ||
* | ||
* If the network request fails, and there is no cache match, this will throw | ||
* a `WorkboxError` exception. | ||
* | ||
* @memberof workbox.strategies | ||
*/ | ||
class CacheFirst { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {Object} options | ||
* @param {string} options.cacheName Cache name to store and retrieve | ||
* requests. Defaults to cache names provided by | ||
* [workbox-core]{@link workbox.core.cacheNames}. | ||
* @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} | ||
* to use in conjunction with this caching strategy. | ||
* @param {Object} options.fetchOptions Values passed along to the | ||
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) | ||
* of all fetch() requests made by this strategy. | ||
* @param {Object} options.matchOptions [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions) | ||
*/ | ||
constructor(options = {}) { | ||
this._cacheName = cacheNames_mjs.cacheNames.getRuntimeName(options.cacheName); | ||
this._plugins = options.plugins || []; | ||
this._fetchOptions = options.fetchOptions || null; | ||
this._matchOptions = options.matchOptions || null; | ||
} | ||
/** | ||
* This method will perform a request strategy and follows an API that | ||
* will work with the | ||
* [Workbox Router]{@link workbox.routing.Router}. | ||
* An implementation of a [cache-first]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#cache-falling-back-to-network} | ||
* request strategy. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request The request to run this strategy for. | ||
* @param {Event} [options.event] The event that triggered the request. | ||
* @return {Promise<Response>} | ||
*/ | ||
async handle({ | ||
event, | ||
request | ||
}) { | ||
return this.makeRequest({ | ||
event, | ||
request: request || event.request | ||
}); | ||
} | ||
/** | ||
* This method can be used to perform a make a standalone request outside the | ||
* context of the [Workbox Router]{@link workbox.routing.Router}. | ||
* A cache first strategy is useful for assets that have been revisioned, | ||
* such as URLs like `/styles/example.a8f5f1.css`, since they | ||
* can be cached for long periods of time. | ||
* | ||
* See "[Advanced Recipes](https://developers.google.com/web/tools/workbox/guides/advanced-recipes#make-requests)" | ||
* for more usage information. | ||
* If the network request fails, and there is no cache match, this will throw | ||
* a `WorkboxError` exception. | ||
* | ||
* @param {Object} options | ||
* @param {Request|string} options.request Either a | ||
* [`Request`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Request} | ||
* object, or a string URL, corresponding to the request to be made. | ||
* @param {FetchEvent} [options.event] If provided, `event.waitUntil()` will | ||
be called automatically to extend the service worker's lifetime. | ||
* @return {Promise<Response>} | ||
* @memberof workbox.strategies | ||
*/ | ||
async makeRequest({ | ||
event, | ||
request | ||
}) { | ||
const logs = []; | ||
if (typeof request === 'string') { | ||
request = new Request(request); | ||
class CacheFirst { | ||
/** | ||
* @param {Object} options | ||
* @param {string} options.cacheName Cache name to store and retrieve | ||
* requests. Defaults to cache names provided by | ||
* [workbox-core]{@link workbox.core.cacheNames}. | ||
* @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} | ||
* to use in conjunction with this caching strategy. | ||
* @param {Object} options.fetchOptions Values passed along to the | ||
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) | ||
* of all fetch() requests made by this strategy. | ||
* @param {Object} options.matchOptions [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions) | ||
*/ | ||
constructor(options = {}) { | ||
this._cacheName = cacheNames_js.cacheNames.getRuntimeName(options.cacheName); | ||
this._plugins = options.plugins || []; | ||
this._fetchOptions = options.fetchOptions; | ||
this._matchOptions = options.matchOptions; | ||
} | ||
/** | ||
* This method will perform a request strategy and follows an API that | ||
* will work with the | ||
* [Workbox Router]{@link workbox.routing.Router}. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request The request to run this strategy for. | ||
* @param {Event} [options.event] The event that triggered the request. | ||
* @return {Promise<Response>} | ||
*/ | ||
{ | ||
assert_mjs.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-strategies', | ||
className: 'CacheFirst', | ||
funcName: 'makeRequest', | ||
paramName: 'request' | ||
}); | ||
} | ||
let response = await cacheWrapper_mjs.cacheWrapper.match({ | ||
cacheName: this._cacheName, | ||
request, | ||
async handle({ | ||
event, | ||
matchOptions: this._matchOptions, | ||
plugins: this._plugins | ||
}); | ||
let error; | ||
request | ||
}) { | ||
const logs = []; | ||
if (!response) { | ||
{ | ||
logs.push(`No response found in the '${this._cacheName}' cache. ` + `Will respond with a network request.`); | ||
assert_js.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-strategies', | ||
className: 'CacheFirst', | ||
funcName: 'makeRequest', | ||
paramName: 'request' | ||
}); | ||
} | ||
try { | ||
response = await this._getFromNetwork(request, event); | ||
} catch (err) { | ||
error = err; | ||
} | ||
let response = await cacheWrapper_js.cacheWrapper.match({ | ||
cacheName: this._cacheName, | ||
request, | ||
event, | ||
matchOptions: this._matchOptions, | ||
plugins: this._plugins | ||
}); | ||
let error; | ||
{ | ||
if (response) { | ||
logs.push(`Got response from network.`); | ||
} else { | ||
logs.push(`Unable to get a response from the network.`); | ||
if (!response) { | ||
{ | ||
logs.push(`No response found in the '${this._cacheName}' cache. ` + `Will respond with a network request.`); | ||
} | ||
try { | ||
response = await this._getFromNetwork(request, event); | ||
} catch (err) { | ||
error = err; | ||
} | ||
{ | ||
if (response) { | ||
logs.push(`Got response from network.`); | ||
} else { | ||
logs.push(`Unable to get a response from the network.`); | ||
} | ||
} | ||
} else { | ||
{ | ||
logs.push(`Found a cached response in the '${this._cacheName}' cache.`); | ||
} | ||
} | ||
} else { | ||
{ | ||
logs.push(`Found a cached response in the '${this._cacheName}' cache.`); | ||
} | ||
} | ||
logger_js.logger.groupCollapsed(messages.strategyStart('CacheFirst', request)); | ||
{ | ||
logger_mjs.logger.groupCollapsed(messages.strategyStart('CacheFirst', request)); | ||
for (let log of logs) { | ||
logger_js.logger.log(log); | ||
} | ||
for (let log of logs) { | ||
logger_mjs.logger.log(log); | ||
messages.printFinalResponse(response); | ||
logger_js.logger.groupEnd(); | ||
} | ||
messages.printFinalResponse(response); | ||
logger_mjs.logger.groupEnd(); | ||
} | ||
if (!response) { | ||
throw new WorkboxError_js.WorkboxError('no-response', { | ||
url: request.url, | ||
error | ||
}); | ||
} | ||
if (!response) { | ||
throw new WorkboxError_mjs.WorkboxError('no-response', { | ||
url: request.url, | ||
error | ||
}); | ||
return response; | ||
} | ||
/** | ||
* Handles the network and cache part of CacheFirst. | ||
* | ||
* @param {Request} request | ||
* @param {Event} [event] | ||
* @return {Promise<Response>} | ||
* | ||
* @private | ||
*/ | ||
return response; | ||
} | ||
/** | ||
* Handles the network and cache part of CacheFirst. | ||
* | ||
* @param {Request} request | ||
* @param {FetchEvent} [event] | ||
* @return {Promise<Response>} | ||
* | ||
* @private | ||
*/ | ||
async _getFromNetwork(request, event) { | ||
const response = await fetchWrapper_js.fetchWrapper.fetch({ | ||
request, | ||
event, | ||
fetchOptions: this._fetchOptions, | ||
plugins: this._plugins | ||
}); // Keep the service worker while we put the request to the cache | ||
async _getFromNetwork(request, event) { | ||
const response = await fetchWrapper_mjs.fetchWrapper.fetch({ | ||
request, | ||
event, | ||
fetchOptions: this._fetchOptions, | ||
plugins: this._plugins | ||
}); // Keep the service worker while we put the request to the cache | ||
const responseClone = response.clone(); | ||
const cachePutPromise = cacheWrapper_js.cacheWrapper.put({ | ||
cacheName: this._cacheName, | ||
request, | ||
response: responseClone, | ||
event, | ||
plugins: this._plugins | ||
}); | ||
const responseClone = response.clone(); | ||
const cachePutPromise = cacheWrapper_mjs.cacheWrapper.put({ | ||
cacheName: this._cacheName, | ||
request, | ||
response: responseClone, | ||
event, | ||
plugins: this._plugins | ||
}); | ||
if (event) { | ||
try { | ||
event.waitUntil(cachePutPromise); | ||
} catch (error) { | ||
{ | ||
logger_mjs.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache for '${getFriendlyURL_mjs.getFriendlyURL(request.url)}'.`); | ||
if (event) { | ||
try { | ||
event.waitUntil(cachePutPromise); | ||
} catch (error) { | ||
{ | ||
logger_js.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache for '${getFriendlyURL_js.getFriendlyURL(request.url)}'.`); | ||
} | ||
} | ||
} | ||
return response; | ||
} | ||
return response; | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* An implementation of a | ||
* [cache-only]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#cache-only} | ||
* request strategy. | ||
* | ||
* This class is useful if you want to take advantage of any | ||
* [Workbox plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}. | ||
* | ||
* If there is no cache match, this will throw a `WorkboxError` exception. | ||
* | ||
* @memberof workbox.strategies | ||
*/ | ||
class CacheOnly { | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* @param {Object} options | ||
* @param {string} options.cacheName Cache name to store and retrieve | ||
* requests. Defaults to cache names provided by | ||
* [workbox-core]{@link workbox.core.cacheNames}. | ||
* @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} | ||
* to use in conjunction with this caching strategy. | ||
* @param {Object} options.matchOptions [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions) | ||
*/ | ||
constructor(options = {}) { | ||
this._cacheName = cacheNames_mjs.cacheNames.getRuntimeName(options.cacheName); | ||
this._plugins = options.plugins || []; | ||
this._matchOptions = options.matchOptions || null; | ||
} | ||
/** | ||
* This method will perform a request strategy and follows an API that | ||
* will work with the | ||
* [Workbox Router]{@link workbox.routing.Router}. | ||
* An implementation of a | ||
* [cache-only]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#cache-only} | ||
* request strategy. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request The request to run this strategy for. | ||
* @param {Event} [options.event] The event that triggered the request. | ||
* @return {Promise<Response>} | ||
*/ | ||
async handle({ | ||
event, | ||
request | ||
}) { | ||
return this.makeRequest({ | ||
event, | ||
request: request || event.request | ||
}); | ||
} | ||
/** | ||
* This method can be used to perform a make a standalone request outside the | ||
* context of the [Workbox Router]{@link workbox.routing.Router}. | ||
* This class is useful if you want to take advantage of any | ||
* [Workbox plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}. | ||
* | ||
* See "[Advanced Recipes](https://developers.google.com/web/tools/workbox/guides/advanced-recipes#make-requests)" | ||
* for more usage information. | ||
* If there is no cache match, this will throw a `WorkboxError` exception. | ||
* | ||
* @param {Object} options | ||
* @param {Request|string} options.request Either a | ||
* [`Request`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Request} | ||
* object, or a string URL, corresponding to the request to be made. | ||
* @param {FetchEvent} [options.event] If provided, `event.waitUntil()` will | ||
* be called automatically to extend the service worker's lifetime. | ||
* @return {Promise<Response>} | ||
* @memberof workbox.strategies | ||
*/ | ||
async makeRequest({ | ||
event, | ||
request | ||
}) { | ||
if (typeof request === 'string') { | ||
request = new Request(request); | ||
class CacheOnly { | ||
/** | ||
* @param {Object} options | ||
* @param {string} options.cacheName Cache name to store and retrieve | ||
* requests. Defaults to cache names provided by | ||
* [workbox-core]{@link workbox.core.cacheNames}. | ||
* @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} | ||
* to use in conjunction with this caching strategy. | ||
* @param {Object} options.matchOptions [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions) | ||
*/ | ||
constructor(options = {}) { | ||
this._cacheName = cacheNames_js.cacheNames.getRuntimeName(options.cacheName); | ||
this._plugins = options.plugins || []; | ||
this._matchOptions = options.matchOptions; | ||
} | ||
/** | ||
* This method will perform a request strategy and follows an API that | ||
* will work with the | ||
* [Workbox Router]{@link workbox.routing.Router}. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request The request to run this strategy for. | ||
* @param {Event} [options.event] The event that triggered the request. | ||
* @return {Promise<Response>} | ||
*/ | ||
{ | ||
assert_mjs.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-strategies', | ||
className: 'CacheOnly', | ||
funcName: 'makeRequest', | ||
paramName: 'request' | ||
}); | ||
} | ||
const response = await cacheWrapper_mjs.cacheWrapper.match({ | ||
cacheName: this._cacheName, | ||
request, | ||
async handle({ | ||
event, | ||
matchOptions: this._matchOptions, | ||
plugins: this._plugins | ||
}); | ||
request | ||
}) { | ||
{ | ||
assert_js.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-strategies', | ||
className: 'CacheOnly', | ||
funcName: 'makeRequest', | ||
paramName: 'request' | ||
}); | ||
} | ||
{ | ||
logger_mjs.logger.groupCollapsed(messages.strategyStart('CacheOnly', request)); | ||
const response = await cacheWrapper_js.cacheWrapper.match({ | ||
cacheName: this._cacheName, | ||
request, | ||
event, | ||
matchOptions: this._matchOptions, | ||
plugins: this._plugins | ||
}); | ||
if (response) { | ||
logger_mjs.logger.log(`Found a cached response in the '${this._cacheName}'` + ` cache.`); | ||
messages.printFinalResponse(response); | ||
} else { | ||
logger_mjs.logger.log(`No response found in the '${this._cacheName}' cache.`); | ||
{ | ||
logger_js.logger.groupCollapsed(messages.strategyStart('CacheOnly', request)); | ||
if (response) { | ||
logger_js.logger.log(`Found a cached response in the '${this._cacheName}'` + ` cache.`); | ||
messages.printFinalResponse(response); | ||
} else { | ||
logger_js.logger.log(`No response found in the '${this._cacheName}' cache.`); | ||
} | ||
logger_js.logger.groupEnd(); | ||
} | ||
logger_mjs.logger.groupEnd(); | ||
} | ||
if (!response) { | ||
throw new WorkboxError_js.WorkboxError('no-response', { | ||
url: request.url | ||
}); | ||
} | ||
if (!response) { | ||
throw new WorkboxError_mjs.WorkboxError('no-response', { | ||
url: request.url | ||
}); | ||
return response; | ||
} | ||
return response; | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const cacheOkAndOpaquePlugin = { | ||
/** | ||
* Returns a valid response (to allow caching) if the status is 200 (OK) or | ||
* 0 (opaque). | ||
* | ||
* @param {Object} options | ||
* @param {Response} options.response | ||
* @return {Response|null} | ||
* | ||
* @private | ||
*/ | ||
cacheWillUpdate: async ({ | ||
response | ||
}) => { | ||
if (response.status === 200 || response.status === 0) { | ||
return response; | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const cacheOkAndOpaquePlugin = { | ||
return null; | ||
} | ||
}; | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Returns a valid response (to allow caching) if the status is 200 (OK) or | ||
* 0 (opaque). | ||
* An implementation of a | ||
* [network first]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-falling-back-to-cache} | ||
* request strategy. | ||
* | ||
* @param {Object} options | ||
* @param {Response} options.response | ||
* @return {Response|null} | ||
* By default, this strategy will cache responses with a 200 status code as | ||
* well as [opaque responses]{@link https://developers.google.com/web/tools/workbox/guides/handle-third-party-requests}. | ||
* Opaque responses are are cross-origin requests where the response doesn't | ||
* support [CORS]{@link https://enable-cors.org/}. | ||
* | ||
* @private | ||
* If the network request fails, and there is no cache match, this will throw | ||
* a `WorkboxError` exception. | ||
* | ||
* @memberof workbox.strategies | ||
*/ | ||
cacheWillUpdate: ({ | ||
response | ||
}) => { | ||
if (response.status === 200 || response.status === 0) { | ||
return response; | ||
} | ||
return null; | ||
} | ||
}; | ||
class NetworkFirst { | ||
/** | ||
* @param {Object} options | ||
* @param {string} options.cacheName Cache name to store and retrieve | ||
* requests. Defaults to cache names provided by | ||
* [workbox-core]{@link workbox.core.cacheNames}. | ||
* @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} | ||
* to use in conjunction with this caching strategy. | ||
* @param {Object} options.fetchOptions Values passed along to the | ||
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) | ||
* of all fetch() requests made by this strategy. | ||
* @param {Object} options.matchOptions [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions) | ||
* @param {number} options.networkTimeoutSeconds If set, any network requests | ||
* that fail to respond within the timeout will fallback to the cache. | ||
* | ||
* This option can be used to combat | ||
* "[lie-fi]{@link https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi}" | ||
* scenarios. | ||
*/ | ||
constructor(options = {}) { | ||
this._cacheName = cacheNames_js.cacheNames.getRuntimeName(options.cacheName); | ||
/* | ||
Copyright 2018 Google LLC | ||
if (options.plugins) { | ||
let isUsingCacheWillUpdate = options.plugins.some(plugin => !!plugin.cacheWillUpdate); | ||
this._plugins = isUsingCacheWillUpdate ? options.plugins : [cacheOkAndOpaquePlugin, ...options.plugins]; | ||
} else { | ||
// No plugins passed in, use the default plugin. | ||
this._plugins = [cacheOkAndOpaquePlugin]; | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* An implementation of a | ||
* [network first]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-falling-back-to-cache} | ||
* request strategy. | ||
* | ||
* By default, this strategy will cache responses with a 200 status code as | ||
* well as [opaque responses]{@link https://developers.google.com/web/tools/workbox/guides/handle-third-party-requests}. | ||
* Opaque responses are are cross-origin requests where the response doesn't | ||
* support [CORS]{@link https://enable-cors.org/}. | ||
* | ||
* If the network request fails, and there is no cache match, this will throw | ||
* a `WorkboxError` exception. | ||
* | ||
* @memberof workbox.strategies | ||
*/ | ||
this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0; | ||
class NetworkFirst { | ||
/** | ||
* @param {Object} options | ||
* @param {string} options.cacheName Cache name to store and retrieve | ||
* requests. Defaults to cache names provided by | ||
* [workbox-core]{@link workbox.core.cacheNames}. | ||
* @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} | ||
* to use in conjunction with this caching strategy. | ||
* @param {Object} options.fetchOptions Values passed along to the | ||
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) | ||
* of all fetch() requests made by this strategy. | ||
* @param {Object} options.matchOptions [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions) | ||
* @param {number} options.networkTimeoutSeconds If set, any network requests | ||
* that fail to respond within the timeout will fallback to the cache. | ||
* | ||
* This option can be used to combat | ||
* "[lie-fi]{@link https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi}" | ||
* scenarios. | ||
*/ | ||
constructor(options = {}) { | ||
this._cacheName = cacheNames_mjs.cacheNames.getRuntimeName(options.cacheName); | ||
{ | ||
if (this._networkTimeoutSeconds) { | ||
assert_js.assert.isType(this._networkTimeoutSeconds, 'number', { | ||
moduleName: 'workbox-strategies', | ||
className: 'NetworkFirst', | ||
funcName: 'constructor', | ||
paramName: 'networkTimeoutSeconds' | ||
}); | ||
} | ||
} | ||
if (options.plugins) { | ||
let isUsingCacheWillUpdate = options.plugins.some(plugin => !!plugin.cacheWillUpdate); | ||
this._plugins = isUsingCacheWillUpdate ? options.plugins : [cacheOkAndOpaquePlugin, ...options.plugins]; | ||
} else { | ||
// No plugins passed in, use the default plugin. | ||
this._plugins = [cacheOkAndOpaquePlugin]; | ||
this._fetchOptions = options.fetchOptions; | ||
this._matchOptions = options.matchOptions; | ||
} | ||
/** | ||
* This method will perform a request strategy and follows an API that | ||
* will work with the | ||
* [Workbox Router]{@link workbox.routing.Router}. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request The request to run this strategy for. | ||
* @param {Event} [options.event] The event that triggered the request. | ||
* @return {Promise<Response>} | ||
*/ | ||
this._networkTimeoutSeconds = options.networkTimeoutSeconds; | ||
{ | ||
if (this._networkTimeoutSeconds) { | ||
assert_mjs.assert.isType(this._networkTimeoutSeconds, 'number', { | ||
async handle({ | ||
event, | ||
request | ||
}) { | ||
const logs = []; | ||
{ | ||
assert_js.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-strategies', | ||
className: 'NetworkFirst', | ||
funcName: 'constructor', | ||
paramName: 'networkTimeoutSeconds' | ||
funcName: 'handle', | ||
paramName: 'makeRequest' | ||
}); | ||
} | ||
} | ||
this._fetchOptions = options.fetchOptions || null; | ||
this._matchOptions = options.matchOptions || null; | ||
} | ||
/** | ||
* This method will perform a request strategy and follows an API that | ||
* will work with the | ||
* [Workbox Router]{@link workbox.routing.Router}. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request The request to run this strategy for. | ||
* @param {Event} [options.event] The event that triggered the request. | ||
* @return {Promise<Response>} | ||
*/ | ||
const promises = []; | ||
let timeoutId; | ||
if (this._networkTimeoutSeconds) { | ||
const { | ||
id, | ||
promise | ||
} = this._getTimeoutPromise({ | ||
request, | ||
event, | ||
logs | ||
}); | ||
async handle({ | ||
event, | ||
request | ||
}) { | ||
return this.makeRequest({ | ||
event, | ||
request: request || event.request | ||
}); | ||
} | ||
/** | ||
* This method can be used to perform a make a standalone request outside the | ||
* context of the [Workbox Router]{@link workbox.routing.Router}. | ||
* | ||
* See "[Advanced Recipes](https://developers.google.com/web/tools/workbox/guides/advanced-recipes#make-requests)" | ||
* for more usage information. | ||
* | ||
* @param {Object} options | ||
* @param {Request|string} options.request Either a | ||
* [`Request`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Request} | ||
* object, or a string URL, corresponding to the request to be made. | ||
* @param {FetchEvent} [options.event] If provided, `event.waitUntil()` will | ||
* be called automatically to extend the service worker's lifetime. | ||
* @return {Promise<Response>} | ||
*/ | ||
timeoutId = id; | ||
promises.push(promise); | ||
} | ||
async makeRequest({ | ||
event, | ||
request | ||
}) { | ||
const logs = []; | ||
if (typeof request === 'string') { | ||
request = new Request(request); | ||
} | ||
{ | ||
assert_mjs.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-strategies', | ||
className: 'NetworkFirst', | ||
funcName: 'handle', | ||
paramName: 'makeRequest' | ||
}); | ||
} | ||
const promises = []; | ||
let timeoutId; | ||
if (this._networkTimeoutSeconds) { | ||
const { | ||
id, | ||
promise | ||
} = this._getTimeoutPromise({ | ||
const networkPromise = this._getNetworkPromise({ | ||
timeoutId, | ||
request, | ||
@@ -521,619 +432,547 @@ event, | ||
timeoutId = id; | ||
promises.push(promise); | ||
} | ||
promises.push(networkPromise); // Promise.race() will resolve as soon as the first promise resolves. | ||
const networkPromise = this._getNetworkPromise({ | ||
timeoutId, | ||
request, | ||
event, | ||
logs | ||
}); | ||
let response = await Promise.race(promises); // If Promise.race() resolved with null, it might be due to a network | ||
// timeout + a cache miss. If that were to happen, we'd rather wait until | ||
// the networkPromise resolves instead of returning null. | ||
// Note that it's fine to await an already-resolved promise, so we don't | ||
// have to check to see if it's still "in flight". | ||
promises.push(networkPromise); // Promise.race() will resolve as soon as the first promise resolves. | ||
if (!response) { | ||
response = await networkPromise; | ||
} | ||
let response = await Promise.race(promises); // If Promise.race() resolved with null, it might be due to a network | ||
// timeout + a cache miss. If that were to happen, we'd rather wait until | ||
// the networkPromise resolves instead of returning null. | ||
// Note that it's fine to await an already-resolved promise, so we don't | ||
// have to check to see if it's still "in flight". | ||
{ | ||
logger_js.logger.groupCollapsed(messages.strategyStart('NetworkFirst', request)); | ||
if (!response) { | ||
response = await networkPromise; | ||
} | ||
for (let log of logs) { | ||
logger_js.logger.log(log); | ||
} | ||
{ | ||
logger_mjs.logger.groupCollapsed(messages.strategyStart('NetworkFirst', request)); | ||
messages.printFinalResponse(response); | ||
logger_js.logger.groupEnd(); | ||
} | ||
for (let log of logs) { | ||
logger_mjs.logger.log(log); | ||
if (!response) { | ||
throw new WorkboxError_js.WorkboxError('no-response', { | ||
url: request.url | ||
}); | ||
} | ||
messages.printFinalResponse(response); | ||
logger_mjs.logger.groupEnd(); | ||
return response; | ||
} | ||
/** | ||
* @param {Object} options | ||
* @param {Request} options.request | ||
* @param {Array} options.logs A reference to the logs array | ||
* @param {Event} [options.event] | ||
* @return {Promise<Response>} | ||
* | ||
* @private | ||
*/ | ||
if (!response) { | ||
throw new WorkboxError_mjs.WorkboxError('no-response', { | ||
url: request.url | ||
}); | ||
} | ||
return response; | ||
} | ||
/** | ||
* @param {Object} options | ||
* @param {Request} options.request | ||
* @param {Array} options.logs A reference to the logs array | ||
* @param {Event} [options.event] | ||
* @return {Promise<Response>} | ||
* | ||
* @private | ||
*/ | ||
_getTimeoutPromise({ | ||
request, | ||
logs, | ||
event | ||
}) { | ||
let timeoutId; | ||
const timeoutPromise = new Promise(resolve => { | ||
const onNetworkTimeout = async () => { | ||
{ | ||
logs.push(`Timing out the network response at ` + `${this._networkTimeoutSeconds} seconds.`); | ||
} | ||
resolve((await this._respondFromCache({ | ||
request, | ||
event | ||
}))); | ||
}; | ||
_getTimeoutPromise({ | ||
request, | ||
logs, | ||
event | ||
}) { | ||
let timeoutId; | ||
const timeoutPromise = new Promise(resolve => { | ||
const onNetworkTimeout = async () => { | ||
{ | ||
logs.push(`Timing out the network response at ` + `${this._networkTimeoutSeconds} seconds.`); | ||
} | ||
resolve((await this._respondFromCache({ | ||
request, | ||
event | ||
}))); | ||
timeoutId = setTimeout(onNetworkTimeout, this._networkTimeoutSeconds * 1000); | ||
}); | ||
return { | ||
promise: timeoutPromise, | ||
id: timeoutId | ||
}; | ||
} | ||
/** | ||
* @param {Object} options | ||
* @param {number|undefined} options.timeoutId | ||
* @param {Request} options.request | ||
* @param {Array} options.logs A reference to the logs Array. | ||
* @param {Event} [options.event] | ||
* @return {Promise<Response>} | ||
* | ||
* @private | ||
*/ | ||
timeoutId = setTimeout(onNetworkTimeout, this._networkTimeoutSeconds * 1000); | ||
}); | ||
return { | ||
promise: timeoutPromise, | ||
id: timeoutId | ||
}; | ||
} | ||
/** | ||
* @param {Object} options | ||
* @param {number|undefined} options.timeoutId | ||
* @param {Request} options.request | ||
* @param {Array} options.logs A reference to the logs Array. | ||
* @param {Event} [options.event] | ||
* @return {Promise<Response>} | ||
* | ||
* @private | ||
*/ | ||
async _getNetworkPromise({ | ||
timeoutId, | ||
request, | ||
logs, | ||
event | ||
}) { | ||
let error; | ||
let response; | ||
async _getNetworkPromise({ | ||
timeoutId, | ||
request, | ||
logs, | ||
event | ||
}) { | ||
let error; | ||
let response; | ||
try { | ||
response = await fetchWrapper_js.fetchWrapper.fetch({ | ||
request, | ||
event, | ||
fetchOptions: this._fetchOptions, | ||
plugins: this._plugins | ||
}); | ||
} catch (err) { | ||
error = err; | ||
} | ||
try { | ||
response = await fetchWrapper_mjs.fetchWrapper.fetch({ | ||
request, | ||
event, | ||
fetchOptions: this._fetchOptions, | ||
plugins: this._plugins | ||
}); | ||
} catch (err) { | ||
error = err; | ||
} | ||
if (timeoutId) { | ||
clearTimeout(timeoutId); | ||
} | ||
{ | ||
if (response) { | ||
logs.push(`Got response from network.`); | ||
} else { | ||
logs.push(`Unable to get a response from the network. Will respond ` + `with a cached response.`); | ||
if (timeoutId) { | ||
clearTimeout(timeoutId); | ||
} | ||
} | ||
if (error || !response) { | ||
response = await this._respondFromCache({ | ||
request, | ||
event | ||
}); | ||
{ | ||
if (response) { | ||
logs.push(`Found a cached response in the '${this._cacheName}'` + ` cache.`); | ||
logs.push(`Got response from network.`); | ||
} else { | ||
logs.push(`No response found in the '${this._cacheName}' cache.`); | ||
logs.push(`Unable to get a response from the network. Will respond ` + `with a cached response.`); | ||
} | ||
} | ||
} else { | ||
// Keep the service worker alive while we put the request in the cache | ||
const responseClone = response.clone(); | ||
const cachePut = cacheWrapper_mjs.cacheWrapper.put({ | ||
cacheName: this._cacheName, | ||
request, | ||
response: responseClone, | ||
event, | ||
plugins: this._plugins | ||
}); | ||
if (event) { | ||
try { | ||
// The event has been responded to so we can keep the SW alive to | ||
// respond to the request | ||
event.waitUntil(cachePut); | ||
} catch (err) { | ||
{ | ||
logger_mjs.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache for '${getFriendlyURL_mjs.getFriendlyURL(request.url)}'.`); | ||
if (error || !response) { | ||
response = await this._respondFromCache({ | ||
request, | ||
event | ||
}); | ||
{ | ||
if (response) { | ||
logs.push(`Found a cached response in the '${this._cacheName}'` + ` cache.`); | ||
} else { | ||
logs.push(`No response found in the '${this._cacheName}' cache.`); | ||
} | ||
} | ||
} else { | ||
// Keep the service worker alive while we put the request in the cache | ||
const responseClone = response.clone(); | ||
const cachePut = cacheWrapper_js.cacheWrapper.put({ | ||
cacheName: this._cacheName, | ||
request, | ||
response: responseClone, | ||
event, | ||
plugins: this._plugins | ||
}); | ||
if (event) { | ||
try { | ||
// The event has been responded to so we can keep the SW alive to | ||
// respond to the request | ||
event.waitUntil(cachePut); | ||
} catch (err) { | ||
{ | ||
logger_js.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache for '${getFriendlyURL_js.getFriendlyURL(request.url)}'.`); | ||
} | ||
} | ||
} | ||
} | ||
return response; | ||
} | ||
/** | ||
* Used if the network timeouts or fails to make the request. | ||
* | ||
* @param {Object} options | ||
* @param {Request} request The request to match in the cache | ||
* @param {Event} [options.event] | ||
* @return {Promise<Object>} | ||
* | ||
* @private | ||
*/ | ||
return response; | ||
} | ||
/** | ||
* Used if the network timeouts or fails to make the request. | ||
* | ||
* @param {Object} options | ||
* @param {Request} request The request to match in the cache | ||
* @param {Event} [options.event] | ||
* @return {Promise<Object>} | ||
* | ||
* @private | ||
*/ | ||
_respondFromCache({ | ||
event, | ||
request | ||
}) { | ||
return cacheWrapper_mjs.cacheWrapper.match({ | ||
cacheName: this._cacheName, | ||
request, | ||
_respondFromCache({ | ||
event, | ||
matchOptions: this._matchOptions, | ||
plugins: this._plugins | ||
}); | ||
} | ||
request | ||
}) { | ||
return cacheWrapper_js.cacheWrapper.match({ | ||
cacheName: this._cacheName, | ||
request, | ||
event, | ||
matchOptions: this._matchOptions, | ||
plugins: this._plugins | ||
}); | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* An implementation of a | ||
* [network-only]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-only} | ||
* request strategy. | ||
* | ||
* This class is useful if you want to take advantage of any | ||
* [Workbox plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}. | ||
* | ||
* If the network request fails, this will throw a `WorkboxError` exception. | ||
* | ||
* @memberof workbox.strategies | ||
*/ | ||
class NetworkOnly { | ||
/** | ||
* @param {Object} options | ||
* @param {string} options.cacheName Cache name to store and retrieve | ||
* requests. Defaults to cache names provided by | ||
* [workbox-core]{@link workbox.core.cacheNames}. | ||
* @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} | ||
* to use in conjunction with this caching strategy. | ||
* @param {Object} options.fetchOptions Values passed along to the | ||
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) | ||
* of all fetch() requests made by this strategy. | ||
*/ | ||
constructor(options = {}) { | ||
this._cacheName = cacheNames_mjs.cacheNames.getRuntimeName(options.cacheName); | ||
this._plugins = options.plugins || []; | ||
this._fetchOptions = options.fetchOptions || null; | ||
} | ||
/** | ||
* This method will perform a request strategy and follows an API that | ||
* will work with the | ||
* [Workbox Router]{@link workbox.routing.Router}. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request The request to run this strategy for. | ||
* @param {Event} [options.event] The event that triggered the request. | ||
* @return {Promise<Response>} | ||
*/ | ||
/* | ||
Copyright 2018 Google LLC | ||
async handle({ | ||
event, | ||
request | ||
}) { | ||
return this.makeRequest({ | ||
event, | ||
request: request || event.request | ||
}); | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* This method can be used to perform a make a standalone request outside the | ||
* context of the [Workbox Router]{@link workbox.routing.Router}. | ||
* An implementation of a | ||
* [network-only]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-only} | ||
* request strategy. | ||
* | ||
* See "[Advanced Recipes](https://developers.google.com/web/tools/workbox/guides/advanced-recipes#make-requests)" | ||
* for more usage information. | ||
* This class is useful if you want to take advantage of any | ||
* [Workbox plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}. | ||
* | ||
* @param {Object} options | ||
* @param {Request|string} options.request Either a | ||
* [`Request`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Request} | ||
* object, or a string URL, corresponding to the request to be made. | ||
* @param {FetchEvent} [options.event] If provided, `event.waitUntil()` will | ||
* be called automatically to extend the service worker's lifetime. | ||
* @return {Promise<Response>} | ||
* If the network request fails, this will throw a `WorkboxError` exception. | ||
* | ||
* @memberof workbox.strategies | ||
*/ | ||
async makeRequest({ | ||
event, | ||
request | ||
}) { | ||
if (typeof request === 'string') { | ||
request = new Request(request); | ||
class NetworkOnly { | ||
/** | ||
* @param {Object} options | ||
* @param {string} options.cacheName Cache name to store and retrieve | ||
* requests. Defaults to cache names provided by | ||
* [workbox-core]{@link workbox.core.cacheNames}. | ||
* @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} | ||
* to use in conjunction with this caching strategy. | ||
* @param {Object} options.fetchOptions Values passed along to the | ||
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) | ||
* of all fetch() requests made by this strategy. | ||
*/ | ||
constructor(options = {}) { | ||
this._plugins = options.plugins || []; | ||
this._fetchOptions = options.fetchOptions; | ||
} | ||
/** | ||
* This method will perform a request strategy and follows an API that | ||
* will work with the | ||
* [Workbox Router]{@link workbox.routing.Router}. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request The request to run this strategy for. | ||
* @param {Event} [options.event] The event that triggered the request. | ||
* @return {Promise<Response>} | ||
*/ | ||
{ | ||
assert_mjs.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-strategies', | ||
className: 'NetworkOnly', | ||
funcName: 'handle', | ||
paramName: 'request' | ||
}); | ||
} | ||
let error; | ||
let response; | ||
async handle({ | ||
event, | ||
request | ||
}) { | ||
{ | ||
assert_js.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-strategies', | ||
className: 'NetworkOnly', | ||
funcName: 'handle', | ||
paramName: 'request' | ||
}); | ||
} | ||
try { | ||
response = await fetchWrapper_mjs.fetchWrapper.fetch({ | ||
request, | ||
event, | ||
fetchOptions: this._fetchOptions, | ||
plugins: this._plugins | ||
}); | ||
} catch (err) { | ||
error = err; | ||
} | ||
let error; | ||
let response; | ||
{ | ||
logger_mjs.logger.groupCollapsed(messages.strategyStart('NetworkOnly', request)); | ||
if (response) { | ||
logger_mjs.logger.log(`Got response from network.`); | ||
} else { | ||
logger_mjs.logger.log(`Unable to get a response from the network.`); | ||
try { | ||
response = await fetchWrapper_js.fetchWrapper.fetch({ | ||
request, | ||
event, | ||
fetchOptions: this._fetchOptions, | ||
plugins: this._plugins | ||
}); | ||
} catch (err) { | ||
error = err; | ||
} | ||
messages.printFinalResponse(response); | ||
logger_mjs.logger.groupEnd(); | ||
} | ||
{ | ||
logger_js.logger.groupCollapsed(messages.strategyStart('NetworkOnly', request)); | ||
if (!response) { | ||
throw new WorkboxError_mjs.WorkboxError('no-response', { | ||
url: request.url, | ||
error | ||
}); | ||
} | ||
if (response) { | ||
logger_js.logger.log(`Got response from network.`); | ||
} else { | ||
logger_js.logger.log(`Unable to get a response from the network.`); | ||
} | ||
return response; | ||
} | ||
messages.printFinalResponse(response); | ||
logger_js.logger.groupEnd(); | ||
} | ||
} | ||
if (!response) { | ||
throw new WorkboxError_js.WorkboxError('no-response', { | ||
url: request.url, | ||
error | ||
}); | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* An implementation of a | ||
* [stale-while-revalidate]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate} | ||
* request strategy. | ||
* | ||
* Resources are requested from both the cache and the network in parallel. | ||
* The strategy will respond with the cached version if available, otherwise | ||
* wait for the network response. The cache is updated with the network response | ||
* with each successful request. | ||
* | ||
* By default, this strategy will cache responses with a 200 status code as | ||
* well as [opaque responses]{@link https://developers.google.com/web/tools/workbox/guides/handle-third-party-requests}. | ||
* Opaque responses are are cross-origin requests where the response doesn't | ||
* support [CORS]{@link https://enable-cors.org/}. | ||
* | ||
* If the network request fails, and there is no cache match, this will throw | ||
* a `WorkboxError` exception. | ||
* | ||
* @memberof workbox.strategies | ||
*/ | ||
class StaleWhileRevalidate { | ||
/** | ||
* @param {Object} options | ||
* @param {string} options.cacheName Cache name to store and retrieve | ||
* requests. Defaults to cache names provided by | ||
* [workbox-core]{@link workbox.core.cacheNames}. | ||
* @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} | ||
* to use in conjunction with this caching strategy. | ||
* @param {Object} options.fetchOptions Values passed along to the | ||
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) | ||
* of all fetch() requests made by this strategy. | ||
* @param {Object} options.matchOptions [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions) | ||
*/ | ||
constructor(options = {}) { | ||
this._cacheName = cacheNames_mjs.cacheNames.getRuntimeName(options.cacheName); | ||
this._plugins = options.plugins || []; | ||
if (options.plugins) { | ||
let isUsingCacheWillUpdate = options.plugins.some(plugin => !!plugin.cacheWillUpdate); | ||
this._plugins = isUsingCacheWillUpdate ? options.plugins : [cacheOkAndOpaquePlugin, ...options.plugins]; | ||
} else { | ||
// No plugins passed in, use the default plugin. | ||
this._plugins = [cacheOkAndOpaquePlugin]; | ||
return response; | ||
} | ||
this._fetchOptions = options.fetchOptions || null; | ||
this._matchOptions = options.matchOptions || null; | ||
} | ||
/** | ||
* This method will perform a request strategy and follows an API that | ||
* will work with the | ||
* [Workbox Router]{@link workbox.routing.Router}. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request The request to run this strategy for. | ||
* @param {Event} [options.event] The event that triggered the request. | ||
* @return {Promise<Response>} | ||
*/ | ||
/* | ||
Copyright 2018 Google LLC | ||
async handle({ | ||
event, | ||
request | ||
}) { | ||
return this.makeRequest({ | ||
event, | ||
request: request || event.request | ||
}); | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* This method can be used to perform a make a standalone request outside the | ||
* context of the [Workbox Router]{@link workbox.routing.Router}. | ||
* An implementation of a | ||
* [stale-while-revalidate]{@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate} | ||
* request strategy. | ||
* | ||
* See "[Advanced Recipes](https://developers.google.com/web/tools/workbox/guides/advanced-recipes#make-requests)" | ||
* for more usage information. | ||
* Resources are requested from both the cache and the network in parallel. | ||
* The strategy will respond with the cached version if available, otherwise | ||
* wait for the network response. The cache is updated with the network response | ||
* with each successful request. | ||
* | ||
* @param {Object} options | ||
* @param {Request|string} options.request Either a | ||
* [`Request`]{@link https://developer.mozilla.org/en-US/docs/Web/API/Request} | ||
* object, or a string URL, corresponding to the request to be made. | ||
* @param {FetchEvent} [options.event] If provided, `event.waitUntil()` will | ||
* be called automatically to extend the service worker's lifetime. | ||
* @return {Promise<Response>} | ||
* By default, this strategy will cache responses with a 200 status code as | ||
* well as [opaque responses]{@link https://developers.google.com/web/tools/workbox/guides/handle-third-party-requests}. | ||
* Opaque responses are are cross-origin requests where the response doesn't | ||
* support [CORS]{@link https://enable-cors.org/}. | ||
* | ||
* If the network request fails, and there is no cache match, this will throw | ||
* a `WorkboxError` exception. | ||
* | ||
* @memberof workbox.strategies | ||
*/ | ||
class StaleWhileRevalidate { | ||
/** | ||
* @param {Object} options | ||
* @param {string} options.cacheName Cache name to store and retrieve | ||
* requests. Defaults to cache names provided by | ||
* [workbox-core]{@link workbox.core.cacheNames}. | ||
* @param {Array<Object>} options.plugins [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins} | ||
* to use in conjunction with this caching strategy. | ||
* @param {Object} options.fetchOptions Values passed along to the | ||
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters) | ||
* of all fetch() requests made by this strategy. | ||
* @param {Object} options.matchOptions [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions) | ||
*/ | ||
constructor(options = {}) { | ||
this._cacheName = cacheNames_js.cacheNames.getRuntimeName(options.cacheName); | ||
this._plugins = options.plugins || []; | ||
async makeRequest({ | ||
event, | ||
request | ||
}) { | ||
const logs = []; | ||
if (options.plugins) { | ||
let isUsingCacheWillUpdate = options.plugins.some(plugin => !!plugin.cacheWillUpdate); | ||
this._plugins = isUsingCacheWillUpdate ? options.plugins : [cacheOkAndOpaquePlugin, ...options.plugins]; | ||
} else { | ||
// No plugins passed in, use the default plugin. | ||
this._plugins = [cacheOkAndOpaquePlugin]; | ||
} | ||
if (typeof request === 'string') { | ||
request = new Request(request); | ||
this._fetchOptions = options.fetchOptions; | ||
this._matchOptions = options.matchOptions; | ||
} | ||
/** | ||
* This method will perform a request strategy and follows an API that | ||
* will work with the | ||
* [Workbox Router]{@link workbox.routing.Router}. | ||
* | ||
* @param {Object} options | ||
* @param {Request} options.request The request to run this strategy for. | ||
* @param {Event} [options.event] The event that triggered the request. | ||
* @return {Promise<Response>} | ||
*/ | ||
{ | ||
assert_mjs.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-strategies', | ||
className: 'StaleWhileRevalidate', | ||
funcName: 'handle', | ||
paramName: 'request' | ||
}); | ||
} | ||
const fetchAndCachePromise = this._getFromNetwork({ | ||
request, | ||
event | ||
}); | ||
let response = await cacheWrapper_mjs.cacheWrapper.match({ | ||
cacheName: this._cacheName, | ||
request, | ||
async handle({ | ||
event, | ||
matchOptions: this._matchOptions, | ||
plugins: this._plugins | ||
}); | ||
let error; | ||
request | ||
}) { | ||
const logs = []; | ||
if (response) { | ||
{ | ||
logs.push(`Found a cached response in the '${this._cacheName}'` + ` cache. Will update with the network response in the background.`); | ||
assert_js.assert.isInstance(request, Request, { | ||
moduleName: 'workbox-strategies', | ||
className: 'StaleWhileRevalidate', | ||
funcName: 'handle', | ||
paramName: 'request' | ||
}); | ||
} | ||
if (event) { | ||
try { | ||
event.waitUntil(fetchAndCachePromise); | ||
} catch (error) { | ||
{ | ||
logger_mjs.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache for '${getFriendlyURL_mjs.getFriendlyURL(request.url)}'.`); | ||
const fetchAndCachePromise = this._getFromNetwork({ | ||
request, | ||
event | ||
}); | ||
let response = await cacheWrapper_js.cacheWrapper.match({ | ||
cacheName: this._cacheName, | ||
request, | ||
event, | ||
matchOptions: this._matchOptions, | ||
plugins: this._plugins | ||
}); | ||
let error; | ||
if (response) { | ||
{ | ||
logs.push(`Found a cached response in the '${this._cacheName}'` + ` cache. Will update with the network response in the background.`); | ||
} | ||
if (event) { | ||
try { | ||
event.waitUntil(fetchAndCachePromise); | ||
} catch (error) { | ||
{ | ||
logger_js.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache for '${getFriendlyURL_js.getFriendlyURL(request.url)}'.`); | ||
} | ||
} | ||
} | ||
} else { | ||
{ | ||
logs.push(`No response found in the '${this._cacheName}' cache. ` + `Will wait for the network response.`); | ||
} | ||
try { | ||
response = await fetchAndCachePromise; | ||
} catch (err) { | ||
error = err; | ||
} | ||
} | ||
} else { | ||
{ | ||
logs.push(`No response found in the '${this._cacheName}' cache. ` + `Will wait for the network response.`); | ||
} | ||
logger_js.logger.groupCollapsed(messages.strategyStart('StaleWhileRevalidate', request)); | ||
try { | ||
response = await fetchAndCachePromise; | ||
} catch (err) { | ||
error = err; | ||
for (let log of logs) { | ||
logger_js.logger.log(log); | ||
} | ||
messages.printFinalResponse(response); | ||
logger_js.logger.groupEnd(); | ||
} | ||
} | ||
{ | ||
logger_mjs.logger.groupCollapsed(messages.strategyStart('StaleWhileRevalidate', request)); | ||
for (let log of logs) { | ||
logger_mjs.logger.log(log); | ||
if (!response) { | ||
throw new WorkboxError_js.WorkboxError('no-response', { | ||
url: request.url, | ||
error | ||
}); | ||
} | ||
messages.printFinalResponse(response); | ||
logger_mjs.logger.groupEnd(); | ||
return response; | ||
} | ||
/** | ||
* @param {Object} options | ||
* @param {Request} options.request | ||
* @param {Event} [options.event] | ||
* @return {Promise<Response>} | ||
* | ||
* @private | ||
*/ | ||
if (!response) { | ||
throw new WorkboxError_mjs.WorkboxError('no-response', { | ||
url: request.url, | ||
error | ||
}); | ||
} | ||
return response; | ||
} | ||
/** | ||
* @param {Object} options | ||
* @param {Request} options.request | ||
* @param {Event} [options.event] | ||
* @return {Promise<Response>} | ||
* | ||
* @private | ||
*/ | ||
async _getFromNetwork({ | ||
request, | ||
event | ||
}) { | ||
const response = await fetchWrapper_mjs.fetchWrapper.fetch({ | ||
async _getFromNetwork({ | ||
request, | ||
event, | ||
fetchOptions: this._fetchOptions, | ||
plugins: this._plugins | ||
}); | ||
const cachePutPromise = cacheWrapper_mjs.cacheWrapper.put({ | ||
cacheName: this._cacheName, | ||
request, | ||
response: response.clone(), | ||
event, | ||
plugins: this._plugins | ||
}); | ||
event | ||
}) { | ||
const response = await fetchWrapper_js.fetchWrapper.fetch({ | ||
request, | ||
event, | ||
fetchOptions: this._fetchOptions, | ||
plugins: this._plugins | ||
}); | ||
const cachePutPromise = cacheWrapper_js.cacheWrapper.put({ | ||
cacheName: this._cacheName, | ||
request, | ||
response: response.clone(), | ||
event, | ||
plugins: this._plugins | ||
}); | ||
if (event) { | ||
try { | ||
event.waitUntil(cachePutPromise); | ||
} catch (error) { | ||
{ | ||
logger_mjs.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache for '${getFriendlyURL_mjs.getFriendlyURL(request.url)}'.`); | ||
if (event) { | ||
try { | ||
event.waitUntil(cachePutPromise); | ||
} catch (error) { | ||
{ | ||
logger_js.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache for '${getFriendlyURL_js.getFriendlyURL(request.url)}'.`); | ||
} | ||
} | ||
} | ||
return response; | ||
} | ||
return response; | ||
} | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const mapping = { | ||
cacheFirst: CacheFirst, | ||
cacheOnly: CacheOnly, | ||
networkFirst: NetworkFirst, | ||
networkOnly: NetworkOnly, | ||
staleWhileRevalidate: StaleWhileRevalidate | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const mapping = { | ||
cacheFirst: CacheFirst, | ||
cacheOnly: CacheOnly, | ||
networkFirst: NetworkFirst, | ||
networkOnly: NetworkOnly, | ||
staleWhileRevalidate: StaleWhileRevalidate | ||
}; | ||
const deprecate = strategy => { | ||
const StrategyCtr = mapping[strategy]; | ||
return options => { | ||
{ | ||
const strategyCtrName = strategy[0].toUpperCase() + strategy.slice(1); | ||
logger_js.logger.warn(`The 'workbox.strategies.${strategy}()' function has been ` + `deprecated and will be removed in a future version of Workbox.\n` + `Please use 'new workbox.strategies.${strategyCtrName}()' instead.`); | ||
} | ||
const deprecate = strategy => { | ||
const StrategyCtr = mapping[strategy]; | ||
return options => { | ||
{ | ||
const strategyCtrName = strategy[0].toUpperCase() + strategy.slice(1); | ||
logger_mjs.logger.warn(`The 'workbox.strategies.${strategy}()' function has been ` + `deprecated and will be removed in a future version of Workbox.\n` + `Please use 'new workbox.strategies.${strategyCtrName}()' instead.`); | ||
} | ||
return new StrategyCtr(options); | ||
return new StrategyCtr(options); | ||
}; | ||
}; | ||
}; | ||
/** | ||
* @function workbox.strategies.cacheFirst | ||
* @param {Object} options See the {@link workbox.strategies.CacheFirst} | ||
* constructor for more info. | ||
* @deprecated since v4.0.0 | ||
*/ | ||
/** | ||
* @function workbox.strategies.cacheFirst | ||
* @param {Object} options See the {@link workbox.strategies.CacheFirst} | ||
* constructor for more info. | ||
* @deprecated since v4.0.0 | ||
*/ | ||
const cacheFirst = deprecate('cacheFirst'); | ||
/** | ||
* @function workbox.strategies.cacheOnly | ||
* @param {Object} options See the {@link workbox.strategies.CacheOnly} | ||
* constructor for more info. | ||
* @deprecated since v4.0.0 | ||
*/ | ||
const cacheFirst = deprecate('cacheFirst'); | ||
/** | ||
* @function workbox.strategies.cacheOnly | ||
* @param {Object} options See the {@link workbox.strategies.CacheOnly} | ||
* constructor for more info. | ||
* @deprecated since v4.0.0 | ||
*/ | ||
const cacheOnly = deprecate('cacheOnly'); | ||
/** | ||
* @function workbox.strategies.networkFirst | ||
* @param {Object} options See the {@link workbox.strategies.NetworkFirst} | ||
* constructor for more info. | ||
* @deprecated since v4.0.0 | ||
*/ | ||
const cacheOnly = deprecate('cacheOnly'); | ||
/** | ||
* @function workbox.strategies.networkFirst | ||
* @param {Object} options See the {@link workbox.strategies.NetworkFirst} | ||
* constructor for more info. | ||
* @deprecated since v4.0.0 | ||
*/ | ||
const networkFirst = deprecate('networkFirst'); | ||
/** | ||
* @function workbox.strategies.networkOnly | ||
* @param {Object} options See the {@link workbox.strategies.NetworkOnly} | ||
* constructor for more info. | ||
* @deprecated since v4.0.0 | ||
*/ | ||
const networkFirst = deprecate('networkFirst'); | ||
/** | ||
* @function workbox.strategies.networkOnly | ||
* @param {Object} options See the {@link workbox.strategies.NetworkOnly} | ||
* constructor for more info. | ||
* @deprecated since v4.0.0 | ||
*/ | ||
const networkOnly = deprecate('networkOnly'); | ||
/** | ||
* @function workbox.strategies.staleWhileRevalidate | ||
* @param {Object} options See the | ||
* {@link workbox.strategies.StaleWhileRevalidate} constructor for more info. | ||
* @deprecated since v4.0.0 | ||
*/ | ||
const networkOnly = deprecate('networkOnly'); | ||
/** | ||
* @function workbox.strategies.staleWhileRevalidate | ||
* @param {Object} options See the | ||
* {@link workbox.strategies.StaleWhileRevalidate} constructor for more info. | ||
* @deprecated since v4.0.0 | ||
*/ | ||
const staleWhileRevalidate = deprecate('staleWhileRevalidate'); | ||
const staleWhileRevalidate = deprecate('staleWhileRevalidate'); | ||
exports.CacheFirst = CacheFirst; | ||
exports.CacheOnly = CacheOnly; | ||
exports.NetworkFirst = NetworkFirst; | ||
exports.NetworkOnly = NetworkOnly; | ||
exports.StaleWhileRevalidate = StaleWhileRevalidate; | ||
exports.cacheFirst = cacheFirst; | ||
exports.cacheOnly = cacheOnly; | ||
exports.networkFirst = networkFirst; | ||
exports.networkOnly = networkOnly; | ||
exports.staleWhileRevalidate = staleWhileRevalidate; | ||
exports.CacheFirst = CacheFirst; | ||
exports.CacheOnly = CacheOnly; | ||
exports.NetworkFirst = NetworkFirst; | ||
exports.NetworkOnly = NetworkOnly; | ||
exports.StaleWhileRevalidate = StaleWhileRevalidate; | ||
exports.cacheFirst = cacheFirst; | ||
exports.cacheOnly = cacheOnly; | ||
exports.networkFirst = networkFirst; | ||
exports.networkOnly = networkOnly; | ||
exports.staleWhileRevalidate = staleWhileRevalidate; | ||
return exports; | ||
return exports; | ||
}({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private)); | ||
@@ -1,2 +0,2 @@ | ||
this.workbox=this.workbox||{},this.workbox.strategies=function(e,t,s,n,r){"use strict";try{self["workbox:strategies:4.3.1"]&&_()}catch(e){}class i{constructor(e={}){this.t=t.cacheNames.getRuntimeName(e.cacheName),this.s=e.plugins||[],this.i=e.fetchOptions||null,this.h=e.matchOptions||null}async handle({event:e,request:t}){return this.makeRequest({event:e,request:t||e.request})}async makeRequest({event:e,request:t}){"string"==typeof t&&(t=new Request(t));let n,i=await s.cacheWrapper.match({cacheName:this.t,request:t,event:e,matchOptions:this.h,plugins:this.s});if(!i)try{i=await this.u(t,e)}catch(e){n=e}if(!i)throw new r.WorkboxError("no-response",{url:t.url,error:n});return i}async u(e,t){const r=await n.fetchWrapper.fetch({request:e,event:t,fetchOptions:this.i,plugins:this.s}),i=r.clone(),h=s.cacheWrapper.put({cacheName:this.t,request:e,response:i,event:t,plugins:this.s});if(t)try{t.waitUntil(h)}catch(e){}return r}}class h{constructor(e={}){this.t=t.cacheNames.getRuntimeName(e.cacheName),this.s=e.plugins||[],this.h=e.matchOptions||null}async handle({event:e,request:t}){return this.makeRequest({event:e,request:t||e.request})}async makeRequest({event:e,request:t}){"string"==typeof t&&(t=new Request(t));const n=await s.cacheWrapper.match({cacheName:this.t,request:t,event:e,matchOptions:this.h,plugins:this.s});if(!n)throw new r.WorkboxError("no-response",{url:t.url});return n}}const u={cacheWillUpdate:({response:e})=>200===e.status||0===e.status?e:null};class a{constructor(e={}){if(this.t=t.cacheNames.getRuntimeName(e.cacheName),e.plugins){let t=e.plugins.some(e=>!!e.cacheWillUpdate);this.s=t?e.plugins:[u,...e.plugins]}else this.s=[u];this.o=e.networkTimeoutSeconds,this.i=e.fetchOptions||null,this.h=e.matchOptions||null}async handle({event:e,request:t}){return this.makeRequest({event:e,request:t||e.request})}async makeRequest({event:e,request:t}){const s=[];"string"==typeof t&&(t=new Request(t));const n=[];let i;if(this.o){const{id:r,promise:h}=this.l({request:t,event:e,logs:s});i=r,n.push(h)}const h=this.q({timeoutId:i,request:t,event:e,logs:s});n.push(h);let u=await Promise.race(n);if(u||(u=await h),!u)throw new r.WorkboxError("no-response",{url:t.url});return u}l({request:e,logs:t,event:s}){let n;return{promise:new Promise(t=>{n=setTimeout(async()=>{t(await this.p({request:e,event:s}))},1e3*this.o)}),id:n}}async q({timeoutId:e,request:t,logs:r,event:i}){let h,u;try{u=await n.fetchWrapper.fetch({request:t,event:i,fetchOptions:this.i,plugins:this.s})}catch(e){h=e}if(e&&clearTimeout(e),h||!u)u=await this.p({request:t,event:i});else{const e=u.clone(),n=s.cacheWrapper.put({cacheName:this.t,request:t,response:e,event:i,plugins:this.s});if(i)try{i.waitUntil(n)}catch(e){}}return u}p({event:e,request:t}){return s.cacheWrapper.match({cacheName:this.t,request:t,event:e,matchOptions:this.h,plugins:this.s})}}class c{constructor(e={}){this.t=t.cacheNames.getRuntimeName(e.cacheName),this.s=e.plugins||[],this.i=e.fetchOptions||null}async handle({event:e,request:t}){return this.makeRequest({event:e,request:t||e.request})}async makeRequest({event:e,request:t}){let s,i;"string"==typeof t&&(t=new Request(t));try{i=await n.fetchWrapper.fetch({request:t,event:e,fetchOptions:this.i,plugins:this.s})}catch(e){s=e}if(!i)throw new r.WorkboxError("no-response",{url:t.url,error:s});return i}}class o{constructor(e={}){if(this.t=t.cacheNames.getRuntimeName(e.cacheName),this.s=e.plugins||[],e.plugins){let t=e.plugins.some(e=>!!e.cacheWillUpdate);this.s=t?e.plugins:[u,...e.plugins]}else this.s=[u];this.i=e.fetchOptions||null,this.h=e.matchOptions||null}async handle({event:e,request:t}){return this.makeRequest({event:e,request:t||e.request})}async makeRequest({event:e,request:t}){"string"==typeof t&&(t=new Request(t));const n=this.u({request:t,event:e});let i,h=await s.cacheWrapper.match({cacheName:this.t,request:t,event:e,matchOptions:this.h,plugins:this.s});if(h){if(e)try{e.waitUntil(n)}catch(i){}}else try{h=await n}catch(e){i=e}if(!h)throw new r.WorkboxError("no-response",{url:t.url,error:i});return h}async u({request:e,event:t}){const r=await n.fetchWrapper.fetch({request:e,event:t,fetchOptions:this.i,plugins:this.s}),i=s.cacheWrapper.put({cacheName:this.t,request:e,response:r.clone(),event:t,plugins:this.s});if(t)try{t.waitUntil(i)}catch(e){}return r}}const l={cacheFirst:i,cacheOnly:h,networkFirst:a,networkOnly:c,staleWhileRevalidate:o},q=e=>{const t=l[e];return e=>new t(e)},w=q("cacheFirst"),p=q("cacheOnly"),v=q("networkFirst"),y=q("networkOnly"),m=q("staleWhileRevalidate");return e.CacheFirst=i,e.CacheOnly=h,e.NetworkFirst=a,e.NetworkOnly=c,e.StaleWhileRevalidate=o,e.cacheFirst=w,e.cacheOnly=p,e.networkFirst=v,e.networkOnly=y,e.staleWhileRevalidate=m,e}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private); | ||
this.workbox=this.workbox||{},this.workbox.strategies=function(t,e,s,i,r){"use strict";try{self["workbox:strategies:5.0.0-alpha.2"]&&_()}catch(t){}class n{constructor(t={}){this.t=e.cacheNames.getRuntimeName(t.cacheName),this.s=t.plugins||[],this.i=t.fetchOptions,this.h=t.matchOptions}async handle({event:t,request:e}){let i,n=await s.cacheWrapper.match({cacheName:this.t,request:e,event:t,matchOptions:this.h,plugins:this.s});if(!n)try{n=await this.o(e,t)}catch(t){i=t}if(!n)throw new r.WorkboxError("no-response",{url:e.url,error:i});return n}async o(t,e){const r=await i.fetchWrapper.fetch({request:t,event:e,fetchOptions:this.i,plugins:this.s}),n=r.clone(),h=s.cacheWrapper.put({cacheName:this.t,request:t,response:n,event:e,plugins:this.s});if(e)try{e.waitUntil(h)}catch(t){}return r}}class h{constructor(t={}){this.t=e.cacheNames.getRuntimeName(t.cacheName),this.s=t.plugins||[],this.h=t.matchOptions}async handle({event:t,request:e}){const i=await s.cacheWrapper.match({cacheName:this.t,request:e,event:t,matchOptions:this.h,plugins:this.s});if(!i)throw new r.WorkboxError("no-response",{url:e.url});return i}}const c={cacheWillUpdate:async({response:t})=>200===t.status||0===t.status?t:null};class a{constructor(t={}){if(this.t=e.cacheNames.getRuntimeName(t.cacheName),t.plugins){let e=t.plugins.some(t=>!!t.cacheWillUpdate);this.s=e?t.plugins:[c,...t.plugins]}else this.s=[c];this.u=t.networkTimeoutSeconds||0,this.i=t.fetchOptions,this.h=t.matchOptions}async handle({event:t,request:e}){const s=[],i=[];let n;if(this.u){const{id:r,promise:h}=this.l({request:e,event:t,logs:s});n=r,i.push(h)}const h=this.p({timeoutId:n,request:e,event:t,logs:s});i.push(h);let c=await Promise.race(i);if(c||(c=await h),!c)throw new r.WorkboxError("no-response",{url:e.url});return c}l({request:t,logs:e,event:s}){let i;return{promise:new Promise(e=>{i=setTimeout(async()=>{e(await this.v({request:t,event:s}))},1e3*this.u)}),id:i}}async p({timeoutId:t,request:e,logs:r,event:n}){let h,c;try{c=await i.fetchWrapper.fetch({request:e,event:n,fetchOptions:this.i,plugins:this.s})}catch(t){h=t}if(t&&clearTimeout(t),h||!c)c=await this.v({request:e,event:n});else{const t=c.clone(),i=s.cacheWrapper.put({cacheName:this.t,request:e,response:t,event:n,plugins:this.s});if(n)try{n.waitUntil(i)}catch(t){}}return c}v({event:t,request:e}){return s.cacheWrapper.match({cacheName:this.t,request:e,event:t,matchOptions:this.h,plugins:this.s})}}class o{constructor(t={}){this.s=t.plugins||[],this.i=t.fetchOptions}async handle({event:t,request:e}){let s,n;try{n=await i.fetchWrapper.fetch({request:e,event:t,fetchOptions:this.i,plugins:this.s})}catch(t){s=t}if(!n)throw new r.WorkboxError("no-response",{url:e.url,error:s});return n}}class u{constructor(t={}){if(this.t=e.cacheNames.getRuntimeName(t.cacheName),this.s=t.plugins||[],t.plugins){let e=t.plugins.some(t=>!!t.cacheWillUpdate);this.s=e?t.plugins:[c,...t.plugins]}else this.s=[c];this.i=t.fetchOptions,this.h=t.matchOptions}async handle({event:t,request:e}){const i=this.o({request:e,event:t});let n,h=await s.cacheWrapper.match({cacheName:this.t,request:e,event:t,matchOptions:this.h,plugins:this.s});if(h){if(t)try{t.waitUntil(i)}catch(n){}}else try{h=await i}catch(t){n=t}if(!h)throw new r.WorkboxError("no-response",{url:e.url,error:n});return h}async o({request:t,event:e}){const r=await i.fetchWrapper.fetch({request:t,event:e,fetchOptions:this.i,plugins:this.s}),n=s.cacheWrapper.put({cacheName:this.t,request:t,response:r.clone(),event:e,plugins:this.s});if(e)try{e.waitUntil(n)}catch(t){}return r}}const l={cacheFirst:n,cacheOnly:h,networkFirst:a,networkOnly:o,staleWhileRevalidate:u},w=t=>{const e=l[t];return t=>new e(t)},p=w("cacheFirst"),v=w("cacheOnly"),m=w("networkFirst"),q=w("networkOnly"),y=w("staleWhileRevalidate");return t.CacheFirst=n,t.CacheOnly=h,t.NetworkFirst=a,t.NetworkOnly=o,t.StaleWhileRevalidate=u,t.cacheFirst=p,t.cacheOnly=v,t.networkFirst=m,t.networkOnly=q,t.staleWhileRevalidate=y,t}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private); | ||
this.workbox = this.workbox || {}; | ||
this.workbox.streams = (function (exports, logger_mjs, assert_mjs) { | ||
'use strict'; | ||
this.workbox.streams = (function (exports, logger_js, assert_js, Deferred_js) { | ||
'use strict'; | ||
try { | ||
self['workbox:streams:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
// @ts-ignore | ||
try { | ||
self['workbox:streams:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Takes either a Response, a ReadableStream, or a | ||
* [BodyInit](https://fetch.spec.whatwg.org/#bodyinit) and returns the | ||
* ReadableStreamReader object associated with it. | ||
* | ||
* @param {workbox.streams.StreamSource} source | ||
* @return {ReadableStreamReader} | ||
* @private | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Takes either a Response, a ReadableStream, or a | ||
* [BodyInit](https://fetch.spec.whatwg.org/#bodyinit) and returns the | ||
* ReadableStreamReader object associated with it. | ||
* | ||
* @param {workbox.streams.StreamSource} source | ||
* @return {ReadableStreamReader} | ||
* @private | ||
*/ | ||
function _getReaderFromSource(source) { | ||
if (source.body && source.body.getReader) { | ||
return source.body.getReader(); | ||
} | ||
function _getReaderFromSource(source) { | ||
if (source instanceof Response) { | ||
return source.body.getReader(); | ||
} | ||
if (source.getReader) { | ||
return source.getReader(); | ||
} // TODO: This should be possible to do by constructing a ReadableStream, but | ||
// I can't get it to work. As a hack, construct a new Response, and use the | ||
// reader associated with its body. | ||
if (source instanceof ReadableStream) { | ||
return source.getReader(); | ||
} | ||
return new Response(source).body.getReader(); | ||
} | ||
/** | ||
* Takes multiple source Promises, each of which could resolve to a Response, a | ||
* ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit). | ||
* | ||
* Returns an object exposing a ReadableStream with each individual stream's | ||
* data returned in sequence, along with a Promise which signals when the | ||
* stream is finished (useful for passing to a FetchEvent's waitUntil()). | ||
* | ||
* @param {Array<Promise<workbox.streams.StreamSource>>} sourcePromises | ||
* @return {Object<{done: Promise, stream: ReadableStream}>} | ||
* | ||
* @memberof workbox.streams | ||
*/ | ||
return new Response(source).body.getReader(); | ||
} | ||
/** | ||
* Takes multiple source Promises, each of which could resolve to a Response, a | ||
* ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit). | ||
* | ||
* Returns an object exposing a ReadableStream with each individual stream's | ||
* data returned in sequence, along with a Promise which signals when the | ||
* stream is finished (useful for passing to a FetchEvent's waitUntil()). | ||
* | ||
* @param {Array<Promise<workbox.streams.StreamSource>>} sourcePromises | ||
* @return {Object<{done: Promise, stream: ReadableStream}>} | ||
* | ||
* @memberof workbox.streams | ||
*/ | ||
function concatenate(sourcePromises) { | ||
{ | ||
assert_js.assert.isArray(sourcePromises, { | ||
moduleName: 'workbox-streams', | ||
funcName: 'concatenate', | ||
paramName: 'sourcePromises' | ||
}); | ||
} | ||
function concatenate(sourcePromises) { | ||
{ | ||
assert_mjs.assert.isArray(sourcePromises, { | ||
moduleName: 'workbox-streams', | ||
funcName: 'concatenate', | ||
paramName: 'sourcePromises' | ||
const readerPromises = sourcePromises.map(sourcePromise => { | ||
return Promise.resolve(sourcePromise).then(source => { | ||
return _getReaderFromSource(source); | ||
}); | ||
}); | ||
} | ||
const streamDeferred = new Deferred_js.Deferred(); | ||
let i = 0; | ||
const logMessages = []; | ||
const stream = new ReadableStream({ | ||
pull(controller) { | ||
return readerPromises[i].then(reader => reader.read()).then(result => { | ||
if (result.done) { | ||
{ | ||
logMessages.push(['Reached the end of source:', sourcePromises[i]]); | ||
} | ||
const readerPromises = sourcePromises.map(sourcePromise => { | ||
return Promise.resolve(sourcePromise).then(source => { | ||
return _getReaderFromSource(source); | ||
}); | ||
}); | ||
let fullyStreamedResolve; | ||
let fullyStreamedReject; | ||
const done = new Promise((resolve, reject) => { | ||
fullyStreamedResolve = resolve; | ||
fullyStreamedReject = reject; | ||
}); | ||
let i = 0; | ||
const logMessages = []; | ||
const stream = new ReadableStream({ | ||
pull(controller) { | ||
return readerPromises[i].then(reader => reader.read()).then(result => { | ||
if (result.done) { | ||
{ | ||
logMessages.push(['Reached the end of source:', sourcePromises[i]]); | ||
} | ||
i++; | ||
i++; | ||
if (i >= readerPromises.length) { | ||
// Log all the messages in the group at once in a single group. | ||
{ | ||
logger_js.logger.groupCollapsed(`Concatenating ${readerPromises.length} sources.`); | ||
if (i >= readerPromises.length) { | ||
// Log all the messages in the group at once in a single group. | ||
{ | ||
logger_mjs.logger.groupCollapsed(`Concatenating ${readerPromises.length} sources.`); | ||
for (const message of logMessages) { | ||
if (Array.isArray(message)) { | ||
logger_js.logger.log(...message); | ||
} else { | ||
logger_js.logger.log(message); | ||
} | ||
} | ||
for (const message of logMessages) { | ||
if (Array.isArray(message)) { | ||
logger_mjs.logger.log(...message); | ||
} else { | ||
logger_mjs.logger.log(message); | ||
} | ||
logger_js.logger.log('Finished reading all sources.'); | ||
logger_js.logger.groupEnd(); | ||
} | ||
logger_mjs.logger.log('Finished reading all sources.'); | ||
logger_mjs.logger.groupEnd(); | ||
} | ||
controller.close(); | ||
streamDeferred.resolve(); | ||
return; | ||
} // The `pull` method is defined because we're inside it. | ||
controller.close(); | ||
fullyStreamedResolve(); | ||
return; | ||
return this.pull(controller); | ||
} else { | ||
controller.enqueue(result.value); | ||
} | ||
}).catch(error => { | ||
{ | ||
logger_js.logger.error('An error occurred:', error); | ||
} | ||
return this.pull(controller); | ||
} else { | ||
controller.enqueue(result.value); | ||
} | ||
}).catch(error => { | ||
streamDeferred.reject(error); | ||
throw error; | ||
}); | ||
}, | ||
cancel() { | ||
{ | ||
logger_mjs.logger.error('An error occurred:', error); | ||
logger_js.logger.warn('The ReadableStream was cancelled.'); | ||
} | ||
fullyStreamedReject(error); | ||
throw error; | ||
}); | ||
}, | ||
cancel() { | ||
{ | ||
logger_mjs.logger.warn('The ReadableStream was cancelled.'); | ||
streamDeferred.resolve(); | ||
} | ||
fullyStreamedResolve(); | ||
} | ||
}); | ||
return { | ||
done: streamDeferred.promise, | ||
stream | ||
}; | ||
} | ||
}); | ||
return { | ||
done, | ||
stream | ||
}; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* This is a utility method that determines whether the current browser supports | ||
* the features required to create streamed responses. Currently, it checks if | ||
* [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream) | ||
* is available. | ||
* | ||
* @param {HeadersInit} [headersInit] If there's no `Content-Type` specified, | ||
* `'text/html'` will be used by default. | ||
* @return {boolean} `true`, if the current browser meets the requirements for | ||
* streaming responses, and `false` otherwise. | ||
* | ||
* @memberof workbox.streams | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* This is a utility method that determines whether the current browser supports | ||
* the features required to create streamed responses. Currently, it checks if | ||
* [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream) | ||
* is available. | ||
* | ||
* @param {HeadersInit} [headersInit] If there's no `Content-Type` specified, | ||
* `'text/html'` will be used by default. | ||
* @return {boolean} `true`, if the current browser meets the requirements for | ||
* streaming responses, and `false` otherwise. | ||
* | ||
* @memberof workbox.streams | ||
*/ | ||
function createHeaders(headersInit = {}) { | ||
// See https://github.com/GoogleChrome/workbox/issues/1461 | ||
const headers = new Headers(headersInit); | ||
function createHeaders(headersInit = {}) { | ||
// See https://github.com/GoogleChrome/workbox/issues/1461 | ||
const headers = new Headers(headersInit); | ||
if (!headers.has('content-type')) { | ||
headers.set('content-type', 'text/html'); | ||
} | ||
if (!headers.has('content-type')) { | ||
headers.set('content-type', 'text/html'); | ||
return headers; | ||
} | ||
return headers; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Takes multiple source Promises, each of which could resolve to a Response, a | ||
* ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit), | ||
* along with a | ||
* [HeadersInit](https://fetch.spec.whatwg.org/#typedefdef-headersinit). | ||
* | ||
* Returns an object exposing a Response whose body consists of each individual | ||
* stream's data returned in sequence, along with a Promise which signals when | ||
* the stream is finished (useful for passing to a FetchEvent's waitUntil()). | ||
* | ||
* @param {Array<Promise<workbox.streams.StreamSource>>} sourcePromises | ||
* @param {HeadersInit} [headersInit] If there's no `Content-Type` specified, | ||
* `'text/html'` will be used by default. | ||
* @return {Object<{done: Promise, response: Response}>} | ||
* | ||
* @memberof workbox.streams | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Takes multiple source Promises, each of which could resolve to a Response, a | ||
* ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit), | ||
* along with a | ||
* [HeadersInit](https://fetch.spec.whatwg.org/#typedefdef-headersinit). | ||
* | ||
* Returns an object exposing a Response whose body consists of each individual | ||
* stream's data returned in sequence, along with a Promise which signals when | ||
* the stream is finished (useful for passing to a FetchEvent's waitUntil()). | ||
* | ||
* @param {Array<Promise<workbox.streams.StreamSource>>} sourcePromises | ||
* @param {HeadersInit} [headersInit] If there's no `Content-Type` specified, | ||
* `'text/html'` will be used by default. | ||
* @return {Object<{done: Promise, response: Response}>} | ||
* | ||
* @memberof workbox.streams | ||
*/ | ||
function concatenateToResponse(sourcePromises, headersInit) { | ||
const { | ||
done, | ||
stream | ||
} = concatenate(sourcePromises); | ||
const headers = createHeaders(headersInit); | ||
const response = new Response(stream, { | ||
headers | ||
}); | ||
return { | ||
done, | ||
response | ||
}; | ||
} | ||
function concatenateToResponse(sourcePromises, headersInit) { | ||
const { | ||
done, | ||
stream | ||
} = concatenate(sourcePromises); | ||
const headers = createHeaders(headersInit); | ||
const response = new Response(stream, { | ||
headers | ||
}); | ||
return { | ||
done, | ||
response | ||
}; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
let cachedIsSupported; | ||
/** | ||
* This is a utility method that determines whether the current browser supports | ||
* the features required to create streamed responses. Currently, it checks if | ||
* [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream) | ||
* can be created. | ||
* | ||
* @return {boolean} `true`, if the current browser meets the requirements for | ||
* streaming responses, and `false` otherwise. | ||
* | ||
* @memberof workbox.streams | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
let cachedIsSupported = undefined; | ||
/** | ||
* This is a utility method that determines whether the current browser supports | ||
* the features required to create streamed responses. Currently, it checks if | ||
* [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream) | ||
* can be created. | ||
* | ||
* @return {boolean} `true`, if the current browser meets the requirements for | ||
* streaming responses, and `false` otherwise. | ||
* | ||
* @memberof workbox.streams | ||
*/ | ||
function isSupported() { | ||
if (cachedIsSupported === undefined) { | ||
// See https://github.com/GoogleChrome/workbox/issues/1473 | ||
try { | ||
new ReadableStream({ | ||
start() {} | ||
function isSupported() { | ||
if (cachedIsSupported === undefined) { | ||
// See https://github.com/GoogleChrome/workbox/issues/1473 | ||
try { | ||
new ReadableStream({ | ||
start() {} | ||
}); | ||
cachedIsSupported = true; | ||
} catch (error) { | ||
cachedIsSupported = false; | ||
} | ||
} | ||
}); | ||
cachedIsSupported = true; | ||
} catch (error) { | ||
cachedIsSupported = false; | ||
} | ||
return cachedIsSupported; | ||
} | ||
return cachedIsSupported; | ||
} | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* A shortcut to create a strategy that could be dropped-in to Workbox's router. | ||
* | ||
* On browsers that do not support constructing new `ReadableStream`s, this | ||
* strategy will automatically wait for all the `sourceFunctions` to complete, | ||
* and create a final response that concatenates their values together. | ||
* | ||
* @param {Array<function({event, request, url, params})>} sourceFunctions | ||
* An array of functions similar to {@link workbox.routing.Route~handlerCallback} | ||
* but that instead return a {@link workbox.streams.StreamSource} (or a | ||
* Promise which resolves to one). | ||
* @param {HeadersInit} [headersInit] If there's no `Content-Type` specified, | ||
* `'text/html'` will be used by default. | ||
* @return {workbox.routing.Route~handlerCallback} | ||
* | ||
* @memberof workbox.streams | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* A shortcut to create a strategy that could be dropped-in to Workbox's router. | ||
* | ||
* On browsers that do not support constructing new `ReadableStream`s, this | ||
* strategy will automatically wait for all the `sourceFunctions` to complete, | ||
* and create a final response that concatenates their values together. | ||
* | ||
* @param { | ||
* Array<function(workbox.routing.Route~handlerCallback)>} sourceFunctions | ||
* Each function should return a {@link workbox.streams.StreamSource} (or a | ||
* Promise which resolves to one). | ||
* @param {HeadersInit} [headersInit] If there's no `Content-Type` specified, | ||
* `'text/html'` will be used by default. | ||
* @return {workbox.routing.Route~handlerCallback} | ||
* | ||
* @memberof workbox.streams | ||
*/ | ||
function strategy(sourceFunctions, headersInit) { | ||
return async ({ | ||
event, | ||
url, | ||
params | ||
}) => { | ||
if (isSupported()) { | ||
const { | ||
done, | ||
response | ||
} = concatenateToResponse(sourceFunctions.map(fn => fn({ | ||
event, | ||
url, | ||
params | ||
})), headersInit); | ||
event.waitUntil(done); | ||
return response; | ||
} | ||
{ | ||
logger_mjs.logger.log(`The current browser doesn't support creating response ` + `streams. Falling back to non-streaming response instead.`); | ||
} // Fallback to waiting for everything to finish, and concatenating the | ||
// responses. | ||
const parts = await Promise.all(sourceFunctions.map(sourceFunction => sourceFunction({ | ||
function strategy(sourceFunctions, headersInit) { | ||
return async ({ | ||
event, | ||
request, | ||
url, | ||
params | ||
})).map(async responsePromise => { | ||
const response = await responsePromise; | ||
}) => { | ||
const sourcePromises = sourceFunctions.map(fn => { | ||
// Ensure the return value of the function is always a promise. | ||
return Promise.resolve(fn({ | ||
event, | ||
request, | ||
url, | ||
params | ||
})); | ||
}); | ||
if (response instanceof Response) { | ||
return response.blob(); | ||
} // Otherwise, assume it's something like a string which can be used | ||
// as-is when constructing the final composite blob. | ||
if (isSupported()) { | ||
const { | ||
done, | ||
response | ||
} = concatenateToResponse(sourcePromises, headersInit); | ||
if (event) { | ||
event.waitUntil(done); | ||
} | ||
return response; | ||
})); | ||
const headers = createHeaders(headersInit); // Constructing a new Response from a Blob source is well-supported. | ||
// So is constructing a new Blob from multiple source Blobs or strings. | ||
return response; | ||
} | ||
return new Response(new Blob(parts), { | ||
headers | ||
}); | ||
}; | ||
} | ||
{ | ||
logger_js.logger.log(`The current browser doesn't support creating response ` + `streams. Falling back to non-streaming response instead.`); | ||
} // Fallback to waiting for everything to finish, and concatenating the | ||
// responses. | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
const blobPartsPromises = sourcePromises.map(async sourcePromise => { | ||
const source = await sourcePromise; | ||
exports.concatenate = concatenate; | ||
exports.concatenateToResponse = concatenateToResponse; | ||
exports.isSupported = isSupported; | ||
exports.strategy = strategy; | ||
if (source instanceof Response) { | ||
return source.blob(); | ||
} else { | ||
// Technically, a `StreamSource` object can include any valid | ||
// `BodyInit` type, including `FormData` and `URLSearchParams`, which | ||
// cannot be passed to the Blob constructor directly, so we have to | ||
// convert them to actual Blobs first. | ||
return new Response(source).blob(); | ||
} | ||
}); | ||
const blobParts = await Promise.all(blobPartsPromises); | ||
const headers = createHeaders(headersInit); // Constructing a new Response from a Blob source is well-supported. | ||
// So is constructing a new Blob from multiple source Blobs or strings. | ||
return exports; | ||
return new Response(new Blob(blobParts), { | ||
headers | ||
}); | ||
}; | ||
} | ||
}({}, workbox.core._private, workbox.core._private)); | ||
exports.concatenate = concatenate; | ||
exports.concatenateToResponse = concatenateToResponse; | ||
exports.isSupported = isSupported; | ||
exports.strategy = strategy; | ||
return exports; | ||
}({}, workbox.core._private, workbox.core._private, workbox.core._private)); | ||
@@ -1,2 +0,2 @@ | ||
this.workbox=this.workbox||{},this.workbox.streams=function(e){"use strict";try{self["workbox:streams:4.3.1"]&&_()}catch(e){}function n(e){const n=e.map(e=>Promise.resolve(e).then(e=>(function(e){return e.body&&e.body.getReader?e.body.getReader():e.getReader?e.getReader():new Response(e).body.getReader()})(e)));let t,r;const s=new Promise((e,n)=>{t=e,r=n});let o=0;return{done:s,stream:new ReadableStream({pull(e){return n[o].then(e=>e.read()).then(r=>{if(r.done)return++o>=n.length?(e.close(),void t()):this.pull(e);e.enqueue(r.value)}).catch(e=>{throw r(e),e})},cancel(){t()}})}}function t(e={}){const n=new Headers(e);return n.has("content-type")||n.set("content-type","text/html"),n}function r(e,r){const{done:s,stream:o}=n(e),a=t(r);return{done:s,response:new Response(o,{headers:a})}}let s=void 0;function o(){if(void 0===s)try{new ReadableStream({start(){}}),s=!0}catch(e){s=!1}return s}return e.concatenate=n,e.concatenateToResponse=r,e.isSupported=o,e.strategy=function(e,n){return async({event:s,url:a,params:c})=>{if(o()){const{done:t,response:o}=r(e.map(e=>e({event:s,url:a,params:c})),n);return s.waitUntil(t),o}const i=await Promise.all(e.map(e=>e({event:s,url:a,params:c})).map(async e=>{const n=await e;return n instanceof Response?n.blob():n})),u=t(n);return new Response(new Blob(i),{headers:u})}},e}({}); | ||
this.workbox=this.workbox||{},this.workbox.streams=function(e,n){"use strict";try{self["workbox:streams:5.0.0-alpha.2"]&&_()}catch(e){}function t(e){const t=e.map(e=>Promise.resolve(e).then(e=>(function(e){return e instanceof Response?e.body.getReader():e instanceof ReadableStream?e.getReader():new Response(e).body.getReader()})(e))),r=new n.Deferred;let s=0;const o=new ReadableStream({pull(e){return t[s].then(e=>e.read()).then(n=>{if(n.done)return++s>=t.length?(e.close(),void r.resolve()):this.pull(e);e.enqueue(n.value)}).catch(e=>{throw r.reject(e),e})},cancel(){r.resolve()}});return{done:r.promise,stream:o}}function r(e={}){const n=new Headers(e);return n.has("content-type")||n.set("content-type","text/html"),n}function s(e,n){const{done:s,stream:o}=t(e),a=r(n);return{done:s,response:new Response(o,{headers:a})}}let o;function a(){if(void 0===o)try{new ReadableStream({start(){}}),o=!0}catch(e){o=!1}return o}return e.concatenate=t,e.concatenateToResponse=s,e.isSupported=a,e.strategy=function(e,n){return async({event:t,request:o,url:c,params:i})=>{const u=e.map(e=>Promise.resolve(e({event:t,request:o,url:c,params:i})));if(a()){const{done:e,response:r}=s(u,n);return t&&t.waitUntil(e),r}const f=u.map(async e=>{const n=await e;return n instanceof Response?n.blob():new Response(n).blob()}),l=await Promise.all(f),p=r(n);return new Response(new Blob(l),{headers:p})}},e}({},workbox.core._private); | ||
@@ -1,2 +0,2 @@ | ||
!function(){"use strict";try{self["workbox:sw:4.3.1"]&&_()}catch(t){}const t="https://cdn.jsdelivr.net/npm/workbox-cdn@4.3.1/workbox",e={backgroundSync:"background-sync",broadcastUpdate:"broadcast-update",cacheableResponse:"cacheable-response",core:"core",expiration:"expiration",googleAnalytics:"offline-ga",navigationPreload:"navigation-preload",precaching:"precaching",rangeRequests:"range-requests",routing:"routing",strategies:"strategies",streams:"streams"};self.workbox=new class{constructor(){return this.v={},this.t={debug:"localhost"===self.location.hostname,modulePathPrefix:null,modulePathCb:null},this.s=this.t.debug?"dev":"prod",this.o=!1,new Proxy(this,{get(t,s){if(t[s])return t[s];const o=e[s];return o&&t.loadModule(`workbox-${o}`),t[s]}})}setConfig(t={}){if(this.o)throw new Error("Config must be set before accessing workbox.* modules");Object.assign(this.t,t),this.s=this.t.debug?"dev":"prod"}loadModule(t){const e=this.i(t);try{importScripts(e),this.o=!0}catch(s){throw console.error(`Unable to import module '${t}' from '${e}'.`),s}}i(e){if(this.t.modulePathCb)return this.t.modulePathCb(e,this.t.debug);let s=[t];const o=`${e}.${this.s}.js`,r=this.t.modulePathPrefix;return r&&""===(s=r.split("/"))[s.length-1]&&s.splice(s.length-1,1),s.push(o),s.join("/")}}}(); | ||
!function(){"use strict";try{self["workbox:sw:5.0.0-alpha.2"]&&_()}catch(t){}const t="https://cdn.jsdelivr.net/npm/workbox-cdn@5.0.0-alpha.2/workbox",e={backgroundSync:"background-sync",broadcastUpdate:"broadcast-update",cacheableResponse:"cacheable-response",core:"core",expiration:"expiration",googleAnalytics:"offline-ga",navigationPreload:"navigation-preload",precaching:"precaching",rangeRequests:"range-requests",routing:"routing",strategies:"strategies",streams:"streams"};self.workbox=new class{constructor(){return this.v={},this.t={debug:"localhost"===self.location.hostname,modulePathPrefix:null,modulePathCb:null},this.s=this.t.debug?"dev":"prod",this.o=!1,new Proxy(this,{get(t,s){if(t[s])return t[s];const o=e[s];return o&&t.loadModule(`workbox-${o}`),t[s]}})}setConfig(t={}){if(this.o)throw new Error("Config must be set before accessing workbox.* modules");Object.assign(this.t,t),this.s=this.t.debug?"dev":"prod"}loadModule(t){const e=this.i(t);try{importScripts(e),this.o=!0}catch(s){throw console.error(`Unable to import module '${t}' from '${e}'.`),s}}i(e){if(this.t.modulePathCb)return this.t.modulePathCb(e,this.t.debug);let s=[t];const o=`${e}.${this.s}.js`,r=this.t.modulePathPrefix;return r&&""===(s=r.split("/"))[s.length-1]&&s.splice(s.length-1,1),s.push(o),s.join("/")}}}(); | ||
(function (global, factory) { | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = global || self, factory(global.workbox = {})); | ||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : | ||
typeof define === 'function' && define.amd ? define(['exports'], factory) : | ||
(global = global || self, factory(global.workbox = {})); | ||
}(this, function (exports) { 'use strict'; | ||
try { | ||
self['workbox:window:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
// @ts-ignore | ||
try { | ||
self['workbox:window:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Sends a data object to a service worker via `postMessage` and resolves with | ||
* a response (if any). | ||
* | ||
* A response can be set in a message handler in the service worker by | ||
* calling `event.ports[0].postMessage(...)`, which will resolve the promise | ||
* returned by `messageSW()`. If no response is set, the promise will not | ||
* resolve. | ||
* | ||
* @param {ServiceWorker} sw The service worker to send the message to. | ||
* @param {Object} data An object to send to the service worker. | ||
* @return {Promise<Object|undefined>} | ||
* | ||
* @memberof module:workbox-window | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Sends a data object to a service worker via `postMessage` and resolves with | ||
* a response (if any). | ||
* | ||
* A response can be set in a message handler in the service worker by | ||
* calling `event.ports[0].postMessage(...)`, which will resolve the promise | ||
* returned by `messageSW()`. If no response is set, the promise will not | ||
* resolve. | ||
* | ||
* @param {ServiceWorker} sw The service worker to send the message to. | ||
* @param {Object} data An object to send to the service worker. | ||
* @return {Promise<Object|undefined>} | ||
* | ||
* @memberof module:workbox-window | ||
*/ | ||
var messageSW = function messageSW(sw, data) { | ||
return new Promise(function (resolve) { | ||
var messageChannel = new MessageChannel(); | ||
function messageSW(sw, data) { | ||
return new Promise(function (resolve) { | ||
var messageChannel = new MessageChannel(); | ||
messageChannel.port1.onmessage = function (evt) { | ||
return resolve(evt.data); | ||
}; | ||
messageChannel.port1.onmessage = function (event) { | ||
resolve(event.data); | ||
}; | ||
sw.postMessage(data, [messageChannel.port2]); | ||
}); | ||
}; | ||
sw.postMessage(data, [messageChannel.port2]); | ||
}); | ||
} | ||
function _defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
function _defineProperties(target, props) { | ||
for (var i = 0; i < props.length; i++) { | ||
var descriptor = props[i]; | ||
descriptor.enumerable = descriptor.enumerable || false; | ||
descriptor.configurable = true; | ||
if ("value" in descriptor) descriptor.writable = true; | ||
Object.defineProperty(target, descriptor.key, descriptor); | ||
} | ||
} | ||
} | ||
function _createClass(Constructor, protoProps, staticProps) { | ||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) _defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
} | ||
function _createClass(Constructor, protoProps, staticProps) { | ||
if (protoProps) _defineProperties(Constructor.prototype, protoProps); | ||
if (staticProps) _defineProperties(Constructor, staticProps); | ||
return Constructor; | ||
} | ||
function _inheritsLoose(subClass, superClass) { | ||
subClass.prototype = Object.create(superClass.prototype); | ||
subClass.prototype.constructor = subClass; | ||
subClass.__proto__ = superClass; | ||
} | ||
function _assertThisInitialized(self) { | ||
if (self === void 0) { | ||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); | ||
function _inheritsLoose(subClass, superClass) { | ||
subClass.prototype = Object.create(superClass.prototype); | ||
subClass.prototype.constructor = subClass; | ||
subClass.__proto__ = superClass; | ||
} | ||
return self; | ||
} | ||
// @ts-ignore | ||
try { | ||
self['workbox:core:5.0.0-alpha.2'] && _(); | ||
} catch (e) {} | ||
try { | ||
self['workbox:core:4.3.1'] && _(); | ||
} catch (e) {} // eslint-disable-line | ||
/* | ||
Copyright 2018 Google LLC | ||
/* | ||
Copyright 2018 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* The Deferred class composes Promises in a way that allows for them to be | ||
* resolved or rejected from outside the constructor. In most cases promises | ||
* should be used directly, but Deferreds can be necessary when the logic to | ||
* resolve a promise must be separate. | ||
* | ||
* @private | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* The Deferred class composes Promises in a way that allows for them to be | ||
* resolved or rejected from outside the constructor. In most cases promises | ||
* should be used directly, but Deferreds can be necessary when the logic to | ||
* resolve a promise must be separate. | ||
* | ||
* @private | ||
*/ | ||
var Deferred = | ||
/** | ||
* Creates a promise and exposes its resolve and reject functions as methods. | ||
*/ | ||
function Deferred() { | ||
var _this = this; | ||
var Deferred = | ||
/** | ||
* Creates a promise and exposes its resolve and reject functions as methods. | ||
*/ | ||
function Deferred() { | ||
var _this = this; | ||
this.promise = new Promise(function (resolve, reject) { | ||
_this.resolve = resolve; | ||
_this.reject = reject; | ||
}); | ||
}; | ||
this.promise = new Promise(function (resolve, reject) { | ||
_this.resolve = resolve; | ||
_this.reject = reject; | ||
}); | ||
}; | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
var logger = function () { | ||
var inGroup = false; | ||
var methodToColorMap = { | ||
debug: "#7f8c8d", | ||
log: "#2ecc71", | ||
warn: "#f39c12", | ||
error: "#c0392b", | ||
groupCollapsed: "#3498db", | ||
groupEnd: null | ||
}; | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
var logger = function () { | ||
var inGroup = false; | ||
var methodToColorMap = { | ||
debug: "#7f8c8d", | ||
// Gray | ||
log: "#2ecc71", | ||
// Green | ||
warn: "#f39c12", | ||
// Yellow | ||
error: "#c0392b", | ||
// Red | ||
groupCollapsed: "#3498db", | ||
// Blue | ||
groupEnd: null // No colored prefix on groupEnd | ||
var print = function print(method, args) { | ||
var _console2; | ||
}; | ||
if (method === 'groupCollapsed') { | ||
// Safari doesn't print all console.groupCollapsed() arguments: | ||
// https://bugs.webkit.org/show_bug.cgi?id=182754 | ||
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { | ||
var _console; | ||
var print = function print(method, args) { | ||
var _console2; | ||
(_console = console)[method].apply(_console, args); | ||
if (method === 'groupCollapsed') { | ||
// Safari doesn't print all console.groupCollapsed() arguments: | ||
// https://bugs.webkit.org/show_bug.cgi?id=182754 | ||
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) { | ||
var _console; | ||
(_console = console)[method].apply(_console, args); | ||
return; | ||
return; | ||
} | ||
} | ||
} | ||
var styles = ["background: " + methodToColorMap[method], "border-radius: 0.5em", "color: white", "font-weight: bold", "padding: 2px 0.5em"]; // When in a group, the workbox prefix is not displayed. | ||
var styles = ["background: " + methodToColorMap[method], "border-radius: 0.5em", "color: white", "font-weight: bold", "padding: 2px 0.5em"]; // When in a group, the workbox prefix is not displayed. | ||
var logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')]; | ||
var logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')]; | ||
(_console2 = console)[method].apply(_console2, logPrefix.concat(args)); | ||
(_console2 = console)[method].apply(_console2, logPrefix.concat(args)); | ||
if (method === 'groupCollapsed') { | ||
inGroup = true; | ||
} | ||
if (method === 'groupCollapsed') { | ||
inGroup = true; | ||
} | ||
if (method === 'groupEnd') { | ||
inGroup = false; | ||
} | ||
}; | ||
if (method === 'groupEnd') { | ||
inGroup = false; | ||
} | ||
}; | ||
var api = {}; | ||
var api = {}; | ||
var loggerMethods = Object.keys(methodToColorMap); | ||
var _arr = Object.keys(methodToColorMap); | ||
var _loop = function _loop() { | ||
var key = loggerMethods[_i]; | ||
var method = key; | ||
var _loop = function _loop() { | ||
var method = _arr[_i]; | ||
api[method] = function () { | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
api[method] = function () { | ||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { | ||
args[_key] = arguments[_key]; | ||
} | ||
print(method, args); | ||
print(method, args); | ||
}; | ||
}; | ||
}; | ||
for (var _i = 0; _i < _arr.length; _i++) { | ||
_loop(); | ||
} | ||
for (var _i = 0; _i < loggerMethods.length; _i++) { | ||
_loop(); | ||
} | ||
return api; | ||
}(); | ||
return api; | ||
}(); | ||
/* | ||
Copyright 2019 Google LLC | ||
/* | ||
Copyright 2019 Google LLC | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* A minimal `EventTarget` shim. | ||
* This is necessary because not all browsers support constructable | ||
* `EventTarget`, so using a real `EventTarget` will error. | ||
* @private | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
var EventTargetShim = | ||
/*#__PURE__*/ | ||
function () { | ||
/** | ||
* Creates an event listener registry | ||
* | ||
* A minimal `EventTarget` shim. | ||
* This is necessary because not all browsers support constructable | ||
* `EventTarget`, so using a real `EventTarget` will error. | ||
* @private | ||
*/ | ||
function EventTargetShim() { | ||
// A registry of event types to listeners. | ||
this._eventListenerRegistry = {}; | ||
} | ||
/** | ||
* @param {string} type | ||
* @param {Function} listener | ||
* @private | ||
*/ | ||
var WorkboxEventTarget = | ||
/*#__PURE__*/ | ||
function () { | ||
function WorkboxEventTarget() { | ||
this._eventListenerRegistry = new Map(); | ||
} | ||
/** | ||
* @param {string} type | ||
* @param {Function} listener | ||
* @private | ||
*/ | ||
var _proto = EventTargetShim.prototype; | ||
var _proto = WorkboxEventTarget.prototype; | ||
_proto.addEventListener = function addEventListener(type, listener) { | ||
this._getEventListenersByType(type).add(listener); | ||
}; | ||
/** | ||
* @param {string} type | ||
* @param {Function} listener | ||
* @private | ||
*/ | ||
_proto.addEventListener = function addEventListener(type, listener) { | ||
var foo = this._getEventListenersByType(type); | ||
foo.add(listener); | ||
}; | ||
/** | ||
* @param {string} type | ||
* @param {Function} listener | ||
* @private | ||
*/ | ||
_proto.removeEventListener = function removeEventListener(type, listener) { | ||
this._getEventListenersByType(type).delete(listener); | ||
}; | ||
/** | ||
* @param {Event} event | ||
* @private | ||
*/ | ||
_proto.removeEventListener = function removeEventListener(type, listener) { | ||
this._getEventListenersByType(type).delete(listener); | ||
}; | ||
/** | ||
* @param {Object} event | ||
* @private | ||
*/ | ||
_proto.dispatchEvent = function dispatchEvent(event) { | ||
event.target = this; | ||
this._getEventListenersByType(event.type).forEach(function (listener) { | ||
return listener(event); | ||
}); | ||
}; | ||
/** | ||
* Returns a Set of listeners associated with the passed event type. | ||
* If no handlers have been registered, an empty Set is returned. | ||
* | ||
* @param {string} type The event type. | ||
* @return {Set} An array of handler functions. | ||
* @private | ||
*/ | ||
_proto.dispatchEvent = function dispatchEvent(event) { | ||
event.target = this; | ||
var listeners = this._getEventListenersByType(event.type); | ||
_proto._getEventListenersByType = function _getEventListenersByType(type) { | ||
return this._eventListenerRegistry[type] = this._eventListenerRegistry[type] || new Set(); | ||
}; | ||
for (var _iterator = listeners, _isArray = Array.isArray(_iterator), _i = 0, _iterator = _isArray ? _iterator : _iterator[Symbol.iterator]();;) { | ||
var _ref; | ||
return EventTargetShim; | ||
}(); | ||
if (_isArray) { | ||
if (_i >= _iterator.length) break; | ||
_ref = _iterator[_i++]; | ||
} else { | ||
_i = _iterator.next(); | ||
if (_i.done) break; | ||
_ref = _i.value; | ||
} | ||
/* | ||
Copyright 2019 Google LLC | ||
var listener = _ref; | ||
listener(event); | ||
} | ||
}; | ||
/** | ||
* Returns a Set of listeners associated with the passed event type. | ||
* If no handlers have been registered, an empty Set is returned. | ||
* | ||
* @param {string} type The event type. | ||
* @return {Set<ListenerCallback>} An array of handler functions. | ||
* @private | ||
*/ | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Returns true if two URLs have the same `.href` property. The URLS can be | ||
* relative, and if they are the current location href is used to resolve URLs. | ||
* | ||
* @private | ||
* @param {string} url1 | ||
* @param {string} url2 | ||
* @return {boolean} | ||
*/ | ||
var urlsMatch = function urlsMatch(url1, url2) { | ||
return new URL(url1, location).href === new URL(url2, location).href; | ||
}; | ||
_proto._getEventListenersByType = function _getEventListenersByType(type) { | ||
if (!this._eventListenerRegistry.has(type)) { | ||
this._eventListenerRegistry.set(type, new Set()); | ||
} | ||
/* | ||
Copyright 2019 Google LLC | ||
return this._eventListenerRegistry.get(type); | ||
}; | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* A minimal `Event` subclass shim. | ||
* This doesn't *actually* subclass `Event` because not all browsers support | ||
* constructable `EventTarget`, and using a real `Event` will error. | ||
* @private | ||
*/ | ||
return WorkboxEventTarget; | ||
}(); | ||
var WorkboxEvent = | ||
/** | ||
* @param {string} type | ||
* @param {Object} props | ||
*/ | ||
function WorkboxEvent(type, props) { | ||
Object.assign(this, props, { | ||
type: type | ||
}); | ||
}; | ||
/* | ||
Copyright 2019 Google LLC | ||
function _catch(body, recover) { | ||
try { | ||
var result = body(); | ||
} catch (e) { | ||
return recover(e); | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* Returns true if two URLs have the same `.href` property. The URLS can be | ||
* relative, and if they are the current location href is used to resolve URLs. | ||
* | ||
* @private | ||
* @param {string} url1 | ||
* @param {string} url2 | ||
* @return {boolean} | ||
*/ | ||
if (result && result.then) { | ||
return result.then(void 0, recover); | ||
function urlsMatch(url1, url2) { | ||
var _location = location, | ||
href = _location.href; | ||
return new URL(url1, href).href === new URL(url2, href).href; | ||
} | ||
return result; | ||
} | ||
/* | ||
Copyright 2019 Google LLC | ||
function _async(f) { | ||
return function () { | ||
for (var args = [], i = 0; i < arguments.length; i++) { | ||
args[i] = arguments[i]; | ||
} | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
/** | ||
* A minimal `Event` subclass shim. | ||
* This doesn't *actually* subclass `Event` because not all browsers support | ||
* constructable `EventTarget`, and using a real `Event` will error. | ||
* @private | ||
*/ | ||
var WorkboxEvent = function WorkboxEvent(type, props) { | ||
this.type = type; | ||
Object.assign(this, props); | ||
}; | ||
function _catch(body, recover) { | ||
try { | ||
return Promise.resolve(f.apply(this, args)); | ||
var result = body(); | ||
} catch (e) { | ||
return Promise.reject(e); | ||
return recover(e); | ||
} | ||
}; | ||
} | ||
function _invoke(body, then) { | ||
var result = body(); | ||
if (result && result.then) { | ||
return result.then(void 0, recover); | ||
} | ||
if (result && result.then) { | ||
return result.then(then); | ||
return result; | ||
} | ||
return then(result); | ||
} | ||
function _invoke(body, then) { | ||
var result = body(); | ||
function _await(value, then, direct) { | ||
if (direct) { | ||
return then ? then(value) : value; | ||
} | ||
if (result && result.then) { | ||
return result.then(then); | ||
} | ||
if (!value || !value.then) { | ||
value = Promise.resolve(value); | ||
return then(result); | ||
} | ||
return then ? value.then(then) : value; | ||
} | ||
function _awaitIgnored(value, direct) { | ||
if (!direct) { | ||
return value && value.then ? value.then(_empty) : Promise.resolve(); | ||
function _awaitIgnored(value, direct) { | ||
if (!direct) { | ||
return value && value.then ? value.then(_empty) : Promise.resolve(); | ||
} | ||
} | ||
} | ||
function _empty() {} | ||
// `skipWaiting()` wasn't called. This 200 amount wasn't scientifically | ||
// chosen, but it seems to avoid false positives in my testing. | ||
function _empty() {} | ||
var WAITING_TIMEOUT_DURATION = 200; // The amount of time after a registration that we can reasonably conclude | ||
// that the registration didn't trigger an update. | ||
function _async(f) { | ||
return function () { | ||
for (var args = [], i = 0; i < arguments.length; i++) { | ||
args[i] = arguments[i]; | ||
} | ||
var REGISTRATION_TIMEOUT_DURATION = 60000; | ||
/** | ||
* A class to aid in handling service worker registration, updates, and | ||
* reacting to service worker lifecycle events. | ||
* | ||
* @fires [message]{@link module:workbox-window.Workbox#message} | ||
* @fires [installed]{@link module:workbox-window.Workbox#installed} | ||
* @fires [waiting]{@link module:workbox-window.Workbox#waiting} | ||
* @fires [controlling]{@link module:workbox-window.Workbox#controlling} | ||
* @fires [activated]{@link module:workbox-window.Workbox#activated} | ||
* @fires [redundant]{@link module:workbox-window.Workbox#redundant} | ||
* @fires [externalinstalled]{@link module:workbox-window.Workbox#externalinstalled} | ||
* @fires [externalwaiting]{@link module:workbox-window.Workbox#externalwaiting} | ||
* @fires [externalactivated]{@link module:workbox-window.Workbox#externalactivated} | ||
* | ||
* @memberof module:workbox-window | ||
*/ | ||
try { | ||
return Promise.resolve(f.apply(this, args)); | ||
} catch (e) { | ||
return Promise.reject(e); | ||
} | ||
}; | ||
} | ||
var Workbox = | ||
/*#__PURE__*/ | ||
function (_EventTargetShim) { | ||
_inheritsLoose(Workbox, _EventTargetShim); | ||
function _await(value, then, direct) { | ||
if (direct) { | ||
return then ? then(value) : value; | ||
} | ||
/** | ||
* Creates a new Workbox instance with a script URL and service worker | ||
* options. The script URL and options are the same as those used when | ||
* calling `navigator.serviceWorker.register(scriptURL, options)`. See: | ||
* https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register | ||
* | ||
* @param {string} scriptURL The service worker script associated with this | ||
* instance. | ||
* @param {Object} [registerOptions] The service worker options associated | ||
* with this instance. | ||
*/ | ||
function Workbox(scriptURL, registerOptions) { | ||
var _this; | ||
if (registerOptions === void 0) { | ||
registerOptions = {}; | ||
if (!value || !value.then) { | ||
value = Promise.resolve(value); | ||
} | ||
_this = _EventTargetShim.call(this) || this; | ||
_this._scriptURL = scriptURL; | ||
_this._registerOptions = registerOptions; | ||
_this._updateFoundCount = 0; // Deferreds we can resolve later. | ||
return then ? value.then(then) : value; | ||
} | ||
// `skipWaiting()` wasn't called. This 200 amount wasn't scientifically | ||
// chosen, but it seems to avoid false positives in my testing. | ||
_this._swDeferred = new Deferred(); | ||
_this._activeDeferred = new Deferred(); | ||
_this._controllingDeferred = new Deferred(); // Bind event handler callbacks. | ||
var WAITING_TIMEOUT_DURATION = 200; // The amount of time after a registration that we can reasonably conclude | ||
// that the registration didn't trigger an update. | ||
_this._onMessage = _this._onMessage.bind(_assertThisInitialized(_assertThisInitialized(_this))); | ||
_this._onStateChange = _this._onStateChange.bind(_assertThisInitialized(_assertThisInitialized(_this))); | ||
_this._onUpdateFound = _this._onUpdateFound.bind(_assertThisInitialized(_assertThisInitialized(_this))); | ||
_this._onControllerChange = _this._onControllerChange.bind(_assertThisInitialized(_assertThisInitialized(_this))); | ||
return _this; | ||
} | ||
var REGISTRATION_TIMEOUT_DURATION = 60000; | ||
/** | ||
* Registers a service worker for this instances script URL and service | ||
* worker options. By default this method delays registration until after | ||
* the window has loaded. | ||
* A class to aid in handling service worker registration, updates, and | ||
* reacting to service worker lifecycle events. | ||
* | ||
* @param {Object} [options] | ||
* @param {Function} [options.immediate=false] Setting this to true will | ||
* register the service worker immediately, even if the window has | ||
* not loaded (not recommended). | ||
* @fires [message]{@link module:workbox-window.Workbox#message} | ||
* @fires [installed]{@link module:workbox-window.Workbox#installed} | ||
* @fires [waiting]{@link module:workbox-window.Workbox#waiting} | ||
* @fires [controlling]{@link module:workbox-window.Workbox#controlling} | ||
* @fires [activated]{@link module:workbox-window.Workbox#activated} | ||
* @fires [redundant]{@link module:workbox-window.Workbox#redundant} | ||
* @fires [externalinstalled]{@link module:workbox-window.Workbox#externalinstalled} | ||
* @fires [externalwaiting]{@link module:workbox-window.Workbox#externalwaiting} | ||
* @fires [externalactivated]{@link module:workbox-window.Workbox#externalactivated} | ||
* | ||
* @memberof module:workbox-window | ||
*/ | ||
var Workbox = | ||
/*#__PURE__*/ | ||
function (_WorkboxEventTarget) { | ||
_inheritsLoose(Workbox, _WorkboxEventTarget); | ||
var _proto = Workbox.prototype; | ||
_proto.register = _async(function (_temp) { | ||
var _this2 = this; | ||
/** | ||
* Creates a new Workbox instance with a script URL and service worker | ||
* options. The script URL and options are the same as those used when | ||
* calling `navigator.serviceWorker.register(scriptURL, options)`. See: | ||
* https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register | ||
* | ||
* @param {string} scriptURL The service worker script associated with this | ||
* instance. | ||
* @param {Object} [registerOptions] The service worker options associated | ||
* with this instance. | ||
*/ | ||
function Workbox(scriptURL, registerOptions) { | ||
var _this; | ||
var _ref = _temp === void 0 ? {} : _temp, | ||
_ref$immediate = _ref.immediate, | ||
immediate = _ref$immediate === void 0 ? false : _ref$immediate; | ||
{ | ||
if (_this2._registrationTime) { | ||
logger.error('Cannot re-register a Workbox instance after it has ' + 'been registered. Create a new instance instead.'); | ||
return; | ||
if (registerOptions === void 0) { | ||
registerOptions = {}; | ||
} | ||
} | ||
return _invoke(function () { | ||
if (!immediate && document.readyState !== 'complete') { | ||
return _awaitIgnored(new Promise(function (res) { | ||
return addEventListener('load', res); | ||
})); | ||
} | ||
}, function () { | ||
// Set this flag to true if any service worker was controlling the page | ||
// at registration time. | ||
_this2._isUpdate = Boolean(navigator.serviceWorker.controller); // Before registering, attempt to determine if a SW is already controlling | ||
// the page, and if that SW script (and version, if specified) matches this | ||
// instance's script. | ||
_this = _WorkboxEventTarget.call(this) || this; | ||
_this._registerOptions = {}; | ||
_this._updateFoundCount = 0; // Deferreds we can resolve later. | ||
_this2._compatibleControllingSW = _this2._getControllingSWIfCompatible(); | ||
return _await(_this2._registerScript(), function (_this2$_registerScrip) { | ||
_this2._registration = _this2$_registerScrip; | ||
_this._swDeferred = new Deferred(); | ||
_this._activeDeferred = new Deferred(); | ||
_this._controllingDeferred = new Deferred(); | ||
_this._registrationTime = 0; | ||
/** | ||
* @private | ||
*/ | ||
// If we have a compatible controller, store the controller as the "own" | ||
// SW, resolve active/controlling deferreds and add necessary listeners. | ||
if (_this2._compatibleControllingSW) { | ||
_this2._sw = _this2._compatibleControllingSW; | ||
_this._onUpdateFound = function () { | ||
// `this._registration` will never be `undefined` after an update is found. | ||
var registration = _this._registration; | ||
var installingSW = registration.installing; // If the script URL passed to `navigator.serviceWorker.register()` is | ||
// different from the current controlling SW's script URL, we know any | ||
// successful registration calls will trigger an `updatefound` event. | ||
// But if the registered script URL is the same as the current controlling | ||
// SW's script URL, we'll only get an `updatefound` event if the file | ||
// changed since it was last registered. This can be a problem if the user | ||
// opens up the same page in a different tab, and that page registers | ||
// a SW that triggers an update. It's a problem because this page has no | ||
// good way of knowing whether the `updatefound` event came from the SW | ||
// script it registered or from a registration attempt made by a newer | ||
// version of the page running in another tab. | ||
// To minimize the possibility of a false positive, we use the logic here: | ||
_this2._activeDeferred.resolve(_this2._compatibleControllingSW); | ||
var updateLikelyTriggeredExternally = // Since we enforce only calling `register()` once, and since we don't | ||
// add the `updatefound` event listener until the `register()` call, if | ||
// `_updateFoundCount` is > 0 then it means this method has already | ||
// been called, thus this SW must be external | ||
_this._updateFoundCount > 0 || // If the script URL of the installing SW is different from this | ||
// instance's script URL, we know it's definitely not from our | ||
// registration. | ||
!urlsMatch(installingSW.scriptURL, _this._scriptURL) || // If all of the above are false, then we use a time-based heuristic: | ||
// Any `updatefound` event that occurs long after our registration is | ||
// assumed to be external. | ||
performance.now() > _this._registrationTime + REGISTRATION_TIMEOUT_DURATION ? // If any of the above are not true, we assume the update was | ||
// triggered by this instance. | ||
true : false; | ||
_this2._controllingDeferred.resolve(_this2._compatibleControllingSW); | ||
if (updateLikelyTriggeredExternally) { | ||
_this._externalSW = installingSW; | ||
registration.removeEventListener('updatefound', _this._onUpdateFound); | ||
} else { | ||
// If the update was not triggered externally we know the installing | ||
// SW is the one we registered, so we set it. | ||
_this._sw = installingSW; | ||
_this2._reportWindowReady(_this2._compatibleControllingSW); | ||
_this._swDeferred.resolve(installingSW); // The `installing` state isn't something we have a dedicated | ||
// callback for, but we do log messages for it in development. | ||
_this2._compatibleControllingSW.addEventListener('statechange', _this2._onStateChange, { | ||
once: true | ||
}); | ||
} // If there's a waiting service worker with a matching URL before the | ||
// `updatefound` event fires, it likely means that this site is open | ||
// in another tab, or the user refreshed the page (and thus the prevoius | ||
// page wasn't fully unloaded before this page started loading). | ||
// https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#waiting | ||
{ | ||
if (navigator.serviceWorker.controller) { | ||
logger.log('Updated service worker found. Installing now...'); | ||
} else { | ||
logger.log('Service worker is installing...'); | ||
} | ||
} | ||
} // Increment the `updatefound` count, so future invocations of this | ||
// method can be sure they were triggered externally. | ||
var waitingSW = _this2._registration.waiting; | ||
if (waitingSW && urlsMatch(waitingSW.scriptURL, _this2._scriptURL)) { | ||
// Store the waiting SW as the "own" Sw, even if it means overwriting | ||
// a compatible controller. | ||
_this2._sw = waitingSW; // Run this in the next microtask, so any code that adds an event | ||
// listener after awaiting `register()` will get this event. | ||
++_this._updateFoundCount; // Add a `statechange` listener regardless of whether this update was | ||
// triggered externally, since we have callbacks for both. | ||
Promise.resolve().then(function () { | ||
_this2.dispatchEvent(new WorkboxEvent('waiting', { | ||
sw: waitingSW, | ||
wasWaitingBeforeRegister: true | ||
})); | ||
installingSW.addEventListener('statechange', _this._onStateChange); | ||
}; | ||
/** | ||
* @private | ||
* @param {Event} originalEvent | ||
*/ | ||
{ | ||
logger.warn('A service worker was already waiting to activate ' + 'before this script was registered...'); | ||
} | ||
}); | ||
} // If an "own" SW is already set, resolve the deferred. | ||
_this._onStateChange = function (originalEvent) { | ||
// `this._registration` will never be `undefined` after an update is found. | ||
var registration = _this._registration; | ||
var sw = originalEvent.target; | ||
var state = sw.state; | ||
var isExternal = sw === _this._externalSW; | ||
var eventPrefix = isExternal ? 'external' : ''; | ||
var eventProps = { | ||
sw: sw, | ||
originalEvent: originalEvent | ||
}; | ||
if (_this2._sw) { | ||
_this2._swDeferred.resolve(_this2._sw); | ||
if (!isExternal && _this._isUpdate) { | ||
eventProps.isUpdate = true; | ||
} | ||
{ | ||
logger.log('Successfully registered service worker.', _this2._scriptURL); | ||
_this.dispatchEvent(new WorkboxEvent(eventPrefix + state, eventProps)); | ||
if (navigator.serviceWorker.controller) { | ||
if (_this2._compatibleControllingSW) { | ||
logger.debug('A service worker with the same script URL ' + 'is already controlling this page.'); | ||
} else { | ||
logger.debug('A service worker with a different script URL is ' + 'currently controlling the page. The browser is now fetching ' + 'the new script now...'); | ||
if (state === 'installed') { | ||
// This timeout is used to ignore cases where the service worker calls | ||
// `skipWaiting()` in the install event, thus moving it directly in the | ||
// activating state. (Since all service workers *must* go through the | ||
// waiting phase, the only way to detect `skipWaiting()` called in the | ||
// install event is to observe that the time spent in the waiting phase | ||
// is very short.) | ||
// NOTE: we don't need separate timeouts for the own and external SWs | ||
// since they can't go through these phases at the same time. | ||
_this._waitingTimeout = self.setTimeout(function () { | ||
// Ensure the SW is still waiting (it may now be redundant). | ||
if (state === 'installed' && registration.waiting === sw) { | ||
_this.dispatchEvent(new WorkboxEvent(eventPrefix + 'waiting', eventProps)); | ||
{ | ||
if (isExternal) { | ||
logger.warn('An external service worker has installed but is ' + 'waiting for this client to close before activating...'); | ||
} else { | ||
logger.warn('The service worker has installed but is waiting ' + 'for existing clients to close before activating...'); | ||
} | ||
} | ||
} | ||
} | ||
}, WAITING_TIMEOUT_DURATION); | ||
} else if (state === 'activating') { | ||
clearTimeout(_this._waitingTimeout); | ||
var currentPageIsOutOfScope = function currentPageIsOutOfScope() { | ||
var scopeURL = new URL(_this2._registerOptions.scope || _this2._scriptURL, document.baseURI); | ||
var scopeURLBasePath = new URL('./', scopeURL.href).pathname; | ||
return !location.pathname.startsWith(scopeURLBasePath); | ||
}; | ||
if (currentPageIsOutOfScope()) { | ||
logger.warn('The current page is not in scope for the registered ' + 'service worker. Was this a mistake?'); | ||
if (!isExternal) { | ||
_this._activeDeferred.resolve(sw); | ||
} | ||
} | ||
_this2._registration.addEventListener('updatefound', _this2._onUpdateFound); | ||
{ | ||
switch (state) { | ||
case 'installed': | ||
if (isExternal) { | ||
logger.warn('An external service worker has installed. ' + 'You may want to suggest users reload this page.'); | ||
} else { | ||
logger.log('Registered service worker installed.'); | ||
} | ||
navigator.serviceWorker.addEventListener('controllerchange', _this2._onControllerChange, { | ||
once: true | ||
}); // Add message listeners. | ||
break; | ||
if ('BroadcastChannel' in self) { | ||
_this2._broadcastChannel = new BroadcastChannel('workbox'); | ||
case 'activated': | ||
if (isExternal) { | ||
logger.warn('An external service worker has activated.'); | ||
} else { | ||
logger.log('Registered service worker activated.'); | ||
_this2._broadcastChannel.addEventListener('message', _this2._onMessage); | ||
} | ||
if (sw !== navigator.serviceWorker.controller) { | ||
logger.warn('The registered service worker is active but ' + 'not yet controlling the page. Reload or run ' + '`clients.claim()` in the service worker.'); | ||
} | ||
} | ||
navigator.serviceWorker.addEventListener('message', _this2._onMessage); | ||
return _this2._registration; | ||
}); | ||
}); | ||
}); | ||
/** | ||
* Resolves to the service worker registered by this instance as soon as it | ||
* is active. If a service worker was already controlling at registration | ||
* time then it will resolve to that if the script URLs (and optionally | ||
* script versions) match, otherwise it will wait until an update is found | ||
* and activates. | ||
* | ||
* @return {Promise<ServiceWorker>} | ||
*/ | ||
break; | ||
/** | ||
* Resolves with a reference to a service worker that matches the script URL | ||
* of this instance, as soon as it's available. | ||
* | ||
* If, at registration time, there's already an active or waiting service | ||
* worker with a matching script URL, it will be used (with the waiting | ||
* service worker taking precedence over the active service worker if both | ||
* match, since the waiting service worker would have been registered more | ||
* recently). | ||
* If there's no matching active or waiting service worker at registration | ||
* time then the promise will not resolve until an update is found and starts | ||
* installing, at which point the installing service worker is used. | ||
* | ||
* @return {Promise<ServiceWorker>} | ||
*/ | ||
_proto.getSW = _async(function () { | ||
var _this3 = this; | ||
case 'redundant': | ||
if (sw === _this._compatibleControllingSW) { | ||
logger.log('Previously controlling service worker now redundant!'); | ||
} else if (!isExternal) { | ||
logger.log('Registered service worker now redundant!'); | ||
} | ||
// If `this._sw` is set, resolve with that as we want `getSW()` to | ||
// return the correct (new) service worker if an update is found. | ||
return _this3._sw || _this3._swDeferred.promise; | ||
}); | ||
/** | ||
* Sends the passed data object to the service worker registered by this | ||
* instance (via [`getSW()`]{@link module:workbox-window.Workbox#getSW}) and resolves | ||
* with a response (if any). | ||
* | ||
* A response can be set in a message handler in the service worker by | ||
* calling `event.ports[0].postMessage(...)`, which will resolve the promise | ||
* returned by `messageSW()`. If no response is set, the promise will never | ||
* resolve. | ||
* | ||
* @param {Object} data An object to send to the service worker | ||
* @return {Promise<Object>} | ||
*/ | ||
break; | ||
} | ||
} | ||
}; | ||
/** | ||
* @private | ||
* @param {Event} originalEvent | ||
*/ | ||
_proto.messageSW = _async(function (data) { | ||
var _this4 = this; | ||
return _await(_this4.getSW(), function (sw) { | ||
return messageSW(sw, data); | ||
}); | ||
}); | ||
/** | ||
* Checks for a service worker already controlling the page and returns | ||
* it if its script URL matchs. | ||
* | ||
* @private | ||
* @return {ServiceWorker|undefined} | ||
*/ | ||
_this._onControllerChange = function (originalEvent) { | ||
var sw = _this._sw; | ||
_proto._getControllingSWIfCompatible = function _getControllingSWIfCompatible() { | ||
var controller = navigator.serviceWorker.controller; | ||
if (sw === navigator.serviceWorker.controller) { | ||
_this.dispatchEvent(new WorkboxEvent('controlling', { | ||
sw: sw, | ||
originalEvent: originalEvent, | ||
isUpdate: _this._isUpdate | ||
})); | ||
if (controller && urlsMatch(controller.scriptURL, this._scriptURL)) { | ||
return controller; | ||
} | ||
}; | ||
/** | ||
* Registers a service worker for this instances script URL and register | ||
* options and tracks the time registration was complete. | ||
* | ||
* @private | ||
*/ | ||
{ | ||
logger.log('Registered service worker now controlling this page.'); | ||
} | ||
_this._controllingDeferred.resolve(sw); | ||
} | ||
}; | ||
/** | ||
* @private | ||
* @param {Event} originalEvent | ||
*/ | ||
_proto._registerScript = _async(function () { | ||
var _this5 = this; | ||
return _catch(function () { | ||
return _await(navigator.serviceWorker.register(_this5._scriptURL, _this5._registerOptions), function (reg) { | ||
// Keep track of when registration happened, so it can be used in the | ||
// `this._onUpdateFound` heuristic. Also use the presence of this | ||
// property as a way to see if `.register()` has been called. | ||
_this5._registrationTime = performance.now(); | ||
return reg; | ||
_this._onMessage = _async(function (originalEvent) { | ||
var data = originalEvent.data; | ||
var _this2 = _this, | ||
_dispatchEvent = _this2.dispatchEvent; | ||
return _await(_this.getSW(), function (_this$getSW) { | ||
_dispatchEvent.call(_this2, new WorkboxEvent('message', { | ||
data: data, | ||
sw: _this$getSW, | ||
originalEvent: originalEvent | ||
})); | ||
}); | ||
}); | ||
}, function (error) { | ||
{ | ||
logger.error(error); | ||
} // Re-throw the error. | ||
_this._scriptURL = scriptURL; | ||
_this._registerOptions = registerOptions; | ||
return _this; | ||
} | ||
/** | ||
* Registers a service worker for this instances script URL and service | ||
* worker options. By default this method delays registration until after | ||
* the window has loaded. | ||
* | ||
* @param {Object} [options] | ||
* @param {Function} [options.immediate=false] Setting this to true will | ||
* register the service worker immediately, even if the window has | ||
* not loaded (not recommended). | ||
*/ | ||
throw error; | ||
}); | ||
}); | ||
/** | ||
* Sends a message to the passed service worker that the window is ready. | ||
* | ||
* @param {ServiceWorker} sw | ||
* @private | ||
*/ | ||
var _proto = Workbox.prototype; | ||
_proto.register = _async(function (_temp) { | ||
var _this3 = this; | ||
_proto._reportWindowReady = function _reportWindowReady(sw) { | ||
messageSW(sw, { | ||
type: 'WINDOW_READY', | ||
meta: 'workbox-window' | ||
}); | ||
}; | ||
/** | ||
* @private | ||
*/ | ||
var _ref = _temp === void 0 ? {} : _temp, | ||
_ref$immediate = _ref.immediate, | ||
immediate = _ref$immediate === void 0 ? false : _ref$immediate; | ||
{ | ||
if (_this3._registrationTime) { | ||
logger.error('Cannot re-register a Workbox instance after it has ' + 'been registered. Create a new instance instead.'); | ||
return; | ||
} | ||
} | ||
_proto._onUpdateFound = function _onUpdateFound() { | ||
var installingSW = this._registration.installing; // If the script URL passed to `navigator.serviceWorker.register()` is | ||
// different from the current controlling SW's script URL, we know any | ||
// successful registration calls will trigger an `updatefound` event. | ||
// But if the registered script URL is the same as the current controlling | ||
// SW's script URL, we'll only get an `updatefound` event if the file | ||
// changed since it was last registered. This can be a problem if the user | ||
// opens up the same page in a different tab, and that page registers | ||
// a SW that triggers an update. It's a problem because this page has no | ||
// good way of knowing whether the `updatefound` event came from the SW | ||
// script it registered or from a registration attempt made by a newer | ||
// version of the page running in another tab. | ||
// To minimize the possibility of a false positive, we use the logic here: | ||
return _invoke(function () { | ||
if (!immediate && document.readyState !== 'complete') { | ||
return _awaitIgnored(new Promise(function (res) { | ||
return addEventListener('load', res); | ||
})); | ||
} | ||
}, function () { | ||
// Set this flag to true if any service worker was controlling the page | ||
// at registration time. | ||
_this3._isUpdate = Boolean(navigator.serviceWorker.controller); // Before registering, attempt to determine if a SW is already controlling | ||
// the page, and if that SW script (and version, if specified) matches this | ||
// instance's script. | ||
var updateLikelyTriggeredExternally = // Since we enforce only calling `register()` once, and since we don't | ||
// add the `updatefound` event listener until the `register()` call, if | ||
// `_updateFoundCount` is > 0 then it means this method has already | ||
// been called, thus this SW must be external | ||
this._updateFoundCount > 0 || // If the script URL of the installing SW is different from this | ||
// instance's script URL, we know it's definitely not from our | ||
// registration. | ||
!urlsMatch(installingSW.scriptURL, this._scriptURL) || // If all of the above are false, then we use a time-based heuristic: | ||
// Any `updatefound` event that occurs long after our registration is | ||
// assumed to be external. | ||
performance.now() > this._registrationTime + REGISTRATION_TIMEOUT_DURATION ? // If any of the above are not true, we assume the update was | ||
// triggered by this instance. | ||
true : false; | ||
_this3._compatibleControllingSW = _this3._getControllingSWIfCompatible(); | ||
return _await(_this3._registerScript(), function (_this3$_registerScrip) { | ||
_this3._registration = _this3$_registerScrip; | ||
if (updateLikelyTriggeredExternally) { | ||
this._externalSW = installingSW; | ||
// If we have a compatible controller, store the controller as the "own" | ||
// SW, resolve active/controlling deferreds and add necessary listeners. | ||
if (_this3._compatibleControllingSW) { | ||
_this3._sw = _this3._compatibleControllingSW; | ||
this._registration.removeEventListener('updatefound', this._onUpdateFound); | ||
} else { | ||
// If the update was not triggered externally we know the installing | ||
// SW is the one we registered, so we set it. | ||
this._sw = installingSW; | ||
_this3._activeDeferred.resolve(_this3._compatibleControllingSW); | ||
this._swDeferred.resolve(installingSW); // The `installing` state isn't something we have a dedicated | ||
// callback for, but we do log messages for it in development. | ||
_this3._controllingDeferred.resolve(_this3._compatibleControllingSW); | ||
_this3._reportWindowReady(_this3._compatibleControllingSW); | ||
{ | ||
if (navigator.serviceWorker.controller) { | ||
logger.log('Updated service worker found. Installing now...'); | ||
} else { | ||
logger.log('Service worker is installing...'); | ||
} | ||
} | ||
} // Increment the `updatefound` count, so future invocations of this | ||
// method can be sure they were triggered externally. | ||
_this3._compatibleControllingSW.addEventListener('statechange', _this3._onStateChange, { | ||
once: true | ||
}); | ||
} // If there's a waiting service worker with a matching URL before the | ||
// `updatefound` event fires, it likely means that this site is open | ||
// in another tab, or the user refreshed the page (and thus the prevoius | ||
// page wasn't fully unloaded before this page started loading). | ||
// https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#waiting | ||
++this._updateFoundCount; // Add a `statechange` listener regardless of whether this update was | ||
// triggered externally, since we have callbacks for both. | ||
var waitingSW = _this3._registration.waiting; | ||
installingSW.addEventListener('statechange', this._onStateChange); | ||
}; | ||
/** | ||
* @private | ||
* @param {Event} originalEvent | ||
*/ | ||
if (waitingSW && urlsMatch(waitingSW.scriptURL, _this3._scriptURL)) { | ||
// Store the waiting SW as the "own" Sw, even if it means overwriting | ||
// a compatible controller. | ||
_this3._sw = waitingSW; // Run this in the next microtask, so any code that adds an event | ||
// listener after awaiting `register()` will get this event. | ||
Promise.resolve().then(function () { | ||
_this3.dispatchEvent(new WorkboxEvent('waiting', { | ||
sw: waitingSW, | ||
wasWaitingBeforeRegister: true | ||
})); | ||
_proto._onStateChange = function _onStateChange(originalEvent) { | ||
var _this6 = this; | ||
{ | ||
logger.warn('A service worker was already waiting to activate ' + 'before this script was registered...'); | ||
} | ||
}); | ||
} // If an "own" SW is already set, resolve the deferred. | ||
var sw = originalEvent.target; | ||
var state = sw.state; | ||
var isExternal = sw === this._externalSW; | ||
var eventPrefix = isExternal ? 'external' : ''; | ||
var eventProps = { | ||
sw: sw, | ||
originalEvent: originalEvent | ||
}; | ||
if (!isExternal && this._isUpdate) { | ||
eventProps.isUpdate = true; | ||
} | ||
if (_this3._sw) { | ||
_this3._swDeferred.resolve(_this3._sw); | ||
} | ||
this.dispatchEvent(new WorkboxEvent(eventPrefix + state, eventProps)); | ||
{ | ||
logger.log('Successfully registered service worker.', _this3._scriptURL); | ||
if (state === 'installed') { | ||
// This timeout is used to ignore cases where the service worker calls | ||
// `skipWaiting()` in the install event, thus moving it directly in the | ||
// activating state. (Since all service workers *must* go through the | ||
// waiting phase, the only way to detect `skipWaiting()` called in the | ||
// install event is to observe that the time spent in the waiting phase | ||
// is very short.) | ||
// NOTE: we don't need separate timeouts for the own and external SWs | ||
// since they can't go through these phases at the same time. | ||
this._waitingTimeout = setTimeout(function () { | ||
// Ensure the SW is still waiting (it may now be redundant). | ||
if (state === 'installed' && _this6._registration.waiting === sw) { | ||
_this6.dispatchEvent(new WorkboxEvent(eventPrefix + 'waiting', eventProps)); | ||
if (navigator.serviceWorker.controller) { | ||
if (_this3._compatibleControllingSW) { | ||
logger.debug('A service worker with the same script URL ' + 'is already controlling this page.'); | ||
} else { | ||
logger.debug('A service worker with a different script URL is ' + 'currently controlling the page. The browser is now fetching ' + 'the new script now...'); | ||
} | ||
} | ||
{ | ||
if (isExternal) { | ||
logger.warn('An external service worker has installed but is ' + 'waiting for this client to close before activating...'); | ||
} else { | ||
logger.warn('The service worker has installed but is waiting ' + 'for existing clients to close before activating...'); | ||
var currentPageIsOutOfScope = function currentPageIsOutOfScope() { | ||
var scopeURL = new URL(_this3._registerOptions.scope || _this3._scriptURL, document.baseURI); | ||
var scopeURLBasePath = new URL('./', scopeURL.href).pathname; | ||
return !location.pathname.startsWith(scopeURLBasePath); | ||
}; | ||
if (currentPageIsOutOfScope()) { | ||
logger.warn('The current page is not in scope for the registered ' + 'service worker. Was this a mistake?'); | ||
} | ||
} | ||
} | ||
}, WAITING_TIMEOUT_DURATION); | ||
} else if (state === 'activating') { | ||
clearTimeout(this._waitingTimeout); | ||
if (!isExternal) { | ||
this._activeDeferred.resolve(sw); | ||
} | ||
} | ||
_this3._registration.addEventListener('updatefound', _this3._onUpdateFound); | ||
{ | ||
switch (state) { | ||
case 'installed': | ||
if (isExternal) { | ||
logger.warn('An external service worker has installed. ' + 'You may want to suggest users reload this page.'); | ||
} else { | ||
logger.log('Registered service worker installed.'); | ||
navigator.serviceWorker.addEventListener('controllerchange', _this3._onControllerChange, { | ||
once: true | ||
}); // Add message listeners. | ||
if ('BroadcastChannel' in self) { | ||
_this3._broadcastChannel = new BroadcastChannel('workbox'); | ||
_this3._broadcastChannel.addEventListener('message', _this3._onMessage); | ||
} | ||
break; | ||
navigator.serviceWorker.addEventListener('message', _this3._onMessage); | ||
return _this3._registration; | ||
}); | ||
}); | ||
}); | ||
/** | ||
* Checks for updates of the registered service worker. | ||
*/ | ||
case 'activated': | ||
if (isExternal) { | ||
logger.warn('An external service worker has activated.'); | ||
} else { | ||
logger.log('Registered service worker activated.'); | ||
_proto.update = _async(function () { | ||
var _this4 = this; | ||
if (sw !== navigator.serviceWorker.controller) { | ||
logger.warn('The registered service worker is active but ' + 'not yet controlling the page. Reload or run ' + '`clients.claim()` in the service worker.'); | ||
} | ||
} | ||
if (!_this4._registration) { | ||
{ | ||
logger.error('Cannot update a Workbox instance without ' + 'being registered. Register the Workbox instance first.'); | ||
} | ||
break; | ||
return; | ||
} // Try to update registration | ||
case 'redundant': | ||
if (sw === this._compatibleControllingSW) { | ||
logger.log('Previously controlling service worker now redundant!'); | ||
} else if (!isExternal) { | ||
logger.log('Registered service worker now redundant!'); | ||
} | ||
break; | ||
} | ||
} | ||
}; | ||
/** | ||
* @private | ||
* @param {Event} originalEvent | ||
*/ | ||
return _awaitIgnored(_this4._registration.update()); | ||
}); | ||
/** | ||
* Resolves to the service worker registered by this instance as soon as it | ||
* is active. If a service worker was already controlling at registration | ||
* time then it will resolve to that if the script URLs (and optionally | ||
* script versions) match, otherwise it will wait until an update is found | ||
* and activates. | ||
* | ||
* @return {Promise<ServiceWorker>} | ||
*/ | ||
/** | ||
* Resolves with a reference to a service worker that matches the script URL | ||
* of this instance, as soon as it's available. | ||
* | ||
* If, at registration time, there's already an active or waiting service | ||
* worker with a matching script URL, it will be used (with the waiting | ||
* service worker taking precedence over the active service worker if both | ||
* match, since the waiting service worker would have been registered more | ||
* recently). | ||
* If there's no matching active or waiting service worker at registration | ||
* time then the promise will not resolve until an update is found and starts | ||
* installing, at which point the installing service worker is used. | ||
* | ||
* @return {Promise<ServiceWorker>} | ||
*/ | ||
_proto.getSW = _async(function () { | ||
var _this5 = this; | ||
_proto._onControllerChange = function _onControllerChange(originalEvent) { | ||
var sw = this._sw; | ||
// If `this._sw` is set, resolve with that as we want `getSW()` to | ||
// return the correct (new) service worker if an update is found. | ||
return _this5._sw !== undefined ? _this5._sw : _this5._swDeferred.promise; | ||
}); | ||
/** | ||
* Sends the passed data object to the service worker registered by this | ||
* instance (via [`getSW()`]{@link module:workbox-window.Workbox#getSW}) and resolves | ||
* with a response (if any). | ||
* | ||
* A response can be set in a message handler in the service worker by | ||
* calling `event.ports[0].postMessage(...)`, which will resolve the promise | ||
* returned by `messageSW()`. If no response is set, the promise will never | ||
* resolve. | ||
* | ||
* @param {Object} data An object to send to the service worker | ||
* @return {Promise<Object>} | ||
*/ | ||
if (sw === navigator.serviceWorker.controller) { | ||
this.dispatchEvent(new WorkboxEvent('controlling', { | ||
sw: sw, | ||
originalEvent: originalEvent | ||
})); | ||
_proto.messageSW = _async(function (data) { | ||
var _this6 = this; | ||
{ | ||
logger.log('Registered service worker now controlling this page.'); | ||
return _await(_this6.getSW(), function (sw) { | ||
return messageSW(sw, data); | ||
}); | ||
}); | ||
/** | ||
* Checks for a service worker already controlling the page and returns | ||
* it if its script URL matches. | ||
* | ||
* @private | ||
* @return {ServiceWorker|undefined} | ||
*/ | ||
_proto._getControllingSWIfCompatible = function _getControllingSWIfCompatible() { | ||
var controller = navigator.serviceWorker.controller; | ||
if (controller && urlsMatch(controller.scriptURL, this._scriptURL)) { | ||
return controller; | ||
} else { | ||
return undefined; | ||
} | ||
}; | ||
/** | ||
* Registers a service worker for this instances script URL and register | ||
* options and tracks the time registration was complete. | ||
* | ||
* @private | ||
*/ | ||
this._controllingDeferred.resolve(sw); | ||
} | ||
}; | ||
/** | ||
* @private | ||
* @param {Event} originalEvent | ||
*/ | ||
_proto._registerScript = _async(function () { | ||
var _this7 = this; | ||
_proto._onMessage = function _onMessage(originalEvent) { | ||
var data = originalEvent.data; | ||
this.dispatchEvent(new WorkboxEvent('message', { | ||
data: data, | ||
originalEvent: originalEvent | ||
})); | ||
}; | ||
return _catch(function () { | ||
return _await(navigator.serviceWorker.register(_this7._scriptURL, _this7._registerOptions), function (reg) { | ||
// Keep track of when registration happened, so it can be used in the | ||
// `this._onUpdateFound` heuristic. Also use the presence of this | ||
// property as a way to see if `.register()` has been called. | ||
_this7._registrationTime = performance.now(); | ||
return reg; | ||
}); | ||
}, function (error) { | ||
{ | ||
logger.error(error); | ||
} // Re-throw the error. | ||
_createClass(Workbox, [{ | ||
key: "active", | ||
get: function get() { | ||
return this._activeDeferred.promise; | ||
} | ||
throw error; | ||
}); | ||
}); | ||
/** | ||
* Resolves to the service worker registered by this instance as soon as it | ||
* is controlling the page. If a service worker was already controlling at | ||
* registration time then it will resolve to that if the script URLs (and | ||
* optionally script versions) match, otherwise it will wait until an update | ||
* is found and starts controlling the page. | ||
* Note: the first time a service worker is installed it will active but | ||
* not start controlling the page unless `clients.claim()` is called in the | ||
* service worker. | ||
* Sends a message to the passed service worker that the window is ready. | ||
* | ||
* @return {Promise<ServiceWorker>} | ||
* @param {ServiceWorker} sw | ||
* @private | ||
*/ | ||
}, { | ||
key: "controlling", | ||
get: function get() { | ||
return this._controllingDeferred.promise; | ||
} | ||
}]); | ||
_proto._reportWindowReady = function _reportWindowReady(sw) { | ||
messageSW(sw, { | ||
type: 'WINDOW_READY', | ||
meta: 'workbox-window' | ||
}); | ||
}; | ||
return Workbox; | ||
}(EventTargetShim); // The jsdoc comments below outline the events this instance may dispatch: | ||
_createClass(Workbox, [{ | ||
key: "active", | ||
get: function get() { | ||
return this._activeDeferred.promise; | ||
} | ||
/** | ||
* Resolves to the service worker registered by this instance as soon as it | ||
* is controlling the page. If a service worker was already controlling at | ||
* registration time then it will resolve to that if the script URLs (and | ||
* optionally script versions) match, otherwise it will wait until an update | ||
* is found and starts controlling the page. | ||
* Note: the first time a service worker is installed it will active but | ||
* not start controlling the page unless `clients.claim()` is called in the | ||
* service worker. | ||
* | ||
* @return {Promise<ServiceWorker>} | ||
*/ | ||
/* | ||
Copyright 2019 Google LLC | ||
}, { | ||
key: "controlling", | ||
get: function get() { | ||
return this._controllingDeferred.promise; | ||
} | ||
}]); | ||
Use of this source code is governed by an MIT-style | ||
license that can be found in the LICENSE file or at | ||
https://opensource.org/licenses/MIT. | ||
*/ | ||
return Workbox; | ||
}(WorkboxEventTarget); // The jsdoc comments below outline the events this instance may dispatch: | ||
exports.Workbox = Workbox; | ||
exports.messageSW = messageSW; | ||
exports.Workbox = Workbox; | ||
exports.messageSW = messageSW; | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
Object.defineProperty(exports, '__esModule', { value: true }); | ||
})); | ||
@@ -1,2 +0,2 @@ | ||
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((n=n||self).workbox={})}(this,function(n){"use strict";try{self["workbox:window:4.3.1"]&&_()}catch(n){}var t=function(n,t){return new Promise(function(i){var e=new MessageChannel;e.port1.onmessage=function(n){return i(n.data)},n.postMessage(t,[e.port2])})};function i(n,t){for(var i=0;i<t.length;i++){var e=t[i];e.enumerable=e.enumerable||!1,e.configurable=!0,"value"in e&&(e.writable=!0),Object.defineProperty(n,e.key,e)}}function e(n){if(void 0===n)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return n}try{self["workbox:core:4.3.1"]&&_()}catch(n){}var r=function(){var n=this;this.promise=new Promise(function(t,i){n.resolve=t,n.reject=i})},o=function(n,t){return new URL(n,location).href===new URL(t,location).href},u=function(n,t){Object.assign(this,t,{type:n})};function s(n){return function(){for(var t=[],i=0;i<arguments.length;i++)t[i]=arguments[i];try{return Promise.resolve(n.apply(this,t))}catch(n){return Promise.reject(n)}}}function a(n,t,i){return i?t?t(n):n:(n&&n.then||(n=Promise.resolve(n)),t?n.then(t):n)}function c(){}var f=function(n){var f,h;function v(t,i){var o;return void 0===i&&(i={}),(o=n.call(this)||this).t=t,o.i=i,o.o=0,o.u=new r,o.s=new r,o.h=new r,o.v=o.v.bind(e(e(o))),o.l=o.l.bind(e(e(o))),o.g=o.g.bind(e(e(o))),o.m=o.m.bind(e(e(o))),o}h=n,(f=v).prototype=Object.create(h.prototype),f.prototype.constructor=f,f.__proto__=h;var l,w,d,g=v.prototype;return g.register=s(function(n){var t,i,e=this,r=(void 0===n?{}:n).immediate,s=void 0!==r&&r;return t=function(){return e.p=Boolean(navigator.serviceWorker.controller),e.P=e.j(),a(e.O(),function(n){e.R=n,e.P&&(e._=e.P,e.s.resolve(e.P),e.h.resolve(e.P),e.k(e.P),e.P.addEventListener("statechange",e.l,{once:!0}));var t=e.R.waiting;return t&&o(t.scriptURL,e.t)&&(e._=t,Promise.resolve().then(function(){e.dispatchEvent(new u("waiting",{sw:t,wasWaitingBeforeRegister:!0}))})),e._&&e.u.resolve(e._),e.R.addEventListener("updatefound",e.g),navigator.serviceWorker.addEventListener("controllerchange",e.m,{once:!0}),"BroadcastChannel"in self&&(e.B=new BroadcastChannel("workbox"),e.B.addEventListener("message",e.v)),navigator.serviceWorker.addEventListener("message",e.v),e.R})},(i=function(){if(!s&&"complete"!==document.readyState)return function(n,t){if(!t)return n&&n.then?n.then(c):Promise.resolve()}(new Promise(function(n){return addEventListener("load",n)}))}())&&i.then?i.then(t):t(i)}),g.getSW=s(function(){return this._||this.u.promise}),g.messageSW=s(function(n){return a(this.getSW(),function(i){return t(i,n)})}),g.j=function(){var n=navigator.serviceWorker.controller;if(n&&o(n.scriptURL,this.t))return n},g.O=s(function(){var n=this;return function(n,t){try{var i=n()}catch(n){return t(n)}return i&&i.then?i.then(void 0,t):i}(function(){return a(navigator.serviceWorker.register(n.t,n.i),function(t){return n.C=performance.now(),t})},function(n){throw n})}),g.k=function(n){t(n,{type:"WINDOW_READY",meta:"workbox-window"})},g.g=function(){var n=this.R.installing;this.o>0||!o(n.scriptURL,this.t)||performance.now()>this.C+6e4?(this.L=n,this.R.removeEventListener("updatefound",this.g)):(this._=n,this.u.resolve(n)),++this.o,n.addEventListener("statechange",this.l)},g.l=function(n){var t=this,i=n.target,e=i.state,r=i===this.L,o=r?"external":"",s={sw:i,originalEvent:n};!r&&this.p&&(s.isUpdate=!0),this.dispatchEvent(new u(o+e,s)),"installed"===e?this.W=setTimeout(function(){"installed"===e&&t.R.waiting===i&&t.dispatchEvent(new u(o+"waiting",s))},200):"activating"===e&&(clearTimeout(this.W),r||this.s.resolve(i))},g.m=function(n){var t=this._;t===navigator.serviceWorker.controller&&(this.dispatchEvent(new u("controlling",{sw:t,originalEvent:n})),this.h.resolve(t))},g.v=function(n){var t=n.data;this.dispatchEvent(new u("message",{data:t,originalEvent:n}))},l=v,(w=[{key:"active",get:function(){return this.s.promise}},{key:"controlling",get:function(){return this.h.promise}}])&&i(l.prototype,w),d&&i(l,d),v}(function(){function n(){this.D={}}var t=n.prototype;return t.addEventListener=function(n,t){this.M(n).add(t)},t.removeEventListener=function(n,t){this.M(n).delete(t)},t.dispatchEvent=function(n){n.target=this,this.M(n.type).forEach(function(t){return t(n)})},t.M=function(n){return this.D[n]=this.D[n]||new Set},n}());n.Workbox=f,n.messageSW=t,Object.defineProperty(n,"__esModule",{value:!0})}); | ||
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((n=n||self).workbox={})}(this,function(n){"use strict";try{self["workbox:window:5.0.0-alpha.2"]&&_()}catch(n){}function t(n,t){return new Promise(function(e){var i=new MessageChannel;i.port1.onmessage=function(n){e(n.data)},n.postMessage(t,[i.port2])})}function e(n,t){for(var e=0;e<t.length;e++){var i=t[e];i.enumerable=i.enumerable||!1,i.configurable=!0,"value"in i&&(i.writable=!0),Object.defineProperty(n,i.key,i)}}try{self["workbox:core:5.0.0-alpha.2"]&&_()}catch(n){}var i=function(){var n=this;this.promise=new Promise(function(t,e){n.resolve=t,n.reject=e})};function r(n,t){var e=location.href;return new URL(n,e).href===new URL(t,e).href}var o=function(n,t){this.type=n,Object.assign(this,t)};function u(n,t){if(!t)return n&&n.then?n.then(a):Promise.resolve()}function a(){}function c(n){return function(){for(var t=[],e=0;e<arguments.length;e++)t[e]=arguments[e];try{return Promise.resolve(n.apply(this,t))}catch(n){return Promise.reject(n)}}}function f(n,t,e){return e?t?t(n):n:(n&&n.then||(n=Promise.resolve(n)),t?n.then(t):n)}var s=200,v=6e4,h=function(n){var a,h;function l(t,e){var u;return void 0===e&&(e={}),(u=n.call(this)||this).t={},u.i=0,u.o=new i,u.u=new i,u.s=new i,u.v=0,u.h=function(){var n=u.l,t=n.installing;u.i>0||!r(t.scriptURL,u.g)||performance.now()>u.v+v?(u.m=t,n.removeEventListener("updatefound",u.h)):(u.p=t,u.o.resolve(t)),++u.i,t.addEventListener("statechange",u.P)},u.P=function(n){var t=u.l,e=n.target,i=e.state,r=e===u.m,a=r?"external":"",c={sw:e,originalEvent:n};!r&&u.k&&(c.isUpdate=!0),u.dispatchEvent(new o(a+i,c)),"installed"===i?u.j=self.setTimeout(function(){"installed"===i&&t.waiting===e&&u.dispatchEvent(new o(a+"waiting",c))},s):"activating"===i&&(clearTimeout(u.j),r||u.u.resolve(e))},u.O=function(n){var t=u.p;t===navigator.serviceWorker.controller&&(u.dispatchEvent(new o("controlling",{sw:t,originalEvent:n,isUpdate:u.k})),u.s.resolve(t))},u._=c(function(n){var t=n.data,e=u,i=e.dispatchEvent;return f(u.getSW(),function(r){i.call(e,new o("message",{data:t,sw:r,originalEvent:n}))})}),u.g=t,u.t=e,u}h=n,(a=l).prototype=Object.create(h.prototype),a.prototype.constructor=a,a.__proto__=h;var w,d,g,m=l.prototype;return m.register=c(function(n){var t,e,i=this,a=(void 0===n?{}:n).immediate,c=void 0!==a&&a;return t=function(){return i.k=Boolean(navigator.serviceWorker.controller),i.B=i.R(),f(i.C(),function(n){i.l=n,i.B&&(i.p=i.B,i.u.resolve(i.B),i.s.resolve(i.B),i.L(i.B),i.B.addEventListener("statechange",i.P,{once:!0}));var t=i.l.waiting;return t&&r(t.scriptURL,i.g)&&(i.p=t,Promise.resolve().then(function(){i.dispatchEvent(new o("waiting",{sw:t,wasWaitingBeforeRegister:!0}))})),i.p&&i.o.resolve(i.p),i.l.addEventListener("updatefound",i.h),navigator.serviceWorker.addEventListener("controllerchange",i.O,{once:!0}),"BroadcastChannel"in self&&(i.M=new BroadcastChannel("workbox"),i.M.addEventListener("message",i._)),navigator.serviceWorker.addEventListener("message",i._),i.l})},(e=function(){if(!c&&"complete"!==document.readyState)return u(new Promise(function(n){return addEventListener("load",n)}))}())&&e.then?e.then(t):t(e)}),m.update=c(function(){if(this.l)return u(this.l.update())}),m.getSW=c(function(){return void 0!==this.p?this.p:this.o.promise}),m.messageSW=c(function(n){return f(this.getSW(),function(e){return t(e,n)})}),m.R=function(){var n=navigator.serviceWorker.controller;return n&&r(n.scriptURL,this.g)?n:void 0},m.C=c(function(){var n=this;return function(n,t){try{var e=n()}catch(n){return t(n)}return e&&e.then?e.then(void 0,t):e}(function(){return f(navigator.serviceWorker.register(n.g,n.t),function(t){return n.v=performance.now(),t})},function(n){throw n})}),m.L=function(n){t(n,{type:"WINDOW_READY",meta:"workbox-window"})},w=l,(d=[{key:"active",get:function(){return this.u.promise}},{key:"controlling",get:function(){return this.s.promise}}])&&e(w.prototype,d),g&&e(w,g),l}(function(){function n(){this.U=new Map}var t=n.prototype;return t.addEventListener=function(n,t){this.W(n).add(t)},t.removeEventListener=function(n,t){this.W(n).delete(t)},t.dispatchEvent=function(n){n.target=this;var t=this.W(n.type),e=Array.isArray(t),i=0;for(t=e?t:t[Symbol.iterator]();;){var r;if(e){if(i>=t.length)break;r=t[i++]}else{if((i=t.next()).done)break;r=i.value}r(n)}},t.W=function(n){return this.U.has(n)||this.U.set(n,new Set),this.U.get(n)},n}());n.Workbox=h,n.messageSW=t,Object.defineProperty(n,"__esModule",{value:!0})}); | ||
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Unidentified License
License(Experimental) Something that seems like a license was found, but its contents could not be matched with a known license.
Found 1 instance in 1 package
391182
13
8986
2