Workbox microsite + gulp task instead of npm-publish-scripts (#518)

* Workbox microsite + gulp task instead of npm-publish-scripts

* Fixing merge conflict

* Patching up the linting errors in the project
This commit is contained in:
Matt Gaunt
2017-05-09 10:48:15 -07:00
committed by GitHub
parent 592fe97528
commit 82f504918c
743 changed files with 173870 additions and 26 deletions
+2
View File
@@ -0,0 +1,2 @@
third_party
/_site/
+1 -3
View File
@@ -2,11 +2,9 @@
.DS_Store
build
docs/Gemfile*
docs/_*
docs/reference-docs
docs/themes
lerna-debug.log
node_modules
npm-debug.log
packages/**/LICENSE
temp
/_site/
+5
View File
@@ -0,0 +1,5 @@
source "http://rubygems.org"
gem 'jekyll', '>=3.1.6'
gem 'kramdown'
gem 'jekyll-github-metadata'
+6
View File
@@ -0,0 +1,6 @@
{
"rules": {
"no-console": 0,
"require-jsdoc": 0,
}
}
+42
View File
@@ -0,0 +1,42 @@
- title: Getting Started
url: /
- title: sw-cli
url: /sw-cli
- title: sw-build
url: /sw-build
- title: sw-lib
url: /sw-lib
- title: Recipes
dropdown:
- title: Web Pack
url: /recipes/webpack
- title: Gulp
url: /recipes/gulp
- title: Rollup
url: /recipes/rollup
- title: Grunt
url: /recipes/grunt
- title: Examples
url: /examples
- title: Reference
dropdown:
- title: sw-background-sync-queue
url: /reference-docs/stable/latest/module-sw-background-sync-queue.html
- title: sw-broadcast-cache-update
url: /reference-docs/stable/latest/module-sw-broadcast-cache-update.html
- title: sw-build
url: /reference-docs/stable/latest/module-sw-build.html
- title: sw-cache-expiration
url: /reference-docs/stable/latest/module-sw-cache-expiration.html
- title: sw-cacheable-response
url: /reference-docs/stable/latest/module-sw-cacheable-response.html
- title: sw-lib
url: /reference-docs/stable/latest/module-sw-lib.html
- title: sw-offline-google-analytics
url: /reference-docs/stable/latest/module-sw-offline-google-analytics.html
- title: sw-precaching
url: /reference-docs/stable/latest/module-sw-precaching.html
- title: sw-routing
url: /reference-docs/stable/latest/module-sw-routing.html
- title: sw-runtime-caching
url: /reference-docs/stable/latest/module-sw-runtime-caching.html
+8
View File
@@ -0,0 +1,8 @@
---
layout: page
title: Recipes > Grunt
---
# Grunt
Need to write up.
+8
View File
@@ -0,0 +1,8 @@
---
layout: page
title: Recipes > Gulp
---
# Gulp
Need to write up.
-16
View File
@@ -1,16 +0,0 @@
---
layout: index
title: Recipes
navigation_weight: 4
---
# Recipes
You can think of recipes as short guides explaining how to achieve a
more specific / niche task like, how do can you generate a service work
with gulp?
> Find a list of available recipes in the side nav.
If there is a recipe that you think should exist
[please raise an issue](https://github.com/GoogleChrome/sw-helpers/issues).
+3 -4
View File
@@ -1,10 +1,9 @@
---
layout: index
title: Rollup Bundling
navigation_weight: 1
layout: page
title: Recipes > Rollup Bundling
---
# Rollup-based Bundling
# Rollup Bundling
These recipes cover the following `sw-lib` use case:
- You want to write and maintain your own `sw.js` file that uses precaching via
+8
View File
@@ -0,0 +1,8 @@
---
layout: page
title: Recipes > Webpack
---
# Webpack
Need to write up.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,773 @@
---
layout: jsdoc
title: "Class: SWCli"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>SWCli</h2>
<p>This class is a wrapper to make test easier. This is used by
./bin/index.js to pass in the args when the CLI is used.</p>
<h2 class="subsection-title">Instance Methods</h2>
<div class="subsection-methods">
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="_generateBuildManifest"><span class="this">this.</span>_generateBuildManifest<span class="signature">(<span class="signature-args"></span>)</span><span class="type-signature"> <span class="return-arrow">&rarr;</span> Promise</span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Generates a file manifest with revisioning details
that can be used in your service worker for precaching assets.</p>
</div>
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
<tr>
<th>Returns</th>
<td>
<span class="param-type">Promise</span>
<div class="param-desc">
<p>Resolves when the node process exits.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="_generateSW"><span class="this">this.</span>_generateSW<span class="signature">(<span class="signature-args"></span>)</span><span class="type-signature"> <span class="return-arrow">&rarr;</span> Promise</span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>This method will generate a working service worker with a file manifest.</p>
</div>
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
<tr>
<th>Returns</th>
<td>
<span class="param-type">Promise</span>
<div class="param-desc">
<p>The promise returned here will be used to exit the
node process cleanly or not.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="handleCommand"><span class="this">this.</span>handleCommand<span class="signature">(<span class="signature-args">command, args, flags</span>)</span><span class="type-signature"> <span class="return-arrow">&rarr;</span> Promise</span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>If a command is given in the command line args, this method will handle
the appropriate action.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">command</td>
<td class="type">
<span class="param-type">string</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The command name.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">args</td>
<td class="type">
<span class="param-type">object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The arguments given to this command.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">flags</td>
<td class="type">
<span class="param-type">object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The flags supplied with the command line.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
<tr>
<th>Returns</th>
<td>
<span class="param-type">Promise</span>
<div class="param-desc">
<p>A promise for the provided task.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="handleFlag"><span class="this">this.</span>handleFlag<span class="signature">(<span class="signature-args">flags</span>)</span><span class="type-signature"> <span class="return-arrow">&rarr;</span> Promise</span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>If there is no command given to the CLI then the flags will be passed
to this function in case a relevant action can be taken.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">flags</td>
<td class="type">
<span class="param-type">object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The available flags = require(the command line.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
<tr>
<th>Returns</th>
<td>
<span class="param-type">Promise</span>
<div class="param-desc">
<p>returns a promise once handled.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="printHelpText"><span class="this">this.</span>printHelpText<span class="signature">(<span class="signature-args"></span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Prints the help text to the terminal.</p>
</div>
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
@@ -0,0 +1,26 @@
---
layout: jsdoc
title: "Reference Docs"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h3> </h3>
@@ -0,0 +1,131 @@
---
layout: jsdoc
title: "Module: sw-appcache-behavior"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<div class="description"><h1 id="sw-appcache-behavior">sw-appcache-behavior</h1>
<p>A service worker implementation of the behavior defined in a page's App
Cache manifest.</p>
<p>In your web page you need to add the client-runtime.js file:</p>
<pre class="prettyprint source"><code>&lt;script src=&quot;../build/client-runtime.js&quot;
data-service-worker=&quot;service-worker.js&quot;>&lt;/script></code></pre><p>Then in your servier worker you must import the appcache-behavior-import.js
file:</p>
<pre class="prettyprint source"><code>importScripts('../build/appcache-behavior-import.js');
self.addEventListener('fetch', (event) => {
event.respondWith(goog.appCacheBehavior.fetch(event));
});</code></pre></div>
<div class="collapsing-entry method-type-module ">
<div class="collapse-details js-collapse-details">
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
@@ -0,0 +1,122 @@
---
layout: jsdoc
title: "Module: sw-background-sync-queue"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<div class="description"><h1 id="sw-background-sync-queue">sw-background-sync-queue</h1>
<p>Queues failed requests and uses the Background Sync API to replay those
requests at a later time when the network state has changed.</p></div>
<div class="collapsing-entry method-type-module ">
<div class="collapse-details js-collapse-details">
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
@@ -0,0 +1,583 @@
---
layout: jsdoc
title: "Class: BroadcastCacheUpdate"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>BroadcastCacheUpdate</h2>
<p>Can be used to compare two <a href="https://developer.mozilla.org/en-US/docs/Web/API/Response">Responses</a>
and uses the <a href="https://developers.google.com/web/updates/2016/09/broadcastchannel">Broadcast Channel API</a>
to notify interested parties when those responses differ.</p>
<p>For efficiency's sake, the underlying response bodies are not compared;
only specific response headers are checked.</p>
<p>This class can be used inside any service worker, without having to use any
of the other modules in this repo.</p>
<p>If you'd like to use this functionality but are already using <code>sw-lib</code> or
<code>sw-runtime-caching</code>, then please see the corresponding plugin,
<code>BroadcastCacheUpdatePlugin</code>, for a easy integration.</p>
<h3>Example</h3>
<p class="code-caption">Using BroadcastCacheUpdate when you're handling your
own fetch and request logic.</p>
<pre class="prettyprint"><code>const url = '/path/to/file';
const cacheName = 'my-runtime-cache';
const bcu = new goog.broadcastCacheUpdate.BroadcastCacheUpdate(
{channelName: 'cache-updates'});
Promise.all([
caches.open(cacheName).then((cache) => cache.match(url)),
fetch(url),
]).then(([first, second]) => {
if (first) {
bcu.notifyIfUpdated({cacheName, first, second});
}
});</code></pre>
<h2 class="subsection-title">Constructor</h2>
<div class="subsection-constructor">
<div class="collapsing-entry method-type-class start-open">
<h4 class="js-collapse-title" id="BroadcastCacheUpdate">new BroadcastCacheUpdate<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Dispatches cache update messages when a cached response has been updated.
Messages will be dispatched on a broadcast channel with the name provided
as channelName parameter in the constructor.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">channelName</td>
<td class="type">
<span class="param-type">string</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The name that will be used when creating
the <code>BroadcastChannel</code>.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">headersToCheck</td>
<td class="type">
<span class="param-type">Array.&lt;string></span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>A list of headers that will be
used to determine whether the responses differ. Defaults to
<code>['content-length', 'etag', 'last-modified']</code>.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">source</td>
<td class="type">
<span class="param-type">string</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>An attribution value that indicates where
the update originated. Defaults to 'sw-broadcast-cache-update'.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
<h2 class="subsection-title">Instance Methods</h2>
<div class="subsection-methods">
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="notifyIfUpdated"><span class="this">this.</span>notifyIfUpdated<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>An explicit method to call from your own code to trigger the comparison of
two <a href="https://developer.mozilla.org/en-US/docs/Web/API/Response">Responses</a>
and fire off a notification via the
<a href="https://developers.google.com/web/updates/2016/09/broadcastchannel">Broadcast Channel API</a>
if they differ.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The input object to this function.</p>
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">first</td>
<td class="type">
<span class="param-type">Response</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>One of the responses to compare.
This should not be an <a href="http://stackoverflow.com/questions/39109789">opaque response</a>.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">second</td>
<td class="type">
<span class="param-type">Response</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>Another of the respones to compare.
This should not be an <a href="http://stackoverflow.com/questions/39109789">opaque response</a>.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">cacheName</td>
<td class="type">
<span class="param-type">string</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>Name of the cache the responses belong to.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
@@ -0,0 +1,57 @@
---
layout: jsdoc
title: "Class: BroadcastCacheUpdatePlugin"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>BroadcastCacheUpdatePlugin</h2>
<p>Can be used to compare two <a href="https://developer.mozilla.org/en-US/docs/Web/API/Response">Responses</a>
and uses the <a href="https://developers.google.com/web/updates/2016/09/broadcastchannel">Broadcast Channel API</a>
to notify interested parties when those responses differ.</p>
<p>For efficiency's sake, the underlying response bodies are not compared;
only specific response headers are checked.</p>
<p>This class is meant to be automatically invoked as a plugin to a
<a href="module-sw-runtime-caching.RequestWrapper.html#main">RequestWrapper</a>, which is
used by the <code>sw-lib</code> and <code>sw-runtime-caching</code> modules.</p>
<p>If you would like to use this functionality outside of the <code>RequestWrapper</code>
context, please use the <code>BroadcastCacheUpdate</code> class directly.</p>
<h3>Example</h3>
<p class="code-caption">Added as a "plugin" to a `RequestWrapper` to
automatically dispatch messages on a cache update</p>
<pre class="prettyprint"><code>const requestWrapper = new goog.runtimeCaching.RequestWrapper({
cacheName: 'runtime-cache',
plugins: [
new goog.broadcastCacheUpdate.Plugin({channelName: 'cache-updates'})
]
});
const route = new goog.routing.RegExpRoute({
match: ({url}) => url.domain === 'example.com',
handler: new goog.runtimeCaching.StaleWhileRevalidate({requestWrapper})
});</code></pre>
@@ -0,0 +1,846 @@
---
layout: jsdoc
title: "Module: sw-broadcast-cache-update"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<div class="description"><h1 id="sw-broadcast-cache-update">sw-broadcast-cache-update</h1>
<p>A helper library that uses the Broadcast Channel API to announce when
two Response objects differ.</p>
<p>The main use of this module will be instantiating a new
<code>BroadcastCacheUpdatePlugin</code> and passing it to a
<a href="module-sw-runtime-caching.RequestWrapper.html#main">RequestWrapper</a>,
as shown in the first example below.</p>
<p>You can listen for updates from your web app by adding an event listener on
a <code>BroadcastChannel</code> within a page, using the same channel name as
what's used within the service worker, as shown in the second example below.</p></div>
<div class="collapsing-entry method-type-module ">
<div class="collapse-details js-collapse-details">
<h5>Examples</h5>
<p class="code-caption">Using the BroadcastCacheUpdatePlugin class in a
service worker.</p>
<pre class="prettyprint"><code>const requestWrapper = new goog.runtimeCaching.RequestWrapper({
cacheName: 'text-files',
plugins: [
new goog.broadcastCacheUpdate.BroadcastCacheUpdatePlugin(
{channelName: 'cache-updates'})
],
});
const route = new goog.routing.RegExpRoute({
regExp: /\.txt$/,
handler: new goog.runtimeCaching.StaleWhileRevalidate({requestWrapper}),
});
const router = new goog.routing.Router();
router.registerRoute({route});</code></pre>
<p class="code-caption">Listening for the broadcast message in the
window.</p>
<pre class="prettyprint"><code>const updateChannel = new BroadcastChannel('cache-updates');
updateChannel.addEventListener('message', event => {
console.log(`Cache updated: ${event.data.payload.updatedUrl}`);
});</code></pre>
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
<h2 class="subsection-title">Classes</h2>
<div class="subsection-classes">
<dl>
<dt><a href="module-sw-broadcast-cache-update.BroadcastCacheUpdate.html#main">BroadcastCacheUpdate</a></dt>
<dd></dd>
<dt><a href="module-sw-broadcast-cache-update.BroadcastCacheUpdatePlugin.html#main">BroadcastCacheUpdatePlugin</a></dt>
<dd></dd>
</dl>
</div>
<h2 class="subsection-title">Member Variables</h2>
<div class="subsection-members">
<div class="collapsing-entry member-type-member">
<h4 class="js-collapse-title" id=".cacheUpdatedMessageType"><span class="type-attribs">(static, constant) </span>cacheUpdatedMessageType <span class="return-arrow">&rarr;</span> <span class="type-signature">string</span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>The value <code>'CACHE_UPDATED'</code>, used as the <code>type</code> field of the update message.</p>
</div>
<h5>Example</h5>
<pre class="prettyprint"><code>// Prints 'CACHE_UPDATED'
console.log(goog.broadcastCacheUpdate.cacheUpdatedMessageType);</code></pre>
<dl class="details">
</dl>
</div>
</div>
</div>
<h2 class="subsection-title">Instance Methods</h2>
<div class="subsection-methods">
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id=".broadcastUpdate"><span class="type-attribs">(static) </span><span class="this">this.</span>broadcastUpdate<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>You would not normally call this method directly; it's called automatically
by an instance of the BroadcastCacheUpdate class. It's exposed here
for the benefit of developers who would rather not use the full
<code>BroadcastCacheUpdate</code> implementation.</p>
<p>Calling this will dispatch a message on the provided <a href="https://developers.google.com/web/updates/2016/09/broadcastchannel">Broadcast Channel</a>
to notify interested subscribers about a change to a cached resource.</p>
<p>The message that's posted has a formation inspired by the
<a href="https://github.com/acdlite/flux-standard-action#introduction">Flux standard action</a>
format like so:</p>
<pre class="prettyprint source"><code>{
type: 'CACHE_UPDATED',
meta: 'sw-broadcast-cache-update',
payload: {
cacheName: 'the-cache-name',
updatedUrl: 'https://example.com/'
}
}</code></pre><p>(Usage of <a href="https://facebook.github.io/flux/">Flux</a> itself is not at
all required.)</p>
</div>
<h5>Example</h5>
<pre class="prettyprint"><code>goog.broadcastCacheUpdate.broadcastUpdate({
channel: new BroadcastChannel('Channel Name'),
cacheName: 'example-cache-name',
url: '/',
source: 'custom-library'
});</code></pre>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">channel</td>
<td class="type">
<span class="param-type">BroadcastChannel</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The <code>BroadcastChannel</code> to use.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">cacheName</td>
<td class="type">
<span class="param-type">string</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The name of the cache in which the updated
<code>Response</code> was stored.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">url</td>
<td class="type">
<span class="param-type">string</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The URL associated with the updated <code>Response</code>.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">source</td>
<td class="type">
<span class="param-type">string</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>A string identifying this library as the source
of the update message.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id=".responsesAreSame"><span class="type-attribs">(static) </span><span class="this">this.</span>responsesAreSame<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"> <span class="return-arrow">&rarr;</span> boolean</span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Given two <code>Response</code>s, compares several header values to see if they are
the same or not.</p>
</div>
<h5>Example</h5>
<pre class="prettyprint"><code>const responseIsSame = responsesAreSame({
first: firstResponse,
second: secondResponse,
headersToCheck: [
'content-length',
'etag',
'last-modified',
]
});</code></pre>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">first</td>
<td class="type">
<span class="param-type">Response</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>One of the <code>Response</code>s.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">second</td>
<td class="type">
<span class="param-type">Response</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>Another of the <code>Response</code>s.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">headersToCheck</td>
<td class="type">
<span class="param-type">Array.&lt;string></span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>A list of headers that will be
used to determine whether the <code>Response</code>s differ.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
<tr>
<th>Returns</th>
<td>
<span class="param-type">boolean</span>
<div class="param-desc">
<p>Whether or not the <code>Response</code> objects are assumed to be
the same.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,166 @@
---
layout: jsdoc
title: "Module: sw-cache-expiration"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<div class="description"><h1 id="sw-cache-expiration">sw-cache-expiration</h1>
<p>The cache expiration plugin allows you define an expiration and/or
limit on the responses cached.</p>
<p>This can be used to ensure that responses aren't used when they are stale
and that the cache size doesn't grow endlessly.</p></div>
<div class="collapsing-entry method-type-module ">
<div class="collapse-details js-collapse-details">
<h5>Examples</h5>
<p class="code-caption">Used as an automatically invoked "plugin".</p>
<pre class="prettyprint"><code>// Add cache expiration plugin to `RequestWrapper`.
const requestWrapper = new goog.runtimeCaching.RequestWrapper({
cacheName: 'runtime-cache',
plugins: [
// The cache size will be capped at 10 entries.
new goog.cacheExpiration.Plugin({maxEntries: 10})
]
});
// Add `RequestWrapper` to a runtime cache handler.
const route = new goog.routing.RegExpRoute({
match: ({url}) => url.domain === 'example.com',
handler: new goog.runtimeCaching.StaleWhileRevalidate({requestWrapper})
});</code></pre>
<p class="code-caption">To use the cache expiration as it's own module, you can
call the <code>expireEntries()</code> method to clean up the cache.</p>
<pre class="prettyprint"><code>expirationPlugin.expireEntries({
cacheName: 'example-cache-name'
});</code></pre>
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
<h2 class="subsection-title">Classes</h2>
<div class="subsection-classes">
<dl>
<dt><a href="module-sw-cache-expiration.Plugin.html#main">Plugin</a></dt>
<dd></dd>
</dl>
</div>
@@ -0,0 +1,287 @@
---
layout: jsdoc
title: "Class: Plugin"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>Plugin</h2>
<p>Use this plugin to cache responses with certain HTTP status codes or
header values.</p>
<p>Defining both status codes and headers will cache requests with a matching
status code and a matching header.</p>
<h3>Example</h3>
<pre class="prettyprint"><code>new goog.cacheableResponse.Plugin({
statuses: [0, 200, 404],
headers: {
'Example-Header-1': 'Header-Value-1'
'Example-Header-2': 'Header-Value-2'
}
})</code></pre>
<h2 class="subsection-title">Constructor</h2>
<div class="subsection-constructor">
<div class="collapsing-entry method-type-class start-open">
<h4 class="js-collapse-title" id="Plugin">new Plugin<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Creates a new <code>Plugin</code> instance, which stores configuration and logic
to determine whether a <code>Response</code> object is cacheable or not.</p>
<p>If multiple criteria are present (e.g. both <code>statuses</code> and <code>headers</code>), then
the <code>Response</code> needs to meet all of the criteria to be cacheable.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">statuses</td>
<td class="type">
<span class="param-type">Array.&lt;Number></span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The status codes that are
checked when determining whether a <code>Response</code> is cacheable.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">headers</td>
<td class="type">
<span class="param-type">Object.&lt;String, String></span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The header values that are
checked when determining whether a <code>Response</code> is cacheable.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
@@ -0,0 +1,161 @@
---
layout: jsdoc
title: "Module: sw-cacheable-response"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<div class="description"><h1 id="sw-cacheable-response">sw-cacheable-response</h1>
<p>Given a Response object this behaviour determines whether
it's cacheable, based on a specific configuration.</p></div>
<div class="collapsing-entry method-type-module ">
<div class="collapse-details js-collapse-details">
<h5>Example</h5>
<p class="code-caption">Used as an automatically invoked
"plugin".</p>
<pre class="prettyprint"><code>// The responses will be cached if the response code is 0, 200, or 404, and
// will not be cached otherwise.
const cacheablePlugin = new goog.cacheableResponse.Plugin({
statuses: [0, 200, 404]
});
const requestWrapper = new goog.runtimeCaching.RequestWrapper({
cacheName: 'runtime-cache',
plugins: [
cacheablePlugin
]
});
const route = new goog.routing.RegExpRoute({
match: ({url}) => url.domain === 'example.com',
handler: new goog.runtimeCaching.StaleWhileRevalidate({requestWrapper})
});</code></pre>
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
<h2 class="subsection-title">Classes</h2>
<div class="subsection-classes">
<dl>
<dt><a href="module-sw-cacheable-response.Plugin.html#main">Plugin</a></dt>
<dd></dd>
</dl>
</div>
@@ -0,0 +1,747 @@
---
layout: jsdoc
title: "Class: Router"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>Router</h2>
<p>Adds a friendly API on top of the router from the
<a href="module-sw-routing.html#main">sw-routing module</a>.</p>
<h3>Examples</h3>
<p class="code-caption">How to define a simple route with caching
strategy.</p>
<pre class="prettyprint"><code>goog.swlib.router.registerRoute('/about', goog.swlib.cacheFirst());</code></pre>
<p class="code-caption">How to define a simple route with custom caching
strategy.</p>
<pre class="prettyprint"><code>self.goog.swlib.router.registerRoute('/about', (args) => {
// The requested URL
console.log(args.url);
// The FetchEvent to handle
console.log(args.event);
// The parameters from the matching route (Commonly
// used with Regex / Express routes).
console.log(args.params);
// Return a promise that resolves with a Response.
return fetch(args.url);
}));</code></pre>
<h2 class="subsection-title">Constructor</h2>
<div class="subsection-constructor">
<div class="collapsing-entry method-type-class start-open">
<h4 class="js-collapse-title" id="Router">new Router<span class="signature">(<span class="signature-args">revisionedCacheName</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Constructs a light wrapper on top of the underlying <code>Router</code>.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">revisionedCacheName</td>
<td class="type">
<span class="param-type">String</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The cache name used for entries cached
via cacheRevisionedAssets().</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
<h2 class="subsection-title">Instance Methods</h2>
<div class="subsection-methods">
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="registerNavigationRoute"><span class="this">this.</span>registerNavigationRoute<span class="signature">(<span class="signature-args">url, options</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>A shortcut used to register a <a href="module-sw-routing.NavigationRoute.html#main">module:sw-routing.NavigationRoute</a>
instance that will respond to navigation requests using a cache entry for
<code>url</code>.</p>
<p>This is useful when following the <a href="https://developers.google.com/web/fundamentals/architecture/app-shell#example-html-for-appshell">App Shell pattern</a>,
in which the previously cached shell is returned for all navigations.</p>
<p>The <code>url</code> value should correspond to an entry that's already in the cache,
perhaps a URL that is managed by
<a href="module-sw-lib.SWLib.html#cacheRevisionedAssets">module:sw-lib.SWLib#cacheRevisionedAssets</a>. Using a URL that isn't
already cached will lead to failed navigations.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">url</td>
<td class="type">
<span class="param-type">String</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The URL of the already cached HTML resource.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">options</td>
<td class="type">
<span class="param-type">Object</span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">blacklist</td>
<td class="type">
<span class="param-type">Array.&lt;RegExp></span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>Defaults to an empty blacklist.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">whitelist</td>
<td class="type">
<span class="param-type">Array.&lt;RegExp></span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>Defaults to <code>[/./]</code>, which will
match all request URLs.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">cacheName</td>
<td class="type">
<span class="param-type">String</span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The name of the cache which contains
the cached response for <code>url</code>. Defaults to the name of the cache
used by cacheRevisionedAssets().</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="registerRoute"><span class="this">this.</span>registerRoute<span class="signature">(<span class="signature-args">capture, handler</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">capture</td>
<td class="type">
<span class="param-type">String</span>
|
<span class="param-type">Regex</span>
|
<span class="param-type">Route</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The capture for a route can be one
of three types.</p>
<ol>
<li>It can be an Express style route, like: '/example/:anything/route/'
The only gotcha with this is that it will only capture URL's on your
origin.</li>
<li>A regex that will be tested against request URL's.</li>
<li>A <a href="module-sw-lib.SWLib.html#Route">Route</a> instance.</li>
</ol>
</td>
</tr>
</tr>
<tr>
<td class="name">handler</td>
<td class="type">
<span class="param-type">function</span>
|
<span class="param-type">Handler</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>Called when the route is caught by the
capture criteria. The handler argument is ignored if
you pass in a Route object, otherwise it's required.
If required, provide a function or a runtime caching strategy.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,190 @@
---
layout: jsdoc
title: "Module: sw-lib"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<div class="description"><h1 id="sw-lib">sw-lib</h1>
<p>A high-level library that makes it easier to configure routes with
caching strategies as well as manage precaching
of assets during the install step of a service worker.</p></div>
<div class="collapsing-entry method-type-module ">
<div class="collapse-details js-collapse-details">
<h5>Examples</h5>
<p class="code-caption">Adding the Library to your project.</p>
<pre class="prettyprint"><code>importScripts('/&lt;Path to Module>/build/sw-lib.min.js');</code></pre>
<p class="code-caption">Caching assets and registering routes.</p>
<pre class="prettyprint"><code>goog.swlib.cacheRevisionedAssets([
{
url: '/',
revision: '1234'
},
{
url: '/styles/main.css',
revision: 'abcd'
}
]);
// Register runtime routes like so.
goog.swlib.router.registerRoute(
'/example/', goog.swlib.staleWhileRevalidate());
goog.swlib.router.registerRoute(
/\/images\/(.*\/)?.*\.(png|jpg|jpeg|gif)/, goog.swlib.cacheFirst());
goog.swlib.router.registerRoute(
'/styles/:filename', goog.swlib.cacheFirst());</code></pre>
<p class="code-caption">Using plugins with caching strategies.</p>
<pre class="prettyprint"><code>goog.swlib.router.registerRoute(/\/images\/(.*\/)?.*\.(png|jpg|jpeg|gif)/,
goog.swlib.cacheFirst({
cacheName: 'example-cache',
cacheExpiration: {
maxEntries: 10,
maxAgeSeconds: 7 * 24 * 60 * 60
},
cacheableResponse: {
statuses: [0, 200]
},
broadcastCacheUpdate: {
channelName: 'example-channel-name'
},
plugins: [
// Additional Plugins
]
})
);</code></pre>
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
<h2 class="subsection-title">Classes</h2>
<div class="subsection-classes">
<dl>
<dt><a href="module-sw-lib.Router.html#main">Router</a></dt>
<dd></dd>
<dt><a href="module-sw-lib.SWLib.html#main">SWLib</a></dt>
<dd></dd>
</dl>
</div>
@@ -0,0 +1,429 @@
---
layout: jsdoc
title: "Module: sw-offline-google-analytics"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<div class="description"><p>In order to use the library, call<code>goog.offlineGoogleAnalytics.initialize()</code>.
It will take care of setting up service worker <code>fetch</code> handlers to ensure
that the Google Analytics JavaScript is available offline, and that any
Google Analytics requests made while offline are saved (using <code>IndexedDB</code>)
and retried the next time the service worker starts up.</p></div>
<div class="collapsing-entry method-type-module ">
<div class="collapse-details js-collapse-details">
<h5>Examples</h5>
<pre class="prettyprint"><code>// This code should live inside your service worker JavaScript, ideally
// before any other 'fetch' event handlers are defined:
// First, import the library into the service worker global scope:
importScripts('path/to/offline-google-analytics-import.js');
// Then, call goog.offlineGoogleAnalytics.initialize():
goog.offlineGoogleAnalytics.initialize();
// At this point, implement any other service worker caching strategies
// appropriate for your web app.</code></pre>
<pre class="prettyprint"><code>// If you need to specify parameters to be sent with each hit, you can use
// the `parameterOverrides` configuration option. This is useful in cases
// where you want to set a custom dimension on all hits sent by the service
// worker to differentiate them in your reports later.
goog.offlineGoogleAnalytics.initialize({
parameterOverrides: {
cd1: 'replay'
}
});</code></pre>
<pre class="prettyprint"><code>// In situations where you need to programmatically modify a hit's
// parameters you can use the `hitFilter` option. One example of when this
// might be useful is if you wanted to track the amount of time that elapsed
// between when the hit was attempted and when it was successfully replayed.
goog.offlineGoogleAnalytics.initialize({
hitFilter: searchParams =>
// Sets the `qt` param as a custom metric.
const qt = searchParams.get('qt');
searchParams.set('cm1', qt);
}
});</code></pre>
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
<h2 class="subsection-title">Instance Methods</h2>
<div class="subsection-methods">
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id=".goog.offlineGoogleAnalytics.initialize"><span class="type-attribs">(static) </span><span class="this">this.</span>goog.offlineGoogleAnalytics.initialize<span class="signature">(<span class="signature-args">config</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">config</td>
<td class="type">
<span class="param-type">Object</span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">parameterOverrides</td>
<td class="type">
<span class="param-type">Object</span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p><a href="https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters">Measurement Protocol parameters</a>,
expressed as key/value pairs, to be added to replayed
Google Analytics requests. This can be used to, e.g., set
a custom dimension indicating that the request was
replayed.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">hitFilter</td>
<td class="type">
<span class="param-type">function</span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>A function that allows you to modify the hit parameters
prior to replaying the hit. The function is invoked with
the original hit's URLSearchParams object as its only
argument. To abort the hit and prevent it from being
replayed, throw an error.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
@@ -0,0 +1,165 @@
---
layout: jsdoc
title: "Module: sw-precaching"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<div class="description"><h1 id="sw-precaching">sw-precaching</h1>
<p>The precaching module provides helpers that make it easy to cache files
during the install step of your service worker.</p>
<p>The revisioned caching will cache bust requests where appropriate and
only cache assets that have a changed revision asset compared to
the currently cached value.</p></div>
<div class="collapsing-entry method-type-module ">
<div class="collapse-details js-collapse-details">
<h5>Example</h5>
<pre class="prettyprint"><code>importScripts('/&lt;Path to Module>/build/sw-precaching.min.js');
const revCacheManager = new goog.precaching.RevisionedCacheManager();
revCacheManager.addToCacheList({
revisionedFiles: [
'/styles/main.1234.css',
{
url: '/',
revision: '1234'
}
],
});
const unrevCacheManager = new goog.precaching.UnrevisionedCacheManager();
unrevCacheManager.addToCacheList({
unrevisionedFiles: [
'/',
'/images/logo.png'
]
});
self.addEventListener('install', (event) => {
const promiseChain = Promise.all([
revCacheManager.install(),
unrevCacheManager.install(),
]);
event.waitUntil(promiseChain);
});
self.addEventListener('activate', (event) => {
const promiseChain = Promise.all([
revCacheManager.cleanup(),
unrevCacheManager.cleanup()
]);
event.waitUntil(promiseChain);
});</code></pre>
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
@@ -0,0 +1,340 @@
---
layout: jsdoc
title: "Class: ExpressRoute"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>ExpressRoute <span class="extends">extends</span> Route</span>
</h2>
<p><code>ExpressRoute</code> is a helper class to make defining Express-style
Routes easy.</p>
<p>Under the hood, it uses the <a href="https://www.npmjs.com/package/path-to-regexp"><code>path-to-regexp</code></a>
library to transform the <code>path</code> parameter into a regular expression, which is
then matched against the URL's path.</p>
<p>Please note that <code>ExpressRoute</code> can only match requests for URLs that are on
the same-origin as the current page. If you need to match cross-origin
requests, you can use either a generic <code>Route</code> or a
<code>RegExpRoute</code>.</p>
<h3>Example</h3>
<pre class="prettyprint"><code>// Any same-origin requests that start with /path/to and end with one
// additional path segment will match this route, with the last path
// segment passed along to the handler via params.file.
const route = new goog.routing.ExpressRoute({
path: '/path/to/:file',
handler: {
handle: ({event, params}) => {
// params.file will be set based on the request URL that matched.
// Do something that returns a Promise.&lt;Response>, like:
return caches.match(event.request);
},
},
});
const router = new goog.routing.Router();
router.registerRoute({route});</code></pre>
<h2 class="subsection-title">Constructor</h2>
<div class="subsection-constructor">
<div class="collapsing-entry method-type-class start-open">
<h4 class="js-collapse-title" id="ExpressRoute">new ExpressRoute<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Constructor for ExpressRoute.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">path</td>
<td class="type">
<span class="param-type">string</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The path to use for routing.
If the path contains <a href="https://github.com/pillarjs/path-to-regexp#named-parameters">named parameters</a>,
then an Object mapping parameter names to the corresponding value
will be passed to the handler via <code>params</code>.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">handler</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>An Object with a <code>handle</code> method that
will be used to respond to matching requests.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">method</td>
<td class="type">
<span class="param-type">string</span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>Only match requests that use this
HTTP method. Defaults to <code>'GET'</code> if not specified.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
@@ -0,0 +1,335 @@
---
layout: jsdoc
title: "Class: NavigationRoute"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>NavigationRoute <span class="extends">extends</span> Route</span>
</h2>
<p>NavigationRoute is a helper class to create a <code>Route</code>
that matches browser navigation requests, i.e. requests for a page.</p>
<p>It will only match incoming requests whose <a href="https://fetch.spec.whatwg.org/#concept-request-mode"><code>mode</code></a>
is set to <code>navigate</code>.</p>
<p>You can optionally only apply this route to a subset of navigation requests
by using one or both of the <code>blacklist</code> and <code>whitelist</code> parameters. If
both lists are provided, and there's a navigation to a URL which matches
both, then the blacklist will take precedence and the request will not be
matched by this route. The regular expressions in <code>whitelist</code> and <code>blacklist</code>
are matched against the <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname"><code>pathname</code></a>
portion of the requested URL.</p>
<p>To match all navigations, use a <code>whitelist</code> array containing a RegExp that
matches everything, i.e. <code>[/./]</code>.</p>
<h3>Example</h3>
<pre class="prettyprint"><code>// Any navigation requests that match the whitelist (i.e. URLs whose path
// starts with /article/) will be handled with the cache entry for
// app-shell.html.
const route = new goog.routing.NavigationRoute({
whitelist: [new RegExp('^/article/')],
handler: {handle: () => caches.match('app-shell.html')},
});
const router = new goog.routing.Router();
router.registerRoute({route});</code></pre>
<h2 class="subsection-title">Constructor</h2>
<div class="subsection-constructor">
<div class="collapsing-entry method-type-class start-open">
<h4 class="js-collapse-title" id="NavigationRoute">new NavigationRoute<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Constructor for NavigationRoute.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">whitelist</td>
<td class="type">
<span class="param-type">Array.&lt;RegExp></span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>If any of these patterns match,
the route will handle the request (assuming the blacklist doesn't match).</p>
</td>
</tr>
</tr>
<tr>
<td class="name">blacklist</td>
<td class="type">
<span class="param-type">Array.&lt;RegExp></span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>If any of these patterns match,
the route will not handle the request (even if a whitelist entry matches).</p>
</td>
</tr>
</tr>
<tr>
<td class="name">handler</td>
<td class="type">
<span class="param-type">function</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The handler to manage the response.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
@@ -0,0 +1,334 @@
---
layout: jsdoc
title: "Class: RegExpRoute"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>RegExpRoute <span class="extends">extends</span> Route</span>
</h2>
<p>RegExpRoute is a helper class to make defining regular expression based
Routes easy.</p>
<p><code>RegExpRoute</code> performs its matches against the full request URL, including
the origin. This means that, unlike <code>ExpressRoute</code>,
it's able to match cross-origin requests.</p>
<h3>Example</h3>
<pre class="prettyprint"><code>// Any requests that match the regular expression will match this route, with
// the capture groups passed along to the handler as an array via params.
const route = new goog.routing.RegExpRoute({
regExp: new RegExp('^https://example.com/path/to/(\\w+)'),
handler: {
handle: ({event, params}) => {
// params[0], etc. will be set based on the regexp capture groups.
// Do something that returns a Promise.&lt;Response>, like:
return caches.match(event.request);
},
},
});
const router = new goog.routing.Router();
router.registerRoute({route});</code></pre>
<h2 class="subsection-title">Constructor</h2>
<div class="subsection-constructor">
<div class="collapsing-entry method-type-class start-open">
<h4 class="js-collapse-title" id="RegExpRoute">new RegExpRoute<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Constructor for RegExpRoute.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">regExp</td>
<td class="type">
<span class="param-type">RegExp</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The regular expression to match against URLs.
If the <code>RegExp</code> contains <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references">capture groups</a>,
then the array of captured values will be passed to the handler via
<code>params</code>.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">handler</td>
<td class="type">
<span class="param-type">function</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The handler to manage the response.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">method</td>
<td class="type">
<span class="param-type">string</span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>Only match requests that use this
HTTP method. Defaults to <code>'GET'</code> if not specified.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
@@ -0,0 +1,358 @@
---
layout: jsdoc
title: "Class: Route"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>Route</h2>
<p>A <code>Route</code> allows you to tell a service worker that it should handle
certain network requests using a specific response strategy.</p>
<p>Two configuration options are required:</p>
<ul>
<li>A <code>match</code> function, which examines
an incoming <a href="https://developer.mozilla.org/en-US/docs/Web/API/Request"><code>Request</code></a>
to determine whether this <code>Route</code> should apply. The function should return
a <a href="https://developer.mozilla.org/en-US/docs/Glossary/Truthy">truthy</a> value
if the <code>Route</code> matches, in which case that return value is passed along to
the <code>handle</code> function.</li>
<li>A <code>handler</code> object, which should in turn have a function defined on it
named <code>handle</code>. This <code>handle</code> function is given the incoming request along
with any additional parameters generated during the <code>match</code>, and returns a
Promise for a <a href="https://developer.mozilla.org/en-US/docs/Web/API/Response"><code>Response</code></a>.</li>
</ul>
<p>Instead of implementing your own <code>handler</code>, you can use one of the
pre-defined runtime caching strategies from the
<a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a> module.</p>
<p>While you can use <code>Route</code> directly, the <code>RegExpRoute</code>
and <code>ExpressRoute</code> subclasses provide a convenient
wrapper with a nicer interface for using regular expressions or Express-style
routes as the <code>match</code> criteria.</p>
<h3>Example</h3>
<pre class="prettyprint"><code>// Any navigate requests for URLs that start with /path/to/ will match.
const route = new goog.routing.Route({
match: ({url, event}) => {
return event.request.mode === 'navigate' &amp;&amp;
url.pathname.startsWith('/path/to/');
},
handler: ({event}) => {
// Do something that returns a Promise.&lt;Response>, like:
return caches.match(event.request);
},
});
const router = new goog.routing.Router();
router.registerRoute({route});</code></pre>
<h2 class="subsection-title">Constructor</h2>
<div class="subsection-constructor">
<div class="collapsing-entry method-type-class start-open">
<h4 class="js-collapse-title" id="Route">new Route<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Constructor for Route class.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">match</td>
<td class="type">
<span class="param-type">function</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The function that determines whether the
route matches. The function is passed an object with two properties:
<code>url</code>, which is a <a href="https://developer.mozilla.org/en-US/docs/Web/API/URL">URL</a>,
and <code>event</code>, which is a <a href="https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent">FetchEvent</a>.
<code>match</code> should return a truthy value when the route applies, and
that value is passed on to the handle function.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">handler</td>
<td class="type">
<span class="param-type">function</span>
|
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>A function, or an Object with a
<code>handle</code> method. As parameters, the handler is passed an object with
the same <code>url</code> and <code>event</code> properties as <code>match</code> received, along
with an additional property, <code>params</code>, set to the truthy value
<code>match</code> returned.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">method</td>
<td class="type">
<span class="param-type">string</span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>Only match requests that use this
HTTP method. Defaults to <code>'GET'</code> if not specified.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
@@ -0,0 +1,928 @@
---
layout: jsdoc
title: "Class: Router"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>Router</h2>
<p>The Router takes one or more Routes and registers a <a href="https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent"><code>fetch</code>
event listener</a>
that will respond to network requests if there's a matching route.</p>
<p>It also allows you to define a &quot;default&quot; handler that applies to any requests
that don't explicitly match a <code>Route</code>, and a &quot;catch&quot; handler that responds
to any requests that throw an exception while being routed.</p>
<h3>Example</h3>
<pre class="prettyprint"><code>// The following example sets up two routes, one to match requests with
// "assets" in their URL, and the other for requests with "images", along
// different runtime caching handlers for each.
// Both the routes are registered with the router, and any requests that
// don't match either route will be handled using the default NetworkFirst
// strategy.
const assetRoute = new RegExpRoute({
regExp: /assets/,
handler: new goog.runtimeCaching.StaleWhileRevalidate(),
});
const imageRoute = new RegExpRoute({
regExp: /images/,
handler: new goog.runtimeCaching.CacheFirst(),
});
const router = new goog.routing.Router();
router.registerRoutes({routes: [assetRoute, imageRoute]});
router.setDefaultHandler({handler: new goog.runtimeCaching.NetworkFirst()});</code></pre>
<h2 class="subsection-title">Constructor</h2>
<div class="subsection-constructor">
<div class="collapsing-entry method-type-class start-open">
<h4 class="js-collapse-title" id="Router">new Router<span class="signature">(<span class="signature-args"></span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Start with an empty array of routes, and set up the fetch handler.</p>
</div>
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
<h2 class="subsection-title">Instance Methods</h2>
<div class="subsection-methods">
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="registerRoute"><span class="this">this.</span>registerRoute<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Registers a single route with the router.</p>
</div>
<h5>Example</h5>
<pre class="prettyprint"><code>router.registerRoutes({
route: new Route({ ... })
});</code></pre>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">route</td>
<td class="type">
<span class="param-type">Route</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The route to register.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="registerRoutes"><span class="this">this.</span>registerRoutes<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Register routes will take an array of Routes to register with the
router.</p>
</div>
<h5>Example</h5>
<pre class="prettyprint"><code>router.registerRoutes({
routes: [
new RegExpRoute({ ... }),
new ExpressRoute({ ... }),
new Route({ ... }),
]
});</code></pre>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">routes</td>
<td class="type">
<span class="param-type">Array.&lt;Route></span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>An array of routes to register.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="setCatchHandler"><span class="this">this.</span>setCatchHandler<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>If a Route throws an error while handling a request, this catch handler
will be called to return an error case.</p>
</div>
<h5>Example</h5>
<pre class="prettyprint"><code>router.setCatchHandler({
handler: ({event, params}) => {
return caches.match('/error-page.html');
}
});</code></pre>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">handler</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>An Object with a <code>handle</code> method.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="setDefaultHandler"><span class="this">this.</span>setDefaultHandler<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>An optional default handler will have its handle method called when a
request doesn't have a matching route.</p>
</div>
<h5>Example</h5>
<pre class="prettyprint"><code>router.setDefaultHandler({
handler: new goog.runtimeCaching.NetworkFirst()
});</code></pre>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">handler</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>An Object with a <code>handle</code> method.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
@@ -0,0 +1,148 @@
---
layout: jsdoc
title: "Module: sw-routing"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<div class="description"><h1 id="sw-routing">sw-routing</h1>
<p>A service worker helper library to route request URLs to handlers.</p></div>
<div class="collapsing-entry method-type-module ">
<div class="collapse-details js-collapse-details">
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
<h2 class="subsection-title">Classes</h2>
<div class="subsection-classes">
<dl>
<dt><a href="module-sw-routing.ExpressRoute.html#main">ExpressRoute</a></dt>
<dd></dd>
<dt><a href="module-sw-routing.NavigationRoute.html#main">NavigationRoute</a></dt>
<dd></dd>
<dt><a href="module-sw-routing.RegExpRoute.html#main">RegExpRoute</a></dt>
<dd></dd>
<dt><a href="module-sw-routing.Route.html#main">Route</a></dt>
<dd></dd>
<dt><a href="module-sw-routing.Router.html#main">Router</a></dt>
<dd></dd>
</dl>
</div>
@@ -0,0 +1,277 @@
---
layout: jsdoc
title: "Class: CacheFirst"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>CacheFirst <span class="extends">extends</span> Handler</span>
</h2>
<p>An implementation of a <a href="https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#cache-falling-back-to-network">cache-first</a>
request strategy.</p>
<p>The important thing to note with this caching strategy is that once a
response is cached, it will not be updated. This is useful for assets
that are revisioned as it caches the asset long term and doesn't waste
the user's data.</p>
<h3>Example</h3>
<pre class="prettyprint"><code>// Set up a route to match any requests made for URLs that end in .txt.
// The requests are handled with a cache-first strategy.
const route = new goog.routing.RegExpRoute({
regExp: /\.txt$/,
handler: new goog.runtimeCaching.CacheFirst(),
});
const router = new goog.routing.Router();
router.registerRoute({route});</code></pre>
<h2 class="subsection-title">Instance Methods</h2>
<div class="subsection-methods">
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="handle"><span class="this">this.</span>handle<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"> <span class="return-arrow">&rarr;</span> Promise.&lt;Response></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>The handle method will be called by the
<a href="module-sw-routing.Route.html#main">Route</a> class when a route matches a request.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">event</td>
<td class="type">
<span class="param-type">FetchEvent</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The event that triggered the service
worker's fetch handler.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
<tr>
<th>Returns</th>
<td>
<span class="param-type">Promise.&lt;Response></span>
<div class="param-desc">
<p>The response, either from the cache,
or if that isn't available, from the network.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
@@ -0,0 +1,275 @@
---
layout: jsdoc
title: "Class: CacheOnly"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>CacheOnly <span class="extends">extends</span> Handler</span>
</h2>
<p>An implementation of a <a href="https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#cache-only">cache-only</a>
request strategy.</p>
<p>The advantage to using this versus directly calling <code>caches.match()</code> is that
it will use the cache configuration and trigger the plugins defined in
the underlying <code>RequestWrapper</code>.</p>
<h3>Example</h3>
<pre class="prettyprint"><code>// Set up a route to match any requests made for URLs that end in .txt.
// The requests are handled with a cache-only strategy.
const route = new goog.routing.RegExpRoute({
regExp: /\.txt$/,
handler: new goog.runtimeCaching.CacheOnly(),
});
const router = new goog.routing.Router();
router.registerRoute({route});</code></pre>
<h2 class="subsection-title">Instance Methods</h2>
<div class="subsection-methods">
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="handle"><span class="this">this.</span>handle<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"> <span class="return-arrow">&rarr;</span> Promise.&lt;Response></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>The handle method will be called by the
<a href="module-sw-routing.Route.html#main">Route</a> class when a route matches a request.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">event</td>
<td class="type">
<span class="param-type">FetchEvent</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The event that triggered the service
worker's fetch handler.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
<tr>
<th>Returns</th>
<td>
<span class="param-type">Promise.&lt;Response></span>
<div class="param-desc">
<p>The response from the cache.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
@@ -0,0 +1,505 @@
---
layout: jsdoc
title: "Class: Handler"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>Handler</h2>
<p>This a base class which each caching strategy extends.</p>
<h2 class="subsection-title">Constructor</h2>
<div class="subsection-constructor">
<div class="collapsing-entry method-type-class start-open">
<h4 class="js-collapse-title" id="Handler">new Handler<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Constructor for a new Handler instance.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">requestWrapper</td>
<td class="type">
<span class="param-type">RequestWrapper</span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>An optional <code>RequestWrapper</code>
that is used to configure the cache name and request plugins. If
not provided, a new <code>RequestWrapper</code> using the
<a href="#defaultCacheName">default cache name</a> will be used.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
<h2 class="subsection-title">Instance Methods</h2>
<div class="subsection-methods">
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="handle"><span class="type-attribs">(abstract) </span><span class="this">this.</span>handle<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"> <span class="return-arrow">&rarr;</span> Promise.&lt;Response></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>An abstract method that each subclass must implement.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">event</td>
<td class="type">
<span class="param-type">FetchEvent</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The event that triggered the service
worker's fetch handler.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">params</td>
<td class="type">
<span class="param-type">Object</span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>Additional parameters that might be passed
in to the method. If used in conjunction with the
<a href="module-sw-routing.Route.html#main">Route</a> class, then the return value
from the <code>match</code> function in the Route constructor
will be passed in as the <code>params</code> value.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
<tr>
<th>Returns</th>
<td>
<span class="param-type">Promise.&lt;Response></span>
<div class="param-desc">
<p>A promise resolving with a response.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
@@ -0,0 +1,515 @@
---
layout: jsdoc
title: "Class: NetworkFirst"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>NetworkFirst <span class="extends">extends</span> Handler</span>
</h2>
<p>An implementation of a <a href="https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-falling-back-to-cache">network first</a>
request strategy.</p>
<h3>Example</h3>
<pre class="prettyprint"><code>// Set up a route to match any requests made for URLs that end in .txt.
// The requests are handled with a network-first strategy.
const route = new goog.routing.RegExpRoute({
regExp: /\.txt$/,
handler: new goog.runtimeCaching.NetworkFirst(),
});
const router = new goog.routing.Router();
router.registerRoute({route});</code></pre>
<h2 class="subsection-title">Constructor</h2>
<div class="subsection-constructor">
<div class="collapsing-entry method-type-class start-open">
<h4 class="js-collapse-title" id="NetworkFirst">new NetworkFirst<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>Constructor for a new Handler instance.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">networkTimeoutSeconds</td>
<td class="type">
<span class="param-type">number</span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>If set, and a network
response isn't returned timeout is reached, then the cached response
will be returned instead. If there is no previously cached response,
then an <code>null</code> response will be returned. This option is meant to
combat &quot;<a href="https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi">lie-fi</a>&quot;
scenarios.</p>
</td>
</tr>
</tr>
<tr>
<td class="name">requestWrapper</td>
<td class="type">
<span class="param-type">RequestWrapper</span>
<span class="param-attributes">(Optional)</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>An optional <code>RequestWrapper</code>
that is used to configure the cache name and request plugins. If
not provided, a new <code>RequestWrapper</code> using the
<a href="#defaultCacheName">default cache name</a> will be used.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
</div>
<h2 class="subsection-title">Instance Methods</h2>
<div class="subsection-methods">
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="handle"><span class="this">this.</span>handle<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"> <span class="return-arrow">&rarr;</span> Promise.&lt;Response></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>The handle method will be called by the
<a href="module-sw-routing.Route.html#main">Route</a> class when a route matches a request.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">event</td>
<td class="type">
<span class="param-type">FetchEvent</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The event that triggered the service
worker's fetch handler.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
<tr>
<th>Returns</th>
<td>
<span class="param-type">Promise.&lt;Response></span>
<div class="param-desc">
<p>The response from the network, or if that's
not available, a previously cached response.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
@@ -0,0 +1,274 @@
---
layout: jsdoc
title: "Class: NetworkOnly"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>NetworkOnly <span class="extends">extends</span> Handler</span>
</h2>
<p>An implementation of a <a href="https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-only">network-only</a>
request strategy.</p>
<p>The advantage to using this versus directly calling <code>fetch()</code> is that it will
trigger the plugins defined in the underlying <code>RequestWrapper</code>.</p>
<h3>Example</h3>
<pre class="prettyprint"><code>// Set up a route to match any requests made for URLs that end in .txt.
// The requests are handled with a network-only strategy.
const route = new goog.routing.RegExpRoute({
regExp: /\.txt$/,
handler: new goog.runtimeCaching.NetworkOnly(),
});
const router = new goog.routing.Router();
router.registerRoute({route});</code></pre>
<h2 class="subsection-title">Instance Methods</h2>
<div class="subsection-methods">
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="handle"><span class="this">this.</span>handle<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"> <span class="return-arrow">&rarr;</span> Promise.&lt;Response></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>The handle method will be called by the
<a href="module-sw-routing.Route.html#main">Route</a> class when a route matches a request.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">event</td>
<td class="type">
<span class="param-type">FetchEvent</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The event that triggered the service
worker's fetch handler.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
<tr>
<th>Returns</th>
<td>
<span class="param-type">Promise.&lt;Response></span>
<div class="param-desc">
<p>The response from the network.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,275 @@
---
layout: jsdoc
title: "Class: StaleWhileRevalidate"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<h2>StaleWhileRevalidate <span class="extends">extends</span> Handler</span>
</h2>
<p>An implementation of a <a href="https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate">stale-while-revalidate</a>
request strategy.</p>
<p>In addition to updating the appropriate caches, it will also trigger any
appropriate plugins defined in the underlying <code>RequestWrapper</code>.</p>
<h3>Example</h3>
<pre class="prettyprint"><code>// Set up a route to match any requests made for URLs that end in .txt.
// The requests are handled with a stale-while-revalidate strategy.
const route = new goog.routing.RegExpRoute({
regExp: /\.txt$/,
handler: new goog.runtimeCaching.StaleWhileRevalidate(),
});
const router = new goog.routing.Router();
router.registerRoute({route});</code></pre>
<h2 class="subsection-title">Instance Methods</h2>
<div class="subsection-methods">
<div class="collapsing-entry method-type-function ">
<h4 class="js-collapse-title" id="handle"><span class="this">this.</span>handle<span class="signature">(<span class="signature-args">input</span>)</span><span class="type-signature"> <span class="return-arrow">&rarr;</span> Promise.&lt;Response></span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>The handle method will be called by the
<a href="module-sw-routing.Route.html#main">Route</a> class when a route matches a request.</p>
</div>
<table class="method-info-table">
<tr>
<th>Parameters</th>
<td>
<table class="params">
<tr>
<td class="name">input</td>
<td class="type">
<span class="param-type">Object</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<h6>Properties</h6>
<table class="params">
<tr>
<td class="name">event</td>
<td class="type">
<span class="param-type">FetchEvent</span>
</td>
<td></td>
<tr>
<td></td>
<td class="description last">
<p>The event that triggered the service
worker's fetch handler.</p>
</td>
</tr>
</tr>
</table>
</td>
</tr>
</tr>
</table>
</td>
</tr>
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
<tr>
<th>Returns</th>
<td>
<span class="param-type">Promise.&lt;Response></span>
<div class="param-desc">
<p>The response from the cache, if present, or
from the network if not.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
@@ -0,0 +1,234 @@
---
layout: jsdoc
title: "Module: sw-runtime-caching"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<div class="description"><h1 id="sw-runtime-caching">sw-runtime-caching</h1>
<p>A service worker helper library that implements various runtime caching
strategies.</p>
<p>You can learn more about each caching strategy on
<a href="https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/">Jake Archibald's blog post</a>
which covers various ways of handling fetch events with a service worker.</p></div>
<div class="collapsing-entry method-type-module ">
<div class="collapse-details js-collapse-details">
<table class="method-info-table">
<!--
<tr>
<th>Details</th>
<td>
<dl class="details">
</dl>
</td>
</tr>
-->
</table>
</div>
</div>
<h2 class="subsection-title">Classes</h2>
<div class="subsection-classes">
<dl>
<dt><a href="module-sw-runtime-caching.CacheFirst.html#main">CacheFirst</a></dt>
<dd></dd>
<dt><a href="module-sw-runtime-caching.CacheOnly.html#main">CacheOnly</a></dt>
<dd></dd>
<dt><a href="module-sw-runtime-caching.Handler.html#main">Handler</a></dt>
<dd></dd>
<dt><a href="module-sw-runtime-caching.NetworkFirst.html#main">NetworkFirst</a></dt>
<dd></dd>
<dt><a href="module-sw-runtime-caching.NetworkOnly.html#main">NetworkOnly</a></dt>
<dd></dd>
<dt><a href="module-sw-runtime-caching.RequestWrapper.html#main">RequestWrapper</a></dt>
<dd></dd>
<dt><a href="module-sw-runtime-caching.StaleWhileRevalidate.html#main">StaleWhileRevalidate</a></dt>
<dd></dd>
</dl>
</div>
<h2 class="subsection-title">Member Variables</h2>
<div class="subsection-members">
<div class="collapsing-entry member-type-member">
<h4 class="js-collapse-title" id=".cacheName"><span class="type-attribs">(static) </span>cacheName <span class="return-arrow">&rarr;</span> <span class="type-signature">string</span></h4>
<div class="collapse-details js-collapse-details">
<div class="description">
<p>The default cache name, used by
<a href="module-sw-runtime-caching.RequestWrapper.html#main">RequestWrapper</a> when there's
no name provided.</p>
<p>It combines a constant prefix with the <code>registration.scope</code> value associated
with the current service worker, ensuring that multiple service workers used
on the same origin will have different default caches.</p>
</div>
<dl class="details">
</dl>
</div>
</div>
</div>
@@ -0,0 +1,558 @@
---
layout: jsdoc
title: "Source: sw-appcache-behavior/src/appcache-behavior-import.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* eslint-env worker, serviceworker */
'use strict';
import IDBHelper from '../../../lib/idb-helper.js';
import logHelper from '../../../lib/log-helper.js';
import constants from './lib/constants.js';
const idbHelpers = {};
Object.keys(constants.STORES).forEach((storeId) => {
idbHelpers[constants.STORES[storeId]] = new IDBHelper(
constants.DB_NAME, constants.DB_VERSION, constants.STORES[storeId]);
});
/**
* Determines what the most likely URL is associated with the client page from
* which the event's request originates. This is used to determine which
* AppCache manifest's rules should be applied.
*
* @private
* @param {FetchEvent} event
* @return {Promise.&lt;String>} The client URL
*/
function getClientUrlForEvent(event) {
// If our service worker implementation supports client identifiers, try
// to get the client URL using that.
return self.clients.get(event.clientId)
.then((client) => client.url)
// If those aren't supported, .catch() any errors and try something else.
.catch((error) => {
logHelper.error(
'Error while using clients.get(event.clientId).url: ' + error);
// Firefox currently sets the referer to 'about:client' for initial
// navigations, but that's not useful for our purposes.
if (event.request.referrer &amp;&amp;
event.request.referrer !== 'about:client') {
return event.request.referrer;
}
// Use the event's request URL as the last resort, with the assumption
// that this is a navigation request.
return event.request.url;
});
}
/**
* Finds the longest matching prefix, given an array of possible matches.
*
* @private
* @param {Array.&lt;String>} urlPrefixes
* @param {String} fullUrl
* @return {String} The longest matching prefix, or '' if none match
*/
function longestMatchingPrefix(urlPrefixes, fullUrl) {
return urlPrefixes
.filter((urlPrefix) => fullUrl.startsWith(urlPrefix))
.reduce((longestSoFar, current) => {
return longestSoFar.length >= current.length ? longestSoFar : current;
}, '');
}
/**
* Performs a fetch(), using a cached response as a fallback if that fails.
*
* @private
* @param {Request} request
* @param {String} fallbackUrl
* @param {String} cacheName
* @return {Promise.&lt;Response>}
*/
function fetchWithFallback(request, fallbackUrl, cacheName) {
logHelper.log('Trying fetch for', request.url);
return fetch(request).then((response) => {
// Succesful but error-like responses are treated as failures.
// Ditto for redirects to other origins.
if (!response.ok || (new URL(response.url).origin !== location.origin)) {
throw Error('Fallback request failure.');
}
return response;
}).catch(() => {
logHelper.warn('fetch() failed. Falling back to cache of', fallbackUrl);
return caches.open(cacheName).then(
(cache) => cache.match(fallbackUrl));
});
}
/**
* Checks IndexedDB for a manifest with a given URL. If found, it fulfills
* with info about the latest version.
*
* @private
* @param {String} manifestUrl
* @return {Promise.&lt;Object>}
*/
function getLatestManifestVersion(manifestUrl) {
return idbHelpers[constants.STORES.MANIFEST_URL_TO_CONTENTS].get(manifestUrl)
.then((versions) => {
if (versions &amp;&amp; versions.length) {
return versions[versions.length - 1];
}
});
}
/**
* Checks IndexedDB for a manifest with a given URL, versioned with the
* given hash. If found, it fulfills with the parsed manifest.
*
* @private
* @param {String} manifestUrl
* @param {String} manifestHash
* @return {Promise.&lt;Object>}
*/
function getParsedManifestVersion(manifestUrl, manifestHash) {
return idbHelpers[constants.STORES.MANIFEST_URL_TO_CONTENTS].get(manifestUrl)
.then((versions) => {
versions = versions || [];
logHelper.log('versions is', versions);
return versions.reduce((result, current) => {
logHelper.log('current is', current);
// If we already have a result, just keep returning it.
if (result) {
logHelper.log('result is', result);
return result;
}
// Otherwise, check to see if the hashes match. If so, use the parsed
// manifest for the current entry as the result.
if (current.hash === manifestHash) {
logHelper.log('manifestHash match', current);
return current.parsed;
}
return null;
}, null);
});
}
/**
* Updates the CLIENT_ID_TO_HASH store in IndexedDB with the client id to
* hash association.
*
* @private
* @param {String} clientId
* @param {String} hash
* @return {Promise.&lt;T>}
*/
function saveClientIdAndHash(clientId, hash) {
if (clientId) {
return idbHelpers[constants.STORES.CLIENT_ID_TO_HASH].put(clientId, hash);
}
// Return a fulfilled Promise so that we can still call .then().
return Promise.resolve();
}
/**
* Implements the actual AppCache logic, given a specific manifest and hash
* used as a cache identifier.
*
* @private
* @param {FetchEvent} event
* @param {Object} manifest
* @param {String} hash
* @param {String} clientUrl
* @return {Promise.&lt;Response>}
*/
function appCacheLogic(event, manifest, hash, clientUrl) {
logHelper.log('manifest is', manifest, 'version is', hash);
const requestUrl = event.request.url;
// Is our request URL listed in the CACHES section?
// Or is our request URL the client URL, since any page that
// registers a manifest is treated as if it were in the CACHE?
if (manifest.cache.includes(requestUrl) || requestUrl === clientUrl) {
logHelper.log('CACHE includes URL; using cache.match()');
// If so, return the cached response.
return caches.open(hash).then((cache) => cache.match(requestUrl));
}
// Otherwise, check the FALLBACK section next.
// FALLBACK keys are URL prefixes, and if more than one prefix
// matches our request URL, the longest prefix "wins".
// (Of course, it might be that none of the prefixes match.)
const fallbackKey = longestMatchingPrefix(Object.keys(manifest.fallback),
requestUrl);
if (fallbackKey) {
logHelper.log('fallbackKey in parsedManifest matches', fallbackKey);
return fetchWithFallback(event.request, manifest.fallback[fallbackKey],
hash);
}
// If CACHE and FALLBACK don't apply, try NETWORK.
if (manifest.network.includes(requestUrl) ||
manifest.network.includes('*')) {
logHelper.log('Match or * in NETWORK; using fetch()');
return fetch(event.request);
}
// If nothing matches, then return an error response.
logHelper.log('Nothing matches; using Response.error()');
return Response.error();
}
/**
* The behavior when there's a matching manifest for our client URL.
*
* @private
* @param {FetchEvent} event
* @param {String} manifestUrl
* @param {String} clientUrl
* @return {Promise.&lt;Response>}
*/
function manifestBehavior(event, manifestUrl, clientUrl) {
if (event.clientId) {
return idbHelpers[constants.STORES.CLIENT_ID_TO_HASH].get(event.clientId)
.then((hash) => {
// If we already have a hash assigned to this client id, use that
// manifest to implement the AppCache logic.
if (hash) {
return getParsedManifestVersion(manifestUrl, hash)
.then((parsedManifest) => appCacheLogic(event, parsedManifest, hash,
clientUrl));
}
// If there's isn't yet a hash for this client id, then get the latest
// version of the manifest, and use that to implement AppCache logic.
// Also, establish the client id to hash mapping for future use.
return getLatestManifestVersion(manifestUrl).then((latest) => {
return saveClientIdAndHash(event.clientId, latest.hash)
.then(() => appCacheLogic(event, latest.parsed, latest.hash,
clientUrl));
});
});
}
// If there's no client id, then just use the latest version of the
// manifest to implement AppCache logic.
return getLatestManifestVersion(manifestUrl).then(
(latest) => appCacheLogic(event, latest.parsed, latest.hash, clientUrl));
}
/**
* The behavior when there is no matching manifest for our client URL.
*
* @private
* @param {FetchEvent} event
* @return {Promise.&lt;Response>}
*/
function noManifestBehavior(event) {
// If we fall through to this point, then we don't have a known
// manifest associated with the client making the request.
// We now need to check to see if our request URL matches a prefix
// from the FALLBACK section of *any* manifest in our origin. If
// there are multiple matches, the longest prefix wins. If there are
// multiple prefixes of the same length in different manifest, then
// the one returned last from IDB wins. (This might not match
// browser behavior.)
// See https://www.w3.org/TR/2011/WD-html5-20110525/offline.html#concept-appcache-matches-fallback
return idbHelpers[constants.STORES.MANIFEST_URL_TO_CONTENTS].getAllValues()
.then((manifests) => {
logHelper.log('All manifests:', manifests);
// Use .map() to create an array of the longest matching prefix
// for each manifest. If no prefixes match for a given manifest,
// the value will be ''.
const longestForEach = manifests.map((manifestVersions) => {
// Use the latest version of a given manifest.
const parsedManifest =
manifestVersions[manifestVersions.length - 1].parsed;
return longestMatchingPrefix(
Object.keys(parsedManifest.fallback), event.request.url);
});
logHelper.log('longestForEach:', longestForEach);
// Next, find which of the longest matching prefixes from each
// manifest is the longest overall. Return both the index of the
// manifest in which that match appears and the prefix itself.
const longest = longestForEach.reduce((soFar, current, i) => {
if (current.length >= soFar.prefix.length) {
return {prefix: current, index: i};
}
return soFar;
}, {prefix: '', index: 0});
logHelper.log('longest:', longest);
// Now that we know the longest overall prefix, we'll use that
// to lookup the fallback URL value in the winning manifest.
const fallbackKey = longest.prefix;
logHelper.log('fallbackKey:', fallbackKey);
if (fallbackKey) {
const winningManifest = manifests[longest.index];
logHelper.log('winningManifest:', winningManifest);
const winningManifestVersion =
winningManifest[winningManifest.length - 1];
logHelper.log('winningManifestVersion:', winningManifestVersion);
const hash = winningManifestVersion.hash;
const parsedManifest = winningManifestVersion.parsed;
return fetchWithFallback(event.request,
parsedManifest.fallback[fallbackKey], hash);
}
// If nothing matches, then just fetch().
logHelper.log('Nothing at all matches. Using fetch()');
return fetch(event.request);
});
}
/**
* An attempt to mimic AppCache behavior, using the primitives available to
* a service worker.
*
* @private
* @param {FetchEvent} event
* @return {Promise.&lt;Response>}
*/
function appCacheBehaviorForEvent(event) {
const requestUrl = new URL(event.request.url);
logHelper.log('Starting appCacheBehaviorForUrl for ' + requestUrl);
// If this is a request that, as per the AppCache spec, should be handled
// via a direct fetch(), then do that and bail early.
if (event.request.headers.get('X-Use-Fetch') === 'true') {
logHelper.log('Using fetch() because X-Use-Fetch: true');
return fetch(event.request);
}
// Appcache rules only apply to GETs &amp; same-scheme requests.
if (event.request.method !== 'GET' ||
requestUrl.protocol !== location.protocol) {
logHelper.log(
'Using fetch() because AppCache does not apply to this request.');
return fetch(event.request);
}
return getClientUrlForEvent(event).then((clientUrl) => {
logHelper.log('clientUrl is', clientUrl);
return idbHelpers[constants.STORES.PATH_TO_MANIFEST].get(clientUrl)
.then((manifestUrl) => {
logHelper.log('manifestUrl is', manifestUrl);
if (manifestUrl) {
return manifestBehavior(event, manifestUrl, clientUrl);
}
logHelper.log('No matching manifest for client found.');
return noManifestBehavior(event);
});
});
}
/**
* Given a list of client ids that are still active, this:
* 1. Gets a list of all the client ids in IndexedDB's CLIENT_ID_TO_HASH
* 2. Filters them to remove the active ones
* 3. Delete the inactive entries from IndexedDB's CLIENT_ID_TO_HASH
* 4. For each inactive one, return the corresponding hash association.
*
* @private
* @param {Array.&lt;String>} idsOfActiveClients
* @return {Promise.&lt;Array.&lt;String>>}
*/
function cleanupClientIdAndHash(idsOfActiveClients) {
return idbHelpers[constants.STORES.CLIENT_ID_TO_HASH].getAllKeys()
.then((allKnownIds) => {
return allKnownIds.filter((id) => !idsOfActiveClients.includes(id));
}).then((idsOfInactiveClients) => {
return Promise.all(idsOfInactiveClients.map((id) => {
const storeName = constants.STORES.CLIENT_ID_TO_HASH;
return idbHelpers[storeName].get(id).then((hash) => {
return idbHelpers[constants.STORES.CLIENT_ID_TO_HASH].delete(id)
.then(() => hash);
});
}));
});
}
/**
* Fulfills with an array of all the hash ids that correspond to outdated
* manifest versions.
*
* @private
* @return {Promise.&lt;Array.&lt;String>>}
*/
function getHashesOfOlderVersions() {
return idbHelpers[constants.STORES.MANIFEST_URL_TO_CONTENTS].getAllValues()
.then((manifests) => {
return manifests.map((versions) => {
// versions.slice(0, -1) will give all the versions other than the
// last, or [] if there's aren't any older versions.
return versions.slice(0, -1).map((version) => version.hash);
}).reduce((prev, curr) => {
// Flatten the array-of-arrays into an array.
return prev.concat(curr);
}, []);
});
}
/**
* Does the following:
* 1. Gets a list of all client ids associated with this service worker.
* 2. Calls cleanupClientIdAndHash() to remove the out of date client id
* to hash associations.
* 3. Calls getHashesOfOlderVersions() to get a list of all the hashes
* that correspond to out-of-date manifest versions.
* 4. If there's a match between an out of date hash and a hash that is no
* longer being used by a client, then it deletes the corresponding cache.
*
* @private
*/
function cleanupOldCaches() {
self.clients.matchAll().then((clients) => {
return clients.map((client) => client.id);
}).then((idsOfActiveClients) => {
return cleanupClientIdAndHash(idsOfActiveClients);
}).then((hashesNotInUse) => {
return getHashesOfOlderVersions().then((hashesOfOlderVersions) => {
return hashesOfOlderVersions.filter((hashOfOlderVersion) => {
return hashesNotInUse.includes(hashOfOlderVersion);
});
});
}).then((idsToDelete) => {
logHelper.log('deleting cache ids', idsToDelete);
return Promise.all(idsToDelete.map((cacheId) => caches.delete(cacheId)));
});
// TODO: Delete the entry in the array stored in MANIFEST_URL_TO_CONTENT.
}
/**
* `goog.appCacheBehavior.fetch` is the main entry point to the library
* from within service worker code.
*
* The goal of the library is to provide equivalent behavior to AppCache
* whenever possible. The one difference in how this library behaves compared to
* a native AppCache implementation is that its client-side code will attempt to
* fetch a fresh AppCache manifest once any cached version is older than 24
* hours. This works around a
* [major pitfall](http://alistapart.com/article/application-cache-is-a-douchebag#section6)
* in the native AppCache implementation.
*
* **Important**
* In addition to calling `goog.appCacheBehavior.fetch()` from within your
* service worker, you *must* add the following to each HTML document that
* contains an App Cache Manifest:
*
* ```html
* &lt;script src="path/to/client-runtime.js"
* data-service-worker="service-worker.js">
* &lt;/script>
* ```
*
* (The `data-service-worker` attribute is optional. If provided, it will
* automatically call
* [`navigator.serviceWorker.register()`](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register)
* for you.)
*
* Once you've added `&lt;script src="path/to/client-runtime.js">&lt;/script>` to
* your HTML pages, you can use `goog.appCacheBehavior.fetch` within your
* service worker script to get a `Response` suitable for passing to
* [`FetchEvent.respondWidth()`](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent/respondWith):
*
* ```js
* // Import the library into the service worker global scope:
* // https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts
* importScripts('path/to/appcache-behavior-import.js');
*
* self.addEventListener('fetch', event => {
* event.respondWith(goog.appCacheBehavior.fetch(event).catch(error => {
* // Fallback behavior goes here, e.g. return fetch(event.request);
* }));
* });
* ```
*
* `goog.appCacheBehavior.fetch()` can be selectively applied to only a subset
* of requests, to aid in the migration off of App Cache and onto a more
* robust service worker implementation:
*
* ```js
* // Import the library into the service worker global scope:
* // https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts
* importScripts('path/to/appcache-behavior-import.js');
*
* self.addEventListener('fetch', event => {
* if (event.request.url.match(/legacyRegex/)) {
* event.respondWith(goog.appCacheBehavior.fetch(event));
* } else {
* event.respondWith(goog.appCacheBehavior.fetch(event));
* }
* });
* ```
*
* @alias goog.appCacheBehavior.fetch
* @param {FetchEvent} event
* @return {Promise.&lt;Response>}
*/
function fetchBehavior(event) {
logHelper.log('client id is', event.clientId);
return appCacheBehaviorForEvent(event).then((response) => {
// If this is a navigation, clean up unused caches that correspond to old
// AppCache manifest versions which are no longer associated with an
// active client. This will be done asynchronously, and won't block the
// response from being returned to the onfetch handler.
if (event.request.mode === 'navigate') {
cleanupOldCaches();
}
return response;
});
}
export {fetchBehavior as fetch};
</code></pre>
</article>
</section>
@@ -0,0 +1,374 @@
---
layout: jsdoc
title: "Source: sw-appcache-behavior/src/client-runtime.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* eslint-env browser */
/**
* # sw-appcache-behavior
*
* A service worker implementation of the behavior defined in a page's App
* Cache manifest.
*
* In your web page you need to add the client-runtime.js file:
*
* ```
* &lt;script src="../build/client-runtime.js"
* data-service-worker="service-worker.js">&lt;/script>
* ```
*
* Then in your servier worker you must import the appcache-behavior-import.js
* file:
*
* ```
* importScripts('../build/appcache-behavior-import.js');
*
* self.addEventListener('fetch', (event) => {
* event.respondWith(goog.appCacheBehavior.fetch(event));
* });
* ```
*
* @module sw-appcache-behavior
*/
import constants from './lib/constants.js';
import idb from 'idb';
import md5 from 'blueimp-md5';
import parseAppCacheManifest from 'parse-appcache-manifest';
const swScript = document.currentScript.dataset.serviceWorker;
const manifestAttribute = document.documentElement.getAttribute('manifest');
if (manifestAttribute &amp;&amp; 'serviceWorker' in navigator) {
const manifestUrl = (new URL(manifestAttribute, location.href)).href;
openIdb().then(function(db) {
return checkManifestVersion(db, manifestUrl).then(function(hash) {
return updateManifestAssociationForCurrentPage(db, manifestUrl, hash);
});
}).then(function() {
if (swScript) {
return navigator.serviceWorker.register(swScript);
}
});
}
/**
* Opens a connection to IndexedDB, using the idb library.
*
* @private
* @return {Promise.&lt;DB>}
*/
function openIdb() {
// TODO: Use idb-helpers.js instead.
return idb.open(constants.DB_NAME, constants.DB_VERSION, function(upgradeDB) {
if (upgradeDB.oldVersion === 0) {
Object.keys(constants.STORES).forEach(function(objectStore) {
upgradeDB.createObjectStore(constants.STORES[objectStore]);
});
}
});
}
/**
* Caches the Responses for one or more URLs, using the Cache Storage API.
*
* @private
* @param {String} hash
* @param {Array.&lt;String>} urls
* @return {Promise.&lt;T>}
*/
function addToCache(hash, urls) {
// Use the manifest hash as the name of the Cache to open.
return caches.open(hash).then(function(cache) {
const fetchRequests = urls.map(function(url) {
// See Item 18.3 of https://html.spec.whatwg.org/multipage/browsers.html#downloading-or-updating-an-application-cache
const request = new Request(url, {
credentials: 'include',
headers: {
'X-Use-Fetch': true,
},
redirect: 'manual',
});
return fetch(request).then(function(response) {
const cacheControl = response.headers.get('Cache-Control');
if (cacheControl &amp;&amp; cacheControl.indexOf('no-store') !== -1) {
// Bail early if we're told not to cache this response.
return;
}
if (response.ok) {
return cache.put(url, response);
}
// See Item 18.5 of https://html.spec.whatwg.org/multipage/browsers.html#downloading-or-updating-an-application-cache
if (response.status !== 404 &amp;&amp;
response.status !== 410) {
// Assuming this isn't a 200, 404 or 410, we want the .catch() to
// trigger, which will cause any previously cached Response for this
// URL to be copied over to this new cache.
return Promise.reject();
}
}).catch(function() {
// We're here if one of the following happens:
// - The fetch() rejected due to a NetworkError.
// - The HTTP status code from the fetch() was something other than
// 200, 404, and 410 AND the response isn't Cache-Control: no-store
return caches.match(url).then(function(response) {
// Add a copy of the cached response to this new cache, if it exists.
if (response) {
return cache.put(url, response.clone());
}
});
});
});
return Promise.all(fetchRequests);
});
}
/**
* Compares the copy of a manifest obtained from fetch() with the copy stored
* in IndexedDB. If they differ, it kicks off the manifest update process.
*
* It returns a Promise which fulfills with the hash for the current manifest.
*
* @private
* @param {DB} db
* @param {String} manifestUrl
* @return {Promise.&lt;String>}
*/
function checkManifestVersion(db, manifestUrl) {
const tx = db.transaction(constants.STORES.MANIFEST_URL_TO_CONTENTS);
const store = tx.objectStore(
constants.STORES.MANIFEST_URL_TO_CONTENTS);
// See Item 4 of https://html.spec.whatwg.org/multipage/browsers.html#downloading-or-updating-an-application-cache
const manifestRequest = new Request(manifestUrl, {
credentials: 'include',
headers: {
'X-Use-Fetch': true,
},
});
return Promise.all([
// TODO: Handle manifest fetch failure errors.
fetch(manifestRequest).then(function(manifestResponse) {
const dateHeaderValue = manifestResponse.headers.get('date');
if (dateHeaderValue) {
const manifestDate = new Date(dateHeaderValue).valueOf();
// Calculate the age of the manifest in milliseconds.
const manifestAgeInMillis = Date.now() - manifestDate;
// If the age is greater than 24 hours, then we need to refetch without
// hitting the cache.
if (manifestAgeInMillis > (24 * 60 * 60 * 1000)) {
const noCacheRequest = new Request(manifestUrl, {
credentials: 'include',
// See https://fetch.spec.whatwg.org/#requestcache
cache: 'reload',
headers: {
'X-Use-Fetch': true,
},
});
return fetch(noCacheRequest).then(function(noCacheResponse) {
return noCacheResponse.text();
});
}
}
return manifestResponse.text();
}).then(function(text) {
return {
// Hash a combination of URL and text so that two identical manifests
// served from a different location are treated distinctly.
hash: md5(manifestUrl + text),
text: text,
};
}),
store.get(manifestUrl),
]).then(function(values) {
// values[0].hash is the MD5 hash of the manifest returned by fetch().
// values[0].text is the manifest text returned by fetch().
// values[1] is array of Objects with {hash, parsed} properties, or null.
const knownManifests = values[1] || [];
const knownManifestVersion = knownManifests.some(function(entry) {
return entry.hash === values[0].hash;
});
if (knownManifestVersion) {
// If we already know about this manifest version, return the hash.
return values[0].hash;
}
// If the hash of the manifest retrieved from the network isn't already
// in the list of known manifest hashes, then trigger an update.
return performManifestUpdate(db, manifestUrl, values[0].hash,
values[0].text, knownManifests);
});
}
/**
* Parses the newest manifest text into the format described at
* https://www.npmjs.com/package/parse-appcache-manifest
* The parsed manifest is stored in IndexedDB.
* This also calls addToCache() to cache the relevant URLs from the manifest.
*
* It returns a Promise which fulfills with the hash for the current manifest.
*
* @private
* @param {DB} db
* @param {String} manifestUrl
* @param {String} hash
* @param {String} text
* @param {Array.&lt;Object>} knownManifests
* @return {Promise.&lt;String>}
*/
function performManifestUpdate(db, manifestUrl, hash, text, knownManifests) {
const parsedManifest = makeManifestUrlsAbsolute(manifestUrl,
parseAppCacheManifest(text));
knownManifests.push({
hash: hash,
parsed: parsedManifest,
});
const fallbackUrls = Object.keys(parsedManifest.fallback).map(function(key) {
return parsedManifest.fallback[key];
});
const urlsToCache = parsedManifest.cache.concat(fallbackUrls);
// All the master entries, i.e. those pages that were associated with an older
// version of the manifest at the same URL, should be copied over to the new
// cache as well.
const readTx = db.transaction(constants.STORES.PATH_TO_MANIFEST, 'readonly');
readTx.objectStore(constants.STORES.PATH_TO_MANIFEST).iterateCursor(
function(cursor) {
if (cursor) {
if (cursor.value === manifestUrl) {
urlsToCache.push(cursor.key);
}
cursor.continue();
}
}
);
return readTx.complete.then(function() {
const writeTx = db.transaction(constants.STORES.MANIFEST_URL_TO_CONTENTS,
'readwrite');
const manifestUrlToContentsStore = writeTx.objectStore(
constants.STORES.MANIFEST_URL_TO_CONTENTS);
return Promise.all([
manifestUrlToContentsStore.put(knownManifests, manifestUrl),
// Wait on tx.complete to ensure that the transaction succeeded.
writeTx.complete,
addToCache(hash, urlsToCache),
]);
}).then(function() {
return hash;
});
}
/**
* Updates IndexedDB to indicate that the current page's URL is associated
* with the AppCache manifest at manifestUrl.
* It also adds the current page to the cache versioned with hash, matching
* the master entry cache-as-you-go behavior you get with AppCache.
*
* @private
* @param {DB} db
* @param {String} manifestUrl
* @param {String} hash
* @return {Promise.&lt;T>}
*/
function updateManifestAssociationForCurrentPage(db, manifestUrl, hash) {
const tx = db.transaction(constants.STORES.PATH_TO_MANIFEST,
'readwrite');
const store = tx.objectStore(constants.STORES.PATH_TO_MANIFEST);
return Promise.all([
store.put(manifestUrl, location.href),
// Wait on tx.complete to ensure that the transaction succeeded.
tx.complete,
addToCache(hash, [location.href]),
]);
}
/**
* Converts all the URLs in a given manifest's CACHE, NETWORK, and FALLBACK
* sections to be absolute URLs.
*
* @private
* @param {String} baseUrl
* @param {Object} originalManifest
* @return {Object}
*/
function makeManifestUrlsAbsolute(baseUrl, originalManifest) {
const manifest = {};
manifest.cache = originalManifest.cache.map(function(relativeUrl) {
return (new URL(relativeUrl, baseUrl)).href;
});
manifest.network = originalManifest.network.map(function(relativeUrl) {
if (relativeUrl === '*') {
return relativeUrl;
}
return (new URL(relativeUrl, baseUrl)).href;
});
manifest.fallback = {};
Object.keys(originalManifest.fallback).forEach(function(key) {
manifest.fallback[(new URL(key, baseUrl)).href] =
(new URL(originalManifest.fallback[key], baseUrl)).href;
});
return manifest;
}
</code></pre>
</article>
</section>
@@ -0,0 +1,84 @@
---
layout: jsdoc
title: "Source: sw-background-sync-queue/src/index.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* # sw-background-sync-queue
*
* Queues failed requests and uses the Background Sync API to replay those
* requests at a later time when the network state has changed.
*
* @module sw-background-sync-queue
*/
import {getResponse} from './lib/response-manager';
import {setDbName} from './lib/background-sync-idb-helper';
import BackgroundSyncQueue from './lib/background-sync-queue';
import {cleanupQueue} from './lib/queue-utils';
import assert from '../../../lib/assert';
/**
* In order to use this library call `goog.backgroundSyncQueue.initialize()`.
* It will take care of setting up IndexedDB for storing requests and broadcast
* channel for communication of request creations. Also this attaches a handler
* to `sync` event and replays the queued requeusts.
*
* @alias goog.backgroundSyncQueue.initialize
*
* @param {Object} [input] The input object to this function
* @param {string} [input.dbName] The name of the db to store requests and
* responses
*/
async function initialize({dbName} = {}) {
if(dbName) {
assert.isType({dbName}, 'string');
setDbName(dbName);
}
await cleanupQueue();
}
export {
initialize,
getResponse,
BackgroundSyncQueue,
};
</code></pre>
</article>
</section>
@@ -0,0 +1,158 @@
---
layout: jsdoc
title: "Source: sw-background-sync-queue/src/lib/background-sync-queue.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>import RequestManager from './request-manager';
import RequestQueue from './request-queue';
import {maxAge} from './constants';
import assert from '../../../../lib/assert';
import IDBHelper from '../../../../lib/idb-helper';
import {getDbName} from './background-sync-idb-helper';
/**
* Use the instance of this class to push the failed requests into the queue.
*
* @example
* // Case 1: When you want to push the requests manually
* let bgQueue = new goog.backgroundSyncQueue.BackgroundSyncQueue();
* self.addEventListener('fetch', function(e) {
* if (e.request.url.startsWith('https://jsonplaceholder.typicode.com')) {
* const clone = e.request.clone();
* e.respondWith(fetch(e.request).catch((err)=>{
* bgQueue.pushIntoQueue({
* request: clone,
* });
* throw err;
* }));
* }
* });
* // Case 2: When you want the higher level framework to take care of failed
* // requests
* let bgQueue = new goog.backgroundSyncQueue.BackgroundSyncQueue({callbacks:
* {
* onResponse: async(hash, res) => {
* self.registration.showNotification('Background sync demo', {
* body: 'Product has been purchased.',
* icon: 'https://shop.polymer-project.org/images/shop-icon-384.png',
* });
* },
* onRetryFailure: (hash) => {},
* },
* });
*
* const requestWrapper = new goog.runtimeCaching.RequestWrapper({
* plugins: [bgQueue],
* });
*
* const route = new goog.routing.RegExpRoute({
* regExp: new RegExp('^https://jsonplaceholder.typicode.com'),
* handler: new goog.runtimeCaching.NetworkOnly({requestWrapper}),
* });
*
* const router = new goog.routing.Router();
* router.registerRoute({route});
*
* @alias goog.backgroundSyncQueue.BackgroundSyncQueue
* @class BackgroundSyncQueue
*
*/
class BackgroundSyncQueue {
/**
* Creates an instance of BackgroundSyncQueue with the given options
*
* @param {Object} [input]
* @param {Number} [input.maxRetentionTime = 5 days] Time for which a queued
* request will live in the queue(irespective of failed/success of replay).
* @param {Object} [input.callbacks] Callbacks for successfull/ failed
* replay of a request.
* @param {string} [input.queueName] Queue name inside db in which
* requests will be queued.
* @param {BroadcastChannel=} [input.broadcastChannel] BroadcastChannel
* which will be used to publish messages when the request will be queued.
*/
constructor({maxRetentionTime = maxAge, callbacks, queueName,
broadcastChannel} = {}) {
if(queueName) {
assert.isType({queueName}, 'string');
}
if(maxRetentionTime) {
assert.isType({maxRetentionTime}, 'number');
}
if(broadcastChannel) {
assert.isInstance({broadcastChannel}, BroadcastChannel);
}
this._queue = new RequestQueue({
config: {
maxAge: maxRetentionTime,
},
queueName,
idbQDb: new IDBHelper(getDbName(), 1, 'QueueStore'),
broadcastChannel,
});
this._requestManager = new RequestManager({callbacks,
queue: this._queue});
}
/**
* This function pushes a given request into the IndexedDb Queue
*
* @param {Object} input
* @param {Request} input.request The request which is to be queued
*
* @return {Promise} Promise which resolves when the request is pushed in
* the queue.
*/
pushIntoQueue({request}) {
assert.isInstance({request}, Request);
return this._queue.push({request});
}
/**
* Wraps `pushIntoQueue` in a callback used by higher level framework.
* This function pushes a given request into the IndexedDb Queue.
* NOTE: If you are writting the fetch handler for background sync manually,
* please ignore this.
*
* @param {Object} input
* @param {Request} input.request The request which is to be queued
*
* @return {Promise} Promise which resolves when the request is pushed in
* the queue.
*/
fetchDidFail({request}) {
return this.pushIntoQueue({request});
}
}
export default BackgroundSyncQueue;
</code></pre>
</article>
</section>
@@ -0,0 +1,74 @@
---
layout: jsdoc
title: "Source: sw-background-sync-queue/src/lib/response-manager.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>import {getDbName} from './background-sync-idb-helper';
import IDBHelper from '../../../../lib/idb-helper';
/**
* Puts the fetched response in the IDB
*
* @param {Object} config
* @private
*/
async function putResponse({hash, idbObject, response, idbQDb}) {
const _idbQHelper = idbQDb;
idbObject.response = {
headers: JSON.stringify([...response.headers]),
status: response.status,
body: await response.blob(),
};
_idbQHelper.put(hash, idbObject);
}
/**
* This function returns the fetched response for the given id of the request
*
* @alias goog.backgroundSyncQueue.getResponse
*
* @param {String} id The ID of the request given back by the broaadcast
* channel
* @return {Object} response Fetched response of the request.
*/
async function getResponse({id}) {
const _idbQHelper = new IDBHelper(getDbName(), 1, 'QueueStore');
const object = await _idbQHelper.get(id);
if (object &amp;&amp; object.response) {
return object.response;
} else {
return null;
}
}
export {
putResponse,
getResponse,
};
</code></pre>
</article>
</section>
@@ -0,0 +1,105 @@
---
layout: jsdoc
title: "Source: sw-broadcast-cache-update/src/index.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* # sw-broadcast-cache-update
*
* A helper library that uses the Broadcast Channel API to announce when
* two Response objects differ.
*
* The main use of this module will be instantiating a new
* `BroadcastCacheUpdatePlugin` and passing it to a
* {@link module:sw-runtime-caching.RequestWrapper|RequestWrapper},
* as shown in the first example below.
*
* You can listen for updates from your web app by adding an event listener on
* a `BroadcastChannel` within a page, using the same channel name as
* what's used within the service worker, as shown in the second example below.
*
* @example &lt;caption>Using the BroadcastCacheUpdatePlugin class in a
* service worker.&lt;/caption>
*
* const requestWrapper = new goog.runtimeCaching.RequestWrapper({
* cacheName: 'text-files',
* plugins: [
* new goog.broadcastCacheUpdate.BroadcastCacheUpdatePlugin(
* {channelName: 'cache-updates'})
* ],
* });
*
* const route = new goog.routing.RegExpRoute({
* regExp: /\.txt$/,
* handler: new goog.runtimeCaching.StaleWhileRevalidate({requestWrapper}),
* });
*
* const router = new goog.routing.Router();
* router.registerRoute({route});
*
* @example &lt;caption>Listening for the broadcast message in the
* window.&lt;/caption>
*
* const updateChannel = new BroadcastChannel('cache-updates');
* updateChannel.addEventListener('message', event => {
* console.log(`Cache updated: ${event.data.payload.updatedUrl}`);
* });
*
* @module sw-broadcast-cache-update
*/
import BroadcastCacheUpdate from './lib/broadcast-cache-update';
import BroadcastCacheUpdatePlugin from './lib/broadcast-cache-update-plugin';
import broadcastUpdate from './lib/broadcast-update';
import {cacheUpdatedMessageType} from './lib/constants';
import responsesAreSame from './lib/responses-are-same';
export {
BroadcastCacheUpdate,
BroadcastCacheUpdatePlugin,
broadcastUpdate,
cacheUpdatedMessageType,
responsesAreSame,
};
</code></pre>
</article>
</section>
@@ -0,0 +1,108 @@
---
layout: jsdoc
title: "Source: sw-broadcast-cache-update/src/lib/broadcast-cache-update-plugin.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import BroadcastCacheUpdate from './broadcast-cache-update';
import assert from '../../../../lib/assert';
/**
* Can be used to compare two [Responses](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* and uses the {@link https://developers.google.com/web/updates/2016/09/broadcastchannel|Broadcast Channel API}
* to notify interested parties when those responses differ.
*
* For efficiency's sake, the underlying response bodies are not compared;
* only specific response headers are checked.
*
* This class is meant to be automatically invoked as a plugin to a
* {@link module:sw-runtime-caching.RequestWrapper|RequestWrapper}, which is
* used by the `sw-lib` and `sw-runtime-caching` modules.
*
* If you would like to use this functionality outside of the `RequestWrapper`
* context, please use the `BroadcastCacheUpdate` class directly.
*
* @example &lt;caption>Added as a "plugin" to a `RequestWrapper` to
* automatically dispatch messages on a cache update&lt;/caption>
*
* const requestWrapper = new goog.runtimeCaching.RequestWrapper({
* cacheName: 'runtime-cache',
* plugins: [
* new goog.broadcastCacheUpdate.Plugin({channelName: 'cache-updates'})
* ]
* });
* const route = new goog.routing.RegExpRoute({
* match: ({url}) => url.domain === 'example.com',
* handler: new goog.runtimeCaching.StaleWhileRevalidate({requestWrapper})
* });
*
* @memberof module:sw-broadcast-cache-update
*/
class BroadcastCacheUpdatePlugin extends BroadcastCacheUpdate {
/**
* A "lifecycle" callback that will be triggered automatically by the
* `sw-lib` and `sw-runtime-caching` handlers when an entry is added to a
* cache.
*
* @private
* @param {Object} input The input object to this function.
* @param {string} input.cacheName Name of the cache the responses belong to.
* @param {Response} [input.oldResponse] The previous cached value, if any.
* @param {Response} input.newResponse The new value in the cache.
*/
cacheDidUpdate({cacheName, oldResponse, newResponse}) {
assert.isType({cacheName}, 'string');
assert.isInstance({newResponse}, Response);
if (oldResponse) {
this.notifyIfUpdated({
cacheName,
first: oldResponse,
second: newResponse,
});
}
}
}
export default BroadcastCacheUpdatePlugin;
</code></pre>
</article>
</section>
@@ -0,0 +1,151 @@
---
layout: jsdoc
title: "Source: sw-broadcast-cache-update/src/lib/broadcast-cache-update.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import ErrorFactory from './error-factory';
import assert from '../../../../lib/assert';
import broadcastUpdate from './broadcast-update';
import responsesAreSame from './responses-are-same';
import {defaultHeadersToCheck, defaultSource} from './constants';
/**
* Can be used to compare two [Responses](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* and uses the {@link https://developers.google.com/web/updates/2016/09/broadcastchannel|Broadcast Channel API}
* to notify interested parties when those responses differ.
*
* For efficiency's sake, the underlying response bodies are not compared;
* only specific response headers are checked.
*
* This class can be used inside any service worker, without having to use any
* of the other modules in this repo.
*
* If you'd like to use this functionality but are already using `sw-lib` or
* `sw-runtime-caching`, then please see the corresponding plugin,
* `BroadcastCacheUpdatePlugin`, for a easy integration.
*
* @example &lt;caption>Using BroadcastCacheUpdate when you're handling your
* own fetch and request logic.&lt;/caption>
*
* const url = '/path/to/file';
* const cacheName = 'my-runtime-cache';
* const bcu = new goog.broadcastCacheUpdate.BroadcastCacheUpdate(
* {channelName: 'cache-updates'});
*
* Promise.all([
* caches.open(cacheName).then((cache) => cache.match(url)),
* fetch(url),
* ]).then(([first, second]) => {
* if (first) {
* bcu.notifyIfUpdated({cacheName, first, second});
* }
* });
*
* @memberof module:sw-broadcast-cache-update
*/
class BroadcastCacheUpdate {
/**
* Dispatches cache update messages when a cached response has been updated.
* Messages will be dispatched on a broadcast channel with the name provided
* as channelName parameter in the constructor.
*
* @param {Object} input
* @param {string} input.channelName The name that will be used when creating
* the `BroadcastChannel`.
* @param {Array&lt;string>} input.headersToCheck A list of headers that will be
* used to determine whether the responses differ. Defaults to
* `['content-length', 'etag', 'last-modified']`.
* @param {string} input.source An attribution value that indicates where
* the update originated. Defaults to 'sw-broadcast-cache-update'.
*/
constructor({channelName, headersToCheck, source}={}) {
if (typeof channelName !== 'string' || channelName.length === 0) {
throw ErrorFactory.createError('channel-name-required');
}
this.channelName = channelName;
this.headersToCheck = headersToCheck || defaultHeadersToCheck;
this.source = source || defaultSource;
}
/**
* @private
* @return {BroadcastChannel} The underlying
* [`BroadcastChannel`](https://developer.mozilla.org/en-US/docs/Web/API/BroadcastChannel/BroadcastChannel)
* instance used for broadcasting updates.
*/
get channel() {
if (!this._channel) {
this._channel = new BroadcastChannel(this.channelName);
}
return this._channel;
}
/**
* An explicit method to call from your own code to trigger the comparison of
* two [Responses](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* and fire off a notification via the
* {@link https://developers.google.com/web/updates/2016/09/broadcastchannel|Broadcast Channel API}
* if they differ.
*
* @param {Object} input The input object to this function.
* @param {Response} input.first One of the responses to compare.
* This should not be an {@link http://stackoverflow.com/questions/39109789|opaque response}.
* @param {Response} input.second Another of the respones to compare.
* This should not be an {@link http://stackoverflow.com/questions/39109789|opaque response}.
* @param {string} input.cacheName Name of the cache the responses belong to.
*/
notifyIfUpdated({first, second, cacheName}) {
assert.isType({cacheName}, 'string');
if (
!responsesAreSame({first, second, headersToCheck: this.headersToCheck})) {
broadcastUpdate({cacheName, url: second.url,
channel: this.channel, source: this.source});
}
}
}
export default BroadcastCacheUpdate;
</code></pre>
</article>
</section>
@@ -0,0 +1,112 @@
---
layout: jsdoc
title: "Source: sw-broadcast-cache-update/src/lib/broadcast-update.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import assert from '../../../../lib/assert';
import {cacheUpdatedMessageType} from './constants';
/**
* You would not normally call this method directly; it's called automatically
* by an instance of the {@link BroadcastCacheUpdate} class. It's exposed here
* for the benefit of developers who would rather not use the full
* `BroadcastCacheUpdate` implementation.
*
* Calling this will dispatch a message on the provided {@link https://developers.google.com/web/updates/2016/09/broadcastchannel|Broadcast Channel}
* to notify interested subscribers about a change to a cached resource.
*
* The message that's posted has a formation inspired by the
* [Flux standard action](https://github.com/acdlite/flux-standard-action#introduction)
* format like so:
*
* ```
* {
* type: 'CACHE_UPDATED',
* meta: 'sw-broadcast-cache-update',
* payload: {
* cacheName: 'the-cache-name',
* updatedUrl: 'https://example.com/'
* }
* }
* ```
*
* (Usage of [Flux](https://facebook.github.io/flux/) itself is not at
* all required.)
*
* @example
* goog.broadcastCacheUpdate.broadcastUpdate({
* channel: new BroadcastChannel('Channel Name'),
* cacheName: 'example-cache-name',
* url: '/',
* source: 'custom-library'
* });
*
* @memberof module:sw-broadcast-cache-update
*
* @param {Object} input
* @param {BroadcastChannel} input.channel The `BroadcastChannel` to use.
* @param {string} input.cacheName The name of the cache in which the updated
* `Response` was stored.
* @param {string} input.url The URL associated with the updated `Response`.
* @param {string} input.source A string identifying this library as the source
* of the update message.
*/
function broadcastUpdate({channel, cacheName, url, source}) {
assert.isInstance({channel}, BroadcastChannel);
assert.isType({cacheName}, 'string');
assert.isType({source}, 'string');
assert.isType({url}, 'string');
channel.postMessage({
type: cacheUpdatedMessageType,
meta: source,
payload: {
cacheName: cacheName,
updatedUrl: url,
},
});
}
export default broadcastUpdate;
</code></pre>
</article>
</section>
@@ -0,0 +1,91 @@
---
layout: jsdoc
title: "Source: sw-broadcast-cache-update/src/lib/constants.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* The value `'CACHE_UPDATED'`, used as the `type` field of the update message.
*
* @type {string}
*
* @example
* // Prints 'CACHE_UPDATED'
* console.log(goog.broadcastCacheUpdate.cacheUpdatedMessageType);
*
* @memberof module:sw-broadcast-cache-update
*/
const cacheUpdatedMessageType = 'CACHE_UPDATED';
/**
* The default headers to compare when determining whether two `Response`
* objects are different.
*
* @private
* @type {Array&lt;string>}
*
* @memberof module:sw-broadcast-cache-update
*/
const defaultHeadersToCheck = [
'content-length',
'etag',
'last-modified',
];
/**
* The value `'sw-broadcast-cache-update'`, used as the `meta` field of the
* update message.
*
* @private
* @type {string}
*
* @memberof module:sw-broadcast-cache-update
*/
const defaultSource = 'sw-broadcast-cache-update';
export {
cacheUpdatedMessageType,
defaultHeadersToCheck,
defaultSource,
};
</code></pre>
</article>
</section>
@@ -0,0 +1,88 @@
---
layout: jsdoc
title: "Source: sw-broadcast-cache-update/src/lib/responses-are-same.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import ErrorFactory from './error-factory';
/**
* Given two `Response`s, compares several header values to see if they are
* the same or not.
*
* @example
* const responseIsSame = responsesAreSame({
* first: firstResponse,
* second: secondResponse,
* headersToCheck: [
* 'content-length',
* 'etag',
* 'last-modified',
* ]
* });
*
* @memberof module:sw-broadcast-cache-update
*
* @param {Object} input
* @param {Response} input.first One of the `Response`s.
* @param {Response} input.second Another of the `Response`s.
* @param {Array&lt;string>} input.headersToCheck A list of headers that will be
* used to determine whether the `Response`s differ.
* @return {boolean} Whether or not the `Response` objects are assumed to be
* the same.
*/
function responsesAreSame({first, second, headersToCheck}={}) {
if (!(first instanceof Response &amp;&amp;
second instanceof Response &amp;&amp;
headersToCheck instanceof Array)) {
throw ErrorFactory.createError('responses-are-same-parameters-required');
}
return headersToCheck.every((header) => {
return (first.headers.has(header) === second.headers.has(header)) &amp;&amp;
(first.headers.get(header) === second.headers.get(header));
});
}
export default responsesAreSame;
</code></pre>
</article>
</section>
@@ -0,0 +1,109 @@
---
layout: jsdoc
title: "Source: sw-build/src/index.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>// This will be the logic that powers both module and CLI
const generateSW = require('./lib/generate-sw');
const getFileManifestEntries = require('./lib/get-file-manifest-entries');
const generateFileManifest = require('./lib/generate-file-manifest');
/**
* # sw-build
*
* To get a list of url's and a corresponding revision details, call either
* `generateFileManifest` or `getFileManifestEntries()`.
*
* `generateFileManifest()` will run globs over a directory of static assets
* and write a JavaScript file containing URL's and revision details for those
* files.
*
* If you'd rather receive the data as a JavaScript Array, use
* `getFileManifestEntries()` instead.
*
* If you only need precaching of your static assets in your service
* worker and nothing else, you can generate complete service worker
* with `generateSW()`.
*
* @example &lt;caption>Generate a build manifest file.&lt;/caption>
* const swBuild = require('sw-build');
*
* swBuild.generateFileManifest({
* rootDirectory: './build/',
* dest: './build/scripts/manifest.js',
* globPatterns: ['**\/*.{html,js,css}'],
* globIgnores: ['admin.html'],
* templatedUrls: {
* '/shell': ['shell.hbs', 'main.css', 'shell.css'],
* },
* })
* .then(() => {
* console.log('Build file has been created.');
* });
*
* @example &lt;caption>Get a list of files with revision details.&lt;/caption>
* const swBuild = require('sw-build');
*
* swBuild.getFileManifestEntries({
* rootDirectory: './build/',
* globPatterns: ['**\/*.{html,js,css}'],
* globIgnores: ['admin.html'],
* templatedUrls: {
* '/shell': ['shell.hbs', 'main.css', 'shell.css'],
* },
* })
* .then((fileDetails) => {
* // An array of file details include a `url` and `revision` parameter.
* });
*
* @example &lt;caption>Generate a service worker for a project.&lt;/caption>
* const swBuild = require('sw-build');
*
* swBuild.generateSW({
* rootDirectory: './build/',
* dest: './build/sw.js',
* globPatterns: ['**\/*.{html,js,css}'],
* globIgnores: ['admin.html'],
* templatedUrls: {
* '/shell': ['shell.hbs', 'main.css', 'shell.css'],
* },
* })
* .then(() => {
* console.log('Service worker generated.');
* });
*
* @module sw-build
*/
module.exports = {
generateSW,
generateFileManifest,
getFileManifestEntries,
};
</code></pre>
</article>
</section>
@@ -0,0 +1,86 @@
---
layout: jsdoc
title: "Source: sw-build/src/lib/generate-file-manifest.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>const getFileManifestEntries = require('./get-file-manifest-entries');
const writeFileManifest = require('./utils/write-file-manifest');
const errors = require('./errors');
/**
* @example &lt;caption>Generate a build manifest of static assets, which could
* then be used with a service worker.&lt;/caption>
* const swBuild = require('sw-build');
*
* swBuild.generateFileManifest({
* dest: './build/manifest.js'
* rootDirectory: './build/',
* globPatterns: ['**\/*.{html,js,css}'],
* globIgnores: ['admin.html'],
* format: 'iife', // alternatively, use 'es'
* })
* .then(() => {
* console.log('Build Manifest generated.');
* });
*
* This method will generate a file manifest that can be used in a service
* worker for caching assets offline.
* @param {Object} input
* @param {String} input.dest The name and path you wish to write your
* manifest file to.
* @param {String} input.rootDirectory The root of the files you wish to
* be cached. This will also be the directory the service worker and library
* files are written to.
* @param {Array&lt;String>} input.globPatterns Patterns to glob for when
* generating the build manifest.
* @param {String|Array&lt;String>} [input.globIgnores] Patterns to exclude when
* generating the build manifest.
* @param {Object&lt;String,Array|String>} [input.templatedUrls]
* If a URL is rendered/templated on the server, its contents may not depend on
* a single file. This maps URLs to a list of file names, or to a string
* value, that uniquely determines each URL's contents.
* @param {String} [input.format] Default format is [`'iife'`](https://en.wikipedia.org/wiki/Immediately-invoked_function_expression), but also
* accepts `'es'`, which outputs an ES2015 module.
* @return {Promise} Resolves once the service worker has been generated
* with a precache list.
*
* @memberof module:sw-build
*/
const generateFileManifest = (input) => {
if (!input || typeof input !== 'object' || input instanceof Array) {
return Promise.reject(
new Error(errors['invalid-generate-file-manifest-arg']));
}
const fileEntries = getFileManifestEntries(input);
return writeFileManifest(input.dest, fileEntries, input.format);
};
module.exports = generateFileManifest;
</code></pre>
</article>
</section>
@@ -0,0 +1,115 @@
---
layout: jsdoc
title: "Source: sw-build/src/lib/generate-sw.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>const copySWLib = require('./utils/copy-sw-lib');
const getFileManifestEntries = require('./get-file-manifest-entries');
const writeServiceWorker = require('./write-sw');
const errors = require('./errors');
/**
* @example &lt;caption>Generate a service worker for a project.&lt;/caption>
* const swBuild = require('sw-build');
*
* swBuild.generateSW({
* rootDirectory: './build/',
* dest: './build/sw.js',
* globPatterns: ['**\/*.{html,js,css}'],
* globIgnores: ['admin.html'],
* templatedUrls: {
* '/shell': ['shell.hbs', 'main.css', 'shell.css'],
* },
* })
* .then(() => {
* console.log('Service worker generated.');
* });
*
* This method will generate a working service worker with an inlined
* file manifest.
* @param {Object} input
* @param {String} input.rootDirectory The root of the files you wish to
* be cached. This will also be the directory the service worker and library
* files are written to.
* @param {Array&lt;String>} input.globPatterns Patterns to glob for when
* generating the build manifest.
* @param {String|Array&lt;String>} [input.globIgnores] Patterns to exclude when
* generating the build manifest.
* @param {String} input.dest The name you wish to give to your
* service worker file.
* @param {Object&lt;String,Array|String>} [input.templatedUrls]
* If a URL is rendered/templated on the server, its contents may not depend on
* a single file. This maps URLs to a list of file names, or to a string
* value, that uniquely determines each URL's contents.
* @return {Promise} Resolves once the service worker has been generated
* with a precache list.
*
* @memberof module:sw-build
*/
const generateSW = function(input) {
if (!input || typeof input !== 'object' || input instanceof Array) {
return Promise.reject(new Error(errors['invalid-generate-sw-input']));
}
const rootDirectory = input.rootDirectory;
const globPatterns = input.globPatterns;
const globIgnores = input.globIgnores;
const dest = input.dest;
const templatedUrls = input.templatedUrls;
if (typeof rootDirectory !== 'string' || rootDirectory.length === 0) {
return Promise.reject(
new Error(errors['invalid-root-directory']));
}
if (typeof dest !== 'string' || dest.length === 0) {
return Promise.reject(
new Error(errors['invalid-dest']));
}
let swlibPath;
return copySWLib(rootDirectory)
.then((libPath) => {
swlibPath = libPath;
globIgnores.push(swlibPath);
})
.then(() => {
const manifestEntries = getFileManifestEntries(
{globPatterns, globIgnores, rootDirectory, templatedUrls});
return writeServiceWorker(
dest,
manifestEntries,
swlibPath,
rootDirectory
);
});
};
module.exports = generateSW;
</code></pre>
</article>
</section>
@@ -0,0 +1,134 @@
---
layout: jsdoc
title: "Source: sw-build/src/lib/get-file-manifest-entries.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>const errors = require('./errors');
const filterFiles = require('./utils/filter-files');
const getCompositeDetails = require('./utils/get-composite-details');
const getFileDetails = require('./utils/get-file-details');
const getStringDetails = require('./utils/get-string-details');
/**
* @typedef {Object} ManifestEntry
* @property {String} url The URL to the asset in the manifest.
* @property {String} revision The revision details for the file. This is a
* hash generated by node based on the file contents.
* @memberof module:sw-build
*/
/**
* To get a list of files and revision details that can be used to ultimately
* precache assets in a service worker.
*
* @param {Object} input
* @param {Array&lt;String>} input.globPatterns Patterns used to select files to
* include in the file entries.
* @param {Array&lt;String>} [input.globIgnores] Patterns used to exclude files
* from the file entries.
* @param {String} input.rootDirectory The directory run the glob patterns over.
* @param {Object&lt;String,Array|String>} [input.templatedUrls]
* If a URL is rendered/templated on the server, its contents may not depend on
* a single file. This maps URLs to a list of file names, or to a string
* value, that uniquely determines each URL's contents.
* @return {Array&lt;ManifestEntry>} An array of ManifestEntries will include
* a url and revision details for each file found.
* @memberof module:sw-build
*/
const getFileManifestEntries = (input) => {
if (!input || typeof input !== 'object' || input instanceof Array) {
throw new Error(errors['invalid-get-manifest-entries-input']);
}
const globPatterns = input.globPatterns;
const globIgnores = input.globIgnores;
const rootDirectory = input.rootDirectory;
const templatedUrls = input.templatedUrls;
if (typeof rootDirectory !== 'string' || rootDirectory.length === 0) {
return Promise.reject(
new Error(errors['invalid-root-directory']));
}
if (!globPatterns || !Array.isArray(globPatterns)) {
return Promise.reject(
new Error(errors['invalid-glob-patterns']));
}
const fileSet = new Set();
const fileDetails = globPatterns.reduce((accumulated, globPattern) => {
const globbedFileDetails = getFileDetails(
rootDirectory, globPattern, globIgnores);
globbedFileDetails.forEach((fileDetails) => {
if (fileSet.has(fileDetails.file)) {
return;
}
fileSet.add(fileDetails.file);
accumulated.push(fileDetails);
});
return accumulated;
}, []);
// templatedUrls is optional.
if (templatedUrls) {
if (typeof templatedUrls !== 'object') {
return Promise.reject(new Error(errors['invalid-templated-urls']));
}
for (let url of Object.keys(templatedUrls)) {
if (fileSet.has(url)) {
return Promise.reject(
new Error(errors['templated-url-matches-glob']));
}
const dependencies = templatedUrls[url];
if (Array.isArray(dependencies)) {
const dependencyDetails = dependencies.reduce((previous, pattern) => {
const globbedFileDetails = getFileDetails(
rootDirectory, pattern, globIgnores);
return previous.concat(globbedFileDetails);
}, []);
fileDetails.push(getCompositeDetails(url, dependencyDetails));
} else if (typeof dependencies === 'string') {
fileDetails.push(getStringDetails(url, dependencies));
} else {
return Promise.reject(
new Error(errors['invalid-templated-urls']));
}
}
}
return filterFiles(fileDetails);
};
module.exports = getFileManifestEntries;
</code></pre>
</article>
</section>
@@ -0,0 +1,97 @@
---
layout: jsdoc
title: "Source: sw-build/src/lib/log-helper.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/**
* Copyright 2016 Google Inc.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
'use strict';
/* eslint-disable no-console */
const chalk = require('chalk');
/**
* Log helper is just a wrapper around the console to print slightly
* nicer / colored messages and could be extended to filter based on log
* level.
* @private
*/
class LogHelper {
/**
* Print a warning message to the console (Colored yellow).
* @param {string} msg Message to print to the console
* @param {Object} [runtimeInfo] Any information available at runtime that
* should be printed to the console.
*/
warn(msg, runtimeInfo) {
let loggedMsg = chalk.yellow(msg);
if (runtimeInfo) {
loggedMsg += ` ${runtimeInfo}`;
}
console.warn(loggedMsg);
}
/**
* Print an error message to the console (Colored red).
* @param {string} msg Message to print to the console
* @param {Error} [err] An optional error to print to the console.
*/
error(msg, err) {
console.error(chalk.red(msg));
if (err &amp;&amp; err.stack) {
console.error(chalk.red(err.stack));
}
}
/**
* Print an info message to the console (Colored dim).
* @param {string} msg Message to print to the console
*/
info(msg) {
console.log(chalk.dim(msg));
}
}
module.exports = new LogHelper();
</code></pre>
</article>
</section>
@@ -0,0 +1,90 @@
---
layout: jsdoc
title: "Source: sw-cache-expiration/src/index.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* # sw-cache-expiration
*
* The cache expiration plugin allows you define an expiration and/or
* limit on the responses cached.
*
* This can be used to ensure that responses aren't used when they are stale
* and that the cache size doesn't grow endlessly.
*
* @example &lt;caption>Used as an automatically invoked "plugin".&lt;/caption>
*
* // Add cache expiration plugin to `RequestWrapper`.
* const requestWrapper = new goog.runtimeCaching.RequestWrapper({
* cacheName: 'runtime-cache',
* plugins: [
* // The cache size will be capped at 10 entries.
* new goog.cacheExpiration.Plugin({maxEntries: 10})
* ]
* });
*
* // Add `RequestWrapper` to a runtime cache handler.
* const route = new goog.routing.RegExpRoute({
* match: ({url}) => url.domain === 'example.com',
* handler: new goog.runtimeCaching.StaleWhileRevalidate({requestWrapper})
* });
*
* @example &lt;caption>To use the cache expiration as it's own module, you can
* call the &lt;code>expireEntries()&lt;/code> method to clean up the cache.&lt;/caption>
* expirationPlugin.expireEntries({
* cacheName: 'example-cache-name'
* });
*
* @module sw-cache-expiration
*/
import {timestampPropertyName, urlPropertyName} from './lib/constants';
import Plugin from './lib/plugin';
export {
timestampPropertyName,
urlPropertyName,
Plugin,
};
</code></pre>
</article>
</section>
@@ -0,0 +1,427 @@
---
layout: jsdoc
title: "Source: sw-cache-expiration/src/lib/plugin.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import idb from 'idb';
import assert from '../../../../lib/assert';
import {
idbName,
idbVersion,
urlPropertyName,
timestampPropertyName,
} from './constants';
import ErrorFactory from './error-factory';
/**
* The cache expiration plugin allows you define an expiration and / or
* limit on the responses cached.
*
* @example
* const expirationPlugin = new goog.cacheExpiration.Plugin({
* maxEntries: 2,
* maxAgeSeconds: 10,
* });
*
* @memberof module:sw-cache-expiration
*/
class Plugin {
/**
* Creates a new `Plugin` instance, which is used to remove entries from a
* [`Cache`](https://developer.mozilla.org/en-US/docs/Web/API/Cache) once
* certain criteria—maximum number of entries, age of entry, or both—is met.
*
* @param {Object} input
* @param {Number} [input.maxEntries] The maximum size of the cache. Entries
* will be expired using a LRU policy once the cache reaches this size.
* @param {Number} [input.maxAgeSeconds] The maximum age for fresh entries.
*/
constructor({maxEntries, maxAgeSeconds} = {}) {
if (!(maxEntries || maxAgeSeconds)) {
throw ErrorFactory.createError('max-entries-or-age-required');
}
if (maxEntries &amp;&amp; typeof maxEntries !== 'number') {
throw ErrorFactory.createError('max-entries-must-be-number');
}
if (maxAgeSeconds &amp;&amp; typeof maxAgeSeconds !== 'number') {
throw ErrorFactory.createError('max-age-seconds-must-be-number');
}
this.maxEntries = maxEntries;
this.maxAgeSeconds = maxAgeSeconds;
// These are used to keep track of open IndexDB and Caches for a given name.
this._dbs = new Map();
this._caches = new Map();
}
/**
* Returns a promise for the IndexedDB database used to keep track of state.
*
* @private
* @param {Object} input
* @param {string} input.cacheName Name of the cache the Responses belong to.
* @return {DB} An open DB instance.
*/
async getDB({cacheName} = {}) {
assert.isType({cacheName}, 'string');
const idbId = `${idbName}-${cacheName}`;
if (!this._dbs.has(idbId)) {
const openDb = await idb.open(idbId, idbVersion, (upgradeDB) => {
const objectStore = upgradeDB.createObjectStore(cacheName,
{keyPath: urlPropertyName});
objectStore.createIndex(timestampPropertyName, timestampPropertyName,
{unique: false});
});
this._dbs.set(idbId, openDb);
}
return this._dbs.get(idbId);
}
/**
* Returns a promise for an open Cache instance named `cacheName`.
*
* @private
* @param {Object} input
* @param {string} input.cacheName Name of the cache the Responses belong to.
* @return {Cache} An open Cache instance.
*/
async getCache({cacheName} = {}) {
assert.isType({cacheName}, 'string');
if (!this._caches.has(cacheName)) {
const openCache = await caches.open(cacheName);
this._caches.set(cacheName, openCache);
}
return this._caches.get(cacheName);
}
/**
* A "lifecycle" callback that will be triggered automatically by the
* `goog.runtimeCaching` handlers when a `Response` is about to be returned
* from a [Cache](https://developer.mozilla.org/en-US/docs/Web/API/Cache) to
* the handler. It allows the `Response` to be inspected for freshness and
* prevents it from being used if the `Response`'s `Date` header value is
* older than the configured `maxAgeSeconds`.
*
* Developers who are not using `goog.runtimeCaching` would normally not call
* this method directly; instead, use [`isResponseFresh`](#isResponseFresh)
* to perform the same freshness check.
*
* @private
* @param {Object} input
* @param {Response} input.cachedResponse The `Response` object that's been
* read from a cache and whose freshness should be checked.
* @param {Number} [input.now] A timestamp. Defaults to the current time.
* @return {Response|null} Either the `cachedResponse`, if it's fresh, or
* `null` if the `Response` is older than `maxAgeSeconds`.
*/
cacheWillMatch({cachedResponse, now} = {}) {
if (this.isResponseFresh({cachedResponse, now})) {
return cachedResponse;
}
return null;
}
/**
* Checks whether a `Response` is fresh, based on the `Response`'s
* `Date` header and the configured `maxAgeSeconds`.
*
* If `maxAgeSeconds` or the `Date` header is not set then it will
* default to returning `true`.
*
* @param {Object} input
* @param {Response} input.cachedResponse The `Response` object that's been
* read from a cache and whose freshness should be checked.
* @param {Number} [input.now] A timestamp. Defaults to the current time.
* @return {boolean} Either the `true`, if it's fresh, or `false` if the
* `Response` is older than `maxAgeSeconds`.
*
* @example
* expirationPlugin.isResponseFresh({
* cachedResponse: responseFromCache
* });
*/
isResponseFresh({cachedResponse, now} = {}) {
// Only bother checking for freshness if we have a valid response and if
// maxAgeSeconds is set. Otherwise, skip the check and always return true.
if (cachedResponse &amp;&amp; this.maxAgeSeconds) {
assert.isInstance({cachedResponse}, Response);
const dateHeader = cachedResponse.headers.get('date');
if (dateHeader) {
if (typeof now === 'undefined') {
now = Date.now();
}
const parsedDate = new Date(dateHeader);
// If the Date header was invalid for some reason, parsedDate.getTime()
// will return NaN, and the comparison will always be false. That means
// that an invalid date will be treated as if the response is fresh.
if ((parsedDate.getTime() + (this.maxAgeSeconds * 1000)) &lt; now) {
// Only return false if all the conditions are met.
return false;
}
}
}
return true;
}
/**
* A "lifecycle" callback that will be triggered automatically by the
* `goog.runtimeCaching` handlers when an entry is added to a cache.
*
* Developers would normally not call this method directly; instead,
* [`updateTimestamp`](#updateTimestamp) combined with
* [`expireEntries`](#expireEntries) provides equivalent plugin.
*
* @private
* @param {Object} input
* @param {string} input.cacheName Name of the cache the responses belong to.
* @param {Response} input.newResponse The new value in the cache.
* @param {string} input.url The URL for the cache entry.
* @param {Number} [input.now] A timestamp. Defaults to the current time.
*/
cacheDidUpdate({cacheName, newResponse, url, now} = {}) {
assert.isType({cacheName}, 'string');
assert.isInstance({newResponse}, Response);
if (typeof now === 'undefined') {
now = Date.now();
}
this.updateTimestamp({cacheName, url, now}).then(() => {
this.expireEntries({cacheName, now});
});
}
/**
* Updates the timestamp stored in IndexedDB for `url` to be equal to `now`.
*
* @param {Object} input
* @param {string} input.cacheName Name of the cache the Responses belong to.
* @param {string} input.url The URL for the entry to update.
* @param {Number} [input.now] A timestamp. Defaults to the current time.
*
* @example
* expirationPlugin.updateTimestamp({
* cacheName: 'example-cache-name',
* url: '/example-url'
* });
*/
async updateTimestamp({cacheName, url, now} = {}) {
assert.isType({url}, 'string');
assert.isType({cacheName}, 'string');
if (typeof now === 'undefined') {
now = Date.now();
}
const db = await this.getDB({cacheName});
const tx = db.transaction(cacheName, 'readwrite');
tx.objectStore(cacheName).put({
[timestampPropertyName]: now,
[urlPropertyName]: url,
});
await tx.complete;
}
/**
* Expires entries, both based on the the maximum age and the maximum number
* of entries, depending on how this instance is configured.
*
* @param {Object} input
* @param {string} input.cacheName Name of the cache the Responses belong to.
* @param {Number} [input.now] A timestamp. Defaults to the current time.
* @return {Array&lt;string>} A list of the URLs that were expired.
*
* @example
* expirationPlugin.expireEntries({
* cacheName: 'example-cache-name'
* });
*/
async expireEntries({cacheName, now} = {}) {
assert.isType({cacheName}, 'string');
if (typeof now === 'undefined') {
now = Date.now();
}
// First, expire old entries, if maxAgeSeconds is set.
const oldEntries = this.maxAgeSeconds ?
await this.findOldEntries({cacheName, now}) :
[];
// Once that's done, check for the maximum size.
const extraEntries = this.maxEntries ?
await this.findExtraEntries({cacheName}) :
[];
// Use a Set to remove any duplicates following the concatenation, then
// convert back into an array.
const urls = [...new Set(oldEntries.concat(extraEntries))];
await this.deleteFromCacheAndIDB({cacheName, urls});
return urls;
}
/**
* Expires entries based on the the maximum age.
*
* @private
* @param {Object} input
* @param {string} input.cacheName Name of the cache the Responses belong to.
* @param {Number} [input.now] A timestamp.
* @return {Array&lt;string>} A list of the URLs that were expired.
*/
async findOldEntries({cacheName, now} = {}) {
assert.isType({cacheName}, 'string');
assert.isType({now}, 'number');
const expireOlderThan = now - (this.maxAgeSeconds * 1000);
const urls = [];
const db = await this.getDB({cacheName});
const tx = db.transaction(cacheName, 'readonly');
const store = tx.objectStore(cacheName);
const timestampIndex = store.index(timestampPropertyName);
timestampIndex.iterateCursor((cursor) => {
if (!cursor) {
return;
}
if (cursor.value[timestampPropertyName] &lt; expireOlderThan) {
urls.push(cursor.value[urlPropertyName]);
}
cursor.continue();
});
await tx.complete;
return urls;
}
/**
* Finds the URLs that should be expired as per the current state of IndexedDB
* and the `maxEntries` configuration. A least-recently used policy is
* enforced, so if `maxEntries` is `N`, and there are `N + M` URLs listed in
* IndexedDB, then this function will return the least-recently used `M` URLs.
*
* @private
* @param {Object} input
* @param {string} input.cacheName Name of the cache the Responses belong to.
* @return {Array&lt;string>} A list of the URLs that are candidates for
* expiration.
*/
async findExtraEntries({cacheName} = {}) {
assert.isType({cacheName}, 'string');
const urls = [];
const db = await this.getDB({cacheName});
let tx = db.transaction(cacheName, 'readonly');
let store = tx.objectStore(cacheName);
let timestampIndex = store.index(timestampPropertyName);
const initialCount = await timestampIndex.count();
if (initialCount > this.maxEntries) {
// We need to create a new transaction to make Firefox happy.
tx = db.transaction(cacheName, 'readonly');
store = tx.objectStore(cacheName);
timestampIndex = store.index(timestampPropertyName);
timestampIndex.iterateCursor((cursor) => {
if (!cursor) {
return;
}
urls.push(cursor.value[urlPropertyName]);
if (initialCount - urls.length > this.maxEntries) {
cursor.continue();
}
});
}
await tx.complete;
return urls;
}
/**
* Removes entries corresponding to each of the URLs from both the Cache
* Storage API and from IndexedDB.
*
* @private
* @param {Object} input
* @param {string} input.cacheName Name of the cache the Responses belong to.
* @param {Array&lt;string>} urls The URLs to delete.
*/
async deleteFromCacheAndIDB({cacheName, urls} = {}) {
assert.isType({cacheName}, 'string');
assert.isArrayOfType({urls}, 'string');
if (urls.length > 0) {
const cache = await this.getCache({cacheName});
const db = await this.getDB({cacheName});
await urls.forEach(async (url) => {
await cache.delete(url);
const tx = db.transaction(cacheName, 'readwrite');
const store = tx.objectStore(cacheName);
await store.delete(url);
await tx.complete;
});
}
}
}
export default Plugin;
</code></pre>
</article>
</section>
@@ -0,0 +1,80 @@
---
layout: jsdoc
title: "Source: sw-cacheable-response/src/index.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* # sw-cacheable-response
*
* Given a Response object this behaviour determines whether
* it's cacheable, based on a specific configuration.
*
* @example &lt;caption>Used as an automatically invoked
* "plugin".&lt;/caption>
*
* // The responses will be cached if the response code is 0, 200, or 404, and
* // will not be cached otherwise.
* const cacheablePlugin = new goog.cacheableResponse.Plugin({
* statuses: [0, 200, 404]
* });
*
* const requestWrapper = new goog.runtimeCaching.RequestWrapper({
* cacheName: 'runtime-cache',
* plugins: [
* cacheablePlugin
* ]
* });
*
* const route = new goog.routing.RegExpRoute({
* match: ({url}) => url.domain === 'example.com',
* handler: new goog.runtimeCaching.StaleWhileRevalidate({requestWrapper})
* });
*
* @module sw-cacheable-response
*/
import Plugin from './lib/plugin';
export {Plugin};
</code></pre>
</article>
</section>
@@ -0,0 +1,141 @@
---
layout: jsdoc
title: "Source: sw-cacheable-response/src/lib/plugin.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import assert from '../../../../lib/assert';
/**
* Use this plugin to cache responses with certain HTTP status codes or
* header values.
*
* Defining both status codes and headers will cache requests with a matching
* status code and a matching header.
*
* @example
* new goog.cacheableResponse.Plugin({
* statuses: [0, 200, 404],
* headers: {
* 'Example-Header-1': 'Header-Value-1'
* 'Example-Header-2': 'Header-Value-2'
* }
* })
*
* @memberof module:sw-cacheable-response
*/
class Plugin {
/**
* Creates a new `Plugin` instance, which stores configuration and logic
* to determine whether a `Response` object is cacheable or not.
*
* If multiple criteria are present (e.g. both `statuses` and `headers`), then
* the `Response` needs to meet all of the criteria to be cacheable.
*
* @param {Object} input
* @param {Array&lt;Number>} [input.statuses] The status codes that are
* checked when determining whether a `Response` is cacheable.
* @param {Object&lt;String,String>} [input.headers] The header values that are
* checked when determining whether a `Response` is cacheable.
*/
constructor({statuses, headers} = {}) {
assert.atLeastOne({statuses, headers});
if (statuses !== undefined) {
assert.isArrayOfType({statuses}, 'number');
}
if (headers !== undefined) {
assert.isType({headers}, 'object');
}
this.statuses = statuses;
this.headers = headers;
}
/**
* A "lifecycle" callback that will be triggered automatically by the
* `goog.runtimeCaching` handlers prior to an entry being added to a cache.
*
* Developers would normally not call this method directly; instead,
* [`isResponseCacheable`](#isResponseCacheable) provides equivalent behavior
* when used outside of `goog.runtimeCaching`.
*
* @private
* @param {Object} input
* @param {Response} input.newResponse The response that might be cached.
* @return {boolean} `true` if the `Response` is cacheable, based on the
* configuration of this object, and `false` otherwise.
*/
cacheWillUpdate({response} = {}) {
return this.isResponseCacheable({response});
}
/**
* Checks a response to see whether it's cacheable or not, based on the
* configuration of this object.
*
* @private
* @param {Object} input
* @param {Response} input.newResponse The response that might be cached.
* @return {boolean} `true` if the `Response` is cacheable, based on the
* configuration of this object, and `false` otherwise.
*/
isResponseCacheable({response} = {}) {
assert.isInstance({response}, Response);
let cacheable = true;
if (this.statuses) {
cacheable = this.statuses.includes(response.status);
}
if (this.headers &amp;&amp; cacheable) {
cacheable = Object.keys(this.headers).some((headerName) => {
return response.headers.get(headerName) === this.headers[headerName];
});
}
return cacheable;
}
}
export default Plugin;
</code></pre>
</article>
</section>
@@ -0,0 +1,268 @@
---
layout: jsdoc
title: "Source: sw-cli/src/index.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/**
* Copyright 2016 Google Inc.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
'use strict';
const fs = require('fs');
const path = require('path');
const minimist = require('minimist');
const updateNotifier = require('update-notifier');
const swBuild = require('sw-build');
const cliLogHelper = require('./lib/log-helper');
const pkg = require('../package.json');
const generateGlobPattern = require('./lib/utils/generate-glob-pattern');
const saveConfigFile = require('./lib/utils/save-config');
const getConfigFile = require('./lib/utils/get-config');
const askForRootOfWebApp = require('./lib/questions/ask-root-of-web-app');
const askForServiceWorkerName = require('./lib/questions/ask-sw-name');
const askSaveConfigFile = require('./lib/questions/ask-save-config');
const askManifestFileName = require('./lib/questions/ask-manifest-name');
const askForExtensionsToCache =
require('./lib/questions/ask-extensions-to-cache');
/**
* This class is a wrapper to make test easier. This is used by
* ./bin/index.js to pass in the args when the CLI is used.
*/
class SWCli {
/**
* This is a helper method that allows the test framework to call argv with
* arguments without worrying about running as an actual CLI.
*
* @private
* @param {Object} argv The value passed in via process.argv.
* @return {Promise} Promise is returned so testing framework knows when
* handling the request has finished.
*/
argv(argv) {
updateNotifier({pkg}).notify();
const cliArgs = minimist(argv);
if (cliArgs._.length > 0) {
// We have a command
return this.handleCommand(cliArgs._[0], cliArgs._.splice(1), cliArgs)
.then(() => {
process.exit(0);
})
.catch(() => {
process.exit(1);
});
} else {
// we have a flag only request
return this.handleFlag(cliArgs)
.then(() => {
process.exit(0);
})
.catch(() => {
process.exit(1);
});
}
}
/**
* Prints the help text to the terminal.
*/
printHelpText() {
const helpText = fs.readFileSync(
path.join(__dirname, 'cli-help.txt'), 'utf8');
cliLogHelper.log(helpText);
}
/**
* If there is no command given to the CLI then the flags will be passed
* to this function in case a relevant action can be taken.
* @param {object} flags The available flags = require(the command line.
* @return {Promise} returns a promise once handled.
*/
handleFlag(flags) {
let handled = false;
if (flags.h || flags.help) {
this.printHelpText();
handled = true;
}
if (flags.v || flags.version) {
cliLogHelper.log(pkg.version);
handled = true;
}
if (handled) {
return Promise.resolve();
}
// This is a fallback
this.printHelpText();
return Promise.reject();
}
/**
* If a command is given in the command line args, this method will handle
* the appropriate action.
* @param {string} command The command name.
* @param {object} args The arguments given to this command.
* @param {object} flags The flags supplied with the command line.
* @return {Promise} A promise for the provided task.
*/
handleCommand(command, args, flags) {
switch (command) {
case 'generate:sw':
return this._generateSW();
case 'generate:manifest':
return this._generateBuildManifest();
default:
cliLogHelper.error(`Invlaid command given '${command}'`);
return Promise.reject();
}
}
/**
* This method will generate a working service worker with a file manifest.
* @return {Promise} The promise returned here will be used to exit the
* node process cleanly or not.
*/
_generateSW() {
let config = {};
return getConfigFile()
.then((savedConfig) => {
if (savedConfig) {
config = savedConfig;
config.wasSaved = true;
}
})
.then(() => {
if (!config.rootDirectory) {
return askForRootOfWebApp()
.then((rDirectory) => {
// This will give a pretty relative path:
// '' => './'
// 'build' => './build/'
config.rootDirectory =
path.join('.', path.relative(process.cwd(), rDirectory), path.sep);
});
}
})
.then(() => {
if (!config.globPatterns) {
return askForExtensionsToCache(config.rootDirectory)
.then((extensionsToCache) => {
config.globPatterns = [
generateGlobPattern(config.rootDirectory, extensionsToCache),
];
});
}
})
.then(() => {
if (!config.dest) {
return askForServiceWorkerName()
.then((swName) => {
const swDest = path.join(config.rootDirectory, swName);
config.dest = swDest;
config.globIgnores = [
swDest,
];
});
}
})
.then(() => {
if (!config.wasSaved) {
return askSaveConfigFile();
}
// False since it's already saved.
return false;
})
.then((saveConfig) => {
if (saveConfig) {
return saveConfigFile(config);
}
})
.then(() => {
return swBuild.generateSW(config);
});
}
/**
* Generates a file manifest with revisioning details
* that can be used in your service worker for precaching assets.
* @return {Promise} Resolves when the node process exits.
*/
_generateBuildManifest() {
let rootDirPath;
let fileManifestName;
let fileExtentionsToCache;
return askForRootOfWebApp()
.then((rDirectory) => {
rootDirPath = rDirectory;
return askForExtensionsToCache(rootDirPath);
})
.then((extensionsToCache) => {
fileExtentionsToCache = extensionsToCache;
return askManifestFileName();
})
.then((manifestName) => {
fileManifestName = manifestName;
})
.then(() => {
const globPattern = generateGlobPattern(
rootDirPath, fileExtentionsToCache);
return swBuild.generateFileManifest({
rootDirectory: rootDirPath,
globPatterns: [globPattern],
globIgnores: [
path.join(rootDirPath, fileManifestName),
],
dest: path.join(rootDirPath, fileManifestName),
});
});
}
}
module.exports = SWCli;
</code></pre>
</article>
</section>
@@ -0,0 +1,97 @@
---
layout: jsdoc
title: "Source: sw-cli/src/lib/log-helper.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/**
* Copyright 2016 Google Inc.
*
* 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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
'use strict';
/* eslint-disable no-console */
const chalk = require('chalk');
/**
* Log helper is just a wrapper around the console to print slightly
* nicer / colored messages and could be extended to filter based on log
* level.
* @private
*/
class LogHelper {
/**
* Print a warning message to the console (Colored yellow).
* @param {string} msg Message to print to the console
* @param {Object} [runtimeInfo] Any information available at runtime that
* should be printed to the console.
*/
warn(msg, runtimeInfo) {
let loggedMsg = chalk.yellow(msg);
if (runtimeInfo) {
loggedMsg += ` ${runtimeInfo}`;
}
console.warn(loggedMsg);
}
/**
* Print an error message to the console (Colored red).
* @param {string} msg Message to print to the console
* @param {Error} [err] An optional error to print to the console.
*/
error(msg, err) {
console.error(chalk.red(msg));
if (err &amp;&amp; err.stack) {
console.error(chalk.red(err.stack));
}
}
/**
* Print an info message to the console (Colored dim).
* @param {string} msg Message to print to the console
*/
log(msg) {
console.log(chalk.dim(msg));
}
}
module.exports = new LogHelper();
</code></pre>
</article>
</section>
@@ -0,0 +1,103 @@
---
layout: jsdoc
title: "Source: sw-cli/src/lib/questions/ask-root-of-web-app.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>const fs = require('fs');
const inquirer = require('inquirer');
const path = require('path');
const constants = require('../constants');
const logHelper = require('../log-helper');
const errors = require('../errors');
/**
* This method requests the root directory of the web app.
* The user can opt to type in the directory OR select = require(a list of
* directories in the current path.
* @return {Promise&lt;string>} Promise that resolves with the name of the root
* directory if given.
*/
module.exports = () => {
const manualEntryChoice = 'Manually Enter Path';
const currentDirectory = process.cwd();
return new Promise((resolve, reject) => {
fs.readdir(currentDirectory, (err, directoryContents) => {
if (err) {
return reject(err);
}
resolve(directoryContents);
});
})
.then((directoryContents) => {
return directoryContents.filter((directoryContent) => {
return fs.statSync(directoryContent).isDirectory();
});
})
.then((subdirectories) => {
return subdirectories.filter((subdirectory) => {
return !constants.blacklistDirectoryNames.includes(subdirectory);
});
})
.then((subdirectories) => {
const choices = subdirectories.concat([
new inquirer.Separator(),
manualEntryChoice,
]);
return inquirer.prompt([
{
name: 'rootDir',
message: 'What is the root of your web app?',
type: 'list',
choices: choices,
},
{
name: 'rootDir',
message: 'Please manually enter the root of your web app?',
when: (answers) => {
return answers.rootDir === manualEntryChoice;
},
},
]);
})
.then((answers) => {
return path.join(currentDirectory, answers.rootDir);
})
.catch((err) => {
logHelper.error(
errors['unable-to-get-rootdir'],
err
);
throw err;
});
};
</code></pre>
</article>
</section>
@@ -0,0 +1,114 @@
---
layout: jsdoc
title: "Source: sw-lib/src/index.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* eslint-env browser */
import ErrorFactory from './lib/error-factory';
import SWLib from './lib/sw-lib';
import assert from '../../../lib/assert.js';
if (!assert.isSWEnv()) {
// We are not running in a service worker, print error message
throw ErrorFactory.createError('not-in-sw');
}
/**
* # sw-lib
*
* A high-level library that makes it easier to configure routes with
* caching strategies as well as manage precaching
* of assets during the install step of a service worker.
*
* @example &lt;caption>Adding the Library to your project.&lt;/caption>
* importScripts('/&lt;Path to Module>/build/sw-lib.min.js');
*
* @example &lt;caption>Caching assets and registering routes.&lt;/caption>
*
* goog.swlib.cacheRevisionedAssets([
* {
* url: '/',
* revision: '1234'
* },
* {
* url: '/styles/main.css',
* revision: 'abcd'
* }
* ]);
*
* // Register runtime routes like so.
* goog.swlib.router.registerRoute(
* '/example/', goog.swlib.staleWhileRevalidate());
* goog.swlib.router.registerRoute(
* /\/images\/(.*\/)?.*\.(png|jpg|jpeg|gif)/, goog.swlib.cacheFirst());
* goog.swlib.router.registerRoute(
* '/styles/:filename', goog.swlib.cacheFirst());
*
* @example &lt;caption>Using plugins with caching strategies.&lt;/caption>
*
* goog.swlib.router.registerRoute(/\/images\/(.*\/)?.*\.(png|jpg|jpeg|gif)/,
* goog.swlib.cacheFirst({
* cacheName: 'example-cache',
* cacheExpiration: {
* maxEntries: 10,
* maxAgeSeconds: 7 * 24 * 60 * 60
* },
* cacheableResponse: {
* statuses: [0, 200]
* },
* broadcastCacheUpdate: {
* channelName: 'example-channel-name'
* },
* plugins: [
* // Additional Plugins
* ]
* })
* );
*
* @module sw-lib
*/
const swLibInstance = new SWLib();
export default swLibInstance;
</code></pre>
</article>
</section>
@@ -0,0 +1,179 @@
---
layout: jsdoc
title: "Source: sw-lib/src/lib/router.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* eslint-env browser, serviceworker */
import {
Router as SWRoutingRouter,
ExpressRoute,
RegExpRoute,
Route,
NavigationRoute,
} from '../../../sw-routing/src/index.js';
import ErrorFactory from './error-factory.js';
/**
* Adds a friendly API on top of the router from the
* {@link module:sw-routing|sw-routing module}.
*
* @example &lt;caption>How to define a simple route with caching
* strategy.&lt;/caption>
*
* goog.swlib.router.registerRoute('/about', goog.swlib.cacheFirst());
*
* @example &lt;caption>How to define a simple route with custom caching
* strategy.&lt;/caption>
*
* self.goog.swlib.router.registerRoute('/about', (args) => {
* // The requested URL
* console.log(args.url);
*
* // The FetchEvent to handle
* console.log(args.event);
*
* // The parameters from the matching route (Commonly
* // used with Regex / Express routes).
* console.log(args.params);
*
* // Return a promise that resolves with a Response.
* return fetch(args.url);
* }));
*
* @memberof module:sw-lib
*/
class Router extends SWRoutingRouter {
/**
* Constructs a light wrapper on top of the underlying `Router`.
* @param {String} revisionedCacheName The cache name used for entries cached
* via cacheRevisionedAssets().
*/
constructor(revisionedCacheName) {
super();
this._revisionedCacheName = revisionedCacheName;
}
/**
* @param {String|Regex|Route} capture The capture for a route can be one
* of three types.
* 1. It can be an Express style route, like: '/example/:anything/route/'
* The only gotcha with this is that it will only capture URL's on your
* origin.
* 1. A regex that will be tested against request URL's.
* 1. A [Route]{@link module:sw-lib.SWLib#Route} instance.
* @param {function|Handler} handler Called when the route is caught by the
* capture criteria. The handler argument is ignored if
* you pass in a Route object, otherwise it's required.
* If required, provide a function or a runtime caching strategy.
*/
registerRoute(capture, handler) {
if (typeof handler === 'function') {
handler = {
handle: handler,
};
}
if (typeof capture === 'string') {
if (capture.length === 0) {
throw ErrorFactory.createError('empty-express-string');
}
super.registerRoute({
route: new ExpressRoute({path: capture, handler}),
});
} else if (capture instanceof RegExp) {
super.registerRoute({
route: new RegExpRoute({regExp: capture, handler}),
});
} else if (capture instanceof Route) {
super.registerRoute({route: capture});
} else {
throw ErrorFactory.createError('unsupported-route-type');
}
}
/**
* A shortcut used to register a {@link module:sw-routing.NavigationRoute}
* instance that will respond to navigation requests using a cache entry for
* `url`.
*
* This is useful when following the [App Shell pattern](https://developers.google.com/web/fundamentals/architecture/app-shell#example-html-for-appshell),
* in which the previously cached shell is returned for all navigations.
*
* The `url` value should correspond to an entry that's already in the cache,
* perhaps a URL that is managed by
* {@link module:sw-lib.SWLib#cacheRevisionedAssets}. Using a URL that isn't
* already cached will lead to failed navigations.
*
* @param {String} url The URL of the already cached HTML resource.
* @param {Object} [options]
* @param {Array&lt;RegExp>} [options.blacklist] Defaults to an empty blacklist.
* @param {Array&lt;RegExp>} [options.whitelist] Defaults to `[/./]`, which will
* match all request URLs.
* @param {String} [options.cacheName] The name of the cache which contains
* the cached response for `url`. Defaults to the name of the cache
* used by cacheRevisionedAssets().
*/
registerNavigationRoute(url, options = {}) {
if (typeof url !== 'string') {
throw ErrorFactory.createError('navigation-route-url-string');
}
// Allow folks to explicitly pass in a null/undefined cacheName option if
// they want that behavior.
const cacheName = 'cacheName' in options ?
options.cacheName :
this._revisionedCacheName;
super.registerRoute({route: new NavigationRoute({
handler: () => caches.match(url, {cacheName}),
whitelist: options.whitelist || [/./],
blacklist: options.blacklist || [],
})});
}
}
export default Router;
</code></pre>
</article>
</section>
@@ -0,0 +1,441 @@
---
layout: jsdoc
title: "Source: sw-lib/src/lib/sw-lib.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* eslint-env browser, serviceworker */
import Router from './router.js';
import ErrorFactory from './error-factory.js';
import {
RevisionedCacheManager, UnrevisionedCacheManager,
} from '../../../sw-precaching/src/index.js';
import {Route} from '../../../sw-routing/src/index.js';
import {Plugin as CacheExpirationPlugin} from
'../../../sw-cache-expiration/src/index.js';
import {BroadcastCacheUpdatePlugin} from
'../../../sw-broadcast-cache-update/src/index.js';
import {Plugin as CacheableResponsePlugin} from
'../../../sw-cacheable-response/src/index.js';
import {
CacheFirst, CacheOnly, NetworkFirst,
NetworkOnly, StaleWhileRevalidate, RequestWrapper,
} from '../../../sw-runtime-caching/src/index.js';
/**
* A high level library to make it as easy as possible to precache assets
* efficiently and define run time caching strategies.
*
* @memberof module:sw-lib
*/
class SWLib {
/**
* You should never need to instantiate this class. The library instantiates
* an instance which can be accessed by `self.goog.swlib`.
*/
constructor() {
this._revisionedCacheManager = new RevisionedCacheManager();
this._router = new Router(this._revisionedCacheManager.getCacheName());
this._unrevisionedCacheManager = new UnrevisionedCacheManager();
this._registerInstallActivateEvents();
this._registerDefaultRoutes();
}
/**
* Revisioned assets can be cached intelligently
* during the install (i.e. old files are cleared from the cache, new files
* are added to the cache and unchanged files are left as is).
*
* The input needs to be an array of URL strings which having revisioning
* details in them otherwise the entry should be an object with `url` and
* `revision` parameters.
*
* @example &lt;caption>Cache revisioned assets.&lt;/caption>
* // Cache a set of revisioned URLs
* goog.swlib.cacheRevisionedAssets([
* '/styles/main.1234.css',
* '/images/logo.abcd.jpg'
* ]);
*
* // ...cacheRevisionedAssets() can also take objects to cache
* // non-revisioned URLs.
* goog.swlib.cacheRevisionedAssets([
* {
* url: '/index.html',
* revision: '1234'
* },
* {
* url: '/about.html',
* revision: 'abcd'
* }
* ]);
*
* @param {Array&lt;String|Object>} revisionedFiles A set of urls to cache
* when the service worker is installed.
*/
cacheRevisionedAssets(revisionedFiles) {
// Add a more helpful error message than assertion error.
if (!Array.isArray(revisionedFiles)) {
throw ErrorFactory.createError('bad-revisioned-cache-list');
}
this._revisionedCacheManager.addToCacheList({
revisionedFiles,
});
}
/**
* Any assets you wish to cache ahead of time which can't be revisioned
* should be cached with this method. All assets are cached on install
* regardless of whether an older version of the request is in the cache.
*
* The input can be a string or a [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request).
*
* @example &lt;caption>Unrevisioned assets can be cached too.&lt;/caption>
* // For unrevisioned assets that should always be downloaded
* // with every service worker update, use this method.
* goog.swlib.warmRuntimeCache([
* '/scripts/main.js',
* '/images/default-avater.png'
* ]);
*
* // warmRuntimeCache can also accept Requests, in case you need greater
* // control over the request.
* goog.swlib.warmRuntimeCache([
* new Request('/images/logo.png'),
* new Request('/api/data.json')
* ]);
*
* @param {Array&lt;String|Request>} unrevisionedFiles A set of urls to cache
* when the service worker is installed.
*/
warmRuntimeCache(unrevisionedFiles) {
// Add a more helpful error message than assertion error.
if (!Array.isArray(unrevisionedFiles)) {
throw ErrorFactory.createError('bad-revisioned-cache-list');
}
this._unrevisionedCacheManager.addToCacheList({
unrevisionedFiles,
});
}
/**
* The router for this library is exposed via the `router` parameter.
* This is an instance of the {@link module:sw-lib.Router|Router}.
*
* @example
* self.goog.swlib.router.registerRoute('/', swlib.goog.cacheFirst());
*
* @type {Router}
*/
get router() {
return this._router;
}
/**
* If you need fine grained control of route matching and handling,
* use the {@link module:sw-routing.Route|Route Class} to define
* the desired plugin and register it to the router.
*
* This is an export of the
* {@link module:sw-routing.Route|sw-runtime Route Class}.
*
* @example &lt;caption>How to define a route using a Route instance.&lt;/caption>
*
* const routeInstance = new goog.swlib.Route({
* match: (url) => {
* // Return true or false
* return true;
* },
* handler: {
* handle: (args) => {
* // The requested URL
* console.log(args.url);
*
* // The FetchEvent to handle
* console.log(args.event);
*
* // The parameters from the matching route.
* console.log(args.params);
*
* // Return a promise that resolves with a Response.
* return fetch(args.url);
* },
* },
* });
* self.goog.swlib.router.registerRoute(routeInstance);
* @type {Route}
*/
get Route() {
return Route;
}
/**
* RuntimeStrategyOptions is just a JavaScript object, but the structure
* explains the options for runtime strategies used in sw-lib.
*
* See the example of how this can be used with the `cacheFirst()` caching
* strategy.
*
* @example
* const cacheFirstStrategy = goog.swlib.cacheFirst({
* cacheName: 'example-cache',
* cacheExpiration: {
* maxEntries: 10,
* maxAgeSeconds: 7 * 24 * 60 * 60
* },
* broadcastCacheUpdate: {
* channelName: 'example-channel-name'
* },
* cacheableResponse: {
* statuses: [0, 200, 404],
* headers: {
* 'Example-Header-1': 'Header-Value-1'
* 'Example-Header-2': 'Header-Value-2'
* }
* }
* plugins: [
* // Additional Plugins
* ]
* });
*
* @typedef {Object} RuntimeStrategyOptions
* @property {String} cacheName Name of cache to use
* for caching (both lookup and updating).
* @property {Object} cacheExpiration Defining this
* object will add a cache expiration plugins to this strategy.
* @property {Number} cacheExpiration.maxEntries
* The maximum number of entries to store in a cache.
* @property {Number} cacheExpiration.maxAgeSeconds
* The maximum lifetime of a request to stay in the cache before it's removed.
* @property {Object} broadcastCacheUpdate Defining
* this object will add a broadcast cache update plugin.
* @property {String} broadcastCacheUpdate.channelName
* The name of the broadcast channel to dispatch messages on.
* @property {Array&lt;plugins>} plugins For
* any additional plugins you wish to add, simply include them in this
* array.
* @memberof module:sw-lib.SWLib
*/
/**
* A [cache first](https://jakearchibald.com/2014/offline-cookbook/#cache-falling-back-to-network)
* run-time caching strategy.
*
* @example
* const cacheFirstStrategy = goog.swlib.cacheFirst();
*
* goog.swlib.router.addRoute('/styles/*', cacheFirstStrategy);
*
* @param {module:sw-lib.SWLib.RuntimeStrategyOptions} [options] To define
* any additional caching or broadcast plugins pass in option values.
* @return {module:sw-runtime-caching.CacheFirst} A CacheFirst handler.
*/
cacheFirst(options) {
return this._getCachingMechanism(CacheFirst, options);
}
/**
* A [cache only](https://jakearchibald.com/2014/offline-cookbook/#cache-only)
* run-time caching strategy.
*
* @example
* const cacheOnlyStrategy = goog.swlib.cacheOnly();
*
* goog.swlib.router.addRoute('/styles/*', cacheOnlyStrategy);
*
* @param {module:sw-lib.SWLib.RuntimeStrategyOptions} [options] To define
* any additional caching or broadcast plugins pass in option values.
* @return {module:sw-runtime-caching.CacheOnly} The caching handler instance.
*/
cacheOnly(options) {
return this._getCachingMechanism(CacheOnly, options);
}
/**
* A [network first](https://jakearchibald.com/2014/offline-cookbook/#network-falling-back-to-cache)
* run-time caching strategy.
*
* @example
* const networkFirstStrategy = goog.swlib.networkFirst();
*
* goog.swlib.router.addRoute('/blog/', networkFirstStrategy);
*
* @param {module:sw-lib.SWLib.RuntimeStrategyOptions} [options] To define
* any additional caching or broadcast plugins pass in option values.
* @return {module:sw-runtime-caching.NetworkFirst} The caching handler
* instance.
*/
networkFirst(options) {
return this._getCachingMechanism(NetworkFirst, options);
}
/**
* A [network only](https://jakearchibald.com/2014/offline-cookbook/#network-only)
* run-time caching strategy.
*
* @example
* const networkOnlyStrategy = goog.swlib.networkOnly();
*
* goog.swlib.router.addRoute('/admin/', networkOnlyStrategy);
*
* @param {module:sw-lib.SWLib.RuntimeStrategyOptions} [options] To define
* any additional caching or broadcast plugins pass in option values.
* @return {module:sw-runtime-caching.NetworkOnly} The caching handler
* instance.
*/
networkOnly(options) {
return this._getCachingMechanism(NetworkOnly, options);
}
/**
* A [stale while revalidate](https://jakearchibald.com/2014/offline-cookbook/#stale-while-revalidate)
* run-time caching strategy.
*
* @example
* const staleWhileRevalidateStrategy = goog.swlib.staleWhileRevalidate();
*
* goog.swlib.router.addRoute('/styles/*', staleWhileRevalidateStrategy);
*
* @param {module:sw-lib.SWLib.RuntimeStrategyOptions} [options] To define
* any additional caching or broadcast plugins pass in option values.
* @return {module:sw-runtime-caching.StaleWhileRevalidate} The caching
* handler instance.
*/
staleWhileRevalidate(options) {
return this._getCachingMechanism(StaleWhileRevalidate, options);
}
/**
* This method will add plugins based on options passed in by the
* developer.
*
* @private
* @param {Class} HandlerClass The class to be configured and instantiated.
* @param {Object} [options] Options to configure the handler.
* @return {Handler} A handler instance configured with the appropriate
* behaviours
*/
_getCachingMechanism(HandlerClass, options = {}) {
const pluginParamsToClass = {
'cacheExpiration': CacheExpirationPlugin,
'broadcastCacheUpdate': BroadcastCacheUpdatePlugin,
'cacheableResponse': CacheableResponsePlugin,
};
const wrapperOptions = {
plugins: [],
};
if (options['cacheName']) {
wrapperOptions['cacheName'] = options['cacheName'];
}
// Iterate over known plugins and add them to Request Wrapper options.
const pluginKeys = Object.keys(pluginParamsToClass);
pluginKeys.forEach((pluginKey) => {
if (options[pluginKey]) {
const PluginClass = pluginParamsToClass[pluginKey];
const pluginParams = options[pluginKey];
wrapperOptions.plugins.push(new PluginClass(pluginParams));
}
});
// Add custom plugins.
if (options.plugins) {
options.plugins.forEach((plugin) => {
wrapperOptions.plugins.push(plugin);
});
}
return new HandlerClass({
requestWrapper: new RequestWrapper(wrapperOptions),
});
}
/**
* This method will register listeners for the install and activate events.
* @private
*/
_registerInstallActivateEvents() {
self.addEventListener('install', (event) => {
event.waitUntil(Promise.all([
this._revisionedCacheManager.install(),
this._unrevisionedCacheManager.install(),
]));
});
self.addEventListener('activate', (event) => {
event.waitUntil(Promise.all([
this._revisionedCacheManager.cleanup(),
this._unrevisionedCacheManager.cleanup(),
]));
});
}
/**
* This method will register any default routes the library will need.
* @private
*/
_registerDefaultRoutes() {
const cacheFirstHandler = this.cacheFirst({
cacheName: this._revisionedCacheManager.getCacheName(),
});
const route = new this.Route({
match: ({url, event}) => {
const cachedUrls = this._revisionedCacheManager.getCachedUrls();
return cachedUrls.indexOf(url.href) !== -1;
},
handler: cacheFirstHandler,
});
this.router.registerRoute(route);
}
}
export default SWLib;
</code></pre>
</article>
</section>
@@ -0,0 +1,169 @@
---
layout: jsdoc
title: "Source: sw-offline-google-analytics/src/index.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/* eslint-env worker, serviceworker */
import constants from './lib/constants.js';
import enqueueRequest from './lib/enqueue-request.js';
import logHelper from '../../../lib/log-helper.js';
import replayQueuedRequests from './lib/replay-queued-requests.js';
/**
* In order to use the library, call`goog.offlineGoogleAnalytics.initialize()`.
* It will take care of setting up service worker `fetch` handlers to ensure
* that the Google Analytics JavaScript is available offline, and that any
* Google Analytics requests made while offline are saved (using `IndexedDB`)
* and retried the next time the service worker starts up.
*
* @example
* // This code should live inside your service worker JavaScript, ideally
* // before any other 'fetch' event handlers are defined:
*
* // First, import the library into the service worker global scope:
* importScripts('path/to/offline-google-analytics-import.js');
*
* // Then, call goog.offlineGoogleAnalytics.initialize():
* goog.offlineGoogleAnalytics.initialize();
*
* // At this point, implement any other service worker caching strategies
* // appropriate for your web app.
*
* @example
* // If you need to specify parameters to be sent with each hit, you can use
* // the `parameterOverrides` configuration option. This is useful in cases
* // where you want to set a custom dimension on all hits sent by the service
* // worker to differentiate them in your reports later.
* goog.offlineGoogleAnalytics.initialize({
* parameterOverrides: {
* cd1: 'replay'
* }
* });
*
* @example
* // In situations where you need to programmatically modify a hit's
* // parameters you can use the `hitFilter` option. One example of when this
* // might be useful is if you wanted to track the amount of time that elapsed
* // between when the hit was attempted and when it was successfully replayed.
* goog.offlineGoogleAnalytics.initialize({
* hitFilter: searchParams =>
* // Sets the `qt` param as a custom metric.
* const qt = searchParams.get('qt');
* searchParams.set('cm1', qt);
* }
* });
*
* @module sw-offline-google-analytics
*/
/**
* @alias goog.offlineGoogleAnalytics.initialize
* @param {Object=} config
* @param {Object=} config.parameterOverrides
* [Measurement Protocol parameters](https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters),
* expressed as key/value pairs, to be added to replayed
* Google Analytics requests. This can be used to, e.g., set
* a custom dimension indicating that the request was
* replayed.
* @param {Function=} config.hitFilter
* A function that allows you to modify the hit parameters
* prior to replaying the hit. The function is invoked with
* the original hit's URLSearchParams object as its only
* argument. To abort the hit and prevent it from being
* replayed, throw an error.
* @memberof module:sw-offline-google-analytics
*/
const initialize = (config) => {
config = config || {};
// Stores whether or not the previous /collect request failed.
let previousHitFailed = false;
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
const request = event.request;
if (url.hostname === constants.URL.HOST) {
if (url.pathname === constants.URL.COLLECT_PATH) {
// If this is a /collect request, then use a network-first strategy,
// falling back to queueing the request in IndexedDB.
// Make a clone of the request before we use it, in case we need
// to read the request body later on.
const clonedRequest = request.clone();
event.respondWith(
fetch(request).then((response) => {
if (previousHitFailed) {
replayQueuedRequests(config);
}
previousHitFailed = false;
return response;
}, (error) => {
logHelper.log('Enqueuing failed request...');
previousHitFailed = true;
return enqueueRequest(clonedRequest).then(() => Response.error());
})
);
} else if (url.pathname === constants.URL.ANALYTICS_JS_PATH) {
// If this is a request for the Google Analytics JavaScript library,
// use the network first, falling back to the previously cached copy.
event.respondWith(
caches.open(constants.CACHE_NAME).then((cache) => {
return fetch(request).then((response) => {
return cache.put(request, response.clone()).then(() => response);
}).catch((error) => {
logHelper.error(error);
return cache.match(request);
});
})
);
}
}
});
replayQueuedRequests(config);
};
export default {initialize};
</code></pre>
</article>
</section>
@@ -0,0 +1,114 @@
---
layout: jsdoc
title: "Source: sw-precaching/src/index.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* # sw-precaching
*
* The precaching module provides helpers that make it easy to cache files
* during the install step of your service worker.
*
* The revisioned caching will cache bust requests where appropriate and
* only cache assets that have a changed revision asset compared to
* the currently cached value.
*
* @example
* importScripts('/&lt;Path to Module>/build/sw-precaching.min.js');
*
* const revCacheManager = new goog.precaching.RevisionedCacheManager();
* revCacheManager.addToCacheList({
* revisionedFiles: [
* '/styles/main.1234.css',
* {
* url: '/',
* revision: '1234'
* }
* ],
* });
*
* const unrevCacheManager = new goog.precaching.UnrevisionedCacheManager();
* unrevCacheManager.addToCacheList({
* unrevisionedFiles: [
* '/',
* '/images/logo.png'
* ]
* });
*
* self.addEventListener('install', (event) => {
* const promiseChain = Promise.all([
* revCacheManager.install(),
* unrevCacheManager.install(),
* ]);
* event.waitUntil(promiseChain);
* });
*
* self.addEventListener('activate', (event) => {
* const promiseChain = Promise.all([
* revCacheManager.cleanup(),
* unrevCacheManager.cleanup()
* ]);
* event.waitUntil(promiseChain);
* });
*
* @module sw-precaching
*/
import ErrorFactory from './lib/error-factory';
import RevisionedCacheManager from
'./lib/controllers/revisioned-cache-manager.js';
import UnrevisionedCacheManager from
'./lib/controllers/unrevisioned-cache-manager.js';
import assert from '../../../lib/assert.js';
if (!assert.isSWEnv()) {
// We are not running in a service worker, print error message
throw ErrorFactory.createError('not-in-sw');
}
export {
RevisionedCacheManager,
UnrevisionedCacheManager,
};
</code></pre>
</article>
</section>
@@ -0,0 +1,294 @@
---
layout: jsdoc
title: "Source: sw-precaching/src/lib/controllers/base-cache-manager.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>import ErrorFactory from '../error-factory';
import {RequestWrapper} from '../../../../sw-runtime-caching/src/index';
/**
* This class handles the shared logic for caching revisioned and unrevisioned
* assets.
* @private
* @memberof module:sw-precaching
*/
class BaseCacheManager {
/**
* Constructor for BaseCacheManager
* @param {String} cacheName This is the cache name to store requested assets.
*/
constructor(cacheName) {
this._entriesToCache = new Map();
this._requestWrapper = new RequestWrapper({
cacheName,
fetchOptions: {
credentials: 'same-origin',
},
});
this._cacheName = this._requestWrapper.cacheName;
}
/**
* Adds entries to the install list.
* This will manage duplicate entries and perform the caching during
* the install step.
*
* @private
* @param {Array&lt;String|Request|Object>} rawEntries A raw entry that can be
* parsed into a BaseCacheEntry by the inheriting CacheManager.
*/
_addEntries(rawEntries) {
this._parsedCacheUrls = null;
rawEntries.forEach((rawEntry) => {
this._addEntryToInstallList(
this._parseEntry(rawEntry)
);
});
}
/**
* Gives access to the cache name used by thie caching manager.
* @return {String} The cache name used for this manager.
*/
getCacheName() {
return this._cacheName;
}
/**
* Returns an array of fully qualified URL's that will be cached by this
* cache manager.
* @return {Array&lt;String>} An array of URLs that will be cached.
*/
getCachedUrls() {
if (!this._parsedCacheUrls) {
this._parsedCacheUrls = Array.from(this._entriesToCache.keys())
.map((url) => new URL(url, location).href);
}
return this._parsedCacheUrls;
}
/**
* Adds an entry to the install list.
*
* Duplicates are filtered out and checks are made for the scenario
* where two entries have the same URL but different revisions. For example
* caching:
* [
* {url: '/hello.txt', revision: '1'},
* {url: '/hello.txt', revision: '2'},
* ]
* This will throw an error as the library can't determine the correct
* revision and this may cause issues in future when updating the service
* worker with new revisions.
*
* @private
* @param {RevisionedCacheEntry} precacheEntry The file entry to be cached
* during the next install event.
*/
_addEntryToInstallList(precacheEntry) {
const entryID = precacheEntry.entryID;
const previousEntry = this._entriesToCache.get(precacheEntry.entryID);
if (!previousEntry) {
// This entry isn't in the install list
this._entriesToCache.set(entryID, precacheEntry);
return;
}
this._onDuplicateInstallEntryFound(precacheEntry, previousEntry);
}
/**
* Manages the service worker install event and caches the revisioned
* assets.
*
* @return {Promise} The promise resolves when all the desired assets are
* cached.
*/
async install() {
if (this._entriesToCache.size === 0) {
return;
}
const cachePromises = [];
this._entriesToCache.forEach((precacheEntry) => {
cachePromises.push(
this._cacheEntry(precacheEntry)
);
});
// Wait for all requests to be cached.
return Promise.all(cachePromises);
}
/**
* Requests the entry and saves it to the cache if the response
* is valid.
*
* @private
* @param {BaseCacheEntry} precacheEntry The entry to fetch and cache.
* @return {Promise} Returns a promise that resolves once the entry is fetched
* and cached.
*/
async _cacheEntry(precacheEntry) {
const isCached = await this._isAlreadyCached(precacheEntry);
if (isCached) {
return;
}
try {
await this._requestWrapper.fetchAndCache({
request: precacheEntry.getNetworkRequest(),
waitOnCache: true,
cacheKey: precacheEntry.request,
});
return this._onEntryCached(precacheEntry);
} catch (err) {
throw ErrorFactory.createError('request-not-cached', {
message: `Failed to get a cacheable response for ` +
`'${precacheEntry.request.url}': ${err.message}`,
});
}
}
/**
* Compare the URL's and determines which assets are no longer required
* in the cache.
*
* This should be called in the service worker activate event.
*
* @return {Promise} Promise that resolves once the cache entries have been
* cleaned.
*/
async cleanup() {
if (!await caches.has(this._cacheName)) {
// Cache doesn't exist, so nothing to delete
return;
}
const requestsCachedOnInstall = [];
this._entriesToCache.forEach((entry) => {
requestsCachedOnInstall.push(entry.request.url);
});
const openCache = await this._getCache();
const allCachedRequests = await openCache.keys();
const cachedRequestsToDelete = allCachedRequests.filter((cachedRequest) => {
if (requestsCachedOnInstall.includes(cachedRequest.url)) {
return false;
}
return true;
});
return Promise.all(
cachedRequestsToDelete.map((cachedRequest) => {
return openCache.delete(cachedRequest);
})
);
}
/**
* A simple helper method to get the open cache used for precaching assets.
*
* @private
* @return {Promise&lt;Cache>} The cache to be used for precaching.
*/
async _getCache() {
if (!this._cache) {
this._cache = await caches.open(this._cacheName);
}
return this._cache;
}
/**
* Ensures the file entry in the maniest is valid and
* can be parsed as a BaseCacheEntry.
*
* @private
* @abstract
* @param {String | Request | Object} input Either a URL string, a Request
* or an object with a `url`, `revision` and optional `cacheBust` parameter.
* @return {BaseCacheEntry} Returns a parsed version of the file entry.
*/
_parseEntry(input) {
throw ErrorFactory.createError('should-override');
}
/**
* Called in case subclasses have cache entries that are to be installed
* but have the same "entryID".
* This means that the user is trying to cache the same thing twice.
* Subclasses can use this method to throw an error if there is an edge
* case that can't be handled.
*
* @private
* @abstract
* @param {BaseCacheEntry} newEntry The entry that is to be cached.
* @param {BaseCacheEntry} previous The entry that is currently cached.
*/
_onDuplicateEntryFound(newEntry, previous) {
throw ErrorFactory.createError('should-override');
}
/**
* Confirms whether a fileEntry is already in the cache with the
* appropriate revision or not.
*
* @private
* @abstract
* @param {BaseCacheEntry} precacheEntry A file entry with `path` and
* `revision` parameters.
* @return {Promise&lt;Boolean>} Returns true is the fileEntry is already
* cached, false otherwise.
*/
_isAlreadyCached(precacheEntry) {
throw ErrorFactory.createError('should-override');
}
/**
* Subclasses can use this method for any work that needs to be done once a
* URL has been cached.
*
* @private
* @abstract
* @param {BaseCacheEntry} precacheEntry A file entry with `path` and
* `revision` parameters.
* @return {Promise} Returns a Promise that resolves once it's work has
* been done.
*/
_onEntryCached(precacheEntry) {
throw ErrorFactory.createError('should-override');
}
}
export default BaseCacheManager;
</code></pre>
</article>
</section>
@@ -0,0 +1,205 @@
---
layout: jsdoc
title: "Source: sw-precaching/src/lib/controllers/revisioned-cache-manager.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>import ErrorFactory from '../error-factory';
import BaseCacheManager from './base-cache-manager';
import RevisionDetailsModel from '../models/revision-details-model';
import {defaultRevisionedCacheName} from '../constants';
import StringPrecacheEntry from
'../models/precache-entries/string-precache-entry';
import ObjectPrecacheEntry from
'../models/precache-entries/object-precache-entry';
import assert from '../../../../../lib/assert';
/**
* This class extends a lot of the internal methods from BaseCacheManager
* to manage caching of revisioned assets.
*
* @private
* @memberof module:sw-precaching
* @extends {module:sw-precaching.BaseCacheManager}
*/
class RevisionedCacheManager extends BaseCacheManager {
/**
* Constructor for RevisionedCacheManager
* @param {Object} input
* @param {String} [input.cacheName] Define the cache used to stash these
* entries.
*/
constructor({cacheName} = {}) {
cacheName = cacheName || defaultRevisionedCacheName;
super(cacheName);
this._revisionDetailsModel = new RevisionDetailsModel();
}
/**
* This method will add the entries to the install list.
* This will manage duplicate entries and perform the caching during
* the install step.
*
* @example
*
* revisionedManager.addToCacheList({
* revisionedFiles: [
* '/styles/hello.1234.css',
* {
* url: '/images/logo.png',
* revision: '1234'
* }
* ]
* });
*
* @param {Array&lt;String|Object>} rawEntries A raw entry that can be
* parsed into a BaseCacheEntry.
*/
addToCacheList({revisionedFiles} = {}) {
assert.isInstance({revisionedFiles}, Array);
super._addEntries(revisionedFiles);
}
/**
* This method ensures that the file entry in the maniest is valid and
* can be parsed as a BaseCacheEntry.
*
* @private
* @abstract
* @param {String | Object} input Either a URL string
* or an object with a `url`, `revision` and optional `cacheBust` parameter.
* @return {BaseCacheEntry} Returns a parsed version of the file entry.
*/
_parseEntry(input) {
if (typeof input === 'undefined' || input === null) {
throw ErrorFactory.createError('invalid-revisioned-entry',
new Error('Invalid file entry: ' + JSON.stringify(input))
);
}
let precacheEntry;
switch(typeof input) {
case 'string':
precacheEntry = new StringPrecacheEntry(input);
break;
case 'object':
precacheEntry = new ObjectPrecacheEntry(input);
break;
default:
throw ErrorFactory.createError('invalid-revisioned-entry',
new Error('Invalid file entry: ' +
JSON.stringify(precacheEntry))
);
}
return precacheEntry;
}
/**
* If a dupe entry exists, check the revision. If the revisions are the same
* it's simply a duplicate entry. If they are different, we have two
* identical requests with two different revisions which will put this
* module into a bad state.
*
* @private
* @param {BaseCacheEntry} newEntry The entry that is to be cached.
* @param {BaseCacheEntry} previousEntry The entry that is currently cached.
*/
_onDuplicateInstallEntryFound(newEntry, previousEntry) {
if (previousEntry.revision !== newEntry.revision) {
throw ErrorFactory.createError(
'duplicate-entry-diff-revisions',
new Error(`${JSON.stringify(previousEntry)} &lt;=> ` +
`${JSON.stringify(newEntry)}`));
}
}
/**
* This method confirms with a precacheEntry is already in the cache with the
* appropriate revision.
* If the revision is known, the requested `precacheEntry.revision` is saved
* and the cache entry exists for the `precacheEntry.path` this method
* will return true.
*
* @private
* @param {BaseCacheEntry} precacheEntry A entry with `path` and `revision`
* parameters.
* @return {Promise&lt;Boolean>} Returns true if the precacheEntry is already
* cached, false otherwise.
*/
async _isAlreadyCached(precacheEntry) {
const revisionDetails = await
this._revisionDetailsModel.get(precacheEntry.entryID);
if (revisionDetails !== precacheEntry.revision) {
return false;
}
const openCache = await this._getCache();
const cachedResponse = await openCache.match(precacheEntry.request);
return cachedResponse ? true : false;
}
/**
* @private
* @param {BaseCacheEntry} precacheEntry A file entry with `path` and
* `revision` parameters.
*/
async _onEntryCached(precacheEntry) {
await this._revisionDetailsModel.put(
precacheEntry.entryID, precacheEntry.revision);
}
/**
* This method closes the indexdDB helper. This is used for unit testing
* to ensure cleanup between tests.
* @private
*/
_close() {
this._revisionDetailsModel._close();
}
/**
* Compare the URL's and determines which assets are no longer required
* in the cache.
*
* This should be called in the service worker activate event.
*
* @return {Promise} Promise that resolves once the cache entries have been
* cleaned.
*/
cleanup() {
return super.cleanup()
.then(() => {
return this._close();
});
}
}
export default RevisionedCacheManager;
</code></pre>
</article>
</section>
@@ -0,0 +1,142 @@
---
layout: jsdoc
title: "Source: sw-precaching/src/lib/controllers/unrevisioned-cache-manager.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>import ErrorFactory from '../error-factory';
import BaseCacheManager from './base-cache-manager';
import RequestCacheEntry from '../models/precache-entries/request-entry';
import StringPrecacheEntry from
'../models/precache-entries/string-precache-entry';
import assert from '../../../../../lib/assert';
/**
* This class extends a lot of the internal methods from BaseCacheManager
* to manage caching of unrevisioned assets.
*
* @private
* @memberof module:sw-precaching
* @extends {module:sw-precaching.BaseCacheManager}
*/
class UnrevisionedCacheManager extends BaseCacheManager {
/**
* Constructor for UnreivisionedCacheManager
* @param {Object} input
* @param {String} [input.cacheName] Define the cache used to stash these
* entries.
*/
constructor({cacheName} = {}) {
super(cacheName);
}
/**
* This method will add the entries to the install list.
* This will manage duplicate entries and perform the caching during
* the install step.
*
* @example
*
* unrevisionedManager.addToCacheList({
* unrevisionedFiles: [
* '/styles/hello.css',
* new Request('/images/logo.png', {
* // Custom Request Options.
* })
* ]
* ]});
*
* @param {Array&lt;String|Request>} rawEntries A raw entry that can be
* parsed into a BaseCacheEntry.
*/
addToCacheList({unrevisionedFiles} = {}) {
assert.isInstance({unrevisionedFiles}, Array);
super._addEntries(unrevisionedFiles);
}
/**
* This method ensures that the file entry in the maniest is valid and
* can be parsed as a BaseCacheEntry.
*
* @private
* @abstract
* @param {String | Request} input Either a URL string or a Request.
* @return {BaseCacheEntry} Returns a parsed version of the file entry.
*/
_parseEntry(input) {
if (typeof input === 'undefined' || input === null) {
throw ErrorFactory.createError('invalid-unrevisioned-entry',
new Error('Invalid file entry: ' + JSON.stringify(input)));
}
if (typeof input === 'string') {
return new StringPrecacheEntry(input);
} else if (input instanceof Request) {
return new RequestCacheEntry(input);
} else {
throw ErrorFactory.createError('invalid-unrevisioned-entry',
new Error('Invalid file entry: ' +
JSON.stringify(input))
);
}
}
/**
* @private
* @param {BaseCacheEntry} newEntry The entry that is to be cached.
* @param {BaseCacheEntry} previousEntry The entry that is currently cached.
*/
_onDuplicateInstallEntryFound(newEntry, previousEntry) {
// NOOP. Just ignore duplicate entries.
}
/**
* We always want to override previously cached versions.
* @private
* @param {BaseCacheEntry} precacheEntry A file entry with `path` and
* `revision` parameters.
* @return {Promise&lt;Boolean>} Returns true is the fileEntry is already
* cached, false otherwise.
*/
async _isAlreadyCached(precacheEntry) {
return false;
}
/**
* @private
* @param {BaseCacheEntry} precacheEntry A file entry with `path` and
* `revision` parameters.
*/
_onEntryCached(precacheEntry) {
// NOOP
}
}
export default UnrevisionedCacheManager;
</code></pre>
</article>
</section>
@@ -0,0 +1,101 @@
---
layout: jsdoc
title: "Source: sw-precaching/src/lib/models/precache-entries/base-precache-entry.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>import {cacheBustParamName} from '../../constants';
/**
* This class is extended by a number of classes that take different inputs
* and generates the required fields for a BaseCacheEntry.
*
* @private
* @memberof module:sw-precaching
*/
class BaseCacheEntry {
/**
* This constructor expects an object and a number or required fields.
* You shouldn't need to use this constructor directly.
*
* @param {Object} input
* @param {String} input.entryID
* @param {String} input.revision
* @param {Request} input.request
* @param {boolean} input.cacheBust
*/
constructor({entryID, revision, request, cacheBust}) {
this.entryID = entryID;
this.revision = revision;
this.request = request;
this.cacheBust = cacheBust;
}
/**
* This method is required since any revisioned request needs to cache bust.
* To ensure this is consistent, CacheManagers will make a network request
* using this specially formatted request.
*
* When caching the response, it will be cached against the origin `request`,
* removing lookup for the cachebusted URL.
*
* @return {Request} Returns a cache busted request if needed, otherwise
* a normal request with credentials set to 'same-origin' and redirect set to
* follow.
*/
getNetworkRequest() {
if (this.cacheBust !== true) {
// For the RequestCacheEntry we should return it to ensure headers are
// kept in tact and part of the request.
return this.request;
}
let url = this.request.url;
const requestOptions = {};
if (this.cacheBust === true) {
if ('cache' in Request.prototype) {
// Make use of the Request cache mode where we can.
// Reload skips the HTTP cache for outgoing requests and updates
// the cache with the returned reponse.
requestOptions.cache = 'reload';
} else {
const parsedURL = new URL(url, location);
parsedURL.search += (parsedURL.search ? '&amp;' : '') +
encodeURIComponent(cacheBustParamName) + '=' +
encodeURIComponent(this.revision);
url = parsedURL.toString();
}
}
return new Request(url, requestOptions);
}
}
export default BaseCacheEntry;
</code></pre>
</article>
</section>
@@ -0,0 +1,83 @@
---
layout: jsdoc
title: "Source: sw-precaching/src/lib/models/revision-details-model.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>import IDBHelper from '../../../../../lib/idb-helper.js';
import {dbName, dbVersion, dbStorename} from '../constants';
/**
* This class is a simple model that stores EntryID's with their current
* revision. This is used when caching revisioned assets so that only entries
* with different revisions are downloaded and updated.
*
* @private
* @memberof module:sw-precaching
*/
class RevisionDetailsModel {
/**
* Constructor for RevisionDetails Model.
*/
constructor() {
this._idbHelper = new IDBHelper(dbName, dbVersion, dbStorename);
}
/**
* This method gets the revision details for a given entryID.
* @param {String} entryID The ID of the revision.
* @return {Promise&lt;String|null>} Returns a revision string or
* null if there is no revision information.
*/
get(entryID) {
return this._idbHelper.get(entryID);
}
/**
* This method saves the revision details to indexedDB.
* @param {String} entryID The ID of the revision.
* @param {String} revision The current revision for this entryID.
* @return {Promise} Promise that resolves once the data has been saved.
*/
put(entryID, revision) {
return this._idbHelper.put(entryID, revision);
}
/**
* This method closes the indexdDB helper. This is only used for unit testing
* to ensure clean state between tests.
*
* @private
*/
_close() {
this._idbHelper.close();
}
}
export default RevisionDetailsModel;
</code></pre>
</article>
</section>
@@ -0,0 +1,68 @@
---
layout: jsdoc
title: "Source: sw-routing/src/index.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* # sw-routing
*
* A service worker helper library to route request URLs to handlers.
*
* @module sw-routing
*/
import ExpressRoute from './lib/express-route';
import NavigationRoute from './lib/navigation-route';
import RegExpRoute from './lib/regexp-route';
import Route from './lib/route';
import Router from './lib/router';
export {
ExpressRoute,
NavigationRoute,
RegExpRoute,
Route,
Router,
};
</code></pre>
</article>
</section>
@@ -0,0 +1,138 @@
---
layout: jsdoc
title: "Source: sw-routing/src/lib/express-route.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import ErrorFactory from './error-factory';
import Route from './route';
import pathToRegExp from 'path-to-regexp';
/**
* `ExpressRoute` is a helper class to make defining Express-style
* [Routes]{@link Route} easy.
*
* Under the hood, it uses the [`path-to-regexp`](https://www.npmjs.com/package/path-to-regexp)
* library to transform the `path` parameter into a regular expression, which is
* then matched against the URL's path.
*
* Please note that `ExpressRoute` can only match requests for URLs that are on
* the same-origin as the current page. If you need to match cross-origin
* requests, you can use either a generic [`Route`]{@link Route} or a
* [`RegExpRoute`]{@link RegExpRoute}.
*
* @example
* // Any same-origin requests that start with /path/to and end with one
* // additional path segment will match this route, with the last path
* // segment passed along to the handler via params.file.
* const route = new goog.routing.ExpressRoute({
* path: '/path/to/:file',
* handler: {
* handle: ({event, params}) => {
* // params.file will be set based on the request URL that matched.
* // Do something that returns a Promise.&lt;Response>, like:
* return caches.match(event.request);
* },
* },
* });
*
* const router = new goog.routing.Router();
* router.registerRoute({route});
*
* @memberof module:sw-routing
* @extends Route
*/
class ExpressRoute extends Route {
/**
* Constructor for ExpressRoute.
*
* @param {Object} input
* @param {string} input.path The path to use for routing.
* If the path contains [named parameters](https://github.com/pillarjs/path-to-regexp#named-parameters),
* then an Object mapping parameter names to the corresponding value
* will be passed to the handler via `params`.
* @param {Object} input.handler - An Object with a `handle` method that
* will be used to respond to matching requests.
* @param {string} [input.method] Only match requests that use this
* HTTP method. Defaults to `'GET'` if not specified.
*/
constructor({path, handler, method}) {
if (path.substring(0, 1) !== '/') {
throw ErrorFactory.createError('express-route-requires-absolute-path');
}
let keys = [];
// keys is populated as a side effect of pathToRegExp. This isn't the nicest
// API, but so it goes.
// https://github.com/pillarjs/path-to-regexp#usage
const regExp = pathToRegExp(path, keys);
const match = ({url}) => {
// Return null immediately if we have a cross-origin request.
if (url.origin !== location.origin) {
return null;
}
const regexpMatches = url.pathname.match(regExp);
// Return null immediately if this route doesn't match.
if (!regexpMatches) {
return null;
}
// If the route does match, then collect values for all the named
// parameters that were returned in keys.
// If there are no named parameters then this will end up returning {},
// which is truthy, and therefore a sufficient return value.
const namedParamsToValues = {};
keys.forEach((key, index) => {
namedParamsToValues[key.name] = regexpMatches[index + 1];
});
return namedParamsToValues;
};
super({match, handler, method});
}
}
export default ExpressRoute;
</code></pre>
</article>
</section>
@@ -0,0 +1,136 @@
---
layout: jsdoc
title: "Source: sw-routing/src/lib/navigation-route.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Route from './route';
import assert from '../../../../lib/assert';
import logHelper from '../../../../lib/log-helper';
/**
* NavigationRoute is a helper class to create a [`Route`]{@link Route}
* that matches browser navigation requests, i.e. requests for a page.
*
* It will only match incoming requests whose [`mode`](https://fetch.spec.whatwg.org/#concept-request-mode)
* is set to `navigate`.
*
* You can optionally only apply this route to a subset of navigation requests
* by using one or both of the `blacklist` and `whitelist` parameters. If
* both lists are provided, and there's a navigation to a URL which matches
* both, then the blacklist will take precedence and the request will not be
* matched by this route. The regular expressions in `whitelist` and `blacklist`
* are matched against the [`pathname`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLHyperlinkElementUtils/pathname)
* portion of the requested URL.
*
* To match all navigations, use a `whitelist` array containing a RegExp that
* matches everything, i.e. `[/./]`.
*
* @memberof module:sw-routing
* @extends Route
*
* @example
* // Any navigation requests that match the whitelist (i.e. URLs whose path
* // starts with /article/) will be handled with the cache entry for
* // app-shell.html.
* const route = new goog.routing.NavigationRoute({
* whitelist: [new RegExp('^/article/')],
* handler: {handle: () => caches.match('app-shell.html')},
* });
*
* const router = new goog.routing.Router();
* router.registerRoute({route});
*/
class NavigationRoute extends Route {
/**
* Constructor for NavigationRoute.
*
* @param {Object} input
* @param {Array&lt;RegExp>} input.whitelist If any of these patterns match,
* the route will handle the request (assuming the blacklist doesn't match).
* @param {Array&lt;RegExp>} [input.blacklist] If any of these patterns match,
* the route will not handle the request (even if a whitelist entry matches).
* @param {function} input.handler The handler to manage the response.
*/
constructor({whitelist, blacklist, handler} = {}) {
assert.isArrayOfClass({whitelist}, RegExp);
if (blacklist) {
assert.isArrayOfClass({blacklist}, RegExp);
} else {
blacklist = [];
}
const match = ({event, url}) => {
let matched = false;
let message;
if (event.request.mode === 'navigate') {
if (whitelist.some((regExp) => regExp.test(url.pathname))) {
if (blacklist.some((regExp) => regExp.test(url.pathname))) {
message = `The navigation route is not being used, since the ` +
`request URL matches both the whitelist and blacklist.`;
} else {
message = `The navigation route is being used.`;
matched = true;
}
} else {
message = `The navigation route is not being used, since the ` +
`URL being navigated to doesn't match the whitelist.`;
}
logHelper.debug({
that: this,
message,
data: {'request-url': url.href, whitelist, blacklist, handler},
});
}
return matched;
};
super({match, handler, method: 'GET'});
}
}
export default NavigationRoute;
</code></pre>
</article>
</section>
@@ -0,0 +1,114 @@
---
layout: jsdoc
title: "Source: sw-routing/src/lib/regexp-route.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Route from './route';
import assert from '../../../../lib/assert';
/**
* RegExpRoute is a helper class to make defining regular expression based
* [Routes]{@link Route} easy.
*
* `RegExpRoute` performs its matches against the full request URL, including
* the origin. This means that, unlike [`ExpressRoute`]{@link ExpressRoute},
* it's able to match cross-origin requests.
*
* @memberof module:sw-routing
* @extends Route
*
* @example
* // Any requests that match the regular expression will match this route, with
* // the capture groups passed along to the handler as an array via params.
* const route = new goog.routing.RegExpRoute({
* regExp: new RegExp('^https://example.com/path/to/(\\w+)'),
* handler: {
* handle: ({event, params}) => {
* // params[0], etc. will be set based on the regexp capture groups.
* // Do something that returns a Promise.&lt;Response>, like:
* return caches.match(event.request);
* },
* },
* });
*
* const router = new goog.routing.Router();
* router.registerRoute({route});
*/
class RegExpRoute extends Route {
/**
* Constructor for RegExpRoute.
*
* @param {Object} input
* @param {RegExp} input.regExp The regular expression to match against URLs.
* If the `RegExp` contains [capture groups](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#grouping-back-references),
* then the array of captured values will be passed to the handler via
* `params`.
* @param {function} input.handler The handler to manage the response.
* @param {string} [input.method] Only match requests that use this
* HTTP method. Defaults to `'GET'` if not specified.
*/
constructor({regExp, handler, method}) {
assert.isInstance({regExp}, RegExp);
const match = ({url}) => {
const regexpMatches = url.href.match(regExp);
// Return null immediately if this route doesn't match.
if (!regexpMatches) {
return null;
}
// If the route matches, but there aren't any capture groups defined, then
// this will return [], which is truthy and therefore sufficient to
// indicate a match.
// If there are capture groups, then it will return their values.
return regexpMatches.slice(1);
};
super({match, handler, method});
}
}
export default RegExpRoute;
</code></pre>
</article>
</section>
@@ -0,0 +1,135 @@
---
layout: jsdoc
title: "Source: sw-routing/src/lib/route.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import assert from '../../../../lib/assert';
import {defaultMethod, validMethods} from './constants';
/**
* A `Route` allows you to tell a service worker that it should handle
* certain network requests using a specific response strategy.
*
* Two configuration options are required:
*
* - A `match` function, which examines
* an incoming [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* to determine whether this `Route` should apply. The function should return
* a [truthy](https://developer.mozilla.org/en-US/docs/Glossary/Truthy) value
* if the `Route` matches, in which case that return value is passed along to
* the `handle` function.
* - A `handler` object, which should in turn have a function defined on it
* named `handle`. This `handle` function is given the incoming request along
* with any additional parameters generated during the `match`, and returns a
* Promise for a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
*
* Instead of implementing your own `handler`, you can use one of the
* pre-defined runtime caching strategies from the
* {@link module:sw-runtime-caching|sw-runtime-caching} module.
*
* While you can use `Route` directly, the [`RegExpRoute`]{@link RegExpRoute}
* and [`ExpressRoute`]{@link ExpressRoute} subclasses provide a convenient
* wrapper with a nicer interface for using regular expressions or Express-style
* routes as the `match` criteria.
*
* @example
* // Any navigate requests for URLs that start with /path/to/ will match.
* const route = new goog.routing.Route({
* match: ({url, event}) => {
* return event.request.mode === 'navigate' &amp;&amp;
* url.pathname.startsWith('/path/to/');
* },
* handler: ({event}) => {
* // Do something that returns a Promise.&lt;Response>, like:
* return caches.match(event.request);
* },
* });
*
* const router = new goog.routing.Router();
* router.registerRoute({route});
*
* @memberof module:sw-routing
*/
class Route {
/**
* Constructor for Route class.
* @param {Object} input
* @param {function} input.match The function that determines whether the
* route matches. The function is passed an object with two properties:
* `url`, which is a [URL](https://developer.mozilla.org/en-US/docs/Web/API/URL),
* and `event`, which is a [FetchEvent](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent).
* `match` should return a truthy value when the route applies, and
* that value is passed on to the handle function.
* @param {function|Object} input.handler A function, or an Object with a
* `handle` method. As parameters, the handler is passed an object with
* the same `url` and `event` properties as `match` received, along
* with an additional property, `params`, set to the truthy value
* `match` returned.
* @param {string} [input.method] Only match requests that use this
* HTTP method. Defaults to `'GET'` if not specified.
*/
constructor({match, handler, method} = {}) {
assert.isType({match}, 'function');
if (typeof handler === 'object') {
assert.hasMethod({handler}, 'handle');
} else {
assert.isType({handler}, 'function');
}
this.match = match;
// Normalize things so that even if the handler is a function, this.handler
// gets set to an object with a handle method.
this.handler = typeof handler === 'function' ? {handle: handler} : handler;
if (method) {
assert.isOneOf({method}, validMethods);
this.method = method;
} else {
this.method = defaultMethod;
}
}
}
export default Route;
</code></pre>
</article>
</section>
@@ -0,0 +1,252 @@
---
layout: jsdoc
title: "Source: sw-routing/src/lib/router.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Route from './route';
import assert from '../../../../lib/assert';
import logHelper from '../../../../lib/log-helper.js';
/**
* The Router takes one or more [Routes]{@link Route} and registers a [`fetch`
* event listener](https://developer.mozilla.org/en-US/docs/Web/API/FetchEvent)
* that will respond to network requests if there's a matching route.
*
* It also allows you to define a "default" handler that applies to any requests
* that don't explicitly match a `Route`, and a "catch" handler that responds
* to any requests that throw an exception while being routed.
*
* @memberof module:sw-routing
*
* @example
* // The following example sets up two routes, one to match requests with
* // "assets" in their URL, and the other for requests with "images", along
* // different runtime caching handlers for each.
* // Both the routes are registered with the router, and any requests that
* // don't match either route will be handled using the default NetworkFirst
* // strategy.
* const assetRoute = new RegExpRoute({
* regExp: /assets/,
* handler: new goog.runtimeCaching.StaleWhileRevalidate(),
* });
* const imageRoute = new RegExpRoute({
* regExp: /images/,
* handler: new goog.runtimeCaching.CacheFirst(),
* });
*
* const router = new goog.routing.Router();
* router.registerRoutes({routes: [assetRoute, imageRoute]});
* router.setDefaultHandler({handler: new goog.runtimeCaching.NetworkFirst()});
*/
class Router {
/**
* Start with an empty array of routes, and set up the fetch handler.
*/
constructor() {
// _routes will contain a mapping of HTTP method name ('GET', etc.) to an
// array of all the corresponding Route instances that are registered.
this._routes = new Map();
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
if (!url.protocol.startsWith('http')) {
logHelper.log({
that: this,
message: 'URL does not start with HTTP and so not passing ' +
'through the router.',
data: {
request: event.request,
},
});
return;
}
let responsePromise;
let matchingRoute;
for (let route of (this._routes.get(event.request.method) || [])) {
const matchResult = route.match({url, event});
if (matchResult) {
matchingRoute = route;
logHelper.log({
that: this,
message: 'The router is founda matching route.',
data: {
route: matchingRoute,
request: event.request,
},
});
let params = matchResult;
if (Array.isArray(params) &amp;&amp; params.length === 0) {
// Instead of passing an empty array in as params, use undefined.
params = undefined;
} else if (params.constructor === Object &amp;&amp;
Object.keys(params).length === 0) {
// Instead of passing an empty object in as params, use undefined.
params = undefined;
}
responsePromise = route.handler.handle({url, event, params});
break;
}
}
if (!responsePromise &amp;&amp; this.defaultHandler) {
responsePromise = this.defaultHandler.handle({url, event});
}
if (responsePromise &amp;&amp; this.catchHandler) {
responsePromise = responsePromise.catch((error) => {
return this.catchHandler.handle({url, event, error});
});
}
if (responsePromise) {
event.respondWith(responsePromise
.then((response) => {
logHelper.debug({
that: this,
message: 'The router is managing a route with a response.',
data: {
route: matchingRoute,
request: event.request,
response: response,
},
});
return response;
}));
}
});
}
/**
* An optional default handler will have its handle method called when a
* request doesn't have a matching route.
*
* @example
* router.setDefaultHandler({
* handler: new goog.runtimeCaching.NetworkFirst()
* });
*
* @param {Object} input
* @param {Object} input.handler An Object with a `handle` method.
*/
setDefaultHandler({handler} = {}) {
assert.hasMethod({handler}, 'handle');
this.defaultHandler = handler;
}
/**
* If a Route throws an error while handling a request, this catch handler
* will be called to return an error case.
*
* @example
* router.setCatchHandler({
* handler: ({event, params}) => {
* return caches.match('/error-page.html');
* }
* });
*
* @param {Object} input
* @param {Object} input.handler An Object with a `handle` method.
*/
setCatchHandler({handler} = {}) {
assert.hasMethod({handler}, 'handle');
this.catchHandler = handler;
}
/**
* Register routes will take an array of Routes to register with the
* router.
*
* @example
* router.registerRoutes({
* routes: [
* new RegExpRoute({ ... }),
* new ExpressRoute({ ... }),
* new Route({ ... }),
* ]
* });
*
* @param {Object} input
* @param {Array.&lt;Route>} input.routes An array of routes to register.
*/
registerRoutes({routes} = {}) {
assert.isArrayOfClass({routes}, Route);
for (let route of routes) {
if (!this._routes.has(route.method)) {
this._routes.set(route.method, []);
}
// Give precedence to the most recent route by listing it first.
this._routes.get(route.method).unshift(route);
}
}
/**
* Registers a single route with the router.
*
* @example
* router.registerRoutes({
* route: new Route({ ... })
* });
*
* @param {Object} input
* @param {Route} input.route The route to register.
*/
registerRoute({route} = {}) {
assert.isInstance({route}, Route);
this.registerRoutes({routes: [route]});
}
}
export default Router;
</code></pre>
</article>
</section>
@@ -0,0 +1,79 @@
---
layout: jsdoc
title: "Source: sw-runtime-caching/src/index.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* # sw-runtime-caching
*
* A service worker helper library that implements various runtime caching
* strategies.
*
* You can learn more about each caching strategy on
* {@link https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/|Jake Archibald's blog post}
* which covers various ways of handling fetch events with a service worker.
*
* @module sw-runtime-caching
*/
import RequestWrapper from './lib/request-wrapper';
import CacheFirst from './lib/cache-first';
import CacheOnly from './lib/cache-only';
import Handler from './lib/handler';
import NetworkFirst from './lib/network-first';
import NetworkOnly from './lib/network-only';
import StaleWhileRevalidate from './lib/stale-while-revalidate';
import {defaultCacheName} from './lib/constants';
export {
CacheFirst,
CacheOnly,
Handler,
NetworkFirst,
NetworkOnly,
RequestWrapper,
StaleWhileRevalidate,
defaultCacheName,
};
</code></pre>
</article>
</section>
@@ -0,0 +1,98 @@
---
layout: jsdoc
title: "Source: sw-runtime-caching/src/lib/cache-first.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Handler from './handler';
import assert from '../../../../lib/assert';
/**
* An implementation of a [cache-first](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#cache-falling-back-to-network)
* request strategy.
*
* The important thing to note with this caching strategy is that once a
* response is cached, it will not be updated. This is useful for assets
* that are revisioned as it caches the asset long term and doesn't waste
* the user's data.
*
* @example
* // Set up a route to match any requests made for URLs that end in .txt.
* // The requests are handled with a cache-first strategy.
* const route = new goog.routing.RegExpRoute({
* regExp: /\.txt$/,
* handler: new goog.runtimeCaching.CacheFirst(),
* });
*
* const router = new goog.routing.Router();
* router.registerRoute({route});
*
* @memberof module:sw-runtime-caching
* @extends Handler
*/
class CacheFirst extends Handler {
/**
* The handle method will be called by the
* {@link module:sw-routing.Route|Route} class when a route matches a request.
*
* @param {Object} input
* @param {FetchEvent} input.event The event that triggered the service
* worker's fetch handler.
* @return {Promise.&lt;Response>} The response, either from the cache,
* or if that isn't available, from the network.
*/
async handle({event} = {}) {
assert.isInstance({event}, FetchEvent);
const cachedResponse = await this.requestWrapper.match({
request: event.request,
});
return cachedResponse || await this.requestWrapper.fetchAndCache({
request: event.request,
});
}
}
export default CacheFirst;
</code></pre>
</article>
</section>
@@ -0,0 +1,90 @@
---
layout: jsdoc
title: "Source: sw-runtime-caching/src/lib/cache-only.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Handler from './handler';
import assert from '../../../../lib/assert';
/**
* An implementation of a [cache-only](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#cache-only)
* request strategy.
*
* The advantage to using this versus directly calling `caches.match()` is that
* it will use the cache configuration and trigger the plugins defined in
* the underlying `RequestWrapper`.
*
* @example
* // Set up a route to match any requests made for URLs that end in .txt.
* // The requests are handled with a cache-only strategy.
* const route = new goog.routing.RegExpRoute({
* regExp: /\.txt$/,
* handler: new goog.runtimeCaching.CacheOnly(),
* });
*
* const router = new goog.routing.Router();
* router.registerRoute({route});
*
* @memberof module:sw-runtime-caching
* @extends Handler
*/
class CacheOnly extends Handler {
/**
* The handle method will be called by the
* {@link module:sw-routing.Route|Route} class when a route matches a request.
*
* @param {Object} input
* @param {FetchEvent} input.event The event that triggered the service
* worker's fetch handler.
* @return {Promise.&lt;Response>} The response from the cache.
*/
async handle({event} = {}) {
assert.isInstance({event}, FetchEvent);
return await this.requestWrapper.match({request: event.request});
}
}
export default CacheOnly;
</code></pre>
</article>
</section>
@@ -0,0 +1,79 @@
---
layout: jsdoc
title: "Source: sw-runtime-caching/src/lib/constants.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* The default cache name, used by
* {@link module:sw-runtime-caching.RequestWrapper|RequestWrapper} when there's
* no name provided.
*
* It combines a constant prefix with the `registration.scope` value associated
* with the current service worker, ensuring that multiple service workers used
* on the same origin will have different default caches.
*
* @type {string}
* @memberof module:sw-runtime-caching
*/
let cacheName = `sw-runtime-caching`;
if (self &amp;&amp; self.registration) {
cacheName += `-${self.registration.scope}`;
}
export const defaultCacheName = cacheName;
/**
* A list of the callback method names that the RequestWrapper might trigger.
*
* @private
* @type {Array.&lt;string>}
* @memberof module:sw-runtime-caching
*/
export const pluginCallbacks = [
'cacheDidUpdate',
'cacheWillMatch',
'cacheWillUpdate',
'fetchDidFail',
'requestWillFetch',
];
</code></pre>
</article>
</section>
@@ -0,0 +1,92 @@
---
layout: jsdoc
title: "Source: sw-runtime-caching/src/lib/handler.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import RequestWrapper from './request-wrapper';
/**
* This a base class which each caching strategy extends.
*
* @memberof module:sw-runtime-caching
*/
class Handler {
/**
* Constructor for a new Handler instance.
*
* @param {Object} input
* @param {RequestWrapper} [input.requestWrapper] An optional `RequestWrapper`
* that is used to configure the cache name and request plugins. If
* not provided, a new `RequestWrapper` using the
* [default cache name](#defaultCacheName) will be used.
*/
constructor({requestWrapper} = {}) {
if (requestWrapper) {
this.requestWrapper = requestWrapper;
} else {
this.requestWrapper = new RequestWrapper();
}
}
/**
* An abstract method that each subclass must implement.
*
* @abstract
* @param {Object} input
* @param {FetchEvent} input.event The event that triggered the service
* worker's fetch handler.
* @param {Object} [input.params] Additional parameters that might be passed
* in to the method. If used in conjunction with the
* {@link module:sw-routing.Route|Route} class, then the return value
* from the `match` function in the Route constructor
* will be passed in as the `params` value.
* @return {Promise.&lt;Response>} A promise resolving with a response.
*/
handle({event, params} = {}) {
throw Error('This abstract method must be implemented in a subclass.');
}
}
export default Handler;
</code></pre>
</article>
</section>
@@ -0,0 +1,136 @@
---
layout: jsdoc
title: "Source: sw-runtime-caching/src/lib/network-first.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Handler from './handler';
import assert from '../../../../lib/assert';
/**
* An implementation of a [network first](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-falling-back-to-cache)
* request strategy.
*
* @example
* // Set up a route to match any requests made for URLs that end in .txt.
* // The requests are handled with a network-first strategy.
* const route = new goog.routing.RegExpRoute({
* regExp: /\.txt$/,
* handler: new goog.runtimeCaching.NetworkFirst(),
* });
*
* const router = new goog.routing.Router();
* router.registerRoute({route});
*
* @memberof module:sw-runtime-caching
* @extends Handler
*/
class NetworkFirst extends Handler {
/**
* Constructor for a new Handler instance.
*
* @param {Object} input
* @param {number} [input.networkTimeoutSeconds] If set, and a network
* response isn't returned timeout is reached, then the cached response
* will be returned instead. If there is no previously cached response,
* then an `null` response will be returned. This option is meant to
* combat "[lie-fi](https://developers.google.com/web/fundamentals/performance/poor-connectivity/#lie-fi)"
* scenarios.
* @param {RequestWrapper} [input.requestWrapper] An optional `RequestWrapper`
* that is used to configure the cache name and request plugins. If
* not provided, a new `RequestWrapper` using the
* [default cache name](#defaultCacheName) will be used.
*/
constructor(input = {}) {
super(input);
const {networkTimeoutSeconds} = input;
if (networkTimeoutSeconds) {
assert.isType({networkTimeoutSeconds}, 'number');
this.networkTimeoutSeconds = networkTimeoutSeconds;
}
}
/**
* The handle method will be called by the
* {@link module:sw-routing.Route|Route} class when a route matches a request.
*
* @param {Object} input
* @param {FetchEvent} input.event The event that triggered the service
* worker's fetch handler.
* @return {Promise.&lt;Response>} The response from the network, or if that's
* not available, a previously cached response.
*/
async handle({event} = {}) {
assert.isInstance({event}, FetchEvent);
const promises = [];
let timeoutId;
if (this.networkTimeoutSeconds) {
promises.push(new Promise((resolve) => {
timeoutId = setTimeout(() => {
resolve(this.requestWrapper.match({request: event.request}));
}, this.networkTimeoutSeconds * 1000);
}));
}
promises.push(this.requestWrapper.fetchAndCache({request: event.request})
.then((response) => {
if (timeoutId) {
clearTimeout(timeoutId);
}
return response ?
response :
Promise.reject('No response received; falling back to cache.');
})
.catch(() => this.requestWrapper.match({request: event.request}))
);
return Promise.race(promises);
}
}
export default NetworkFirst;
</code></pre>
</article>
</section>
@@ -0,0 +1,90 @@
---
layout: jsdoc
title: "Source: sw-runtime-caching/src/lib/network-only.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Handler from './handler';
import assert from '../../../../lib/assert';
/**
* An implementation of a [network-only](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#network-only)
* request strategy.
*
* The advantage to using this versus directly calling `fetch()` is that it will
* trigger the plugins defined in the underlying `RequestWrapper`.
*
*
* @example
* // Set up a route to match any requests made for URLs that end in .txt.
* // The requests are handled with a network-only strategy.
* const route = new goog.routing.RegExpRoute({
* regExp: /\.txt$/,
* handler: new goog.runtimeCaching.NetworkOnly(),
* });
*
* const router = new goog.routing.Router();
* router.registerRoute({route});
*
* @memberof module:sw-runtime-caching
* @extends Handler
*/
class NetworkOnly extends Handler {
/**
* The handle method will be called by the
* {@link module:sw-routing.Route|Route} class when a route matches a request.
*
* @param {Object} input
* @param {FetchEvent} input.event The event that triggered the service
* worker's fetch handler.
* @return {Promise.&lt;Response>} The response from the network.
*/
async handle({event} = {}) {
assert.isInstance({event}, FetchEvent);
return await this.requestWrapper.fetch({request: event.request});
}
}
export default NetworkOnly;
</code></pre>
</article>
</section>
@@ -0,0 +1,337 @@
---
layout: jsdoc
title: "Source: sw-runtime-caching/src/lib/request-wrapper.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import assert from '../../../../lib/assert';
import logHelper from '../../../../lib/log-helper.js';
import {pluginCallbacks, defaultCacheName} from './constants';
import ErrorFactory from './error-factory';
/**
* This class is used by the various subclasses of `Handler` to configure the
* cache name and any desired plugins, which is to say classes that implement
* request lifecycle callbacks.
*
* It automatically triggers any registered callbacks at the appropriate time.
* The current set of plugin callbacks, along with the parameters they're
* given and when they're called, is:
*
* - `cacheWillUpdate({request, response})`: Called prior to writing an entry
* to the cache, allowing the callback to decide whether or not the cache
* entry should be written.
* - `cacheDidUpdate({cacheName, oldResponse, newResponse, url})`: Called
* whenever an entry is written to the cache, giving the callback a chance to
* notify clients about the update or implement cache expiration.
* - `cacheWillMatch({cachedResponse})`: Called whenever a response is read
* from the cache and is about to be used, giving the callback a chance to
* perform validity/freshness checks.
* - `fetchDidFail({request})`: Called whenever a network request fails.
*
* @memberof module:sw-runtime-caching
*/
class RequestWrapper {
/**
* Constructor for RequestWrapper.
* @param {Object} input
* @param {string} [input.cacheName] The name of the cache to use for Handlers
* that involve caching. If none is provided, a default name that
* includes the current service worker scope will be used.
* @param {Array.&lt;Object>} [input.plugins] Any plugins that should be
* invoked.
* @param {Object} [input.fetchOptions] Values passed along to the
* [`init`](https://developer.mozilla.org/en-US/docs/Web/API/GlobalFetch/fetch#Parameters)
* of all `fetch()` requests made by this wrapper.
* @param {Object} [input.matchOptions] Values passed along to the
* [`options`](https://developer.mozilla.org/en-US/docs/Web/API/Cache/match#Parameters)
* of all cache `match()` requests made by this wrapper.
*/
constructor({cacheName, plugins, fetchOptions, matchOptions} = {}) {
if (cacheName) {
assert.isType({cacheName}, 'string');
this.cacheName = cacheName;
} else {
this.cacheName = defaultCacheName;
}
if (fetchOptions) {
assert.isType({fetchOptions}, 'object');
this.fetchOptions = fetchOptions;
}
if (matchOptions) {
assert.isType({matchOptions}, 'object');
this.matchOptions = matchOptions;
}
this.pluginCallbacks = {};
if (plugins) {
assert.isArrayOfType({plugins}, 'object');
plugins.forEach((plugin) => {
for (let callbackName of pluginCallbacks) {
if (typeof plugin[callbackName] === 'function') {
if (!this.pluginCallbacks[callbackName]) {
this.pluginCallbacks[callbackName] = [];
}
this.pluginCallbacks[callbackName].push(
plugin[callbackName].bind(plugin));
}
}
});
}
if (this.pluginCallbacks.cacheWillUpdate) {
if (this.pluginCallbacks.cacheWillUpdate.length !== 1) {
throw ErrorFactory.createError('multiple-cache-will-update-plugins');
}
}
if (this.pluginCallbacks.cacheWillMatch) {
if (this.pluginCallbacks.cacheWillMatch.length !== 1) {
throw ErrorFactory.createError('multiple-cache-will-match-plugins');
}
}
}
/**
* Opens a cache and maintains a reference to that cache
* for future use.
*
* @example
* requestWrapper.getCache()
* .then((openCache) => {
* ...
* });
*
* @return {Promise&lt;Cache>} An open `Cache` instance based on the configured
* `cacheName`.
*/
async getCache() {
if (!this._cache) {
this._cache = await caches.open(this.cacheName);
}
return this._cache;
}
/**
* Wraps `cache.match()`, using the previously configured cache name and match
* options.
*
* @example
* requestWrapper.match({event.request})
* .then((response) => {
* if (!response) {
* // No response in cache.
* return;
* }
* ...
* });
*
* @param {Object} input
* @param {Request|string} input.request The key for the cache lookup.
* @return {Promise.&lt;Response>} The cached response.
*/
async match({request}) {
assert.atLeastOne({request});
const cache = await this.getCache();
let cachedResponse = await cache.match(request, this.matchOptions);
if (this.pluginCallbacks.cacheWillMatch) {
cachedResponse = this.pluginCallbacks.cacheWillMatch[0](
{cachedResponse});
}
return cachedResponse;
}
/**
* Wraps `fetch()`, calls all `requestWillFetch` before making the network
* request, and calls any `fetchDidFail` callbacks from the
* registered plugins if the request fails.
*
* @example
* requestWrapper.fetch({
* request: event.request
* })
* .then((response) => {
* ...
* })
* .catch((err) => {
* ...
* });
*
* @param {Object} input
* @param {Request|string} input.request The request or URL to be fetched.
* @return {Promise.&lt;Response>} The network response.
*/
async fetch({request}) {
assert.atLeastOne({request});
const clonedRequest = this.pluginCallbacks.fetchDidFail ?
request.clone() : null;
if (this.pluginCallbacks.requestWillFetch) {
for (let callback of this.pluginCallbacks.requestWillFetch) {
const returnedPromise = callback({request});
assert.isInstance({returnedPromise}, Promise);
const returnedRequest = await returnedPromise;
assert.isInstance({returnedRequest}, Request);
request = returnedRequest;
}
}
try {
return await fetch(request, this.fetchOptions);
} catch (err) {
if (this.pluginCallbacks.fetchDidFail) {
for (let callback of this.pluginCallbacks.fetchDidFail) {
callback({request: clonedRequest.clone()});
}
}
throw err;
}
}
/**
* Combines both fetching and caching using the previously configured options
* and calling the appropriate plugins.
*
* By default, responses with a status of [2xx](https://fetch.spec.whatwg.org/#ok-status)
* will be considered valid and cacheable, but this could be overridden by
* configuring one or more plugins that implement the `cacheWillUpdate`
* lifecycle callback.
*
* @example
* requestWrapper.fetchAndCache({
* request: event.request
* })
* .then((response) => {
* ...
* })
* .catch((err) => {
* ...
* });
*
* @param {Object} input
* @param {Request} input.request The request to fetch.
* @param {boolean} [input.waitOnCache] `true` means the method should wait
* for the cache.put() to complete before returning. The default value
* of `false` means return without waiting. It this value is true
* and the response can't be cached, an error will be thrown.
* @param {Request} [input.cacheKey] Supply a cacheKey if you wish to cache
* the response against an alternative request to the `request`
* argument.
* @return {Promise.&lt;Response>} The network response.
*/
async fetchAndCache({request, waitOnCache, cacheKey}) {
assert.atLeastOne({request});
let cachingComplete;
const response = await this.fetch({request});
// response.ok is true if the response status is 2xx.
// That's the default condition.
let cacheable = response.ok;
if (this.pluginCallbacks.cacheWillUpdate) {
cacheable = this.pluginCallbacks.cacheWillUpdate[0](
{request, response});
}
if (cacheable) {
const newResponse = response.clone();
// cacheDelay is a promise that may or may not be used to delay the
// completion of this method, depending on the value of `waitOnCache`.
cachingComplete = this.getCache().then(async (cache) => {
let oldResponse;
// Only bother getting the old response if the new response isn't opaque
// and there's at least one cacheDidUpdateCallbacks. Otherwise, we don't
// need it.
if (response.type !== 'opaque' &amp;&amp;
this.pluginCallbacks.cacheDidUpdate) {
oldResponse = await this.match({request});
}
// Regardless of whether or not we'll end up invoking
// cacheDidUpdateCallbacks, wait until the cache is updated.
const cacheRequest = cacheKey || request;
await cache.put(cacheRequest, newResponse);
for (let callback of (this.pluginCallbacks.cacheDidUpdate || [])) {
callback({
cacheName: this.cacheName,
oldResponse,
newResponse,
url: request.url,
});
}
});
} else if (!cacheable) {
logHelper.debug(`[RequestWrapper] The response for ${request.url}, with
a status of ${response.status}, wasn't cached. By default, only
responses with a status of 200 are cached. You can configure the
cacheableResponse plugin to change this default.`.replace(/\s+/g, ' '));
if (waitOnCache) {
// If the developer request to wait on the cache but the response
// isn't cacheable, throw an error.
throw ErrorFactory.createError('invalid-reponse-for-caching');
}
}
// Only conditionally await the caching completion, giving developers the
// option of returning early for, e.g., read-through-caching scenarios.
if (waitOnCache &amp;&amp; cachingComplete) {
await cachingComplete;
}
return response;
}
}
export default RequestWrapper;
</code></pre>
</article>
</section>
@@ -0,0 +1,97 @@
---
layout: jsdoc
title: "Source: sw-runtime-caching/src/lib/stale-while-revalidate.js"
jsdocNav:
- Modules:
- <a href="module-sw-appcache-behavior.html#main">sw-appcache-behavior</a>
- <a href="module-sw-background-sync-queue.html#main">sw-background-sync-queue</a>
- <a href="module-sw-broadcast-cache-update.html#main">sw-broadcast-cache-update</a>
- <a href="module-sw-build.html#main">sw-build</a>
- <a href="module-sw-cache-expiration.html#main">sw-cache-expiration</a>
- <a href="module-sw-cacheable-response.html#main">sw-cacheable-response</a>
- <a href="module-sw-lib.html#main">sw-lib</a>
- <a href="module-sw-offline-google-analytics.html#main">sw-offline-google-analytics</a>
- <a href="module-sw-precaching.html#main">sw-precaching</a>
- <a href="module-sw-routing.html#main">sw-routing</a>
- <a href="module-sw-runtime-caching.html#main">sw-runtime-caching</a>
---
<section>
<article>
<pre class="prettyprint source linenums"><code>/*
Copyright 2016 Google Inc. All Rights Reserved.
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
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import Handler from './handler';
import assert from '../../../../lib/assert';
/**
* An implementation of a [stale-while-revalidate](https://developers.google.com/web/fundamentals/instant-and-offline/offline-cookbook/#stale-while-revalidate)
* request strategy.
*
* In addition to updating the appropriate caches, it will also trigger any
* appropriate plugins defined in the underlying `RequestWrapper`.
*
* @example
* // Set up a route to match any requests made for URLs that end in .txt.
* // The requests are handled with a stale-while-revalidate strategy.
* const route = new goog.routing.RegExpRoute({
* regExp: /\.txt$/,
* handler: new goog.runtimeCaching.StaleWhileRevalidate(),
* });
*
* const router = new goog.routing.Router();
* router.registerRoute({route});
*
* @memberof module:sw-runtime-caching
* @extends Handler
*/
class StaleWhileRevalidate extends Handler {
/**
* The handle method will be called by the
* {@link module:sw-routing.Route|Route} class when a route matches a request.
*
* @param {Object} input
* @param {FetchEvent} input.event The event that triggered the service
* worker's fetch handler.
* @return {Promise.&lt;Response>} The response from the cache, if present, or
* from the network if not.
*/
async handle({event} = {}) {
assert.isInstance({event}, FetchEvent);
const fetchAndCacheResponse = this.requestWrapper.fetchAndCache({
request: event.request,
}).catch(() => Response.error());
const cachedResponse = await this.requestWrapper.match({
request: event.request,
});
return cachedResponse || await fetchAndCacheResponse;
}
}
export default StaleWhileRevalidate;
</code></pre>
</article>
</section>
@@ -0,0 +1,4 @@
---
layout: redirect
relPath: reference-docs/stable/v0.0.19/BackgroundSyncQueue.html
---
@@ -0,0 +1,4 @@
---
layout: redirect
relPath: reference-docs/stable/v0.0.20/SWCli.html
---
@@ -0,0 +1,4 @@
---
layout: redirect
relPath: reference-docs/stable/v0.0.20/SwBuildWebpackPlugin.SwBuildWebpackPlugin.html
---
@@ -0,0 +1,4 @@
---
layout: redirect
relPath: reference-docs/stable/v0.0.20/SwBuildWebpackPlugin.html
---
@@ -0,0 +1,4 @@
---
layout: redirect
relPath: reference-docs/stable/v0.0.20/global.html
---
@@ -0,0 +1,4 @@
---
layout: redirect
relPath: reference-docs/stable/v0.0.20/index.html
---
@@ -0,0 +1,4 @@
---
layout: redirect
relPath: reference-docs/stable/v0.0.20/module-sw-background-sync-queue.BackgroundSyncQueue.html
---
@@ -0,0 +1,4 @@
---
layout: redirect
relPath: reference-docs/stable/v0.0.20/module-sw-background-sync-queue.html
---
@@ -0,0 +1,4 @@
---
layout: redirect
relPath: reference-docs/stable/v0.0.20/module-sw-broadcast-cache-update.BroadcastCacheUpdate.html
---
@@ -0,0 +1,4 @@
---
layout: redirect
relPath: reference-docs/stable/v0.0.20/module-sw-broadcast-cache-update.BroadcastCacheUpdatePlugin.html
---

Some files were not shown because too many files have changed in this diff Show More