@nktkas/hyperliquid 0.19.2 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +61 -79
- package/esm/mod.d.ts +5 -3
- package/esm/mod.d.ts.map +1 -1
- package/esm/mod.js +4 -3
- package/esm/src/base.d.ts +1 -47
- package/esm/src/base.d.ts.map +1 -1
- package/esm/src/base.js +1 -8
- package/{script/src/clients/wallet.d.ts → esm/src/clients/exchange.d.ts} +111 -112
- package/esm/src/clients/exchange.d.ts.map +1 -0
- package/esm/src/clients/{wallet.js → exchange.js} +96 -306
- package/esm/src/clients/{public.d.ts → info.d.ts} +121 -102
- package/esm/src/clients/info.d.ts.map +1 -0
- package/esm/src/clients/{public.js → info.js} +73 -57
- package/{script/src/clients/event.d.ts → esm/src/clients/subscription.d.ts} +61 -80
- package/esm/src/clients/subscription.d.ts.map +1 -0
- package/esm/src/clients/{event.js → subscription.js} +106 -136
- package/esm/src/signing.d.ts +4 -0
- package/esm/src/signing.d.ts.map +1 -1
- package/esm/src/signing.js +23 -5
- package/esm/src/transports/base.d.ts +47 -0
- package/esm/src/transports/base.d.ts.map +1 -0
- package/esm/src/transports/base.js +8 -0
- package/esm/src/transports/http/http_transport.d.ts +8 -5
- package/esm/src/transports/http/http_transport.d.ts.map +1 -1
- package/esm/src/transports/http/http_transport.js +15 -62
- package/esm/src/transports/websocket/_hyperliquid_event_target.d.ts +36 -39
- package/esm/src/transports/websocket/_hyperliquid_event_target.d.ts.map +1 -1
- package/esm/src/transports/websocket/_reconnecting_websocket.d.ts +21 -27
- package/esm/src/transports/websocket/_reconnecting_websocket.d.ts.map +1 -1
- package/esm/src/transports/websocket/_reconnecting_websocket.js +89 -182
- package/{script/src/transports/websocket/_websocket_request_dispatcher.d.ts → esm/src/transports/websocket/_websocket_async_request.d.ts} +17 -19
- package/esm/src/transports/websocket/_websocket_async_request.d.ts.map +1 -0
- package/esm/src/transports/websocket/_websocket_async_request.js +177 -0
- package/esm/src/transports/websocket/websocket_transport.d.ts +54 -41
- package/esm/src/transports/websocket/websocket_transport.d.ts.map +1 -1
- package/esm/src/transports/websocket/websocket_transport.js +101 -113
- package/esm/src/types/info/accounts.d.ts +1 -0
- package/esm/src/types/info/accounts.d.ts.map +1 -1
- package/esm/src/types/info/assets.d.ts +0 -48
- package/esm/src/types/info/assets.d.ts.map +1 -1
- package/esm/src/types/info/markets.d.ts +52 -0
- package/esm/src/types/info/markets.d.ts.map +1 -0
- package/esm/src/types/info/markets.js +1 -0
- package/esm/src/types/info/requests.d.ts +9 -0
- package/esm/src/types/info/requests.d.ts.map +1 -1
- package/esm/src/types/mod.d.ts +3 -0
- package/esm/src/types/mod.d.ts.map +1 -1
- package/esm/src/types/mod.js +2 -0
- package/package.json +12 -9
- package/script/mod.d.ts +5 -3
- package/script/mod.d.ts.map +1 -1
- package/script/mod.js +5 -4
- package/script/src/base.d.ts +1 -47
- package/script/src/base.d.ts.map +1 -1
- package/script/src/base.js +2 -10
- package/{esm/src/clients/wallet.d.ts → script/src/clients/exchange.d.ts} +111 -112
- package/script/src/clients/exchange.d.ts.map +1 -0
- package/script/src/clients/{wallet.js → exchange.js} +98 -308
- package/script/src/clients/{public.d.ts → info.d.ts} +121 -102
- package/script/src/clients/info.d.ts.map +1 -0
- package/script/src/clients/{public.js → info.js} +75 -59
- package/{esm/src/clients/event.d.ts → script/src/clients/subscription.d.ts} +61 -80
- package/script/src/clients/subscription.d.ts.map +1 -0
- package/script/src/clients/{event.js → subscription.js} +108 -138
- package/script/src/signing.d.ts +4 -0
- package/script/src/signing.d.ts.map +1 -1
- package/script/src/signing.js +25 -7
- package/script/src/transports/base.d.ts +47 -0
- package/script/src/transports/base.d.ts.map +1 -0
- package/script/src/transports/base.js +22 -0
- package/script/src/transports/http/http_transport.d.ts +8 -5
- package/script/src/transports/http/http_transport.d.ts.map +1 -1
- package/script/src/transports/http/http_transport.js +16 -63
- package/script/src/transports/websocket/_hyperliquid_event_target.d.ts +36 -39
- package/script/src/transports/websocket/_hyperliquid_event_target.d.ts.map +1 -1
- package/script/src/transports/websocket/_reconnecting_websocket.d.ts +21 -27
- package/script/src/transports/websocket/_reconnecting_websocket.d.ts.map +1 -1
- package/script/src/transports/websocket/_reconnecting_websocket.js +90 -183
- package/{esm/src/transports/websocket/_websocket_request_dispatcher.d.ts → script/src/transports/websocket/_websocket_async_request.d.ts} +17 -19
- package/script/src/transports/websocket/_websocket_async_request.d.ts.map +1 -0
- package/script/src/transports/websocket/_websocket_async_request.js +192 -0
- package/script/src/transports/websocket/websocket_transport.d.ts +54 -41
- package/script/src/transports/websocket/websocket_transport.d.ts.map +1 -1
- package/script/src/transports/websocket/websocket_transport.js +103 -115
- package/script/src/types/info/accounts.d.ts +1 -0
- package/script/src/types/info/accounts.d.ts.map +1 -1
- package/script/src/types/info/assets.d.ts +0 -48
- package/script/src/types/info/assets.d.ts.map +1 -1
- package/script/src/types/info/markets.d.ts +52 -0
- package/script/src/types/info/markets.d.ts.map +1 -0
- package/script/{deps/jsr.io/@noble/hashes/1.8.0/src/crypto.js → src/types/info/markets.js} +0 -2
- package/script/src/types/info/requests.d.ts +9 -0
- package/script/src/types/info/requests.d.ts.map +1 -1
- package/script/src/types/mod.d.ts +3 -0
- package/script/src/types/mod.d.ts.map +1 -1
- package/script/src/types/mod.js +2 -0
- package/esm/deps/jsr.io/@noble/hashes/1.8.0/src/_u64.d.ts +0 -55
- package/esm/deps/jsr.io/@noble/hashes/1.8.0/src/_u64.d.ts.map +0 -1
- package/esm/deps/jsr.io/@noble/hashes/1.8.0/src/_u64.js +0 -66
- package/esm/deps/jsr.io/@noble/hashes/1.8.0/src/crypto.d.ts +0 -2
- package/esm/deps/jsr.io/@noble/hashes/1.8.0/src/crypto.d.ts.map +0 -1
- package/esm/deps/jsr.io/@noble/hashes/1.8.0/src/crypto.js +0 -1
- package/esm/deps/jsr.io/@noble/hashes/1.8.0/src/sha3.d.ts +0 -53
- package/esm/deps/jsr.io/@noble/hashes/1.8.0/src/sha3.d.ts.map +0 -1
- package/esm/deps/jsr.io/@noble/hashes/1.8.0/src/sha3.js +0 -294
- package/esm/deps/jsr.io/@noble/hashes/1.8.0/src/utils.d.ts +0 -161
- package/esm/deps/jsr.io/@noble/hashes/1.8.0/src/utils.d.ts.map +0 -1
- package/esm/deps/jsr.io/@noble/hashes/1.8.0/src/utils.js +0 -280
- package/esm/src/clients/event.d.ts.map +0 -1
- package/esm/src/clients/public.d.ts.map +0 -1
- package/esm/src/clients/wallet.d.ts.map +0 -1
- package/esm/src/transports/websocket/_websocket_request_dispatcher.d.ts.map +0 -1
- package/esm/src/transports/websocket/_websocket_request_dispatcher.js +0 -191
- package/script/deps/jsr.io/@noble/hashes/1.8.0/src/_u64.d.ts +0 -55
- package/script/deps/jsr.io/@noble/hashes/1.8.0/src/_u64.d.ts.map +0 -1
- package/script/deps/jsr.io/@noble/hashes/1.8.0/src/_u64.js +0 -99
- package/script/deps/jsr.io/@noble/hashes/1.8.0/src/crypto.d.ts +0 -2
- package/script/deps/jsr.io/@noble/hashes/1.8.0/src/crypto.d.ts.map +0 -1
- package/script/deps/jsr.io/@noble/hashes/1.8.0/src/sha3.d.ts +0 -53
- package/script/deps/jsr.io/@noble/hashes/1.8.0/src/sha3.d.ts.map +0 -1
- package/script/deps/jsr.io/@noble/hashes/1.8.0/src/sha3.js +0 -309
- package/script/deps/jsr.io/@noble/hashes/1.8.0/src/utils.d.ts +0 -161
- package/script/deps/jsr.io/@noble/hashes/1.8.0/src/utils.d.ts.map +0 -1
- package/script/deps/jsr.io/@noble/hashes/1.8.0/src/utils.js +0 -322
- package/script/src/clients/event.d.ts.map +0 -1
- package/script/src/clients/public.d.ts.map +0 -1
- package/script/src/clients/wallet.d.ts.map +0 -1
- package/script/src/transports/websocket/_websocket_request_dispatcher.d.ts.map +0 -1
- package/script/src/transports/websocket/_websocket_request_dispatcher.js +0 -206
|
@@ -1,43 +1,29 @@
|
|
|
1
1
|
// deno-lint-ignore-file no-explicit-any
|
|
2
2
|
import { delay } from "../../../deps/jsr.io/@std/async/1.0.13/delay.js";
|
|
3
|
-
import { TransportError } from "
|
|
3
|
+
import { TransportError } from "../base.js";
|
|
4
4
|
/** Simple FIFO (First In, First Out) buffer implementation. */
|
|
5
5
|
class FIFOMessageBuffer {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
configurable: true,
|
|
10
|
-
writable: true,
|
|
11
|
-
value: []
|
|
12
|
-
});
|
|
6
|
+
queue = [];
|
|
7
|
+
push(data, signal) {
|
|
8
|
+
this.queue.push({ data, signal });
|
|
13
9
|
}
|
|
14
|
-
|
|
15
|
-
this.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
this.messages = [];
|
|
10
|
+
*[Symbol.iterator]() {
|
|
11
|
+
while (this.queue.length > 0) {
|
|
12
|
+
const { data, signal } = this.queue.shift();
|
|
13
|
+
if (signal?.aborted)
|
|
14
|
+
continue;
|
|
15
|
+
yield data;
|
|
16
|
+
}
|
|
22
17
|
}
|
|
23
18
|
}
|
|
24
19
|
/** Error thrown when reconnection problems occur. */
|
|
25
20
|
export class ReconnectingWebSocketError extends TransportError {
|
|
26
|
-
|
|
21
|
+
code;
|
|
22
|
+
constructor(code, cause) {
|
|
27
23
|
super(`Error when reconnecting WebSocket: ${code}`);
|
|
28
|
-
|
|
29
|
-
enumerable: true,
|
|
30
|
-
configurable: true,
|
|
31
|
-
writable: true,
|
|
32
|
-
value: code
|
|
33
|
-
});
|
|
34
|
-
Object.defineProperty(this, "originalError", {
|
|
35
|
-
enumerable: true,
|
|
36
|
-
configurable: true,
|
|
37
|
-
writable: true,
|
|
38
|
-
value: originalError
|
|
39
|
-
});
|
|
24
|
+
this.code = code;
|
|
40
25
|
this.name = "ReconnectingWebSocketError";
|
|
26
|
+
this.cause = cause;
|
|
41
27
|
}
|
|
42
28
|
}
|
|
43
29
|
/**
|
|
@@ -45,131 +31,13 @@ export class ReconnectingWebSocketError extends TransportError {
|
|
|
45
31
|
* Fully compatible with standard WebSocket API.
|
|
46
32
|
*/
|
|
47
33
|
export class ReconnectingWebSocket {
|
|
34
|
+
_socket;
|
|
35
|
+
_protocols;
|
|
36
|
+
_listeners = [];
|
|
37
|
+
_attempt = 0;
|
|
38
|
+
reconnectOptions;
|
|
39
|
+
reconnectAbortController = new AbortController();
|
|
48
40
|
constructor(url, protocols, options) {
|
|
49
|
-
Object.defineProperty(this, "_socket", {
|
|
50
|
-
enumerable: true,
|
|
51
|
-
configurable: true,
|
|
52
|
-
writable: true,
|
|
53
|
-
value: void 0
|
|
54
|
-
});
|
|
55
|
-
Object.defineProperty(this, "_protocols", {
|
|
56
|
-
enumerable: true,
|
|
57
|
-
configurable: true,
|
|
58
|
-
writable: true,
|
|
59
|
-
value: void 0
|
|
60
|
-
});
|
|
61
|
-
Object.defineProperty(this, "_listeners", {
|
|
62
|
-
enumerable: true,
|
|
63
|
-
configurable: true,
|
|
64
|
-
writable: true,
|
|
65
|
-
value: []
|
|
66
|
-
});
|
|
67
|
-
Object.defineProperty(this, "_attempt", {
|
|
68
|
-
enumerable: true,
|
|
69
|
-
configurable: true,
|
|
70
|
-
writable: true,
|
|
71
|
-
value: 0
|
|
72
|
-
});
|
|
73
|
-
Object.defineProperty(this, "reconnectOptions", {
|
|
74
|
-
enumerable: true,
|
|
75
|
-
configurable: true,
|
|
76
|
-
writable: true,
|
|
77
|
-
value: void 0
|
|
78
|
-
});
|
|
79
|
-
Object.defineProperty(this, "reconnectAbortController", {
|
|
80
|
-
enumerable: true,
|
|
81
|
-
configurable: true,
|
|
82
|
-
writable: true,
|
|
83
|
-
value: new AbortController()
|
|
84
|
-
});
|
|
85
|
-
Object.defineProperty(this, "_open", {
|
|
86
|
-
enumerable: true,
|
|
87
|
-
configurable: true,
|
|
88
|
-
writable: true,
|
|
89
|
-
value: () => {
|
|
90
|
-
// Reset the attempt counter
|
|
91
|
-
this._attempt = 0;
|
|
92
|
-
// Send all buffered messages
|
|
93
|
-
let message;
|
|
94
|
-
while ((message = this.reconnectOptions.messageBuffer.shift()) !== undefined) {
|
|
95
|
-
this._socket.send(message);
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
Object.defineProperty(this, "_close", {
|
|
100
|
-
enumerable: true,
|
|
101
|
-
configurable: true,
|
|
102
|
-
writable: true,
|
|
103
|
-
value: async (event) => {
|
|
104
|
-
try {
|
|
105
|
-
// If the event was triggered but the socket is not closing, ignore it
|
|
106
|
-
if (this._socket.readyState !== ReconnectingWebSocket.CLOSING &&
|
|
107
|
-
this._socket.readyState !== ReconnectingWebSocket.CLOSED)
|
|
108
|
-
return;
|
|
109
|
-
// If the instance is terminated, do not attempt to reconnect
|
|
110
|
-
if (this.reconnectAbortController.signal.aborted)
|
|
111
|
-
return;
|
|
112
|
-
// Check if reconnection should be attempted
|
|
113
|
-
if (++this._attempt > this.reconnectOptions.maxRetries) {
|
|
114
|
-
this._cleanup("RECONNECTION_LIMIT_REACHED");
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
const userDecision = await this.reconnectOptions.shouldReconnect(event, this.reconnectAbortController.signal);
|
|
118
|
-
if (this.reconnectAbortController.signal.aborted)
|
|
119
|
-
return;
|
|
120
|
-
if (!userDecision) {
|
|
121
|
-
this._cleanup("RECONNECTION_STOPPED_BY_USER");
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
// Delay before reconnecting
|
|
125
|
-
const reconnectDelay = typeof this.reconnectOptions.connectionDelay === "number"
|
|
126
|
-
? this.reconnectOptions.connectionDelay
|
|
127
|
-
: await this.reconnectOptions.connectionDelay(this._attempt, this.reconnectAbortController.signal);
|
|
128
|
-
if (this.reconnectAbortController.signal.aborted)
|
|
129
|
-
return;
|
|
130
|
-
await delay(reconnectDelay, { signal: this.reconnectAbortController.signal });
|
|
131
|
-
// Create a new WebSocket instance
|
|
132
|
-
const { onclose, onerror, onmessage, onopen } = this._socket;
|
|
133
|
-
this._socket = this._createSocket(this._socket.url, this._protocols);
|
|
134
|
-
// Reconnect all listeners
|
|
135
|
-
this._setupEventListeners();
|
|
136
|
-
this._listeners.forEach(({ type, listenerProxy, options }) => {
|
|
137
|
-
this._socket.addEventListener(type, listenerProxy, options);
|
|
138
|
-
});
|
|
139
|
-
this._socket.onclose = onclose;
|
|
140
|
-
this._socket.onerror = onerror;
|
|
141
|
-
this._socket.onmessage = onmessage;
|
|
142
|
-
this._socket.onopen = onopen;
|
|
143
|
-
}
|
|
144
|
-
catch (error) {
|
|
145
|
-
this._cleanup("UNKNOWN_ERROR", error);
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
Object.defineProperty(this, "CONNECTING", {
|
|
150
|
-
enumerable: true,
|
|
151
|
-
configurable: true,
|
|
152
|
-
writable: true,
|
|
153
|
-
value: 0
|
|
154
|
-
});
|
|
155
|
-
Object.defineProperty(this, "OPEN", {
|
|
156
|
-
enumerable: true,
|
|
157
|
-
configurable: true,
|
|
158
|
-
writable: true,
|
|
159
|
-
value: 1
|
|
160
|
-
});
|
|
161
|
-
Object.defineProperty(this, "CLOSING", {
|
|
162
|
-
enumerable: true,
|
|
163
|
-
configurable: true,
|
|
164
|
-
writable: true,
|
|
165
|
-
value: 2
|
|
166
|
-
});
|
|
167
|
-
Object.defineProperty(this, "CLOSED", {
|
|
168
|
-
enumerable: true,
|
|
169
|
-
configurable: true,
|
|
170
|
-
writable: true,
|
|
171
|
-
value: 3
|
|
172
|
-
});
|
|
173
41
|
this.reconnectOptions = {
|
|
174
42
|
maxRetries: options?.maxRetries ?? 3,
|
|
175
43
|
connectionTimeout: options?.connectionTimeout === undefined ? 10_000 : options.connectionTimeout,
|
|
@@ -207,10 +75,62 @@ export class ReconnectingWebSocket {
|
|
|
207
75
|
this._socket.addEventListener("open", this._open, { once: true });
|
|
208
76
|
this._socket.addEventListener("close", this._close, { once: true });
|
|
209
77
|
}
|
|
78
|
+
_open = () => {
|
|
79
|
+
// Reset the attempt counter
|
|
80
|
+
this._attempt = 0;
|
|
81
|
+
// Send all buffered messages
|
|
82
|
+
for (const message of this.reconnectOptions.messageBuffer) {
|
|
83
|
+
this._socket.send(message);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
_close = async (event) => {
|
|
87
|
+
try {
|
|
88
|
+
// If the event was triggered but the socket is not closing, ignore it
|
|
89
|
+
if (this._socket.readyState !== ReconnectingWebSocket.CLOSING &&
|
|
90
|
+
this._socket.readyState !== ReconnectingWebSocket.CLOSED)
|
|
91
|
+
return;
|
|
92
|
+
// If the instance is terminated, do not attempt to reconnect
|
|
93
|
+
if (this.reconnectAbortController.signal.aborted)
|
|
94
|
+
return;
|
|
95
|
+
// Check if reconnection should be attempted
|
|
96
|
+
if (++this._attempt > this.reconnectOptions.maxRetries) {
|
|
97
|
+
this._cleanup("RECONNECTION_LIMIT_REACHED");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const userDecision = await this.reconnectOptions.shouldReconnect(event, this.reconnectAbortController.signal);
|
|
101
|
+
if (this.reconnectAbortController.signal.aborted)
|
|
102
|
+
return;
|
|
103
|
+
if (!userDecision) {
|
|
104
|
+
this._cleanup("RECONNECTION_STOPPED_BY_USER");
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
// Delay before reconnecting
|
|
108
|
+
const reconnectDelay = typeof this.reconnectOptions.connectionDelay === "number"
|
|
109
|
+
? this.reconnectOptions.connectionDelay
|
|
110
|
+
: await this.reconnectOptions.connectionDelay(this._attempt, this.reconnectAbortController.signal);
|
|
111
|
+
if (this.reconnectAbortController.signal.aborted)
|
|
112
|
+
return;
|
|
113
|
+
await delay(reconnectDelay, { signal: this.reconnectAbortController.signal });
|
|
114
|
+
// Create a new WebSocket instance
|
|
115
|
+
const { onclose, onerror, onmessage, onopen } = this._socket;
|
|
116
|
+
this._socket = this._createSocket(this._socket.url, this._protocols);
|
|
117
|
+
// Reconnect all listeners
|
|
118
|
+
this._setupEventListeners();
|
|
119
|
+
this._listeners.forEach(({ type, listenerProxy, options }) => {
|
|
120
|
+
this._socket.addEventListener(type, listenerProxy, options);
|
|
121
|
+
});
|
|
122
|
+
this._socket.onclose = onclose;
|
|
123
|
+
this._socket.onerror = onerror;
|
|
124
|
+
this._socket.onmessage = onmessage;
|
|
125
|
+
this._socket.onopen = onopen;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
this._cleanup("UNKNOWN_ERROR", error);
|
|
129
|
+
}
|
|
130
|
+
};
|
|
210
131
|
/** Clean up internal resources. */
|
|
211
|
-
_cleanup(code,
|
|
212
|
-
this.reconnectAbortController.abort(new ReconnectingWebSocketError(code,
|
|
213
|
-
this.reconnectOptions.messageBuffer.clear();
|
|
132
|
+
_cleanup(code, cause) {
|
|
133
|
+
this.reconnectAbortController.abort(new ReconnectingWebSocketError(code, cause));
|
|
214
134
|
this._listeners = [];
|
|
215
135
|
this._socket.close();
|
|
216
136
|
}
|
|
@@ -236,6 +156,14 @@ export class ReconnectingWebSocket {
|
|
|
236
156
|
set binaryType(value) {
|
|
237
157
|
this._socket.binaryType = value;
|
|
238
158
|
}
|
|
159
|
+
CONNECTING = 0;
|
|
160
|
+
OPEN = 1;
|
|
161
|
+
CLOSING = 2;
|
|
162
|
+
CLOSED = 3;
|
|
163
|
+
static CONNECTING = 0;
|
|
164
|
+
static OPEN = 1;
|
|
165
|
+
static CLOSING = 2;
|
|
166
|
+
static CLOSED = 3;
|
|
239
167
|
get onclose() {
|
|
240
168
|
return this._socket.onclose;
|
|
241
169
|
}
|
|
@@ -269,11 +197,14 @@ export class ReconnectingWebSocket {
|
|
|
269
197
|
this._cleanup("USER_INITIATED_CLOSE");
|
|
270
198
|
}
|
|
271
199
|
/**
|
|
200
|
+
* @param signal - `AbortSignal` to cancel sending a message if it was in the buffer.
|
|
272
201
|
* @note If the connection is not open, the data will be buffered and sent when the connection is established.
|
|
273
202
|
*/
|
|
274
|
-
send(data) {
|
|
203
|
+
send(data, signal) {
|
|
204
|
+
if (signal?.aborted)
|
|
205
|
+
return;
|
|
275
206
|
if (this._socket.readyState !== ReconnectingWebSocket.OPEN && !this.reconnectAbortController.signal.aborted) {
|
|
276
|
-
this.reconnectOptions.messageBuffer.push(data);
|
|
207
|
+
this.reconnectOptions.messageBuffer.push(data, signal);
|
|
277
208
|
}
|
|
278
209
|
else {
|
|
279
210
|
this._socket.send(data);
|
|
@@ -337,30 +268,6 @@ export class ReconnectingWebSocket {
|
|
|
337
268
|
return this._socket.dispatchEvent(event);
|
|
338
269
|
}
|
|
339
270
|
}
|
|
340
|
-
Object.defineProperty(ReconnectingWebSocket, "CONNECTING", {
|
|
341
|
-
enumerable: true,
|
|
342
|
-
configurable: true,
|
|
343
|
-
writable: true,
|
|
344
|
-
value: 0
|
|
345
|
-
});
|
|
346
|
-
Object.defineProperty(ReconnectingWebSocket, "OPEN", {
|
|
347
|
-
enumerable: true,
|
|
348
|
-
configurable: true,
|
|
349
|
-
writable: true,
|
|
350
|
-
value: 1
|
|
351
|
-
});
|
|
352
|
-
Object.defineProperty(ReconnectingWebSocket, "CLOSING", {
|
|
353
|
-
enumerable: true,
|
|
354
|
-
configurable: true,
|
|
355
|
-
writable: true,
|
|
356
|
-
value: 2
|
|
357
|
-
});
|
|
358
|
-
Object.defineProperty(ReconnectingWebSocket, "CLOSED", {
|
|
359
|
-
enumerable: true,
|
|
360
|
-
configurable: true,
|
|
361
|
-
writable: true,
|
|
362
|
-
value: 3
|
|
363
|
-
});
|
|
364
271
|
/** Check if two event listeners are the same (just like EventTarget). */
|
|
365
272
|
function listenersMatch(a, b) {
|
|
366
273
|
// EventTarget only compares capture in options, even if one is an object and the other is boolean
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { TransportError } from "
|
|
1
|
+
import { TransportError } from "../base.js";
|
|
2
|
+
import type { ReconnectingWebSocket } from "./_reconnecting_websocket.js";
|
|
2
3
|
import type { HyperliquidEventTarget } from "./_hyperliquid_event_target.js";
|
|
3
4
|
/**
|
|
4
5
|
* Error thrown when a WebSocket request fails:
|
|
@@ -12,31 +13,28 @@ export declare class WebSocketRequestError extends TransportError {
|
|
|
12
13
|
* Manages WebSocket requests to the Hyperliquid API.
|
|
13
14
|
* Handles request creation, sending, and mapping responses to their corresponding requests.
|
|
14
15
|
*/
|
|
15
|
-
export declare class
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
export declare class WebSocketAsyncRequest {
|
|
17
|
+
protected socket: ReconnectingWebSocket;
|
|
18
|
+
protected lastId: number;
|
|
19
|
+
protected queue: {
|
|
20
|
+
id: number | string;
|
|
21
|
+
resolve: (value?: any) => void;
|
|
22
|
+
reject: (reason?: any) => void;
|
|
23
|
+
}[];
|
|
24
|
+
lastRequestTime: number;
|
|
21
25
|
/**
|
|
22
|
-
* Creates a new WebSocket request
|
|
26
|
+
* Creates a new WebSocket async request handler.
|
|
23
27
|
* @param socket - WebSocket connection instance for sending requests to the Hyperliquid WebSocket API
|
|
24
28
|
* @param hlEvents - Used to recognize Hyperliquid responses and match them with sent requests
|
|
25
29
|
*/
|
|
26
|
-
constructor(socket:
|
|
30
|
+
constructor(socket: ReconnectingWebSocket, hlEvents: HyperliquidEventTarget);
|
|
27
31
|
/**
|
|
28
32
|
* Sends a request to the Hyperliquid API.
|
|
29
|
-
* @param method - The method of websocket request.
|
|
30
|
-
* @param payload - The payload to send with the request.
|
|
31
|
-
* @param signal - An optional abort signal.
|
|
32
33
|
* @returns A promise that resolves with the parsed JSON response body.
|
|
33
34
|
*/
|
|
34
|
-
request(method: "
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
* @param value - A request object.
|
|
38
|
-
* @returns A stringified request.
|
|
39
|
-
*/
|
|
35
|
+
request(method: "ping", signal?: AbortSignal): Promise<void>;
|
|
36
|
+
request<T>(method: "post" | "subscribe" | "unsubscribe", payload: unknown, signal?: AbortSignal): Promise<T>;
|
|
37
|
+
/** Normalizes an object and then converts it to a string. */
|
|
40
38
|
static requestToId(value: unknown): string;
|
|
41
39
|
}
|
|
42
|
-
//# sourceMappingURL=
|
|
40
|
+
//# sourceMappingURL=_websocket_async_request.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_websocket_async_request.d.ts","sourceRoot":"","sources":["../../../../src/src/transports/websocket/_websocket_async_request.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC;AAC1E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAC;AAiB7E;;;;GAIG;AACH,qBAAa,qBAAsB,SAAQ,cAAc;gBACzC,OAAO,EAAE,MAAM;CAI9B;AAED;;;GAGG;AACH,qBAAa,qBAAqB;IAgBlB,SAAS,CAAC,MAAM,EAAE,qBAAqB;IAfnD,SAAS,CAAC,MAAM,EAAE,MAAM,CAAK;IAC7B,SAAS,CAAC,KAAK,EAAE;QACb,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;QAEpB,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;QAE/B,MAAM,EAAE,CAAC,MAAM,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC;KAClC,EAAE,CAAM;IACT,eAAe,EAAE,MAAM,CAAK;IAE5B;;;;OAIG;gBACmB,MAAM,EAAE,qBAAqB,EAAE,QAAQ,EAAE,sBAAsB;IAsFrF;;;OAGG;IACG,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAC5D,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,GAAG,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC;IA6ClH,6DAA6D;IAC7D,MAAM,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM;CAK7C"}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { TransportError } from "../base.js";
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown when a WebSocket request fails:
|
|
4
|
+
* - When the WebSocket connection is closed
|
|
5
|
+
* - When the server responds with an error message
|
|
6
|
+
*/
|
|
7
|
+
export class WebSocketRequestError extends TransportError {
|
|
8
|
+
constructor(message) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.name = "WebSocketRequestError";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Manages WebSocket requests to the Hyperliquid API.
|
|
15
|
+
* Handles request creation, sending, and mapping responses to their corresponding requests.
|
|
16
|
+
*/
|
|
17
|
+
export class WebSocketAsyncRequest {
|
|
18
|
+
socket;
|
|
19
|
+
lastId = 0;
|
|
20
|
+
queue = [];
|
|
21
|
+
lastRequestTime = 0;
|
|
22
|
+
/**
|
|
23
|
+
* Creates a new WebSocket async request handler.
|
|
24
|
+
* @param socket - WebSocket connection instance for sending requests to the Hyperliquid WebSocket API
|
|
25
|
+
* @param hlEvents - Used to recognize Hyperliquid responses and match them with sent requests
|
|
26
|
+
*/
|
|
27
|
+
constructor(socket, hlEvents) {
|
|
28
|
+
this.socket = socket;
|
|
29
|
+
// Monitor responses and match the pending request
|
|
30
|
+
hlEvents.addEventListener("subscriptionResponse", (event) => {
|
|
31
|
+
// Use a stringified request as an id
|
|
32
|
+
const id = WebSocketAsyncRequest.requestToId(event.detail);
|
|
33
|
+
this.queue.findLast((item) => item.id === id)?.resolve(event.detail);
|
|
34
|
+
});
|
|
35
|
+
hlEvents.addEventListener("post", (event) => {
|
|
36
|
+
const data = event.detail.response.type === "info"
|
|
37
|
+
? event.detail.response.payload.data
|
|
38
|
+
: event.detail.response.payload;
|
|
39
|
+
this.queue.findLast((item) => item.id === event.detail.id)?.resolve(data);
|
|
40
|
+
});
|
|
41
|
+
hlEvents.addEventListener("pong", () => {
|
|
42
|
+
this.queue.findLast((item) => item.id === "ping")?.resolve();
|
|
43
|
+
});
|
|
44
|
+
hlEvents.addEventListener("error", (event) => {
|
|
45
|
+
try {
|
|
46
|
+
// Error event doesn't have an id, use original request to match
|
|
47
|
+
const request = event.detail.match(/{.*}/)?.[0];
|
|
48
|
+
if (!request)
|
|
49
|
+
return;
|
|
50
|
+
const parsedRequest = JSON.parse(request);
|
|
51
|
+
// For `post` requests
|
|
52
|
+
if ("id" in parsedRequest && typeof parsedRequest.id === "number") {
|
|
53
|
+
this.queue.findLast((item) => item.id === parsedRequest.id)?.reject(new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// For `subscribe` and `unsubscribe` requests
|
|
57
|
+
if ("subscription" in parsedRequest &&
|
|
58
|
+
typeof parsedRequest.subscription === "object" && parsedRequest.subscription !== null) {
|
|
59
|
+
const id = WebSocketAsyncRequest.requestToId(parsedRequest);
|
|
60
|
+
this.queue.findLast((item) => item.id === id)?.reject(new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
// For already/invalid subscribed requests
|
|
64
|
+
if (event.detail.startsWith("Already subscribed") || event.detail.startsWith("Invalid subscription")) {
|
|
65
|
+
const id = WebSocketAsyncRequest.requestToId({
|
|
66
|
+
method: "subscribe",
|
|
67
|
+
subscription: parsedRequest,
|
|
68
|
+
});
|
|
69
|
+
this.queue.findLast((item) => item.id === id)?.reject(new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// For already unsubscribed requests
|
|
73
|
+
if (event.detail.startsWith("Already unsubscribed")) {
|
|
74
|
+
const id = WebSocketAsyncRequest.requestToId({
|
|
75
|
+
method: "unsubscribe",
|
|
76
|
+
subscription: parsedRequest,
|
|
77
|
+
});
|
|
78
|
+
this.queue.findLast((item) => item.id === id)?.reject(new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// For unknown requests
|
|
82
|
+
const id = WebSocketAsyncRequest.requestToId(parsedRequest);
|
|
83
|
+
this.queue.findLast((item) => item.id === id)?.reject(new WebSocketRequestError(`Cannot complete WebSocket request: ${event.detail}`));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Ignore JSON parsing errors
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
// Throws all pending requests if the connection is dropped
|
|
90
|
+
socket.addEventListener("close", () => {
|
|
91
|
+
this.queue.forEach(({ reject }) => {
|
|
92
|
+
reject(new WebSocketRequestError("Cannot complete WebSocket request: connection is closed"));
|
|
93
|
+
});
|
|
94
|
+
this.queue = [];
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async request(method, payload_or_signal, maybeSignal) {
|
|
98
|
+
const payload = payload_or_signal instanceof AbortSignal ? undefined : payload_or_signal;
|
|
99
|
+
const signal = payload_or_signal instanceof AbortSignal ? payload_or_signal : maybeSignal;
|
|
100
|
+
// Reject the request if the signal is aborted
|
|
101
|
+
if (signal?.aborted)
|
|
102
|
+
return Promise.reject(signal.reason);
|
|
103
|
+
// Create a request
|
|
104
|
+
let id;
|
|
105
|
+
let request;
|
|
106
|
+
if (method === "post") {
|
|
107
|
+
id = ++this.lastId;
|
|
108
|
+
request = { method, id, request: payload };
|
|
109
|
+
}
|
|
110
|
+
else if (method === "ping") {
|
|
111
|
+
id = "ping";
|
|
112
|
+
request = { method };
|
|
113
|
+
}
|
|
114
|
+
else {
|
|
115
|
+
request = { method, subscription: payload };
|
|
116
|
+
id = WebSocketAsyncRequest.requestToId(request);
|
|
117
|
+
}
|
|
118
|
+
// Send the request
|
|
119
|
+
this.socket.send(JSON.stringify(request), signal);
|
|
120
|
+
this.lastRequestTime = Date.now();
|
|
121
|
+
// Wait for a response
|
|
122
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
123
|
+
this.queue.push({ id, resolve, reject });
|
|
124
|
+
const onAbort = () => reject(signal?.reason);
|
|
125
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
126
|
+
return await promise.finally(() => {
|
|
127
|
+
const index = this.queue.findLastIndex((item) => item.id === id);
|
|
128
|
+
if (index !== -1)
|
|
129
|
+
this.queue.splice(index, 1);
|
|
130
|
+
signal?.removeEventListener("abort", onAbort);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
/** Normalizes an object and then converts it to a string. */
|
|
134
|
+
static requestToId(value) {
|
|
135
|
+
const lowerHex = containsUppercaseHex(value) ? deepLowerHex(value) : value;
|
|
136
|
+
const sorted = deepSortKeys(lowerHex);
|
|
137
|
+
return JSON.stringify(sorted); // Also removes undefined
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
/** Deeply converts hexadecimal strings in an object/array to lowercase. */
|
|
141
|
+
function deepLowerHex(obj) {
|
|
142
|
+
if (typeof obj === "string") {
|
|
143
|
+
return /^(0X[0-9a-fA-F]*|0x[0-9a-fA-F]*[A-F][0-9a-fA-F]*)$/.test(obj) ? obj.toLowerCase() : obj;
|
|
144
|
+
}
|
|
145
|
+
if (Array.isArray(obj)) {
|
|
146
|
+
return obj.map(deepLowerHex);
|
|
147
|
+
}
|
|
148
|
+
if (typeof obj === "object" && obj !== null) {
|
|
149
|
+
const result = {};
|
|
150
|
+
const entries = Object.entries(obj);
|
|
151
|
+
for (const [key, value] of entries) {
|
|
152
|
+
result[key] = deepLowerHex(value);
|
|
153
|
+
}
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
return obj;
|
|
157
|
+
}
|
|
158
|
+
/** Check if an object contains uppercase hexadecimal strings. */
|
|
159
|
+
function containsUppercaseHex(obj) {
|
|
160
|
+
const str = JSON.stringify(obj);
|
|
161
|
+
return /0X[0-9a-fA-F]*|0x[0-9a-fA-F]*[A-F][0-9a-fA-F]*/.test(str);
|
|
162
|
+
}
|
|
163
|
+
/** Deeply sort the keys of an object. */
|
|
164
|
+
function deepSortKeys(obj) {
|
|
165
|
+
if (typeof obj !== "object" || obj === null) {
|
|
166
|
+
return obj;
|
|
167
|
+
}
|
|
168
|
+
if (Array.isArray(obj)) {
|
|
169
|
+
return obj.map(deepSortKeys);
|
|
170
|
+
}
|
|
171
|
+
const result = {};
|
|
172
|
+
const keys = Object.keys(obj).sort();
|
|
173
|
+
for (const key of keys) {
|
|
174
|
+
result[key] = deepSortKeys(obj[key]);
|
|
175
|
+
}
|
|
176
|
+
return result;
|
|
177
|
+
}
|