@pythnetwork/pyth-lazer-sdk 5.2.1 → 6.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
+ import type WebSocket from "isomorphic-ws";
1
2
  import type { ErrorEvent } from "isomorphic-ws";
2
- import WebSocket from "isomorphic-ws";
3
3
  import type { Logger } from "ts-log";
4
+ import { IsomorphicEventEmitter } from "../emitter/index.js";
4
5
  import type { Request } from "../protocol.js";
5
6
  import type { ResilientWebSocketConfig } from "./resilient-websocket.js";
6
7
  import { ResilientWebSocket } from "./resilient-websocket.js";
7
- import { IsomorphicEventEmitter } from "../emitter/index.js";
8
- type WebSocketOnMessageCallback = (data: WebSocket.Data) => void | Promise<void>;
8
+ type WebSocketOnMessageCallback = (data: WebSocket.Data) => Promise<void>;
9
9
  export type WebSocketPoolConfig = {
10
10
  /**
11
11
  * Maximum number of open, parallel websocket connections
@@ -46,13 +46,18 @@ export type WebSocketPoolEvents = {
46
46
  };
47
47
  export declare class WebSocketPool extends IsomorphicEventEmitter<WebSocketPoolEvents> {
48
48
  private readonly logger;
49
+ private readonly abortSignal?;
49
50
  rwsPool: ResilientWebSocket[];
50
51
  private cache;
51
52
  private subscriptions;
52
53
  private messageListeners;
53
54
  private allConnectionsDownListeners;
55
+ private connectionRestoredListeners;
56
+ private connectionTimeoutListeners;
57
+ private connectionReconnectListeners;
54
58
  private wasAllDown;
55
59
  private checkConnectionStatesInterval;
60
+ private isShutdown;
56
61
  private constructor();
57
62
  /**
58
63
  * Creates a new WebSocketPool instance that uses multiple redundant WebSocket connections for reliability.
@@ -62,7 +67,7 @@ export declare class WebSocketPool extends IsomorphicEventEmitter<WebSocketPoolE
62
67
  * @param numConnections - Number of parallel WebSocket connections to maintain (default: 3)
63
68
  * @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
64
69
  */
65
- static create(config: WebSocketPoolConfig, token: string, logger?: Logger): Promise<WebSocketPool>;
70
+ static create(config: WebSocketPoolConfig, token: string, abortSignal: AbortSignal | null | undefined, logger?: Logger): Promise<WebSocketPool>;
66
71
  private emitPoolError;
67
72
  /**
68
73
  * Checks for error responses in JSON messages and throws appropriate errors
@@ -83,6 +88,20 @@ export declare class WebSocketPool extends IsomorphicEventEmitter<WebSocketPoolE
83
88
  * The connections may still try to reconnect in the background.
84
89
  */
85
90
  addAllConnectionsDownListener(handler: () => void): void;
91
+ /**
92
+ * Calls the handler when at least one connection is restored after all connections were down.
93
+ */
94
+ addConnectionRestoredListener(handler: () => void): void;
95
+ /**
96
+ * Calls the handler when an individual connection times out (heartbeat timeout).
97
+ * @param handler - Callback with connection index and endpoint URL
98
+ */
99
+ addConnectionTimeoutListener(handler: (connectionIndex: number, endpoint: string) => void): void;
100
+ /**
101
+ * Calls the handler when an individual connection reconnects after being down.
102
+ * @param handler - Callback with connection index and endpoint URL
103
+ */
104
+ addConnectionReconnectListener(handler: (connectionIndex: number, endpoint: string) => void): void;
86
105
  private areAllConnectionsDown;
87
106
  private isAnyConnectionEstablished;
88
107
  private checkConnectionStates;
@@ -1,5 +1,5 @@
1
1
  import type { Logger } from "ts-log";
2
- import type { ParsedPayload, Request, Response, SymbolResponse, SymbolsQueryParams, LatestPriceRequest, PriceRequest, JsonUpdate } from "./protocol.js";
2
+ import type { JsonUpdate, LatestPriceRequest, ParsedPayload, PriceRequest, Request, Response, SymbolResponse, SymbolsQueryParams } from "./protocol.js";
3
3
  import type { WebSocketPoolConfig } from "./socket/websocket-pool.js";
4
4
  export type BinaryResponse = {
5
5
  subscriptionId: number;
@@ -17,25 +17,25 @@ export type JsonOrBinaryResponse = {
17
17
  value: BinaryResponse;
18
18
  };
19
19
  export type LazerClientConfig = {
20
+ /**
21
+ * if provided and the signal is detected as canceled,
22
+ * all active listeners and connections will be unbound and killed.
23
+ */
24
+ abortSignal?: AbortSignal;
20
25
  token: string;
21
26
  metadataServiceUrl?: string;
22
27
  priceServiceUrl?: string;
23
28
  logger?: Logger;
24
- webSocketPoolConfig?: WebSocketPoolConfig;
29
+ webSocketPoolConfig: WebSocketPoolConfig;
25
30
  };
26
31
  export declare class PythLazerClient {
27
- private readonly token;
28
- private readonly metadataServiceUrl;
29
- private readonly priceServiceUrl;
30
- private readonly logger;
31
- private readonly wsp?;
32
+ private logger;
33
+ private metadataServiceUrl;
34
+ private priceServiceUrl;
35
+ private token;
36
+ private wsp;
37
+ private abortSignal;
32
38
  private constructor();
33
- /**
34
- * Gets the WebSocket pool. If the WebSocket pool is not configured, an error is thrown.
35
- * @throws Error if WebSocket pool is not configured
36
- * @returns The WebSocket pool
37
- */
38
- private getWebSocketPool;
39
39
  /**
40
40
  * Creates a new PythLazerClient instance.
41
41
  * @param config - Configuration including token, metadata service URL, and price service URL, and WebSocket pool configuration
@@ -48,6 +48,10 @@ export declare class PythLazerClient {
48
48
  * or a binary response containing EVM, Solana, or parsed payload data.
49
49
  */
50
50
  addMessageListener(handler: (event: JsonOrBinaryResponse) => void): void;
51
+ /**
52
+ * binds any internal event handlers
53
+ */
54
+ bindHandlers(): void;
51
55
  subscribe(request: Request): void;
52
56
  unsubscribe(subscriptionId: number): void;
53
57
  send(request: Request): void;
@@ -57,6 +61,25 @@ export declare class PythLazerClient {
57
61
  * @param handler - Function to be called when all connections are down
58
62
  */
59
63
  addAllConnectionsDownListener(handler: () => void): void;
64
+ /**
65
+ * Registers a handler function that will be called when at least one connection is restored after all were down.
66
+ * @param handler - Function to be called when connection is restored
67
+ */
68
+ addConnectionRestoredListener(handler: () => void): void;
69
+ /**
70
+ * Registers a handler function that will be called when an individual connection times out (heartbeat timeout).
71
+ * @param handler - Function to be called with connection index and endpoint URL
72
+ */
73
+ addConnectionTimeoutListener(handler: (connectionIndex: number, endpoint: string) => void): void;
74
+ /**
75
+ * Registers a handler function that will be called when an individual connection reconnects.
76
+ * @param handler - Function to be called with connection index and endpoint URL
77
+ */
78
+ addConnectionReconnectListener(handler: (connectionIndex: number, endpoint: string) => void): void;
79
+ /**
80
+ * called if and only if a user provided an abort signal and it was aborted
81
+ */
82
+ protected abortHandler: () => void;
60
83
  shutdown(): void;
61
84
  /**
62
85
  * Private helper method to make authenticated HTTP requests with Bearer token
@@ -7,27 +7,20 @@ const UINT16_NUM_BYTES = 2;
7
7
  const UINT32_NUM_BYTES = 4;
8
8
  const UINT64_NUM_BYTES = 8;
9
9
  export class PythLazerClient {
10
- token;
10
+ logger;
11
11
  metadataServiceUrl;
12
12
  priceServiceUrl;
13
- logger;
13
+ token;
14
14
  wsp;
15
- constructor(token, metadataServiceUrl, priceServiceUrl, logger, wsp){
16
- this.token = token;
15
+ abortSignal;
16
+ constructor({ abortSignal, logger, metadataServiceUrl, priceServiceUrl, token, wsp }){
17
+ this.abortSignal = abortSignal;
18
+ this.logger = logger;
17
19
  this.metadataServiceUrl = metadataServiceUrl;
18
20
  this.priceServiceUrl = priceServiceUrl;
19
- this.logger = logger;
21
+ this.token = token;
20
22
  this.wsp = wsp;
21
- }
22
- /**
23
- * Gets the WebSocket pool. If the WebSocket pool is not configured, an error is thrown.
24
- * @throws Error if WebSocket pool is not configured
25
- * @returns The WebSocket pool
26
- */ getWebSocketPool() {
27
- if (!this.wsp) {
28
- throw new Error("WebSocket pool is not available. Make sure to provide webSocketPoolConfig when creating the client.");
29
- }
30
- return this.wsp;
23
+ this.bindHandlers();
31
24
  }
32
25
  /**
33
26
  * Creates a new PythLazerClient instance.
@@ -38,12 +31,20 @@ export class PythLazerClient {
38
31
  const metadataServiceUrl = (config.metadataServiceUrl ?? DEFAULT_METADATA_SERVICE_URL).replace(/\/+$/, "");
39
32
  const priceServiceUrl = (config.priceServiceUrl ?? DEFAULT_PRICE_SERVICE_URL).replace(/\/+$/, "");
40
33
  const logger = config.logger ?? dummyLogger;
41
- // If webSocketPoolConfig is provided, create a WebSocket pool and block until at least one connection is established.
42
- let wsp;
43
- if (config.webSocketPoolConfig) {
44
- wsp = await WebSocketPool.create(config.webSocketPoolConfig, token, logger);
45
- }
46
- return new PythLazerClient(token, metadataServiceUrl, priceServiceUrl, logger, wsp);
34
+ // the prior API was mismatched, in that it marked a websocket pool as optional,
35
+ // yet all internal code on the Pyth Pro client used it and threw if it didn't exist.
36
+ // now, the typings indicate it's no longer optional and we don't sanity check
37
+ // if it's set
38
+ const wsp = await WebSocketPool.create(config.webSocketPoolConfig, token, config.abortSignal, logger);
39
+ const client = new PythLazerClient({
40
+ abortSignal: config.abortSignal,
41
+ logger,
42
+ metadataServiceUrl,
43
+ priceServiceUrl,
44
+ token,
45
+ wsp
46
+ });
47
+ return client;
47
48
  }
48
49
  /**
49
50
  * Adds a message listener that receives either JSON or binary responses from the WebSocket connections.
@@ -51,9 +52,8 @@ export class PythLazerClient {
51
52
  * @param handler - Callback function that receives the parsed message. The message can be either a JSON response
52
53
  * or a binary response containing EVM, Solana, or parsed payload data.
53
54
  */ addMessageListener(handler) {
54
- const wsp = this.getWebSocketPool();
55
- wsp.addMessageListener(async (data)=>{
56
- if (typeof data == "string") {
55
+ this.wsp.addMessageListener(async (data)=>{
56
+ if (typeof data === "string") {
57
57
  handler({
58
58
  type: "json",
59
59
  value: JSON.parse(data)
@@ -64,7 +64,7 @@ export class PythLazerClient {
64
64
  let pos = 0;
65
65
  const magic = buffData.subarray(pos, pos + UINT32_NUM_BYTES).readUint32LE();
66
66
  pos += UINT32_NUM_BYTES;
67
- if (magic != BINARY_UPDATE_FORMAT_MAGIC_LE) {
67
+ if (magic !== BINARY_UPDATE_FORMAT_MAGIC_LE) {
68
68
  throw new Error("binary update format magic mismatch");
69
69
  }
70
70
  // TODO: some uint64 values may not be representable as Number.
@@ -77,15 +77,15 @@ export class PythLazerClient {
77
77
  const len = buffData.subarray(pos, pos + UINT16_NUM_BYTES).readUint16BE();
78
78
  pos += UINT16_NUM_BYTES;
79
79
  const magic = buffData.subarray(pos, pos + UINT32_NUM_BYTES).readUint32LE();
80
- if (magic == FORMAT_MAGICS_LE.EVM) {
80
+ if (magic === FORMAT_MAGICS_LE.EVM) {
81
81
  value.evm = buffData.subarray(pos, pos + len);
82
- } else if (magic == FORMAT_MAGICS_LE.SOLANA) {
82
+ } else if (magic === FORMAT_MAGICS_LE.SOLANA) {
83
83
  value.solana = buffData.subarray(pos, pos + len);
84
- } else if (magic == FORMAT_MAGICS_LE.LE_ECDSA) {
84
+ } else if (magic === FORMAT_MAGICS_LE.LE_ECDSA) {
85
85
  value.leEcdsa = buffData.subarray(pos, pos + len);
86
- } else if (magic == FORMAT_MAGICS_LE.LE_UNSIGNED) {
86
+ } else if (magic === FORMAT_MAGICS_LE.LE_UNSIGNED) {
87
87
  value.leUnsigned = buffData.subarray(pos, pos + len);
88
- } else if (magic == FORMAT_MAGICS_LE.JSON) {
88
+ } else if (magic === FORMAT_MAGICS_LE.JSON) {
89
89
  value.parsed = JSON.parse(buffData.subarray(pos + UINT32_NUM_BYTES, pos + len).toString());
90
90
  } else {
91
91
  throw new Error(`unknown magic: ${magic.toString()}`);
@@ -98,43 +98,67 @@ export class PythLazerClient {
98
98
  });
99
99
  });
100
100
  }
101
+ /**
102
+ * binds any internal event handlers
103
+ */ bindHandlers() {
104
+ this.abortSignal?.addEventListener("abort", this.abortHandler);
105
+ }
101
106
  subscribe(request) {
102
- const wsp = this.getWebSocketPool();
103
107
  if (request.type !== "subscribe") {
104
108
  throw new Error("Request must be a subscribe request");
105
109
  }
106
- wsp.addSubscription(request);
110
+ this.wsp.addSubscription(request);
107
111
  }
108
112
  unsubscribe(subscriptionId) {
109
- const wsp = this.getWebSocketPool();
110
- wsp.removeSubscription(subscriptionId);
113
+ this.wsp.removeSubscription(subscriptionId);
111
114
  }
112
115
  send(request) {
113
- const wsp = this.getWebSocketPool();
114
- wsp.sendRequest(request);
116
+ this.wsp.sendRequest(request);
115
117
  }
116
118
  /**
117
119
  * Registers a handler function that will be called whenever all WebSocket connections are down or attempting to reconnect.
118
120
  * The connections may still try to reconnect in the background. To shut down the pool, call `shutdown()`.
119
121
  * @param handler - Function to be called when all connections are down
120
122
  */ addAllConnectionsDownListener(handler) {
121
- const wsp = this.getWebSocketPool();
122
- wsp.addAllConnectionsDownListener(handler);
123
+ this.wsp.addAllConnectionsDownListener(handler);
124
+ }
125
+ /**
126
+ * Registers a handler function that will be called when at least one connection is restored after all were down.
127
+ * @param handler - Function to be called when connection is restored
128
+ */ addConnectionRestoredListener(handler) {
129
+ this.wsp.addConnectionRestoredListener(handler);
123
130
  }
131
+ /**
132
+ * Registers a handler function that will be called when an individual connection times out (heartbeat timeout).
133
+ * @param handler - Function to be called with connection index and endpoint URL
134
+ */ addConnectionTimeoutListener(handler) {
135
+ this.wsp.addConnectionTimeoutListener(handler);
136
+ }
137
+ /**
138
+ * Registers a handler function that will be called when an individual connection reconnects.
139
+ * @param handler - Function to be called with connection index and endpoint URL
140
+ */ addConnectionReconnectListener(handler) {
141
+ this.wsp.addConnectionReconnectListener(handler);
142
+ }
143
+ /**
144
+ * called if and only if a user provided an abort signal and it was aborted
145
+ */ abortHandler = ()=>{
146
+ this.shutdown();
147
+ };
124
148
  shutdown() {
125
- const wsp = this.getWebSocketPool();
126
- wsp.shutdown();
149
+ // Clean up abort signal listener to prevent memory leak
150
+ this.abortSignal?.removeEventListener("abort", this.abortHandler);
151
+ this.wsp.shutdown();
127
152
  }
128
153
  /**
129
154
  * Private helper method to make authenticated HTTP requests with Bearer token
130
155
  * @param url - The URL to fetch
131
156
  * @param options - Additional fetch options
132
157
  * @returns Promise resolving to the fetch Response
133
- */ async authenticatedFetch(url, options = {}) {
134
- const headers = {
135
- Authorization: `Bearer ${this.token}`,
136
- ...options.headers
137
- };
158
+ */ authenticatedFetch(url, options = {}) {
159
+ // Handle all possible types of headers (Headers object, array, or plain object)
160
+ const headers = new Headers(options.headers);
161
+ headers.set("Authorization", headers.get("authorization") ?? `Bearer ${this.token}`);
138
162
  return fetch(url, {
139
163
  ...options,
140
164
  headers
@@ -171,15 +195,15 @@ export class PythLazerClient {
171
195
  try {
172
196
  const body = JSON.stringify(params);
173
197
  this.logger.debug("getLatestPrice", {
174
- url,
175
- body
198
+ body,
199
+ url
176
200
  });
177
201
  const response = await this.authenticatedFetch(url, {
178
- method: "POST",
202
+ body: body,
179
203
  headers: {
180
204
  "Content-Type": "application/json"
181
205
  },
182
- body: body
206
+ method: "POST"
183
207
  });
184
208
  if (!response.ok) {
185
209
  throw new Error(`HTTP error! status: ${String(response.status)} - ${await response.text()}`);
@@ -198,15 +222,15 @@ export class PythLazerClient {
198
222
  try {
199
223
  const body = JSON.stringify(params);
200
224
  this.logger.debug("getPrice", {
201
- url,
202
- body
225
+ body,
226
+ url
203
227
  });
204
228
  const response = await this.authenticatedFetch(url, {
205
- method: "POST",
229
+ body: body,
206
230
  headers: {
207
231
  "Content-Type": "application/json"
208
232
  },
209
- body: body
233
+ method: "POST"
210
234
  });
211
235
  if (!response.ok) {
212
236
  throw new Error(`HTTP error! status: ${String(response.status)} - ${await response.text()}`);
@@ -1,3 +1,3 @@
1
1
  export * from "./client.js";
2
- export * from "./protocol.js";
3
2
  export * from "./constants.js";
3
+ export * from "./protocol.js";
@@ -1,3 +1,3 @@
1
1
  export * from "./client.mjs";
2
- export * from "./protocol.mjs";
3
2
  export * from "./constants.mjs";
3
+ export * from "./protocol.mjs";
@@ -1,18 +1,19 @@
1
1
  export type Format = "evm" | "solana" | "leEcdsa" | "leUnsigned";
2
2
  export type DeliveryFormat = "json" | "binary";
3
3
  export type JsonBinaryEncoding = "base64" | "hex";
4
- export type PriceFeedProperty = "price" | "bestBidPrice" | "bestAskPrice" | "exponent" | "publisherCount" | "confidence" | "fundingRate" | "fundingTimestamp" | "fundingRateInterval";
5
- export type Channel = "real_time" | "fixed_rate@50ms" | "fixed_rate@200ms";
4
+ export type PriceFeedProperty = "price" | "bestBidPrice" | "bestAskPrice" | "exponent" | "publisherCount" | "confidence" | "fundingRate" | "fundingTimestamp" | "fundingRateInterval" | "marketSession" | "emaPrice" | "emaConfidence" | "feedUpdateTimestamp";
5
+ export type Channel = "real_time" | "fixed_rate@50ms" | "fixed_rate@200ms" | "fixed_rate@1000ms";
6
6
  export type Request = {
7
7
  type: "subscribe";
8
8
  subscriptionId: number;
9
- priceFeedIds: number[];
9
+ priceFeedIds?: number[] | undefined;
10
+ symbols?: string[] | undefined;
10
11
  properties: PriceFeedProperty[];
11
12
  formats: Format[];
12
- deliveryFormat?: DeliveryFormat;
13
- jsonBinaryEncoding?: JsonBinaryEncoding;
14
- parsed?: boolean;
15
- ignoreInvalidFeedIds?: boolean;
13
+ deliveryFormat?: DeliveryFormat | undefined;
14
+ jsonBinaryEncoding?: JsonBinaryEncoding | undefined;
15
+ parsed?: boolean | undefined;
16
+ ignoreInvalidFeedIds?: boolean | undefined;
16
17
  channel: Channel;
17
18
  } | {
18
19
  type: "unsubscribe";
@@ -25,7 +26,14 @@ export type ParsedFeedPayload = {
25
26
  bestAskPrice?: string | undefined;
26
27
  publisherCount?: number | undefined;
27
28
  exponent?: number | undefined;
28
- confidence?: string | undefined;
29
+ confidence?: number | undefined;
30
+ fundingRate?: number | undefined;
31
+ fundingTimestamp?: number | undefined;
32
+ fundingRateInterval?: number | undefined;
33
+ marketSession?: string | undefined;
34
+ emaPrice?: string | undefined;
35
+ emaConfidence?: number | undefined;
36
+ feedUpdateTimestamp?: number | undefined;
29
37
  };
30
38
  export type ParsedPayload = {
31
39
  timestampUs: string;
@@ -70,13 +78,13 @@ export type Response = {
70
78
  };
71
79
  export declare const BINARY_UPDATE_FORMAT_MAGIC_LE = 461928307;
72
80
  export declare const FORMAT_MAGICS_LE: {
73
- JSON: number;
74
81
  EVM: number;
75
- SOLANA: number;
82
+ JSON: number;
76
83
  LE_ECDSA: number;
77
84
  LE_UNSIGNED: number;
85
+ SOLANA: number;
78
86
  };
79
- export type AssetType = "crypto" | "fx" | "equity" | "metal" | "rates" | "nav" | "commodity" | "funding-rate";
87
+ export type AssetType = "crypto" | "fx" | "equity" | "metal" | "rates" | "nav" | "commodity" | "funding-rate" | "eco" | "kalshi";
80
88
  export type SymbolsQueryParams = {
81
89
  query?: string;
82
90
  asset_type?: AssetType;
@@ -1,10 +1,10 @@
1
1
  export const BINARY_UPDATE_FORMAT_MAGIC_LE = 461_928_307;
2
2
  export const FORMAT_MAGICS_LE = {
3
- JSON: 3_302_625_434,
4
3
  EVM: 2_593_727_018,
5
- SOLANA: 2_182_742_457,
4
+ JSON: 3_302_625_434,
6
5
  LE_ECDSA: 1_296_547_300,
7
- LE_UNSIGNED: 1_499_680_012
6
+ LE_UNSIGNED: 1_499_680_012,
7
+ SOLANA: 2_182_742_457
8
8
  };
9
9
  export var CustomSocketClosureCodes = /*#__PURE__*/ function(CustomSocketClosureCodes) {
10
10
  CustomSocketClosureCodes[CustomSocketClosureCodes["CLIENT_TIMEOUT_BUT_RECONNECTING"] = 4000] = "CLIENT_TIMEOUT_BUT_RECONNECTING";
@@ -44,6 +44,7 @@ export declare class ResilientWebSocket {
44
44
  onError: (error: ErrorEvent) => void;
45
45
  onMessage: (data: WebSocket.Data) => void;
46
46
  onReconnect: () => void;
47
+ onTimeout: () => void;
47
48
  constructor(config: ResilientWebSocketConfig);
48
49
  send(data: string | Buffer): void;
49
50
  startWebSocket(): void;
@@ -30,6 +30,7 @@ export class ResilientWebSocket {
30
30
  onError;
31
31
  onMessage;
32
32
  onReconnect;
33
+ onTimeout;
33
34
  constructor(config){
34
35
  this.endpoint = config.endpoint;
35
36
  this.wsOptions = config.wsOptions;
@@ -47,6 +48,9 @@ export class ResilientWebSocket {
47
48
  this.onReconnect = ()=>{
48
49
  // Empty function, can be set by the user.
49
50
  };
51
+ this.onTimeout = ()=>{
52
+ // Empty function, can be set by the user.
53
+ };
50
54
  }
51
55
  send(data) {
52
56
  this.logger.debug(`Sending message`);
@@ -114,6 +118,7 @@ export class ResilientWebSocket {
114
118
  this.heartbeatTimeout = setTimeout(()=>{
115
119
  const warnMsg = "Connection timed out. Reconnecting...";
116
120
  this.logger.warn(warnMsg);
121
+ this.onTimeout();
117
122
  if (this.wsClient) {
118
123
  if (typeof this.wsClient.terminate === "function") {
119
124
  this.wsClient.terminate();
@@ -1,11 +1,11 @@
1
+ import type WebSocket from "isomorphic-ws";
1
2
  import type { ErrorEvent } from "isomorphic-ws";
2
- import WebSocket from "isomorphic-ws";
3
3
  import type { Logger } from "ts-log";
4
+ import { IsomorphicEventEmitter } from "../emitter/index.js";
4
5
  import type { Request } from "../protocol.js";
5
6
  import type { ResilientWebSocketConfig } from "./resilient-websocket.js";
6
7
  import { ResilientWebSocket } from "./resilient-websocket.js";
7
- import { IsomorphicEventEmitter } from "../emitter/index.js";
8
- type WebSocketOnMessageCallback = (data: WebSocket.Data) => void | Promise<void>;
8
+ type WebSocketOnMessageCallback = (data: WebSocket.Data) => Promise<void>;
9
9
  export type WebSocketPoolConfig = {
10
10
  /**
11
11
  * Maximum number of open, parallel websocket connections
@@ -46,13 +46,18 @@ export type WebSocketPoolEvents = {
46
46
  };
47
47
  export declare class WebSocketPool extends IsomorphicEventEmitter<WebSocketPoolEvents> {
48
48
  private readonly logger;
49
+ private readonly abortSignal?;
49
50
  rwsPool: ResilientWebSocket[];
50
51
  private cache;
51
52
  private subscriptions;
52
53
  private messageListeners;
53
54
  private allConnectionsDownListeners;
55
+ private connectionRestoredListeners;
56
+ private connectionTimeoutListeners;
57
+ private connectionReconnectListeners;
54
58
  private wasAllDown;
55
59
  private checkConnectionStatesInterval;
60
+ private isShutdown;
56
61
  private constructor();
57
62
  /**
58
63
  * Creates a new WebSocketPool instance that uses multiple redundant WebSocket connections for reliability.
@@ -62,7 +67,7 @@ export declare class WebSocketPool extends IsomorphicEventEmitter<WebSocketPoolE
62
67
  * @param numConnections - Number of parallel WebSocket connections to maintain (default: 3)
63
68
  * @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
64
69
  */
65
- static create(config: WebSocketPoolConfig, token: string, logger?: Logger): Promise<WebSocketPool>;
70
+ static create(config: WebSocketPoolConfig, token: string, abortSignal: AbortSignal | null | undefined, logger?: Logger): Promise<WebSocketPool>;
66
71
  private emitPoolError;
67
72
  /**
68
73
  * Checks for error responses in JSON messages and throws appropriate errors
@@ -83,6 +88,20 @@ export declare class WebSocketPool extends IsomorphicEventEmitter<WebSocketPoolE
83
88
  * The connections may still try to reconnect in the background.
84
89
  */
85
90
  addAllConnectionsDownListener(handler: () => void): void;
91
+ /**
92
+ * Calls the handler when at least one connection is restored after all connections were down.
93
+ */
94
+ addConnectionRestoredListener(handler: () => void): void;
95
+ /**
96
+ * Calls the handler when an individual connection times out (heartbeat timeout).
97
+ * @param handler - Callback with connection index and endpoint URL
98
+ */
99
+ addConnectionTimeoutListener(handler: (connectionIndex: number, endpoint: string) => void): void;
100
+ /**
101
+ * Calls the handler when an individual connection reconnects after being down.
102
+ * @param handler - Callback with connection index and endpoint URL
103
+ */
104
+ addConnectionReconnectListener(handler: (connectionIndex: number, endpoint: string) => void): void;
86
105
  private areAllConnectionsDown;
87
106
  private isAnyConnectionEstablished;
88
107
  private checkConnectionStates;