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

oddsockets-nodejs

v1.0.7

Published

Official Node.js SDK for OddSockets real-time messaging platform

Readme

OddSockets Node.js SDK

Real-time messaging for AI agents — as simple as git.

Your agents connect in real-time. Autonomously. OddSockets is the agent-first real-time platform: publish, subscribe, and coordinate across services with <30ms global latency and a 99.99% uptime SLA. This is the official Node.js SDK — automatic manager discovery, worker load balancing, auto-reconnect, and a drop-in migration compatibility layer.

npm version node latency uptime

npm install oddsockets-nodejs

Building an agent? OddSockets also ships a zero-boilerplate CLI — npm install -g oddsockets-cli, then oddsockets signup my-app --local and oddsockets publish alerts "Deploy complete". No API calls, no SDK wiring, just commands. Drop one line in your CLAUDE.md or .cursorrules and your agent has real-time messaging.

Why OddSockets

  • Agent-first — Built for autonomous workflows. Agents publish, subscribe, and coordinate without a human in the loop.
  • Blazing fast<30ms global latency on a multi-worker cluster with automatic failover and cross-worker fan-out.
  • Drop-in migration layer — a PubNubCompat class mirrors the classic pub/sub listener API, so existing apps migrate in minutes, not weeks (see PubNub Compatibility).
  • 19 SDKs, one platform — JavaScript, Node.js, Python, Go, Java, Rust, C#, Kotlin, Swift, Flutter, PHP, React Native, Ruby, Elixir, Svelte, C, C++, Unity, and Unreal Engine.
  • Presence & history built in — Real-time user presence, state, and message history out of the box.
  • 7-day free trial on every plan — signup issues a working key instantly (keyless for the first 48h, nothing charged during the trial). Get a key.

Features

  • Simple API - Easy-to-use interface for real-time messaging
  • Auto-reconnection - Automatic reconnection with exponential backoff
  • Message Size Validation - Industry-standard 32KB message size limits
  • Session Stickiness - Consistent worker assignment for optimal performance
  • PubNub Compatibility - Drop-in replacement for PubNub applications
  • Bulk Operations - Publish multiple messages efficiently
  • Presence Tracking - Real-time user presence and state management

Quick Start

const OddSockets = require('oddsockets-nodejs');

// Initialize the client
const client = new OddSockets({
  apiKey: 'your-api-key-here',
  userId: 'user-123'
});

// Listen for connection events
client.on('connected', () => {
  console.log('Connected to OddSockets');
});

// Get a channel and subscribe
const channel = client.channel('my-channel');

await channel.subscribe((message) => {
  console.log('Received:', message);
});

// Publish a message
await channel.publish({
  text: 'Hello World!',
  timestamp: new Date().toISOString()
});

Configuration Options

const client = new OddSockets({
  apiKey: 'your-api-key-here',        // Your OddSockets API key (or use tokenProvider)
  userId: 'user-123',                 // Optional: User identifier
  autoConnect: true,                  // Optional: Auto-connect on initialization (default: true)
  options: {                          // Optional: Additional Socket.IO options
    transports: ['websocket', 'polling'],
    timeout: 10000
  }
});

Provide either apiKey or tokenProvider (see below).

Token auth for game clients (tokenProvider)

Game clients should never ship a raw API key. Instead, exchange the player's own signed JWT for a short-lived, scoped OddSockets token at the OddSockets token front door, and hand the SDK a tokenProvider callback that returns it. The SDK presents the token on the connection handshake and silently refreshes it — both shortly before it expires and on every reconnect — so the connection never lapses.

