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
This commit is contained in:
Sébastien Lorber 2021-03-10 20:00:42 +01:00 committed by GitHub
parent efbd8fa351
commit 02cd5d343b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 396 additions and 229 deletions

View file

@ -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",

View file

@ -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),

View file

@ -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,19 +136,10 @@ function createServiceWorkerUrl(params) {
return url;
}
(async () => {
if (typeof window === 'undefined') {
return;
}
if (debug) {
console.log('[Docusaurus-PWA][registerSw]: debug mode enabled');
}
if ('serviceWorker' in navigator) {
async function registerSW() {
const {Workbox} = await import('workbox-window');
const offlineMode = isOfflineModeEnabled();
const offlineMode = await isOfflineModeEnabled();
const url = createServiceWorkerUrl({offlineMode, debug});
const wb = new Workbox(url);
@ -115,7 +157,7 @@ function createServiceWorkerUrl(params) {
sendSkipWaiting();
} else if (PWA_RELOAD_POPUP) {
const renderReloadPopup = (await import('./renderReloadPopup')).default;
renderReloadPopup({
await renderReloadPopup({
onReload() {
wb.addEventListener('controlling', () => {
window.location.reload();
@ -147,12 +189,6 @@ function createServiceWorkerUrl(params) {
}
}
// 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) => {
@ -166,46 +202,98 @@ function createServiceWorkerUrl(params) {
// moves to waiting state in another tab.
wb.addEventListener('externalwaiting', (event) => {
if (debug) {
console.log(
'[Docusaurus-PWA][registerSw]: event externalwaiting',
event,
);
console.log('[Docusaurus-PWA][registerSw]: event externalwaiting', event);
}
handleServiceWorkerWaiting();
});
window.addEventListener('appinstalled', (event) => {
// 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',
);
}
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) {
if (debug) {
console.log(
'[Docusaurus-PWA][registerSw]: browser does not support service workers',
'[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));
}
}

View file

@ -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') {

View file

@ -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';
<CodeBlock className="language-json">
{JSON.stringify(require("@site/static/manifest.json"),null,2)}
</CodeBlock>
```

View file

@ -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: [

View file

@ -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",

206
yarn.lock
View file

@ -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"