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

stelar-time-real

v3.3.7

Published

node.js realtime framework server Zero-dependency production

Readme

stelar-time-real v3

Real-time library for production. Zero dependencies. Custom binary TCP protocol + Manual WebSocket (RFC 6455 implemented from scratch). No external packages.

npm license zero-deps production


What is stelar-time-real?

stelar-time-real is a real-time communication library designed from scratch for production. It does not wrap nor depend on any external library — it implements its own binary TCP protocol and its own WebSocket (RFC 6455) using exclusively Node.js built-in modules (http, net, crypto, tls).

This means total control: no dependencies that break, no third-party vulnerabilities, no bloat, and no surprises. Every byte that travels across the network is controlled by the library.

What is it for?

  • Real-time chat (messaging, notifications, typing indicators)
  • Collaborative applications (editors, whiteboards, shared documents)
  • Binary data streaming (images, files, audio, video)
  • Internal microservices with ultra-fast communication via TCP
  • Live dashboards (metrics, monitoring, trading)
  • Real-time multiplayer games
  • Social networks, Discord-like platforms
  • IoT and connected devices

Main Features

Dual Protocol

The server supports two protocols simultaneously on different ports:

  • WebSocket — For browsers and web clients. Complete manual implementation of RFC 6455: handshake, framing, masking, close codes, RSV bits validation, max frame size enforcement.
  • Custom TCP — For server-to-server and Node.js microservices communication. Custom binary protocol with minimal overhead (7-byte header). Ultra-low latency.

Both protocols share the same server API. A WebSocket client and a TCP client can be in the same room, receive the same broadcasts, and interact as if they were the same type of connection.

Zero Dependencies

dependencies: {}

No ws, no engine.io, nothing. Just pure Node.js. This means:

  • No vulnerabilities in third-party dependencies
  • No breaking changes from external updates
  • No supply chain attacks
  • Minimal size in node_modules
  • Instant installation

Production-Ready

Every feature was designed with a real environment in mind — with users, attacks, and errors:

| Feature | Description | |---------|-------------| | Rate Limiting | Token bucket per client. Limits how many messages each client can send per time window. Prevents spam and abuse. | | Per-IP Throttling | Limits simultaneous connections from the same IP address. Prevents brute force attacks and bots. | | Max Connections | Global limit of concurrent connections. The server rejects new connections when the limit is reached. | | Max Rooms | Global limit of rooms and per-client limit. Prevents a single client from creating thousands of rooms and consuming memory. | | Graceful Shutdown | Captures SIGINT/SIGTERM, stops accepting new connections, waits for existing ones to close (with configurable timeout), and cleans up all resources. | | Health Check | HTTP /health endpoint with live server statistics. Compatible with Kubernetes, Docker, and load balancers. | | Server Metrics | getStats() method with: active connections, messages sent/received, rooms, uptime, memory usage, rate limiter entries. | | TLS/SSL | Native support for wss:// and TCP over TLS. Simple configuration with key and cert. | | Origin Checking | Whitelist of allowed origins for WebSocket connections. Prevents CSRF and cross-origin abuse. | | CORS | Automatic CORS headers on the health endpoint with support for OPTIONS preflight. | | Input Validation | Validation of event names (non-empty strings), max payload size, max frame size. | | Backpressure Handling | Handles the socket's drain event. No data is lost when the network buffer is full. | | Message Queue | On the client: message queue when disconnected. Sent automatically upon reconnection. Configurable size, discards oldest if full. | | Exponential Backoff | Smart reconnection with exponential backoff and jitter. Prevents thundering herd when the server restarts. | | O(1) Client Lookup | Client lookup by ID in constant time using an indexed Map. Scalable to tens of thousands of clients. | | No Signal Handler Leaks | SIGINT/SIGTERM handlers are properly cleaned up when calling stop(). Multiple instances don't cause MaxListenersExceeded. | | Timer unref | All internal timers use .unref(). They don't prevent the Node.js process from terminating naturally. | | Custom Rate Limiter | IRateLimiter interface to replace the built-in rate limiter with your own implementation (Redis, MongoDB, etc). | | Custom IP Tracker | IIPTracker interface to replace the built-in IP tracker with your own logic. | | Custom Client ID | generateClientId function to generate IDs with your own format. | | Event Rate Limits | Rate limits per individual event. Each event can have its own message limit. | | Per-Client Rate Limits | Rate limits per individual client with setClientRateLimit(). Override the global limit for specific clients. | | Hook System | Callbacks for every server event: rate limit exceeded, max connections, payload too large, join/leave room, etc. | | Custom Health Handler | customHealthHandler function to replace the built-in health check with your own logic. | | Runtime Config | updateConfig() method to change server configuration on the fly, without restarting. | | Client Hooks | Hooks on the client: onBeforeEmit, onMessage, onStateChange, onReconnectDelay, onMessageQueued, onQueueDrained, onError. | | Custom Reconnect | customReconnectDelay function or onReconnectDelay hook to control client reconnection logic. | | Client Runtime Config | updateOptions() method to change client configuration on the fly. |


