@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 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
- case ServiceWorkerErrorType.MESSAGE_ERROR:
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
- case ServiceWorkerErrorType.PLUGIN_ERROR:
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
- Утилиты, доступные для использования в своих плагинах. Импорт: `@budarin/pluggable-serviceworker/utils`.
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,3 @@
1
+ export declare function updateWhenControlled(registration: ServiceWorkerRegistration, options?: {
2
+ timeoutMs?: number;
3
+ }): Promise<void>;
@@ -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
- 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);
@@ -0,0 +1,2 @@
1
+ import type { Plugin } from '../index.js';
2
+ export declare function reloadUncontrolledClients(): Plugin;
@@ -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.4",
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
  }