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

hubbie

v4.2.0

Published

WebSocket manager

Downloads

15

Readme

Hubbie

Manager for WebSocket server and clients

Why?

WebSocket client objects take a URL in their constructor, and then live through only one cycle of open/close. This may be fine for short-lived web pages that get refreshed soon enough, but for long-lived contexts such as unhosted web apps or browser extensions, it's more useful to have a client object that represents multiple incarnations of a WebSocket, makes sure it opens a new incarnation when necessary, and always sends messages through the currently connected incarnation.

The idea for Hubbie evolved from my earlier BtpSpider work, which at the time was part of the Interledgerjs project at Ripple. In its current version, I'm using it as the basis for agent-to-agent messaging in the LedgerLoops project.

What else?

Hubbie can:

  • take care of reconnecting clients when the server restarts
  • queue up messages until they can be sent
  • be used as one or multiple WebSocket clients
  • or act as a hub for in-process messaging, which is nice when are testing your multi-agent messaging, or simulating a network
  • WebRTC support is planned

And when used in node, Hubbie can also:

  • be configured to act as one WebSocket server, handling a simple form of client auth through the URL path
  • register a TLS certificate registration for you, or run on localhost.
  • act as http cross-posting peer (useful to transparently optimize the communication when both sender and receiver are servers)

Creating a local server:

See examples/localServer.js

const Hubbie = require('.');
const userCredentials = {
  alice: 'psst'
};
localServer = new Hubbie();
localServer.listen({
  port: 8000
});

localServer.on('peer', (eventObj) => {
  if (eventObj.peerSecret === userCredentials[eventObj.peerName]) {
    console.log('Accepting connection', eventObj);
    localServer.send(eventObj.peerName, 'Welcome!');
    return Promise.resolve(true);
  } else {
    console.log('Client rejected');
    return Promise.resolve(false);
  }
});
localServer.on('message', (peerName, msg) => {
  console.log(`Server sees message from ${peerName}`, msg)
});
setTimeout(() => {
  console.log('Closing server');
  localServer.close();
}, 10000);

Creating a client:

See examples/localClient.js

const Hubbie = require('.');

localClient = new Hubbie();
localClient.addClient({
  peerName: 'bob', // for local reference only
  peerUrl: 'ws://localhost:8000',
  myName: 'alice', // for remote credentials
  mySecret: 'psst' // for remote credentials
});
localClient.send('bob', 'hi there!');

localClient.on('message', (peerName, msg) => {
  console.log(`Client sees message from ${peerName}`, msg)
});

setTimeout(() => {
  console.log('Closing client');
  localClient.close();
}, 5000);

Running multiple agents in the same process

When agents run in the same process, there is no need for them to connect over a WebSocket. Hubbie allows them to listen on a name:

const Hubbie = require('.')

const alice = new Hubbie();
alice.listen({ myName: 'alice' });

const bob = new Hubbie();
bob.listen({ myName: 'bob' });

alice.send('bob', 'Hello Bob!');

bob.on('message', (peerName, msg) => {
  console.log(`Bob sees message from ${peerName}`, msg)
});

Running two Hubbies in Server-Server configuration

Here, instead of using a WebSocket server and a WebSocket client, both Hubbies run a http server and a http client. To send a message to the other Hubbie, they do a http post. This setup is not compatible with Hubbie#listen({ server }), since Hubbie will not interfere with the POST handler of the existing http server you give it. So please either use Hubbie#listen({ port }) or Hubbie#listen({ tls }) instead.

The 'peer' event will be triggered the first time a message from a new peer is received. If you don't return false from any handler of this event, all subsequent POSTs that use that peerName and peerSecret in the URL will trigger a 'message' event.

const Hubbie = require('.');
const alice = new Hubbie();
const bob = new Hubbie();
alice.listen({ port: 8081 });
bob.listen({ port: 8082 });
alice.on('peer', ({ peerName, peerSecret }) => {
  if (peerName == 'bob' && peerSecret == 'boo') {
    console.og('Accepting connection from Bob');
    return true;
  } else {
    console.log('Client rejected');
    return false;
  }
});
bob.on('peer', ({ peerName, peerSecret }) => {
  if (peerName == 'alice' && peerSecret == 'pssst') {
    console.og('Accepting connection from Alice');
    return true;
  } else {
    console.log('Client rejected');
    return false;
  }
});
alice.addClient({
  myName: 'alice',
  mySecret: 'pssst'
  peerName: 'bob',
  peerUrl: 'http://localhost:8082'
});
bob.addClient({
  myName: 'bob',
  mySecret: 'boo'
  peerName: 'alice',
  peerUrl: 'http://localhost:8081'
});
alice.on('message', (peerName, msg) => {
  console.log(`Alice sees message from ${peerName}`, msg)
});
alice.on('message', (peerName, msg) => {
  console.log(`Alice sees message from ${peerName}`, msg)
});
alice.send('bob', 'Hello Bob!');
bob.send('alice', 'Hello Alice!');

setTimeout(() => {
  console.log('Closing servers');
  alice.close();
  bob.close();
}, 10000);

Built-in LetsEncrypt registration

If instead of listen you specify tls, the server will listen for secure WebSockets on port 443. This will not work on your laptop, or on a PaaS service like Heroku; you need a server (VPS) with its own IPv4 address, and make sure 'ws.example.com' points to your server and DNS has propagated.

Then, SSH into your server:

ssh [email protected]

Then run this node script:

const a = new Hubbie();
a.listen({ tls: 'ws.example.com' });

Use in test environments

This is useful if you want to get rid of previously registered in-memory channels without ending the node process:

  afterEach(function () {
    Hubbie.unregisterNames();
  });