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

@actualwave/worker-dispatcher

v1.3.3

Published

WorkerDispatcher is a JavaScript class that adds events layer to Worker API

Readme

@actualwave/worker-dispatcher

npm

Named-event communication layer for Dedicated Workers, Shared Workers, and Service Workers. Built on top of @actualwave/messageport-dispatcher, it replaces raw postMessage/message boilerplate with typed named events on both sides of every worker channel.

Installation

npm install @actualwave/worker-dispatcher
# or
yarn add @actualwave/worker-dispatcher

Core concepts

  • DedicatedWorkerDispatcher — wraps a Worker on the main thread (or self inside the worker).
  • SharedWorkerDispatcher — wraps a SharedWorker on the main thread; handles the MessagePort internally.
  • ServiceWorkerDispatcher — wraps a MessageChannel to the active Service Worker; auto-recovers when the worker restarts.
  • Server dispatchers (SharedServerDispatcher, ServiceServerDispatcher) — used inside the worker script to receive connections and events.
  • Client dispatchers (SharedClientDispatcher, ServiceClientDispatcher) — one instance per connected client, created automatically on each new connection.
  • WorkerEvent — extends Event; carries nativeEvent, client, type, and data.
  • WorkerType — string enum of all dispatcher type identifiers.

Usage

1. Dedicated Worker

Main thread:

import { DedicatedWorkerDispatcher } from '@actualwave/worker-dispatcher';

const dispatcher = new DedicatedWorkerDispatcher('/workers/worker.js');

dispatcher.addEventListener('result', (event) => {
  console.log('Worker result:', event.data);
});

dispatcher.dispatchEvent('compute', { input: [1, 2, 3] });

Inside the worker (worker.js):

import { createForSelf } from '@actualwave/worker-dispatcher';

const dispatcher = createForSelf();

dispatcher.addEventListener('compute', (event) => {
  dispatcher.dispatchEvent('result', { output: (event.data as any).input.map(Number) });
});

2. Shared Worker

Main thread:

import { SharedWorkerDispatcher } from '@actualwave/worker-dispatcher';

const dispatcher = new SharedWorkerDispatcher('/workers/shared.js');
// start() is called automatically in the constructor

dispatcher.addEventListener('broadcast', (event) => {
  console.log('Broadcast from worker:', event.data);
});

dispatcher.dispatchEvent('subscribe', { topic: 'news' });

Inside the shared worker (shared.js):

import { createForSelf } from '@actualwave/worker-dispatcher';
import WorkerEvent from '@actualwave/worker-dispatcher/WorkerEvent';

const server = createForSelf(); // returns SharedServerDispatcher

server.addEventListener(WorkerEvent.CONNECT, (event) => {
  const client = event.client; // SharedClientDispatcher
  client.start();

  client.addEventListener('subscribe', (e) => {
    console.log('Client subscribed to', (e.data as any).topic);
    client.dispatchEvent('broadcast', { message: 'Welcome!' });
  });
});

Close a client when done:

client.close();

3. Service Worker

Main thread (registers and communicates with a Service Worker):

import { ServiceWorkerDispatcher } from '@actualwave/worker-dispatcher';

const dispatcher = new ServiceWorkerDispatcher();

dispatcher.onReady((registration) => {
  console.log('Service worker ready');
  dispatcher.dispatchEvent('init', { config: { offline: true } });
});

dispatcher.addEventListener('push-received', (event) => {
  console.log('Push data:', event.data);
});

Inside the service worker (sw.js):

import { ServiceServerDispatcher } from '@actualwave/worker-dispatcher';
import WorkerEvent from '@actualwave/worker-dispatcher/WorkerEvent';

const server = new ServiceServerDispatcher();

server.addEventListener('init', (event) => {
  console.log('Config received:', event.data);
  const client = event.client; // ServiceClientDispatcher
  if (client) {
    client.dispatchEvent('ack', { ok: true });
  }
});

// Lifecycle events are wired automatically
server.addEventListener(WorkerEvent.INSTALL, (e) => {
  (e.nativeEvent as ExtendableEvent).waitUntil(caches.open('v1'));
});

