sw-toolbox
Advanced tools
Comparing version 3.0.1 to 3.1.0
/* | ||
Copyright 2014 Google Inc. All Rights Reserved. | ||
Copyright 2016 Google Inc. All Rights Reserved. | ||
@@ -4,0 +4,0 @@ Licensed under the Apache License, Version 2.0 (the "License"); |
@@ -36,3 +36,2 @@ /* | ||
debug('Opening cache "' + cacheName + '"', options); | ||
return caches.open(cacheName); | ||
@@ -43,32 +42,25 @@ } | ||
options = options || {}; | ||
var successResponses = options.successResponses || globalOptions.successResponses; | ||
var successResponses = options.successResponses || | ||
globalOptions.successResponses; | ||
return fetch(request.clone()).then(function(response) { | ||
// Only cache GET requests with successful responses. | ||
// Since this is not part of the promise chain, it will be done asynchronously and will not | ||
// block the response from being returned to the page. | ||
// Since this is not part of the promise chain, it will be done | ||
// asynchronously and will not block the response from being returned to the | ||
// page. | ||
if (request.method === 'GET' && successResponses.test(response.status)) { | ||
openCache(options).then(function(cache) { | ||
cache.put(request, response).then(function() { | ||
var maxCacheEntries; | ||
var maxCacheAgeSeconds; | ||
var cacheName; | ||
// If any of the options are provided in options.cache then use them. | ||
// Do not fallback to the global options for any that are missing | ||
// unless they are all missing. | ||
var cacheOptions = options.cache || globalOptions.cache; | ||
if (options.cache) { | ||
// If someone explicitly sets options.cache, then read all three settings from there. | ||
// Don't fall back on globalOptions. | ||
maxCacheEntries = options.cache.maxEntries; | ||
maxCacheAgeSeconds = options.cache.maxAgeSeconds; | ||
cacheName = options.cache.name; | ||
} else { | ||
maxCacheEntries = globalOptions.cache.maxEntries; | ||
maxCacheAgeSeconds = globalOptions.cache.maxAgeSeconds; | ||
cacheName = globalOptions.cache.name; | ||
// Only run the cache expiration logic if at least one of the maximums | ||
// is set, and if we have a name for the cache that the options are | ||
// being applied to. | ||
if ((cacheOptions.maxEntries || cacheOptions.maxAgeSeconds) && | ||
cacheOptions.name) { | ||
queueCacheExpiration(request, cache, cacheOptions); | ||
} | ||
// Only run the cache expiration logic if at least one of the maximums is set, and if | ||
// we have a name for the cache that the options are being applied to. | ||
if ((maxCacheEntries || maxCacheAgeSeconds) && cacheName) { | ||
queueCacheExpiration(request, cache, cacheName, maxCacheEntries, maxCacheAgeSeconds); | ||
} | ||
}); | ||
@@ -82,20 +74,22 @@ }); | ||
var cacheExpirationPromiseChain; | ||
function queueCacheExpiration(request, cache, cacheName, maxCacheEntries, maxCacheAgeSeconds) { | ||
var cacheExpiration = cacheExpirationPromiseFactory.bind(null, request, cache, cacheName, maxCacheEntries, | ||
maxCacheAgeSeconds); | ||
var cleanupQueue; | ||
function queueCacheExpiration(request, cache, cacheOptions) { | ||
var cleanup = cleanupCache.bind(null, request, cache, cacheOptions); | ||
if (cacheExpirationPromiseChain) { | ||
cacheExpirationPromiseChain = cacheExpirationPromiseChain.then(cacheExpiration); | ||
if (cleanupQueue) { | ||
cleanupQueue = cleanupQueue.then(cleanup); | ||
} else { | ||
cacheExpirationPromiseChain = cacheExpiration(); | ||
cleanupQueue = cleanup(); | ||
} | ||
} | ||
function cacheExpirationPromiseFactory(request, cache, cacheName, maxCacheEntries, maxCacheAgeSeconds) { | ||
function cleanupCache(request, cache, cacheOptions) { | ||
var requestUrl = request.url; | ||
var maxAgeSeconds = cacheOptions.maxAgeSeconds; | ||
var maxEntries = cacheOptions.maxEntries; | ||
var cacheName = cacheOptions.name; | ||
var now = Date.now(); | ||
debug('Updating LRU order for ' + requestUrl + '. Max entries is ' + maxCacheEntries + | ||
', max age is ' + maxCacheAgeSeconds); | ||
debug('Updating LRU order for ' + requestUrl + '. Max entries is ' + | ||
maxEntries + ', max age is ' + maxAgeSeconds); | ||
@@ -105,3 +99,3 @@ return idbCacheExpiration.getDb(cacheName).then(function(db) { | ||
}).then(function(db) { | ||
return idbCacheExpiration.expireEntries(db, maxCacheEntries, maxCacheAgeSeconds, now); | ||
return idbCacheExpiration.expireEntries(db, maxEntries, maxAgeSeconds, now); | ||
}).then(function(urlsToDelete) { | ||
@@ -108,0 +102,0 @@ debug('Successfully updated IDB.'); |
@@ -30,4 +30,6 @@ /* | ||
request.onupgradeneeded = function() { | ||
var objectStore = request.result.createObjectStore(STORE_NAME, {keyPath: URL_PROPERTY}); | ||
objectStore.createIndex(TIMESTAMP_PROPERTY, TIMESTAMP_PROPERTY, {unique: false}); | ||
var objectStore = request.result.createObjectStore(STORE_NAME, | ||
{keyPath: URL_PROPERTY}); | ||
objectStore.createIndex(TIMESTAMP_PROPERTY, TIMESTAMP_PROPERTY, | ||
{unique: false}); | ||
}; | ||
@@ -70,3 +72,4 @@ | ||
function expireOldEntries(db, maxAgeSeconds, now) { | ||
// Bail out early by resolving with an empty array if we're not using maxAgeSeconds. | ||
// Bail out early by resolving with an empty array if we're not using | ||
// maxAgeSeconds. | ||
if (!maxAgeSeconds) { | ||
@@ -105,3 +108,4 @@ return Promise.resolve([]); | ||
function expireExtraEntries(db, maxEntries) { | ||
// Bail out early by resolving with an empty array if we're not using maxEntries. | ||
// Bail out early by resolving with an empty array if we're not using | ||
// maxEntries. | ||
if (!maxEntries) { | ||
@@ -146,5 +150,5 @@ return Promise.resolve([]); | ||
function expireEntries(db, maxEntries, maxAgeSeconds, now) { | ||
return expireOldEntries(db, maxAgeSeconds, now).then(function(oldExpiredUrls) { | ||
return expireExtraEntries(db, maxEntries).then(function(extraExpiredUrls) { | ||
return oldExpiredUrls.concat(extraExpiredUrls); | ||
return expireOldEntries(db, maxAgeSeconds, now).then(function(oldUrls) { | ||
return expireExtraEntries(db, maxEntries).then(function(extraUrls) { | ||
return oldUrls.concat(extraUrls); | ||
}); | ||
@@ -151,0 +155,0 @@ }); |
@@ -24,14 +24,19 @@ /* | ||
var Route = function(method, path, handler, options) { | ||
// The URL() constructor can't parse express-style routes as they are not | ||
// valid urls. This means we have to manually manipulate relative urls into | ||
// absolute ones. This check is extremely naive but implementing a tweaked | ||
// version of the full algorithm seems like overkill | ||
// (https://url.spec.whatwg.org/#concept-basic-url-parser) | ||
if (path.indexOf('/') !== 0) { | ||
path = basePath + path; | ||
if (path instanceof RegExp) { | ||
this.fullUrlRegExp = path; | ||
} else { | ||
// The URL() constructor can't parse express-style routes as they are not | ||
// valid urls. This means we have to manually manipulate relative urls into | ||
// absolute ones. This check is extremely naive but implementing a tweaked | ||
// version of the full algorithm seems like overkill | ||
// (https://url.spec.whatwg.org/#concept-basic-url-parser) | ||
if (path.indexOf('/') !== 0) { | ||
path = basePath + path; | ||
} | ||
this.keys = []; | ||
this.regexp = pathRegexp(path, this.keys); | ||
} | ||
this.method = method; | ||
this.keys = []; | ||
this.regexp = pathRegexp(path, this.keys); | ||
this.options = options; | ||
@@ -42,7 +47,11 @@ this.handler = handler; | ||
Route.prototype.makeHandler = function(url) { | ||
var match = this.regexp.exec(url); | ||
var values = {}; | ||
this.keys.forEach(function(key, index) { | ||
values[key.name] = match[index + 1]; | ||
}); | ||
var values; | ||
if (this.regexp) { | ||
var match = this.regexp.exec(url); | ||
values = {}; | ||
this.keys.forEach(function(key, index) { | ||
values[key.name] = match[index + 1]; | ||
}); | ||
} | ||
return function(request) { | ||
@@ -49,0 +58,0 @@ return this.handler(request, values, this.options); |
@@ -25,4 +25,4 @@ /* | ||
var keyMatch = function(map, string) { | ||
// This would be better written as a for..of loop, but that would break the minifyify process | ||
// in the build. | ||
// This would be better written as a for..of loop, but that would break the | ||
// minifyify process in the build. | ||
var entriesIterator = map.entries(); | ||
@@ -53,8 +53,18 @@ var item = entriesIterator.next(); | ||
options = options || {}; | ||
var origin = options.origin || self.location.origin; | ||
if (origin instanceof RegExp) { | ||
origin = origin.source; | ||
var origin; | ||
if (path instanceof RegExp) { | ||
// We need a unique key to use in the Map to distinguish RegExp paths | ||
// from Express-style paths + origins. Since we can use any object as the | ||
// key in a Map, let's use the RegExp constructor! | ||
origin = RegExp; | ||
} else { | ||
origin = regexEscape(origin); | ||
origin = options.origin || self.location.origin; | ||
if (origin instanceof RegExp) { | ||
origin = origin.source; | ||
} else { | ||
origin = regexEscape(origin); | ||
} | ||
} | ||
method = method.toLowerCase(); | ||
@@ -74,27 +84,31 @@ | ||
var routeMap = methodMap.get(method); | ||
routeMap.set(route.regexp.source, route); | ||
var regExp = route.regexp || route.fullUrlRegExp; | ||
routeMap.set(regExp.source, route); | ||
}; | ||
Router.prototype.matchMethod = function(method, url) { | ||
url = new URL(url); | ||
var origin = url.origin; | ||
var path = url.pathname; | ||
method = method.toLowerCase(); | ||
var urlObject = new URL(url); | ||
var origin = urlObject.origin; | ||
var path = urlObject.pathname; | ||
var methods = keyMatch(this.routes, origin); | ||
if (!methods) { | ||
return null; | ||
} | ||
// We want to first check to see if there's a match against any | ||
// "Express-style" routes (string for the path, RegExp for the origin). | ||
// Checking for Express-style matches first maintains the legacy behavior. | ||
// If there's no match, we next check for a match against any RegExp routes, | ||
// where the RegExp in question matches the full URL (both origin and path). | ||
return this._match(method, keyMatch(this.routes, origin), path) || | ||
this._match(method, this.routes.get(RegExp), url); | ||
}; | ||
var routes = methods.get(method); | ||
if (!routes) { | ||
return null; | ||
Router.prototype._match = function(method, methodMap, pathOrUrl) { | ||
if (methodMap) { | ||
var routeMap = methodMap.get(method.toLowerCase()); | ||
if (routeMap) { | ||
var route = keyMatch(routeMap, pathOrUrl); | ||
if (route) { | ||
return route.makeHandler(pathOrUrl); | ||
} | ||
} | ||
} | ||
var route = keyMatch(routes, path); | ||
if (route) { | ||
return route.makeHandler(path); | ||
} | ||
return null; | ||
@@ -104,5 +118,6 @@ }; | ||
Router.prototype.match = function(request) { | ||
return this.matchMethod(request.method, request.url) || this.matchMethod('any', request.url); | ||
return this.matchMethod(request.method, request.url) || | ||
this.matchMethod('any', request.url); | ||
}; | ||
module.exports = new Router(); |
@@ -30,3 +30,4 @@ /* | ||
if (rejected) { | ||
reject(new Error('Both cache and network failed: "' + reasons.join('", "') + '"')); | ||
reject(new Error('Both cache and network failed: "' + | ||
reasons.join('", "') + '"')); | ||
} else { | ||
@@ -33,0 +34,0 @@ rejected = true; |
@@ -22,6 +22,8 @@ /* | ||
options = options || {}; | ||
var successResponses = options.successResponses || globalOptions.successResponses; | ||
// This will bypass options.networkTimeout if it's set to a false-y value like 0, but that's the | ||
// sane thing to do anyway. | ||
var networkTimeoutSeconds = options.networkTimeoutSeconds || globalOptions.networkTimeoutSeconds; | ||
var successResponses = options.successResponses || | ||
globalOptions.successResponses; | ||
// This will bypass options.networkTimeout if it's set to a false-y value like | ||
// 0, but that's the sane thing to do anyway. | ||
var networkTimeoutSeconds = options.networkTimeoutSeconds || | ||
globalOptions.networkTimeoutSeconds; | ||
helpers.debug('Strategy: network first [' + request.url + ']', options); | ||
@@ -32,2 +34,3 @@ | ||
var promises = []; | ||
var originalResponse; | ||
@@ -39,5 +42,6 @@ if (networkTimeoutSeconds) { | ||
if (response) { | ||
// Only resolve this promise if there's a valid response in the cache. | ||
// This ensures that we won't time out a network request unless there's a cached entry | ||
// to fallback on, which is arguably the preferable behavior. | ||
// Only resolve this promise if there's a valid response in the | ||
// cache. This ensures that we won't time out a network request | ||
// unless there's a cached entry to fallback on, which is arguably | ||
// the preferable behavior. | ||
resolve(response); | ||
@@ -51,18 +55,25 @@ } | ||
var networkPromise = helpers.fetchAndCache(request, options).then(function(response) { | ||
// We've got a response, so clear the network timeout if there is one. | ||
if (timeoutId) { | ||
clearTimeout(timeoutId); | ||
} | ||
var networkPromise = helpers.fetchAndCache(request, options) | ||
.then(function(response) { | ||
// We've got a response, so clear the network timeout if there is one. | ||
if (timeoutId) { | ||
clearTimeout(timeoutId); | ||
} | ||
if (successResponses.test(response.status)) { | ||
return response; | ||
} | ||
if (successResponses.test(response.status)) { | ||
return response; | ||
} | ||
helpers.debug('Response was an HTTP error: ' + response.statusText, options); | ||
throw new Error('Bad response'); | ||
}).catch(function() { | ||
helpers.debug('Network or response error, fallback to cache [' + request.url + ']', options); | ||
return cache.match(request); | ||
}); | ||
helpers.debug('Response was an HTTP error: ' + response.statusText, | ||
options); | ||
originalResponse = response; | ||
throw new Error('Bad response'); | ||
}).catch(function() { | ||
helpers.debug('Network or response error, fallback to cache [' + | ||
request.url + ']', options); | ||
return cache.match(request).then(function(response) { | ||
return response || originalResponse; | ||
}); | ||
}); | ||
promises.push(networkPromise); | ||
@@ -69,0 +80,0 @@ |
@@ -43,3 +43,4 @@ /* | ||
.then(function(preCacheItems) { | ||
helpers.debug('preCache list: ' + (preCacheItems.join(', ') || '(none)')); | ||
helpers.debug('preCache list: ' + | ||
(preCacheItems.join(', ') || '(none)')); | ||
return cache.addAll(preCacheItems); | ||
@@ -46,0 +47,0 @@ }); |
{ | ||
"name": "sw-toolbox", | ||
"version": "3.0.1", | ||
"version": "3.1.0", | ||
"license": "Apache-2.0", | ||
@@ -15,15 +15,18 @@ "scripts": { | ||
"devDependencies": { | ||
"browserify": "^6.3.2", | ||
"browserify": "^12.0.1", | ||
"browserify-header": "^0.9.2", | ||
"eslint": "^1.4.3", | ||
"eslint-config-xo": "^0.6.0", | ||
"gulp": "^3.8.10", | ||
"gulp-eslint": "^1.0.0", | ||
"gulp-gh-pages": "^0.5.2", | ||
"jshint-stylish": "^1.0.0", | ||
"minifyify": "^6.4.0", | ||
"qunitjs": "^1.18.0", | ||
"chai": "^3.4.1", | ||
"eslint": "^1.10.3", | ||
"eslint-config-google": "^0.3.0", | ||
"express": "^4.13.3", | ||
"gulp": "^3.9.0", | ||
"gulp-eslint": "^1.1.1", | ||
"gulp-gh-pages": "^0.5.4", | ||
"jshint-stylish": "^2.1.0", | ||
"minifyify": "^7.1.0", | ||
"mocha": "^2.3.4", | ||
"qunitjs": "^1.20.0", | ||
"temp": "^0.8.3", | ||
"vinyl-source-stream": "^1.0.0" | ||
"vinyl-source-stream": "^1.1.0" | ||
} | ||
} |
222
README.md
@@ -5,9 +5,7 @@ # Service Worker Toolbox | ||
## Service Worker helpers | ||
Service Worker Toolbox provides some simple helpers for use in creating your own service workers. If you're not sure what service workers are or what they are for, start with [the explainer doc](https://github.com/slightlyoff/ServiceWorker/blob/master/explainer.md). | ||
### Installing Service Worker Toolbox | ||
## Install | ||
Service Worker Toolbox is available through Bower, npm or direct from github: | ||
Service Worker Toolbox is available through Bower, npm or direct from GitHub: | ||
@@ -20,3 +18,3 @@ `bower install --save sw-toolbox` | ||
### Registering your service worker | ||
### Register your service worker | ||
@@ -29,3 +27,3 @@ From your registering page, register your service worker in the normal way. For example: | ||
For even lower friction, if you don't intend to doing anything more fancy than just registering with a default scope, you can instead include the Service Worker Toolbox companion script in your HTML: | ||
For even lower friction, especially if you don't do anything more fancy than registering with a default scope, you can instead include the Service Worker Toolbox companion script in your HTML: | ||
@@ -36,5 +34,5 @@ ```html | ||
As currently implemented in Chrome 40+, a service worker must exist at the root of the scope that you intend it to control, or higher. So if you want all of the pages under `/myapp/` to be controlled by the worker, the worker script itself must be served from either `/` or `/myapp/`. The default scope is the containing path of the service worker script. | ||
As implemented in Chrome 40 or later, a service worker must exist at the root of the scope that you intend it to control, or higher. So if you want all of the pages under `/myapp/` to be controlled by the worker, the worker script itself must be served from either `/` or `/myapp/`. The default scope is the containing path of the service worker script. | ||
### Using Service Worker Toolbox in your worker script | ||
### Add Service Worker Toolbox to your service worker script | ||
@@ -47,12 +45,45 @@ In your service worker you just need to use `importScripts` to load Service Worker Toolbox | ||
## Basic usage | ||
Within your service worker file | ||
```javascript | ||
// Set up routes from URL patterns to request handlers | ||
toolbox.router.get('/myapp/index.html', someHandler); | ||
## Usage | ||
// For some common cases Service Worker Toolbox provides a built-in handler | ||
toolbox.router.get('/', toolbox.networkFirst); | ||
### Basic Routes | ||
// URL patterns are the same syntax as ExpressJS routes | ||
A _route_ is a URL pattern and request method associated with a handler. | ||
It defines the behaviour for a section of the site. | ||
_Routing_ is the process of matching an incoming request with the most | ||
appropriate route. To define a route you call the appropriate method on | ||
`toolbox.router`. | ||
For example, to send `GET` requests for the URL `'/myapp/index.html'` to the | ||
built-in `toolbox.networkFirst` handler, you would write the following in your | ||
service worker file: | ||
`toolbox.router.get('/myapp/index.html', toolbox.networkFirst);` | ||
If you don't need wildcards in your route, and your route applies to the same | ||
domain as your main site, then you can use a string like `'/myapp/index.html'`. | ||
However, if you need wildcards (e.g. match _any_ URL that begins with | ||
`/myapp/`), or if you need to match URLs that belong to different domains (e.g. | ||
match `https://othersite.com/api/`), `sw-toolbox` has two options for | ||
configuring your routes. | ||
### Express-style Routes | ||
For developers familiar with [Express routing](http://expressjs.com/en/guide/routing.html), | ||
`sw-toolbox` offers support for similar named wildcards, via the | ||
[`path-to-regexp`](https://github.com/pillarjs/path-to-regexp) library. | ||
If you use a `String` to define your route, it's assumed you're using | ||
Express-style routes. | ||
By default, a route will only match URLs on the same origin as the service | ||
worker. If you'd like your Express-style routes to match URLs on different | ||
origins, you need to pass in a value for the `origin` option. The value could be | ||
either a `String` (which is checked for an exact match) or a `RegExp` object. | ||
In both cases, it's matched against the full origin of the URL | ||
(e.g. `'https://example.com'`). | ||
Some examples of using Express-style routing include: | ||
```javascript | ||
// URL patterns are the same syntax as Express routes | ||
// (http://expressjs.com/guide/routing.html) | ||
@@ -66,12 +97,55 @@ toolbox.router.get(':foo/index.html', function(request, values) { | ||
toolbox.router.post('/(.*)', apiHandler, {origin: 'https://api.example.com'}); | ||
``` | ||
### Regular Expression Routes | ||
Developers who are more comfortable using [regular expressions](https://regex101.com/) | ||
can use an alternative syntax to define routes, passing in a [`RegExp`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) | ||
object as the first parameter. This `RegExp` will be matched against the full | ||
request URL when determining whether the route applies, including the origin and | ||
path. This can lead to simpler cross-origin routing vs. Express-style routes, | ||
since both the origin and the path are matched simultaneously, without having | ||
to specify a separate `origin` option. | ||
Note that while Express-style routes allow you to name path fragment | ||
parameters that will be passed to your handler (see `values.foo` in the previous | ||
example), that functionality is not supported while using regular expression | ||
routes. | ||
Some examples of using Regular Expression routing include: | ||
```javascript | ||
// Match URLs that end in index.html | ||
toolbox.router.get(/index.html$/, function(request) { | ||
return new Response('Handled a request for ' + request.url); | ||
}); | ||
// Match URLs that begin with https://api.example.com | ||
toolbox.router.post(/^https:\/\/api.example.com\//, apiHandler); | ||
``` | ||
### The Default Route | ||
`sw-toolbox` supports defining an optional "default" route via | ||
`toolbox.router.default` that is used whenever there is no alternative route for | ||
a given URL. If `toolbox.router.default` is not set, then `sw-toolbox` will | ||
just ignore requests for URLs that don't match any alternative routes, and the | ||
requests will potentially be handled by the browser as if there were no | ||
service worker involvement. | ||
```javascript | ||
// Provide a default handler for GET requests | ||
toolbox.router.default = myDefaultRequestHandler; | ||
``` | ||
// You can provide a list of resources which will be cached at service worker install time | ||
### Precaching | ||
You can provide a list of resources which will be cached at service worker install time | ||
```javascript | ||
toolbox.precache(['/index.html', '/site.css', '/images/logo.png']); | ||
``` | ||
## Request handlers | ||
A request handler receives three arguments | ||
### Defining Request Handlers | ||
A request handler takes three arguments. | ||
@@ -84,37 +158,24 @@ ```javascript | ||
- `request` - [Request](https://fetch.spec.whatwg.org/#request) object that triggered the `fetch` event | ||
- `values` - Object whose keys are the placeholder names in the URL pattern, with the values being the corresponding part of the request URL. For example, with a URL pattern of `'/images/:size/:name.jpg'` and an actual URL of `'/images/large/unicorns.jpg'`, `values` would be `{size: 'large', name: 'unicorns'}` | ||
- `options` - the options object that was used when [creating the route](#api) | ||
- `request` - The [Request](https://fetch.spec.whatwg.org/#request) object that triggered the `fetch` event | ||
- `values` - When using Express-style routing paths, this will be an object | ||
whose keys are the placeholder names in the URL pattern, with the values being | ||
the corresponding part of the request URL. For example, with a URL pattern of | ||
`'/images/:size/:name.jpg'` and an actual URL of `'/images/large/unicorns.jpg'`, | ||
`values` would be `{size: 'large', name: 'unicorns'}`. | ||
When using a RegExp for the path, `values` will not be set. | ||
- `options` - the [options](#options) passed to one of the [router methods](#methods). | ||
The return value should be a [Response](https://fetch.spec.whatwg.org/#response), or a [Promise](http://www.html5rocks.com/en/tutorials/es6/promises/) that resolves with a Response. If another value is returned, or if the returned Promise is rejected, the Request will fail which will appear to be a [NetworkError](https://developer.mozilla.org/en-US/docs/Web/API/DOMException#exception-NetworkError) to the page that made the request. | ||
### Built-in handlers | ||
There are 5 built-in handlers to cover the most common network strategies. For more information about offline strategies see the [Offline Cookbook](http://jakearchibald.com/2014/offline-cookbook/). | ||
#### `toolbox.networkFirst` | ||
Try to handle the request by fetching from the network. If it succeeds, store the response in the cache. Otherwise, try to fulfill the request from the cache. This is the strategy to use for basic read-through caching. Also good for API requests where you always want the freshest data when it is available but would rather have stale data than no data. | ||
#### `toolbox.cacheFirst` | ||
If the request matches a cache entry, respond with that. Otherwise try to fetch the resource from the network. If the network request succeeds, update the cache. Good for resources that don't change, or for which you have some other update mechanism. | ||
#### `toolbox.fastest` | ||
Request the resource from both the cache and the network in parallel. Respond with whichever returns first. Usually this will be the cached version, if there is one. On the one hand this strategy will always make a network request, even if the resource is cached. On the other hand, if/when the network request completes the cache is updated, so that future cache reads will be more up-to-date. | ||
#### `toolbox.cacheOnly` | ||
Resolve the request from the cache, or fail. Good for when you need to guarantee that no network request will be made - to save battery on mobile, for example. | ||
#### `toolbox.networkOnly` | ||
Handle the request by trying to fetch the URL from the network. If the fetch fails, fail the request. Essentially the same as not creating a route for the URL at all. | ||
## API | ||
### Global Options | ||
### Options | ||
All options can be specified globally via properties of `toolbox.options`. | ||
Any individual options can be configured on a per-handler basis, via the `Object` passed as the | ||
third parameter to the `toolbox.router.get(urlPattern, handler, options)`, etc. methods. | ||
third parameter to `toolbox.router` methods. | ||
#### debug [Boolean] | ||
Determines whether extra information is logged to the browser's `console`. | ||
Determines whether extra information is logged to the browser's console. | ||
_Default_: `false` | ||
@@ -125,4 +186,5 @@ | ||
If `networkTimeoutSeconds` is set, then any network requests that take longer than that amount of time | ||
will automatically fall back to the cached response if one exists. By default, when | ||
will automatically fall back to the cached response if one exists. When | ||
`networkTimeoutSeconds` is not set, the browser's native networking timeout logic applies. | ||
_Default_: `null` | ||
@@ -135,9 +197,10 @@ | ||
#### cache.name [String] | ||
The name of [`Cache`](https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#cache) | ||
used to store [`Response`](https://fetch.spec.whatwg.org/#response-class)s. Using a unique name | ||
The name of the [`Cache`](https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#cache) | ||
used to store [`Response`](https://fetch.spec.whatwg.org/#response-class) objects. Using a unique name | ||
allows you to customize the cache's maximum size and age of entries. | ||
_Default_: Generated at runtime based on the service worker's `registration.scope` value. | ||
#### cache.maxEntries [Number] | ||
The `cache.maxEntries` option can be used to impose a least-recently used cache expiration policy | ||
Imposes a least-recently used cache expiration policy | ||
on entries cached via the various built-in handlers. You can use this with a cache that's dedicated | ||
@@ -149,6 +212,7 @@ to storing entries for a dynamic set of resources with no natural limit. Setting `cache.maxEntries` to, e.g., | ||
It can be used alone or in conjunction with `cache.maxAgeSeconds`. | ||
_Default_: `null` | ||
#### cache.maxAgeSeconds [Number] | ||
The `maxAgeSeconds` option can be used to impose a maximum age for cache entries, in seconds. | ||
Imposes a maximum age for cache entries, in seconds. | ||
You can use this with a cache that's dedicated to storing entries for a dynamic set of resources | ||
@@ -159,33 +223,55 @@ with no natural limit. Setting `cache.maxAgeSeconds` to, e.g., `60 * 60 * 24` would mean that any | ||
It can be used alone or in conjunction with `cache.maxEntries`. | ||
_Default_: `null` | ||
### `toolbox.router.get(urlPattern, handler, options)` | ||
### `toolbox.router.post(urlPattern, handler, options)` | ||
### `toolbox.router.put(urlPattern, handler, options)` | ||
### `toolbox.router.delete(urlPattern, handler, options)` | ||
### `toolbox.router.head(urlPattern, handler, options)` | ||
### Built-in handlers | ||
There are five built-in handlers to cover the most common network strategies. For more information about offline strategies see the [Offline Cookbook](http://jakearchibald.com/2014/offline-cookbook/). | ||
#### `toolbox.networkFirst` | ||
Try to handle the request by fetching from the network. If it succeeds, store the response in the cache. Otherwise, try to fulfill the request from the cache. This is the strategy to use for basic read-through caching. It's also good for API requests where you always want the freshest data when it is available but would rather have stale data than no data. | ||
#### `toolbox.cacheFirst` | ||
If the request matches a cache entry, respond with that. Otherwise try to fetch the resource from the network. If the network request succeeds, update the cache. This option is good for resources that don't change, or have some other update mechanism. | ||
#### `toolbox.fastest` | ||
Request the resource from both the cache and the network in parallel. Respond with whichever returns first. Usually this will be the cached version, if there is one. On the one hand this strategy will always make a network request, even if the resource is cached. On the other hand, if/when the network request completes the cache is updated, so that future cache reads will be more up-to-date. | ||
#### `toolbox.cacheOnly` | ||
Resolve the request from the cache, or fail. This option is good for when you need to guarantee that no network request will be made, for example saving battery on mobile. | ||
#### `toolbox.networkOnly` | ||
Handle the request by trying to fetch the URL from the network. If the fetch fails, fail the request. Essentially the same as not creating a route for the URL at all. | ||
###Methods | ||
#### `toolbox.router.get(urlPattern, handler, options)` | ||
#### `toolbox.router.post(urlPattern, handler, options)` | ||
#### `toolbox.router.put(urlPattern, handler, options)` | ||
#### `toolbox.router.delete(urlPattern, handler, options)` | ||
#### `toolbox.router.head(urlPattern, handler, options)` | ||
Create a route that causes requests for URLs matching `urlPattern` to be resolved by calling `handler`. Matches requests using the GET, POST, PUT, DELETE or HEAD HTTP methods respectively. | ||
- `urlPattern` - an Express style route. See the docs for the [path-to-regexp](https://github.com/pillarjs/path-to-regexp) module for the full syntax | ||
- `handler` - a request handler, as [described above](#request-handlers) | ||
- `options` - an object containing options for the route. This options object will be available to the request handler. The `origin` option is specific to the route methods, and is an exact string or a Regexp against which the origin of the Request must match for the route to be used. | ||
- `handler` - a request handler, as [described above](#using-request-handlers) | ||
- `options` - an object containing options for the route. This options object will be passed to the request handler. The `origin` option is specific to the router methods, and can be either an exact string or a Regexp against which the origin of the Request must match for the route to be used. | ||
### `toolbox.router.any(urlPattern, handler, options)` | ||
#### `toolbox.router.any(urlPattern, handler, options)` | ||
Like `toolbox.router.get`, etc., but matches any HTTP method. | ||
### `toolbox.router.default` | ||
If you set this property to a function it will be used as the request handler for any GET request that does not match a route. | ||
#### `toolbox.router.default` | ||
Takes a function to use as the request handler for any GET request that does not match a route. | ||
### `toolbox.precache(arrayOfURLs)` | ||
#### `toolbox.precache(arrayOfURLs)` | ||
Add each URL in arrayOfURLs to the list of resources that should be cached during the service worker install step. Note that this needs to be called before the install event is triggered, so you should do it on the first run of your script. | ||
### `toolbox.cache(url, options)` | ||
Causes the resource at `url` to be added to the cache. Returns a Promise. Supports the `debug` and `cache` [global options](#global-options). | ||
#### `toolbox.cache(url, options)` | ||
Causes the resource at `url` to be added to the cache and returns a Promise that resolves with void. The `options` parameter supports the `debug` and `cache` [global options](#global-options). | ||
### `toolbox.uncache(url, options)` | ||
Causes the resource at `url` to be removed from the cache. Returns a Promise. Supports the `debug` and `cache` [global options](#global-options). | ||
#### `toolbox.uncache(url, options)` | ||
Causes the resource at `url` to be removed from the cache and returns a Promise that resolves to true if the cache entry is deleted. The `options` parameter supports the `debug` and `cache` [global options](#global-options). | ||
## Support | ||
If you’ve found an error in this library, please file an issue: https://github.com/GoogleChrome/sw-toolbox/issues | ||
If you’ve found an error in this library, please file an issue at: https://github.com/GoogleChrome/sw-toolbox/issues. | ||
@@ -198,5 +284,5 @@ Patches are encouraged, and may be submitted by forking this project and submitting a pull request through GitHub. | ||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
Licensed under the [Apache License, Version 2.0](LICENSE) (the "License"); | ||
you may not use this file except in compliance with the License. You may | ||
obtain a copy of the License at | ||
@@ -203,0 +289,0 @@ http://www.apache.org/licenses/LICENSE-2.0 |
@@ -11,5 +11,8 @@ (global => { | ||
// Set up a handler for HTTP GET requests: | ||
// - '/(.*)' means any URL pathname will be matched. | ||
// - toolbox.cacheFirst let us to use the predefined cache strategy for those requests. | ||
global.toolbox.router.get('/(.*)', global.toolbox.cacheFirst, { | ||
// - /\.ytimg\.com\// will match any requests whose URL contains 'ytimg.com'. | ||
// A narrower RegExp could be used, but just checking for ytimg.com anywhere | ||
// in the URL should be fine for this sample. | ||
// - toolbox.cacheFirst let us to use the predefined cache strategy for those | ||
// requests. | ||
global.toolbox.router.get(/\.ytimg\.com\//, global.toolbox.cacheFirst, { | ||
// Use a dedicated cache for the responses, separate from the default cache. | ||
@@ -22,15 +25,16 @@ cache: { | ||
maxAgeSeconds: 30 | ||
}, | ||
// origin allows us to restrict the handler to requests whose origin matches a regexp. | ||
// In this case, we want to match anything that ends in 'ytimg.com'. | ||
origin: /\.ytimg\.com$/ | ||
} | ||
}); | ||
// By default, all requests that don't match our custom handler will use the toolbox.networkFirst | ||
// cache strategy, and their responses will be stored in the default cache. | ||
// By default, all requests that don't match our custom handler will use the | ||
// toolbox.networkFirst cache strategy, and their responses will be stored in | ||
// the default cache. | ||
global.toolbox.router.default = global.toolbox.networkFirst; | ||
// Boilerplate to ensure our service worker takes control of the page as soon as possible. | ||
global.addEventListener('install', event => event.waitUntil(global.skipWaiting())); | ||
global.addEventListener('activate', event => event.waitUntil(global.clients.claim())); | ||
// Boilerplate to ensure our service worker takes control of the page as soon | ||
// as possible. | ||
global.addEventListener('install', | ||
event => event.waitUntil(global.skipWaiting())); | ||
global.addEventListener('activate', | ||
event => event.waitUntil(global.clients.claim())); | ||
})(self); |
@@ -17,33 +17,33 @@ /* | ||
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var o;"undefined"!=typeof window?o=window:"undefined"!=typeof global?o=global:"undefined"!=typeof self&&(o=self),o.toolbox=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | ||
"use strict";function cache(e,t){return helpers.openCache(t).then(function(t){return t.add(e)})}function uncache(e,t){return helpers.openCache(t).then(function(t){return t["delete"](e)})}function precache(e){Array.isArray(e)||(e=[e]),options.preCacheItems=options.preCacheItems.concat(e)}require("serviceworker-cache-polyfill");var options=require("./options"),router=require("./router"),helpers=require("./helpers"),strategies=require("./strategies");helpers.debug("Service Worker Toolbox is loading");var flatten=function(e){return e.reduce(function(e,t){return e.concat(t)},[])};self.addEventListener("install",function(e){var t=options.cache.name+"$$$inactive$$$";helpers.debug("install event fired"),helpers.debug("creating cache ["+t+"]"),e.waitUntil(helpers.openCache({cache:{name:t}}).then(function(e){return Promise.all(options.preCacheItems).then(flatten).then(function(t){return helpers.debug("preCache list: "+(t.join(", ")||"(none)")),e.addAll(t)})}))}),self.addEventListener("activate",function(e){helpers.debug("activate event fired");var t=options.cache.name+"$$$inactive$$$";e.waitUntil(helpers.renameCache(t,options.cache.name))}),self.addEventListener("fetch",function(e){var t=router.match(e.request);t?e.respondWith(t(e.request)):router["default"]&&"GET"===e.request.method&&e.respondWith(router["default"](e.request))}),module.exports={networkOnly:strategies.networkOnly,networkFirst:strategies.networkFirst,cacheOnly:strategies.cacheOnly,cacheFirst:strategies.cacheFirst,fastest:strategies.fastest,router:router,options:options,cache:cache,uncache:uncache,precache:precache}; | ||
},{"./helpers":2,"./options":4,"./router":6,"./strategies":10,"serviceworker-cache-polyfill":15}],2:[function(require,module,exports){ | ||
"use strict";function debug(e,n){n=n||{};var c=n.debug||globalOptions.debug;c&&console.log("[sw-toolbox] "+e)}function openCache(e){var n;return e&&e.cache&&(n=e.cache.name),n=n||globalOptions.cache.name,debug('Opening cache "'+n+'"',e),caches.open(n)}function fetchAndCache(e,n){n=n||{};var c=n.successResponses||globalOptions.successResponses;return fetch(e.clone()).then(function(a){return"GET"===e.method&&c.test(a.status)&&openCache(n).then(function(c){c.put(e,a).then(function(){var a,t,i;n.cache?(a=n.cache.maxEntries,t=n.cache.maxAgeSeconds,i=n.cache.name):(a=globalOptions.cache.maxEntries,t=globalOptions.cache.maxAgeSeconds,i=globalOptions.cache.name),(a||t)&&i&&queueCacheExpiration(e,c,i,a,t)})}),a.clone()})}function queueCacheExpiration(e,n,c,a,t){var i=cacheExpirationPromiseFactory.bind(null,e,n,c,a,t);cacheExpirationPromiseChain=cacheExpirationPromiseChain?cacheExpirationPromiseChain.then(i):i()}function cacheExpirationPromiseFactory(e,n,c,a,t){var i=e.url,o=Date.now();return debug("Updating LRU order for "+i+". Max entries is "+a+", max age is "+t),idbCacheExpiration.getDb(c).then(function(e){return idbCacheExpiration.setTimestampForUrl(e,i,o)}).then(function(e){return idbCacheExpiration.expireEntries(e,a,t,o)}).then(function(e){debug("Successfully updated IDB.");var c=e.map(function(e){return n["delete"](e)});return Promise.all(c).then(function(){debug("Done with cache cleanup.")})})["catch"](function(e){debug(e)})}function renameCache(e,n,c){return debug("Renaming cache: ["+e+"] to ["+n+"]",c),caches["delete"](n).then(function(){return Promise.all([caches.open(e),caches.open(n)]).then(function(n){var c=n[0],a=n[1];return c.keys().then(function(e){return Promise.all(e.map(function(e){return c.match(e).then(function(n){return a.put(e,n)})}))}).then(function(){return caches["delete"](e)})})})}var globalOptions=require("./options"),idbCacheExpiration=require("./idb-cache-expiration"),cacheExpirationPromiseChain;module.exports={debug:debug,fetchAndCache:fetchAndCache,openCache:openCache,renameCache:renameCache}; | ||
},{"./idb-cache-expiration":3,"./options":4}],3:[function(require,module,exports){ | ||
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.toolbox = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ | ||
"use strict";function debug(e,n){n=n||{};var t=n.debug||globalOptions.debug;t&&console.log("[sw-toolbox] "+e)}function openCache(e){var n;return e&&e.cache&&(n=e.cache.name),n=n||globalOptions.cache.name,caches.open(n)}function fetchAndCache(e,n){n=n||{};var t=n.successResponses||globalOptions.successResponses;return fetch(e.clone()).then(function(c){return"GET"===e.method&&t.test(c.status)&&openCache(n).then(function(t){t.put(e,c).then(function(){var c=n.cache||globalOptions.cache;(c.maxEntries||c.maxAgeSeconds)&&c.name&&queueCacheExpiration(e,t,c)})}),c.clone()})}function queueCacheExpiration(e,n,t){var c=cleanupCache.bind(null,e,n,t);cleanupQueue=cleanupQueue?cleanupQueue.then(c):c()}function cleanupCache(e,n,t){var c=e.url,a=t.maxAgeSeconds,u=t.maxEntries,o=t.name,r=Date.now();return debug("Updating LRU order for "+c+". Max entries is "+u+", max age is "+a),idbCacheExpiration.getDb(o).then(function(e){return idbCacheExpiration.setTimestampForUrl(e,c,r)}).then(function(e){return idbCacheExpiration.expireEntries(e,u,a,r)}).then(function(e){debug("Successfully updated IDB.");var t=e.map(function(e){return n["delete"](e)});return Promise.all(t).then(function(){debug("Done with cache cleanup.")})})["catch"](function(e){debug(e)})}function renameCache(e,n,t){return debug("Renaming cache: ["+e+"] to ["+n+"]",t),caches["delete"](n).then(function(){return Promise.all([caches.open(e),caches.open(n)]).then(function(n){var t=n[0],c=n[1];return t.keys().then(function(e){return Promise.all(e.map(function(e){return t.match(e).then(function(n){return c.put(e,n)})}))}).then(function(){return caches["delete"](e)})})})}var globalOptions=require("./options"),idbCacheExpiration=require("./idb-cache-expiration"),cleanupQueue;module.exports={debug:debug,fetchAndCache:fetchAndCache,openCache:openCache,renameCache:renameCache}; | ||
},{"./idb-cache-expiration":2,"./options":3}],2:[function(require,module,exports){ | ||
"use strict";function openDb(e){return new Promise(function(r,n){var t=indexedDB.open(DB_PREFIX+e,DB_VERSION);t.onupgradeneeded=function(){var e=t.result.createObjectStore(STORE_NAME,{keyPath:URL_PROPERTY});e.createIndex(TIMESTAMP_PROPERTY,TIMESTAMP_PROPERTY,{unique:!1})},t.onsuccess=function(){r(t.result)},t.onerror=function(){n(t.error)}})}function getDb(e){return e in cacheNameToDbPromise||(cacheNameToDbPromise[e]=openDb(e)),cacheNameToDbPromise[e]}function setTimestampForUrl(e,r,n){return new Promise(function(t,o){var i=e.transaction(STORE_NAME,"readwrite"),u=i.objectStore(STORE_NAME);u.put({url:r,timestamp:n}),i.oncomplete=function(){t(e)},i.onabort=function(){o(i.error)}})}function expireOldEntries(e,r,n){return r?new Promise(function(t,o){var i=1e3*r,u=[],c=e.transaction(STORE_NAME,"readwrite"),s=c.objectStore(STORE_NAME),a=s.index(TIMESTAMP_PROPERTY);a.openCursor().onsuccess=function(e){var r=e.target.result;if(r&&n-i>r.value[TIMESTAMP_PROPERTY]){var t=r.value[URL_PROPERTY];u.push(t),s["delete"](t),r["continue"]()}},c.oncomplete=function(){t(u)},c.onabort=o}):Promise.resolve([])}function expireExtraEntries(e,r){return r?new Promise(function(n,t){var o=[],i=e.transaction(STORE_NAME,"readwrite"),u=i.objectStore(STORE_NAME),c=u.index(TIMESTAMP_PROPERTY),s=c.count();c.count().onsuccess=function(){var e=s.result;e>r&&(c.openCursor().onsuccess=function(n){var t=n.target.result;if(t){var i=t.value[URL_PROPERTY];o.push(i),u["delete"](i),e-o.length>r&&t["continue"]()}})},i.oncomplete=function(){n(o)},i.onabort=t}):Promise.resolve([])}function expireEntries(e,r,n,t){return expireOldEntries(e,n,t).then(function(n){return expireExtraEntries(e,r).then(function(e){return n.concat(e)})})}var DB_PREFIX="sw-toolbox-",DB_VERSION=1,STORE_NAME="store",URL_PROPERTY="url",TIMESTAMP_PROPERTY="timestamp",cacheNameToDbPromise={};module.exports={getDb:getDb,setTimestampForUrl:setTimestampForUrl,expireEntries:expireEntries}; | ||
},{}],3:[function(require,module,exports){ | ||
"use strict";var scope;scope=self.registration?self.registration.scope:self.scope||new URL("./",self.location).href,module.exports={cache:{name:"$$$toolbox-cache$$$"+scope+"$$$",maxAgeSeconds:null,maxEntries:null},debug:!1,networkTimeoutSeconds:null,preCacheItems:[],successResponses:/^0|([123]\d\d)|(40[14567])|410$/}; | ||
},{}],4:[function(require,module,exports){ | ||
"use strict";var scope;scope=self.registration?self.registration.scope:self.scope||new URL("./",self.location).href,module.exports={cache:{name:"$$$toolbox-cache$$$"+scope+"$$$",maxAgeSeconds:null,maxEntries:null},debug:!1,networkTimeoutSeconds:null,preCacheItems:[],successResponses:/^0|([123]\d\d)|(40[14567])|410$/}; | ||
},{}],5:[function(require,module,exports){ | ||
"use strict";var url=new URL("./",self.location),basePath=url.pathname,pathRegexp=require("path-to-regexp"),Route=function(e,t,i,s){0!==t.indexOf("/")&&(t=basePath+t),this.method=e,this.keys=[],this.regexp=pathRegexp(t,this.keys),this.options=s,this.handler=i};Route.prototype.makeHandler=function(e){var t=this.regexp.exec(e),i={};return this.keys.forEach(function(e,s){i[e.name]=t[s+1]}),function(e){return this.handler(e,i,this.options)}.bind(this)},module.exports=Route; | ||
},{"path-to-regexp":13}],6:[function(require,module,exports){ | ||
"use strict";function regexEscape(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var Route=require("./route"),keyMatch=function(e,t){for(var r=e.entries(),o=r.next();!o.done;){var n=new RegExp(o.value[0]);if(n.test(t))return o.value[1];o=r.next()}return null},Router=function(){this.routes=new Map,this["default"]=null};["get","post","put","delete","head","any"].forEach(function(e){Router.prototype[e]=function(t,r,o){return this.add(e,t,r,o)}}),Router.prototype.add=function(e,t,r,o){o=o||{};var n=o.origin||self.location.origin;n=n instanceof RegExp?n.source:regexEscape(n),e=e.toLowerCase();var u=new Route(e,t,r,o);this.routes.has(n)||this.routes.set(n,new Map);var a=this.routes.get(n);a.has(e)||a.set(e,new Map);var s=a.get(e);s.set(u.regexp.source,u)},Router.prototype.matchMethod=function(e,t){t=new URL(t);var r=t.origin,o=t.pathname;e=e.toLowerCase();var n=keyMatch(this.routes,r);if(!n)return null;var u=n.get(e);if(!u)return null;var a=keyMatch(u,o);return a?a.makeHandler(o):null},Router.prototype.match=function(e){return this.matchMethod(e.method,e.url)||this.matchMethod("any",e.url)},module.exports=new Router; | ||
},{"./route":5}],7:[function(require,module,exports){ | ||
"use strict";var url=new URL("./",self.location),basePath=url.pathname,pathRegexp=require("path-to-regexp"),Route=function(e,t,i,s){t instanceof RegExp?this.fullUrlRegExp=t:(0!==t.indexOf("/")&&(t=basePath+t),this.keys=[],this.regexp=pathRegexp(t,this.keys)),this.method=e,this.options=s,this.handler=i};Route.prototype.makeHandler=function(e){if(this.regexp){var t=this.regexp.exec(e),i={};this.keys.forEach(function(e,s){i[e.name]=t[s+1]})}return function(e){return this.handler(e,i,this.options)}.bind(this)},module.exports=Route; | ||
},{"path-to-regexp":14}],5:[function(require,module,exports){ | ||
"use strict";function regexEscape(e){return e.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}var Route=require("./route"),keyMatch=function(e,t){for(var r=e.entries(),o=r.next();!o.done;){var n=new RegExp(o.value[0]);if(n.test(t))return o.value[1];o=r.next()}return null},Router=function(){this.routes=new Map,this["default"]=null};["get","post","put","delete","head","any"].forEach(function(e){Router.prototype[e]=function(t,r,o){return this.add(e,t,r,o)}}),Router.prototype.add=function(e,t,r,o){o=o||{};var n;t instanceof RegExp?n=RegExp:(n=o.origin||self.location.origin,n=n instanceof RegExp?n.source:regexEscape(n)),e=e.toLowerCase();var u=new Route(e,t,r,o);this.routes.has(n)||this.routes.set(n,new Map);var a=this.routes.get(n);a.has(e)||a.set(e,new Map);var s=a.get(e),i=u.regexp||u.fullUrlRegExp;s.set(i.source,u)},Router.prototype.matchMethod=function(e,t){var r=new URL(t),o=r.origin,n=r.pathname;return this._match(e,keyMatch(this.routes,o),n)||this._match(e,this.routes.get(RegExp),t)},Router.prototype._match=function(e,t,r){if(t){var o=t.get(e.toLowerCase());if(o){var n=keyMatch(o,r);if(n)return n.makeHandler(r)}}return null},Router.prototype.match=function(e){return this.matchMethod(e.method,e.url)||this.matchMethod("any",e.url)},module.exports=new Router; | ||
},{"./route":4}],6:[function(require,module,exports){ | ||
"use strict";function cacheFirst(e,r,t){return helpers.debug("Strategy: cache first ["+e.url+"]",t),helpers.openCache(t).then(function(r){return r.match(e).then(function(r){return r?r:helpers.fetchAndCache(e,t)})})}var helpers=require("../helpers");module.exports=cacheFirst; | ||
},{"../helpers":2}],8:[function(require,module,exports){ | ||
},{"../helpers":1}],7:[function(require,module,exports){ | ||
"use strict";function cacheOnly(e,r,c){return helpers.debug("Strategy: cache only ["+e.url+"]",c),helpers.openCache(c).then(function(r){return r.match(e)})}var helpers=require("../helpers");module.exports=cacheOnly; | ||
},{"../helpers":2}],9:[function(require,module,exports){ | ||
},{"../helpers":1}],8:[function(require,module,exports){ | ||
"use strict";function fastest(e,n,t){return helpers.debug("Strategy: fastest ["+e.url+"]",t),new Promise(function(r,s){var c=!1,o=[],a=function(e){o.push(e.toString()),c?s(new Error('Both cache and network failed: "'+o.join('", "')+'"')):c=!0},h=function(e){e instanceof Response?r(e):a("No result returned")};helpers.fetchAndCache(e.clone(),t).then(h,a),cacheOnly(e,n,t).then(h,a)})}var helpers=require("../helpers"),cacheOnly=require("./cacheOnly");module.exports=fastest; | ||
},{"../helpers":2,"./cacheOnly":8}],10:[function(require,module,exports){ | ||
},{"../helpers":1,"./cacheOnly":7}],9:[function(require,module,exports){ | ||
module.exports={networkOnly:require("./networkOnly"),networkFirst:require("./networkFirst"),cacheOnly:require("./cacheOnly"),cacheFirst:require("./cacheFirst"),fastest:require("./fastest")}; | ||
},{"./cacheFirst":7,"./cacheOnly":8,"./fastest":9,"./networkFirst":11,"./networkOnly":12}],11:[function(require,module,exports){ | ||
"use strict";function networkFirst(e,r,t){t=t||{};var s=t.successResponses||globalOptions.successResponses,o=t.networkTimeoutSeconds||globalOptions.networkTimeoutSeconds;return helpers.debug("Strategy: network first ["+e.url+"]",t),helpers.openCache(t).then(function(r){var n,u=[];if(o){var c=new Promise(function(t){n=setTimeout(function(){r.match(e).then(function(e){e&&t(e)})},1e3*o)});u.push(c)}var i=helpers.fetchAndCache(e,t).then(function(e){if(n&&clearTimeout(n),s.test(e.status))return e;throw helpers.debug("Response was an HTTP error: "+e.statusText,t),new Error("Bad response")})["catch"](function(){return helpers.debug("Network or response error, fallback to cache ["+e.url+"]",t),r.match(e)});return u.push(i),Promise.race(u)})}var globalOptions=require("../options"),helpers=require("../helpers");module.exports=networkFirst; | ||
},{"../helpers":2,"../options":4}],12:[function(require,module,exports){ | ||
},{"./cacheFirst":6,"./cacheOnly":7,"./fastest":8,"./networkFirst":10,"./networkOnly":11}],10:[function(require,module,exports){ | ||
"use strict";function networkFirst(e,r,t){t=t||{};var s=t.successResponses||globalOptions.successResponses,n=t.networkTimeoutSeconds||globalOptions.networkTimeoutSeconds;return helpers.debug("Strategy: network first ["+e.url+"]",t),helpers.openCache(t).then(function(r){var o,u,c=[];if(n){var i=new Promise(function(t){o=setTimeout(function(){r.match(e).then(function(e){e&&t(e)})},1e3*n)});c.push(i)}var a=helpers.fetchAndCache(e,t).then(function(e){if(o&&clearTimeout(o),s.test(e.status))return e;throw helpers.debug("Response was an HTTP error: "+e.statusText,t),u=e,new Error("Bad response")})["catch"](function(){return helpers.debug("Network or response error, fallback to cache ["+e.url+"]",t),r.match(e).then(function(e){return e||u})});return c.push(a),Promise.race(c)})}var globalOptions=require("../options"),helpers=require("../helpers");module.exports=networkFirst; | ||
},{"../helpers":1,"../options":3}],11:[function(require,module,exports){ | ||
"use strict";function networkOnly(e,r,t){return helpers.debug("Strategy: network only ["+e.url+"]",t),fetch(e)}var helpers=require("../helpers");module.exports=networkOnly; | ||
},{"../helpers":2}],13:[function(require,module,exports){ | ||
},{"../helpers":1}],12:[function(require,module,exports){ | ||
"use strict";function cache(e,t){return helpers.openCache(t).then(function(t){return t.add(e)})}function uncache(e,t){return helpers.openCache(t).then(function(t){return t["delete"](e)})}function precache(e){Array.isArray(e)||(e=[e]),options.preCacheItems=options.preCacheItems.concat(e)}require("serviceworker-cache-polyfill");var options=require("./options"),router=require("./router"),helpers=require("./helpers"),strategies=require("./strategies");helpers.debug("Service Worker Toolbox is loading");var flatten=function(e){return e.reduce(function(e,t){return e.concat(t)},[])};self.addEventListener("install",function(e){var t=options.cache.name+"$$$inactive$$$";helpers.debug("install event fired"),helpers.debug("creating cache ["+t+"]"),e.waitUntil(helpers.openCache({cache:{name:t}}).then(function(e){return Promise.all(options.preCacheItems).then(flatten).then(function(t){return helpers.debug("preCache list: "+(t.join(", ")||"(none)")),e.addAll(t)})}))}),self.addEventListener("activate",function(e){helpers.debug("activate event fired");var t=options.cache.name+"$$$inactive$$$";e.waitUntil(helpers.renameCache(t,options.cache.name))}),self.addEventListener("fetch",function(e){var t=router.match(e.request);t?e.respondWith(t(e.request)):router["default"]&&"GET"===e.request.method&&e.respondWith(router["default"](e.request))}),module.exports={networkOnly:strategies.networkOnly,networkFirst:strategies.networkFirst,cacheOnly:strategies.cacheOnly,cacheFirst:strategies.cacheFirst,fastest:strategies.fastest,router:router,options:options,cache:cache,uncache:uncache,precache:precache}; | ||
},{"./helpers":1,"./options":3,"./router":5,"./strategies":9,"serviceworker-cache-polyfill":15}],13:[function(require,module,exports){ | ||
module.exports=Array.isArray||function(r){return"[object Array]"==Object.prototype.toString.call(r)}; | ||
},{}],14:[function(require,module,exports){ | ||
function parse(e){for(var t,r=[],n=0,o=0,p="";null!=(t=PATH_REGEXP.exec(e));){var a=t[0],i=t[1],s=t.index;if(p+=e.slice(o,s),o=s+a.length,i)p+=i[1];else{p&&(r.push(p),p="");var u=t[2],c=t[3],l=t[4],f=t[5],g=t[6],x=t[7],h="+"===g||"*"===g,m="?"===g||"*"===g,y=u||"/",T=l||f||(x?".*":"[^"+y+"]+?");r.push({name:c||n++,prefix:u||"",delimiter:y,optional:m,repeat:h,pattern:escapeGroup(T)})}}return o<e.length&&(p+=e.substr(o)),p&&r.push(p),r}function compile(e){return tokensToFunction(parse(e))}function tokensToFunction(e){for(var t=new Array(e.length),r=0;r<e.length;r++)"object"==typeof e[r]&&(t[r]=new RegExp("^"+e[r].pattern+"$"));return function(r){for(var n="",o=r||{},p=0;p<e.length;p++){var a=e[p];if("string"!=typeof a){var i,s=o[a.name];if(null==s){if(a.optional)continue;throw new TypeError('Expected "'+a.name+'" to be defined')}if(isarray(s)){if(!a.repeat)throw new TypeError('Expected "'+a.name+'" to not repeat, but received "'+s+'"');if(0===s.length){if(a.optional)continue;throw new TypeError('Expected "'+a.name+'" to not be empty')}for(var u=0;u<s.length;u++){if(i=encodeURIComponent(s[u]),!t[p].test(i))throw new TypeError('Expected all "'+a.name+'" to match "'+a.pattern+'", but received "'+i+'"');n+=(0===u?a.prefix:a.delimiter)+i}}else{if(i=encodeURIComponent(s),!t[p].test(i))throw new TypeError('Expected "'+a.name+'" to match "'+a.pattern+'", but received "'+i+'"');n+=a.prefix+i}}else n+=a}return n}}function escapeString(e){return e.replace(/([.+*?=^!:${}()[\]|\/])/g,"\\$1")}function escapeGroup(e){return e.replace(/([=!:$\/()])/g,"\\$1")}function attachKeys(e,t){return e.keys=t,e}function flags(e){return e.sensitive?"":"i"}function regexpToRegexp(e,t){var r=e.source.match(/\((?!\?)/g);if(r)for(var n=0;n<r.length;n++)t.push({name:n,prefix:null,delimiter:null,optional:!1,repeat:!1,pattern:null});return attachKeys(e,t)}function arrayToRegexp(e,t,r){for(var n=[],o=0;o<e.length;o++)n.push(pathToRegexp(e[o],t,r).source);var p=new RegExp("(?:"+n.join("|")+")",flags(r));return attachKeys(p,t)}function stringToRegexp(e,t,r){for(var n=parse(e),o=tokensToRegExp(n,r),p=0;p<n.length;p++)"string"!=typeof n[p]&&t.push(n[p]);return attachKeys(o,t)}function tokensToRegExp(e,t){t=t||{};for(var r=t.strict,n=t.end!==!1,o="",p=e[e.length-1],a="string"==typeof p&&/\/$/.test(p),i=0;i<e.length;i++){var s=e[i];if("string"==typeof s)o+=escapeString(s);else{var u=escapeString(s.prefix),c=s.pattern;s.repeat&&(c+="(?:"+u+c+")*"),c=s.optional?u?"(?:"+u+"("+c+"))?":"("+c+")?":u+"("+c+")",o+=c}}return r||(o=(a?o.slice(0,-2):o)+"(?:\\/(?=$))?"),o+=n?"$":r&&a?"":"(?=\\/|$)",new RegExp("^"+o,flags(t))}function pathToRegexp(e,t,r){return t=t||[],isarray(t)?r||(r={}):(r=t,t=[]),e instanceof RegExp?regexpToRegexp(e,t,r):isarray(e)?arrayToRegexp(e,t,r):stringToRegexp(e,t,r)}var isarray=require("isarray");module.exports=pathToRegexp,module.exports.parse=parse,module.exports.compile=compile,module.exports.tokensToFunction=tokensToFunction,module.exports.tokensToRegExp=tokensToRegExp;var PATH_REGEXP=new RegExp(["(\\\\.)","([\\/.])?(?:(?:\\:(\\w+)(?:\\(((?:\\\\.|[^()])+)\\))?|\\(((?:\\\\.|[^()])+)\\))([+*?])?|(\\*))"].join("|"),"g"); | ||
},{"isarray":14}],14:[function(require,module,exports){ | ||
module.exports=Array.isArray||function(r){return"[object Array]"==Object.prototype.toString.call(r)}; | ||
},{}],15:[function(require,module,exports){ | ||
Cache.prototype.addAll||(Cache.prototype.addAll=function(t){function e(t){this.name="NetworkError",this.code=19,this.message=t}var r=this;return e.prototype=Object.create(Error.prototype),Promise.resolve().then(function(){if(arguments.length<1)throw new TypeError;return t=t.map(function(t){return t instanceof Request?t:String(t)}),Promise.all(t.map(function(t){"string"==typeof t&&(t=new Request(t));var r=new URL(t.url).protocol;if("http:"!==r&&"https:"!==r)throw new e("Invalid scheme");return fetch(t.clone())}))}).then(function(e){return Promise.all(e.map(function(e,n){return r.put(t[n],e)}))}).then(function(){return void 0})}); | ||
},{}]},{},[1])(1) | ||
},{"isarray":13}],15:[function(require,module,exports){ | ||
Cache.prototype.addAll||(Cache.prototype.addAll=function(t){function e(t){this.name="NetworkError",this.code=19,this.message=t}var r=this;return e.prototype=Object.create(Error.prototype),Promise.resolve().then(function(){if(arguments.length<1)throw new TypeError;return t=t.map(function(t){return t instanceof Request?t:String(t)}),Promise.all(t.map(function(t){"string"==typeof t&&(t=new Request(t));var r=new URL(t.url).protocol;if("http:"!==r&&"https:"!==r)throw new e("Invalid scheme");return fetch(t.clone())}))}).then(function(e){return Promise.all(e.map(function(e,n){return r.put(t[n],e)}))}).then(function(){})}); | ||
},{}]},{},[12])(12) | ||
}); | ||
@@ -50,0 +50,0 @@ |
@@ -1,1 +0,1 @@ | ||
{"version":3,"sources":["node_modules/browserify/node_modules/browser-pack/_prelude.js","lib/sw-toolbox.js","lib/options.js","lib/router.js","lib/helpers.js","lib/strategies/index.js","lib/route.js","lib/idb-cache-expiration.js","lib/strategies/networkOnly.js","lib/strategies/cacheOnly.js","lib/strategies/networkFirst.js","lib/strategies/cacheFirst.js","lib/strategies/fastest.js","node_modules/serviceworker-cache-polyfill/index.js","node_modules/path-to-regexp/index.js","node_modules/path-to-regexp/node_modules/isarray/index.js"],"names":["cache","url","options","helpers","openCache","then","add","uncache","precache","items","Array","isArray","preCacheItems","concat","require","router","strategies","debug","flatten","reduce","a","b","self","addEventListener","event","inactiveCache","name","waitUntil","Promise","all","join","addAll","renameCache","handler","match","request","respondWith","method","module","exports","networkOnly","networkFirst","cacheOnly","cacheFirst","fastest","scope","registration","URL","location","href","maxAgeSeconds","maxEntries","networkTimeoutSeconds","successResponses","regexEscape","s","replace","Route","keyMatch","map","string","entriesIterator","entries","item","next","done","pattern","RegExp","value","test","Router","this","routes","Map","forEach","prototype","path","origin","source","toLowerCase","route","has","set","methodMap","get","routeMap","regexp","matchMethod","pathname","methods","makeHandler","message","flag","globalOptions","console","log","cacheName","caches","open","fetchAndCache","fetch","clone","response","status","put","maxCacheEntries","maxCacheAgeSeconds","queueCacheExpiration","cacheExpiration","cacheExpirationPromiseFactory","bind","cacheExpirationPromiseChain","requestUrl","now","Date","idbCacheExpiration","getDb","db","setTimestampForUrl","expireEntries","urlsToDelete","deletionPromises","urlToDelete","error","destination","results","sourceCache","destCache","keys","requests","basePath","pathRegexp","indexOf","exec","values","key","index","openDb","resolve","reject","indexedDB","DB_PREFIX","DB_VERSION","onupgradeneeded","objectStore","result","createObjectStore","STORE_NAME","keyPath","URL_PROPERTY","createIndex","TIMESTAMP_PROPERTY","unique","onsuccess","onerror","cacheNameToDbPromise","transaction","timestamp","oncomplete","onabort","expireOldEntries","maxAgeMillis","urls","openCursor","cursorEvent","cursor","target","push","expireExtraEntries","countRequest","count","initialCount","length","oldExpiredUrls","extraExpiredUrls","timeoutId","promises","cacheWhenTimedOutPromise","setTimeout","networkPromise","clearTimeout","statusText","Error","race","rejected","reasons","maybeReject","reason","toString","maybeResolve","Response","Cache","NetworkError","code","Object","create","arguments","TypeError","Request","String","scheme","protocol","responses","i","undefined","parse","str","res","tokens","PATH_REGEXP","m","escaped","offset","slice","prefix","capture","group","suffix","asterisk","repeat","optional","delimiter","escapeGroup","substr","compile","tokensToFunction","matches","obj","data","token","segment","isarray","j","encodeURIComponent","escapeString","attachKeys","re","flags","sensitive","regexpToRegexp","groups","arrayToRegexp","parts","pathToRegexp","stringToRegexp","tokensToRegExp","strict","end","lastToken","endsWithSlash","arr","call"],"mappings":"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","file":"bundle.js","sourcesContent":["(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})","/*\n Copyright 2014 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\n\nrequire('serviceworker-cache-polyfill');\nvar options = require('./options');\nvar router = require('./router');\nvar helpers = require('./helpers');\nvar strategies = require('./strategies');\n\nhelpers.debug('Service Worker Toolbox is loading');\n\n// Install\n\nvar flatten = function(items) {\n return items.reduce(function(a, b) {\n return a.concat(b);\n }, []);\n};\n\nself.addEventListener('install', function(event) {\n var inactiveCache = options.cache.name + '$$$inactive$$$';\n helpers.debug('install event fired');\n helpers.debug('creating cache [' + inactiveCache + ']');\n event.waitUntil(\n helpers.openCache({cache: {name: inactiveCache}}).then(function(cache) {\n return Promise.all(options.preCacheItems)\n .then(flatten)\n .then(function(preCacheItems) {\n helpers.debug('preCache list: ' + (preCacheItems.join(', ') || '(none)'));\n return cache.addAll(preCacheItems);\n });\n })\n );\n});\n\n// Activate\n\nself.addEventListener('activate', function(event) {\n helpers.debug('activate event fired');\n var inactiveCache = options.cache.name + '$$$inactive$$$';\n event.waitUntil(helpers.renameCache(inactiveCache, options.cache.name));\n});\n\n// Fetch\n\nself.addEventListener('fetch', function(event) {\n var handler = router.match(event.request);\n\n if (handler) {\n event.respondWith(handler(event.request));\n } else if (router.default && event.request.method === 'GET') {\n event.respondWith(router.default(event.request));\n }\n});\n\n// Caching\n\nfunction cache(url, options) {\n return helpers.openCache(options).then(function(cache) {\n return cache.add(url);\n });\n}\n\nfunction uncache(url, options) {\n return helpers.openCache(options).then(function(cache) {\n return cache.delete(url);\n });\n}\n\nfunction precache(items) {\n if (!Array.isArray(items)) {\n items = [items];\n }\n options.preCacheItems = options.preCacheItems.concat(items);\n}\n\nmodule.exports = {\n networkOnly: strategies.networkOnly,\n networkFirst: strategies.networkFirst,\n cacheOnly: strategies.cacheOnly,\n cacheFirst: strategies.cacheFirst,\n fastest: strategies.fastest,\n router: router,\n options: options,\n cache: cache,\n uncache: uncache,\n precache: precache\n};\n","/*\n\tCopyright 2015 Google Inc. All Rights Reserved.\n\n\tLicensed under the Apache License, Version 2.0 (the \"License\");\n\tyou may not use this file except in compliance with the License.\n\tYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n\tUnless required by applicable law or agreed to in writing, software\n\tdistributed under the License is distributed on an \"AS IS\" BASIS,\n\tWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\tSee the License for the specific language governing permissions and\n\tlimitations under the License.\n*/\n'use strict';\n\n// TODO: This is necessary to handle different implementations in the wild\n// The spec defines self.registration, but it was not implemented in Chrome 40.\nvar scope;\nif (self.registration) {\n scope = self.registration.scope;\n} else {\n scope = self.scope || new URL('./', self.location).href;\n}\n\nmodule.exports = {\n cache: {\n name: '$$$toolbox-cache$$$' + scope + '$$$',\n maxAgeSeconds: null,\n maxEntries: null\n },\n debug: false,\n networkTimeoutSeconds: null,\n preCacheItems: [],\n // A regular expression to apply to HTTP response codes. Codes that match\n // will be considered successes, while others will not, and will not be\n // cached.\n successResponses: /^0|([123]\\d\\d)|(40[14567])|410$/\n};\n","/*\n Copyright 2014 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\n\nvar Route = require('./route');\n\nfunction regexEscape(s) {\n return s.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n}\n\nvar keyMatch = function(map, string) {\n // This would be better written as a for..of loop, but that would break the minifyify process\n // in the build.\n var entriesIterator = map.entries();\n var item = entriesIterator.next();\n while (!item.done) {\n var pattern = new RegExp(item.value[0]);\n if (pattern.test(string)) {\n return item.value[1];\n }\n item = entriesIterator.next();\n }\n return null;\n};\n\nvar Router = function() {\n this.routes = new Map();\n this.default = null;\n};\n\n['get', 'post', 'put', 'delete', 'head', 'any'].forEach(function(method) {\n Router.prototype[method] = function(path, handler, options) {\n return this.add(method, path, handler, options);\n };\n});\n\nRouter.prototype.add = function(method, path, handler, options) {\n options = options || {};\n var origin = options.origin || self.location.origin;\n if (origin instanceof RegExp) {\n origin = origin.source;\n } else {\n origin = regexEscape(origin);\n }\n method = method.toLowerCase();\n\n var route = new Route(method, path, handler, options);\n\n if (!this.routes.has(origin)) {\n this.routes.set(origin, new Map());\n }\n\n var methodMap = this.routes.get(origin);\n if (!methodMap.has(method)) {\n methodMap.set(method, new Map());\n }\n\n var routeMap = methodMap.get(method);\n routeMap.set(route.regexp.source, route);\n};\n\nRouter.prototype.matchMethod = function(method, url) {\n url = new URL(url);\n var origin = url.origin;\n var path = url.pathname;\n method = method.toLowerCase();\n\n var methods = keyMatch(this.routes, origin);\n if (!methods) {\n return null;\n }\n\n var routes = methods.get(method);\n if (!routes) {\n return null;\n }\n\n var route = keyMatch(routes, path);\n\n if (route) {\n return route.makeHandler(path);\n }\n\n return null;\n};\n\nRouter.prototype.match = function(request) {\n return this.matchMethod(request.method, request.url) || this.matchMethod('any', request.url);\n};\n\nmodule.exports = new Router();\n","/*\n Copyright 2014 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\n\nvar globalOptions = require('./options');\nvar idbCacheExpiration = require('./idb-cache-expiration');\n\nfunction debug(message, options) {\n options = options || {};\n var flag = options.debug || globalOptions.debug;\n if (flag) {\n console.log('[sw-toolbox] ' + message);\n }\n}\n\nfunction openCache(options) {\n var cacheName;\n if (options && options.cache) {\n cacheName = options.cache.name;\n }\n cacheName = cacheName || globalOptions.cache.name;\n\n debug('Opening cache \"' + cacheName + '\"', options);\n return caches.open(cacheName);\n}\n\nfunction fetchAndCache(request, options) {\n options = options || {};\n var successResponses = options.successResponses || globalOptions.successResponses;\n\n return fetch(request.clone()).then(function(response) {\n // Only cache GET requests with successful responses.\n // Since this is not part of the promise chain, it will be done asynchronously and will not\n // block the response from being returned to the page.\n if (request.method === 'GET' && successResponses.test(response.status)) {\n openCache(options).then(function(cache) {\n cache.put(request, response).then(function() {\n var maxCacheEntries;\n var maxCacheAgeSeconds;\n var cacheName;\n\n if (options.cache) {\n // If someone explicitly sets options.cache, then read all three settings from there.\n // Don't fall back on globalOptions.\n maxCacheEntries = options.cache.maxEntries;\n maxCacheAgeSeconds = options.cache.maxAgeSeconds;\n cacheName = options.cache.name;\n } else {\n maxCacheEntries = globalOptions.cache.maxEntries;\n maxCacheAgeSeconds = globalOptions.cache.maxAgeSeconds;\n cacheName = globalOptions.cache.name;\n }\n\n // Only run the cache expiration logic if at least one of the maximums is set, and if\n // we have a name for the cache that the options are being applied to.\n if ((maxCacheEntries || maxCacheAgeSeconds) && cacheName) {\n queueCacheExpiration(request, cache, cacheName, maxCacheEntries, maxCacheAgeSeconds);\n }\n });\n });\n }\n\n return response.clone();\n });\n}\n\nvar cacheExpirationPromiseChain;\nfunction queueCacheExpiration(request, cache, cacheName, maxCacheEntries, maxCacheAgeSeconds) {\n var cacheExpiration = cacheExpirationPromiseFactory.bind(null, request, cache, cacheName, maxCacheEntries,\n maxCacheAgeSeconds);\n\n if (cacheExpirationPromiseChain) {\n cacheExpirationPromiseChain = cacheExpirationPromiseChain.then(cacheExpiration);\n } else {\n cacheExpirationPromiseChain = cacheExpiration();\n }\n}\n\nfunction cacheExpirationPromiseFactory(request, cache, cacheName, maxCacheEntries, maxCacheAgeSeconds) {\n var requestUrl = request.url;\n\n var now = Date.now();\n debug('Updating LRU order for ' + requestUrl + '. Max entries is ' + maxCacheEntries +\n ', max age is ' + maxCacheAgeSeconds);\n\n return idbCacheExpiration.getDb(cacheName).then(function(db) {\n return idbCacheExpiration.setTimestampForUrl(db, requestUrl, now);\n }).then(function(db) {\n return idbCacheExpiration.expireEntries(db, maxCacheEntries, maxCacheAgeSeconds, now);\n }).then(function(urlsToDelete) {\n debug('Successfully updated IDB.');\n\n var deletionPromises = urlsToDelete.map(function(urlToDelete) {\n return cache.delete(urlToDelete);\n });\n\n return Promise.all(deletionPromises).then(function() {\n debug('Done with cache cleanup.');\n });\n }).catch(function(error) {\n debug(error);\n });\n}\n\nfunction renameCache(source, destination, options) {\n debug('Renaming cache: [' + source + '] to [' + destination + ']', options);\n return caches.delete(destination).then(function() {\n return Promise.all([\n caches.open(source),\n caches.open(destination)\n ]).then(function(results) {\n var sourceCache = results[0];\n var destCache = results[1];\n\n return sourceCache.keys().then(function(requests) {\n return Promise.all(requests.map(function(request) {\n return sourceCache.match(request).then(function(response) {\n return destCache.put(request, response);\n });\n }));\n }).then(function() {\n return caches.delete(source);\n });\n });\n });\n}\n\nmodule.exports = {\n debug: debug,\n fetchAndCache: fetchAndCache,\n openCache: openCache,\n renameCache: renameCache\n};\n","/*\n\tCopyright 2014 Google Inc. All Rights Reserved.\n\n\tLicensed under the Apache License, Version 2.0 (the \"License\");\n\tyou may not use this file except in compliance with the License.\n\tYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n\tUnless required by applicable law or agreed to in writing, software\n\tdistributed under the License is distributed on an \"AS IS\" BASIS,\n\tWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\tSee the License for the specific language governing permissions and\n\tlimitations under the License.\n*/\nmodule.exports = {\n networkOnly: require('./networkOnly'),\n networkFirst: require('./networkFirst'),\n cacheOnly: require('./cacheOnly'),\n cacheFirst: require('./cacheFirst'),\n fastest: require('./fastest')\n};\n","/*\n Copyright 2014 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\n\n// TODO: Use self.registration.scope instead of self.location\nvar url = new URL('./', self.location);\nvar basePath = url.pathname;\nvar pathRegexp = require('path-to-regexp');\n\nvar Route = function(method, path, handler, options) {\n // The URL() constructor can't parse express-style routes as they are not\n // valid urls. This means we have to manually manipulate relative urls into\n // absolute ones. This check is extremely naive but implementing a tweaked\n // version of the full algorithm seems like overkill\n // (https://url.spec.whatwg.org/#concept-basic-url-parser)\n if (path.indexOf('/') !== 0) {\n path = basePath + path;\n }\n\n this.method = method;\n this.keys = [];\n this.regexp = pathRegexp(path, this.keys);\n this.options = options;\n this.handler = handler;\n};\n\nRoute.prototype.makeHandler = function(url) {\n var match = this.regexp.exec(url);\n var values = {};\n this.keys.forEach(function(key, index) {\n values[key.name] = match[index + 1];\n });\n return function(request) {\n return this.handler(request, values, this.options);\n }.bind(this);\n};\n\nmodule.exports = Route;\n","/*\n Copyright 2015 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\n\nvar DB_PREFIX = 'sw-toolbox-';\nvar DB_VERSION = 1;\nvar STORE_NAME = 'store';\nvar URL_PROPERTY = 'url';\nvar TIMESTAMP_PROPERTY = 'timestamp';\nvar cacheNameToDbPromise = {};\n\nfunction openDb(cacheName) {\n return new Promise(function(resolve, reject) {\n var request = indexedDB.open(DB_PREFIX + cacheName, DB_VERSION);\n\n request.onupgradeneeded = function() {\n var objectStore = request.result.createObjectStore(STORE_NAME, {keyPath: URL_PROPERTY});\n objectStore.createIndex(TIMESTAMP_PROPERTY, TIMESTAMP_PROPERTY, {unique: false});\n };\n\n request.onsuccess = function() {\n resolve(request.result);\n };\n\n request.onerror = function() {\n reject(request.error);\n };\n });\n}\n\nfunction getDb(cacheName) {\n if (!(cacheName in cacheNameToDbPromise)) {\n cacheNameToDbPromise[cacheName] = openDb(cacheName);\n }\n\n return cacheNameToDbPromise[cacheName];\n}\n\nfunction setTimestampForUrl(db, url, now) {\n return new Promise(function(resolve, reject) {\n var transaction = db.transaction(STORE_NAME, 'readwrite');\n var objectStore = transaction.objectStore(STORE_NAME);\n objectStore.put({url: url, timestamp: now});\n\n transaction.oncomplete = function() {\n resolve(db);\n };\n\n transaction.onabort = function() {\n reject(transaction.error);\n };\n });\n}\n\nfunction expireOldEntries(db, maxAgeSeconds, now) {\n // Bail out early by resolving with an empty array if we're not using maxAgeSeconds.\n if (!maxAgeSeconds) {\n return Promise.resolve([]);\n }\n\n return new Promise(function(resolve, reject) {\n var maxAgeMillis = maxAgeSeconds * 1000;\n var urls = [];\n\n var transaction = db.transaction(STORE_NAME, 'readwrite');\n var objectStore = transaction.objectStore(STORE_NAME);\n var index = objectStore.index(TIMESTAMP_PROPERTY);\n\n index.openCursor().onsuccess = function(cursorEvent) {\n var cursor = cursorEvent.target.result;\n if (cursor) {\n if (now - maxAgeMillis > cursor.value[TIMESTAMP_PROPERTY]) {\n var url = cursor.value[URL_PROPERTY];\n urls.push(url);\n objectStore.delete(url);\n cursor.continue();\n }\n }\n };\n\n transaction.oncomplete = function() {\n resolve(urls);\n };\n\n transaction.onabort = reject;\n });\n}\n\nfunction expireExtraEntries(db, maxEntries) {\n // Bail out early by resolving with an empty array if we're not using maxEntries.\n if (!maxEntries) {\n return Promise.resolve([]);\n }\n\n return new Promise(function(resolve, reject) {\n var urls = [];\n\n var transaction = db.transaction(STORE_NAME, 'readwrite');\n var objectStore = transaction.objectStore(STORE_NAME);\n var index = objectStore.index(TIMESTAMP_PROPERTY);\n\n var countRequest = index.count();\n index.count().onsuccess = function() {\n var initialCount = countRequest.result;\n\n if (initialCount > maxEntries) {\n index.openCursor().onsuccess = function(cursorEvent) {\n var cursor = cursorEvent.target.result;\n if (cursor) {\n var url = cursor.value[URL_PROPERTY];\n urls.push(url);\n objectStore.delete(url);\n if (initialCount - urls.length > maxEntries) {\n cursor.continue();\n }\n }\n };\n }\n };\n\n transaction.oncomplete = function() {\n resolve(urls);\n };\n\n transaction.onabort = reject;\n });\n}\n\nfunction expireEntries(db, maxEntries, maxAgeSeconds, now) {\n return expireOldEntries(db, maxAgeSeconds, now).then(function(oldExpiredUrls) {\n return expireExtraEntries(db, maxEntries).then(function(extraExpiredUrls) {\n return oldExpiredUrls.concat(extraExpiredUrls);\n });\n });\n}\n\nmodule.exports = {\n getDb: getDb,\n setTimestampForUrl: setTimestampForUrl,\n expireEntries: expireEntries\n};\n","/*\n\tCopyright 2014 Google Inc. All Rights Reserved.\n\n\tLicensed under the Apache License, Version 2.0 (the \"License\");\n\tyou may not use this file except in compliance with the License.\n\tYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n\tUnless required by applicable law or agreed to in writing, software\n\tdistributed under the License is distributed on an \"AS IS\" BASIS,\n\tWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\tSee the License for the specific language governing permissions and\n\tlimitations under the License.\n*/\n'use strict';\nvar helpers = require('../helpers');\n\nfunction networkOnly(request, values, options) {\n helpers.debug('Strategy: network only [' + request.url + ']', options);\n return fetch(request);\n}\n\nmodule.exports = networkOnly;\n","/*\n\tCopyright 2014 Google Inc. All Rights Reserved.\n\n\tLicensed under the Apache License, Version 2.0 (the \"License\");\n\tyou may not use this file except in compliance with the License.\n\tYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n\tUnless required by applicable law or agreed to in writing, software\n\tdistributed under the License is distributed on an \"AS IS\" BASIS,\n\tWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\tSee the License for the specific language governing permissions and\n\tlimitations under the License.\n*/\n'use strict';\nvar helpers = require('../helpers');\n\nfunction cacheOnly(request, values, options) {\n helpers.debug('Strategy: cache only [' + request.url + ']', options);\n return helpers.openCache(options).then(function(cache) {\n return cache.match(request);\n });\n}\n\nmodule.exports = cacheOnly;\n","/*\n Copyright 2015 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\nvar globalOptions = require('../options');\nvar helpers = require('../helpers');\n\nfunction networkFirst(request, values, options) {\n options = options || {};\n var successResponses = options.successResponses || globalOptions.successResponses;\n // This will bypass options.networkTimeout if it's set to a false-y value like 0, but that's the\n // sane thing to do anyway.\n var networkTimeoutSeconds = options.networkTimeoutSeconds || globalOptions.networkTimeoutSeconds;\n helpers.debug('Strategy: network first [' + request.url + ']', options);\n\n return helpers.openCache(options).then(function(cache) {\n var timeoutId;\n var promises = [];\n\n if (networkTimeoutSeconds) {\n var cacheWhenTimedOutPromise = new Promise(function(resolve) {\n timeoutId = setTimeout(function() {\n cache.match(request).then(function(response) {\n if (response) {\n // Only resolve this promise if there's a valid response in the cache.\n // This ensures that we won't time out a network request unless there's a cached entry\n // to fallback on, which is arguably the preferable behavior.\n resolve(response);\n }\n });\n }, networkTimeoutSeconds * 1000);\n });\n promises.push(cacheWhenTimedOutPromise);\n }\n\n var networkPromise = helpers.fetchAndCache(request, options).then(function(response) {\n // We've got a response, so clear the network timeout if there is one.\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n if (successResponses.test(response.status)) {\n return response;\n }\n\n helpers.debug('Response was an HTTP error: ' + response.statusText, options);\n throw new Error('Bad response');\n }).catch(function() {\n helpers.debug('Network or response error, fallback to cache [' + request.url + ']', options);\n return cache.match(request);\n });\n promises.push(networkPromise);\n\n return Promise.race(promises);\n });\n}\n\nmodule.exports = networkFirst;\n","/*\n\tCopyright 2014 Google Inc. All Rights Reserved.\n\n\tLicensed under the Apache License, Version 2.0 (the \"License\");\n\tyou may not use this file except in compliance with the License.\n\tYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n\tUnless required by applicable law or agreed to in writing, software\n\tdistributed under the License is distributed on an \"AS IS\" BASIS,\n\tWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\tSee the License for the specific language governing permissions and\n\tlimitations under the License.\n*/\n'use strict';\nvar helpers = require('../helpers');\n\nfunction cacheFirst(request, values, options) {\n helpers.debug('Strategy: cache first [' + request.url + ']', options);\n return helpers.openCache(options).then(function(cache) {\n return cache.match(request).then(function(response) {\n if (response) {\n return response;\n }\n\n return helpers.fetchAndCache(request, options);\n });\n });\n}\n\nmodule.exports = cacheFirst;\n","/*\n Copyright 2014 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\nvar helpers = require('../helpers');\nvar cacheOnly = require('./cacheOnly');\n\nfunction fastest(request, values, options) {\n helpers.debug('Strategy: fastest [' + request.url + ']', options);\n\n return new Promise(function(resolve, reject) {\n var rejected = false;\n var reasons = [];\n\n var maybeReject = function(reason) {\n reasons.push(reason.toString());\n if (rejected) {\n reject(new Error('Both cache and network failed: \"' + reasons.join('\", \"') + '\"'));\n } else {\n rejected = true;\n }\n };\n\n var maybeResolve = function(result) {\n if (result instanceof Response) {\n resolve(result);\n } else {\n maybeReject('No result returned');\n }\n };\n\n helpers.fetchAndCache(request.clone(), options)\n .then(maybeResolve, maybeReject);\n\n cacheOnly(request, values, options)\n .then(maybeResolve, maybeReject);\n });\n}\n\nmodule.exports = fastest;\n","/**\n * Copyright 2015 Google Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\nif (!Cache.prototype.addAll) {\n Cache.prototype.addAll = function addAll(requests) {\n var cache = this;\n\n // Since DOMExceptions are not constructable:\n function NetworkError(message) {\n this.name = 'NetworkError';\n this.code = 19;\n this.message = message;\n }\n NetworkError.prototype = Object.create(Error.prototype);\n\n return Promise.resolve().then(function() {\n if (arguments.length < 1) throw new TypeError();\n\n // Simulate sequence<(Request or USVString)> binding:\n var sequence = [];\n\n requests = requests.map(function(request) {\n if (request instanceof Request) {\n return request;\n }\n else {\n return String(request); // may throw TypeError\n }\n });\n\n return Promise.all(\n requests.map(function(request) {\n if (typeof request === 'string') {\n request = new Request(request);\n }\n\n var scheme = new URL(request.url).protocol;\n\n if (scheme !== 'http:' && scheme !== 'https:') {\n throw new NetworkError(\"Invalid scheme\");\n }\n\n return fetch(request.clone());\n })\n );\n }).then(function(responses) {\n // TODO: check that requests don't overwrite one another\n // (don't think this is possible to polyfill due to opaque responses)\n return Promise.all(\n responses.map(function(response, i) {\n return cache.put(requests[i], response);\n })\n );\n }).then(function() {\n return undefined;\n });\n };\n}\n","var isarray = require('isarray')\n\n/**\n * Expose `pathToRegexp`.\n */\nmodule.exports = pathToRegexp\nmodule.exports.parse = parse\nmodule.exports.compile = compile\nmodule.exports.tokensToFunction = tokensToFunction\nmodule.exports.tokensToRegExp = tokensToRegExp\n\n/**\n * The main path matching regexp utility.\n *\n * @type {RegExp}\n */\nvar PATH_REGEXP = new RegExp([\n // Match escaped characters that would otherwise appear in future matches.\n // This allows the user to escape special characters that won't transform.\n '(\\\\\\\\.)',\n // Match Express-style parameters and un-named parameters with a prefix\n // and optional suffixes. Matches appear as:\n //\n // \"/:test(\\\\d+)?\" => [\"/\", \"test\", \"\\d+\", undefined, \"?\", undefined]\n // \"/route(\\\\d+)\" => [undefined, undefined, undefined, \"\\d+\", undefined, undefined]\n // \"/*\" => [\"/\", undefined, undefined, undefined, undefined, \"*\"]\n '([\\\\/.])?(?:(?:\\\\:(\\\\w+)(?:\\\\(((?:\\\\\\\\.|[^()])+)\\\\))?|\\\\(((?:\\\\\\\\.|[^()])+)\\\\))([+*?])?|(\\\\*))'\n].join('|'), 'g')\n\n/**\n * Parse a string for the raw tokens.\n *\n * @param {String} str\n * @return {Array}\n */\nfunction parse (str) {\n var tokens = []\n var key = 0\n var index = 0\n var path = ''\n var res\n\n while ((res = PATH_REGEXP.exec(str)) != null) {\n var m = res[0]\n var escaped = res[1]\n var offset = res.index\n path += str.slice(index, offset)\n index = offset + m.length\n\n // Ignore already escaped sequences.\n if (escaped) {\n path += escaped[1]\n continue\n }\n\n // Push the current path onto the tokens.\n if (path) {\n tokens.push(path)\n path = ''\n }\n\n var prefix = res[2]\n var name = res[3]\n var capture = res[4]\n var group = res[5]\n var suffix = res[6]\n var asterisk = res[7]\n\n var repeat = suffix === '+' || suffix === '*'\n var optional = suffix === '?' || suffix === '*'\n var delimiter = prefix || '/'\n var pattern = capture || group || (asterisk ? '.*' : '[^' + delimiter + ']+?')\n\n tokens.push({\n name: name || key++,\n prefix: prefix || '',\n delimiter: delimiter,\n optional: optional,\n repeat: repeat,\n pattern: escapeGroup(pattern)\n })\n }\n\n // Match any characters still remaining.\n if (index < str.length) {\n path += str.substr(index)\n }\n\n // If the path exists, push it onto the end.\n if (path) {\n tokens.push(path)\n }\n\n return tokens\n}\n\n/**\n * Compile a string to a template function for the path.\n *\n * @param {String} str\n * @return {Function}\n */\nfunction compile (str) {\n return tokensToFunction(parse(str))\n}\n\n/**\n * Expose a method for transforming tokens into the path function.\n */\nfunction tokensToFunction (tokens) {\n // Compile all the tokens into regexps.\n var matches = new Array(tokens.length)\n\n // Compile all the patterns before compilation.\n for (var i = 0; i < tokens.length; i++) {\n if (typeof tokens[i] === 'object') {\n matches[i] = new RegExp('^' + tokens[i].pattern + '$')\n }\n }\n\n return function (obj) {\n var path = ''\n var data = obj || {}\n\n for (var i = 0; i < tokens.length; i++) {\n var token = tokens[i]\n\n if (typeof token === 'string') {\n path += token\n\n continue\n }\n\n var value = data[token.name]\n var segment\n\n if (value == null) {\n if (token.optional) {\n continue\n } else {\n throw new TypeError('Expected \"' + token.name + '\" to be defined')\n }\n }\n\n if (isarray(value)) {\n if (!token.repeat) {\n throw new TypeError('Expected \"' + token.name + '\" to not repeat, but received \"' + value + '\"')\n }\n\n if (value.length === 0) {\n if (token.optional) {\n continue\n } else {\n throw new TypeError('Expected \"' + token.name + '\" to not be empty')\n }\n }\n\n for (var j = 0; j < value.length; j++) {\n segment = encodeURIComponent(value[j])\n\n if (!matches[i].test(segment)) {\n throw new TypeError('Expected all \"' + token.name + '\" to match \"' + token.pattern + '\", but received \"' + segment + '\"')\n }\n\n path += (j === 0 ? token.prefix : token.delimiter) + segment\n }\n\n continue\n }\n\n segment = encodeURIComponent(value)\n\n if (!matches[i].test(segment)) {\n throw new TypeError('Expected \"' + token.name + '\" to match \"' + token.pattern + '\", but received \"' + segment + '\"')\n }\n\n path += token.prefix + segment\n }\n\n return path\n }\n}\n\n/**\n * Escape a regular expression string.\n *\n * @param {String} str\n * @return {String}\n */\nfunction escapeString (str) {\n return str.replace(/([.+*?=^!:${}()[\\]|\\/])/g, '\\\\$1')\n}\n\n/**\n * Escape the capturing group by escaping special characters and meaning.\n *\n * @param {String} group\n * @return {String}\n */\nfunction escapeGroup (group) {\n return group.replace(/([=!:$\\/()])/g, '\\\\$1')\n}\n\n/**\n * Attach the keys as a property of the regexp.\n *\n * @param {RegExp} re\n * @param {Array} keys\n * @return {RegExp}\n */\nfunction attachKeys (re, keys) {\n re.keys = keys\n return re\n}\n\n/**\n * Get the flags for a regexp from the options.\n *\n * @param {Object} options\n * @return {String}\n */\nfunction flags (options) {\n return options.sensitive ? '' : 'i'\n}\n\n/**\n * Pull out keys from a regexp.\n *\n * @param {RegExp} path\n * @param {Array} keys\n * @return {RegExp}\n */\nfunction regexpToRegexp (path, keys) {\n // Use a negative lookahead to match only capturing groups.\n var groups = path.source.match(/\\((?!\\?)/g)\n\n if (groups) {\n for (var i = 0; i < groups.length; i++) {\n keys.push({\n name: i,\n prefix: null,\n delimiter: null,\n optional: false,\n repeat: false,\n pattern: null\n })\n }\n }\n\n return attachKeys(path, keys)\n}\n\n/**\n * Transform an array into a regexp.\n *\n * @param {Array} path\n * @param {Array} keys\n * @param {Object} options\n * @return {RegExp}\n */\nfunction arrayToRegexp (path, keys, options) {\n var parts = []\n\n for (var i = 0; i < path.length; i++) {\n parts.push(pathToRegexp(path[i], keys, options).source)\n }\n\n var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options))\n\n return attachKeys(regexp, keys)\n}\n\n/**\n * Create a path regexp from string input.\n *\n * @param {String} path\n * @param {Array} keys\n * @param {Object} options\n * @return {RegExp}\n */\nfunction stringToRegexp (path, keys, options) {\n var tokens = parse(path)\n var re = tokensToRegExp(tokens, options)\n\n // Attach keys back to the regexp.\n for (var i = 0; i < tokens.length; i++) {\n if (typeof tokens[i] !== 'string') {\n keys.push(tokens[i])\n }\n }\n\n return attachKeys(re, keys)\n}\n\n/**\n * Expose a function for taking tokens and returning a RegExp.\n *\n * @param {Array} tokens\n * @param {Array} keys\n * @param {Object} options\n * @return {RegExp}\n */\nfunction tokensToRegExp (tokens, options) {\n options = options || {}\n\n var strict = options.strict\n var end = options.end !== false\n var route = ''\n var lastToken = tokens[tokens.length - 1]\n var endsWithSlash = typeof lastToken === 'string' && /\\/$/.test(lastToken)\n\n // Iterate over the tokens and create our regexp string.\n for (var i = 0; i < tokens.length; i++) {\n var token = tokens[i]\n\n if (typeof token === 'string') {\n route += escapeString(token)\n } else {\n var prefix = escapeString(token.prefix)\n var capture = token.pattern\n\n if (token.repeat) {\n capture += '(?:' + prefix + capture + ')*'\n }\n\n if (token.optional) {\n if (prefix) {\n capture = '(?:' + prefix + '(' + capture + '))?'\n } else {\n capture = '(' + capture + ')?'\n }\n } else {\n capture = prefix + '(' + capture + ')'\n }\n\n route += capture\n }\n }\n\n // In non-strict mode we allow a slash at the end of match. If the path to\n // match already ends with a slash, we remove it for consistency. The slash\n // is valid at the end of a path match, not in the middle. This is important\n // in non-ending mode, where \"/test/\" shouldn't match \"/test//route\".\n if (!strict) {\n route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\\\/(?=$))?'\n }\n\n if (end) {\n route += '$'\n } else {\n // In non-ending mode, we need the capturing groups to match as much as\n // possible by using a positive lookahead to the end or next path segment.\n route += strict && endsWithSlash ? '' : '(?=\\\\/|$)'\n }\n\n return new RegExp('^' + route, flags(options))\n}\n\n/**\n * Normalize the given path string, returning a regular expression.\n *\n * An empty array can be passed in for the keys, which will hold the\n * placeholder key descriptions. For example, using `/user/:id`, `keys` will\n * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.\n *\n * @param {(String|RegExp|Array)} path\n * @param {Array} [keys]\n * @param {Object} [options]\n * @return {RegExp}\n */\nfunction pathToRegexp (path, keys, options) {\n keys = keys || []\n\n if (!isarray(keys)) {\n options = keys\n keys = []\n } else if (!options) {\n options = {}\n }\n\n if (path instanceof RegExp) {\n return regexpToRegexp(path, keys, options)\n }\n\n if (isarray(path)) {\n return arrayToRegexp(path, keys, options)\n }\n\n return stringToRegexp(path, keys, options)\n}\n","module.exports = Array.isArray || function (arr) {\n return Object.prototype.toString.call(arr) == '[object Array]';\n};\n"]} | ||
{"version":3,"sources":["node_modules/browser-pack/_prelude.js","lib/sw-toolbox.js","lib/router.js","lib/options.js","lib/helpers.js","lib/strategies/index.js","lib/route.js","lib/idb-cache-expiration.js","node_modules/serviceworker-cache-polyfill/index.js","lib/strategies/networkOnly.js","lib/strategies/networkFirst.js","lib/strategies/cacheOnly.js","lib/strategies/cacheFirst.js","lib/strategies/fastest.js","node_modules/path-to-regexp/index.js","node_modules/isarray/index.js"],"names":["cache","url","options","helpers","openCache","then","add","uncache","precache","items","Array","isArray","preCacheItems","concat","require","router","strategies","debug","flatten","reduce","a","b","self","addEventListener","event","inactiveCache","name","waitUntil","Promise","all","join","addAll","renameCache","handler","match","request","respondWith","method","module","exports","networkOnly","networkFirst","cacheOnly","cacheFirst","fastest","regexEscape","s","replace","Route","keyMatch","map","string","entriesIterator","entries","item","next","done","pattern","RegExp","value","test","Router","this","routes","Map","forEach","prototype","path","origin","location","source","toLowerCase","route","has","set","methodMap","get","routeMap","regExp","regexp","fullUrlRegExp","matchMethod","urlObject","URL","pathname","_match","pathOrUrl","makeHandler","scope","registration","href","maxAgeSeconds","maxEntries","networkTimeoutSeconds","successResponses","message","flag","globalOptions","console","log","cacheName","caches","open","fetchAndCache","fetch","clone","response","status","put","cacheOptions","queueCacheExpiration","cleanup","cleanupCache","bind","cleanupQueue","requestUrl","now","Date","idbCacheExpiration","getDb","db","setTimestampForUrl","expireEntries","urlsToDelete","deletionPromises","urlToDelete","error","destination","results","sourceCache","destCache","keys","requests","basePath","pathRegexp","indexOf","exec","values","key","index","openDb","resolve","reject","indexedDB","DB_PREFIX","DB_VERSION","onupgradeneeded","objectStore","result","createObjectStore","STORE_NAME","keyPath","URL_PROPERTY","createIndex","TIMESTAMP_PROPERTY","unique","onsuccess","onerror","cacheNameToDbPromise","transaction","timestamp","oncomplete","onabort","expireOldEntries","maxAgeMillis","urls","openCursor","cursorEvent","cursor","target","push","expireExtraEntries","countRequest","count","initialCount","length","oldUrls","extraUrls","Cache","NetworkError","code","Object","create","Error","arguments","TypeError","Request","String","scheme","protocol","responses","i","timeoutId","originalResponse","promises","cacheWhenTimedOutPromise","setTimeout","networkPromise","clearTimeout","statusText","race","rejected","reasons","maybeReject","reason","toString","maybeResolve","Response","parse","str","res","tokens","PATH_REGEXP","m","escaped","offset","slice","prefix","capture","group","suffix","asterisk","repeat","optional","delimiter","escapeGroup","substr","compile","tokensToFunction","matches","obj","data","token","segment","isarray","j","encodeURIComponent","escapeString","attachKeys","re","flags","sensitive","regexpToRegexp","groups","arrayToRegexp","parts","pathToRegexp","stringToRegexp","tokensToRegExp","strict","end","lastToken","endsWithSlash","arr","call"],"mappings":"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","file":"bundle.js","sourcesContent":["(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})","/*\n Copyright 2014 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\n\nrequire('serviceworker-cache-polyfill');\nvar options = require('./options');\nvar router = require('./router');\nvar helpers = require('./helpers');\nvar strategies = require('./strategies');\n\nhelpers.debug('Service Worker Toolbox is loading');\n\n// Install\n\nvar flatten = function(items) {\n return items.reduce(function(a, b) {\n return a.concat(b);\n }, []);\n};\n\nself.addEventListener('install', function(event) {\n var inactiveCache = options.cache.name + '$$$inactive$$$';\n helpers.debug('install event fired');\n helpers.debug('creating cache [' + inactiveCache + ']');\n event.waitUntil(\n helpers.openCache({cache: {name: inactiveCache}}).then(function(cache) {\n return Promise.all(options.preCacheItems)\n .then(flatten)\n .then(function(preCacheItems) {\n helpers.debug('preCache list: ' +\n (preCacheItems.join(', ') || '(none)'));\n return cache.addAll(preCacheItems);\n });\n })\n );\n});\n\n// Activate\n\nself.addEventListener('activate', function(event) {\n helpers.debug('activate event fired');\n var inactiveCache = options.cache.name + '$$$inactive$$$';\n event.waitUntil(helpers.renameCache(inactiveCache, options.cache.name));\n});\n\n// Fetch\n\nself.addEventListener('fetch', function(event) {\n var handler = router.match(event.request);\n\n if (handler) {\n event.respondWith(handler(event.request));\n } else if (router.default && event.request.method === 'GET') {\n event.respondWith(router.default(event.request));\n }\n});\n\n// Caching\n\nfunction cache(url, options) {\n return helpers.openCache(options).then(function(cache) {\n return cache.add(url);\n });\n}\n\nfunction uncache(url, options) {\n return helpers.openCache(options).then(function(cache) {\n return cache.delete(url);\n });\n}\n\nfunction precache(items) {\n if (!Array.isArray(items)) {\n items = [items];\n }\n options.preCacheItems = options.preCacheItems.concat(items);\n}\n\nmodule.exports = {\n networkOnly: strategies.networkOnly,\n networkFirst: strategies.networkFirst,\n cacheOnly: strategies.cacheOnly,\n cacheFirst: strategies.cacheFirst,\n fastest: strategies.fastest,\n router: router,\n options: options,\n cache: cache,\n uncache: uncache,\n precache: precache\n};\n","/*\n Copyright 2014 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\n\nvar Route = require('./route');\n\nfunction regexEscape(s) {\n return s.replace(/[-\\/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&');\n}\n\nvar keyMatch = function(map, string) {\n // This would be better written as a for..of loop, but that would break the\n // minifyify process in the build.\n var entriesIterator = map.entries();\n var item = entriesIterator.next();\n while (!item.done) {\n var pattern = new RegExp(item.value[0]);\n if (pattern.test(string)) {\n return item.value[1];\n }\n item = entriesIterator.next();\n }\n return null;\n};\n\nvar Router = function() {\n this.routes = new Map();\n this.default = null;\n};\n\n['get', 'post', 'put', 'delete', 'head', 'any'].forEach(function(method) {\n Router.prototype[method] = function(path, handler, options) {\n return this.add(method, path, handler, options);\n };\n});\n\nRouter.prototype.add = function(method, path, handler, options) {\n options = options || {};\n var origin;\n\n if (path instanceof RegExp) {\n // We need a unique key to use in the Map to distinguish RegExp paths\n // from Express-style paths + origins. Since we can use any object as the\n // key in a Map, let's use the RegExp constructor!\n origin = RegExp;\n } else {\n origin = options.origin || self.location.origin;\n if (origin instanceof RegExp) {\n origin = origin.source;\n } else {\n origin = regexEscape(origin);\n }\n }\n\n method = method.toLowerCase();\n\n var route = new Route(method, path, handler, options);\n\n if (!this.routes.has(origin)) {\n this.routes.set(origin, new Map());\n }\n\n var methodMap = this.routes.get(origin);\n if (!methodMap.has(method)) {\n methodMap.set(method, new Map());\n }\n\n var routeMap = methodMap.get(method);\n var regExp = route.regexp || route.fullUrlRegExp;\n routeMap.set(regExp.source, route);\n};\n\nRouter.prototype.matchMethod = function(method, url) {\n var urlObject = new URL(url);\n var origin = urlObject.origin;\n var path = urlObject.pathname;\n\n // We want to first check to see if there's a match against any\n // \"Express-style\" routes (string for the path, RegExp for the origin).\n // Checking for Express-style matches first maintains the legacy behavior.\n // If there's no match, we next check for a match against any RegExp routes,\n // where the RegExp in question matches the full URL (both origin and path).\n return this._match(method, keyMatch(this.routes, origin), path) ||\n this._match(method, this.routes.get(RegExp), url);\n};\n\nRouter.prototype._match = function(method, methodMap, pathOrUrl) {\n if (methodMap) {\n var routeMap = methodMap.get(method.toLowerCase());\n if (routeMap) {\n var route = keyMatch(routeMap, pathOrUrl);\n if (route) {\n return route.makeHandler(pathOrUrl);\n }\n }\n }\n\n return null;\n};\n\nRouter.prototype.match = function(request) {\n return this.matchMethod(request.method, request.url) ||\n this.matchMethod('any', request.url);\n};\n\nmodule.exports = new Router();\n","/*\n\tCopyright 2015 Google Inc. All Rights Reserved.\n\n\tLicensed under the Apache License, Version 2.0 (the \"License\");\n\tyou may not use this file except in compliance with the License.\n\tYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n\tUnless required by applicable law or agreed to in writing, software\n\tdistributed under the License is distributed on an \"AS IS\" BASIS,\n\tWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\tSee the License for the specific language governing permissions and\n\tlimitations under the License.\n*/\n'use strict';\n\n// TODO: This is necessary to handle different implementations in the wild\n// The spec defines self.registration, but it was not implemented in Chrome 40.\nvar scope;\nif (self.registration) {\n scope = self.registration.scope;\n} else {\n scope = self.scope || new URL('./', self.location).href;\n}\n\nmodule.exports = {\n cache: {\n name: '$$$toolbox-cache$$$' + scope + '$$$',\n maxAgeSeconds: null,\n maxEntries: null\n },\n debug: false,\n networkTimeoutSeconds: null,\n preCacheItems: [],\n // A regular expression to apply to HTTP response codes. Codes that match\n // will be considered successes, while others will not, and will not be\n // cached.\n successResponses: /^0|([123]\\d\\d)|(40[14567])|410$/\n};\n","/*\n Copyright 2014 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\n\nvar globalOptions = require('./options');\nvar idbCacheExpiration = require('./idb-cache-expiration');\n\nfunction debug(message, options) {\n options = options || {};\n var flag = options.debug || globalOptions.debug;\n if (flag) {\n console.log('[sw-toolbox] ' + message);\n }\n}\n\nfunction openCache(options) {\n var cacheName;\n if (options && options.cache) {\n cacheName = options.cache.name;\n }\n cacheName = cacheName || globalOptions.cache.name;\n\n return caches.open(cacheName);\n}\n\nfunction fetchAndCache(request, options) {\n options = options || {};\n var successResponses = options.successResponses ||\n globalOptions.successResponses;\n\n return fetch(request.clone()).then(function(response) {\n // Only cache GET requests with successful responses.\n // Since this is not part of the promise chain, it will be done\n // asynchronously and will not block the response from being returned to the\n // page.\n if (request.method === 'GET' && successResponses.test(response.status)) {\n openCache(options).then(function(cache) {\n cache.put(request, response).then(function() {\n // If any of the options are provided in options.cache then use them.\n // Do not fallback to the global options for any that are missing\n // unless they are all missing.\n var cacheOptions = options.cache || globalOptions.cache;\n\n // Only run the cache expiration logic if at least one of the maximums\n // is set, and if we have a name for the cache that the options are\n // being applied to.\n if ((cacheOptions.maxEntries || cacheOptions.maxAgeSeconds) &&\n cacheOptions.name) {\n queueCacheExpiration(request, cache, cacheOptions);\n }\n });\n });\n }\n\n return response.clone();\n });\n}\n\nvar cleanupQueue;\nfunction queueCacheExpiration(request, cache, cacheOptions) {\n var cleanup = cleanupCache.bind(null, request, cache, cacheOptions);\n\n if (cleanupQueue) {\n cleanupQueue = cleanupQueue.then(cleanup);\n } else {\n cleanupQueue = cleanup();\n }\n}\n\nfunction cleanupCache(request, cache, cacheOptions) {\n var requestUrl = request.url;\n var maxAgeSeconds = cacheOptions.maxAgeSeconds;\n var maxEntries = cacheOptions.maxEntries;\n var cacheName = cacheOptions.name;\n\n var now = Date.now();\n debug('Updating LRU order for ' + requestUrl + '. Max entries is ' +\n maxEntries + ', max age is ' + maxAgeSeconds);\n\n return idbCacheExpiration.getDb(cacheName).then(function(db) {\n return idbCacheExpiration.setTimestampForUrl(db, requestUrl, now);\n }).then(function(db) {\n return idbCacheExpiration.expireEntries(db, maxEntries, maxAgeSeconds, now);\n }).then(function(urlsToDelete) {\n debug('Successfully updated IDB.');\n\n var deletionPromises = urlsToDelete.map(function(urlToDelete) {\n return cache.delete(urlToDelete);\n });\n\n return Promise.all(deletionPromises).then(function() {\n debug('Done with cache cleanup.');\n });\n }).catch(function(error) {\n debug(error);\n });\n}\n\nfunction renameCache(source, destination, options) {\n debug('Renaming cache: [' + source + '] to [' + destination + ']', options);\n return caches.delete(destination).then(function() {\n return Promise.all([\n caches.open(source),\n caches.open(destination)\n ]).then(function(results) {\n var sourceCache = results[0];\n var destCache = results[1];\n\n return sourceCache.keys().then(function(requests) {\n return Promise.all(requests.map(function(request) {\n return sourceCache.match(request).then(function(response) {\n return destCache.put(request, response);\n });\n }));\n }).then(function() {\n return caches.delete(source);\n });\n });\n });\n}\n\nmodule.exports = {\n debug: debug,\n fetchAndCache: fetchAndCache,\n openCache: openCache,\n renameCache: renameCache\n};\n","/*\n\tCopyright 2014 Google Inc. All Rights Reserved.\n\n\tLicensed under the Apache License, Version 2.0 (the \"License\");\n\tyou may not use this file except in compliance with the License.\n\tYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n\tUnless required by applicable law or agreed to in writing, software\n\tdistributed under the License is distributed on an \"AS IS\" BASIS,\n\tWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\tSee the License for the specific language governing permissions and\n\tlimitations under the License.\n*/\nmodule.exports = {\n networkOnly: require('./networkOnly'),\n networkFirst: require('./networkFirst'),\n cacheOnly: require('./cacheOnly'),\n cacheFirst: require('./cacheFirst'),\n fastest: require('./fastest')\n};\n","/*\n Copyright 2014 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\n\n// TODO: Use self.registration.scope instead of self.location\nvar url = new URL('./', self.location);\nvar basePath = url.pathname;\nvar pathRegexp = require('path-to-regexp');\n\nvar Route = function(method, path, handler, options) {\n if (path instanceof RegExp) {\n this.fullUrlRegExp = path;\n } else {\n // The URL() constructor can't parse express-style routes as they are not\n // valid urls. This means we have to manually manipulate relative urls into\n // absolute ones. This check is extremely naive but implementing a tweaked\n // version of the full algorithm seems like overkill\n // (https://url.spec.whatwg.org/#concept-basic-url-parser)\n if (path.indexOf('/') !== 0) {\n path = basePath + path;\n }\n\n this.keys = [];\n this.regexp = pathRegexp(path, this.keys);\n }\n\n this.method = method;\n this.options = options;\n this.handler = handler;\n};\n\nRoute.prototype.makeHandler = function(url) {\n if (this.regexp) {\n var match = this.regexp.exec(url);\n var values = {};\n this.keys.forEach(function(key, index) {\n values[key.name] = match[index + 1];\n });\n }\n\n return function(request) {\n return this.handler(request, values, this.options);\n }.bind(this);\n};\n\nmodule.exports = Route;\n","/*\n Copyright 2015 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\n\nvar DB_PREFIX = 'sw-toolbox-';\nvar DB_VERSION = 1;\nvar STORE_NAME = 'store';\nvar URL_PROPERTY = 'url';\nvar TIMESTAMP_PROPERTY = 'timestamp';\nvar cacheNameToDbPromise = {};\n\nfunction openDb(cacheName) {\n return new Promise(function(resolve, reject) {\n var request = indexedDB.open(DB_PREFIX + cacheName, DB_VERSION);\n\n request.onupgradeneeded = function() {\n var objectStore = request.result.createObjectStore(STORE_NAME,\n {keyPath: URL_PROPERTY});\n objectStore.createIndex(TIMESTAMP_PROPERTY, TIMESTAMP_PROPERTY,\n {unique: false});\n };\n\n request.onsuccess = function() {\n resolve(request.result);\n };\n\n request.onerror = function() {\n reject(request.error);\n };\n });\n}\n\nfunction getDb(cacheName) {\n if (!(cacheName in cacheNameToDbPromise)) {\n cacheNameToDbPromise[cacheName] = openDb(cacheName);\n }\n\n return cacheNameToDbPromise[cacheName];\n}\n\nfunction setTimestampForUrl(db, url, now) {\n return new Promise(function(resolve, reject) {\n var transaction = db.transaction(STORE_NAME, 'readwrite');\n var objectStore = transaction.objectStore(STORE_NAME);\n objectStore.put({url: url, timestamp: now});\n\n transaction.oncomplete = function() {\n resolve(db);\n };\n\n transaction.onabort = function() {\n reject(transaction.error);\n };\n });\n}\n\nfunction expireOldEntries(db, maxAgeSeconds, now) {\n // Bail out early by resolving with an empty array if we're not using\n // maxAgeSeconds.\n if (!maxAgeSeconds) {\n return Promise.resolve([]);\n }\n\n return new Promise(function(resolve, reject) {\n var maxAgeMillis = maxAgeSeconds * 1000;\n var urls = [];\n\n var transaction = db.transaction(STORE_NAME, 'readwrite');\n var objectStore = transaction.objectStore(STORE_NAME);\n var index = objectStore.index(TIMESTAMP_PROPERTY);\n\n index.openCursor().onsuccess = function(cursorEvent) {\n var cursor = cursorEvent.target.result;\n if (cursor) {\n if (now - maxAgeMillis > cursor.value[TIMESTAMP_PROPERTY]) {\n var url = cursor.value[URL_PROPERTY];\n urls.push(url);\n objectStore.delete(url);\n cursor.continue();\n }\n }\n };\n\n transaction.oncomplete = function() {\n resolve(urls);\n };\n\n transaction.onabort = reject;\n });\n}\n\nfunction expireExtraEntries(db, maxEntries) {\n // Bail out early by resolving with an empty array if we're not using\n // maxEntries.\n if (!maxEntries) {\n return Promise.resolve([]);\n }\n\n return new Promise(function(resolve, reject) {\n var urls = [];\n\n var transaction = db.transaction(STORE_NAME, 'readwrite');\n var objectStore = transaction.objectStore(STORE_NAME);\n var index = objectStore.index(TIMESTAMP_PROPERTY);\n\n var countRequest = index.count();\n index.count().onsuccess = function() {\n var initialCount = countRequest.result;\n\n if (initialCount > maxEntries) {\n index.openCursor().onsuccess = function(cursorEvent) {\n var cursor = cursorEvent.target.result;\n if (cursor) {\n var url = cursor.value[URL_PROPERTY];\n urls.push(url);\n objectStore.delete(url);\n if (initialCount - urls.length > maxEntries) {\n cursor.continue();\n }\n }\n };\n }\n };\n\n transaction.oncomplete = function() {\n resolve(urls);\n };\n\n transaction.onabort = reject;\n });\n}\n\nfunction expireEntries(db, maxEntries, maxAgeSeconds, now) {\n return expireOldEntries(db, maxAgeSeconds, now).then(function(oldUrls) {\n return expireExtraEntries(db, maxEntries).then(function(extraUrls) {\n return oldUrls.concat(extraUrls);\n });\n });\n}\n\nmodule.exports = {\n getDb: getDb,\n setTimestampForUrl: setTimestampForUrl,\n expireEntries: expireEntries\n};\n","/**\n * Copyright 2015 Google Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n *\n */\n\nif (!Cache.prototype.addAll) {\n Cache.prototype.addAll = function addAll(requests) {\n var cache = this;\n\n // Since DOMExceptions are not constructable:\n function NetworkError(message) {\n this.name = 'NetworkError';\n this.code = 19;\n this.message = message;\n }\n NetworkError.prototype = Object.create(Error.prototype);\n\n return Promise.resolve().then(function() {\n if (arguments.length < 1) throw new TypeError();\n\n // Simulate sequence<(Request or USVString)> binding:\n var sequence = [];\n\n requests = requests.map(function(request) {\n if (request instanceof Request) {\n return request;\n }\n else {\n return String(request); // may throw TypeError\n }\n });\n\n return Promise.all(\n requests.map(function(request) {\n if (typeof request === 'string') {\n request = new Request(request);\n }\n\n var scheme = new URL(request.url).protocol;\n\n if (scheme !== 'http:' && scheme !== 'https:') {\n throw new NetworkError(\"Invalid scheme\");\n }\n\n return fetch(request.clone());\n })\n );\n }).then(function(responses) {\n // TODO: check that requests don't overwrite one another\n // (don't think this is possible to polyfill due to opaque responses)\n return Promise.all(\n responses.map(function(response, i) {\n return cache.put(requests[i], response);\n })\n );\n }).then(function() {\n return undefined;\n });\n };\n}\n","/*\n\tCopyright 2014 Google Inc. All Rights Reserved.\n\n\tLicensed under the Apache License, Version 2.0 (the \"License\");\n\tyou may not use this file except in compliance with the License.\n\tYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n\tUnless required by applicable law or agreed to in writing, software\n\tdistributed under the License is distributed on an \"AS IS\" BASIS,\n\tWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\tSee the License for the specific language governing permissions and\n\tlimitations under the License.\n*/\n'use strict';\nvar helpers = require('../helpers');\n\nfunction networkOnly(request, values, options) {\n helpers.debug('Strategy: network only [' + request.url + ']', options);\n return fetch(request);\n}\n\nmodule.exports = networkOnly;\n","/*\n Copyright 2015 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\nvar globalOptions = require('../options');\nvar helpers = require('../helpers');\n\nfunction networkFirst(request, values, options) {\n options = options || {};\n var successResponses = options.successResponses ||\n globalOptions.successResponses;\n // This will bypass options.networkTimeout if it's set to a false-y value like\n // 0, but that's the sane thing to do anyway.\n var networkTimeoutSeconds = options.networkTimeoutSeconds ||\n globalOptions.networkTimeoutSeconds;\n helpers.debug('Strategy: network first [' + request.url + ']', options);\n\n return helpers.openCache(options).then(function(cache) {\n var timeoutId;\n var promises = [];\n var originalResponse;\n\n if (networkTimeoutSeconds) {\n var cacheWhenTimedOutPromise = new Promise(function(resolve) {\n timeoutId = setTimeout(function() {\n cache.match(request).then(function(response) {\n if (response) {\n // Only resolve this promise if there's a valid response in the\n // cache. This ensures that we won't time out a network request\n // unless there's a cached entry to fallback on, which is arguably\n // the preferable behavior.\n resolve(response);\n }\n });\n }, networkTimeoutSeconds * 1000);\n });\n promises.push(cacheWhenTimedOutPromise);\n }\n\n var networkPromise = helpers.fetchAndCache(request, options)\n .then(function(response) {\n // We've got a response, so clear the network timeout if there is one.\n if (timeoutId) {\n clearTimeout(timeoutId);\n }\n\n if (successResponses.test(response.status)) {\n return response;\n }\n\n helpers.debug('Response was an HTTP error: ' + response.statusText,\n options);\n originalResponse = response;\n throw new Error('Bad response');\n }).catch(function() {\n helpers.debug('Network or response error, fallback to cache [' +\n request.url + ']', options);\n return cache.match(request).then(function(response) {\n return response || originalResponse;\n });\n });\n\n promises.push(networkPromise);\n\n return Promise.race(promises);\n });\n}\n\nmodule.exports = networkFirst;\n","/*\n\tCopyright 2014 Google Inc. All Rights Reserved.\n\n\tLicensed under the Apache License, Version 2.0 (the \"License\");\n\tyou may not use this file except in compliance with the License.\n\tYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n\tUnless required by applicable law or agreed to in writing, software\n\tdistributed under the License is distributed on an \"AS IS\" BASIS,\n\tWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\tSee the License for the specific language governing permissions and\n\tlimitations under the License.\n*/\n'use strict';\nvar helpers = require('../helpers');\n\nfunction cacheOnly(request, values, options) {\n helpers.debug('Strategy: cache only [' + request.url + ']', options);\n return helpers.openCache(options).then(function(cache) {\n return cache.match(request);\n });\n}\n\nmodule.exports = cacheOnly;\n","/*\n\tCopyright 2014 Google Inc. All Rights Reserved.\n\n\tLicensed under the Apache License, Version 2.0 (the \"License\");\n\tyou may not use this file except in compliance with the License.\n\tYou may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n\tUnless required by applicable law or agreed to in writing, software\n\tdistributed under the License is distributed on an \"AS IS\" BASIS,\n\tWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\tSee the License for the specific language governing permissions and\n\tlimitations under the License.\n*/\n'use strict';\nvar helpers = require('../helpers');\n\nfunction cacheFirst(request, values, options) {\n helpers.debug('Strategy: cache first [' + request.url + ']', options);\n return helpers.openCache(options).then(function(cache) {\n return cache.match(request).then(function(response) {\n if (response) {\n return response;\n }\n\n return helpers.fetchAndCache(request, options);\n });\n });\n}\n\nmodule.exports = cacheFirst;\n","/*\n Copyright 2014 Google Inc. All Rights Reserved.\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n*/\n'use strict';\nvar helpers = require('../helpers');\nvar cacheOnly = require('./cacheOnly');\n\nfunction fastest(request, values, options) {\n helpers.debug('Strategy: fastest [' + request.url + ']', options);\n\n return new Promise(function(resolve, reject) {\n var rejected = false;\n var reasons = [];\n\n var maybeReject = function(reason) {\n reasons.push(reason.toString());\n if (rejected) {\n reject(new Error('Both cache and network failed: \"' +\n reasons.join('\", \"') + '\"'));\n } else {\n rejected = true;\n }\n };\n\n var maybeResolve = function(result) {\n if (result instanceof Response) {\n resolve(result);\n } else {\n maybeReject('No result returned');\n }\n };\n\n helpers.fetchAndCache(request.clone(), options)\n .then(maybeResolve, maybeReject);\n\n cacheOnly(request, values, options)\n .then(maybeResolve, maybeReject);\n });\n}\n\nmodule.exports = fastest;\n","var isarray = require('isarray')\n\n/**\n * Expose `pathToRegexp`.\n */\nmodule.exports = pathToRegexp\nmodule.exports.parse = parse\nmodule.exports.compile = compile\nmodule.exports.tokensToFunction = tokensToFunction\nmodule.exports.tokensToRegExp = tokensToRegExp\n\n/**\n * The main path matching regexp utility.\n *\n * @type {RegExp}\n */\nvar PATH_REGEXP = new RegExp([\n // Match escaped characters that would otherwise appear in future matches.\n // This allows the user to escape special characters that won't transform.\n '(\\\\\\\\.)',\n // Match Express-style parameters and un-named parameters with a prefix\n // and optional suffixes. Matches appear as:\n //\n // \"/:test(\\\\d+)?\" => [\"/\", \"test\", \"\\d+\", undefined, \"?\", undefined]\n // \"/route(\\\\d+)\" => [undefined, undefined, undefined, \"\\d+\", undefined, undefined]\n // \"/*\" => [\"/\", undefined, undefined, undefined, undefined, \"*\"]\n '([\\\\/.])?(?:(?:\\\\:(\\\\w+)(?:\\\\(((?:\\\\\\\\.|[^()])+)\\\\))?|\\\\(((?:\\\\\\\\.|[^()])+)\\\\))([+*?])?|(\\\\*))'\n].join('|'), 'g')\n\n/**\n * Parse a string for the raw tokens.\n *\n * @param {String} str\n * @return {Array}\n */\nfunction parse (str) {\n var tokens = []\n var key = 0\n var index = 0\n var path = ''\n var res\n\n while ((res = PATH_REGEXP.exec(str)) != null) {\n var m = res[0]\n var escaped = res[1]\n var offset = res.index\n path += str.slice(index, offset)\n index = offset + m.length\n\n // Ignore already escaped sequences.\n if (escaped) {\n path += escaped[1]\n continue\n }\n\n // Push the current path onto the tokens.\n if (path) {\n tokens.push(path)\n path = ''\n }\n\n var prefix = res[2]\n var name = res[3]\n var capture = res[4]\n var group = res[5]\n var suffix = res[6]\n var asterisk = res[7]\n\n var repeat = suffix === '+' || suffix === '*'\n var optional = suffix === '?' || suffix === '*'\n var delimiter = prefix || '/'\n var pattern = capture || group || (asterisk ? '.*' : '[^' + delimiter + ']+?')\n\n tokens.push({\n name: name || key++,\n prefix: prefix || '',\n delimiter: delimiter,\n optional: optional,\n repeat: repeat,\n pattern: escapeGroup(pattern)\n })\n }\n\n // Match any characters still remaining.\n if (index < str.length) {\n path += str.substr(index)\n }\n\n // If the path exists, push it onto the end.\n if (path) {\n tokens.push(path)\n }\n\n return tokens\n}\n\n/**\n * Compile a string to a template function for the path.\n *\n * @param {String} str\n * @return {Function}\n */\nfunction compile (str) {\n return tokensToFunction(parse(str))\n}\n\n/**\n * Expose a method for transforming tokens into the path function.\n */\nfunction tokensToFunction (tokens) {\n // Compile all the tokens into regexps.\n var matches = new Array(tokens.length)\n\n // Compile all the patterns before compilation.\n for (var i = 0; i < tokens.length; i++) {\n if (typeof tokens[i] === 'object') {\n matches[i] = new RegExp('^' + tokens[i].pattern + '$')\n }\n }\n\n return function (obj) {\n var path = ''\n var data = obj || {}\n\n for (var i = 0; i < tokens.length; i++) {\n var token = tokens[i]\n\n if (typeof token === 'string') {\n path += token\n\n continue\n }\n\n var value = data[token.name]\n var segment\n\n if (value == null) {\n if (token.optional) {\n continue\n } else {\n throw new TypeError('Expected \"' + token.name + '\" to be defined')\n }\n }\n\n if (isarray(value)) {\n if (!token.repeat) {\n throw new TypeError('Expected \"' + token.name + '\" to not repeat, but received \"' + value + '\"')\n }\n\n if (value.length === 0) {\n if (token.optional) {\n continue\n } else {\n throw new TypeError('Expected \"' + token.name + '\" to not be empty')\n }\n }\n\n for (var j = 0; j < value.length; j++) {\n segment = encodeURIComponent(value[j])\n\n if (!matches[i].test(segment)) {\n throw new TypeError('Expected all \"' + token.name + '\" to match \"' + token.pattern + '\", but received \"' + segment + '\"')\n }\n\n path += (j === 0 ? token.prefix : token.delimiter) + segment\n }\n\n continue\n }\n\n segment = encodeURIComponent(value)\n\n if (!matches[i].test(segment)) {\n throw new TypeError('Expected \"' + token.name + '\" to match \"' + token.pattern + '\", but received \"' + segment + '\"')\n }\n\n path += token.prefix + segment\n }\n\n return path\n }\n}\n\n/**\n * Escape a regular expression string.\n *\n * @param {String} str\n * @return {String}\n */\nfunction escapeString (str) {\n return str.replace(/([.+*?=^!:${}()[\\]|\\/])/g, '\\\\$1')\n}\n\n/**\n * Escape the capturing group by escaping special characters and meaning.\n *\n * @param {String} group\n * @return {String}\n */\nfunction escapeGroup (group) {\n return group.replace(/([=!:$\\/()])/g, '\\\\$1')\n}\n\n/**\n * Attach the keys as a property of the regexp.\n *\n * @param {RegExp} re\n * @param {Array} keys\n * @return {RegExp}\n */\nfunction attachKeys (re, keys) {\n re.keys = keys\n return re\n}\n\n/**\n * Get the flags for a regexp from the options.\n *\n * @param {Object} options\n * @return {String}\n */\nfunction flags (options) {\n return options.sensitive ? '' : 'i'\n}\n\n/**\n * Pull out keys from a regexp.\n *\n * @param {RegExp} path\n * @param {Array} keys\n * @return {RegExp}\n */\nfunction regexpToRegexp (path, keys) {\n // Use a negative lookahead to match only capturing groups.\n var groups = path.source.match(/\\((?!\\?)/g)\n\n if (groups) {\n for (var i = 0; i < groups.length; i++) {\n keys.push({\n name: i,\n prefix: null,\n delimiter: null,\n optional: false,\n repeat: false,\n pattern: null\n })\n }\n }\n\n return attachKeys(path, keys)\n}\n\n/**\n * Transform an array into a regexp.\n *\n * @param {Array} path\n * @param {Array} keys\n * @param {Object} options\n * @return {RegExp}\n */\nfunction arrayToRegexp (path, keys, options) {\n var parts = []\n\n for (var i = 0; i < path.length; i++) {\n parts.push(pathToRegexp(path[i], keys, options).source)\n }\n\n var regexp = new RegExp('(?:' + parts.join('|') + ')', flags(options))\n\n return attachKeys(regexp, keys)\n}\n\n/**\n * Create a path regexp from string input.\n *\n * @param {String} path\n * @param {Array} keys\n * @param {Object} options\n * @return {RegExp}\n */\nfunction stringToRegexp (path, keys, options) {\n var tokens = parse(path)\n var re = tokensToRegExp(tokens, options)\n\n // Attach keys back to the regexp.\n for (var i = 0; i < tokens.length; i++) {\n if (typeof tokens[i] !== 'string') {\n keys.push(tokens[i])\n }\n }\n\n return attachKeys(re, keys)\n}\n\n/**\n * Expose a function for taking tokens and returning a RegExp.\n *\n * @param {Array} tokens\n * @param {Array} keys\n * @param {Object} options\n * @return {RegExp}\n */\nfunction tokensToRegExp (tokens, options) {\n options = options || {}\n\n var strict = options.strict\n var end = options.end !== false\n var route = ''\n var lastToken = tokens[tokens.length - 1]\n var endsWithSlash = typeof lastToken === 'string' && /\\/$/.test(lastToken)\n\n // Iterate over the tokens and create our regexp string.\n for (var i = 0; i < tokens.length; i++) {\n var token = tokens[i]\n\n if (typeof token === 'string') {\n route += escapeString(token)\n } else {\n var prefix = escapeString(token.prefix)\n var capture = token.pattern\n\n if (token.repeat) {\n capture += '(?:' + prefix + capture + ')*'\n }\n\n if (token.optional) {\n if (prefix) {\n capture = '(?:' + prefix + '(' + capture + '))?'\n } else {\n capture = '(' + capture + ')?'\n }\n } else {\n capture = prefix + '(' + capture + ')'\n }\n\n route += capture\n }\n }\n\n // In non-strict mode we allow a slash at the end of match. If the path to\n // match already ends with a slash, we remove it for consistency. The slash\n // is valid at the end of a path match, not in the middle. This is important\n // in non-ending mode, where \"/test/\" shouldn't match \"/test//route\".\n if (!strict) {\n route = (endsWithSlash ? route.slice(0, -2) : route) + '(?:\\\\/(?=$))?'\n }\n\n if (end) {\n route += '$'\n } else {\n // In non-ending mode, we need the capturing groups to match as much as\n // possible by using a positive lookahead to the end or next path segment.\n route += strict && endsWithSlash ? '' : '(?=\\\\/|$)'\n }\n\n return new RegExp('^' + route, flags(options))\n}\n\n/**\n * Normalize the given path string, returning a regular expression.\n *\n * An empty array can be passed in for the keys, which will hold the\n * placeholder key descriptions. For example, using `/user/:id`, `keys` will\n * contain `[{ name: 'id', delimiter: '/', optional: false, repeat: false }]`.\n *\n * @param {(String|RegExp|Array)} path\n * @param {Array} [keys]\n * @param {Object} [options]\n * @return {RegExp}\n */\nfunction pathToRegexp (path, keys, options) {\n keys = keys || []\n\n if (!isarray(keys)) {\n options = keys\n keys = []\n } else if (!options) {\n options = {}\n }\n\n if (path instanceof RegExp) {\n return regexpToRegexp(path, keys, options)\n }\n\n if (isarray(path)) {\n return arrayToRegexp(path, keys, options)\n }\n\n return stringToRegexp(path, keys, options)\n}\n","module.exports = Array.isArray || function (arr) {\n return Object.prototype.toString.call(arr) == '[object Array]';\n};\n"]} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
162629
48
1550
284
15
8