@pythnetwork/pyth-lazer-sdk 6.0.0 → 6.2.1
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.
- package/dist/cjs/client.cjs +89 -52
- package/dist/cjs/client.d.ts +28 -13
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.d.ts +1 -1
- package/dist/cjs/protocol.cjs +3 -3
- package/dist/cjs/protocol.d.ts +3 -3
- package/dist/cjs/socket/resilient-websocket.cjs +5 -0
- package/dist/cjs/socket/resilient-websocket.d.ts +1 -0
- package/dist/cjs/socket/websocket-pool.cjs +145 -62
- package/dist/cjs/socket/websocket-pool.d.ts +21 -3
- package/dist/esm/client.d.ts +28 -13
- package/dist/esm/client.mjs +89 -52
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/protocol.d.ts +3 -3
- package/dist/esm/protocol.mjs +3 -3
- package/dist/esm/socket/resilient-websocket.d.ts +1 -0
- package/dist/esm/socket/resilient-websocket.mjs +5 -0
- package/dist/esm/socket/websocket-pool.d.ts +21 -3
- package/dist/esm/socket/websocket-pool.mjs +145 -62
- package/package.json +113 -123
package/dist/esm/index.d.ts
CHANGED
package/dist/esm/index.mjs
CHANGED
package/dist/esm/protocol.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ export type Format = "evm" | "solana" | "leEcdsa" | "leUnsigned";
|
|
|
2
2
|
export type DeliveryFormat = "json" | "binary";
|
|
3
3
|
export type JsonBinaryEncoding = "base64" | "hex";
|
|
4
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";
|
|
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;
|
|
@@ -78,11 +78,11 @@ export type Response = {
|
|
|
78
78
|
};
|
|
79
79
|
export declare const BINARY_UPDATE_FORMAT_MAGIC_LE = 461928307;
|
|
80
80
|
export declare const FORMAT_MAGICS_LE: {
|
|
81
|
-
JSON: number;
|
|
82
81
|
EVM: number;
|
|
83
|
-
|
|
82
|
+
JSON: number;
|
|
84
83
|
LE_ECDSA: number;
|
|
85
84
|
LE_UNSIGNED: number;
|
|
85
|
+
SOLANA: number;
|
|
86
86
|
};
|
|
87
87
|
export type AssetType = "crypto" | "fx" | "equity" | "metal" | "rates" | "nav" | "commodity" | "funding-rate" | "eco" | "kalshi";
|
|
88
88
|
export type SymbolsQueryParams = {
|
package/dist/esm/protocol.mjs
CHANGED
|
@@ -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
|
-
|
|
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,10 +1,10 @@
|
|
|
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
8
|
type WebSocketOnMessageCallback = (data: WebSocket.Data) => Promise<void>;
|
|
9
9
|
export type WebSocketPoolConfig = {
|
|
10
10
|
/**
|
|
@@ -51,8 +51,12 @@ export declare class WebSocketPool extends IsomorphicEventEmitter<WebSocketPoolE
|
|
|
51
51
|
private subscriptions;
|
|
52
52
|
private messageListeners;
|
|
53
53
|
private allConnectionsDownListeners;
|
|
54
|
+
private connectionRestoredListeners;
|
|
55
|
+
private connectionTimeoutListeners;
|
|
56
|
+
private connectionReconnectListeners;
|
|
54
57
|
private wasAllDown;
|
|
55
58
|
private checkConnectionStatesInterval;
|
|
59
|
+
private isShutdown;
|
|
56
60
|
private constructor();
|
|
57
61
|
/**
|
|
58
62
|
* Creates a new WebSocketPool instance that uses multiple redundant WebSocket connections for reliability.
|
|
@@ -62,7 +66,7 @@ export declare class WebSocketPool extends IsomorphicEventEmitter<WebSocketPoolE
|
|
|
62
66
|
* @param numConnections - Number of parallel WebSocket connections to maintain (default: 3)
|
|
63
67
|
* @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
|
|
64
68
|
*/
|
|
65
|
-
static create(config: WebSocketPoolConfig, token: string, logger?: Logger): Promise<WebSocketPool>;
|
|
69
|
+
static create(config: WebSocketPoolConfig, token: string, abortSignal?: AbortSignal | null | undefined, logger?: Logger): Promise<WebSocketPool>;
|
|
66
70
|
private emitPoolError;
|
|
67
71
|
/**
|
|
68
72
|
* Checks for error responses in JSON messages and throws appropriate errors
|
|
@@ -83,6 +87,20 @@ export declare class WebSocketPool extends IsomorphicEventEmitter<WebSocketPoolE
|
|
|
83
87
|
* The connections may still try to reconnect in the background.
|
|
84
88
|
*/
|
|
85
89
|
addAllConnectionsDownListener(handler: () => void): void;
|
|
90
|
+
/**
|
|
91
|
+
* Calls the handler when at least one connection is restored after all connections were down.
|
|
92
|
+
*/
|
|
93
|
+
addConnectionRestoredListener(handler: () => void): void;
|
|
94
|
+
/**
|
|
95
|
+
* Calls the handler when an individual connection times out (heartbeat timeout).
|
|
96
|
+
* @param handler - Callback with connection index and endpoint URL
|
|
97
|
+
*/
|
|
98
|
+
addConnectionTimeoutListener(handler: (connectionIndex: number, endpoint: string) => void): void;
|
|
99
|
+
/**
|
|
100
|
+
* Calls the handler when an individual connection reconnects after being down.
|
|
101
|
+
* @param handler - Callback with connection index and endpoint URL
|
|
102
|
+
*/
|
|
103
|
+
addConnectionReconnectListener(handler: (connectionIndex: number, endpoint: string) => void): void;
|
|
86
104
|
private areAllConnectionsDown;
|
|
87
105
|
private isAnyConnectionEstablished;
|
|
88
106
|
private checkConnectionStates;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import TTLCache from "@isaacs/ttlcache";
|
|
2
2
|
import { dummyLogger } from "ts-log";
|
|
3
|
-
import { ResilientWebSocket } from "./resilient-websocket.mjs";
|
|
4
3
|
import { DEFAULT_STREAM_SERVICE_0_URL, DEFAULT_STREAM_SERVICE_1_URL } from "../constants.mjs";
|
|
5
4
|
import { IsomorphicEventEmitter } from "../emitter/index.mjs";
|
|
6
5
|
import { addAuthTokenToWebSocketUrl, bufferFromWebsocketData, envIsBrowserOrWorker } from "../util/index.mjs";
|
|
6
|
+
import { ResilientWebSocket } from "./resilient-websocket.mjs";
|
|
7
7
|
const DEFAULT_NUM_CONNECTIONS = 4;
|
|
8
8
|
export class WebSocketPool extends IsomorphicEventEmitter {
|
|
9
9
|
logger;
|
|
@@ -12,8 +12,12 @@ export class WebSocketPool extends IsomorphicEventEmitter {
|
|
|
12
12
|
subscriptions;
|
|
13
13
|
messageListeners;
|
|
14
14
|
allConnectionsDownListeners;
|
|
15
|
+
connectionRestoredListeners;
|
|
16
|
+
connectionTimeoutListeners;
|
|
17
|
+
connectionReconnectListeners;
|
|
15
18
|
wasAllDown = true;
|
|
16
19
|
checkConnectionStatesInterval;
|
|
20
|
+
isShutdown = false;
|
|
17
21
|
constructor(logger){
|
|
18
22
|
super(), this.logger = logger;
|
|
19
23
|
this.rwsPool = [];
|
|
@@ -23,6 +27,9 @@ export class WebSocketPool extends IsomorphicEventEmitter {
|
|
|
23
27
|
this.subscriptions = new Map();
|
|
24
28
|
this.messageListeners = [];
|
|
25
29
|
this.allConnectionsDownListeners = [];
|
|
30
|
+
this.connectionRestoredListeners = [];
|
|
31
|
+
this.connectionTimeoutListeners = [];
|
|
32
|
+
this.connectionReconnectListeners = [];
|
|
26
33
|
// Start monitoring connection states
|
|
27
34
|
this.checkConnectionStatesInterval = setInterval(()=>{
|
|
28
35
|
this.checkConnectionStates();
|
|
@@ -35,76 +42,103 @@ export class WebSocketPool extends IsomorphicEventEmitter {
|
|
|
35
42
|
* @param token - Authentication token to use for the connections
|
|
36
43
|
* @param numConnections - Number of parallel WebSocket connections to maintain (default: 3)
|
|
37
44
|
* @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
|
|
38
|
-
*/ static async create(config, token, logger) {
|
|
45
|
+
*/ static async create(config, token, abortSignal, logger) {
|
|
46
|
+
// Helper to check if aborted and throw
|
|
47
|
+
const throwIfAborted = ()=>{
|
|
48
|
+
if (abortSignal?.aborted) {
|
|
49
|
+
throw new DOMException("WebSocketPool.create() was aborted", "AbortError");
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
// Check before starting
|
|
53
|
+
throwIfAborted();
|
|
39
54
|
const urls = config.urls ?? [
|
|
40
55
|
DEFAULT_STREAM_SERVICE_0_URL,
|
|
41
56
|
DEFAULT_STREAM_SERVICE_1_URL
|
|
42
57
|
];
|
|
43
58
|
const log = logger ?? dummyLogger;
|
|
44
59
|
const pool = new WebSocketPool(log);
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
for(let i = 0; i < numConnections; i++){
|
|
56
|
-
const baseUrl = urls[i % urls.length];
|
|
57
|
-
const isBrowser = envIsBrowserOrWorker();
|
|
58
|
-
const url = isBrowser ? addAuthTokenToWebSocketUrl(baseUrl, token) : baseUrl;
|
|
59
|
-
if (!url) {
|
|
60
|
-
throw new Error(`URLs must not be null or empty`);
|
|
60
|
+
try {
|
|
61
|
+
const numConnections = config.numConnections ?? DEFAULT_NUM_CONNECTIONS;
|
|
62
|
+
// bind a handler to capture any emitted errors and send them to the user-provided
|
|
63
|
+
// onWebSocketPoolError callback (if it is present)
|
|
64
|
+
if (typeof config.onWebSocketPoolError === "function") {
|
|
65
|
+
pool.on("error", config.onWebSocketPoolError);
|
|
66
|
+
pool.once("shutdown", ()=>{
|
|
67
|
+
// unbind all error handlers so we don't leak memory
|
|
68
|
+
pool.off("error");
|
|
69
|
+
});
|
|
61
70
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const rws = new ResilientWebSocket({
|
|
69
|
-
...config.rwsConfig,
|
|
70
|
-
endpoint: url,
|
|
71
|
-
wsOptions,
|
|
72
|
-
logger: log
|
|
73
|
-
});
|
|
74
|
-
// If a websocket client unexpectedly disconnects, ResilientWebSocket will reestablish
|
|
75
|
-
// the connection and call the onReconnect callback.
|
|
76
|
-
rws.onReconnect = ()=>{
|
|
77
|
-
if (rws.wsUserClosed) {
|
|
78
|
-
return;
|
|
71
|
+
for(let i = 0; i < numConnections; i++){
|
|
72
|
+
const baseUrl = urls[i % urls.length];
|
|
73
|
+
const isBrowser = envIsBrowserOrWorker();
|
|
74
|
+
const url = isBrowser ? addAuthTokenToWebSocketUrl(baseUrl, token) : baseUrl;
|
|
75
|
+
if (!url) {
|
|
76
|
+
throw new Error(`URLs must not be null or empty`);
|
|
79
77
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
const wsOptions = {
|
|
79
|
+
...config.rwsConfig?.wsOptions,
|
|
80
|
+
headers: isBrowser ? undefined : {
|
|
81
|
+
Authorization: `Bearer ${token}`
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
const rws = new ResilientWebSocket({
|
|
85
|
+
...config.rwsConfig,
|
|
86
|
+
endpoint: url,
|
|
87
|
+
logger: log,
|
|
88
|
+
wsOptions
|
|
89
|
+
});
|
|
90
|
+
const connectionIndex = i;
|
|
91
|
+
const connectionEndpoint = url;
|
|
92
|
+
// If a websocket client unexpectedly disconnects, ResilientWebSocket will reestablish
|
|
93
|
+
// the connection and call the onReconnect callback.
|
|
94
|
+
rws.onReconnect = ()=>{
|
|
95
|
+
if (rws.wsUserClosed || pool.isShutdown) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
for (const listener of pool.connectionReconnectListeners){
|
|
99
|
+
listener(connectionIndex, connectionEndpoint);
|
|
85
100
|
}
|
|
101
|
+
for (const [, request] of pool.subscriptions){
|
|
102
|
+
try {
|
|
103
|
+
rws.send(JSON.stringify(request));
|
|
104
|
+
} catch (error) {
|
|
105
|
+
pool.logger.error("Failed to resend subscription on reconnect:", error);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
rws.onTimeout = ()=>{
|
|
110
|
+
for (const listener of pool.connectionTimeoutListeners){
|
|
111
|
+
listener(connectionIndex, connectionEndpoint);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
|
115
|
+
const onErrorHandler = config.onWebSocketError ?? config.onError;
|
|
116
|
+
if (typeof onErrorHandler === "function") {
|
|
117
|
+
rws.onError = onErrorHandler;
|
|
86
118
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
119
|
+
// Handle all client messages ourselves. Dedupe before sending to registered message handlers.
|
|
120
|
+
rws.onMessage = (data)=>{
|
|
121
|
+
pool.dedupeHandler(data).catch((error)=>{
|
|
122
|
+
pool.emitPoolError(error, "Error in WebSocketPool dedupeHandler");
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
pool.rwsPool.push(rws);
|
|
126
|
+
rws.startWebSocket();
|
|
92
127
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
128
|
+
pool.logger.info(`Started WebSocketPool with ${numConnections.toString()} connections. Waiting for at least one to connect...`);
|
|
129
|
+
while(!pool.isAnyConnectionEstablished() && !pool.isShutdown){
|
|
130
|
+
throwIfAborted();
|
|
131
|
+
await new Promise((resolve)=>setTimeout(resolve, 100));
|
|
132
|
+
}
|
|
133
|
+
// Final check after loop exits
|
|
134
|
+
throwIfAborted();
|
|
135
|
+
pool.logger.info(`At least one WebSocket connection is established. WebSocketPool is ready.`);
|
|
136
|
+
return pool;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
// Clean up the pool if aborted during connection wait
|
|
139
|
+
pool.shutdown();
|
|
140
|
+
throw error;
|
|
105
141
|
}
|
|
106
|
-
pool.logger.info(`At least one WebSocket connection is established. WebSocketPool is ready.`);
|
|
107
|
-
return pool;
|
|
108
142
|
}
|
|
109
143
|
emitPoolError(error, context) {
|
|
110
144
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
@@ -156,6 +190,10 @@ export class WebSocketPool extends IsomorphicEventEmitter {
|
|
|
156
190
|
}
|
|
157
191
|
};
|
|
158
192
|
sendRequest(request) {
|
|
193
|
+
if (this.isShutdown) {
|
|
194
|
+
this.logger.warn("Cannot send request: WebSocketPool is shutdown");
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
159
197
|
for (const rws of this.rwsPool){
|
|
160
198
|
try {
|
|
161
199
|
rws.send(JSON.stringify(request));
|
|
@@ -165,6 +203,10 @@ export class WebSocketPool extends IsomorphicEventEmitter {
|
|
|
165
203
|
}
|
|
166
204
|
}
|
|
167
205
|
addSubscription(request) {
|
|
206
|
+
if (this.isShutdown) {
|
|
207
|
+
this.logger.warn("Cannot add subscription: WebSocketPool is shutdown");
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
168
210
|
if (request.type !== "subscribe") {
|
|
169
211
|
this.emitPoolError(new Error("Request must be a subscribe request"));
|
|
170
212
|
return;
|
|
@@ -173,10 +215,14 @@ export class WebSocketPool extends IsomorphicEventEmitter {
|
|
|
173
215
|
this.sendRequest(request);
|
|
174
216
|
}
|
|
175
217
|
removeSubscription(subscriptionId) {
|
|
218
|
+
if (this.isShutdown) {
|
|
219
|
+
this.logger.warn("Cannot remove subscription: WebSocketPool is shutdown");
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
176
222
|
this.subscriptions.delete(subscriptionId);
|
|
177
223
|
const request = {
|
|
178
|
-
|
|
179
|
-
|
|
224
|
+
subscriptionId,
|
|
225
|
+
type: "unsubscribe"
|
|
180
226
|
};
|
|
181
227
|
this.sendRequest(request);
|
|
182
228
|
}
|
|
@@ -189,6 +235,23 @@ export class WebSocketPool extends IsomorphicEventEmitter {
|
|
|
189
235
|
*/ addAllConnectionsDownListener(handler) {
|
|
190
236
|
this.allConnectionsDownListeners.push(handler);
|
|
191
237
|
}
|
|
238
|
+
/**
|
|
239
|
+
* Calls the handler when at least one connection is restored after all connections were down.
|
|
240
|
+
*/ addConnectionRestoredListener(handler) {
|
|
241
|
+
this.connectionRestoredListeners.push(handler);
|
|
242
|
+
}
|
|
243
|
+
/**
|
|
244
|
+
* Calls the handler when an individual connection times out (heartbeat timeout).
|
|
245
|
+
* @param handler - Callback with connection index and endpoint URL
|
|
246
|
+
*/ addConnectionTimeoutListener(handler) {
|
|
247
|
+
this.connectionTimeoutListeners.push(handler);
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Calls the handler when an individual connection reconnects after being down.
|
|
251
|
+
* @param handler - Callback with connection index and endpoint URL
|
|
252
|
+
*/ addConnectionReconnectListener(handler) {
|
|
253
|
+
this.connectionReconnectListeners.push(handler);
|
|
254
|
+
}
|
|
192
255
|
areAllConnectionsDown() {
|
|
193
256
|
return this.rwsPool.every((ws)=>!ws.isConnected() || ws.isReconnecting());
|
|
194
257
|
}
|
|
@@ -196,6 +259,10 @@ export class WebSocketPool extends IsomorphicEventEmitter {
|
|
|
196
259
|
return this.rwsPool.some((ws)=>ws.isConnected());
|
|
197
260
|
}
|
|
198
261
|
checkConnectionStates() {
|
|
262
|
+
// Stop monitoring if shutdown
|
|
263
|
+
if (this.isShutdown) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
199
266
|
const allDown = this.areAllConnectionsDown();
|
|
200
267
|
// If all connections just went down
|
|
201
268
|
if (allDown && !this.wasAllDown) {
|
|
@@ -210,12 +277,25 @@ export class WebSocketPool extends IsomorphicEventEmitter {
|
|
|
210
277
|
}
|
|
211
278
|
}
|
|
212
279
|
}
|
|
213
|
-
// If at least one connection was restored
|
|
280
|
+
// If at least one connection was restored after all were down
|
|
214
281
|
if (!allDown && this.wasAllDown) {
|
|
215
282
|
this.wasAllDown = false;
|
|
283
|
+
this.logger.info("At least one WebSocket connection restored");
|
|
284
|
+
for (const listener of this.connectionRestoredListeners){
|
|
285
|
+
try {
|
|
286
|
+
listener();
|
|
287
|
+
} catch (error) {
|
|
288
|
+
this.emitPoolError(error, "Connection-restored listener threw");
|
|
289
|
+
}
|
|
290
|
+
}
|
|
216
291
|
}
|
|
217
292
|
}
|
|
218
293
|
shutdown() {
|
|
294
|
+
// Prevent multiple shutdown calls
|
|
295
|
+
if (this.isShutdown) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
this.isShutdown = true;
|
|
219
299
|
for (const rws of this.rwsPool){
|
|
220
300
|
rws.closeWebSocket();
|
|
221
301
|
}
|
|
@@ -223,6 +303,9 @@ export class WebSocketPool extends IsomorphicEventEmitter {
|
|
|
223
303
|
this.subscriptions.clear();
|
|
224
304
|
this.messageListeners = [];
|
|
225
305
|
this.allConnectionsDownListeners = [];
|
|
306
|
+
this.connectionRestoredListeners = [];
|
|
307
|
+
this.connectionTimeoutListeners = [];
|
|
308
|
+
this.connectionReconnectListeners = [];
|
|
226
309
|
clearInterval(this.checkConnectionStatesInterval);
|
|
227
310
|
// execute all bound shutdown handlers
|
|
228
311
|
for (const shutdownHandler of this.getListeners("shutdown")){
|