4. Factory functions

import {
  create,
  createForSelf,
  createForDedicatedWorker,
  createForSharedWorker,
  createForServiceWorker,
  WorkerType,
} from '@actualwave/worker-dispatcher';

// auto-detect from a target object
const d1 = create(worker, WorkerType.DEDICATED_WORKER);
const d2 = create(sharedWorker, WorkerType.SHARED_WORKER);

// inside a worker script — auto-detects context
const dispatcher = createForSelf();

// explicit per-type helpers
const dw = createForDedicatedWorker('/worker.js');
const sw = createForSharedWorker('/shared.js');
const svc = createForServiceWorker();

5. Event preprocessors

Transform every incoming or outgoing event:

import type { EventProcessor } from '@actualwave/event-dispatcher';

const stamp: EventProcessor = (event) => ({
  ...event,
  data: { ...(event.data as object), ts: Date.now() },
});

const dispatcher = new DedicatedWorkerDispatcher(
  '/worker.js',
  stamp,   // receiverEventPreprocessor
  stamp,   // senderEventPreprocessor
);

API reference

DedicatedWorkerDispatcher

new DedicatedWorkerDispatcher(
  worker?: Worker | string,
  receiverEventPreprocessor?: EventProcessor | null,
  senderEventPreprocessor?: EventProcessor | null,
)

| Member | Description | |---|---| | addEventListener(type, listener, priority?) | Listen for events from the worker. | | hasEventListener(type) | Check if listener exists. | | removeEventListener(type, listener) | Remove a listener. | | removeAllEventListeners(type) | Remove all listeners for a type. | | dispatchEvent(type, data?, transferList?) | Send an event to the worker. | | receiver | IEventDispatcher for incoming events. | | sender | IEventDispatcher for outgoing echo. | | target | Underlying Worker or WorkerLike. | | terminate() | Terminate the worker and clean up listeners. |

SharedWorkerDispatcher

new SharedWorkerDispatcher(
  target: SharedWorker | string,
  name?: string,
  receiverEventPreprocessor?: EventProcessor | null,
  senderEventPreprocessor?: EventProcessor | null,
)

| Member | Description | |---|---| | start() | Start the port (called automatically). | | close() | Close the port and clean up listeners. | | worker | The underlying SharedWorker instance. | | (inherits all dispatcher methods) | |

ServiceWorkerDispatcher

new ServiceWorkerDispatcher(
  receiverEventPreprocessor?: EventProcessor | null,
  senderEventPreprocessor?: EventProcessor | null,
)

| Member | Description | |---|---| | start() | Start the internal MessageChannel port. | | close() | Close the port and remove error listeners. | | ready | Promise<ServiceWorkerRegistration> | | onReady(handler) | Calls handler when the SW registration resolves. Returns Promise<void>. | | (inherits all dispatcher methods) | |

Auto-recovers the MessageChannel when the active Service Worker changes (e.g., after an update).

SharedServerDispatcher (inside a Shared Worker)

new SharedServerDispatcher(
  target?: EventTarget,      // defaults to self
  receiverEventPreprocessor?: EventProcessor,
  clientReceiverEventPreprocessor?: EventProcessor,
  clientSenderEventPreprocessor?: EventProcessor,
)

| Member | Description | |---|---| | addEventListener(type, listener) | Listen for connect and worker-lifecycle events. | | destroy() | Remove all native listeners from the target. | | receiver | IEventDispatcher for dispatched events. |

Fires WorkerEvent.CONNECT whenever a new client connects. The event's .client is a SharedClientDispatcher.

ServiceServerDispatcher (inside a Service Worker)

new ServiceServerDispatcher(
  target?: EventTarget,      // defaults to self
  receiverEventPreprocessor?: EventProcessor,
  clientReceiverEventPreprocessor?: EventProcessor,
  clientSenderEventPreprocessor?: EventProcessor,
)

Automatically registers listeners for error, messageerror, install, activate, fetch, sync, and push on target. The event's .client is a ServiceClientDispatcher when a port was transferred.

