@nktkas/hyperliquid 0.15.1 → 0.15.3

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.
Files changed (46) hide show
  1. package/README.md +6 -5
  2. package/esm/src/clients/wallet.d.ts +30 -3
  3. package/esm/src/clients/wallet.d.ts.map +1 -1
  4. package/esm/src/clients/wallet.js +49 -0
  5. package/esm/src/signing.d.ts +3 -3
  6. package/esm/src/signing.d.ts.map +1 -1
  7. package/esm/src/transports/http/http_transport.d.ts +2 -6
  8. package/esm/src/transports/http/http_transport.d.ts.map +1 -1
  9. package/esm/src/transports/http/http_transport.js +2 -4
  10. package/esm/src/transports/websocket/_hyperliquid_event_target.js +13 -15
  11. package/esm/src/transports/websocket/_reconnecting_websocket.d.ts +22 -42
  12. package/esm/src/transports/websocket/_reconnecting_websocket.d.ts.map +1 -1
  13. package/esm/src/transports/websocket/_reconnecting_websocket.js +133 -82
  14. package/esm/src/transports/websocket/_websocket_request_dispatcher.d.ts +4 -21
  15. package/esm/src/transports/websocket/_websocket_request_dispatcher.d.ts.map +1 -1
  16. package/esm/src/transports/websocket/_websocket_request_dispatcher.js +15 -35
  17. package/esm/src/transports/websocket/websocket_transport.d.ts +2 -4
  18. package/esm/src/transports/websocket/websocket_transport.d.ts.map +1 -1
  19. package/esm/src/transports/websocket/websocket_transport.js +14 -12
  20. package/esm/src/types/exchange/requests.d.ts +20 -0
  21. package/esm/src/types/exchange/requests.d.ts.map +1 -1
  22. package/esm/src/types/exchange/responses.d.ts +12 -0
  23. package/esm/src/types/exchange/responses.d.ts.map +1 -1
  24. package/package.json +1 -1
  25. package/script/src/clients/wallet.d.ts +30 -3
  26. package/script/src/clients/wallet.d.ts.map +1 -1
  27. package/script/src/clients/wallet.js +49 -0
  28. package/script/src/signing.d.ts +3 -3
  29. package/script/src/signing.d.ts.map +1 -1
  30. package/script/src/transports/http/http_transport.d.ts +2 -6
  31. package/script/src/transports/http/http_transport.d.ts.map +1 -1
  32. package/script/src/transports/http/http_transport.js +2 -4
  33. package/script/src/transports/websocket/_hyperliquid_event_target.js +13 -15
  34. package/script/src/transports/websocket/_reconnecting_websocket.d.ts +22 -42
  35. package/script/src/transports/websocket/_reconnecting_websocket.d.ts.map +1 -1
  36. package/script/src/transports/websocket/_reconnecting_websocket.js +133 -82
  37. package/script/src/transports/websocket/_websocket_request_dispatcher.d.ts +4 -21
  38. package/script/src/transports/websocket/_websocket_request_dispatcher.d.ts.map +1 -1
  39. package/script/src/transports/websocket/_websocket_request_dispatcher.js +15 -35
  40. package/script/src/transports/websocket/websocket_transport.d.ts +2 -4
  41. package/script/src/transports/websocket/websocket_transport.d.ts.map +1 -1
  42. package/script/src/transports/websocket/websocket_transport.js +14 -12
  43. package/script/src/types/exchange/requests.d.ts +20 -0
  44. package/script/src/types/exchange/requests.d.ts.map +1 -1
  45. package/script/src/types/exchange/responses.d.ts +12 -0
  46. package/script/src/types/exchange/responses.d.ts.map +1 -1
