@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 CHANGED
@@ -77,13 +77,13 @@ pnpm add @budarin/pluggable-serviceworker
77
77
  ### Базовое использование
78
78
 
79
79
  ```typescript
80
- // sw.js
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
- ## `initServiceWorker(plugins, options)`
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
- case ServiceWorkerErrorType.MESSAGE_ERROR:
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
- case ServiceWorkerErrorType.PLUGIN_ERROR:
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
- **Пример: claimAndReloadClients как композиция двух примитивов**
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
- logger: customLogger,
629
- onError: (err, event, type) => customLogger.error(type, err),
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.0"
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
- PLUGIN_ERROR = "plugin_error",
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["PLUGIN_ERROR"] = "plugin_error";
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.PLUGIN_ERROR)))));
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.PLUGIN_ERROR)))));
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.PLUGIN_ERROR);
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.PLUGIN_ERROR);
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.PLUGIN_ERROR);
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.PLUGIN_ERROR)))));
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.PLUGIN_ERROR)))));
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.PLUGIN_ERROR);
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.PLUGIN_ERROR);
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.MESSAGE_ERROR);
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@budarin/pluggable-serviceworker",
3
- "version": "1.5.3",
3
+ "version": "1.5.5",
4
4
  "description": "Extensible via plugins service worker",
5
5
  "keywords": [
6
6
  "serviceworker",