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

@bytesocket/server

v0.5.0

Published

Shared server logic for ByteSocket WebSocket server implementations

Downloads

269

Readme

@bytesocket/server

Shared server logic for ByteSocket WebSocket server implementations. This package provides the composable building blocks -- event registries, a serializer, a middleware runner, and a message dispatcher -- that transport‑specific adaptors (@bytesocket/node, @bytesocket/uws) wire together to create a fully‑typed, real‑time server.

You do not need to install this package directly; it is a dependency of the main packages.

npm version MIT node-current GitHub GitHub stars Socket Badge

Features

  • Transport-agnostic message dispatch - MessageHandler handles decoding, middleware execution, authentication, room join/leave, and event routing. Your adaptor only needs to forward raw message/open/close events from your transport library into it.
  • Type-safe event system - full TypeScript generics for emit/listen maps, room events, socket data, and middleware callbacks.
  • Pluggable serialization - JSON (text) or MessagePack (binary) encoding, selectable per-server instance or per-message, via the shared Serializer from @bytesocket/core.
  • Shared test utilities - common test factories (/test-utils) let you run the exact same test suite against every adaptor.

Installation

npm install @bytesocket/server

This package is not meant to be used directly. Choose an adaptor that matches your WebSocket library:


Exports

Main entry (@bytesocket/server)

  • MessageHandler - decodes incoming WebSocket messages and dispatches them to auth, room, and event handlers.
  • MiddlewareRunner - registers global middleware (use()) and exposes the three dispatch primitives every adaptor needs: runGlobal (the next()-driven global middleware chain), runHooks (the next()-driven chain used for room join/leave guards), and runSyncHooks (plain fan-out for void lifecycle listeners like open/close/upgrade).
  • LifecycleServer - implements ILifecycleServer; the typed on/once/off namespace for upgrade, open, auth_success, auth_error, message, close, and error.
  • RoomsServer - implements IRoomsServer; the server-wide io.rooms namespace (typed emit, on/off/once, publishRaw, and bulk operations).
  • SocketAuthManager - drives the per-connection authentication handshake (timeout handling, success/failure dispatch, broadcast-room auto-join).
  • SocketRooms / SocketRoomsBulk - the per-connection socket.rooms namespace.
  • IByteSocket / ISocket / ISocketServer / ISocketRooms / IRoomsServer / ILifecycleServer - public API interfaces.
  • ByteSocketOptionsBase - configuration options shared by every adaptor.
  • ServerIncomingData / ServerOutgoingData - type aliases for raw WebSocket data.
  • Middleware, EventCallback, RoomEventMiddleware, AuthFunction, MiddlewareNext - callback types.
  • SocketData - the user data shape attached to every socket.
  • Everything from @bytesocket/core (CallbackRegistry, ScopedRegistry, Serializer, LifecycleTypes, type guards, etc.) is re-exported as well.

Test utilities (@bytesocket/server/test-utils)

Factory functions that create a server + test client for running integration tests across adaptors:

import { serverConnectionTest } from "@bytesocket/server/test-utils";

Available test suites:

  • serverConnectionTest - connection open/close, origin checks, header getters
  • serverHeartbeatTest - empty-binary ping/pong, automatic keep-alive
  • serverAuthTest - authentication flow (success / failure / timeout)
  • serverLifecycleTest - lifecycle hook ordering and errors
  • serverMessagingTest - message send / receive, serialization
  • serverRoomsSingleTest - single-room join/leave/emit
  • serverRoomsBulkTest - bulk room operations

Each factory function receives:

  1. The Vitest instance (import * as vitest from 'vitest')
  2. A createByteSocket function
  3. A createByteSocketServer function
  4. A destroyByteSocketServer function

See the adaptor packages for concrete examples.


API overview

Building an adaptor

There's no base class to extend -- each adaptor implements the IByteSocket<TEvents, SD, UpgradeCallback> interface directly and composes the shared building blocks in its constructor:

import {
	ByteSocketOptionsBase,
	CallbackRegistry,
	LifecycleServer,
	LifecycleTypes,
	MessageHandler,
	MiddlewareRunner,
	RoomsServer,
	ScopedRegistry,
	Serializer,
	type IByteSocket,
	type ISocketServer,
	type SocketEvents,
} from "@bytesocket/server";

class MyByteSocket<TEvents extends SocketEvents> implements IByteSocket<TEvents> {
	#globalEvents: CallbackRegistry;
	#lifecycleEvents: CallbackRegistry<LifecycleTypes>;
	#roomsEvents: ScopedRegistry<string>;
	#serializer: Serializer;
	#middlewareRunner: MiddlewareRunner<TEvents, SocketData>;
	#messageHandler: MessageHandler<TEvents, SocketData>;
	#destroyed = false;

	readonly lifecycle: LifecycleServer<TEvents, SocketData, () => void>;
	readonly rooms: RoomsServer<TEvents, SocketData>;

