2023-01-26 23:42:20 +05:30
define ( [ 'exports' ] , ( function ( exports ) { 'use strict' ;
// @ts-ignore
try {
2024-08-01 21:08:57 +05:30
self [ 'workbox:core:6.6.0' ] && _ ( ) ;
2023-01-26 23:42:20 +05:30
} catch ( e ) { }
/ *
Copyright 2019 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.
* /
const logger = ( ( ) => {
// Don't overwrite this value if it's already set.
// See https://github.com/GoogleChrome/workbox/pull/2284#issuecomment-560470923
2024-08-01 21:08:57 +05:30
if ( ! ( '__WB_DISABLE_DEV_LOGS' in globalThis ) ) {
2023-01-26 23:42:20 +05:30
self . _ _WB _DISABLE _DEV _LOGS = false ;
}
let inGroup = false ;
const methodToColorMap = {
debug : ` #7f8c8d ` ,
log : ` #2ecc71 ` ,
warn : ` #f39c12 ` ,
error : ` #c0392b ` ,
groupCollapsed : ` #3498db ` ,
groupEnd : null // No colored prefix on groupEnd
} ;
const print = function ( method , args ) {
if ( self . _ _WB _DISABLE _DEV _LOGS ) {
return ;
}
if ( method === 'groupCollapsed' ) {
// Safari doesn't print all console.groupCollapsed() arguments:
// https://bugs.webkit.org/show_bug.cgi?id=182754
if ( /^((?!chrome|android).)*safari/i . test ( navigator . userAgent ) ) {
console [ method ] ( ... args ) ;
return ;
}
}
const styles = [ ` background: ${ methodToColorMap [ method ] } ` , ` border-radius: 0.5em ` , ` color: white ` , ` font-weight: bold ` , ` padding: 2px 0.5em ` ] ;
// When in a group, the workbox prefix is not displayed.
const logPrefix = inGroup ? [ ] : [ '%cworkbox' , styles . join ( ';' ) ] ;
console [ method ] ( ... logPrefix , ... args ) ;
if ( method === 'groupCollapsed' ) {
inGroup = true ;
}
if ( method === 'groupEnd' ) {
inGroup = false ;
}
} ;
// eslint-disable-next-line @typescript-eslint/ban-types
const api = { } ;
const loggerMethods = Object . keys ( methodToColorMap ) ;
for ( const key of loggerMethods ) {
const method = key ;
api [ method ] = ( ... args ) => {
print ( method , args ) ;
} ;
}
return api ;
} ) ( ) ;
/ *
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.
* /
const messages$1 = {
'invalid-value' : ( {
paramName ,
validValueDescription ,
value
} ) => {
if ( ! paramName || ! validValueDescription ) {
throw new Error ( ` Unexpected input to 'invalid-value' error. ` ) ;
}
return ` The ' ${ paramName } ' parameter was given a value with an ` + ` unexpected value. ${ validValueDescription } Received a value of ` + ` ${ JSON . stringify ( value ) } . ` ;
} ,
'not-an-array' : ( {
moduleName ,
className ,
funcName ,
paramName
} ) => {
if ( ! moduleName || ! className || ! funcName || ! paramName ) {
throw new Error ( ` Unexpected input to 'not-an-array' error. ` ) ;
}
return ` The parameter ' ${ paramName } ' passed into ` + ` ' ${ moduleName } . ${ className } . ${ funcName } ()' must be an array. ` ;
} ,
'incorrect-type' : ( {
expectedType ,
paramName ,
moduleName ,
className ,
funcName
} ) => {
if ( ! expectedType || ! paramName || ! moduleName || ! funcName ) {
throw new Error ( ` Unexpected input to 'incorrect-type' error. ` ) ;
}
const classNameStr = className ? ` ${ className } . ` : '' ;
return ` The parameter ' ${ paramName } ' passed into ` + ` ' ${ moduleName } . ${ classNameStr } ` + ` ${ funcName } ()' must be of type ${ expectedType } . ` ;
} ,
'incorrect-class' : ( {
expectedClassName ,
paramName ,
moduleName ,
className ,
funcName ,
isReturnValueProblem
} ) => {
if ( ! expectedClassName || ! moduleName || ! funcName ) {
throw new Error ( ` Unexpected input to 'incorrect-class' error. ` ) ;
}
const classNameStr = className ? ` ${ className } . ` : '' ;
if ( isReturnValueProblem ) {
return ` The return value from ` + ` ' ${ moduleName } . ${ classNameStr } ${ funcName } ()' ` + ` must be an instance of class ${ expectedClassName } . ` ;
}
return ` The parameter ' ${ paramName } ' passed into ` + ` ' ${ moduleName } . ${ classNameStr } ${ funcName } ()' ` + ` must be an instance of class ${ expectedClassName } . ` ;
} ,
'missing-a-method' : ( {
expectedMethod ,
paramName ,
moduleName ,
className ,
funcName
} ) => {
if ( ! expectedMethod || ! paramName || ! moduleName || ! className || ! funcName ) {
throw new Error ( ` Unexpected input to 'missing-a-method' error. ` ) ;
}
return ` ${ moduleName } . ${ className } . ${ funcName } () expected the ` + ` ' ${ paramName } ' parameter to expose a ' ${ expectedMethod } ' method. ` ;
} ,
'add-to-cache-list-unexpected-type' : ( {
entry
} ) => {
return ` An unexpected entry was passed to ` + ` 'workbox-precaching.PrecacheController.addToCacheList()' The entry ` + ` ' ${ JSON . stringify ( entry ) } ' isn't supported. You must supply an array of ` + ` strings with one or more characters, objects with a url property or ` + ` Request objects. ` ;
} ,
'add-to-cache-list-conflicting-entries' : ( {
firstEntry ,
secondEntry
} ) => {
if ( ! firstEntry || ! secondEntry ) {
throw new Error ( ` Unexpected input to ` + ` 'add-to-cache-list-duplicate-entries' error. ` ) ;
}
return ` Two of the entries passed to ` + ` 'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + ` ${ firstEntry } but different revision details. Workbox is ` + ` unable to cache and version the asset correctly. Please remove one ` + ` of the entries. ` ;
} ,
'plugin-error-request-will-fetch' : ( {
thrownErrorMessage
} ) => {
if ( ! thrownErrorMessage ) {
throw new Error ( ` Unexpected input to ` + ` 'plugin-error-request-will-fetch', error. ` ) ;
}
return ` An error was thrown by a plugins 'requestWillFetch()' method. ` + ` The thrown error message was: ' ${ thrownErrorMessage } '. ` ;
} ,
'invalid-cache-name' : ( {
cacheNameId ,
value
} ) => {
if ( ! cacheNameId ) {
throw new Error ( ` Expected a 'cacheNameId' for error 'invalid-cache-name' ` ) ;
}
return ` You must provide a name containing at least one character for ` + ` setCacheDetails({ ${ cacheNameId } : '...'}). Received a value of ` + ` ' ${ JSON . stringify ( value ) } ' ` ;
} ,
'unregister-route-but-not-found-with-method' : ( {
method
} ) => {
if ( ! method ) {
throw new Error ( ` Unexpected input to ` + ` 'unregister-route-but-not-found-with-method' error. ` ) ;
}
return ` The route you're trying to unregister was not previously ` + ` registered for the method type ' ${ method } '. ` ;
} ,
'unregister-route-route-not-registered' : ( ) => {
return ` The route you're trying to unregister was not previously ` + ` registered. ` ;
} ,
'queue-replay-failed' : ( {
name
} ) => {
return ` Replaying the background sync queue ' ${ name } ' failed. ` ;
} ,
'duplicate-queue-name' : ( {
name
} ) => {
return ` The Queue name ' ${ name } ' is already being used. ` + ` All instances of backgroundSync.Queue must be given unique names. ` ;
} ,
'expired-test-without-max-age' : ( {
methodName ,
paramName
} ) => {
return ` The ' ${ methodName } ()' method can only be used when the ` + ` ' ${ paramName } ' is used in the constructor. ` ;
} ,
'unsupported-route-type' : ( {
moduleName ,
className ,
funcName ,
paramName
} ) => {
return ` The supplied ' ${ paramName } ' parameter was an unsupported type. ` + ` Please check the docs for ${ moduleName } . ${ className } . ${ funcName } for ` + ` valid input types. ` ;
} ,
'not-array-of-class' : ( {
value ,
expectedClass ,
moduleName ,
className ,
funcName ,
paramName
} ) => {
return ` The supplied ' ${ paramName } ' parameter must be an array of ` + ` ' ${ expectedClass } ' objects. Received ' ${ JSON . stringify ( value ) } ,'. ` + ` Please check the call to ${ moduleName } . ${ className } . ${ funcName } () ` + ` to fix the issue. ` ;
} ,
'max-entries-or-age-required' : ( {
moduleName ,
className ,
funcName
} ) => {
return ` You must define either config.maxEntries or config.maxAgeSeconds ` + ` in ${ moduleName } . ${ className } . ${ funcName } ` ;
} ,
'statuses-or-headers-required' : ( {
moduleName ,
className ,
funcName
} ) => {
return ` You must define either config.statuses or config.headers ` + ` in ${ moduleName } . ${ className } . ${ funcName } ` ;
} ,
'invalid-string' : ( {
moduleName ,
funcName ,
paramName
} ) => {
if ( ! paramName || ! moduleName || ! funcName ) {
throw new Error ( ` Unexpected input to 'invalid-string' error. ` ) ;
}
return ` When using strings, the ' ${ paramName } ' parameter must start with ` + ` 'http' (for cross-origin matches) or '/' (for same-origin matches). ` + ` Please see the docs for ${ moduleName } . ${ funcName } () for ` + ` more info. ` ;
} ,
'channel-name-required' : ( ) => {
return ` You must provide a channelName to construct a ` + ` BroadcastCacheUpdate instance. ` ;
} ,
'invalid-responses-are-same-args' : ( ) => {
return ` The arguments passed into responsesAreSame() appear to be ` + ` invalid. Please ensure valid Responses are used. ` ;
} ,
'expire-custom-caches-only' : ( ) => {
return ` You must provide a 'cacheName' property when using the ` + ` expiration plugin with a runtime caching strategy. ` ;
} ,
'unit-must-be-bytes' : ( {
normalizedRangeHeader
} ) => {
if ( ! normalizedRangeHeader ) {
throw new Error ( ` Unexpected input to 'unit-must-be-bytes' error. ` ) ;
}
return ` The 'unit' portion of the Range header must be set to 'bytes'. ` + ` The Range header provided was " ${ normalizedRangeHeader } " ` ;
} ,
'single-range-only' : ( {
normalizedRangeHeader
} ) => {
if ( ! normalizedRangeHeader ) {
throw new Error ( ` Unexpected input to 'single-range-only' error. ` ) ;
}
return ` Multiple ranges are not supported. Please use a single start ` + ` value, and optional end value. The Range header provided was ` + ` " ${ normalizedRangeHeader } " ` ;
} ,
'invalid-range-values' : ( {
normalizedRangeHeader
} ) => {
if ( ! normalizedRangeHeader ) {
throw new Error ( ` Unexpected input to 'invalid-range-values' error. ` ) ;
}
return ` The Range header is missing both start and end values. At least ` + ` one of those values is needed. The Range header provided was ` + ` " ${ normalizedRangeHeader } " ` ;
} ,
'no-range-header' : ( ) => {
return ` No Range header was found in the Request provided. ` ;
} ,
'range-not-satisfiable' : ( {
size ,
start ,
end
} ) => {
return ` The start ( ${ start } ) and end ( ${ end } ) values in the Range are ` + ` not satisfiable by the cached response, which is ${ size } bytes. ` ;
} ,
'attempt-to-cache-non-get-request' : ( {
url ,
method
} ) => {
return ` Unable to cache ' ${ url } ' because it is a ' ${ method } ' request and ` + ` only 'GET' requests can be cached. ` ;
} ,
'cache-put-with-no-response' : ( {
url
} ) => {
return ` There was an attempt to cache ' ${ url } ' but the response was not ` + ` defined. ` ;
} ,
'no-response' : ( {
url ,
error
} ) => {
let message = ` The strategy could not generate a response for ' ${ url } '. ` ;
if ( error ) {
message += ` The underlying error is ${ error } . ` ;
}
return message ;
} ,
'bad-precaching-response' : ( {
url ,
status
} ) => {
return ` The precaching request for ' ${ url } ' failed ` + ( status ? ` with an HTTP status of ${ status } . ` : ` . ` ) ;
} ,
'non-precached-url' : ( {
url
} ) => {
return ` createHandlerBoundToURL(' ${ url } ') was called, but that URL is ` + ` not precached. Please pass in a URL that is precached instead. ` ;
} ,
'add-to-cache-list-conflicting-integrities' : ( {
url
} ) => {
return ` Two of the entries passed to ` + ` 'workbox-precaching.PrecacheController.addToCacheList()' had the URL ` + ` ${ url } with different integrity values. Please remove one of them. ` ;
} ,
'missing-precache-entry' : ( {
cacheName ,
url
} ) => {
return ` Unable to find a precached response in ${ cacheName } for ${ url } . ` ;
} ,
'cross-origin-copy-response' : ( {
origin
} ) => {
return ` workbox-core.copyResponse() can only be used with same-origin ` + ` responses. It was passed a response with origin ${ origin } . ` ;
} ,
'opaque-streams-source' : ( {
type
} ) => {
const message = ` One of the workbox-streams sources resulted in an ` + ` ' ${ type } ' response. ` ;
if ( type === 'opaqueredirect' ) {
return ` ${ message } Please do not use a navigation request that results ` + ` in a redirect as a source. ` ;
}
return ` ${ message } Please ensure your sources are CORS-enabled. ` ;
}
} ;
/ *
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.
* /
const generatorFunction = ( code , details = { } ) => {
const message = messages$1 [ code ] ;
if ( ! message ) {
throw new Error ( ` Unable to find message for code ' ${ code } '. ` ) ;
}
return message ( details ) ;
} ;
const messageGenerator = generatorFunction ;
/ *
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.
* /
/ * *
* Workbox errors should be thrown with this class .
* This allows use to ensure the type easily in tests ,
* helps developers identify errors from workbox
* easily and allows use to optimise error
* messages correctly .
*
* @ private
* /
class WorkboxError extends Error {
/ * *
*
* @ param { string } errorCode The error code that
* identifies this particular error .
* @ param { Object = } details Any relevant arguments
* that will help developers identify issues should
* be added as a key on the context object .
* /
constructor ( errorCode , details ) {
const message = messageGenerator ( errorCode , details ) ;
super ( message ) ;
this . name = errorCode ;
this . details = details ;
}
}
/ *
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.
* /
/ *
* This method throws if the supplied value is not an array .
* The destructed values are required to produce a meaningful error for users .
* The destructed and restructured object is so it ' s clear what is
* needed .
* /
const isArray = ( value , details ) => {
if ( ! Array . isArray ( value ) ) {
throw new WorkboxError ( 'not-an-array' , details ) ;
}
} ;
const hasMethod = ( object , expectedMethod , details ) => {
const type = typeof object [ expectedMethod ] ;
if ( type !== 'function' ) {
details [ 'expectedMethod' ] = expectedMethod ;
throw new WorkboxError ( 'missing-a-method' , details ) ;
}
} ;
const isType = ( object , expectedType , details ) => {
if ( typeof object !== expectedType ) {
details [ 'expectedType' ] = expectedType ;
throw new WorkboxError ( 'incorrect-type' , details ) ;
}
} ;
const isInstance = ( object ,
// Need the general type to do the check later.
// eslint-disable-next-line @typescript-eslint/ban-types
expectedClass , details ) => {
if ( ! ( object instanceof expectedClass ) ) {
details [ 'expectedClassName' ] = expectedClass . name ;
throw new WorkboxError ( 'incorrect-class' , details ) ;
}
} ;
const isOneOf = ( value , validValues , details ) => {
if ( ! validValues . includes ( value ) ) {
details [ 'validValueDescription' ] = ` Valid values are ${ JSON . stringify ( validValues ) } . ` ;
throw new WorkboxError ( 'invalid-value' , details ) ;
}
} ;
const isArrayOfClass = ( value ,
// Need general type to do check later.
expectedClass ,
// eslint-disable-line
details ) => {
const error = new WorkboxError ( 'not-array-of-class' , details ) ;
if ( ! Array . isArray ( value ) ) {
throw error ;
}
for ( const item of value ) {
if ( ! ( item instanceof expectedClass ) ) {
throw error ;
}
}
} ;
const finalAssertExports = {
hasMethod ,
isArray ,
isInstance ,
isOneOf ,
isType ,
isArrayOfClass
} ;
// @ts-ignore
try {
2024-08-01 21:08:57 +05:30
self [ 'workbox:routing:6.6.0' ] && _ ( ) ;
2023-01-26 23:42:20 +05:30
} catch ( e ) { }
/ *
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.
* /
/ * *
* The default HTTP method , 'GET' , used when there ' s no specific method
* configured for a route .
*
* @ type { string }
*
* @ private
* /
const defaultMethod = 'GET' ;
/ * *
* The list of valid HTTP methods associated with requests that could be routed .
*
* @ type { Array < string > }
*
* @ private
* /
const validMethods = [ 'DELETE' , 'GET' , 'HEAD' , 'PATCH' , 'POST' , 'PUT' ] ;
/ *
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.
* /
/ * *
* @ param { function ( ) | Object } handler Either a function , or an object with a
* 'handle' method .
* @ return { Object } An object with a handle method .
*
* @ private
* /
const normalizeHandler = handler => {
if ( handler && typeof handler === 'object' ) {
{
finalAssertExports . hasMethod ( handler , 'handle' , {
moduleName : 'workbox-routing' ,
className : 'Route' ,
funcName : 'constructor' ,
paramName : 'handler'
} ) ;
}
return handler ;
} else {
{
finalAssertExports . isType ( handler , 'function' , {
moduleName : 'workbox-routing' ,
className : 'Route' ,
funcName : 'constructor' ,
paramName : 'handler'
} ) ;
}
return {
handle : handler
} ;
}
} ;
/ *
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.
* /
/ * *
* A ` Route ` consists of a pair of callback functions , "match" and "handler" .
* The "match" callback determine if a route should be used to "handle" a
* request by returning a non - falsy value if it can . The "handler" callback
* is called when there is a match and should return a Promise that resolves
* to a ` Response ` .
*
* @ memberof workbox - routing
* /
class Route {
/ * *
* Constructor for Route class .
*
* @ param { workbox - routing ~ matchCallback } match
* A callback function that determines whether the route matches a given
* ` fetch ` event by returning a non - falsy value .
* @ param { workbox - routing ~ handlerCallback } handler A callback
* function that returns a Promise resolving to a Response .
* @ param { string } [ method = 'GET' ] The HTTP method to match the Route
* against .
* /
constructor ( match , handler , method = defaultMethod ) {
{
finalAssertExports . isType ( match , 'function' , {
moduleName : 'workbox-routing' ,
className : 'Route' ,
funcName : 'constructor' ,
paramName : 'match'
} ) ;
if ( method ) {
finalAssertExports . isOneOf ( method , validMethods , {
paramName : 'method'
} ) ;
}
}
// These values are referenced directly by Router so cannot be
// altered by minificaton.
this . handler = normalizeHandler ( handler ) ;
this . match = match ;
this . method = method ;
}
/ * *
*
* @ param { workbox - routing - handlerCallback } handler A callback
* function that returns a Promise resolving to a Response
* /
setCatchHandler ( handler ) {
this . catchHandler = normalizeHandler ( handler ) ;
}
}
/ *
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.
* /
/ * *
* RegExpRoute makes it easy to create a regular expression based
* { @ link workbox - routing . Route } .
*
* For same - origin requests the RegExp only needs to match part of the URL . For
* requests against third - party servers , you must define a RegExp that matches
* the start of the URL .
*
* @ memberof workbox - routing
* @ extends workbox - routing . Route
* /
class RegExpRoute extends Route {
/ * *
* If the regular expression contains
* [ capture groups ] { @ link https : //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references},
* the captured values will be passed to the
* { @ link workbox - routing ~ handlerCallback } ` params `
* argument .
*
* @ param { RegExp } regExp The regular expression to match against URLs .
* @ param { workbox - routing ~ handlerCallback } handler A callback
* function that returns a Promise resulting in a Response .
* @ param { string } [ method = 'GET' ] The HTTP method to match the Route
* against .
* /
constructor ( regExp , handler , method ) {
{
finalAssertExports . isInstance ( regExp , RegExp , {
moduleName : 'workbox-routing' ,
className : 'RegExpRoute' ,
funcName : 'constructor' ,
paramName : 'pattern'
} ) ;
}
const match = ( {
url
} ) => {
const result = regExp . exec ( url . href ) ;
// Return immediately if there's no match.
if ( ! result ) {
return ;
}
// Require that the match start at the first character in the URL string
// if it's a cross-origin request.
// See https://github.com/GoogleChrome/workbox/issues/281 for the context
// behind this behavior.
if ( url . origin !== location . origin && result . index !== 0 ) {
{
logger . debug ( ` The regular expression ' ${ regExp . toString ( ) } ' only partially matched ` + ` against the cross-origin URL ' ${ url . toString ( ) } '. RegExpRoute's will only ` + ` handle cross-origin requests if they match the entire URL. ` ) ;
}
return ;
}
// If the route matches, but there aren't any capture groups defined, then
// this will return [], which is truthy and therefore sufficient to
// indicate a match.
// If there are capture groups, then it will return their values.
return result . slice ( 1 ) ;
} ;
super ( match , handler , method ) ;
}
}
/ *
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.
* /
const getFriendlyURL = url => {
const urlObj = new URL ( String ( url ) , location . href ) ;
// See https://github.com/GoogleChrome/workbox/issues/2323
// We want to include everything, except for the origin if it's same-origin.
return urlObj . href . replace ( new RegExp ( ` ^ ${ location . origin } ` ) , '' ) ;
} ;
/ *
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.
* /
/ * *
* The Router can be used to process a ` FetchEvent ` using one or more
* { @ link workbox - routing . Route } , responding with a ` Response ` if
* a matching route exists .
*
* If no route matches a given a request , the Router will use a "default"
* handler if one is defined .
*
* Should the matching Route throw an error , the Router will use a "catch"
* handler if one is defined to gracefully deal with issues and respond with a
* Request .
*
* If a request matches multiple routes , the * * earliest * * registered route will
* be used to respond to the request .
*
* @ memberof workbox - routing
* /
class Router {
/ * *
* Initializes a new Router .
* /
constructor ( ) {
this . _routes = new Map ( ) ;
this . _defaultHandlerMap = new Map ( ) ;
}
/ * *
* @ return { Map < string , Array < workbox - routing . Route >> } routes A ` Map ` of HTTP
* method name ( 'GET' , etc . ) to an array of all the corresponding ` Route `
* instances that are registered .
* /
get routes ( ) {
return this . _routes ;
}
/ * *
* Adds a fetch event listener to respond to events when a route matches
* the event ' s request .
* /
addFetchListener ( ) {
// See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705
self . addEventListener ( 'fetch' , event => {
const {
request
} = event ;
const responsePromise = this . handleRequest ( {
request ,
event
} ) ;
if ( responsePromise ) {
event . respondWith ( responsePromise ) ;
}
} ) ;
}
/ * *
* Adds a message event listener for URLs to cache from the window .
* This is useful to cache resources loaded on the page prior to when the
* service worker started controlling it .
*
* The format of the message data sent from the window should be as follows .
* Where the ` urlsToCache ` array may consist of URL strings or an array of
* URL string + ` requestInit ` object ( the same as you ' d pass to ` fetch() ` ) .
*
* ` ` `
* {
* type : 'CACHE_URLS' ,
* payload : {
* urlsToCache : [
* './script1.js' ,
* './script2.js' ,
* [ './script3.js' , { mode : 'no-cors' } ] ,
* ] ,
* } ,
* }
* ` ` `
* /
addCacheListener ( ) {
// See https://github.com/Microsoft/TypeScript/issues/28357#issuecomment-436484705
self . addEventListener ( 'message' , event => {
// event.data is type 'any'
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if ( event . data && event . data . type === 'CACHE_URLS' ) {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const {
payload
} = event . data ;
{
logger . debug ( ` Caching URLs from the window ` , payload . urlsToCache ) ;
}
const requestPromises = Promise . all ( payload . urlsToCache . map ( entry => {
if ( typeof entry === 'string' ) {
entry = [ entry ] ;
}
const request = new Request ( ... entry ) ;
return this . handleRequest ( {
request ,
event
} ) ;
// TODO(philipwalton): TypeScript errors without this typecast for
// some reason (probably a bug). The real type here should work but
// doesn't: `Array<Promise<Response> | undefined>`.
} ) ) ; // TypeScript
event . waitUntil ( requestPromises ) ;
// If a MessageChannel was used, reply to the message on success.
if ( event . ports && event . ports [ 0 ] ) {
void requestPromises . then ( ( ) => event . ports [ 0 ] . postMessage ( true ) ) ;
}
}
} ) ;
}
/ * *
* Apply the routing rules to a FetchEvent object to get a Response from an
* appropriate Route ' s handler .
*
* @ param { Object } options
* @ param { Request } options . request The request to handle .
* @ param { ExtendableEvent } options . event The event that triggered the
* request .
* @ return { Promise < Response > | undefined } A promise is returned if a
* registered route can handle the request . If there is no matching
* route and there ' s no ` defaultHandler ` , ` undefined ` is returned .
* /
handleRequest ( {
request ,
event
} ) {
{
finalAssertExports . isInstance ( request , Request , {
moduleName : 'workbox-routing' ,
className : 'Router' ,
funcName : 'handleRequest' ,
paramName : 'options.request'
} ) ;
}
const url = new URL ( request . url , location . href ) ;
if ( ! url . protocol . startsWith ( 'http' ) ) {
{
logger . debug ( ` Workbox Router only supports URLs that start with 'http'. ` ) ;
}
return ;
}
const sameOrigin = url . origin === location . origin ;
const {
params ,
route
} = this . findMatchingRoute ( {
event ,
request ,
sameOrigin ,
url
} ) ;
let handler = route && route . handler ;
const debugMessages = [ ] ;
{
if ( handler ) {
debugMessages . push ( [ ` Found a route to handle this request: ` , route ] ) ;
if ( params ) {
debugMessages . push ( [ ` Passing the following params to the route's handler: ` , params ] ) ;
}
}
}
// If we don't have a handler because there was no matching route, then
// fall back to defaultHandler if that's defined.
const method = request . method ;
if ( ! handler && this . _defaultHandlerMap . has ( method ) ) {
{
debugMessages . push ( ` Failed to find a matching route. Falling ` + ` back to the default handler for ${ method } . ` ) ;
}
handler = this . _defaultHandlerMap . get ( method ) ;
}
if ( ! handler ) {
{
// No handler so Workbox will do nothing. If logs is set of debug
// i.e. verbose, we should print out this information.
logger . debug ( ` No route found for: ${ getFriendlyURL ( url ) } ` ) ;
}
return ;
}
{
// We have a handler, meaning Workbox is going to handle the route.
// print the routing details to the console.
logger . groupCollapsed ( ` Router is responding to: ${ getFriendlyURL ( url ) } ` ) ;
debugMessages . forEach ( msg => {
if ( Array . isArray ( msg ) ) {
logger . log ( ... msg ) ;
} else {
logger . log ( msg ) ;
}
} ) ;
logger . groupEnd ( ) ;
}
// Wrap in try and catch in case the handle method throws a synchronous
// error. It should still callback to the catch handler.
let responsePromise ;
try {
responsePromise = handler . handle ( {
url ,
request ,
event ,
params
} ) ;
} catch ( err ) {
responsePromise = Promise . reject ( err ) ;
}
// Get route's catch handler, if it exists
const catchHandler = route && route . catchHandler ;
if ( responsePromise instanceof Promise && ( this . _catchHandler || catchHandler ) ) {
responsePromise = responsePromise . catch ( async err => {
// If there's a route catch handler, process that first
if ( catchHandler ) {
{
// Still include URL here as it will be async from the console group
// and may not make sense without the URL
logger . groupCollapsed ( ` Error thrown when responding to: ` + ` ${ getFriendlyURL ( url ) } . Falling back to route's Catch Handler. ` ) ;
logger . error ( ` Error thrown by: ` , route ) ;
logger . error ( err ) ;
logger . groupEnd ( ) ;
}
try {
return await catchHandler . handle ( {
url ,
request ,
event ,
params
} ) ;
} catch ( catchErr ) {
if ( catchErr instanceof Error ) {
err = catchErr ;
}
}
}
if ( this . _catchHandler ) {
{
// Still include URL here as it will be async from the console group
// and may not make sense without the URL
logger . groupCollapsed ( ` Error thrown when responding to: ` + ` ${ getFriendlyURL ( url ) } . Falling back to global Catch Handler. ` ) ;
logger . error ( ` Error thrown by: ` , route ) ;
logger . error ( err ) ;
logger . groupEnd ( ) ;
}
return this . _catchHandler . handle ( {
url ,
request ,
event
} ) ;
}
throw err ;
} ) ;
}
return responsePromise ;
}
/ * *
* Checks a request and URL ( and optionally an event ) against the list of
* registered routes , and if there ' s a match , returns the corresponding
* route along with any params generated by the match .
*
* @ param { Object } options
* @ param { URL } options . url
* @ param { boolean } options . sameOrigin The result of comparing ` url.origin `
* against the current origin .
* @ param { Request } options . request The request to match .
* @ param { Event } options . event The corresponding event .
* @ return { Object } An object with ` route ` and ` params ` properties .
* They are populated if a matching route was found or ` undefined `
* otherwise .
* /
findMatchingRoute ( {
url ,
sameOrigin ,
request ,
event
} ) {
const routes = this . _routes . get ( request . method ) || [ ] ;
for ( const route of routes ) {
let params ;
// route.match returns type any, not possible to change right now.
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const matchResult = route . match ( {
url ,
sameOrigin ,
request ,
event
} ) ;
if ( matchResult ) {
{
// Warn developers that using an async matchCallback is almost always
// not the right thing to do.
if ( matchResult instanceof Promise ) {
logger . warn ( ` While routing ${ getFriendlyURL ( url ) } , an async ` + ` matchCallback function was used. Please convert the ` + ` following route to use a synchronous matchCallback function: ` , route ) ;
}
}
// See https://github.com/GoogleChrome/workbox/issues/2079
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
params = matchResult ;
if ( Array . isArray ( params ) && params . length === 0 ) {
// Instead of passing an empty array in as params, use undefined.
params = undefined ;
} else if ( matchResult . constructor === Object &&
// eslint-disable-line
Object . keys ( matchResult ) . length === 0 ) {
// Instead of passing an empty object in as params, use undefined.
params = undefined ;
} else if ( typeof matchResult === 'boolean' ) {
// For the boolean value true (rather than just something truth-y),
// don't set params.
// See https://github.com/GoogleChrome/workbox/pull/2134#issuecomment-513924353
params = undefined ;
}
// Return early if have a match.
return {
route ,
params
} ;
}
}
// If no match was found above, return and empty object.
return { } ;
}
/ * *
* Define a default ` handler ` that ' s called when no routes explicitly
* match the incoming request .
*
* Each HTTP method ( 'GET' , 'POST' , etc . ) gets its own default handler .
*
* Without a default handler , unmatched requests will go against the
* network as if there were no service worker present .
*
* @ param { workbox - routing ~ handlerCallback } handler A callback
* function that returns a Promise resulting in a Response .
* @ param { string } [ method = 'GET' ] The HTTP method to associate with this
* default handler . Each method has its own default .
* /
setDefaultHandler ( handler , method = defaultMethod ) {
this . _defaultHandlerMap . set ( method , normalizeHandler ( handler ) ) ;
}
/ * *
* If a Route throws an error while handling a request , this ` handler `
* will be called and given a chance to provide a response .
*
* @ param { workbox - routing ~ handlerCallback } handler A callback
* function that returns a Promise resulting in a Response .
* /
setCatchHandler ( handler ) {
this . _catchHandler = normalizeHandler ( handler ) ;
}
/ * *
* Registers a route with the router .
*
* @ param { workbox - routing . Route } route The route to register .
* /
registerRoute ( route ) {
{
finalAssertExports . isType ( route , 'object' , {
moduleName : 'workbox-routing' ,
className : 'Router' ,
funcName : 'registerRoute' ,
paramName : 'route'
} ) ;
finalAssertExports . hasMethod ( route , 'match' , {
moduleName : 'workbox-routing' ,
className : 'Router' ,
funcName : 'registerRoute' ,
paramName : 'route'
} ) ;
finalAssertExports . isType ( route . handler , 'object' , {
moduleName : 'workbox-routing' ,
className : 'Router' ,
funcName : 'registerRoute' ,
paramName : 'route'
} ) ;
finalAssertExports . hasMethod ( route . handler , 'handle' , {
moduleName : 'workbox-routing' ,
className : 'Router' ,
funcName : 'registerRoute' ,
paramName : 'route.handler'
} ) ;
finalAssertExports . isType ( route . method , 'string' , {
moduleName : 'workbox-routing' ,
className : 'Router' ,
funcName : 'registerRoute' ,
paramName : 'route.method'
} ) ;
}
if ( ! this . _routes . has ( route . method ) ) {
this . _routes . set ( route . method , [ ] ) ;
}
// Give precedence to all of the earlier routes by adding this additional
// route to the end of the array.
this . _routes . get ( route . method ) . push ( route ) ;
}
/ * *
* Unregisters a route with the router .
*
* @ param { workbox - routing . Route } route The route to unregister .
* /
unregisterRoute ( route ) {
if ( ! this . _routes . has ( route . method ) ) {
throw new WorkboxError ( 'unregister-route-but-not-found-with-method' , {
method : route . method
} ) ;
}
const routeIndex = this . _routes . get ( route . method ) . indexOf ( route ) ;
if ( routeIndex > - 1 ) {
this . _routes . get ( route . method ) . splice ( routeIndex , 1 ) ;
} else {
throw new WorkboxError ( 'unregister-route-route-not-registered' ) ;
}
}
}
/ *
Copyright 2019 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.
* /
let defaultRouter ;
/ * *
* Creates a new , singleton Router instance if one does not exist . If one
* does already exist , that instance is returned .
*
* @ private
* @ return { Router }
* /
const getOrCreateDefaultRouter = ( ) => {
if ( ! defaultRouter ) {
defaultRouter = new Router ( ) ;
// The helpers that use the default Router assume these listeners exist.
defaultRouter . addFetchListener ( ) ;
defaultRouter . addCacheListener ( ) ;
}
return defaultRouter ;
} ;
/ *
Copyright 2019 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.
* /
/ * *
* Easily register a RegExp , string , or function with a caching
* strategy to a singleton Router instance .
*
* This method will generate a Route for you if needed and
* call { @ link workbox - routing . Router # registerRoute } .
*
* @ param { RegExp | string | workbox - routing . Route ~ matchCallback | workbox - routing . Route } capture
* If the capture param is a ` Route ` , all other arguments will be ignored .
* @ param { workbox - routing ~ handlerCallback } [ handler ] A callback
* function that returns a Promise resulting in a Response . This parameter
* is required if ` capture ` is not a ` Route ` object .
* @ param { string } [ method = 'GET' ] The HTTP method to match the Route
* against .
* @ return { workbox - routing . Route } The generated ` Route ` .
*
* @ memberof workbox - routing
* /
function registerRoute ( capture , handler , method ) {
let route ;
if ( typeof capture === 'string' ) {
const captureUrl = new URL ( capture , location . href ) ;
{
if ( ! ( capture . startsWith ( '/' ) || capture . startsWith ( 'http' ) ) ) {
throw new WorkboxError ( 'invalid-string' , {
moduleName : 'workbox-routing' ,
funcName : 'registerRoute' ,
paramName : 'capture'
} ) ;
}
// We want to check if Express-style wildcards are in the pathname only.
// TODO: Remove this log message in v4.
const valueToCheck = capture . startsWith ( 'http' ) ? captureUrl . pathname : capture ;
// See https://github.com/pillarjs/path-to-regexp#parameters
const wildcards = '[*:?+]' ;
if ( new RegExp ( ` ${ wildcards } ` ) . exec ( valueToCheck ) ) {
logger . debug ( ` The ' $ capture' parameter contains an Express-style wildcard ` + ` character ( ${ wildcards } ). Strings are now always interpreted as ` + ` exact matches; use a RegExp for partial or wildcard matches. ` ) ;
}
}
const matchCallback = ( {
url
} ) => {
{
if ( url . pathname === captureUrl . pathname && url . origin !== captureUrl . origin ) {
logger . debug ( ` ${ capture } only partially matches the cross-origin URL ` + ` ${ url . toString ( ) } . This route will only handle cross-origin requests ` + ` if they match the entire URL. ` ) ;
}
}
return url . href === captureUrl . href ;
} ;
// If `capture` is a string then `handler` and `method` must be present.
route = new Route ( matchCallback , handler , method ) ;
} else if ( capture instanceof RegExp ) {
// If `capture` is a `RegExp` then `handler` and `method` must be present.
route = new RegExpRoute ( capture , handler , method ) ;
} else if ( typeof capture === 'function' ) {
// If `capture` is a function then `handler` and `method` must be present.
route = new Route ( capture , handler , method ) ;
} else if ( capture instanceof Route ) {
route = capture ;
} else {
throw new WorkboxError ( 'unsupported-route-type' , {
moduleName : 'workbox-routing' ,
funcName : 'registerRoute' ,
paramName : 'capture'
} ) ;
}
const defaultRouter = getOrCreateDefaultRouter ( ) ;
defaultRouter . registerRoute ( route ) ;
return route ;
}
// @ts-ignore
try {
2024-08-01 21:08:57 +05:30
self [ 'workbox:strategies:6.6.0' ] && _ ( ) ;
2023-01-26 23:42:20 +05:30
} catch ( e ) { }
/ *
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.
* /
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 ;
}
} ;
/ *
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.
* /
const _cacheNameDetails = {
googleAnalytics : 'googleAnalytics' ,
precache : 'precache-v2' ,
prefix : 'workbox' ,
runtime : 'runtime' ,
suffix : typeof registration !== 'undefined' ? registration . scope : ''
} ;
const _createCacheName = cacheName => {
return [ _cacheNameDetails . prefix , cacheName , _cacheNameDetails . suffix ] . filter ( value => value && value . length > 0 ) . join ( '-' ) ;
} ;
const eachCacheNameDetail = fn => {
for ( const key of Object . keys ( _cacheNameDetails ) ) {
fn ( key ) ;
}
} ;
const cacheNames = {
updateDetails : details => {
eachCacheNameDetail ( key => {
if ( typeof details [ key ] === 'string' ) {
_cacheNameDetails [ key ] = details [ key ] ;
}
} ) ;
} ,
getGoogleAnalyticsName : userCacheName => {
return userCacheName || _createCacheName ( _cacheNameDetails . googleAnalytics ) ;
} ,
getPrecacheName : userCacheName => {
return userCacheName || _createCacheName ( _cacheNameDetails . precache ) ;
} ,
getPrefix : ( ) => {
return _cacheNameDetails . prefix ;
} ,
getRuntimeName : userCacheName => {
return userCacheName || _createCacheName ( _cacheNameDetails . runtime ) ;
} ,
getSuffix : ( ) => {
return _cacheNameDetails . suffix ;
}
} ;
/ *
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.
* /
function stripParams ( fullURL , ignoreParams ) {
const strippedURL = new URL ( fullURL ) ;
for ( const param of ignoreParams ) {
strippedURL . searchParams . delete ( param ) ;
}
return strippedURL . href ;
}
/ * *
* Matches an item in the cache , ignoring specific URL params . This is similar
* to the ` ignoreSearch ` option , but it allows you to ignore just specific
* params ( while continuing to match on the others ) .
*
* @ private
* @ param { Cache } cache
* @ param { Request } request
* @ param { Object } matchOptions
* @ param { Array < string > } ignoreParams
* @ return { Promise < Response | undefined > }
* /
async function cacheMatchIgnoreParams ( cache , request , ignoreParams , matchOptions ) {
const strippedRequestURL = stripParams ( request . url , ignoreParams ) ;
// If the request doesn't include any ignored params, match as normal.
if ( request . url === strippedRequestURL ) {
return cache . match ( request , matchOptions ) ;
}
// Otherwise, match by comparing keys
const keysOptions = Object . assign ( Object . assign ( { } , matchOptions ) , {
ignoreSearch : true
} ) ;
const cacheKeys = await cache . keys ( request , keysOptions ) ;
for ( const cacheKey of cacheKeys ) {
const strippedCacheKeyURL = stripParams ( cacheKey . url , ignoreParams ) ;
if ( strippedRequestURL === strippedCacheKeyURL ) {
return cache . match ( cacheKey , matchOptions ) ;
}
}
return ;
}
/ *
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.
* /
/ * *
* The Deferred class composes Promises in a way that allows for them to be
* resolved or rejected from outside the constructor . In most cases promises
* should be used directly , but Deferreds can be necessary when the logic to
* resolve a promise must be separate .
*
* @ private
* /
class Deferred {
/ * *
* Creates a promise and exposes its resolve and reject functions as methods .
* /
constructor ( ) {
this . promise = new Promise ( ( resolve , reject ) => {
this . resolve = resolve ;
this . reject = reject ;
} ) ;
}
}
/ *
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.
* /
// Callbacks to be executed whenever there's a quota error.
// Can't change Function type right now.
// eslint-disable-next-line @typescript-eslint/ban-types
const quotaErrorCallbacks = new Set ( ) ;
/ *
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.
* /
/ * *
* Runs all of the callback functions , one at a time sequentially , in the order
* in which they were registered .
*
* @ memberof workbox - core
* @ private
* /
async function executeQuotaErrorCallbacks ( ) {
{
logger . log ( ` About to run ${ quotaErrorCallbacks . size } ` + ` callbacks to clean up caches. ` ) ;
}
for ( const callback of quotaErrorCallbacks ) {
await callback ( ) ;
{
logger . log ( callback , 'is complete.' ) ;
}
}
{
logger . log ( 'Finished running callbacks.' ) ;
}
}
/ *
Copyright 2019 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.
* /
/ * *
* Returns a promise that resolves and the passed number of milliseconds .
* This utility is an async / await - friendly version of ` setTimeout ` .
*
* @ param { number } ms
* @ return { Promise }
* @ private
* /
function timeout ( ms ) {
return new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
}
/ *
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.
* /
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
* /
{
finalAssertExports . 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 ) {
{
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 ( "development" !== '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 ) {
{
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 ( 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 ( 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 ) {
{
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 ) {
{
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 ;
{
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 ( 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 ;
}
}
/ *
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.
* /
/ * *
* 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 {
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 ;
}
}
}
/ * *
* 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
* /
/ *
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.
* /
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 ( ) ;
}
}
} ;
/ *
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.
* /
/ * *
* 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 ( this . _networkTimeoutSeconds ) {
finalAssertExports . 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 = [ ] ;
{
finalAssertExports . 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 ) ;
} ) ( ) ) ;
{
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 ( ) => {
{
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 ( 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 ( 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 ;
}
}
/ *
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.
* /
/ * *
* 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 ) {
{
finalAssertExports . 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 ;
}
}
{
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 ;
}
}
/ *
Copyright 2019 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.
* /
/ * *
* Claim any currently available clients once the service worker
* becomes active . This is normally used in conjunction with ` skipWaiting() ` .
*
* @ memberof workbox - core
* /
function clientsClaim ( ) {
self . addEventListener ( 'activate' , ( ) => self . clients . claim ( ) ) ;
}
exports . NetworkFirst = NetworkFirst ;
exports . NetworkOnly = NetworkOnly ;
exports . clientsClaim = clientsClaim ;
exports . registerRoute = registerRoute ;
} ) ) ;
2024-08-01 21:08:57 +05:30
//# sourceMappingURL=workbox-9f2f79cf.js.map