| Member | Description | |---|---| | destroy() | Remove all native listeners from the target. |

SharedClientDispatcher / ServiceClientDispatcher

One instance per client connection. Returned via event.client on connect/message events.

| Member | Description | |---|---| | start() | Start the MessagePort. | | close() | Close the MessagePort. | | dispatchEvent(type, data?) | Send an event to this specific client. | | (inherits all dispatcher methods) | |

WorkerEvent

Static event type constants (all prefixed worker:):

| Constant | Value | When fired | |---|---|---| | WorkerEvent.CONNECT | 'worker:connect' | New client connected (Shared Worker) | | WorkerEvent.MESSAGE | 'worker:message' | Incoming message | | WorkerEvent.ERROR | 'worker:error' | Worker error | | WorkerEvent.MESSAGEERROR | 'worker:messageerror' | Message deserialization error | | WorkerEvent.LANGUAGECHANGE | 'worker:languagechange' | Language changed | | WorkerEvent.ONLINE | 'worker:online' | Network online | | WorkerEvent.OFFLINE | 'worker:offline' | Network offline | | WorkerEvent.INSTALL | 'worker:install' | SW install event | | WorkerEvent.ACTIVATE | 'worker:activate' | SW activate event | | WorkerEvent.FETCH | 'worker:fetch' | SW fetch event | | WorkerEvent.SYNC | 'worker:sync' | SW background sync | | WorkerEvent.PUSH | 'worker:push' | SW push notification |

Instance properties:

| Property | Type | Description | |---|---|---| | type | string | The worker-prefixed event type | | data | unknown | Event payload | | nativeEvent | unknown | The original native DOM event | | client | unknown | Client dispatcher, or null |

WorkerType

String constants used with create():

| Constant | Value | |---|---| | WorkerType.DEDICATED_WORKER | 'dedicated' | | WorkerType.SHARED_WORKER | 'shared' | | WorkerType.SERVICE_WORKER | 'service' |

NativeEventType

Raw DOM event type strings: 'connect', 'message', 'error', 'messageerror', 'install', 'activate', 'fetch', 'sync', 'push', 'languagechange', 'online', 'offline'.

Utility: dispatchWorkerEvent / dispatchWorkerEvents / dispatchWorkerErrorEvent

Lower-level helpers that register native event listeners on an EventTarget and forward translated WorkerEvents to an IEventDispatcher. Each returns a () => void cleanup function.

import { dispatchWorkerErrorEvent } from '@actualwave/worker-dispatcher/WorkerEvent';

const cleanup = dispatchWorkerErrorEvent(target, myDispatcher.receiver);
// later:
cleanup();

TypeScript types

import type { WorkerTypeValue } from '@actualwave/worker-dispatcher/WorkerType';
import type { NativeEventHandler } from '@actualwave/worker-dispatcher/WorkerEvent';
import type { WorkerLike } from '@actualwave/worker-dispatcher/AbstractDispatcher';

import type {
  EventObject,
  IEventDispatcher,
  EventProcessor,
  EventListener,
} from '@actualwave/event-dispatcher';

Common edge cases

  • Service Worker recovery: ServiceWorkerDispatcher automatically recreates the MessageChannel and retransmits pending port2 when the active worker changes. The first dispatchEvent() after construction always transfers port2.
  • destroy() on server dispatchers: Call destroy() when tearing down a SharedServerDispatcher or ServiceServerDispatcher to remove all native addEventListener registrations from self.
  • start() on SharedWorkerDispatcher: start() is called automatically in the constructor; calling it again is a no-op in most environments but safe.
  • WorkerEvent.MESSAGEERROR value: The correct value is 'worker:messageerror', not 'messageerror'. Use the constant, not the string literal.
  • createForSelf() auto-detection: Checks typeof self.postMessage === 'function' (Dedicated Worker) then checks self.registration.scope (Service Worker), then falls back to SharedServerDispatcher.
  • Preprocessors apply on both sides: Pass the same preprocessor to receiver and sender if you want symmetric transformation; they are independent by default.