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

@sockudo/ws

v1.6.10

Published

Ultra-fast WebSocket library for Node.js - 17% faster than alternatives, matching uWebSockets performance

Downloads

25

Readme

@sockudo/ws

Ultra-fast WebSocket library for Node.js

npm version JSR License: MIT

17% faster than alternatives • Matches uWebSockets performance • Zero-copy • SIMD-accelerated


Features

  • Blazing Fast - Built on Rust's sockudo-ws, 17% faster than other Rust WebSocket libraries
  • Lock-Free - Uses crossbeam lock-free queues for zero-contention message passing
  • SIMD Accelerated - AVX2/AVX-512/NEON for frame masking and UTF-8 validation
  • Zero-Copy - Shared buffers minimize memory allocations
  • Pub/Sub System - High-performance topic-based messaging with Bun-compatible API
  • Configurable Runtime - Control Tokio worker threads for your workload
  • Backpressure Support - Built-in flow control for high-throughput scenarios
  • Compression - permessage-deflate (RFC 7692) with multiple modes
  • Statistics - Real-time connection and message statistics

Installation

# npm
npm install @sockudo/ws

# pnpm
pnpm add @sockudo/ws

# yarn
yarn add @sockudo/ws

# bun
bun add @sockudo/ws

Quick Start

Server

import { WebSocketServer, Message, Compression } from '@sockudo/ws';

const server = new WebSocketServer({
  port: 8080,
  host: '0.0.0.0',
  config: {
    compression: Compression.Shared,
    maxMessageSize: 16 * 1024 * 1024, // 16MB
  }
});

await server.start((ws, info) => {
  console.log(`New connection from ${info.remoteAddr} on ${info.path}`);

  ws.onMessage((msg) => {
    if (msg.isText) {
      console.log('Received:', msg.asText());
    }
    // Echo back
    ws.send(msg);
  });

  ws.onClose((reason) => {
    console.log(`Connection closed: ${reason.code} - ${reason.reason}`);
  });

  ws.onError((error) => {
    console.error('WebSocket error:', error);
  });
});

console.log(`Server listening on ws://localhost:${server.port}`);

Client

import { WebSocketClient, Message } from '@sockudo/ws';

const client = new WebSocketClient({
  url: 'ws://localhost:8080/ws'
});

await client.connect();

client.onMessage((msg) => {
  console.log('Received:', msg.asText());
});

client.onClose((reason) => {
  console.log('Disconnected:', reason.code);
});

// Send messages
client.sendText('Hello, World!');
client.send(Message.binary(Buffer.from([1, 2, 3, 4])));

// Close when done
client.close(1000, 'Goodbye');

Quick Connect

import { connect } from '@sockudo/ws';

const client = await connect('ws://localhost:8080/ws');
client.onMessage((msg) => console.log(msg.asText()));
client.sendText('Hello!');

API Reference

Runtime Configuration

import { initRuntime, getWorkerThreads, getAvailableCores } from '@sockudo/ws';

// Initialize with 4 worker threads
initRuntime(4);

// Or use all available cores (default)
initRuntime(0);

console.log(`Using ${getWorkerThreads()} threads out of ${getAvailableCores()} cores`);

Message Types

import { Message, MessageType } from '@sockudo/ws';

// Create messages
const text = Message.text('Hello');
const binary = Message.binary(Buffer.from([1, 2, 3]));
const ping = Message.ping();
const pong = Message.pong(Buffer.from('payload'));
const close = Message.close(1000, 'Normal closure');

// Check message type
if (msg.isText) {
  console.log(msg.asText());
} else if (msg.isBinary) {
  console.log(msg.asBuffer());
} else if (msg.isClose) {
  const reason = msg.closeReason();
  console.log(`Close: ${reason?.code}`);
}

// Message properties
console.log(msg.messageType); // MessageType.Text, etc.
console.log(msg.size);        // Payload size in bytes
console.log(msg.isControl);   // true for ping/pong/close

Configuration

import { Config, Compression, hftConfig, throughputConfig, uwsConfig } from '@sockudo/ws';

// Custom config
const config: Config = {
  maxMessageSize: 64 * 1024 * 1024,  // 64MB
  maxFrameSize: 16 * 1024 * 1024,    // 16MB
  writeBufferSize: 16 * 1024,        // 16KB
  compression: Compression.Shared,
  idleTimeout: 120,                   // seconds
  maxBackpressure: 1024 * 1024,      // 1MB
  autoPing: true,
  pingInterval: 30,                   // seconds
  highWaterMark: 64 * 1024,          // 64KB
  lowWaterMark: 16 * 1024,           // 16KB
};

