From 5ae021796cdaf0e78c44bdc3547f52bf9ae8e5ca Mon Sep 17 00:00:00 2001 From: digitalSloth Date: Thu, 5 Feb 2026 14:14:56 +0000 Subject: [PATCH] feat: auto reconnect websocket connections --- README.md | 6 ++++-- docs/examples.md | 32 ++++++++++++++++++++++++++++++-- src/client/index.ts | 2 +- src/client/websocket.ts | 25 +++++++++++++++++++++---- src/index.ts | 4 ++++ src/zenon.ts | 6 +++--- 6 files changed, 63 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 9b1e676..25413e6 100644 --- a/README.md +++ b/README.md @@ -179,18 +179,20 @@ const path = Zenon.getPowBasePath(); ### Instance Methods -##### `initialize(url: string): Promise` +##### `initialize(url: string, timeout?: number, wsOptions?: WsClientOptions): Promise` Connect to a Zenon node via HTTP or WebSocket. ```javascript -// WebSocket (for subscriptions) +// WebSocket (for subscriptions and transactions) await zenon.initialize('wss://node.zenonhub.io:35998'); // HTTP (for simple requests) await zenon.initialize('https://node.zenonhub.io:35997'); ``` +> **Note:** WebSocket connections automatically reconnect if dropped during long-running operations (e.g., PoW generation). The default settings are suitable for most use cases. + ##### `clearConnection(): void` Disconnect and clean up resources. diff --git a/docs/examples.md b/docs/examples.md index d5d4898..7a6d5e7 100644 --- a/docs/examples.md +++ b/docs/examples.md @@ -14,8 +14,36 @@ await zenon.initialize('wss://node.zenonhub.io:35998'); ``` Connection options: -- **HTTP**: `https://node.zenonhub.io:35997` -- **WebSocket**: `wss://node.zenonhub.io:35998` +- **HTTP**: `https://node.zenonhub.io:35997` - For simple API calls +- **WebSocket**: `wss://node.zenonhub.io:35998` - For real-time subscriptions and transactions + +### WebSocket Configuration + +For long-running operations (like PoW generation), you can configure WebSocket reconnection behavior: + +```javascript +import { Zenon } from 'znn-typescript-sdk'; +import { WsClientOptions } from 'znn-typescript-sdk'; + +const zenon = Zenon.getInstance(); + +// Use defaults (auto-reconnect enabled) +await zenon.initialize('wss://node.zenonhub.io:35998'); + +// Custom timeout (in milliseconds) +await zenon.initialize('wss://node.zenonhub.io:35998', 60000); + +// Custom reconnection settings +const wsOptions: WsClientOptions = { + reconnect: true, // Enable auto-reconnect + reconnect_interval: 1000, // 1 second between attempts + max_reconnects: 0 // 0 = unlimited attempts +}; + +await zenon.initialize('wss://node.zenonhub.io:35998', 30000, wsOptions); +``` + +**Note:** WebSocket connections automatically reconnect if dropped during operations. The default settings work well for most cases, including PoW generation. --- diff --git a/src/client/index.ts b/src/client/index.ts index 4c6d59b..c914b3d 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -1,5 +1,5 @@ export type { Client } from "./interfaces.js" -export { WSUpdateStream, WsClient } from "./websocket.js" +export { WSUpdateStream, WsClient, type WsClientOptions } from "./websocket.js" export { HttpClient } from "./http.js" export { newClient } from "./factory.js" export { ZnnClientException } from "./errors.js" diff --git a/src/client/websocket.ts b/src/client/websocket.ts index c814012..785f2d1 100644 --- a/src/client/websocket.ts +++ b/src/client/websocket.ts @@ -14,6 +14,13 @@ enum WebsocketStatus { Stopped, } +export interface WsClientOptions { + autoconnect?: boolean; + reconnect?: boolean; + reconnect_interval?: number; + max_reconnects?: number; +} + type WSSubscriptionCallback = (data: any[]) => void; class WSSubscriptions { @@ -69,11 +76,21 @@ export class WsClient implements ClientInterface { this.subscriptions = new WSSubscriptions(); } - initialize(url: string, retry = true, timeout = 30000): Promise { + initialize(url: string, timeout = 30000, options?: WsClientOptions): Promise { return new Promise(async (resolve, reject) => { try { this.url = url; - this._wsRpc2Client = new webSocket(this.url, {autoconnect: true, reconnect: retry}); + + // Merge default options with user-provided options + const wsOptions = { + autoconnect: true, + reconnect: true, + reconnect_interval: 1000, + max_reconnects: 0, // 0 = unlimited reconnects + ...options + }; + + this._wsRpc2Client = new webSocket(this.url, wsOptions); logger.info( `Initializing websocket connection to:${this.url} on chainIdentifier ${Zenon.getChainIdentifier()}` @@ -108,13 +125,13 @@ export class WsClient implements ClientInterface { return this._websocketIntendedState; } - async restart(): Promise { + async restart(options?: WsClientOptions): Promise { if (this._websocketIntendedState != WebsocketStatus.Running) { return; } if (this._wsRpc2Client != null && this._wsRpc2Client!.isClosed == true) { logger.info("Restarting websocket connection ..."); - await this.initialize(this.url!, true); + await this.initialize(this.url!, 30000, options); logger.info("Websocket connection successfully restarted"); } } diff --git a/src/index.ts b/src/index.ts index ec0cd89..71dc385 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,6 +5,10 @@ export { DEFAULT_CHAIN_ID, DEFAULT_NET_ID, DEFAULT_POW_BASE_PATH } from "./zenon.js"; +// +// Client exports +export type { WsClientOptions } from "./client/index.js"; + // // ABI exports export * from "./abi/index.js"; diff --git a/src/zenon.ts b/src/zenon.ts index 5a68087..092e857 100644 --- a/src/zenon.ts +++ b/src/zenon.ts @@ -1,4 +1,4 @@ -import { Client, WsClient, newClient } from "./client/index.js"; +import { Client, WsClient, newClient, type WsClientOptions } from "./client/index.js"; import { SubscribeApi, LedgerApi, StatsApi, EmbeddedApi } from "./api/index.js"; import { AccountBlockTemplate } from "./model/nom/accountBlock.js"; import { KeyPair } from "./wallet/index.js"; @@ -53,12 +53,12 @@ export class Zenon { } } - async initialize(serverUrl = this.defaultServerUrl, retry = true, timeout = 30000) { + async initialize(serverUrl = this.defaultServerUrl, timeout = 30000, wsOptions?: WsClientOptions) { this.client = newClient(serverUrl); // If it's a WebSocket client, initialize it if (this.client instanceof WsClient) { - await this.client.initialize(serverUrl, retry, timeout); + await this.client.initialize(serverUrl, timeout, wsOptions); } this._setClient(this.client);