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

sockr-server

v1.2.0

Published

Server-side websocket messaging implementation

Readme

sockr-server

A plugin-based WebSocket server built on Socket.IO for real-time messaging, authentication, and presence tracking.

Installation

npm install sockr-server

Quick Start

import { SocketServer } from "sockr-server";

const server = new SocketServer({
  cors: { origin: "*" },
  voice: {
    iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
    turn: { urls: "turn:turn.example.com", secret: "my-secret", ttl: 3600 },
  },
})
  .createStandalone()
  .useAuth(async (token) => {
    const user = await validateToken(token);
    return user ? { id: user.id } : null;
  })
  .usePresence()
  .useMessaging()
  .useGroupMessaging()
  .useVoice();

await server.listen(3000);

Server Modes

Standalone

const server = new SocketServer()
  .createStandalone()
  .useAuth(authHandler)
  .usePresence()
  .useMessaging();

await server.listen(3000);

Attach to Existing HTTP Server

import express from "express";
import { createServer } from "http";
import { SocketServer } from "sockr-server";

const app = express();
const httpServer = createServer(app);

new SocketServer()
  .attach(httpServer)
  .useAuth(authHandler)
  .usePresence()
  .useMessaging()
  .initialize();

httpServer.listen(3000);

Attach to Express (Convenience)

import express from "express";
import { SocketServer } from "sockr-server";

const app = express();

const sockr = new SocketServer()
  .attachToExpress(app)
  .useAuth(authHandler)
  .useMessaging();

await sockr.listen(3000);

API

SocketServer

The main server class that orchestrates plugins and manages the Socket.IO server lifecycle.

Constructor

new SocketServer(config?: ServerConfig)

ServerConfig options:

| Option | Type | Description | | --- | --- | --- | | port | number | Default port for listen() | | cors | object | CORS configuration for Socket.IO | | pingTimeout | number | Ping timeout in ms | | pingInterval | number | Ping interval in ms | | transports | string[] | Allowed transports (e.g. ["websocket"]) | | voice | VoiceConfig | Voice calling configuration (required to use useVoice()) |

Methods

| Method | Returns | Description | | --- | --- | --- | | createStandalone() | this | Create a standalone HTTP + Socket.IO server | | attach(httpServer) | this | Attach to an existing HTTP/HTTPS server | | attachToExpress(app) | this | Wrap an Express app and attach | | useAuth(handler) | this | Enable authentication with a handler | | usePresence() | this | Enable presence tracking | | useMessaging() | this | Enable direct messaging and typing indicators | | useGroupMessaging() | this | Enable group chats with membership and message history | | useVoice() | this | Enable WebRTC voice call signaling | | use(plugin) | this | Register a custom plugin | | initialize() | this | Initialize plugins (for use with attach()) | | listen(port?) | Promise<void> | Start the server on the given port | | close() | Promise<void> | Gracefully shut down the server | | getConnectionManager() | ConnectionManager | Access the connection manager | | getIO() | Server | Access the raw Socket.IO server instance |

ConnectionManager

Tracks active connections with dual-map lookups by socket ID and user ID.

| Method | Returns | Description | | --- | --- | --- | | addConnection(connection) | void | Register a new connection | | removeConnection(socketId) | void | Remove a connection and clean up | | getConnection(socketId) | Connection \| undefined | Look up by socket ID | | getConnectionByUserId(userId) | Connection \| undefined | Look up by user ID | | authenticateConnection(socketId, user) | void | Authenticate and map a user | | isUserOnline(userId) | boolean | Check if a user is connected | | getOnlineUsers() | string[] | Get all online user IDs | | getUsersOnlineStatus(userIds) | Record<string, boolean> | Batch online status check | | getTotalConnections() | number | Count all active connections |

Connection

Wraps a Socket.IO socket with authentication state.

| Method | Returns | Description | | --- | --- | --- | | authenticate(user) | void | Mark the connection as authenticated | | getUser() | User \| null | Get the authenticated user | | getUserId() | string \| null | Get the user ID | | getSocketId() | string | Get the socket ID | | isAuth() | boolean | Check if authenticated | | emit(event, data) | void | Send an event to the client | | disconnect() | void | Disconnect the socket | | getSocket() | Socket | Get the underlying Socket.IO socket |

Built-in Plugins

AuthPlugin

Handles token-based authentication. When a client emits an AUTHENTICATE event with a token, the provided AuthHandler validates it and either authenticates the connection or disconnects the client.

type AuthHandler = (token: string) => Promise<User | null>;

server.useAuth(async (token) => {
  const user = await db.findUserByToken(token);
  return user ? { id: user.id } : null;
});

PresencePlugin

Broadcasts user online/offline status to all connected clients. Responds to GET_ONLINE_STATUS requests with batch status lookups.

server.usePresence();

MessagePlugin

Routes direct messages between authenticated users and supports typing indicators (TYPING_START / TYPING_STOP). Messages are delivered with a generated UUID, timestamp, and sender metadata.

server.useMessaging();

GroupPlugin

Manages multi-user group chats. Handles group creation and membership, fan-out message delivery, offline queuing, message history, and per-group typing indicators.

server.useGroupMessaging();

Key behaviors:

  • Creator is added as admin; invited members are added as member.
  • All of a user's active sockets (tabs/devices) are joined to group rooms simultaneously.
  • Messages are delivered to online members via Socket.IO rooms and queued for offline members.
  • Membership and message history are backed by the configured messageStore provider.

VoicePlugin

Handles WebRTC signaling for peer-to-peer voice calls. Media never passes through the server — only SDP offers/answers and ICE candidates are relayed.

server.useVoice();

Configure ICE/TURN servers in ServerConfig.voice:

new SocketServer({
  voice: {
    iceServers: [{ urls: "stun:stun.l.google.com:19302" }],
    turn: {
      urls: "turn:turn.example.com:3478",
      secret: "my-coturn-static-secret",
      ttl: 3600, // credential lifetime in seconds (default: 3600)
    },
  },
}).useVoice();

Key behaviors:

  • Short-lived HMAC-SHA1 TURN credentials are generated per user — the TURN secret never reaches the client.
  • Callee receives CALL_INCOMING on all their connected devices simultaneously.
  • A CALL_BUSY signal is sent if the callee is already in an active or ringing call.
  • Calls are automatically cleaned up when a user fully disconnects.

Custom Plugins

Extend the abstract Plugin class to create custom plugins:

import { Plugin } from "sockr-server";
import type { Server, Socket } from "socket.io";
import type { ConnectionManager } from "sockr-server";

class MyPlugin extends Plugin {
  constructor(io: Server, connectionManager: ConnectionManager) {
    super(io, connectionManager);
  }

  initialize(): void {
    // Called when server starts
  }

  handleConnection(socket: Socket): void {
    // Called for each new socket connection
    socket.on("my-event", (data) => {
      // Handle custom events
    });
  }
}

// Register it
server.use(new MyPlugin(server.getIO(), server.getConnectionManager()));

Connection Lifecycle

Client connects
  -> Connection created & tracked
  -> Plugins receive handleConnection()
  -> Client sends AUTHENTICATE with token
  -> AuthPlugin validates & authenticates
  -> PresencePlugin broadcasts USER_ONLINE
  -> Client can send/receive messages
  -> Client disconnects
  -> PresencePlugin broadcasts USER_OFFLINE
  -> Connection removed & cleaned up

Scripts

npm run dev     # Watch mode
npm run build   # Production build
npm run clean   # Remove dist/

Documentation

See Documentation.md for the full API reference.

License

MIT