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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@hypersignals/dex-ws

v1.0.2

Published

A robust WebSocket client for DEX trading applications

Readme

@hypersignals/dex-ws

A robust WebSocket client for DEX trading applications.

Installation

npm install @hypersignals/dex-ws

Quick Start

import DexWS from '@hypersignals/dex-ws';

const ws = new DexWS('wss://api.hyperliquid.xyz/ws');

ws.on('open', ({ isReconnect }) => {
  console.log('Connected!', isReconnect ? '(reconnected)' : '');
  ws.send({ subscribe: 'trades' });
});

ws.on('message', (data) => {
  console.log('Received:', data);
});

ws.on('close', ({ code, reason }) => {
  console.log('Disconnected:', code, reason);
});

ws.on('error', (error) => {
  console.error('Error:', error);
});

// Clean up when done
ws.destroy();

Features

  • Automatic Reconnection: Exponential backoff with configurable max retries and jitter
  • Heartbeat: Keep connections alive with customizable ping messages
  • Visibility Handling: Disconnect when page is hidden, reconnect when visible
  • Network Awareness: Automatically reconnect when coming back online
  • Message Buffering: Queue messages while disconnected with overflow strategies
  • Metrics: Track connection attempts, messages sent/received, and uptime
  • TypeScript: Full type definitions included

API Reference

Constructor

new DexWS(url: string | (() => string), options?: DexWSOptions)