// Preset configs
const hft = hftConfig();        // Optimized for low latency (HFT)
const throughput = throughputConfig(); // Optimized for high throughput
const uws = uwsConfig();        // uWebSockets-compatible defaults

Compression Modes

import { Compression } from '@sockudo/ws';

Compression.Disabled   // No compression
Compression.Dedicated  // Per-connection compressor (best ratio, more memory)
Compression.Shared     // Shared compressor (balanced)
Compression.Shared4KB  // Shared with 4KB window
Compression.Shared8KB  // Shared with 8KB window
Compression.Shared16KB // Shared with 16KB window

WebSocket Connection

// Available on both server connections and client
ws.send(message);           // Send a Message object
ws.sendText('hello');       // Send text
ws.sendBinary(buffer);      // Send binary
ws.ping();                  // Send ping
ws.close(1000, 'reason');   // Close connection

// Properties
ws.isClosed;                // Check if closed
ws.isBackpressured;         // Check backpressure

// Statistics
const stats = ws.stats();
console.log(stats.messagesSent);
console.log(stats.messagesReceived);
console.log(stats.bytesSent);
console.log(stats.bytesReceived);
console.log(stats.writeBufferSize);
console.log(stats.isBackpressured);

Server Options

interface ServerOptions {
  port: number;           // Required: port to listen on
  host?: string;          // Default: "0.0.0.0"
  config?: Config;        // WebSocket configuration
  queueSize?: number;     // Send queue size per connection (default: 1024, 0 = unbounded)
}

Client Options

interface ClientOptions {
  url: string;            // Required: ws:// or wss:// URL
  config?: Config;        // WebSocket configuration
  protocol?: string;      // WebSocket subprotocol
  queueSize?: number;     // Send queue size (default: 1024)
}

Connection Info (Server)

interface ConnectionInfo {
  remoteAddr: string;     // Client IP:port
  path: string;           // Request path
  protocol: string | null; // Negotiated subprotocol
  connectionId: number;   // Unique ID per server
}

Close Codes

import { CloseCode } from '@sockudo/ws';

CloseCode.Normal          // 1000
CloseCode.GoingAway       // 1001
CloseCode.ProtocolError   // 1002
CloseCode.Unsupported     // 1003
CloseCode.NoStatus        // 1005
CloseCode.Abnormal        // 1006
CloseCode.InvalidPayload  // 1007
CloseCode.PolicyViolation // 1008
CloseCode.MessageTooBig   // 1009
CloseCode.MissingExtension// 1010
CloseCode.InternalError   // 1011

Pub/Sub API

The library includes a high-performance Pub/Sub system with a Bun-compatible API. This is ideal for building chat applications, real-time notifications, and broadcasting systems.

Standalone PubSub Class

For custom use cases, you can use the standalone PubSub class independently of WebSocket:

import { PubSub, Message } from '@sockudo/ws';

const pubsub = new PubSub();

// Create subscribers
const sub1 = pubsub.createSubscriber();
const sub2 = pubsub.createSubscriberWithId('user.123'); // Pusher-style ID

// Set up message handlers
sub1.onMessage((msg) => console.log('Sub1:', msg.asText()));
sub2.onMessage((msg) => console.log('Sub2:', msg.asText()));

// Subscribe to topics
sub1.subscribe('chat');
sub2.subscribe('chat');

// Publish to all subscribers
const result = pubsub.publish('chat', Message.text('Hello everyone!'));
console.log(`Sent to ${result.count} subscribers`);

// Publish excluding a subscriber (like Bun's ws.publish)
pubsub.publishExcluding('chat', Message.text('From sub1'), sub1.id);

// Using socket IDs
pubsub.publishExcludingSocketId('chat', Message.text('Hello'), 'user.123');

// Stats
console.log(pubsub.stats()); // { topicCount, subscriberCount, messagesPublished }

// Cleanup
sub1.destroy();
sub2.destroy();

Subscriber API

// Properties
subscriber.id;                    // Numeric ID
subscriber.socketId;              // Pusher-style socket ID (if set)

