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

websocket-toolkit

v1.0.2

Published

Universal WebSocket client. Auto-reconnect, message queue, channels, request/response, heartbeat. Zero dependencies.

Downloads

281

Readme

websocket-toolkit — Universal WebSocket Client

A standalone, universal WebSocket client that works with any backend. One script tag. Zero dependencies.

Also published as wskit-client.

Install

npm

npm install websocket-toolkit

ES Modules

import WSKit from 'websocket-toolkit';

CommonJS

const WSKit = require('websocket-toolkit');

Script Tag

<script src="https://unpkg.com/websocket-toolkit/wskit.js"></script>

Quick Start

const ws = WSKit.connect('wss://yourserver.com/ws', {
  onOpen: () => console.log('Connected'),
  onMessage: (msg) => console.log('Received:', msg),
});

ws.send({ type: 'chat', text: 'Hello!' });

Features

| Feature | Description | |---|---| | Auto-Reconnect | Exponential backoff (1s → 2s → 4s → ... up to 30s) | | Message Queue | Buffers sends while disconnected, flushes on reconnect | | Channels | Subscribe to message types: ws.on('chat', handler) | | Request/Response | ws.request(data) returns a Promise | | Heartbeat | Configurable keep-alive ping | | Auto-JSON | Auto parse/stringify JSON messages | | Debug Mode | debug: true for verbose console logging | | TypeScript | Full type declarations included |

Channels

Route messages by type:

// Subscribe — returns an unsubscribe function
const off = ws.on('chat', (msg) => {
  console.log(msg.user + ':', msg.text);
});

// Unsubscribe
off();

// Or unsubscribe by type
ws.off('chat');

Messages are routed by the type field by default (configurable via typeField).

Request/Response

Send a message and wait for a matching response:

const user = await ws.request({ type: 'getUser', id: 123 });
console.log(user.name);

The server should include the same _id field in its response. The _id is auto-generated and matched.

// Server-side example (Node.js)
socket.on('message', (raw) => {
  const msg = JSON.parse(raw);
  if (msg.type === 'getUser') {
    socket.send(JSON.stringify({
      _id: msg._id,  // echo back the _id
      name: 'John',
      email: '[email protected]',
    }));
  }
});

Message Queue

Messages sent while disconnected are buffered and flushed on reconnect:

const ws = WSKit.connect(url, {
  queueWhileDisconnected: true,  // default
  maxQueueSize: 100,             // default
});

// These will be queued if not connected yet
ws.send({ type: 'init', token: 'abc' });
ws.send({ type: 'subscribe', channel: 'updates' });

// Check queue
console.log(ws.queueSize); // 2

// Clear queue if needed
ws.clearQueue();

Front-End Configuration

const ws = WSKit.connect('wss://yourserver.com/ws', {
  // Reconnection
  reconnect: true,              // auto-reconnect (default: true)
  reconnectBaseMs: 1000,        // initial delay (default: 1000)
  reconnectMaxMs: 30000,        // max delay (default: 30000)
  maxReconnectAttempts: 0,      // 0 = unlimited (default: 0)

  // Heartbeat
  heartbeatMs: 30000,           // ping interval (default: 30000)
  heartbeatMessage: { type: 'ping' },

  // Message queue
  queueWhileDisconnected: true, // buffer sends (default: true)
  maxQueueSize: 100,            // max queue size (default: 100)

  // JSON
  autoJSON: true,               // auto parse/stringify (default: true)
  typeField: 'type',            // channel routing field (default: 'type')

  // Request/response
  requestTimeout: 10000,        // timeout in ms (default: 10000)
  idField: '_id',               // matching field (default: '_id')

  // Debug
  debug: false,                 // console logging (default: false)

  // Callbacks
  onOpen: () => {},
  onClose: (e) => {},
  onError: (e) => {},
  onMessage: (data) => {},
  onReconnect: (attempt) => {},
  onStateChange: (newState, prevState) => {},
});

API

| Method / Property | Description | |---|---| | ws.send(data) | Send data (auto-JSON if enabled). Returns true if sent, false if queued. | | ws.request(data, timeout?) | Send and wait for matching response. Returns Promise. | | ws.on(type, handler) | Subscribe to message type. Returns unsubscribe function. | | ws.off(type, handler?) | Unsubscribe. Omit handler to remove all for that type. | | ws.disconnect() | Close connection and stop reconnecting. | | ws.reconnect() | Manually reconnect. | | ws.clearQueue() | Clear the message queue. | | ws.state | "connecting", "open", "closed", or "reconnecting" | | ws.queueSize | Number of queued messages | | ws.socket | Raw WebSocket instance | | ws.url | The WebSocket URL | | ws.reconnectAttempts | Current reconnect attempt count |