Installation

npm install stelar-time-real

Quick Start

Basic Server

import express from 'express';
import { StelarServer } from 'stelar-time-real';

const app = express();
const server = app.listen(3000);

const stelar = new StelarServer({ server });

stelar.onConnection((client) => {
  console.log('Connected:', client.id);
  client.emit('welcome', { message: 'Welcome to the server!' });
});

stelar.on('chat', (ctx) => {
  ctx.broadcast('chat', ctx.data, ctx.id);
});

await stelar.start();

Server with Production Configuration

import express from 'express';
import { StelarServer } from 'stelar-time-real';

const app = express();
const server = app.listen(3000);

const stelar = new StelarServer({
  server,
  maxConnections: 10000,
  maxConnectionsPerIP: 50,
  maxRooms: 10000,
  maxRoomsPerClient: 50,
  maxPayloadSize: 10 * 1024 * 1024,
  rateLimit: { maxPoints: 100, windowMs: 1000 },
  healthEndpoint: '/health',
  heartbeatInterval: 30000,
  heartbeatTimeout: 60000,
  gracefulShutdown: true,
  shutdownTimeout: 10000,
  allowedOrigins: ['https://mydomain.com'],
  logger: 'info',
});

// Authentication middleware
stelar.use((ctx, next) => {
  const token = ctx.req?.headers?.authorization;
  if (!token) return ctx.ack('error', { message: 'Token required' });
  next();
});

stelar.onConnection((client) => {
  console.log(`[${client.protocol}] Client connected: ${client.id} from ${client.remoteAddress}`);
  client.setMetadata('role', 'user');
  client.emit('welcome', { id: client.id });
});

stelar.onDisconnect((client) => {
  console.log('Client disconnected:', client.id);
});

stelar.on('chat', (ctx) => {
  ctx.broadcast('chat', ctx.data, ctx.id);
});

stelar.onAck('getUser', (ctx) => {
  return { id: ctx.data.id, name: 'John', role: ctx.getMetadata('role') };
});

await stelar.start();
console.log('Server ready on port', stelar.getPort());

Client (Browser or Node.js)

import { StelarClient } from 'stelar-time-real';

const client = new StelarClient('localhost:3000', {
  reconnection: true,
  reconnectionAttempts: 10,
  reconnectionDelay: 1000,
  maxReconnectionDelay: 30000,
  ackTimeout: 5000,
  messageQueueSize: 100,
});

client.on('connect', () => console.log('Connected!'));
client.on('disconnect', () => console.log('Disconnected'));
client.on('welcome', (data) => console.log('Welcome:', data));

client.connect();

// Send message
client.emit('chat', { message: 'Hello everyone!' });

// Request-response with Promise
const user = await client.request('getUser', { id: 1 }, 'getUser');
console.log(user); // { id: 1, name: 'John', role: 'user' }

// Join rooms
client.joinRoom('general');
client.joinRoom('random');

// Send binary
const buffer = Buffer.from('binary data');
client.emitBinary('file', buffer);

Client with TCP Mode (Node.js only — maximum efficiency)

const client = new StelarClient('localhost:3001', {
  mode: 'tcp',
  reconnection: true,
});

client.on('connect', () => console.log('TCP connected!'));
client.connect();

TCP mode uses the custom binary protocol instead of WebSocket. Less overhead, lower latency, ideal for server-to-server communication.

Client with TLS/WSS (secure connections)

// WSS — Secure WebSocket
const client = new StelarClient('wss://secure.mydomain.com', {
  tls: true,
  rejectUnauthorized: true,
});

// TCP + TLS
const client = new StelarClient('secure.mydomain.com:3001', {
  mode: 'tcp',
  tls: true,
  rejectUnauthorized: true,
});

Server with TLS

import { readFileSync } from 'fs';

const stelar = new StelarServer({
  port: 3000,
  tls: {
    key: readFileSync('server-key.pem'),
    cert: readFileSync('server-cert.pem'),
  },
  tcpPort: 3001,
});

Architecture

