@budarin/pluggable-serviceworker
v1.0.32
Published
Extensible via plugins service worker
Maintainers
Readme
@budarin/pluggable-serviceworker
🔌 Расширяемый через плагины Service Worker
Библиотека для создания модульных и расширяемых Service Worker'ов с помощью системы плагинов.
🚀 Почему этот пакет облегчает разработку?
Разработка Service Worker'ов традиционно сложна из-за необходимости вручную управлять множественными обработчиками событий, обработкой ошибок и порядком выполнения. Этот пакет решает эти проблемы:
🔌 Модульная архитектура
- Плагинная система позволяет разбивать функциональность на независимые модули
- Каждый плагин отвечает за свою задачу (кеширование, аутентификация, уведомления)
- Легко добавлять/удалять функциональность без изменения основного кода
- Не нужно думать об инфраструктурном коде в обработчиках событий - пишите простой код не думая о сложностях кода самого сервисворкера
🎯 Управление порядком выполнения
- Предсказуемый порядок - плагины без
orderвыполняются первыми, затем по возрастаниюorder - Гибкость - можно контролировать последовательность инициализации
- Масштабируемость - легко добавлять новые плагины в нужном месте
⚡ Оптимизированная логика выполнения
- Параллельно для
install,activate,message,sync- независимые задачи выполняются одновременно - Последовательно для
fetch,push- первый успешный результат прерывает цепочку - Производительность - правильный выбор стратегии для каждого типа события
🛡️ Централизованная обработка ошибок
- Единый обработчик для всех типов ошибок
- Типизированные ошибки - знаешь, что именно сломалось
- Изоляция - ошибка в одном плагине не ломает остальные
- Автоматическая обработка глобальных событий ошибок
📝 Удобное логирование
- Настраиваемый логгер с разными уровнями
- Контекстная информация в логах
- Отладка становится намного проще
📦 Установка
npm install @budarin/pluggable-serviceworkerили
pnpm add @budarin/pluggable-serviceworker🚀 Быстрый старт
Базовое использование
// sw.js
import { initServiceWorker } from '@budarin/pluggable-serviceworker';
// Простой плагин для кеширования
const cachePlugin = {
name: 'cache-plugin',
install: async (event) => {
const cache = await caches.open('my-cache-v1');
await cache.addAll(['/', '/styles.css', '/script.js']);
},
fetch: async (event) => {
const cachedResponse = await caches.match(event.request);
return cachedResponse || fetch(event.request);
},
};
// Инициализация Service Worker с плагинами
initServiceWorker([cachePlugin], { logger: console });⚙️ Конфигурация
Функция initializeServiceWorker принимает второй параметр config типа ServiceWorkerConfig:
interface ServiceWorkerConfig {
logger: Logger; // Обязательное поле
// Опциональное поле
onError?: (
error: Error | any,
event: Event,
errorType?: ServiceWorkerErrorType
) => void;
}Поля конфигурации
logger: Logger (обязательное)
Объект для логирования с методами info, warn, error, debug. Может быть передан console или любой другой объект, реализующий интерфейс Logger.
interface Logger {
info: (...data: unknown[]) => void;
warn: (...data: unknown[]) => void;
error: (...data: unknown[]) => void;
debug: (...data: unknown[]) => void;
}Пример:
const config = {
logger: console, // Использование стандартного console
// или
logger: {
info: (...data) => customLog('INFO', ...data),
warn: (...data) => customLog('WARN', ...data),
error: (...data) => customLog('ERROR', ...data),
debug: (...data) => customLog('DEBUG', ...data),
},
};onError?: (error, event, errorType) => void (опциональное)
Единый обработчик для всех типов ошибок в Service Worker. Дефолтного обработчика ошибок нет - если onError не передан, ошибки будут проигнорированы (не обработаны).
Параметры:
error: Error | any- объект ошибкиevent: Event- событие, в контексте которого произошла ошибкаerrorType?: ServiceWorkerErrorType- тип ошибки (см. раздел "Обработка ошибок")
Важно: Если onError не указан, ошибки в плагинах и глобальные ошибки будут проигнорированы. Для production-окружения рекомендуется всегда указывать onError для логирования и мониторинга ошибок.
Пример минимальной конфигурации:
// Без onError - ошибки будут проигнорированы
initializeServiceWorker([cachePlugin], { logger: console });
// С onError - ошибки будут обработаны
initializeServiceWorker([cachePlugin], {
logger: console,
onError: (error, event, errorType) => {
console.error('Service Worker error:', error, errorType);
},
});Обработка ошибок
Библиотека предоставляет единый обработчик для всех типов ошибок в Service Worker:
import {
initServiceWorker,
ServiceWorkerErrorType,
} from '@budarin/pluggable-serviceworker';
const config = {
onError: (error, event, errorType) => {
console.log(`Ошибка типа "${errorType}":`, error);
switch (errorType) {
case ServiceWorkerErrorType.ERROR:
// JavaScript ошибки
console.error('JavaScript error:', error);
break;
case ServiceWorkerErrorType.MESSAGE_ERROR:
// Ошибки сообщений
console.error('Message error:', error);
break;
case ServiceWorkerErrorType.UNHANDLED_REJECTION:
// Необработанные Promise rejection
console.error('Unhandled promise rejection:', error);
break;
case ServiceWorkerErrorType.REJECTION_HANDLED:
// Обработанные Promise rejection
console.log('Promise rejection handled:', error);
break;
case ServiceWorkerErrorType.PLUGIN_ERROR:
// Ошибки в плагинах
console.error('Plugin error:', error);
break;
default:
// Неизвестные типы ошибок
console.error('Unknown error type:', error);
// Отправка ошибки в аналитику
fetch('/api/errors', {
method: 'POST',
body: JSON.stringify({
error: error.message,
eventType: event.type,
url: event.request?.url,
timestamp: Date.now(),
}),
}).catch(() => {
// Игнорируем ошибки отправки логов
});
}
},
};
initServiceWorker(
[
/* ваши плагины */
],
config
);🔌 Интерфейс плагина
Каждый плагин должен реализовывать интерфейс ServiceWorkerPlugin:
interface ServiceWorkerPlugin {
/** Уникальное имя плагина */
name: string;
/** Порядок выполнения (опционально) */
order?: number;
/** Обработчик события install */
install?: (event: ExtendableEvent) => Promise<void> | void;
/** Обработчик события activate */
activate?: (event: ExtendableEvent) => Promise<void> | void;
/** Обработчик события fetch */
fetch?: (
event: FetchEvent
) => Promise<Response | undefined> | Response | undefined;
/** Обработчик события message */
message?: (event: ExtendableMessageEvent) => Promise<void> | void;
/** Обработчик события sync */
sync?: (event: SyncEvent) => Promise<void> | void;
/** Обработчик события push */
push?: (event: PushEvent) => Promise<void> | void;
/** Обработчик события periodicsync */
periodicsync?: (event: PeriodicSyncEvent) => Promise<void> | void;
}📝 Описание методов
| Метод | Событие | Возвращает | Описание |
| -------------- | -------------- | ----------------------- | --------------------------------------- |
| install | install | void | Инициализация плагина при установке SW |
| activate | activate | void | Активация плагина при обновлении SW |
| fetch | fetch | Response \| undefined | Обработка сетевых запросов |
| message | message | void | Обработка сообщений от основного потока |
| sync | sync | void | Синхронизация данных в фоне |
| push | push | void | Обработка push-уведомлений |
| periodicsync | periodicsync | void | Периодические фоновые задачи |
🎯 Особенности обработчиков
fetch: ВозвращаетResponseдля завершения цепочки илиundefinedдля передачи следующему плагину- Остальные: Не возвращают значения, выполняются для всех плагинов
- Все методы опциональны - реализуйте только нужные события
🎯 Порядок выполнения
Плагины выполняются в следующем порядке:
- Сначала ВСЕ плагины без
order- в том порядке, в котором они были добавлены - Затем плагины с
order- в порядке возрастания значенийorder
Пример:
const plugins = [
{ name: 'first' }, // без order - выполняется первым
{ name: 'fourth', order: 2 },
{ name: 'second' }, // без order - выполняется вторым
{ name: 'third', order: 1 },
{ name: 'fifth' }, // без order - выполняется третьим
];
// Порядок выполнения: first → second → fifth → third → fourthПреимущества новой системы:
- 🎯 Предсказуемость - плагины без
orderвсегда выполняются первыми - 🔧 Простота - не нужно знать, какие номера уже заняты
- 📈 Масштабируемость - легко добавлять новые плагины в нужном порядке
⚡ Логика выполнения обработчиков
Разные типы событий Service Worker обрабатываются по-разному в зависимости от их специфики:
🔄 Параллельное выполнение
События: install, activate, message, sync, periodicsync
Все обработчики выполняются одновременно с помощью Promise.all():
// Все плагины инициализируются параллельно
const installPlugin1 = {
name: 'cache-assets',
install: async () => {
/* кеширование ресурсов приложения*/
},
};
const installPlugin2 = {
name: 'cache-ext',
install: async () => {
/* кэширование вспомогательных ресурсов */
},
};
// Оба install обработчика выполнятся одновременноПочему параллельно:
- install/activate: Все плагины должны инициализироваться независимо
- message: Все плагины должны получить сообщение одновременно
- sync: Разные задачи синхронизации независимы (синхронизация данных + кеша)
- periodicsync: Периодические задачи независимы друг от друга
➡️ Последовательное выполнение
События: fetch, push
Обработчики выполняются по очереди до первого успешного результата:
Fetch - с прерыванием цепочки
const authPlugin = {
name: 'auth',
// Без order - выполняется первым
fetch: async (event) => {
if (needsAuth(event.request)) {
return new Response('Unauthorized', { status: 401 }); // Прерывает цепочку
}
return undefined; // Передает следующему плагину
},
};Почему последовательно:
- fetch: Нужен только один ответ, первый успешный прерывает цепочку
- push: Избегает конфликтов уведомлений, но все плагины должны обработать событие
📋 Сводная таблица
| Событие | Выполнение | Прерывание | Причина |
| -------------- | --------------- | ---------- | -------------------------------- |
| install | Параллельно | Нет | Независимая инициализация |
| activate | Параллельно | Нет | Независимая активация |
| fetch | Последовательно | Да | Нужен один ответ |
| message | Параллельно | Нет | Все получают сообщение |
| sync | Параллельно | Нет | Независимые задачи |
| periodicsync | Параллельно | Нет | Независимые периодические задачи |
| push | Последовательно | Нет | Избегание конфликтов |
🛡️ Обработка ошибок
Библиотека автоматически перехватывает и обрабатывает все типы ошибок в Service Worker через единый обработчик config.onError (см. раздел Конфигурация).
Особенности:
- Единый обработчик - все типы ошибок обрабатываются через
config.onError(опциональное поле) - Дефолтного обработчика нет - если
onErrorне указан, ошибки будут проигнорированы - Типизированные ошибки - третий параметр
errorTypeуказывает тип ошибки изServiceWorkerErrorType - Глобальные события - автоматическая обработка
error,messageerror,unhandledrejection,rejectionhandled - Изоляция ошибок - ошибка в одном плагине не останавливает выполнение других
- Безопасность - ошибки в самих обработчиках ошибок логируются через
logger.error
Режим разработки
Важно, чтобы в режиме разработки ни один из плагинов ничего не кэшировал и не пытался что-либо отдавать из кэша.
В пакетах для этого можно использовть import.meta.env.DEV для Vite
📄 Лицензия
MIT © Vadim Budarin
