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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@vrowser/service-worker

v0.0.1

Published

Safely deploy and manage Service Workers with version control, bidirectional communication, and emergency shutdown capabilities

Readme

@vrowser/service-worker

Safely deploy and manage Service Workers with version control, bidirectional communication, and emergency shutdown capabilities.

✨ Features

  • Version Management - Tag and verify Service Worker versions for controlled updates
  • Singleton Controller - One controller instance per scriptURL + version combination
  • State Management - Track Service Worker lifecycle states with events
  • Heartbeat Monitoring - Automatic session health checks and stale session cleanup
  • Continuing Sessions - MessageChannel-based bidirectional communication between page and Service Worker
  • Kill Switch / Circuit Breaker - Suspend (soft kill) or terminate (hard kill) Service Workers on demand

💿 Installation

# npm
npm install --save @vrowser/service-worker

# pnpm
pnpm add @vrowser/service-worker

# yarn
yarn add @vrowser/service-worker

# deno
deno add npm:@vrowser/service-worker

# bun
bun add @vrowser/service-worker

🚀 Usage

Service Worker Controller

The controller manages Service Worker registration, version verification, and lifecycle on the page side.

import { createSvcWorkerController } from '@vrowser/service-worker/controller'

const controller = createSvcWorkerController({
  scriptURL: '/sw.js',
  version: '1.0.0'
})

// Listen to state changes
controller.on('changeState', ({ state, version }) => {
  console.log(`State: ${state}, Version: ${version}`)
})

// Listen for reload suggestions (when Service Worker doesn't call clients.claim())
controller.on('reloadSuggested', ({ reason }) => {
  if (reason === 'unclaimed') {
    showReloadBanner()
  }
})

// Wait for Service Worker to become active
const success = await controller.ready({
  timeout: 5000,
  skipWaitingPolicy: 'strict' // or 'force'
})

if (success) {
  console.log('Service Worker is ready:', controller.version)
}

Service Worker

Use createSvcWorker to wrap the Service Worker global scope. It provides a Proxy-based wrapper that transparently passes through all native APIs while adding version management and session handling.

// sw.js
import { createSvcWorker } from '@vrowser/service-worker/worker'

const sw = createSvcWorker(self, {
  version: '1.0.0',
  heartbeatInterval: 30000,
  sessionTimeout: 60000
})

// Use suspended flag for kill switch bypass
sw.addEventListener('fetch', (event) => {
  // When suspended, bypass all custom logic
  if (sw.suspended) {
    event.respondWith(fetch(event.request))
    return
  }

  // Normal fetch handling
  event.respondWith(handleFetch(event.request))
})

sw.addEventListener('activate', (event) => {
  event.waitUntil(self.clients.claim())
})

Admin

The admin API provides centralized management for all registered controllers.

import {
  getAllControllers,
  getController,
  suspendServiceWorker,
  resumeServiceWorker,
  terminateServiceWorker,
  suspendAllServiceWorkers,
  terminateAllServiceWorkers
} from '@vrowser/service-worker/admin'

// List all registered controllers
const controllers = getAllControllers()
controllers.forEach(c => {
  console.log(`${c.scriptURL} v${c.version}: ${c.state}`)
})

// Suspend a specific Service Worker (soft kill)
const result = await suspendServiceWorker('/sw.js', '1.0.0', {
  clearCaches: true
})
console.log('Suspended:', result.mode === 'suspend')

// Resume after suspension
await resumeServiceWorker('/sw.js', '1.0.0')

// Terminate a Service Worker (hard kill - unregisters)
await terminateServiceWorker('/sw.js', '1.0.0', {
  clearCaches: true
})

// Emergency: suspend all Service Workers
await suspendAllServiceWorkers()

// Emergency: terminate all Service Workers
await terminateAllServiceWorkers({ clearCaches: true })

🏛️ Architecture

flowchart TB
    subgraph Page["📄 Page Context"]
        Admin["🔧 Admin API<br/>getAllControllers()<br/>suspend/resume/terminate()"]
        Registry["📋 Registry<br/>register/unregister<br/>get/getAll"]
        Controller["🎮 Controller<br/>ready()<br/>suspend()/resume()<br/>dispose()"]
        Session["📡 Session<br/>request()<br/>send()<br/>close()"]

        Admin --> Registry
        Controller --> Registry
        Controller --> Session
    end

    Session <-->|"MessageChannel<br/>(MessagePort)"| SvcWorker

    subgraph SW["⚙️ Service Worker Context"]
        SvcWorker["🔄 SvcWorker (Proxy wrapper)<br/>version / suspended / sessionCount<br/><br/>Built-in Handlers:<br/>VERSION / SKIP_WAITING<br/>SESSION_INIT / SESSION_CLOSE<br/>CIRCUIT_BREAKER / RESUME"]
    end

State Diagram

stateDiagram-v2
    [*] --> installing

    installing --> waiting : install complete
    installing --> activating : skipWaiting

    waiting --> activating : promotion

    activating --> activated : activation complete

    activated --> suspended : suspend()
    activated --> terminated : terminate()

    suspended --> activated : resume()
    suspended --> terminated : terminate()

    terminated --> [*] : dispose()

    note right of activated
        Circuit Breaker
        activated ↔ suspended
    end note

    note right of terminated
        Hard Kill
        unregister()
    end note

Message Protocols

Communication between Page and Service Worker uses the following message protocols:

Core Protocols (via postMessage)