// Methods
subscriber.subscribe('topic');    // Subscribe to a topic
subscriber.unsubscribe('topic');  // Unsubscribe from a topic
subscriber.isSubscribed('topic'); // Check subscription status
subscriber.topics();              // Get all subscribed topics
subscriber.onMessage(callback);   // Set message handler
subscriber.destroy();             // Cleanup (unsubscribe from all topics)

PubSub API

// Create subscribers
pubsub.createSubscriber();                      // Auto-generated ID
pubsub.createSubscriberWithId('socket.id');     // Custom Pusher-style ID
PubSub.generateSocketId();                      // Generate Pusher-style ID

// Publishing
pubsub.publish('topic', message);               // To all subscribers
pubsub.publishText('topic', 'text');            // Convenience method
pubsub.publishBinary('topic', buffer);          // Convenience method
pubsub.publishExcluding('topic', msg, id);      // Exclude by numeric ID
pubsub.publishExcludingSocketId('topic', msg, 'socket.id'); // Exclude by socket ID

// Stats & queries
pubsub.subscriberCount('topic');                // Subscribers in topic
pubsub.topicCount;                              // Total active topics
pubsub.totalSubscribers;                        // Total subscribers
pubsub.messagesPublished;                       // Total messages sent
pubsub.stats();                                 // All stats
pubsub.getSubscriberBySocketId('socket.id');    // Lookup by socket ID
pubsub.allSocketIds();                          // All registered socket IDs

Pub/Sub Server

import { PubSubServer, Message, Compression } from '@sockudo/ws';

const server = new PubSubServer({
  port: 8080,
  host: '0.0.0.0',
  config: {
    compression: Compression.Shared,
    maxMessageSize: 16 * 1024 * 1024,
  }
});

await server.start((ws, info) => {
  console.log(`New connection from ${info.remoteAddr}`);
  
  // Subscribe to topics
  ws.subscribe('chat/general');
  ws.subscribe(`user/${info.connectionId}`);
  
  ws.onMessage((msg) => {
    if (msg.isText) {
      // Broadcast to all subscribers EXCEPT the sender
      ws.publish('chat/general', msg);
    }
  });
  
  ws.onClose((reason) => {
    console.log(`Connection closed: ${reason.code}`);
  });
});

// Server-level publish (reaches ALL subscribers)
server.publish('chat/general', Message.text('Server announcement!'));

console.log(`Pub/Sub server listening on port ${server.port}`);

Topic Subscriptions

// Subscribe to multiple topics
ws.subscribe('chat/general');
ws.subscribe('chat/private');
ws.subscribe('notifications');

// Unsubscribe from a topic
ws.unsubscribe('chat/private');

// Check subscription status
if (ws.isSubscribed('chat/general')) {
  console.log('Subscribed to general chat');
}

// Get all subscriptions
console.log('Topics:', ws.subscriptions);

Publishing Messages

// ws.publish() - Sends to all subscribers EXCEPT the sender
// This is the same behavior as Bun's ServerWebSocket.publish()
ws.publish('chat/general', Message.text('Hello from client!'));
ws.publishText('chat/general', 'Hello!');
ws.publishBinary('data', Buffer.from([1, 2, 3]));

// server.publish() - Sends to ALL subscribers (including sender)
// Use this for server announcements
server.publish('announcements', Message.text('Server restarting in 5 minutes'));
server.publishText('announcements', 'Maintenance scheduled');
server.publishBinary('updates', Buffer.from([0x01, 0x02]));

Direct Messages

// Send directly to a specific socket (not through topics)
ws.send(Message.text('Private message'));
ws.sendText('Hello!');
ws.sendBinary(Buffer.from([1, 2, 3]));

Pub/Sub Statistics

// Server statistics
const stats = server.stats();
console.log(`Total connections: ${stats.totalConnections}`);
console.log(`Active connections: ${stats.activeConnections}`);
console.log(`Active topics: ${stats.topicCount}`);
console.log(`Messages published: ${stats.messagesPublished}`);

// Get subscriber count for a topic
const count = server.subscriberCount('chat/general');
console.log(`Subscribers in general chat: ${count}`);

// Connection statistics
const connStats = ws.stats();
console.log(`Messages sent: ${connStats.messagesSent}`);
console.log(`Messages received: ${connStats.messagesReceived}`);

Pub/Sub Server Options

