@pythnetwork/pyth-lazer-sdk 6.2.1 → 6.2.2

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.
@@ -84,46 +84,68 @@ class ResilientWebSocket {
84
84
  this.logger.info("WebSocket client already started.");
85
85
  return;
86
86
  }
87
- if (this.wsFailedAttempts == 0) {
87
+ if (this.wsFailedAttempts === 0) {
88
88
  this.logger.info(`Creating Web Socket client`);
89
89
  }
90
90
  if (this.retryTimeout !== undefined) {
91
91
  clearTimeout(this.retryTimeout);
92
92
  this.retryTimeout = undefined;
93
93
  }
94
- // browser constructor supports a different 2nd argument for the constructor,
95
- // so we need to ensure it's not included if we're running in that environment:
96
- // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket#protocols
97
- this.wsClient = new _isomorphicws.default(this.endpoint, (0, _envutil.envIsBrowserOrWorker)() ? undefined : this.wsOptions);
98
- this.wsClient.addEventListener("open", ()=>{
99
- this.logger.info("WebSocket connection established");
100
- this.wsFailedAttempts = 0;
101
- this._isReconnecting = false;
102
- this.resetHeartbeat();
103
- this.onReconnect();
104
- });
105
- this.wsClient.addEventListener("close", (e)=>{
106
- if (this.wsUserClosed) {
107
- this.logger.info(`WebSocket connection to ${this.endpoint} closed by user`);
108
- } else {
109
- if (this.shouldLogRetry()) {
110
- this.logger.warn(`WebSocket connection to ${this.endpoint} closed unexpectedly: Code: ${e.code.toString()}`);
94
+ // we wrap the new WebSocket() construction in a try / catch because,
95
+ // if one of our instances legitimately goes down or there's some network
96
+ // error that impacts connectivity, the WebSocket constructor will throw
97
+ // an uncaught DOMException in the browser (or equivalent in Node, Bun or Deno)
98
+ // and potentially blow up the process (if running in Node, Bun or Deno)
99
+ try {
100
+ // browser constructor supports a different 2nd argument for the constructor,
101
+ // so we need to ensure it's not included if we're running in that environment:
102
+ // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket#protocols
103
+ this.wsClient = new _isomorphicws.default(this.endpoint, (0, _envutil.envIsBrowserOrWorker)() ? undefined : this.wsOptions);
104
+ this.wsClient.addEventListener("open", ()=>{
105
+ this.logger.info("WebSocket connection established");
106
+ this.wsFailedAttempts = 0;
107
+ this._isReconnecting = false;
108
+ this.resetHeartbeat();
109
+ try {
110
+ this.onReconnect();
111
+ } catch (error) {
112
+ this.logger.error("Error in onReconnect callback:", error);
111
113
  }
112
- this.handleReconnect();
113
- }
114
- });
115
- this.wsClient.addEventListener("error", (event)=>{
116
- this.onError(event);
117
- });
118
- this.wsClient.addEventListener("message", (event)=>{
119
- this.resetHeartbeat();
120
- this.onMessage(event.data);
121
- });
122
- if ("on" in this.wsClient) {
123
- this.wsClient.on("ping", ()=>{
124
- this.logger.info("Ping received");
114
+ });
115
+ this.wsClient.addEventListener("close", (e)=>{
116
+ if (this.wsUserClosed) {
117
+ this.logger.info(`WebSocket connection to ${this.endpoint} closed by user`);
118
+ } else {
119
+ if (this.shouldLogRetry()) {
120
+ this.logger.warn(`WebSocket connection to ${this.endpoint} closed unexpectedly: Code: ${e.code.toString()}`);
121
+ }
122
+ this.handleReconnect();
123
+ }
124
+ });
125
+ this.wsClient.addEventListener("error", (event)=>{
126
+ try {
127
+ this.onError(event);
128
+ } catch (error) {
129
+ this.logger.error("Error in onError callback:", error);
130
+ }
131
+ });
132
+ this.wsClient.addEventListener("message", (event)=>{
125
133
  this.resetHeartbeat();
134
+ try {
135
+ this.onMessage(event.data);
136
+ } catch (error) {
137
+ this.logger.error("Error in onMessage callback:", error);
138
+ }
126
139
  });
140
+ if (typeof this.wsClient?.on === "function") {
141
+ this.wsClient.on("ping", ()=>{
142
+ this.logger.debug("Ping received");
143
+ this.resetHeartbeat();
144
+ });
145
+ }
146
+ } catch (error) {
147
+ this.logger.error("Failed to create WebSocket client:", error);
148
+ this.handleReconnect();
127
149
  }
128
150
  }
129
151
  resetHeartbeat() {
@@ -133,7 +155,11 @@ class ResilientWebSocket {
133
155
  this.heartbeatTimeout = setTimeout(()=>{
134
156
  const warnMsg = "Connection timed out. Reconnecting...";
135
157
  this.logger.warn(warnMsg);
136
- this.onTimeout();
158
+ try {
159
+ this.onTimeout();
160
+ } catch (error) {
161
+ this.logger.error("Error in onTimeout callback:", error);
162
+ }
137
163
  if (this.wsClient) {
138
164
  if (typeof this.wsClient.terminate === "function") {
139
165
  this.wsClient.terminate();
@@ -144,7 +170,8 @@ class ResilientWebSocket {
144
170
  this.wsClient.close(_protocol.CustomSocketClosureCodes.CLIENT_TIMEOUT_BUT_RECONNECTING, warnMsg);
145
171
  }
146
172
  }
147
- this.handleReconnect();
173
+ // No direct call to handleReconnect() here
174
+ // terminate/close will trigger the 'close' event, which will call it.
148
175
  }, this.heartbeatTimeoutDurationMs);
149
176
  }
150
177
  handleReconnect() {
@@ -169,11 +196,12 @@ class ResilientWebSocket {
169
196
  }, this.retryDelayMs());
170
197
  }
171
198
  closeWebSocket() {
172
- if (this.wsClient !== undefined) {
199
+ // immediately block duplicate calls to this function
200
+ this.wsUserClosed = true;
201
+ if (typeof this.wsClient?.close === "function") {
173
202
  this.wsClient.close();
174
203
  this.wsClient = undefined;
175
204
  }
176
- this.wsUserClosed = true;
177
205
  }
178
206
  /**
179
207
  * Calculates the delay in milliseconds for exponential backoff based on the number of failed attempts.
@@ -8,17 +8,12 @@ Object.defineProperty(exports, "WebSocketPool", {
8
8
  return WebSocketPool;
9
9
  }
10
10
  });
11
- const _ttlcache = /*#__PURE__*/ _interop_require_default(require("@isaacs/ttlcache"));
11
+ const _ttlcache = require("@isaacs/ttlcache");
12
12
  const _tslog = require("ts-log");
13
13
  const _constants = require("../constants.cjs");
14
14
  const _index = require("../emitter/index.cjs");
15
15
  const _index1 = require("../util/index.cjs");
16
16
  const _resilientwebsocket = require("./resilient-websocket.cjs");
17
- function _interop_require_default(obj) {
18
- return obj && obj.__esModule ? obj : {
19
- default: obj
20
- };
21
- }
22
17
  const DEFAULT_NUM_CONNECTIONS = 4;
23
18
  class WebSocketPool extends _index.IsomorphicEventEmitter {
24
19
  logger;
@@ -36,7 +31,7 @@ class WebSocketPool extends _index.IsomorphicEventEmitter {
36
31
  constructor(logger){
37
32
  super(), this.logger = logger;
38
33
  this.rwsPool = [];
39
- this.cache = new _ttlcache.default({
34
+ this.cache = new _ttlcache.TTLCache({
40
35
  ttl: 1000 * 10
41
36
  }); // TTL of 10 seconds
42
37
  this.subscriptions = new Map();
@@ -69,46 +69,68 @@ export class ResilientWebSocket {
69
69
  this.logger.info("WebSocket client already started.");
70
70
  return;
71
71
  }
72
- if (this.wsFailedAttempts == 0) {
72
+ if (this.wsFailedAttempts === 0) {
73
73
  this.logger.info(`Creating Web Socket client`);
74
74
  }
75
75
  if (this.retryTimeout !== undefined) {
76
76
  clearTimeout(this.retryTimeout);
77
77
  this.retryTimeout = undefined;
78
78
  }
79
- // browser constructor supports a different 2nd argument for the constructor,
80
- // so we need to ensure it's not included if we're running in that environment:
81
- // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket#protocols
82
- this.wsClient = new WebSocket(this.endpoint, envIsBrowserOrWorker() ? undefined : this.wsOptions);
83
- this.wsClient.addEventListener("open", ()=>{
84
- this.logger.info("WebSocket connection established");
85
- this.wsFailedAttempts = 0;
86
- this._isReconnecting = false;
87
- this.resetHeartbeat();
88
- this.onReconnect();
89
- });
90
- this.wsClient.addEventListener("close", (e)=>{
91
- if (this.wsUserClosed) {
92
- this.logger.info(`WebSocket connection to ${this.endpoint} closed by user`);
93
- } else {
94
- if (this.shouldLogRetry()) {
95
- this.logger.warn(`WebSocket connection to ${this.endpoint} closed unexpectedly: Code: ${e.code.toString()}`);
79
+ // we wrap the new WebSocket() construction in a try / catch because,
80
+ // if one of our instances legitimately goes down or there's some network
81
+ // error that impacts connectivity, the WebSocket constructor will throw
82
+ // an uncaught DOMException in the browser (or equivalent in Node, Bun or Deno)
83
+ // and potentially blow up the process (if running in Node, Bun or Deno)
84
+ try {
85
+ // browser constructor supports a different 2nd argument for the constructor,
86
+ // so we need to ensure it's not included if we're running in that environment:
87
+ // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/WebSocket#protocols
88
+ this.wsClient = new WebSocket(this.endpoint, envIsBrowserOrWorker() ? undefined : this.wsOptions);
89
+ this.wsClient.addEventListener("open", ()=>{
90
+ this.logger.info("WebSocket connection established");
91
+ this.wsFailedAttempts = 0;
92
+ this._isReconnecting = false;
93
+ this.resetHeartbeat();
94
+ try {
95
+ this.onReconnect();
96
+ } catch (error) {
97
+ this.logger.error("Error in onReconnect callback:", error);
96
98
  }
97
- this.handleReconnect();
98
- }
99
- });
100
- this.wsClient.addEventListener("error", (event)=>{
101
- this.onError(event);
102
- });
103
- this.wsClient.addEventListener("message", (event)=>{
104
- this.resetHeartbeat();
105
- this.onMessage(event.data);
106
- });
107
- if ("on" in this.wsClient) {
108
- this.wsClient.on("ping", ()=>{
109
- this.logger.info("Ping received");
99
+ });
100
+ this.wsClient.addEventListener("close", (e)=>{
101
+ if (this.wsUserClosed) {
102
+ this.logger.info(`WebSocket connection to ${this.endpoint} closed by user`);
103
+ } else {
104
+ if (this.shouldLogRetry()) {
105
+ this.logger.warn(`WebSocket connection to ${this.endpoint} closed unexpectedly: Code: ${e.code.toString()}`);
106
+ }
107
+ this.handleReconnect();
108
+ }
109
+ });
110
+ this.wsClient.addEventListener("error", (event)=>{
111
+ try {
112
+ this.onError(event);
113
+ } catch (error) {
114
+ this.logger.error("Error in onError callback:", error);
115
+ }
116
+ });
117
+ this.wsClient.addEventListener("message", (event)=>{
110
118
  this.resetHeartbeat();
119
+ try {
120
+ this.onMessage(event.data);
121
+ } catch (error) {
122
+ this.logger.error("Error in onMessage callback:", error);
123
+ }
111
124
  });
125
+ if (typeof this.wsClient?.on === "function") {
126
+ this.wsClient.on("ping", ()=>{
127
+ this.logger.debug("Ping received");
128
+ this.resetHeartbeat();
129
+ });
130
+ }
131
+ } catch (error) {
132
+ this.logger.error("Failed to create WebSocket client:", error);
133
+ this.handleReconnect();
112
134
  }
113
135
  }
114
136
  resetHeartbeat() {
@@ -118,7 +140,11 @@ export class ResilientWebSocket {
118
140
  this.heartbeatTimeout = setTimeout(()=>{
119
141
  const warnMsg = "Connection timed out. Reconnecting...";
120
142
  this.logger.warn(warnMsg);
121
- this.onTimeout();
143
+ try {
144
+ this.onTimeout();
145
+ } catch (error) {
146
+ this.logger.error("Error in onTimeout callback:", error);
147
+ }
122
148
  if (this.wsClient) {
123
149
  if (typeof this.wsClient.terminate === "function") {
124
150
  this.wsClient.terminate();
@@ -129,7 +155,8 @@ export class ResilientWebSocket {
129
155
  this.wsClient.close(CustomSocketClosureCodes.CLIENT_TIMEOUT_BUT_RECONNECTING, warnMsg);
130
156
  }
131
157
  }
132
- this.handleReconnect();
158
+ // No direct call to handleReconnect() here
159
+ // terminate/close will trigger the 'close' event, which will call it.
133
160
  }, this.heartbeatTimeoutDurationMs);
134
161
  }
135
162
  handleReconnect() {
@@ -154,11 +181,12 @@ export class ResilientWebSocket {
154
181
  }, this.retryDelayMs());
155
182
  }
156
183
  closeWebSocket() {
157
- if (this.wsClient !== undefined) {
184
+ // immediately block duplicate calls to this function
185
+ this.wsUserClosed = true;
186
+ if (typeof this.wsClient?.close === "function") {
158
187
  this.wsClient.close();
159
188
  this.wsClient = undefined;
160
189
  }
161
- this.wsUserClosed = true;
162
190
  }
163
191
  /**
164
192
  * Calculates the delay in milliseconds for exponential backoff based on the number of failed attempts.
@@ -1,4 +1,4 @@
1
- import TTLCache from "@isaacs/ttlcache";
1
+ import { TTLCache } from "@isaacs/ttlcache";
2
2
  import { dummyLogger } from "ts-log";
3
3
  import { DEFAULT_STREAM_SERVICE_0_URL, DEFAULT_STREAM_SERVICE_1_URL } from "../constants.mjs";
4
4
  import { IsomorphicEventEmitter } from "../emitter/index.mjs";
package/package.json CHANGED
@@ -3,18 +3,18 @@
3
3
  "url": "https://github.com/pyth-network/pyth-crosschain/issues"
4
4
  },
5
5
  "dependencies": {
6
- "@isaacs/ttlcache": "^1.4.1",
6
+ "@isaacs/ttlcache": "^2.1.4",
7
7
  "buffer": "^6.0.3",
8
8
  "isomorphic-ws": "^5.0.0",
9
9
  "ts-log": "^2.2.7",
10
- "ws": "^8.19.0"
10
+ "ws": "^8.20.0"
11
11
  },
12
12
  "description": "Pyth Lazer SDK",
13
13
  "devDependencies": {
14
14
  "@cprussin/tsconfig": "^4.0.2",
15
15
  "@types/node": "^24.10.1",
16
16
  "@types/ws": "^8.18.1",
17
- "typedoc": "^0.28.17"
17
+ "typedoc": "^0.28.18"
18
18
  },
19
19
  "engines": {
20
20
  "node": "^24.0.0"
@@ -164,5 +164,5 @@
164
164
  },
165
165
  "type": "module",
166
166
  "types": "./dist/cjs/index.d.ts",
167
- "version": "6.2.1"
167
+ "version": "6.2.2"
168
168
  }