@hypersignals/dex-ws
v1.0.2
Published
A robust WebSocket client for DEX trading applications
Maintainers
Readme
@hypersignals/dex-ws
A robust WebSocket client for DEX trading applications.
Installation
npm install @hypersignals/dex-wsQuick 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 withws://orwss://) or a function returning the URLoptions- 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- Disabledtrueorundefined- Enabled with defaultsobject- 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 destroyedMetrics
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 closure1008- Policy violation1009- Message too big1010- Missing extension1011- Internal error1015- 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
