Updates
|
@ -0,0 +1 @@
|
|||
{"app":["/app-51e70f8e1fb75adb779d.js"]}
|
After Width: | Height: | Size: 13 KiB |
After Width: | Height: | Size: 18 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 8.3 KiB |
|
@ -0,0 +1 @@
|
|||
var idbKeyval=function(e){"use strict";class t{constructor(e="keyval-store",t="keyval"){this.storeName=t,this._dbp=new Promise((r,n)=>{const o=indexedDB.open(e,1);o.onerror=(()=>n(o.error)),o.onsuccess=(()=>r(o.result)),o.onupgradeneeded=(()=>{o.result.createObjectStore(t)})})}_withIDBStore(e,t){return this._dbp.then(r=>new Promise((n,o)=>{const s=r.transaction(this.storeName,e);s.oncomplete=(()=>n()),s.onabort=s.onerror=(()=>o(s.error)),t(s.objectStore(this.storeName))}))}}let r;function n(){return r||(r=new t),r}return e.Store=t,e.get=function(e,t=n()){let r;return t._withIDBStore("readonly",t=>{r=t.get(e)}).then(()=>r.result)},e.set=function(e,t,r=n()){return r._withIDBStore("readwrite",r=>{r.put(t,e)})},e.del=function(e,t=n()){return t._withIDBStore("readwrite",t=>{t.delete(e)})},e.clear=function(e=n()){return e._withIDBStore("readwrite",e=>{e.clear()})},e.keys=function(e=n()){const t=[];return e._withIDBStore("readonly",e=>{(e.openKeyCursor||e.openCursor).call(e).onsuccess=function(){this.result&&(t.push(this.result.key),this.result.continue())}}).then(()=>t)},e}({});
|
|
@ -0,0 +1 @@
|
|||
{"name":"gatsby-starter-default","short_name":"starter","start_url":"/","background_color":"#722ED1","theme_color":"#722ED1","display":"minimal-ui","icons":[{"src":"icons/icon-48x48.png?v=40b8872b4d87c3cdc6162b11ecf88dc1","sizes":"48x48","type":"image/png"},{"src":"icons/icon-72x72.png?v=40b8872b4d87c3cdc6162b11ecf88dc1","sizes":"72x72","type":"image/png"},{"src":"icons/icon-96x96.png?v=40b8872b4d87c3cdc6162b11ecf88dc1","sizes":"96x96","type":"image/png"},{"src":"icons/icon-144x144.png?v=40b8872b4d87c3cdc6162b11ecf88dc1","sizes":"144x144","type":"image/png"},{"src":"icons/icon-192x192.png?v=40b8872b4d87c3cdc6162b11ecf88dc1","sizes":"192x192","type":"image/png"},{"src":"icons/icon-256x256.png?v=40b8872b4d87c3cdc6162b11ecf88dc1","sizes":"256x256","type":"image/png"},{"src":"icons/icon-384x384.png?v=40b8872b4d87c3cdc6162b11ecf88dc1","sizes":"384x384","type":"image/png"},{"src":"icons/icon-512x512.png?v=40b8872b4d87c3cdc6162b11ecf88dc1","sizes":"512x512","type":"image/png"}]}
|
|
@ -0,0 +1 @@
|
|||
{"componentChunkName":"component---node-modules-antv-gatsby-theme-antv-site-pages-404-tsx","path":"/404.html","result":{"pageContext":{"isCreatedByStatefulCreatePages":true,"slug":"/404/","langKey":"zh"}}}
|
|
@ -0,0 +1 @@
|
|||
{"componentChunkName":"component---node-modules-antv-gatsby-theme-antv-site-pages-404-tsx","path":"/404","result":{"pageContext":{"isCreatedByStatefulCreatePages":true,"slug":"/404/","langKey":"zh"}}}
|
|
@ -0,0 +1 @@
|
|||
{"webpackCompilationHash":"7bfb4d1a12c65b5c3447"}
|
|
@ -0,0 +1 @@
|
|||
{"componentChunkName":"component---site-pages-index-en-ts","path":"/en","result":{"pageContext":{"isCreatedByStatefulCreatePages":true,"slug":"/en/","langKey":"en"}}}
|
|
@ -0,0 +1 @@
|
|||
{"componentChunkName":"component---node-modules-antv-gatsby-theme-antv-site-pages-index-tsx","path":"/","result":{"pageContext":{"isCreatedByStatefulCreatePages":true,"slug":"/","langKey":"zh"}}}
|
|
@ -0,0 +1 @@
|
|||
{"componentChunkName":"component---node-modules-gatsby-plugin-offline-app-shell-js","path":"/offline-plugin-app-shell-fallback","result":{"pageContext":{"isCreatedByStatefulCreatePages":false}}}
|
|
@ -0,0 +1 @@
|
|||
{"componentChunkName":"component---node-modules-antv-gatsby-theme-antv-site-templates-document-tsx","path":"/zh/docs/API/L7","result":{"data":{"site":{"siteMetadata":{"title":"L7","githubUrl":"https://github.com/antvis/antvis.github.io","docs":[{"slug":"specification","title":{"zh":"简介","en":"introduction"},"order":null},{"slug":"manual/tutorial","title":{"zh":"教程","en":"tutorial"},"order":null},{"slug":"API/L7.md","title":{"zh":"简介","en":"intro"},"order":1},{"slug":"API/component","title":{"zh":"组件","en":"Component"},"order":1}]},"pathPrefix":"/gatsby-theme-antv"},"markdownRemark":{"html":"","tableOfContents":"","fields":{"slug":"/zh/docs/API/L7","readingTime":{"text":"0 min read","time":0}},"frontmatter":{"title":"简介"},"parent":{"__typename":"File","relativePath":"API/L7.md"}},"allMarkdownRemark":{"edges":[{"node":{"fields":{"slug":"/zh/docs/manual/tutorial/quickStart"},"frontmatter":{"title":"快速上手"}}},{"node":{"fields":{"slug":"/zh/docs/API/L7"},"frontmatter":{"title":"简介"}}},{"node":{"fields":{"slug":"/zh/docs/API/component/popup"},"frontmatter":{"title":"地图信息框"}}},{"node":{"fields":{"slug":"/zh/docs/API/component/marker"},"frontmatter":{"title":"地图标注"}}},{"node":{"fields":{"slug":"/zh/examples/point/basic"},"frontmatter":{"title":"气泡图"}}},{"node":{"fields":{"slug":"/zh/docs/manual/tutorial/data"},"frontmatter":{"title":"数据"}}},{"node":{"fields":{"slug":"/zh/docs/API/component/control"},"frontmatter":{"title":"地图组件"}}},{"node":{"fields":{"slug":"/zh/docs/specification/introduction"},"frontmatter":{"title":"简介"}}}]}},"pageContext":{"isCreatedByStatefulCreatePages":false,"prev":{"fields":{"slug":"/zh/docs/manual/tutorial/quickStart"}},"next":{"fields":{"slug":"/zh/docs/manual/tutorial/data"}}}}}
|
|
@ -0,0 +1 @@
|
|||
{"componentChunkName":"component---node-modules-antv-gatsby-theme-antv-site-templates-document-tsx","path":"/zh/docs/manual/tutorial/quickStart","result":{"data":{"site":{"siteMetadata":{"title":"L7","githubUrl":"https://github.com/antvis/antvis.github.io","docs":[{"slug":"specification","title":{"zh":"简介","en":"introduction"},"order":null},{"slug":"manual/tutorial","title":{"zh":"教程","en":"tutorial"},"order":null},{"slug":"API/L7.md","title":{"zh":"简介","en":"intro"},"order":1},{"slug":"API/component","title":{"zh":"组件","en":"Component"},"order":1}]},"pathPrefix":"/gatsby-theme-antv"},"markdownRemark":{"html":"<p>内容</p>","tableOfContents":"","fields":{"slug":"/zh/docs/manual/tutorial/quickStart","readingTime":{"text":"1 min read","time":300}},"frontmatter":{"title":"快速上手"},"parent":{"__typename":"File","relativePath":"manual/tutorial/quickStart.md"}},"allMarkdownRemark":{"edges":[{"node":{"fields":{"slug":"/zh/docs/manual/tutorial/quickStart"},"frontmatter":{"title":"快速上手"}}},{"node":{"fields":{"slug":"/zh/docs/API/L7"},"frontmatter":{"title":"简介"}}},{"node":{"fields":{"slug":"/zh/docs/API/component/popup"},"frontmatter":{"title":"地图信息框"}}},{"node":{"fields":{"slug":"/zh/docs/API/component/marker"},"frontmatter":{"title":"地图标注"}}},{"node":{"fields":{"slug":"/zh/examples/point/basic"},"frontmatter":{"title":"气泡图"}}},{"node":{"fields":{"slug":"/zh/docs/manual/tutorial/data"},"frontmatter":{"title":"数据"}}},{"node":{"fields":{"slug":"/zh/docs/API/component/control"},"frontmatter":{"title":"地图组件"}}},{"node":{"fields":{"slug":"/zh/docs/specification/introduction"},"frontmatter":{"title":"简介"}}}]}},"pageContext":{"isCreatedByStatefulCreatePages":false,"prev":null,"next":{"fields":{"slug":"/zh/docs/API/L7"}}}}}
|
|
@ -0,0 +1 @@
|
|||
{"componentChunkName":"component---node-modules-antv-gatsby-theme-antv-site-templates-document-tsx","path":"/zh/docs/specification/introduction","result":{"data":{"site":{"siteMetadata":{"title":"L7","githubUrl":"https://github.com/antvis/antvis.github.io","docs":[{"slug":"specification","title":{"zh":"简介","en":"introduction"},"order":null},{"slug":"manual/tutorial","title":{"zh":"教程","en":"tutorial"},"order":null},{"slug":"API/L7.md","title":{"zh":"简介","en":"intro"},"order":1},{"slug":"API/component","title":{"zh":"组件","en":"Component"},"order":1}]},"pathPrefix":"/gatsby-theme-antv"},"markdownRemark":{"html":"<p>L7 地理空间可视化设计语言</p>","tableOfContents":"","fields":{"slug":"/zh/docs/specification/introduction","readingTime":{"text":"1 min read","time":600}},"frontmatter":{"title":"简介"},"parent":{"__typename":"File","relativePath":"specification/introduction.md"}},"allMarkdownRemark":{"edges":[{"node":{"fields":{"slug":"/zh/docs/manual/tutorial/quickStart"},"frontmatter":{"title":"快速上手"}}},{"node":{"fields":{"slug":"/zh/docs/API/L7"},"frontmatter":{"title":"简介"}}},{"node":{"fields":{"slug":"/zh/docs/API/component/popup"},"frontmatter":{"title":"地图信息框"}}},{"node":{"fields":{"slug":"/zh/docs/API/component/marker"},"frontmatter":{"title":"地图标注"}}},{"node":{"fields":{"slug":"/zh/examples/point/basic"},"frontmatter":{"title":"气泡图"}}},{"node":{"fields":{"slug":"/zh/docs/manual/tutorial/data"},"frontmatter":{"title":"数据"}}},{"node":{"fields":{"slug":"/zh/docs/API/component/control"},"frontmatter":{"title":"地图组件"}}},{"node":{"fields":{"slug":"/zh/docs/specification/introduction"},"frontmatter":{"title":"简介"}}}]}},"pageContext":{"isCreatedByStatefulCreatePages":false,"prev":{"fields":{"slug":"/zh/docs/API/component/control"}},"next":{"fields":{"slug":"/zh/docs/API/component/popup"}}}}}
|
|
@ -0,0 +1 @@
|
|||
{"componentChunkName":"component---site-pages-index-zh-ts","path":"/zh","result":{"pageContext":{"isCreatedByStatefulCreatePages":true,"slug":"/zh/","langKey":"zh"}}}
|
|
@ -0,0 +1 @@
|
|||
{"data":{"placeholderImage":null}}
|
|
@ -0,0 +1 @@
|
|||
{"data":{"site":{"siteMetadata":{"title":"L7","githubUrl":"https://github.com/antvis/antvis.github.io","navs":[{"slug":"docs/specification","title":{"zh":"设计语言","en":"Specification"},"target":null},{"slug":"docs/API","title":{"zh":"文档","en":"document"},"target":null},{"slug":"docs/tutorial","title":{"zh":"教程","en":"tutorial"},"target":null},{"slug":"examples","title":{"zh":"图表演示","en":"Examples"},"target":null}]}}}}
|
|
@ -0,0 +1 @@
|
|||
{"data":{"site":{"siteMetadata":{"title":"L7","description":"Large-scale WebGL-powered Geospatial data visualization analysis framework"}}}}
|
|
@ -0,0 +1,2 @@
|
|||
(window.webpackJsonp=window.webpackJsonp||[]).push([[2],{"3GoW":function(o,n,e){o.exports={input:"Search-module--input--lOvOF"}},"44il":function(o,n,e){o.exports={container:"PlayGrounds-module--container--17Tai",cards:"PlayGrounds-module--cards--3hJak",card:"PlayGrounds-module--card--3Vq_o",current:"PlayGrounds-module--current--2cQLr"}},"49qf":function(o,n,e){},6488:function(o,n,e){},"7H9b":function(o,n,e){},"8nl2":function(o,n,e){},"9d8Q":function(o,n,e){},FPB1:function(o,n,e){o.exports={header:"Header-module--header--hrdnh",left:"Header-module--left--1dVXS",divider:"Header-module--divider--2qNp3",subProduceName:"Header-module--subProduceName--NMLtC",nav:"Header-module--nav--2_Knj",menu:"Header-module--menu--3hWNx",activeItem:"Header-module--activeItem--2xNW-",githubCorner:"Header-module--githubCorner--14Csh"}},"FoS+":function(o,n,e){},"G3+4":function(o,n,e){},GiSQ:function(o,n,e){o.exports={article:"Article-module--article--1-Z4k"}},HYfu:function(o,n,e){o.exports={markdown:"markdown-module--markdown--1TLJm",editOnGtiHubButton:"markdown-module--editOnGtiHubButton--1hV6y",main:"markdown-module--main--1VRvj",toc:"markdown-module--toc--3a23c",sider:"markdown-module--sider--26riA",menuIcon:"markdown-module--menuIcon--hTGH6",layout:"markdown-module--layout--20GIh"}},IDGY:function(o,n,e){},"Ibq+":function(o,n,e){},"L/94":function(o,n,e){},Lk4S:function(o,n,e){o.exports={products:"Product-module--products--wZmFW",product:"Product-module--product--1pyvr",productContent:"Product-module--productContent--3xt59",productDescription:"Product-module--productDescription--3dOob",productLinks:"Product-module--productLinks--2gCa8"}},OwP9:function(o,n,e){},Svq7:function(o,n,e){},VEUW:function(o,n,e){},Wfs9:function(o,n,e){},XJbt:function(o,n,e){},YCql:function(o,n,e){},e0s7:function(o,n,e){},fcTV:function(o,n,e){},"h+Wa":function(o,n,e){},hEgN:function(o,n,e){},m7UV:function(o,n,e){o.exports={playground:"PlayGround-module--playground--20RTt",preview:"PlayGround-module--preview--6whqA",editor:"PlayGround-module--editor--1HTqe",toolbar:"PlayGround-module--toolbar--2i0SG",codemirror:"PlayGround-module--codemirror--nEJBa"}},mo9N:function(o,n,e){o.exports={tabs:"Tabs-module--tabs--3WFbV",active:"Tabs-module--active--2GIM4"}},"p77/":function(o,n,e){},slaK:function(o,n,e){o.exports={footer:"Footer-module--footer--1oDCB"}},vLCf:function(o,n,e){},zP18:function(o,n,e){o.exports={main:"layout-module--main--2k9yj"}}}]);
|
||||
//# sourceMappingURL=styles-5d5ac81f3c32d0998151.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"sources":["webpack:///./node_modules/@antv/gatsby-theme-antv/site/components/Search.module.less","webpack:///./node_modules/@antv/gatsby-theme-antv/site/components/PlayGrounds.module.less","webpack:///./node_modules/@antv/gatsby-theme-antv/site/components/Header.module.less","webpack:///./node_modules/@antv/gatsby-theme-antv/site/components/Article.module.less","webpack:///./node_modules/@antv/gatsby-theme-antv/site/templates/markdown.module.less","webpack:///./node_modules/@antv/gatsby-theme-antv/site/components/Product.module.less","webpack:///./node_modules/@antv/gatsby-theme-antv/site/components/PlayGround.module.less","webpack:///./node_modules/@antv/gatsby-theme-antv/site/components/Tabs.module.less","webpack:///./node_modules/@antv/gatsby-theme-antv/site/components/Footer.module.less","webpack:///./node_modules/@antv/gatsby-theme-antv/site/layouts/layout.module.less"],"names":["module","exports"],"mappings":"gFACAA,EAAOC,QAAU,CAAC,MAAQ,gC,uBCA1BD,EAAOC,QAAU,CAAC,UAAY,uCAAuC,MAAQ,mCAAmC,KAAO,kCAAkC,QAAU,uC,gJCAnKD,EAAOC,QAAU,CAAC,OAAS,+BAA+B,KAAO,6BAA6B,QAAU,gCAAgC,eAAiB,uCAAuC,IAAM,4BAA4B,KAAO,6BAA6B,WAAa,mCAAmC,aAAe,uC,uECArUD,EAAOC,QAAU,CAAC,QAAU,mC,qBCA5BD,EAAOC,QAAU,CAAC,SAAW,mCAAmC,mBAAqB,6CAA6C,KAAO,+BAA+B,IAAM,8BAA8B,MAAQ,gCAAgC,SAAW,mCAAmC,OAAS,mC,8FCA3SD,EAAOC,QAAU,CAAC,SAAW,kCAAkC,QAAU,iCAAiC,eAAiB,wCAAwC,mBAAqB,4CAA4C,aAAe,wC,6PCAnPD,EAAOC,QAAU,CAAC,WAAa,uCAAuC,QAAU,oCAAoC,OAAS,mCAAmC,QAAU,oCAAoC,WAAa,yC,qBCA3ND,EAAOC,QAAU,CAAC,KAAO,2BAA2B,OAAS,+B,8CCA7DD,EAAOC,QAAU,CAAC,OAAS,iC,4CCA3BD,EAAOC,QAAU,CAAC,KAAO","file":"styles-5d5ac81f3c32d0998151.js","sourcesContent":["// extracted by mini-css-extract-plugin\nmodule.exports = {\"input\":\"Search-module--input--lOvOF\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"container\":\"PlayGrounds-module--container--17Tai\",\"cards\":\"PlayGrounds-module--cards--3hJak\",\"card\":\"PlayGrounds-module--card--3Vq_o\",\"current\":\"PlayGrounds-module--current--2cQLr\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"header\":\"Header-module--header--hrdnh\",\"left\":\"Header-module--left--1dVXS\",\"divider\":\"Header-module--divider--2qNp3\",\"subProduceName\":\"Header-module--subProduceName--NMLtC\",\"nav\":\"Header-module--nav--2_Knj\",\"menu\":\"Header-module--menu--3hWNx\",\"activeItem\":\"Header-module--activeItem--2xNW-\",\"githubCorner\":\"Header-module--githubCorner--14Csh\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"article\":\"Article-module--article--1-Z4k\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"markdown\":\"markdown-module--markdown--1TLJm\",\"editOnGtiHubButton\":\"markdown-module--editOnGtiHubButton--1hV6y\",\"main\":\"markdown-module--main--1VRvj\",\"toc\":\"markdown-module--toc--3a23c\",\"sider\":\"markdown-module--sider--26riA\",\"menuIcon\":\"markdown-module--menuIcon--hTGH6\",\"layout\":\"markdown-module--layout--20GIh\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"products\":\"Product-module--products--wZmFW\",\"product\":\"Product-module--product--1pyvr\",\"productContent\":\"Product-module--productContent--3xt59\",\"productDescription\":\"Product-module--productDescription--3dOob\",\"productLinks\":\"Product-module--productLinks--2gCa8\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"playground\":\"PlayGround-module--playground--20RTt\",\"preview\":\"PlayGround-module--preview--6whqA\",\"editor\":\"PlayGround-module--editor--1HTqe\",\"toolbar\":\"PlayGround-module--toolbar--2i0SG\",\"codemirror\":\"PlayGround-module--codemirror--nEJBa\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"tabs\":\"Tabs-module--tabs--3WFbV\",\"active\":\"Tabs-module--active--2GIM4\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"footer\":\"Footer-module--footer--1oDCB\"};","// extracted by mini-css-extract-plugin\nmodule.exports = {\"main\":\"layout-module--main--2k9yj\"};"],"sourceRoot":""}
|
|
@ -0,0 +1,165 @@
|
|||
/**
|
||||
* Welcome to your Workbox-powered service worker!
|
||||
*
|
||||
* You'll need to register this file in your web app and you should
|
||||
* disable HTTP caching for this file too.
|
||||
* See https://goo.gl/nhQhGp
|
||||
*
|
||||
* The rest of the code is auto-generated. Please don't update this file
|
||||
* directly; instead, make changes to your Workbox build configuration
|
||||
* and re-run your build process.
|
||||
* See https://goo.gl/2aRDsh
|
||||
*/
|
||||
|
||||
importScripts("workbox-v4.3.1/workbox-sw.js");
|
||||
workbox.setConfig({modulePathPrefix: "workbox-v4.3.1"});
|
||||
|
||||
workbox.core.setCacheNameDetails({prefix: "gatsby-plugin-offline"});
|
||||
|
||||
workbox.core.skipWaiting();
|
||||
|
||||
workbox.core.clientsClaim();
|
||||
|
||||
/**
|
||||
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
|
||||
* requests for URLs in the manifest.
|
||||
* See https://goo.gl/S9QRab
|
||||
*/
|
||||
self.__precacheManifest = [
|
||||
{
|
||||
"url": "webpack-runtime-c93a82d3addc73eb1b52.js"
|
||||
},
|
||||
{
|
||||
"url": "styles.e32956506dacddf17950.css"
|
||||
},
|
||||
{
|
||||
"url": "styles-5d5ac81f3c32d0998151.js"
|
||||
},
|
||||
{
|
||||
"url": "commons-f6783ede68f8c7ff9b6d.js"
|
||||
},
|
||||
{
|
||||
"url": "offline-plugin-app-shell-fallback/index.html",
|
||||
"revision": "3bb6e989197b330c20e203bdea194e49"
|
||||
},
|
||||
{
|
||||
"url": "page-data/offline-plugin-app-shell-fallback/page-data.json",
|
||||
"revision": "5636d5ab217f85530a92b2939e270518"
|
||||
},
|
||||
{
|
||||
"url": "manifest.webmanifest",
|
||||
"revision": "123daa3226c4e9bd715f027dfb4e03ab"
|
||||
}
|
||||
].concat(self.__precacheManifest || []);
|
||||
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
|
||||
|
||||
workbox.routing.registerRoute(/(\.js$|\.css$|static\/)/, new workbox.strategies.CacheFirst(), 'GET');
|
||||
workbox.routing.registerRoute(/^https?:.*\page-data\/.*\/page-data\.json/, new workbox.strategies.NetworkFirst(), 'GET');
|
||||
workbox.routing.registerRoute(/^https?:.*\.(png|jpg|jpeg|webp|svg|gif|tiff|js|woff|woff2|json|css)$/, new workbox.strategies.StaleWhileRevalidate(), 'GET');
|
||||
workbox.routing.registerRoute(/^https?:\/\/fonts\.googleapis\.com\/css/, new workbox.strategies.StaleWhileRevalidate(), 'GET');
|
||||
|
||||
/* global importScripts, workbox, idbKeyval */
|
||||
|
||||
importScripts(`idb-keyval-iife.min.js`)
|
||||
|
||||
const { NavigationRoute } = workbox.routing
|
||||
|
||||
let lastNavigationRequest = null
|
||||
let offlineShellEnabled = true
|
||||
|
||||
// prefer standard object syntax to support more browsers
|
||||
const MessageAPI = {
|
||||
setPathResources: (event, { path, resources }) => {
|
||||
event.waitUntil(idbKeyval.set(`resources:${path}`, resources))
|
||||
},
|
||||
|
||||
clearPathResources: event => {
|
||||
event.waitUntil(idbKeyval.clear())
|
||||
},
|
||||
|
||||
enableOfflineShell: () => {
|
||||
offlineShellEnabled = true
|
||||
},
|
||||
|
||||
disableOfflineShell: () => {
|
||||
offlineShellEnabled = false
|
||||
},
|
||||
}
|
||||
|
||||
self.addEventListener(`message`, event => {
|
||||
const { gatsbyApi: api } = event.data
|
||||
if (api) MessageAPI[api](event, event.data)
|
||||
})
|
||||
|
||||
function handleAPIRequest({ event }) {
|
||||
const { pathname } = new URL(event.request.url)
|
||||
|
||||
const params = pathname.match(/:(.+)/)[1]
|
||||
const data = {}
|
||||
|
||||
if (params.indexOf(`=`) !== -1) {
|
||||
params.split(`&`).forEach(param => {
|
||||
const [key, val] = param.split(`=`)
|
||||
data[key] = val
|
||||
})
|
||||
} else {
|
||||
data.api = params
|
||||
}
|
||||
|
||||
if (MessageAPI[data.api] !== undefined) {
|
||||
MessageAPI[data.api]()
|
||||
}
|
||||
|
||||
if (!data.redirect) {
|
||||
return new Response()
|
||||
}
|
||||
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: {
|
||||
Location: lastNavigationRequest,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const navigationRoute = new NavigationRoute(async ({ event }) => {
|
||||
// handle API requests separately to normal navigation requests, so do this
|
||||
// check first
|
||||
if (event.request.url.match(/\/.gatsby-plugin-offline:.+/)) {
|
||||
return handleAPIRequest({ event })
|
||||
}
|
||||
|
||||
if (!offlineShellEnabled) {
|
||||
return await fetch(event.request)
|
||||
}
|
||||
|
||||
lastNavigationRequest = event.request.url
|
||||
|
||||
let { pathname } = new URL(event.request.url)
|
||||
pathname = pathname.replace(new RegExp(`^/gatsby-theme-antv`), ``)
|
||||
|
||||
// Check for resources + the app bundle
|
||||
// The latter may not exist if the SW is updating to a new version
|
||||
const resources = await idbKeyval.get(`resources:${pathname}`)
|
||||
if (!resources || !(await caches.match(`/gatsby-theme-antv/app-51e70f8e1fb75adb779d.js`))) {
|
||||
return await fetch(event.request)
|
||||
}
|
||||
|
||||
for (const resource of resources) {
|
||||
// As soon as we detect a failed resource, fetch the entire page from
|
||||
// network - that way we won't risk being in an inconsistent state with
|
||||
// some parts of the page failing.
|
||||
if (!(await caches.match(resource))) {
|
||||
return await fetch(event.request)
|
||||
}
|
||||
}
|
||||
|
||||
const offlineShell = `/gatsby-theme-antv/offline-plugin-app-shell-fallback/index.html`
|
||||
const offlineShellWithKey = workbox.precaching.getCacheKeyForURL(offlineShell)
|
||||
return await caches.match(offlineShellWithKey)
|
||||
})
|
||||
|
||||
workbox.routing.registerRoute(navigationRoute)
|
||||
|
||||
// this route is used when performing a non-navigation request (e.g. fetch)
|
||||
workbox.routing.registerRoute(/\/.gatsby-plugin-offline:.+/, handleAPIRequest)
|
|
@ -0,0 +1,2 @@
|
|||
!function(e){function r(r){for(var n,l,a=r[0],f=r[1],i=r[2],c=0,s=[];c<a.length;c++)l=a[c],Object.prototype.hasOwnProperty.call(o,l)&&o[l]&&s.push(o[l][0]),o[l]=0;for(n in f)Object.prototype.hasOwnProperty.call(f,n)&&(e[n]=f[n]);for(p&&p(r);s.length;)s.shift()();return u.push.apply(u,i||[]),t()}function t(){for(var e,r=0;r<u.length;r++){for(var t=u[r],n=!0,a=1;a<t.length;a++){var f=t[a];0!==o[f]&&(n=!1)}n&&(u.splice(r--,1),e=l(l.s=t[0]))}return e}var n={},o={3:0},u=[];function l(r){if(n[r])return n[r].exports;var t=n[r]={i:r,l:!1,exports:{}};return e[r].call(t.exports,t,t.exports,l),t.l=!0,t.exports}l.m=e,l.c=n,l.d=function(e,r,t){l.o(e,r)||Object.defineProperty(e,r,{enumerable:!0,get:t})},l.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},l.t=function(e,r){if(1&r&&(e=l(e)),8&r)return e;if(4&r&&"object"==typeof e&&e&&e.__esModule)return e;var t=Object.create(null);if(l.r(t),Object.defineProperty(t,"default",{enumerable:!0,value:e}),2&r&&"string"!=typeof e)for(var n in e)l.d(t,n,function(r){return e[r]}.bind(null,n));return t},l.n=function(e){var r=e&&e.__esModule?function(){return e.default}:function(){return e};return l.d(r,"a",r),r},l.o=function(e,r){return Object.prototype.hasOwnProperty.call(e,r)},l.p="/gatsby-theme-antv/";var a=window.webpackJsonp=window.webpackJsonp||[],f=a.push.bind(a);a.push=r,a=a.slice();for(var i=0;i<a.length;i++)r(a[i]);var p=f;t()}([]);
|
||||
//# sourceMappingURL=webpack-runtime-c93a82d3addc73eb1b52.js.map
|
|
@ -0,0 +1 @@
|
|||
{"errors":[],"warnings":[],"namedChunkGroups":{"app":{"chunks":[3,2,1,0],"assets":["webpack-runtime-c93a82d3addc73eb1b52.js","webpack-runtime-c93a82d3addc73eb1b52.js.map","styles.e32956506dacddf17950.css","styles-5d5ac81f3c32d0998151.js","styles-5d5ac81f3c32d0998151.js.map","commons-f6783ede68f8c7ff9b6d.js","commons-f6783ede68f8c7ff9b6d.js.map","app-51e70f8e1fb75adb779d.js","app-51e70f8e1fb75adb779d.js.map"],"children":{},"childAssets":{}}},"assetsByChunkName":{"app":["webpack-runtime-c93a82d3addc73eb1b52.js","styles.e32956506dacddf17950.css","styles-5d5ac81f3c32d0998151.js","commons-f6783ede68f8c7ff9b6d.js","app-51e70f8e1fb75adb779d.js"]}}
|
|
@ -0,0 +1,822 @@
|
|||
this.workbox = this.workbox || {};
|
||||
this.workbox.backgroundSync = (function (exports, WorkboxError_mjs, logger_mjs, assert_mjs, getFriendlyURL_mjs, DBWrapper_mjs) {
|
||||
'use strict';
|
||||
|
||||
try {
|
||||
self['workbox:background-sync:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
const DB_VERSION = 3;
|
||||
const DB_NAME = 'workbox-background-sync';
|
||||
const OBJECT_STORE_NAME = 'requests';
|
||||
const INDEXED_PROP = 'queueName';
|
||||
/**
|
||||
* A class to manage storing requests from a Queue in IndexedbDB,
|
||||
* indexed by their queue name for easier access.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
class QueueStore {
|
||||
/**
|
||||
* Associates this instance with a Queue instance, so entries added can be
|
||||
* identified by their queue name.
|
||||
*
|
||||
* @param {string} queueName
|
||||
* @private
|
||||
*/
|
||||
constructor(queueName) {
|
||||
this._queueName = queueName;
|
||||
this._db = new DBWrapper_mjs.DBWrapper(DB_NAME, DB_VERSION, {
|
||||
onupgradeneeded: this._upgradeDb
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Append an entry last in the queue.
|
||||
*
|
||||
* @param {Object} entry
|
||||
* @param {Object} entry.requestData
|
||||
* @param {number} [entry.timestamp]
|
||||
* @param {Object} [entry.metadata]
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async pushEntry(entry) {
|
||||
{
|
||||
assert_mjs.assert.isType(entry, 'object', {
|
||||
moduleName: 'workbox-background-sync',
|
||||
className: 'QueueStore',
|
||||
funcName: 'pushEntry',
|
||||
paramName: 'entry'
|
||||
});
|
||||
assert_mjs.assert.isType(entry.requestData, 'object', {
|
||||
moduleName: 'workbox-background-sync',
|
||||
className: 'QueueStore',
|
||||
funcName: 'pushEntry',
|
||||
paramName: 'entry.requestData'
|
||||
});
|
||||
} // Don't specify an ID since one is automatically generated.
|
||||
|
||||
|
||||
delete entry.id;
|
||||
entry.queueName = this._queueName;
|
||||
await this._db.add(OBJECT_STORE_NAME, entry);
|
||||
}
|
||||
/**
|
||||
* Preppend an entry first in the queue.
|
||||
*
|
||||
* @param {Object} entry
|
||||
* @param {Object} entry.requestData
|
||||
* @param {number} [entry.timestamp]
|
||||
* @param {Object} [entry.metadata]
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async unshiftEntry(entry) {
|
||||
{
|
||||
assert_mjs.assert.isType(entry, 'object', {
|
||||
moduleName: 'workbox-background-sync',
|
||||
className: 'QueueStore',
|
||||
funcName: 'unshiftEntry',
|
||||
paramName: 'entry'
|
||||
});
|
||||
assert_mjs.assert.isType(entry.requestData, 'object', {
|
||||
moduleName: 'workbox-background-sync',
|
||||
className: 'QueueStore',
|
||||
funcName: 'unshiftEntry',
|
||||
paramName: 'entry.requestData'
|
||||
});
|
||||
}
|
||||
|
||||
const [firstEntry] = await this._db.getAllMatching(OBJECT_STORE_NAME, {
|
||||
count: 1
|
||||
});
|
||||
|
||||
if (firstEntry) {
|
||||
// Pick an ID one less than the lowest ID in the object store.
|
||||
entry.id = firstEntry.id - 1;
|
||||
} else {
|
||||
// Otherwise let the auto-incrementor assign the ID.
|
||||
delete entry.id;
|
||||
}
|
||||
|
||||
entry.queueName = this._queueName;
|
||||
await this._db.add(OBJECT_STORE_NAME, entry);
|
||||
}
|
||||
/**
|
||||
* Removes and returns the last entry in the queue matching the `queueName`.
|
||||
*
|
||||
* @return {Promise<Object>}
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async popEntry() {
|
||||
return this._removeEntry({
|
||||
direction: 'prev'
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Removes and returns the first entry in the queue matching the `queueName`.
|
||||
*
|
||||
* @return {Promise<Object>}
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async shiftEntry() {
|
||||
return this._removeEntry({
|
||||
direction: 'next'
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Returns all entries in the store matching the `queueName`.
|
||||
*
|
||||
* @param {Object} options See workbox.backgroundSync.Queue~getAll}
|
||||
* @return {Promise<Array<Object>>}
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async getAll() {
|
||||
return await this._db.getAllMatching(OBJECT_STORE_NAME, {
|
||||
index: INDEXED_PROP,
|
||||
query: IDBKeyRange.only(this._queueName)
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Deletes the entry for the given ID.
|
||||
*
|
||||
* WARNING: this method does not ensure the deleted enry belongs to this
|
||||
* queue (i.e. matches the `queueName`). But this limitation is acceptable
|
||||
* as this class is not publicly exposed. An additional check would make
|
||||
* this method slower than it needs to be.
|
||||
*
|
||||
* @private
|
||||
* @param {number} id
|
||||
*/
|
||||
|
||||
|
||||
async deleteEntry(id) {
|
||||
await this._db.delete(OBJECT_STORE_NAME, id);
|
||||
}
|
||||
/**
|
||||
* Removes and returns the first or last entry in the queue (based on the
|
||||
* `direction` argument) matching the `queueName`.
|
||||
*
|
||||
* @return {Promise<Object>}
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async _removeEntry({
|
||||
direction
|
||||
}) {
|
||||
const [entry] = await this._db.getAllMatching(OBJECT_STORE_NAME, {
|
||||
direction,
|
||||
index: INDEXED_PROP,
|
||||
query: IDBKeyRange.only(this._queueName),
|
||||
count: 1
|
||||
});
|
||||
|
||||
if (entry) {
|
||||
await this.deleteEntry(entry.id);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Upgrades the database given an `upgradeneeded` event.
|
||||
*
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_upgradeDb(event) {
|
||||
const db = event.target.result;
|
||||
|
||||
if (event.oldVersion > 0 && event.oldVersion < DB_VERSION) {
|
||||
if (db.objectStoreNames.contains(OBJECT_STORE_NAME)) {
|
||||
db.deleteObjectStore(OBJECT_STORE_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
const objStore = db.createObjectStore(OBJECT_STORE_NAME, {
|
||||
autoIncrement: true,
|
||||
keyPath: 'id'
|
||||
});
|
||||
objStore.createIndex(INDEXED_PROP, INDEXED_PROP, {
|
||||
unique: false
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
const serializableProperties = ['method', 'referrer', 'referrerPolicy', 'mode', 'credentials', 'cache', 'redirect', 'integrity', 'keepalive'];
|
||||
/**
|
||||
* A class to make it easier to serialize and de-serialize requests so they
|
||||
* can be stored in IndexedDB.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
class StorableRequest {
|
||||
/**
|
||||
* Converts a Request object to a plain object that can be structured
|
||||
* cloned or JSON-stringified.
|
||||
*
|
||||
* @param {Request} request
|
||||
* @return {Promise<StorableRequest>}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
static async fromRequest(request) {
|
||||
const requestData = {
|
||||
url: request.url,
|
||||
headers: {}
|
||||
}; // Set the body if present.
|
||||
|
||||
if (request.method !== 'GET') {
|
||||
// Use ArrayBuffer to support non-text request bodies.
|
||||
// NOTE: we can't use Blobs becuse Safari doesn't support storing
|
||||
// Blobs in IndexedDB in some cases:
|
||||
// https://github.com/dfahlander/Dexie.js/issues/618#issuecomment-398348457
|
||||
requestData.body = await request.clone().arrayBuffer();
|
||||
} // Convert the headers from an iterable to an object.
|
||||
|
||||
|
||||
for (const [key, value] of request.headers.entries()) {
|
||||
requestData.headers[key] = value;
|
||||
} // Add all other serializable request properties
|
||||
|
||||
|
||||
for (const prop of serializableProperties) {
|
||||
if (request[prop] !== undefined) {
|
||||
requestData[prop] = request[prop];
|
||||
}
|
||||
}
|
||||
|
||||
return new StorableRequest(requestData);
|
||||
}
|
||||
/**
|
||||
* Accepts an object of request data that can be used to construct a
|
||||
* `Request` but can also be stored in IndexedDB.
|
||||
*
|
||||
* @param {Object} requestData An object of request data that includes the
|
||||
* `url` plus any relevant properties of
|
||||
* [requestInit]{@link https://fetch.spec.whatwg.org/#requestinit}.
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
constructor(requestData) {
|
||||
{
|
||||
assert_mjs.assert.isType(requestData, 'object', {
|
||||
moduleName: 'workbox-background-sync',
|
||||
className: 'StorableRequest',
|
||||
funcName: 'constructor',
|
||||
paramName: 'requestData'
|
||||
});
|
||||
assert_mjs.assert.isType(requestData.url, 'string', {
|
||||
moduleName: 'workbox-background-sync',
|
||||
className: 'StorableRequest',
|
||||
funcName: 'constructor',
|
||||
paramName: 'requestData.url'
|
||||
});
|
||||
} // If the request's mode is `navigate`, convert it to `same-origin` since
|
||||
// navigation requests can't be constructed via script.
|
||||
|
||||
|
||||
if (requestData.mode === 'navigate') {
|
||||
requestData.mode = 'same-origin';
|
||||
}
|
||||
|
||||
this._requestData = requestData;
|
||||
}
|
||||
/**
|
||||
* Returns a deep clone of the instances `_requestData` object.
|
||||
*
|
||||
* @return {Object}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
toObject() {
|
||||
const requestData = Object.assign({}, this._requestData);
|
||||
requestData.headers = Object.assign({}, this._requestData.headers);
|
||||
|
||||
if (requestData.body) {
|
||||
requestData.body = requestData.body.slice(0);
|
||||
}
|
||||
|
||||
return requestData;
|
||||
}
|
||||
/**
|
||||
* Converts this instance to a Request.
|
||||
*
|
||||
* @return {Request}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
toRequest() {
|
||||
return new Request(this._requestData.url, this._requestData);
|
||||
}
|
||||
/**
|
||||
* Creates and returns a deep clone of the instance.
|
||||
*
|
||||
* @return {StorableRequest}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
clone() {
|
||||
return new StorableRequest(this.toObject());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
const TAG_PREFIX = 'workbox-background-sync';
|
||||
const MAX_RETENTION_TIME = 60 * 24 * 7; // 7 days in minutes
|
||||
|
||||
const queueNames = new Set();
|
||||
/**
|
||||
* A class to manage storing failed requests in IndexedDB and retrying them
|
||||
* later. All parts of the storing and replaying process are observable via
|
||||
* callbacks.
|
||||
*
|
||||
* @memberof workbox.backgroundSync
|
||||
*/
|
||||
|
||||
class Queue {
|
||||
/**
|
||||
* Creates an instance of Queue with the given options
|
||||
*
|
||||
* @param {string} name The unique name for this queue. This name must be
|
||||
* unique as it's used to register sync events and store requests
|
||||
* in IndexedDB specific to this instance. An error will be thrown if
|
||||
* a duplicate name is detected.
|
||||
* @param {Object} [options]
|
||||
* @param {Function} [options.onSync] A function that gets invoked whenever
|
||||
* the 'sync' event fires. The function is invoked with an object
|
||||
* containing the `queue` property (referencing this instance), and you
|
||||
* can use the callback to customize the replay behavior of the queue.
|
||||
* When not set the `replayRequests()` method is called.
|
||||
* Note: if the replay fails after a sync event, make sure you throw an
|
||||
* error, so the browser knows to retry the sync event later.
|
||||
* @param {number} [options.maxRetentionTime=7 days] The amount of time (in
|
||||
* minutes) a request may be retried. After this amount of time has
|
||||
* passed, the request will be deleted from the queue.
|
||||
*/
|
||||
constructor(name, {
|
||||
onSync,
|
||||
maxRetentionTime
|
||||
} = {}) {
|
||||
// Ensure the store name is not already being used
|
||||
if (queueNames.has(name)) {
|
||||
throw new WorkboxError_mjs.WorkboxError('duplicate-queue-name', {
|
||||
name
|
||||
});
|
||||
} else {
|
||||
queueNames.add(name);
|
||||
}
|
||||
|
||||
this._name = name;
|
||||
this._onSync = onSync || this.replayRequests;
|
||||
this._maxRetentionTime = maxRetentionTime || MAX_RETENTION_TIME;
|
||||
this._queueStore = new QueueStore(this._name);
|
||||
|
||||
this._addSyncListener();
|
||||
}
|
||||
/**
|
||||
* @return {string}
|
||||
*/
|
||||
|
||||
|
||||
get name() {
|
||||
return this._name;
|
||||
}
|
||||
/**
|
||||
* Stores the passed request in IndexedDB (with its timestamp and any
|
||||
* metadata) at the end of the queue.
|
||||
*
|
||||
* @param {Object} entry
|
||||
* @param {Request} entry.request The request to store in the queue.
|
||||
* @param {Object} [entry.metadata] Any metadata you want associated with the
|
||||
* stored request. When requests are replayed you'll have access to this
|
||||
* metadata object in case you need to modify the request beforehand.
|
||||
* @param {number} [entry.timestamp] The timestamp (Epoch time in
|
||||
* milliseconds) when the request was first added to the queue. This is
|
||||
* used along with `maxRetentionTime` to remove outdated requests. In
|
||||
* general you don't need to set this value, as it's automatically set
|
||||
* for you (defaulting to `Date.now()`), but you can update it if you
|
||||
* don't want particular requests to expire.
|
||||
*/
|
||||
|
||||
|
||||
async pushRequest(entry) {
|
||||
{
|
||||
assert_mjs.assert.isType(entry, 'object', {
|
||||
moduleName: 'workbox-background-sync',
|
||||
className: 'Queue',
|
||||
funcName: 'pushRequest',
|
||||
paramName: 'entry'
|
||||
});
|
||||
assert_mjs.assert.isInstance(entry.request, Request, {
|
||||
moduleName: 'workbox-background-sync',
|
||||
className: 'Queue',
|
||||
funcName: 'pushRequest',
|
||||
paramName: 'entry.request'
|
||||
});
|
||||
}
|
||||
|
||||
await this._addRequest(entry, 'push');
|
||||
}
|
||||
/**
|
||||
* Stores the passed request in IndexedDB (with its timestamp and any
|
||||
* metadata) at the beginning of the queue.
|
||||
*
|
||||
* @param {Object} entry
|
||||
* @param {Request} entry.request The request to store in the queue.
|
||||
* @param {Object} [entry.metadata] Any metadata you want associated with the
|
||||
* stored request. When requests are replayed you'll have access to this
|
||||
* metadata object in case you need to modify the request beforehand.
|
||||
* @param {number} [entry.timestamp] The timestamp (Epoch time in
|
||||
* milliseconds) when the request was first added to the queue. This is
|
||||
* used along with `maxRetentionTime` to remove outdated requests. In
|
||||
* general you don't need to set this value, as it's automatically set
|
||||
* for you (defaulting to `Date.now()`), but you can update it if you
|
||||
* don't want particular requests to expire.
|
||||
*/
|
||||
|
||||
|
||||
async unshiftRequest(entry) {
|
||||
{
|
||||
assert_mjs.assert.isType(entry, 'object', {
|
||||
moduleName: 'workbox-background-sync',
|
||||
className: 'Queue',
|
||||
funcName: 'unshiftRequest',
|
||||
paramName: 'entry'
|
||||
});
|
||||
assert_mjs.assert.isInstance(entry.request, Request, {
|
||||
moduleName: 'workbox-background-sync',
|
||||
className: 'Queue',
|
||||
funcName: 'unshiftRequest',
|
||||
paramName: 'entry.request'
|
||||
});
|
||||
}
|
||||
|
||||
await this._addRequest(entry, 'unshift');
|
||||
}
|
||||
/**
|
||||
* Removes and returns the last request in the queue (along with its
|
||||
* timestamp and any metadata). The returned object takes the form:
|
||||
* `{request, timestamp, metadata}`.
|
||||
*
|
||||
* @return {Promise<Object>}
|
||||
*/
|
||||
|
||||
|
||||
async popRequest() {
|
||||
return this._removeRequest('pop');
|
||||
}
|
||||
/**
|
||||
* Removes and returns the first request in the queue (along with its
|
||||
* timestamp and any metadata). The returned object takes the form:
|
||||
* `{request, timestamp, metadata}`.
|
||||
*
|
||||
* @return {Promise<Object>}
|
||||
*/
|
||||
|
||||
|
||||
async shiftRequest() {
|
||||
return this._removeRequest('shift');
|
||||
}
|
||||
/**
|
||||
* Returns all the entries that have not expired (per `maxRetentionTime`).
|
||||
* Any expired entries are removed from the queue.
|
||||
*
|
||||
* @return {Promise<Array<Object>>}
|
||||
*/
|
||||
|
||||
|
||||
async getAll() {
|
||||
const allEntries = await this._queueStore.getAll();
|
||||
const now = Date.now();
|
||||
const unexpiredEntries = [];
|
||||
|
||||
for (const entry of allEntries) {
|
||||
// Ignore requests older than maxRetentionTime. Call this function
|
||||
// recursively until an unexpired request is found.
|
||||
const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000;
|
||||
|
||||
if (now - entry.timestamp > maxRetentionTimeInMs) {
|
||||
await this._queueStore.deleteEntry(entry.id);
|
||||
} else {
|
||||
unexpiredEntries.push(convertEntry(entry));
|
||||
}
|
||||
}
|
||||
|
||||
return unexpiredEntries;
|
||||
}
|
||||
/**
|
||||
* Adds the entry to the QueueStore and registers for a sync event.
|
||||
*
|
||||
* @param {Object} entry
|
||||
* @param {Request} entry.request
|
||||
* @param {Object} [entry.metadata]
|
||||
* @param {number} [entry.timestamp=Date.now()]
|
||||
* @param {string} operation ('push' or 'unshift')
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async _addRequest({
|
||||
request,
|
||||
metadata,
|
||||
timestamp = Date.now()
|
||||
}, operation) {
|
||||
const storableRequest = await StorableRequest.fromRequest(request.clone());
|
||||
const entry = {
|
||||
requestData: storableRequest.toObject(),
|
||||
timestamp
|
||||
}; // Only include metadata if it's present.
|
||||
|
||||
if (metadata) {
|
||||
entry.metadata = metadata;
|
||||
}
|
||||
|
||||
await this._queueStore[`${operation}Entry`](entry);
|
||||
|
||||
{
|
||||
logger_mjs.logger.log(`Request for '${getFriendlyURL_mjs.getFriendlyURL(request.url)}' has ` + `been added to background sync queue '${this._name}'.`);
|
||||
} // Don't register for a sync if we're in the middle of a sync. Instead,
|
||||
// we wait until the sync is complete and call register if
|
||||
// `this._requestsAddedDuringSync` is true.
|
||||
|
||||
|
||||
if (this._syncInProgress) {
|
||||
this._requestsAddedDuringSync = true;
|
||||
} else {
|
||||
await this.registerSync();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Removes and returns the first or last (depending on `operation`) entry
|
||||
* from the QueueStore that's not older than the `maxRetentionTime`.
|
||||
*
|
||||
* @param {string} operation ('pop' or 'shift')
|
||||
* @return {Object|undefined}
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async _removeRequest(operation) {
|
||||
const now = Date.now();
|
||||
const entry = await this._queueStore[`${operation}Entry`]();
|
||||
|
||||
if (entry) {
|
||||
// Ignore requests older than maxRetentionTime. Call this function
|
||||
// recursively until an unexpired request is found.
|
||||
const maxRetentionTimeInMs = this._maxRetentionTime * 60 * 1000;
|
||||
|
||||
if (now - entry.timestamp > maxRetentionTimeInMs) {
|
||||
return this._removeRequest(operation);
|
||||
}
|
||||
|
||||
return convertEntry(entry);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Loops through each request in the queue and attempts to re-fetch it.
|
||||
* If any request fails to re-fetch, it's put back in the same position in
|
||||
* the queue (which registers a retry for the next sync event).
|
||||
*/
|
||||
|
||||
|
||||
async replayRequests() {
|
||||
let entry;
|
||||
|
||||
while (entry = await this.shiftRequest()) {
|
||||
try {
|
||||
await fetch(entry.request.clone());
|
||||
|
||||
{
|
||||
logger_mjs.logger.log(`Request for '${getFriendlyURL_mjs.getFriendlyURL(entry.request.url)}'` + `has been replayed in queue '${this._name}'`);
|
||||
}
|
||||
} catch (error) {
|
||||
await this.unshiftRequest(entry);
|
||||
|
||||
{
|
||||
logger_mjs.logger.log(`Request for '${getFriendlyURL_mjs.getFriendlyURL(entry.request.url)}'` + `failed to replay, putting it back in queue '${this._name}'`);
|
||||
}
|
||||
|
||||
throw new WorkboxError_mjs.WorkboxError('queue-replay-failed', {
|
||||
name: this._name
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
logger_mjs.logger.log(`All requests in queue '${this.name}' have successfully ` + `replayed; the queue is now empty!`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Registers a sync event with a tag unique to this instance.
|
||||
*/
|
||||
|
||||
|
||||
async registerSync() {
|
||||
if ('sync' in registration) {
|
||||
try {
|
||||
await registration.sync.register(`${TAG_PREFIX}:${this._name}`);
|
||||
} catch (err) {
|
||||
// This means the registration failed for some reason, possibly due to
|
||||
// the user disabling it.
|
||||
{
|
||||
logger_mjs.logger.warn(`Unable to register sync event for '${this._name}'.`, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* In sync-supporting browsers, this adds a listener for the sync event.
|
||||
* In non-sync-supporting browsers, this will retry the queue on service
|
||||
* worker startup.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_addSyncListener() {
|
||||
if ('sync' in registration) {
|
||||
self.addEventListener('sync', event => {
|
||||
if (event.tag === `${TAG_PREFIX}:${this._name}`) {
|
||||
{
|
||||
logger_mjs.logger.log(`Background sync for tag '${event.tag}'` + `has been received`);
|
||||
}
|
||||
|
||||
const syncComplete = async () => {
|
||||
this._syncInProgress = true;
|
||||
let syncError;
|
||||
|
||||
try {
|
||||
await this._onSync({
|
||||
queue: this
|
||||
});
|
||||
} catch (error) {
|
||||
syncError = error; // Rethrow the error. Note: the logic in the finally clause
|
||||
// will run before this gets rethrown.
|
||||
|
||||
throw syncError;
|
||||
} finally {
|
||||
// New items may have been added to the queue during the sync,
|
||||
// so we need to register for a new sync if that's happened...
|
||||
// Unless there was an error during the sync, in which
|
||||
// case the browser will automatically retry later, as long
|
||||
// as `event.lastChance` is not true.
|
||||
if (this._requestsAddedDuringSync && !(syncError && !event.lastChance)) {
|
||||
await this.registerSync();
|
||||
}
|
||||
|
||||
this._syncInProgress = false;
|
||||
this._requestsAddedDuringSync = false;
|
||||
}
|
||||
};
|
||||
|
||||
event.waitUntil(syncComplete());
|
||||
}
|
||||
});
|
||||
} else {
|
||||
{
|
||||
logger_mjs.logger.log(`Background sync replaying without background sync event`);
|
||||
} // If the browser doesn't support background sync, retry
|
||||
// every time the service worker starts up as a fallback.
|
||||
|
||||
|
||||
this._onSync({
|
||||
queue: this
|
||||
});
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Returns the set of queue names. This is primarily used to reset the list
|
||||
* of queue names in tests.
|
||||
*
|
||||
* @return {Set}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
static get _queueNames() {
|
||||
return queueNames;
|
||||
}
|
||||
|
||||
}
|
||||
/**
|
||||
* Converts a QueueStore entry into the format exposed by Queue. This entails
|
||||
* converting the request data into a real request and omitting the `id` and
|
||||
* `queueName` properties.
|
||||
*
|
||||
* @param {Object} queueStoreEntry
|
||||
* @return {Object}
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
const convertEntry = queueStoreEntry => {
|
||||
const queueEntry = {
|
||||
request: new StorableRequest(queueStoreEntry.requestData).toRequest(),
|
||||
timestamp: queueStoreEntry.timestamp
|
||||
};
|
||||
|
||||
if (queueStoreEntry.metadata) {
|
||||
queueEntry.metadata = queueStoreEntry.metadata;
|
||||
}
|
||||
|
||||
return queueEntry;
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* A class implementing the `fetchDidFail` lifecycle callback. This makes it
|
||||
* easier to add failed requests to a background sync Queue.
|
||||
*
|
||||
* @memberof workbox.backgroundSync
|
||||
*/
|
||||
|
||||
class Plugin {
|
||||
/**
|
||||
* @param {...*} queueArgs Args to forward to the composed Queue instance.
|
||||
* See the [Queue]{@link workbox.backgroundSync.Queue} documentation for
|
||||
* parameter details.
|
||||
*/
|
||||
constructor(...queueArgs) {
|
||||
this._queue = new Queue(...queueArgs);
|
||||
this.fetchDidFail = this.fetchDidFail.bind(this);
|
||||
}
|
||||
/**
|
||||
* @param {Object} options
|
||||
* @param {Request} options.request
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async fetchDidFail({
|
||||
request
|
||||
}) {
|
||||
await this._queue.pushRequest({
|
||||
request
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
exports.Queue = Queue;
|
||||
exports.Plugin = Plugin;
|
||||
|
||||
return exports;
|
||||
|
||||
}({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private));
|
||||
//# sourceMappingURL=workbox-background-sync.dev.js.map
|
|
@ -0,0 +1,2 @@
|
|||
this.workbox=this.workbox||{},this.workbox.backgroundSync=function(t,e,s){"use strict";try{self["workbox:background-sync:4.3.1"]&&_()}catch(t){}const i=3,n="workbox-background-sync",a="requests",r="queueName";class c{constructor(t){this.t=t,this.s=new s.DBWrapper(n,i,{onupgradeneeded:this.i})}async pushEntry(t){delete t.id,t.queueName=this.t,await this.s.add(a,t)}async unshiftEntry(t){const[e]=await this.s.getAllMatching(a,{count:1});e?t.id=e.id-1:delete t.id,t.queueName=this.t,await this.s.add(a,t)}async popEntry(){return this.h({direction:"prev"})}async shiftEntry(){return this.h({direction:"next"})}async getAll(){return await this.s.getAllMatching(a,{index:r,query:IDBKeyRange.only(this.t)})}async deleteEntry(t){await this.s.delete(a,t)}async h({direction:t}){const[e]=await this.s.getAllMatching(a,{direction:t,index:r,query:IDBKeyRange.only(this.t),count:1});if(e)return await this.deleteEntry(e.id),e}i(t){const e=t.target.result;t.oldVersion>0&&t.oldVersion<i&&e.objectStoreNames.contains(a)&&e.deleteObjectStore(a),e.createObjectStore(a,{autoIncrement:!0,keyPath:"id"}).createIndex(r,r,{unique:!1})}}const h=["method","referrer","referrerPolicy","mode","credentials","cache","redirect","integrity","keepalive"];class o{static async fromRequest(t){const e={url:t.url,headers:{}};"GET"!==t.method&&(e.body=await t.clone().arrayBuffer());for(const[s,i]of t.headers.entries())e.headers[s]=i;for(const s of h)void 0!==t[s]&&(e[s]=t[s]);return new o(e)}constructor(t){"navigate"===t.mode&&(t.mode="same-origin"),this.o=t}toObject(){const t=Object.assign({},this.o);return t.headers=Object.assign({},this.o.headers),t.body&&(t.body=t.body.slice(0)),t}toRequest(){return new Request(this.o.url,this.o)}clone(){return new o(this.toObject())}}const u="workbox-background-sync",y=10080,w=new Set;class d{constructor(t,{onSync:s,maxRetentionTime:i}={}){if(w.has(t))throw new e.WorkboxError("duplicate-queue-name",{name:t});w.add(t),this.u=t,this.l=s||this.replayRequests,this.q=i||y,this.m=new c(this.u),this.p()}get name(){return this.u}async pushRequest(t){await this.g(t,"push")}async unshiftRequest(t){await this.g(t,"unshift")}async popRequest(){return this.R("pop")}async shiftRequest(){return this.R("shift")}async getAll(){const t=await this.m.getAll(),e=Date.now(),s=[];for(const i of t){const t=60*this.q*1e3;e-i.timestamp>t?await this.m.deleteEntry(i.id):s.push(f(i))}return s}async g({request:t,metadata:e,timestamp:s=Date.now()},i){const n={requestData:(await o.fromRequest(t.clone())).toObject(),timestamp:s};e&&(n.metadata=e),await this.m[`${i}Entry`](n),this.k?this.D=!0:await this.registerSync()}async R(t){const e=Date.now(),s=await this.m[`${t}Entry`]();if(s){const i=60*this.q*1e3;return e-s.timestamp>i?this.R(t):f(s)}}async replayRequests(){let t;for(;t=await this.shiftRequest();)try{await fetch(t.request.clone())}catch(s){throw await this.unshiftRequest(t),new e.WorkboxError("queue-replay-failed",{name:this.u})}}async registerSync(){if("sync"in registration)try{await registration.sync.register(`${u}:${this.u}`)}catch(t){}}p(){"sync"in registration?self.addEventListener("sync",t=>{if(t.tag===`${u}:${this.u}`){const e=async()=>{let e;this.k=!0;try{await this.l({queue:this})}catch(t){throw e=t}finally{!this.D||e&&!t.lastChance||await this.registerSync(),this.k=!1,this.D=!1}};t.waitUntil(e())}}):this.l({queue:this})}static get _(){return w}}const f=t=>{const e={request:new o(t.requestData).toRequest(),timestamp:t.timestamp};return t.metadata&&(e.metadata=t.metadata),e};return t.Queue=d,t.Plugin=class{constructor(...t){this.v=new d(...t),this.fetchDidFail=this.fetchDidFail.bind(this)}async fetchDidFail({request:t}){await this.v.pushRequest({request:t})}},t}({},workbox.core._private,workbox.core._private);
|
||||
//# sourceMappingURL=workbox-background-sync.prod.js.map
|
|
@ -0,0 +1,496 @@
|
|||
this.workbox = this.workbox || {};
|
||||
this.workbox.broadcastUpdate = (function (exports, assert_mjs, getFriendlyURL_mjs, logger_mjs, Deferred_mjs, WorkboxError_mjs) {
|
||||
'use strict';
|
||||
|
||||
try {
|
||||
self['workbox:broadcast-update:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Given two `Response's`, compares several header values to see if they are
|
||||
* the same or not.
|
||||
*
|
||||
* @param {Response} firstResponse
|
||||
* @param {Response} secondResponse
|
||||
* @param {Array<string>} headersToCheck
|
||||
* @return {boolean}
|
||||
*
|
||||
* @memberof workbox.broadcastUpdate
|
||||
* @private
|
||||
*/
|
||||
|
||||
const responsesAreSame = (firstResponse, secondResponse, headersToCheck) => {
|
||||
{
|
||||
if (!(firstResponse instanceof Response && secondResponse instanceof Response)) {
|
||||
throw new WorkboxError_mjs.WorkboxError('invalid-responses-are-same-args');
|
||||
}
|
||||
}
|
||||
|
||||
const atLeastOneHeaderAvailable = headersToCheck.some(header => {
|
||||
return firstResponse.headers.has(header) && secondResponse.headers.has(header);
|
||||
});
|
||||
|
||||
if (!atLeastOneHeaderAvailable) {
|
||||
{
|
||||
logger_mjs.logger.warn(`Unable to determine where the response has been updated ` + `because none of the headers that would be checked are present.`);
|
||||
logger_mjs.logger.debug(`Attempting to compare the following: `, firstResponse, secondResponse, headersToCheck);
|
||||
} // Just return true, indicating the that responses are the same, since we
|
||||
// can't determine otherwise.
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return headersToCheck.every(header => {
|
||||
const headerStateComparison = firstResponse.headers.has(header) === secondResponse.headers.has(header);
|
||||
const headerValueComparison = firstResponse.headers.get(header) === secondResponse.headers.get(header);
|
||||
return headerStateComparison && headerValueComparison;
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
const CACHE_UPDATED_MESSAGE_TYPE = 'CACHE_UPDATED';
|
||||
const CACHE_UPDATED_MESSAGE_META = 'workbox-broadcast-update';
|
||||
const DEFAULT_BROADCAST_CHANNEL_NAME = 'workbox';
|
||||
const DEFAULT_DEFER_NOTIFICATION_TIMEOUT = 10000;
|
||||
const DEFAULT_HEADERS_TO_CHECK = ['content-length', 'etag', 'last-modified'];
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* 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: 'workbox-broadcast-update',
|
||||
* payload: {
|
||||
* cacheName: 'the-cache-name',
|
||||
* updatedURL: 'https://example.com/'
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* (Usage of [Flux](https://facebook.github.io/flux/) itself is not at
|
||||
* all required.)
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {string} options.cacheName The name of the cache in which the updated
|
||||
* `Response` was stored.
|
||||
* @param {string} options.url The URL associated with the updated `Response`.
|
||||
* @param {BroadcastChannel} [options.channel] The `BroadcastChannel` to use.
|
||||
* If no channel is set or the browser doesn't support the BroadcastChannel
|
||||
* api, then an attempt will be made to `postMessage` each window client.
|
||||
*
|
||||
* @memberof workbox.broadcastUpdate
|
||||
*/
|
||||
|
||||
const broadcastUpdate = async ({
|
||||
channel,
|
||||
cacheName,
|
||||
url
|
||||
}) => {
|
||||
{
|
||||
assert_mjs.assert.isType(cacheName, 'string', {
|
||||
moduleName: 'workbox-broadcast-update',
|
||||
className: '~',
|
||||
funcName: 'broadcastUpdate',
|
||||
paramName: 'cacheName'
|
||||
});
|
||||
assert_mjs.assert.isType(url, 'string', {
|
||||
moduleName: 'workbox-broadcast-update',
|
||||
className: '~',
|
||||
funcName: 'broadcastUpdate',
|
||||
paramName: 'url'
|
||||
});
|
||||
}
|
||||
|
||||
const data = {
|
||||
type: CACHE_UPDATED_MESSAGE_TYPE,
|
||||
meta: CACHE_UPDATED_MESSAGE_META,
|
||||
payload: {
|
||||
cacheName: cacheName,
|
||||
updatedURL: url
|
||||
}
|
||||
};
|
||||
|
||||
if (channel) {
|
||||
channel.postMessage(data);
|
||||
} else {
|
||||
const windows = await clients.matchAll({
|
||||
type: 'window'
|
||||
});
|
||||
|
||||
for (const win of windows) {
|
||||
win.postMessage(data);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Uses the [Broadcast Channel API]{@link https://developers.google.com/web/updates/2016/09/broadcastchannel}
|
||||
* to notify interested parties when a cached response has been updated.
|
||||
* In browsers that do not support the Broadcast Channel API, the instance
|
||||
* falls back to sending the update via `postMessage()` to all window clients.
|
||||
*
|
||||
* For efficiency's sake, the underlying response bodies are not compared;
|
||||
* only specific response headers are checked.
|
||||
*
|
||||
* @memberof workbox.broadcastUpdate
|
||||
*/
|
||||
|
||||
class BroadcastCacheUpdate {
|
||||
/**
|
||||
* Construct a BroadcastCacheUpdate instance with a specific `channelName` to
|
||||
* broadcast messages on
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Array<string>}
|
||||
* [options.headersToCheck=['content-length', 'etag', 'last-modified']]
|
||||
* A list of headers that will be used to determine whether the responses
|
||||
* differ.
|
||||
* @param {string} [options.channelName='workbox'] The name that will be used
|
||||
*. when creating the `BroadcastChannel`, which defaults to 'workbox' (the
|
||||
* channel name used by the `workbox-window` package).
|
||||
* @param {string} [options.deferNoticationTimeout=10000] The amount of time
|
||||
* to wait for a ready message from the window on navigation requests
|
||||
* before sending the update.
|
||||
*/
|
||||
constructor({
|
||||
headersToCheck,
|
||||
channelName,
|
||||
deferNoticationTimeout
|
||||
} = {}) {
|
||||
this._headersToCheck = headersToCheck || DEFAULT_HEADERS_TO_CHECK;
|
||||
this._channelName = channelName || DEFAULT_BROADCAST_CHANNEL_NAME;
|
||||
this._deferNoticationTimeout = deferNoticationTimeout || DEFAULT_DEFER_NOTIFICATION_TIMEOUT;
|
||||
|
||||
{
|
||||
assert_mjs.assert.isType(this._channelName, 'string', {
|
||||
moduleName: 'workbox-broadcast-update',
|
||||
className: 'BroadcastCacheUpdate',
|
||||
funcName: 'constructor',
|
||||
paramName: 'channelName'
|
||||
});
|
||||
assert_mjs.assert.isArray(this._headersToCheck, {
|
||||
moduleName: 'workbox-broadcast-update',
|
||||
className: 'BroadcastCacheUpdate',
|
||||
funcName: 'constructor',
|
||||
paramName: 'headersToCheck'
|
||||
});
|
||||
}
|
||||
|
||||
this._initWindowReadyDeferreds();
|
||||
}
|
||||
/**
|
||||
* Compare two [Responses](https://developer.mozilla.org/en-US/docs/Web/API/Response)
|
||||
* and send a message via the
|
||||
* {@link https://developers.google.com/web/updates/2016/09/broadcastchannel|Broadcast Channel API}
|
||||
* if they differ.
|
||||
*
|
||||
* Neither of the Responses can be {@link http://stackoverflow.com/questions/39109789|opaque}.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Response} options.oldResponse Cached response to compare.
|
||||
* @param {Response} options.newResponse Possibly updated response to compare.
|
||||
* @param {string} options.url The URL of the request.
|
||||
* @param {string} options.cacheName Name of the cache the responses belong
|
||||
* to. This is included in the broadcast message.
|
||||
* @param {Event} [options.event] event An optional event that triggered
|
||||
* this possible cache update.
|
||||
* @return {Promise} Resolves once the update is sent.
|
||||
*/
|
||||
|
||||
|
||||
notifyIfUpdated({
|
||||
oldResponse,
|
||||
newResponse,
|
||||
url,
|
||||
cacheName,
|
||||
event
|
||||
}) {
|
||||
if (!responsesAreSame(oldResponse, newResponse, this._headersToCheck)) {
|
||||
{
|
||||
logger_mjs.logger.log(`Newer response found (and cached) for:`, url);
|
||||
}
|
||||
|
||||
const sendUpdate = async () => {
|
||||
// In the case of a navigation request, the requesting page will likely
|
||||
// not have loaded its JavaScript in time to recevied the update
|
||||
// notification, so we defer it until ready (or we timeout waiting).
|
||||
if (event && event.request && event.request.mode === 'navigate') {
|
||||
{
|
||||
logger_mjs.logger.debug(`Original request was a navigation request, ` + `waiting for a ready message from the window`, event.request);
|
||||
}
|
||||
|
||||
await this._windowReadyOrTimeout(event);
|
||||
}
|
||||
|
||||
await this._broadcastUpdate({
|
||||
channel: this._getChannel(),
|
||||
cacheName,
|
||||
url
|
||||
});
|
||||
}; // Send the update and ensure the SW stays alive until it's sent.
|
||||
|
||||
|
||||
const done = sendUpdate();
|
||||
|
||||
if (event) {
|
||||
try {
|
||||
event.waitUntil(done);
|
||||
} catch (error) {
|
||||
{
|
||||
logger_mjs.logger.warn(`Unable to ensure service worker stays alive ` + `when broadcasting cache update for ` + `${getFriendlyURL_mjs.getFriendlyURL(event.request.url)}'.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return done;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* NOTE: this is exposed on the instance primarily so it can be spied on
|
||||
* in tests.
|
||||
*
|
||||
* @param {Object} opts
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async _broadcastUpdate(opts) {
|
||||
await broadcastUpdate(opts);
|
||||
}
|
||||
/**
|
||||
* @return {BroadcastChannel|undefined} The BroadcastChannel instance used for
|
||||
* broadcasting updates, or undefined if the browser doesn't support the
|
||||
* Broadcast Channel API.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_getChannel() {
|
||||
if ('BroadcastChannel' in self && !this._channel) {
|
||||
this._channel = new BroadcastChannel(this._channelName);
|
||||
}
|
||||
|
||||
return this._channel;
|
||||
}
|
||||
/**
|
||||
* Waits for a message from the window indicating that it's capable of
|
||||
* receiving broadcasts. By default, this will only wait for the amount of
|
||||
* time specified via the `deferNoticationTimeout` option.
|
||||
*
|
||||
* @param {Event} event The navigation fetch event.
|
||||
* @return {Promise}
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_windowReadyOrTimeout(event) {
|
||||
if (!this._navigationEventsDeferreds.has(event)) {
|
||||
const deferred = new Deferred_mjs.Deferred(); // Set the deferred on the `_navigationEventsDeferreds` map so it will
|
||||
// be resolved when the next ready message event comes.
|
||||
|
||||
this._navigationEventsDeferreds.set(event, deferred); // But don't wait too long for the message since it may never come.
|
||||
|
||||
|
||||
const timeout = setTimeout(() => {
|
||||
{
|
||||
logger_mjs.logger.debug(`Timed out after ${this._deferNoticationTimeout}` + `ms waiting for message from window`);
|
||||
}
|
||||
|
||||
deferred.resolve();
|
||||
}, this._deferNoticationTimeout); // Ensure the timeout is cleared if the deferred promise is resolved.
|
||||
|
||||
deferred.promise.then(() => clearTimeout(timeout));
|
||||
}
|
||||
|
||||
return this._navigationEventsDeferreds.get(event).promise;
|
||||
}
|
||||
/**
|
||||
* Creates a mapping between navigation fetch events and deferreds, and adds
|
||||
* a listener for message events from the window. When message events arrive,
|
||||
* all deferreds in the mapping are resolved.
|
||||
*
|
||||
* Note: it would be easier if we could only resolve the deferred of
|
||||
* navigation fetch event whose client ID matched the source ID of the
|
||||
* message event, but currently client IDs are not exposed on navigation
|
||||
* fetch events: https://www.chromestatus.com/feature/4846038800138240
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_initWindowReadyDeferreds() {
|
||||
// A mapping between navigation events and their deferreds.
|
||||
this._navigationEventsDeferreds = new Map(); // The message listener needs to be added in the initial run of the
|
||||
// service worker, but since we don't actually need to be listening for
|
||||
// messages until the cache updates, we only invoke the callback if set.
|
||||
|
||||
self.addEventListener('message', event => {
|
||||
if (event.data.type === 'WINDOW_READY' && event.data.meta === 'workbox-window' && this._navigationEventsDeferreds.size > 0) {
|
||||
{
|
||||
logger_mjs.logger.debug(`Received WINDOW_READY event: `, event);
|
||||
} // Resolve any pending deferreds.
|
||||
|
||||
|
||||
for (const deferred of this._navigationEventsDeferreds.values()) {
|
||||
deferred.resolve();
|
||||
}
|
||||
|
||||
this._navigationEventsDeferreds.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* This plugin will automatically broadcast a message whenever a cached response
|
||||
* is updated.
|
||||
*
|
||||
* @memberof workbox.broadcastUpdate
|
||||
*/
|
||||
|
||||
class Plugin {
|
||||
/**
|
||||
* Construct a BroadcastCacheUpdate instance with the passed options and
|
||||
* calls its `notifyIfUpdated()` method whenever the plugin's
|
||||
* `cacheDidUpdate` callback is invoked.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Array<string>}
|
||||
* [options.headersToCheck=['content-length', 'etag', 'last-modified']]
|
||||
* A list of headers that will be used to determine whether the responses
|
||||
* differ.
|
||||
* @param {string} [options.channelName='workbox'] The name that will be used
|
||||
*. when creating the `BroadcastChannel`, which defaults to 'workbox' (the
|
||||
* channel name used by the `workbox-window` package).
|
||||
* @param {string} [options.deferNoticationTimeout=10000] The amount of time
|
||||
* to wait for a ready message from the window on navigation requests
|
||||
* before sending the update.
|
||||
*/
|
||||
constructor(options) {
|
||||
this._broadcastUpdate = new BroadcastCacheUpdate(options);
|
||||
}
|
||||
/**
|
||||
* A "lifecycle" callback that will be triggered automatically by the
|
||||
* `workbox-sw` and `workbox-runtime-caching` handlers when an entry is
|
||||
* added to a cache.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} options The input object to this function.
|
||||
* @param {string} options.cacheName Name of the cache being updated.
|
||||
* @param {Response} [options.oldResponse] The previous cached value, if any.
|
||||
* @param {Response} options.newResponse The new value in the cache.
|
||||
* @param {Request} options.request The request that triggered the udpate.
|
||||
* @param {Request} [options.event] The event that triggered the update.
|
||||
*/
|
||||
|
||||
|
||||
cacheDidUpdate({
|
||||
cacheName,
|
||||
oldResponse,
|
||||
newResponse,
|
||||
request,
|
||||
event
|
||||
}) {
|
||||
{
|
||||
assert_mjs.assert.isType(cacheName, 'string', {
|
||||
moduleName: 'workbox-broadcast-update',
|
||||
className: 'Plugin',
|
||||
funcName: 'cacheDidUpdate',
|
||||
paramName: 'cacheName'
|
||||
});
|
||||
assert_mjs.assert.isInstance(newResponse, Response, {
|
||||
moduleName: 'workbox-broadcast-update',
|
||||
className: 'Plugin',
|
||||
funcName: 'cacheDidUpdate',
|
||||
paramName: 'newResponse'
|
||||
});
|
||||
assert_mjs.assert.isInstance(request, Request, {
|
||||
moduleName: 'workbox-broadcast-update',
|
||||
className: 'Plugin',
|
||||
funcName: 'cacheDidUpdate',
|
||||
paramName: 'request'
|
||||
});
|
||||
}
|
||||
|
||||
if (!oldResponse) {
|
||||
// Without a two responses there is nothing to compare.
|
||||
return;
|
||||
}
|
||||
|
||||
this._broadcastUpdate.notifyIfUpdated({
|
||||
cacheName,
|
||||
oldResponse,
|
||||
newResponse,
|
||||
event,
|
||||
url: request.url
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
exports.BroadcastCacheUpdate = BroadcastCacheUpdate;
|
||||
exports.Plugin = Plugin;
|
||||
exports.broadcastUpdate = broadcastUpdate;
|
||||
exports.responsesAreSame = responsesAreSame;
|
||||
|
||||
return exports;
|
||||
|
||||
}({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private));
|
||||
//# sourceMappingURL=workbox-broadcast-update.dev.js.map
|
|
@ -0,0 +1,2 @@
|
|||
this.workbox=this.workbox||{},this.workbox.broadcastUpdate=function(e,t){"use strict";try{self["workbox:broadcast-update:4.3.1"]&&_()}catch(e){}const s=(e,t,s)=>{return!s.some(s=>e.headers.has(s)&&t.headers.has(s))||s.every(s=>{const n=e.headers.has(s)===t.headers.has(s),a=e.headers.get(s)===t.headers.get(s);return n&&a})},n="workbox",a=1e4,i=["content-length","etag","last-modified"],o=async({channel:e,cacheName:t,url:s})=>{const n={type:"CACHE_UPDATED",meta:"workbox-broadcast-update",payload:{cacheName:t,updatedURL:s}};if(e)e.postMessage(n);else{const e=await clients.matchAll({type:"window"});for(const t of e)t.postMessage(n)}};class c{constructor({headersToCheck:e,channelName:t,deferNoticationTimeout:s}={}){this.t=e||i,this.s=t||n,this.i=s||a,this.o()}notifyIfUpdated({oldResponse:e,newResponse:t,url:n,cacheName:a,event:i}){if(!s(e,t,this.t)){const e=(async()=>{i&&i.request&&"navigate"===i.request.mode&&await this.h(i),await this.l({channel:this.u(),cacheName:a,url:n})})();if(i)try{i.waitUntil(e)}catch(e){}return e}}async l(e){await o(e)}u(){return"BroadcastChannel"in self&&!this.p&&(this.p=new BroadcastChannel(this.s)),this.p}h(e){if(!this.m.has(e)){const s=new t.Deferred;this.m.set(e,s);const n=setTimeout(()=>{s.resolve()},this.i);s.promise.then(()=>clearTimeout(n))}return this.m.get(e).promise}o(){this.m=new Map,self.addEventListener("message",e=>{if("WINDOW_READY"===e.data.type&&"workbox-window"===e.data.meta&&this.m.size>0){for(const e of this.m.values())e.resolve();this.m.clear()}})}}return e.BroadcastCacheUpdate=c,e.Plugin=class{constructor(e){this.l=new c(e)}cacheDidUpdate({cacheName:e,oldResponse:t,newResponse:s,request:n,event:a}){t&&this.l.notifyIfUpdated({cacheName:e,oldResponse:t,newResponse:s,event:a,url:n.url})}},e.broadcastUpdate=o,e.responsesAreSame=s,e}({},workbox.core._private);
|
||||
//# sourceMappingURL=workbox-broadcast-update.prod.js.map
|
|
@ -0,0 +1,200 @@
|
|||
this.workbox = this.workbox || {};
|
||||
this.workbox.cacheableResponse = (function (exports, WorkboxError_mjs, assert_mjs, getFriendlyURL_mjs, logger_mjs) {
|
||||
'use strict';
|
||||
|
||||
try {
|
||||
self['workbox:cacheable-response:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* This class allows you to set up rules determining what
|
||||
* status codes and/or headers need to be present in order for a
|
||||
* [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
|
||||
* to be considered cacheable.
|
||||
*
|
||||
* @memberof workbox.cacheableResponse
|
||||
*/
|
||||
|
||||
class CacheableResponse {
|
||||
/**
|
||||
* To construct a new CacheableResponse instance you must provide at least
|
||||
* one of the `config` properties.
|
||||
*
|
||||
* If both `statuses` and `headers` are specified, then both conditions must
|
||||
* be met for the `Response` to be considered cacheable.
|
||||
*
|
||||
* @param {Object} config
|
||||
* @param {Array<number>} [config.statuses] One or more status codes that a
|
||||
* `Response` can have and be considered cacheable.
|
||||
* @param {Object<string,string>} [config.headers] A mapping of header names
|
||||
* and expected values that a `Response` can have and be considered cacheable.
|
||||
* If multiple headers are provided, only one needs to be present.
|
||||
*/
|
||||
constructor(config = {}) {
|
||||
{
|
||||
if (!(config.statuses || config.headers)) {
|
||||
throw new WorkboxError_mjs.WorkboxError('statuses-or-headers-required', {
|
||||
moduleName: 'workbox-cacheable-response',
|
||||
className: 'CacheableResponse',
|
||||
funcName: 'constructor'
|
||||
});
|
||||
}
|
||||
|
||||
if (config.statuses) {
|
||||
assert_mjs.assert.isArray(config.statuses, {
|
||||
moduleName: 'workbox-cacheable-response',
|
||||
className: 'CacheableResponse',
|
||||
funcName: 'constructor',
|
||||
paramName: 'config.statuses'
|
||||
});
|
||||
}
|
||||
|
||||
if (config.headers) {
|
||||
assert_mjs.assert.isType(config.headers, 'object', {
|
||||
moduleName: 'workbox-cacheable-response',
|
||||
className: 'CacheableResponse',
|
||||
funcName: 'constructor',
|
||||
paramName: 'config.headers'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this._statuses = config.statuses;
|
||||
this._headers = config.headers;
|
||||
}
|
||||
/**
|
||||
* Checks a response to see whether it's cacheable or not, based on this
|
||||
* object's configuration.
|
||||
*
|
||||
* @param {Response} response The response whose cacheability is being
|
||||
* checked.
|
||||
* @return {boolean} `true` if the `Response` is cacheable, and `false`
|
||||
* otherwise.
|
||||
*/
|
||||
|
||||
|
||||
isResponseCacheable(response) {
|
||||
{
|
||||
assert_mjs.assert.isInstance(response, Response, {
|
||||
moduleName: 'workbox-cacheable-response',
|
||||
className: 'CacheableResponse',
|
||||
funcName: 'isResponseCacheable',
|
||||
paramName: 'response'
|
||||
});
|
||||
}
|
||||
|
||||
let cacheable = true;
|
||||
|
||||
if (this._statuses) {
|
||||
cacheable = this._statuses.includes(response.status);
|
||||
}
|
||||
|
||||
if (this._headers && cacheable) {
|
||||
cacheable = Object.keys(this._headers).some(headerName => {
|
||||
return response.headers.get(headerName) === this._headers[headerName];
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
if (!cacheable) {
|
||||
logger_mjs.logger.groupCollapsed(`The request for ` + `'${getFriendlyURL_mjs.getFriendlyURL(response.url)}' returned a response that does ` + `not meet the criteria for being cached.`);
|
||||
logger_mjs.logger.groupCollapsed(`View cacheability criteria here.`);
|
||||
logger_mjs.logger.log(`Cacheable statuses: ` + JSON.stringify(this._statuses));
|
||||
logger_mjs.logger.log(`Cacheable headers: ` + JSON.stringify(this._headers, null, 2));
|
||||
logger_mjs.logger.groupEnd();
|
||||
const logFriendlyHeaders = {};
|
||||
response.headers.forEach((value, key) => {
|
||||
logFriendlyHeaders[key] = value;
|
||||
});
|
||||
logger_mjs.logger.groupCollapsed(`View response status and headers here.`);
|
||||
logger_mjs.logger.log(`Response status: ` + response.status);
|
||||
logger_mjs.logger.log(`Response headers: ` + JSON.stringify(logFriendlyHeaders, null, 2));
|
||||
logger_mjs.logger.groupEnd();
|
||||
logger_mjs.logger.groupCollapsed(`View full response details here.`);
|
||||
logger_mjs.logger.log(response.headers);
|
||||
logger_mjs.logger.log(response);
|
||||
logger_mjs.logger.groupEnd();
|
||||
logger_mjs.logger.groupEnd();
|
||||
}
|
||||
}
|
||||
|
||||
return cacheable;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* A class implementing the `cacheWillUpdate` lifecycle callback. This makes it
|
||||
* easier to add in cacheability checks to requests made via Workbox's built-in
|
||||
* strategies.
|
||||
*
|
||||
* @memberof workbox.cacheableResponse
|
||||
*/
|
||||
|
||||
class Plugin {
|
||||
/**
|
||||
* To construct a new cacheable response Plugin instance you must provide at
|
||||
* least one of the `config` properties.
|
||||
*
|
||||
* If both `statuses` and `headers` are specified, then both conditions must
|
||||
* be met for the `Response` to be considered cacheable.
|
||||
*
|
||||
* @param {Object} config
|
||||
* @param {Array<number>} [config.statuses] One or more status codes that a
|
||||
* `Response` can have and be considered cacheable.
|
||||
* @param {Object<string,string>} [config.headers] A mapping of header names
|
||||
* and expected values that a `Response` can have and be considered cacheable.
|
||||
* If multiple headers are provided, only one needs to be present.
|
||||
*/
|
||||
constructor(config) {
|
||||
this._cacheableResponse = new CacheableResponse(config);
|
||||
}
|
||||
/**
|
||||
* @param {Object} options
|
||||
* @param {Response} options.response
|
||||
* @return {boolean}
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
cacheWillUpdate({
|
||||
response
|
||||
}) {
|
||||
if (this._cacheableResponse.isResponseCacheable(response)) {
|
||||
return response;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
exports.CacheableResponse = CacheableResponse;
|
||||
exports.Plugin = Plugin;
|
||||
|
||||
return exports;
|
||||
|
||||
}({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private));
|
||||
//# sourceMappingURL=workbox-cacheable-response.dev.js.map
|
|
@ -0,0 +1,2 @@
|
|||
this.workbox=this.workbox||{},this.workbox.cacheableResponse=function(t){"use strict";try{self["workbox:cacheable-response:4.3.1"]&&_()}catch(t){}class s{constructor(t={}){this.t=t.statuses,this.s=t.headers}isResponseCacheable(t){let s=!0;return this.t&&(s=this.t.includes(t.status)),this.s&&s&&(s=Object.keys(this.s).some(s=>t.headers.get(s)===this.s[s])),s}}return t.CacheableResponse=s,t.Plugin=class{constructor(t){this.i=new s(t)}cacheWillUpdate({response:t}){return this.i.isResponseCacheable(t)?t:null}},t}({});
|
||||
//# sourceMappingURL=workbox-cacheable-response.prod.js.map
|
|
@ -0,0 +1,652 @@
|
|||
this.workbox = this.workbox || {};
|
||||
this.workbox.expiration = (function (exports, DBWrapper_mjs, deleteDatabase_mjs, WorkboxError_mjs, assert_mjs, logger_mjs, cacheNames_mjs, getFriendlyURL_mjs, registerQuotaErrorCallback_mjs) {
|
||||
'use strict';
|
||||
|
||||
try {
|
||||
self['workbox:expiration:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
const DB_NAME = 'workbox-expiration';
|
||||
const OBJECT_STORE_NAME = 'cache-entries';
|
||||
|
||||
const normalizeURL = unNormalizedUrl => {
|
||||
const url = new URL(unNormalizedUrl, location);
|
||||
url.hash = '';
|
||||
return url.href;
|
||||
};
|
||||
/**
|
||||
* Returns the timestamp model.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
class CacheTimestampsModel {
|
||||
/**
|
||||
*
|
||||
* @param {string} cacheName
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
constructor(cacheName) {
|
||||
this._cacheName = cacheName;
|
||||
this._db = new DBWrapper_mjs.DBWrapper(DB_NAME, 1, {
|
||||
onupgradeneeded: event => this._handleUpgrade(event)
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Should perform an upgrade of indexedDB.
|
||||
*
|
||||
* @param {Event} event
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_handleUpgrade(event) {
|
||||
const db = event.target.result; // TODO(philipwalton): EdgeHTML doesn't support arrays as a keyPath, so we
|
||||
// have to use the `id` keyPath here and create our own values (a
|
||||
// concatenation of `url + cacheName`) instead of simply using
|
||||
// `keyPath: ['url', 'cacheName']`, which is supported in other browsers.
|
||||
|
||||
const objStore = db.createObjectStore(OBJECT_STORE_NAME, {
|
||||
keyPath: 'id'
|
||||
}); // TODO(philipwalton): once we don't have to support EdgeHTML, we can
|
||||
// create a single index with the keyPath `['cacheName', 'timestamp']`
|
||||
// instead of doing both these indexes.
|
||||
|
||||
objStore.createIndex('cacheName', 'cacheName', {
|
||||
unique: false
|
||||
});
|
||||
objStore.createIndex('timestamp', 'timestamp', {
|
||||
unique: false
|
||||
}); // Previous versions of `workbox-expiration` used `this._cacheName`
|
||||
// as the IDBDatabase name.
|
||||
|
||||
deleteDatabase_mjs.deleteDatabase(this._cacheName);
|
||||
}
|
||||
/**
|
||||
* @param {string} url
|
||||
* @param {number} timestamp
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async setTimestamp(url, timestamp) {
|
||||
url = normalizeURL(url);
|
||||
await this._db.put(OBJECT_STORE_NAME, {
|
||||
url,
|
||||
timestamp,
|
||||
cacheName: this._cacheName,
|
||||
// Creating an ID from the URL and cache name won't be necessary once
|
||||
// Edge switches to Chromium and all browsers we support work with
|
||||
// array keyPaths.
|
||||
id: this._getId(url)
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Returns the timestamp stored for a given URL.
|
||||
*
|
||||
* @param {string} url
|
||||
* @return {number}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async getTimestamp(url) {
|
||||
const entry = await this._db.get(OBJECT_STORE_NAME, this._getId(url));
|
||||
return entry.timestamp;
|
||||
}
|
||||
/**
|
||||
* Iterates through all the entries in the object store (from newest to
|
||||
* oldest) and removes entries once either `maxCount` is reached or the
|
||||
* entry's timestamp is less than `minTimestamp`.
|
||||
*
|
||||
* @param {number} minTimestamp
|
||||
* @param {number} maxCount
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async expireEntries(minTimestamp, maxCount) {
|
||||
const entriesToDelete = await this._db.transaction(OBJECT_STORE_NAME, 'readwrite', (txn, done) => {
|
||||
const store = txn.objectStore(OBJECT_STORE_NAME);
|
||||
const entriesToDelete = [];
|
||||
let entriesNotDeletedCount = 0;
|
||||
|
||||
store.index('timestamp').openCursor(null, 'prev').onsuccess = ({
|
||||
target
|
||||
}) => {
|
||||
const cursor = target.result;
|
||||
|
||||
if (cursor) {
|
||||
const result = cursor.value; // TODO(philipwalton): once we can use a multi-key index, we
|
||||
// won't have to check `cacheName` here.
|
||||
|
||||
if (result.cacheName === this._cacheName) {
|
||||
// Delete an entry if it's older than the max age or
|
||||
// if we already have the max number allowed.
|
||||
if (minTimestamp && result.timestamp < minTimestamp || maxCount && entriesNotDeletedCount >= maxCount) {
|
||||
// TODO(philipwalton): we should be able to delete the
|
||||
// entry right here, but doing so causes an iteration
|
||||
// bug in Safari stable (fixed in TP). Instead we can
|
||||
// store the keys of the entries to delete, and then
|
||||
// delete the separate transactions.
|
||||
// https://github.com/GoogleChrome/workbox/issues/1978
|
||||
// cursor.delete();
|
||||
// We only need to return the URL, not the whole entry.
|
||||
entriesToDelete.push(cursor.value);
|
||||
} else {
|
||||
entriesNotDeletedCount++;
|
||||
}
|
||||
}
|
||||
|
||||
cursor.continue();
|
||||
} else {
|
||||
done(entriesToDelete);
|
||||
}
|
||||
};
|
||||
}); // TODO(philipwalton): once the Safari bug in the following issue is fixed,
|
||||
// we should be able to remove this loop and do the entry deletion in the
|
||||
// cursor loop above:
|
||||
// https://github.com/GoogleChrome/workbox/issues/1978
|
||||
|
||||
const urlsDeleted = [];
|
||||
|
||||
for (const entry of entriesToDelete) {
|
||||
await this._db.delete(OBJECT_STORE_NAME, entry.id);
|
||||
urlsDeleted.push(entry.url);
|
||||
}
|
||||
|
||||
return urlsDeleted;
|
||||
}
|
||||
/**
|
||||
* Takes a URL and returns an ID that will be unique in the object store.
|
||||
*
|
||||
* @param {string} url
|
||||
* @return {string}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_getId(url) {
|
||||
// Creating an ID from the URL and cache name won't be necessary once
|
||||
// Edge switches to Chromium and all browsers we support work with
|
||||
// array keyPaths.
|
||||
return this._cacheName + '|' + normalizeURL(url);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* The `CacheExpiration` class allows you define an expiration and / or
|
||||
* limit on the number of responses stored in a
|
||||
* [`Cache`](https://developer.mozilla.org/en-US/docs/Web/API/Cache).
|
||||
*
|
||||
* @memberof workbox.expiration
|
||||
*/
|
||||
|
||||
class CacheExpiration {
|
||||
/**
|
||||
* To construct a new CacheExpiration instance you must provide at least
|
||||
* one of the `config` properties.
|
||||
*
|
||||
* @param {string} cacheName Name of the cache to apply restrictions to.
|
||||
* @param {Object} config
|
||||
* @param {number} [config.maxEntries] The maximum number of entries to cache.
|
||||
* Entries used the least will be removed as the maximum is reached.
|
||||
* @param {number} [config.maxAgeSeconds] The maximum age of an entry before
|
||||
* it's treated as stale and removed.
|
||||
*/
|
||||
constructor(cacheName, config = {}) {
|
||||
{
|
||||
assert_mjs.assert.isType(cacheName, 'string', {
|
||||
moduleName: 'workbox-expiration',
|
||||
className: 'CacheExpiration',
|
||||
funcName: 'constructor',
|
||||
paramName: 'cacheName'
|
||||
});
|
||||
|
||||
if (!(config.maxEntries || config.maxAgeSeconds)) {
|
||||
throw new WorkboxError_mjs.WorkboxError('max-entries-or-age-required', {
|
||||
moduleName: 'workbox-expiration',
|
||||
className: 'CacheExpiration',
|
||||
funcName: 'constructor'
|
||||
});
|
||||
}
|
||||
|
||||
if (config.maxEntries) {
|
||||
assert_mjs.assert.isType(config.maxEntries, 'number', {
|
||||
moduleName: 'workbox-expiration',
|
||||
className: 'CacheExpiration',
|
||||
funcName: 'constructor',
|
||||
paramName: 'config.maxEntries'
|
||||
}); // TODO: Assert is positive
|
||||
}
|
||||
|
||||
if (config.maxAgeSeconds) {
|
||||
assert_mjs.assert.isType(config.maxAgeSeconds, 'number', {
|
||||
moduleName: 'workbox-expiration',
|
||||
className: 'CacheExpiration',
|
||||
funcName: 'constructor',
|
||||
paramName: 'config.maxAgeSeconds'
|
||||
}); // TODO: Assert is positive
|
||||
}
|
||||
}
|
||||
|
||||
this._isRunning = false;
|
||||
this._rerunRequested = false;
|
||||
this._maxEntries = config.maxEntries;
|
||||
this._maxAgeSeconds = config.maxAgeSeconds;
|
||||
this._cacheName = cacheName;
|
||||
this._timestampModel = new CacheTimestampsModel(cacheName);
|
||||
}
|
||||
/**
|
||||
* Expires entries for the given cache and given criteria.
|
||||
*/
|
||||
|
||||
|
||||
async expireEntries() {
|
||||
if (this._isRunning) {
|
||||
this._rerunRequested = true;
|
||||
return;
|
||||
}
|
||||
|
||||
this._isRunning = true;
|
||||
const minTimestamp = this._maxAgeSeconds ? Date.now() - this._maxAgeSeconds * 1000 : undefined;
|
||||
const urlsExpired = await this._timestampModel.expireEntries(minTimestamp, this._maxEntries); // Delete URLs from the cache
|
||||
|
||||
const cache = await caches.open(this._cacheName);
|
||||
|
||||
for (const url of urlsExpired) {
|
||||
await cache.delete(url);
|
||||
}
|
||||
|
||||
{
|
||||
if (urlsExpired.length > 0) {
|
||||
logger_mjs.logger.groupCollapsed(`Expired ${urlsExpired.length} ` + `${urlsExpired.length === 1 ? 'entry' : 'entries'} and removed ` + `${urlsExpired.length === 1 ? 'it' : 'them'} from the ` + `'${this._cacheName}' cache.`);
|
||||
logger_mjs.logger.log(`Expired the following ${urlsExpired.length === 1 ? 'URL' : 'URLs'}:`);
|
||||
urlsExpired.forEach(url => logger_mjs.logger.log(` ${url}`));
|
||||
logger_mjs.logger.groupEnd();
|
||||
} else {
|
||||
logger_mjs.logger.debug(`Cache expiration ran and found no entries to remove.`);
|
||||
}
|
||||
}
|
||||
|
||||
this._isRunning = false;
|
||||
|
||||
if (this._rerunRequested) {
|
||||
this._rerunRequested = false;
|
||||
this.expireEntries();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Update the timestamp for the given URL. This ensures the when
|
||||
* removing entries based on maximum entries, most recently used
|
||||
* is accurate or when expiring, the timestamp is up-to-date.
|
||||
*
|
||||
* @param {string} url
|
||||
*/
|
||||
|
||||
|
||||
async updateTimestamp(url) {
|
||||
{
|
||||
assert_mjs.assert.isType(url, 'string', {
|
||||
moduleName: 'workbox-expiration',
|
||||
className: 'CacheExpiration',
|
||||
funcName: 'updateTimestamp',
|
||||
paramName: 'url'
|
||||
});
|
||||
}
|
||||
|
||||
await this._timestampModel.setTimestamp(url, Date.now());
|
||||
}
|
||||
/**
|
||||
* Can be used to check if a URL has expired or not before it's used.
|
||||
*
|
||||
* This requires a look up from IndexedDB, so can be slow.
|
||||
*
|
||||
* Note: This method will not remove the cached entry, call
|
||||
* `expireEntries()` to remove indexedDB and Cache entries.
|
||||
*
|
||||
* @param {string} url
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
|
||||
async isURLExpired(url) {
|
||||
{
|
||||
if (!this._maxAgeSeconds) {
|
||||
throw new WorkboxError_mjs.WorkboxError(`expired-test-without-max-age`, {
|
||||
methodName: 'isURLExpired',
|
||||
paramName: 'maxAgeSeconds'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const timestamp = await this._timestampModel.getTimestamp(url);
|
||||
const expireOlderThan = Date.now() - this._maxAgeSeconds * 1000;
|
||||
return timestamp < expireOlderThan;
|
||||
}
|
||||
/**
|
||||
* Removes the IndexedDB object store used to keep track of cache expiration
|
||||
* metadata.
|
||||
*/
|
||||
|
||||
|
||||
async delete() {
|
||||
// Make sure we don't attempt another rerun if we're called in the middle of
|
||||
// a cache expiration.
|
||||
this._rerunRequested = false;
|
||||
await this._timestampModel.expireEntries(Infinity); // Expires all.
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* This plugin can be used in the Workbox APIs to regularly enforce a
|
||||
* limit on the age and / or the number of cached requests.
|
||||
*
|
||||
* Whenever a cached request is used or updated, this plugin will look
|
||||
* at the used Cache and remove any old or extra requests.
|
||||
*
|
||||
* When using `maxAgeSeconds`, requests may be used *once* after expiring
|
||||
* because the expiration clean up will not have occurred until *after* the
|
||||
* cached request has been used. If the request has a "Date" header, then
|
||||
* a light weight expiration check is performed and the request will not be
|
||||
* used immediately.
|
||||
*
|
||||
* When using `maxEntries`, the entry least-recently requested will be removed from the cache first.
|
||||
*
|
||||
* @memberof workbox.expiration
|
||||
*/
|
||||
|
||||
class Plugin {
|
||||
/**
|
||||
* @param {Object} config
|
||||
* @param {number} [config.maxEntries] The maximum number of entries to cache.
|
||||
* Entries used the least will be removed as the maximum is reached.
|
||||
* @param {number} [config.maxAgeSeconds] The maximum age of an entry before
|
||||
* it's treated as stale and removed.
|
||||
* @param {boolean} [config.purgeOnQuotaError] Whether to opt this cache in to
|
||||
* automatic deletion if the available storage quota has been exceeded.
|
||||
*/
|
||||
constructor(config = {}) {
|
||||
{
|
||||
if (!(config.maxEntries || config.maxAgeSeconds)) {
|
||||
throw new WorkboxError_mjs.WorkboxError('max-entries-or-age-required', {
|
||||
moduleName: 'workbox-expiration',
|
||||
className: 'Plugin',
|
||||
funcName: 'constructor'
|
||||
});
|
||||
}
|
||||
|
||||
if (config.maxEntries) {
|
||||
assert_mjs.assert.isType(config.maxEntries, 'number', {
|
||||
moduleName: 'workbox-expiration',
|
||||
className: 'Plugin',
|
||||
funcName: 'constructor',
|
||||
paramName: 'config.maxEntries'
|
||||
});
|
||||
}
|
||||
|
||||
if (config.maxAgeSeconds) {
|
||||
assert_mjs.assert.isType(config.maxAgeSeconds, 'number', {
|
||||
moduleName: 'workbox-expiration',
|
||||
className: 'Plugin',
|
||||
funcName: 'constructor',
|
||||
paramName: 'config.maxAgeSeconds'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this._config = config;
|
||||
this._maxAgeSeconds = config.maxAgeSeconds;
|
||||
this._cacheExpirations = new Map();
|
||||
|
||||
if (config.purgeOnQuotaError) {
|
||||
registerQuotaErrorCallback_mjs.registerQuotaErrorCallback(() => this.deleteCacheAndMetadata());
|
||||
}
|
||||
}
|
||||
/**
|
||||
* A simple helper method to return a CacheExpiration instance for a given
|
||||
* cache name.
|
||||
*
|
||||
* @param {string} cacheName
|
||||
* @return {CacheExpiration}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_getCacheExpiration(cacheName) {
|
||||
if (cacheName === cacheNames_mjs.cacheNames.getRuntimeName()) {
|
||||
throw new WorkboxError_mjs.WorkboxError('expire-custom-caches-only');
|
||||
}
|
||||
|
||||
let cacheExpiration = this._cacheExpirations.get(cacheName);
|
||||
|
||||
if (!cacheExpiration) {
|
||||
cacheExpiration = new CacheExpiration(cacheName, this._config);
|
||||
|
||||
this._cacheExpirations.set(cacheName, cacheExpiration);
|
||||
}
|
||||
|
||||
return cacheExpiration;
|
||||
}
|
||||
/**
|
||||
* A "lifecycle" callback that will be triggered automatically by the
|
||||
* `workbox.strategies` 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`.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {string} options.cacheName Name of the cache the response is in.
|
||||
* @param {Response} options.cachedResponse The `Response` object that's been
|
||||
* read from a cache and whose freshness should be checked.
|
||||
* @return {Response} Either the `cachedResponse`, if it's
|
||||
* fresh, or `null` if the `Response` is older than `maxAgeSeconds`.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
cachedResponseWillBeUsed({
|
||||
event,
|
||||
request,
|
||||
cacheName,
|
||||
cachedResponse
|
||||
}) {
|
||||
if (!cachedResponse) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let isFresh = this._isResponseDateFresh(cachedResponse); // Expire entries to ensure that even if the expiration date has
|
||||
// expired, it'll only be used once.
|
||||
|
||||
|
||||
const cacheExpiration = this._getCacheExpiration(cacheName);
|
||||
|
||||
cacheExpiration.expireEntries(); // Update the metadata for the request URL to the current timestamp,
|
||||
// but don't `await` it as we don't want to block the response.
|
||||
|
||||
const updateTimestampDone = cacheExpiration.updateTimestamp(request.url);
|
||||
|
||||
if (event) {
|
||||
try {
|
||||
event.waitUntil(updateTimestampDone);
|
||||
} catch (error) {
|
||||
{
|
||||
logger_mjs.logger.warn(`Unable to ensure service worker stays alive when ` + `updating cache entry for '${getFriendlyURL_mjs.getFriendlyURL(event.request.url)}'.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return isFresh ? cachedResponse : null;
|
||||
}
|
||||
/**
|
||||
* @param {Response} cachedResponse
|
||||
* @return {boolean}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_isResponseDateFresh(cachedResponse) {
|
||||
if (!this._maxAgeSeconds) {
|
||||
// We aren't expiring by age, so return true, it's fresh
|
||||
return true;
|
||||
} // Check if the 'date' header will suffice a quick expiration check.
|
||||
// See https://github.com/GoogleChromeLabs/sw-toolbox/issues/164 for
|
||||
// discussion.
|
||||
|
||||
|
||||
const dateHeaderTimestamp = this._getDateHeaderTimestamp(cachedResponse);
|
||||
|
||||
if (dateHeaderTimestamp === null) {
|
||||
// Unable to parse date, so assume it's fresh.
|
||||
return true;
|
||||
} // If we have a valid headerTime, then our response is fresh iff the
|
||||
// headerTime plus maxAgeSeconds is greater than the current time.
|
||||
|
||||
|
||||
const now = Date.now();
|
||||
return dateHeaderTimestamp >= now - this._maxAgeSeconds * 1000;
|
||||
}
|
||||
/**
|
||||
* This method will extract the data header and parse it into a useful
|
||||
* value.
|
||||
*
|
||||
* @param {Response} cachedResponse
|
||||
* @return {number}
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_getDateHeaderTimestamp(cachedResponse) {
|
||||
if (!cachedResponse.headers.has('date')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const dateHeader = cachedResponse.headers.get('date');
|
||||
const parsedDate = new Date(dateHeader);
|
||||
const headerTime = parsedDate.getTime(); // If the Date header was invalid for some reason, parsedDate.getTime()
|
||||
// will return NaN.
|
||||
|
||||
if (isNaN(headerTime)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return headerTime;
|
||||
}
|
||||
/**
|
||||
* A "lifecycle" callback that will be triggered automatically by the
|
||||
* `workbox.strategies` handlers when an entry is added to a cache.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {string} options.cacheName Name of the cache that was updated.
|
||||
* @param {string} options.request The Request for the cached entry.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async cacheDidUpdate({
|
||||
cacheName,
|
||||
request
|
||||
}) {
|
||||
{
|
||||
assert_mjs.assert.isType(cacheName, 'string', {
|
||||
moduleName: 'workbox-expiration',
|
||||
className: 'Plugin',
|
||||
funcName: 'cacheDidUpdate',
|
||||
paramName: 'cacheName'
|
||||
});
|
||||
assert_mjs.assert.isInstance(request, Request, {
|
||||
moduleName: 'workbox-expiration',
|
||||
className: 'Plugin',
|
||||
funcName: 'cacheDidUpdate',
|
||||
paramName: 'request'
|
||||
});
|
||||
}
|
||||
|
||||
const cacheExpiration = this._getCacheExpiration(cacheName);
|
||||
|
||||
await cacheExpiration.updateTimestamp(request.url);
|
||||
await cacheExpiration.expireEntries();
|
||||
}
|
||||
/**
|
||||
* This is a helper method that performs two operations:
|
||||
*
|
||||
* - Deletes *all* the underlying Cache instances associated with this plugin
|
||||
* instance, by calling caches.delete() on your behalf.
|
||||
* - Deletes the metadata from IndexedDB used to keep track of expiration
|
||||
* details for each Cache instance.
|
||||
*
|
||||
* When using cache expiration, calling this method is preferable to calling
|
||||
* `caches.delete()` directly, since this will ensure that the IndexedDB
|
||||
* metadata is also cleanly removed and open IndexedDB instances are deleted.
|
||||
*
|
||||
* Note that if you're *not* using cache expiration for a given cache, calling
|
||||
* `caches.delete()` and passing in the cache's name should be sufficient.
|
||||
* There is no Workbox-specific method needed for cleanup in that case.
|
||||
*/
|
||||
|
||||
|
||||
async deleteCacheAndMetadata() {
|
||||
// Do this one at a time instead of all at once via `Promise.all()` to
|
||||
// reduce the chance of inconsistency if a promise rejects.
|
||||
for (const [cacheName, cacheExpiration] of this._cacheExpirations) {
|
||||
await caches.delete(cacheName);
|
||||
await cacheExpiration.delete();
|
||||
} // Reset this._cacheExpirations to its initial state.
|
||||
|
||||
|
||||
this._cacheExpirations = new Map();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
exports.CacheExpiration = CacheExpiration;
|
||||
exports.Plugin = Plugin;
|
||||
|
||||
return exports;
|
||||
|
||||
}({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core));
|
||||
//# sourceMappingURL=workbox-expiration.dev.js.map
|
|
@ -0,0 +1,2 @@
|
|||
this.workbox=this.workbox||{},this.workbox.expiration=function(t,e,s,i,a,n){"use strict";try{self["workbox:expiration:4.3.1"]&&_()}catch(t){}const h="workbox-expiration",c="cache-entries",r=t=>{const e=new URL(t,location);return e.hash="",e.href};class o{constructor(t){this.t=t,this.s=new e.DBWrapper(h,1,{onupgradeneeded:t=>this.i(t)})}i(t){const e=t.target.result.createObjectStore(c,{keyPath:"id"});e.createIndex("cacheName","cacheName",{unique:!1}),e.createIndex("timestamp","timestamp",{unique:!1}),s.deleteDatabase(this.t)}async setTimestamp(t,e){t=r(t),await this.s.put(c,{url:t,timestamp:e,cacheName:this.t,id:this.h(t)})}async getTimestamp(t){return(await this.s.get(c,this.h(t))).timestamp}async expireEntries(t,e){const s=await this.s.transaction(c,"readwrite",(s,i)=>{const a=s.objectStore(c),n=[];let h=0;a.index("timestamp").openCursor(null,"prev").onsuccess=(({target:s})=>{const a=s.result;if(a){const s=a.value;s.cacheName===this.t&&(t&&s.timestamp<t||e&&h>=e?n.push(a.value):h++),a.continue()}else i(n)})}),i=[];for(const t of s)await this.s.delete(c,t.id),i.push(t.url);return i}h(t){return this.t+"|"+r(t)}}class u{constructor(t,e={}){this.o=!1,this.u=!1,this.l=e.maxEntries,this.p=e.maxAgeSeconds,this.t=t,this.m=new o(t)}async expireEntries(){if(this.o)return void(this.u=!0);this.o=!0;const t=this.p?Date.now()-1e3*this.p:void 0,e=await this.m.expireEntries(t,this.l),s=await caches.open(this.t);for(const t of e)await s.delete(t);this.o=!1,this.u&&(this.u=!1,this.expireEntries())}async updateTimestamp(t){await this.m.setTimestamp(t,Date.now())}async isURLExpired(t){return await this.m.getTimestamp(t)<Date.now()-1e3*this.p}async delete(){this.u=!1,await this.m.expireEntries(1/0)}}return t.CacheExpiration=u,t.Plugin=class{constructor(t={}){this.D=t,this.p=t.maxAgeSeconds,this.g=new Map,t.purgeOnQuotaError&&n.registerQuotaErrorCallback(()=>this.deleteCacheAndMetadata())}k(t){if(t===a.cacheNames.getRuntimeName())throw new i.WorkboxError("expire-custom-caches-only");let e=this.g.get(t);return e||(e=new u(t,this.D),this.g.set(t,e)),e}cachedResponseWillBeUsed({event:t,request:e,cacheName:s,cachedResponse:i}){if(!i)return null;let a=this.N(i);const n=this.k(s);n.expireEntries();const h=n.updateTimestamp(e.url);if(t)try{t.waitUntil(h)}catch(t){}return a?i:null}N(t){if(!this.p)return!0;const e=this._(t);return null===e||e>=Date.now()-1e3*this.p}_(t){if(!t.headers.has("date"))return null;const e=t.headers.get("date"),s=new Date(e).getTime();return isNaN(s)?null:s}async cacheDidUpdate({cacheName:t,request:e}){const s=this.k(t);await s.updateTimestamp(e.url),await s.expireEntries()}async deleteCacheAndMetadata(){for(const[t,e]of this.g)await caches.delete(t),await e.delete();this.g=new Map}},t}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private,workbox.core);
|
||||
//# sourceMappingURL=workbox-expiration.prod.js.map
|
|
@ -0,0 +1,110 @@
|
|||
this.workbox = this.workbox || {};
|
||||
this.workbox.navigationPreload = (function (exports, logger_mjs) {
|
||||
'use strict';
|
||||
|
||||
try {
|
||||
self['workbox:navigation-preload:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* @return {boolean} Whether or not the current browser supports enabling
|
||||
* navigation preload.
|
||||
*
|
||||
* @memberof workbox.navigationPreload
|
||||
*/
|
||||
|
||||
function isSupported() {
|
||||
return Boolean(self.registration && self.registration.navigationPreload);
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* If the browser supports Navigation Preload, then this will disable it.
|
||||
*
|
||||
* @memberof workbox.navigationPreload
|
||||
*/
|
||||
|
||||
function disable() {
|
||||
if (isSupported()) {
|
||||
self.addEventListener('activate', event => {
|
||||
event.waitUntil(self.registration.navigationPreload.disable().then(() => {
|
||||
{
|
||||
logger_mjs.logger.log(`Navigation preload is disabled.`);
|
||||
}
|
||||
}));
|
||||
});
|
||||
} else {
|
||||
{
|
||||
logger_mjs.logger.log(`Navigation preload is not supported in this browser.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* If the browser supports Navigation Preload, then this will enable it.
|
||||
*
|
||||
* @param {string} [headerValue] Optionally, allows developers to
|
||||
* [override](https://developers.google.com/web/updates/2017/02/navigation-preload#changing_the_header)
|
||||
* the value of the `Service-Worker-Navigation-Preload` header which will be
|
||||
* sent to the server when making the navigation request.
|
||||
*
|
||||
* @memberof workbox.navigationPreload
|
||||
*/
|
||||
|
||||
function enable(headerValue) {
|
||||
if (isSupported()) {
|
||||
self.addEventListener('activate', event => {
|
||||
event.waitUntil(self.registration.navigationPreload.enable().then(() => {
|
||||
// Defaults to Service-Worker-Navigation-Preload: true if not set.
|
||||
if (headerValue) {
|
||||
self.registration.navigationPreload.setHeaderValue(headerValue);
|
||||
}
|
||||
|
||||
{
|
||||
logger_mjs.logger.log(`Navigation preload is enabled.`);
|
||||
}
|
||||
}));
|
||||
});
|
||||
} else {
|
||||
{
|
||||
logger_mjs.logger.log(`Navigation preload is not supported in this browser.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
exports.disable = disable;
|
||||
exports.enable = enable;
|
||||
exports.isSupported = isSupported;
|
||||
|
||||
return exports;
|
||||
|
||||
}({}, workbox.core._private));
|
||||
//# sourceMappingURL=workbox-navigation-preload.dev.js.map
|
|
@ -0,0 +1,2 @@
|
|||
this.workbox=this.workbox||{},this.workbox.navigationPreload=function(t){"use strict";try{self["workbox:navigation-preload:4.3.1"]&&_()}catch(t){}function e(){return Boolean(self.registration&&self.registration.navigationPreload)}return t.disable=function(){e()&&self.addEventListener("activate",t=>{t.waitUntil(self.registration.navigationPreload.disable().then(()=>{}))})},t.enable=function(t){e()&&self.addEventListener("activate",e=>{e.waitUntil(self.registration.navigationPreload.enable().then(()=>{t&&self.registration.navigationPreload.setHeaderValue(t)}))})},t.isSupported=e,t}({});
|
||||
//# sourceMappingURL=workbox-navigation-preload.prod.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"workbox-navigation-preload.prod.js","sources":["../_version.mjs","../isSupported.mjs","../disable.mjs","../enable.mjs"],"sourcesContent":["try{self['workbox:navigation-preload:4.3.1']&&_()}catch(e){}// eslint-disable-line","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\n\nimport './_version.mjs';\n\n/**\n * @return {boolean} Whether or not the current browser supports enabling\n * navigation preload.\n *\n * @memberof workbox.navigationPreload\n */\nfunction isSupported() {\n return Boolean(self.registration && self.registration.navigationPreload);\n}\n\nexport {isSupported};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\n\nimport {logger} from 'workbox-core/_private/logger.mjs';\n\nimport {isSupported} from './isSupported.mjs';\n\nimport './_version.mjs';\n\n/**\n * If the browser supports Navigation Preload, then this will disable it.\n *\n * @memberof workbox.navigationPreload\n */\nfunction disable() {\n if (isSupported()) {\n self.addEventListener('activate', (event) => {\n event.waitUntil(\n self.registration.navigationPreload.disable().then(() => {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`Navigation preload is disabled.`);\n }\n })\n );\n });\n } else {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`Navigation preload is not supported in this browser.`);\n }\n }\n}\n\nexport {disable};\n","/*\n Copyright 2018 Google LLC\n\n Use of this source code is governed by an MIT-style\n license that can be found in the LICENSE file or at\n https://opensource.org/licenses/MIT.\n*/\n\nimport {logger} from 'workbox-core/_private/logger.mjs';\n\nimport {isSupported} from './isSupported.mjs';\n\nimport './_version.mjs';\n\n/**\n * If the browser supports Navigation Preload, then this will enable it.\n *\n * @param {string} [headerValue] Optionally, allows developers to\n * [override](https://developers.google.com/web/updates/2017/02/navigation-preload#changing_the_header)\n * the value of the `Service-Worker-Navigation-Preload` header which will be\n * sent to the server when making the navigation request.\n *\n * @memberof workbox.navigationPreload\n */\nfunction enable(headerValue) {\n if (isSupported()) {\n self.addEventListener('activate', (event) => {\n event.waitUntil(\n self.registration.navigationPreload.enable().then(() => {\n // Defaults to Service-Worker-Navigation-Preload: true if not set.\n if (headerValue) {\n self.registration.navigationPreload.setHeaderValue(headerValue);\n }\n\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`Navigation preload is enabled.`);\n }\n })\n );\n });\n } else {\n if (process.env.NODE_ENV !== 'production') {\n logger.log(`Navigation preload is not supported in this browser.`);\n }\n }\n}\n\nexport {enable};\n"],"names":["self","_","e","isSupported","Boolean","registration","navigationPreload","addEventListener","event","waitUntil","disable","then","headerValue","enable","setHeaderValue"],"mappings":"sFAAA,IAAIA,KAAK,qCAAqCC,IAAI,MAAMC,ICgBxD,SAASC,WACAC,QAAQJ,KAAKK,cAAgBL,KAAKK,aAAaC,oCCExD,WACMH,KACFH,KAAKO,iBAAiB,WAAaC,IACjCA,EAAMC,UACFT,KAAKK,aAAaC,kBAAkBI,UAAUC,KAAK,qBCC7D,SAAgBC,GACVT,KACFH,KAAKO,iBAAiB,WAAaC,IACjCA,EAAMC,UACFT,KAAKK,aAAaC,kBAAkBO,SAASF,KAAK,KAE5CC,GACFZ,KAAKK,aAAaC,kBAAkBQ,eAAeF"}
|
|
@ -0,0 +1,243 @@
|
|||
this.workbox = this.workbox || {};
|
||||
this.workbox.googleAnalytics = (function (exports, Plugin_mjs, cacheNames_mjs, getFriendlyURL_mjs, logger_mjs, Route_mjs, Router_mjs, NetworkFirst_mjs, NetworkOnly_mjs) {
|
||||
'use strict';
|
||||
|
||||
try {
|
||||
self['workbox:google-analytics:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
const QUEUE_NAME = 'workbox-google-analytics';
|
||||
const MAX_RETENTION_TIME = 60 * 48; // Two days in minutes
|
||||
|
||||
const GOOGLE_ANALYTICS_HOST = 'www.google-analytics.com';
|
||||
const GTM_HOST = 'www.googletagmanager.com';
|
||||
const ANALYTICS_JS_PATH = '/analytics.js';
|
||||
const GTAG_JS_PATH = '/gtag/js';
|
||||
const GTM_JS_PATH = '/gtm.js';
|
||||
// endpoints. Most of the time the default path (/collect) is used, but
|
||||
// occasionally an experimental endpoint is used when testing new features,
|
||||
// (e.g. /r/collect or /j/collect)
|
||||
|
||||
const COLLECT_PATHS_REGEX = /^\/(\w+\/)?collect/;
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Creates the requestWillDequeue callback to be used with the background
|
||||
* sync queue plugin. The callback takes the failed request and adds the
|
||||
* `qt` param based on the current time, as well as applies any other
|
||||
* user-defined hit modifications.
|
||||
*
|
||||
* @param {Object} config See workbox.googleAnalytics.initialize.
|
||||
* @return {Function} The requestWillDequeu callback function.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
const createOnSyncCallback = config => {
|
||||
return async ({
|
||||
queue
|
||||
}) => {
|
||||
let entry;
|
||||
|
||||
while (entry = await queue.shiftRequest()) {
|
||||
const {
|
||||
request,
|
||||
timestamp
|
||||
} = entry;
|
||||
const url = new URL(request.url);
|
||||
|
||||
try {
|
||||
// Measurement protocol requests can set their payload parameters in
|
||||
// either the URL query string (for GET requests) or the POST body.
|
||||
const params = request.method === 'POST' ? new URLSearchParams((await request.clone().text())) : url.searchParams; // Calculate the qt param, accounting for the fact that an existing
|
||||
// qt param may be present and should be updated rather than replaced.
|
||||
|
||||
const originalHitTime = timestamp - (Number(params.get('qt')) || 0);
|
||||
const queueTime = Date.now() - originalHitTime; // Set the qt param prior to applying hitFilter or parameterOverrides.
|
||||
|
||||
params.set('qt', queueTime); // Apply `paramterOverrideds`, if set.
|
||||
|
||||
if (config.parameterOverrides) {
|
||||
for (const param of Object.keys(config.parameterOverrides)) {
|
||||
const value = config.parameterOverrides[param];
|
||||
params.set(param, value);
|
||||
}
|
||||
} // Apply `hitFilter`, if set.
|
||||
|
||||
|
||||
if (typeof config.hitFilter === 'function') {
|
||||
config.hitFilter.call(null, params);
|
||||
} // Retry the fetch. Ignore URL search params from the URL as they're
|
||||
// now in the post body.
|
||||
|
||||
|
||||
await fetch(new Request(url.origin + url.pathname, {
|
||||
body: params.toString(),
|
||||
method: 'POST',
|
||||
mode: 'cors',
|
||||
credentials: 'omit',
|
||||
headers: {
|
||||
'Content-Type': 'text/plain'
|
||||
}
|
||||
}));
|
||||
|
||||
{
|
||||
logger_mjs.logger.log(`Request for '${getFriendlyURL_mjs.getFriendlyURL(url.href)}'` + `has been replayed`);
|
||||
}
|
||||
} catch (err) {
|
||||
await queue.unshiftRequest(entry);
|
||||
|
||||
{
|
||||
logger_mjs.logger.log(`Request for '${getFriendlyURL_mjs.getFriendlyURL(url.href)}'` + `failed to replay, putting it back in the queue.`);
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
logger_mjs.logger.log(`All Google Analytics request successfully replayed; ` + `the queue is now empty!`);
|
||||
}
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Creates GET and POST routes to catch failed Measurement Protocol hits.
|
||||
*
|
||||
* @param {Plugin} queuePlugin
|
||||
* @return {Array<Route>} The created routes.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
const createCollectRoutes = queuePlugin => {
|
||||
const match = ({
|
||||
url
|
||||
}) => url.hostname === GOOGLE_ANALYTICS_HOST && COLLECT_PATHS_REGEX.test(url.pathname);
|
||||
|
||||
const handler = new NetworkOnly_mjs.NetworkOnly({
|
||||
plugins: [queuePlugin]
|
||||
});
|
||||
return [new Route_mjs.Route(match, handler, 'GET'), new Route_mjs.Route(match, handler, 'POST')];
|
||||
};
|
||||
/**
|
||||
* Creates a route with a network first strategy for the analytics.js script.
|
||||
*
|
||||
* @param {string} cacheName
|
||||
* @return {Route} The created route.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
const createAnalyticsJsRoute = cacheName => {
|
||||
const match = ({
|
||||
url
|
||||
}) => url.hostname === GOOGLE_ANALYTICS_HOST && url.pathname === ANALYTICS_JS_PATH;
|
||||
|
||||
const handler = new NetworkFirst_mjs.NetworkFirst({
|
||||
cacheName
|
||||
});
|
||||
return new Route_mjs.Route(match, handler, 'GET');
|
||||
};
|
||||
/**
|
||||
* Creates a route with a network first strategy for the gtag.js script.
|
||||
*
|
||||
* @param {string} cacheName
|
||||
* @return {Route} The created route.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
const createGtagJsRoute = cacheName => {
|
||||
const match = ({
|
||||
url
|
||||
}) => url.hostname === GTM_HOST && url.pathname === GTAG_JS_PATH;
|
||||
|
||||
const handler = new NetworkFirst_mjs.NetworkFirst({
|
||||
cacheName
|
||||
});
|
||||
return new Route_mjs.Route(match, handler, 'GET');
|
||||
};
|
||||
/**
|
||||
* Creates a route with a network first strategy for the gtm.js script.
|
||||
*
|
||||
* @param {string} cacheName
|
||||
* @return {Route} The created route.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
const createGtmJsRoute = cacheName => {
|
||||
const match = ({
|
||||
url
|
||||
}) => url.hostname === GTM_HOST && url.pathname === GTM_JS_PATH;
|
||||
|
||||
const handler = new NetworkFirst_mjs.NetworkFirst({
|
||||
cacheName
|
||||
});
|
||||
return new Route_mjs.Route(match, handler, 'GET');
|
||||
};
|
||||
/**
|
||||
* @param {Object=} [options]
|
||||
* @param {Object} [options.cacheName] The cache name to store and retrieve
|
||||
* analytics.js. Defaults to the cache names provided by `workbox-core`.
|
||||
* @param {Object} [options.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} [options.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.
|
||||
*
|
||||
* @memberof workbox.googleAnalytics
|
||||
*/
|
||||
|
||||
|
||||
const initialize = (options = {}) => {
|
||||
const cacheName = cacheNames_mjs.cacheNames.getGoogleAnalyticsName(options.cacheName);
|
||||
const queuePlugin = new Plugin_mjs.Plugin(QUEUE_NAME, {
|
||||
maxRetentionTime: MAX_RETENTION_TIME,
|
||||
onSync: createOnSyncCallback(options)
|
||||
});
|
||||
const routes = [createGtmJsRoute(cacheName), createAnalyticsJsRoute(cacheName), createGtagJsRoute(cacheName), ...createCollectRoutes(queuePlugin)];
|
||||
const router = new Router_mjs.Router();
|
||||
|
||||
for (const route of routes) {
|
||||
router.registerRoute(route);
|
||||
}
|
||||
|
||||
router.addFetchListener();
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
exports.initialize = initialize;
|
||||
|
||||
return exports;
|
||||
|
||||
}({}, workbox.backgroundSync, workbox.core._private, workbox.core._private, workbox.core._private, workbox.routing, workbox.routing, workbox.strategies, workbox.strategies));
|
||||
//# sourceMappingURL=workbox-offline-ga.dev.js.map
|
|
@ -0,0 +1,2 @@
|
|||
this.workbox=this.workbox||{},this.workbox.googleAnalytics=function(e,t,o,n,a,c,w){"use strict";try{self["workbox:google-analytics:4.3.1"]&&_()}catch(e){}const r=/^\/(\w+\/)?collect/,s=e=>async({queue:t})=>{let o;for(;o=await t.shiftRequest();){const{request:n,timestamp:a}=o,c=new URL(n.url);try{const w="POST"===n.method?new URLSearchParams(await n.clone().text()):c.searchParams,r=a-(Number(w.get("qt"))||0),s=Date.now()-r;if(w.set("qt",s),e.parameterOverrides)for(const t of Object.keys(e.parameterOverrides)){const o=e.parameterOverrides[t];w.set(t,o)}"function"==typeof e.hitFilter&&e.hitFilter.call(null,w),await fetch(new Request(c.origin+c.pathname,{body:w.toString(),method:"POST",mode:"cors",credentials:"omit",headers:{"Content-Type":"text/plain"}}))}catch(e){throw await t.unshiftRequest(o),e}}},i=e=>{const t=({url:e})=>"www.google-analytics.com"===e.hostname&&r.test(e.pathname),o=new w.NetworkOnly({plugins:[e]});return[new n.Route(t,o,"GET"),new n.Route(t,o,"POST")]},l=e=>{const t=new c.NetworkFirst({cacheName:e});return new n.Route(({url:e})=>"www.google-analytics.com"===e.hostname&&"/analytics.js"===e.pathname,t,"GET")},m=e=>{const t=new c.NetworkFirst({cacheName:e});return new n.Route(({url:e})=>"www.googletagmanager.com"===e.hostname&&"/gtag/js"===e.pathname,t,"GET")},u=e=>{const t=new c.NetworkFirst({cacheName:e});return new n.Route(({url:e})=>"www.googletagmanager.com"===e.hostname&&"/gtm.js"===e.pathname,t,"GET")};return e.initialize=((e={})=>{const n=o.cacheNames.getGoogleAnalyticsName(e.cacheName),c=new t.Plugin("workbox-google-analytics",{maxRetentionTime:2880,onSync:s(e)}),w=[u(n),l(n),m(n),...i(c)],r=new a.Router;for(const e of w)r.registerRoute(e);r.addFetchListener()}),e}({},workbox.backgroundSync,workbox.core._private,workbox.routing,workbox.routing,workbox.strategies,workbox.strategies);
|
||||
//# sourceMappingURL=workbox-offline-ga.prod.js.map
|
|
@ -0,0 +1,989 @@
|
|||
this.workbox = this.workbox || {};
|
||||
this.workbox.precaching = (function (exports, assert_mjs, cacheNames_mjs, getFriendlyURL_mjs, logger_mjs, cacheWrapper_mjs, fetchWrapper_mjs, WorkboxError_mjs) {
|
||||
'use strict';
|
||||
|
||||
try {
|
||||
self['workbox:precaching:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
const plugins = [];
|
||||
const precachePlugins = {
|
||||
/*
|
||||
* @return {Array}
|
||||
* @private
|
||||
*/
|
||||
get() {
|
||||
return plugins;
|
||||
},
|
||||
|
||||
/*
|
||||
* @param {Array} newPlugins
|
||||
* @private
|
||||
*/
|
||||
add(newPlugins) {
|
||||
plugins.push(...newPlugins);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Adds plugins to precaching.
|
||||
*
|
||||
* @param {Array<Object>} newPlugins
|
||||
*
|
||||
* @alias workbox.precaching.addPlugins
|
||||
*/
|
||||
|
||||
const addPlugins = newPlugins => {
|
||||
precachePlugins.add(newPlugins);
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* @param {Response} response
|
||||
* @return {Response}
|
||||
*
|
||||
* @private
|
||||
* @memberof module:workbox-precaching
|
||||
*/
|
||||
|
||||
async function cleanRedirect(response) {
|
||||
const clonedResponse = response.clone(); // Not all browsers support the Response.body stream, so fall back
|
||||
// to reading the entire body into memory as a blob.
|
||||
|
||||
const bodyPromise = 'body' in clonedResponse ? Promise.resolve(clonedResponse.body) : clonedResponse.blob();
|
||||
const body = await bodyPromise; // new Response() is happy when passed either a stream or a Blob.
|
||||
|
||||
return new Response(body, {
|
||||
headers: clonedResponse.headers,
|
||||
status: clonedResponse.status,
|
||||
statusText: clonedResponse.statusText
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
const REVISION_SEARCH_PARAM = '__WB_REVISION__';
|
||||
/**
|
||||
* Converts a manifest entry into a versioned URL suitable for precaching.
|
||||
*
|
||||
* @param {Object} entry
|
||||
* @return {string} A URL with versioning info.
|
||||
*
|
||||
* @private
|
||||
* @memberof module:workbox-precaching
|
||||
*/
|
||||
|
||||
function createCacheKey(entry) {
|
||||
if (!entry) {
|
||||
throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-unexpected-type', {
|
||||
entry
|
||||
});
|
||||
} // If a precache manifest entry is a string, it's assumed to be a versioned
|
||||
// URL, like '/app.abcd1234.js'. Return as-is.
|
||||
|
||||
|
||||
if (typeof entry === 'string') {
|
||||
const urlObject = new URL(entry, location);
|
||||
return {
|
||||
cacheKey: urlObject.href,
|
||||
url: urlObject.href
|
||||
};
|
||||
}
|
||||
|
||||
const {
|
||||
revision,
|
||||
url
|
||||
} = entry;
|
||||
|
||||
if (!url) {
|
||||
throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-unexpected-type', {
|
||||
entry
|
||||
});
|
||||
} // If there's just a URL and no revision, then it's also assumed to be a
|
||||
// versioned URL.
|
||||
|
||||
|
||||
if (!revision) {
|
||||
const urlObject = new URL(url, location);
|
||||
return {
|
||||
cacheKey: urlObject.href,
|
||||
url: urlObject.href
|
||||
};
|
||||
} // Otherwise, construct a properly versioned URL using the custom Workbox
|
||||
// search parameter along with the revision info.
|
||||
|
||||
|
||||
const originalURL = new URL(url, location);
|
||||
const cacheKeyURL = new URL(url, location);
|
||||
cacheKeyURL.searchParams.set(REVISION_SEARCH_PARAM, revision);
|
||||
return {
|
||||
cacheKey: cacheKeyURL.href,
|
||||
url: originalURL.href
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
const logGroup = (groupTitle, deletedURLs) => {
|
||||
logger_mjs.logger.groupCollapsed(groupTitle);
|
||||
|
||||
for (const url of deletedURLs) {
|
||||
logger_mjs.logger.log(url);
|
||||
}
|
||||
|
||||
logger_mjs.logger.groupEnd();
|
||||
};
|
||||
/**
|
||||
* @param {Array<string>} deletedURLs
|
||||
*
|
||||
* @private
|
||||
* @memberof module:workbox-precaching
|
||||
*/
|
||||
|
||||
|
||||
function printCleanupDetails(deletedURLs) {
|
||||
const deletionCount = deletedURLs.length;
|
||||
|
||||
if (deletionCount > 0) {
|
||||
logger_mjs.logger.groupCollapsed(`During precaching cleanup, ` + `${deletionCount} cached ` + `request${deletionCount === 1 ? ' was' : 's were'} deleted.`);
|
||||
logGroup('Deleted Cache Requests', deletedURLs);
|
||||
logger_mjs.logger.groupEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* @param {string} groupTitle
|
||||
* @param {Array<string>} urls
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
function _nestedGroup(groupTitle, urls) {
|
||||
if (urls.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
logger_mjs.logger.groupCollapsed(groupTitle);
|
||||
|
||||
for (const url of urls) {
|
||||
logger_mjs.logger.log(url);
|
||||
}
|
||||
|
||||
logger_mjs.logger.groupEnd();
|
||||
}
|
||||
/**
|
||||
* @param {Array<string>} urlsToPrecache
|
||||
* @param {Array<string>} urlsAlreadyPrecached
|
||||
*
|
||||
* @private
|
||||
* @memberof module:workbox-precaching
|
||||
*/
|
||||
|
||||
|
||||
function printInstallDetails(urlsToPrecache, urlsAlreadyPrecached) {
|
||||
const precachedCount = urlsToPrecache.length;
|
||||
const alreadyPrecachedCount = urlsAlreadyPrecached.length;
|
||||
|
||||
if (precachedCount || alreadyPrecachedCount) {
|
||||
let message = `Precaching ${precachedCount} file${precachedCount === 1 ? '' : 's'}.`;
|
||||
|
||||
if (alreadyPrecachedCount > 0) {
|
||||
message += ` ${alreadyPrecachedCount} ` + `file${alreadyPrecachedCount === 1 ? ' is' : 's are'} already cached.`;
|
||||
}
|
||||
|
||||
logger_mjs.logger.groupCollapsed(message);
|
||||
|
||||
_nestedGroup(`View newly precached URLs.`, urlsToPrecache);
|
||||
|
||||
_nestedGroup(`View previously precached URLs.`, urlsAlreadyPrecached);
|
||||
|
||||
logger_mjs.logger.groupEnd();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Performs efficient precaching of assets.
|
||||
*
|
||||
* @memberof module:workbox-precaching
|
||||
*/
|
||||
|
||||
class PrecacheController {
|
||||
/**
|
||||
* Create a new PrecacheController.
|
||||
*
|
||||
* @param {string} [cacheName] An optional name for the cache, to override
|
||||
* the default precache name.
|
||||
*/
|
||||
constructor(cacheName) {
|
||||
this._cacheName = cacheNames_mjs.cacheNames.getPrecacheName(cacheName);
|
||||
this._urlsToCacheKeys = new Map();
|
||||
}
|
||||
/**
|
||||
* This method will add items to the precache list, removing duplicates
|
||||
* and ensuring the information is valid.
|
||||
*
|
||||
* @param {
|
||||
* Array<module:workbox-precaching.PrecacheController.PrecacheEntry|string>
|
||||
* } entries Array of entries to precache.
|
||||
*/
|
||||
|
||||
|
||||
addToCacheList(entries) {
|
||||
{
|
||||
assert_mjs.assert.isArray(entries, {
|
||||
moduleName: 'workbox-precaching',
|
||||
className: 'PrecacheController',
|
||||
funcName: 'addToCacheList',
|
||||
paramName: 'entries'
|
||||
});
|
||||
}
|
||||
|
||||
for (const entry of entries) {
|
||||
const {
|
||||
cacheKey,
|
||||
url
|
||||
} = createCacheKey(entry);
|
||||
|
||||
if (this._urlsToCacheKeys.has(url) && this._urlsToCacheKeys.get(url) !== cacheKey) {
|
||||
throw new WorkboxError_mjs.WorkboxError('add-to-cache-list-conflicting-entries', {
|
||||
firstEntry: this._urlsToCacheKeys.get(url),
|
||||
secondEntry: cacheKey
|
||||
});
|
||||
}
|
||||
|
||||
this._urlsToCacheKeys.set(url, cacheKey);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Precaches new and updated assets. Call this method from the service worker
|
||||
* install event.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Event} [options.event] The install event (if needed).
|
||||
* @param {Array<Object>} [options.plugins] Plugins to be used for fetching
|
||||
* and caching during install.
|
||||
* @return {Promise<workbox.precaching.InstallResult>}
|
||||
*/
|
||||
|
||||
|
||||
async install({
|
||||
event,
|
||||
plugins
|
||||
} = {}) {
|
||||
{
|
||||
if (plugins) {
|
||||
assert_mjs.assert.isArray(plugins, {
|
||||
moduleName: 'workbox-precaching',
|
||||
className: 'PrecacheController',
|
||||
funcName: 'install',
|
||||
paramName: 'plugins'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const urlsToPrecache = [];
|
||||
const urlsAlreadyPrecached = [];
|
||||
const cache = await caches.open(this._cacheName);
|
||||
const alreadyCachedRequests = await cache.keys();
|
||||
const alreadyCachedURLs = new Set(alreadyCachedRequests.map(request => request.url));
|
||||
|
||||
for (const cacheKey of this._urlsToCacheKeys.values()) {
|
||||
if (alreadyCachedURLs.has(cacheKey)) {
|
||||
urlsAlreadyPrecached.push(cacheKey);
|
||||
} else {
|
||||
urlsToPrecache.push(cacheKey);
|
||||
}
|
||||
}
|
||||
|
||||
const precacheRequests = urlsToPrecache.map(url => {
|
||||
return this._addURLToCache({
|
||||
event,
|
||||
plugins,
|
||||
url
|
||||
});
|
||||
});
|
||||
await Promise.all(precacheRequests);
|
||||
|
||||
{
|
||||
printInstallDetails(urlsToPrecache, urlsAlreadyPrecached);
|
||||
}
|
||||
|
||||
return {
|
||||
updatedURLs: urlsToPrecache,
|
||||
notUpdatedURLs: urlsAlreadyPrecached
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Deletes assets that are no longer present in the current precache manifest.
|
||||
* Call this method from the service worker activate event.
|
||||
*
|
||||
* @return {Promise<workbox.precaching.CleanupResult>}
|
||||
*/
|
||||
|
||||
|
||||
async activate() {
|
||||
const cache = await caches.open(this._cacheName);
|
||||
const currentlyCachedRequests = await cache.keys();
|
||||
const expectedCacheKeys = new Set(this._urlsToCacheKeys.values());
|
||||
const deletedURLs = [];
|
||||
|
||||
for (const request of currentlyCachedRequests) {
|
||||
if (!expectedCacheKeys.has(request.url)) {
|
||||
await cache.delete(request);
|
||||
deletedURLs.push(request.url);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
printCleanupDetails(deletedURLs);
|
||||
}
|
||||
|
||||
return {
|
||||
deletedURLs
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Requests the entry and saves it to the cache if the response is valid.
|
||||
* By default, any response with a status code of less than 400 (including
|
||||
* opaque responses) is considered valid.
|
||||
*
|
||||
* If you need to use custom criteria to determine what's valid and what
|
||||
* isn't, then pass in an item in `options.plugins` that implements the
|
||||
* `cacheWillUpdate()` lifecycle event.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} options
|
||||
* @param {string} options.url The URL to fetch and cache.
|
||||
* @param {Event} [options.event] The install event (if passed).
|
||||
* @param {Array<Object>} [options.plugins] An array of plugins to apply to
|
||||
* fetch and caching.
|
||||
*/
|
||||
|
||||
|
||||
async _addURLToCache({
|
||||
url,
|
||||
event,
|
||||
plugins
|
||||
}) {
|
||||
const request = new Request(url, {
|
||||
credentials: 'same-origin'
|
||||
});
|
||||
let response = await fetchWrapper_mjs.fetchWrapper.fetch({
|
||||
event,
|
||||
plugins,
|
||||
request
|
||||
}); // Allow developers to override the default logic about what is and isn't
|
||||
// valid by passing in a plugin implementing cacheWillUpdate(), e.g.
|
||||
// a workbox.cacheableResponse.Plugin instance.
|
||||
|
||||
let cacheWillUpdateCallback;
|
||||
|
||||
for (const plugin of plugins || []) {
|
||||
if ('cacheWillUpdate' in plugin) {
|
||||
cacheWillUpdateCallback = plugin.cacheWillUpdate.bind(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
const isValidResponse = cacheWillUpdateCallback ? // Use a callback if provided. It returns a truthy value if valid.
|
||||
cacheWillUpdateCallback({
|
||||
event,
|
||||
request,
|
||||
response
|
||||
}) : // Otherwise, default to considering any response status under 400 valid.
|
||||
// This includes, by default, considering opaque responses valid.
|
||||
response.status < 400; // Consider this a failure, leading to the `install` handler failing, if
|
||||
// we get back an invalid response.
|
||||
|
||||
if (!isValidResponse) {
|
||||
throw new WorkboxError_mjs.WorkboxError('bad-precaching-response', {
|
||||
url,
|
||||
status: response.status
|
||||
});
|
||||
}
|
||||
|
||||
if (response.redirected) {
|
||||
response = await cleanRedirect(response);
|
||||
}
|
||||
|
||||
await cacheWrapper_mjs.cacheWrapper.put({
|
||||
event,
|
||||
plugins,
|
||||
request,
|
||||
response,
|
||||
cacheName: this._cacheName,
|
||||
matchOptions: {
|
||||
ignoreSearch: true
|
||||
}
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Returns a mapping of a precached URL to the corresponding cache key, taking
|
||||
* into account the revision information for the URL.
|
||||
*
|
||||
* @return {Map<string, string>} A URL to cache key mapping.
|
||||
*/
|
||||
|
||||
|
||||
getURLsToCacheKeys() {
|
||||
return this._urlsToCacheKeys;
|
||||
}
|
||||
/**
|
||||
* Returns a list of all the URLs that have been precached by the current
|
||||
* service worker.
|
||||
*
|
||||
* @return {Array<string>} The precached URLs.
|
||||
*/
|
||||
|
||||
|
||||
getCachedURLs() {
|
||||
return [...this._urlsToCacheKeys.keys()];
|
||||
}
|
||||
/**
|
||||
* Returns the cache key used for storing a given URL. If that URL is
|
||||
* unversioned, like `/index.html', then the cache key will be the original
|
||||
* URL with a search parameter appended to it.
|
||||
*
|
||||
* @param {string} url A URL whose cache key you want to look up.
|
||||
* @return {string} The versioned URL that corresponds to a cache key
|
||||
* for the original URL, or undefined if that URL isn't precached.
|
||||
*/
|
||||
|
||||
|
||||
getCacheKeyForURL(url) {
|
||||
const urlObject = new URL(url, location);
|
||||
return this._urlsToCacheKeys.get(urlObject.href);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
let precacheController;
|
||||
/**
|
||||
* @return {PrecacheController}
|
||||
* @private
|
||||
*/
|
||||
|
||||
const getOrCreatePrecacheController = () => {
|
||||
if (!precacheController) {
|
||||
precacheController = new PrecacheController();
|
||||
}
|
||||
|
||||
return precacheController;
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Removes any URL search parameters that should be ignored.
|
||||
*
|
||||
* @param {URL} urlObject The original URL.
|
||||
* @param {Array<RegExp>} ignoreURLParametersMatching RegExps to test against
|
||||
* each search parameter name. Matches mean that the search parameter should be
|
||||
* ignored.
|
||||
* @return {URL} The URL with any ignored search parameters removed.
|
||||
*
|
||||
* @private
|
||||
* @memberof module:workbox-precaching
|
||||
*/
|
||||
|
||||
function removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching) {
|
||||
// Convert the iterable into an array at the start of the loop to make sure
|
||||
// deletion doesn't mess up iteration.
|
||||
for (const paramName of [...urlObject.searchParams.keys()]) {
|
||||
if (ignoreURLParametersMatching.some(regExp => regExp.test(paramName))) {
|
||||
urlObject.searchParams.delete(paramName);
|
||||
}
|
||||
}
|
||||
|
||||
return urlObject;
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Generator function that yields possible variations on the original URL to
|
||||
* check, one at a time.
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {Object} options
|
||||
*
|
||||
* @private
|
||||
* @memberof module:workbox-precaching
|
||||
*/
|
||||
|
||||
function* generateURLVariations(url, {
|
||||
ignoreURLParametersMatching,
|
||||
directoryIndex,
|
||||
cleanURLs,
|
||||
urlManipulation
|
||||
} = {}) {
|
||||
const urlObject = new URL(url, location);
|
||||
urlObject.hash = '';
|
||||
yield urlObject.href;
|
||||
const urlWithoutIgnoredParams = removeIgnoredSearchParams(urlObject, ignoreURLParametersMatching);
|
||||
yield urlWithoutIgnoredParams.href;
|
||||
|
||||
if (directoryIndex && urlWithoutIgnoredParams.pathname.endsWith('/')) {
|
||||
const directoryURL = new URL(urlWithoutIgnoredParams);
|
||||
directoryURL.pathname += directoryIndex;
|
||||
yield directoryURL.href;
|
||||
}
|
||||
|
||||
if (cleanURLs) {
|
||||
const cleanURL = new URL(urlWithoutIgnoredParams);
|
||||
cleanURL.pathname += '.html';
|
||||
yield cleanURL.href;
|
||||
}
|
||||
|
||||
if (urlManipulation) {
|
||||
const additionalURLs = urlManipulation({
|
||||
url: urlObject
|
||||
});
|
||||
|
||||
for (const urlToAttempt of additionalURLs) {
|
||||
yield urlToAttempt.href;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* This function will take the request URL and manipulate it based on the
|
||||
* configuration options.
|
||||
*
|
||||
* @param {string} url
|
||||
* @param {Object} options
|
||||
* @return {string} Returns the URL in the cache that matches the request,
|
||||
* if possible.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
const getCacheKeyForURL = (url, options) => {
|
||||
const precacheController = getOrCreatePrecacheController();
|
||||
const urlsToCacheKeys = precacheController.getURLsToCacheKeys();
|
||||
|
||||
for (const possibleURL of generateURLVariations(url, options)) {
|
||||
const possibleCacheKey = urlsToCacheKeys.get(possibleURL);
|
||||
|
||||
if (possibleCacheKey) {
|
||||
return possibleCacheKey;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Adds a `fetch` listener to the service worker that will
|
||||
* respond to
|
||||
* [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests}
|
||||
* with precached assets.
|
||||
*
|
||||
* Requests for assets that aren't precached, the `FetchEvent` will not be
|
||||
* responded to, allowing the event to fall through to other `fetch` event
|
||||
* listeners.
|
||||
*
|
||||
* NOTE: when called more than once this method will replace the previously set
|
||||
* configuration options. Calling it more than once is not recommended outside
|
||||
* of tests.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} options
|
||||
* @param {string} [options.directoryIndex=index.html] The `directoryIndex` will
|
||||
* check cache entries for a URLs ending with '/' to see if there is a hit when
|
||||
* appending the `directoryIndex` value.
|
||||
* @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/]] An
|
||||
* array of regex's to remove search params when looking for a cache match.
|
||||
* @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will
|
||||
* check the cache for the URL with a `.html` added to the end of the end.
|
||||
* @param {workbox.precaching~urlManipulation} [options.urlManipulation]
|
||||
* This is a function that should take a URL and return an array of
|
||||
* alternative URL's that should be checked for precache matches.
|
||||
*/
|
||||
|
||||
const addFetchListener = ({
|
||||
ignoreURLParametersMatching = [/^utm_/],
|
||||
directoryIndex = 'index.html',
|
||||
cleanURLs = true,
|
||||
urlManipulation = null
|
||||
} = {}) => {
|
||||
const cacheName = cacheNames_mjs.cacheNames.getPrecacheName();
|
||||
addEventListener('fetch', event => {
|
||||
const precachedURL = getCacheKeyForURL(event.request.url, {
|
||||
cleanURLs,
|
||||
directoryIndex,
|
||||
ignoreURLParametersMatching,
|
||||
urlManipulation
|
||||
});
|
||||
|
||||
if (!precachedURL) {
|
||||
{
|
||||
logger_mjs.logger.debug(`Precaching did not find a match for ` + getFriendlyURL_mjs.getFriendlyURL(event.request.url));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
let responsePromise = caches.open(cacheName).then(cache => {
|
||||
return cache.match(precachedURL);
|
||||
}).then(cachedResponse => {
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
} // Fall back to the network if we don't have a cached response
|
||||
// (perhaps due to manual cache cleanup).
|
||||
|
||||
|
||||
{
|
||||
logger_mjs.logger.warn(`The precached response for ` + `${getFriendlyURL_mjs.getFriendlyURL(precachedURL)} in ${cacheName} was not found. ` + `Falling back to the network instead.`);
|
||||
}
|
||||
|
||||
return fetch(precachedURL);
|
||||
});
|
||||
|
||||
{
|
||||
responsePromise = responsePromise.then(response => {
|
||||
// Workbox is going to handle the route.
|
||||
// print the routing details to the console.
|
||||
logger_mjs.logger.groupCollapsed(`Precaching is responding to: ` + getFriendlyURL_mjs.getFriendlyURL(event.request.url));
|
||||
logger_mjs.logger.log(`Serving the precached url: ${precachedURL}`);
|
||||
logger_mjs.logger.groupCollapsed(`View request details here.`);
|
||||
logger_mjs.logger.log(event.request);
|
||||
logger_mjs.logger.groupEnd();
|
||||
logger_mjs.logger.groupCollapsed(`View response details here.`);
|
||||
logger_mjs.logger.log(response);
|
||||
logger_mjs.logger.groupEnd();
|
||||
logger_mjs.logger.groupEnd();
|
||||
return response;
|
||||
});
|
||||
}
|
||||
|
||||
event.respondWith(responsePromise);
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
let listenerAdded = false;
|
||||
/**
|
||||
* Add a `fetch` listener to the service worker that will
|
||||
* respond to
|
||||
* [network requests]{@link https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API/Using_Service_Workers#Custom_responses_to_requests}
|
||||
* with precached assets.
|
||||
*
|
||||
* Requests for assets that aren't precached, the `FetchEvent` will not be
|
||||
* responded to, allowing the event to fall through to other `fetch` event
|
||||
* listeners.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {string} [options.directoryIndex=index.html] The `directoryIndex` will
|
||||
* check cache entries for a URLs ending with '/' to see if there is a hit when
|
||||
* appending the `directoryIndex` value.
|
||||
* @param {Array<RegExp>} [options.ignoreURLParametersMatching=[/^utm_/]] An
|
||||
* array of regex's to remove search params when looking for a cache match.
|
||||
* @param {boolean} [options.cleanURLs=true] The `cleanURLs` option will
|
||||
* check the cache for the URL with a `.html` added to the end of the end.
|
||||
* @param {workbox.precaching~urlManipulation} [options.urlManipulation]
|
||||
* This is a function that should take a URL and return an array of
|
||||
* alternative URL's that should be checked for precache matches.
|
||||
*
|
||||
* @alias workbox.precaching.addRoute
|
||||
*/
|
||||
|
||||
const addRoute = options => {
|
||||
if (!listenerAdded) {
|
||||
addFetchListener(options);
|
||||
listenerAdded = true;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
const SUBSTRING_TO_FIND = '-precache-';
|
||||
/**
|
||||
* Cleans up incompatible precaches that were created by older versions of
|
||||
* Workbox, by a service worker registered under the current scope.
|
||||
*
|
||||
* This is meant to be called as part of the `activate` event.
|
||||
*
|
||||
* This should be safe to use as long as you don't include `substringToFind`
|
||||
* (defaulting to `-precache-`) in your non-precache cache names.
|
||||
*
|
||||
* @param {string} currentPrecacheName The cache name currently in use for
|
||||
* precaching. This cache won't be deleted.
|
||||
* @param {string} [substringToFind='-precache-'] Cache names which include this
|
||||
* substring will be deleted (excluding `currentPrecacheName`).
|
||||
* @return {Array<string>} A list of all the cache names that were deleted.
|
||||
*
|
||||
* @private
|
||||
* @memberof module:workbox-precaching
|
||||
*/
|
||||
|
||||
const deleteOutdatedCaches = async (currentPrecacheName, substringToFind = SUBSTRING_TO_FIND) => {
|
||||
const cacheNames = await caches.keys();
|
||||
const cacheNamesToDelete = cacheNames.filter(cacheName => {
|
||||
return cacheName.includes(substringToFind) && cacheName.includes(self.registration.scope) && cacheName !== currentPrecacheName;
|
||||
});
|
||||
await Promise.all(cacheNamesToDelete.map(cacheName => caches.delete(cacheName)));
|
||||
return cacheNamesToDelete;
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Adds an `activate` event listener which will clean up incompatible
|
||||
* precaches that were created by older versions of Workbox.
|
||||
*
|
||||
* @alias workbox.precaching.cleanupOutdatedCaches
|
||||
*/
|
||||
|
||||
const cleanupOutdatedCaches = () => {
|
||||
addEventListener('activate', event => {
|
||||
const cacheName = cacheNames_mjs.cacheNames.getPrecacheName();
|
||||
event.waitUntil(deleteOutdatedCaches(cacheName).then(cachesDeleted => {
|
||||
{
|
||||
if (cachesDeleted.length > 0) {
|
||||
logger_mjs.logger.log(`The following out-of-date precaches were cleaned up ` + `automatically:`, cachesDeleted);
|
||||
}
|
||||
}
|
||||
}));
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Takes in a URL, and returns the corresponding URL that could be used to
|
||||
* lookup the entry in the precache.
|
||||
*
|
||||
* If a relative URL is provided, the location of the service worker file will
|
||||
* be used as the base.
|
||||
*
|
||||
* For precached entries without revision information, the cache key will be the
|
||||
* same as the original URL.
|
||||
*
|
||||
* For precached entries with revision information, the cache key will be the
|
||||
* original URL with the addition of a query parameter used for keeping track of
|
||||
* the revision info.
|
||||
*
|
||||
* @param {string} url The URL whose cache key to look up.
|
||||
* @return {string} The cache key that corresponds to that URL.
|
||||
*
|
||||
* @alias workbox.precaching.getCacheKeyForURL
|
||||
*/
|
||||
|
||||
const getCacheKeyForURL$1 = url => {
|
||||
const precacheController = getOrCreatePrecacheController();
|
||||
return precacheController.getCacheKeyForURL(url);
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
const installListener = event => {
|
||||
const precacheController = getOrCreatePrecacheController();
|
||||
const plugins = precachePlugins.get();
|
||||
event.waitUntil(precacheController.install({
|
||||
event,
|
||||
plugins
|
||||
}).catch(error => {
|
||||
{
|
||||
logger_mjs.logger.error(`Service worker installation failed. It will ` + `be retried automatically during the next navigation.`);
|
||||
} // Re-throw the error to ensure installation fails.
|
||||
|
||||
|
||||
throw error;
|
||||
}));
|
||||
};
|
||||
|
||||
const activateListener = event => {
|
||||
const precacheController = getOrCreatePrecacheController();
|
||||
const plugins = precachePlugins.get();
|
||||
event.waitUntil(precacheController.activate({
|
||||
event,
|
||||
plugins
|
||||
}));
|
||||
};
|
||||
/**
|
||||
* Adds items to the precache list, removing any duplicates and
|
||||
* stores the files in the
|
||||
* ["precache cache"]{@link module:workbox-core.cacheNames} when the service
|
||||
* worker installs.
|
||||
*
|
||||
* This method can be called multiple times.
|
||||
*
|
||||
* Please note: This method **will not** serve any of the cached files for you.
|
||||
* It only precaches files. To respond to a network request you call
|
||||
* [addRoute()]{@link module:workbox-precaching.addRoute}.
|
||||
*
|
||||
* If you have a single array of files to precache, you can just call
|
||||
* [precacheAndRoute()]{@link module:workbox-precaching.precacheAndRoute}.
|
||||
*
|
||||
* @param {Array<Object|string>} entries Array of entries to precache.
|
||||
*
|
||||
* @alias workbox.precaching.precache
|
||||
*/
|
||||
|
||||
|
||||
const precache = entries => {
|
||||
const precacheController = getOrCreatePrecacheController();
|
||||
precacheController.addToCacheList(entries);
|
||||
|
||||
if (entries.length > 0) {
|
||||
// NOTE: these listeners will only be added once (even if the `precache()`
|
||||
// method is called multiple times) because event listeners are implemented
|
||||
// as a set, where each listener must be unique.
|
||||
addEventListener('install', installListener);
|
||||
addEventListener('activate', activateListener);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* This method will add entries to the precache list and add a route to
|
||||
* respond to fetch events.
|
||||
*
|
||||
* This is a convenience method that will call
|
||||
* [precache()]{@link module:workbox-precaching.precache} and
|
||||
* [addRoute()]{@link module:workbox-precaching.addRoute} in a single call.
|
||||
*
|
||||
* @param {Array<Object|string>} entries Array of entries to precache.
|
||||
* @param {Object} options See
|
||||
* [addRoute() options]{@link module:workbox-precaching.addRoute}.
|
||||
*
|
||||
* @alias workbox.precaching.precacheAndRoute
|
||||
*/
|
||||
|
||||
const precacheAndRoute = (entries, options) => {
|
||||
precache(entries);
|
||||
addRoute(options);
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
{
|
||||
assert_mjs.assert.isSWEnv('workbox-precaching');
|
||||
}
|
||||
|
||||
exports.addPlugins = addPlugins;
|
||||
exports.addRoute = addRoute;
|
||||
exports.cleanupOutdatedCaches = cleanupOutdatedCaches;
|
||||
exports.getCacheKeyForURL = getCacheKeyForURL$1;
|
||||
exports.precache = precache;
|
||||
exports.precacheAndRoute = precacheAndRoute;
|
||||
exports.PrecacheController = PrecacheController;
|
||||
|
||||
return exports;
|
||||
|
||||
}({}, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private, workbox.core._private));
|
||||
//# sourceMappingURL=workbox-precaching.dev.js.map
|
|
@ -0,0 +1,2 @@
|
|||
this.workbox=this.workbox||{},this.workbox.precaching=function(t,e,n,s,c){"use strict";try{self["workbox:precaching:4.3.1"]&&_()}catch(t){}const o=[],i={get:()=>o,add(t){o.push(...t)}};const a="__WB_REVISION__";function r(t){if(!t)throw new c.WorkboxError("add-to-cache-list-unexpected-type",{entry:t});if("string"==typeof t){const e=new URL(t,location);return{cacheKey:e.href,url:e.href}}const{revision:e,url:n}=t;if(!n)throw new c.WorkboxError("add-to-cache-list-unexpected-type",{entry:t});if(!e){const t=new URL(n,location);return{cacheKey:t.href,url:t.href}}const s=new URL(n,location),o=new URL(n,location);return o.searchParams.set(a,e),{cacheKey:o.href,url:s.href}}class l{constructor(t){this.t=e.cacheNames.getPrecacheName(t),this.s=new Map}addToCacheList(t){for(const e of t){const{cacheKey:t,url:n}=r(e);if(this.s.has(n)&&this.s.get(n)!==t)throw new c.WorkboxError("add-to-cache-list-conflicting-entries",{firstEntry:this.s.get(n),secondEntry:t});this.s.set(n,t)}}async install({event:t,plugins:e}={}){const n=[],s=[],c=await caches.open(this.t),o=await c.keys(),i=new Set(o.map(t=>t.url));for(const t of this.s.values())i.has(t)?s.push(t):n.push(t);const a=n.map(n=>this.o({event:t,plugins:e,url:n}));return await Promise.all(a),{updatedURLs:n,notUpdatedURLs:s}}async activate(){const t=await caches.open(this.t),e=await t.keys(),n=new Set(this.s.values()),s=[];for(const c of e)n.has(c.url)||(await t.delete(c),s.push(c.url));return{deletedURLs:s}}async o({url:t,event:e,plugins:o}){const i=new Request(t,{credentials:"same-origin"});let a,r=await s.fetchWrapper.fetch({event:e,plugins:o,request:i});for(const t of o||[])"cacheWillUpdate"in t&&(a=t.cacheWillUpdate.bind(t));if(!(a?a({event:e,request:i,response:r}):r.status<400))throw new c.WorkboxError("bad-precaching-response",{url:t,status:r.status});r.redirected&&(r=await async function(t){const e=t.clone(),n="body"in e?Promise.resolve(e.body):e.blob(),s=await n;return new Response(s,{headers:e.headers,status:e.status,statusText:e.statusText})}(r)),await n.cacheWrapper.put({event:e,plugins:o,request:i,response:r,cacheName:this.t,matchOptions:{ignoreSearch:!0}})}getURLsToCacheKeys(){return this.s}getCachedURLs(){return[...this.s.keys()]}getCacheKeyForURL(t){const e=new URL(t,location);return this.s.get(e.href)}}let u;const h=()=>(u||(u=new l),u);const d=(t,e)=>{const n=h().getURLsToCacheKeys();for(const s of function*(t,{ignoreURLParametersMatching:e,directoryIndex:n,cleanURLs:s,urlManipulation:c}={}){const o=new URL(t,location);o.hash="",yield o.href;const i=function(t,e){for(const n of[...t.searchParams.keys()])e.some(t=>t.test(n))&&t.searchParams.delete(n);return t}(o,e);if(yield i.href,n&&i.pathname.endsWith("/")){const t=new URL(i);t.pathname+=n,yield t.href}if(s){const t=new URL(i);t.pathname+=".html",yield t.href}if(c){const t=c({url:o});for(const e of t)yield e.href}}(t,e)){const t=n.get(s);if(t)return t}};let w=!1;const f=t=>{w||((({ignoreURLParametersMatching:t=[/^utm_/],directoryIndex:n="index.html",cleanURLs:s=!0,urlManipulation:c=null}={})=>{const o=e.cacheNames.getPrecacheName();addEventListener("fetch",e=>{const i=d(e.request.url,{cleanURLs:s,directoryIndex:n,ignoreURLParametersMatching:t,urlManipulation:c});if(!i)return;let a=caches.open(o).then(t=>t.match(i)).then(t=>t||fetch(i));e.respondWith(a)})})(t),w=!0)},y=t=>{const e=h(),n=i.get();t.waitUntil(e.install({event:t,plugins:n}).catch(t=>{throw t}))},p=t=>{const e=h(),n=i.get();t.waitUntil(e.activate({event:t,plugins:n}))},L=t=>{h().addToCacheList(t),t.length>0&&(addEventListener("install",y),addEventListener("activate",p))};return t.addPlugins=(t=>{i.add(t)}),t.addRoute=f,t.cleanupOutdatedCaches=(()=>{addEventListener("activate",t=>{const n=e.cacheNames.getPrecacheName();t.waitUntil((async(t,e="-precache-")=>{const n=(await caches.keys()).filter(n=>n.includes(e)&&n.includes(self.registration.scope)&&n!==t);return await Promise.all(n.map(t=>caches.delete(t))),n})(n).then(t=>{}))})}),t.getCacheKeyForURL=(t=>{return h().getCacheKeyForURL(t)}),t.precache=L,t.precacheAndRoute=((t,e)=>{L(t),f(e)}),t.PrecacheController=l,t}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private);
|
||||
//# sourceMappingURL=workbox-precaching.prod.js.map
|
|
@ -0,0 +1,268 @@
|
|||
this.workbox = this.workbox || {};
|
||||
this.workbox.rangeRequests = (function (exports, WorkboxError_mjs, assert_mjs, logger_mjs) {
|
||||
'use strict';
|
||||
|
||||
try {
|
||||
self['workbox:range-requests:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* @param {Blob} blob A source blob.
|
||||
* @param {number|null} start The offset to use as the start of the
|
||||
* slice.
|
||||
* @param {number|null} end The offset to use as the end of the slice.
|
||||
* @return {Object} An object with `start` and `end` properties, reflecting
|
||||
* the effective boundaries to use given the size of the blob.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
function calculateEffectiveBoundaries(blob, start, end) {
|
||||
{
|
||||
assert_mjs.assert.isInstance(blob, Blob, {
|
||||
moduleName: 'workbox-range-requests',
|
||||
funcName: 'calculateEffectiveBoundaries',
|
||||
paramName: 'blob'
|
||||
});
|
||||
}
|
||||
|
||||
const blobSize = blob.size;
|
||||
|
||||
if (end > blobSize || start < 0) {
|
||||
throw new WorkboxError_mjs.WorkboxError('range-not-satisfiable', {
|
||||
size: blobSize,
|
||||
end,
|
||||
start
|
||||
});
|
||||
}
|
||||
|
||||
let effectiveStart;
|
||||
let effectiveEnd;
|
||||
|
||||
if (start === null) {
|
||||
effectiveStart = blobSize - end;
|
||||
effectiveEnd = blobSize;
|
||||
} else if (end === null) {
|
||||
effectiveStart = start;
|
||||
effectiveEnd = blobSize;
|
||||
} else {
|
||||
effectiveStart = start; // Range values are inclusive, so add 1 to the value.
|
||||
|
||||
effectiveEnd = end + 1;
|
||||
}
|
||||
|
||||
return {
|
||||
start: effectiveStart,
|
||||
end: effectiveEnd
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* @param {string} rangeHeader A Range: header value.
|
||||
* @return {Object} An object with `start` and `end` properties, reflecting
|
||||
* the parsed value of the Range: header. If either the `start` or `end` are
|
||||
* omitted, then `null` will be returned.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
function parseRangeHeader(rangeHeader) {
|
||||
{
|
||||
assert_mjs.assert.isType(rangeHeader, 'string', {
|
||||
moduleName: 'workbox-range-requests',
|
||||
funcName: 'parseRangeHeader',
|
||||
paramName: 'rangeHeader'
|
||||
});
|
||||
}
|
||||
|
||||
const normalizedRangeHeader = rangeHeader.trim().toLowerCase();
|
||||
|
||||
if (!normalizedRangeHeader.startsWith('bytes=')) {
|
||||
throw new WorkboxError_mjs.WorkboxError('unit-must-be-bytes', {
|
||||
normalizedRangeHeader
|
||||
});
|
||||
} // Specifying multiple ranges separate by commas is valid syntax, but this
|
||||
// library only attempts to handle a single, contiguous sequence of bytes.
|
||||
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Range#Syntax
|
||||
|
||||
|
||||
if (normalizedRangeHeader.includes(',')) {
|
||||
throw new WorkboxError_mjs.WorkboxError('single-range-only', {
|
||||
normalizedRangeHeader
|
||||
});
|
||||
}
|
||||
|
||||
const rangeParts = /(\d*)-(\d*)/.exec(normalizedRangeHeader); // We need either at least one of the start or end values.
|
||||
|
||||
if (rangeParts === null || !(rangeParts[1] || rangeParts[2])) {
|
||||
throw new WorkboxError_mjs.WorkboxError('invalid-range-values', {
|
||||
normalizedRangeHeader
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
start: rangeParts[1] === '' ? null : Number(rangeParts[1]),
|
||||
end: rangeParts[2] === '' ? null : Number(rangeParts[2])
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Given a `Request` and `Response` objects as input, this will return a
|
||||
* promise for a new `Response`.
|
||||
*
|
||||
* If the original `Response` already contains partial content (i.e. it has
|
||||
* a status of 206), then this assumes it already fulfills the `Range:`
|
||||
* requirements, and will return it as-is.
|
||||
*
|
||||
* @param {Request} request A request, which should contain a Range:
|
||||
* header.
|
||||
* @param {Response} originalResponse A response.
|
||||
* @return {Promise<Response>} Either a `206 Partial Content` response, with
|
||||
* the response body set to the slice of content specified by the request's
|
||||
* `Range:` header, or a `416 Range Not Satisfiable` response if the
|
||||
* conditions of the `Range:` header can't be met.
|
||||
*
|
||||
* @memberof workbox.rangeRequests
|
||||
*/
|
||||
|
||||
async function createPartialResponse(request, originalResponse) {
|
||||
try {
|
||||
{
|
||||
assert_mjs.assert.isInstance(request, Request, {
|
||||
moduleName: 'workbox-range-requests',
|
||||
funcName: 'createPartialResponse',
|
||||
paramName: 'request'
|
||||
});
|
||||
assert_mjs.assert.isInstance(originalResponse, Response, {
|
||||
moduleName: 'workbox-range-requests',
|
||||
funcName: 'createPartialResponse',
|
||||
paramName: 'originalResponse'
|
||||
});
|
||||
}
|
||||
|
||||
if (originalResponse.status === 206) {
|
||||
// If we already have a 206, then just pass it through as-is;
|
||||
// see https://github.com/GoogleChrome/workbox/issues/1720
|
||||
return originalResponse;
|
||||
}
|
||||
|
||||
const rangeHeader = request.headers.get('range');
|
||||
|
||||
if (!rangeHeader) {
|
||||
throw new WorkboxError_mjs.WorkboxError('no-range-header');
|
||||
}
|
||||
|
||||
const boundaries = parseRangeHeader(rangeHeader);
|
||||
const originalBlob = await originalResponse.blob();
|
||||
const effectiveBoundaries = calculateEffectiveBoundaries(originalBlob, boundaries.start, boundaries.end);
|
||||
const slicedBlob = originalBlob.slice(effectiveBoundaries.start, effectiveBoundaries.end);
|
||||
const slicedBlobSize = slicedBlob.size;
|
||||
const slicedResponse = new Response(slicedBlob, {
|
||||
// Status code 206 is for a Partial Content response.
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/206
|
||||
status: 206,
|
||||
statusText: 'Partial Content',
|
||||
headers: originalResponse.headers
|
||||
});
|
||||
slicedResponse.headers.set('Content-Length', slicedBlobSize);
|
||||
slicedResponse.headers.set('Content-Range', `bytes ${effectiveBoundaries.start}-${effectiveBoundaries.end - 1}/` + originalBlob.size);
|
||||
return slicedResponse;
|
||||
} catch (error) {
|
||||
{
|
||||
logger_mjs.logger.warn(`Unable to construct a partial response; returning a ` + `416 Range Not Satisfiable response instead.`);
|
||||
logger_mjs.logger.groupCollapsed(`View details here.`);
|
||||
logger_mjs.logger.log(error);
|
||||
logger_mjs.logger.log(request);
|
||||
logger_mjs.logger.log(originalResponse);
|
||||
logger_mjs.logger.groupEnd();
|
||||
}
|
||||
|
||||
return new Response('', {
|
||||
status: 416,
|
||||
statusText: 'Range Not Satisfiable'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* The range request plugin makes it easy for a request with a 'Range' header to
|
||||
* be fulfilled by a cached response.
|
||||
*
|
||||
* It does this by intercepting the `cachedResponseWillBeUsed` plugin callback
|
||||
* and returning the appropriate subset of the cached response body.
|
||||
*
|
||||
* @memberof workbox.rangeRequests
|
||||
*/
|
||||
|
||||
class Plugin {
|
||||
/**
|
||||
* @param {Object} options
|
||||
* @param {Request} options.request The original request, which may or may not
|
||||
* contain a Range: header.
|
||||
* @param {Response} options.cachedResponse The complete cached response.
|
||||
* @return {Promise<Response>} If request contains a 'Range' header, then a
|
||||
* new response with status 206 whose body is a subset of `cachedResponse` is
|
||||
* returned. Otherwise, `cachedResponse` is returned as-is.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
async cachedResponseWillBeUsed({
|
||||
request,
|
||||
cachedResponse
|
||||
}) {
|
||||
// Only return a sliced response if there's something valid in the cache,
|
||||
// and there's a Range: header in the request.
|
||||
if (cachedResponse && request.headers.has('range')) {
|
||||
return await createPartialResponse(request, cachedResponse);
|
||||
} // If there was no Range: header, or if cachedResponse wasn't valid, just
|
||||
// pass it through as-is.
|
||||
|
||||
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
exports.createPartialResponse = createPartialResponse;
|
||||
exports.Plugin = Plugin;
|
||||
|
||||
return exports;
|
||||
|
||||
}({}, workbox.core._private, workbox.core._private, workbox.core._private));
|
||||
//# sourceMappingURL=workbox-range-requests.dev.js.map
|
|
@ -0,0 +1,2 @@
|
|||
this.workbox=this.workbox||{},this.workbox.rangeRequests=function(e,n){"use strict";try{self["workbox:range-requests:4.3.1"]&&_()}catch(e){}async function t(e,t){try{if(206===t.status)return t;const s=e.headers.get("range");if(!s)throw new n.WorkboxError("no-range-header");const a=function(e){const t=e.trim().toLowerCase();if(!t.startsWith("bytes="))throw new n.WorkboxError("unit-must-be-bytes",{normalizedRangeHeader:t});if(t.includes(","))throw new n.WorkboxError("single-range-only",{normalizedRangeHeader:t});const s=/(\d*)-(\d*)/.exec(t);if(null===s||!s[1]&&!s[2])throw new n.WorkboxError("invalid-range-values",{normalizedRangeHeader:t});return{start:""===s[1]?null:Number(s[1]),end:""===s[2]?null:Number(s[2])}}(s),r=await t.blob(),i=function(e,t,s){const a=e.size;if(s>a||t<0)throw new n.WorkboxError("range-not-satisfiable",{size:a,end:s,start:t});let r,i;return null===t?(r=a-s,i=a):null===s?(r=t,i=a):(r=t,i=s+1),{start:r,end:i}}(r,a.start,a.end),o=r.slice(i.start,i.end),u=o.size,l=new Response(o,{status:206,statusText:"Partial Content",headers:t.headers});return l.headers.set("Content-Length",u),l.headers.set("Content-Range",`bytes ${i.start}-${i.end-1}/`+r.size),l}catch(e){return new Response("",{status:416,statusText:"Range Not Satisfiable"})}}return e.createPartialResponse=t,e.Plugin=class{async cachedResponseWillBeUsed({request:e,cachedResponse:n}){return n&&e.headers.has("range")?await t(e,n):n}},e}({},workbox.core._private);
|
||||
//# sourceMappingURL=workbox-range-requests.prod.js.map
|
|
@ -0,0 +1,2 @@
|
|||
this.workbox=this.workbox||{},this.workbox.routing=function(t,e,r){"use strict";try{self["workbox:routing:4.3.1"]&&_()}catch(t){}const s="GET",n=t=>t&&"object"==typeof t?t:{handle:t};class o{constructor(t,e,r){this.handler=n(e),this.match=t,this.method=r||s}}class i extends o{constructor(t,{whitelist:e=[/./],blacklist:r=[]}={}){super(t=>this.t(t),t),this.s=e,this.o=r}t({url:t,request:e}){if("navigate"!==e.mode)return!1;const r=t.pathname+t.search;for(const t of this.o)if(t.test(r))return!1;return!!this.s.some(t=>t.test(r))}}class u extends o{constructor(t,e,r){super(({url:e})=>{const r=t.exec(e.href);return r?e.origin!==location.origin&&0!==r.index?null:r.slice(1):null},e,r)}}class c{constructor(){this.i=new Map}get routes(){return this.i}addFetchListener(){self.addEventListener("fetch",t=>{const{request:e}=t,r=this.handleRequest({request:e,event:t});r&&t.respondWith(r)})}addCacheListener(){self.addEventListener("message",async t=>{if(t.data&&"CACHE_URLS"===t.data.type){const{payload:e}=t.data,r=Promise.all(e.urlsToCache.map(t=>{"string"==typeof t&&(t=[t]);const e=new Request(...t);return this.handleRequest({request:e})}));t.waitUntil(r),t.ports&&t.ports[0]&&(await r,t.ports[0].postMessage(!0))}})}handleRequest({request:t,event:e}){const r=new URL(t.url,location);if(!r.protocol.startsWith("http"))return;let s,{params:n,route:o}=this.findMatchingRoute({url:r,request:t,event:e}),i=o&&o.handler;if(!i&&this.u&&(i=this.u),i){try{s=i.handle({url:r,request:t,event:e,params:n})}catch(t){s=Promise.reject(t)}return s&&this.h&&(s=s.catch(t=>this.h.handle({url:r,event:e,err:t}))),s}}findMatchingRoute({url:t,request:e,event:r}){const s=this.i.get(e.method)||[];for(const n of s){let s,o=n.match({url:t,request:e,event:r});if(o)return Array.isArray(o)&&o.length>0?s=o:o.constructor===Object&&Object.keys(o).length>0&&(s=o),{route:n,params:s}}return{}}setDefaultHandler(t){this.u=n(t)}setCatchHandler(t){this.h=n(t)}registerRoute(t){this.i.has(t.method)||this.i.set(t.method,[]),this.i.get(t.method).push(t)}unregisterRoute(t){if(!this.i.has(t.method))throw new r.WorkboxError("unregister-route-but-not-found-with-method",{method:t.method});const e=this.i.get(t.method).indexOf(t);if(!(e>-1))throw new r.WorkboxError("unregister-route-route-not-registered");this.i.get(t.method).splice(e,1)}}let a;const h=()=>(a||((a=new c).addFetchListener(),a.addCacheListener()),a);return t.NavigationRoute=i,t.RegExpRoute=u,t.registerNavigationRoute=((t,r={})=>{const s=e.cacheNames.getPrecacheName(r.cacheName),n=new i(async()=>{try{const e=await caches.match(t,{cacheName:s});if(e)return e;throw new Error(`The cache ${s} did not have an entry for `+`${t}.`)}catch(e){return fetch(t)}},{whitelist:r.whitelist,blacklist:r.blacklist});return h().registerRoute(n),n}),t.registerRoute=((t,e,s="GET")=>{let n;if("string"==typeof t){const r=new URL(t,location);n=new o(({url:t})=>t.href===r.href,e,s)}else if(t instanceof RegExp)n=new u(t,e,s);else if("function"==typeof t)n=new o(t,e,s);else{if(!(t instanceof o))throw new r.WorkboxError("unsupported-route-type",{moduleName:"workbox-routing",funcName:"registerRoute",paramName:"capture"});n=t}return h().registerRoute(n),n}),t.Route=o,t.Router=c,t.setCatchHandler=(t=>{h().setCatchHandler(t)}),t.setDefaultHandler=(t=>{h().setDefaultHandler(t)}),t}({},workbox.core._private,workbox.core._private);
|
||||
//# sourceMappingURL=workbox-routing.prod.js.map
|
|
@ -0,0 +1,2 @@
|
|||
this.workbox=this.workbox||{},this.workbox.strategies=function(e,t,s,n,r){"use strict";try{self["workbox:strategies:4.3.1"]&&_()}catch(e){}class i{constructor(e={}){this.t=t.cacheNames.getRuntimeName(e.cacheName),this.s=e.plugins||[],this.i=e.fetchOptions||null,this.h=e.matchOptions||null}async handle({event:e,request:t}){return this.makeRequest({event:e,request:t||e.request})}async makeRequest({event:e,request:t}){"string"==typeof t&&(t=new Request(t));let n,i=await s.cacheWrapper.match({cacheName:this.t,request:t,event:e,matchOptions:this.h,plugins:this.s});if(!i)try{i=await this.u(t,e)}catch(e){n=e}if(!i)throw new r.WorkboxError("no-response",{url:t.url,error:n});return i}async u(e,t){const r=await n.fetchWrapper.fetch({request:e,event:t,fetchOptions:this.i,plugins:this.s}),i=r.clone(),h=s.cacheWrapper.put({cacheName:this.t,request:e,response:i,event:t,plugins:this.s});if(t)try{t.waitUntil(h)}catch(e){}return r}}class h{constructor(e={}){this.t=t.cacheNames.getRuntimeName(e.cacheName),this.s=e.plugins||[],this.h=e.matchOptions||null}async handle({event:e,request:t}){return this.makeRequest({event:e,request:t||e.request})}async makeRequest({event:e,request:t}){"string"==typeof t&&(t=new Request(t));const n=await s.cacheWrapper.match({cacheName:this.t,request:t,event:e,matchOptions:this.h,plugins:this.s});if(!n)throw new r.WorkboxError("no-response",{url:t.url});return n}}const u={cacheWillUpdate:({response:e})=>200===e.status||0===e.status?e:null};class a{constructor(e={}){if(this.t=t.cacheNames.getRuntimeName(e.cacheName),e.plugins){let t=e.plugins.some(e=>!!e.cacheWillUpdate);this.s=t?e.plugins:[u,...e.plugins]}else this.s=[u];this.o=e.networkTimeoutSeconds,this.i=e.fetchOptions||null,this.h=e.matchOptions||null}async handle({event:e,request:t}){return this.makeRequest({event:e,request:t||e.request})}async makeRequest({event:e,request:t}){const s=[];"string"==typeof t&&(t=new Request(t));const n=[];let i;if(this.o){const{id:r,promise:h}=this.l({request:t,event:e,logs:s});i=r,n.push(h)}const h=this.q({timeoutId:i,request:t,event:e,logs:s});n.push(h);let u=await Promise.race(n);if(u||(u=await h),!u)throw new r.WorkboxError("no-response",{url:t.url});return u}l({request:e,logs:t,event:s}){let n;return{promise:new Promise(t=>{n=setTimeout(async()=>{t(await this.p({request:e,event:s}))},1e3*this.o)}),id:n}}async q({timeoutId:e,request:t,logs:r,event:i}){let h,u;try{u=await n.fetchWrapper.fetch({request:t,event:i,fetchOptions:this.i,plugins:this.s})}catch(e){h=e}if(e&&clearTimeout(e),h||!u)u=await this.p({request:t,event:i});else{const e=u.clone(),n=s.cacheWrapper.put({cacheName:this.t,request:t,response:e,event:i,plugins:this.s});if(i)try{i.waitUntil(n)}catch(e){}}return u}p({event:e,request:t}){return s.cacheWrapper.match({cacheName:this.t,request:t,event:e,matchOptions:this.h,plugins:this.s})}}class c{constructor(e={}){this.t=t.cacheNames.getRuntimeName(e.cacheName),this.s=e.plugins||[],this.i=e.fetchOptions||null}async handle({event:e,request:t}){return this.makeRequest({event:e,request:t||e.request})}async makeRequest({event:e,request:t}){let s,i;"string"==typeof t&&(t=new Request(t));try{i=await n.fetchWrapper.fetch({request:t,event:e,fetchOptions:this.i,plugins:this.s})}catch(e){s=e}if(!i)throw new r.WorkboxError("no-response",{url:t.url,error:s});return i}}class o{constructor(e={}){if(this.t=t.cacheNames.getRuntimeName(e.cacheName),this.s=e.plugins||[],e.plugins){let t=e.plugins.some(e=>!!e.cacheWillUpdate);this.s=t?e.plugins:[u,...e.plugins]}else this.s=[u];this.i=e.fetchOptions||null,this.h=e.matchOptions||null}async handle({event:e,request:t}){return this.makeRequest({event:e,request:t||e.request})}async makeRequest({event:e,request:t}){"string"==typeof t&&(t=new Request(t));const n=this.u({request:t,event:e});let i,h=await s.cacheWrapper.match({cacheName:this.t,request:t,event:e,matchOptions:this.h,plugins:this.s});if(h){if(e)try{e.waitUntil(n)}catch(i){}}else try{h=await n}catch(e){i=e}if(!h)throw new r.WorkboxError("no-response",{url:t.url,error:i});return h}async u({request:e,event:t}){const r=await n.fetchWrapper.fetch({request:e,event:t,fetchOptions:this.i,plugins:this.s}),i=s.cacheWrapper.put({cacheName:this.t,request:e,response:r.clone(),event:t,plugins:this.s});if(t)try{t.waitUntil(i)}catch(e){}return r}}const l={cacheFirst:i,cacheOnly:h,networkFirst:a,networkOnly:c,staleWhileRevalidate:o},q=e=>{const t=l[e];return e=>new t(e)},w=q("cacheFirst"),p=q("cacheOnly"),v=q("networkFirst"),y=q("networkOnly"),m=q("staleWhileRevalidate");return e.CacheFirst=i,e.CacheOnly=h,e.NetworkFirst=a,e.NetworkOnly=c,e.StaleWhileRevalidate=o,e.cacheFirst=w,e.cacheOnly=p,e.networkFirst=v,e.networkOnly=y,e.staleWhileRevalidate=m,e}({},workbox.core._private,workbox.core._private,workbox.core._private,workbox.core._private);
|
||||
//# sourceMappingURL=workbox-strategies.prod.js.map
|
|
@ -0,0 +1,337 @@
|
|||
this.workbox = this.workbox || {};
|
||||
this.workbox.streams = (function (exports, logger_mjs, assert_mjs) {
|
||||
'use strict';
|
||||
|
||||
try {
|
||||
self['workbox:streams:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Takes either a Response, a ReadableStream, or a
|
||||
* [BodyInit](https://fetch.spec.whatwg.org/#bodyinit) and returns the
|
||||
* ReadableStreamReader object associated with it.
|
||||
*
|
||||
* @param {workbox.streams.StreamSource} source
|
||||
* @return {ReadableStreamReader}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function _getReaderFromSource(source) {
|
||||
if (source.body && source.body.getReader) {
|
||||
return source.body.getReader();
|
||||
}
|
||||
|
||||
if (source.getReader) {
|
||||
return source.getReader();
|
||||
} // TODO: This should be possible to do by constructing a ReadableStream, but
|
||||
// I can't get it to work. As a hack, construct a new Response, and use the
|
||||
// reader associated with its body.
|
||||
|
||||
|
||||
return new Response(source).body.getReader();
|
||||
}
|
||||
/**
|
||||
* Takes multiple source Promises, each of which could resolve to a Response, a
|
||||
* ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit).
|
||||
*
|
||||
* Returns an object exposing a ReadableStream with each individual stream's
|
||||
* data returned in sequence, along with a Promise which signals when the
|
||||
* stream is finished (useful for passing to a FetchEvent's waitUntil()).
|
||||
*
|
||||
* @param {Array<Promise<workbox.streams.StreamSource>>} sourcePromises
|
||||
* @return {Object<{done: Promise, stream: ReadableStream}>}
|
||||
*
|
||||
* @memberof workbox.streams
|
||||
*/
|
||||
|
||||
|
||||
function concatenate(sourcePromises) {
|
||||
{
|
||||
assert_mjs.assert.isArray(sourcePromises, {
|
||||
moduleName: 'workbox-streams',
|
||||
funcName: 'concatenate',
|
||||
paramName: 'sourcePromises'
|
||||
});
|
||||
}
|
||||
|
||||
const readerPromises = sourcePromises.map(sourcePromise => {
|
||||
return Promise.resolve(sourcePromise).then(source => {
|
||||
return _getReaderFromSource(source);
|
||||
});
|
||||
});
|
||||
let fullyStreamedResolve;
|
||||
let fullyStreamedReject;
|
||||
const done = new Promise((resolve, reject) => {
|
||||
fullyStreamedResolve = resolve;
|
||||
fullyStreamedReject = reject;
|
||||
});
|
||||
let i = 0;
|
||||
const logMessages = [];
|
||||
const stream = new ReadableStream({
|
||||
pull(controller) {
|
||||
return readerPromises[i].then(reader => reader.read()).then(result => {
|
||||
if (result.done) {
|
||||
{
|
||||
logMessages.push(['Reached the end of source:', sourcePromises[i]]);
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
if (i >= readerPromises.length) {
|
||||
// Log all the messages in the group at once in a single group.
|
||||
{
|
||||
logger_mjs.logger.groupCollapsed(`Concatenating ${readerPromises.length} sources.`);
|
||||
|
||||
for (const message of logMessages) {
|
||||
if (Array.isArray(message)) {
|
||||
logger_mjs.logger.log(...message);
|
||||
} else {
|
||||
logger_mjs.logger.log(message);
|
||||
}
|
||||
}
|
||||
|
||||
logger_mjs.logger.log('Finished reading all sources.');
|
||||
logger_mjs.logger.groupEnd();
|
||||
}
|
||||
|
||||
controller.close();
|
||||
fullyStreamedResolve();
|
||||
return;
|
||||
}
|
||||
|
||||
return this.pull(controller);
|
||||
} else {
|
||||
controller.enqueue(result.value);
|
||||
}
|
||||
}).catch(error => {
|
||||
{
|
||||
logger_mjs.logger.error('An error occurred:', error);
|
||||
}
|
||||
|
||||
fullyStreamedReject(error);
|
||||
throw error;
|
||||
});
|
||||
},
|
||||
|
||||
cancel() {
|
||||
{
|
||||
logger_mjs.logger.warn('The ReadableStream was cancelled.');
|
||||
}
|
||||
|
||||
fullyStreamedResolve();
|
||||
}
|
||||
|
||||
});
|
||||
return {
|
||||
done,
|
||||
stream
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* This is a utility method that determines whether the current browser supports
|
||||
* the features required to create streamed responses. Currently, it checks if
|
||||
* [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
|
||||
* is available.
|
||||
*
|
||||
* @param {HeadersInit} [headersInit] If there's no `Content-Type` specified,
|
||||
* `'text/html'` will be used by default.
|
||||
* @return {boolean} `true`, if the current browser meets the requirements for
|
||||
* streaming responses, and `false` otherwise.
|
||||
*
|
||||
* @memberof workbox.streams
|
||||
*/
|
||||
|
||||
function createHeaders(headersInit = {}) {
|
||||
// See https://github.com/GoogleChrome/workbox/issues/1461
|
||||
const headers = new Headers(headersInit);
|
||||
|
||||
if (!headers.has('content-type')) {
|
||||
headers.set('content-type', 'text/html');
|
||||
}
|
||||
|
||||
return headers;
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Takes multiple source Promises, each of which could resolve to a Response, a
|
||||
* ReadableStream, or a [BodyInit](https://fetch.spec.whatwg.org/#bodyinit),
|
||||
* along with a
|
||||
* [HeadersInit](https://fetch.spec.whatwg.org/#typedefdef-headersinit).
|
||||
*
|
||||
* Returns an object exposing a Response whose body consists of each individual
|
||||
* stream's data returned in sequence, along with a Promise which signals when
|
||||
* the stream is finished (useful for passing to a FetchEvent's waitUntil()).
|
||||
*
|
||||
* @param {Array<Promise<workbox.streams.StreamSource>>} sourcePromises
|
||||
* @param {HeadersInit} [headersInit] If there's no `Content-Type` specified,
|
||||
* `'text/html'` will be used by default.
|
||||
* @return {Object<{done: Promise, response: Response}>}
|
||||
*
|
||||
* @memberof workbox.streams
|
||||
*/
|
||||
|
||||
function concatenateToResponse(sourcePromises, headersInit) {
|
||||
const {
|
||||
done,
|
||||
stream
|
||||
} = concatenate(sourcePromises);
|
||||
const headers = createHeaders(headersInit);
|
||||
const response = new Response(stream, {
|
||||
headers
|
||||
});
|
||||
return {
|
||||
done,
|
||||
response
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
let cachedIsSupported = undefined;
|
||||
/**
|
||||
* This is a utility method that determines whether the current browser supports
|
||||
* the features required to create streamed responses. Currently, it checks if
|
||||
* [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream/ReadableStream)
|
||||
* can be created.
|
||||
*
|
||||
* @return {boolean} `true`, if the current browser meets the requirements for
|
||||
* streaming responses, and `false` otherwise.
|
||||
*
|
||||
* @memberof workbox.streams
|
||||
*/
|
||||
|
||||
function isSupported() {
|
||||
if (cachedIsSupported === undefined) {
|
||||
// See https://github.com/GoogleChrome/workbox/issues/1473
|
||||
try {
|
||||
new ReadableStream({
|
||||
start() {}
|
||||
|
||||
});
|
||||
cachedIsSupported = true;
|
||||
} catch (error) {
|
||||
cachedIsSupported = false;
|
||||
}
|
||||
}
|
||||
|
||||
return cachedIsSupported;
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* A shortcut to create a strategy that could be dropped-in to Workbox's router.
|
||||
*
|
||||
* On browsers that do not support constructing new `ReadableStream`s, this
|
||||
* strategy will automatically wait for all the `sourceFunctions` to complete,
|
||||
* and create a final response that concatenates their values together.
|
||||
*
|
||||
* @param {
|
||||
* Array<function(workbox.routing.Route~handlerCallback)>} sourceFunctions
|
||||
* Each function should return a {@link workbox.streams.StreamSource} (or a
|
||||
* Promise which resolves to one).
|
||||
* @param {HeadersInit} [headersInit] If there's no `Content-Type` specified,
|
||||
* `'text/html'` will be used by default.
|
||||
* @return {workbox.routing.Route~handlerCallback}
|
||||
*
|
||||
* @memberof workbox.streams
|
||||
*/
|
||||
|
||||
function strategy(sourceFunctions, headersInit) {
|
||||
return async ({
|
||||
event,
|
||||
url,
|
||||
params
|
||||
}) => {
|
||||
if (isSupported()) {
|
||||
const {
|
||||
done,
|
||||
response
|
||||
} = concatenateToResponse(sourceFunctions.map(fn => fn({
|
||||
event,
|
||||
url,
|
||||
params
|
||||
})), headersInit);
|
||||
event.waitUntil(done);
|
||||
return response;
|
||||
}
|
||||
|
||||
{
|
||||
logger_mjs.logger.log(`The current browser doesn't support creating response ` + `streams. Falling back to non-streaming response instead.`);
|
||||
} // Fallback to waiting for everything to finish, and concatenating the
|
||||
// responses.
|
||||
|
||||
|
||||
const parts = await Promise.all(sourceFunctions.map(sourceFunction => sourceFunction({
|
||||
event,
|
||||
url,
|
||||
params
|
||||
})).map(async responsePromise => {
|
||||
const response = await responsePromise;
|
||||
|
||||
if (response instanceof Response) {
|
||||
return response.blob();
|
||||
} // Otherwise, assume it's something like a string which can be used
|
||||
// as-is when constructing the final composite blob.
|
||||
|
||||
|
||||
return response;
|
||||
}));
|
||||
const headers = createHeaders(headersInit); // Constructing a new Response from a Blob source is well-supported.
|
||||
// So is constructing a new Blob from multiple source Blobs or strings.
|
||||
|
||||
return new Response(new Blob(parts), {
|
||||
headers
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
exports.concatenate = concatenate;
|
||||
exports.concatenateToResponse = concatenateToResponse;
|
||||
exports.isSupported = isSupported;
|
||||
exports.strategy = strategy;
|
||||
|
||||
return exports;
|
||||
|
||||
}({}, workbox.core._private, workbox.core._private));
|
||||
//# sourceMappingURL=workbox-streams.dev.js.map
|
|
@ -0,0 +1,2 @@
|
|||
this.workbox=this.workbox||{},this.workbox.streams=function(e){"use strict";try{self["workbox:streams:4.3.1"]&&_()}catch(e){}function n(e){const n=e.map(e=>Promise.resolve(e).then(e=>(function(e){return e.body&&e.body.getReader?e.body.getReader():e.getReader?e.getReader():new Response(e).body.getReader()})(e)));let t,r;const s=new Promise((e,n)=>{t=e,r=n});let o=0;return{done:s,stream:new ReadableStream({pull(e){return n[o].then(e=>e.read()).then(r=>{if(r.done)return++o>=n.length?(e.close(),void t()):this.pull(e);e.enqueue(r.value)}).catch(e=>{throw r(e),e})},cancel(){t()}})}}function t(e={}){const n=new Headers(e);return n.has("content-type")||n.set("content-type","text/html"),n}function r(e,r){const{done:s,stream:o}=n(e),a=t(r);return{done:s,response:new Response(o,{headers:a})}}let s=void 0;function o(){if(void 0===s)try{new ReadableStream({start(){}}),s=!0}catch(e){s=!1}return s}return e.concatenate=n,e.concatenateToResponse=r,e.isSupported=o,e.strategy=function(e,n){return async({event:s,url:a,params:c})=>{if(o()){const{done:t,response:o}=r(e.map(e=>e({event:s,url:a,params:c})),n);return s.waitUntil(t),o}const i=await Promise.all(e.map(e=>e({event:s,url:a,params:c})).map(async e=>{const n=await e;return n instanceof Response?n.blob():n})),u=t(n);return new Response(new Blob(i),{headers:u})}},e}({});
|
||||
//# sourceMappingURL=workbox-streams.prod.js.map
|
|
@ -0,0 +1,2 @@
|
|||
!function(){"use strict";try{self["workbox:sw:4.3.1"]&&_()}catch(t){}const t="https://storage.googleapis.com/workbox-cdn/releases/4.3.1",e={backgroundSync:"background-sync",broadcastUpdate:"broadcast-update",cacheableResponse:"cacheable-response",core:"core",expiration:"expiration",googleAnalytics:"offline-ga",navigationPreload:"navigation-preload",precaching:"precaching",rangeRequests:"range-requests",routing:"routing",strategies:"strategies",streams:"streams"};self.workbox=new class{constructor(){return this.v={},this.t={debug:"localhost"===self.location.hostname,modulePathPrefix:null,modulePathCb:null},this.s=this.t.debug?"dev":"prod",this.o=!1,new Proxy(this,{get(t,s){if(t[s])return t[s];const o=e[s];return o&&t.loadModule(`workbox-${o}`),t[s]}})}setConfig(t={}){if(this.o)throw new Error("Config must be set before accessing workbox.* modules");Object.assign(this.t,t),this.s=this.t.debug?"dev":"prod"}loadModule(t){const e=this.i(t);try{importScripts(e),this.o=!0}catch(s){throw console.error(`Unable to import module '${t}' from '${e}'.`),s}}i(e){if(this.t.modulePathCb)return this.t.modulePathCb(e,this.t.debug);let s=[t];const o=`${e}.${this.s}.js`,r=this.t.modulePathPrefix;return r&&""===(s=r.split("/"))[s.length-1]&&s.splice(s.length-1,1),s.push(o),s.join("/")}}}();
|
||||
//# sourceMappingURL=workbox-sw.js.map
|
|
@ -0,0 +1,885 @@
|
|||
try {
|
||||
self['workbox:window:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Sends a data object to a service worker via `postMessage` and resolves with
|
||||
* a response (if any).
|
||||
*
|
||||
* A response can be set in a message handler in the service worker by
|
||||
* calling `event.ports[0].postMessage(...)`, which will resolve the promise
|
||||
* returned by `messageSW()`. If no response is set, the promise will not
|
||||
* resolve.
|
||||
*
|
||||
* @param {ServiceWorker} sw The service worker to send the message to.
|
||||
* @param {Object} data An object to send to the service worker.
|
||||
* @return {Promise<Object|undefined>}
|
||||
*
|
||||
* @memberof module:workbox-window
|
||||
*/
|
||||
|
||||
var messageSW = function messageSW(sw, data) {
|
||||
return new Promise(function (resolve) {
|
||||
var messageChannel = new MessageChannel();
|
||||
|
||||
messageChannel.port1.onmessage = function (evt) {
|
||||
return resolve(evt.data);
|
||||
};
|
||||
|
||||
sw.postMessage(data, [messageChannel.port2]);
|
||||
});
|
||||
};
|
||||
|
||||
function _defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
function _createClass(Constructor, protoProps, staticProps) {
|
||||
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) _defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
}
|
||||
|
||||
function _inheritsLoose(subClass, superClass) {
|
||||
subClass.prototype = Object.create(superClass.prototype);
|
||||
subClass.prototype.constructor = subClass;
|
||||
subClass.__proto__ = superClass;
|
||||
}
|
||||
|
||||
function _assertThisInitialized(self) {
|
||||
if (self === void 0) {
|
||||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
try {
|
||||
self['workbox:core:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* The Deferred class composes Promises in a way that allows for them to be
|
||||
* resolved or rejected from outside the constructor. In most cases promises
|
||||
* should be used directly, but Deferreds can be necessary when the logic to
|
||||
* resolve a promise must be separate.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
var Deferred =
|
||||
/**
|
||||
* Creates a promise and exposes its resolve and reject functions as methods.
|
||||
*/
|
||||
function Deferred() {
|
||||
var _this = this;
|
||||
|
||||
this.promise = new Promise(function (resolve, reject) {
|
||||
_this.resolve = resolve;
|
||||
_this.reject = reject;
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
var logger = function () {
|
||||
var inGroup = false;
|
||||
var methodToColorMap = {
|
||||
debug: "#7f8c8d",
|
||||
// Gray
|
||||
log: "#2ecc71",
|
||||
// Green
|
||||
warn: "#f39c12",
|
||||
// Yellow
|
||||
error: "#c0392b",
|
||||
// Red
|
||||
groupCollapsed: "#3498db",
|
||||
// Blue
|
||||
groupEnd: null // No colored prefix on groupEnd
|
||||
|
||||
};
|
||||
|
||||
var print = function print(method, args) {
|
||||
var _console2;
|
||||
|
||||
if (method === 'groupCollapsed') {
|
||||
// Safari doesn't print all console.groupCollapsed() arguments:
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=182754
|
||||
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
|
||||
var _console;
|
||||
|
||||
(_console = console)[method].apply(_console, args);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var styles = ["background: " + methodToColorMap[method], "border-radius: 0.5em", "color: white", "font-weight: bold", "padding: 2px 0.5em"]; // When in a group, the workbox prefix is not displayed.
|
||||
|
||||
var logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')];
|
||||
|
||||
(_console2 = console)[method].apply(_console2, logPrefix.concat(args));
|
||||
|
||||
if (method === 'groupCollapsed') {
|
||||
inGroup = true;
|
||||
}
|
||||
|
||||
if (method === 'groupEnd') {
|
||||
inGroup = false;
|
||||
}
|
||||
};
|
||||
|
||||
var api = {};
|
||||
|
||||
var _arr = Object.keys(methodToColorMap);
|
||||
|
||||
var _loop = function _loop() {
|
||||
var method = _arr[_i];
|
||||
|
||||
api[method] = function () {
|
||||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||
args[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
print(method, args);
|
||||
};
|
||||
};
|
||||
|
||||
for (var _i = 0; _i < _arr.length; _i++) {
|
||||
_loop();
|
||||
}
|
||||
|
||||
return api;
|
||||
}();
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* A minimal `EventTarget` shim.
|
||||
* This is necessary because not all browsers support constructable
|
||||
* `EventTarget`, so using a real `EventTarget` will error.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var EventTargetShim =
|
||||
/*#__PURE__*/
|
||||
function () {
|
||||
/**
|
||||
* Creates an event listener registry
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function EventTargetShim() {
|
||||
// A registry of event types to listeners.
|
||||
this._eventListenerRegistry = {};
|
||||
}
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {Function} listener
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
var _proto = EventTargetShim.prototype;
|
||||
|
||||
_proto.addEventListener = function addEventListener(type, listener) {
|
||||
this._getEventListenersByType(type).add(listener);
|
||||
};
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {Function} listener
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_proto.removeEventListener = function removeEventListener(type, listener) {
|
||||
this._getEventListenersByType(type).delete(listener);
|
||||
};
|
||||
/**
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_proto.dispatchEvent = function dispatchEvent(event) {
|
||||
event.target = this;
|
||||
|
||||
this._getEventListenersByType(event.type).forEach(function (listener) {
|
||||
return listener(event);
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Returns a Set of listeners associated with the passed event type.
|
||||
* If no handlers have been registered, an empty Set is returned.
|
||||
*
|
||||
* @param {string} type The event type.
|
||||
* @return {Set} An array of handler functions.
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_proto._getEventListenersByType = function _getEventListenersByType(type) {
|
||||
return this._eventListenerRegistry[type] = this._eventListenerRegistry[type] || new Set();
|
||||
};
|
||||
|
||||
return EventTargetShim;
|
||||
}();
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Returns true if two URLs have the same `.href` property. The URLS can be
|
||||
* relative, and if they are the current location href is used to resolve URLs.
|
||||
*
|
||||
* @private
|
||||
* @param {string} url1
|
||||
* @param {string} url2
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
var urlsMatch = function urlsMatch(url1, url2) {
|
||||
return new URL(url1, location).href === new URL(url2, location).href;
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* A minimal `Event` subclass shim.
|
||||
* This doesn't *actually* subclass `Event` because not all browsers support
|
||||
* constructable `EventTarget`, and using a real `Event` will error.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var WorkboxEvent =
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {Object} props
|
||||
*/
|
||||
function WorkboxEvent(type, props) {
|
||||
Object.assign(this, props, {
|
||||
type: type
|
||||
});
|
||||
};
|
||||
|
||||
function _catch(body, recover) {
|
||||
try {
|
||||
var result = body();
|
||||
} catch (e) {
|
||||
return recover(e);
|
||||
}
|
||||
|
||||
if (result && result.then) {
|
||||
return result.then(void 0, recover);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function _async(f) {
|
||||
return function () {
|
||||
for (var args = [], i = 0; i < arguments.length; i++) {
|
||||
args[i] = arguments[i];
|
||||
}
|
||||
|
||||
try {
|
||||
return Promise.resolve(f.apply(this, args));
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function _invoke(body, then) {
|
||||
var result = body();
|
||||
|
||||
if (result && result.then) {
|
||||
return result.then(then);
|
||||
}
|
||||
|
||||
return then(result);
|
||||
}
|
||||
|
||||
function _await(value, then, direct) {
|
||||
if (direct) {
|
||||
return then ? then(value) : value;
|
||||
}
|
||||
|
||||
if (!value || !value.then) {
|
||||
value = Promise.resolve(value);
|
||||
}
|
||||
|
||||
return then ? value.then(then) : value;
|
||||
}
|
||||
|
||||
function _awaitIgnored(value, direct) {
|
||||
if (!direct) {
|
||||
return value && value.then ? value.then(_empty) : Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
function _empty() {}
|
||||
// `skipWaiting()` wasn't called. This 200 amount wasn't scientifically
|
||||
// chosen, but it seems to avoid false positives in my testing.
|
||||
|
||||
var WAITING_TIMEOUT_DURATION = 200; // The amount of time after a registration that we can reasonably conclude
|
||||
// that the registration didn't trigger an update.
|
||||
|
||||
var REGISTRATION_TIMEOUT_DURATION = 60000;
|
||||
/**
|
||||
* A class to aid in handling service worker registration, updates, and
|
||||
* reacting to service worker lifecycle events.
|
||||
*
|
||||
* @fires [message]{@link module:workbox-window.Workbox#message}
|
||||
* @fires [installed]{@link module:workbox-window.Workbox#installed}
|
||||
* @fires [waiting]{@link module:workbox-window.Workbox#waiting}
|
||||
* @fires [controlling]{@link module:workbox-window.Workbox#controlling}
|
||||
* @fires [activated]{@link module:workbox-window.Workbox#activated}
|
||||
* @fires [redundant]{@link module:workbox-window.Workbox#redundant}
|
||||
* @fires [externalinstalled]{@link module:workbox-window.Workbox#externalinstalled}
|
||||
* @fires [externalwaiting]{@link module:workbox-window.Workbox#externalwaiting}
|
||||
* @fires [externalactivated]{@link module:workbox-window.Workbox#externalactivated}
|
||||
*
|
||||
* @memberof module:workbox-window
|
||||
*/
|
||||
|
||||
var Workbox =
|
||||
/*#__PURE__*/
|
||||
function (_EventTargetShim) {
|
||||
_inheritsLoose(Workbox, _EventTargetShim);
|
||||
|
||||
/**
|
||||
* Creates a new Workbox instance with a script URL and service worker
|
||||
* options. The script URL and options are the same as those used when
|
||||
* calling `navigator.serviceWorker.register(scriptURL, options)`. See:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register
|
||||
*
|
||||
* @param {string} scriptURL The service worker script associated with this
|
||||
* instance.
|
||||
* @param {Object} [registerOptions] The service worker options associated
|
||||
* with this instance.
|
||||
*/
|
||||
function Workbox(scriptURL, registerOptions) {
|
||||
var _this;
|
||||
|
||||
if (registerOptions === void 0) {
|
||||
registerOptions = {};
|
||||
}
|
||||
|
||||
_this = _EventTargetShim.call(this) || this;
|
||||
_this._scriptURL = scriptURL;
|
||||
_this._registerOptions = registerOptions;
|
||||
_this._updateFoundCount = 0; // Deferreds we can resolve later.
|
||||
|
||||
_this._swDeferred = new Deferred();
|
||||
_this._activeDeferred = new Deferred();
|
||||
_this._controllingDeferred = new Deferred(); // Bind event handler callbacks.
|
||||
|
||||
_this._onMessage = _this._onMessage.bind(_assertThisInitialized(_assertThisInitialized(_this)));
|
||||
_this._onStateChange = _this._onStateChange.bind(_assertThisInitialized(_assertThisInitialized(_this)));
|
||||
_this._onUpdateFound = _this._onUpdateFound.bind(_assertThisInitialized(_assertThisInitialized(_this)));
|
||||
_this._onControllerChange = _this._onControllerChange.bind(_assertThisInitialized(_assertThisInitialized(_this)));
|
||||
return _this;
|
||||
}
|
||||
/**
|
||||
* Registers a service worker for this instances script URL and service
|
||||
* worker options. By default this method delays registration until after
|
||||
* the window has loaded.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {Function} [options.immediate=false] Setting this to true will
|
||||
* register the service worker immediately, even if the window has
|
||||
* not loaded (not recommended).
|
||||
*/
|
||||
|
||||
|
||||
var _proto = Workbox.prototype;
|
||||
_proto.register = _async(function (_temp) {
|
||||
var _this2 = this;
|
||||
|
||||
var _ref = _temp === void 0 ? {} : _temp,
|
||||
_ref$immediate = _ref.immediate,
|
||||
immediate = _ref$immediate === void 0 ? false : _ref$immediate;
|
||||
|
||||
{
|
||||
if (_this2._registrationTime) {
|
||||
logger.error('Cannot re-register a Workbox instance after it has ' + 'been registered. Create a new instance instead.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return _invoke(function () {
|
||||
if (!immediate && document.readyState !== 'complete') {
|
||||
return _awaitIgnored(new Promise(function (res) {
|
||||
return addEventListener('load', res);
|
||||
}));
|
||||
}
|
||||
}, function () {
|
||||
// Set this flag to true if any service worker was controlling the page
|
||||
// at registration time.
|
||||
_this2._isUpdate = Boolean(navigator.serviceWorker.controller); // Before registering, attempt to determine if a SW is already controlling
|
||||
// the page, and if that SW script (and version, if specified) matches this
|
||||
// instance's script.
|
||||
|
||||
_this2._compatibleControllingSW = _this2._getControllingSWIfCompatible();
|
||||
return _await(_this2._registerScript(), function (_this2$_registerScrip) {
|
||||
_this2._registration = _this2$_registerScrip;
|
||||
|
||||
// If we have a compatible controller, store the controller as the "own"
|
||||
// SW, resolve active/controlling deferreds and add necessary listeners.
|
||||
if (_this2._compatibleControllingSW) {
|
||||
_this2._sw = _this2._compatibleControllingSW;
|
||||
|
||||
_this2._activeDeferred.resolve(_this2._compatibleControllingSW);
|
||||
|
||||
_this2._controllingDeferred.resolve(_this2._compatibleControllingSW);
|
||||
|
||||
_this2._reportWindowReady(_this2._compatibleControllingSW);
|
||||
|
||||
_this2._compatibleControllingSW.addEventListener('statechange', _this2._onStateChange, {
|
||||
once: true
|
||||
});
|
||||
} // If there's a waiting service worker with a matching URL before the
|
||||
// `updatefound` event fires, it likely means that this site is open
|
||||
// in another tab, or the user refreshed the page (and thus the prevoius
|
||||
// page wasn't fully unloaded before this page started loading).
|
||||
// https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#waiting
|
||||
|
||||
|
||||
var waitingSW = _this2._registration.waiting;
|
||||
|
||||
if (waitingSW && urlsMatch(waitingSW.scriptURL, _this2._scriptURL)) {
|
||||
// Store the waiting SW as the "own" Sw, even if it means overwriting
|
||||
// a compatible controller.
|
||||
_this2._sw = waitingSW; // Run this in the next microtask, so any code that adds an event
|
||||
// listener after awaiting `register()` will get this event.
|
||||
|
||||
Promise.resolve().then(function () {
|
||||
_this2.dispatchEvent(new WorkboxEvent('waiting', {
|
||||
sw: waitingSW,
|
||||
wasWaitingBeforeRegister: true
|
||||
}));
|
||||
|
||||
{
|
||||
logger.warn('A service worker was already waiting to activate ' + 'before this script was registered...');
|
||||
}
|
||||
});
|
||||
} // If an "own" SW is already set, resolve the deferred.
|
||||
|
||||
|
||||
if (_this2._sw) {
|
||||
_this2._swDeferred.resolve(_this2._sw);
|
||||
}
|
||||
|
||||
{
|
||||
logger.log('Successfully registered service worker.', _this2._scriptURL);
|
||||
|
||||
if (navigator.serviceWorker.controller) {
|
||||
if (_this2._compatibleControllingSW) {
|
||||
logger.debug('A service worker with the same script URL ' + 'is already controlling this page.');
|
||||
} else {
|
||||
logger.debug('A service worker with a different script URL is ' + 'currently controlling the page. The browser is now fetching ' + 'the new script now...');
|
||||
}
|
||||
}
|
||||
|
||||
var currentPageIsOutOfScope = function currentPageIsOutOfScope() {
|
||||
var scopeURL = new URL(_this2._registerOptions.scope || _this2._scriptURL, document.baseURI);
|
||||
var scopeURLBasePath = new URL('./', scopeURL.href).pathname;
|
||||
return !location.pathname.startsWith(scopeURLBasePath);
|
||||
};
|
||||
|
||||
if (currentPageIsOutOfScope()) {
|
||||
logger.warn('The current page is not in scope for the registered ' + 'service worker. Was this a mistake?');
|
||||
}
|
||||
}
|
||||
|
||||
_this2._registration.addEventListener('updatefound', _this2._onUpdateFound);
|
||||
|
||||
navigator.serviceWorker.addEventListener('controllerchange', _this2._onControllerChange, {
|
||||
once: true
|
||||
}); // Add message listeners.
|
||||
|
||||
if ('BroadcastChannel' in self) {
|
||||
_this2._broadcastChannel = new BroadcastChannel('workbox');
|
||||
|
||||
_this2._broadcastChannel.addEventListener('message', _this2._onMessage);
|
||||
}
|
||||
|
||||
navigator.serviceWorker.addEventListener('message', _this2._onMessage);
|
||||
return _this2._registration;
|
||||
});
|
||||
});
|
||||
});
|
||||
/**
|
||||
* Resolves to the service worker registered by this instance as soon as it
|
||||
* is active. If a service worker was already controlling at registration
|
||||
* time then it will resolve to that if the script URLs (and optionally
|
||||
* script versions) match, otherwise it will wait until an update is found
|
||||
* and activates.
|
||||
*
|
||||
* @return {Promise<ServiceWorker>}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves with a reference to a service worker that matches the script URL
|
||||
* of this instance, as soon as it's available.
|
||||
*
|
||||
* If, at registration time, there's already an active or waiting service
|
||||
* worker with a matching script URL, it will be used (with the waiting
|
||||
* service worker taking precedence over the active service worker if both
|
||||
* match, since the waiting service worker would have been registered more
|
||||
* recently).
|
||||
* If there's no matching active or waiting service worker at registration
|
||||
* time then the promise will not resolve until an update is found and starts
|
||||
* installing, at which point the installing service worker is used.
|
||||
*
|
||||
* @return {Promise<ServiceWorker>}
|
||||
*/
|
||||
_proto.getSW = _async(function () {
|
||||
var _this3 = this;
|
||||
|
||||
// If `this._sw` is set, resolve with that as we want `getSW()` to
|
||||
// return the correct (new) service worker if an update is found.
|
||||
return _this3._sw || _this3._swDeferred.promise;
|
||||
});
|
||||
/**
|
||||
* Sends the passed data object to the service worker registered by this
|
||||
* instance (via [`getSW()`]{@link module:workbox-window.Workbox#getSW}) and resolves
|
||||
* with a response (if any).
|
||||
*
|
||||
* A response can be set in a message handler in the service worker by
|
||||
* calling `event.ports[0].postMessage(...)`, which will resolve the promise
|
||||
* returned by `messageSW()`. If no response is set, the promise will never
|
||||
* resolve.
|
||||
*
|
||||
* @param {Object} data An object to send to the service worker
|
||||
* @return {Promise<Object>}
|
||||
*/
|
||||
|
||||
_proto.messageSW = _async(function (data) {
|
||||
var _this4 = this;
|
||||
|
||||
return _await(_this4.getSW(), function (sw) {
|
||||
return messageSW(sw, data);
|
||||
});
|
||||
});
|
||||
/**
|
||||
* Checks for a service worker already controlling the page and returns
|
||||
* it if its script URL matchs.
|
||||
*
|
||||
* @private
|
||||
* @return {ServiceWorker|undefined}
|
||||
*/
|
||||
|
||||
_proto._getControllingSWIfCompatible = function _getControllingSWIfCompatible() {
|
||||
var controller = navigator.serviceWorker.controller;
|
||||
|
||||
if (controller && urlsMatch(controller.scriptURL, this._scriptURL)) {
|
||||
return controller;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Registers a service worker for this instances script URL and register
|
||||
* options and tracks the time registration was complete.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_proto._registerScript = _async(function () {
|
||||
var _this5 = this;
|
||||
|
||||
return _catch(function () {
|
||||
return _await(navigator.serviceWorker.register(_this5._scriptURL, _this5._registerOptions), function (reg) {
|
||||
// Keep track of when registration happened, so it can be used in the
|
||||
// `this._onUpdateFound` heuristic. Also use the presence of this
|
||||
// property as a way to see if `.register()` has been called.
|
||||
_this5._registrationTime = performance.now();
|
||||
return reg;
|
||||
});
|
||||
}, function (error) {
|
||||
{
|
||||
logger.error(error);
|
||||
} // Re-throw the error.
|
||||
|
||||
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
/**
|
||||
* Sends a message to the passed service worker that the window is ready.
|
||||
*
|
||||
* @param {ServiceWorker} sw
|
||||
* @private
|
||||
*/
|
||||
|
||||
_proto._reportWindowReady = function _reportWindowReady(sw) {
|
||||
messageSW(sw, {
|
||||
type: 'WINDOW_READY',
|
||||
meta: 'workbox-window'
|
||||
});
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_proto._onUpdateFound = function _onUpdateFound() {
|
||||
var installingSW = this._registration.installing; // If the script URL passed to `navigator.serviceWorker.register()` is
|
||||
// different from the current controlling SW's script URL, we know any
|
||||
// successful registration calls will trigger an `updatefound` event.
|
||||
// But if the registered script URL is the same as the current controlling
|
||||
// SW's script URL, we'll only get an `updatefound` event if the file
|
||||
// changed since it was last registered. This can be a problem if the user
|
||||
// opens up the same page in a different tab, and that page registers
|
||||
// a SW that triggers an update. It's a problem because this page has no
|
||||
// good way of knowing whether the `updatefound` event came from the SW
|
||||
// script it registered or from a registration attempt made by a newer
|
||||
// version of the page running in another tab.
|
||||
// To minimize the possibility of a false positive, we use the logic here:
|
||||
|
||||
var updateLikelyTriggeredExternally = // Since we enforce only calling `register()` once, and since we don't
|
||||
// add the `updatefound` event listener until the `register()` call, if
|
||||
// `_updateFoundCount` is > 0 then it means this method has already
|
||||
// been called, thus this SW must be external
|
||||
this._updateFoundCount > 0 || // If the script URL of the installing SW is different from this
|
||||
// instance's script URL, we know it's definitely not from our
|
||||
// registration.
|
||||
!urlsMatch(installingSW.scriptURL, this._scriptURL) || // If all of the above are false, then we use a time-based heuristic:
|
||||
// Any `updatefound` event that occurs long after our registration is
|
||||
// assumed to be external.
|
||||
performance.now() > this._registrationTime + REGISTRATION_TIMEOUT_DURATION ? // If any of the above are not true, we assume the update was
|
||||
// triggered by this instance.
|
||||
true : false;
|
||||
|
||||
if (updateLikelyTriggeredExternally) {
|
||||
this._externalSW = installingSW;
|
||||
|
||||
this._registration.removeEventListener('updatefound', this._onUpdateFound);
|
||||
} else {
|
||||
// If the update was not triggered externally we know the installing
|
||||
// SW is the one we registered, so we set it.
|
||||
this._sw = installingSW;
|
||||
|
||||
this._swDeferred.resolve(installingSW); // The `installing` state isn't something we have a dedicated
|
||||
// callback for, but we do log messages for it in development.
|
||||
|
||||
|
||||
{
|
||||
if (navigator.serviceWorker.controller) {
|
||||
logger.log('Updated service worker found. Installing now...');
|
||||
} else {
|
||||
logger.log('Service worker is installing...');
|
||||
}
|
||||
}
|
||||
} // Increment the `updatefound` count, so future invocations of this
|
||||
// method can be sure they were triggered externally.
|
||||
|
||||
|
||||
++this._updateFoundCount; // Add a `statechange` listener regardless of whether this update was
|
||||
// triggered externally, since we have callbacks for both.
|
||||
|
||||
installingSW.addEventListener('statechange', this._onStateChange);
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} originalEvent
|
||||
*/
|
||||
|
||||
|
||||
_proto._onStateChange = function _onStateChange(originalEvent) {
|
||||
var _this6 = this;
|
||||
|
||||
var sw = originalEvent.target;
|
||||
var state = sw.state;
|
||||
var isExternal = sw === this._externalSW;
|
||||
var eventPrefix = isExternal ? 'external' : '';
|
||||
var eventProps = {
|
||||
sw: sw,
|
||||
originalEvent: originalEvent
|
||||
};
|
||||
|
||||
if (!isExternal && this._isUpdate) {
|
||||
eventProps.isUpdate = true;
|
||||
}
|
||||
|
||||
this.dispatchEvent(new WorkboxEvent(eventPrefix + state, eventProps));
|
||||
|
||||
if (state === 'installed') {
|
||||
// This timeout is used to ignore cases where the service worker calls
|
||||
// `skipWaiting()` in the install event, thus moving it directly in the
|
||||
// activating state. (Since all service workers *must* go through the
|
||||
// waiting phase, the only way to detect `skipWaiting()` called in the
|
||||
// install event is to observe that the time spent in the waiting phase
|
||||
// is very short.)
|
||||
// NOTE: we don't need separate timeouts for the own and external SWs
|
||||
// since they can't go through these phases at the same time.
|
||||
this._waitingTimeout = setTimeout(function () {
|
||||
// Ensure the SW is still waiting (it may now be redundant).
|
||||
if (state === 'installed' && _this6._registration.waiting === sw) {
|
||||
_this6.dispatchEvent(new WorkboxEvent(eventPrefix + 'waiting', eventProps));
|
||||
|
||||
{
|
||||
if (isExternal) {
|
||||
logger.warn('An external service worker has installed but is ' + 'waiting for this client to close before activating...');
|
||||
} else {
|
||||
logger.warn('The service worker has installed but is waiting ' + 'for existing clients to close before activating...');
|
||||
}
|
||||
}
|
||||
}
|
||||
}, WAITING_TIMEOUT_DURATION);
|
||||
} else if (state === 'activating') {
|
||||
clearTimeout(this._waitingTimeout);
|
||||
|
||||
if (!isExternal) {
|
||||
this._activeDeferred.resolve(sw);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
switch (state) {
|
||||
case 'installed':
|
||||
if (isExternal) {
|
||||
logger.warn('An external service worker has installed. ' + 'You may want to suggest users reload this page.');
|
||||
} else {
|
||||
logger.log('Registered service worker installed.');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'activated':
|
||||
if (isExternal) {
|
||||
logger.warn('An external service worker has activated.');
|
||||
} else {
|
||||
logger.log('Registered service worker activated.');
|
||||
|
||||
if (sw !== navigator.serviceWorker.controller) {
|
||||
logger.warn('The registered service worker is active but ' + 'not yet controlling the page. Reload or run ' + '`clients.claim()` in the service worker.');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'redundant':
|
||||
if (sw === this._compatibleControllingSW) {
|
||||
logger.log('Previously controlling service worker now redundant!');
|
||||
} else if (!isExternal) {
|
||||
logger.log('Registered service worker now redundant!');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} originalEvent
|
||||
*/
|
||||
|
||||
|
||||
_proto._onControllerChange = function _onControllerChange(originalEvent) {
|
||||
var sw = this._sw;
|
||||
|
||||
if (sw === navigator.serviceWorker.controller) {
|
||||
this.dispatchEvent(new WorkboxEvent('controlling', {
|
||||
sw: sw,
|
||||
originalEvent: originalEvent
|
||||
}));
|
||||
|
||||
{
|
||||
logger.log('Registered service worker now controlling this page.');
|
||||
}
|
||||
|
||||
this._controllingDeferred.resolve(sw);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} originalEvent
|
||||
*/
|
||||
|
||||
|
||||
_proto._onMessage = function _onMessage(originalEvent) {
|
||||
var data = originalEvent.data;
|
||||
this.dispatchEvent(new WorkboxEvent('message', {
|
||||
data: data,
|
||||
originalEvent: originalEvent
|
||||
}));
|
||||
};
|
||||
|
||||
_createClass(Workbox, [{
|
||||
key: "active",
|
||||
get: function get() {
|
||||
return this._activeDeferred.promise;
|
||||
}
|
||||
/**
|
||||
* Resolves to the service worker registered by this instance as soon as it
|
||||
* is controlling the page. If a service worker was already controlling at
|
||||
* registration time then it will resolve to that if the script URLs (and
|
||||
* optionally script versions) match, otherwise it will wait until an update
|
||||
* is found and starts controlling the page.
|
||||
* Note: the first time a service worker is installed it will active but
|
||||
* not start controlling the page unless `clients.claim()` is called in the
|
||||
* service worker.
|
||||
*
|
||||
* @return {Promise<ServiceWorker>}
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: "controlling",
|
||||
get: function get() {
|
||||
return this._controllingDeferred.promise;
|
||||
}
|
||||
}]);
|
||||
|
||||
return Workbox;
|
||||
}(EventTargetShim); // The jsdoc comments below outline the events this instance may dispatch:
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
export { Workbox, messageSW };
|
||||
//# sourceMappingURL=workbox-window.dev.es5.mjs.map
|
|
@ -0,0 +1,751 @@
|
|||
try {
|
||||
self['workbox:window:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Sends a data object to a service worker via `postMessage` and resolves with
|
||||
* a response (if any).
|
||||
*
|
||||
* A response can be set in a message handler in the service worker by
|
||||
* calling `event.ports[0].postMessage(...)`, which will resolve the promise
|
||||
* returned by `messageSW()`. If no response is set, the promise will not
|
||||
* resolve.
|
||||
*
|
||||
* @param {ServiceWorker} sw The service worker to send the message to.
|
||||
* @param {Object} data An object to send to the service worker.
|
||||
* @return {Promise<Object|undefined>}
|
||||
*
|
||||
* @memberof module:workbox-window
|
||||
*/
|
||||
|
||||
const messageSW = (sw, data) => {
|
||||
return new Promise(resolve => {
|
||||
let messageChannel = new MessageChannel();
|
||||
|
||||
messageChannel.port1.onmessage = evt => resolve(evt.data);
|
||||
|
||||
sw.postMessage(data, [messageChannel.port2]);
|
||||
});
|
||||
};
|
||||
|
||||
try {
|
||||
self['workbox:core:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* The Deferred class composes Promises in a way that allows for them to be
|
||||
* resolved or rejected from outside the constructor. In most cases promises
|
||||
* should be used directly, but Deferreds can be necessary when the logic to
|
||||
* resolve a promise must be separate.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
class Deferred {
|
||||
/**
|
||||
* Creates a promise and exposes its resolve and reject functions as methods.
|
||||
*/
|
||||
constructor() {
|
||||
this.promise = new Promise((resolve, reject) => {
|
||||
this.resolve = resolve;
|
||||
this.reject = reject;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
const logger = (() => {
|
||||
let inGroup = false;
|
||||
const methodToColorMap = {
|
||||
debug: `#7f8c8d`,
|
||||
// Gray
|
||||
log: `#2ecc71`,
|
||||
// Green
|
||||
warn: `#f39c12`,
|
||||
// Yellow
|
||||
error: `#c0392b`,
|
||||
// Red
|
||||
groupCollapsed: `#3498db`,
|
||||
// Blue
|
||||
groupEnd: null // No colored prefix on groupEnd
|
||||
|
||||
};
|
||||
|
||||
const print = function (method, args) {
|
||||
if (method === 'groupCollapsed') {
|
||||
// Safari doesn't print all console.groupCollapsed() arguments:
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=182754
|
||||
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
|
||||
console[method](...args);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const styles = [`background: ${methodToColorMap[method]}`, `border-radius: 0.5em`, `color: white`, `font-weight: bold`, `padding: 2px 0.5em`]; // When in a group, the workbox prefix is not displayed.
|
||||
|
||||
const logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')];
|
||||
console[method](...logPrefix, ...args);
|
||||
|
||||
if (method === 'groupCollapsed') {
|
||||
inGroup = true;
|
||||
}
|
||||
|
||||
if (method === 'groupEnd') {
|
||||
inGroup = false;
|
||||
}
|
||||
};
|
||||
|
||||
const api = {};
|
||||
|
||||
for (const method of Object.keys(methodToColorMap)) {
|
||||
api[method] = (...args) => {
|
||||
print(method, args);
|
||||
};
|
||||
}
|
||||
|
||||
return api;
|
||||
})();
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* A minimal `EventTarget` shim.
|
||||
* This is necessary because not all browsers support constructable
|
||||
* `EventTarget`, so using a real `EventTarget` will error.
|
||||
* @private
|
||||
*/
|
||||
|
||||
class EventTargetShim {
|
||||
/**
|
||||
* Creates an event listener registry
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
constructor() {
|
||||
// A registry of event types to listeners.
|
||||
this._eventListenerRegistry = {};
|
||||
}
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {Function} listener
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
addEventListener(type, listener) {
|
||||
this._getEventListenersByType(type).add(listener);
|
||||
}
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {Function} listener
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
removeEventListener(type, listener) {
|
||||
this._getEventListenersByType(type).delete(listener);
|
||||
}
|
||||
/**
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
dispatchEvent(event) {
|
||||
event.target = this;
|
||||
|
||||
this._getEventListenersByType(event.type).forEach(listener => listener(event));
|
||||
}
|
||||
/**
|
||||
* Returns a Set of listeners associated with the passed event type.
|
||||
* If no handlers have been registered, an empty Set is returned.
|
||||
*
|
||||
* @param {string} type The event type.
|
||||
* @return {Set} An array of handler functions.
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_getEventListenersByType(type) {
|
||||
return this._eventListenerRegistry[type] = this._eventListenerRegistry[type] || new Set();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Returns true if two URLs have the same `.href` property. The URLS can be
|
||||
* relative, and if they are the current location href is used to resolve URLs.
|
||||
*
|
||||
* @private
|
||||
* @param {string} url1
|
||||
* @param {string} url2
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
const urlsMatch = (url1, url2) => {
|
||||
return new URL(url1, location).href === new URL(url2, location).href;
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* A minimal `Event` subclass shim.
|
||||
* This doesn't *actually* subclass `Event` because not all browsers support
|
||||
* constructable `EventTarget`, and using a real `Event` will error.
|
||||
* @private
|
||||
*/
|
||||
|
||||
class WorkboxEvent {
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {Object} props
|
||||
*/
|
||||
constructor(type, props) {
|
||||
Object.assign(this, props, {
|
||||
type
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
// `skipWaiting()` wasn't called. This 200 amount wasn't scientifically
|
||||
// chosen, but it seems to avoid false positives in my testing.
|
||||
|
||||
const WAITING_TIMEOUT_DURATION = 200; // The amount of time after a registration that we can reasonably conclude
|
||||
// that the registration didn't trigger an update.
|
||||
|
||||
const REGISTRATION_TIMEOUT_DURATION = 60000;
|
||||
/**
|
||||
* A class to aid in handling service worker registration, updates, and
|
||||
* reacting to service worker lifecycle events.
|
||||
*
|
||||
* @fires [message]{@link module:workbox-window.Workbox#message}
|
||||
* @fires [installed]{@link module:workbox-window.Workbox#installed}
|
||||
* @fires [waiting]{@link module:workbox-window.Workbox#waiting}
|
||||
* @fires [controlling]{@link module:workbox-window.Workbox#controlling}
|
||||
* @fires [activated]{@link module:workbox-window.Workbox#activated}
|
||||
* @fires [redundant]{@link module:workbox-window.Workbox#redundant}
|
||||
* @fires [externalinstalled]{@link module:workbox-window.Workbox#externalinstalled}
|
||||
* @fires [externalwaiting]{@link module:workbox-window.Workbox#externalwaiting}
|
||||
* @fires [externalactivated]{@link module:workbox-window.Workbox#externalactivated}
|
||||
*
|
||||
* @memberof module:workbox-window
|
||||
*/
|
||||
|
||||
class Workbox extends EventTargetShim {
|
||||
/**
|
||||
* Creates a new Workbox instance with a script URL and service worker
|
||||
* options. The script URL and options are the same as those used when
|
||||
* calling `navigator.serviceWorker.register(scriptURL, options)`. See:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register
|
||||
*
|
||||
* @param {string} scriptURL The service worker script associated with this
|
||||
* instance.
|
||||
* @param {Object} [registerOptions] The service worker options associated
|
||||
* with this instance.
|
||||
*/
|
||||
constructor(scriptURL, registerOptions = {}) {
|
||||
super();
|
||||
this._scriptURL = scriptURL;
|
||||
this._registerOptions = registerOptions;
|
||||
this._updateFoundCount = 0; // Deferreds we can resolve later.
|
||||
|
||||
this._swDeferred = new Deferred();
|
||||
this._activeDeferred = new Deferred();
|
||||
this._controllingDeferred = new Deferred(); // Bind event handler callbacks.
|
||||
|
||||
this._onMessage = this._onMessage.bind(this);
|
||||
this._onStateChange = this._onStateChange.bind(this);
|
||||
this._onUpdateFound = this._onUpdateFound.bind(this);
|
||||
this._onControllerChange = this._onControllerChange.bind(this);
|
||||
}
|
||||
/**
|
||||
* Registers a service worker for this instances script URL and service
|
||||
* worker options. By default this method delays registration until after
|
||||
* the window has loaded.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {Function} [options.immediate=false] Setting this to true will
|
||||
* register the service worker immediately, even if the window has
|
||||
* not loaded (not recommended).
|
||||
*/
|
||||
|
||||
|
||||
async register({
|
||||
immediate = false
|
||||
} = {}) {
|
||||
{
|
||||
if (this._registrationTime) {
|
||||
logger.error('Cannot re-register a Workbox instance after it has ' + 'been registered. Create a new instance instead.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!immediate && document.readyState !== 'complete') {
|
||||
await new Promise(res => addEventListener('load', res));
|
||||
} // Set this flag to true if any service worker was controlling the page
|
||||
// at registration time.
|
||||
|
||||
|
||||
this._isUpdate = Boolean(navigator.serviceWorker.controller); // Before registering, attempt to determine if a SW is already controlling
|
||||
// the page, and if that SW script (and version, if specified) matches this
|
||||
// instance's script.
|
||||
|
||||
this._compatibleControllingSW = this._getControllingSWIfCompatible();
|
||||
this._registration = await this._registerScript(); // If we have a compatible controller, store the controller as the "own"
|
||||
// SW, resolve active/controlling deferreds and add necessary listeners.
|
||||
|
||||
if (this._compatibleControllingSW) {
|
||||
this._sw = this._compatibleControllingSW;
|
||||
|
||||
this._activeDeferred.resolve(this._compatibleControllingSW);
|
||||
|
||||
this._controllingDeferred.resolve(this._compatibleControllingSW);
|
||||
|
||||
this._reportWindowReady(this._compatibleControllingSW);
|
||||
|
||||
this._compatibleControllingSW.addEventListener('statechange', this._onStateChange, {
|
||||
once: true
|
||||
});
|
||||
} // If there's a waiting service worker with a matching URL before the
|
||||
// `updatefound` event fires, it likely means that this site is open
|
||||
// in another tab, or the user refreshed the page (and thus the prevoius
|
||||
// page wasn't fully unloaded before this page started loading).
|
||||
// https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#waiting
|
||||
|
||||
|
||||
const waitingSW = this._registration.waiting;
|
||||
|
||||
if (waitingSW && urlsMatch(waitingSW.scriptURL, this._scriptURL)) {
|
||||
// Store the waiting SW as the "own" Sw, even if it means overwriting
|
||||
// a compatible controller.
|
||||
this._sw = waitingSW; // Run this in the next microtask, so any code that adds an event
|
||||
// listener after awaiting `register()` will get this event.
|
||||
|
||||
Promise.resolve().then(() => {
|
||||
this.dispatchEvent(new WorkboxEvent('waiting', {
|
||||
sw: waitingSW,
|
||||
wasWaitingBeforeRegister: true
|
||||
}));
|
||||
|
||||
{
|
||||
logger.warn('A service worker was already waiting to activate ' + 'before this script was registered...');
|
||||
}
|
||||
});
|
||||
} // If an "own" SW is already set, resolve the deferred.
|
||||
|
||||
|
||||
if (this._sw) {
|
||||
this._swDeferred.resolve(this._sw);
|
||||
}
|
||||
|
||||
{
|
||||
logger.log('Successfully registered service worker.', this._scriptURL);
|
||||
|
||||
if (navigator.serviceWorker.controller) {
|
||||
if (this._compatibleControllingSW) {
|
||||
logger.debug('A service worker with the same script URL ' + 'is already controlling this page.');
|
||||
} else {
|
||||
logger.debug('A service worker with a different script URL is ' + 'currently controlling the page. The browser is now fetching ' + 'the new script now...');
|
||||
}
|
||||
}
|
||||
|
||||
const currentPageIsOutOfScope = () => {
|
||||
const scopeURL = new URL(this._registerOptions.scope || this._scriptURL, document.baseURI);
|
||||
const scopeURLBasePath = new URL('./', scopeURL.href).pathname;
|
||||
return !location.pathname.startsWith(scopeURLBasePath);
|
||||
};
|
||||
|
||||
if (currentPageIsOutOfScope()) {
|
||||
logger.warn('The current page is not in scope for the registered ' + 'service worker. Was this a mistake?');
|
||||
}
|
||||
}
|
||||
|
||||
this._registration.addEventListener('updatefound', this._onUpdateFound);
|
||||
|
||||
navigator.serviceWorker.addEventListener('controllerchange', this._onControllerChange, {
|
||||
once: true
|
||||
}); // Add message listeners.
|
||||
|
||||
if ('BroadcastChannel' in self) {
|
||||
this._broadcastChannel = new BroadcastChannel('workbox');
|
||||
|
||||
this._broadcastChannel.addEventListener('message', this._onMessage);
|
||||
}
|
||||
|
||||
navigator.serviceWorker.addEventListener('message', this._onMessage);
|
||||
return this._registration;
|
||||
}
|
||||
/**
|
||||
* Resolves to the service worker registered by this instance as soon as it
|
||||
* is active. If a service worker was already controlling at registration
|
||||
* time then it will resolve to that if the script URLs (and optionally
|
||||
* script versions) match, otherwise it will wait until an update is found
|
||||
* and activates.
|
||||
*
|
||||
* @return {Promise<ServiceWorker>}
|
||||
*/
|
||||
|
||||
|
||||
get active() {
|
||||
return this._activeDeferred.promise;
|
||||
}
|
||||
/**
|
||||
* Resolves to the service worker registered by this instance as soon as it
|
||||
* is controlling the page. If a service worker was already controlling at
|
||||
* registration time then it will resolve to that if the script URLs (and
|
||||
* optionally script versions) match, otherwise it will wait until an update
|
||||
* is found and starts controlling the page.
|
||||
* Note: the first time a service worker is installed it will active but
|
||||
* not start controlling the page unless `clients.claim()` is called in the
|
||||
* service worker.
|
||||
*
|
||||
* @return {Promise<ServiceWorker>}
|
||||
*/
|
||||
|
||||
|
||||
get controlling() {
|
||||
return this._controllingDeferred.promise;
|
||||
}
|
||||
/**
|
||||
* Resolves with a reference to a service worker that matches the script URL
|
||||
* of this instance, as soon as it's available.
|
||||
*
|
||||
* If, at registration time, there's already an active or waiting service
|
||||
* worker with a matching script URL, it will be used (with the waiting
|
||||
* service worker taking precedence over the active service worker if both
|
||||
* match, since the waiting service worker would have been registered more
|
||||
* recently).
|
||||
* If there's no matching active or waiting service worker at registration
|
||||
* time then the promise will not resolve until an update is found and starts
|
||||
* installing, at which point the installing service worker is used.
|
||||
*
|
||||
* @return {Promise<ServiceWorker>}
|
||||
*/
|
||||
|
||||
|
||||
async getSW() {
|
||||
// If `this._sw` is set, resolve with that as we want `getSW()` to
|
||||
// return the correct (new) service worker if an update is found.
|
||||
return this._sw || this._swDeferred.promise;
|
||||
}
|
||||
/**
|
||||
* Sends the passed data object to the service worker registered by this
|
||||
* instance (via [`getSW()`]{@link module:workbox-window.Workbox#getSW}) and resolves
|
||||
* with a response (if any).
|
||||
*
|
||||
* A response can be set in a message handler in the service worker by
|
||||
* calling `event.ports[0].postMessage(...)`, which will resolve the promise
|
||||
* returned by `messageSW()`. If no response is set, the promise will never
|
||||
* resolve.
|
||||
*
|
||||
* @param {Object} data An object to send to the service worker
|
||||
* @return {Promise<Object>}
|
||||
*/
|
||||
|
||||
|
||||
async messageSW(data) {
|
||||
const sw = await this.getSW();
|
||||
return messageSW(sw, data);
|
||||
}
|
||||
/**
|
||||
* Checks for a service worker already controlling the page and returns
|
||||
* it if its script URL matchs.
|
||||
*
|
||||
* @private
|
||||
* @return {ServiceWorker|undefined}
|
||||
*/
|
||||
|
||||
|
||||
_getControllingSWIfCompatible() {
|
||||
const controller = navigator.serviceWorker.controller;
|
||||
|
||||
if (controller && urlsMatch(controller.scriptURL, this._scriptURL)) {
|
||||
return controller;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Registers a service worker for this instances script URL and register
|
||||
* options and tracks the time registration was complete.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
async _registerScript() {
|
||||
try {
|
||||
const reg = await navigator.serviceWorker.register(this._scriptURL, this._registerOptions); // Keep track of when registration happened, so it can be used in the
|
||||
// `this._onUpdateFound` heuristic. Also use the presence of this
|
||||
// property as a way to see if `.register()` has been called.
|
||||
|
||||
this._registrationTime = performance.now();
|
||||
return reg;
|
||||
} catch (error) {
|
||||
{
|
||||
logger.error(error);
|
||||
} // Re-throw the error.
|
||||
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Sends a message to the passed service worker that the window is ready.
|
||||
*
|
||||
* @param {ServiceWorker} sw
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_reportWindowReady(sw) {
|
||||
messageSW(sw, {
|
||||
type: 'WINDOW_READY',
|
||||
meta: 'workbox-window'
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_onUpdateFound() {
|
||||
const installingSW = this._registration.installing; // If the script URL passed to `navigator.serviceWorker.register()` is
|
||||
// different from the current controlling SW's script URL, we know any
|
||||
// successful registration calls will trigger an `updatefound` event.
|
||||
// But if the registered script URL is the same as the current controlling
|
||||
// SW's script URL, we'll only get an `updatefound` event if the file
|
||||
// changed since it was last registered. This can be a problem if the user
|
||||
// opens up the same page in a different tab, and that page registers
|
||||
// a SW that triggers an update. It's a problem because this page has no
|
||||
// good way of knowing whether the `updatefound` event came from the SW
|
||||
// script it registered or from a registration attempt made by a newer
|
||||
// version of the page running in another tab.
|
||||
// To minimize the possibility of a false positive, we use the logic here:
|
||||
|
||||
let updateLikelyTriggeredExternally = // Since we enforce only calling `register()` once, and since we don't
|
||||
// add the `updatefound` event listener until the `register()` call, if
|
||||
// `_updateFoundCount` is > 0 then it means this method has already
|
||||
// been called, thus this SW must be external
|
||||
this._updateFoundCount > 0 || // If the script URL of the installing SW is different from this
|
||||
// instance's script URL, we know it's definitely not from our
|
||||
// registration.
|
||||
!urlsMatch(installingSW.scriptURL, this._scriptURL) || // If all of the above are false, then we use a time-based heuristic:
|
||||
// Any `updatefound` event that occurs long after our registration is
|
||||
// assumed to be external.
|
||||
performance.now() > this._registrationTime + REGISTRATION_TIMEOUT_DURATION ? // If any of the above are not true, we assume the update was
|
||||
// triggered by this instance.
|
||||
true : false;
|
||||
|
||||
if (updateLikelyTriggeredExternally) {
|
||||
this._externalSW = installingSW;
|
||||
|
||||
this._registration.removeEventListener('updatefound', this._onUpdateFound);
|
||||
} else {
|
||||
// If the update was not triggered externally we know the installing
|
||||
// SW is the one we registered, so we set it.
|
||||
this._sw = installingSW;
|
||||
|
||||
this._swDeferred.resolve(installingSW); // The `installing` state isn't something we have a dedicated
|
||||
// callback for, but we do log messages for it in development.
|
||||
|
||||
|
||||
{
|
||||
if (navigator.serviceWorker.controller) {
|
||||
logger.log('Updated service worker found. Installing now...');
|
||||
} else {
|
||||
logger.log('Service worker is installing...');
|
||||
}
|
||||
}
|
||||
} // Increment the `updatefound` count, so future invocations of this
|
||||
// method can be sure they were triggered externally.
|
||||
|
||||
|
||||
++this._updateFoundCount; // Add a `statechange` listener regardless of whether this update was
|
||||
// triggered externally, since we have callbacks for both.
|
||||
|
||||
installingSW.addEventListener('statechange', this._onStateChange);
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} originalEvent
|
||||
*/
|
||||
|
||||
|
||||
_onStateChange(originalEvent) {
|
||||
const sw = originalEvent.target;
|
||||
const {
|
||||
state
|
||||
} = sw;
|
||||
const isExternal = sw === this._externalSW;
|
||||
const eventPrefix = isExternal ? 'external' : '';
|
||||
const eventProps = {
|
||||
sw,
|
||||
originalEvent
|
||||
};
|
||||
|
||||
if (!isExternal && this._isUpdate) {
|
||||
eventProps.isUpdate = true;
|
||||
}
|
||||
|
||||
this.dispatchEvent(new WorkboxEvent(eventPrefix + state, eventProps));
|
||||
|
||||
if (state === 'installed') {
|
||||
// This timeout is used to ignore cases where the service worker calls
|
||||
// `skipWaiting()` in the install event, thus moving it directly in the
|
||||
// activating state. (Since all service workers *must* go through the
|
||||
// waiting phase, the only way to detect `skipWaiting()` called in the
|
||||
// install event is to observe that the time spent in the waiting phase
|
||||
// is very short.)
|
||||
// NOTE: we don't need separate timeouts for the own and external SWs
|
||||
// since they can't go through these phases at the same time.
|
||||
this._waitingTimeout = setTimeout(() => {
|
||||
// Ensure the SW is still waiting (it may now be redundant).
|
||||
if (state === 'installed' && this._registration.waiting === sw) {
|
||||
this.dispatchEvent(new WorkboxEvent(eventPrefix + 'waiting', eventProps));
|
||||
|
||||
{
|
||||
if (isExternal) {
|
||||
logger.warn('An external service worker has installed but is ' + 'waiting for this client to close before activating...');
|
||||
} else {
|
||||
logger.warn('The service worker has installed but is waiting ' + 'for existing clients to close before activating...');
|
||||
}
|
||||
}
|
||||
}
|
||||
}, WAITING_TIMEOUT_DURATION);
|
||||
} else if (state === 'activating') {
|
||||
clearTimeout(this._waitingTimeout);
|
||||
|
||||
if (!isExternal) {
|
||||
this._activeDeferred.resolve(sw);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
switch (state) {
|
||||
case 'installed':
|
||||
if (isExternal) {
|
||||
logger.warn('An external service worker has installed. ' + 'You may want to suggest users reload this page.');
|
||||
} else {
|
||||
logger.log('Registered service worker installed.');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'activated':
|
||||
if (isExternal) {
|
||||
logger.warn('An external service worker has activated.');
|
||||
} else {
|
||||
logger.log('Registered service worker activated.');
|
||||
|
||||
if (sw !== navigator.serviceWorker.controller) {
|
||||
logger.warn('The registered service worker is active but ' + 'not yet controlling the page. Reload or run ' + '`clients.claim()` in the service worker.');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'redundant':
|
||||
if (sw === this._compatibleControllingSW) {
|
||||
logger.log('Previously controlling service worker now redundant!');
|
||||
} else if (!isExternal) {
|
||||
logger.log('Registered service worker now redundant!');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} originalEvent
|
||||
*/
|
||||
|
||||
|
||||
_onControllerChange(originalEvent) {
|
||||
const sw = this._sw;
|
||||
|
||||
if (sw === navigator.serviceWorker.controller) {
|
||||
this.dispatchEvent(new WorkboxEvent('controlling', {
|
||||
sw,
|
||||
originalEvent
|
||||
}));
|
||||
|
||||
{
|
||||
logger.log('Registered service worker now controlling this page.');
|
||||
}
|
||||
|
||||
this._controllingDeferred.resolve(sw);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} originalEvent
|
||||
*/
|
||||
|
||||
|
||||
_onMessage(originalEvent) {
|
||||
const {
|
||||
data
|
||||
} = originalEvent;
|
||||
this.dispatchEvent(new WorkboxEvent('message', {
|
||||
data,
|
||||
originalEvent
|
||||
}));
|
||||
}
|
||||
|
||||
} // The jsdoc comments below outline the events this instance may dispatch:
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
export { Workbox, messageSW };
|
||||
//# sourceMappingURL=workbox-window.dev.mjs.map
|
|
@ -0,0 +1,896 @@
|
|||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
|
||||
typeof define === 'function' && define.amd ? define(['exports'], factory) :
|
||||
(global = global || self, factory(global.workbox = {}));
|
||||
}(this, function (exports) { 'use strict';
|
||||
|
||||
try {
|
||||
self['workbox:window:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Sends a data object to a service worker via `postMessage` and resolves with
|
||||
* a response (if any).
|
||||
*
|
||||
* A response can be set in a message handler in the service worker by
|
||||
* calling `event.ports[0].postMessage(...)`, which will resolve the promise
|
||||
* returned by `messageSW()`. If no response is set, the promise will not
|
||||
* resolve.
|
||||
*
|
||||
* @param {ServiceWorker} sw The service worker to send the message to.
|
||||
* @param {Object} data An object to send to the service worker.
|
||||
* @return {Promise<Object|undefined>}
|
||||
*
|
||||
* @memberof module:workbox-window
|
||||
*/
|
||||
|
||||
var messageSW = function messageSW(sw, data) {
|
||||
return new Promise(function (resolve) {
|
||||
var messageChannel = new MessageChannel();
|
||||
|
||||
messageChannel.port1.onmessage = function (evt) {
|
||||
return resolve(evt.data);
|
||||
};
|
||||
|
||||
sw.postMessage(data, [messageChannel.port2]);
|
||||
});
|
||||
};
|
||||
|
||||
function _defineProperties(target, props) {
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var descriptor = props[i];
|
||||
descriptor.enumerable = descriptor.enumerable || false;
|
||||
descriptor.configurable = true;
|
||||
if ("value" in descriptor) descriptor.writable = true;
|
||||
Object.defineProperty(target, descriptor.key, descriptor);
|
||||
}
|
||||
}
|
||||
|
||||
function _createClass(Constructor, protoProps, staticProps) {
|
||||
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
|
||||
if (staticProps) _defineProperties(Constructor, staticProps);
|
||||
return Constructor;
|
||||
}
|
||||
|
||||
function _inheritsLoose(subClass, superClass) {
|
||||
subClass.prototype = Object.create(superClass.prototype);
|
||||
subClass.prototype.constructor = subClass;
|
||||
subClass.__proto__ = superClass;
|
||||
}
|
||||
|
||||
function _assertThisInitialized(self) {
|
||||
if (self === void 0) {
|
||||
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
try {
|
||||
self['workbox:core:4.3.1'] && _();
|
||||
} catch (e) {} // eslint-disable-line
|
||||
|
||||
/*
|
||||
Copyright 2018 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* The Deferred class composes Promises in a way that allows for them to be
|
||||
* resolved or rejected from outside the constructor. In most cases promises
|
||||
* should be used directly, but Deferreds can be necessary when the logic to
|
||||
* resolve a promise must be separate.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
var Deferred =
|
||||
/**
|
||||
* Creates a promise and exposes its resolve and reject functions as methods.
|
||||
*/
|
||||
function Deferred() {
|
||||
var _this = this;
|
||||
|
||||
this.promise = new Promise(function (resolve, reject) {
|
||||
_this.resolve = resolve;
|
||||
_this.reject = reject;
|
||||
});
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
var logger = function () {
|
||||
var inGroup = false;
|
||||
var methodToColorMap = {
|
||||
debug: "#7f8c8d",
|
||||
// Gray
|
||||
log: "#2ecc71",
|
||||
// Green
|
||||
warn: "#f39c12",
|
||||
// Yellow
|
||||
error: "#c0392b",
|
||||
// Red
|
||||
groupCollapsed: "#3498db",
|
||||
// Blue
|
||||
groupEnd: null // No colored prefix on groupEnd
|
||||
|
||||
};
|
||||
|
||||
var print = function print(method, args) {
|
||||
var _console2;
|
||||
|
||||
if (method === 'groupCollapsed') {
|
||||
// Safari doesn't print all console.groupCollapsed() arguments:
|
||||
// https://bugs.webkit.org/show_bug.cgi?id=182754
|
||||
if (/^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
|
||||
var _console;
|
||||
|
||||
(_console = console)[method].apply(_console, args);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
var styles = ["background: " + methodToColorMap[method], "border-radius: 0.5em", "color: white", "font-weight: bold", "padding: 2px 0.5em"]; // When in a group, the workbox prefix is not displayed.
|
||||
|
||||
var logPrefix = inGroup ? [] : ['%cworkbox', styles.join(';')];
|
||||
|
||||
(_console2 = console)[method].apply(_console2, logPrefix.concat(args));
|
||||
|
||||
if (method === 'groupCollapsed') {
|
||||
inGroup = true;
|
||||
}
|
||||
|
||||
if (method === 'groupEnd') {
|
||||
inGroup = false;
|
||||
}
|
||||
};
|
||||
|
||||
var api = {};
|
||||
|
||||
var _arr = Object.keys(methodToColorMap);
|
||||
|
||||
var _loop = function _loop() {
|
||||
var method = _arr[_i];
|
||||
|
||||
api[method] = function () {
|
||||
for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
|
||||
args[_key] = arguments[_key];
|
||||
}
|
||||
|
||||
print(method, args);
|
||||
};
|
||||
};
|
||||
|
||||
for (var _i = 0; _i < _arr.length; _i++) {
|
||||
_loop();
|
||||
}
|
||||
|
||||
return api;
|
||||
}();
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* A minimal `EventTarget` shim.
|
||||
* This is necessary because not all browsers support constructable
|
||||
* `EventTarget`, so using a real `EventTarget` will error.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var EventTargetShim =
|
||||
/*#__PURE__*/
|
||||
function () {
|
||||
/**
|
||||
* Creates an event listener registry
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
function EventTargetShim() {
|
||||
// A registry of event types to listeners.
|
||||
this._eventListenerRegistry = {};
|
||||
}
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {Function} listener
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
var _proto = EventTargetShim.prototype;
|
||||
|
||||
_proto.addEventListener = function addEventListener(type, listener) {
|
||||
this._getEventListenersByType(type).add(listener);
|
||||
};
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {Function} listener
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_proto.removeEventListener = function removeEventListener(type, listener) {
|
||||
this._getEventListenersByType(type).delete(listener);
|
||||
};
|
||||
/**
|
||||
* @param {Event} event
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_proto.dispatchEvent = function dispatchEvent(event) {
|
||||
event.target = this;
|
||||
|
||||
this._getEventListenersByType(event.type).forEach(function (listener) {
|
||||
return listener(event);
|
||||
});
|
||||
};
|
||||
/**
|
||||
* Returns a Set of listeners associated with the passed event type.
|
||||
* If no handlers have been registered, an empty Set is returned.
|
||||
*
|
||||
* @param {string} type The event type.
|
||||
* @return {Set} An array of handler functions.
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_proto._getEventListenersByType = function _getEventListenersByType(type) {
|
||||
return this._eventListenerRegistry[type] = this._eventListenerRegistry[type] || new Set();
|
||||
};
|
||||
|
||||
return EventTargetShim;
|
||||
}();
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* Returns true if two URLs have the same `.href` property. The URLS can be
|
||||
* relative, and if they are the current location href is used to resolve URLs.
|
||||
*
|
||||
* @private
|
||||
* @param {string} url1
|
||||
* @param {string} url2
|
||||
* @return {boolean}
|
||||
*/
|
||||
|
||||
var urlsMatch = function urlsMatch(url1, url2) {
|
||||
return new URL(url1, location).href === new URL(url2, location).href;
|
||||
};
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
/**
|
||||
* A minimal `Event` subclass shim.
|
||||
* This doesn't *actually* subclass `Event` because not all browsers support
|
||||
* constructable `EventTarget`, and using a real `Event` will error.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var WorkboxEvent =
|
||||
/**
|
||||
* @param {string} type
|
||||
* @param {Object} props
|
||||
*/
|
||||
function WorkboxEvent(type, props) {
|
||||
Object.assign(this, props, {
|
||||
type: type
|
||||
});
|
||||
};
|
||||
|
||||
function _catch(body, recover) {
|
||||
try {
|
||||
var result = body();
|
||||
} catch (e) {
|
||||
return recover(e);
|
||||
}
|
||||
|
||||
if (result && result.then) {
|
||||
return result.then(void 0, recover);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function _async(f) {
|
||||
return function () {
|
||||
for (var args = [], i = 0; i < arguments.length; i++) {
|
||||
args[i] = arguments[i];
|
||||
}
|
||||
|
||||
try {
|
||||
return Promise.resolve(f.apply(this, args));
|
||||
} catch (e) {
|
||||
return Promise.reject(e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function _invoke(body, then) {
|
||||
var result = body();
|
||||
|
||||
if (result && result.then) {
|
||||
return result.then(then);
|
||||
}
|
||||
|
||||
return then(result);
|
||||
}
|
||||
|
||||
function _await(value, then, direct) {
|
||||
if (direct) {
|
||||
return then ? then(value) : value;
|
||||
}
|
||||
|
||||
if (!value || !value.then) {
|
||||
value = Promise.resolve(value);
|
||||
}
|
||||
|
||||
return then ? value.then(then) : value;
|
||||
}
|
||||
|
||||
function _awaitIgnored(value, direct) {
|
||||
if (!direct) {
|
||||
return value && value.then ? value.then(_empty) : Promise.resolve();
|
||||
}
|
||||
}
|
||||
|
||||
function _empty() {}
|
||||
// `skipWaiting()` wasn't called. This 200 amount wasn't scientifically
|
||||
// chosen, but it seems to avoid false positives in my testing.
|
||||
|
||||
var WAITING_TIMEOUT_DURATION = 200; // The amount of time after a registration that we can reasonably conclude
|
||||
// that the registration didn't trigger an update.
|
||||
|
||||
var REGISTRATION_TIMEOUT_DURATION = 60000;
|
||||
/**
|
||||
* A class to aid in handling service worker registration, updates, and
|
||||
* reacting to service worker lifecycle events.
|
||||
*
|
||||
* @fires [message]{@link module:workbox-window.Workbox#message}
|
||||
* @fires [installed]{@link module:workbox-window.Workbox#installed}
|
||||
* @fires [waiting]{@link module:workbox-window.Workbox#waiting}
|
||||
* @fires [controlling]{@link module:workbox-window.Workbox#controlling}
|
||||
* @fires [activated]{@link module:workbox-window.Workbox#activated}
|
||||
* @fires [redundant]{@link module:workbox-window.Workbox#redundant}
|
||||
* @fires [externalinstalled]{@link module:workbox-window.Workbox#externalinstalled}
|
||||
* @fires [externalwaiting]{@link module:workbox-window.Workbox#externalwaiting}
|
||||
* @fires [externalactivated]{@link module:workbox-window.Workbox#externalactivated}
|
||||
*
|
||||
* @memberof module:workbox-window
|
||||
*/
|
||||
|
||||
var Workbox =
|
||||
/*#__PURE__*/
|
||||
function (_EventTargetShim) {
|
||||
_inheritsLoose(Workbox, _EventTargetShim);
|
||||
|
||||
/**
|
||||
* Creates a new Workbox instance with a script URL and service worker
|
||||
* options. The script URL and options are the same as those used when
|
||||
* calling `navigator.serviceWorker.register(scriptURL, options)`. See:
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register
|
||||
*
|
||||
* @param {string} scriptURL The service worker script associated with this
|
||||
* instance.
|
||||
* @param {Object} [registerOptions] The service worker options associated
|
||||
* with this instance.
|
||||
*/
|
||||
function Workbox(scriptURL, registerOptions) {
|
||||
var _this;
|
||||
|
||||
if (registerOptions === void 0) {
|
||||
registerOptions = {};
|
||||
}
|
||||
|
||||
_this = _EventTargetShim.call(this) || this;
|
||||
_this._scriptURL = scriptURL;
|
||||
_this._registerOptions = registerOptions;
|
||||
_this._updateFoundCount = 0; // Deferreds we can resolve later.
|
||||
|
||||
_this._swDeferred = new Deferred();
|
||||
_this._activeDeferred = new Deferred();
|
||||
_this._controllingDeferred = new Deferred(); // Bind event handler callbacks.
|
||||
|
||||
_this._onMessage = _this._onMessage.bind(_assertThisInitialized(_assertThisInitialized(_this)));
|
||||
_this._onStateChange = _this._onStateChange.bind(_assertThisInitialized(_assertThisInitialized(_this)));
|
||||
_this._onUpdateFound = _this._onUpdateFound.bind(_assertThisInitialized(_assertThisInitialized(_this)));
|
||||
_this._onControllerChange = _this._onControllerChange.bind(_assertThisInitialized(_assertThisInitialized(_this)));
|
||||
return _this;
|
||||
}
|
||||
/**
|
||||
* Registers a service worker for this instances script URL and service
|
||||
* worker options. By default this method delays registration until after
|
||||
* the window has loaded.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {Function} [options.immediate=false] Setting this to true will
|
||||
* register the service worker immediately, even if the window has
|
||||
* not loaded (not recommended).
|
||||
*/
|
||||
|
||||
|
||||
var _proto = Workbox.prototype;
|
||||
_proto.register = _async(function (_temp) {
|
||||
var _this2 = this;
|
||||
|
||||
var _ref = _temp === void 0 ? {} : _temp,
|
||||
_ref$immediate = _ref.immediate,
|
||||
immediate = _ref$immediate === void 0 ? false : _ref$immediate;
|
||||
|
||||
{
|
||||
if (_this2._registrationTime) {
|
||||
logger.error('Cannot re-register a Workbox instance after it has ' + 'been registered. Create a new instance instead.');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return _invoke(function () {
|
||||
if (!immediate && document.readyState !== 'complete') {
|
||||
return _awaitIgnored(new Promise(function (res) {
|
||||
return addEventListener('load', res);
|
||||
}));
|
||||
}
|
||||
}, function () {
|
||||
// Set this flag to true if any service worker was controlling the page
|
||||
// at registration time.
|
||||
_this2._isUpdate = Boolean(navigator.serviceWorker.controller); // Before registering, attempt to determine if a SW is already controlling
|
||||
// the page, and if that SW script (and version, if specified) matches this
|
||||
// instance's script.
|
||||
|
||||
_this2._compatibleControllingSW = _this2._getControllingSWIfCompatible();
|
||||
return _await(_this2._registerScript(), function (_this2$_registerScrip) {
|
||||
_this2._registration = _this2$_registerScrip;
|
||||
|
||||
// If we have a compatible controller, store the controller as the "own"
|
||||
// SW, resolve active/controlling deferreds and add necessary listeners.
|
||||
if (_this2._compatibleControllingSW) {
|
||||
_this2._sw = _this2._compatibleControllingSW;
|
||||
|
||||
_this2._activeDeferred.resolve(_this2._compatibleControllingSW);
|
||||
|
||||
_this2._controllingDeferred.resolve(_this2._compatibleControllingSW);
|
||||
|
||||
_this2._reportWindowReady(_this2._compatibleControllingSW);
|
||||
|
||||
_this2._compatibleControllingSW.addEventListener('statechange', _this2._onStateChange, {
|
||||
once: true
|
||||
});
|
||||
} // If there's a waiting service worker with a matching URL before the
|
||||
// `updatefound` event fires, it likely means that this site is open
|
||||
// in another tab, or the user refreshed the page (and thus the prevoius
|
||||
// page wasn't fully unloaded before this page started loading).
|
||||
// https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#waiting
|
||||
|
||||
|
||||
var waitingSW = _this2._registration.waiting;
|
||||
|
||||
if (waitingSW && urlsMatch(waitingSW.scriptURL, _this2._scriptURL)) {
|
||||
// Store the waiting SW as the "own" Sw, even if it means overwriting
|
||||
// a compatible controller.
|
||||
_this2._sw = waitingSW; // Run this in the next microtask, so any code that adds an event
|
||||
// listener after awaiting `register()` will get this event.
|
||||
|
||||
Promise.resolve().then(function () {
|
||||
_this2.dispatchEvent(new WorkboxEvent('waiting', {
|
||||
sw: waitingSW,
|
||||
wasWaitingBeforeRegister: true
|
||||
}));
|
||||
|
||||
{
|
||||
logger.warn('A service worker was already waiting to activate ' + 'before this script was registered...');
|
||||
}
|
||||
});
|
||||
} // If an "own" SW is already set, resolve the deferred.
|
||||
|
||||
|
||||
if (_this2._sw) {
|
||||
_this2._swDeferred.resolve(_this2._sw);
|
||||
}
|
||||
|
||||
{
|
||||
logger.log('Successfully registered service worker.', _this2._scriptURL);
|
||||
|
||||
if (navigator.serviceWorker.controller) {
|
||||
if (_this2._compatibleControllingSW) {
|
||||
logger.debug('A service worker with the same script URL ' + 'is already controlling this page.');
|
||||
} else {
|
||||
logger.debug('A service worker with a different script URL is ' + 'currently controlling the page. The browser is now fetching ' + 'the new script now...');
|
||||
}
|
||||
}
|
||||
|
||||
var currentPageIsOutOfScope = function currentPageIsOutOfScope() {
|
||||
var scopeURL = new URL(_this2._registerOptions.scope || _this2._scriptURL, document.baseURI);
|
||||
var scopeURLBasePath = new URL('./', scopeURL.href).pathname;
|
||||
return !location.pathname.startsWith(scopeURLBasePath);
|
||||
};
|
||||
|
||||
if (currentPageIsOutOfScope()) {
|
||||
logger.warn('The current page is not in scope for the registered ' + 'service worker. Was this a mistake?');
|
||||
}
|
||||
}
|
||||
|
||||
_this2._registration.addEventListener('updatefound', _this2._onUpdateFound);
|
||||
|
||||
navigator.serviceWorker.addEventListener('controllerchange', _this2._onControllerChange, {
|
||||
once: true
|
||||
}); // Add message listeners.
|
||||
|
||||
if ('BroadcastChannel' in self) {
|
||||
_this2._broadcastChannel = new BroadcastChannel('workbox');
|
||||
|
||||
_this2._broadcastChannel.addEventListener('message', _this2._onMessage);
|
||||
}
|
||||
|
||||
navigator.serviceWorker.addEventListener('message', _this2._onMessage);
|
||||
return _this2._registration;
|
||||
});
|
||||
});
|
||||
});
|
||||
/**
|
||||
* Resolves to the service worker registered by this instance as soon as it
|
||||
* is active. If a service worker was already controlling at registration
|
||||
* time then it will resolve to that if the script URLs (and optionally
|
||||
* script versions) match, otherwise it will wait until an update is found
|
||||
* and activates.
|
||||
*
|
||||
* @return {Promise<ServiceWorker>}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Resolves with a reference to a service worker that matches the script URL
|
||||
* of this instance, as soon as it's available.
|
||||
*
|
||||
* If, at registration time, there's already an active or waiting service
|
||||
* worker with a matching script URL, it will be used (with the waiting
|
||||
* service worker taking precedence over the active service worker if both
|
||||
* match, since the waiting service worker would have been registered more
|
||||
* recently).
|
||||
* If there's no matching active or waiting service worker at registration
|
||||
* time then the promise will not resolve until an update is found and starts
|
||||
* installing, at which point the installing service worker is used.
|
||||
*
|
||||
* @return {Promise<ServiceWorker>}
|
||||
*/
|
||||
_proto.getSW = _async(function () {
|
||||
var _this3 = this;
|
||||
|
||||
// If `this._sw` is set, resolve with that as we want `getSW()` to
|
||||
// return the correct (new) service worker if an update is found.
|
||||
return _this3._sw || _this3._swDeferred.promise;
|
||||
});
|
||||
/**
|
||||
* Sends the passed data object to the service worker registered by this
|
||||
* instance (via [`getSW()`]{@link module:workbox-window.Workbox#getSW}) and resolves
|
||||
* with a response (if any).
|
||||
*
|
||||
* A response can be set in a message handler in the service worker by
|
||||
* calling `event.ports[0].postMessage(...)`, which will resolve the promise
|
||||
* returned by `messageSW()`. If no response is set, the promise will never
|
||||
* resolve.
|
||||
*
|
||||
* @param {Object} data An object to send to the service worker
|
||||
* @return {Promise<Object>}
|
||||
*/
|
||||
|
||||
_proto.messageSW = _async(function (data) {
|
||||
var _this4 = this;
|
||||
|
||||
return _await(_this4.getSW(), function (sw) {
|
||||
return messageSW(sw, data);
|
||||
});
|
||||
});
|
||||
/**
|
||||
* Checks for a service worker already controlling the page and returns
|
||||
* it if its script URL matchs.
|
||||
*
|
||||
* @private
|
||||
* @return {ServiceWorker|undefined}
|
||||
*/
|
||||
|
||||
_proto._getControllingSWIfCompatible = function _getControllingSWIfCompatible() {
|
||||
var controller = navigator.serviceWorker.controller;
|
||||
|
||||
if (controller && urlsMatch(controller.scriptURL, this._scriptURL)) {
|
||||
return controller;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Registers a service worker for this instances script URL and register
|
||||
* options and tracks the time registration was complete.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_proto._registerScript = _async(function () {
|
||||
var _this5 = this;
|
||||
|
||||
return _catch(function () {
|
||||
return _await(navigator.serviceWorker.register(_this5._scriptURL, _this5._registerOptions), function (reg) {
|
||||
// Keep track of when registration happened, so it can be used in the
|
||||
// `this._onUpdateFound` heuristic. Also use the presence of this
|
||||
// property as a way to see if `.register()` has been called.
|
||||
_this5._registrationTime = performance.now();
|
||||
return reg;
|
||||
});
|
||||
}, function (error) {
|
||||
{
|
||||
logger.error(error);
|
||||
} // Re-throw the error.
|
||||
|
||||
|
||||
throw error;
|
||||
});
|
||||
});
|
||||
/**
|
||||
* Sends a message to the passed service worker that the window is ready.
|
||||
*
|
||||
* @param {ServiceWorker} sw
|
||||
* @private
|
||||
*/
|
||||
|
||||
_proto._reportWindowReady = function _reportWindowReady(sw) {
|
||||
messageSW(sw, {
|
||||
type: 'WINDOW_READY',
|
||||
meta: 'workbox-window'
|
||||
});
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
|
||||
_proto._onUpdateFound = function _onUpdateFound() {
|
||||
var installingSW = this._registration.installing; // If the script URL passed to `navigator.serviceWorker.register()` is
|
||||
// different from the current controlling SW's script URL, we know any
|
||||
// successful registration calls will trigger an `updatefound` event.
|
||||
// But if the registered script URL is the same as the current controlling
|
||||
// SW's script URL, we'll only get an `updatefound` event if the file
|
||||
// changed since it was last registered. This can be a problem if the user
|
||||
// opens up the same page in a different tab, and that page registers
|
||||
// a SW that triggers an update. It's a problem because this page has no
|
||||
// good way of knowing whether the `updatefound` event came from the SW
|
||||
// script it registered or from a registration attempt made by a newer
|
||||
// version of the page running in another tab.
|
||||
// To minimize the possibility of a false positive, we use the logic here:
|
||||
|
||||
var updateLikelyTriggeredExternally = // Since we enforce only calling `register()` once, and since we don't
|
||||
// add the `updatefound` event listener until the `register()` call, if
|
||||
// `_updateFoundCount` is > 0 then it means this method has already
|
||||
// been called, thus this SW must be external
|
||||
this._updateFoundCount > 0 || // If the script URL of the installing SW is different from this
|
||||
// instance's script URL, we know it's definitely not from our
|
||||
// registration.
|
||||
!urlsMatch(installingSW.scriptURL, this._scriptURL) || // If all of the above are false, then we use a time-based heuristic:
|
||||
// Any `updatefound` event that occurs long after our registration is
|
||||
// assumed to be external.
|
||||
performance.now() > this._registrationTime + REGISTRATION_TIMEOUT_DURATION ? // If any of the above are not true, we assume the update was
|
||||
// triggered by this instance.
|
||||
true : false;
|
||||
|
||||
if (updateLikelyTriggeredExternally) {
|
||||
this._externalSW = installingSW;
|
||||
|
||||
this._registration.removeEventListener('updatefound', this._onUpdateFound);
|
||||
} else {
|
||||
// If the update was not triggered externally we know the installing
|
||||
// SW is the one we registered, so we set it.
|
||||
this._sw = installingSW;
|
||||
|
||||
this._swDeferred.resolve(installingSW); // The `installing` state isn't something we have a dedicated
|
||||
// callback for, but we do log messages for it in development.
|
||||
|
||||
|
||||
{
|
||||
if (navigator.serviceWorker.controller) {
|
||||
logger.log('Updated service worker found. Installing now...');
|
||||
} else {
|
||||
logger.log('Service worker is installing...');
|
||||
}
|
||||
}
|
||||
} // Increment the `updatefound` count, so future invocations of this
|
||||
// method can be sure they were triggered externally.
|
||||
|
||||
|
||||
++this._updateFoundCount; // Add a `statechange` listener regardless of whether this update was
|
||||
// triggered externally, since we have callbacks for both.
|
||||
|
||||
installingSW.addEventListener('statechange', this._onStateChange);
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} originalEvent
|
||||
*/
|
||||
|
||||
|
||||
_proto._onStateChange = function _onStateChange(originalEvent) {
|
||||
var _this6 = this;
|
||||
|
||||
var sw = originalEvent.target;
|
||||
var state = sw.state;
|
||||
var isExternal = sw === this._externalSW;
|
||||
var eventPrefix = isExternal ? 'external' : '';
|
||||
var eventProps = {
|
||||
sw: sw,
|
||||
originalEvent: originalEvent
|
||||
};
|
||||
|
||||
if (!isExternal && this._isUpdate) {
|
||||
eventProps.isUpdate = true;
|
||||
}
|
||||
|
||||
this.dispatchEvent(new WorkboxEvent(eventPrefix + state, eventProps));
|
||||
|
||||
if (state === 'installed') {
|
||||
// This timeout is used to ignore cases where the service worker calls
|
||||
// `skipWaiting()` in the install event, thus moving it directly in the
|
||||
// activating state. (Since all service workers *must* go through the
|
||||
// waiting phase, the only way to detect `skipWaiting()` called in the
|
||||
// install event is to observe that the time spent in the waiting phase
|
||||
// is very short.)
|
||||
// NOTE: we don't need separate timeouts for the own and external SWs
|
||||
// since they can't go through these phases at the same time.
|
||||
this._waitingTimeout = setTimeout(function () {
|
||||
// Ensure the SW is still waiting (it may now be redundant).
|
||||
if (state === 'installed' && _this6._registration.waiting === sw) {
|
||||
_this6.dispatchEvent(new WorkboxEvent(eventPrefix + 'waiting', eventProps));
|
||||
|
||||
{
|
||||
if (isExternal) {
|
||||
logger.warn('An external service worker has installed but is ' + 'waiting for this client to close before activating...');
|
||||
} else {
|
||||
logger.warn('The service worker has installed but is waiting ' + 'for existing clients to close before activating...');
|
||||
}
|
||||
}
|
||||
}
|
||||
}, WAITING_TIMEOUT_DURATION);
|
||||
} else if (state === 'activating') {
|
||||
clearTimeout(this._waitingTimeout);
|
||||
|
||||
if (!isExternal) {
|
||||
this._activeDeferred.resolve(sw);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
switch (state) {
|
||||
case 'installed':
|
||||
if (isExternal) {
|
||||
logger.warn('An external service worker has installed. ' + 'You may want to suggest users reload this page.');
|
||||
} else {
|
||||
logger.log('Registered service worker installed.');
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'activated':
|
||||
if (isExternal) {
|
||||
logger.warn('An external service worker has activated.');
|
||||
} else {
|
||||
logger.log('Registered service worker activated.');
|
||||
|
||||
if (sw !== navigator.serviceWorker.controller) {
|
||||
logger.warn('The registered service worker is active but ' + 'not yet controlling the page. Reload or run ' + '`clients.claim()` in the service worker.');
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case 'redundant':
|
||||
if (sw === this._compatibleControllingSW) {
|
||||
logger.log('Previously controlling service worker now redundant!');
|
||||
} else if (!isExternal) {
|
||||
logger.log('Registered service worker now redundant!');
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} originalEvent
|
||||
*/
|
||||
|
||||
|
||||
_proto._onControllerChange = function _onControllerChange(originalEvent) {
|
||||
var sw = this._sw;
|
||||
|
||||
if (sw === navigator.serviceWorker.controller) {
|
||||
this.dispatchEvent(new WorkboxEvent('controlling', {
|
||||
sw: sw,
|
||||
originalEvent: originalEvent
|
||||
}));
|
||||
|
||||
{
|
||||
logger.log('Registered service worker now controlling this page.');
|
||||
}
|
||||
|
||||
this._controllingDeferred.resolve(sw);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @private
|
||||
* @param {Event} originalEvent
|
||||
*/
|
||||
|
||||
|
||||
_proto._onMessage = function _onMessage(originalEvent) {
|
||||
var data = originalEvent.data;
|
||||
this.dispatchEvent(new WorkboxEvent('message', {
|
||||
data: data,
|
||||
originalEvent: originalEvent
|
||||
}));
|
||||
};
|
||||
|
||||
_createClass(Workbox, [{
|
||||
key: "active",
|
||||
get: function get() {
|
||||
return this._activeDeferred.promise;
|
||||
}
|
||||
/**
|
||||
* Resolves to the service worker registered by this instance as soon as it
|
||||
* is controlling the page. If a service worker was already controlling at
|
||||
* registration time then it will resolve to that if the script URLs (and
|
||||
* optionally script versions) match, otherwise it will wait until an update
|
||||
* is found and starts controlling the page.
|
||||
* Note: the first time a service worker is installed it will active but
|
||||
* not start controlling the page unless `clients.claim()` is called in the
|
||||
* service worker.
|
||||
*
|
||||
* @return {Promise<ServiceWorker>}
|
||||
*/
|
||||
|
||||
}, {
|
||||
key: "controlling",
|
||||
get: function get() {
|
||||
return this._controllingDeferred.promise;
|
||||
}
|
||||
}]);
|
||||
|
||||
return Workbox;
|
||||
}(EventTargetShim); // The jsdoc comments below outline the events this instance may dispatch:
|
||||
|
||||
/*
|
||||
Copyright 2019 Google LLC
|
||||
|
||||
Use of this source code is governed by an MIT-style
|
||||
license that can be found in the LICENSE file or at
|
||||
https://opensource.org/licenses/MIT.
|
||||
*/
|
||||
|
||||
exports.Workbox = Workbox;
|
||||
exports.messageSW = messageSW;
|
||||
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
|
||||
}));
|
||||
//# sourceMappingURL=workbox-window.dev.umd.js.map
|