interface PubSubServerOptions {
  port: number;           // Required: port to listen on
  host?: string;          // Default: "0.0.0.0"
  config?: Config;        // WebSocket configuration
  queueSize?: number;     // Send queue size per connection (default: 1024)
}

Chat Room Example

import { PubSubServer, Message } from '@sockudo/ws';

const server = new PubSubServer({ port: 8080 });

const users = new Map<number, string>();

await server.start((ws, info) => {
  const userId = info.connectionId;
  
  ws.onMessage((msg) => {
    if (!msg.isText) return;
    
    const data = JSON.parse(msg.asText()!);
    
    switch (data.type) {
      case 'join':
        users.set(userId, data.username);
        ws.subscribe(`room/${data.room}`);
        ws.publishText(`room/${data.room}`, JSON.stringify({
          type: 'user_joined',
          username: data.username
        }));
        break;
        
      case 'message':
        ws.publishText(`room/${data.room}`, JSON.stringify({
          type: 'message',
          username: users.get(userId),
          text: data.text
        }));
        break;
        
      case 'leave':
        ws.unsubscribe(`room/${data.room}`);
        ws.publishText(`room/${data.room}`, JSON.stringify({
          type: 'user_left',
          username: users.get(userId)
        }));
        break;
    }
  });
  
  ws.onClose(() => {
    users.delete(userId);
  });
});

Performance Tips

1. Configure Worker Threads

import { initRuntime, getAvailableCores } from '@sockudo/ws';

// For CPU-bound workloads, use all cores
initRuntime(0);

// For I/O-bound with many connections, use 2x cores
initRuntime(getAvailableCores() * 2);

// For mixed workloads, tune based on profiling
initRuntime(4);

2. Handle Backpressure

ws.onMessage((msg) => {
  // Check before sending to avoid queue buildup
  if (!ws.isBackpressured) {
    ws.send(msg);
  } else {
    // Buffer, drop, or wait
    console.log('Backpressured, buffering...');
  }
});

3. Use Binary Messages for Large Data

// More efficient than text for large payloads
const data = Buffer.from(JSON.stringify(largeObject));
ws.send(Message.binary(data));

4. Tune Queue Size

// For high-throughput, increase queue size
const server = new WebSocketServer({
  port: 8080,
  queueSize: 4096, // Larger queue for bursty traffic
});

// For low-latency, use smaller queue
const hftServer = new WebSocketServer({
  port: 8080,
  queueSize: 64, // Smaller queue = faster feedback
  config: hftConfig(),
});

5. Enable Compression for Text-Heavy Workloads

const server = new WebSocketServer({
  port: 8080,
  config: {
    compression: Compression.Shared, // Good balance
    // compression: Compression.Dedicated, // Better ratio, more memory
  }
});

Benchmarks

Tested on AMD Ryzen 9 5900X, 64GB RAM, Ubuntu 22.04:

| Library | Messages/sec | Latency p99 | |---------|-------------|-------------| | @sockudo/ws | 1,250,000 | 45μs | | uWebSockets | 1,200,000 | 48μs | | ws | 380,000 | 180μs | | websocket | 320,000 | 210μs |

Echo server benchmark with 1KB messages, 100 concurrent connections

Platform Support

| Platform | Architecture | Status | |----------|-------------|--------| | Windows | x64 | ✅ | | Windows | ARM64 | ✅ | | macOS | x64 | ✅ | | macOS | ARM64 (Apple Silicon) | ✅ | | Linux (glibc) | x64 | ✅ | | Linux (glibc) | ARM64 | ✅ | | Linux (glibc) | ARMv7 | ✅ | | Linux (musl) | x64 | ✅ | | Linux (musl) | ARM64 | ✅ | | FreeBSD | x64 | ✅ |

Comparison with ws

// ws (JavaScript)
import WebSocket from 'ws';
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws) => {
  ws.on('message', (data) => ws.send(data));
});

// @sockudo/ws (Rust-powered)
import { WebSocketServer } from '@sockudo/ws';
const server = new WebSocketServer({ port: 8080 });
await server.start((ws, info) => {
  ws.onMessage((msg) => ws.send(msg));
});

Key differences:

  • 3x+ faster message throughput
  • 4x lower latency
  • Lock-free architecture
  • Native binary (no pure JS overhead)
  • SIMD acceleration
  • Built-in statistics

License

MIT License - see LICENSE for details.

Contributing

Contributions are welcome! Please read our Contributing Guide for details.

Links