const client = new OddSockets({
  userId: 'player_42',
  // Called for every (re)connect and by the pre-expiry refresh timer.
  // Return the token string, or { token, expiresAt } (expiresAt: ISO-8601 or epoch).
  tokenProvider: async () => {
    const res = await fetch('https://connect.oddsockets.tyga.network/v1/token', {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${myPlayerJwt}`, // the player's own signed game JWT
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ channels: ['match:9', 'lobby'] }) // optional least-privilege scoping
    });
    return res.json(); // { token, expiresAt, ... }
  },
  tokenRefreshLeadMs: 120000 // Optional: refresh this long before expiry (default 2 min)
});

client.on('token_refreshed', ({ expiresAt }) => {
  // Optional: observe silent refreshes.
});

Channel Operations

Subscribe to Messages

const channel = client.channel('my-channel');

// Basic subscription
await channel.subscribe((message) => {
  console.log('Message:', message.message);
  console.log('Publisher:', message.publisher);
  console.log('Timestamp:', message.timestamp);
});

// Subscribe with options
await channel.subscribe((message) => {
  console.log('Received:', message);
}, {
  enablePresence: true,     // Enable presence tracking
  retainHistory: true,      // Keep message history in memory
  maxHistory: 100          // Maximum messages to retain
});

Publish Messages

// Simple message
await channel.publish('Hello World!');

// Complex message with metadata
await channel.publish({
  text: 'Hello World!',
  user: 'john_doe',
  timestamp: new Date().toISOString()
}, {
  ttl: 3600,              // Time to live in seconds
  metadata: {             // Additional metadata
    priority: 'high'
  }
});

Bulk Publishing

// Publish multiple messages at once
const results = await client.publishBulk([
  { channel: 'channel-1', message: 'Message 1' },
  { channel: 'channel-2', message: 'Message 2' },
  { channel: 'channel-1', message: 'Message 3', options: { ttl: 3600 } }
]);

results.forEach((result, index) => {
  if (result.success) {
    console.log(`Message ${index + 1} published successfully`);
  } else {
    console.error(`Message ${index + 1} failed:`, result.error);
  }
});

Message History

// Get recent messages
const history = await channel.getHistory({
  count: 50,                    // Number of messages (default: 50)
  start: '2023-01-01T00:00:00Z', // Start time (ISO string)
  end: '2023-12-31T23:59:59Z'    // End time (ISO string)
});

console.log('History:', history);

Presence Management

// Get current presence
const presence = await channel.getPresence();
console.log('Online users:', presence.occupancy);
console.log('User list:', presence.occupants);

// Update user state
await channel.updateState({
  status: 'online',
  mood: 'happy',
  location: 'New York'
});

// Listen for presence changes
channel.on('presence_change', (event) => {
  console.log(`User ${event.user.userId} ${event.action}`);
  console.log('Current occupancy:', event.occupancy);
});

Enhanced Features

Everything beyond core pub/sub — reactions, typing indicators, threads, read receipts, presence/status, direct messages, notifications, file uploads and channel management — lives on client.enhanced. The pattern is always the same:

  1. Send an action with a client.enhanced.* method.
  2. Receive the resulting broadcast on the client event surface with client.on('<event>', handler).

The worker fans each action out to the other members of the room, so a typing or reaction event fired by one client surfaces on every other subscribed client — a genuine round-trip, not a local echo.

const client = new OddSockets({ apiKey: 'your-api-key-here', userId: 'bob' });
const channel = client.channel('my-channel');
await channel.subscribe(() => {});

// Reactions — everyone in the room hears reaction_added
client.on('reaction_added', (p) => console.log(`reaction: ${p.emoji} by ${p.userId}`));
client.enhanced.addReaction({
  messageId: 'msg-1',
  channel: 'my-channel',
  emoji: ':thumbsup:',
  userId: 'bob',
  userName: 'Bob'
});

// Typing indicators
client.on('user_typing', (p) => console.log(`${p.userId} is typing in ${p.channel}`));
client.enhanced.startTyping('bob', 'my-channel');

// Threads
client.on('thread_reply', (p) => console.log(`reply in ${p.channel}`));
await client.enhanced.threadReply({
  channel: 'my-channel',
  parentMessageId: 'parent-1',
  message: 'nice!',
  userId: 'bob',
  userName: 'Bob'
});

The full enhanced surface (all backed by the worker):

| Area | Requests (client.enhanced.*) | Broadcast events (client.on) | |---|---|---| | Reactions | addReaction, removeReaction, getReactions | reaction_added, reaction_removed | | Typing | startTyping, stopTyping | user_typing, user_stopped_typing | | Threads | threadReply, getThread, subscribeThread, markThreadRead, followThread, unfollowThread | thread_reply, thread_subscribed, thread_followed, thread_unfollowed, thread_read_updated | | Message editing | editMessage, deleteMessage, pinMessage, unpinMessage, getPinnedMessages | message_edited, message_deleted, message_pinned, message_unpinned | | Read receipts | markRead, getUnreadCounts, markAllRead | user_read, unread_count_updated, all_marked_read | | Presence & status | setStatus, setCustomStatus, clearCustomStatus, setDND, clearDND, getUserPresence | user_status_changed, custom_status_updated, custom_status_cleared, dnd_status_changed, status_updated | | File uploads | startFileUpload, uploadProgress, uploadComplete | file_upload_completed, file_upload_progress, file_upload_failed | | Direct messages | createDM, sendDM, getDMConversations | dm_created, dm_received | | Notifications | subscribeNotifications, markNotificationRead, markAllNotificationsRead, clearNotifications, getNotifications | notification, notification_read, all_notifications_read, notifications_cleared | | Channel management | createChannel, updateChannel, archiveChannel, inviteToChannel, removeFromChannel, joinChannel, leaveChannel, getChannelMembers | channel_created, channel_updated, user_invited, user_joined_channel, user_left_channel, user_removed | | Search | searchMessages, filterMessages, searchChannel | (promise results, no broadcast) |

Request methods that read data (getReactions, getThread, getUnreadCounts, getUserPresence, getPinnedMessages, getChannelMembers, getDMConversations, getNotifications, searchMessages, …) return a Promise that resolves with the worker's response. Fire-and-forget actions (addReaction, startTyping, setStatus, …) send immediately and surface as broadcasts on the other clients.

For any worker broadcast not listed above, subscribe to it directly with client.on('<event_name>', handler) — every enhanced broadcast is forwarded onto the client event surface.

PubNub Compatibility

Migrate from PubNub with minimal code changes:

const { PubNubCompat } = require('oddsockets-nodejs');

// Initialize with PubNub-style config
const pubnub = new PubNubCompat({
  publishKey: 'your-api-key-here',
  subscribeKey: 'your-api-key-here',
  userId: 'user-123'
});

// Add listeners (PubNub style)
pubnub.addListener({
  message: (messageEvent) => {
    console.log('Message:', messageEvent.message);
    console.log('Channel:', messageEvent.channel);
    console.log('Publisher:', messageEvent.publisher);
  },
  presence: (presenceEvent) => {
    console.log('Presence:', presenceEvent.action);
    console.log('User:', presenceEvent.uuid);
  },
  status: (statusEvent) => {
    console.log('Status:', statusEvent.category);
  }
});

// Subscribe to channels
pubnub.subscribe({
  channels: ['channel-1', 'channel-2'],
  withPresence: true
});

// Publish messages
pubnub.publish({
  channel: 'channel-1',
  message: 'Hello from PubNub compatibility!'
}, (response) => {
  if (response.error) {
    console.error('Error:', response.error);
  } else {
    console.log('Published with timetoken:', response.timetoken);
  }
});

Event Handling

// Connection events
client.on('connected', () => {
  console.log('Connected to OddSockets');
});

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

client.on('reconnecting', (attempt) => {
  console.log(`Reconnecting... attempt ${attempt.attempt}/${attempt.maxAttempts}`);
});

client.on('error', (error) => {
  console.error('Connection error:', error);
});

client.on('worker_assigned', (info) => {
  console.log('Assigned to worker:', info.workerId);
  console.log('Worker URL:', info.workerUrl);
});

// Channel events
channel.on('subscribed', (data) => {
  console.log('Subscribed to channel:', data.channel);
});

channel.on('unsubscribed', (data) => {
  console.log('Unsubscribed from channel:', data.channel);
});

channel.on('published', (data) => {
  console.log('Message published:', data);
});

Error Handling

try {
  await channel.publish('Hello World!');
} catch (error) {
  if (error.message.includes('Message size')) {
    console.error('Message too large (max 32KB)');
  } else if (error.message.includes('Not connected')) {
    console.error('Client not connected');
  } else {
    console.error('Publish failed:', error.message);
  }
}

Advanced Usage

Custom Connection Options

const client = new OddSockets({
  apiKey: 'your-api-key-here',
  userId: 'user-123',
  options: {
    transports: ['websocket'],     // Force WebSocket only
    timeout: 15000,                // Connection timeout
    forceNew: true,                // Force new connection
    reconnection: true,            // Enable reconnection
    reconnectionDelay: 1000,       // Initial reconnection delay
    reconnectionAttempts: 5,       // Max reconnection attempts
    maxReconnectionAttempts: 10    // Max total attempts
  }
});

Manual Connection Management

const client = new OddSockets({
  apiKey: 'your-api-key-here',
  autoConnect: false  // Don't auto-connect
});

// Connect manually
await client.connect();

// Check connection state
console.log('State:', client.getState()); // 'connected', 'connecting', 'disconnected', 'reconnecting'

// Get worker info
const workerInfo = client.getWorkerInfo();
console.log('Worker ID:', workerInfo.workerId);
console.log('Worker URL:', workerInfo.workerUrl);

// Disconnect
client.disconnect();

Session Information

// Get client identifier (used for session stickiness)
const clientId = client.getClientIdentifier();
console.log('Client ID:', clientId);

// Get session info
const session = client.getSessionInfo();
console.log('Session:', session);

Message Size Limits

The SDK enforces industry-standard message size limits:

  • Maximum message size: 32KB (32,768 bytes)
  • Encoding: UTF-8
  • Validation: Automatic before publishing
// This will throw an error if message exceeds 32KB
try {
  await channel.publish({
    data: 'x'.repeat(40000) // Too large!
  });
} catch (error) {
  console.error(error.message);
  // "Message size (39KB) exceeds maximum allowed size of 32KB"
}

Examples

Run the included examples:

# Basic usage example
node examples/basic-usage.js

# PubNub compatibility example
node examples/basic-usage.js pubnub

API Reference

OddSockets Class

Constructor

  • new OddSockets(config) - Create a new client instance

Methods

  • connect() - Connect to OddSockets platform
  • disconnect() - Disconnect from platform
  • channel(name) - Get or create a channel
  • publishBulk(messages) - Publish multiple messages
  • getState() - Get connection state
  • getWorkerInfo() - Get assigned worker information
  • getClientIdentifier() - Get client identifier
  • getSessionInfo() - Get session information

Properties

  • enhanced - Enhanced feature surface (reactions, typing, threads, presence/status, DMs, notifications, file uploads, channel management). See Enhanced Features.

Events

  • connected - Connected to platform
  • disconnected - Disconnected from platform
  • reconnecting - Attempting to reconnect
  • error - Connection error occurred
  • worker_assigned - Assigned to a worker
  • Enhanced broadcast events (reaction_added, user_typing, thread_reply, message_edited, notification, …) are forwarded onto this surface — subscribe with client.on('<event>', handler). Full list under Enhanced Features.

Channel Class

Methods

  • subscribe(callback, options) - Subscribe to messages
  • unsubscribe() - Unsubscribe from channel
  • publish(message, options) - Publish a message
  • getHistory(options) - Get message history
  • getPresence() - Get presence information
  • updateState(state) - Update user state
  • isSubscribed() - Check subscription status
  • getName() - Get channel name

Events

  • message - New message received
  • subscribed - Successfully subscribed
  • unsubscribed - Successfully unsubscribed
  • published - Message published
  • presence - Presence information
  • presence_change - Presence changed

Get an API Key

No free tier — every plan starts with a 7-day free trial (nothing is charged during the trial). Signup issues a working API key instantly; the key runs keyless for 48 hours, and adding a card within that window extends it through the trial.

Scriptable signup (CLI):

npm i -g oddsockets-cli
oddsockets plans                                  # list live plan ids
oddsockets signup [email protected] --plan oddsockets-starter
oddsockets publish smoke-test '{"hello":"world"}' # verify in one line

AI agents can also self-provision via MCP: connect to https://mcp.oddsockets.ai/sse and call oddsockets_signup.

Plans

oddsockets-starter $29/mo · oddsockets-pro $99/mo · oddsockets-scale $299/mo · oddsockets-enterprise (contact us)

See oddsockets.com/pricing for current limits per tier. All limits are enforced in real time; when a limit is reached, the SDK receives a RATE_LIMIT_EXCEEDED error with a retryAfter value.

Requirements

  • Node.js 14.0.0 or higher
  • Active OddSockets API key (get one)

Get Accredited

Prove you can build and operate real-time features on OddSockets — channels, presence, pub/sub, delivery guarantees and production liveops — on the stack itself. Three tiers (TCU / TCA / TCP), certified through tyga.games and delivered on ClassaaS.

Get accredited on tyga.games →

Support

License

Proprietary - Copyright (c) 2026 Tyga.Cloud Ltd. Licensed for use with the OddSockets platform. See LICENSE for the full End User License Agreement.