{"_id":"@0xandreja/dex-ws","_rev":"3-04ed488838e1816c7680bbb23370e0b5","name":"@0xandreja/dex-ws","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.0":{"name":"@0xandreja/dex-ws","version":"1.0.0","keywords":["websocket","dex","trading","realtime"],"author":"","license":"MIT","_id":"@0xandreja/dex-ws@1.0.0","maintainers":[{"name":"0xandreja","email":"andreja.kojadinovic@gmail.com"}],"dist":{"shasum":"0078d449c09e3162c444f1ed97b91ec012d09646","tarball":"https://registry.npmjs.org/@0xandreja/dex-ws/-/dex-ws-1.0.0.tgz","fileCount":3,"integrity":"sha512-U6IcBLj+133ymEmk9uB6AjjIM8k8iiRavRPD865aT5/r79t4l2tFxz5ZbP8+7tK23emot7ghhLBDN4uUiFEiZg==","signatures":[{"sig":"MEYCIQDPojA1UDDTSGO3Ul507Ir176P2n1lhj0P2vUsQQGZ/XwIhAM9NlRG+nOIjyhXO66PomJLRPTAntWdTdIInoCEqw4g5","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":11223},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"f1b953a1d4cfd89a88726171bb51bd0f694f28ed","scripts":{"test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts --clean","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"0xandreja","email":"andreja.kojadinovic@gmail.com"},"_npmVersion":"10.9.2","description":"Production-ready WebSocket client for DEX connections","directories":{},"_nodeVersion":"22.16.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","jsdom":"^27.3.0","vitest":"^4.0.15","typescript":"^5.9.3","@types/node":"^24.10.2","@vitest/coverage-v8":"^4.0.15"},"_npmOperationalInternal":{"tmp":"tmp/dex-ws_1.0.0_1765366068553_0.49107601908933574","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@0xandreja/dex-ws","version":"1.0.1","keywords":["websocket","dex","trading","realtime"],"author":"","license":"MIT","_id":"@0xandreja/dex-ws@1.0.1","maintainers":[{"name":"0xandreja","email":"andreja.kojadinovic@gmail.com"}],"dist":{"shasum":"852ba543737b97f88ea0965ec72f9aba37a9722c","tarball":"https://registry.npmjs.org/@0xandreja/dex-ws/-/dex-ws-1.0.1.tgz","fileCount":7,"integrity":"sha512-ZaIY3Fpodh1mxYLtJ8nYYHNLj57ZfZWba9hHBdqBFxIuC6BfyGrjHwl/+8ql3s8lggGT2XiRFlsBdEeTUEtJ9w==","signatures":[{"sig":"MEQCID98G5wCsxsDTDIxbayR4xLjRhsiut6Ox36kmg2iEEamAiABQa6GR+lWjzbbeFlCKKDrO46vfCngMmLQpE/UpiwgTA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":62904},"main":"./dist/index.cjs","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"gitHead":"f1b953a1d4cfd89a88726171bb51bd0f694f28ed","scripts":{"test":"vitest run","build":"tsup src/index.ts --format cjs,esm --dts --clean","test:coverage":"vitest run --coverage"},"_npmUser":{"name":"0xandreja","email":"andreja.kojadinovic@gmail.com"},"_npmVersion":"10.9.2","description":"Production-ready WebSocket client for DEX connections","directories":{},"_nodeVersion":"22.16.0","_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.5.1","jsdom":"^27.3.0","vitest":"^4.0.15","typescript":"^5.9.3","@types/node":"^24.10.2","@vitest/coverage-v8":"^4.0.15"},"_npmOperationalInternal":{"tmp":"tmp/dex-ws_1.0.1_1765367671646_0.17381391564049964","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@0xandreja/dex-ws","version":"1.0.2","description":"A robust WebSocket client for DEX trading applications","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"scripts":{"build":"tsup src/index.ts --format cjs,esm --dts --clean","test":"vitest run","test:coverage":"vitest run --coverage"},"keywords":["websocket","dex","trading","realtime"],"author":"","license":"MIT","devDependencies":{"@types/node":"^24.10.2","@vitest/coverage-v8":"^4.0.15","jsdom":"^27.3.0","tsup":"^8.5.1","typescript":"^5.9.3","vitest":"^4.0.15"},"_id":"@0xandreja/dex-ws@1.0.2","gitHead":"f1b953a1d4cfd89a88726171bb51bd0f694f28ed","_nodeVersion":"22.16.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-KiXHI+Anf+SOJf5/aCOrgn2cP2QickOxdZg27gmbgLrB7tpumEtvZD4qschJzgNHMI3DFQcti6Pw7gZu4UyfvA==","shasum":"e5e08ef7ba5c4c34f2b0ee985292449675f9067b","tarball":"https://registry.npmjs.org/@0xandreja/dex-ws/-/dex-ws-1.0.2.tgz","fileCount":7,"unpackedSize":62838,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIDSyZ5QwIX8ERLjkK4oxhkpou1ySVicdmvPDGthWLigOAiAhhrqt0kdcDqkksKgi1eOiQJXDSHmSj46PNGlWUxX25Q=="}]},"_npmUser":{"name":"0xandreja","email":"andreja.kojadinovic@gmail.com"},"directories":{},"maintainers":[{"name":"0xandreja","email":"andreja.kojadinovic@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/dex-ws_1.0.2_1765369466580_0.8213927702531365"},"_hasShrinkwrap":false}},"time":{"created":"2025-12-10T11:27:48.478Z","modified":"2025-12-10T12:24:26.930Z","1.0.0":"2025-12-10T11:27:48.705Z","1.0.1":"2025-12-10T11:54:31.798Z","1.0.2":"2025-12-10T12:24:26.744Z"},"license":"MIT","keywords":["websocket","dex","trading","realtime"],"description":"A robust WebSocket client for DEX trading applications","maintainers":[{"name":"0xandreja","email":"andreja.kojadinovic@gmail.com"}],"readme":"# @0xandreja/dex-ws\n\nA robust WebSocket client for DEX trading applications.\n\n## Installation\n\n```bash\nnpm install @0xandreja/dex-ws\n```\n\n## Quick Start\n\n```typescript\nimport DexWS from '@0xandreja/dex-ws';\n\nconst ws = new DexWS('wss://api.hyperliquid.xyz/ws');\n\nws.on('open', ({ isReconnect }) => {\n  console.log('Connected!', isReconnect ? '(reconnected)' : '');\n  ws.send({ subscribe: 'trades' });\n});\n\nws.on('message', (data) => {\n  console.log('Received:', data);\n});\n\nws.on('close', ({ code, reason }) => {\n  console.log('Disconnected:', code, reason);\n});\n\nws.on('error', (error) => {\n  console.error('Error:', error);\n});\n\n// Clean up when done\nws.destroy();\n```\n\n## Features\n\n- **Automatic Reconnection**: Exponential backoff with configurable max retries and jitter\n- **Heartbeat**: Keep connections alive with customizable ping messages\n- **Visibility Handling**: Disconnect when page is hidden, reconnect when visible\n- **Network Awareness**: Automatically reconnect when coming back online\n- **Message Buffering**: Queue messages while disconnected with overflow strategies\n- **Metrics**: Track connection attempts, messages sent/received, and uptime\n- **TypeScript**: Full type definitions included\n\n## API Reference\n\n### Constructor\n\n```typescript\nnew DexWS(url: string | (() => string), options?: DexWSOptions)\n```\n\n#### Parameters\n\n- `url` - WebSocket URL (must start with `ws://` or `wss://`) or a function returning the URL\n- `options` - Configuration options (see below)\n\n### Options\n\n```typescript\ninterface DexWSOptions {\n  autoConnect?: boolean;        // Auto-connect on instantiation (default: true)\n  timeout?: number;             // Connection timeout in ms (default: 30000)\n  protocols?: string | string[]; // WebSocket subprotocols\n  logger?: LogLevel | Logger;   // Logging configuration\n  binaryType?: BinaryType;      // 'blob' or 'arraybuffer' (default: 'blob')\n  reconnect?: ReconnectOptions | boolean;\n  heartbeat?: HeartbeatOptions | boolean;\n  visibility?: VisibilityOptions | boolean;\n  network?: NetworkOptions | boolean;\n  buffer?: BufferOptions | boolean;\n}\n```\n\n### Feature Options\n\nEach feature can be:\n- `false` - Disabled\n- `true` or `undefined` - Enabled with defaults\n- `object` - Enabled with custom options merged with defaults\n\n#### Reconnect Options\n\n```typescript\ninterface ReconnectOptions {\n  enabled?: boolean;            // Enable reconnection (default: true)\n  maxRetries?: number;          // Max reconnection attempts (default: Infinity)\n  delay?: number | ((attempt: number) => number); // Delay in ms or function\n  jitter?: boolean;             // Add random jitter to delay (default: true)\n}\n```\n\n**Default delay function**: `Math.min(1000 * 2^n, 30000)` (exponential backoff, max 30s)\n\n**Jitter formula**: `baseDelay + (baseDelay * 0.2 * Math.random())`\n\n#### Heartbeat Options\n\n```typescript\ninterface HeartbeatOptions {\n  enabled?: boolean;            // Enable heartbeat (default: true)\n  interval?: number;            // Heartbeat interval in ms (default: 30000)\n  message?: string | (() => string); // Ping message (default: '{\"method\":\"ping\"}')\n  pongTimeout?: number;         // Pong timeout in ms (default: 10000)\n}\n```\n\n#### Visibility Options\n\n```typescript\ninterface VisibilityOptions {\n  enabled?: boolean;            // Enable visibility handling (default: true)\n  timeout?: number;             // Time before disconnect when hidden (default: 120000)\n  verifyOnVisible?: boolean;    // Reconnect when visible (default: true)\n}\n```\n\n#### Network Options\n\n```typescript\ninterface NetworkOptions {\n  enabled?: boolean;            // Enable network handling (default: true)\n  autoReconnect?: boolean;      // Reconnect when online (default: true)\n}\n```\n\n#### Buffer Options\n\n```typescript\ninterface BufferOptions {\n  enabled?: boolean;            // Enable message buffering (default: true)\n  maxSize?: number;             // Max buffer size (default: 100)\n  overflow?: OverflowStrategy;  // 'drop-oldest' | 'drop-newest' | 'error'\n}\n```\n\n### Methods\n\n#### `connect(): this`\nInitiate connection. Called automatically if `autoConnect` is true.\n\n#### `disconnect(): this`\nGracefully close the connection.\n\n#### `destroy(): void`\nPermanently destroy the instance. Cleans up all listeners and timers. Idempotent.\n\n#### `send(data: unknown): boolean`\nSend data through the WebSocket. Returns `true` if sent successfully, `false` otherwise.\n- Objects are JSON-stringified automatically\n- Messages are buffered if not connected (when buffering is enabled)\n\n#### `on(event, listener): this`\nSubscribe to events. Returns `this` for chaining.\n\n#### `off(event, listener): this`\nUnsubscribe from events. Returns `this` for chaining.\n\n#### `once(event, listener): this`\nSubscribe to a single event occurrence. Returns `this` for chaining.\n\n#### `removeAllListeners(event?): this`\nRemove all listeners for an event, or all listeners if no event specified.\n\n### Properties\n\n| Property | Type | Description |\n|----------|------|-------------|\n| `status` | `ConnectionState` | Current connection state |\n| `connected` | `boolean` | True if currently connected |\n| `endpoint` | `string` | The WebSocket URL |\n| `config` | `DexWSOptions` | Current configuration |\n| `bufferSize` | `number` | Number of buffered messages |\n| `metrics` | `Metrics` | Connection metrics |\n| `destroyed` | `boolean` | True if instance is destroyed |\n\n### Events\n\n| Event | Payload | Description |\n|-------|---------|-------------|\n| `open` | `{ isReconnect: boolean }` | Connection opened |\n| `close` | `{ code: number, reason: string }` | Connection closed |\n| `message` | `unknown` | Message received (auto-parsed if JSON) |\n| `error` | `Error` | Error occurred |\n| `status` | `{ status: ConnectionState, previousStatus: ConnectionState }` | Status changed |\n\n**Note**: `on('status', fn)` immediately emits the current status.\n\n### Connection States\n\n```typescript\ntype ConnectionState =\n  | 'disconnected'   // Not connected\n  | 'connecting'     // Initial connection in progress\n  | 'connected'      // Connected\n  | 'reconnecting'   // Reconnection in progress\n  | 'terminated';    // Instance destroyed\n```\n\n### Metrics\n\n```typescript\ninterface Metrics {\n  connectionAttempts: number;   // Total connection attempts\n  messagesSent: number;         // Total messages sent\n  messagesReceived: number;     // Total messages received\n  lastConnectedAt: number | null;     // Timestamp of last connection\n  lastDisconnectedAt: number | null;  // Timestamp of last disconnection\n  uptime: number;               // Total connected time in ms\n}\n```\n\n## Defaults Summary\n\n| Feature | Default |\n|---------|---------|\n| `autoConnect` | `true` |\n| `timeout` | `30000` (30s) |\n| `binaryType` | `'blob'` |\n| `reconnect.maxRetries` | `Infinity` |\n| `reconnect.delay` | Exponential backoff, max 30s |\n| `reconnect.jitter` | `true` (0-20% random) |\n| `heartbeat.interval` | `30000` (30s) |\n| `heartbeat.message` | `'{\"method\":\"ping\"}'` |\n| `heartbeat.pongTimeout` | `10000` (10s) |\n| `visibility.timeout` | `120000` (2 min) |\n| `visibility.verifyOnVisible` | `true` |\n| `network.autoReconnect` | `true` |\n| `buffer.maxSize` | `100` |\n| `buffer.overflow` | `'drop-oldest'` |\n\n## Close Codes\n\nThe following close codes will **NOT** trigger automatic reconnection:\n- `1000` - Normal closure\n- `1008` - Policy violation\n- `1009` - Message too big\n- `1010` - Missing extension\n- `1011` - Internal error\n- `1015` - TLS handshake failure\n\nAll other close codes will trigger reconnection (if enabled).\n\n## Factory Function\n\n```typescript\nimport { connect } from '@dex-ws/core';\n\nconst ws = connect('wss://api.example.com/ws', { /* options */ });\n```\n\n## Examples\n\n### Custom Reconnection Strategy\n\n```typescript\nconst ws = new DexWS('wss://api.example.com/ws', {\n  reconnect: {\n    maxRetries: 10,\n    delay: (attempt) => Math.min(500 * Math.pow(1.5, attempt), 10000),\n    jitter: true,\n  },\n});\n```\n\n### Custom Heartbeat\n\n```typescript\nlet seq = 0;\nconst ws = new DexWS('wss://api.example.com/ws', {\n  heartbeat: {\n    interval: 15000,\n    message: () => JSON.stringify({ type: 'ping', seq: ++seq }),\n    pongTimeout: 5000,\n  },\n});\n```\n\n### Disable Features\n\n```typescript\nconst ws = new DexWS('wss://api.example.com/ws', {\n  reconnect: false,      // No auto-reconnect\n  heartbeat: false,      // No heartbeat\n  visibility: false,     // Ignore page visibility\n  network: false,        // Ignore network changes\n  buffer: false,         // No message buffering\n});\n```\n\n### Custom Logger\n\n```typescript\nconst ws = new DexWS('wss://api.example.com/ws', {\n  logger: {\n    debug: (...args) => console.debug('[WS]', ...args),\n    info: (...args) => console.info('[WS]', ...args),\n    warn: (...args) => console.warn('[WS]', ...args),\n    error: (...args) => console.error('[WS]', ...args),\n  },\n});\n\n// Or use built-in levels: 'debug' | 'info' | 'warn' | 'error' | 'none'\nconst ws2 = new DexWS('wss://api.example.com/ws', { logger: 'debug' });\n```\n\n### Dynamic URL\n\n```typescript\nlet serverIndex = 0;\nconst servers = ['wss://server1.com/ws', 'wss://server2.com/ws'];\n\nconst ws = new DexWS(() => servers[serverIndex++ % servers.length]);\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md"}