@budarin/pluggable-serviceworker 1.5.3 → 1.5.5
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 +46 -59
- package/dist/index.d.ts +7 -1
- package/dist/index.js +17 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -77,13 +77,13 @@ pnpm add @budarin/pluggable-serviceworker
|
|
|
77
77
|
### Базовое использование
|
|
78
78
|
|
|
79
79
|
```typescript
|
|
80
|
-
//
|
|
80
|
+
// precacheAndServePlugin.js
|
|
81
81
|
import {
|
|
82
82
|
type Plugin,
|
|
83
83
|
initServiceWorker,
|
|
84
84
|
} from '@budarin/pluggable-serviceworker';
|
|
85
85
|
|
|
86
|
-
function precacheAndServePlugin(config: {
|
|
86
|
+
export function precacheAndServePlugin(config: {
|
|
87
87
|
cacheName: string;
|
|
88
88
|
assets: string[];
|
|
89
89
|
}): Plugin {
|
|
@@ -115,6 +115,11 @@ function precacheAndServePlugin(config: {
|
|
|
115
115
|
},
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
```typescript
|
|
121
|
+
// sw.ts
|
|
122
|
+
import { precacheAndServePlugin } from './precacheAndServePlugin';
|
|
118
123
|
|
|
119
124
|
initServiceWorker([
|
|
120
125
|
precacheAndServePlugin({
|
|
@@ -124,22 +129,11 @@ initServiceWorker([
|
|
|
124
129
|
]);
|
|
125
130
|
```
|
|
126
131
|
|
|
127
|
-
**Важно:**
|
|
128
|
-
|
|
129
|
-
- для `fetch` плагину не нужно самому вызывать `fetch(event.request)` — если все плагины вернули `undefined`, фреймворк сам идёт в сеть.
|
|
130
|
-
- конфиг плагина задаётся по месту вызова фабрики; в `options` только `logger?` и `onError?`.
|
|
131
|
-
|
|
132
|
-
### Фабрика плагинов
|
|
133
|
-
|
|
134
|
-
**Плагин** — это объект с полем `name` и опциональными обработчиками (`install`, `fetch`, `activate` и т.д.). В массив `initServiceWorker(plugins, options)` передаются именно такие объекты.
|
|
135
|
-
|
|
136
|
-
**Фабрика плагина** — функция, которая принимает конфиг и возвращает плагин (объект). Например: `precache(config)`, `serveFromCache(config)` или собственная `precacheAndServePlugin(config)` из примера выше. Конфиг задаётся по месту вызова фабрики; в общий `options` попадают только `logger?` и `onError?`.
|
|
137
|
-
|
|
138
132
|
## Демо
|
|
139
133
|
|
|
140
134
|
В папке [demo/](demo/) — приложение **React + Vite** с пресетом **offlineFirst** и типовым сервис-воркером **activateOnSignal**. Запуск из корня: `pnpm start`. Подробности — в [demo/README.md](demo/README.md).
|
|
141
135
|
|
|
142
|
-
##
|
|
136
|
+
## initServiceWorker(plugins, options)
|
|
143
137
|
|
|
144
138
|
`initServiceWorker` — точка входа: регистрирует обработчики событий Service Worker (`install`, `activate`, `fetch`, …) и прогоняет их через список плагинов.
|
|
145
139
|
|
|
@@ -226,7 +220,6 @@ initServiceWorker([cachePlugin]);
|
|
|
226
220
|
|
|
227
221
|
// С onError - ошибки будут обработаны
|
|
228
222
|
initServiceWorker([cachePlugin], {
|
|
229
|
-
logger: console,
|
|
230
223
|
onError: (error, event, errorType) => {
|
|
231
224
|
console.error('Service Worker error:', error, errorType);
|
|
232
225
|
},
|
|
@@ -251,33 +244,45 @@ const options = {
|
|
|
251
244
|
logger.info(`Ошибка типа "${errorType}":`, error);
|
|
252
245
|
|
|
253
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 ошибки
|
|
254
265
|
case ServiceWorkerErrorType.ERROR:
|
|
255
|
-
// JavaScript ошибки
|
|
256
266
|
logger.error('JavaScript error:', error);
|
|
257
267
|
break;
|
|
258
268
|
|
|
259
|
-
|
|
260
|
-
|
|
269
|
+
// Глобальное событие messageerror (например, ошибка structured clone)
|
|
270
|
+
case ServiceWorkerErrorType.MESSAGE_ERROR_HANDLER:
|
|
261
271
|
logger.error('Message error:', error);
|
|
262
272
|
break;
|
|
263
273
|
|
|
274
|
+
// Необработанные Promise rejection
|
|
264
275
|
case ServiceWorkerErrorType.UNHANDLED_REJECTION:
|
|
265
|
-
// Необработанные Promise rejection
|
|
266
276
|
logger.error('Unhandled promise rejection:', error);
|
|
267
277
|
break;
|
|
268
278
|
|
|
279
|
+
// Обработанные Promise rejection
|
|
269
280
|
case ServiceWorkerErrorType.REJECTION_HANDLED:
|
|
270
|
-
// Обработанные Promise rejection
|
|
271
281
|
logger.info('Promise rejection handled:', error);
|
|
272
282
|
break;
|
|
273
283
|
|
|
274
|
-
|
|
275
|
-
// Ошибки в плагинах
|
|
276
|
-
logger.error('Plugin error:', error);
|
|
277
|
-
break;
|
|
278
|
-
|
|
284
|
+
// Неизвестные типы ошибок
|
|
279
285
|
default:
|
|
280
|
-
// Неизвестные типы ошибок
|
|
281
286
|
logger.error('Unknown error type:', error);
|
|
282
287
|
|
|
283
288
|
// можно даже так - отправка ошибки в аналитику
|
|
@@ -304,7 +309,13 @@ initServiceWorker(
|
|
|
304
309
|
);
|
|
305
310
|
```
|
|
306
311
|
|
|
307
|
-
##
|
|
312
|
+
## Плагины
|
|
313
|
+
|
|
314
|
+
**Плагин** — это объект с полем `name` и опциональными обработчиками (`install`, `fetch`, `activate` и т.д.). В массив `initServiceWorker(plugins, options)` передаются именно такие объекты.
|
|
315
|
+
|
|
316
|
+
**Фабрика плагина** — функция, которая принимает конфиг и возвращает плагин (объект). Например: `precache(config)`, `serveFromCache(config)` или собственная `precacheAndServePlugin(config)` из примера выше. Конфиг задаётся по месту вызова фабрики; в общий `options` попадают только `logger?` и `onError?`.
|
|
317
|
+
|
|
318
|
+
### 🔌 Интерфейс плагина
|
|
308
319
|
|
|
309
320
|
Плагин — объект, реализующий интерфейс `ServiceWorkerPlugin`. Во все обработчики вторым аргументом передаётся **logger** (всегда определён: из `options` или `console`). Специфичный для плагина конфиг задаётся при вызове **фабрики** плагина; в `options` только `logger?` и `onError?`. Параметр типа `_C` (например `PluginContext`) используется для типизации; по умолчанию контекст содержит только `logger`.
|
|
310
321
|
|
|
@@ -488,7 +499,6 @@ function authPlugin(config: { protectedPaths: string[] }): Plugin {
|
|
|
488
499
|
### Примитивы (плагины)
|
|
489
500
|
|
|
490
501
|
Один примитив — одна операция. Импорт: `@budarin/pluggable-serviceworker/plugins`.
|
|
491
|
-
|
|
492
502
|
Примитивы с конфигом — **фабрики плагинов** (см. раздел «Фабрика плагинов»): конфиг передаётся при вызове по месту использования; в `options` в `initServiceWorker` попадают только `logger?` и `onError?`. Примитивы без конфига (`skipWaiting`, `claim`, …) — готовые объекты плагинов.
|
|
493
503
|
|
|
494
504
|
| Название | Событие | Описание |
|
|
@@ -512,9 +522,7 @@ function authPlugin(config: { protectedPaths: string[] }): Plugin {
|
|
|
512
522
|
|
|
513
523
|
Обработчики одного типа (`install`, `activate` и т.д.) у разных плагинов выполняются **параллельно**. Если нужна строгая последовательность (например «сначала claim, потом перезагрузка клиентов»), соберите один плагин, который по очереди вызывает логику примитивов — для гарантии порядка.
|
|
514
524
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
Плагин **claimAndReloadClients** вызывает существующие примитивы **claim** и **reloadClients** по очереди:
|
|
525
|
+
Пример: claimAndReloadClients как композиция двух примитивов. Плагин вызывает существующие примитивы **claim** и **reloadClients** по очереди:
|
|
518
526
|
|
|
519
527
|
```typescript
|
|
520
528
|
import { claim } from '@budarin/pluggable-serviceworker/plugins';
|
|
@@ -537,43 +545,24 @@ activate: (event, logger) =>
|
|
|
537
545
|
// postsSwrPlugin.ts
|
|
538
546
|
import type { Plugin } from '@budarin/pluggable-serviceworker';
|
|
539
547
|
|
|
540
|
-
import {
|
|
541
|
-
precache,
|
|
542
|
-
serveFromCache,
|
|
543
|
-
} from '@budarin/pluggable-serviceworker/plugins';
|
|
544
|
-
import { initServiceWorker } from '@budarin/pluggable-serviceworker';
|
|
548
|
+
import { staleWhileRevalidate } from '@budarin/pluggable-serviceworker/plugins';
|
|
545
549
|
|
|
546
550
|
function postsSwrPlugin(config: {
|
|
547
551
|
cacheName: string;
|
|
548
552
|
pathPattern?: RegExp;
|
|
549
553
|
}): Plugin {
|
|
550
554
|
const { cacheName, pathPattern = /\/api\/posts(\/|$)/ } = config;
|
|
555
|
+
const swrPlugin = staleWhileRevalidate({ cacheName });
|
|
551
556
|
|
|
552
557
|
return {
|
|
553
558
|
name: 'postsSwr',
|
|
554
559
|
order: 0,
|
|
555
560
|
|
|
556
|
-
fetch: async (event) => {
|
|
561
|
+
fetch: async (event, logger) => {
|
|
557
562
|
if (!pathPattern.test(new URL(event.request.url).pathname)) {
|
|
558
563
|
return undefined;
|
|
559
564
|
}
|
|
560
|
-
|
|
561
|
-
const cache = await caches.open(cacheName);
|
|
562
|
-
const cached = await cache.match(event.request);
|
|
563
|
-
const revalidate = fetch(event.request).then(async (response) => {
|
|
564
|
-
if (response.ok) {
|
|
565
|
-
await cache.put(event.request, response.clone());
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
return response;
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
if (cached) {
|
|
572
|
-
void revalidate;
|
|
573
|
-
return cached;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
return revalidate;
|
|
565
|
+
return swrPlugin.fetch!(event, logger);
|
|
577
566
|
},
|
|
578
567
|
};
|
|
579
568
|
}
|
|
@@ -619,14 +608,12 @@ initServiceWorker(
|
|
|
619
608
|
|
|
620
609
|
```typescript
|
|
621
610
|
// sw.js — точка входа вашего сервис-воркера
|
|
622
|
-
import { customLogger } from './customLogger';
|
|
623
611
|
import { activateOnNextVisitServiceWorker } from '@budarin/pluggable-serviceworker/sw';
|
|
624
612
|
|
|
625
613
|
activateOnNextVisitServiceWorker({
|
|
626
|
-
assets: ['/', '/styles.css', '/script.js'],
|
|
627
614
|
cacheName: 'my-cache-v1',
|
|
628
|
-
|
|
629
|
-
onError: (err, event, type) =>
|
|
615
|
+
assets: ['/', '/styles.css', '/script.js'],
|
|
616
|
+
onError: (err, event, type) => console.error(type, err),
|
|
630
617
|
});
|
|
631
618
|
```
|
|
632
619
|
|
|
@@ -655,7 +642,7 @@ activateOnNextVisitServiceWorker({
|
|
|
655
642
|
"@budarin/pluggable-serviceworker": "^1.0.0"
|
|
656
643
|
},
|
|
657
644
|
"devDependencies": {
|
|
658
|
-
"@budarin/pluggable-serviceworker": "^1.5.
|
|
645
|
+
"@budarin/pluggable-serviceworker": "^1.5.5"
|
|
659
646
|
}
|
|
660
647
|
}
|
|
661
648
|
```
|
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);
|