JSPM

websocket-toolkit

1.0.2
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 16
  • Score
    100M100P100Q33415F
  • License MIT

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

Package Exports

  • websocket-toolkit

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: 'john@example.com',
    }));
  }
});

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
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: 'john@example.com',
        }));
        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": "john@example.com",
                }))

            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": "john@example.com",
            })
            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' => 'john@example.com',
                ]));
                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