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

clear-ws

v1.0.6

Published

Route websocket messages. Reconnect and resubscribe.

Downloads

6

Readme

clear-ws

Working with websockets traditionally has some issues. Messy handling of messages, convoluted and leaky reconnect logic. This is especially true when dealing with messages of different types on a single connection.

This package aims to resolve these issues by:

  • providing ways to route messages by template object (similar to app.use('/endpoint', handler) in express),
  • tracking of connection status and reconnecting if needed,
  • taking steps necessary to restore subscriptions to data channels after reconnect.

API

  • Send and receive messages (can be used on client and on server):
    • open, close and send methods return promises.
    • on method accepts a template object or a matcher function to listen* for matching incoming messages.
    • once method returns a promise to await for a single incoming message.
  • Reconnect:
    • Send ping requests on a set interval (ping.heartbeatMs option).
    • Send a ping request if there are no incoming messages for a set amount of time (ping.checkAliveMs option).
    • Restore dead connection and subscriptions (reconnect.delayMs option).
    • subscribe method accepts a function that performs steps required to restore a subsciption.

*) NOTE: Listeners are cleared after the connection is closed. Use keep: true option or subscribe to preserve listeners after reconnect.

Check out typescript types, tests and examples below for more details.

Examples

Send and receive messages

Imagine there is a server that provides the information about weather on a websocket.

  • When a client connects to it the server sends a message with a list of available channels.
  • A client then selects a channel. After that a server sends updates to the client.
  • After some time a client is not interested in getting 'temperature' updates anymore so he unselects the channel.

So the communication between them may look like this:

1. [client connects]
2. server: { type: 'listChannels', channels: ['temperature', 'pressure'] }
3. client: { type: 'selectChannel', channel: 'temperature' }
4. server: { type: 'update', channel: 'temperature', value: 21.8' }
   server: { type: 'update', channel: 'temperature', value: 22.2' }
   ...
5. client: { type: 'unselectChannel', channel: 'temperature' }

This would look like this in the code:

const { WsService } = require('clear-ws')
// 1. Open connection to the server
const config = { url: 'ws://weather-server.com' }
const ws = new WsService(config)
await ws.open()

// 2. Wait for the list of channels from server
const responseTemplate = { type: 'listChannels' }
const response = await ws.once(responseTemplate)
if (!response.channels.includes('temperature')) {
  throw Error('temperature channel not available')
}

// 3. Select temperature channel
const request = { type: 'selectChannel', channel: 'temperature' }
await ws.send(request)

// 4. Listen for updates on the channel
const template = { type: 'update', channel: 'temperature' }
const listener = (event) => {
  const message = `Current temperature is ${event.value} degrees`
  console.log(message)
}
ws.on(template, listener)
...

// 5. Stop listening for updates on the channel
ws.off(template, listener)
await ws.send({ type: 'unselectChannel', channel: 'temperature' })

Reconnect

// 1. Configure and open connection
const config = {
  url: 'ws://host.com',
  ping: {
    request: { type: 'ping' },
    response: { type: 'pong' },
    checkAliveMs: 20 * 1000,
    heartbeatMs: 60 * 1000,
  },
  reconnect: {
    delayMs: 10 * 1000,
  }
}
const ws = new WsReconnectService(config)
await ws.open()

// 2. Subscribe
const template = { type: 'subscriptionData', channelId: 18 }
const listener = (msg) => console.log('received new message', msg)
const subscribtion = await ws.subscribe(async () => {
  await ws.send({ type: 'subscribe', channelId: 18 })
  await ws.once({ type: 'subscribtionStatus', channelId: 18, status: 'subscribed' })
  ws.on(template, listener)
})
...

// 3. Unsubscribe
ws.off(template, listener)
ws.unsubscribe(subscribtion)
await ws.send({ type: 'unsubscribe', channelId: 18 })