WIP - add extractor, generate snippet_data
This commit is contained in:
281
node_modules/workbox-cache-expiration/CacheExpiration.mjs
generated
vendored
Normal file
281
node_modules/workbox-cache-expiration/CacheExpiration.mjs
generated
vendored
Normal file
@ -0,0 +1,281 @@
|
||||
/*
|
||||
Copyright 2017 Google Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
https://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
import CacheTimestampsModel from './models/CacheTimestampsModel.mjs';
|
||||
import {WorkboxError} from 'workbox-core/_private/WorkboxError.mjs';
|
||||
import {assert} from 'workbox-core/_private/assert.mjs';
|
||||
import {logger} from 'workbox-core/_private/logger.mjs';
|
||||
|
||||
import './_version.mjs';
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
/**
|
||||
* 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 = {}) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
assert.isType(cacheName, 'string', {
|
||||
moduleName: 'workbox-cache-expiration',
|
||||
className: 'CacheExpiration',
|
||||
funcName: 'constructor',
|
||||
paramName: 'cacheName',
|
||||
});
|
||||
|
||||
if (!(config.maxEntries || config.maxAgeSeconds)) {
|
||||
throw new WorkboxError('max-entries-or-age-required', {
|
||||
moduleName: 'workbox-cache-expiration',
|
||||
className: 'CacheExpiration',
|
||||
funcName: 'constructor',
|
||||
});
|
||||
}
|
||||
|
||||
if (config.maxEntries) {
|
||||
assert.isType(config.maxEntries, 'number', {
|
||||
moduleName: 'workbox-cache-expiration',
|
||||
className: 'CacheExpiration',
|
||||
funcName: 'constructor',
|
||||
paramName: 'config.maxEntries',
|
||||
});
|
||||
|
||||
// TODO: Assert is positive
|
||||
}
|
||||
|
||||
if (config.maxAgeSeconds) {
|
||||
assert.isType(config.maxAgeSeconds, 'number', {
|
||||
moduleName: 'workbox-cache-expiration',
|
||||
className: 'CacheExpiration',
|
||||
funcName: 'constructor',
|
||||
paramName: 'config.maxAgeSeconds',
|
||||
});
|
||||
|
||||
// TODO: Assert is positive
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
async expireEntries() {
|
||||
if (this._isRunning) {
|
||||
this._rerunRequested = true;
|
||||
return;
|
||||
}
|
||||
this._isRunning = true;
|
||||
|
||||
const now = Date.now();
|
||||
|
||||
// First, expire old entries, if maxAgeSeconds is set.
|
||||
const oldEntries = await this._findOldEntries(now);
|
||||
|
||||
// Once that's done, check for the maximum size.
|
||||
const extraEntries = await this._findExtraEntries();
|
||||
|
||||
// Use a Set to remove any duplicates following the concatenation, then
|
||||
// convert back into an array.
|
||||
const allUrls = [...new Set(oldEntries.concat(extraEntries))];
|
||||
|
||||
await Promise.all([
|
||||
this._deleteFromCache(allUrls),
|
||||
this._deleteFromIDB(allUrls),
|
||||
]);
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
// TODO: break apart entries deleted due to expiration vs size restraints
|
||||
if (allUrls.length > 0) {
|
||||
logger.groupCollapsed(
|
||||
`Expired ${allUrls.length} ` +
|
||||
`${allUrls.length === 1 ? 'entry' : 'entries'} and removed ` +
|
||||
`${allUrls.length === 1 ? 'it' : 'them'} from the ` +
|
||||
`'${this._cacheName}' cache.`);
|
||||
logger.log(
|
||||
`Expired the following ${allUrls.length === 1 ? 'URL' : 'URLs'}:`);
|
||||
allUrls.forEach((url) => logger.log(` ${url}`));
|
||||
logger.groupEnd();
|
||||
} else {
|
||||
logger.debug(`Cache expiration ran and found no entries to remove.`);
|
||||
}
|
||||
}
|
||||
|
||||
this._isRunning = false;
|
||||
if (this._rerunRequested) {
|
||||
this._rerunRequested = false;
|
||||
this.expireEntries();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Expires entries based on the maximum age.
|
||||
*
|
||||
* @param {number} expireFromTimestamp A timestamp.
|
||||
* @return {Promise<Array<string>>} A list of the URLs that were expired.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async _findOldEntries(expireFromTimestamp) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
assert.isType(expireFromTimestamp, 'number', {
|
||||
moduleName: 'workbox-cache-expiration',
|
||||
className: 'CacheExpiration',
|
||||
funcName: '_findOldEntries',
|
||||
paramName: 'expireFromTimestamp',
|
||||
});
|
||||
}
|
||||
|
||||
if (!this._maxAgeSeconds) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const expireOlderThan = expireFromTimestamp - (this._maxAgeSeconds * 1000);
|
||||
const timestamps = await this._timestampModel.getAllTimestamps();
|
||||
const expiredUrls = [];
|
||||
timestamps.forEach((timestampDetails) => {
|
||||
if (timestampDetails.timestamp < expireOlderThan) {
|
||||
expiredUrls.push(timestampDetails.url);
|
||||
}
|
||||
});
|
||||
|
||||
return expiredUrls;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {Promise<Array>}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async _findExtraEntries() {
|
||||
const extraUrls = [];
|
||||
|
||||
if (!this._maxEntries) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const timestamps = await this._timestampModel.getAllTimestamps();
|
||||
while (timestamps.length > this._maxEntries) {
|
||||
const lastUsed = timestamps.shift();
|
||||
extraUrls.push(lastUsed.url);
|
||||
}
|
||||
|
||||
return extraUrls;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<string>} urls Array of URLs to delete from cache.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async _deleteFromCache(urls) {
|
||||
const cache = await caches.open(this._cacheName);
|
||||
for (const url of urls) {
|
||||
await cache.delete(url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array<string>} urls Array of URLs to delete from IDB
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async _deleteFromIDB(urls) {
|
||||
for (const url of urls) {
|
||||
await this._timestampModel.deleteUrl(url);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
async updateTimestamp(url) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
assert.isType(url, 'string', {
|
||||
moduleName: 'workbox-cache-expiration',
|
||||
className: 'CacheExpiration',
|
||||
funcName: 'updateTimestamp',
|
||||
paramName: 'url',
|
||||
});
|
||||
}
|
||||
|
||||
const urlObject = new URL(url, location);
|
||||
urlObject.hash = '';
|
||||
|
||||
await this._timestampModel.setTimestamp(urlObject.href, 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}
|
||||
*/
|
||||
async isURLExpired(url) {
|
||||
if (!this._maxAgeSeconds) {
|
||||
throw new WorkboxError(`expired-test-without-max-age`, {
|
||||
methodName: 'isURLExpired',
|
||||
paramName: 'maxAgeSeconds',
|
||||
});
|
||||
}
|
||||
const urlObject = new URL(url, location);
|
||||
urlObject.hash = '';
|
||||
|
||||
const timestamp = await this._timestampModel.getTimestamp(urlObject.href);
|
||||
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.delete();
|
||||
}
|
||||
}
|
||||
|
||||
export {CacheExpiration};
|
||||
Reference in New Issue
Block a user