@budarin/pluggable-serviceworker 1.5.4 → 1.5.6
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/README.md +37 -19
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -0
- package/dist/client/register.d.ts +1 -0
- package/dist/client/register.js +22 -0
- package/dist/client/updateWhenControlled.d.ts +3 -0
- package/dist/client/updateWhenControlled.js +25 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +17 -11
- package/dist/plugins/reloadUncontrolledClients.d.ts +2 -0
- package/dist/plugins/reloadUncontrolledClients.js +15 -0
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -220,7 +220,6 @@ initServiceWorker([cachePlugin]);
|
|
|
220
220
|
|
|
221
221
|
// С onError - ошибки будут обработаны
|
|
222
222
|
initServiceWorker([cachePlugin], {
|
|
223
|
-
logger: console,
|
|
224
223
|
onError: (error, event, errorType) => {
|
|
225
224
|
console.error('Service Worker error:', error, errorType);
|
|
226
225
|
},
|
|
@@ -245,33 +244,45 @@ const options = {
|
|
|
245
244
|
logger.info(`Ошибка типа "${errorType}":`, error);
|
|
246
245
|
|
|
247
246
|
switch (errorType) {
|
|
247
|
+
// Ошибки в плагинах при обработке соответствующего события
|
|
248
|
+
case ServiceWorkerErrorType.INSTALL_ERROR:
|
|
249
|
+
case ServiceWorkerErrorType.ACTIVATE_ERROR:
|
|
250
|
+
case ServiceWorkerErrorType.FETCH_ERROR:
|
|
251
|
+
case ServiceWorkerErrorType.MESSAGE_ERROR:
|
|
252
|
+
case ServiceWorkerErrorType.SYNC_ERROR:
|
|
253
|
+
case ServiceWorkerErrorType.PERIODICSYNC_ERROR:
|
|
254
|
+
case ServiceWorkerErrorType.PUSH_ERROR:
|
|
255
|
+
logger.error(`Plugin error (${errorType}):`, error);
|
|
256
|
+
|
|
257
|
+
// если нужно - мы можем получить конкретную точку в коде того плагина в котором произошла ошибка
|
|
258
|
+
if (error instanceof Error && error.stack) {
|
|
259
|
+
logger.error('Plugin error Stack:', error.stack);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
break;
|
|
263
|
+
|
|
264
|
+
// Глобальные JavaScript ошибки
|
|
248
265
|
case ServiceWorkerErrorType.ERROR:
|
|
249
|
-
// JavaScript ошибки
|
|
250
266
|
logger.error('JavaScript error:', error);
|
|
251
267
|
break;
|
|
252
268
|
|
|
253
|
-
|
|
254
|
-
|
|
269
|
+
// Глобальное событие messageerror (например, ошибка structured clone)
|
|
270
|
+
case ServiceWorkerErrorType.MESSAGE_ERROR_HANDLER:
|
|
255
271
|
logger.error('Message error:', error);
|
|
256
272
|
break;
|
|
257
273
|
|
|
274
|
+
// Необработанные Promise rejection
|
|
258
275
|
case ServiceWorkerErrorType.UNHANDLED_REJECTION:
|
|
259
|
-
// Необработанные Promise rejection
|
|
260
276
|
logger.error('Unhandled promise rejection:', error);
|
|
261
277
|
break;
|
|
262
278
|
|
|
279
|
+
// Обработанные Promise rejection
|
|
263
280
|
case ServiceWorkerErrorType.REJECTION_HANDLED:
|
|
264
|
-
// Обработанные Promise rejection
|
|
265
281
|
logger.info('Promise rejection handled:', error);
|
|
266
282
|
break;
|
|
267
283
|
|
|
268
|
-
|
|
269
|
-
// Ошибки в плагинах
|
|
270
|
-
logger.error('Plugin error:', error);
|
|
271
|
-
break;
|
|
272
|
-
|
|
284
|
+
// Неизвестные типы ошибок
|
|
273
285
|
default:
|
|
274
|
-
// Неизвестные типы ошибок
|
|
275
286
|
logger.error('Unknown error type:', error);
|
|
276
287
|
|
|
277
288
|
// можно даже так - отправка ошибки в аналитику
|
|
@@ -606,16 +617,23 @@ activateOnNextVisitServiceWorker({
|
|
|
606
617
|
});
|
|
607
618
|
```
|
|
608
619
|
|
|
609
|
-
На странице регистрируйте этот файл: `navigator.serviceWorker.register('/sw.js')` (или путь, по которому сборка отдаёт ваш sw.js).
|
|
610
|
-
|
|
611
620
|
### Публикуемые утилиты
|
|
612
621
|
|
|
613
|
-
|
|
622
|
+
| Название | Где использовать | Описание |
|
|
623
|
+
| -------------------------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
624
|
+
| `registerServiceWorker(scriptURL, options?)` | клиент | Регистрация SW; при первом заходе при необходимости один автоматический reload (обход [бага браузера](https://issues.chromium.org/issues/482903583)). Импорт: `@budarin/pluggable-serviceworker/client`. |
|
|
625
|
+
| `normalizeUrl(url)` | SW | Нормализует URL (относительный → абсолютный по origin SW) для сравнения. Импорт: `@budarin/pluggable-serviceworker/utils`. |
|
|
626
|
+
| `notifyClients(messageType)` | SW | Отправляет сообщение `{ type: messageType }` всем окнам-клиентам. Импорт: `@budarin/pluggable-serviceworker/utils`. |
|
|
627
|
+
|
|
628
|
+
На странице используйте **клиентский API** библиотеки, чтобы SW корректно взял контроль уже на первой загрузке (обход [бага браузера](https://issues.chromium.org/issues/482903583)):
|
|
629
|
+
|
|
630
|
+
```typescript
|
|
631
|
+
import { registerServiceWorker } from '@budarin/pluggable-serviceworker/client';
|
|
632
|
+
|
|
633
|
+
const reg = await registerServiceWorker('/sw.js');
|
|
634
|
+
```
|
|
614
635
|
|
|
615
|
-
|
|
616
|
-
| ---------------------------- | ------------------------------------------------------------------------ |
|
|
617
|
-
| `normalizeUrl(url)` | Нормализует URL (относительный → абсолютный по origin SW) для сравнения. |
|
|
618
|
-
| `notifyClients(messageType)` | Отправляет сообщение `{ type: messageType }` всем окнам-клиентам (SW). |
|
|
636
|
+
Без этого API на первом визите страница может остаться без контроллера до перезагрузки.
|
|
619
637
|
|
|
620
638
|
## Разработка пакета плагина
|
|
621
639
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { registerServiceWorker } from './register.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { registerServiceWorker } from './register.js';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function registerServiceWorker(scriptURL: string, options?: RegistrationOptions): Promise<ServiceWorkerRegistration | undefined>;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export async function registerServiceWorker(scriptURL, options) {
|
|
2
|
+
if (!('serviceWorker' in navigator)) {
|
|
3
|
+
return undefined;
|
|
4
|
+
}
|
|
5
|
+
const reg = await navigator.serviceWorker.register(scriptURL, options);
|
|
6
|
+
await navigator.serviceWorker.ready;
|
|
7
|
+
if (navigator.serviceWorker.controller != null) {
|
|
8
|
+
return reg;
|
|
9
|
+
}
|
|
10
|
+
const key = `pluggable-sw-reload-${reg.scope}`;
|
|
11
|
+
try {
|
|
12
|
+
if (sessionStorage.getItem(key) != null) {
|
|
13
|
+
return reg;
|
|
14
|
+
}
|
|
15
|
+
sessionStorage.setItem(key, '1');
|
|
16
|
+
location.reload();
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return reg;
|
|
20
|
+
}
|
|
21
|
+
return reg;
|
|
22
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export function updateWhenControlled(registration, options = {}) {
|
|
2
|
+
const { timeoutMs = 5000 } = options;
|
|
3
|
+
if (navigator.serviceWorker.controller != null) {
|
|
4
|
+
registration.update();
|
|
5
|
+
return Promise.resolve();
|
|
6
|
+
}
|
|
7
|
+
return new Promise((resolve) => {
|
|
8
|
+
const doUpdate = () => {
|
|
9
|
+
registration.update();
|
|
10
|
+
cleanup();
|
|
11
|
+
resolve();
|
|
12
|
+
};
|
|
13
|
+
const cleanup = () => {
|
|
14
|
+
navigator.serviceWorker.removeEventListener('controllerchange', onControllerChange);
|
|
15
|
+
clearTimeout(timer);
|
|
16
|
+
};
|
|
17
|
+
const onControllerChange = () => {
|
|
18
|
+
if (navigator.serviceWorker.controller != null) {
|
|
19
|
+
doUpdate();
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
navigator.serviceWorker.addEventListener('controllerchange', onControllerChange);
|
|
23
|
+
const timer = setTimeout(doUpdate, timeoutMs);
|
|
24
|
+
});
|
|
25
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
export declare enum ServiceWorkerErrorType {
|
|
2
2
|
ERROR = "error",
|
|
3
|
-
|
|
3
|
+
INSTALL_ERROR = "install_error",
|
|
4
|
+
ACTIVATE_ERROR = "activate_error",
|
|
5
|
+
FETCH_ERROR = "fetch_error",
|
|
4
6
|
MESSAGE_ERROR = "messageerror",
|
|
7
|
+
MESSAGE_ERROR_HANDLER = "message_error_handler",
|
|
8
|
+
SYNC_ERROR = "sync_error",
|
|
9
|
+
PERIODICSYNC_ERROR = "periodicsync_error",
|
|
10
|
+
PUSH_ERROR = "push_error",
|
|
5
11
|
REJECTION_HANDLED = "rejectionhandled",
|
|
6
12
|
UNHANDLED_REJECTION = "unhandledrejection"
|
|
7
13
|
}
|
package/dist/index.js
CHANGED
|
@@ -2,8 +2,14 @@ import { SW_EVENT_PUSH, SW_EVENT_SYNC, SW_EVENT_ERROR, SW_EVENT_FETCH, SW_EVENT_
|
|
|
2
2
|
export var ServiceWorkerErrorType;
|
|
3
3
|
(function (ServiceWorkerErrorType) {
|
|
4
4
|
ServiceWorkerErrorType["ERROR"] = "error";
|
|
5
|
-
ServiceWorkerErrorType["
|
|
5
|
+
ServiceWorkerErrorType["INSTALL_ERROR"] = "install_error";
|
|
6
|
+
ServiceWorkerErrorType["ACTIVATE_ERROR"] = "activate_error";
|
|
7
|
+
ServiceWorkerErrorType["FETCH_ERROR"] = "fetch_error";
|
|
6
8
|
ServiceWorkerErrorType["MESSAGE_ERROR"] = "messageerror";
|
|
9
|
+
ServiceWorkerErrorType["MESSAGE_ERROR_HANDLER"] = "message_error_handler";
|
|
10
|
+
ServiceWorkerErrorType["SYNC_ERROR"] = "sync_error";
|
|
11
|
+
ServiceWorkerErrorType["PERIODICSYNC_ERROR"] = "periodicsync_error";
|
|
12
|
+
ServiceWorkerErrorType["PUSH_ERROR"] = "push_error";
|
|
7
13
|
ServiceWorkerErrorType["REJECTION_HANDLED"] = "rejectionhandled";
|
|
8
14
|
ServiceWorkerErrorType["UNHANDLED_REJECTION"] = "unhandledrejection";
|
|
9
15
|
})(ServiceWorkerErrorType || (ServiceWorkerErrorType = {}));
|
|
@@ -48,12 +54,12 @@ export function createEventHandlers(plugins, options) {
|
|
|
48
54
|
install: (event) => {
|
|
49
55
|
event.waitUntil(Promise.all(handlers.install.map((handler) => Promise.resolve()
|
|
50
56
|
.then(() => handler(event, logger))
|
|
51
|
-
.catch((error) => options.onError?.(error, event, ServiceWorkerErrorType.
|
|
57
|
+
.catch((error) => options.onError?.(error, event, ServiceWorkerErrorType.INSTALL_ERROR)))));
|
|
52
58
|
},
|
|
53
59
|
activate: (event) => {
|
|
54
60
|
event.waitUntil(Promise.all(handlers.activate.map((handler) => Promise.resolve()
|
|
55
61
|
.then(() => handler(event, logger))
|
|
56
|
-
.catch((error) => options.onError?.(error, event, ServiceWorkerErrorType.
|
|
62
|
+
.catch((error) => options.onError?.(error, event, ServiceWorkerErrorType.ACTIVATE_ERROR)))));
|
|
57
63
|
},
|
|
58
64
|
fetch: (event) => {
|
|
59
65
|
event.respondWith((async () => {
|
|
@@ -65,14 +71,14 @@ export function createEventHandlers(plugins, options) {
|
|
|
65
71
|
}
|
|
66
72
|
}
|
|
67
73
|
catch (error) {
|
|
68
|
-
options.onError?.(error, event, ServiceWorkerErrorType.
|
|
74
|
+
options.onError?.(error, event, ServiceWorkerErrorType.FETCH_ERROR);
|
|
69
75
|
}
|
|
70
76
|
}
|
|
71
77
|
try {
|
|
72
78
|
return await fetch(event.request);
|
|
73
79
|
}
|
|
74
80
|
catch (error) {
|
|
75
|
-
options.onError?.(error, event, ServiceWorkerErrorType.
|
|
81
|
+
options.onError?.(error, event, ServiceWorkerErrorType.FETCH_ERROR);
|
|
76
82
|
return new Response('', {
|
|
77
83
|
status: 503,
|
|
78
84
|
statusText: 'Service Unavailable',
|
|
@@ -86,19 +92,19 @@ export function createEventHandlers(plugins, options) {
|
|
|
86
92
|
handler(event, logger);
|
|
87
93
|
}
|
|
88
94
|
catch (error) {
|
|
89
|
-
options.onError?.(error, event, ServiceWorkerErrorType.
|
|
95
|
+
options.onError?.(error, event, ServiceWorkerErrorType.MESSAGE_ERROR);
|
|
90
96
|
}
|
|
91
97
|
});
|
|
92
98
|
},
|
|
93
99
|
sync: (event) => {
|
|
94
100
|
event.waitUntil(Promise.all(handlers.sync.map((handler) => Promise.resolve()
|
|
95
101
|
.then(() => handler(event, logger))
|
|
96
|
-
.catch((error) => options.onError?.(error, event, ServiceWorkerErrorType.
|
|
102
|
+
.catch((error) => options.onError?.(error, event, ServiceWorkerErrorType.SYNC_ERROR)))));
|
|
97
103
|
},
|
|
98
104
|
periodicsync: (event) => {
|
|
99
105
|
event.waitUntil(Promise.all(handlers.periodicsync.map((handler) => Promise.resolve()
|
|
100
106
|
.then(() => handler(event, logger))
|
|
101
|
-
.catch((error) => options.onError?.(error, event, ServiceWorkerErrorType.
|
|
107
|
+
.catch((error) => options.onError?.(error, event, ServiceWorkerErrorType.PERIODICSYNC_ERROR)))));
|
|
102
108
|
},
|
|
103
109
|
push: (event) => {
|
|
104
110
|
event.waitUntil((async () => {
|
|
@@ -109,7 +115,7 @@ export function createEventHandlers(plugins, options) {
|
|
|
109
115
|
returns.push(result);
|
|
110
116
|
}
|
|
111
117
|
catch (error) {
|
|
112
|
-
options.onError?.(error, event, ServiceWorkerErrorType.
|
|
118
|
+
options.onError?.(error, event, ServiceWorkerErrorType.PUSH_ERROR);
|
|
113
119
|
returns.push(undefined);
|
|
114
120
|
}
|
|
115
121
|
}
|
|
@@ -161,7 +167,7 @@ export function createEventHandlers(plugins, options) {
|
|
|
161
167
|
await self.registration.showNotification(title, opts);
|
|
162
168
|
}
|
|
163
169
|
catch (error) {
|
|
164
|
-
options.onError?.(error, event, ServiceWorkerErrorType.
|
|
170
|
+
options.onError?.(error, event, ServiceWorkerErrorType.PUSH_ERROR);
|
|
165
171
|
}
|
|
166
172
|
})());
|
|
167
173
|
},
|
|
@@ -175,7 +181,7 @@ export function createEventHandlers(plugins, options) {
|
|
|
175
181
|
},
|
|
176
182
|
messageerror: (event) => {
|
|
177
183
|
try {
|
|
178
|
-
options.onError?.(event.data, event, ServiceWorkerErrorType.
|
|
184
|
+
options.onError?.(event.data, event, ServiceWorkerErrorType.MESSAGE_ERROR_HANDLER);
|
|
179
185
|
}
|
|
180
186
|
catch (error) {
|
|
181
187
|
logger.error('Error in messageerror handler:', error);
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export function reloadUncontrolledClients() {
|
|
2
|
+
return {
|
|
3
|
+
name: 'reloadUncontrolledClients',
|
|
4
|
+
activate: async () => {
|
|
5
|
+
const controlled = await self.clients.matchAll({ type: 'window' });
|
|
6
|
+
if (controlled.length > 0)
|
|
7
|
+
return;
|
|
8
|
+
const all = await self.clients.matchAll({
|
|
9
|
+
type: 'window',
|
|
10
|
+
includeUncontrolled: true,
|
|
11
|
+
});
|
|
12
|
+
await Promise.all(all.map((client) => client.navigate(client.url)));
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@budarin/pluggable-serviceworker",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.6",
|
|
4
4
|
"description": "Extensible via plugins service worker",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"serviceworker",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
],
|
|
22
22
|
"exports": {
|
|
23
23
|
".": "./dist/index.js",
|
|
24
|
+
"./client": "./dist/client/index.js",
|
|
24
25
|
"./plugins": "./dist/plugins/index.js",
|
|
25
26
|
"./presets": "./dist/presets/index.js",
|
|
26
27
|
"./sw": "./dist/sw/index.js",
|
|
@@ -44,6 +45,7 @@
|
|
|
44
45
|
"test:watch": "vitest",
|
|
45
46
|
"clear-caches": "sh scripts/clear-caches.sh",
|
|
46
47
|
"pnpm-install": "pnpm install",
|
|
48
|
+
"sync-workspace": "sh scripts/sync-workspace.sh",
|
|
47
49
|
"upgrade-deps": "sh scripts/upgrade-deps.sh",
|
|
48
50
|
"publish-package": "pnpm build && pnpm publish --access=public"
|
|
49
51
|
}
|