Parameters

  • url - WebSocket URL (must start with ws:// or wss://) or a function returning the URL
  • options - Configuration options (see below)

Options

interface DexWSOptions {
  autoConnect?: boolean;        // Auto-connect on instantiation (default: true)
  timeout?: number;             // Connection timeout in ms (default: 30000)
  protocols?: string | string[]; // WebSocket subprotocols
  logger?: LogLevel | Logger;   // Logging configuration
  binaryType?: BinaryType;      // 'blob' or 'arraybuffer' (default: 'blob')
  reconnect?: ReconnectOptions | boolean;
  heartbeat?: HeartbeatOptions | boolean;
  visibility?: VisibilityOptions | boolean;
  network?: NetworkOptions | boolean;
  buffer?: BufferOptions | boolean;
}

Feature Options

Each feature can be:

  • false - Disabled
  • true or undefined - Enabled with defaults
  • object - Enabled with custom options merged with defaults

Reconnect Options

interface ReconnectOptions {
  enabled?: boolean;            // Enable reconnection (default: true)
  maxRetries?: number;          // Max reconnection attempts (default: Infinity)
  delay?: number | ((attempt: number) => number); // Delay in ms or function
  jitter?: boolean;             // Add random jitter to delay (default: true)
}

Default delay function: Math.min(1000 * 2^n, 30000) (exponential backoff, max 30s)

Jitter formula: baseDelay + (baseDelay * 0.2 * Math.random())

Heartbeat Options

interface HeartbeatOptions {
  enabled?: boolean;            // Enable heartbeat (default: true)
  interval?: number;            // Heartbeat interval in ms (default: 30000)
  message?: string | (() => string); // Ping message (default: '{"method":"ping"}')
  pongTimeout?: number;         // Pong timeout in ms (default: 10000)
}

Visibility Options

interface VisibilityOptions {
  enabled?: boolean;            // Enable visibility handling (default: true)
  timeout?: number;             // Time before disconnect when hidden (default: 120000)
  verifyOnVisible?: boolean;    // Reconnect when visible (default: true)
}

Network Options

interface NetworkOptions {
  enabled?: boolean;            // Enable network handling (default: true)
  autoReconnect?: boolean;      // Reconnect when online (default: true)
}

Buffer Options

interface BufferOptions {
  enabled?: boolean;            // Enable message buffering (default: true)
  maxSize?: number;             // Max buffer size (default: 100)
  overflow?: OverflowStrategy;  // 'drop-oldest' | 'drop-newest' | 'error'
}

Methods

connect(): this

Initiate connection. Called automatically if autoConnect is true.

disconnect(): this

Gracefully close the connection.

destroy(): void

Permanently destroy the instance. Cleans up all listeners and timers. Idempotent.

send(data: unknown): boolean

Send data through the WebSocket. Returns true if sent successfully, false otherwise.

  • Objects are JSON-stringified automatically
  • Messages are buffered if not connected (when buffering is enabled)

on(event, listener): this

Subscribe to events. Returns this for chaining.

off(event, listener): this

Unsubscribe from events. Returns this for chaining.

once(event, listener): this

Subscribe to a single event occurrence. Returns this for chaining.

removeAllListeners(event?): this

Remove all listeners for an event, or all listeners if no event specified.

Properties

| Property | Type | Description | |----------|------|-------------| | status | ConnectionState | Current connection state | | connected | boolean | True if currently connected | | endpoint | string | The WebSocket URL | | config | DexWSOptions | Current configuration | | bufferSize | number | Number of buffered messages | | metrics | Metrics | Connection metrics | | destroyed | boolean | True if instance is destroyed |

Events

| Event | Payload | Description | |-------|---------|-------------| | open | { isReconnect: boolean } | Connection opened | | close | { code: number, reason: string } | Connection closed | | message | unknown | Message received (auto-parsed if JSON) | | error | Error | Error occurred | | status | { status: ConnectionState, previousStatus: ConnectionState } | Status changed |

Note: on('status', fn) immediately emits the current status.

Connection States

type ConnectionState =
  | 'disconnected'   // Not connected
  | 'connecting'     // Initial connection in progress
  | 'connected'      // Connected
  | 'reconnecting'   // Reconnection in progress
  | 'terminated';    // Instance destroyed

Metrics

interface Metrics {
  connectionAttempts: number;   // Total connection attempts
  messagesSent: number;         // Total messages sent
  messagesReceived: number;     // Total messages received
  lastConnectedAt: number | null;     // Timestamp of last connection
  lastDisconnectedAt: number | null;  // Timestamp of last disconnection
  uptime: number;               // Total connected time in ms
}

Defaults Summary

| Feature | Default | |---------|---------| | autoConnect | true | | timeout | 30000 (30s) | | binaryType | 'blob' | | reconnect.maxRetries | Infinity | | reconnect.delay | Exponential backoff, max 30s | | reconnect.jitter | true (0-20% random) | | heartbeat.interval | 30000 (30s) | | heartbeat.message | '{"method":"ping"}' | | heartbeat.pongTimeout | 10000 (10s) | | visibility.timeout | 120000 (2 min) | | visibility.verifyOnVisible | true | | network.autoReconnect | true | | buffer.maxSize | 100 | | buffer.overflow | 'drop-oldest' |

Close Codes

The following close codes will NOT trigger automatic reconnection:

  • 1000 - Normal closure
  • 1008 - Policy violation
  • 1009 - Message too big
  • 1010 - Missing extension
  • 1011 - Internal error
  • 1015 - TLS handshake failure

All other close codes will trigger reconnection (if enabled).

Factory Function

import { connect } from '@dex-ws/core';

const ws = connect('wss://api.example.com/ws', { /* options */ });

Examples

Custom Reconnection Strategy

const ws = new DexWS('wss://api.example.com/ws', {
  reconnect: {
    maxRetries: 10,
    delay: (attempt) => Math.min(500 * Math.pow(1.5, attempt), 10000),
    jitter: true,
  },
});

Custom Heartbeat

let seq = 0;
const ws = new DexWS('wss://api.example.com/ws', {
  heartbeat: {
    interval: 15000,
    message: () => JSON.stringify({ type: 'ping', seq: ++seq }),
    pongTimeout: 5000,
  },
});

Disable Features

const ws = new DexWS('wss://api.example.com/ws', {
  reconnect: false,      // No auto-reconnect
  heartbeat: false,      // No heartbeat
  visibility: false,     // Ignore page visibility
  network: false,        // Ignore network changes
  buffer: false,         // No message buffering
});

Custom Logger

const ws = new DexWS('wss://api.example.com/ws', {
  logger: {
    debug: (...args) => console.debug('[WS]', ...args),
    info: (...args) => console.info('[WS]', ...args),
    warn: (...args) => console.warn('[WS]', ...args),
    error: (...args) => console.error('[WS]', ...args),
  },
});

// Or use built-in levels: 'debug' | 'info' | 'warn' | 'error' | 'none'
const ws2 = new DexWS('wss://api.example.com/ws', { logger: 'debug' });

Dynamic URL

let serverIndex = 0;
const servers = ['wss://server1.com/ws', 'wss://server2.com/ws'];

const ws = new DexWS(() => servers[serverIndex++ % servers.length]);

License

MIT