@pythnetwork/pyth-lazer-sdk 5.0.0 → 5.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.js → client.cjs} +93 -98
- package/dist/cjs/constants.cjs +36 -0
- package/dist/cjs/emitter/index.cjs +53 -0
- package/dist/cjs/emitter/index.d.ts +29 -0
- package/dist/cjs/index.cjs +20 -0
- package/dist/cjs/package.json +1 -1
- package/dist/cjs/protocol.cjs +33 -0
- package/dist/cjs/protocol.d.ts +1 -1
- package/dist/cjs/socket/{resilient-websocket.js → resilient-websocket.cjs} +47 -48
- package/dist/cjs/socket/websocket-pool.cjs +253 -0
- package/dist/cjs/socket/websocket-pool.d.ts +37 -3
- package/dist/cjs/util/{buffer-util.js → buffer-util.cjs} +14 -14
- package/dist/cjs/util/env-util.cjs +33 -0
- package/dist/cjs/util/index.cjs +20 -0
- package/dist/cjs/util/url-util.cjs +17 -0
- package/dist/esm/{client.js → client.mjs} +76 -88
- package/dist/esm/emitter/index.d.ts +29 -0
- package/dist/esm/emitter/index.mjs +43 -0
- package/dist/esm/index.mjs +3 -0
- package/dist/esm/package.json +1 -1
- package/dist/esm/protocol.d.ts +1 -1
- package/dist/esm/{protocol.js → protocol.mjs} +4 -4
- package/dist/esm/socket/{resilient-websocket.js → resilient-websocket.mjs} +27 -36
- package/dist/esm/socket/websocket-pool.d.ts +37 -3
- package/dist/esm/socket/websocket-pool.mjs +238 -0
- package/dist/esm/util/{buffer-util.js → buffer-util.mjs} +3 -6
- package/dist/esm/util/{env-util.js → env-util.mjs} +4 -8
- package/dist/esm/util/index.mjs +3 -0
- package/dist/esm/util/{url-util.js → url-util.mjs} +2 -4
- package/package.json +119 -15
- package/dist/cjs/constants.js +0 -9
- package/dist/cjs/index.js +0 -19
- package/dist/cjs/protocol.js +0 -15
- package/dist/cjs/socket/websocket-pool.js +0 -201
- package/dist/cjs/util/env-util.js +0 -32
- package/dist/cjs/util/index.js +0 -19
- package/dist/cjs/util/url-util.js +0 -18
- package/dist/esm/index.js +0 -3
- package/dist/esm/socket/websocket-pool.js +0 -195
- package/dist/esm/util/index.js +0 -3
- /package/dist/esm/{constants.js → constants.mjs} +0 -0
|
@@ -1,201 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.WebSocketPool = void 0;
|
|
7
|
-
const ttlcache_1 = __importDefault(require("@isaacs/ttlcache"));
|
|
8
|
-
const ts_log_1 = require("ts-log");
|
|
9
|
-
const resilient_websocket_js_1 = require("./resilient-websocket.js");
|
|
10
|
-
const constants_js_1 = require("../constants.js");
|
|
11
|
-
const index_js_1 = require("../util/index.js");
|
|
12
|
-
const DEFAULT_NUM_CONNECTIONS = 4;
|
|
13
|
-
class WebSocketPool {
|
|
14
|
-
logger;
|
|
15
|
-
rwsPool;
|
|
16
|
-
cache;
|
|
17
|
-
subscriptions; // id -> subscription Request
|
|
18
|
-
messageListeners;
|
|
19
|
-
allConnectionsDownListeners;
|
|
20
|
-
wasAllDown = true;
|
|
21
|
-
checkConnectionStatesInterval;
|
|
22
|
-
constructor(logger) {
|
|
23
|
-
this.logger = logger;
|
|
24
|
-
this.rwsPool = [];
|
|
25
|
-
this.cache = new ttlcache_1.default({ ttl: 1000 * 10 }); // TTL of 10 seconds
|
|
26
|
-
this.subscriptions = new Map();
|
|
27
|
-
this.messageListeners = [];
|
|
28
|
-
this.allConnectionsDownListeners = [];
|
|
29
|
-
// Start monitoring connection states
|
|
30
|
-
this.checkConnectionStatesInterval = setInterval(() => {
|
|
31
|
-
this.checkConnectionStates();
|
|
32
|
-
}, 100);
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Creates a new WebSocketPool instance that uses multiple redundant WebSocket connections for reliability.
|
|
36
|
-
* Usage semantics are similar to using a regular WebSocket client.
|
|
37
|
-
* @param urls - List of WebSocket URLs to connect to
|
|
38
|
-
* @param token - Authentication token to use for the connections
|
|
39
|
-
* @param numConnections - Number of parallel WebSocket connections to maintain (default: 3)
|
|
40
|
-
* @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
|
|
41
|
-
*/
|
|
42
|
-
static async create(config, token, logger) {
|
|
43
|
-
const urls = config.urls ?? [
|
|
44
|
-
constants_js_1.DEFAULT_STREAM_SERVICE_0_URL,
|
|
45
|
-
constants_js_1.DEFAULT_STREAM_SERVICE_1_URL,
|
|
46
|
-
];
|
|
47
|
-
const log = logger ?? ts_log_1.dummyLogger;
|
|
48
|
-
const pool = new WebSocketPool(log);
|
|
49
|
-
const numConnections = config.numConnections ?? DEFAULT_NUM_CONNECTIONS;
|
|
50
|
-
for (let i = 0; i < numConnections; i++) {
|
|
51
|
-
const baseUrl = urls[i % urls.length];
|
|
52
|
-
const isBrowser = (0, index_js_1.envIsBrowserOrWorker)();
|
|
53
|
-
const url = isBrowser
|
|
54
|
-
? (0, index_js_1.addAuthTokenToWebSocketUrl)(baseUrl, token)
|
|
55
|
-
: baseUrl;
|
|
56
|
-
if (!url) {
|
|
57
|
-
throw new Error(`URLs must not be null or empty`);
|
|
58
|
-
}
|
|
59
|
-
const wsOptions = {
|
|
60
|
-
...config.rwsConfig?.wsOptions,
|
|
61
|
-
headers: isBrowser ? undefined : { Authorization: `Bearer ${token}` },
|
|
62
|
-
};
|
|
63
|
-
const rws = new resilient_websocket_js_1.ResilientWebSocket({
|
|
64
|
-
...config.rwsConfig,
|
|
65
|
-
endpoint: url,
|
|
66
|
-
wsOptions,
|
|
67
|
-
logger: log,
|
|
68
|
-
});
|
|
69
|
-
// If a websocket client unexpectedly disconnects, ResilientWebSocket will reestablish
|
|
70
|
-
// the connection and call the onReconnect callback.
|
|
71
|
-
rws.onReconnect = () => {
|
|
72
|
-
if (rws.wsUserClosed) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
for (const [, request] of pool.subscriptions) {
|
|
76
|
-
try {
|
|
77
|
-
rws.send(JSON.stringify(request));
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
pool.logger.error("Failed to resend subscription on reconnect:", error);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
if (config.onError) {
|
|
85
|
-
rws.onError = config.onError;
|
|
86
|
-
}
|
|
87
|
-
// Handle all client messages ourselves. Dedupe before sending to registered message handlers.
|
|
88
|
-
rws.onMessage = (data) => {
|
|
89
|
-
pool.dedupeHandler(data).catch((error) => {
|
|
90
|
-
const errMsg = `An error occurred in the WebSocket pool's dedupeHandler: ${error instanceof Error ? error.message : String(error)}`;
|
|
91
|
-
throw new Error(errMsg);
|
|
92
|
-
});
|
|
93
|
-
};
|
|
94
|
-
pool.rwsPool.push(rws);
|
|
95
|
-
rws.startWebSocket();
|
|
96
|
-
}
|
|
97
|
-
pool.logger.info(`Started WebSocketPool with ${numConnections.toString()} connections. Waiting for at least one to connect...`);
|
|
98
|
-
while (!pool.isAnyConnectionEstablished()) {
|
|
99
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
100
|
-
}
|
|
101
|
-
pool.logger.info(`At least one WebSocket connection is established. WebSocketPool is ready.`);
|
|
102
|
-
return pool;
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Checks for error responses in JSON messages and throws appropriate errors
|
|
106
|
-
*/
|
|
107
|
-
handleErrorMessages(data) {
|
|
108
|
-
const message = JSON.parse(data);
|
|
109
|
-
if (message.type === "subscriptionError") {
|
|
110
|
-
throw new Error(`Error occurred for subscription ID ${String(message.subscriptionId)}: ${message.error}`);
|
|
111
|
-
}
|
|
112
|
-
else if (message.type === "error") {
|
|
113
|
-
throw new Error(`Error: ${message.error}`);
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
async constructCacheKeyFromWebsocketData(data) {
|
|
117
|
-
if (typeof data === "string")
|
|
118
|
-
return data;
|
|
119
|
-
const buff = await (0, index_js_1.bufferFromWebsocketData)(data);
|
|
120
|
-
return buff.toString("hex");
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Handles incoming websocket messages by deduplicating identical messages received across
|
|
124
|
-
* multiple connections before forwarding to registered handlers
|
|
125
|
-
*/
|
|
126
|
-
dedupeHandler = async (data) => {
|
|
127
|
-
const cacheKey = await this.constructCacheKeyFromWebsocketData(data);
|
|
128
|
-
if (this.cache.has(cacheKey)) {
|
|
129
|
-
this.logger.debug("Dropping duplicate message");
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
this.cache.set(cacheKey, true);
|
|
133
|
-
if (typeof data === "string") {
|
|
134
|
-
this.handleErrorMessages(data);
|
|
135
|
-
}
|
|
136
|
-
await Promise.all(this.messageListeners.map((handler) => handler(data)));
|
|
137
|
-
};
|
|
138
|
-
sendRequest(request) {
|
|
139
|
-
for (const rws of this.rwsPool) {
|
|
140
|
-
rws.send(JSON.stringify(request));
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
addSubscription(request) {
|
|
144
|
-
if (request.type !== "subscribe") {
|
|
145
|
-
throw new Error("Request must be a subscribe request");
|
|
146
|
-
}
|
|
147
|
-
this.subscriptions.set(request.subscriptionId, request);
|
|
148
|
-
this.sendRequest(request);
|
|
149
|
-
}
|
|
150
|
-
removeSubscription(subscriptionId) {
|
|
151
|
-
this.subscriptions.delete(subscriptionId);
|
|
152
|
-
const request = {
|
|
153
|
-
type: "unsubscribe",
|
|
154
|
-
subscriptionId,
|
|
155
|
-
};
|
|
156
|
-
this.sendRequest(request);
|
|
157
|
-
}
|
|
158
|
-
addMessageListener(handler) {
|
|
159
|
-
this.messageListeners.push(handler);
|
|
160
|
-
}
|
|
161
|
-
/**
|
|
162
|
-
* Calls the handler if all websocket connections are currently down or in reconnecting state.
|
|
163
|
-
* The connections may still try to reconnect in the background.
|
|
164
|
-
*/
|
|
165
|
-
addAllConnectionsDownListener(handler) {
|
|
166
|
-
this.allConnectionsDownListeners.push(handler);
|
|
167
|
-
}
|
|
168
|
-
areAllConnectionsDown() {
|
|
169
|
-
return this.rwsPool.every((ws) => !ws.isConnected() || ws.isReconnecting());
|
|
170
|
-
}
|
|
171
|
-
isAnyConnectionEstablished() {
|
|
172
|
-
return this.rwsPool.some((ws) => ws.isConnected());
|
|
173
|
-
}
|
|
174
|
-
checkConnectionStates() {
|
|
175
|
-
const allDown = this.areAllConnectionsDown();
|
|
176
|
-
// If all connections just went down
|
|
177
|
-
if (allDown && !this.wasAllDown) {
|
|
178
|
-
this.wasAllDown = true;
|
|
179
|
-
this.logger.error("All WebSocket connections are down or reconnecting");
|
|
180
|
-
// Notify all listeners
|
|
181
|
-
for (const listener of this.allConnectionsDownListeners) {
|
|
182
|
-
listener();
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
// If at least one connection was restored
|
|
186
|
-
if (!allDown && this.wasAllDown) {
|
|
187
|
-
this.wasAllDown = false;
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
shutdown() {
|
|
191
|
-
for (const rws of this.rwsPool) {
|
|
192
|
-
rws.closeWebSocket();
|
|
193
|
-
}
|
|
194
|
-
this.rwsPool = [];
|
|
195
|
-
this.subscriptions.clear();
|
|
196
|
-
this.messageListeners = [];
|
|
197
|
-
this.allConnectionsDownListeners = [];
|
|
198
|
-
clearInterval(this.checkConnectionStatesInterval);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
exports.WebSocketPool = WebSocketPool;
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.envIsServiceOrWebWorker = envIsServiceOrWebWorker;
|
|
4
|
-
exports.envIsBrowser = envIsBrowser;
|
|
5
|
-
exports.envIsBrowserOrWorker = envIsBrowserOrWorker;
|
|
6
|
-
// we create this local-only type, which has assertions made to indicate
|
|
7
|
-
// that we do not know and cannot guarantee which JS environment we are in
|
|
8
|
-
const g = globalThis;
|
|
9
|
-
/**
|
|
10
|
-
* Detects if this code is running within any Service or WebWorker context.
|
|
11
|
-
* @returns true if in a worker of some kind, false if otherwise
|
|
12
|
-
*/
|
|
13
|
-
function envIsServiceOrWebWorker() {
|
|
14
|
-
return (typeof WorkerGlobalScope !== "undefined" &&
|
|
15
|
-
g.self instanceof WorkerGlobalScope);
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Detects if the code is running in a regular DOM or Web Worker context.
|
|
19
|
-
* @returns true if running in a DOM or Web Worker context, false if running in Node.js
|
|
20
|
-
*/
|
|
21
|
-
function envIsBrowser() {
|
|
22
|
-
return g.window !== undefined;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* a convenience method that returns whether or not
|
|
26
|
-
* this code is executing in some type of browser-centric environment
|
|
27
|
-
*
|
|
28
|
-
* @returns true if in the browser's main UI thread or in a worker, false if otherwise
|
|
29
|
-
*/
|
|
30
|
-
function envIsBrowserOrWorker() {
|
|
31
|
-
return envIsServiceOrWebWorker() || envIsBrowser();
|
|
32
|
-
}
|
package/dist/cjs/util/index.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
-
};
|
|
16
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./buffer-util.js"), exports);
|
|
18
|
-
__exportStar(require("./env-util.js"), exports);
|
|
19
|
-
__exportStar(require("./url-util.js"), exports);
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.addAuthTokenToWebSocketUrl = addAuthTokenToWebSocketUrl;
|
|
4
|
-
const ACCESS_TOKEN_QUERY_PARAM_KEY = "ACCESS_TOKEN";
|
|
5
|
-
/**
|
|
6
|
-
* Given a URL to a hosted lazer stream service and a possible auth token,
|
|
7
|
-
* appends the auth token as a query parameter and returns the URL with the token
|
|
8
|
-
* contained within.
|
|
9
|
-
* If the URL provided is nullish, it is returned as-is (in the same nullish format).
|
|
10
|
-
* If the token is nullish, the baseUrl given is returned, instead.
|
|
11
|
-
*/
|
|
12
|
-
function addAuthTokenToWebSocketUrl(baseUrl, authToken) {
|
|
13
|
-
if (!baseUrl || !authToken)
|
|
14
|
-
return baseUrl;
|
|
15
|
-
const parsedUrl = new URL(baseUrl);
|
|
16
|
-
parsedUrl.searchParams.set(ACCESS_TOKEN_QUERY_PARAM_KEY, authToken);
|
|
17
|
-
return parsedUrl.toString();
|
|
18
|
-
}
|
package/dist/esm/index.js
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import TTLCache from "@isaacs/ttlcache";
|
|
2
|
-
import WebSocket from "isomorphic-ws";
|
|
3
|
-
import { dummyLogger } from "ts-log";
|
|
4
|
-
import { ResilientWebSocket } from "./resilient-websocket.js";
|
|
5
|
-
import { DEFAULT_STREAM_SERVICE_0_URL, DEFAULT_STREAM_SERVICE_1_URL, } from "../constants.js";
|
|
6
|
-
import { addAuthTokenToWebSocketUrl, bufferFromWebsocketData, envIsBrowserOrWorker, } from "../util/index.js";
|
|
7
|
-
const DEFAULT_NUM_CONNECTIONS = 4;
|
|
8
|
-
export class WebSocketPool {
|
|
9
|
-
logger;
|
|
10
|
-
rwsPool;
|
|
11
|
-
cache;
|
|
12
|
-
subscriptions; // id -> subscription Request
|
|
13
|
-
messageListeners;
|
|
14
|
-
allConnectionsDownListeners;
|
|
15
|
-
wasAllDown = true;
|
|
16
|
-
checkConnectionStatesInterval;
|
|
17
|
-
constructor(logger) {
|
|
18
|
-
this.logger = logger;
|
|
19
|
-
this.rwsPool = [];
|
|
20
|
-
this.cache = new TTLCache({ ttl: 1000 * 10 }); // TTL of 10 seconds
|
|
21
|
-
this.subscriptions = new Map();
|
|
22
|
-
this.messageListeners = [];
|
|
23
|
-
this.allConnectionsDownListeners = [];
|
|
24
|
-
// Start monitoring connection states
|
|
25
|
-
this.checkConnectionStatesInterval = setInterval(() => {
|
|
26
|
-
this.checkConnectionStates();
|
|
27
|
-
}, 100);
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Creates a new WebSocketPool instance that uses multiple redundant WebSocket connections for reliability.
|
|
31
|
-
* Usage semantics are similar to using a regular WebSocket client.
|
|
32
|
-
* @param urls - List of WebSocket URLs to connect to
|
|
33
|
-
* @param token - Authentication token to use for the connections
|
|
34
|
-
* @param numConnections - Number of parallel WebSocket connections to maintain (default: 3)
|
|
35
|
-
* @param logger - Optional logger to get socket level logs. Compatible with most loggers such as the built-in console and `bunyan`.
|
|
36
|
-
*/
|
|
37
|
-
static async create(config, token, logger) {
|
|
38
|
-
const urls = config.urls ?? [
|
|
39
|
-
DEFAULT_STREAM_SERVICE_0_URL,
|
|
40
|
-
DEFAULT_STREAM_SERVICE_1_URL,
|
|
41
|
-
];
|
|
42
|
-
const log = logger ?? dummyLogger;
|
|
43
|
-
const pool = new WebSocketPool(log);
|
|
44
|
-
const numConnections = config.numConnections ?? DEFAULT_NUM_CONNECTIONS;
|
|
45
|
-
for (let i = 0; i < numConnections; i++) {
|
|
46
|
-
const baseUrl = urls[i % urls.length];
|
|
47
|
-
const isBrowser = envIsBrowserOrWorker();
|
|
48
|
-
const url = isBrowser
|
|
49
|
-
? addAuthTokenToWebSocketUrl(baseUrl, token)
|
|
50
|
-
: baseUrl;
|
|
51
|
-
if (!url) {
|
|
52
|
-
throw new Error(`URLs must not be null or empty`);
|
|
53
|
-
}
|
|
54
|
-
const wsOptions = {
|
|
55
|
-
...config.rwsConfig?.wsOptions,
|
|
56
|
-
headers: isBrowser ? undefined : { Authorization: `Bearer ${token}` },
|
|
57
|
-
};
|
|
58
|
-
const rws = new ResilientWebSocket({
|
|
59
|
-
...config.rwsConfig,
|
|
60
|
-
endpoint: url,
|
|
61
|
-
wsOptions,
|
|
62
|
-
logger: log,
|
|
63
|
-
});
|
|
64
|
-
// If a websocket client unexpectedly disconnects, ResilientWebSocket will reestablish
|
|
65
|
-
// the connection and call the onReconnect callback.
|
|
66
|
-
rws.onReconnect = () => {
|
|
67
|
-
if (rws.wsUserClosed) {
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
for (const [, request] of pool.subscriptions) {
|
|
71
|
-
try {
|
|
72
|
-
rws.send(JSON.stringify(request));
|
|
73
|
-
}
|
|
74
|
-
catch (error) {
|
|
75
|
-
pool.logger.error("Failed to resend subscription on reconnect:", error);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
};
|
|
79
|
-
if (config.onError) {
|
|
80
|
-
rws.onError = config.onError;
|
|
81
|
-
}
|
|
82
|
-
// Handle all client messages ourselves. Dedupe before sending to registered message handlers.
|
|
83
|
-
rws.onMessage = (data) => {
|
|
84
|
-
pool.dedupeHandler(data).catch((error) => {
|
|
85
|
-
const errMsg = `An error occurred in the WebSocket pool's dedupeHandler: ${error instanceof Error ? error.message : String(error)}`;
|
|
86
|
-
throw new Error(errMsg);
|
|
87
|
-
});
|
|
88
|
-
};
|
|
89
|
-
pool.rwsPool.push(rws);
|
|
90
|
-
rws.startWebSocket();
|
|
91
|
-
}
|
|
92
|
-
pool.logger.info(`Started WebSocketPool with ${numConnections.toString()} connections. Waiting for at least one to connect...`);
|
|
93
|
-
while (!pool.isAnyConnectionEstablished()) {
|
|
94
|
-
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
95
|
-
}
|
|
96
|
-
pool.logger.info(`At least one WebSocket connection is established. WebSocketPool is ready.`);
|
|
97
|
-
return pool;
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* Checks for error responses in JSON messages and throws appropriate errors
|
|
101
|
-
*/
|
|
102
|
-
handleErrorMessages(data) {
|
|
103
|
-
const message = JSON.parse(data);
|
|
104
|
-
if (message.type === "subscriptionError") {
|
|
105
|
-
throw new Error(`Error occurred for subscription ID ${String(message.subscriptionId)}: ${message.error}`);
|
|
106
|
-
}
|
|
107
|
-
else if (message.type === "error") {
|
|
108
|
-
throw new Error(`Error: ${message.error}`);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
async constructCacheKeyFromWebsocketData(data) {
|
|
112
|
-
if (typeof data === "string")
|
|
113
|
-
return data;
|
|
114
|
-
const buff = await bufferFromWebsocketData(data);
|
|
115
|
-
return buff.toString("hex");
|
|
116
|
-
}
|
|
117
|
-
/**
|
|
118
|
-
* Handles incoming websocket messages by deduplicating identical messages received across
|
|
119
|
-
* multiple connections before forwarding to registered handlers
|
|
120
|
-
*/
|
|
121
|
-
dedupeHandler = async (data) => {
|
|
122
|
-
const cacheKey = await this.constructCacheKeyFromWebsocketData(data);
|
|
123
|
-
if (this.cache.has(cacheKey)) {
|
|
124
|
-
this.logger.debug("Dropping duplicate message");
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
this.cache.set(cacheKey, true);
|
|
128
|
-
if (typeof data === "string") {
|
|
129
|
-
this.handleErrorMessages(data);
|
|
130
|
-
}
|
|
131
|
-
await Promise.all(this.messageListeners.map((handler) => handler(data)));
|
|
132
|
-
};
|
|
133
|
-
sendRequest(request) {
|
|
134
|
-
for (const rws of this.rwsPool) {
|
|
135
|
-
rws.send(JSON.stringify(request));
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
addSubscription(request) {
|
|
139
|
-
if (request.type !== "subscribe") {
|
|
140
|
-
throw new Error("Request must be a subscribe request");
|
|
141
|
-
}
|
|
142
|
-
this.subscriptions.set(request.subscriptionId, request);
|
|
143
|
-
this.sendRequest(request);
|
|
144
|
-
}
|
|
145
|
-
removeSubscription(subscriptionId) {
|
|
146
|
-
this.subscriptions.delete(subscriptionId);
|
|
147
|
-
const request = {
|
|
148
|
-
type: "unsubscribe",
|
|
149
|
-
subscriptionId,
|
|
150
|
-
};
|
|
151
|
-
this.sendRequest(request);
|
|
152
|
-
}
|
|
153
|
-
addMessageListener(handler) {
|
|
154
|
-
this.messageListeners.push(handler);
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Calls the handler if all websocket connections are currently down or in reconnecting state.
|
|
158
|
-
* The connections may still try to reconnect in the background.
|
|
159
|
-
*/
|
|
160
|
-
addAllConnectionsDownListener(handler) {
|
|
161
|
-
this.allConnectionsDownListeners.push(handler);
|
|
162
|
-
}
|
|
163
|
-
areAllConnectionsDown() {
|
|
164
|
-
return this.rwsPool.every((ws) => !ws.isConnected() || ws.isReconnecting());
|
|
165
|
-
}
|
|
166
|
-
isAnyConnectionEstablished() {
|
|
167
|
-
return this.rwsPool.some((ws) => ws.isConnected());
|
|
168
|
-
}
|
|
169
|
-
checkConnectionStates() {
|
|
170
|
-
const allDown = this.areAllConnectionsDown();
|
|
171
|
-
// If all connections just went down
|
|
172
|
-
if (allDown && !this.wasAllDown) {
|
|
173
|
-
this.wasAllDown = true;
|
|
174
|
-
this.logger.error("All WebSocket connections are down or reconnecting");
|
|
175
|
-
// Notify all listeners
|
|
176
|
-
for (const listener of this.allConnectionsDownListeners) {
|
|
177
|
-
listener();
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
// If at least one connection was restored
|
|
181
|
-
if (!allDown && this.wasAllDown) {
|
|
182
|
-
this.wasAllDown = false;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
shutdown() {
|
|
186
|
-
for (const rws of this.rwsPool) {
|
|
187
|
-
rws.closeWebSocket();
|
|
188
|
-
}
|
|
189
|
-
this.rwsPool = [];
|
|
190
|
-
this.subscriptions.clear();
|
|
191
|
-
this.messageListeners = [];
|
|
192
|
-
this.allConnectionsDownListeners = [];
|
|
193
|
-
clearInterval(this.checkConnectionStatesInterval);
|
|
194
|
-
}
|
|
195
|
-
}
|
package/dist/esm/util/index.js
DELETED
|
File without changes
|