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 🙏

© 2024 – Pkg Stats / Ryan Hefner

swivel

v4.0.3

Published

Message passing between ServiceWorker and pages made simple

Downloads

50

Readme

swivel

Message passing between ServiceWorker and pages made simple

Inspiration

Understanding the raw API for message passing between ServiceWorker and pages can be kind of confusing. There's MessageChannel, ports, postMessage deeply buried in navigator.serviceWorker.controller.postMessage, addEventListener, multiple ServiceWorkerRegistration instances, and even promises are involved.

ServiceWorker is too awesome to let this problem hinder its adoption, so I made swivel in hopes people will find it easier to share messages across pages and their ServiceWorker. For an introduction of ServiceWorker you should look at this article.

I named it swivel mostly because it starts with sw and it wasn't taken on npm. And, because.

Installation

Install it from npm. You can then include swivel in your pages and your ServiceWorker, and Swivel will figure out what API to export depending on whether it's running within the ServiceWorker or a regular web page.

npm i -S swivel

Usage

On your web pages, you can listen for messages from the ServiceWorker. Remember to wait for ServiceWorker to become active, and always feature test to ensure ServiceWorker is available.

if (!('serviceWorker' in navigator)) {
  return;
}
navigator.serviceWorker
  .register('/service-worker.js')
  .then(navigator.serviceWorker.ready)
  .then(function () {
    swivel.on('data', function handler (context, ...data) {
      // do something with ...data
    });
  });

You can also emit messages to the ServiceWorker from your pages.

swivel.emit('data', ...data);

Emitting returns a Promise, in case you want to wait until the message is transferred to do something else.

swivel.emit('data', ...data).then(function () {
  // ... more swivelling!
});

In your ServiceWorker, the API barely changes. You can listen to messages posted from web pages with swivel.emit using swivel.on in the ServiceWorker code.

swivel.on('data', function handler (context, ...data) {
  // do something with ...data
});

If you need to reply to this particular page in the ServiceWorker, you could just use code like the following.

swivel.on('data', function handler (context, ...data) {
  context.reply('data', ...response);
});

You guessed correctly, context.reply returns a Promise.

swivel.on('data', function handler (context, ...data) {
  context.reply('data', ...response).then(function () {
    // ... more swivelling!
  });
});

You can also emit messages to every page using swivel.broadcast.

swivel.broadcast(type, ...data);

Broadcast also returns a Promise that awaits all client.postMessage signals to be processed.

swivel.broadcast(type, ...data).then(function () {
  // ... more swivelling!
});

Pages can then listen for the type event, which will go out to every page controlled by the ServiceWorker.

swivel.on(type, function (...data) {
  // do something with ...data
});

Importing

The swivel package performs a convenient bit of feature testing in order to decide what API to export. In your web pages, it'll export an API corresponding to web pages. In a ServiceWorker script, it'll export an API corresponding to the ServiceWorker side.

Automatic

import swivel from 'swivel'

If you prefer being explicit, you could import the individual modules separately.

Manual

Here's an example of manual setup for your web pages.

import createChannel from 'swivel/page'
var swivel = createChannel()

Here's an example of manual setup for your ServiceWorker script.

import createChannel from 'swivel/worker'
var swivel = createChannel()

API in Web Pages

The public swivel API exports a number of methods designed for web pages.

swivel.on(type, handler)

This method listens for events emitted by the ServiceWorker API. You can bind an event handler that receives all arguments emitted at that level. It can be triggered from a ServiceWorker in 3 different ways. Returns swivel for chaining.

The following methods -- when called from a ServiceWorker -- trigger handlers registered with swivel.on(type, fn) on web pages.

The handler has a context, ...data signature.

A context.broadcast flag in the handler indicates whether the message was broadcasted or unicasted by the ServiceWorker.

Example
swivel.on('data', function handler (context, ...data) {
  console.log(...data);
});

swivel.once(type, handler)

Equivalent to swivel.on but will only ever be called once. Returns swivel for chaining.

Example
swivel.once('data', function handler (context, ...data) {
  console.log(...data);
});

swivel.off(type, handler)

Unregisters a handler of type type that was previously registered using swivel.on or swivel.once. Returns swivel for chaining.

Example
swivel.on('data', function handler (context, ...data) {
  swivel.off('data', handler);
});

swivel.emit(type, ...data)

This method posts a message to the ServiceWorker. You can then use swivel.on(type, handler) from the ServiceWorker to listen to it.

Example
swivel.emit('data', { foo: 'bar' }, 'baz');

This method returns a Promise so you can await for the message to be successfully transferred to the ServiceWorker.

Example
swivel.emit('data', { foo: 'bar' }, 'baz').then(function () {
  console.log('success');
});

swivel.at(worker)

The swivel.at(worker) method returns an API identical to swivel that talks strictly with worker. The default swivel API interacts with the worker found at navigator.serviceWorker.controller. You can use as many channels as necessary.

Example
navigator.serviceWorker
  .getRegistration('/other')
  .then(function (registration) {
    var otherChannel = swivel.at(registration.active);
    otherChannel.emit('data', { hello: 'world' });
    otherChannel.on('data', function handler (context, ...data) {
      console.log(...data);
    });
  });

API in ServiceWorker

The public swivel API exports a number of methods designed for ServiceWorker scripts.

swivel.broadcast(type, ...data)

This method sends a message of type type from the ServiceWorker to every client it controls. Pages can listen for broadcasted messages using swivel.on or swivel.once.

Example
swivel.broadcast('urgent', ...data);

This method returns a Promise so you can await for the message to be successfully transferred to all clients.

Example
swivel.broadcast('urgent', ...data).then(function () {
  console.log('success');
});

swivel.emit(clientId, type, ...data)

During fetch events in a ServiceWorker, it's possible to message a client using swivel.emit. The web page can then receive and handle the message using swivel.on.

Example
self.addEventListener('fetch', function (e) {
  swivel.emit(e.clientId, 'data', { foo: 'bar' });
});

Furthermore, swivel.emit returns a Promise, so you can await for the message to be successfully transferred.

Example
self.addEventListener('fetch', function (e) {
  swivel.emit(e.clientId, 'data', { foo: 'bar' }).then(function () {
    console.log('success');
  });
});

swivel.on(type, handler)

You can use this method to listen from messages sent from a web page to the ServiceWorker using swivel.emit. Returns swivel for chaining.

Example
swivel.on('data', function handler (context, ...data) {
  console.log(...data);
});

The event handler is provided with a context object that allows for replies to messages originating from a web page.

context.reply(type, ...data)

Using this method, your ServiceWorker can reply to messages received from an individual web page.

Example
swivel.on('data', function handler (context, [x, y]) {
  context.reply('result', x * y);
});

Furthermore, context.reply returns a Promise, so you can await for the reply to be successfully transferred.

Example
swivel.on('data', function handler (context, [x, y]) {
  context.reply('result', x * y).then(function () {
    console.log('success');
  });
});

swivel.once(type, handler)

Equivalent to swivel.on but will only ever be called once. Returns swivel for chaining. Also able to use context.reply in handler(context, ...data) for bidirectional communication.

Example
swivel.once('data', function handler (context, [x, y]) {
  context.reply('result', x * y).then(function () {
    console.log('success');
  });
});

swivel.off(type, handler)

Unregisters a handler of type type that was previously registered using swivel.on or swivel.once. Returns swivel for chaining.

Example
swivel.on('data', function handler (context, ...data) {
  swivel.off('data', handler);
});

License

MIT