@vite-pwa/workbox-window 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +19 -0
- package/README.md +80 -0
- package/dist/index.cjs +583 -0
- package/dist/index.d.cts +373 -0
- package/dist/index.d.ts +373 -0
- package/dist/index.js +554 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
Copyright 2018 Google LLC
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
in the Software without restriction, including without limitation the rights
|
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
furnished to do so, subject to the following conditions:
|
|
9
|
+
|
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
|
11
|
+
all copies or substantial portions of the Software.
|
|
12
|
+
|
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
<h1 align='center'>
|
|
2
|
+
ESM build of <a href='https://www.npmjs.com/package/workbox-window' target="__blank">workbox-window</a>
|
|
3
|
+
</h1>
|
|
4
|
+
<p align='center'>
|
|
5
|
+
<a href='https://www.npmjs.com/package/@vite-pwa/workbox-window' target="__blank">
|
|
6
|
+
<img src='https://img.shields.io/npm/v/@vite-pwa/workbox-window?color=33A6B8&label=' alt="NPM version">
|
|
7
|
+
</a>
|
|
8
|
+
<a href="https://www.npmjs.com/package/@vite-pwa/workbox-window" target="__blank">
|
|
9
|
+
<img alt="NPM Downloads" src="https://img.shields.io/npm/dm/@vite-pwa/workbox-window?color=476582&label=">
|
|
10
|
+
</a>
|
|
11
|
+
<a href="https://developers.google.com/web/tools/workbox/modules/workbox-window" target="__blank">
|
|
12
|
+
<img src="https://img.shields.io/static/v1?label=&message=docs%20%26%20guides&color=2e859c" alt="Docs & Guides">
|
|
13
|
+
</a>
|
|
14
|
+
<br>
|
|
15
|
+
<a href="https://github.com/vite-pwa/workbox-window-es" target="__blank">
|
|
16
|
+
<img alt="GitHub stars" src="https://img.shields.io/github/stars/vite-pwa/workbox-window-es?style=social">
|
|
17
|
+
</a>
|
|
18
|
+
</p>
|
|
19
|
+
|
|
20
|
+
<br>
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<a href="https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg">
|
|
24
|
+
<img src='https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg'/>
|
|
25
|
+
</a>
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## ๐ Features
|
|
30
|
+
|
|
31
|
+
- ๐ [**Documentation & guides**](https://vite-pwa-org.netlify.app/)
|
|
32
|
+
- ๐ **Zero-Config**: sensible built-in default configs for common use cases
|
|
33
|
+
- ๐ฉ **Extensible**: expose the full ability to customize the behavior of the plugin
|
|
34
|
+
- ๐ฆพ **Type Strong**: written in [TypeScript](https://www.typescriptlang.org/)
|
|
35
|
+
- ๐ **Offline Support**: generate service worker with offline support (via Workbox)
|
|
36
|
+
- โก **Fully tree shakable**: auto-inject Web App Manifest
|
|
37
|
+
- ๐ฌ **Prompt for new content**: built-in support for Vanilla JavaScript, Vue 3, React, Svelte, SolidJS and Preact
|
|
38
|
+
- โ๏ธ **Stale-while-revalidate**: automatic reload when new content is available
|
|
39
|
+
- โจ **Static assets handling**: configure static assets for offline support
|
|
40
|
+
- ๐ **Development Support**: debug your custom service worker logic as you develop your application
|
|
41
|
+
- ๐ ๏ธ **Versatile**: integration with meta-frameworks: [รฎles](https://github.com/ElMassimo/iles), [SvelteKit](https://github.com/sveltejs/kit), [VitePress](https://github.com/vuejs/vitepress), [Astro](https://github.com/withastro/astro), and [Nuxt 3](https://github.com/nuxt/nuxt)
|
|
42
|
+
- ๐ฅ **PWA Assets Generator**: generate all the PWA assets from a single command and a single source image
|
|
43
|
+
|
|
44
|
+
## ๐ ๏ธ @vite-pwa/workbox-window
|
|
45
|
+
|
|
46
|
+
ESM build of [workbox-window](https://www.npmjs.com/package/workbox-window).
|
|
47
|
+
|
|
48
|
+
This module's documentation can be found at https://developers.google.com/web/tools/workbox/modules/workbox-window.
|
|
49
|
+
|
|
50
|
+
`@vite-pwa/workbox-window` includes missing `installing` event in `Workbox` class.
|
|
51
|
+
|
|
52
|
+
## ๐ฆ Install
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
# pnpm
|
|
56
|
+
pnpm add @vite-pwa/workbox-window
|
|
57
|
+
|
|
58
|
+
# npm
|
|
59
|
+
npm i @vite-pwa/workbox-window
|
|
60
|
+
|
|
61
|
+
# yarn
|
|
62
|
+
yarn add @vite-pwa/workbox-window
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## ๐ฆ Usage
|
|
66
|
+
|
|
67
|
+
```js
|
|
68
|
+
// ESM
|
|
69
|
+
import { Workbox } from '@vite-pwa/workbox-window'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
// CommonJS
|
|
74
|
+
const { Workbox } = require('@vite-pwa/workbox-window')
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## ๐ License
|
|
78
|
+
|
|
79
|
+
[MIT](./LICENSE)
|
|
80
|
+
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,583 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var src_exports = {};
|
|
22
|
+
__export(src_exports, {
|
|
23
|
+
Workbox: () => Workbox,
|
|
24
|
+
WorkboxEvent: () => WorkboxEvent,
|
|
25
|
+
messageSW: () => messageSW
|
|
26
|
+
});
|
|
27
|
+
module.exports = __toCommonJS(src_exports);
|
|
28
|
+
|
|
29
|
+
// src/messageSW.ts
|
|
30
|
+
function messageSW(sw, data) {
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
const messageChannel = new MessageChannel();
|
|
33
|
+
messageChannel.port1.onmessage = (event) => {
|
|
34
|
+
resolve(event.data);
|
|
35
|
+
};
|
|
36
|
+
sw.postMessage(data, [messageChannel.port2]);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// src/utils/dontWaitFor.ts
|
|
41
|
+
function dontWaitFor(promise) {
|
|
42
|
+
void promise.then(() => {
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/utils/logger.ts
|
|
47
|
+
var logger = process.env.NODE_ENV === "production" ? null : (() => {
|
|
48
|
+
if (!("__WB_DISABLE_DEV_LOGS" in globalThis))
|
|
49
|
+
self.__WB_DISABLE_DEV_LOGS = false;
|
|
50
|
+
let inGroup = false;
|
|
51
|
+
const methodToColorMap = {
|
|
52
|
+
debug: "#7f8c8d",
|
|
53
|
+
// Gray
|
|
54
|
+
log: "#2ecc71",
|
|
55
|
+
// Green
|
|
56
|
+
warn: "#f39c12",
|
|
57
|
+
// Yellow
|
|
58
|
+
error: "#c0392b",
|
|
59
|
+
// Red
|
|
60
|
+
groupCollapsed: "#3498db",
|
|
61
|
+
// Blue
|
|
62
|
+
groupEnd: null
|
|
63
|
+
// No colored prefix on groupEnd
|
|
64
|
+
};
|
|
65
|
+
const print = function(method, args) {
|
|
66
|
+
if (self.__WB_DISABLE_DEV_LOGS)
|
|
67
|
+
return;
|
|
68
|
+
if (method === "groupCollapsed") {
|
|
69
|
+
if (typeof navigator !== "undefined" && /^((?!chrome|android).)*safari/i.test(navigator.userAgent)) {
|
|
70
|
+
console[method](...args);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const styles = [
|
|
75
|
+
`background: ${methodToColorMap[method]}`,
|
|
76
|
+
"border-radius: 0.5em",
|
|
77
|
+
"color: white",
|
|
78
|
+
"font-weight: bold",
|
|
79
|
+
"padding: 2px 0.5em"
|
|
80
|
+
];
|
|
81
|
+
const logPrefix = inGroup ? [] : ["%c@vite-pwa/workbox-window", styles.join(";")];
|
|
82
|
+
console[method](...logPrefix, ...args);
|
|
83
|
+
if (method === "groupCollapsed")
|
|
84
|
+
inGroup = true;
|
|
85
|
+
if (method === "groupEnd")
|
|
86
|
+
inGroup = false;
|
|
87
|
+
};
|
|
88
|
+
const api = {};
|
|
89
|
+
const loggerMethods = Object.keys(methodToColorMap);
|
|
90
|
+
for (const key of loggerMethods) {
|
|
91
|
+
const method = key;
|
|
92
|
+
api[method] = (...args) => {
|
|
93
|
+
print(method, args);
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return api;
|
|
97
|
+
})();
|
|
98
|
+
|
|
99
|
+
// src/utils/Deferred.ts
|
|
100
|
+
var Deferred = class {
|
|
101
|
+
promise;
|
|
102
|
+
resolve;
|
|
103
|
+
reject;
|
|
104
|
+
/**
|
|
105
|
+
* Creates a promise and exposes its resolve and reject functions as methods.
|
|
106
|
+
*/
|
|
107
|
+
constructor() {
|
|
108
|
+
this.promise = new Promise((resolve, reject) => {
|
|
109
|
+
this.resolve = resolve;
|
|
110
|
+
this.reject = reject;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
// src/utils/WorkboxEventTarget.ts
|
|
116
|
+
var WorkboxEventTarget = class {
|
|
117
|
+
_eventListenerRegistry = /* @__PURE__ */ new Map();
|
|
118
|
+
/**
|
|
119
|
+
* @param {string} type
|
|
120
|
+
* @param {Function} listener
|
|
121
|
+
* @private
|
|
122
|
+
*/
|
|
123
|
+
addEventListener(type, listener) {
|
|
124
|
+
const foo = this._getEventListenersByType(type);
|
|
125
|
+
foo.add(listener);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* @param {string} type
|
|
129
|
+
* @param {Function} listener
|
|
130
|
+
* @private
|
|
131
|
+
*/
|
|
132
|
+
removeEventListener(type, listener) {
|
|
133
|
+
this._getEventListenersByType(type).delete(listener);
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* @param {object} event
|
|
137
|
+
* @private
|
|
138
|
+
*/
|
|
139
|
+
dispatchEvent(event) {
|
|
140
|
+
event.target = this;
|
|
141
|
+
const listeners = this._getEventListenersByType(event.type);
|
|
142
|
+
for (const listener of listeners)
|
|
143
|
+
listener(event);
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Returns a Set of listeners associated with the passed event type.
|
|
147
|
+
* If no handlers have been registered, an empty Set is returned.
|
|
148
|
+
*
|
|
149
|
+
* @param {string} type The event type.
|
|
150
|
+
* @return {Set<ListenerCallback>} An array of handler functions.
|
|
151
|
+
* @private
|
|
152
|
+
*/
|
|
153
|
+
_getEventListenersByType(type) {
|
|
154
|
+
if (!this._eventListenerRegistry.has(type))
|
|
155
|
+
this._eventListenerRegistry.set(type, /* @__PURE__ */ new Set());
|
|
156
|
+
return this._eventListenerRegistry.get(type);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
// src/utils/urlsMatch.ts
|
|
161
|
+
function urlsMatch(url1, url2) {
|
|
162
|
+
const { href } = location;
|
|
163
|
+
return new URL(url1, href).href === new URL(url2, href).href;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/utils/WorkboxEvent.ts
|
|
167
|
+
var WorkboxEvent = class {
|
|
168
|
+
constructor(type, props) {
|
|
169
|
+
this.type = type;
|
|
170
|
+
Object.assign(this, props);
|
|
171
|
+
}
|
|
172
|
+
target;
|
|
173
|
+
sw;
|
|
174
|
+
originalEvent;
|
|
175
|
+
isExternal;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
// src/Workbox.ts
|
|
179
|
+
var WAITING_TIMEOUT_DURATION = 200;
|
|
180
|
+
var REGISTRATION_TIMEOUT_DURATION = 6e4;
|
|
181
|
+
var SKIP_WAITING_MESSAGE = { type: "SKIP_WAITING" };
|
|
182
|
+
var Workbox = class extends WorkboxEventTarget {
|
|
183
|
+
_scriptURL;
|
|
184
|
+
_registerOptions = {};
|
|
185
|
+
_updateFoundCount = 0;
|
|
186
|
+
// Deferreds we can resolve later.
|
|
187
|
+
_swDeferred = new Deferred();
|
|
188
|
+
_activeDeferred = new Deferred();
|
|
189
|
+
_controllingDeferred = new Deferred();
|
|
190
|
+
_registrationTime = 0;
|
|
191
|
+
_isUpdate;
|
|
192
|
+
_compatibleControllingSW;
|
|
193
|
+
_registration;
|
|
194
|
+
_sw;
|
|
195
|
+
_ownSWs = /* @__PURE__ */ new Set();
|
|
196
|
+
_externalSW;
|
|
197
|
+
_waitingTimeout;
|
|
198
|
+
/**
|
|
199
|
+
* Creates a new Workbox instance with a script URL and service worker
|
|
200
|
+
* options. The script URL and options are the same as those used when
|
|
201
|
+
* calling [navigator.serviceWorker.register(scriptURL, options)](https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerContainer/register).
|
|
202
|
+
*
|
|
203
|
+
* @param {string|TrustedScriptURL} scriptURL The service worker script
|
|
204
|
+
* associated with this instance. Using a
|
|
205
|
+
* [`TrustedScriptURL`](https://web.dev/trusted-types/) is supported.
|
|
206
|
+
* @param {object} [registerOptions] The service worker options associated
|
|
207
|
+
* with this instance.
|
|
208
|
+
*/
|
|
209
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
210
|
+
constructor(scriptURL, registerOptions = {}) {
|
|
211
|
+
super();
|
|
212
|
+
this._scriptURL = scriptURL;
|
|
213
|
+
this._registerOptions = registerOptions;
|
|
214
|
+
navigator.serviceWorker.addEventListener("message", this._onMessage);
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Registers a service worker for this instances script URL and service
|
|
218
|
+
* worker options. By default this method delays registration until after
|
|
219
|
+
* the window has loaded.
|
|
220
|
+
*
|
|
221
|
+
* @param {object} [options]
|
|
222
|
+
* @param {Function} [options.immediate] Setting this to true will
|
|
223
|
+
* register the service worker immediately, even if the window has
|
|
224
|
+
* not loaded (not recommended).
|
|
225
|
+
*/
|
|
226
|
+
async register({ immediate = false } = {}) {
|
|
227
|
+
if (process.env.NODE_ENV !== "production") {
|
|
228
|
+
if (this._registrationTime) {
|
|
229
|
+
logger.error(
|
|
230
|
+
"Cannot re-register a Workbox instance after it has been registered. Create a new instance instead."
|
|
231
|
+
);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (!immediate && document.readyState !== "complete")
|
|
236
|
+
await new Promise((resolve) => window.addEventListener("load", resolve));
|
|
237
|
+
this._isUpdate = Boolean(navigator.serviceWorker.controller);
|
|
238
|
+
this._compatibleControllingSW = this._getControllingSWIfCompatible();
|
|
239
|
+
this._registration = await this._registerScript();
|
|
240
|
+
if (this._compatibleControllingSW) {
|
|
241
|
+
this._sw = this._compatibleControllingSW;
|
|
242
|
+
this._activeDeferred.resolve(this._compatibleControllingSW);
|
|
243
|
+
this._controllingDeferred.resolve(this._compatibleControllingSW);
|
|
244
|
+
this._compatibleControllingSW.addEventListener(
|
|
245
|
+
"statechange",
|
|
246
|
+
this._onStateChange,
|
|
247
|
+
{ once: true }
|
|
248
|
+
);
|
|
249
|
+
}
|
|
250
|
+
const waitingSW = this._registration.waiting;
|
|
251
|
+
if (waitingSW && urlsMatch(waitingSW.scriptURL, this._scriptURL.toString())) {
|
|
252
|
+
this._sw = waitingSW;
|
|
253
|
+
dontWaitFor(
|
|
254
|
+
Promise.resolve().then(() => {
|
|
255
|
+
this.dispatchEvent(
|
|
256
|
+
new WorkboxEvent("waiting", {
|
|
257
|
+
sw: waitingSW,
|
|
258
|
+
wasWaitingBeforeRegister: true
|
|
259
|
+
})
|
|
260
|
+
);
|
|
261
|
+
if (process.env.NODE_ENV !== "production") {
|
|
262
|
+
logger.warn(
|
|
263
|
+
"A service worker was already waiting to activate before this script was registered..."
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
})
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
if (this._sw) {
|
|
270
|
+
this._swDeferred.resolve(this._sw);
|
|
271
|
+
this._ownSWs.add(this._sw);
|
|
272
|
+
}
|
|
273
|
+
if (process.env.NODE_ENV !== "production") {
|
|
274
|
+
logger.log(
|
|
275
|
+
"Successfully registered service worker.",
|
|
276
|
+
this._scriptURL.toString()
|
|
277
|
+
);
|
|
278
|
+
if (navigator.serviceWorker.controller) {
|
|
279
|
+
if (this._compatibleControllingSW) {
|
|
280
|
+
logger.debug(
|
|
281
|
+
"A service worker with the same script URL is already controlling this page."
|
|
282
|
+
);
|
|
283
|
+
} else {
|
|
284
|
+
logger.debug(
|
|
285
|
+
"A service worker with a different script URL is currently controlling the page. The browser is now fetching the new script now..."
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
const currentPageIsOutOfScope = () => {
|
|
290
|
+
const scopeURL = new URL(
|
|
291
|
+
this._registerOptions.scope || this._scriptURL.toString(),
|
|
292
|
+
document.baseURI
|
|
293
|
+
);
|
|
294
|
+
const scopeURLBasePath = new URL("./", scopeURL.href).pathname;
|
|
295
|
+
return !location.pathname.startsWith(scopeURLBasePath);
|
|
296
|
+
};
|
|
297
|
+
if (currentPageIsOutOfScope()) {
|
|
298
|
+
logger.warn(
|
|
299
|
+
"The current page is not in scope for the registered service worker. Was this a mistake?"
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
this._registration.addEventListener("updatefound", this._onUpdateFound);
|
|
304
|
+
navigator.serviceWorker.addEventListener(
|
|
305
|
+
"controllerchange",
|
|
306
|
+
this._onControllerChange
|
|
307
|
+
);
|
|
308
|
+
return this._registration;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Checks for updates of the registered service worker.
|
|
312
|
+
*/
|
|
313
|
+
async update() {
|
|
314
|
+
if (!this._registration) {
|
|
315
|
+
if (process.env.NODE_ENV !== "production") {
|
|
316
|
+
logger.error(
|
|
317
|
+
"Cannot update a Workbox instance without being registered. Register the Workbox instance first."
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
return;
|
|
321
|
+
}
|
|
322
|
+
await this._registration.update();
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* Resolves to the service worker registered by this instance as soon as it
|
|
326
|
+
* is active. If a service worker was already controlling at registration
|
|
327
|
+
* time then it will resolve to that if the script URLs (and optionally
|
|
328
|
+
* script versions) match, otherwise it will wait until an update is found
|
|
329
|
+
* and activates.
|
|
330
|
+
*
|
|
331
|
+
* @return {Promise<ServiceWorker>}
|
|
332
|
+
*/
|
|
333
|
+
get active() {
|
|
334
|
+
return this._activeDeferred.promise;
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Resolves to the service worker registered by this instance as soon as it
|
|
338
|
+
* is controlling the page. If a service worker was already controlling at
|
|
339
|
+
* registration time then it will resolve to that if the script URLs (and
|
|
340
|
+
* optionally script versions) match, otherwise it will wait until an update
|
|
341
|
+
* is found and starts controlling the page.
|
|
342
|
+
* Note: the first time a service worker is installed it will active but
|
|
343
|
+
* not start controlling the page unless `clients.claim()` is called in the
|
|
344
|
+
* service worker.
|
|
345
|
+
*
|
|
346
|
+
* @return {Promise<ServiceWorker>}
|
|
347
|
+
*/
|
|
348
|
+
get controlling() {
|
|
349
|
+
return this._controllingDeferred.promise;
|
|
350
|
+
}
|
|
351
|
+
/**
|
|
352
|
+
* Resolves with a reference to a service worker that matches the script URL
|
|
353
|
+
* of this instance, as soon as it's available.
|
|
354
|
+
*
|
|
355
|
+
* If, at registration time, there's already an active or waiting service
|
|
356
|
+
* worker with a matching script URL, it will be used (with the waiting
|
|
357
|
+
* service worker taking precedence over the active service worker if both
|
|
358
|
+
* match, since the waiting service worker would have been registered more
|
|
359
|
+
* recently).
|
|
360
|
+
* If there's no matching active or waiting service worker at registration
|
|
361
|
+
* time then the promise will not resolve until an update is found and starts
|
|
362
|
+
* installing, at which point the installing service worker is used.
|
|
363
|
+
*
|
|
364
|
+
* @return {Promise<ServiceWorker>}
|
|
365
|
+
*/
|
|
366
|
+
getSW() {
|
|
367
|
+
return this._sw !== void 0 ? Promise.resolve(this._sw) : this._swDeferred.promise;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Sends the passed data object to the service worker registered by this
|
|
371
|
+
* instance (via {@link workbox-window.Workbox#getSW}) and resolves
|
|
372
|
+
* with a response (if any).
|
|
373
|
+
*
|
|
374
|
+
* A response can be set in a message handler in the service worker by
|
|
375
|
+
* calling `event.ports[0].postMessage(...)`, which will resolve the promise
|
|
376
|
+
* returned by `messageSW()`. If no response is set, the promise will never
|
|
377
|
+
* resolve.
|
|
378
|
+
*
|
|
379
|
+
* @param {object} data An object to send to the service worker
|
|
380
|
+
* @return {Promise<object>}
|
|
381
|
+
*/
|
|
382
|
+
// We might be able to change the 'data' type to Record<string, unknown> in the future.
|
|
383
|
+
async messageSW(data) {
|
|
384
|
+
const sw = await this.getSW();
|
|
385
|
+
return messageSW(sw, data);
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Sends a `{type: 'SKIP_WAITING'}` message to the service worker that's
|
|
389
|
+
* currently in the `waiting` state associated with the current registration.
|
|
390
|
+
*
|
|
391
|
+
* If there is no current registration or no service worker is `waiting`,
|
|
392
|
+
* calling this will have no effect.
|
|
393
|
+
*/
|
|
394
|
+
messageSkipWaiting() {
|
|
395
|
+
if (this._registration && this._registration.waiting)
|
|
396
|
+
void messageSW(this._registration.waiting, SKIP_WAITING_MESSAGE);
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* Checks for a service worker already controlling the page and returns
|
|
400
|
+
* it if its script URL matches.
|
|
401
|
+
*
|
|
402
|
+
* @private
|
|
403
|
+
* @return {ServiceWorker|undefined}
|
|
404
|
+
*/
|
|
405
|
+
_getControllingSWIfCompatible() {
|
|
406
|
+
const controller = navigator.serviceWorker.controller;
|
|
407
|
+
if (controller && urlsMatch(controller.scriptURL, this._scriptURL.toString()))
|
|
408
|
+
return controller;
|
|
409
|
+
else
|
|
410
|
+
return void 0;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Registers a service worker for this instances script URL and register
|
|
414
|
+
* options and tracks the time registration was complete.
|
|
415
|
+
*
|
|
416
|
+
* @private
|
|
417
|
+
*/
|
|
418
|
+
async _registerScript() {
|
|
419
|
+
try {
|
|
420
|
+
const reg = await navigator.serviceWorker.register(
|
|
421
|
+
this._scriptURL,
|
|
422
|
+
this._registerOptions
|
|
423
|
+
);
|
|
424
|
+
this._registrationTime = performance.now();
|
|
425
|
+
return reg;
|
|
426
|
+
} catch (error) {
|
|
427
|
+
if (process.env.NODE_ENV !== "production")
|
|
428
|
+
logger.error(error);
|
|
429
|
+
throw error;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* @private
|
|
434
|
+
*/
|
|
435
|
+
_onUpdateFound = (originalEvent) => {
|
|
436
|
+
const registration = this._registration;
|
|
437
|
+
const installingSW = registration.installing;
|
|
438
|
+
const updateLikelyTriggeredExternally = !!(this._updateFoundCount > 0 || !urlsMatch(installingSW.scriptURL, this._scriptURL.toString()) || performance.now() > this._registrationTime + REGISTRATION_TIMEOUT_DURATION);
|
|
439
|
+
if (updateLikelyTriggeredExternally) {
|
|
440
|
+
this._externalSW = installingSW;
|
|
441
|
+
registration.removeEventListener("updatefound", this._onUpdateFound);
|
|
442
|
+
} else {
|
|
443
|
+
this._sw = installingSW;
|
|
444
|
+
this._ownSWs.add(installingSW);
|
|
445
|
+
this._swDeferred.resolve(installingSW);
|
|
446
|
+
if (process.env.NODE_ENV !== "production") {
|
|
447
|
+
if (navigator.serviceWorker.controller)
|
|
448
|
+
logger.log("Updated service worker found. Installing now...");
|
|
449
|
+
else
|
|
450
|
+
logger.log("Service worker is installing...");
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
this.dispatchEvent(
|
|
454
|
+
new WorkboxEvent("installing", {
|
|
455
|
+
sw: installingSW,
|
|
456
|
+
originalEvent,
|
|
457
|
+
isExternal: updateLikelyTriggeredExternally,
|
|
458
|
+
isUpdate: this._isUpdate
|
|
459
|
+
})
|
|
460
|
+
);
|
|
461
|
+
++this._updateFoundCount;
|
|
462
|
+
installingSW.addEventListener("statechange", this._onStateChange);
|
|
463
|
+
};
|
|
464
|
+
/**
|
|
465
|
+
* @private
|
|
466
|
+
* @param {Event} originalEvent
|
|
467
|
+
*/
|
|
468
|
+
_onStateChange = (originalEvent) => {
|
|
469
|
+
const registration = this._registration;
|
|
470
|
+
const sw = originalEvent.target;
|
|
471
|
+
const { state } = sw;
|
|
472
|
+
const isExternal = sw === this._externalSW;
|
|
473
|
+
const eventProps = {
|
|
474
|
+
sw,
|
|
475
|
+
isExternal,
|
|
476
|
+
originalEvent
|
|
477
|
+
};
|
|
478
|
+
if (!isExternal && this._isUpdate)
|
|
479
|
+
eventProps.isUpdate = true;
|
|
480
|
+
this.dispatchEvent(
|
|
481
|
+
new WorkboxEvent(state, eventProps)
|
|
482
|
+
);
|
|
483
|
+
if (state === "installed") {
|
|
484
|
+
this._waitingTimeout = self.setTimeout(() => {
|
|
485
|
+
if (state === "installed" && registration.waiting === sw) {
|
|
486
|
+
this.dispatchEvent(new WorkboxEvent("waiting", eventProps));
|
|
487
|
+
if (process.env.NODE_ENV !== "production") {
|
|
488
|
+
if (isExternal) {
|
|
489
|
+
logger.warn(
|
|
490
|
+
"An external service worker has installed but is waiting for this client to close before activating..."
|
|
491
|
+
);
|
|
492
|
+
} else {
|
|
493
|
+
logger.warn(
|
|
494
|
+
"The service worker has installed but is waiting for existing clients to close before activating..."
|
|
495
|
+
);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}, WAITING_TIMEOUT_DURATION);
|
|
500
|
+
} else if (state === "activating") {
|
|
501
|
+
clearTimeout(this._waitingTimeout);
|
|
502
|
+
if (!isExternal)
|
|
503
|
+
this._activeDeferred.resolve(sw);
|
|
504
|
+
}
|
|
505
|
+
if (process.env.NODE_ENV !== "production") {
|
|
506
|
+
switch (state) {
|
|
507
|
+
case "installed":
|
|
508
|
+
if (isExternal) {
|
|
509
|
+
logger.warn(
|
|
510
|
+
"An external service worker has installed. You may want to suggest users reload this page."
|
|
511
|
+
);
|
|
512
|
+
} else {
|
|
513
|
+
logger.log("Registered service worker installed.");
|
|
514
|
+
}
|
|
515
|
+
break;
|
|
516
|
+
case "activated":
|
|
517
|
+
if (isExternal) {
|
|
518
|
+
logger.warn("An external service worker has activated.");
|
|
519
|
+
} else {
|
|
520
|
+
logger.log("Registered service worker activated.");
|
|
521
|
+
if (sw !== navigator.serviceWorker.controller) {
|
|
522
|
+
logger.warn(
|
|
523
|
+
"The registered service worker is active but not yet controlling the page. Reload or run `clients.claim()` in the service worker."
|
|
524
|
+
);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
break;
|
|
528
|
+
case "redundant":
|
|
529
|
+
if (sw === this._compatibleControllingSW)
|
|
530
|
+
logger.log("Previously controlling service worker now redundant!");
|
|
531
|
+
else if (!isExternal)
|
|
532
|
+
logger.log("Registered service worker now redundant!");
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
/**
|
|
538
|
+
* @private
|
|
539
|
+
* @param {Event} originalEvent
|
|
540
|
+
*/
|
|
541
|
+
_onControllerChange = (originalEvent) => {
|
|
542
|
+
const sw = this._sw;
|
|
543
|
+
const isExternal = sw !== navigator.serviceWorker.controller;
|
|
544
|
+
this.dispatchEvent(
|
|
545
|
+
new WorkboxEvent("controlling", {
|
|
546
|
+
isExternal,
|
|
547
|
+
originalEvent,
|
|
548
|
+
sw,
|
|
549
|
+
isUpdate: this._isUpdate
|
|
550
|
+
})
|
|
551
|
+
);
|
|
552
|
+
if (!isExternal) {
|
|
553
|
+
if (process.env.NODE_ENV !== "production")
|
|
554
|
+
logger.log("Registered service worker now controlling this page.");
|
|
555
|
+
this._controllingDeferred.resolve(sw);
|
|
556
|
+
}
|
|
557
|
+
};
|
|
558
|
+
/**
|
|
559
|
+
* @private
|
|
560
|
+
* @param {Event} originalEvent
|
|
561
|
+
*/
|
|
562
|
+
_onMessage = async (originalEvent) => {
|
|
563
|
+
const { data, ports, source } = originalEvent;
|
|
564
|
+
await this.getSW();
|
|
565
|
+
if (this._ownSWs.has(source)) {
|
|
566
|
+
this.dispatchEvent(
|
|
567
|
+
new WorkboxEvent("message", {
|
|
568
|
+
// Can't change type 'any' of data.
|
|
569
|
+
data,
|
|
570
|
+
originalEvent,
|
|
571
|
+
ports,
|
|
572
|
+
sw: source
|
|
573
|
+
})
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
};
|
|
577
|
+
};
|
|
578
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
579
|
+
0 && (module.exports = {
|
|
580
|
+
Workbox,
|
|
581
|
+
WorkboxEvent,
|
|
582
|
+
messageSW
|
|
583
|
+
});
|