npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@budarin/pluggable-serviceworker

v1.0.32

Published

Extensible via plugins service worker

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 для передачи следующему плагину
  • Остальные: Не возвращают значения, выполняются для всех плагинов
  • Все методы опциональны - реализуйте только нужные события

🎯 Порядок выполнения

Плагины выполняются в следующем порядке:

  1. Сначала ВСЕ плагины без order - в том порядке, в котором они были добавлены
  2. Затем плагины с 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