From 02cd5d343b8a9cd9fe5bb0975448a830567ee96a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Lorber?= Date: Wed, 10 Mar 2021 20:00:42 +0100 Subject: [PATCH] fix(v2): PWA issues + improve docs (#4377) * debug pwa * more debugging logs + attempt to upgrade workbox * fix PWA ? * fix PWA ? * enable pwa debugging for deploy previews * try to fix the app installed issue? * try to fix appinstalled not firing * try to add related applications to the PWA manifest * attempt to fix related_applications * attempt to fix related_applications * attempt to fix related_applications * improve PWA strategies * improve PWA doc * refactor/cleanup registerSw * cleanup --- packages/docusaurus-plugin-pwa/package.json | 6 +- .../src/pluginOptionSchema.js | 15 +- .../docusaurus-plugin-pwa/src/registerSw.js | 312 +++++++++++------- packages/docusaurus-plugin-pwa/src/sw.js | 33 +- website/docs/api/plugins/plugin-pwa.md | 39 ++- website/docusaurus.config.js | 8 +- website/static/manifest.json | 6 + yarn.lock | 206 ++++++------ 8 files changed, 396 insertions(+), 229 deletions(-) diff --git a/packages/docusaurus-plugin-pwa/package.json b/packages/docusaurus-plugin-pwa/package.json index a4fb618952..1f45e5c122 100644 --- a/packages/docusaurus-plugin-pwa/package.json +++ b/packages/docusaurus-plugin-pwa/package.json @@ -24,9 +24,9 @@ "terser-webpack-plugin": "^4.1.0", "webpack": "^4.44.1", "webpack-merge": "^4.2.2", - "workbox-build": "^6.1.0", - "workbox-precaching": "^6.1.0", - "workbox-window": "^6.1.0" + "workbox-build": "^6.1.1", + "workbox-precaching": "^6.1.1", + "workbox-window": "^6.1.1" }, "peerDependencies": { "react": "^16.8.4 || ^17.0.0", diff --git a/packages/docusaurus-plugin-pwa/src/pluginOptionSchema.js b/packages/docusaurus-plugin-pwa/src/pluginOptionSchema.js index cd9cb886dc..ca00654835 100644 --- a/packages/docusaurus-plugin-pwa/src/pluginOptionSchema.js +++ b/packages/docusaurus-plugin-pwa/src/pluginOptionSchema.js @@ -10,7 +10,11 @@ const path = require('path'); const DEFAULT_OPTIONS = { debug: false, - offlineModeActivationStrategies: ['appInstalled', 'queryString'], + offlineModeActivationStrategies: [ + 'appInstalled', + 'queryString', + 'standalone', + ], injectManifestConfig: {}, pwaHead: [], swCustom: undefined, @@ -23,7 +27,14 @@ exports.PluginOptionSchema = Joi.object({ offlineModeActivationStrategies: Joi.array() .items( Joi.string() - .valid('appInstalled', 'queryString', 'mobile', 'saveData', 'always') + .valid( + 'appInstalled', + 'queryString', + 'standalone', + 'mobile', + 'saveData', + 'always', + ) .required(), ) .default(DEFAULT_OPTIONS.offlineModeActivationStrategies), diff --git a/packages/docusaurus-plugin-pwa/src/registerSw.js b/packages/docusaurus-plugin-pwa/src/registerSw.js index 130b368010..50e32c9d4c 100644 --- a/packages/docusaurus-plugin-pwa/src/registerSw.js +++ b/packages/docusaurus-plugin-pwa/src/registerSw.js @@ -16,40 +16,91 @@ const PWA_DEBUG = process.env.PWA_DEBUG; const debug = PWA_DEBUG; // shortcut +const MAX_MOBILE_WIDTH = 940; +const APP_INSTALLED_EVENT_FIRED_KEY = 'docusaurus.pwa.event.appInstalled.fired'; + async function clearRegistrations() { const registrations = await navigator.serviceWorker.getRegistrations(); - if (debug && registrations.length > 0) { + if (debug) { console.log( - `[Docusaurus-PWA][registerSw]: unregister service workers`, + `[Docusaurus-PWA][registerSw]: will unregister all service worker registrations`, + registrations, + ); + } + await Promise.all( + registrations.map(async (registration) => { + const result = await registration?.registration?.unregister(); + if (debug) { + console.log( + `[Docusaurus-PWA][registerSw]: unregister() service worker registration`, + registrations, + result, + ); + } + }), + ); + if (debug) { + console.log( + `[Docusaurus-PWA][registerSw]: unregistered all service worker registrations`, registrations, ); } - registrations.forEach((registration) => { - registration.registration.unregister(); - }); window.location.reload(); } -const MAX_MOBILE_WIDTH = 940; -const APP_INSTALLED_KEY = 'docusaurus.pwa.appInstalled'; +/* +As of 2021: +It is complicated and not very reliable to detect an app is actually installed. +https://stackoverflow.com/questions/51735869/check-if-user-has-already-installed-pwa-to-homescreen-on-chrome + +- appinstalled event is not in the spec anymore and seems to not fire? https://firt.dev/pwa-2021#less-capabilities-%E2%98%B9%EF%B8%8F +- getInstalledRelatedApps() is only supported in recent Chrome and does not seem to reliable either https://github.com/WICG/get-installed-related-apps +- display-mode: standalone is not exactly the same concept, but looks like a decent fallback https://petelepage.com/blog/2019/07/is-my-pwa-installed/ + */ +async function isAppInstalledEventFired() { + return localStorage.getItem(APP_INSTALLED_EVENT_FIRED_KEY) === 'true'; +} +async function isAppInstalledRelatedApps() { + if ('getInstalledRelatedApps' in window.navigator) { + const relatedApps = await navigator.getInstalledRelatedApps(); + return relatedApps.some((app) => app.platform === 'webapp'); + } + return false; +} +function isStandaloneDisplayMode() { + return window.matchMedia('(display-mode: standalone)').matches; +} const OfflineModeActivationStrategiesImplementations = { always: () => true, mobile: () => window.innerWidth <= MAX_MOBILE_WIDTH, saveData: () => !!(navigator.connection && navigator.connection.saveData), - appInstalled: () => !!localStorage.getItem(APP_INSTALLED_KEY), + appInstalled: async () => { + const installedEventFired = await isAppInstalledEventFired(); + const installedRelatedApps = await isAppInstalledRelatedApps(); + return installedEventFired || installedRelatedApps; + }, + standalone: () => isStandaloneDisplayMode(), queryString: () => new URLSearchParams(window.location.search).get('offlineMode') === 'true', }; -function isOfflineModeEnabled() { - const activeStrategies = PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES.filter( - (strategyName) => { - const strategyImpl = - OfflineModeActivationStrategiesImplementations[strategyName]; - return strategyImpl(); - }, +async function isStrategyActive(strategyName) { + return OfflineModeActivationStrategiesImplementations[strategyName](); +} + +async function getActiveStrategies() { + const activeStrategies = await Promise.all( + PWA_OFFLINE_MODE_ACTIVATION_STRATEGIES.map(async (strategyName) => { + const isActive = await isStrategyActive(strategyName); + return isActive ? strategyName : undefined; + }), ); + return activeStrategies.filter(Boolean); // remove undefined values +} + +async function isOfflineModeEnabled() { + const activeStrategies = await getActiveStrategies(); const enabled = activeStrategies.length > 0; if (debug) { const logObject = { @@ -85,127 +136,164 @@ function createServiceWorkerUrl(params) { return url; } -(async () => { - if (typeof window === 'undefined') { - return; - } +async function registerSW() { + const {Workbox} = await import('workbox-window'); + + const offlineMode = await isOfflineModeEnabled(); + + const url = createServiceWorkerUrl({offlineMode, debug}); + const wb = new Workbox(url); + + const registration = await wb.register(); + + const sendSkipWaiting = () => wb.messageSW({type: 'SKIP_WAITING'}); + + const handleServiceWorkerWaiting = async () => { + if (debug) { + console.log('[Docusaurus-PWA][registerSw]: handleServiceWorkerWaiting'); + } + // Immediately load new service worker when files aren't cached + if (!offlineMode) { + sendSkipWaiting(); + } else if (PWA_RELOAD_POPUP) { + const renderReloadPopup = (await import('./renderReloadPopup')).default; + await renderReloadPopup({ + onReload() { + wb.addEventListener('controlling', () => { + window.location.reload(); + }); + sendSkipWaiting(); + }, + }); + } + }; if (debug) { - console.log('[Docusaurus-PWA][registerSw]: debug mode enabled'); + if (registration.active) { + console.log( + '[Docusaurus-PWA][registerSw]: registration.active', + registration, + ); + } + if (registration.installing) { + console.log( + '[Docusaurus-PWA][registerSw]: registration.installing', + registration, + ); + } + if (registration.waiting) { + console.log( + '[Docusaurus-PWA][registerSw]: registration.waiting', + registration, + ); + } } - if ('serviceWorker' in navigator) { - const {Workbox} = await import('workbox-window'); - - const offlineMode = isOfflineModeEnabled(); - - const url = createServiceWorkerUrl({offlineMode, debug}); - const wb = new Workbox(url); - - const registration = await wb.register(); - - const sendSkipWaiting = () => wb.messageSW({type: 'SKIP_WAITING'}); - - const handleServiceWorkerWaiting = async () => { - if (debug) { - console.log('[Docusaurus-PWA][registerSw]: handleServiceWorkerWaiting'); - } - // Immediately load new service worker when files aren't cached - if (!offlineMode) { - sendSkipWaiting(); - } else if (PWA_RELOAD_POPUP) { - const renderReloadPopup = (await import('./renderReloadPopup')).default; - renderReloadPopup({ - onReload() { - wb.addEventListener('controlling', () => { - window.location.reload(); - }); - sendSkipWaiting(); - }, - }); - } - }; - + // Update the current service worker when the next one has finished + // installing and transitions to waiting state. + wb.addEventListener('waiting', (event) => { if (debug) { - if (registration.active) { - console.log( - '[Docusaurus-PWA][registerSw]: registration.active', - registration, - ); - } - if (registration.installing) { - console.log( - '[Docusaurus-PWA][registerSw]: registration.installing', - registration, - ); - } - if (registration.waiting) { - console.log( - '[Docusaurus-PWA][registerSw]: registration.waiting', - registration, - ); - } + console.log('[Docusaurus-PWA][registerSw]: event waiting', event); + } + handleServiceWorkerWaiting(); + }); + + // Update current service worker if the next one finishes installing and + // moves to waiting state in another tab. + wb.addEventListener('externalwaiting', (event) => { + if (debug) { + console.log('[Docusaurus-PWA][registerSw]: event externalwaiting', event); + } + handleServiceWorkerWaiting(); + }); + + // Update service worker if the next one is already in the waiting state. + // This happens when the user doesn't click on `reload` in the popup. + if (registration.waiting) { + await handleServiceWorkerWaiting(); + } +} + +// TODO these events still works in chrome but have been removed from the spec in 2019! +// See https://github.com/w3c/manifest/pull/836 +function addLegacyAppInstalledEventsListeners() { + if (typeof window !== 'undefined') { + if (debug) { + console.log( + '[Docusaurus-PWA][registerSw]: addLegacyAppInstalledEventsListeners', + ); } - // Update service worker if the next one is already in the waiting state. - // This happens when the user doesn't click on `reload` in the popup. - if (registration.waiting) { - handleServiceWorkerWaiting(); - } - - // Update the current service worker when the next one has finished - // installing and transitions to waiting state. - wb.addEventListener('waiting', (event) => { - if (debug) { - console.log('[Docusaurus-PWA][registerSw]: event waiting', event); - } - handleServiceWorkerWaiting(); - }); - - // Update current service worker if the next one finishes installing and - // moves to waiting state in another tab. - wb.addEventListener('externalwaiting', (event) => { - if (debug) { - console.log( - '[Docusaurus-PWA][registerSw]: event externalwaiting', - event, - ); - } - handleServiceWorkerWaiting(); - }); - - window.addEventListener('appinstalled', (event) => { + window.addEventListener('appinstalled', async (event) => { if (debug) { console.log('[Docusaurus-PWA][registerSw]: event appinstalled', event); } - localStorage.setItem(APP_INSTALLED_KEY, true); + + localStorage.setItem(APP_INSTALLED_EVENT_FIRED_KEY, 'true'); + if (debug) { + console.log( + "[Docusaurus-PWA][registerSw]: localStorage.setItem(APP_INSTALLED_EVENT_FIRED_KEY, 'true');", + ); + } // After the app is installed, we register a service worker with the path // `/sw?enabled`. Since the previous service worker was `/sw`, it'll be // treated as a new one. The previous registration will need to be // cleared, otherwise the reload popup will show. - clearRegistrations(); + await clearRegistrations(); }); - window.addEventListener('beforeinstallprompt', (event) => { + // TODO this event still works in chrome but has been removed from the spec in 2019!!! + window.addEventListener('beforeinstallprompt', async (event) => { if (debug) { - console.log('[Docusaurus-PWA][registerSw]: event appinstalled', event); + console.log( + '[Docusaurus-PWA][registerSw]: event beforeinstallprompt', + event, + ); } // TODO instead of default browser install UI, show custom docusaurus prompt? // event.preventDefault(); - - if (localStorage.getItem(APP_INSTALLED_KEY)) { - localStorage.removeItem(APP_INSTALLED_KEY); - + if (debug) { + console.log( + '[Docusaurus-PWA][registerSw]: localStorage.getItem(APP_INSTALLED_EVENT_FIRED_KEY)', + localStorage.getItem(APP_INSTALLED_EVENT_FIRED_KEY), + ); + } + if (localStorage.getItem(APP_INSTALLED_EVENT_FIRED_KEY)) { + localStorage.removeItem(APP_INSTALLED_EVENT_FIRED_KEY); + if (debug) { + console.log( + '[Docusaurus-PWA][registerSw]: localStorage.removeItem(APP_INSTALLED_EVENT_FIRED_KEY)', + ); + } // After uninstalling the app, if the user doesn't clear all data, then // the previous service worker will continue serving cached files. We // need to clear registrations and reload, otherwise the popup will show. - clearRegistrations(); + await clearRegistrations(); } }); - } else if (debug) { - console.log( - '[Docusaurus-PWA][registerSw]: browser does not support service workers', - ); + + if (debug) { + console.log( + '[Docusaurus-PWA][registerSw]: legacy appinstalled and beforeinstallprompt event listeners installed', + ); + } } -})(); +} + +/* +Init code to run on the client! + */ +if (typeof window !== 'undefined') { + if (debug) { + console.log('[Docusaurus-PWA][registerSw]: debug mode enabled'); + } + + if ('serviceWorker' in navigator) { + // First: add the listeners asap/synchronously + addLegacyAppInstalledEventsListeners(); + + // Then try to register the SW using lazy/dynamic imports + registerSW().catch((e) => console.error('registerSW failed', e)); + } +} diff --git a/packages/docusaurus-plugin-pwa/src/sw.js b/packages/docusaurus-plugin-pwa/src/sw.js index ecfe1cfa35..4834f2bcf0 100644 --- a/packages/docusaurus-plugin-pwa/src/sw.js +++ b/packages/docusaurus-plugin-pwa/src/sw.js @@ -71,20 +71,37 @@ function getPossibleURLs(url) { const params = parseSwParams(); const precacheManifest = self.__WB_MANIFEST; - const controller = new PrecacheController(); + const controller = new PrecacheController({ + fallbackToNetwork: true, // safer to turn this true? + }); if (params.offlineMode) { controller.addToCacheList(precacheManifest); + if (params.debug) { + console.log('[Docusaurus-PWA][SW]: addToCacheList', { + precacheManifest, + }); + } } await runSWCustomCode(params); self.addEventListener('install', (event) => { - event.waitUntil(controller.install()); + if (params.debug) { + console.log('[Docusaurus-PWA][SW]: install event', { + event, + }); + } + event.waitUntil(controller.install(event)); }); self.addEventListener('activate', (event) => { - event.waitUntil(controller.activate()); + if (params.debug) { + console.log('[Docusaurus-PWA][SW]: activate event', { + event, + }); + } + event.waitUntil(controller.activate(event)); }); self.addEventListener('fetch', async (event) => { @@ -95,15 +112,17 @@ function getPossibleURLs(url) { const possibleURL = possibleURLs[i]; const cacheKey = controller.getCacheKeyForURL(possibleURL); if (cacheKey) { + const cachedResponse = caches.match(cacheKey); if (params.debug) { console.log('[Docusaurus-PWA][SW]: serving cached asset', { requestURL, possibleURL, possibleURLs, cacheKey, + cachedResponse, }); } - event.respondWith(caches.match(cacheKey)); + event.respondWith(cachedResponse); break; } } @@ -111,6 +130,12 @@ function getPossibleURLs(url) { }); self.addEventListener('message', async (event) => { + if (params.debug) { + console.log('[Docusaurus-PWA][SW]: message event', { + event, + }); + } + const type = event.data && event.data.type; if (type === 'SKIP_WAITING') { diff --git a/website/docs/api/plugins/plugin-pwa.md b/website/docs/api/plugins/plugin-pwa.md index 9b81e4b2ea..94bcd63d4c 100644 --- a/website/docs/api/plugins/plugin-pwa.md +++ b/website/docs/api/plugins/plugin-pwa.md @@ -25,7 +25,11 @@ module.exports = { '@docusaurus/plugin-pwa', { debug: true, - offlineModeActivationStrategies: ['appInstalled', 'queryString'], + offlineModeActivationStrategies: [ + 'appInstalled', + 'standalone', + 'queryString', + ], pwaHead: [ { tagName: 'link', @@ -63,6 +67,12 @@ If your browser supports it, you should be able to install a Docusaurus site as ![pwa_install.gif](/img/pwa_install.gif) +:::note + +App installation requires the https protocol and a valid manifest. + +::: + ## Offline mode (precaching) We enable users to browse a Docusaurus site offline, by using service-worker precaching. @@ -102,11 +112,12 @@ Turn debug mode on: ### `offlineModeActivationStrategies` - Type: `Array<'appInstalled' | 'mobile' | 'saveData'| 'queryString' | 'always'>` -- Default: `['appInstalled','queryString']` +- Default: `['appInstalled','queryString','standalone']` Strategies used to turn the offline mode on: -- `appInstalled`: activates for users having installed the site as an app +- `appInstalled`: activates for users having installed the site as an app (not 100% reliable) +- `standalone`: activates for users running the app as standalone (often the case once a PWA is installed) - `queryString`: activates if queryString contains `offlineMode=true` (convenient for PWA debugging) - `mobile`: activates for mobile users (width <= 940px) - `saveData`: activates for users with `navigator.connection.saveData === true` @@ -118,6 +129,16 @@ Use this carefully: some users may not like to be forced to use the offline mode ::: +:::danger + +It is not possible to detect if an as a PWA in a very reliable way. + +The `appinstalled` event has been [removed from the specification](https://github.com/w3c/manifest/pull/836), and the [`navigator.getInstalledRelatedApps()`](https://web.dev/get-installed-related-apps/) API is only supported in recent Chrome versions and require `related_applications` declared in the manifest. + +The [`standalone` strategy](https://petelepage.com/blog/2019/07/is-my-pwa-installed/) is a nice fallback to activate the offline mode (at least when running the installed app). + +::: + ### `injectManifestConfig` [Workbox options](https://developers.google.com/web/tools/workbox/reference-docs/latest/module-workbox-build#.injectManifest) to pass to `workbox.injectManifest()`. This gives you control over which assets will be precached, and be available offline. @@ -278,3 +299,15 @@ The module should have a `default` function export, and receives some params. Adds an entry before the Docusaurus app so that registration can happen before the app runs. The default `registerSW.js` file is enough for simple registration. Passing `false` will disable registration entirely. + +## Manifest example + +The Docusaurus site manifest can serve as an inspiration: + +```mdx-code-block +import CodeBlock from '@theme/CodeBlock'; + + + {JSON.stringify(require("@site/static/manifest.json"),null,2)} + +``` diff --git a/website/docusaurus.config.js b/website/docusaurus.config.js index cddd0d0a53..7cb3ffc07b 100644 --- a/website/docusaurus.config.js +++ b/website/docusaurus.config.js @@ -162,8 +162,12 @@ const LocaleConfigs = isI18nStaging [ '@docusaurus/plugin-pwa', { - debug: false, - offlineModeActivationStrategies: ['appInstalled', 'queryString'], + debug: isDeployPreview, + offlineModeActivationStrategies: [ + 'appInstalled', + 'standalone', + 'queryString', + ], // swRegister: false, swCustom: path.resolve(__dirname, 'src/sw.js'), pwaHead: [ diff --git a/website/static/manifest.json b/website/static/manifest.json index e63e70590c..220fbfc5f0 100644 --- a/website/static/manifest.json +++ b/website/static/manifest.json @@ -6,6 +6,12 @@ "display": "standalone", "scope": "./", "start_url": "./index.html", + "related_applications": [ + { + "platform": "webapp", + "url": "https://v2.docusaurus.io/manifest.json" + } + ], "icons": [ { "src": "img/icons/icon-72x72.png", diff --git a/yarn.lock b/yarn.lock index a33f11209a..7ed85c9484 100644 --- a/yarn.lock +++ b/yarn.lock @@ -20787,24 +20787,24 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus= -workbox-background-sync@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.1.0.tgz#817de1ac1546fb6035759f151b0b4c5f0d3d9506" - integrity sha512-A7YWWmAqzLkWYqqxzxoX4mciVjdSHpfX+JMADXoJ9SoLb6l/QReNJE+CNPew+gGPH6JLKNjZeecDmUpXFhzFPA== +workbox-background-sync@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-background-sync/-/workbox-background-sync-6.1.1.tgz#db51214299b4be7a8aa274d8037f22d917241101" + integrity sha512-w1b3j7snz4pQ8xp0i5Nci40qlglqdk70pbORBtMfl9uikI1qGjYfKq6oYeResCXYxb5mUYS245HsUclb6RFVJA== dependencies: - workbox-core "^6.1.0" + workbox-core "^6.1.1" -workbox-broadcast-update@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.1.0.tgz#63c1dc2d519aa6a7b9ce1db2f8da3e1db45b3422" - integrity sha512-70G821I1Lb4Ex+rcjfKCbuFJ4WL4RSQsqvcByt/bLpPTTLoE6+VvLX3+1QtSK8P2+NmOsKkAqx9qiQkUdGbaYw== +workbox-broadcast-update@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-broadcast-update/-/workbox-broadcast-update-6.1.1.tgz#5815749c9ad22ba4ef5184064a62fbdae3b04bf0" + integrity sha512-8fBNOQt8ojWWtz3FbkDnKo8CpN6l8UjD2HpQr8tue7HJVfk0X1gfnzZLIDg7HCXhqF7ld3iQbGQqGPf1ihTY6A== dependencies: - workbox-core "^6.1.0" + workbox-core "^6.1.1" -workbox-build@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.1.0.tgz#e0ba4a0004da1079e934c7452c72c92ef7b52cba" - integrity sha512-xJPqTEf+Pg9KAoTrNeVWpMjqi4cJIRn14i02bZjjbHsLNN38qrqc8xwAW48TwoPCYLjp104ST164/3RDgrc7yw== +workbox-build@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-build/-/workbox-build-6.1.1.tgz#8333626fad45734d842293e6c2c1b725f4e15752" + integrity sha512-mAI3dS4VnXri6BFg02arK1403SqHy2sOlzC4NVAk6Rl2+Ddxs+PmJO4cMTyHw0KEhQFcwk6V8cJeGiXJXYzinA== dependencies: "@babel/core" "^7.11.1" "@babel/preset-env" "^7.11.0" @@ -20828,90 +20828,90 @@ workbox-build@^6.1.0: strip-comments "^2.0.1" tempy "^0.6.0" upath "^1.2.0" - workbox-background-sync "^6.1.0" - workbox-broadcast-update "^6.1.0" - workbox-cacheable-response "^6.1.0" - workbox-core "^6.1.0" - workbox-expiration "^6.1.0" - workbox-google-analytics "^6.1.0" - workbox-navigation-preload "^6.1.0" - workbox-precaching "^6.1.0" - workbox-range-requests "^6.1.0" - workbox-recipes "^6.1.0" - workbox-routing "^6.1.0" - workbox-strategies "^6.1.0" - workbox-streams "^6.1.0" - workbox-sw "^6.1.0" - workbox-window "^6.1.0" + workbox-background-sync "^6.1.1" + workbox-broadcast-update "^6.1.1" + workbox-cacheable-response "^6.1.1" + workbox-core "^6.1.1" + workbox-expiration "^6.1.1" + workbox-google-analytics "^6.1.1" + workbox-navigation-preload "^6.1.1" + workbox-precaching "^6.1.1" + workbox-range-requests "^6.1.1" + workbox-recipes "^6.1.1" + workbox-routing "^6.1.1" + workbox-strategies "^6.1.1" + workbox-streams "^6.1.1" + workbox-sw "^6.1.1" + workbox-window "^6.1.1" -workbox-cacheable-response@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.1.0.tgz#a99fdfe1507848486579df7b204c30e4cd0a74f2" - integrity sha512-oDAi0vXHGaE5p9NOo4N180UTcEKm6t2JMgmlrq0PkEW2PZEu9YR/atSnCwzMW7xpDqpKWaQr/LGP4+eixS8gcA== +workbox-cacheable-response@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-cacheable-response/-/workbox-cacheable-response-6.1.1.tgz#1dc71393cbce83559ad05a8ccb6c6fafa4cccc70" + integrity sha512-jasNxelRrqCbzIAIMjHk7Ej9BOViBTQlvRJzv3Y0nYuWvxK0CDPQJSraGmTbu3LGiTBbrWEmxe1hVqvLyFKR9A== dependencies: - workbox-core "^6.1.0" + workbox-core "^6.1.1" workbox-core@^5.1.4: version "5.1.4" resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-5.1.4.tgz#8bbfb2362ecdff30e25d123c82c79ac65d9264f4" integrity sha512-+4iRQan/1D8I81nR2L5vcbaaFskZC2CL17TLbvWVzQ4qiF/ytOGF6XeV54pVxAvKUtkLANhk8TyIUMtiMw2oDg== -workbox-core@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.1.0.tgz#2671b64f76550e83a4c2202676b67ce372e10881" - integrity sha512-s3KqTJfBreO4xCZpR2LB5p/EknAx8eg0QumKiIgxM4hRO0RtwS2pJvTieNEM23X3RqxRhqweriLD8To19KUvjg== +workbox-core@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-core/-/workbox-core-6.1.1.tgz#c8a9b424031b0cf7dacf9d7b8e023d126c9d0167" + integrity sha512-xsc/72AQxFtt2BHmwU8QtnVV+W5ln4nnYGuz9Q5sPWYGqW4cH0P+FpZDoGM59bmNEyNf+W9bEmidW//e5GsbwQ== -workbox-expiration@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.1.0.tgz#cf6bb384e49d0c92b79233c46671d9c6d82478a2" - integrity sha512-jp2xGk+LC4AhCoOxO/bC06GQkq/oVp0ZIf1zXLQh6OD2fWZPkXNjLLSuDnjXoGGPibYrq7gEE/xjAdYGjNWl1A== +workbox-expiration@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-expiration/-/workbox-expiration-6.1.1.tgz#4468c3cdfe76b5888f4ae7e3aad63797a7bc24b1" + integrity sha512-WbEv8NG1ZUiWI+jv3v7Jqed/PyCMoTpLcf3Nw7tKq0nGy6DFQtmSizO37uJ73oc8vttck97UBPQRiwyP1bZnAg== dependencies: - workbox-core "^6.1.0" + workbox-core "^6.1.1" -workbox-google-analytics@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.1.0.tgz#cd34100536250abc54070bcc23603213eb8e47e4" - integrity sha512-BuUAJ747bMPC6IOKaQBXfotGybOfeHDRIC8ElF65ouB4O9kUJ3zh4EFxXmmJLgzTnji6265gXqNWcfuGiidk6A== +workbox-google-analytics@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-google-analytics/-/workbox-google-analytics-6.1.1.tgz#c31876954779d65e1334c2dc3232e46d6a5f925a" + integrity sha512-79PyeE4TyabGXqlDcRG2LKejs8yZ8OoU0/El0BwP8RGrZgp5GMDGuJkat4xggpRTVaOk8rb0aoSbVAYBWpa0pg== dependencies: - workbox-background-sync "^6.1.0" - workbox-core "^6.1.0" - workbox-routing "^6.1.0" - workbox-strategies "^6.1.0" + workbox-background-sync "^6.1.1" + workbox-core "^6.1.1" + workbox-routing "^6.1.1" + workbox-strategies "^6.1.1" -workbox-navigation-preload@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.1.0.tgz#e36d19f0d49ab5277e6c4e13b92f40da8955d62f" - integrity sha512-N0c5Kmzu7lPKvirukbeZ3lN8KEAZU9xA4b1wmpV0VXUfRXVEk2ayXXqwHwMGFVi6FNCHiDLOcC8a2zW5kFLAeg== +workbox-navigation-preload@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-navigation-preload/-/workbox-navigation-preload-6.1.1.tgz#3c7d39d5f102f4a76f24b48f97701b16ae56bd40" + integrity sha512-vX5qJDk1Z663nuSSSHkcBFQQJwEe4UHynd5uoX3oC0IlecPclAbyT3QetVh0wYdXv6G6XD/LBd3iNZmlSbTosw== dependencies: - workbox-core "^6.1.0" + workbox-core "^6.1.1" -workbox-precaching@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.1.0.tgz#9ee3d28f27cd78daa62f5bd6a0d33f5682ac97a7" - integrity sha512-zjye8MVzieBVJ3sS0hFcbKLp7pTHMfJM17YqxCxB0KykXWnxLOpYnStQ9M+bjWJsKJOQvbkPqvq5u9+mtA923g== +workbox-precaching@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-precaching/-/workbox-precaching-6.1.1.tgz#f387ccdf60aab30228a4c7ed20a1cd8dee6aaaa4" + integrity sha512-x8OKwtjd5ewe/x3VlKcXri1P3Tm0uV+uChdMYg/QryrCR9K8x9xwhAw8PZPkwrY0bLLsJMUoX9/lBu8DmjVqTA== dependencies: - workbox-core "^6.1.0" - workbox-routing "^6.1.0" - workbox-strategies "^6.1.0" + workbox-core "^6.1.1" + workbox-routing "^6.1.1" + workbox-strategies "^6.1.1" -workbox-range-requests@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.1.0.tgz#5fbe9edfbcdb97153ed5260575a54e53b0f85a2d" - integrity sha512-BO025BdAvc6vTBXJfkfibcikMFLmLRECt0FrVrTiiQafdO3jWH9qX9zTdrjYf6GkiIjvejvvmSYegwU1mL6N3Q== +workbox-range-requests@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-range-requests/-/workbox-range-requests-6.1.1.tgz#4e6d30e91cfc3855ff16cfa3df458e0487da2b4d" + integrity sha512-ikZ0ZwbFAVMzJ08rM/spn9zC2tohGllFVii9R1q0+xMKvoGDsyzoQnoKrXgyUvcjRPn6ByFncAJ5lUKKG4TGkA== dependencies: - workbox-core "^6.1.0" + workbox-core "^6.1.1" -workbox-recipes@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.1.0.tgz#b925f2727ace05ce8762a1b6da6c0d749fd687ee" - integrity sha512-r8YLtMtQnvfkK1htnfrrX1CxKHglZJiVlqnct9rYIU17n2LCalHdI0zQrPqzYdLLHZxTX25UpBsdib0cAATy0A== +workbox-recipes@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-recipes/-/workbox-recipes-6.1.1.tgz#0cd1bd3b2ba223db563428ec5d17e960081f70d4" + integrity sha512-GuzJXBQM+YaFxQwFvcRarAScUoRDoaWXKxxkLWHnCJf0H//MQ8zR9Ay1mv21N6iRoSH11S0u/4yxSeembG/fLA== dependencies: - workbox-cacheable-response "^6.1.0" - workbox-core "^6.1.0" - workbox-expiration "^6.1.0" - workbox-precaching "^6.1.0" - workbox-routing "^6.1.0" - workbox-strategies "^6.1.0" + workbox-cacheable-response "^6.1.1" + workbox-core "^6.1.1" + workbox-expiration "^6.1.1" + workbox-precaching "^6.1.1" + workbox-routing "^6.1.1" + workbox-strategies "^6.1.1" workbox-routing@^5.0.0, workbox-routing@^5.1.4: version "5.1.4" @@ -20920,12 +20920,12 @@ workbox-routing@^5.0.0, workbox-routing@^5.1.4: dependencies: workbox-core "^5.1.4" -workbox-routing@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.1.0.tgz#f885cb7801e2c9c5678f197656cf27a2b649c1d5" - integrity sha512-FXQ5cwb6Mk90fC0rfQLX0pN+r/N4eBafwkh/QanJUq0e6jMPdDFLrlsikZL/0LcXEx+yAkWLytoiS+d2HOEBOw== +workbox-routing@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-routing/-/workbox-routing-6.1.1.tgz#833ef6439905757241f9e4d56d8e282c20199c02" + integrity sha512-Az3Gt3cHNK+W0gTfSb4eKGfwEap9Slak16Krr5SiLhE1gXUY2C2O123HucVCedXgIoqTLOXMtNj71Cm6SwYDEg== dependencies: - workbox-core "^6.1.0" + workbox-core "^6.1.1" workbox-strategies@^5.0.0: version "5.1.4" @@ -20935,32 +20935,32 @@ workbox-strategies@^5.0.0: workbox-core "^5.1.4" workbox-routing "^5.1.4" -workbox-strategies@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.1.0.tgz#9ddcee44408d2fb403f22a7989803b5c58560590" - integrity sha512-HvUknzJdZWeV3x7Eq33a7TGAv9/r1TEiQK6kQ1QNzN+IKiqhIjnhKFHmMxb5hK1Gw9/aDSJTLNPDaLPfIJRQFQ== +workbox-strategies@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-strategies/-/workbox-strategies-6.1.1.tgz#6e0adda84bcda17d3d0c48baec2eab9b988b9ca6" + integrity sha512-7qYA9Eiq6hnP2dyenlD7ZtWI1ArBMT8yhTvHVlaOl9kYY7W+Iv3lAfRCjj/nucOKeVXATx4iVJEuFPn5J+8lzw== dependencies: - workbox-core "^6.1.0" + workbox-core "^6.1.1" -workbox-streams@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.1.0.tgz#2dbc78ddc863b47aa4fe399d9385d3ed8567e881" - integrity sha512-V80OIfoIXaDkjWIGFSae5sBJuaG2r4bXk6HKpntBYaVQ72LD1CgkXRmZKmLJQ9ltHCx9Vmq/7+q1OF5mTKb8Qw== +workbox-streams@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-streams/-/workbox-streams-6.1.1.tgz#0f204f070861eb1afccddeca4a5a8ba069596bd1" + integrity sha512-EMhY+Y2O7+XVy8MFRmiDwKezAXLzbgjQOJDbxWaGKtwNPbwOF6gGZjCvmnNAU1K+MAvvUNsAFR6AAUKMSfOyaw== dependencies: - workbox-core "^6.1.0" - workbox-routing "^6.1.0" + workbox-core "^6.1.1" + workbox-routing "^6.1.1" -workbox-sw@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.1.0.tgz#dfaca1029264af71f13a90fdfb16cf8d64ed0537" - integrity sha512-e2jnIWSmNrpO9Psy4D6euDdRUW8FTXAdMxOj5O02gxa01fri1kfTSM9irDnTGKUiSGc+hlycsvzGdr8bnvzDiA== +workbox-sw@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-sw/-/workbox-sw-6.1.1.tgz#203ce4611309df1bf9c142d1e3b3a214b1b62944" + integrity sha512-t6LLSx/rOS8d6w4+fsJOHDqGrjO89iBF0F8nBQgBleEPjvs9Be5j4B11y34Fw7s0CggeA3Kciutr4CqnQtPQUg== -workbox-window@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.1.0.tgz#5856127f183bcccfd93655b0e3cba5f2432b9156" - integrity sha512-sjnE+nTSnrBvYx5KmpESvsTC82P3yy8h5l4Ae4Q8uLqdH29UQ3bMd8puGVVhX1JZFCmV40cvrbZ1fUj+3/TQ9g== +workbox-window@^6.1.1: + version "6.1.1" + resolved "https://registry.yarnpkg.com/workbox-window/-/workbox-window-6.1.1.tgz#c1d60f6a56b49235e36edc73c593fa470ffffc72" + integrity sha512-ZT1enHgi6gYfm+HgRWq8nkqLFEtjOjkq3yGV/qhMmKvI39/sIdO4g2LcjqhnUjbhweedX+9KOOu3U4xasQpGcQ== dependencies: - workbox-core "^6.1.0" + workbox-core "^6.1.1" worker-farm@^1.7.0: version "1.7.0"