Related Packages

| Package | Description | |---|---| | rte-rich-text-editor | Core editor — lightweight, 33 toolbar controls | | rte-rich-text-editor-ws | WebSocket connector for RTE | | rte-rich-text-editor-bundle | RTE + WebSocket in one file | | rte-rich-text-editor-pro | Pro editor — 16 toolbar groups, AI, slash commands, mentions | | rte-rich-text-editor-pro-ws | RTEPro + WebSocket in one file | | wskit-client | Universal WebSocket client | | websocket-toolkit | Universal WebSocket client (alternate name) |

Website: rte.whitneys.co · GitHub: MIR-2025/rte

Backend Examples

Node.js (ws)

import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 8080 });

wss.on('connection', (socket) => {
  socket.on('message', (raw) => {
    const msg = JSON.parse(raw);

    switch (msg.type) {
      case 'chat':
        // Broadcast to all clients
        wss.clients.forEach((client) => {
          if (client.readyState === 1) {
            client.send(JSON.stringify(msg));
          }
        });
        break;

      case 'getUser':
        // Request/response — echo back _id
        socket.send(JSON.stringify({
          _id: msg._id,
          name: 'John',
          email: '[email protected]',
        }));
        break;

      case 'ping':
        socket.send(JSON.stringify({ type: 'pong' }));
        break;
    }
  });
});

Python (websockets)

import asyncio, json, websockets

connected = set()

async def handler(ws):
    connected.add(ws)
    try:
        async for raw in ws:
            msg = json.loads(raw)

            if msg.get("type") == "chat":
                for client in connected:
                    if client != ws:
                        await client.send(json.dumps(msg))

            elif msg.get("type") == "getUser":
                await ws.send(json.dumps({
                    "_id": msg["_id"],
                    "name": "John",
                    "email": "[email protected]",
                }))

            elif msg.get("type") == "ping":
                await ws.send(json.dumps({"type": "pong"}))
    finally:
        connected.discard(ws)

asyncio.run(websockets.serve(handler, "0.0.0.0", 8080))

Go (gorilla/websocket)

package main

import (
    "encoding/json"
    "log"
    "net/http"
    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}

func handler(w http.ResponseWriter, r *http.Request) {
    conn, _ := upgrader.Upgrade(w, r, nil)
    defer conn.Close()

    for {
        _, raw, err := conn.ReadMessage()
        if err != nil { break }

        var msg map[string]interface{}
        json.Unmarshal(raw, &msg)

        switch msg["type"] {
        case "getUser":
            resp, _ := json.Marshal(map[string]interface{}{
                "_id":   msg["_id"],
                "name":  "John",
                "email": "[email protected]",
            })
            conn.WriteMessage(websocket.TextMessage, resp)

        case "ping":
            resp, _ := json.Marshal(map[string]string{"type": "pong"})
            conn.WriteMessage(websocket.TextMessage, resp)
        }
    }
}

func main() {
    http.HandleFunc("/ws", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

PHP (Ratchet)

<?php
// composer require cboden/ratchet

use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;

require __DIR__ . '/vendor/autoload.php';

class Handler implements MessageComponentInterface {
    protected $clients;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
    }

    public function onOpen(ConnectionInterface $conn) {
        $this->clients->attach($conn);
    }

    public function onMessage(ConnectionInterface $from, $raw) {
        $msg = json_decode($raw, true);

        switch ($msg['type'] ?? '') {
            case 'chat':
                // Broadcast to all other clients
                foreach ($this->clients as $client) {
                    if ($client !== $from) {
                        $client->send(json_encode($msg));
                    }
                }
                break;

            case 'getUser':
                // Request/response — echo back _id
                $from->send(json_encode([
                    '_id'   => $msg['_id'],
                    'name'  => 'John',
                    'email' => '[email protected]',
                ]));
                break;

            case 'ping':
                $from->send(json_encode(['type' => 'pong']));
                break;
        }
    }

    public function onClose(ConnectionInterface $conn) {
        $this->clients->detach($conn);
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        $conn->close();
    }
}

$server = IoServer::factory(
    new HttpServer(new WsServer(new Handler())),
    8080
);
$server->run();

License

MIT — phpMyDEV, LLC