It's fairly common to want to put restrictions on a cache in terms of how long
it should allow items to be stored in a cache or how many items should be kept
in a cache. Workbox provides this functionality through the
`workbox-expiration` plugin that allows you to limit the number of
entries in a cache and / or remove entries that have been cached for a long
period of time.

## Restrict the Number of Cache Entries

To restrict the number of entries stored in a cache, you can use the
`maxEntries` option like so:

    import {registerRoute} from 'workbox-routing';
    import {CacheFirst} from 'workbox-strategies';
    import {ExpirationPlugin} from 'workbox-expiration';

    registerRoute(
      ({request}) => request.destination === 'image',
      new CacheFirst({
        cacheName: 'image-cache',
        plugins: [
          new ExpirationPlugin({
            maxEntries: 20,
          }),
        ],
      })
    );

With this, the
[Plugin](https://developer.chrome.com/docs/workbox/reference/workbox-expiration#type-ExpirationPlugin)
will be added to this route. After a cached response is used or a new request
is added to the cache, the plugin will look at the configured cache and ensure
that the number of cached entries doesn't exceed the limit. If it does,
**the oldest entries will be removed**.

## Restrict the Age of Cached Entries

To restrict how long a request is cached for, you can define a max age in
seconds using the `maxAgeSeconds` option like so:

    import {registerRoute} from 'workbox-routing';
    import {CacheFirst} from 'workbox-strategies';
    import {ExpirationPlugin} from 'workbox-expiration';

    registerRoute(
      ({request}) => request.destination === 'image',
      new CacheFirst({
        cacheName: 'image-cache',
        plugins: [
          new ExpirationPlugin({
            maxAgeSeconds: 24 * 60 * 60,
          }),
        ],
      })
    );

The plugin will check and remove entries after each request or cache update.

> [!NOTE]
> **Note:** Because it's slow to open IndexedDB, expiration won't occur until *after* the request is used. This means that an expired request may be used once, but will be expired after that. To alleviate this, the plugin will check the `Date` header of the cached response, if one exists and the date can be parsed, it'll expire based on this as it doesn't require an IndexedDB lookup.

## Advanced Usage

If you'd like to use the expiration logic separate from any other Workbox
module, you can do so with the
[`CacheExpiration`](https://developer.chrome.com/docs/workbox/reference/workbox-expiration#type-CacheExpiration)
class.

To apply restrictions to a cache, you'd create an instance of `CacheExpiration`
for the cache you want to control like so:

    import {CacheExpiration} from 'workbox-expiration';

    const cacheName = 'my-cache';
    const expirationManager = new CacheExpiration(cacheName, {
      maxAgeSeconds: 24 * 60 * 60,
      maxEntries: 20,
    });

Whenever you update a cached entry, you need to call the `updateTimestamp()`
method so that its age is updated.

    await openCache.put(request, response);

    await expirationManager.updateTimestamp(request.url);

Then, whenever you want to expire a set of entries, you can call the
`expireEntries()` method which will enforce the `maxAgeSeconds` and
`maxEntries` configuration.

    await expirationManager.expireEntries();

<br />

## Types

### CacheExpiration

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).

#### Properties

- constructor void

  To construct a new CacheExpiration instance you must provide at least
  one of the `config` properties.


  The `constructor` function looks like:

  ```typescript
  (cacheName: string, config?: CacheExpirationConfig) => {...}
  ```

  <br />

  - cacheName string

    Name of the cache to apply restrictions to.
  - config CacheExpirationConfig optional
  - returns[CacheExpiration](https://developer.chrome.com/docs/workbox/modules/workbox-expiration#type-CacheExpiration)
- delete void

  Removes the IndexedDB object store used to keep track of cache expiration
  metadata.


  The `delete` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsPromise\<void\>
- expireEntries void

  Expires entries for the given cache and given criteria.


  The `expireEntries` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsPromise\<void\>
- isURLExpired void

  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.


  The `isURLExpired` function looks like:

  ```typescript
  (url: string) => {...}
  ```

  <br />

  - url string
  - returnsPromise\<boolean\>
- updateTimestamp void

  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.


  The `updateTimestamp` function looks like:

  ```typescript
  (url: string) => {...}
  ```

  <br />

  - url string
  - returnsPromise\<void\>

### ExpirationPlugin

This plugin can be used in a `workbox-strategy` to regularly enforce a
limit on the age and / or the number of cached requests.

It can only be used with `workbox-strategy` instances that have a
[custom `cacheName` property set](https://developer.chrome.com/web/tools/workbox/guides/configure-workbox#custom_cache_names_in_strategies).
In other words, it can't be used to expire entries in strategy that uses the
default runtime cache name.

Whenever a cached response is used or updated, this plugin will look
at the associated cache and remove any old or extra responses.

When using `maxAgeSeconds`, responses may be used *once* after expiring
because the expiration clean up will not have occurred until *after* the
cached response has been used. If the response has a "Date" header, then
a light weight expiration check is performed and the response will not be
used immediately.

When using `maxEntries`, the entry least-recently requested will be removed
from the cache first.

#### Properties

- constructor void


  The `constructor` function looks like:

  ```typescript
  (config?: ExpirationPluginOptions) => {...}
  ```

  <br />

  - config [ExpirationPluginOptions](https://developer.chrome.com/docs/workbox/modules/workbox-expiration#type-ExpirationPluginOptions) optional
  - returns[ExpirationPlugin](https://developer.chrome.com/docs/workbox/modules/workbox-expiration#type-ExpirationPlugin)
- deleteCacheAndMetadata void

  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.


  The `deleteCacheAndMetadata` function looks like:

  ```typescript
  () => {...}
  ```

  <br />

  - returnsPromise\<void\>

### ExpirationPluginOptions

#### Properties

- matchOptions CacheQueryOptions optional
- maxAgeSeconds number optional
- maxEntries number optional
- purgeOnQuotaError boolean optional

<br />