Index: frontend/node_modules/workbox-strategies/CacheFirst.d.ts
===================================================================
--- frontend/node_modules/workbox-strategies/CacheFirst.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/CacheFirst.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,28 @@
+import { Strategy } from './Strategy.js';
+import { StrategyHandler } from './StrategyHandler.js';
+import './_version.js';
+/**
+ * An implementation of a [cache-first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-first-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.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+declare class CacheFirst extends Strategy {
+    /**
+     * @private
+     * @param {Request|string} request A request to run this strategy for.
+     * @param {workbox-strategies.StrategyHandler} handler The event that
+     *     triggered the request.
+     * @return {Promise<Response>}
+     */
+    _handle(request: Request, handler: StrategyHandler): Promise<Response>;
+}
+export { CacheFirst };
Index: frontend/node_modules/workbox-strategies/CacheFirst.js
===================================================================
--- frontend/node_modules/workbox-strategies/CacheFirst.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/CacheFirst.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,89 @@
+/*
+  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.
+*/
+import { assert } from 'workbox-core/_private/assert.js';
+import { logger } from 'workbox-core/_private/logger.js';
+import { WorkboxError } from 'workbox-core/_private/WorkboxError.js';
+import { Strategy } from './Strategy.js';
+import { messages } from './utils/messages.js';
+import './_version.js';
+/**
+ * An implementation of a [cache-first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-first-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.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+class CacheFirst extends Strategy {
+    /**
+     * @private
+     * @param {Request|string} request A request to run this strategy for.
+     * @param {workbox-strategies.StrategyHandler} handler The event that
+     *     triggered the request.
+     * @return {Promise<Response>}
+     */
+    async _handle(request, handler) {
+        const logs = [];
+        if (process.env.NODE_ENV !== 'production') {
+            assert.isInstance(request, Request, {
+                moduleName: 'workbox-strategies',
+                className: this.constructor.name,
+                funcName: 'makeRequest',
+                paramName: 'request',
+            });
+        }
+        let response = await handler.cacheMatch(request);
+        let error = undefined;
+        if (!response) {
+            if (process.env.NODE_ENV !== 'production') {
+                logs.push(`No response found in the '${this.cacheName}' cache. ` +
+                    `Will respond with a network request.`);
+            }
+            try {
+                response = await handler.fetchAndCachePut(request);
+            }
+            catch (err) {
+                if (err instanceof Error) {
+                    error = err;
+                }
+            }
+            if (process.env.NODE_ENV !== 'production') {
+                if (response) {
+                    logs.push(`Got response from network.`);
+                }
+                else {
+                    logs.push(`Unable to get a response from the network.`);
+                }
+            }
+        }
+        else {
+            if (process.env.NODE_ENV !== 'production') {
+                logs.push(`Found a cached response in the '${this.cacheName}' cache.`);
+            }
+        }
+        if (process.env.NODE_ENV !== 'production') {
+            logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
+            for (const log of logs) {
+                logger.log(log);
+            }
+            messages.printFinalResponse(response);
+            logger.groupEnd();
+        }
+        if (!response) {
+            throw new WorkboxError('no-response', { url: request.url, error });
+        }
+        return response;
+    }
+}
+export { CacheFirst };
Index: frontend/node_modules/workbox-strategies/CacheFirst.mjs
===================================================================
--- frontend/node_modules/workbox-strategies/CacheFirst.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/CacheFirst.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+export * from './CacheFirst.js';
Index: frontend/node_modules/workbox-strategies/CacheOnly.d.ts
===================================================================
--- frontend/node_modules/workbox-strategies/CacheOnly.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/CacheOnly.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,26 @@
+import { Strategy } from './Strategy.js';
+import { StrategyHandler } from './StrategyHandler.js';
+import './_version.js';
+/**
+ * An implementation of a [cache-only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-only)
+ * request strategy.
+ *
+ * This class is useful if you want to take advantage of any
+ * [Workbox plugins](https://developer.chrome.com/docs/workbox/using-plugins/).
+ *
+ * If there is no cache match, this will throw a `WorkboxError` exception.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+declare class CacheOnly extends Strategy {
+    /**
+     * @private
+     * @param {Request|string} request A request to run this strategy for.
+     * @param {workbox-strategies.StrategyHandler} handler The event that
+     *     triggered the request.
+     * @return {Promise<Response>}
+     */
+    _handle(request: Request, handler: StrategyHandler): Promise<Response>;
+}
+export { CacheOnly };
Index: frontend/node_modules/workbox-strategies/CacheOnly.js
===================================================================
--- frontend/node_modules/workbox-strategies/CacheOnly.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/CacheOnly.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,61 @@
+/*
+  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.
+*/
+import { assert } from 'workbox-core/_private/assert.js';
+import { logger } from 'workbox-core/_private/logger.js';
+import { WorkboxError } from 'workbox-core/_private/WorkboxError.js';
+import { Strategy } from './Strategy.js';
+import { messages } from './utils/messages.js';
+import './_version.js';
+/**
+ * An implementation of a [cache-only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-only)
+ * request strategy.
+ *
+ * This class is useful if you want to take advantage of any
+ * [Workbox plugins](https://developer.chrome.com/docs/workbox/using-plugins/).
+ *
+ * If there is no cache match, this will throw a `WorkboxError` exception.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+class CacheOnly extends Strategy {
+    /**
+     * @private
+     * @param {Request|string} request A request to run this strategy for.
+     * @param {workbox-strategies.StrategyHandler} handler The event that
+     *     triggered the request.
+     * @return {Promise<Response>}
+     */
+    async _handle(request, handler) {
+        if (process.env.NODE_ENV !== 'production') {
+            assert.isInstance(request, Request, {
+                moduleName: 'workbox-strategies',
+                className: this.constructor.name,
+                funcName: 'makeRequest',
+                paramName: 'request',
+            });
+        }
+        const response = await handler.cacheMatch(request);
+        if (process.env.NODE_ENV !== 'production') {
+            logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
+            if (response) {
+                logger.log(`Found a cached response in the '${this.cacheName}' ` + `cache.`);
+                messages.printFinalResponse(response);
+            }
+            else {
+                logger.log(`No response found in the '${this.cacheName}' cache.`);
+            }
+            logger.groupEnd();
+        }
+        if (!response) {
+            throw new WorkboxError('no-response', { url: request.url });
+        }
+        return response;
+    }
+}
+export { CacheOnly };
Index: frontend/node_modules/workbox-strategies/CacheOnly.mjs
===================================================================
--- frontend/node_modules/workbox-strategies/CacheOnly.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/CacheOnly.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+export * from './CacheOnly.js';
Index: frontend/node_modules/workbox-strategies/LICENSE
===================================================================
--- frontend/node_modules/workbox-strategies/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/LICENSE	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,19 @@
+Copyright 2018 Google LLC
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
Index: frontend/node_modules/workbox-strategies/NetworkFirst.d.ts
===================================================================
--- frontend/node_modules/workbox-strategies/NetworkFirst.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/NetworkFirst.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,80 @@
+import { Strategy, StrategyOptions } from './Strategy.js';
+import { StrategyHandler } from './StrategyHandler.js';
+import './_version.js';
+export interface NetworkFirstOptions extends StrategyOptions {
+    networkTimeoutSeconds?: number;
+}
+/**
+ * An implementation of a
+ * [network first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-first-falling-back-to-cache)
+ * request strategy.
+ *
+ * By default, this strategy will cache responses with a 200 status code as
+ * well as [opaque responses](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
+ * Opaque responses are are cross-origin requests where the response doesn't
+ * support [CORS](https://enable-cors.org/).
+ *
+ * If the network request fails, and there is no cache match, this will throw
+ * a `WorkboxError` exception.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+declare class NetworkFirst extends Strategy {
+    private readonly _networkTimeoutSeconds;
+    /**
+     * @param {Object} [options]
+     * @param {string} [options.cacheName] Cache name to store and retrieve
+     * requests. Defaults to cache names provided by
+     * {@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 [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
+     * `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?: NetworkFirstOptions);
+    /**
+     * @private
+     * @param {Request|string} request A request to run this strategy for.
+     * @param {workbox-strategies.StrategyHandler} handler The event that
+     *     triggered the request.
+     * @return {Promise<Response>}
+     */
+    _handle(request: Request, handler: StrategyHandler): Promise<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
+     */
+    private _getTimeoutPromise;
+    /**
+     * @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
+     */
+    _getNetworkPromise({ timeoutId, request, logs, handler, }: {
+        request: Request;
+        logs: any[];
+        timeoutId?: number;
+        handler: StrategyHandler;
+    }): Promise<Response | undefined>;
+}
+export { NetworkFirst };
Index: frontend/node_modules/workbox-strategies/NetworkFirst.js
===================================================================
--- frontend/node_modules/workbox-strategies/NetworkFirst.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/NetworkFirst.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,197 @@
+/*
+  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.
+*/
+import { assert } from 'workbox-core/_private/assert.js';
+import { logger } from 'workbox-core/_private/logger.js';
+import { WorkboxError } from 'workbox-core/_private/WorkboxError.js';
+import { cacheOkAndOpaquePlugin } from './plugins/cacheOkAndOpaquePlugin.js';
+import { Strategy } from './Strategy.js';
+import { messages } from './utils/messages.js';
+import './_version.js';
+/**
+ * An implementation of a
+ * [network first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-first-falling-back-to-cache)
+ * request strategy.
+ *
+ * By default, this strategy will cache responses with a 200 status code as
+ * well as [opaque responses](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
+ * Opaque responses are are cross-origin requests where the response doesn't
+ * support [CORS](https://enable-cors.org/).
+ *
+ * If the network request fails, and there is no cache match, this will throw
+ * a `WorkboxError` exception.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+class NetworkFirst extends Strategy {
+    /**
+     * @param {Object} [options]
+     * @param {string} [options.cacheName] Cache name to store and retrieve
+     * requests. Defaults to cache names provided by
+     * {@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 [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
+     * `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 = {}) {
+        super(options);
+        // If this instance contains no plugins with a 'cacheWillUpdate' callback,
+        // prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.
+        if (!this.plugins.some((p) => 'cacheWillUpdate' in p)) {
+            this.plugins.unshift(cacheOkAndOpaquePlugin);
+        }
+        this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
+        if (process.env.NODE_ENV !== 'production') {
+            if (this._networkTimeoutSeconds) {
+                assert.isType(this._networkTimeoutSeconds, 'number', {
+                    moduleName: 'workbox-strategies',
+                    className: this.constructor.name,
+                    funcName: 'constructor',
+                    paramName: 'networkTimeoutSeconds',
+                });
+            }
+        }
+    }
+    /**
+     * @private
+     * @param {Request|string} request A request to run this strategy for.
+     * @param {workbox-strategies.StrategyHandler} handler The event that
+     *     triggered the request.
+     * @return {Promise<Response>}
+     */
+    async _handle(request, handler) {
+        const logs = [];
+        if (process.env.NODE_ENV !== 'production') {
+            assert.isInstance(request, Request, {
+                moduleName: 'workbox-strategies',
+                className: this.constructor.name,
+                funcName: 'handle',
+                paramName: 'makeRequest',
+            });
+        }
+        const promises = [];
+        let timeoutId;
+        if (this._networkTimeoutSeconds) {
+            const { id, promise } = this._getTimeoutPromise({ request, logs, handler });
+            timeoutId = id;
+            promises.push(promise);
+        }
+        const networkPromise = this._getNetworkPromise({
+            timeoutId,
+            request,
+            logs,
+            handler,
+        });
+        promises.push(networkPromise);
+        const response = await handler.waitUntil((async () => {
+            // Promise.race() will resolve as soon as the first promise resolves.
+            return ((await handler.waitUntil(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".
+                (await networkPromise));
+        })());
+        if (process.env.NODE_ENV !== 'production') {
+            logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
+            for (const log of logs) {
+                logger.log(log);
+            }
+            messages.printFinalResponse(response);
+            logger.groupEnd();
+        }
+        if (!response) {
+            throw new 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, handler, }) {
+        let timeoutId;
+        const timeoutPromise = new Promise((resolve) => {
+            const onNetworkTimeout = async () => {
+                if (process.env.NODE_ENV !== 'production') {
+                    logs.push(`Timing out the network response at ` +
+                        `${this._networkTimeoutSeconds} seconds.`);
+                }
+                resolve(await handler.cacheMatch(request));
+            };
+            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, handler, }) {
+        let error;
+        let response;
+        try {
+            response = await handler.fetchAndCachePut(request);
+        }
+        catch (fetchError) {
+            if (fetchError instanceof Error) {
+                error = fetchError;
+            }
+        }
+        if (timeoutId) {
+            clearTimeout(timeoutId);
+        }
+        if (process.env.NODE_ENV !== 'production') {
+            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 (error || !response) {
+            response = await handler.cacheMatch(request);
+            if (process.env.NODE_ENV !== 'production') {
+                if (response) {
+                    logs.push(`Found a cached response in the '${this.cacheName}'` + ` cache.`);
+                }
+                else {
+                    logs.push(`No response found in the '${this.cacheName}' cache.`);
+                }
+            }
+        }
+        return response;
+    }
+}
+export { NetworkFirst };
Index: frontend/node_modules/workbox-strategies/NetworkFirst.mjs
===================================================================
--- frontend/node_modules/workbox-strategies/NetworkFirst.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/NetworkFirst.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+export * from './NetworkFirst.js';
Index: frontend/node_modules/workbox-strategies/NetworkOnly.d.ts
===================================================================
--- frontend/node_modules/workbox-strategies/NetworkOnly.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/NetworkOnly.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,43 @@
+import { Strategy, StrategyOptions } from './Strategy.js';
+import { StrategyHandler } from './StrategyHandler.js';
+import './_version.js';
+interface NetworkOnlyOptions extends Omit<StrategyOptions, 'cacheName' | 'matchOptions'> {
+    networkTimeoutSeconds?: number;
+}
+/**
+ * An implementation of a
+ * [network-only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-only)
+ * request strategy.
+ *
+ * This class is useful if you want to take advantage of any
+ * [Workbox plugins](https://developer.chrome.com/docs/workbox/using-plugins/).
+ *
+ * If the network request fails, this will throw a `WorkboxError` exception.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+declare class NetworkOnly extends Strategy {
+    private readonly _networkTimeoutSeconds;
+    /**
+     * @param {Object} [options]
+     * @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 [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
+     * `fetch()` requests made by this strategy.
+     * @param {number} [options.networkTimeoutSeconds] If set, any network requests
+     * that fail to respond within the timeout will result in a network error.
+     */
+    constructor(options?: NetworkOnlyOptions);
+    /**
+     * @private
+     * @param {Request|string} request A request to run this strategy for.
+     * @param {workbox-strategies.StrategyHandler} handler The event that
+     *     triggered the request.
+     * @return {Promise<Response>}
+     */
+    _handle(request: Request, handler: StrategyHandler): Promise<Response>;
+}
+export { NetworkOnly, NetworkOnlyOptions };
Index: frontend/node_modules/workbox-strategies/NetworkOnly.js
===================================================================
--- frontend/node_modules/workbox-strategies/NetworkOnly.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/NetworkOnly.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,98 @@
+/*
+  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.
+*/
+import { assert } from 'workbox-core/_private/assert.js';
+import { logger } from 'workbox-core/_private/logger.js';
+import { timeout } from 'workbox-core/_private/timeout.js';
+import { WorkboxError } from 'workbox-core/_private/WorkboxError.js';
+import { Strategy } from './Strategy.js';
+import { messages } from './utils/messages.js';
+import './_version.js';
+/**
+ * An implementation of a
+ * [network-only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-only)
+ * request strategy.
+ *
+ * This class is useful if you want to take advantage of any
+ * [Workbox plugins](https://developer.chrome.com/docs/workbox/using-plugins/).
+ *
+ * If the network request fails, this will throw a `WorkboxError` exception.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+class NetworkOnly extends Strategy {
+    /**
+     * @param {Object} [options]
+     * @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 [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
+     * `fetch()` requests made by this strategy.
+     * @param {number} [options.networkTimeoutSeconds] If set, any network requests
+     * that fail to respond within the timeout will result in a network error.
+     */
+    constructor(options = {}) {
+        super(options);
+        this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
+    }
+    /**
+     * @private
+     * @param {Request|string} request A request to run this strategy for.
+     * @param {workbox-strategies.StrategyHandler} handler The event that
+     *     triggered the request.
+     * @return {Promise<Response>}
+     */
+    async _handle(request, handler) {
+        if (process.env.NODE_ENV !== 'production') {
+            assert.isInstance(request, Request, {
+                moduleName: 'workbox-strategies',
+                className: this.constructor.name,
+                funcName: '_handle',
+                paramName: 'request',
+            });
+        }
+        let error = undefined;
+        let response;
+        try {
+            const promises = [
+                handler.fetch(request),
+            ];
+            if (this._networkTimeoutSeconds) {
+                const timeoutPromise = timeout(this._networkTimeoutSeconds * 1000);
+                promises.push(timeoutPromise);
+            }
+            response = await Promise.race(promises);
+            if (!response) {
+                throw new Error(`Timed out the network response after ` +
+                    `${this._networkTimeoutSeconds} seconds.`);
+            }
+        }
+        catch (err) {
+            if (err instanceof Error) {
+                error = err;
+            }
+        }
+        if (process.env.NODE_ENV !== 'production') {
+            logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
+            if (response) {
+                logger.log(`Got response from network.`);
+            }
+            else {
+                logger.log(`Unable to get a response from the network.`);
+            }
+            messages.printFinalResponse(response);
+            logger.groupEnd();
+        }
+        if (!response) {
+            throw new WorkboxError('no-response', { url: request.url, error });
+        }
+        return response;
+    }
+}
+export { NetworkOnly };
Index: frontend/node_modules/workbox-strategies/NetworkOnly.mjs
===================================================================
--- frontend/node_modules/workbox-strategies/NetworkOnly.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/NetworkOnly.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+export * from './NetworkOnly.js';
Index: frontend/node_modules/workbox-strategies/README.md
===================================================================
--- frontend/node_modules/workbox-strategies/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/README.md	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+This module's documentation can be found at https://developers.google.com/web/tools/workbox/modules/workbox-strategies
Index: frontend/node_modules/workbox-strategies/StaleWhileRevalidate.d.ts
===================================================================
--- frontend/node_modules/workbox-strategies/StaleWhileRevalidate.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/StaleWhileRevalidate.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,49 @@
+import { Strategy, StrategyOptions } from './Strategy.js';
+import { StrategyHandler } from './StrategyHandler.js';
+import './_version.js';
+/**
+ * An implementation of a
+ * [stale-while-revalidate](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#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](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
+ * Opaque responses are cross-origin requests where the response doesn't
+ * support [CORS](https://enable-cors.org/).
+ *
+ * If the network request fails, and there is no cache match, this will throw
+ * a `WorkboxError` exception.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+declare class StaleWhileRevalidate extends Strategy {
+    /**
+     * @param {Object} [options]
+     * @param {string} [options.cacheName] Cache name to store and retrieve
+     * requests. Defaults to cache names provided by
+     * {@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 [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
+     * `fetch()` requests made by this strategy.
+     * @param {Object} [options.matchOptions] [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions)
+     */
+    constructor(options?: StrategyOptions);
+    /**
+     * @private
+     * @param {Request|string} request A request to run this strategy for.
+     * @param {workbox-strategies.StrategyHandler} handler The event that
+     *     triggered the request.
+     * @return {Promise<Response>}
+     */
+    _handle(request: Request, handler: StrategyHandler): Promise<Response>;
+}
+export { StaleWhileRevalidate };
Index: frontend/node_modules/workbox-strategies/StaleWhileRevalidate.js
===================================================================
--- frontend/node_modules/workbox-strategies/StaleWhileRevalidate.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/StaleWhileRevalidate.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,118 @@
+/*
+  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.
+*/
+import { assert } from 'workbox-core/_private/assert.js';
+import { logger } from 'workbox-core/_private/logger.js';
+import { WorkboxError } from 'workbox-core/_private/WorkboxError.js';
+import { cacheOkAndOpaquePlugin } from './plugins/cacheOkAndOpaquePlugin.js';
+import { Strategy } from './Strategy.js';
+import { messages } from './utils/messages.js';
+import './_version.js';
+/**
+ * An implementation of a
+ * [stale-while-revalidate](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#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](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
+ * Opaque responses are cross-origin requests where the response doesn't
+ * support [CORS](https://enable-cors.org/).
+ *
+ * If the network request fails, and there is no cache match, this will throw
+ * a `WorkboxError` exception.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+class StaleWhileRevalidate extends Strategy {
+    /**
+     * @param {Object} [options]
+     * @param {string} [options.cacheName] Cache name to store and retrieve
+     * requests. Defaults to cache names provided by
+     * {@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 [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
+     * `fetch()` requests made by this strategy.
+     * @param {Object} [options.matchOptions] [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions)
+     */
+    constructor(options = {}) {
+        super(options);
+        // If this instance contains no plugins with a 'cacheWillUpdate' callback,
+        // prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.
+        if (!this.plugins.some((p) => 'cacheWillUpdate' in p)) {
+            this.plugins.unshift(cacheOkAndOpaquePlugin);
+        }
+    }
+    /**
+     * @private
+     * @param {Request|string} request A request to run this strategy for.
+     * @param {workbox-strategies.StrategyHandler} handler The event that
+     *     triggered the request.
+     * @return {Promise<Response>}
+     */
+    async _handle(request, handler) {
+        const logs = [];
+        if (process.env.NODE_ENV !== 'production') {
+            assert.isInstance(request, Request, {
+                moduleName: 'workbox-strategies',
+                className: this.constructor.name,
+                funcName: 'handle',
+                paramName: 'request',
+            });
+        }
+        const fetchAndCachePromise = handler.fetchAndCachePut(request).catch(() => {
+            // Swallow this error because a 'no-response' error will be thrown in
+            // main handler return flow. This will be in the `waitUntil()` flow.
+        });
+        void handler.waitUntil(fetchAndCachePromise);
+        let response = await handler.cacheMatch(request);
+        let error;
+        if (response) {
+            if (process.env.NODE_ENV !== 'production') {
+                logs.push(`Found a cached response in the '${this.cacheName}'` +
+                    ` cache. Will update with the network response in the background.`);
+            }
+        }
+        else {
+            if (process.env.NODE_ENV !== 'production') {
+                logs.push(`No response found in the '${this.cacheName}' cache. ` +
+                    `Will wait for the network response.`);
+            }
+            try {
+                // NOTE(philipwalton): Really annoying that we have to type cast here.
+                // https://github.com/microsoft/TypeScript/issues/20006
+                response = (await fetchAndCachePromise);
+            }
+            catch (err) {
+                if (err instanceof Error) {
+                    error = err;
+                }
+            }
+        }
+        if (process.env.NODE_ENV !== 'production') {
+            logger.groupCollapsed(messages.strategyStart(this.constructor.name, request));
+            for (const log of logs) {
+                logger.log(log);
+            }
+            messages.printFinalResponse(response);
+            logger.groupEnd();
+        }
+        if (!response) {
+            throw new WorkboxError('no-response', { url: request.url, error });
+        }
+        return response;
+    }
+}
+export { StaleWhileRevalidate };
Index: frontend/node_modules/workbox-strategies/StaleWhileRevalidate.mjs
===================================================================
--- frontend/node_modules/workbox-strategies/StaleWhileRevalidate.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/StaleWhileRevalidate.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+export * from './StaleWhileRevalidate.js';
Index: frontend/node_modules/workbox-strategies/Strategy.d.ts
===================================================================
--- frontend/node_modules/workbox-strategies/Strategy.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/Strategy.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,107 @@
+import { HandlerCallbackOptions, RouteHandlerObject, WorkboxPlugin } from 'workbox-core/types.js';
+import { StrategyHandler } from './StrategyHandler.js';
+import './_version.js';
+export interface StrategyOptions {
+    cacheName?: string;
+    plugins?: WorkboxPlugin[];
+    fetchOptions?: RequestInit;
+    matchOptions?: CacheQueryOptions;
+}
+/**
+ * An abstract base class that all other strategy classes must extend from:
+ *
+ * @memberof workbox-strategies
+ */
+declare abstract class Strategy implements RouteHandlerObject {
+    cacheName: string;
+    plugins: WorkboxPlugin[];
+    fetchOptions?: RequestInit;
+    matchOptions?: CacheQueryOptions;
+    protected abstract _handle(request: Request, handler: StrategyHandler): Promise<Response | undefined>;
+    /**
+     * Creates a new instance of the strategy and sets all documented option
+     * properties as public instance properties.
+     *
+     * Note: if a custom strategy class extends the base Strategy class and does
+     * not need more than these properties, it does not need to define its own
+     * constructor.
+     *
+     * @param {Object} [options]
+     * @param {string} [options.cacheName] Cache name to store and retrieve
+     * requests. Defaults to the cache names provided by
+     * {@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 [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
+     * `fetch()` requests made by this strategy.
+     * @param {Object} [options.matchOptions] The
+     * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}
+     * for any `cache.match()` or `cache.put()` calls made by this strategy.
+     */
+    constructor(options?: StrategyOptions);
+    /**
+     * Perform a request strategy and returns a `Promise` that will resolve with
+     * a `Response`, invoking all relevant plugin callbacks.
+     *
+     * When a strategy instance is registered with a Workbox
+     * {@link workbox-routing.Route}, this method is automatically
+     * called when the route matches.
+     *
+     * Alternatively, this method can be used in a standalone `FetchEvent`
+     * listener by passing it to `event.respondWith()`.
+     *
+     * @param {FetchEvent|Object} options A `FetchEvent` or an object with the
+     *     properties listed below.
+     * @param {Request|string} options.request A request to run this strategy for.
+     * @param {ExtendableEvent} options.event The event associated with the
+     *     request.
+     * @param {URL} [options.url]
+     * @param {*} [options.params]
+     */
+    handle(options: FetchEvent | HandlerCallbackOptions): Promise<Response>;
+    /**
+     * Similar to {@link workbox-strategies.Strategy~handle}, but
+     * instead of just returning a `Promise` that resolves to a `Response` it
+     * it will return an tuple of `[response, done]` promises, where the former
+     * (`response`) is equivalent to what `handle()` returns, and the latter is a
+     * Promise that will resolve once any promises that were added to
+     * `event.waitUntil()` as part of performing the strategy have completed.
+     *
+     * You can await the `done` promise to ensure any extra work performed by
+     * the strategy (usually caching responses) completes successfully.
+     *
+     * @param {FetchEvent|Object} options A `FetchEvent` or an object with the
+     *     properties listed below.
+     * @param {Request|string} options.request A request to run this strategy for.
+     * @param {ExtendableEvent} options.event The event associated with the
+     *     request.
+     * @param {URL} [options.url]
+     * @param {*} [options.params]
+     * @return {Array<Promise>} A tuple of [response, done]
+     *     promises that can be used to determine when the response resolves as
+     *     well as when the handler has completed all its work.
+     */
+    handleAll(options: FetchEvent | HandlerCallbackOptions): [Promise<Response>, Promise<void>];
+    _getResponse(handler: StrategyHandler, request: Request, event: ExtendableEvent): Promise<Response>;
+    _awaitComplete(responseDone: Promise<Response>, handler: StrategyHandler, request: Request, event: ExtendableEvent): Promise<void>;
+}
+export { Strategy };
+/**
+ * Classes extending the `Strategy` based class should implement this method,
+ * and leverage the {@link workbox-strategies.StrategyHandler}
+ * arg to perform all fetching and cache logic, which will ensure all relevant
+ * cache, cache options, fetch options and plugins are used (per the current
+ * strategy instance).
+ *
+ * @name _handle
+ * @instance
+ * @abstract
+ * @function
+ * @param {Request} request
+ * @param {workbox-strategies.StrategyHandler} handler
+ * @return {Promise<Response>}
+ *
+ * @memberof workbox-strategies.Strategy
+ */
Index: frontend/node_modules/workbox-strategies/Strategy.js
===================================================================
--- frontend/node_modules/workbox-strategies/Strategy.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/Strategy.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,228 @@
+/*
+  Copyright 2020 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.
+*/
+import { cacheNames } from 'workbox-core/_private/cacheNames.js';
+import { WorkboxError } from 'workbox-core/_private/WorkboxError.js';
+import { logger } from 'workbox-core/_private/logger.js';
+import { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';
+import { StrategyHandler } from './StrategyHandler.js';
+import './_version.js';
+/**
+ * An abstract base class that all other strategy classes must extend from:
+ *
+ * @memberof workbox-strategies
+ */
+class Strategy {
+    /**
+     * Creates a new instance of the strategy and sets all documented option
+     * properties as public instance properties.
+     *
+     * Note: if a custom strategy class extends the base Strategy class and does
+     * not need more than these properties, it does not need to define its own
+     * constructor.
+     *
+     * @param {Object} [options]
+     * @param {string} [options.cacheName] Cache name to store and retrieve
+     * requests. Defaults to the cache names provided by
+     * {@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 [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
+     * `fetch()` requests made by this strategy.
+     * @param {Object} [options.matchOptions] The
+     * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}
+     * for any `cache.match()` or `cache.put()` calls made by this strategy.
+     */
+    constructor(options = {}) {
+        /**
+         * Cache name to store and retrieve
+         * requests. Defaults to the cache names provided by
+         * {@link workbox-core.cacheNames}.
+         *
+         * @type {string}
+         */
+        this.cacheName = cacheNames.getRuntimeName(options.cacheName);
+        /**
+         * The list
+         * [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
+         * used by this strategy.
+         *
+         * @type {Array<Object>}
+         */
+        this.plugins = options.plugins || [];
+        /**
+         * Values passed along to the
+         * [`init`]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters}
+         * of all fetch() requests made by this strategy.
+         *
+         * @type {Object}
+         */
+        this.fetchOptions = options.fetchOptions;
+        /**
+         * The
+         * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}
+         * for any `cache.match()` or `cache.put()` calls made by this strategy.
+         *
+         * @type {Object}
+         */
+        this.matchOptions = options.matchOptions;
+    }
+    /**
+     * Perform a request strategy and returns a `Promise` that will resolve with
+     * a `Response`, invoking all relevant plugin callbacks.
+     *
+     * When a strategy instance is registered with a Workbox
+     * {@link workbox-routing.Route}, this method is automatically
+     * called when the route matches.
+     *
+     * Alternatively, this method can be used in a standalone `FetchEvent`
+     * listener by passing it to `event.respondWith()`.
+     *
+     * @param {FetchEvent|Object} options A `FetchEvent` or an object with the
+     *     properties listed below.
+     * @param {Request|string} options.request A request to run this strategy for.
+     * @param {ExtendableEvent} options.event The event associated with the
+     *     request.
+     * @param {URL} [options.url]
+     * @param {*} [options.params]
+     */
+    handle(options) {
+        const [responseDone] = this.handleAll(options);
+        return responseDone;
+    }
+    /**
+     * Similar to {@link workbox-strategies.Strategy~handle}, but
+     * instead of just returning a `Promise` that resolves to a `Response` it
+     * it will return an tuple of `[response, done]` promises, where the former
+     * (`response`) is equivalent to what `handle()` returns, and the latter is a
+     * Promise that will resolve once any promises that were added to
+     * `event.waitUntil()` as part of performing the strategy have completed.
+     *
+     * You can await the `done` promise to ensure any extra work performed by
+     * the strategy (usually caching responses) completes successfully.
+     *
+     * @param {FetchEvent|Object} options A `FetchEvent` or an object with the
+     *     properties listed below.
+     * @param {Request|string} options.request A request to run this strategy for.
+     * @param {ExtendableEvent} options.event The event associated with the
+     *     request.
+     * @param {URL} [options.url]
+     * @param {*} [options.params]
+     * @return {Array<Promise>} A tuple of [response, done]
+     *     promises that can be used to determine when the response resolves as
+     *     well as when the handler has completed all its work.
+     */
+    handleAll(options) {
+        // Allow for flexible options to be passed.
+        if (options instanceof FetchEvent) {
+            options = {
+                event: options,
+                request: options.request,
+            };
+        }
+        const event = options.event;
+        const request = typeof options.request === 'string'
+            ? new Request(options.request)
+            : options.request;
+        const params = 'params' in options ? options.params : undefined;
+        const handler = new StrategyHandler(this, { event, request, params });
+        const responseDone = this._getResponse(handler, request, event);
+        const handlerDone = this._awaitComplete(responseDone, handler, request, event);
+        // Return an array of promises, suitable for use with Promise.all().
+        return [responseDone, handlerDone];
+    }
+    async _getResponse(handler, request, event) {
+        await handler.runCallbacks('handlerWillStart', { event, request });
+        let response = undefined;
+        try {
+            response = await this._handle(request, handler);
+            // The "official" Strategy subclasses all throw this error automatically,
+            // but in case a third-party Strategy doesn't, ensure that we have a
+            // consistent failure when there's no response or an error response.
+            if (!response || response.type === 'error') {
+                throw new WorkboxError('no-response', { url: request.url });
+            }
+        }
+        catch (error) {
+            if (error instanceof Error) {
+                for (const callback of handler.iterateCallbacks('handlerDidError')) {
+                    response = await callback({ error, event, request });
+                    if (response) {
+                        break;
+                    }
+                }
+            }
+            if (!response) {
+                throw error;
+            }
+            else if (process.env.NODE_ENV !== 'production') {
+                logger.log(`While responding to '${getFriendlyURL(request.url)}', ` +
+                    `an ${error instanceof Error ? error.toString() : ''} error occurred. Using a fallback response provided by ` +
+                    `a handlerDidError plugin.`);
+            }
+        }
+        for (const callback of handler.iterateCallbacks('handlerWillRespond')) {
+            response = await callback({ event, request, response });
+        }
+        return response;
+    }
+    async _awaitComplete(responseDone, handler, request, event) {
+        let response;
+        let error;
+        try {
+            response = await responseDone;
+        }
+        catch (error) {
+            // Ignore errors, as response errors should be caught via the `response`
+            // promise above. The `done` promise will only throw for errors in
+            // promises passed to `handler.waitUntil()`.
+        }
+        try {
+            await handler.runCallbacks('handlerDidRespond', {
+                event,
+                request,
+                response,
+            });
+            await handler.doneWaiting();
+        }
+        catch (waitUntilError) {
+            if (waitUntilError instanceof Error) {
+                error = waitUntilError;
+            }
+        }
+        await handler.runCallbacks('handlerDidComplete', {
+            event,
+            request,
+            response,
+            error: error,
+        });
+        handler.destroy();
+        if (error) {
+            throw error;
+        }
+    }
+}
+export { Strategy };
+/**
+ * Classes extending the `Strategy` based class should implement this method,
+ * and leverage the {@link workbox-strategies.StrategyHandler}
+ * arg to perform all fetching and cache logic, which will ensure all relevant
+ * cache, cache options, fetch options and plugins are used (per the current
+ * strategy instance).
+ *
+ * @name _handle
+ * @instance
+ * @abstract
+ * @function
+ * @param {Request} request
+ * @param {workbox-strategies.StrategyHandler} handler
+ * @return {Promise<Response>}
+ *
+ * @memberof workbox-strategies.Strategy
+ */
Index: frontend/node_modules/workbox-strategies/Strategy.mjs
===================================================================
--- frontend/node_modules/workbox-strategies/Strategy.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/Strategy.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+export * from './Strategy.js';
Index: frontend/node_modules/workbox-strategies/StrategyHandler.d.ts
===================================================================
--- frontend/node_modules/workbox-strategies/StrategyHandler.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/StrategyHandler.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,184 @@
+import { HandlerCallbackOptions, WorkboxPlugin, WorkboxPluginCallbackParam } from 'workbox-core/types.js';
+import { Strategy } from './Strategy.js';
+import './_version.js';
+/**
+ * A class created every time a Strategy instance instance calls
+ * {@link workbox-strategies.Strategy~handle} or
+ * {@link workbox-strategies.Strategy~handleAll} that wraps all fetch and
+ * cache actions around plugin callbacks and keeps track of when the strategy
+ * is "done" (i.e. all added `event.waitUntil()` promises have resolved).
+ *
+ * @memberof workbox-strategies
+ */
+declare class StrategyHandler {
+    request: Request;
+    url?: URL;
+    event: ExtendableEvent;
+    params?: any;
+    private _cacheKeys;
+    private readonly _strategy;
+    private readonly _extendLifetimePromises;
+    private readonly _handlerDeferred;
+    private readonly _plugins;
+    private readonly _pluginStateMap;
+    /**
+     * Creates a new instance associated with the passed strategy and event
+     * that's handling the request.
+     *
+     * The constructor also initializes the state that will be passed to each of
+     * the plugins handling this request.
+     *
+     * @param {workbox-strategies.Strategy} strategy
+     * @param {Object} options
+     * @param {Request|string} options.request A request to run this strategy for.
+     * @param {ExtendableEvent} options.event The event associated with the
+     *     request.
+     * @param {URL} [options.url]
+     * @param {*} [options.params] The return value from the
+     *     {@link workbox-routing~matchCallback} (if applicable).
+     */
+    constructor(strategy: Strategy, options: HandlerCallbackOptions);
+    /**
+     * Fetches a given request (and invokes any applicable plugin callback
+     * methods) using the `fetchOptions` (for non-navigation requests) and
+     * `plugins` defined on the `Strategy` object.
+     *
+     * The following plugin lifecycle methods are invoked when using this method:
+     * - `requestWillFetch()`
+     * - `fetchDidSucceed()`
+     * - `fetchDidFail()`
+     *
+     * @param {Request|string} input The URL or request to fetch.
+     * @return {Promise<Response>}
+     */
+    fetch(input: RequestInfo): Promise<Response>;
+    /**
+     * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on
+     * the response generated by `this.fetch()`.
+     *
+     * The call to `this.cachePut()` automatically invokes `this.waitUntil()`,
+     * so you do not have to manually call `waitUntil()` on the event.
+     *
+     * @param {Request|string} input The request or URL to fetch and cache.
+     * @return {Promise<Response>}
+     */
+    fetchAndCachePut(input: RequestInfo): Promise<Response>;
+    /**
+     * Matches a request from the cache (and invokes any applicable plugin
+     * callback methods) using the `cacheName`, `matchOptions`, and `plugins`
+     * defined on the strategy object.
+     *
+     * The following plugin lifecycle methods are invoked when using this method:
+     * - cacheKeyWillByUsed()
+     * - cachedResponseWillByUsed()
+     *
+     * @param {Request|string} key The Request or URL to use as the cache key.
+     * @return {Promise<Response|undefined>} A matching response, if found.
+     */
+    cacheMatch(key: RequestInfo): Promise<Response | undefined>;
+    /**
+     * Puts a request/response pair in the cache (and invokes any applicable
+     * plugin callback methods) using the `cacheName` and `plugins` defined on
+     * the strategy object.
+     *
+     * The following plugin lifecycle methods are invoked when using this method:
+     * - cacheKeyWillByUsed()
+     * - cacheWillUpdate()
+     * - cacheDidUpdate()
+     *
+     * @param {Request|string} key The request or URL to use as the cache key.
+     * @param {Response} response The response to cache.
+     * @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response
+     * not be cached, and `true` otherwise.
+     */
+    cachePut(key: RequestInfo, response: Response): Promise<boolean>;
+    /**
+     * 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. If no `cacheKeyWillBeUsed` plugin callbacks have
+     * been registered, the passed request is returned unmodified
+     *
+     * @param {Request} request
+     * @param {string} mode
+     * @return {Promise<Request>}
+     */
+    getCacheKey(request: Request, mode: 'read' | 'write'): Promise<Request>;
+    /**
+     * Returns true if the strategy has at least one plugin with the given
+     * callback.
+     *
+     * @param {string} name The name of the callback to check for.
+     * @return {boolean}
+     */
+    hasCallback<C extends keyof WorkboxPlugin>(name: C): boolean;
+    /**
+     * Runs all plugin callbacks matching the given name, in order, passing the
+     * given param object (merged ith the current plugin state) as the only
+     * argument.
+     *
+     * Note: since this method runs all plugins, it's not suitable for cases
+     * where the return value of a callback needs to be applied prior to calling
+     * the next callback. See
+     * {@link workbox-strategies.StrategyHandler#iterateCallbacks}
+     * below for how to handle that case.
+     *
+     * @param {string} name The name of the callback to run within each plugin.
+     * @param {Object} param The object to pass as the first (and only) param
+     *     when executing each callback. This object will be merged with the
+     *     current plugin state prior to callback execution.
+     */
+    runCallbacks<C extends keyof NonNullable<WorkboxPlugin>>(name: C, param: Omit<WorkboxPluginCallbackParam[C], 'state'>): Promise<void>;
+    /**
+     * Accepts a callback and returns an iterable of matching plugin callbacks,
+     * where each callback is wrapped with the current handler state (i.e. when
+     * you call each callback, whatever object parameter you pass it will
+     * be merged with the plugin's current state).
+     *
+     * @param {string} name The name fo the callback to run
+     * @return {Array<Function>}
+     */
+    iterateCallbacks<C extends keyof WorkboxPlugin>(name: C): Generator<NonNullable<WorkboxPlugin[C]>>;
+    /**
+     * Adds a promise to the
+     * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises}
+     * of the event event associated with the request being handled (usually a
+     * `FetchEvent`).
+     *
+     * Note: you can await
+     * {@link workbox-strategies.StrategyHandler~doneWaiting}
+     * to know when all added promises have settled.
+     *
+     * @param {Promise} promise A promise to add to the extend lifetime promises
+     *     of the event that triggered the request.
+     */
+    waitUntil<T>(promise: Promise<T>): Promise<T>;
+    /**
+     * Returns a promise that resolves once all promises passed to
+     * {@link workbox-strategies.StrategyHandler~waitUntil}
+     * have settled.
+     *
+     * Note: any work done after `doneWaiting()` settles should be manually
+     * passed to an event's `waitUntil()` method (not this handler's
+     * `waitUntil()` method), otherwise the service worker thread my be killed
+     * prior to your work completing.
+     */
+    doneWaiting(): Promise<void>;
+    /**
+     * Stops running the strategy and immediately resolves any pending
+     * `waitUntil()` promises.
+     */
+    destroy(): void;
+    /**
+     * 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 {Request} options.request
+     * @param {Response} options.response
+     * @return {Promise<Response|undefined>}
+     *
+     * @private
+     */
+    _ensureResponseSafeToCache(response: Response): Promise<Response | undefined>;
+}
+export { StrategyHandler };
Index: frontend/node_modules/workbox-strategies/StrategyHandler.js
===================================================================
--- frontend/node_modules/workbox-strategies/StrategyHandler.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/StrategyHandler.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,517 @@
+/*
+  Copyright 2020 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.
+*/
+import { assert } from 'workbox-core/_private/assert.js';
+import { cacheMatchIgnoreParams } from 'workbox-core/_private/cacheMatchIgnoreParams.js';
+import { Deferred } from 'workbox-core/_private/Deferred.js';
+import { executeQuotaErrorCallbacks } from 'workbox-core/_private/executeQuotaErrorCallbacks.js';
+import { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';
+import { logger } from 'workbox-core/_private/logger.js';
+import { timeout } from 'workbox-core/_private/timeout.js';
+import { WorkboxError } from 'workbox-core/_private/WorkboxError.js';
+import './_version.js';
+function toRequest(input) {
+    return typeof input === 'string' ? new Request(input) : input;
+}
+/**
+ * A class created every time a Strategy instance instance calls
+ * {@link workbox-strategies.Strategy~handle} or
+ * {@link workbox-strategies.Strategy~handleAll} that wraps all fetch and
+ * cache actions around plugin callbacks and keeps track of when the strategy
+ * is "done" (i.e. all added `event.waitUntil()` promises have resolved).
+ *
+ * @memberof workbox-strategies
+ */
+class StrategyHandler {
+    /**
+     * Creates a new instance associated with the passed strategy and event
+     * that's handling the request.
+     *
+     * The constructor also initializes the state that will be passed to each of
+     * the plugins handling this request.
+     *
+     * @param {workbox-strategies.Strategy} strategy
+     * @param {Object} options
+     * @param {Request|string} options.request A request to run this strategy for.
+     * @param {ExtendableEvent} options.event The event associated with the
+     *     request.
+     * @param {URL} [options.url]
+     * @param {*} [options.params] The return value from the
+     *     {@link workbox-routing~matchCallback} (if applicable).
+     */
+    constructor(strategy, options) {
+        this._cacheKeys = {};
+        /**
+         * The request the strategy is performing (passed to the strategy's
+         * `handle()` or `handleAll()` method).
+         * @name request
+         * @instance
+         * @type {Request}
+         * @memberof workbox-strategies.StrategyHandler
+         */
+        /**
+         * The event associated with this request.
+         * @name event
+         * @instance
+         * @type {ExtendableEvent}
+         * @memberof workbox-strategies.StrategyHandler
+         */
+        /**
+         * A `URL` instance of `request.url` (if passed to the strategy's
+         * `handle()` or `handleAll()` method).
+         * Note: the `url` param will be present if the strategy was invoked
+         * from a workbox `Route` object.
+         * @name url
+         * @instance
+         * @type {URL|undefined}
+         * @memberof workbox-strategies.StrategyHandler
+         */
+        /**
+         * A `param` value (if passed to the strategy's
+         * `handle()` or `handleAll()` method).
+         * Note: the `param` param will be present if the strategy was invoked
+         * from a workbox `Route` object and the
+         * {@link workbox-routing~matchCallback} returned
+         * a truthy value (it will be that value).
+         * @name params
+         * @instance
+         * @type {*|undefined}
+         * @memberof workbox-strategies.StrategyHandler
+         */
+        if (process.env.NODE_ENV !== 'production') {
+            assert.isInstance(options.event, ExtendableEvent, {
+                moduleName: 'workbox-strategies',
+                className: 'StrategyHandler',
+                funcName: 'constructor',
+                paramName: 'options.event',
+            });
+        }
+        Object.assign(this, options);
+        this.event = options.event;
+        this._strategy = strategy;
+        this._handlerDeferred = new Deferred();
+        this._extendLifetimePromises = [];
+        // Copy the plugins list (since it's mutable on the strategy),
+        // so any mutations don't affect this handler instance.
+        this._plugins = [...strategy.plugins];
+        this._pluginStateMap = new Map();
+        for (const plugin of this._plugins) {
+            this._pluginStateMap.set(plugin, {});
+        }
+        this.event.waitUntil(this._handlerDeferred.promise);
+    }
+    /**
+     * Fetches a given request (and invokes any applicable plugin callback
+     * methods) using the `fetchOptions` (for non-navigation requests) and
+     * `plugins` defined on the `Strategy` object.
+     *
+     * The following plugin lifecycle methods are invoked when using this method:
+     * - `requestWillFetch()`
+     * - `fetchDidSucceed()`
+     * - `fetchDidFail()`
+     *
+     * @param {Request|string} input The URL or request to fetch.
+     * @return {Promise<Response>}
+     */
+    async fetch(input) {
+        const { event } = this;
+        let request = toRequest(input);
+        if (request.mode === 'navigate' &&
+            event instanceof FetchEvent &&
+            event.preloadResponse) {
+            const possiblePreloadResponse = (await event.preloadResponse);
+            if (possiblePreloadResponse) {
+                if (process.env.NODE_ENV !== 'production') {
+                    logger.log(`Using a preloaded navigation response for ` +
+                        `'${getFriendlyURL(request.url)}'`);
+                }
+                return possiblePreloadResponse;
+            }
+        }
+        // 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().
+        const originalRequest = this.hasCallback('fetchDidFail')
+            ? request.clone()
+            : null;
+        try {
+            for (const cb of this.iterateCallbacks('requestWillFetch')) {
+                request = await cb({ request: request.clone(), event });
+            }
+        }
+        catch (err) {
+            if (err instanceof Error) {
+                throw new WorkboxError('plugin-error-request-will-fetch', {
+                    thrownErrorMessage: err.message,
+                });
+            }
+        }
+        // The request can be altered by plugins with `requestWillFetch` making
+        // the original request (most likely from a `fetch` event) different
+        // from the Request we make. Pass both to `fetchDidFail` to aid debugging.
+        const pluginFilteredRequest = request.clone();
+        try {
+            let fetchResponse;
+            // See https://github.com/GoogleChrome/workbox/issues/1796
+            fetchResponse = await fetch(request, request.mode === 'navigate' ? undefined : this._strategy.fetchOptions);
+            if (process.env.NODE_ENV !== 'production') {
+                logger.debug(`Network request for ` +
+                    `'${getFriendlyURL(request.url)}' returned a response with ` +
+                    `status '${fetchResponse.status}'.`);
+            }
+            for (const callback of this.iterateCallbacks('fetchDidSucceed')) {
+                fetchResponse = await callback({
+                    event,
+                    request: pluginFilteredRequest,
+                    response: fetchResponse,
+                });
+            }
+            return fetchResponse;
+        }
+        catch (error) {
+            if (process.env.NODE_ENV !== 'production') {
+                logger.log(`Network request for ` +
+                    `'${getFriendlyURL(request.url)}' threw an error.`, error);
+            }
+            // `originalRequest` will only exist if a `fetchDidFail` callback
+            // is being used (see above).
+            if (originalRequest) {
+                await this.runCallbacks('fetchDidFail', {
+                    error: error,
+                    event,
+                    originalRequest: originalRequest.clone(),
+                    request: pluginFilteredRequest.clone(),
+                });
+            }
+            throw error;
+        }
+    }
+    /**
+     * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on
+     * the response generated by `this.fetch()`.
+     *
+     * The call to `this.cachePut()` automatically invokes `this.waitUntil()`,
+     * so you do not have to manually call `waitUntil()` on the event.
+     *
+     * @param {Request|string} input The request or URL to fetch and cache.
+     * @return {Promise<Response>}
+     */
+    async fetchAndCachePut(input) {
+        const response = await this.fetch(input);
+        const responseClone = response.clone();
+        void this.waitUntil(this.cachePut(input, responseClone));
+        return response;
+    }
+    /**
+     * Matches a request from the cache (and invokes any applicable plugin
+     * callback methods) using the `cacheName`, `matchOptions`, and `plugins`
+     * defined on the strategy object.
+     *
+     * The following plugin lifecycle methods are invoked when using this method:
+     * - cacheKeyWillByUsed()
+     * - cachedResponseWillByUsed()
+     *
+     * @param {Request|string} key The Request or URL to use as the cache key.
+     * @return {Promise<Response|undefined>} A matching response, if found.
+     */
+    async cacheMatch(key) {
+        const request = toRequest(key);
+        let cachedResponse;
+        const { cacheName, matchOptions } = this._strategy;
+        const effectiveRequest = await this.getCacheKey(request, 'read');
+        const multiMatchOptions = Object.assign(Object.assign({}, matchOptions), { cacheName });
+        cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);
+        if (process.env.NODE_ENV !== 'production') {
+            if (cachedResponse) {
+                logger.debug(`Found a cached response in '${cacheName}'.`);
+            }
+            else {
+                logger.debug(`No cached response found in '${cacheName}'.`);
+            }
+        }
+        for (const callback of this.iterateCallbacks('cachedResponseWillBeUsed')) {
+            cachedResponse =
+                (await callback({
+                    cacheName,
+                    matchOptions,
+                    cachedResponse,
+                    request: effectiveRequest,
+                    event: this.event,
+                })) || undefined;
+        }
+        return cachedResponse;
+    }
+    /**
+     * Puts a request/response pair in the cache (and invokes any applicable
+     * plugin callback methods) using the `cacheName` and `plugins` defined on
+     * the strategy object.
+     *
+     * The following plugin lifecycle methods are invoked when using this method:
+     * - cacheKeyWillByUsed()
+     * - cacheWillUpdate()
+     * - cacheDidUpdate()
+     *
+     * @param {Request|string} key The request or URL to use as the cache key.
+     * @param {Response} response The response to cache.
+     * @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response
+     * not be cached, and `true` otherwise.
+     */
+    async cachePut(key, response) {
+        const request = toRequest(key);
+        // Run in the next task to avoid blocking other cache reads.
+        // https://github.com/w3c/ServiceWorker/issues/1397
+        await timeout(0);
+        const effectiveRequest = await this.getCacheKey(request, 'write');
+        if (process.env.NODE_ENV !== 'production') {
+            if (effectiveRequest.method && effectiveRequest.method !== 'GET') {
+                throw new WorkboxError('attempt-to-cache-non-get-request', {
+                    url: getFriendlyURL(effectiveRequest.url),
+                    method: effectiveRequest.method,
+                });
+            }
+            // See https://github.com/GoogleChrome/workbox/issues/2818
+            const vary = response.headers.get('Vary');
+            if (vary) {
+                logger.debug(`The response for ${getFriendlyURL(effectiveRequest.url)} ` +
+                    `has a 'Vary: ${vary}' header. ` +
+                    `Consider setting the {ignoreVary: true} option on your strategy ` +
+                    `to ensure cache matching and deletion works as expected.`);
+            }
+        }
+        if (!response) {
+            if (process.env.NODE_ENV !== 'production') {
+                logger.error(`Cannot cache non-existent response for ` +
+                    `'${getFriendlyURL(effectiveRequest.url)}'.`);
+            }
+            throw new WorkboxError('cache-put-with-no-response', {
+                url: getFriendlyURL(effectiveRequest.url),
+            });
+        }
+        const responseToCache = await this._ensureResponseSafeToCache(response);
+        if (!responseToCache) {
+            if (process.env.NODE_ENV !== 'production') {
+                logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' ` +
+                    `will not be cached.`, responseToCache);
+            }
+            return false;
+        }
+        const { cacheName, matchOptions } = this._strategy;
+        const cache = await self.caches.open(cacheName);
+        const hasCacheUpdateCallback = this.hasCallback('cacheDidUpdate');
+        const oldResponse = hasCacheUpdateCallback
+            ? await cacheMatchIgnoreParams(
+            // TODO(philipwalton): the `__WB_REVISION__` param is a precaching
+            // feature. Consider into ways to only add this behavior if using
+            // precaching.
+            cache, effectiveRequest.clone(), ['__WB_REVISION__'], matchOptions)
+            : null;
+        if (process.env.NODE_ENV !== 'production') {
+            logger.debug(`Updating the '${cacheName}' cache with a new Response ` +
+                `for ${getFriendlyURL(effectiveRequest.url)}.`);
+        }
+        try {
+            await cache.put(effectiveRequest, hasCacheUpdateCallback ? responseToCache.clone() : responseToCache);
+        }
+        catch (error) {
+            if (error instanceof Error) {
+                // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError
+                if (error.name === 'QuotaExceededError') {
+                    await executeQuotaErrorCallbacks();
+                }
+                throw error;
+            }
+        }
+        for (const callback of this.iterateCallbacks('cacheDidUpdate')) {
+            await callback({
+                cacheName,
+                oldResponse,
+                newResponse: responseToCache.clone(),
+                request: effectiveRequest,
+                event: this.event,
+            });
+        }
+        return true;
+    }
+    /**
+     * 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. If no `cacheKeyWillBeUsed` plugin callbacks have
+     * been registered, the passed request is returned unmodified
+     *
+     * @param {Request} request
+     * @param {string} mode
+     * @return {Promise<Request>}
+     */
+    async getCacheKey(request, mode) {
+        const key = `${request.url} | ${mode}`;
+        if (!this._cacheKeys[key]) {
+            let effectiveRequest = request;
+            for (const callback of this.iterateCallbacks('cacheKeyWillBeUsed')) {
+                effectiveRequest = toRequest(await callback({
+                    mode,
+                    request: effectiveRequest,
+                    event: this.event,
+                    // params has a type any can't change right now.
+                    params: this.params, // eslint-disable-line
+                }));
+            }
+            this._cacheKeys[key] = effectiveRequest;
+        }
+        return this._cacheKeys[key];
+    }
+    /**
+     * Returns true if the strategy has at least one plugin with the given
+     * callback.
+     *
+     * @param {string} name The name of the callback to check for.
+     * @return {boolean}
+     */
+    hasCallback(name) {
+        for (const plugin of this._strategy.plugins) {
+            if (name in plugin) {
+                return true;
+            }
+        }
+        return false;
+    }
+    /**
+     * Runs all plugin callbacks matching the given name, in order, passing the
+     * given param object (merged ith the current plugin state) as the only
+     * argument.
+     *
+     * Note: since this method runs all plugins, it's not suitable for cases
+     * where the return value of a callback needs to be applied prior to calling
+     * the next callback. See
+     * {@link workbox-strategies.StrategyHandler#iterateCallbacks}
+     * below for how to handle that case.
+     *
+     * @param {string} name The name of the callback to run within each plugin.
+     * @param {Object} param The object to pass as the first (and only) param
+     *     when executing each callback. This object will be merged with the
+     *     current plugin state prior to callback execution.
+     */
+    async runCallbacks(name, param) {
+        for (const callback of this.iterateCallbacks(name)) {
+            // TODO(philipwalton): not sure why `any` is needed. It seems like
+            // this should work with `as WorkboxPluginCallbackParam[C]`.
+            await callback(param);
+        }
+    }
+    /**
+     * Accepts a callback and returns an iterable of matching plugin callbacks,
+     * where each callback is wrapped with the current handler state (i.e. when
+     * you call each callback, whatever object parameter you pass it will
+     * be merged with the plugin's current state).
+     *
+     * @param {string} name The name fo the callback to run
+     * @return {Array<Function>}
+     */
+    *iterateCallbacks(name) {
+        for (const plugin of this._strategy.plugins) {
+            if (typeof plugin[name] === 'function') {
+                const state = this._pluginStateMap.get(plugin);
+                const statefulCallback = (param) => {
+                    const statefulParam = Object.assign(Object.assign({}, param), { state });
+                    // TODO(philipwalton): not sure why `any` is needed. It seems like
+                    // this should work with `as WorkboxPluginCallbackParam[C]`.
+                    return plugin[name](statefulParam);
+                };
+                yield statefulCallback;
+            }
+        }
+    }
+    /**
+     * Adds a promise to the
+     * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises}
+     * of the event event associated with the request being handled (usually a
+     * `FetchEvent`).
+     *
+     * Note: you can await
+     * {@link workbox-strategies.StrategyHandler~doneWaiting}
+     * to know when all added promises have settled.
+     *
+     * @param {Promise} promise A promise to add to the extend lifetime promises
+     *     of the event that triggered the request.
+     */
+    waitUntil(promise) {
+        this._extendLifetimePromises.push(promise);
+        return promise;
+    }
+    /**
+     * Returns a promise that resolves once all promises passed to
+     * {@link workbox-strategies.StrategyHandler~waitUntil}
+     * have settled.
+     *
+     * Note: any work done after `doneWaiting()` settles should be manually
+     * passed to an event's `waitUntil()` method (not this handler's
+     * `waitUntil()` method), otherwise the service worker thread my be killed
+     * prior to your work completing.
+     */
+    async doneWaiting() {
+        let promise;
+        while ((promise = this._extendLifetimePromises.shift())) {
+            await promise;
+        }
+    }
+    /**
+     * Stops running the strategy and immediately resolves any pending
+     * `waitUntil()` promises.
+     */
+    destroy() {
+        this._handlerDeferred.resolve(null);
+    }
+    /**
+     * 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 {Request} options.request
+     * @param {Response} options.response
+     * @return {Promise<Response|undefined>}
+     *
+     * @private
+     */
+    async _ensureResponseSafeToCache(response) {
+        let responseToCache = response;
+        let pluginsUsed = false;
+        for (const callback of this.iterateCallbacks('cacheWillUpdate')) {
+            responseToCache =
+                (await callback({
+                    request: this.request,
+                    response: responseToCache,
+                    event: this.event,
+                })) || undefined;
+            pluginsUsed = true;
+            if (!responseToCache) {
+                break;
+            }
+        }
+        if (!pluginsUsed) {
+            if (responseToCache && responseToCache.status !== 200) {
+                responseToCache = undefined;
+            }
+            if (process.env.NODE_ENV !== 'production') {
+                if (responseToCache) {
+                    if (responseToCache.status !== 200) {
+                        if (responseToCache.status === 0) {
+                            logger.warn(`The response for '${this.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 '${this.request.url}' ` +
+                                `returned a status code of '${response.status}' and won't ` +
+                                `be cached as a result.`);
+                        }
+                    }
+                }
+            }
+        }
+        return responseToCache;
+    }
+}
+export { StrategyHandler };
Index: frontend/node_modules/workbox-strategies/StrategyHandler.mjs
===================================================================
--- frontend/node_modules/workbox-strategies/StrategyHandler.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/StrategyHandler.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+export * from './StrategyHandler.js';
Index: frontend/node_modules/workbox-strategies/_version.js
===================================================================
--- frontend/node_modules/workbox-strategies/_version.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/_version.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,6 @@
+"use strict";
+// @ts-ignore
+try {
+    self['workbox:strategies:6.5.4'] && _();
+}
+catch (e) { }
Index: frontend/node_modules/workbox-strategies/_version.mjs
===================================================================
--- frontend/node_modules/workbox-strategies/_version.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/_version.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+try{self['workbox:strategies:6.6.0']&&_()}catch(e){}// eslint-disable-line
Index: frontend/node_modules/workbox-strategies/index.d.ts
===================================================================
--- frontend/node_modules/workbox-strategies/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/index.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,20 @@
+import { CacheFirst } from './CacheFirst.js';
+import { CacheOnly } from './CacheOnly.js';
+import { NetworkFirst, NetworkFirstOptions } from './NetworkFirst.js';
+import { NetworkOnly, NetworkOnlyOptions } from './NetworkOnly.js';
+import { StaleWhileRevalidate } from './StaleWhileRevalidate.js';
+import { Strategy, StrategyOptions } from './Strategy.js';
+import { StrategyHandler } from './StrategyHandler.js';
+import './_version.js';
+declare global {
+    interface FetchEvent {
+        readonly preloadResponse: Promise<any>;
+    }
+}
+/**
+ * There are common caching strategies that most service workers will need
+ * and use. This module provides simple implementations of these strategies.
+ *
+ * @module workbox-strategies
+ */
+export { CacheFirst, CacheOnly, NetworkFirst, NetworkFirstOptions, NetworkOnly, NetworkOnlyOptions, StaleWhileRevalidate, Strategy, StrategyHandler, StrategyOptions, };
Index: frontend/node_modules/workbox-strategies/index.js
===================================================================
--- frontend/node_modules/workbox-strategies/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/index.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,22 @@
+/*
+  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.
+*/
+import { CacheFirst } from './CacheFirst.js';
+import { CacheOnly } from './CacheOnly.js';
+import { NetworkFirst } from './NetworkFirst.js';
+import { NetworkOnly } from './NetworkOnly.js';
+import { StaleWhileRevalidate } from './StaleWhileRevalidate.js';
+import { Strategy } from './Strategy.js';
+import { StrategyHandler } from './StrategyHandler.js';
+import './_version.js';
+/**
+ * There are common caching strategies that most service workers will need
+ * and use. This module provides simple implementations of these strategies.
+ *
+ * @module workbox-strategies
+ */
+export { CacheFirst, CacheOnly, NetworkFirst, NetworkOnly, StaleWhileRevalidate, Strategy, StrategyHandler, };
Index: frontend/node_modules/workbox-strategies/index.mjs
===================================================================
--- frontend/node_modules/workbox-strategies/index.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/index.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+export * from './index.js';
Index: frontend/node_modules/workbox-strategies/package.json
===================================================================
--- frontend/node_modules/workbox-strategies/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/package.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,29 @@
+{
+  "name": "workbox-strategies",
+  "version": "6.6.0",
+  "license": "MIT",
+  "author": "Google's Web DevRel Team",
+  "description": "A service worker helper library implementing common caching strategies.",
+  "repository": "googlechrome/workbox",
+  "bugs": "https://github.com/googlechrome/workbox/issues",
+  "homepage": "https://github.com/GoogleChrome/workbox",
+  "keywords": [
+    "workbox",
+    "workboxjs",
+    "service worker",
+    "sw",
+    "router",
+    "routing"
+  ],
+  "workbox": {
+    "browserNamespace": "workbox.strategies",
+    "packageType": "sw"
+  },
+  "main": "index.js",
+  "module": "index.mjs",
+  "types": "index.d.ts",
+  "dependencies": {
+    "workbox-core": "6.6.0"
+  },
+  "gitHead": "252644491d9bb5a67518935ede6df530107c9475"
+}
Index: frontend/node_modules/workbox-strategies/plugins/cacheOkAndOpaquePlugin.d.ts
===================================================================
--- frontend/node_modules/workbox-strategies/plugins/cacheOkAndOpaquePlugin.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/plugins/cacheOkAndOpaquePlugin.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,3 @@
+import { WorkboxPlugin } from 'workbox-core/types.js';
+import '../_version.js';
+export declare const cacheOkAndOpaquePlugin: WorkboxPlugin;
Index: frontend/node_modules/workbox-strategies/plugins/cacheOkAndOpaquePlugin.js
===================================================================
--- frontend/node_modules/workbox-strategies/plugins/cacheOkAndOpaquePlugin.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/plugins/cacheOkAndOpaquePlugin.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,26 @@
+/*
+  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.
+*/
+import '../_version.js';
+export 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;
+        }
+        return null;
+    },
+};
Index: frontend/node_modules/workbox-strategies/plugins/cacheOkAndOpaquePlugin.mjs
===================================================================
--- frontend/node_modules/workbox-strategies/plugins/cacheOkAndOpaquePlugin.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/plugins/cacheOkAndOpaquePlugin.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+export * from './cacheOkAndOpaquePlugin.js';
Index: frontend/node_modules/workbox-strategies/src/CacheFirst.ts
===================================================================
--- frontend/node_modules/workbox-strategies/src/CacheFirst.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/src/CacheFirst.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,101 @@
+/*
+  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.
+*/
+
+import {assert} from 'workbox-core/_private/assert.js';
+import {logger} from 'workbox-core/_private/logger.js';
+import {WorkboxError} from 'workbox-core/_private/WorkboxError.js';
+
+import {Strategy} from './Strategy.js';
+import {StrategyHandler} from './StrategyHandler.js';
+import {messages} from './utils/messages.js';
+import './_version.js';
+
+/**
+ * An implementation of a [cache-first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-first-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.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+class CacheFirst extends Strategy {
+  /**
+   * @private
+   * @param {Request|string} request A request to run this strategy for.
+   * @param {workbox-strategies.StrategyHandler} handler The event that
+   *     triggered the request.
+   * @return {Promise<Response>}
+   */
+  async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
+    const logs = [];
+
+    if (process.env.NODE_ENV !== 'production') {
+      assert!.isInstance(request, Request, {
+        moduleName: 'workbox-strategies',
+        className: this.constructor.name,
+        funcName: 'makeRequest',
+        paramName: 'request',
+      });
+    }
+
+    let response = await handler.cacheMatch(request);
+
+    let error: Error | undefined = undefined;
+    if (!response) {
+      if (process.env.NODE_ENV !== 'production') {
+        logs.push(
+          `No response found in the '${this.cacheName}' cache. ` +
+            `Will respond with a network request.`,
+        );
+      }
+      try {
+        response = await handler.fetchAndCachePut(request);
+      } catch (err) {
+        if (err instanceof Error) {
+          error = err;
+        }
+      }
+
+      if (process.env.NODE_ENV !== 'production') {
+        if (response) {
+          logs.push(`Got response from network.`);
+        } else {
+          logs.push(`Unable to get a response from the network.`);
+        }
+      }
+    } else {
+      if (process.env.NODE_ENV !== 'production') {
+        logs.push(`Found a cached response in the '${this.cacheName}' cache.`);
+      }
+    }
+
+    if (process.env.NODE_ENV !== 'production') {
+      logger.groupCollapsed(
+        messages.strategyStart(this.constructor.name, request),
+      );
+      for (const log of logs) {
+        logger.log(log);
+      }
+      messages.printFinalResponse(response);
+      logger.groupEnd();
+    }
+
+    if (!response) {
+      throw new WorkboxError('no-response', {url: request.url, error});
+    }
+    return response;
+  }
+}
+
+export {CacheFirst};
Index: frontend/node_modules/workbox-strategies/src/CacheOnly.ts
===================================================================
--- frontend/node_modules/workbox-strategies/src/CacheOnly.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/src/CacheOnly.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,72 @@
+/*
+  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.
+*/
+
+import {assert} from 'workbox-core/_private/assert.js';
+import {logger} from 'workbox-core/_private/logger.js';
+import {WorkboxError} from 'workbox-core/_private/WorkboxError.js';
+
+import {Strategy} from './Strategy.js';
+import {StrategyHandler} from './StrategyHandler.js';
+import {messages} from './utils/messages.js';
+import './_version.js';
+
+/**
+ * An implementation of a [cache-only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#cache-only)
+ * request strategy.
+ *
+ * This class is useful if you want to take advantage of any
+ * [Workbox plugins](https://developer.chrome.com/docs/workbox/using-plugins/).
+ *
+ * If there is no cache match, this will throw a `WorkboxError` exception.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+class CacheOnly extends Strategy {
+  /**
+   * @private
+   * @param {Request|string} request A request to run this strategy for.
+   * @param {workbox-strategies.StrategyHandler} handler The event that
+   *     triggered the request.
+   * @return {Promise<Response>}
+   */
+  async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
+    if (process.env.NODE_ENV !== 'production') {
+      assert!.isInstance(request, Request, {
+        moduleName: 'workbox-strategies',
+        className: this.constructor.name,
+        funcName: 'makeRequest',
+        paramName: 'request',
+      });
+    }
+
+    const response = await handler.cacheMatch(request);
+
+    if (process.env.NODE_ENV !== 'production') {
+      logger.groupCollapsed(
+        messages.strategyStart(this.constructor.name, request),
+      );
+      if (response) {
+        logger.log(
+          `Found a cached response in the '${this.cacheName}' ` + `cache.`,
+        );
+        messages.printFinalResponse(response);
+      } else {
+        logger.log(`No response found in the '${this.cacheName}' cache.`);
+      }
+      logger.groupEnd();
+    }
+
+    if (!response) {
+      throw new WorkboxError('no-response', {url: request.url});
+    }
+    return response;
+  }
+}
+
+export {CacheOnly};
Index: frontend/node_modules/workbox-strategies/src/NetworkFirst.ts
===================================================================
--- frontend/node_modules/workbox-strategies/src/NetworkFirst.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/src/NetworkFirst.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,259 @@
+/*
+  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.
+*/
+
+import {assert} from 'workbox-core/_private/assert.js';
+import {logger} from 'workbox-core/_private/logger.js';
+import {WorkboxError} from 'workbox-core/_private/WorkboxError.js';
+
+import {cacheOkAndOpaquePlugin} from './plugins/cacheOkAndOpaquePlugin.js';
+import {Strategy, StrategyOptions} from './Strategy.js';
+import {StrategyHandler} from './StrategyHandler.js';
+import {messages} from './utils/messages.js';
+import './_version.js';
+
+export interface NetworkFirstOptions extends StrategyOptions {
+  networkTimeoutSeconds?: number;
+}
+
+/**
+ * An implementation of a
+ * [network first](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-first-falling-back-to-cache)
+ * request strategy.
+ *
+ * By default, this strategy will cache responses with a 200 status code as
+ * well as [opaque responses](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
+ * Opaque responses are are cross-origin requests where the response doesn't
+ * support [CORS](https://enable-cors.org/).
+ *
+ * If the network request fails, and there is no cache match, this will throw
+ * a `WorkboxError` exception.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+class NetworkFirst extends Strategy {
+  private readonly _networkTimeoutSeconds: number;
+
+  /**
+   * @param {Object} [options]
+   * @param {string} [options.cacheName] Cache name to store and retrieve
+   * requests. Defaults to cache names provided by
+   * {@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 [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
+   * `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: NetworkFirstOptions = {}) {
+    super(options);
+
+    // If this instance contains no plugins with a 'cacheWillUpdate' callback,
+    // prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.
+    if (!this.plugins.some((p) => 'cacheWillUpdate' in p)) {
+      this.plugins.unshift(cacheOkAndOpaquePlugin);
+    }
+
+    this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
+    if (process.env.NODE_ENV !== 'production') {
+      if (this._networkTimeoutSeconds) {
+        assert!.isType(this._networkTimeoutSeconds, 'number', {
+          moduleName: 'workbox-strategies',
+          className: this.constructor.name,
+          funcName: 'constructor',
+          paramName: 'networkTimeoutSeconds',
+        });
+      }
+    }
+  }
+
+  /**
+   * @private
+   * @param {Request|string} request A request to run this strategy for.
+   * @param {workbox-strategies.StrategyHandler} handler The event that
+   *     triggered the request.
+   * @return {Promise<Response>}
+   */
+  async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
+    const logs: any[] = [];
+
+    if (process.env.NODE_ENV !== 'production') {
+      assert!.isInstance(request, Request, {
+        moduleName: 'workbox-strategies',
+        className: this.constructor.name,
+        funcName: 'handle',
+        paramName: 'makeRequest',
+      });
+    }
+
+    const promises: Promise<Response | undefined>[] = [];
+    let timeoutId: number | undefined;
+
+    if (this._networkTimeoutSeconds) {
+      const {id, promise} = this._getTimeoutPromise({request, logs, handler});
+      timeoutId = id;
+      promises.push(promise);
+    }
+
+    const networkPromise = this._getNetworkPromise({
+      timeoutId,
+      request,
+      logs,
+      handler,
+    });
+
+    promises.push(networkPromise);
+
+    const response = await handler.waitUntil(
+      (async () => {
+        // Promise.race() will resolve as soon as the first promise resolves.
+        return (
+          (await handler.waitUntil(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".
+          (await networkPromise)
+        );
+      })(),
+    );
+
+    if (process.env.NODE_ENV !== 'production') {
+      logger.groupCollapsed(
+        messages.strategyStart(this.constructor.name, request),
+      );
+      for (const log of logs) {
+        logger.log(log);
+      }
+      messages.printFinalResponse(response);
+      logger.groupEnd();
+    }
+
+    if (!response) {
+      throw new 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
+   */
+  private _getTimeoutPromise({
+    request,
+    logs,
+    handler,
+  }: {
+    request: Request;
+    logs: any[];
+    handler: StrategyHandler;
+  }): {promise: Promise<Response | undefined>; id?: number} {
+    let timeoutId;
+    const timeoutPromise: Promise<Response | undefined> = new Promise(
+      (resolve) => {
+        const onNetworkTimeout = async () => {
+          if (process.env.NODE_ENV !== 'production') {
+            logs.push(
+              `Timing out the network response at ` +
+                `${this._networkTimeoutSeconds} seconds.`,
+            );
+          }
+          resolve(await handler.cacheMatch(request));
+        };
+        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,
+    handler,
+  }: {
+    request: Request;
+    logs: any[];
+    timeoutId?: number;
+    handler: StrategyHandler;
+  }): Promise<Response | undefined> {
+    let error;
+    let response;
+    try {
+      response = await handler.fetchAndCachePut(request);
+    } catch (fetchError) {
+      if (fetchError instanceof Error) {
+        error = fetchError;
+      }
+    }
+
+    if (timeoutId) {
+      clearTimeout(timeoutId);
+    }
+
+    if (process.env.NODE_ENV !== 'production') {
+      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 (error || !response) {
+      response = await handler.cacheMatch(request);
+
+      if (process.env.NODE_ENV !== 'production') {
+        if (response) {
+          logs.push(
+            `Found a cached response in the '${this.cacheName}'` + ` cache.`,
+          );
+        } else {
+          logs.push(`No response found in the '${this.cacheName}' cache.`);
+        }
+      }
+    }
+
+    return response;
+  }
+}
+
+export {NetworkFirst};
Index: frontend/node_modules/workbox-strategies/src/NetworkOnly.ts
===================================================================
--- frontend/node_modules/workbox-strategies/src/NetworkOnly.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/src/NetworkOnly.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,122 @@
+/*
+  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.
+*/
+
+import {assert} from 'workbox-core/_private/assert.js';
+import {logger} from 'workbox-core/_private/logger.js';
+import {timeout} from 'workbox-core/_private/timeout.js';
+import {WorkboxError} from 'workbox-core/_private/WorkboxError.js';
+
+import {Strategy, StrategyOptions} from './Strategy.js';
+import {StrategyHandler} from './StrategyHandler.js';
+import {messages} from './utils/messages.js';
+import './_version.js';
+
+interface NetworkOnlyOptions
+  extends Omit<StrategyOptions, 'cacheName' | 'matchOptions'> {
+  networkTimeoutSeconds?: number;
+}
+
+/**
+ * An implementation of a
+ * [network-only](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#network-only)
+ * request strategy.
+ *
+ * This class is useful if you want to take advantage of any
+ * [Workbox plugins](https://developer.chrome.com/docs/workbox/using-plugins/).
+ *
+ * If the network request fails, this will throw a `WorkboxError` exception.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+class NetworkOnly extends Strategy {
+  private readonly _networkTimeoutSeconds: number;
+
+  /**
+   * @param {Object} [options]
+   * @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 [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
+   * `fetch()` requests made by this strategy.
+   * @param {number} [options.networkTimeoutSeconds] If set, any network requests
+   * that fail to respond within the timeout will result in a network error.
+   */
+  constructor(options: NetworkOnlyOptions = {}) {
+    super(options);
+
+    this._networkTimeoutSeconds = options.networkTimeoutSeconds || 0;
+  }
+
+  /**
+   * @private
+   * @param {Request|string} request A request to run this strategy for.
+   * @param {workbox-strategies.StrategyHandler} handler The event that
+   *     triggered the request.
+   * @return {Promise<Response>}
+   */
+  async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
+    if (process.env.NODE_ENV !== 'production') {
+      assert!.isInstance(request, Request, {
+        moduleName: 'workbox-strategies',
+        className: this.constructor.name,
+        funcName: '_handle',
+        paramName: 'request',
+      });
+    }
+
+    let error: Error | undefined = undefined;
+    let response: Response | undefined;
+
+    try {
+      const promises: Promise<Response | undefined>[] = [
+        handler.fetch(request),
+      ];
+
+      if (this._networkTimeoutSeconds) {
+        const timeoutPromise = timeout(
+          this._networkTimeoutSeconds * 1000,
+        ) as Promise<undefined>;
+        promises.push(timeoutPromise);
+      }
+
+      response = await Promise.race(promises);
+      if (!response) {
+        throw new Error(
+          `Timed out the network response after ` +
+            `${this._networkTimeoutSeconds} seconds.`,
+        );
+      }
+    } catch (err) {
+      if (err instanceof Error) {
+        error = err;
+      }
+    }
+
+    if (process.env.NODE_ENV !== 'production') {
+      logger.groupCollapsed(
+        messages.strategyStart(this.constructor.name, request),
+      );
+      if (response) {
+        logger.log(`Got response from network.`);
+      } else {
+        logger.log(`Unable to get a response from the network.`);
+      }
+      messages.printFinalResponse(response);
+      logger.groupEnd();
+    }
+
+    if (!response) {
+      throw new WorkboxError('no-response', {url: request.url, error});
+    }
+    return response;
+  }
+}
+
+export {NetworkOnly, NetworkOnlyOptions};
Index: frontend/node_modules/workbox-strategies/src/StaleWhileRevalidate.ts
===================================================================
--- frontend/node_modules/workbox-strategies/src/StaleWhileRevalidate.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/src/StaleWhileRevalidate.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,135 @@
+/*
+  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.
+*/
+
+import {assert} from 'workbox-core/_private/assert.js';
+import {logger} from 'workbox-core/_private/logger.js';
+import {WorkboxError} from 'workbox-core/_private/WorkboxError.js';
+
+import {cacheOkAndOpaquePlugin} from './plugins/cacheOkAndOpaquePlugin.js';
+import {Strategy, StrategyOptions} from './Strategy.js';
+import {StrategyHandler} from './StrategyHandler.js';
+import {messages} from './utils/messages.js';
+import './_version.js';
+
+/**
+ * An implementation of a
+ * [stale-while-revalidate](https://developer.chrome.com/docs/workbox/caching-strategies-overview/#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](https://developer.chrome.com/docs/workbox/caching-resources-during-runtime/#opaque-responses).
+ * Opaque responses are cross-origin requests where the response doesn't
+ * support [CORS](https://enable-cors.org/).
+ *
+ * If the network request fails, and there is no cache match, this will throw
+ * a `WorkboxError` exception.
+ *
+ * @extends workbox-strategies.Strategy
+ * @memberof workbox-strategies
+ */
+class StaleWhileRevalidate extends Strategy {
+  /**
+   * @param {Object} [options]
+   * @param {string} [options.cacheName] Cache name to store and retrieve
+   * requests. Defaults to cache names provided by
+   * {@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 [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
+   * `fetch()` requests made by this strategy.
+   * @param {Object} [options.matchOptions] [`CacheQueryOptions`](https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions)
+   */
+  constructor(options: StrategyOptions = {}) {
+    super(options);
+
+    // If this instance contains no plugins with a 'cacheWillUpdate' callback,
+    // prepend the `cacheOkAndOpaquePlugin` plugin to the plugins list.
+    if (!this.plugins.some((p) => 'cacheWillUpdate' in p)) {
+      this.plugins.unshift(cacheOkAndOpaquePlugin);
+    }
+  }
+
+  /**
+   * @private
+   * @param {Request|string} request A request to run this strategy for.
+   * @param {workbox-strategies.StrategyHandler} handler The event that
+   *     triggered the request.
+   * @return {Promise<Response>}
+   */
+  async _handle(request: Request, handler: StrategyHandler): Promise<Response> {
+    const logs = [];
+
+    if (process.env.NODE_ENV !== 'production') {
+      assert!.isInstance(request, Request, {
+        moduleName: 'workbox-strategies',
+        className: this.constructor.name,
+        funcName: 'handle',
+        paramName: 'request',
+      });
+    }
+
+    const fetchAndCachePromise = handler.fetchAndCachePut(request).catch(() => {
+      // Swallow this error because a 'no-response' error will be thrown in
+      // main handler return flow. This will be in the `waitUntil()` flow.
+    });
+    void handler.waitUntil(fetchAndCachePromise);
+
+    let response = await handler.cacheMatch(request);
+
+    let error;
+    if (response) {
+      if (process.env.NODE_ENV !== 'production') {
+        logs.push(
+          `Found a cached response in the '${this.cacheName}'` +
+            ` cache. Will update with the network response in the background.`,
+        );
+      }
+    } else {
+      if (process.env.NODE_ENV !== 'production') {
+        logs.push(
+          `No response found in the '${this.cacheName}' cache. ` +
+            `Will wait for the network response.`,
+        );
+      }
+      try {
+        // NOTE(philipwalton): Really annoying that we have to type cast here.
+        // https://github.com/microsoft/TypeScript/issues/20006
+        response = (await fetchAndCachePromise) as Response | undefined;
+      } catch (err) {
+        if (err instanceof Error) {
+          error = err;
+        }
+      }
+    }
+
+    if (process.env.NODE_ENV !== 'production') {
+      logger.groupCollapsed(
+        messages.strategyStart(this.constructor.name, request),
+      );
+      for (const log of logs) {
+        logger.log(log);
+      }
+      messages.printFinalResponse(response);
+      logger.groupEnd();
+    }
+
+    if (!response) {
+      throw new WorkboxError('no-response', {url: request.url, error});
+    }
+    return response;
+  }
+}
+
+export {StaleWhileRevalidate};
Index: frontend/node_modules/workbox-strategies/src/Strategy.ts
===================================================================
--- frontend/node_modules/workbox-strategies/src/Strategy.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/src/Strategy.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,289 @@
+/*
+  Copyright 2020 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.
+*/
+
+import {cacheNames} from 'workbox-core/_private/cacheNames.js';
+import {WorkboxError} from 'workbox-core/_private/WorkboxError.js';
+import {logger} from 'workbox-core/_private/logger.js';
+import {getFriendlyURL} from 'workbox-core/_private/getFriendlyURL.js';
+import {
+  HandlerCallbackOptions,
+  RouteHandlerObject,
+  WorkboxPlugin,
+} from 'workbox-core/types.js';
+
+import {StrategyHandler} from './StrategyHandler.js';
+
+import './_version.js';
+
+export interface StrategyOptions {
+  cacheName?: string;
+  plugins?: WorkboxPlugin[];
+  fetchOptions?: RequestInit;
+  matchOptions?: CacheQueryOptions;
+}
+
+/**
+ * An abstract base class that all other strategy classes must extend from:
+ *
+ * @memberof workbox-strategies
+ */
+abstract class Strategy implements RouteHandlerObject {
+  cacheName: string;
+  plugins: WorkboxPlugin[];
+  fetchOptions?: RequestInit;
+  matchOptions?: CacheQueryOptions;
+
+  protected abstract _handle(
+    request: Request,
+    handler: StrategyHandler,
+  ): Promise<Response | undefined>;
+
+  /**
+   * Creates a new instance of the strategy and sets all documented option
+   * properties as public instance properties.
+   *
+   * Note: if a custom strategy class extends the base Strategy class and does
+   * not need more than these properties, it does not need to define its own
+   * constructor.
+   *
+   * @param {Object} [options]
+   * @param {string} [options.cacheName] Cache name to store and retrieve
+   * requests. Defaults to the cache names provided by
+   * {@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 [non-navigation](https://github.com/GoogleChrome/workbox/issues/1796)
+   * `fetch()` requests made by this strategy.
+   * @param {Object} [options.matchOptions] The
+   * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}
+   * for any `cache.match()` or `cache.put()` calls made by this strategy.
+   */
+  constructor(options: StrategyOptions = {}) {
+    /**
+     * Cache name to store and retrieve
+     * requests. Defaults to the cache names provided by
+     * {@link workbox-core.cacheNames}.
+     *
+     * @type {string}
+     */
+    this.cacheName = cacheNames.getRuntimeName(options.cacheName);
+    /**
+     * The list
+     * [Plugins]{@link https://developers.google.com/web/tools/workbox/guides/using-plugins}
+     * used by this strategy.
+     *
+     * @type {Array<Object>}
+     */
+    this.plugins = options.plugins || [];
+    /**
+     * Values passed along to the
+     * [`init`]{@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters}
+     * of all fetch() requests made by this strategy.
+     *
+     * @type {Object}
+     */
+    this.fetchOptions = options.fetchOptions;
+    /**
+     * The
+     * [`CacheQueryOptions`]{@link https://w3c.github.io/ServiceWorker/#dictdef-cachequeryoptions}
+     * for any `cache.match()` or `cache.put()` calls made by this strategy.
+     *
+     * @type {Object}
+     */
+    this.matchOptions = options.matchOptions;
+  }
+
+  /**
+   * Perform a request strategy and returns a `Promise` that will resolve with
+   * a `Response`, invoking all relevant plugin callbacks.
+   *
+   * When a strategy instance is registered with a Workbox
+   * {@link workbox-routing.Route}, this method is automatically
+   * called when the route matches.
+   *
+   * Alternatively, this method can be used in a standalone `FetchEvent`
+   * listener by passing it to `event.respondWith()`.
+   *
+   * @param {FetchEvent|Object} options A `FetchEvent` or an object with the
+   *     properties listed below.
+   * @param {Request|string} options.request A request to run this strategy for.
+   * @param {ExtendableEvent} options.event The event associated with the
+   *     request.
+   * @param {URL} [options.url]
+   * @param {*} [options.params]
+   */
+  handle(options: FetchEvent | HandlerCallbackOptions): Promise<Response> {
+    const [responseDone] = this.handleAll(options);
+    return responseDone;
+  }
+
+  /**
+   * Similar to {@link workbox-strategies.Strategy~handle}, but
+   * instead of just returning a `Promise` that resolves to a `Response` it
+   * it will return an tuple of `[response, done]` promises, where the former
+   * (`response`) is equivalent to what `handle()` returns, and the latter is a
+   * Promise that will resolve once any promises that were added to
+   * `event.waitUntil()` as part of performing the strategy have completed.
+   *
+   * You can await the `done` promise to ensure any extra work performed by
+   * the strategy (usually caching responses) completes successfully.
+   *
+   * @param {FetchEvent|Object} options A `FetchEvent` or an object with the
+   *     properties listed below.
+   * @param {Request|string} options.request A request to run this strategy for.
+   * @param {ExtendableEvent} options.event The event associated with the
+   *     request.
+   * @param {URL} [options.url]
+   * @param {*} [options.params]
+   * @return {Array<Promise>} A tuple of [response, done]
+   *     promises that can be used to determine when the response resolves as
+   *     well as when the handler has completed all its work.
+   */
+  handleAll(
+    options: FetchEvent | HandlerCallbackOptions,
+  ): [Promise<Response>, Promise<void>] {
+    // Allow for flexible options to be passed.
+    if (options instanceof FetchEvent) {
+      options = {
+        event: options,
+        request: options.request,
+      };
+    }
+
+    const event = options.event;
+    const request =
+      typeof options.request === 'string'
+        ? new Request(options.request)
+        : options.request;
+    const params = 'params' in options ? options.params : undefined;
+
+    const handler = new StrategyHandler(this, {event, request, params});
+
+    const responseDone = this._getResponse(handler, request, event);
+    const handlerDone = this._awaitComplete(
+      responseDone,
+      handler,
+      request,
+      event,
+    );
+
+    // Return an array of promises, suitable for use with Promise.all().
+    return [responseDone, handlerDone];
+  }
+
+  async _getResponse(
+    handler: StrategyHandler,
+    request: Request,
+    event: ExtendableEvent,
+  ): Promise<Response> {
+    await handler.runCallbacks('handlerWillStart', {event, request});
+
+    let response: Response | undefined = undefined;
+    try {
+      response = await this._handle(request, handler);
+      // The "official" Strategy subclasses all throw this error automatically,
+      // but in case a third-party Strategy doesn't, ensure that we have a
+      // consistent failure when there's no response or an error response.
+      if (!response || response.type === 'error') {
+        throw new WorkboxError('no-response', {url: request.url});
+      }
+    } catch (error) {
+      if (error instanceof Error) {
+        for (const callback of handler.iterateCallbacks('handlerDidError')) {
+          response = await callback({error, event, request});
+          if (response) {
+            break;
+          }
+        }
+      }
+
+      if (!response) {
+        throw error;
+      } else if (process.env.NODE_ENV !== 'production') {
+        logger.log(
+          `While responding to '${getFriendlyURL(request.url)}', ` +
+            `an ${
+              error instanceof Error ? error.toString() : ''
+            } error occurred. Using a fallback response provided by ` +
+            `a handlerDidError plugin.`,
+        );
+      }
+    }
+
+    for (const callback of handler.iterateCallbacks('handlerWillRespond')) {
+      response = await callback({event, request, response});
+    }
+
+    return response;
+  }
+
+  async _awaitComplete(
+    responseDone: Promise<Response>,
+    handler: StrategyHandler,
+    request: Request,
+    event: ExtendableEvent,
+  ): Promise<void> {
+    let response;
+    let error;
+
+    try {
+      response = await responseDone;
+    } catch (error) {
+      // Ignore errors, as response errors should be caught via the `response`
+      // promise above. The `done` promise will only throw for errors in
+      // promises passed to `handler.waitUntil()`.
+    }
+
+    try {
+      await handler.runCallbacks('handlerDidRespond', {
+        event,
+        request,
+        response,
+      });
+      await handler.doneWaiting();
+    } catch (waitUntilError) {
+      if (waitUntilError instanceof Error) {
+        error = waitUntilError;
+      }
+    }
+
+    await handler.runCallbacks('handlerDidComplete', {
+      event,
+      request,
+      response,
+      error: error as Error,
+    });
+    handler.destroy();
+
+    if (error) {
+      throw error;
+    }
+  }
+}
+
+export {Strategy};
+
+/**
+ * Classes extending the `Strategy` based class should implement this method,
+ * and leverage the {@link workbox-strategies.StrategyHandler}
+ * arg to perform all fetching and cache logic, which will ensure all relevant
+ * cache, cache options, fetch options and plugins are used (per the current
+ * strategy instance).
+ *
+ * @name _handle
+ * @instance
+ * @abstract
+ * @function
+ * @param {Request} request
+ * @param {workbox-strategies.StrategyHandler} handler
+ * @return {Promise<Response>}
+ *
+ * @memberof workbox-strategies.Strategy
+ */
Index: frontend/node_modules/workbox-strategies/src/StrategyHandler.ts
===================================================================
--- frontend/node_modules/workbox-strategies/src/StrategyHandler.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/src/StrategyHandler.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,636 @@
+/*
+  Copyright 2020 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.
+*/
+
+import {assert} from 'workbox-core/_private/assert.js';
+import {cacheMatchIgnoreParams} from 'workbox-core/_private/cacheMatchIgnoreParams.js';
+import {Deferred} from 'workbox-core/_private/Deferred.js';
+import {executeQuotaErrorCallbacks} from 'workbox-core/_private/executeQuotaErrorCallbacks.js';
+import {getFriendlyURL} from 'workbox-core/_private/getFriendlyURL.js';
+import {logger} from 'workbox-core/_private/logger.js';
+import {timeout} from 'workbox-core/_private/timeout.js';
+import {WorkboxError} from 'workbox-core/_private/WorkboxError.js';
+import {
+  HandlerCallbackOptions,
+  MapLikeObject,
+  WorkboxPlugin,
+  WorkboxPluginCallbackParam,
+} from 'workbox-core/types.js';
+
+import {Strategy} from './Strategy.js';
+import './_version.js';
+
+function toRequest(input: RequestInfo) {
+  return typeof input === 'string' ? new Request(input) : input;
+}
+
+/**
+ * A class created every time a Strategy instance instance calls
+ * {@link workbox-strategies.Strategy~handle} or
+ * {@link workbox-strategies.Strategy~handleAll} that wraps all fetch and
+ * cache actions around plugin callbacks and keeps track of when the strategy
+ * is "done" (i.e. all added `event.waitUntil()` promises have resolved).
+ *
+ * @memberof workbox-strategies
+ */
+class StrategyHandler {
+  public request!: Request;
+  public url?: URL;
+  public event: ExtendableEvent;
+  public params?: any;
+
+  private _cacheKeys: Record<string, Request> = {};
+
+  private readonly _strategy: Strategy;
+  private readonly _extendLifetimePromises: Promise<any>[];
+  private readonly _handlerDeferred: Deferred<any>;
+  private readonly _plugins: WorkboxPlugin[];
+  private readonly _pluginStateMap: Map<WorkboxPlugin, MapLikeObject>;
+
+  /**
+   * Creates a new instance associated with the passed strategy and event
+   * that's handling the request.
+   *
+   * The constructor also initializes the state that will be passed to each of
+   * the plugins handling this request.
+   *
+   * @param {workbox-strategies.Strategy} strategy
+   * @param {Object} options
+   * @param {Request|string} options.request A request to run this strategy for.
+   * @param {ExtendableEvent} options.event The event associated with the
+   *     request.
+   * @param {URL} [options.url]
+   * @param {*} [options.params] The return value from the
+   *     {@link workbox-routing~matchCallback} (if applicable).
+   */
+  constructor(strategy: Strategy, options: HandlerCallbackOptions) {
+    /**
+     * The request the strategy is performing (passed to the strategy's
+     * `handle()` or `handleAll()` method).
+     * @name request
+     * @instance
+     * @type {Request}
+     * @memberof workbox-strategies.StrategyHandler
+     */
+    /**
+     * The event associated with this request.
+     * @name event
+     * @instance
+     * @type {ExtendableEvent}
+     * @memberof workbox-strategies.StrategyHandler
+     */
+    /**
+     * A `URL` instance of `request.url` (if passed to the strategy's
+     * `handle()` or `handleAll()` method).
+     * Note: the `url` param will be present if the strategy was invoked
+     * from a workbox `Route` object.
+     * @name url
+     * @instance
+     * @type {URL|undefined}
+     * @memberof workbox-strategies.StrategyHandler
+     */
+    /**
+     * A `param` value (if passed to the strategy's
+     * `handle()` or `handleAll()` method).
+     * Note: the `param` param will be present if the strategy was invoked
+     * from a workbox `Route` object and the
+     * {@link workbox-routing~matchCallback} returned
+     * a truthy value (it will be that value).
+     * @name params
+     * @instance
+     * @type {*|undefined}
+     * @memberof workbox-strategies.StrategyHandler
+     */
+    if (process.env.NODE_ENV !== 'production') {
+      assert!.isInstance(options.event, ExtendableEvent, {
+        moduleName: 'workbox-strategies',
+        className: 'StrategyHandler',
+        funcName: 'constructor',
+        paramName: 'options.event',
+      });
+    }
+
+    Object.assign(this, options);
+
+    this.event = options.event;
+    this._strategy = strategy;
+    this._handlerDeferred = new Deferred();
+    this._extendLifetimePromises = [];
+
+    // Copy the plugins list (since it's mutable on the strategy),
+    // so any mutations don't affect this handler instance.
+    this._plugins = [...strategy.plugins];
+    this._pluginStateMap = new Map();
+    for (const plugin of this._plugins) {
+      this._pluginStateMap.set(plugin, {});
+    }
+
+    this.event.waitUntil(this._handlerDeferred.promise);
+  }
+
+  /**
+   * Fetches a given request (and invokes any applicable plugin callback
+   * methods) using the `fetchOptions` (for non-navigation requests) and
+   * `plugins` defined on the `Strategy` object.
+   *
+   * The following plugin lifecycle methods are invoked when using this method:
+   * - `requestWillFetch()`
+   * - `fetchDidSucceed()`
+   * - `fetchDidFail()`
+   *
+   * @param {Request|string} input The URL or request to fetch.
+   * @return {Promise<Response>}
+   */
+  async fetch(input: RequestInfo): Promise<Response> {
+    const {event} = this;
+    let request: Request = toRequest(input);
+
+    if (
+      request.mode === 'navigate' &&
+      event instanceof FetchEvent &&
+      event.preloadResponse
+    ) {
+      const possiblePreloadResponse = (await event.preloadResponse) as
+        | Response
+        | undefined;
+      if (possiblePreloadResponse) {
+        if (process.env.NODE_ENV !== 'production') {
+          logger.log(
+            `Using a preloaded navigation response for ` +
+              `'${getFriendlyURL(request.url)}'`,
+          );
+        }
+        return possiblePreloadResponse;
+      }
+    }
+
+    // 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().
+    const originalRequest = this.hasCallback('fetchDidFail')
+      ? request.clone()
+      : null;
+
+    try {
+      for (const cb of this.iterateCallbacks('requestWillFetch')) {
+        request = await cb({request: request.clone(), event});
+      }
+    } catch (err) {
+      if (err instanceof Error) {
+        throw new WorkboxError('plugin-error-request-will-fetch', {
+          thrownErrorMessage: err.message,
+        });
+      }
+    }
+
+    // The request can be altered by plugins with `requestWillFetch` making
+    // the original request (most likely from a `fetch` event) different
+    // from the Request we make. Pass both to `fetchDidFail` to aid debugging.
+    const pluginFilteredRequest: Request = request.clone();
+
+    try {
+      let fetchResponse: Response;
+
+      // See https://github.com/GoogleChrome/workbox/issues/1796
+      fetchResponse = await fetch(
+        request,
+        request.mode === 'navigate' ? undefined : this._strategy.fetchOptions,
+      );
+
+      if (process.env.NODE_ENV !== 'production') {
+        logger.debug(
+          `Network request for ` +
+            `'${getFriendlyURL(request.url)}' returned a response with ` +
+            `status '${fetchResponse.status}'.`,
+        );
+      }
+
+      for (const callback of this.iterateCallbacks('fetchDidSucceed')) {
+        fetchResponse = await callback({
+          event,
+          request: pluginFilteredRequest,
+          response: fetchResponse,
+        });
+      }
+      return fetchResponse;
+    } catch (error) {
+      if (process.env.NODE_ENV !== 'production') {
+        logger.log(
+          `Network request for ` +
+            `'${getFriendlyURL(request.url)}' threw an error.`,
+          error,
+        );
+      }
+
+      // `originalRequest` will only exist if a `fetchDidFail` callback
+      // is being used (see above).
+      if (originalRequest) {
+        await this.runCallbacks('fetchDidFail', {
+          error: error as Error,
+          event,
+          originalRequest: originalRequest.clone(),
+          request: pluginFilteredRequest.clone(),
+        });
+      }
+      throw error;
+    }
+  }
+
+  /**
+   * Calls `this.fetch()` and (in the background) runs `this.cachePut()` on
+   * the response generated by `this.fetch()`.
+   *
+   * The call to `this.cachePut()` automatically invokes `this.waitUntil()`,
+   * so you do not have to manually call `waitUntil()` on the event.
+   *
+   * @param {Request|string} input The request or URL to fetch and cache.
+   * @return {Promise<Response>}
+   */
+  async fetchAndCachePut(input: RequestInfo): Promise<Response> {
+    const response = await this.fetch(input);
+    const responseClone = response.clone();
+
+    void this.waitUntil(this.cachePut(input, responseClone));
+
+    return response;
+  }
+
+  /**
+   * Matches a request from the cache (and invokes any applicable plugin
+   * callback methods) using the `cacheName`, `matchOptions`, and `plugins`
+   * defined on the strategy object.
+   *
+   * The following plugin lifecycle methods are invoked when using this method:
+   * - cacheKeyWillByUsed()
+   * - cachedResponseWillByUsed()
+   *
+   * @param {Request|string} key The Request or URL to use as the cache key.
+   * @return {Promise<Response|undefined>} A matching response, if found.
+   */
+  async cacheMatch(key: RequestInfo): Promise<Response | undefined> {
+    const request: Request = toRequest(key);
+    let cachedResponse: Response | undefined;
+    const {cacheName, matchOptions} = this._strategy;
+
+    const effectiveRequest = await this.getCacheKey(request, 'read');
+    const multiMatchOptions = {...matchOptions, ...{cacheName}};
+
+    cachedResponse = await caches.match(effectiveRequest, multiMatchOptions);
+
+    if (process.env.NODE_ENV !== 'production') {
+      if (cachedResponse) {
+        logger.debug(`Found a cached response in '${cacheName}'.`);
+      } else {
+        logger.debug(`No cached response found in '${cacheName}'.`);
+      }
+    }
+
+    for (const callback of this.iterateCallbacks('cachedResponseWillBeUsed')) {
+      cachedResponse =
+        (await callback({
+          cacheName,
+          matchOptions,
+          cachedResponse,
+          request: effectiveRequest,
+          event: this.event,
+        })) || undefined;
+    }
+    return cachedResponse;
+  }
+
+  /**
+   * Puts a request/response pair in the cache (and invokes any applicable
+   * plugin callback methods) using the `cacheName` and `plugins` defined on
+   * the strategy object.
+   *
+   * The following plugin lifecycle methods are invoked when using this method:
+   * - cacheKeyWillByUsed()
+   * - cacheWillUpdate()
+   * - cacheDidUpdate()
+   *
+   * @param {Request|string} key The request or URL to use as the cache key.
+   * @param {Response} response The response to cache.
+   * @return {Promise<boolean>} `false` if a cacheWillUpdate caused the response
+   * not be cached, and `true` otherwise.
+   */
+  async cachePut(key: RequestInfo, response: Response): Promise<boolean> {
+    const request: Request = toRequest(key);
+
+    // Run in the next task to avoid blocking other cache reads.
+    // https://github.com/w3c/ServiceWorker/issues/1397
+    await timeout(0);
+
+    const effectiveRequest = await this.getCacheKey(request, 'write');
+
+    if (process.env.NODE_ENV !== 'production') {
+      if (effectiveRequest.method && effectiveRequest.method !== 'GET') {
+        throw new WorkboxError('attempt-to-cache-non-get-request', {
+          url: getFriendlyURL(effectiveRequest.url),
+          method: effectiveRequest.method,
+        });
+      }
+
+      // See https://github.com/GoogleChrome/workbox/issues/2818
+      const vary = response.headers.get('Vary');
+      if (vary) {
+        logger.debug(
+          `The response for ${getFriendlyURL(effectiveRequest.url)} ` +
+            `has a 'Vary: ${vary}' header. ` +
+            `Consider setting the {ignoreVary: true} option on your strategy ` +
+            `to ensure cache matching and deletion works as expected.`,
+        );
+      }
+    }
+
+    if (!response) {
+      if (process.env.NODE_ENV !== 'production') {
+        logger.error(
+          `Cannot cache non-existent response for ` +
+            `'${getFriendlyURL(effectiveRequest.url)}'.`,
+        );
+      }
+
+      throw new WorkboxError('cache-put-with-no-response', {
+        url: getFriendlyURL(effectiveRequest.url),
+      });
+    }
+
+    const responseToCache = await this._ensureResponseSafeToCache(response);
+
+    if (!responseToCache) {
+      if (process.env.NODE_ENV !== 'production') {
+        logger.debug(
+          `Response '${getFriendlyURL(effectiveRequest.url)}' ` +
+            `will not be cached.`,
+          responseToCache,
+        );
+      }
+      return false;
+    }
+
+    const {cacheName, matchOptions} = this._strategy;
+    const cache = await self.caches.open(cacheName);
+
+    const hasCacheUpdateCallback = this.hasCallback('cacheDidUpdate');
+    const oldResponse = hasCacheUpdateCallback
+      ? await cacheMatchIgnoreParams(
+          // TODO(philipwalton): the `__WB_REVISION__` param is a precaching
+          // feature. Consider into ways to only add this behavior if using
+          // precaching.
+          cache,
+          effectiveRequest.clone(),
+          ['__WB_REVISION__'],
+          matchOptions,
+        )
+      : null;
+
+    if (process.env.NODE_ENV !== 'production') {
+      logger.debug(
+        `Updating the '${cacheName}' cache with a new Response ` +
+          `for ${getFriendlyURL(effectiveRequest.url)}.`,
+      );
+    }
+
+    try {
+      await cache.put(
+        effectiveRequest,
+        hasCacheUpdateCallback ? responseToCache.clone() : responseToCache,
+      );
+    } catch (error) {
+      if (error instanceof Error) {
+        // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError
+        if (error.name === 'QuotaExceededError') {
+          await executeQuotaErrorCallbacks();
+        }
+        throw error;
+      }
+    }
+
+    for (const callback of this.iterateCallbacks('cacheDidUpdate')) {
+      await callback({
+        cacheName,
+        oldResponse,
+        newResponse: responseToCache.clone(),
+        request: effectiveRequest,
+        event: this.event,
+      });
+    }
+
+    return true;
+  }
+
+  /**
+   * 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. If no `cacheKeyWillBeUsed` plugin callbacks have
+   * been registered, the passed request is returned unmodified
+   *
+   * @param {Request} request
+   * @param {string} mode
+   * @return {Promise<Request>}
+   */
+  async getCacheKey(
+    request: Request,
+    mode: 'read' | 'write',
+  ): Promise<Request> {
+    const key = `${request.url} | ${mode}`;
+    if (!this._cacheKeys[key]) {
+      let effectiveRequest = request;
+
+      for (const callback of this.iterateCallbacks('cacheKeyWillBeUsed')) {
+        effectiveRequest = toRequest(
+          await callback({
+            mode,
+            request: effectiveRequest,
+            event: this.event,
+            // params has a type any can't change right now.
+            params: this.params, // eslint-disable-line
+          }),
+        );
+      }
+
+      this._cacheKeys[key] = effectiveRequest;
+    }
+    return this._cacheKeys[key];
+  }
+
+  /**
+   * Returns true if the strategy has at least one plugin with the given
+   * callback.
+   *
+   * @param {string} name The name of the callback to check for.
+   * @return {boolean}
+   */
+  hasCallback<C extends keyof WorkboxPlugin>(name: C): boolean {
+    for (const plugin of this._strategy.plugins) {
+      if (name in plugin) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Runs all plugin callbacks matching the given name, in order, passing the
+   * given param object (merged ith the current plugin state) as the only
+   * argument.
+   *
+   * Note: since this method runs all plugins, it's not suitable for cases
+   * where the return value of a callback needs to be applied prior to calling
+   * the next callback. See
+   * {@link workbox-strategies.StrategyHandler#iterateCallbacks}
+   * below for how to handle that case.
+   *
+   * @param {string} name The name of the callback to run within each plugin.
+   * @param {Object} param The object to pass as the first (and only) param
+   *     when executing each callback. This object will be merged with the
+   *     current plugin state prior to callback execution.
+   */
+  async runCallbacks<C extends keyof NonNullable<WorkboxPlugin>>(
+    name: C,
+    param: Omit<WorkboxPluginCallbackParam[C], 'state'>,
+  ): Promise<void> {
+    for (const callback of this.iterateCallbacks(name)) {
+      // TODO(philipwalton): not sure why `any` is needed. It seems like
+      // this should work with `as WorkboxPluginCallbackParam[C]`.
+      await callback(param as any);
+    }
+  }
+
+  /**
+   * Accepts a callback and returns an iterable of matching plugin callbacks,
+   * where each callback is wrapped with the current handler state (i.e. when
+   * you call each callback, whatever object parameter you pass it will
+   * be merged with the plugin's current state).
+   *
+   * @param {string} name The name fo the callback to run
+   * @return {Array<Function>}
+   */
+  *iterateCallbacks<C extends keyof WorkboxPlugin>(
+    name: C,
+  ): Generator<NonNullable<WorkboxPlugin[C]>> {
+    for (const plugin of this._strategy.plugins) {
+      if (typeof plugin[name] === 'function') {
+        const state = this._pluginStateMap.get(plugin);
+        const statefulCallback = (
+          param: Omit<WorkboxPluginCallbackParam[C], 'state'>,
+        ) => {
+          const statefulParam = {...param, state};
+
+          // TODO(philipwalton): not sure why `any` is needed. It seems like
+          // this should work with `as WorkboxPluginCallbackParam[C]`.
+          return plugin[name]!(statefulParam as any);
+        };
+        yield statefulCallback as NonNullable<WorkboxPlugin[C]>;
+      }
+    }
+  }
+
+  /**
+   * Adds a promise to the
+   * [extend lifetime promises]{@link https://w3c.github.io/ServiceWorker/#extendableevent-extend-lifetime-promises}
+   * of the event event associated with the request being handled (usually a
+   * `FetchEvent`).
+   *
+   * Note: you can await
+   * {@link workbox-strategies.StrategyHandler~doneWaiting}
+   * to know when all added promises have settled.
+   *
+   * @param {Promise} promise A promise to add to the extend lifetime promises
+   *     of the event that triggered the request.
+   */
+  waitUntil<T>(promise: Promise<T>): Promise<T> {
+    this._extendLifetimePromises.push(promise);
+    return promise;
+  }
+
+  /**
+   * Returns a promise that resolves once all promises passed to
+   * {@link workbox-strategies.StrategyHandler~waitUntil}
+   * have settled.
+   *
+   * Note: any work done after `doneWaiting()` settles should be manually
+   * passed to an event's `waitUntil()` method (not this handler's
+   * `waitUntil()` method), otherwise the service worker thread my be killed
+   * prior to your work completing.
+   */
+  async doneWaiting(): Promise<void> {
+    let promise;
+    while ((promise = this._extendLifetimePromises.shift())) {
+      await promise;
+    }
+  }
+
+  /**
+   * Stops running the strategy and immediately resolves any pending
+   * `waitUntil()` promises.
+   */
+  destroy(): void {
+    this._handlerDeferred.resolve(null);
+  }
+
+  /**
+   * 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 {Request} options.request
+   * @param {Response} options.response
+   * @return {Promise<Response|undefined>}
+   *
+   * @private
+   */
+  async _ensureResponseSafeToCache(
+    response: Response,
+  ): Promise<Response | undefined> {
+    let responseToCache: Response | undefined = response;
+    let pluginsUsed = false;
+
+    for (const callback of this.iterateCallbacks('cacheWillUpdate')) {
+      responseToCache =
+        (await callback({
+          request: this.request,
+          response: responseToCache,
+          event: this.event,
+        })) || undefined;
+      pluginsUsed = true;
+
+      if (!responseToCache) {
+        break;
+      }
+    }
+
+    if (!pluginsUsed) {
+      if (responseToCache && responseToCache.status !== 200) {
+        responseToCache = undefined;
+      }
+      if (process.env.NODE_ENV !== 'production') {
+        if (responseToCache) {
+          if (responseToCache.status !== 200) {
+            if (responseToCache.status === 0) {
+              logger.warn(
+                `The response for '${this.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 '${this.request.url}' ` +
+                  `returned a status code of '${response.status}' and won't ` +
+                  `be cached as a result.`,
+              );
+            }
+          }
+        }
+      }
+    }
+
+    return responseToCache;
+  }
+}
+
+export {StrategyHandler};
Index: frontend/node_modules/workbox-strategies/src/_version.ts
===================================================================
--- frontend/node_modules/workbox-strategies/src/_version.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/src/_version.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,2 @@
+// @ts-ignore
+try{self['workbox:strategies:6.6.0']&&_()}catch(e){}
Index: frontend/node_modules/workbox-strategies/src/index.ts
===================================================================
--- frontend/node_modules/workbox-strategies/src/index.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/src/index.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,44 @@
+/*
+  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.
+*/
+
+import {CacheFirst} from './CacheFirst.js';
+import {CacheOnly} from './CacheOnly.js';
+import {NetworkFirst, NetworkFirstOptions} from './NetworkFirst.js';
+import {NetworkOnly, NetworkOnlyOptions} from './NetworkOnly.js';
+import {StaleWhileRevalidate} from './StaleWhileRevalidate.js';
+import {Strategy, StrategyOptions} from './Strategy.js';
+import {StrategyHandler} from './StrategyHandler.js';
+import './_version.js';
+
+// See https://github.com/GoogleChrome/workbox/issues/2946
+declare global {
+  interface FetchEvent {
+    // See https://github.com/GoogleChrome/workbox/issues/2974
+    readonly preloadResponse: Promise<any>;
+  }
+}
+
+/**
+ * There are common caching strategies that most service workers will need
+ * and use. This module provides simple implementations of these strategies.
+ *
+ * @module workbox-strategies
+ */
+
+export {
+  CacheFirst,
+  CacheOnly,
+  NetworkFirst,
+  NetworkFirstOptions,
+  NetworkOnly,
+  NetworkOnlyOptions,
+  StaleWhileRevalidate,
+  Strategy,
+  StrategyHandler,
+  StrategyOptions,
+};
Index: frontend/node_modules/workbox-strategies/src/plugins/cacheOkAndOpaquePlugin.ts
===================================================================
--- frontend/node_modules/workbox-strategies/src/plugins/cacheOkAndOpaquePlugin.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/src/plugins/cacheOkAndOpaquePlugin.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,29 @@
+/*
+  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.
+*/
+
+import {WorkboxPlugin} from 'workbox-core/types.js';
+import '../_version.js';
+
+export const cacheOkAndOpaquePlugin: WorkboxPlugin = {
+  /**
+   * 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;
+    }
+    return null;
+  },
+};
Index: frontend/node_modules/workbox-strategies/src/utils/messages.ts
===================================================================
--- frontend/node_modules/workbox-strategies/src/utils/messages.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/src/utils/messages.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,23 @@
+/*
+  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.
+*/
+
+import {logger} from 'workbox-core/_private/logger.js';
+import {getFriendlyURL} from 'workbox-core/_private/getFriendlyURL.js';
+import '../_version.js';
+
+export const messages = {
+  strategyStart: (strategyName: string, request: Request): string =>
+    `Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`,
+  printFinalResponse: (response?: Response): void => {
+    if (response) {
+      logger.groupCollapsed(`View the final response here.`);
+      logger.log(response || '[No response returned]');
+      logger.groupEnd();
+    }
+  },
+};
Index: frontend/node_modules/workbox-strategies/tsconfig.json
===================================================================
--- frontend/node_modules/workbox-strategies/tsconfig.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/tsconfig.json	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,10 @@
+{
+  "extends": "../../tsconfig",
+  "compilerOptions": {
+    "outDir": "./",
+    "rootDir": "./src",
+    "tsBuildInfoFile": "./tsconfig.tsbuildinfo"
+  },
+  "include": ["src/**/*.ts"],
+  "references": [{"path": "../workbox-core/"}]
+}
Index: frontend/node_modules/workbox-strategies/tsconfig.tsbuildinfo
===================================================================
--- frontend/node_modules/workbox-strategies/tsconfig.tsbuildinfo	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/tsconfig.tsbuildinfo	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+{"program":{"fileNames":["../../node_modules/typescript/lib/lib.es5.d.ts","../../node_modules/typescript/lib/lib.es2015.d.ts","../../node_modules/typescript/lib/lib.es2016.d.ts","../../node_modules/typescript/lib/lib.es2017.d.ts","../../node_modules/typescript/lib/lib.es2018.d.ts","../../node_modules/typescript/lib/lib.webworker.d.ts","../../node_modules/typescript/lib/lib.es2015.core.d.ts","../../node_modules/typescript/lib/lib.es2015.collection.d.ts","../../node_modules/typescript/lib/lib.es2015.generator.d.ts","../../node_modules/typescript/lib/lib.es2015.iterable.d.ts","../../node_modules/typescript/lib/lib.es2015.promise.d.ts","../../node_modules/typescript/lib/lib.es2015.proxy.d.ts","../../node_modules/typescript/lib/lib.es2015.reflect.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.d.ts","../../node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts","../../node_modules/typescript/lib/lib.es2016.array.include.d.ts","../../node_modules/typescript/lib/lib.es2017.object.d.ts","../../node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts","../../node_modules/typescript/lib/lib.es2017.string.d.ts","../../node_modules/typescript/lib/lib.es2017.intl.d.ts","../../node_modules/typescript/lib/lib.es2017.typedarrays.d.ts","../../node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts","../../node_modules/typescript/lib/lib.es2018.asynciterable.d.ts","../../node_modules/typescript/lib/lib.es2018.intl.d.ts","../../node_modules/typescript/lib/lib.es2018.promise.d.ts","../../node_modules/typescript/lib/lib.es2018.regexp.d.ts","../../node_modules/typescript/lib/lib.es2020.bigint.d.ts","../../node_modules/typescript/lib/lib.es2020.intl.d.ts","../../node_modules/typescript/lib/lib.esnext.intl.d.ts","../../infra/type-overrides.d.ts","../workbox-core/_version.d.ts","../workbox-core/types.d.ts","../workbox-core/_private/assert.d.ts","../workbox-core/_private/logger.d.ts","../workbox-core/_private/workboxerror.d.ts","../workbox-core/_private/cachenames.d.ts","../workbox-core/_private/getfriendlyurl.d.ts","../workbox-core/_private/cachematchignoreparams.d.ts","../workbox-core/_private/deferred.d.ts","../workbox-core/_private/executequotaerrorcallbacks.d.ts","../workbox-core/_private/timeout.d.ts","./src/_version.ts","./src/strategyhandler.ts","./src/strategy.ts","./src/utils/messages.ts","./src/cachefirst.ts","./src/cacheonly.ts","./src/plugins/cacheokandopaqueplugin.ts","./src/networkfirst.ts","./src/networkonly.ts","./src/stalewhilerevalidate.ts","./src/index.ts","../../node_modules/@babel/types/lib/index.d.ts","../../node_modules/@types/babel__generator/index.d.ts","../../node_modules/@babel/parser/typings/babel-parser.d.ts","../../node_modules/@types/babel__template/index.d.ts","../../node_modules/@types/babel__traverse/index.d.ts","../../node_modules/@types/babel__core/index.d.ts","../../node_modules/@types/babel__preset-env/index.d.ts","../../node_modules/@types/common-tags/index.d.ts","../../node_modules/@types/eslint/helpers.d.ts","../../node_modules/@types/json-schema/index.d.ts","../../node_modules/@types/estree/index.d.ts","../../node_modules/@types/eslint/index.d.ts","../../node_modules/@types/eslint-scope/index.d.ts","../../node_modules/@types/node/globals.d.ts","../../node_modules/@types/node/async_hooks.d.ts","../../node_modules/@types/node/buffer.d.ts","../../node_modules/@types/node/child_process.d.ts","../../node_modules/@types/node/cluster.d.ts","../../node_modules/@types/node/console.d.ts","../../node_modules/@types/node/constants.d.ts","../../node_modules/@types/node/crypto.d.ts","../../node_modules/@types/node/dgram.d.ts","../../node_modules/@types/node/dns.d.ts","../../node_modules/@types/node/domain.d.ts","../../node_modules/@types/node/events.d.ts","../../node_modules/@types/node/fs.d.ts","../../node_modules/@types/node/fs/promises.d.ts","../../node_modules/@types/node/http.d.ts","../../node_modules/@types/node/http2.d.ts","../../node_modules/@types/node/https.d.ts","../../node_modules/@types/node/inspector.d.ts","../../node_modules/@types/node/module.d.ts","../../node_modules/@types/node/net.d.ts","../../node_modules/@types/node/os.d.ts","../../node_modules/@types/node/path.d.ts","../../node_modules/@types/node/perf_hooks.d.ts","../../node_modules/@types/node/process.d.ts","../../node_modules/@types/node/punycode.d.ts","../../node_modules/@types/node/querystring.d.ts","../../node_modules/@types/node/readline.d.ts","../../node_modules/@types/node/repl.d.ts","../../node_modules/@types/node/stream.d.ts","../../node_modules/@types/node/string_decoder.d.ts","../../node_modules/@types/node/timers.d.ts","../../node_modules/@types/node/tls.d.ts","../../node_modules/@types/node/trace_events.d.ts","../../node_modules/@types/node/tty.d.ts","../../node_modules/@types/node/url.d.ts","../../node_modules/@types/node/util.d.ts","../../node_modules/@types/node/v8.d.ts","../../node_modules/@types/node/vm.d.ts","../../node_modules/@types/node/worker_threads.d.ts","../../node_modules/@types/node/zlib.d.ts","../../node_modules/@types/node/ts3.4/base.d.ts","../../node_modules/@types/node/globals.global.d.ts","../../node_modules/@types/node/wasi.d.ts","../../node_modules/@types/node/ts3.6/base.d.ts","../../node_modules/@types/node/assert.d.ts","../../node_modules/@types/node/base.d.ts","../../node_modules/@types/node/index.d.ts","../../node_modules/@types/fs-extra/index.d.ts","../../node_modules/@types/minimatch/index.d.ts","../../node_modules/@types/glob/index.d.ts","../../node_modules/@types/html-minifier-terser/index.d.ts","../../node_modules/@types/linkify-it/index.d.ts","../../node_modules/@types/lodash/common/common.d.ts","../../node_modules/@types/lodash/common/array.d.ts","../../node_modules/@types/lodash/common/collection.d.ts","../../node_modules/@types/lodash/common/date.d.ts","../../node_modules/@types/lodash/common/function.d.ts","../../node_modules/@types/lodash/common/lang.d.ts","../../node_modules/@types/lodash/common/math.d.ts","../../node_modules/@types/lodash/common/number.d.ts","../../node_modules/@types/lodash/common/object.d.ts","../../node_modules/@types/lodash/common/seq.d.ts","../../node_modules/@types/lodash/common/string.d.ts","../../node_modules/@types/lodash/common/util.d.ts","../../node_modules/@types/lodash/index.d.ts","../../node_modules/@types/mdurl/encode.d.ts","../../node_modules/@types/mdurl/decode.d.ts","../../node_modules/@types/mdurl/parse.d.ts","../../node_modules/@types/mdurl/format.d.ts","../../node_modules/@types/mdurl/index.d.ts","../../node_modules/@types/markdown-it/lib/common/utils.d.ts","../../node_modules/@types/markdown-it/lib/token.d.ts","../../node_modules/@types/markdown-it/lib/rules_inline/state_inline.d.ts","../../node_modules/@types/markdown-it/lib/helpers/parse_link_label.d.ts","../../node_modules/@types/markdown-it/lib/helpers/parse_link_destination.d.ts","../../node_modules/@types/markdown-it/lib/helpers/parse_link_title.d.ts","../../node_modules/@types/markdown-it/lib/helpers/index.d.ts","../../node_modules/@types/markdown-it/lib/ruler.d.ts","../../node_modules/@types/markdown-it/lib/rules_block/state_block.d.ts","../../node_modules/@types/markdown-it/lib/parser_block.d.ts","../../node_modules/@types/markdown-it/lib/rules_core/state_core.d.ts","../../node_modules/@types/markdown-it/lib/parser_core.d.ts","../../node_modules/@types/markdown-it/lib/parser_inline.d.ts","../../node_modules/@types/markdown-it/lib/renderer.d.ts","../../node_modules/@types/markdown-it/lib/index.d.ts","../../node_modules/@types/markdown-it/index.d.ts","../../node_modules/@types/minimist/index.d.ts","../../node_modules/@types/normalize-package-data/index.d.ts","../../node_modules/@types/parse-json/index.d.ts","../../node_modules/@types/resolve/index.d.ts","../../node_modules/@types/semver/classes/semver.d.ts","../../node_modules/@types/semver/functions/parse.d.ts","../../node_modules/@types/semver/functions/valid.d.ts","../../node_modules/@types/semver/functions/clean.d.ts","../../node_modules/@types/semver/functions/inc.d.ts","../../node_modules/@types/semver/functions/diff.d.ts","../../node_modules/@types/semver/functions/major.d.ts","../../node_modules/@types/semver/functions/minor.d.ts","../../node_modules/@types/semver/functions/patch.d.ts","../../node_modules/@types/semver/functions/prerelease.d.ts","../../node_modules/@types/semver/functions/compare.d.ts","../../node_modules/@types/semver/functions/rcompare.d.ts","../../node_modules/@types/semver/functions/compare-loose.d.ts","../../node_modules/@types/semver/functions/compare-build.d.ts","../../node_modules/@types/semver/functions/sort.d.ts","../../node_modules/@types/semver/functions/rsort.d.ts","../../node_modules/@types/semver/functions/gt.d.ts","../../node_modules/@types/semver/functions/lt.d.ts","../../node_modules/@types/semver/functions/eq.d.ts","../../node_modules/@types/semver/functions/neq.d.ts","../../node_modules/@types/semver/functions/gte.d.ts","../../node_modules/@types/semver/functions/lte.d.ts","../../node_modules/@types/semver/functions/cmp.d.ts","../../node_modules/@types/semver/functions/coerce.d.ts","../../node_modules/@types/semver/classes/comparator.d.ts","../../node_modules/@types/semver/classes/range.d.ts","../../node_modules/@types/semver/functions/satisfies.d.ts","../../node_modules/@types/semver/ranges/max-satisfying.d.ts","../../node_modules/@types/semver/ranges/min-satisfying.d.ts","../../node_modules/@types/semver/ranges/to-comparators.d.ts","../../node_modules/@types/semver/ranges/min-version.d.ts","../../node_modules/@types/semver/ranges/valid.d.ts","../../node_modules/@types/semver/ranges/outside.d.ts","../../node_modules/@types/semver/ranges/gtr.d.ts","../../node_modules/@types/semver/ranges/ltr.d.ts","../../node_modules/@types/semver/ranges/intersects.d.ts","../../node_modules/@types/semver/ranges/simplify.d.ts","../../node_modules/@types/semver/ranges/subset.d.ts","../../node_modules/@types/semver/internals/identifiers.d.ts","../../node_modules/@types/semver/index.d.ts","../../node_modules/@types/source-list-map/index.d.ts","../../node_modules/@types/stringify-object/index.d.ts","../../node_modules/@types/tapable/index.d.ts","../../node_modules/@types/uglify-js/node_modules/source-map/source-map.d.ts","../../node_modules/@types/uglify-js/index.d.ts","../../node_modules/@types/webpack-sources/node_modules/source-map/source-map.d.ts","../../node_modules/@types/webpack-sources/lib/source.d.ts","../../node_modules/@types/webpack-sources/lib/compatsource.d.ts","../../node_modules/@types/webpack-sources/lib/concatsource.d.ts","../../node_modules/@types/webpack-sources/lib/originalsource.d.ts","../../node_modules/@types/webpack-sources/lib/prefixsource.d.ts","../../node_modules/@types/webpack-sources/lib/rawsource.d.ts","../../node_modules/@types/webpack-sources/lib/replacesource.d.ts","../../node_modules/@types/webpack-sources/lib/sizeonlysource.d.ts","../../node_modules/@types/webpack-sources/lib/sourcemapsource.d.ts","../../node_modules/@types/webpack-sources/lib/index.d.ts","../../node_modules/@types/webpack-sources/lib/cachedsource.d.ts","../../node_modules/@types/webpack-sources/index.d.ts"],"fileInfos":[{"version":"8730f4bf322026ff5229336391a18bcaa1f94d4f82416c8b2f3954e2ccaae2ba","affectsGlobalScope":true},"dc47c4fa66b9b9890cf076304de2a9c5201e94b740cffdf09f87296d877d71f6","7a387c58583dfca701b6c85e0adaf43fb17d590fb16d5b2dc0a2fbd89f35c467","8a12173c586e95f4433e0c6dc446bc88346be73ffe9ca6eec7aa63c8f3dca7f9","5f4e733ced4e129482ae2186aae29fde948ab7182844c3a5a51dd346182c7b06",{"version":"d3f4771304b6b07e5a2bb992e75af76ac060de78803b1b21f0475ffc5654d817","affectsGlobalScope":true},{"version":"adb996790133eb33b33aadb9c09f15c2c575e71fb57a62de8bf74dbf59ec7dfb","affectsGlobalScope":true},{"version":"8cc8c5a3bac513368b0157f3d8b31cfdcfe78b56d3724f30f80ed9715e404af8","affectsGlobalScope":true},{"version":"cdccba9a388c2ee3fd6ad4018c640a471a6c060e96f1232062223063b0a5ac6a","affectsGlobalScope":true},{"version":"c5c05907c02476e4bde6b7e76a79ffcd948aedd14b6a8f56e4674221b0417398","affectsGlobalScope":true},{"version":"5f406584aef28a331c36523df688ca3650288d14f39c5d2e555c95f0d2ff8f6f","affectsGlobalScope":true},{"version":"22f230e544b35349cfb3bd9110b6ef37b41c6d6c43c3314a31bd0d9652fcec72","affectsGlobalScope":true},{"version":"7ea0b55f6b315cf9ac2ad622b0a7813315bb6e97bf4bb3fbf8f8affbca7dc695","affectsGlobalScope":true},{"version":"3013574108c36fd3aaca79764002b3717da09725a36a6fc02eac386593110f93","affectsGlobalScope":true},{"version":"eb26de841c52236d8222f87e9e6a235332e0788af8c87a71e9e210314300410a","affectsGlobalScope":true},{"version":"3be5a1453daa63e031d266bf342f3943603873d890ab8b9ada95e22389389006","affectsGlobalScope":true},{"version":"17bb1fc99591b00515502d264fa55dc8370c45c5298f4a5c2083557dccba5a2a","affectsGlobalScope":true},{"version":"7ce9f0bde3307ca1f944119f6365f2d776d281a393b576a18a2f2893a2d75c98","affectsGlobalScope":true},{"version":"6a6b173e739a6a99629a8594bfb294cc7329bfb7b227f12e1f7c11bc163b8577","affectsGlobalScope":true},{"version":"81cac4cbc92c0c839c70f8ffb94eb61e2d32dc1c3cf6d95844ca099463cf37ea","affectsGlobalScope":true},{"version":"b0124885ef82641903d232172577f2ceb5d3e60aed4da1153bab4221e1f6dd4e","affectsGlobalScope":true},{"version":"0eb85d6c590b0d577919a79e0084fa1744c1beba6fd0d4e951432fa1ede5510a","affectsGlobalScope":true},{"version":"da233fc1c8a377ba9e0bed690a73c290d843c2c3d23a7bd7ec5cd3d7d73ba1e0","affectsGlobalScope":true},{"version":"d154ea5bb7f7f9001ed9153e876b2d5b8f5c2bb9ec02b3ae0d239ec769f1f2ae","affectsGlobalScope":true},{"version":"bb2d3fb05a1d2ffbca947cc7cbc95d23e1d053d6595391bd325deb265a18d36c","affectsGlobalScope":true},{"version":"c80df75850fea5caa2afe43b9949338ce4e2de086f91713e9af1a06f973872b8","affectsGlobalScope":true},{"version":"09aa50414b80c023553090e2f53827f007a301bc34b0495bfb2c3c08ab9ad1eb","affectsGlobalScope":true},{"version":"2768ef564cfc0689a1b76106c421a2909bdff0acbe87da010785adab80efdd5c","affectsGlobalScope":true},{"version":"52d1bb7ab7a3306fd0375c8bff560feed26ed676a5b0457fa8027b563aecb9a4","affectsGlobalScope":true},{"version":"0396119f8b76a074eddc16de8dbc4231a448f2534f4c64c5ab7b71908eb6e646","affectsGlobalScope":true},"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","f0ae1ac99c66a4827469b8942101642ae65971e36db438afe67d4985caa31222","7f5bced3f1bd3647585b59564e0b0fda67e1c2930325507ee922698fe8366aca",{"version":"d763b9ef68a16f3896187af5b51b5a959b479218cc65c2930bcb440cbbf10728","affectsGlobalScope":true},"0b066351c69855f76970a460fe20a500d20f369a02d2069aa49e1195cd04c3c5","dda5c129fa8b8e72bee6609a4fc48148f58f2f656d70a395d3122431193569f9","9fd40388bab591ded1f8c05b64fbfe3e342c6cd70d594d5238f42dd2186980ff","9848c9644b2c1209f90ca5bf13f81d268365178241ba40bee6cf334643a8d5a0","88fa7615e71089c0cab3b688aae073a6e9dae6f489ec1357da407c155d2e9d84","4e7a6d022b2a963e993bec8a6e92bd6053f2e46495ef058b015cfd25fa6520b9","f69dd422840e809218633c76acb5eed2a6aa81914299f9b8d74101148526e27e",{"version":"fbed0e2c4bbc36c53072322fe678bb9588a7c06e33372e26c947343c579d4857","signature":"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855","affectsGlobalScope":true},{"version":"f36f31a3098f6134bb8a7d4c60e64e9b55fb96324e9985c437873eee178fbdc3","signature":"e2f6b922ccd7b58655b27ec94b0a05383cc045bf7d34a84c7f771d8685a5eb05"},{"version":"709b7e9bbcb674471087b3c0ed1a1f5f8281716606751f8c1ac77ff03a6f8f8d","signature":"98962a67c31e31dbb0f7eb27082465ca9bfd4b59c9f36537a70c27b1a3982e65"},{"version":"e0920a4cf384f4e5b669d369c2b2c807308b3f1b7f0f1fee0336c86e5745a617","signature":"cf3911e52a035c3412ffaac2597bcda4b211022a9f9f3172f1b2f5f5786a9a8e"},{"version":"3c479b29aaea6365e3ab2b22d720b80924f51a4d22048e06bf718056b3420299","signature":"b7a945ae3d451d0150046845f80e668e0dcbeeee2c8b7616b85623c5619b8ecb"},{"version":"f6802c83be81f4724096014bc99fd01745085cdd10004be8841b5fbf6952476a","signature":"4da3eff99723cfb997d249dd6f008c605671fbb47d2f670b4f9e76bf441afa48"},{"version":"e2f1cbd77a92bf2611826404da3e4ab2c60a10019a42c696628a60fbf8aac761","signature":"697ecabf6994c375c59c13d044ecf94d7d01b931a7122e11d0ec4fcc08c6b996"},{"version":"592a1d38f841175483eee47035e578fdf61344fb8f96e245717cdfea387d60dc","signature":"504186e6db5cd2c600ce2f134732e4d6ff94beddac01357e597d720735a3100e"},{"version":"75362edce62f192fff1c8bedbbf6ac9fcf3d1c1664c0de9043379df181b77742","signature":"ca6d208c53e8678ded971927ab4dec73a131ff603b0d3739cb16a905751e7275"},{"version":"b0dc8d7745f05499e819a5bda82f59387bfe164e3f2c97568ea5cee6cf97c3fe","signature":"f47f28fe817c1c06e48ba005a96d4166e0e66b32d1dacd020a0fc8b7af91f20d"},{"version":"8bf18b8b55ef77bd493dd0d1f74dfeea5cba48514c5e0de6f65b7232f19ffee3","signature":"28e03569a522e96af67ad1007d85fd78ed86be623133ff59e6645d77375126e7","affectsGlobalScope":true},"3eb8ad25895d53cc6229dc83decbc338d649ed6f3d5b537c9966293b056b1f57","b25c5f2970d06c729f464c0aeaa64b1a5b5f1355aa93554bb5f9c199b8624b1e","8678956904af215fe917b2df07b6c54f876fa64eb1f8a158e4ff38404cef3ff4","3051751533eee92572241b3cef28333212401408c4e7aa21718714b793c0f4ed","691aea9772797ca98334eb743e7686e29325b02c6931391bcee4cc7bf27a9f3b","6f1d39d26959517da3bd105c552eded4c34702705c64d75b03f54d864b6e41c2","5d1b955e6b1974fe5f47fbde474343113ab701ca30b80e463635a29e58d80944","3b93231babdb3ee9470a7e6103e48bf6585c4185f96941c08a77e097f8f469ae",{"version":"f345b0888d003fd69cb32bad3a0aa04c615ccafc572019e4bd86a52bd5e49e46","affectsGlobalScope":true},"0359682c54e487c4cab2b53b2b4d35cc8dea4d9914bc6abcdb5701f8b8e745a4","6a38e250306ceccbab257d11b846d5bd12491157d20901fa01afe4050c93c1b5","ffa048767a32a0f6354e611b15d8b53d882da1a9a35455c35c3f6811f2416d17","e050a0afcdbb269720a900c85076d18e0c1ab73e580202a2bf6964978181222a",{"version":"68aba9c37b535b42ce96b78a4cfa93813bf4525f86dceb88a6d726c5f7c6c14f","affectsGlobalScope":true},"c438b413e94ff76dfa20ae005f33a1c84f2480d1d66e0fd687501020d0de9b50","bc6a78961535181265845bf9b9e8a147ffd0ca275097ceb670a9b92afa825152","1fc4b0908c44f39b1f2e5a728d472670a0ea0970d2c6b5691c88167fe541ff82","123ec69e4b3a686eb49afd94ebe3292a5c84a867ecbcb6bb84bdd720a12af803",{"version":"51851805d06a6878796c3a00ccf0839fe18111a38d1bae84964c269f16bcc2b7","affectsGlobalScope":true},"90c85ddbb8de82cd19198bda062065fc51b7407c0f206f2e399e65a52e979720","c5ecc351d5eaa36dc682b4c398b57a9d37c108857b71a09464a06e0185831ac2","7ecfe97b43aa6c8b8f90caa599d5648bb559962e74e6f038f73a77320569dd78","7db7569fbb3e2b01ba8751c761cdd3f0debd104170d5665b7dc20a11630df3a9",{"version":"cde4d7f6274468180fa39847b183aec22626e8212ff885d535c53f4cd7c225fd","affectsGlobalScope":true},{"version":"072b0ac82ae8fe05b0d4f2eadb7f6edd0ebd84175ecad2f9e09261290a86bcee","affectsGlobalScope":true},"f6eedd1053167b8a651d8d9c70b1772e1b501264a36dfa881d7d4b30d623a9bc","fb28748ff8d015f52e99daee4f454e57cec1a22141f1257c317f3630a15edeb7","08fb2b0e1ef13a2df43f6d8e97019c36dfbc0475cf4d274c6838e2c9223fe39d","5d9394b829cfd504b2fe17287aaad8ce1dcfb2a2183c962a90a85b96da2c1c90","c969bf4c7cdfe4d5dd28aa09432f99d09ad1d8d8b839959646579521d0467d1a","6c3857edaeeaaf43812f527830ebeece9266b6e8eb5271ab6d2f0008306c9947","bc6a77e750f4d34584e46b1405b771fb69a224197dd6bafe5b0392a29a70b665","46cac76114704902baa535b30fb66a26aeaf9430f3b3ab44746e329f12e85498","ed4ae81196cccc10f297d228bca8d02e31058e6d723a3c5bc4be5fb3c61c6a34","84044697c8b3e08ef24e4b32cfe6440143d07e469a5e34bda0635276d32d9f35","6999f789ed86a40f3bc4d7e644e8d42ffda569465969df8077cd6c4e3505dd76",{"version":"0c9f2b308e5696d0802b613aff47c99f092add29408e654f7ab6026134250c18","affectsGlobalScope":true},"4a9008d79750801375605e6cfefa4e04643f20f2aaa58404c6aae1c894e9b049","884560fda6c3868f925f022adc3a1289fe6507bbb45adb10fa1bbcc73a941bb0","6b2bb67b0942bcfce93e1d6fad5f70afd54940a2b13df7f311201fba54b2cbe9","dd3706b25d06fe23c73d16079e8c66ac775831ef419da00716bf2aee530a04a4","1298327149e93a60c24a3b5db6048f7cc8fd4e3259e91d05fc44306a04b1b873","d67e08745494b000da9410c1ae2fdc9965fc6d593fe0f381a47491f75417d457","b40652bf8ce4a18133b31349086523b219724dca8df3448c1a0742528e7ad5b9","3181290a158e54a78c1a57c41791ec1cbdc860ae565916daa1bf4e425b7edac7","a77fdb357c78b70142b2fdbbfb72958d69e8f765fd2a3c69946c1018e89d4638","3c2ac350c3baa61fd2b1925844109e098f4376d0768a4643abc82754fd752748","826d48e49c905cedb906cbde6ccaf758827ff5867d4daa006b5a79e0fb489357","5ef157fbb39494a581bd24f21b60488fe248d452c479738b5e41b48720ea69b8","289be113bad7ee27ee7fa5b1e373c964c9789a5e9ed7db5ddcb631371120b953","a1136cf18dbe1b9b600c65538fd48609a1a4772d115a0c1d775839fe6544487c","24638ed25631a94a9b0d7b580b146329f82e158e8d1e90171a73d87bebf79255","638f49a0db5d30977533a8cfabf3e10ab30724360424698e8d5fd41ca272e070","d44028ae0127eb3e9fcfa5f55a8b81d64775ce15aca1020fe25c511bbb055834",{"version":"2708349d5a11a5c2e5f3a0765259ebe7ee00cdcc8161cb9990cb4910328442a1","affectsGlobalScope":true},"4e0a4d84b15692ea8669fe4f3d05a4f204567906b1347da7a58b75f45bae48d3","0f04bc8950ad634ac8ac70f704f200ef06f8852af9017f97c446de4def5b3546","d0c575d48d6dad75648017ff18762eb97f9398cc9486541b3070e79ce12719e6","d20072cb51d8baad944bedd935a25c7f10c29744e9a648d2c72c215337356077","35cbbc58882d2c158032d7f24ba8953d7e1caeb8cb98918d85819496109f55d2","8d01c38ccb9af3a4035a68818799e5ef32ccc8cf70bdb83e181e1921d7ad32f6","1d1e6bd176eee5970968423d7e215bfd66828b6db8d54d17afec05a831322633","393137c76bd922ba70a2f8bf1ade4f59a16171a02fb25918c168d48875b0cfb0","6767cce098e1e6369c26258b7a1f9e569c5467d501a47a090136d5ea6e80ae6d","6503fb6addf62f9b10f8564d9869ad824565a914ec1ac3dd7d13da14a3f57036","3594c022901a1c8993b0f78a3f534cfb81e7b619ed215348f7f6882f3db02abc","438284c7c455a29b9c0e2d1e72abc62ee93d9a163029ffe918a34c5db3b92da2","0c75b204aed9cf6ff1c7b4bed87a3ece0d9d6fc857a6350c0c95ed0c38c814e8","187119ff4f9553676a884e296089e131e8cc01691c546273b1d0089c3533ce42","c9f396e71966bd3a890d8a36a6a497dbf260e9b868158ea7824d4b5421210afe","509235563ea2b939e1bbe92aae17e71e6a82ceab8f568b45fb4fce7d72523a32","9364c7566b0be2f7b70ff5285eb34686f83ccb01bda529b82d23b2a844653bfb","00baffbe8a2f2e4875367479489b5d43b5fc1429ecb4a4cc98cfc3009095f52a","c311349ec71bb69399ffc4092853e7d8a86c1ca39ddb4cd129e775c19d985793","3c92b6dfd43cc1c2485d9eba5ff0b74a19bb8725b692773ef1d66dac48cda4bd","4908e4c00832b26ce77a629de8501b0e23a903c094f9e79a7fec313a15da796a","2630a7cbb597e85d713b7ef47f2946d4280d3d4c02733282770741d40672b1a5",{"version":"0714e2046df66c0e93c3330d30dbc0565b3e8cd3ee302cf99e4ede6220e5fec8","affectsGlobalScope":true},"f313731860257325f13351575f381fef333d4dfe30daf5a2e72f894208feea08","951b37f7d86f6012f09e6b35f1de57c69d75f16908cb0adaa56b93675ea0b853","3816fc03ffd9cbd1a7a3362a264756a4a1d547caabea50ca68303046be40e376","0c417b4ec46b88fb62a43ec00204700b560d01eb5677c7faa8ecd34610f096a8","13d29cdeb64e8496424edf42749bbb47de5e42d201cf958911a4638cbcffbd3f","0f9e381eecc5860f693c31fe463b3ca20a64ca9b8db0cf6208cd4a053f064809","95902d5561c6aac5dfc40568a12b0aca324037749dcd32a81f23423bfde69bab","5dfb2aca4136abdc5a2740f14be8134a6e6b66fd53470bb2e954e40f8abfaf3e","577463167dd69bd81f76697dfc3f7b22b77a6152f60a602a9218e52e3183ad67","b8396e9024d554b611cbe31a024b176ba7116063d19354b5a02dccd8f0118989","4b28e1c5bf88d891e07a1403358b81a51b3ba2eae1ffada51cca7476b5ac6407","7150ad575d28bf98fae321a1c0f10ad17b127927811f488ded6ff1d88d4244e5","8b155c4757d197969553de3762c8d23d5866710301de41e1b66b97c9ed867003","93733466609dd8bf72eace502a24ca7574bd073d934216e628f1b615c8d3cb3c","45e9228761aabcadb79c82fb3008523db334491525bdb8e74e0f26eaf7a4f7f4","aeacac2778c9821512b6b889da79ac31606a863610c8f28da1e483579627bf90","569fdb354062fc098a6a3ba93a029edf22d6fe480cf72b231b3c07832b2e7c97","bf9876e62fb7f4237deafab8c7444770ef6e82b4cad2d5dc768664ff340feeb2","6cf60e76d37faf0fbc2f80a873eab0fd545f6b1bf300e7f0823f956ddb3083e9","6adaa6103086f931e3eee20f0987e86e8879e9d13aa6bd6075ccfc58b9c5681c","ee0af0f2b8d3b4d0baf669f2ff6fcef4a8816a473c894cc7c905029f7505fed0","3602dfff3072caea42f23a9b63fb34a7b0c95a62b93ce2add5fe6b159447845e","c9ad058b2cc9ce6dc2ed92960d6d009e8c04bef46d3f5312283debca6869f613","2b8264b2fefd7367e0f20e2c04eed5d3038831fe00f5efbc110ff0131aab899b","8a19491eba2108d5c333c249699f40aff05ad312c04a17504573b27d91f0aede","2b93035328f7778d200252681c1d86285d501ed424825a18f81e4c3028aa51d9","2ac9c8332c5f8510b8bdd571f8271e0f39b0577714d5e95c1e79a12b2616f069","42c21aa963e7b86fa00801d96e88b36803188018d5ad91db2a9101bccd40b3ff","d31eb848cdebb4c55b4893b335a7c0cca95ad66dee13cbb7d0893810c0a9c301","77c1d91a129ba60b8c405f9f539e42df834afb174fe0785f89d92a2c7c16b77a","7a9e0a564fee396cacf706523b5aeed96e04c6b871a8bebefad78499fbffc5bc","906c751ef5822ec0dadcea2f0e9db64a33fb4ee926cc9f7efa38afe5d5371b2a","5387c049e9702f2d2d7ece1a74836a14b47fbebe9bbeb19f94c580a37c855351","c68391fb9efad5d99ff332c65b1606248c4e4a9f1dd9a087204242b56c7126d6","e9cf02252d3a0ced987d24845dcb1f11c1be5541f17e5daa44c6de2d18138d0c","e8b02b879754d85f48489294f99147aeccc352c760d95a6fe2b6e49cd400b2fe","9f6908ab3d8a86c68b86e38578afc7095114e66b2fc36a2a96e9252aac3998e0","0eedb2344442b143ddcd788f87096961cd8572b64f10b4afc3356aa0460171c6","71405cc70f183d029cc5018375f6c35117ffdaf11846c35ebf85ee3956b1b2a6","c68baff4d8ba346130e9753cefe2e487a16731bf17e05fdacc81e8c9a26aae9d","2cd15528d8bb5d0453aa339b4b52e0696e8b07e790c153831c642c3dea5ac8af","479d622e66283ffa9883fbc33e441f7fc928b2277ff30aacbec7b7761b4e9579","ade307876dc5ca267ca308d09e737b611505e015c535863f22420a11fffc1c54","f8cdefa3e0dee639eccbe9794b46f90291e5fd3989fcba60d2f08fde56179fb9","86c5a62f99aac7053976e317dbe9acb2eaf903aaf3d2e5bb1cafe5c2df7b37a8","2b300954ce01a8343866f737656e13243e86e5baef51bd0631b21dcef1f6e954","a2d409a9ffd872d6b9d78ead00baa116bbc73cfa959fce9a2f29d3227876b2a1","b288936f560cd71f4a6002953290de9ff8dfbfbf37f5a9391be5c83322324898","61178a781ef82e0ff54f9430397e71e8f365fc1e3725e0e5346f2de7b0d50dfa","6a6ccb37feb3aad32d9be026a3337db195979cd5727a616fc0f557e974101a54","c649ea79205c029a02272ef55b7ab14ada0903db26144d2205021f24727ac7a3","38e2b02897c6357bbcff729ef84c736727b45cc152abe95a7567caccdfad2a1d","d6610ea7e0b1a7686dba062a1e5544dd7d34140f4545305b7c6afaebfb348341","3dee35db743bdba2c8d19aece7ac049bde6fa587e195d86547c882784e6ba34c","b15e55c5fa977c2f25ca0b1db52cfa2d1fd4bf0baf90a8b90d4a7678ca462ff1","f41d30972724714763a2698ae949fbc463afb203b5fa7c4ad7e4de0871129a17","843dd7b6a7c6269fd43827303f5cbe65c1fecabc30b4670a50d5a15d57daeeb9","f06d8b8567ee9fd799bf7f806efe93b67683ef24f4dea5b23ef12edff4434d9d","6017384f697ff38bc3ef6a546df5b230c3c31329db84cbfe686c83bec011e2b2","e1a5b30d9248549ca0c0bb1d653bafae20c64c4aa5928cc4cd3017b55c2177b0","a593632d5878f17295bd53e1c77f27bf4c15212822f764a2bfc1702f4b413fa0","a868a534ba1c2ca9060b8a13b0ffbbbf78b4be7b0ff80d8c75b02773f7192c29","da7545aba8f54a50fde23e2ede00158dc8112560d934cee58098dfb03aae9b9d","34baf65cfee92f110d6653322e2120c2d368ee64b3c7981dff08ed105c4f19b0","6aee496bf0ecfbf6731aa8cca32f4b6e92cdc0a444911a7d88410408a45ecc5d","67fc055eb86a0632e2e072838f889ffe1754083cb13c8c80a06a7d895d877aae","67d3e19b3b6e2c082ffd11ae5064c7a81b13d151326953b90fc26103067a1945","d558a0fe921ebcc88d3212c2c42108abf9f0d694d67ebdeba37d7728c044f579","2887592574fcdfd087647c539dcb0fbe5af2521270dad4a37f9d17c16190d579","9d74c7330800b325bb19cc8c1a153a612c080a60094e1ab6cfb6e39cf1b88c36","b90c59ac4682368a01c83881b814738eb151de8a58f52eb7edadea2bcffb11b9","8560a87b2e9f8e2c3808c8f6172c9b7eb6c9b08cb9f937db71c285ecf292c81d","ffe3931ff864f28d80ae2f33bd11123ad3d7bad9896b910a1e61504cc093e1f5","083c1bd82f8dc3a1ed6fc9e8eaddf141f7c05df418eca386598821e045253af9","274ebe605bd7f71ce161f9f5328febc7d547a2929f803f04b44ec4a7d8729517","6ca0207e70d985a24396583f55836b10dc181063ab6069733561bfde404d1bad","5908142efeaab38ffdf43927ee0af681ae77e0d7672b956dfb8b6c705dbfe106","f772b188b943549b5c5eb803133314b8aa7689eced80eed0b70e2f30ca07ab9c","0026b816ef05cfbf290e8585820eef0f13250438669107dfc44482bac007b14f","05d64cc1118031b29786632a9a0f6d7cf1dcacb303f27023a466cf3cdc860538","e0fff9119e1a5d2fdd46345734126cd6cb99c2d98a9debf0257047fe3937cc3f","d84398556ba4595ee6be554671da142cfe964cbdebb2f0c517a10f76f2b016c0","e275297155ec3251200abbb334c7f5641fecc68b2a9573e40eed50dff7584762"],"options":{"composite":true,"declaration":true,"module":99,"noFallthroughCasesInSwitch":true,"noImplicitReturns":true,"noUnusedLocals":true,"noUnusedParameters":true,"outDir":"./","preserveConstEnums":true,"rootDir":"./src","strict":true,"target":4,"tsBuildInfoFile":"./tsconfig.tsbuildinfo"},"fileIdsList":[[53],[53,54,55,56,57],[53,55],[63,64],[61,62,63],[78,112],[77,112,114],[118,120,121,122,123,124,125,126,127,128,129,130],[118,119,121,122,123,124,125,126,127,128,129,130],[119,120,121,122,123,124,125,126,127,128,129,130],[118,119,120,122,123,124,125,126,127,128,129,130],[118,119,120,121,123,124,125,126,127,128,129,130],[118,119,120,121,122,124,125,126,127,128,129,130],[118,119,120,121,122,123,125,126,127,128,129,130],[118,119,120,121,122,123,124,126,127,128,129,130],[118,119,120,121,122,123,124,125,127,128,129,130],[118,119,120,121,122,123,124,125,126,128,129,130],[118,119,120,121,122,123,124,125,126,127,129,130],[118,119,120,121,122,123,124,125,126,127,128,130],[118,119,120,121,122,123,124,125,126,127,128,129],[150],[135],[139,140,141],[138],[140],[117,136,137,142,145,147,148,149],[137,143,144,150],[143,146],[137,138,143,150],[137,150],[131,132,133,134],[109,110],[77,78,85,94],[69,77,85],[101],[73,78,86],[94],[75,77,85],[77],[77,79,94,100],[78],[85,94,100],[77,78,80,85,94,97,100],[77,80,97,100],[111],[100],[75,77,94],[67],[99],[77,94],[92,101,103],[73,75,85,94],[66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105],[106,107,108],[85],[91],[77,79,94,100,103],[112],[156,195],[156,180,195],[195],[156],[156,181,195],[156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194],[181,195],[199],[112,202,203,204,205,206,207,208,209,210,211,212],[201,202,211],[202,211],[196,201,202,211],[201,202,203,204,205,206,207,208,209,210,212],[202],[73,201,211],[32],[33,34,35,43,44,45],[43,44,46,47,49,50,51],[33,34,35,43,44,45,48],[33,34,35,41,43,44,45],[32,34,35,36,37,43],[32,33,34,35,37,38,39,40,41,44],[34,37],[43,44],[32,43],[32,44]],"referencedMap":[[55,1],[58,2],[54,1],[56,3],[57,1],[65,4],[64,5],[113,6],[115,7],[119,8],[120,9],[118,10],[121,11],[122,12],[123,13],[124,14],[125,15],[126,16],[127,17],[128,18],[129,19],[130,20],[151,21],[136,22],[142,23],[139,24],[141,25],[150,26],[145,27],[147,28],[148,29],[149,30],[144,30],[146,30],[138,30],[134,22],[135,31],[133,22],[111,32],[69,33],[70,34],[71,35],[72,36],[73,37],[74,38],[76,39],[78,40],[79,41],[80,42],[81,43],[82,44],[112,45],[83,39],[84,46],[85,47],[88,48],[89,49],[92,50],[93,51],[94,39],[97,52],[106,53],[109,54],[99,55],[100,56],[102,37],[104,57],[105,37],[155,58],[180,59],[181,60],[156,61],[159,61],[178,59],[179,59],[169,59],[168,62],[166,59],[161,59],[174,59],[172,59],[176,59],[160,59],[173,59],[177,59],[162,59],[163,59],[175,59],[157,59],[164,59],[165,59],[167,59],[171,59],[182,63],[170,59],[158,59],[195,64],[189,63],[191,65],[190,63],[183,63],[184,63],[186,63],[188,63],[192,65],[193,65],[185,65],[187,65],[200,66],[213,67],[212,68],[203,69],[204,70],[211,71],[205,70],[206,69],[207,69],[208,69],[209,72],[202,73],[210,68],[33,74],[35,74],[46,75],[47,75],[52,76],[49,77],[50,78],[48,74],[51,77],[44,79],[43,80],[45,81]],"exportedModulesMap":[[55,1],[58,2],[54,1],[56,3],[57,1],[65,4],[64,5],[113,6],[115,7],[119,8],[120,9],[118,10],[121,11],[122,12],[123,13],[124,14],[125,15],[126,16],[127,17],[128,18],[129,19],[130,20],[151,21],[136,22],[142,23],[139,24],[141,25],[150,26],[145,27],[147,28],[148,29],[149,30],[144,30],[146,30],[138,30],[134,22],[135,31],[133,22],[111,32],[69,33],[70,34],[71,35],[72,36],[73,37],[74,38],[76,39],[78,40],[79,41],[80,42],[81,43],[82,44],[112,45],[83,39],[84,46],[85,47],[88,48],[89,49],[92,50],[93,51],[94,39],[97,52],[106,53],[109,54],[99,55],[100,56],[102,37],[104,57],[105,37],[155,58],[180,59],[181,60],[156,61],[159,61],[178,59],[179,59],[169,59],[168,62],[166,59],[161,59],[174,59],[172,59],[176,59],[160,59],[173,59],[177,59],[162,59],[163,59],[175,59],[157,59],[164,59],[165,59],[167,59],[171,59],[182,63],[170,59],[158,59],[195,64],[189,63],[191,65],[190,63],[183,63],[184,63],[186,63],[188,63],[192,65],[193,65],[185,65],[187,65],[200,66],[213,67],[212,68],[203,69],[204,70],[211,71],[205,70],[206,69],[207,69],[208,69],[209,72],[202,73],[210,68],[33,74],[35,74],[46,82],[47,82],[52,76],[49,82],[50,82],[48,74],[51,82],[44,83],[43,84]],"semanticDiagnosticsPerFile":[30,55,53,58,54,59,56,57,60,65,61,64,63,113,115,116,62,117,119,120,118,121,122,123,124,125,126,127,128,129,130,151,136,142,140,139,141,150,145,147,148,149,143,144,146,138,137,132,131,134,135,133,114,152,110,67,111,68,69,70,71,72,73,74,75,76,77,78,79,66,107,80,81,82,112,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,106,109,99,100,101,102,103,108,104,105,153,154,155,180,181,156,159,178,179,169,168,166,161,174,172,176,160,173,177,162,163,175,157,164,165,167,171,182,170,158,195,194,189,191,190,183,184,186,188,192,193,185,187,196,197,198,200,199,213,212,203,204,211,205,206,207,208,209,202,210,201,8,7,2,9,10,11,12,13,14,15,16,3,4,20,17,18,19,21,22,23,5,24,25,26,27,28,1,29,6,33,38,36,39,40,37,34,41,35,31,32,42,46,47,52,49,50,48,51,44,43,45],"latestChangedDtsFile":"./index.d.ts"},"version":"4.9.5"}
Index: frontend/node_modules/workbox-strategies/utils/messages.d.ts
===================================================================
--- frontend/node_modules/workbox-strategies/utils/messages.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/utils/messages.d.ts	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,5 @@
+import '../_version.js';
+export declare const messages: {
+    strategyStart: (strategyName: string, request: Request) => string;
+    printFinalResponse: (response?: Response) => void;
+};
Index: frontend/node_modules/workbox-strategies/utils/messages.js
===================================================================
--- frontend/node_modules/workbox-strategies/utils/messages.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/utils/messages.js	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,20 @@
+/*
+  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.
+*/
+import { logger } from 'workbox-core/_private/logger.js';
+import { getFriendlyURL } from 'workbox-core/_private/getFriendlyURL.js';
+import '../_version.js';
+export const messages = {
+    strategyStart: (strategyName, request) => `Using ${strategyName} to respond to '${getFriendlyURL(request.url)}'`,
+    printFinalResponse: (response) => {
+        if (response) {
+            logger.groupCollapsed(`View the final response here.`);
+            logger.log(response || '[No response returned]');
+            logger.groupEnd();
+        }
+    },
+};
Index: frontend/node_modules/workbox-strategies/utils/messages.mjs
===================================================================
--- frontend/node_modules/workbox-strategies/utils/messages.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
+++ frontend/node_modules/workbox-strategies/utils/messages.mjs	(revision 9af201e94b5a79beb92a30858fc54de4bc3b9449)
@@ -0,0 +1,1 @@
+export * from './messages.js';