@@ -51,21 +51,21 @@ export class ReconnectingWebSocket {
51
51
  * @param options - The configuration options.
52
52
  */
53
53
  constructor(url, protocols, options) {
54
- /** Controller for handling connection termination */
54
+ /** Controller for handling connection termination. */
55
55
  Object.defineProperty(this, "_terminationController", {
56
56
  enumerable: true,
57
57
  configurable: true,
58
58
  writable: true,
59
59
  value: new AbortController()
60
60
  });
61
- /** WebSocket protocols */
61
+ /** WebSocket protocols defined in constructor. */
62
62
  Object.defineProperty(this, "_protocols", {
63
63
  enumerable: true,
64
64
  configurable: true,
65
65
  writable: true,
66
66
  value: void 0
67
67
  });
68
- /** The underlying WebSocket instance */
68
+ /** Non-permanent original instance of WebSocket. */
69
69
  Object.defineProperty(this, "_socket", {
70
70
  enumerable: true,
71
71
  configurable: true,
@@ -79,21 +79,49 @@ export class ReconnectingWebSocket {
79
79
  writable: true,
80
80
  value: 0
81
81
  });
82
- /** Array of registered event listeners */
82
+ /** The array of registered event listeners to recover from reconnection. */
83
83
  Object.defineProperty(this, "_eventListeners", {
84
84
  enumerable: true,
85
85
  configurable: true,
86
86
  writable: true,
87
87
  value: []
88
88
  });
89
- /** Configuration options */
89
+ /** WebSocket event handlers for reconnection. */
90
+ Object.defineProperty(this, "_onclose", {
91
+ enumerable: true,
92
+ configurable: true,
93
+ writable: true,
94
+ value: void 0
95
+ });
96
+ /** WebSocket event handlers for reconnection. */
97
+ Object.defineProperty(this, "_onerror", {
98
+ enumerable: true,
99
+ configurable: true,
100
+ writable: true,
101
+ value: void 0
102
+ });
103
+ /** WebSocket event handlers for reconnection. */
104
+ Object.defineProperty(this, "_onmessage", {
105
+ enumerable: true,
106
+ configurable: true,
107
+ writable: true,
108
+ value: void 0
109
+ });
110
+ /** WebSocket event handlers for reconnection. */
111
+ Object.defineProperty(this, "_onopen", {
112
+ enumerable: true,
113
+ configurable: true,
114
+ writable: true,
115
+ value: void 0
116
+ });
117
+ /** Configuration options for WebSocket reconnection. */
90
118
  Object.defineProperty(this, "reconnectOptions", {
91
119
  enumerable: true,
92
120
  configurable: true,
93
121
  writable: true,
94
122
  value: void 0
95
123
  });
96
- /** An AbortSignal that is triggered when the connection is permanently closed */
124
+ /** The signal that is aborted when the connection is permanently closed. */
97
125
  Object.defineProperty(this, "terminationSignal", {
98
126
  enumerable: true,
99
127
  configurable: true,
@@ -124,6 +152,7 @@ export class ReconnectingWebSocket {
124
152
  writable: true,
125
153
  value: WebSocket.OPEN
126
154
  });
155
+ // Set the default options
127
156
  this.reconnectOptions = {
128
157
  maxRetries: options?.maxRetries ?? 3,
129
158
  connectionTimeout: options?.connectionTimeout === undefined ? 10_000 : options.connectionTimeout,
@@ -132,42 +161,19 @@ export class ReconnectingWebSocket {
132
161
  messageBuffer: options?.messageBuffer ?? new FIFOMessageBuffer(),
133
162
  };
134
163
  this._protocols = protocols;
135
- this._socket = this.reconnectOptions.connectionTimeout
136
- ? this._connectWithTimeout(url, this._protocols, this.reconnectOptions.connectionTimeout)
137
- : new WebSocket(url, this._protocols);
164
+ // Create the WebSocket instance
165
+ this._socket = createWebSocketWithTimeout(url, this._protocols, this.reconnectOptions.connectionTimeout);
166
+ // Initialize the reconnection event listeners
138
167
  this._initEventListeners();
139
- }
140
- /**
141
- * Creates a WebSocket connection with timeout.
142
- * @param url - The WebSocket URL to connect to.
143
- * @param protocols - The WebSocket protocols to use.
144
- * @param timeout - The connection timeout in ms.
145
- * @returns A new WebSocket instance.
146
- */
147
- _connectWithTimeout(url, protocols, timeout) {
148
- const socket = new WebSocket(url, protocols);
149
- const timeoutId = setTimeout(() => {
150
- socket.removeEventListener("open", openHandler);
151
- socket.removeEventListener("close", closeHandler);
152
- socket.close(3008, "Timeout"); // https://www.iana.org/assignments/websocket/websocket.xml#close-code-number
153
- }, timeout);
154
- const openHandler = () => {
155
- socket.removeEventListener("close", closeHandler);
156
- clearTimeout(timeoutId);
157
- };
158
- const closeHandler = () => {
159
- socket.removeEventListener("open", openHandler);
160
- clearTimeout(timeoutId);
161
- };
162
- socket.addEventListener("open", openHandler, { once: true });
163
- socket.addEventListener("close", closeHandler, { once: true });
164
- return socket;
168
+ // Store the original event listeners for reconnection
169
+ this._onclose = this._socket.onclose;
170
+ this._onerror = this._socket.onerror;
171
+ this._onmessage = this._socket.onmessage;
172
+ this._onopen = this._socket.onopen;
165
173
  }
166
174
  /** Initializes the internal event listeners for the WebSocket. */
167
175
  _initEventListeners() {
168
176
  this._socket.addEventListener("open", () => {
169
- // Reset reconnection count
170
- this._reconnectCount = 0;
171
177
  // Send all buffered messages
172
178
  let message;
173
179
  while ((message = this.reconnectOptions.messageBuffer.shift()) !== undefined) {
@@ -177,7 +183,7 @@ export class ReconnectingWebSocket {
177
183
  this._socket.addEventListener("close", async (event) => {
178
184
  try {
179
185
  // If the termination signal is already aborted, do not attempt to reconnect
180
- if (this.terminationSignal.aborted)
186
+ if (this._terminationController.signal.aborted)
181
187
  return;
182
188
  // Check if reconnection should be attempted
183
189
  if (++this._reconnectCount > this.reconnectOptions.maxRetries) {
@@ -185,7 +191,7 @@ export class ReconnectingWebSocket {
185
191
  return;
186
192
  }
187
193
  const userDecision = await this.reconnectOptions.shouldReconnect(event);
188
- if (this.terminationSignal.aborted)
194
+ if (this._terminationController.signal.aborted)
189
195
  return; // Check again after the await
190
196
  if (!userDecision) {
191
197
  this._cleanup(new ReconnectingWebSocketError("RECONNECTION_STOPPED_BY_USER"));
@@ -195,18 +201,20 @@ export class ReconnectingWebSocket {
195
201
  const delay = typeof this.reconnectOptions.connectionDelay === "number"
196
202
  ? this.reconnectOptions.connectionDelay
197
203
  : await this.reconnectOptions.connectionDelay(this._reconnectCount);
198
- if (this.terminationSignal.aborted)
204
+ if (this._terminationController.signal.aborted)
199
205
  return; // Check again after the await
200
- await sleep(delay, this.terminationSignal);
201
- // Reconnect the socket
202
- this._socket = this.reconnectOptions.connectionTimeout
203
- ? this._connectWithTimeout(this.url, this._protocols, this.reconnectOptions.connectionTimeout)
204
- : new WebSocket(this.url, this._protocols);
206
+ await sleep(delay, this._terminationController.signal);
207
+ // Create a new WebSocket instance
208
+ this._socket = createWebSocketWithTimeout(this.url, this._protocols, this.reconnectOptions.connectionTimeout);
205
209
  // Reconnect all listeners
206
210
  this._initEventListeners();
207
211
  this._eventListeners.forEach(({ type, listenerProxy, options }) => {
208
212
  this._socket.addEventListener(type, listenerProxy, options);
209
213
  });
214
+ this._socket.onclose = this._onclose;
215
+ this._socket.onerror = this._onerror;
216
+ this._socket.onmessage = this._onmessage;
217
+ this._socket.onopen = this._onopen;
210
218
  }
211
219
  catch (error) {
212
220
  this._cleanup(new ReconnectingWebSocketError("UNKNOWN_ERROR", error));
@@ -221,17 +229,7 @@ export class ReconnectingWebSocket {
221
229
  this._terminationController.abort(reason);
222
230
  this.reconnectOptions.messageBuffer.clear();
223
231
  this._eventListeners = [];
224
- }
225
- /**
226
- * Check if two event listeners are the same (just like EventTarget).
227
- * @param a - First event listener configuration.
228
- * @param b - Second event listener configuration.
229
- * @returns True if the listeners match.
230
- */
231
- _isListenerMatch(a, b) {
232
- const aCapture = Boolean(typeof a.options === "object" ? a.options.capture : a.options);
233
- const bCapture = Boolean(typeof b.options === "object" ? b.options.capture : b.options);
234
- return a.type === b.type && a.listener === b.listener && aCapture === bCapture;
232
+ this._socket.dispatchEvent(new CustomEvent("error", { detail: reason }));
235
233
  }
236
234
  // WebSocket property implementations
237
235
  get url() {
@@ -260,38 +258,43 @@ export class ReconnectingWebSocket {
260
258
  }
261
259
  set onclose(value) {
262
260
  this._socket.onclose = value;
261
+ this._onclose = value; // Store the listener for reconnection
263
262
  }
264
263
  get onerror() {
265
264
  return this._socket.onerror;
266
265
  }
267
266
  set onerror(value) {
268
267
  this._socket.onerror = value;
268
+ this._onerror = value; // Store the listener for reconnection
269
269
  }
270
270
  get onmessage() {
271
271
  return this._socket.onmessage;
272
272
  }
273
273
  set onmessage(value) {
274
274
  this._socket.onmessage = value;
275
+ this._onmessage = value; // Store the listener for reconnection
275
276
  }
276
277
  get onopen() {
277
278
  return this._socket.onopen;
278
279
  }
279
280
  set onopen(value) {
280
281
  this._socket.onopen = value;
282
+ this._onopen = value; // Store the listener for reconnection
281
283
  }
282
284
  /**
283
- * @param permanently - If true, the connection will be permanently closed. Default is true.
285
+ * @param permanently - If `true`, the connection will be permanently closed. Default is `true`.
284
286
  */
285
287
  close(code, reason, permanently = true) {
286
- if (permanently)
287
- this._cleanup(new ReconnectingWebSocketError("USER_INITIATED_CLOSE"));
288
288
  this._socket.close(code, reason);
289
+ if (permanently) {
290
+ this._cleanup(new ReconnectingWebSocketError("USER_INITIATED_CLOSE"));
291
+ }
289
292
  }
290
293
  /**
291
294
  * @note If the connection is not open, the data will be buffered and sent when the connection is established.
292
295
  */
293
296
  send(data) {
294
- if (this._socket.readyState !== WebSocket.OPEN && !this.terminationSignal.aborted) {
297
+ if (this._socket.readyState !== WebSocket.OPEN && !this._terminationController.signal.aborted) {
295
298
  this.reconnectOptions.messageBuffer.push(data);
296
299
  }
297
300
  else {
@@ -299,38 +302,57 @@ export class ReconnectingWebSocket {
299
302
  }
300
303
  }
301
304
  addEventListener(type, listener, options) {
302
- // Check if the listener is already added
303
- const exists = this._eventListeners.some((e) => this._isListenerMatch(e, { type, listener, options }));
304
- if (exists)
305
- return;
306
- // Wrap the original listener to follow the once option when reconnecting
307
- const listenerProxy = (event) => {
308
- try {
309
- if (typeof listener === "function") {
310
- listener.call(this, event);
311
- }
312
- else {
313
- listener.handleEvent(event);
314
- }
305
+ // Wrap the listener to handle reconnection
306
+ let listenerProxy;
307
+ if (this._terminationController.signal.aborted) {
308
+ // If the connection is permanently closed, use the original listener
309
+ listenerProxy = listener;
310
+ }
311
+ else {
312
+ // Check if the listener is already registered
313
+ const index = this._eventListeners.findIndex((e) => listenersMatch(e, { type, listener, options }));
314
+ if (index !== -1) {
315
+ // Use the existing listener proxy
316
+ listenerProxy = this._eventListeners[index].listenerProxy;
315
317
  }
316
- finally {
317
- if (typeof options === "object" && options.once === true) {
318
- const index = this._eventListeners.findIndex((e) => this._isListenerMatch(e, { type, listener, options }));
319
- if (index !== -1)
320
- this._eventListeners.splice(index, 1);
321
- }
318
+ else {
319
+ // Wrap the original listener to follow the once option when reconnecting
320
+ listenerProxy = (event) => {
321
+ try {
322
+ if (typeof listener === "function") {
323
+ listener.call(this, event);
324
+ }
325
+ else {
326
+ listener.handleEvent(event);
327
+ }
328
+ }
329
+ finally {
330
+ if (typeof options === "object" && options.once === true) {
331
+ const index = this._eventListeners.findIndex((e) => listenersMatch(e, { type, listener, options }));
332
+ if (index !== -1) {
333
+ this._eventListeners.splice(index, 1);
334
+ }
335
+ }
336
+ }
337
+ };
338
+ this._eventListeners.push({ type, listener, options, listenerProxy });
322
339
  }
323
- };
324
- this._eventListeners.push({ type, listener, options, listenerProxy });
340
+ }
341
+ // Add the wrapped (or original) listener
325
342
  this._socket.addEventListener(type, listenerProxy, options);
326
343
  }
327
344
  removeEventListener(type, listener, options) {
328
- const index = this._eventListeners.findIndex((e) => this._isListenerMatch(e, { type, listener, options }));
345
+ // Remove a wrapped listener, not an original listener
346
+ const index = this._eventListeners.findIndex((e) => listenersMatch(e, { type, listener, options }));
329
347
  if (index !== -1) {
330
348
  const { listenerProxy } = this._eventListeners[index];
331
349
  this._socket.removeEventListener(type, listenerProxy, options);
332
350
  this._eventListeners.splice(index, 1);
333
351
  }
352
+ else {
353
+ // If the wrapped listener is not found, remove the original listener
354
+ this._socket.removeEventListener(type, listener, options);
355
+ }
334
356
  }
335
357
  dispatchEvent(event) {
336
358
  return this._socket.dispatchEvent(event);
@@ -360,6 +382,35 @@ Object.defineProperty(ReconnectingWebSocket, "OPEN", {
360
382
  writable: true,
361
383
  value: WebSocket.OPEN
362
384
  });
385
+ /** Creates a WebSocket with connection timeout. */
386
+ function createWebSocketWithTimeout(url, protocols, timeout) {
387
+ const socket = new WebSocket(url, protocols);
388
+ if (timeout === null || timeout === undefined)
389
+ return socket;
390
+ const timeoutId = setTimeout(() => {
391
+ socket.removeEventListener("open", openHandler);
392
+ socket.removeEventListener("close", closeHandler);
393
+ socket.close(3008, "Timeout"); // https://www.iana.org/assignments/websocket/websocket.xml#close-code-number
394
+ }, timeout);
395
+ const openHandler = () => {
396
+ socket.removeEventListener("close", closeHandler);
397
+ clearTimeout(timeoutId);
398
+ };
399
+ const closeHandler = () => {
400
+ socket.removeEventListener("open", openHandler);
401
+ clearTimeout(timeoutId);
402
+ };
403
+ socket.addEventListener("open", openHandler, { once: true });
404
+ socket.addEventListener("close", closeHandler, { once: true });
405
+ return socket;
406
+ }
407
+ /** Check if two event listeners are the same (just like EventTarget). */
408
+ function listenersMatch(a, b) {
409
+ // EventTarget only compares capture in options, even if one is an object and the other is boolean
410
+ const aCapture = Boolean(typeof a.options === "object" ? a.options.capture : a.options);
411
+ const bCapture = Boolean(typeof b.options === "object" ? b.options.capture : b.options);
412
+ return a.type === b.type && a.listener === b.listener && aCapture === bCapture;
413
+ }
363
414
  /**
364
415
  * Returns a promise that resolves after the specified number of ms,
365
416
  * or rejects as soon as the given signal is aborted.
@@ -13,22 +13,17 @@ export declare class WebSocketRequestError extends TransportError {
13
13
  * Handles request creation, sending, and mapping responses to their corresponding requests.
14
14
  */
15
15
  export declare class WebSocketRequestDispatcher {
16
- protected socket: WebSocket;
17
- /** Last used request ID */
18
- protected lastId: number;
16
+ private socket;
17
+ /** Last used post request ID */
18
+ private lastId;
19
19
  /** Map of pending requests waiting for responses */
20
- protected pending: Map<number | string, {
21
- resolve: (value: unknown) => void;
22
- reject: (reason: unknown) => void;
23
- }>;
20
+ private pending;
24
21
  /**
25
22
  * Creates a new WebSocket request dispatcher.
26
23
  * @param socket - WebSocket connection instance for sending requests to the Hyperliquid WebSocket API
27
24
  * @param hlEvents - Used to recognize Hyperliquid responses and match them with sent requests
28
25
  */
29
26
  constructor(socket: WebSocket, hlEvents: HyperliquidEventTarget);
30
- /** Gets the next request ID */
31
- protected get nextId(): number;
32
27
  /**
33
28
  * Sends a request to the Hyperliquid API.
34
29
  * @param method - The method of websocket request.
@@ -37,18 +32,6 @@ export declare class WebSocketRequestDispatcher {
37
32
  * @returns A promise that resolves with the parsed JSON response body.
38
33
  */
39
34
  request(method: "post" | "subscribe" | "unsubscribe", payload: unknown, signal?: AbortSignal): Promise<unknown>;
40
- /**
41
- * Resolves a pending request.
42
- * @param id - A request ID or a stringified request.
43
- * @param value - A resolution value.
44
- */
45
- protected resolve(id: number | string, value: unknown): void;
46
- /**
47
- * Rejects a pending request.
48
- * @param id - A request ID or a stringified request.
49
- * @param reason - A rejection reason.
50
- */
51
- protected reject(id: number | string, reason: unknown): void;
52
35
  /**
53
36
  * Normalizes a request object to an ID.
54
37
  * @param value - A request object.
@@ -1 +1 @@
1
- {"version":3,"file":"_websocket_request_dispatcher.d.ts","sourceRoot":"","sources":["../../../../src/src/transports/websocket/_websocket_request_dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAE7E;;;;GAIG;AACH,qBAAa,qBAAsB,SAAQ,cAAc;gBACzC,OAAO,EAAE,MAAM;CAI9B;AAED;;;GAGG;AACH,qBAAa,0BAA0B;IAevB,SAAS,CAAC,MAAM,EAAE,SAAS;IAdvC,2BAA2B;IAC3B,SAAS,CAAC,MAAM,EAAE,MAAM,CAAK;IAE7B,oDAAoD;IACpD,SAAS,CAAC,OAAO,EAAE,GAAG,CAClB,MAAM,GAAG,MAAM,EACf;QAAE,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;QAAC,MAAM,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;KAAE,CAC3E,CAAa;IAEd;;;;OAIG;gBACmB,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,sBAAsB;IA2DzE,+BAA+B;IAC/B,SAAS,KAAK,MAAM,IAAI,MAAM,CAE7B;IAED;;;;;;OAMG;IACG,OAAO,CACT,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,EAC5C,OAAO,EAAE,OAAO,EAChB,MAAM,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,OAAO,CAAC;IAkCnB;;;;OAIG;IACH,SAAS,CAAC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAK5D;;;;OAIG;IACH,SAAS,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAK5D;;;;OAIG;IACH,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;CAK7C"}
1
+ {"version":3,"file":"_websocket_request_dispatcher.d.ts","sourceRoot":"","sources":["../../../../src/src/transports/websocket/_websocket_request_dispatcher.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAY7E;;;;GAIG;AACH,qBAAa,qBAAsB,SAAQ,cAAc;gBACzC,OAAO,EAAE,MAAM;CAI9B;AAED;;;GAGG;AACH,qBAAa,0BAA0B;IAkBvB,OAAO,CAAC,MAAM;IAjB1B,gCAAgC;IAChC,OAAO,CAAC,MAAM,CAAa;IAE3B,oDAAoD;IACpD,OAAO,CAAC,OAAO,CAMD;IAEd;;;;OAIG;gBACiB,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,sBAAsB;IAwDvE;;;;;;OAMG;IACG,OAAO,CACT,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,EAC5C,OAAO,EAAE,OAAO,EAChB,MAAM,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,OAAO,CAAC;IAoCnB;;;;OAIG;IACH,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;CAK7C"}
@@ -27,7 +27,7 @@ export class WebSocketRequestDispatcher {
27
27
  writable: true,
28
28
  value: socket
29
29
  });
30
- /** Last used request ID */
30
+ /** Last used post request ID */
31
31
  Object.defineProperty(this, "lastId", {
32
32
  enumerable: true,
33
33
  configurable: true,
@@ -45,13 +45,13 @@ export class WebSocketRequestDispatcher {
45
45
  hlEvents.addEventListener("subscriptionResponse", (event) => {
46
46
  // Use a stringified request as an id
47
47
  const id = WebSocketRequestDispatcher.requestToId(event.detail.subscription);
48
- this.resolve(id, event.detail);
48
+ this.pending.get(id)?.resolve(event.detail);
49
49
  });
50
50
  hlEvents.addEventListener("post", (event) => {
51
51
  const data = event.detail.response.type === "info"
52
52
  ? event.detail.response.payload.data
53
53
  : event.detail.response.payload;
54
- this.resolve(event.detail.id, data);
54
+ this.pending.get(event.detail.id)?.resolve(data);
55
55
  });
56
56
  hlEvents.addEventListener("error", (event) => {
57
57
  try {
@@ -61,19 +61,19 @@ export class WebSocketRequestDispatcher {
61
61
  const parsedRequest = JSON.parse(request);
62
62
  if ("id" in parsedRequest && typeof parsedRequest.id === "number") {
63
63
  // If a post request was sent, it is possible to get the id from the request
64
- this.reject(parsedRequest.id, new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
64
+ this.pending.get(parsedRequest.id)?.reject(new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
65
65
  }
66
66
  else if ("subscription" in parsedRequest &&
67
67
  typeof parsedRequest.subscription === "object" &&
68
68
  parsedRequest.subscription !== null) {
69
69
  // If a subscription/unsubscribe request was sent, use the request as an id
70
70
  const id = WebSocketRequestDispatcher.requestToId(parsedRequest.subscription);
71
- this.reject(id, new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
71
+ this.pending.get(id)?.reject(new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
72
72
  }
73
73
  else {
74
74
  // If the request is not recognized, use the parsed request as an id
75
75
  const id = WebSocketRequestDispatcher.requestToId(parsedRequest);
76
- this.reject(id, new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
76
+ this.pending.get(id)?.reject(new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
77
77
  }
78
78
  }
79
79
  }
@@ -89,10 +89,6 @@ export class WebSocketRequestDispatcher {
89
89
  this.pending.clear();
90
90
  });
91
91
  }
92
- /** Gets the next request ID */
93
- get nextId() {
94
- return ++this.lastId;
95
- }
96
92
  /**
97
93
  * Sends a request to the Hyperliquid API.
98
94
  * @param method - The method of websocket request.
@@ -101,12 +97,14 @@ export class WebSocketRequestDispatcher {
101
97
  * @returns A promise that resolves with the parsed JSON response body.
102
98
  */
103
99
  async request(method, payload, signal) {
104
- signal?.throwIfAborted();
100
+ // Reject the request if the signal is aborted
101
+ if (signal?.aborted)
102
+ return Promise.reject(signal.reason);
105
103
  // Create a request object
106
104
  let id;
107
105
  let request;
108
106
  if (method === "post") {
109
- id = this.nextId;
107
+ id = ++this.lastId;
110
108
  request = { method, id, request: payload };
111
109
  }
112
110
  else {
@@ -126,26 +124,10 @@ export class WebSocketRequestDispatcher {
126
124
  }).finally(() => {
127
125
  // Remove the abort listener when the promise is settled
128
126
  signal?.removeEventListener("abort", onAbort);
127
+ // Clean up the pending list
128
+ this.pending.delete(id);
129
129
  });
130
130
  }
131
- /**
132
- * Resolves a pending request.
133
- * @param id - A request ID or a stringified request.
134
- * @param value - A resolution value.
135
- */
136
- resolve(id, value) {
137
- this.pending.get(id)?.resolve(value);
138
- this.pending.delete(id);
139
- }
140
- /**
141
- * Rejects a pending request.
142
- * @param id - A request ID or a stringified request.
143
- * @param reason - A rejection reason.
144
- */
145
- reject(id, reason) {
146
- this.pending.get(id)?.reject(reason);
147
- this.pending.delete(id);
148
- }
149
131
  /**
150
132
  * Normalizes a request object to an ID.
151
133
  * @param value - A request object.
@@ -166,18 +148,16 @@ function deepLowerHex(obj) {
166
148
  if (Array.isArray(obj)) {
167
149
  return obj.map(deepLowerHex);
168
150
  }
169
- else if (obj && typeof obj === "object") {
151
+ if (typeof obj === "object" && obj !== null) {
170
152
  return Object.entries(obj).reduce((acc, [key, val]) => ({
171
153
  ...acc,
172
154
  [key]: deepLowerHex(val),
173
155
  }), {});
174
156
  }
175
- else if (typeof obj === "string" && /^0x[0-9A-Fa-f]+$/.test(obj)) {
157
+ if (typeof obj === "string" && /^0x[0-9A-Fa-f]+$/.test(obj)) {
176
158
  return obj.toLowerCase();
177
159
  }
178
- else {
179
- return obj;
180
- }
160
+ return obj;
181
161
  }
182
162
  /**
183
163
  * Deeply sort the keys of an object.
@@ -1,9 +1,9 @@
1
- import { ReconnectingWebSocket, type ReconnectingWebSocketOptions } from "./_reconnecting_websocket.js";
1
+ import { type MessageBufferStrategy, ReconnectingWebSocket, ReconnectingWebSocketError, type ReconnectingWebSocketOptions } from "./_reconnecting_websocket.js";
2
2
  import { HyperliquidEventTarget } from "./_hyperliquid_event_target.js";
3
3
  import { WebSocketRequestDispatcher, WebSocketRequestError } from "./_websocket_request_dispatcher.js";
4
4
  import type { IRequestTransport, ISubscriptionTransport, Subscription } from "../../base.js";
5
5
  export { WebSocketRequestError };
6
- export type { MessageBufferStrategy, ReconnectingWebSocketOptions } from "./_reconnecting_websocket.js";
6
+ export { type MessageBufferStrategy, ReconnectingWebSocketError, type ReconnectingWebSocketOptions };
7
7
  /** Configuration options for the WebSocket transport layer. */
8
8
  export interface WebSocketTransportOptions {
9
9
  /**
@@ -105,7 +105,5 @@ export declare class WebSocketTransport implements IRequestTransport, ISubscript
105
105
  * @returns A promise that resolves when the connection is fully closed.
106
106
  */
107
107
  close(signal?: AbortSignal): Promise<void>;
108
- /** Combines a timeout with an optional abort signal. */
109
- protected _combineTimeoutWithSignal(timeout?: number | null, signal?: AbortSignal): AbortSignal | undefined;
110
108
  }
111
109
  //# sourceMappingURL=websocket_transport.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"websocket_transport.d.ts","sourceRoot":"","sources":["../../../../src/src/transports/websocket/websocket_transport.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,KAAK,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AACxG,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AACvG,OAAO,KAAK,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7F,OAAO,EAAE,qBAAqB,EAAE,CAAC;AACjC,YAAY,EAAE,qBAAqB,EAAE,4BAA4B,EAAE,MAAM,8BAA8B,CAAC;AAExG,+DAA+D;AAC/D,MAAM,WAAW,yBAAyB;IACtC;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;IAEnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB;;;OAGG;IACH,SAAS,CAAC,EAAE;QACR;;;;WAIG;QACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC5B,CAAC;IAEF;;OAEG;IACH,SAAS,CAAC,EAAE,4BAA4B,CAAC;CAC5C;AAED,kFAAkF;AAClF,qBAAa,kBAAmB,YAAW,iBAAiB,EAAE,sBAAsB;IAChF,qDAAqD;IACrD,SAAS,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEhD,iDAAiD;IACjD,SAAS,CAAC,YAAY,EAAE,0BAA0B,CAAC;IAEnD,6CAA6C;IAC7C,SAAS,CAAC,SAAS,EAAE,sBAAsB,CAAC;IAE5C;;;;;OAKG;IACH,SAAS,CAAC,cAAc,EAAE,GAAG,CACzB,MAAM,EACN;QACI,SAAS,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACrF,cAAc,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;KACpC,CACJ,CAAa;IAEd;;;OAGG;IACH,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvB,yCAAyC;IACzC,QAAQ,CAAC,SAAS,EAAE;QAChB;;;WAGG;QACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;KACpC,CAAC;IAEF,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IAEvC;;;OAGG;gBACS,OAAO,CAAC,EAAE,yBAAyB;IAuC/C;;;;;;;;OAQG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAoBzG;;;;;;;OAOG;IACG,SAAS,CACX,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,EACrC,MAAM,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,YAAY,CAAC;IAgExB;;;;OAIG;IACH,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB1C;;;;OAIG;IACH,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAqB1C,wDAAwD;IACxD,SAAS,CAAC,yBAAyB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,WAAW,GAAG,SAAS;CAK9G"}
1
+ {"version":3,"file":"websocket_transport.d.ts","sourceRoot":"","sources":["../../../../src/src/transports/websocket/websocket_transport.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,KAAK,qBAAqB,EAC1B,qBAAqB,EACrB,0BAA0B,EAC1B,KAAK,4BAA4B,EACpC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,qBAAqB,EAAE,MAAM,oCAAoC,CAAC;AACvG,OAAO,KAAK,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAE7F,OAAO,EAAE,qBAAqB,EAAE,CAAC;AACjC,OAAO,EAAE,KAAK,qBAAqB,EAAE,0BAA0B,EAAE,KAAK,4BAA4B,EAAE,CAAC;AAErG,+DAA+D;AAC/D,MAAM,WAAW,yBAAyB;IACtC;;;;;OAKG;IACH,GAAG,CAAC,EAAE,MAAM,GAAG,GAAG,CAAC;IAEnB;;;;OAIG;IACH,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAExB;;;OAGG;IACH,SAAS,CAAC,EAAE;QACR;;;;WAIG;QACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KAC5B,CAAC;IAEF;;OAEG;IACH,SAAS,CAAC,EAAE,4BAA4B,CAAC;CAC5C;AAED,kFAAkF;AAClF,qBAAa,kBAAmB,YAAW,iBAAiB,EAAE,sBAAsB;IAChF,qDAAqD;IACrD,SAAS,CAAC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEhD,iDAAiD;IACjD,SAAS,CAAC,YAAY,EAAE,0BAA0B,CAAC;IAEnD,6CAA6C;IAC7C,SAAS,CAAC,SAAS,EAAE,sBAAsB,CAAC;IAE5C;;;;;OAKG;IACH,SAAS,CAAC,cAAc,EAAE,GAAG,CACzB,MAAM,EACN;QACI,SAAS,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,EAAE,CAAC,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;QACrF,cAAc,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;KACpC,CACJ,CAAa;IAEd;;;OAGG;IACH,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAEvB,yCAAyC;IACzC,QAAQ,CAAC,SAAS,EAAE;QAChB;;;WAGG;QACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;KACpC,CAAC;IAEF,oDAAoD;IACpD,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;IAEvC;;;OAGG;gBACS,OAAO,CAAC,EAAE,yBAAyB;IAuC/C;;;;;;;;OAQG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,GAAG,UAAU,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;IAwBzG;;;;;;;OAOG;IACG,SAAS,CACX,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,EACrC,MAAM,CAAC,EAAE,WAAW,GACrB,OAAO,CAAC,YAAY,CAAC;IAwExB;;;;OAIG;IACH,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB1C;;;;OAIG;IACH,KAAK,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAoB7C"}
@@ -1,7 +1,8 @@
1
- import { ReconnectingWebSocket } from "./_reconnecting_websocket.js";
1
+ import { ReconnectingWebSocket, ReconnectingWebSocketError, } from "./_reconnecting_websocket.js";
2
2
  import { HyperliquidEventTarget } from "./_hyperliquid_event_target.js";
3
3
  import { WebSocketRequestDispatcher, WebSocketRequestError } from "./_websocket_request_dispatcher.js";
4
4
  export { WebSocketRequestError };
5
+ export { ReconnectingWebSocketError };
5
6
  /** WebSocket implementation of the REST and Subscription transport interfaces. */
6
7
  export class WebSocketTransport {
7
8
  /**
@@ -111,7 +112,10 @@ export class WebSocketTransport {
111
112
  return Promise.reject(new WebSocketRequestError("Explorer requests are not supported in the Hyperliquid WebSocket API."));
112
113
  }
113
114
  // Send the request and wait for a response
114
- const combinedSignal = this._combineTimeoutWithSignal(this.timeout, signal);
115
+ const timeoutSignal = this.timeout ? AbortSignal.timeout(this.timeout) : undefined;
116
+ const combinedSignal = signal && timeoutSignal
117
+ ? AbortSignal.any([signal, timeoutSignal])
118
+ : signal ?? timeoutSignal;
115
119
  return this._wsRequester.request("post", {
116
120
  type: type === "exchange" ? "action" : type,
117
121
  payload,
@@ -132,7 +136,10 @@ export class WebSocketTransport {
132
136
  let subscription = this._subscriptions.get(id);
133
137
  if (!subscription) {
134
138
  // Send subscription request
135
- const combinedSignal = this._combineTimeoutWithSignal(this.timeout, signal);
139
+ const timeoutSignal = this.timeout ? AbortSignal.timeout(this.timeout) : undefined;
140
+ const combinedSignal = signal && timeoutSignal
141
+ ? AbortSignal.any([signal, timeoutSignal])
142
+ : signal ?? timeoutSignal;
136
143
  const requestPromise = this._wsRequester.request("subscribe", payload, combinedSignal);
137
144
  // Cache subscription info
138
145
  subscription = { listeners: new Map(), requestPromise };
@@ -153,7 +160,10 @@ export class WebSocketTransport {
153
160
  this._subscriptions.delete(id);
154
161
  // If the socket is open, send unsubscription request
155
162
  if (this.socket.readyState === WebSocket.OPEN) {
156
- const combinedSignal = this._combineTimeoutWithSignal(this.timeout, signal);
163
+ const timeoutSignal = this.timeout ? AbortSignal.timeout(this.timeout) : undefined;
164
+ const combinedSignal = signal && timeoutSignal
165
+ ? AbortSignal.any([signal, timeoutSignal])
166
+ : signal ?? timeoutSignal;
157
167
  await this._wsRequester.request("unsubscribe", payload, combinedSignal);
158
168
  }
159
169
  }
@@ -228,12 +238,4 @@ export class WebSocketTransport {
228
238
  this.socket.close();
229
239
  });
230
240
  }
231
- /** Combines a timeout with an optional abort signal. */
232
- _combineTimeoutWithSignal(timeout, signal) {
233
- if (typeof timeout !== "number")
234
- return signal;
235
- if (!(signal instanceof AbortSignal))
236
- return AbortSignal.timeout(timeout);
237
- return AbortSignal.any([AbortSignal.timeout(timeout), signal]);
238
- }
239
241
  }