@nostrwatch/websocket
v0.0.1
Published
Cross-platform WebSocket client with a unified event-driven API for Node.js, browser, and Deno.
Readme
@nostrwatch/websocket
Cross-platform WebSocket client with a unified event-driven API for Node.js, browser, and Deno.
Overview
@nostrwatch/websocket wraps platform-specific WebSocket implementations behind the UniversalWebSocket class, providing a consistent on/once/off event API and connection lifecycle management across Node.js, browser, and Deno environments. The class detects the runtime automatically and selects the appropriate underlying implementation (ws in Node.js, native WebSocket in browser/Deno). It is the WebSocket foundation used by the relay check adapters in @nostrwatch/nocap.
A relay in Nostr is a WebSocket server (identified by a wss:// URL) that stores and forwards signed protocol messages (events). UniversalWebSocket connects to these endpoints and handles connection state, message dispatch, and graceful teardown regardless of the JavaScript runtime.
Installation
pnpm add @nostrwatch/websocketOr with npm:
npm install @nostrwatch/websocketQuick Start
import {UniversalWebSocket} from '@nostrwatch/websocket'
const ws = new UniversalWebSocket('wss://relay.damus.io')
ws.on('open', () => console.log('connected'))
ws.on('message', (ev) => console.log('received:', ev.data))
await ws.connect()
ws.send(JSON.stringify(['REQ', 'sub1', {kinds: [1], limit: 1}]))
// received: ["EVENT","sub1",{"id":"...","kind":1,...}]API
UniversalWebSocket
class UniversalWebSocket {
constructor(relay: string, protocols?: string | string[], options?: UniversalWebSocketOptions)
static create(relay: string, protocols?: string | string[], options?: UniversalWebSocketOptions): Promise<UniversalWebSocket>
}Constructor
new UniversalWebSocket(relay: string, protocols?, options?)Creates a new cross-platform WebSocket instance. By default (autoConnect: true), begins connecting immediately in the background. To control connection timing manually, set autoConnect: false and call connect() when ready.
| Parameter | Type | Description |
|-----------|------|-------------|
| relay | string | WebSocket URL of the relay (e.g. wss://relay.damus.io) |
| protocols | string \| string[] | Optional WebSocket subprotocols |
| options | UniversalWebSocketOptions | Optional configuration object |
UniversalWebSocketOptions
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| autoConnect | boolean | true | Connect automatically on construction |
| connectTimeout | number | 10000 | Timeout in milliseconds before connect() rejects |
| agent | unknown | undefined | HTTP agent for Node.js proxy support |
| wsOptions | Record<string, unknown> | undefined | Additional options forwarded to the underlying ws constructor in Node.js |
UniversalWebSocket.create()
static async create(relay: string, protocols?, options?): Promise<UniversalWebSocket>Factory method that creates an instance with autoConnect: false, calls connect(), and resolves when the connection is open. Use this when you need to await a fully connected socket before proceeding.
Event methods
on(type, listener)
on<K extends WebSocketEventType>(type: K, listener: WebSocketListener<K>): voidRegisters a persistent event listener. Fires on every occurrence of the event.
once(type, listener)
once<K extends WebSocketEventType>(type: K, listener: WebSocketListener<K>): voidRegisters a one-shot event listener. Automatically removed after the first invocation.
off(type?, listener?)
off<K extends WebSocketEventType>(type?: K, listener?: WebSocketListener<K>): voidRemoves event listeners. With no arguments, clears all listeners for all event types. With only type, clears all listeners for that type. With both arguments, removes the specific listener.
addEventListener(type, listener) / removeEventListener(type, listener)
DOM-compatible aliases for on and off. Useful when passing a UniversalWebSocket to code that expects a native WebSocket.
Lifecycle methods
connect()
async connect(): Promise<boolean>Initiates the WebSocket connection. Resolves true when the connection opens, false if the connection fails or times out. Safe to call multiple times — concurrent calls share the same promise.
ready()
async ready(): Promise<void>Awaits a successful connection. Throws if the connection fails. Use this when you want to ensure the socket is open before sending.
closed()
async closed(): Promise<void>Resolves when the socket transitions to the CLOSED state.
close(code?, reason?)
close(code?: number, reason?: string): voidInitiates a graceful WebSocket close handshake with an optional close code and reason string.
terminate()
terminate(): voidForcefully destroys the connection without completing the close handshake. In browser environments where terminate() is not available on native WebSocket, falls back to close().
Communication
send(data)
send(data: unknown): voidSends data over the open WebSocket. Accepts strings, Blob, ArrayBuffer, ArrayBufferView, plain objects (JSON-serialized automatically), or arrays (JSON-serialized automatically). Throws if the socket is not connected.
Properties
| Property | Type | Description |
|----------|------|-------------|
| readyState | number | Current connection state: 0 CONNECTING, 1 OPEN, 2 CLOSING, 3 CLOSED |
| url | string | The relay URL this socket was constructed with |
| CONNECTED | boolean | Alias for readyState === 1 |
| CONNECTING | boolean | Alias for readyState === 0 |
| CLOSING | boolean | Alias for readyState === 2 |
| CLOSED | boolean | Alias for readyState === 3 |
| BUSY | boolean | True while CONNECTING or CLOSING |
| OPEN | boolean | Alias for CONNECTED |
Convenience state-check methods: isConnecting(), isConnected(), isOpen(), isClosing(), isClosed().
Events
| Event | Payload type | Description |
|-------|-------------|-------------|
| open | Event | Connection established |
| message | MessageEvent<string \| Buffer \| ArrayBuffer \| Blob> | Message received from the relay |
| close | CloseEvent | Connection closed; inspect ev.code and ev.reason |
| error | Event | Connection error occurred |
Types
type WebSocketEventType = 'open' | 'close' | 'error' | 'message'
type Sendable = string | ArrayBufferLike | Blob | ArrayBufferView
type WebSocketData = Sendable | Buffer
interface UniversalWebSocketOptions {
agent?: unknown
connectTimeout?: number
autoConnect?: boolean
wsOptions?: Record<string, unknown>
}Known Limitations
No known limitations at this time.
Agent Skills
No agent skills defined yet for this package.
Related Packages
@nostrwatch/nocap— relay capability checker; uses@nostrwatch/websocketas its underlying WebSocket transport via the websocket adapter@nostrwatch/route66— relay aggregation layer; websocket adapter wrapsUniversalWebSocketfor connection pooling@nostrwatch/memory-relay— in-memory relay for testing; pairs withUniversalWebSocketin integration test scenarios
