123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245 |
- /*
- 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 './assert.js';
- import { executeQuotaErrorCallbacks } from './executeQuotaErrorCallbacks.js';
- import { getFriendlyURL } from './getFriendlyURL.js';
- import { logger } from './logger.js';
- import { pluginUtils } from '../utils/pluginUtils.js';
- import { WorkboxError } from './WorkboxError.js';
- import '../_version.js';
- /**
- * Checks the list of plugins for the cacheKeyWillBeUsed callback, and
- * executes any of those callbacks found in sequence. The final `Request` object
- * returned by the last plugin is treated as the cache key for cache reads
- * and/or writes.
- *
- * @param {Object} options
- * @param {Request} options.request
- * @param {string} options.mode
- * @param {Array<Object>} [options.plugins=[]]
- * @return {Promise<Request>}
- *
- * @private
- * @memberof module:workbox-core
- */
- const _getEffectiveRequest = async ({ request, mode, plugins = [], }) => {
- const cacheKeyWillBeUsedPlugins = pluginUtils.filter(plugins, "cacheKeyWillBeUsed" /* CACHE_KEY_WILL_BE_USED */);
- let effectiveRequest = request;
- for (const plugin of cacheKeyWillBeUsedPlugins) {
- effectiveRequest = await plugin["cacheKeyWillBeUsed" /* CACHE_KEY_WILL_BE_USED */].call(plugin, { mode, request: effectiveRequest });
- if (typeof effectiveRequest === 'string') {
- effectiveRequest = new Request(effectiveRequest);
- }
- if (process.env.NODE_ENV !== 'production') {
- assert.isInstance(effectiveRequest, Request, {
- moduleName: 'Plugin',
- funcName: "cacheKeyWillBeUsed" /* CACHE_KEY_WILL_BE_USED */,
- isReturnValueProblem: true,
- });
- }
- }
- return effectiveRequest;
- };
- /**
- * This method will call cacheWillUpdate on the available plugins (or use
- * status === 200) to determine if the Response is safe and valid to cache.
- *
- * @param {Object} options
- * @param {Request} options.request
- * @param {Response} options.response
- * @param {Event} [options.event]
- * @param {Array<Object>} [options.plugins=[]]
- * @return {Promise<Response>}
- *
- * @private
- * @memberof module:workbox-core
- */
- const _isResponseSafeToCache = async ({ request, response, event, plugins = [], }) => {
- let responseToCache = response;
- let pluginsUsed = false;
- for (const plugin of plugins) {
- if ("cacheWillUpdate" /* CACHE_WILL_UPDATE */ in plugin) {
- pluginsUsed = true;
- const pluginMethod = plugin["cacheWillUpdate" /* CACHE_WILL_UPDATE */];
- responseToCache = await pluginMethod.call(plugin, {
- request,
- response: responseToCache,
- event,
- });
- if (process.env.NODE_ENV !== 'production') {
- if (responseToCache) {
- assert.isInstance(responseToCache, Response, {
- moduleName: 'Plugin',
- funcName: "cacheWillUpdate" /* CACHE_WILL_UPDATE */,
- isReturnValueProblem: true,
- });
- }
- }
- if (!responseToCache) {
- break;
- }
- }
- }
- if (!pluginsUsed) {
- if (process.env.NODE_ENV !== 'production') {
- if (responseToCache) {
- if (responseToCache.status !== 200) {
- if (responseToCache.status === 0) {
- logger.warn(`The response for '${request.url}' is an opaque ` +
- `response. The caching strategy that you're using will not ` +
- `cache opaque responses by default.`);
- }
- else {
- logger.debug(`The response for '${request.url}' returned ` +
- `a status code of '${response.status}' and won't be cached as a ` +
- `result.`);
- }
- }
- }
- }
- responseToCache = responseToCache && responseToCache.status === 200 ?
- responseToCache : undefined;
- }
- return responseToCache ? responseToCache : null;
- };
- /**
- * This is a wrapper around cache.match().
- *
- * @param {Object} options
- * @param {string} options.cacheName Name of the cache to match against.
- * @param {Request} options.request The Request that will be used to look up
- * cache entries.
- * @param {Event} [options.event] The event that prompted the action.
- * @param {Object} [options.matchOptions] Options passed to cache.match().
- * @param {Array<Object>} [options.plugins=[]] Array of plugins.
- * @return {Response} A cached response if available.
- *
- * @private
- * @memberof module:workbox-core
- */
- const matchWrapper = async ({ cacheName, request, event, matchOptions, plugins = [], }) => {
- const cache = await self.caches.open(cacheName);
- const effectiveRequest = await _getEffectiveRequest({
- plugins, request, mode: 'read'
- });
- let cachedResponse = await cache.match(effectiveRequest, matchOptions);
- 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 plugin of plugins) {
- if ("cachedResponseWillBeUsed" /* CACHED_RESPONSE_WILL_BE_USED */ in plugin) {
- const pluginMethod = plugin["cachedResponseWillBeUsed" /* CACHED_RESPONSE_WILL_BE_USED */];
- cachedResponse = await pluginMethod.call(plugin, {
- cacheName,
- event,
- matchOptions,
- cachedResponse,
- request: effectiveRequest,
- });
- if (process.env.NODE_ENV !== 'production') {
- if (cachedResponse) {
- assert.isInstance(cachedResponse, Response, {
- moduleName: 'Plugin',
- funcName: "cachedResponseWillBeUsed" /* CACHED_RESPONSE_WILL_BE_USED */,
- isReturnValueProblem: true,
- });
- }
- }
- }
- }
- return cachedResponse;
- };
- /**
- * Wrapper around cache.put().
- *
- * Will call `cacheDidUpdate` on plugins if the cache was updated, using
- * `matchOptions` when determining what the old entry is.
- *
- * @param {Object} options
- * @param {string} options.cacheName
- * @param {Request} options.request
- * @param {Response} options.response
- * @param {Event} [options.event]
- * @param {Array<Object>} [options.plugins=[]]
- * @param {Object} [options.matchOptions]
- *
- * @private
- * @memberof module:workbox-core
- */
- const putWrapper = async ({ cacheName, request, response, event, plugins = [], matchOptions, }) => {
- if (process.env.NODE_ENV !== 'production') {
- if (request.method && request.method !== 'GET') {
- throw new WorkboxError('attempt-to-cache-non-get-request', {
- url: getFriendlyURL(request.url),
- method: request.method,
- });
- }
- }
- const effectiveRequest = await _getEffectiveRequest({
- plugins, request, mode: 'write'
- });
- 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 _isResponseSafeToCache({
- event,
- plugins,
- response,
- request: effectiveRequest,
- });
- if (!responseToCache) {
- if (process.env.NODE_ENV !== 'production') {
- logger.debug(`Response '${getFriendlyURL(effectiveRequest.url)}' will ` +
- `not be cached.`, responseToCache);
- }
- return;
- }
- const cache = await self.caches.open(cacheName);
- const updatePlugins = pluginUtils.filter(plugins, "cacheDidUpdate" /* CACHE_DID_UPDATE */);
- const oldResponse = updatePlugins.length > 0 ?
- await matchWrapper({ cacheName, matchOptions, request: effectiveRequest }) :
- 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, responseToCache);
- }
- catch (error) {
- // See https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-QuotaExceededError
- if (error.name === 'QuotaExceededError') {
- await executeQuotaErrorCallbacks();
- }
- throw error;
- }
- for (const plugin of updatePlugins) {
- await plugin["cacheDidUpdate" /* CACHE_DID_UPDATE */].call(plugin, {
- cacheName,
- event,
- oldResponse,
- newResponse: responseToCache,
- request: effectiveRequest,
- });
- }
- };
- export const cacheWrapper = {
- put: putWrapper,
- match: matchWrapper,
- };
|