	constructor(private readonly options: ByteSocketOptionsBase<TEvents> /* + defaults applied */) {
		this.#globalEvents = new CallbackRegistry(options.debug ?? false);
		this.#lifecycleEvents = new CallbackRegistry(options.debug ?? false);
		this.#roomsEvents = new ScopedRegistry(options.debug ?? false);
		this.#serializer = new Serializer(options.msgpackrOptions, options.serialization);
		this.#middlewareRunner = new MiddlewareRunner(this.#lifecycleEvents, options);
		this.#messageHandler = new MessageHandler(
			this.#lifecycleEvents,
			this.#globalEvents,
			this.#roomsEvents,
			this.#serializer,
			this.#middlewareRunner,
			options,
		);

		this.lifecycle = new LifecycleServer(this.#lifecycleEvents);
		this.rooms = new RoomsServer(this.#lifecycleEvents, this.#roomsEvents, this.#serializer, this.#publishRaw.bind(this), () => this.#destroyed);
	}

	// attach(server, path), publishRaw(...), and per-connection wiring are transport-specific.
	#publishRaw(room: string, message: unknown) {
		/* publish via your transport */
	}
}

From your transport's event handlers, forward into the shared dispatcher:

// on "message"
this.#messageHandler.handle(socket, rawData, isBinary);

// on "upgrade" / "open" / "close" — plain void listeners, no next() involved
this.#middlewareRunner.runSyncHooks(this.#lifecycleEvents.listeners.get(LifecycleTypes.open), [socket], (error) => {
	if (error != null) this.#lifecycleEvents.trigger(LifecycleTypes.error, socket, { phase: "onOpen", error });
});

Per-connection sockets

Each connection gets its own auth handshake and room membership, via SocketAuthManager and SocketRooms:

import { SocketAuthManager, SocketRooms, type ISocketServer } from "@bytesocket/server";

class MySocket<TEvents extends SocketEvents> implements ISocketServer<TEvents> {
	readonly rooms: SocketRooms<TEvents>;
	#authManager: SocketAuthManager<TEvents, SocketData>;
	#closed = false;

	constructor(/* ws, serializer, userData, roomManager, options */) {
		this.rooms = new SocketRooms();
		/* publishRaw, joinRoom, leaveRoom, getRoomList, serializer, getCanSend, getClosed */
		this.#authManager = new SocketAuthManager(
			this,
			/* sendUnchecked, joinRoom, getClosed, wsClose, options, startHeartbeat? */
		);
	}

	_handleAuth(parsed, next) {
		return this.#authManager.handle(parsed, next);
	}
}

ISocket is the public, user-facing interface (what your io.on/io.lifecycle.onOpen handlers receive). ISocketServer extends it with the internal _handleAuth method that only the adaptor itself calls during the upgrade/open flow.

Common options (ByteSocketOptionsBase)

| Option | Type | Default | Description | | ------------------------ | ------------------------------------- | ---------------------------- | -------------------------------------------------- | | debug | boolean | false | Enable debug logging | | serialization | "json" | "binary" | "binary" | Payload encoding format | | broadcastRoom | string | "__bytesocket_broadcast__" | Internal room used for global broadcasts | | authTimeout | number | 0 | Max milliseconds to wait for an auth response | | middlewareTimeout | number | 0 | Timeout for global middleware | | roomMiddlewareTimeout | number | 0 | Timeout for room middleware | | idleTimeout | number | 120000 | Milliseconds before an idle connection is closed | | sendPingsAutomatically | boolean | true | Send WebSocket pings to keep the connection alive | | origins | string[] | - | Allowed origin list (empty = all allowed) | | onMiddlewareError | "ignore" | "close" | function | "ignore" | Action when global middleware errors | | onMiddlewareTimeout | "ignore" | "close" | function | "ignore" | Action when global middleware times out | | msgpackrOptions | object | - | Options forwarded to the msgpackr Packr instance | | auth | AuthFunction | - | User‑supplied authentication handler |


Usage example (via an adaptor)

import { ByteSocket } from '@bytesocket/node';   // or '@bytesocket/uws'
import { SocketEvents } from '@bytesocket/core';

type MyEvents = SocketEvents<{
  "chat:message": { text: string };
  "user:joined": { userId: string };
}>;

const io = new ByteSocket<MyEvents>({ debug: true });

io.on('chat:message', (socket, data) => {
  console.log(\`${socket.id} says: ${data.text}\`);
});

io.broadcast('user:joined', { userId: 'server' });

// attach to an HTTP server or uWS app
io.attach(server, '/ws');

Adaptors

Transport‑specific implementations:

Both expose a concrete ByteSocket class that you instantiate directly and that implements IByteSocket.


Testing with shared utilities

// packages/node/tests/connection.test.ts
import * as vitest from "vitest";
import { serverConnectionTest } from "@bytesocket/server/test-utils";
import { createByteSocket, createByteSocketServer, destroyByteSocketServer } from "./factory";

describe("ByteSocket node: Connection", () => {
	serverConnectionTest(vitest, createByteSocket, createByteSocketServer, destroyByteSocketServer);
});

Your factory file provides the three functions that wrap your specific transport setup, returning instances typed as IByteSocket<TestEvents>. The shared test suite handles the rest.


License

MIT © 2026 Ahmed Ouda