@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.
- package/README.md +6 -5
- package/esm/src/clients/wallet.d.ts +30 -3
- package/esm/src/clients/wallet.d.ts.map +1 -1
- package/esm/src/clients/wallet.js +49 -0
- package/esm/src/signing.d.ts +3 -3
- package/esm/src/signing.d.ts.map +1 -1
- package/esm/src/transports/http/http_transport.d.ts +2 -6
- package/esm/src/transports/http/http_transport.d.ts.map +1 -1
- package/esm/src/transports/http/http_transport.js +2 -4
- package/esm/src/transports/websocket/_hyperliquid_event_target.js +13 -15
- package/esm/src/transports/websocket/_reconnecting_websocket.d.ts +22 -42
- package/esm/src/transports/websocket/_reconnecting_websocket.d.ts.map +1 -1
- package/esm/src/transports/websocket/_reconnecting_websocket.js +133 -82
- package/esm/src/transports/websocket/_websocket_request_dispatcher.d.ts +4 -21
- package/esm/src/transports/websocket/_websocket_request_dispatcher.d.ts.map +1 -1
- package/esm/src/transports/websocket/_websocket_request_dispatcher.js +15 -35
- package/esm/src/transports/websocket/websocket_transport.d.ts +2 -4
- package/esm/src/transports/websocket/websocket_transport.d.ts.map +1 -1
- package/esm/src/transports/websocket/websocket_transport.js +14 -12
- package/esm/src/types/exchange/requests.d.ts +20 -0
- package/esm/src/types/exchange/requests.d.ts.map +1 -1
- package/esm/src/types/exchange/responses.d.ts +12 -0
- package/esm/src/types/exchange/responses.d.ts.map +1 -1
- package/package.json +1 -1
- package/script/src/clients/wallet.d.ts +30 -3
- package/script/src/clients/wallet.d.ts.map +1 -1
- package/script/src/clients/wallet.js +49 -0
- package/script/src/signing.d.ts +3 -3
- package/script/src/signing.d.ts.map +1 -1
- package/script/src/transports/http/http_transport.d.ts +2 -6
- package/script/src/transports/http/http_transport.d.ts.map +1 -1
- package/script/src/transports/http/http_transport.js +2 -4
- package/script/src/transports/websocket/_hyperliquid_event_target.js +13 -15
- package/script/src/transports/websocket/_reconnecting_websocket.d.ts +22 -42
- package/script/src/transports/websocket/_reconnecting_websocket.d.ts.map +1 -1
- package/script/src/transports/websocket/_reconnecting_websocket.js +133 -82
- package/script/src/transports/websocket/_websocket_request_dispatcher.d.ts +4 -21
- package/script/src/transports/websocket/_websocket_request_dispatcher.d.ts.map +1 -1
- package/script/src/transports/websocket/_websocket_request_dispatcher.js +15 -35
- package/script/src/transports/websocket/websocket_transport.d.ts +2 -4
- package/script/src/transports/websocket/websocket_transport.d.ts.map +1 -1
- package/script/src/transports/websocket/websocket_transport.js +14 -12
- package/script/src/types/exchange/requests.d.ts +20 -0
- package/script/src/types/exchange/requests.d.ts.map +1 -1
- package/script/src/types/exchange/responses.d.ts +12 -0
- 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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
/**
|
|
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
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
204
|
+
if (this._terminationController.signal.aborted)
|
|
199
205
|
return; // Check again after the await
|
|
200
|
-
await sleep(delay, this.
|
|
201
|
-
//
|
|
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
|
|
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.
|
|
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
|
-
//
|
|
303
|
-
|
|
304
|
-
if (
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
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
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17
|
-
/** Last used request ID */
|
|
18
|
-
|
|
16
|
+
private socket;
|
|
17
|
+
/** Last used post request ID */
|
|
18
|
+
private lastId;
|
|
19
19
|
/** Map of pending requests waiting for responses */
|
|
20
|
-
|
|
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;
|
|
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(
|
|
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.
|
|
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.
|
|
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(
|
|
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(
|
|
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
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
157
|
+
if (typeof obj === "string" && /^0x[0-9A-Fa-f]+$/.test(obj)) {
|
|
176
158
|
return obj.toLowerCase();
|
|
177
159
|
}
|
|
178
|
-
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
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
|
}
|