{"version":3,"file":"workbox-expiration.prod.js","sources":["../_version.mjs","../models/CacheTimestampsModel.mjs","../CacheExpiration.mjs","../Plugin.mjs"],"sourcesContent":["try{self['workbox:expiration:4.3.1']&&_()}catch(e){}// eslint-disable-line","/*\n  Copyright 2018 Google LLC\n\n  Use of this source code is governed by an MIT-style\n  license that can be found in the LICENSE file or at\n  https://opensource.org/licenses/MIT.\n*/\n\nimport {DBWrapper} from 'workbox-core/_private/DBWrapper.mjs';\nimport {deleteDatabase} from 'workbox-core/_private/deleteDatabase.mjs';\nimport '../_version.mjs';\n\n\nconst DB_NAME = 'workbox-expiration';\nconst OBJECT_STORE_NAME = 'cache-entries';\n\nconst normalizeURL = (unNormalizedUrl) => {\n  const url = new URL(unNormalizedUrl, location);\n  url.hash = '';\n\n  return url.href;\n};\n\n\n/**\n * Returns the timestamp model.\n *\n * @private\n */\nclass CacheTimestampsModel {\n  /**\n   *\n   * @param {string} cacheName\n   *\n   * @private\n   */\n  constructor(cacheName) {\n    this._cacheName = cacheName;\n\n    this._db = new DBWrapper(DB_NAME, 1, {\n      onupgradeneeded: (event) => this._handleUpgrade(event),\n    });\n  }\n\n  /**\n   * Should perform an upgrade of indexedDB.\n   *\n   * @param {Event} event\n   *\n   * @private\n   */\n  _handleUpgrade(event) {\n    const db = event.target.result;\n\n    // TODO(philipwalton): EdgeHTML doesn't support arrays as a keyPath, so we\n    // have to use the `id` keyPath here and create our own values (a\n    // concatenation of `url + cacheName`) instead of simply using\n    // `keyPath: ['url', 'cacheName']`, which is supported in other browsers.\n    const objStore = db.createObjectStore(OBJECT_STORE_NAME, {keyPath: 'id'});\n\n    // TODO(philipwalton): once we don't have to support EdgeHTML, we can\n    // create a single index with the keyPath `['cacheName', 'timestamp']`\n    // instead of doing both these indexes.\n    objStore.createIndex('cacheName', 'cacheName', {unique: false});\n    objStore.createIndex('timestamp', 'timestamp', {unique: false});\n\n    // Previous versions of `workbox-expiration` used `this._cacheName`\n    // as the IDBDatabase name.\n    deleteDatabase(this._cacheName);\n  }\n\n  /**\n   * @param {string} url\n   * @param {number} timestamp\n   *\n   * @private\n   */\n  async setTimestamp(url, timestamp) {\n    url = normalizeURL(url);\n\n    await this._db.put(OBJECT_STORE_NAME, {\n      url,\n      timestamp,\n      cacheName: this._cacheName,\n      // Creating an ID from the URL and cache name won't be necessary once\n      // Edge switches to Chromium and all browsers we support work with\n      // array keyPaths.\n      id: this._getId(url),\n    });\n  }\n\n  /**\n   * Returns the timestamp stored for a given URL.\n   *\n   * @param {string} url\n   * @return {number}\n   *\n   * @private\n   */\n  async getTimestamp(url) {\n    const entry = await this._db.get(OBJECT_STORE_NAME, this._getId(url));\n    return entry.timestamp;\n  }\n\n  /**\n   * Iterates through all the entries in the object store (from newest to\n   * oldest) and removes entries once either `maxCount` is reached or the\n   * entry's timestamp is less than `minTimestamp`.\n   *\n   * @param {number} minTimestamp\n   * @param {number} maxCount\n   *\n   * @private\n   */\n  async expireEntries(minTimestamp, maxCount) {\n    const entriesToDelete = await this._db.transaction(\n        OBJECT_STORE_NAME, 'readwrite', (txn, done) => {\n          const store = txn.objectStore(OBJECT_STORE_NAME);\n          const entriesToDelete = [];\n          let entriesNotDeletedCount = 0;\n\n          store.index('timestamp')\n              .openCursor(null, 'prev')\n              .onsuccess = ({target}) => {\n                const cursor = target.result;\n                if (cursor) {\n                  const result = cursor.value;\n                  // TODO(philipwalton): once we can use a multi-key index, we\n                  // won't have to check `cacheName` here.\n                  if (result.cacheName === this._cacheName) {\n                    // Delete an entry if it's older than the max age or\n                    // if we already have the max number allowed.\n                    if ((minTimestamp && result.timestamp < minTimestamp) ||\n                        (maxCount && entriesNotDeletedCount >= maxCount)) {\n                      // TODO(philipwalton): we should be able to delete the\n                      // entry right here, but doing so causes an iteration\n                      // bug in Safari stable (fixed in TP). Instead we can\n                      // store the keys of the entries to delete, and then\n                      // delete the separate transactions.\n                      // https://github.com/GoogleChrome/workbox/issues/1978\n                      // cursor.delete();\n\n                      // We only need to return the URL, not the whole entry.\n                      entriesToDelete.push(cursor.value);\n                    } else {\n                      entriesNotDeletedCount++;\n                    }\n                  }\n                  cursor.continue();\n                } else {\n                  done(entriesToDelete);\n                }\n              };\n        });\n\n    // TODO(philipwalton): once the Safari bug in the following issue is fixed,\n    // we should be able to remove this loop and do the entry deletion in the\n    // cursor loop above:\n    // https://github.com/GoogleChrome/workbox/issues/1978\n    const urlsDeleted = [];\n    for (const entry of entriesToDelete) {\n      await this._db.delete(OBJECT_STORE_NAME, entry.id);\n      urlsDeleted.push(entry.url);\n    }\n\n    return urlsDeleted;\n  }\n\n  /**\n   * Takes a URL and returns an ID that will be unique in the object store.\n   *\n   * @param {string} url\n   * @return {string}\n   *\n   * @private\n   */\n  _getId(url) {\n    // Creating an ID from the URL and cache name won't be necessary once\n    // Edge switches to Chromium and all browsers we support work with\n    // array keyPaths.\n    return this._cacheName + '|' + normalizeURL(url);\n  }\n}\n\nexport {CacheTimestampsModel};\n","/*\n  Copyright 2018 Google LLC\n\n  Use of this source code is governed by an MIT-style\n  license that can be found in the LICENSE file or at\n  https://opensource.org/licenses/MIT.\n*/\n\nimport {CacheTimestampsModel} from './models/CacheTimestampsModel.mjs';\nimport {WorkboxError} from 'workbox-core/_private/WorkboxError.mjs';\nimport {assert} from 'workbox-core/_private/assert.mjs';\nimport {logger} from 'workbox-core/_private/logger.mjs';\n\nimport './_version.mjs';\n\n/**\n * The `CacheExpiration` class allows you define an expiration and / or\n * limit on the number of responses stored in a\n * [`Cache`](https://developer.mozilla.org/en-US/docs/Web/API/Cache).\n *\n * @memberof workbox.expiration\n */\nclass CacheExpiration {\n  /**\n   * To construct a new CacheExpiration instance you must provide at least\n   * one of the `config` properties.\n   *\n   * @param {string} cacheName Name of the cache to apply restrictions to.\n   * @param {Object} config\n   * @param {number} [config.maxEntries] The maximum number of entries to cache.\n   * Entries used the least will be removed as the maximum is reached.\n   * @param {number} [config.maxAgeSeconds] The maximum age of an entry before\n   * it's treated as stale and removed.\n   */\n  constructor(cacheName, config = {}) {\n    if (process.env.NODE_ENV !== 'production') {\n      assert.isType(cacheName, 'string', {\n        moduleName: 'workbox-expiration',\n        className: 'CacheExpiration',\n        funcName: 'constructor',\n        paramName: 'cacheName',\n      });\n\n      if (!(config.maxEntries || config.maxAgeSeconds)) {\n        throw new WorkboxError('max-entries-or-age-required', {\n          moduleName: 'workbox-expiration',\n          className: 'CacheExpiration',\n          funcName: 'constructor',\n        });\n      }\n\n      if (config.maxEntries) {\n        assert.isType(config.maxEntries, 'number', {\n          moduleName: 'workbox-expiration',\n          className: 'CacheExpiration',\n          funcName: 'constructor',\n          paramName: 'config.maxEntries',\n        });\n\n        // TODO: Assert is positive\n      }\n\n      if (config.maxAgeSeconds) {\n        assert.isType(config.maxAgeSeconds, 'number', {\n          moduleName: 'workbox-expiration',\n          className: 'CacheExpiration',\n          funcName: 'constructor',\n          paramName: 'config.maxAgeSeconds',\n        });\n\n        // TODO: Assert is positive\n      }\n    }\n\n    this._isRunning = false;\n    this._rerunRequested = false;\n    this._maxEntries = config.maxEntries;\n    this._maxAgeSeconds = config.maxAgeSeconds;\n    this._cacheName = cacheName;\n    this._timestampModel = new CacheTimestampsModel(cacheName);\n  }\n\n  /**\n   * Expires entries for the given cache and given criteria.\n   */\n  async expireEntries() {\n    if (this._isRunning) {\n      this._rerunRequested = true;\n      return;\n    }\n    this._isRunning = true;\n\n    const minTimestamp = this._maxAgeSeconds ?\n        Date.now() - (this._maxAgeSeconds * 1000) : undefined;\n\n    const urlsExpired = await this._timestampModel.expireEntries(\n        minTimestamp, this._maxEntries);\n\n    // Delete URLs from the cache\n    const cache = await caches.open(this._cacheName);\n    for (const url of urlsExpired) {\n      await cache.delete(url);\n    }\n\n    if (process.env.NODE_ENV !== 'production') {\n      if (urlsExpired.length > 0) {\n        logger.groupCollapsed(\n            `Expired ${urlsExpired.length} ` +\n          `${urlsExpired.length === 1 ? 'entry' : 'entries'} and removed ` +\n          `${urlsExpired.length === 1 ? 'it' : 'them'} from the ` +\n          `'${this._cacheName}' cache.`);\n        logger.log(`Expired the following ${urlsExpired.length === 1 ?\n            'URL' : 'URLs'}:`);\n        urlsExpired.forEach((url) => logger.log(`    ${url}`));\n        logger.groupEnd();\n      } else {\n        logger.debug(`Cache expiration ran and found no entries to remove.`);\n      }\n    }\n\n    this._isRunning = false;\n    if (this._rerunRequested) {\n      this._rerunRequested = false;\n      this.expireEntries();\n    }\n  }\n\n  /**\n   * Update the timestamp for the given URL. This ensures the when\n   * removing entries based on maximum entries, most recently used\n   * is accurate or when expiring, the timestamp is up-to-date.\n   *\n   * @param {string} url\n   */\n  async updateTimestamp(url) {\n    if (process.env.NODE_ENV !== 'production') {\n      assert.isType(url, 'string', {\n        moduleName: 'workbox-expiration',\n        className: 'CacheExpiration',\n        funcName: 'updateTimestamp',\n        paramName: 'url',\n      });\n    }\n\n    await this._timestampModel.setTimestamp(url, Date.now());\n  }\n\n  /**\n   * Can be used to check if a URL has expired or not before it's used.\n   *\n   * This requires a look up from IndexedDB, so can be slow.\n   *\n   * Note: This method will not remove the cached entry, call\n   * `expireEntries()` to remove indexedDB and Cache entries.\n   *\n   * @param {string} url\n   * @return {boolean}\n   */\n  async isURLExpired(url) {\n    if (process.env.NODE_ENV !== 'production') {\n      if (!this._maxAgeSeconds) {\n        throw new WorkboxError(`expired-test-without-max-age`, {\n          methodName: 'isURLExpired',\n          paramName: 'maxAgeSeconds',\n        });\n      }\n    }\n\n    const timestamp = await this._timestampModel.getTimestamp(url);\n    const expireOlderThan = Date.now() - (this._maxAgeSeconds * 1000);\n    return (timestamp < expireOlderThan);\n  }\n\n  /**\n   * Removes the IndexedDB object store used to keep track of cache expiration\n   * metadata.\n   */\n  async delete() {\n    // Make sure we don't attempt another rerun if we're called in the middle of\n    // a cache expiration.\n    this._rerunRequested = false;\n    await this._timestampModel.expireEntries(Infinity); // Expires all.\n  }\n}\n\nexport {CacheExpiration};\n","/*\n  Copyright 2018 Google LLC\n\n  Use of this source code is governed by an MIT-style\n  license that can be found in the LICENSE file or at\n  https://opensource.org/licenses/MIT.\n*/\n\nimport {assert} from 'workbox-core/_private/assert.mjs';\nimport {cacheNames} from 'workbox-core/_private/cacheNames.mjs';\nimport {getFriendlyURL} from 'workbox-core/_private/getFriendlyURL.mjs';\nimport {logger} from 'workbox-core/_private/logger.mjs';\nimport {WorkboxError} from 'workbox-core/_private/WorkboxError.mjs';\nimport {registerQuotaErrorCallback}\n  from 'workbox-core/registerQuotaErrorCallback.mjs';\n\nimport {CacheExpiration} from './CacheExpiration.mjs';\nimport './_version.mjs';\n\n/**\n * This plugin can be used in the Workbox APIs to regularly enforce a\n * limit on the age and / or the number of cached requests.\n *\n * Whenever a cached request is used or updated, this plugin will look\n * at the used Cache and remove any old or extra requests.\n *\n * When using `maxAgeSeconds`, requests may be used *once* after expiring\n * because the expiration clean up will not have occurred until *after* the\n * cached request has been used. If the request has a \"Date\" header, then\n * a light weight expiration check is performed and the request will not be\n * used immediately.\n *\n * When using `maxEntries`, the entry least-recently requested will be removed from the cache first.\n *\n * @memberof workbox.expiration\n */\nclass Plugin {\n  /**\n   * @param {Object} config\n   * @param {number} [config.maxEntries] The maximum number of entries to cache.\n   * Entries used the least will be removed as the maximum is reached.\n   * @param {number} [config.maxAgeSeconds] The maximum age of an entry before\n   * it's treated as stale and removed.\n   * @param {boolean} [config.purgeOnQuotaError] Whether to opt this cache in to\n   * automatic deletion if the available storage quota has been exceeded.\n   */\n  constructor(config = {}) {\n    if (process.env.NODE_ENV !== 'production') {\n      if (!(config.maxEntries || config.maxAgeSeconds)) {\n        throw new WorkboxError('max-entries-or-age-required', {\n          moduleName: 'workbox-expiration',\n          className: 'Plugin',\n          funcName: 'constructor',\n        });\n      }\n\n      if (config.maxEntries) {\n        assert.isType(config.maxEntries, 'number', {\n          moduleName: 'workbox-expiration',\n          className: 'Plugin',\n          funcName: 'constructor',\n          paramName: 'config.maxEntries',\n        });\n      }\n\n      if (config.maxAgeSeconds) {\n        assert.isType(config.maxAgeSeconds, 'number', {\n          moduleName: 'workbox-expiration',\n          className: 'Plugin',\n          funcName: 'constructor',\n          paramName: 'config.maxAgeSeconds',\n        });\n      }\n    }\n\n    this._config = config;\n    this._maxAgeSeconds = config.maxAgeSeconds;\n    this._cacheExpirations = new Map();\n\n    if (config.purgeOnQuotaError) {\n      registerQuotaErrorCallback(() => this.deleteCacheAndMetadata());\n    }\n  }\n\n  /**\n   * A simple helper method to return a CacheExpiration instance for a given\n   * cache name.\n   *\n   * @param {string} cacheName\n   * @return {CacheExpiration}\n   *\n   * @private\n   */\n  _getCacheExpiration(cacheName) {\n    if (cacheName === cacheNames.getRuntimeName()) {\n      throw new WorkboxError('expire-custom-caches-only');\n    }\n\n    let cacheExpiration = this._cacheExpirations.get(cacheName);\n    if (!cacheExpiration) {\n      cacheExpiration = new CacheExpiration(cacheName, this._config);\n      this._cacheExpirations.set(cacheName, cacheExpiration);\n    }\n    return cacheExpiration;\n  }\n\n  /**\n   * A \"lifecycle\" callback that will be triggered automatically by the\n   * `workbox.strategies` handlers when a `Response` is about to be returned\n   * from a [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache) to\n   * the handler. It allows the `Response` to be inspected for freshness and\n   * prevents it from being used if the `Response`'s `Date` header value is\n   * older than the configured `maxAgeSeconds`.\n   *\n   * @param {Object} options\n   * @param {string} options.cacheName Name of the cache the response is in.\n   * @param {Response} options.cachedResponse The `Response` object that's been\n   *     read from a cache and whose freshness should be checked.\n   * @return {Response} Either the `cachedResponse`, if it's\n   *     fresh, or `null` if the `Response` is older than `maxAgeSeconds`.\n   *\n   * @private\n   */\n  cachedResponseWillBeUsed({event, request, cacheName, cachedResponse}) {\n    if (!cachedResponse) {\n      return null;\n    }\n\n    let isFresh = this._isResponseDateFresh(cachedResponse);\n\n    // Expire entries to ensure that even if the expiration date has\n    // expired, it'll only be used once.\n    const cacheExpiration = this._getCacheExpiration(cacheName);\n    cacheExpiration.expireEntries();\n\n    // Update the metadata for the request URL to the current timestamp,\n    // but don't `await` it as we don't want to block the response.\n    const updateTimestampDone = cacheExpiration.updateTimestamp(request.url);\n    if (event) {\n      try {\n        event.waitUntil(updateTimestampDone);\n      } catch (error) {\n        if (process.env.NODE_ENV !== 'production') {\n          logger.warn(`Unable to ensure service worker stays alive when ` +\n            `updating cache entry for '${getFriendlyURL(event.request.url)}'.`);\n        }\n      }\n    }\n\n    return isFresh ? cachedResponse : null;\n  }\n\n  /**\n   * @param {Response} cachedResponse\n   * @return {boolean}\n   *\n   * @private\n   */\n  _isResponseDateFresh(cachedResponse) {\n    if (!this._maxAgeSeconds) {\n      // We aren't expiring by age, so return true, it's fresh\n      return true;\n    }\n\n    // Check if the 'date' header will suffice a quick expiration check.\n    // See https://github.com/GoogleChromeLabs/sw-toolbox/issues/164 for\n    // discussion.\n    const dateHeaderTimestamp = this._getDateHeaderTimestamp(cachedResponse);\n    if (dateHeaderTimestamp === null) {\n      // Unable to parse date, so assume it's fresh.\n      return true;\n    }\n\n    // If we have a valid headerTime, then our response is fresh iff the\n    // headerTime plus maxAgeSeconds is greater than the current time.\n    const now = Date.now();\n    return dateHeaderTimestamp >= now - (this._maxAgeSeconds * 1000);\n  }\n\n  /**\n   * This method will extract the data header and parse it into a useful\n   * value.\n   *\n   * @param {Response} cachedResponse\n   * @return {number}\n   *\n   * @private\n   */\n  _getDateHeaderTimestamp(cachedResponse) {\n    if (!cachedResponse.headers.has('date')) {\n      return null;\n    }\n\n    const dateHeader = cachedResponse.headers.get('date');\n    const parsedDate = new Date(dateHeader);\n    const headerTime = parsedDate.getTime();\n\n    // If the Date header was invalid for some reason, parsedDate.getTime()\n    // will return NaN.\n    if (isNaN(headerTime)) {\n      return null;\n    }\n\n    return headerTime;\n  }\n\n  /**\n   * A \"lifecycle\" callback that will be triggered automatically by the\n   * `workbox.strategies` handlers when an entry is added to a cache.\n   *\n   * @param {Object} options\n   * @param {string} options.cacheName Name of the cache that was updated.\n   * @param {string} options.request The Request for the cached entry.\n   *\n   * @private\n   */\n  async cacheDidUpdate({cacheName, request}) {\n    if (process.env.NODE_ENV !== 'production') {\n      assert.isType(cacheName, 'string', {\n        moduleName: 'workbox-expiration',\n        className: 'Plugin',\n        funcName: 'cacheDidUpdate',\n        paramName: 'cacheName',\n      });\n      assert.isInstance(request, Request, {\n        moduleName: 'workbox-expiration',\n        className: 'Plugin',\n        funcName: 'cacheDidUpdate',\n        paramName: 'request',\n      });\n    }\n\n    const cacheExpiration = this._getCacheExpiration(cacheName);\n    await cacheExpiration.updateTimestamp(request.url);\n    await cacheExpiration.expireEntries();\n  }\n\n\n  /**\n   * This is a helper method that performs two operations:\n   *\n   * - Deletes *all* the underlying Cache instances associated with this plugin\n   * instance, by calling caches.delete() on your behalf.\n   * - Deletes the metadata from IndexedDB used to keep track of expiration\n   * details for each Cache instance.\n   *\n   * When using cache expiration, calling this method is preferable to calling\n   * `caches.delete()` directly, since this will ensure that the IndexedDB\n   * metadata is also cleanly removed and open IndexedDB instances are deleted.\n   *\n   * Note that if you're *not* using cache expiration for a given cache, calling\n   * `caches.delete()` and passing in the cache's name should be sufficient.\n   * There is no Workbox-specific method needed for cleanup in that case.\n   */\n  async deleteCacheAndMetadata() {\n    // Do this one at a time instead of all at once via `Promise.all()` to\n    // reduce the chance of inconsistency if a promise rejects.\n    for (const [cacheName, cacheExpiration] of this._cacheExpirations) {\n      await caches.delete(cacheName);\n      await cacheExpiration.delete();\n    }\n\n    // Reset this._cacheExpirations to its initial state.\n    this._cacheExpirations = new Map();\n  }\n}\n\nexport {Plugin};\n"],"names":["self","_","e","DB_NAME","OBJECT_STORE_NAME","normalizeURL","unNormalizedUrl","url","URL","location","hash","href","CacheTimestampsModel","constructor","cacheName","_cacheName","_db","DBWrapper","onupgradeneeded","event","this","_handleUpgrade","objStore","target","result","createObjectStore","keyPath","createIndex","unique","deleteDatabase","timestamp","put","id","_getId","get","minTimestamp","maxCount","entriesToDelete","transaction","txn","done","store","objectStore","entriesNotDeletedCount","index","openCursor","onsuccess","cursor","value","push","continue","urlsDeleted","entry","delete","CacheExpiration","config","_isRunning","_rerunRequested","_maxEntries","maxEntries","_maxAgeSeconds","maxAgeSeconds","_timestampModel","Date","now","undefined","urlsExpired","expireEntries","cache","caches","open","setTimestamp","getTimestamp","Infinity","_config","_cacheExpirations","Map","purgeOnQuotaError","registerQuotaErrorCallback","deleteCacheAndMetadata","_getCacheExpiration","cacheNames","getRuntimeName","WorkboxError","cacheExpiration","set","cachedResponseWillBeUsed","request","cachedResponse","isFresh","_isResponseDateFresh","updateTimestampDone","updateTimestamp","waitUntil","error","dateHeaderTimestamp","_getDateHeaderTimestamp","headers","has","dateHeader","headerTime","getTime","isNaN"],"mappings":"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"}