@pythnetwork/pyth-lazer-sdk 6.0.0 → 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.
- package/dist/cjs/client.cjs +77 -53
- package/dist/cjs/client.d.ts +36 -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 +83 -11
- package/dist/cjs/socket/websocket-pool.d.ts +22 -3
- package/dist/esm/client.d.ts +36 -13
- package/dist/esm/client.mjs +77 -53
- 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 +22 -3
- package/dist/esm/socket/websocket-pool.mjs +83 -11
- package/package.json +113 -123
|
@@ -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
|
/**
|
|
@@ -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;
|
package/dist/esm/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Logger } from "ts-log";
|
|
2
|
-
import type {
|
|
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
|
|
29
|
+
webSocketPoolConfig: WebSocketPoolConfig;
|
|
25
30
|
};
|
|
26
31
|
export declare class PythLazerClient {
|
|
27
|
-
private
|
|
28
|
-
private
|
|
29
|
-
private
|
|
30
|
-
private
|
|
31
|
-
private
|
|
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
|
package/dist/esm/client.mjs
CHANGED
|
@@ -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
|
-
|
|
10
|
+
logger;
|
|
11
11
|
metadataServiceUrl;
|
|
12
12
|
priceServiceUrl;
|
|
13
|
-
|
|
13
|
+
token;
|
|
14
14
|
wsp;
|
|
15
|
-
|
|
16
|
-
|
|
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.
|
|
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
|
-
//
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
55
|
-
|
|
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
|
|
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
|
|
80
|
+
if (magic === FORMAT_MAGICS_LE.EVM) {
|
|
81
81
|
value.evm = buffData.subarray(pos, pos + len);
|
|
82
|
-
} else if (magic
|
|
82
|
+
} else if (magic === FORMAT_MAGICS_LE.SOLANA) {
|
|
83
83
|
value.solana = buffData.subarray(pos, pos + len);
|
|
84
|
-
} else if (magic
|
|
84
|
+
} else if (magic === FORMAT_MAGICS_LE.LE_ECDSA) {
|
|
85
85
|
value.leEcdsa = buffData.subarray(pos, pos + len);
|
|
86
|
-
} else if (magic
|
|
86
|
+
} else if (magic === FORMAT_MAGICS_LE.LE_UNSIGNED) {
|
|
87
87
|
value.leUnsigned = buffData.subarray(pos, pos + len);
|
|
88
|
-
} else if (magic
|
|
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
|
-
|
|
110
|
-
wsp.removeSubscription(subscriptionId);
|
|
113
|
+
this.wsp.removeSubscription(subscriptionId);
|
|
111
114
|
}
|
|
112
115
|
send(request) {
|
|
113
|
-
|
|
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
|
-
|
|
122
|
-
|
|
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
|
-
|
|
126
|
-
|
|
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
|
-
*/
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
175
|
-
|
|
198
|
+
body,
|
|
199
|
+
url
|
|
176
200
|
});
|
|
177
201
|
const response = await this.authenticatedFetch(url, {
|
|
178
|
-
|
|
202
|
+
body: body,
|
|
179
203
|
headers: {
|
|
180
204
|
"Content-Type": "application/json"
|
|
181
205
|
},
|
|
182
|
-
|
|
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
|
-
|
|
202
|
-
|
|
225
|
+
body,
|
|
226
|
+
url
|
|
203
227
|
});
|
|
204
228
|
const response = await this.authenticatedFetch(url, {
|
|
205
|
-
|
|
229
|
+
body: body,
|
|
206
230
|
headers: {
|
|
207
231
|
"Content-Type": "application/json"
|
|
208
232
|
},
|
|
209
|
-
|
|
233
|
+
method: "POST"
|
|
210
234
|
});
|
|
211
235
|
if (!response.ok) {
|
|
212
236
|
throw new Error(`HTTP error! status: ${String(response.status)} - ${await response.text()}`);
|
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
|
/**
|
|
@@ -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;
|