| Protocol | Direction | Description | | ------------------- | ---------------------------- | ---------------------------------------------- | | V_SW_VERSION | Page → Service Worker → Page | Version verification request/response | | V_SW_SKIP_WAITING | Page → Service Worker | Request Service Worker to call skipWaiting() |

Session Protocols (via MessagePort)

| Protocol | Direction | Description | | -------------------- | ---------------------------- | ---------------------------------------------- | | V_SW_SESSION_INIT | Page → Service Worker → Page | Establish persistent session with version info | | V_SW_SESSION_CLOSE | Page → Service Worker | Close the session gracefully | | V_SW_SESSION_PING | Service Worker → Page | Heartbeat ping from Service Worker | | V_SW_SESSION_PONG | Page → Service Worker | Heartbeat pong response |

Circuit Breaker Protocols (via MessagePort)

| Protocol | Direction | Description | | ------------------------------ | ---------------------------- | ------------------------------------------------- | | V_SW_SESSION_CIRCUIT_BREAKER | Page → Service Worker → Page | Suspend or terminate Service Worker | | V_SW_SESSION_RESUME | Page → Service Worker → Page | Resume suspended Service Worker | | V_SW_SESSION_TERMINATED | Service Worker → Page | Notification that Service Worker has unregistered |

sequenceDiagram
    participant Page
    participant ServiceWorker as Service Worker

    Note over Page,ServiceWorker: Session Establishment
    Page->>ServiceWorker: SESSION_INIT (with MessagePort)
    ServiceWorker-->>Page: SESSION_INIT Response (version)

    Note over Page,ServiceWorker: Heartbeat Loop
    loop Every heartbeatInterval
        ServiceWorker->>Page: PING
        Page-->>ServiceWorker: PONG
    end

    Note over Page,ServiceWorker: Circuit Breaker
    Page->>ServiceWorker: CIRCUIT_BREAKER (mode: suspend)
    ServiceWorker-->>Page: Response (suspended)

    Page->>ServiceWorker: RESUME
    ServiceWorker-->>Page: Response (resumed)

    Page->>ServiceWorker: CIRCUIT_BREAKER (mode: terminate)
    ServiceWorker-->>Page: TERMINATED (reason)

📖 Details of Features

Versioning

Each Service Worker must declare a version tag that the controller uses to identify and verify the expected Service Worker.

How it works:

  1. Controller sends V_SW_VERSION message to Service Worker
  2. Service Worker responds with its version via MessagePort
  3. Controller verifies the version matches before proceeding

Version verification occurs:

  • When checking if current controller is the expected version
  • During Service Worker promotion (installing → waiting → active)
  • When establishing a session
// Page side
const controller = createSvcWorkerController({
  scriptURL: '/sw.js',
  version: '2.0.0' // Expected version
})

// Service Worker side
const sw = createSvcWorker(self, {
  version: '2.0.0' // Must match
})

Singleton: Controllers are cached by scriptURL::version key. Creating a controller with the same scriptURL and version returns the existing instance.

Kill Switch

The kill switch (circuit breaker) provides two modes for controlling Service Workers:

Suspend (Soft Kill)

Disables Service Worker functionality without unregistering it.

// Via controller
await controller.suspend({ clearCaches: true })

// Via admin API
await suspendServiceWorker('/sw.js', '1.0.0')

Behavior:

  • Sets sw.suspended = true on Service Worker side
  • Optionally clears all caches
  • Service Worker remains registered
  • Fetch handlers should check sw.suspended and bypass logic
  • State changes to 'suspended'
  • Emits 'suspended' event

Resume

Restores Service Worker functionality after suspension.

// Via controller
await controller.resume()

// Via admin API
await resumeServiceWorker('/sw.js', '1.0.0')

Behavior:

  • Sets sw.suspended = false on Service Worker side
  • State changes back to 'activated'
  • Emits 'resumed' event

Terminate (Hard Kill)

Unregisters the Service Worker completely.

// Via admin API only
await terminateServiceWorker('/sw.js', '1.0.0', {
  clearCaches: true
})

Behavior:

  • Optionally clears all caches
  • Calls self.registration.unregister() on Service Worker side
  • Notifies all connected sessions with TERMINATED message
  • State changes to 'terminated'
  • Emits 'terminated' event with reason
  • Controller is removed from registry

Service Worker Management

Controller Lifecycle

const controller = createSvcWorkerController({ scriptURL, version })

// 1. Wait for activation
await controller.ready()

// 2. Use Service Worker
// ... application logic ...

// 3. Suspend if needed (recoverable)
await controller.suspend()

// 4. Resume when ready
await controller.resume()

// 5. Dispose when done
controller.dispose()

Events

| Event | Payload | Description | | ----------------- | ----------------------------------- | -------------------------------- | | progress | string | Debug/UI progress phases | | changeState | { state, version, serviceWorker } | State transitions | | reloadSuggested | { reason, version } | Reload needed (no clients.claim) | | suspended | void | Service Worker suspended | | resumed | void | Service Worker resumed | | terminated | SvcWorkerTerminatedReason | Service Worker unregistered |

Heartbeat & Session Cleanup

  • Service Worker sends PING to all sessions at configured interval
  • Page responds with PONG
  • Sessions without PONG response within timeout are cleaned up
  • Stale sessions (disconnected clients) are automatically removed

📦 Exports

| Entry Point | Description | | ------------------------------------ | --------------------------- | | @vrowser/service-worker/admin | Admin API | | @vrowser/service-worker/controller | Controller | | @vrowser/service-worker/worker | Service Worker wrapper | | @vrowser/service-worker/protocols | Message protocols and types |

📚 API References

See the API References

🤝 Sponsors

©️ License

MIT