Dual Protocol

                        stelar-time-real Server
                       ┌──────────────────────────┐
                       │                          │
   Browsers  ──────►   │  Port 3000 (WebSocket)  │
   (ws://)              │         │                │
                       │    Same logic            │
   Node.js     ──────►  │  Port 3001 (Custom TCP) │
   (tcp mode)           │         │                │
                       │                          │
                       └──────────────────────────┘

Both protocols share:

  • Same event handlers
  • Same rooms
  • Same broadcast system
  • Same ACK system
  • Same middleware
  • Same metrics

A WebSocket client and a TCP client can be in the same room and communicate without issues.

WebSocket Mode vs TCP Mode

| Aspect | WebSocket | Custom TCP | |---------|-----------|------------| | Browser | Yes | No | | Node.js | Yes | Yes | | Overhead per frame | 2-14 bytes (RFC 6455) | 7 bytes (custom header) | | Latency | Low | Ultra low | | TLS | wss:// | Native TLS | | Use case | Frontend, web apps | Microservices, backend |

Binary Protocol Format (TCP)

┌──────────────┬──────────┬───────────────┬──────────────┬──────────────┐
│ totalLen (4B) │ type (1B)│ eventLen (2B) │ event (N B)  │ payload      │
│ Big Endian   │          │ Big Endian    │ UTF-8 string │ JSON/Binary  │
└──────────────┴──────────┴───────────────┴──────────────┴──────────────┘

11 frame types:

| Type | Code | Description | |------|--------|-------------| | JSON | 0 | Event with JSON payload | | Binary | 1 | Pure binary data | | Ping | 2 | Client heartbeat | | Pong | 3 | Server response | | ACK Request | 4 | Request expecting response | | ACK Response | 5 | Response to an ACK request | | Connect | 6 | Initial connection frame | | Disconnect | 7 | Disconnection frame | | Join Room | 8 | Join a room | | Leave Room | 9 | Leave a room | | Error | 10 | Error frame |

Manual WebSocket (RFC 6455)

stelar-time-real implements WebSocket from scratch using only Node.js http and crypto. It doesn't use the ws library or any other.

The implementation includes:

  • Handshake — Calculates Sec-WebSocket-Accept with SHA-1 per RFC 6455
  • Framing — Frame parsing and creation (text, binary, ping, pong, close)
  • Masking — Applies/removes XOR mask (required client→server)
  • Fragmentation — Fragmented frame handling
  • Close codes — All close codes supported
  • Validation — RSV bits, opcode validation, max frame size
  • PING/PONG — Server responds PONG to PING correctly

Complete API

StelarServer — Options

new StelarServer({
  // Connection
  port: 3000,                     // HTTP/WebSocket port
  server: httpServer,             // Existing HTTP server (alternative to port)
  namespace: '/',                 // Namespace path
  tcpPort: 3001,                  // TCP port (false = disabled)

  // Limits
  maxConnections: 10000,          // Maximum concurrent connections
  maxConnectionsPerIP: 50,        // Maximum connections per IP address
  maxRooms: 10000,                // Maximum global rooms
  maxRoomsPerClient: 50,          // Maximum rooms per client
  maxPayloadSize: 10 * 1024 * 1024,  // Maximum payload size (10MB)
  maxFrameSize: 10 * 1024 * 1024,    // Maximum WebSocket frame size (10MB)

  // Rate Limiting
  rateLimit: {
    maxPoints: 100,               // Maximum points (messages) per window
    windowMs: 1000,               // Time window in milliseconds
  },

  // Timeouts
  heartbeatInterval: 30000,       // Ping interval (30s)
  heartbeatTimeout: 60000,        // Timeout before disconnecting (60s)
  connectTimeout: 10000,          // Initial connection timeout (10s)

  // Production
  healthEndpoint: '/health',      // Health check URL (false = disabled)
  gracefulShutdown: true,         // Capture SIGINT/SIGTERM
  shutdownTimeout: 10000,         // Maximum wait time when closing (10s)
  allowedOrigins: ['https://mydomain.com'],  // Allowed origins (null = all)
  tls: { key, cert },             // TLS options for wss:// and TCP TLS

  // Logging
  logger: 'info',                 // Level: 'debug'|'info'|'warn'|'error'|'silent'
                                  // Also accepts Logger instance or false
});

StelarServer — Methods

Events

| Method | Description | |--------|-------------| | .on(event, handler) | Listen to client events | | .onAll(handler) | Listen to all events | | .onConnection(handler) | Client connected | | .onDisconnect(handler) | Client disconnected | | .onAck(name, handler) | Register ACK handler (returns value to client) |

Message Sending

| Method | Description | |--------|-------------| | .broadcast(event, data, excludeId?) | Send to all clients (optionally exclude one) | | .to(room, event, data, excludeId?) | Send to a room (optionally exclude) | | .toId(id, event, data) | Send to a specific client — O(1) lookup | | .broadcastBinary(event, buffer) | Broadcast binary data |

Information

| Method | Description | |--------|-------------| | .getClients(room?) | Client list with their rooms | | .getRoomMembers(room) | Client IDs in a room | | .getRooms() | List of active rooms | | .getStats() | Server statistics | | .getPort() | Port the server is running on |

Lifecycle

| Method | Description | |--------|-------------| | .use(middleware) | Add connection middleware | | .start(callback?) | Start server, returns Promise<number> with the port | | .stop() | Stop server, close connections, clean up handlers |

StelarContext (ctx) — Inside handlers

Every event handler receives a context (ctx) with all available information and actions:

stelar.on('message', (ctx) => {
  // Client information
  ctx.id                        // Unique client ID
  ctx.socket                    // Raw net.Socket
  ctx.req                       // HTTP request (null for TCP)
  ctx.data                      // Received data
  ctx.clientInfo                // Client info
  ctx.clientInfo.rooms          // Client's room Set
  ctx.clientInfo.metadata       // Custom metadata Map
  ctx.clientInfo.remoteAddress  // Client's IP address
  ctx.clientInfo.protocol       // 'ws' or 'tcp'

  // Actions — Send messages
  ctx.emit('event', data)               // Send to this client
  ctx.send('response', data)            // Respond to ACK
  ctx.emitBinary('event', buffer)       // Send binary
  ctx.broadcast('event', data)          // Send to all (excluding self)
  ctx.broadcastBinary('event', buf)     // Binary broadcast
  ctx.to('room', 'event', data)         // Send to a room
  ctx.toId('id', 'event', data)         // Send to specific client (O(1))

  // Actions — Rooms
  ctx.joinRoom('room')                  // Join a room
  ctx.leaveRoom('room')                 // Leave a room
  ctx.getClients('room')                // List room clients

  // Actions — Metadata
  ctx.setMetadata('role', 'admin')      // Store custom data
  ctx.getMetadata('role')               // Read custom data

  // Actions — ACK
  ctx.ack('myAck', data)                // Respond to an ACK request
});

StelarClient — Options

new StelarClient(urlOrPort, {
  // Connection
  reconnection: true,            // Auto reconnect
  reconnectionAttempts: 10,      // Maximum attempts
  reconnectionDelay: 1000,       // Base delay (ms)
  maxReconnectionDelay: 30000,   // Maximum delay (ms)
  heartbeatInterval: 30000,      // Heartbeat interval

  // Protocol
  mode: 'ws',                    // 'ws' or 'tcp'
  maxPayloadSize: 10 * 1024 * 1024,
  maxFrameSize: 10 * 1024 * 1024,

  // ACK
  ackTimeout: 5000,              // ACK timeout (ms)

  // Message queue
  messageQueueSize: 100,         // Queued messages when disconnected

  // Security
  tls: false,                    // Enable TLS for wss:// or TCP TLS
  rejectUnauthorized: true,      // Validate TLS certificate

  // Custom headers
  headers: {},                   // Headers for WebSocket handshake

  // Logging
  logger: 'warn',                // Log level
});

StelarClient — Methods

Events

| Method | Description | |--------|-------------| | .on(event, handler) | Listen to event | | .off(event, handler) | Remove listener | | .once(event, handler) | Listen once | | .onAll(handler) | Listen to all events | | .onAck(name, handler) | Listen to ACK responses |

Sending

| Method | Description | |--------|-------------| | .emit(event, data, opts?) | Send event (opts.ack for ACK) | | .emitBinary(event, data) | Send binary data | | .sendFile(file) | Send file | | .sendImage(blob) | Send image | | .request(event, data, ackName) | Request-response with Promise |

Rooms

| Method | Description | |--------|-------------| | .joinRoom(room) | Join a room | | .leaveRoom(room) | Leave a room |

Lifecycle

| Method | Description | |--------|-------------| | .connect(callback?) | Connect to server | | .disconnect() | Disconnect and clean up all resources |

State and Metrics

| Method | Description | |--------|-------------| | .isConnected() | Is connected? | | .getState() | State: 'disconnected' | 'connecting' | 'connected' | 'reconnecting' | | .getId() | ID assigned by the server | | .getUrl() | Server URL | | .setUrl(url) | Change URL before connecting | | .getMessagesSent() | Total messages sent | | .getMessagesReceived() | Total messages received | | .getLastError() | Last error | | .getConnectTime() | Timestamp of last successful connection | | .getQueueSize() | Pending messages in queue | | .removeAllListeners(event?) | Clear listeners |

Client Events

client.on('connect', () => {
  // Connection established
});

client.on('disconnect', (info) => {
  // info = { code, reason } for WebSocket
});

client.on('reconnecting', (attempt) => {
  // Reconnection attempt number `attempt`
});

client.on('reconnect_failed', () => {
  // Reconnection attempts exhausted
});

client.on('error', (err) => {
  // Connection or protocol error
});

Health Check

The health check endpoint is designed to integrate with orchestrators like Kubernetes, Docker Swarm, or any load balancer.

curl http://localhost:3000/health

Response:

{
  "status": "ok",
  "totalConnections": 150,
  "activeConnections": 42,
  "totalMessagesReceived": 5000,
  "totalMessagesSent": 4800,
  "totalRooms": 12,
  "uptime": 3600000,
  "uptimeSeconds": 3600,
  "wsConnections": 38,
  "tcpConnections": 4,
  "memoryMB": 10.54,
  "memoryUsage": {
    "heapUsed": 11062016,
    "heapTotal": 17301504,
    "rss": 24576000,
    "external": 1245184
  },
  "rateLimiterEntries": 42
}

CORS is automatic on the health endpoint. If allowedOrigins is configured, the Access-Control-Allow-Origin header is added for matching origins. OPTIONS preflight requests return 204.


Middleware

The middleware system allows validating connections before a client is accepted:

// Token authentication
stelar.use((ctx, next) => {
  const token = ctx.req?.headers?.authorization;
  if (!token) {
    return ctx.ack('error', { message: 'Token required' });
  }
  // Validate token...
  ctx.setMetadata('userId', getUserIdFromToken(token));
  next();
});

// Custom rate limiting
stelar.use((ctx, next) => {
  const ip = ctx.req?.headers?.['x-forwarded-for'] || ctx.socket.remoteAddress;
  if (isBlocked(ip)) {
    return ctx.socket.destroy();
  }
  next();
});

// Logging
stelar.use((ctx, next) => {
  console.log(`New connection from ${ctx.clientInfo.remoteAddress}`);
  next();
});

Multiple middlewares execute in order. If a middleware doesn't call next(), the connection is rejected.


Rooms

Rooms are communication channels. A client can be in multiple rooms simultaneously:

// Server
stelar.on('joinChannel', (ctx) => {
  ctx.joinRoom(ctx.data.channel);
  ctx.to(ctx.data.channel, 'userJoined', { userId: ctx.id });
});

stelar.on('channelMessage', (ctx) => {
  const rooms = ctx.clientInfo.rooms;
  for (const room of rooms) {
    ctx.to(room, 'channelMessage', ctx.data, ctx.id);
  }
});

// Client
client.joinRoom('general');
client.joinRoom('random');
client.joinRoom('project-alpha');

Rooms are automatically cleaned up when the last client leaves or disconnects. No manual resource release needed.


ACK (Request-Response)

The ACK system enables reliable request-response communication over the real-time protocol:

// Server — Register ACK handler
stelar.onAck('getUsers', (ctx) => {
  return { users: ['John', 'Mary', 'Peter'] };
});

stelar.onAck('validateToken', (ctx) => {
  const valid = validateToken(ctx.data.token);
  if (!valid) throw new Error('Invalid token');
  return { userId: 123 };
});

// Client — Send request and wait for response
const users = await client.request('getUsers', {}, 'getUsers');
console.log(users); // { users: ['John', 'Mary', 'Peter'] }

try {
  const result = await client.request('validateToken', { token: 'abc' }, 'validateToken');
} catch (err) {
  console.log('Invalid token');
}

ACK requests have configurable timeout (ackTimeout). If the server doesn't respond within that time, the Promise is rejected.


Binary Data

Send files, images, audio, or any binary data without base64 overhead:

// Server — Receive and forward binary
stelar.on('file', (ctx) => {
  ctx.broadcastBinary('file', ctx.data); // ctx.data is a Buffer
});

// Client — Send binary
const imageBuffer = await fs.readFile('photo.png');
client.emitBinary('file', imageBuffer);

// Client — Receive binary
client.on('file', (buffer) => {
  console.log('File received:', buffer.length, 'bytes');
  fs.writeFile('received.png', buffer);
});

Server Metrics

const stats = stelar.getStats();
console.log(stats);

// {
//   totalConnections: 150,
//   activeConnections: 42,
//   totalMessagesReceived: 5000,
//   totalMessagesSent: 4800,
//   totalRooms: 12,
//   uptime: 3600000,
//   uptimeSeconds: 3600,
//   wsConnections: 38,
//   tcpConnections: 4,
//   memoryMB: 10.54,
//   memoryUsage: { ... },
//   rateLimiterEntries: 42
// }

Client Metrics

console.log('Messages sent:', client.getMessagesSent());
console.log('Messages received:', client.getMessagesReceived());
console.log('Connection time:', client.getConnectTime());
console.log('Last error:', client.getLastError());
console.log('Messages in queue:', client.getQueueSize());
console.log('State:', client.getState());
console.log('Connected?', client.isConnected());

Horizontal Scalability

stelar-time-real runs on a single server per instance. To scale to multiple instances, use Redis Pub/Sub as a bridge:

import { StelarServer } from 'stelar-time-real';
import Redis from 'redis';

const redis = Redis.createClient();
const stelar = new StelarServer({ port: 3000, tcpPort: 3001 });

// When a broadcast happens on this instance, publish to Redis
stelar.onAll((ctx) => {
  redis.publish('stelar:events', JSON.stringify({
    event: ctx.eventName,
    data: ctx.data,
    excludeId: ctx.id,
  }));
});

// When another instance publishes, emit locally
redis.subscribe('stelar:events', (message) => {
  const { event, data, excludeId } = JSON.parse(message);
  stelar.broadcast(event, data, excludeId);
});

Performance

Measurements with stress test (50 WebSocket + 20 TCP clients):

| Metric | Value | |---------|-------| | Simultaneous connections | 70 | | RAM per client | ~58 KB | | Throughput | 3,425 msg/sec | | Stable heap | ~10 MB | | Memory leaks | None detected | | MaxListeners warnings | 0 |

The library uses ~58KB per connected client. A server with 1GB of RAM can handle approximately 17,000 simultaneous connections.


Project Structure

stelar-time-real/
├── src/
│   ├── index.ts        # Server (StelarServer, RateLimiter, IPConnectionTracker)
│   ├── client.ts       # Client (StelarClient, MessageQueue)
│   ├── protocol.ts     # Binary TCP protocol (encode/decode, FrameParser)
│   ├── websocket.ts    # Manual WebSocket RFC 6455 (WSFrameParser, framing)
│   └── logger.ts       # Logger with levels
├── package.json
├── tsconfig.json
└── README.md

TypeScript

stelar-time-real is written in TypeScript and includes type definitions (.d.ts). You don't need to install separate @types:

import { StelarServer, StelarClient, StelarStats } from 'stelar-time-real';

const server: StelarServer = new StelarServer({ port: 3000 });
const stats: StelarStats = server.getStats();

Tests

# Production tests (54 assertions, 16 suites)
node test-production.mjs

# Stress test (70 clients, throughput, memory)
node test-stress.mjs

Coverage: server start/stop, health check, CORS, WS connect/emit/broadcast, TCP connect/emit/reply, rooms, ACK, max connections, rate limiting, server stats, max rooms, O(1) lookup, client metrics, binary data, origin checking, middleware.


Extensible Configuration

stelar-time-real v3.2 gives you total control over every aspect of the server and client. You can replace entire components, add hooks to customize behavior, and change configuration at runtime.

Custom Rate Limiter

Replace the built-in rate limiter (token bucket) with your own implementation. Ideal for using Redis, MongoDB, or any other store:

import { StelarServer, IRateLimiter } from 'stelar-time-real';

// Your own rate limiter with Redis
class RedisRateLimiter implements IRateLimiter {
  private redis; // your Redis connection

  constructor(redisClient) {
    this.redis = redisClient;
  }

  async check(id, cost = 1) {
    const key = `ratelimit:${id}`;
    const current = await this.redis.incr(key);
    if (current === 1) {
      await this.redis.expire(key, 1); // 1 second window
    }
    return current <= 100; // 100 per second
  }

  async reset(id) {
    await this.redis.del(`ratelimit:${id}`);
  }

  async cleanup() {
    // Redis handles expiration automatically
  }

  async size() {
    return 0; // Not applicable with Redis
  }
}

const stelar = new StelarServer({
  port: 3000,
  customRateLimiter: new RedisRateLimiter(redisClient),
});

Custom IP Tracker

Replace the per-IP connection tracker with your own logic. Useful for using a database of blocked IPs or whitelist logic:

class CustomIPTracker implements IIPTracker {
  private blockedIPs = new Set(['1.2.3.4', '5.6.7.8']);
  private vipIPs = new Set(['10.0.0.1']);
  private counts = new Map<string, number>();

  check(ip) {
    if (this.blockedIPs.has(ip)) return false; // Blocked IP
    if (this.vipIPs.has(ip)) return true; // VIP no limit
    return (this.counts.get(ip) || 0) < 20; // 20 for normal
  }

  add(ip) { this.counts.set(ip, (this.counts.get(ip) || 0) + 1); }
  remove(ip) { /* ... */ }
  getCount(ip) { return this.counts.get(ip) || 0; }
  cleanup() { /* clean expired entries */ }
}

const stelar = new StelarServer({
  port: 3000,
  customIPTracker: new CustomIPTracker(),
});

Custom Client ID Generator

Generate client IDs with your own format. By default uses UUID v4:

const stelar = new StelarServer({
  port: 3000,
  generateClientId: () => {
    return `user_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
  },
});

Event-Specific Rate Limits

Each event can have its own rate limit, independent from the global one:

const stelar = new StelarServer({
  port: 3000,
  rateLimit: { maxPoints: 100, windowMs: 1000 }, // Global: 100 msg/sec
  eventRateLimits: {
    'chat': { maxPoints: 5, windowMs: 1000 },        // Chat: 5 msg/sec
    'file-upload': { maxPoints: 2, windowMs: 10000 }, // Files: 2 every 10s
    'typing': { maxPoints: 10, windowMs: 1000 },      // Typing: 10 msg/sec
    'location': { maxPoints: 1, windowMs: 5000 },     // Location: 1 every 5s
  },
});

// You can also add/remove at runtime:
stelar.setEventRateLimit('voice', { maxPoints: 50, windowMs: 1000 });
stelar.removeEventRateLimit('voice');

Per-Client Rate Limits

Give specific clients different rate limits. Useful for premium vs free users:

stelar.onConnection((ctx) => {
  const role = ctx.getMetadata('role');

  // Premium user: 500 msg/sec
  if (role === 'premium') {
    stelar.setClientRateLimit(ctx.id, { maxPoints: 500, windowMs: 1000 });
  }
  // Verified bot: 1000 msg/sec
  else if (role === 'bot') {
    stelar.setClientRateLimit(ctx.id, { maxPoints: 1000, windowMs: 1000 });
  }
  // Normal user: uses global rate limit (100 msg/sec)
});

// Remove override (reverts to global):
stelar.removeClientRateLimit(clientId);

Rate limiting priority is: per-client override > event-specific > global > custom rate limiter.

Hook System (Server)

Hooks let you customize what happens when the server detects an event. Each hook can return false to cancel the default action:

const stelar = new StelarServer({
  port: 3000,
  hooks: {
    // When a client exceeds the rate limit
    // Return false to NOT disconnect (e.g.: just warn)
    onRateLimitExceeded: ({ clientId, event, protocol }) => {
      console.warn(`Rate limit: ${clientId} on event ${event}`);
      // return false; // Uncomment to NOT disconnect the client
    },

    // When maximum connections is reached
    onMaxConnectionsReached: ({ activeConnections, max, ip }) => {
      console.error(`Server full: ${activeConnections}/${max} from ${ip}`);
      // Send alert to Slack, etc.
    },

    // When a client tries to join a room
    // Return false to REJECT the join
    onClientJoinRoom: ({ clientId, room, metadata }) => {
      const role = metadata.get('role');
      if (room.startsWith('admin-') && role !== 'admin') {
        return false; // Reject: admins only
      }
    },

    // When a client leaves a room
    // Return false to REJECT the leave
    onClientLeaveRoom: ({ clientId, room }) => {
      // Custom logic...
    },

    // When global maximum rooms is reached
    onMaxRoomsReached: ({ clientId, room, totalRooms, max }) => {
      console.warn(`Max rooms: ${totalRooms}/${max}`);
    },

    // When a client exceeds rooms per client
    onMaxRoomsPerClientReached: ({ clientId, room, currentRooms, max }) => {
      console.warn(`Client ${clientId}: ${currentRooms}/${max} rooms`);
    },

    // When a payload is too large
    onPayloadTooLarge: ({ clientId, event, size, max }) => {
      console.warn(`Large payload: ${size} bytes from ${clientId}`);
    },

    // When an invalid message is received
    onInvalidMessage: ({ clientId, reason, protocol }) => {
      console.warn(`Invalid message from ${clientId}: ${reason}`);
    },

    // Before a broadcast
    // Return false to CANCEL the broadcast
    onBeforeBroadcast: ({ event, data, excludeId }) => {
      if (event === 'spam') return false; // Cancel spam broadcast
    },

    // When a client connects
    onClientConnect: ({ clientId, ip, protocol, metadata }) => {
      console.log(`Connected: ${clientId} via ${protocol} from ${ip}`);
    },

    // When a client disconnects
    onClientDisconnect: ({ clientId, ip, protocol, rooms }) => {
      console.log(`Disconnected: ${clientId} was in ${rooms.size} rooms`);
    },
  },
});

Custom Health Check

Replace the built-in health check with your own handler. Useful for adding database checks, disk space, etc:

const stelar = new StelarServer({
  port: 3000,
  customHealthHandler: (req, res, stats) => {
    // stats contains all server statistics

    const dbConnected = await checkDatabase();
    const diskSpace = checkDiskSpace();

    res.writeHead(dbConnected && diskSpace > 100 ? 200 : 503, {
      'Content-Type': 'application/json',
    });
    res.end(JSON.stringify({
      status: dbConnected && diskSpace > 100 ? 'healthy' : 'degraded',
      server: stats,
      database: dbConnected ? 'connected' : 'disconnected',
      diskSpaceMB: diskSpace,
      version: '3.2.0',
    }));
  },
});

Runtime Configuration

Change server configuration without restarting:

const stelar = new StelarServer({ port: 3000, maxConnections: 100 });
await stelar.start();

// Later... you need more capacity
stelar.updateConfig({
  maxConnections: 500,
  maxRooms: 5000,
  rateLimit: { maxPoints: 200, windowMs: 1000 },
  allowedOrigins: ['https://app.com', 'https://admin.app.com'],
});

// Change hooks at runtime
stelar.updateConfig({
  hooks: {
    onRateLimitExceeded: ({ clientId }) => {
      banUser(clientId); // Auto-ban instead of disconnecting
      return false; // Don't disconnect, you already banned them
    },
  },
});

// View current configuration
const config = stelar.getConfig();
console.log(config);
// {
//   maxConnections: 500,
//   maxRooms: 5000,
//   hasCustomRateLimiter: false,
//   eventRateLimits: [],
//   hooks: ['onRateLimitExceeded'],
//   ...
// }

Client Hooks

Customize client behavior with hooks:

const client = new StelarClient('localhost:3000', {
  hooks: {
    // Before sending a message — return false to cancel
    onBeforeEmit: ({ event, data }) => {
      if (event === 'debug') return false; // Don't send debug in production
      console.log(`Sending: ${event}`);
    },

    // When any message is received
    onMessage: ({ event, data, isBinary }) => {
      metrics.increment('messages.received');
      if (isBinary) metrics.increment('binary.received');
    },

    // When connection state changes
    onStateChange: ({ from, to }) => {
      console.log(`State: ${from} -> ${to}`);
      if (to === 'reconnecting') showReconnectingUI();
      if (to === 'connected') hideReconnectingUI();
    },

    // Customize reconnection delay
    onReconnectDelay: ({ attempt, defaultDelay }) => {
      // Business hours: fast reconnection
      const hour = new Date().getHours();
      if (hour >= 9 && hour <= 18) return 500;
      return defaultDelay; // Off-hours: normal delay
    },

    // When a message is queued (disconnected)
    onMessageQueued: ({ event, queueSize }) => {
      console.log(`Message queued: ${event} (queue: ${queueSize})`);
    },

    // When queue is drained after reconnecting
    onQueueDrained: ({ count }) => {
      console.log(`${count} messages sent after reconnecting`);
    },

    // When an error occurs
    onError: ({ error, context }) => {
      errorReporter.report(error, { context });
    },
  },
});

Custom Reconnect Delay

Control exactly how long to wait before each reconnection attempt:

// Option 1: Custom function
const client = new StelarClient('localhost:3000', {
  customReconnectDelay: (attempt, baseDelay, maxDelay) => {
    // Fast retry for first 3 attempts, then slow
    if (attempt <= 3) return 200;
    if (attempt <= 10) return 2000;
    return 30000; // 30s for later attempts
  },
});

// Option 2: Via hook (can change at runtime)
const client = new StelarClient('localhost:3000', {
  hooks: {
    onReconnectDelay: ({ attempt, defaultDelay }) => {
      return Math.min(100 * attempt, 10000); // Linear instead of exponential
    },
  },
});

Client Runtime Configuration

Change client configuration without reconnecting:

const client = new StelarClient('localhost:3000');
client.connect();

// Later... adjust timeouts
client.updateOptions({
  heartbeatInterval: 15000,
  ackTimeout: 10000,
  maxPayloadSize: 50 * 1024 * 1024, // 50MB
  hooks: {
    onBeforeEmit: ({ event }) => {
      if (event === 'log') return false; // No longer send logs
    },
  },
});

// View current configuration
const opts = client.getOptions();
console.log(opts);

License

MIT — Stelar