@binance/common 2.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,1766 +1,1743 @@
1
- "use strict";
1
+ //#region rolldown:runtime
2
2
  var __create = Object.create;
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __getProtoOf = Object.getPrototypeOf;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
- var __export = (target, all) => {
9
- for (var name in all)
10
- __defProp(target, name, { get: all[name], enumerable: true });
11
- };
12
8
  var __copyProps = (to, from, except, desc) => {
13
- if (from && typeof from === "object" || typeof from === "function") {
14
- for (let key of __getOwnPropNames(from))
15
- if (!__hasOwnProp.call(to, key) && key !== except)
16
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
- }
18
- return to;
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
29
22
 
30
- // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
33
- ALGO_REST_API_PROD_URL: () => ALGO_REST_API_PROD_URL,
34
- BadRequestError: () => BadRequestError,
35
- C2C_REST_API_PROD_URL: () => C2C_REST_API_PROD_URL,
36
- CONVERT_REST_API_PROD_URL: () => CONVERT_REST_API_PROD_URL,
37
- COPY_TRADING_REST_API_PROD_URL: () => COPY_TRADING_REST_API_PROD_URL,
38
- CRYPTO_LOAN_REST_API_PROD_URL: () => CRYPTO_LOAN_REST_API_PROD_URL,
39
- ConfigurationRestAPI: () => ConfigurationRestAPI,
40
- ConfigurationWebsocketAPI: () => ConfigurationWebsocketAPI2,
41
- ConfigurationWebsocketStreams: () => ConfigurationWebsocketStreams,
42
- ConnectorClientError: () => ConnectorClientError,
43
- DERIVATIVES_TRADING_COIN_FUTURES_REST_API_PROD_URL: () => DERIVATIVES_TRADING_COIN_FUTURES_REST_API_PROD_URL,
44
- DERIVATIVES_TRADING_COIN_FUTURES_REST_API_TESTNET_URL: () => DERIVATIVES_TRADING_COIN_FUTURES_REST_API_TESTNET_URL,
45
- DERIVATIVES_TRADING_COIN_FUTURES_WS_API_PROD_URL: () => DERIVATIVES_TRADING_COIN_FUTURES_WS_API_PROD_URL,
46
- DERIVATIVES_TRADING_COIN_FUTURES_WS_API_TESTNET_URL: () => DERIVATIVES_TRADING_COIN_FUTURES_WS_API_TESTNET_URL,
47
- DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_PROD_URL: () => DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_PROD_URL,
48
- DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_TESTNET_URL: () => DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_TESTNET_URL,
49
- DERIVATIVES_TRADING_OPTIONS_REST_API_PROD_URL: () => DERIVATIVES_TRADING_OPTIONS_REST_API_PROD_URL,
50
- DERIVATIVES_TRADING_OPTIONS_WS_STREAMS_PROD_URL: () => DERIVATIVES_TRADING_OPTIONS_WS_STREAMS_PROD_URL,
51
- DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_REST_API_PROD_URL: () => DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_REST_API_PROD_URL,
52
- DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_WS_STREAMS_PROD_URL: () => DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_WS_STREAMS_PROD_URL,
53
- DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_PROD_URL: () => DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_PROD_URL,
54
- DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_TESTNET_URL: () => DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_TESTNET_URL,
55
- DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_PROD_URL: () => DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_PROD_URL,
56
- DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_TESTNET_URL: () => DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_TESTNET_URL,
57
- DERIVATIVES_TRADING_USDS_FUTURES_REST_API_PROD_URL: () => DERIVATIVES_TRADING_USDS_FUTURES_REST_API_PROD_URL,
58
- DERIVATIVES_TRADING_USDS_FUTURES_REST_API_TESTNET_URL: () => DERIVATIVES_TRADING_USDS_FUTURES_REST_API_TESTNET_URL,
59
- DERIVATIVES_TRADING_USDS_FUTURES_WS_API_PROD_URL: () => DERIVATIVES_TRADING_USDS_FUTURES_WS_API_PROD_URL,
60
- DERIVATIVES_TRADING_USDS_FUTURES_WS_API_TESTNET_URL: () => DERIVATIVES_TRADING_USDS_FUTURES_WS_API_TESTNET_URL,
61
- DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_PROD_URL: () => DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_PROD_URL,
62
- DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_TESTNET_URL: () => DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_TESTNET_URL,
63
- DUAL_INVESTMENT_REST_API_PROD_URL: () => DUAL_INVESTMENT_REST_API_PROD_URL,
64
- FIAT_REST_API_PROD_URL: () => FIAT_REST_API_PROD_URL,
65
- ForbiddenError: () => ForbiddenError,
66
- GIFT_CARD_REST_API_PROD_URL: () => GIFT_CARD_REST_API_PROD_URL,
67
- LogLevel: () => LogLevel,
68
- Logger: () => Logger,
69
- MARGIN_TRADING_REST_API_PROD_URL: () => MARGIN_TRADING_REST_API_PROD_URL,
70
- MARGIN_TRADING_RISK_WS_STREAMS_PROD_URL: () => MARGIN_TRADING_RISK_WS_STREAMS_PROD_URL,
71
- MARGIN_TRADING_WS_STREAMS_PROD_URL: () => MARGIN_TRADING_WS_STREAMS_PROD_URL,
72
- MINING_REST_API_PROD_URL: () => MINING_REST_API_PROD_URL,
73
- NFT_REST_API_PROD_URL: () => NFT_REST_API_PROD_URL,
74
- NetworkError: () => NetworkError,
75
- NotFoundError: () => NotFoundError,
76
- PAY_REST_API_PROD_URL: () => PAY_REST_API_PROD_URL,
77
- REBATE_REST_API_PROD_URL: () => REBATE_REST_API_PROD_URL,
78
- RateLimitBanError: () => RateLimitBanError,
79
- RequiredError: () => RequiredError,
80
- SIMPLE_EARN_REST_API_PROD_URL: () => SIMPLE_EARN_REST_API_PROD_URL,
81
- SPOT_REST_API_MARKET_URL: () => SPOT_REST_API_MARKET_URL,
82
- SPOT_REST_API_PROD_URL: () => SPOT_REST_API_PROD_URL,
83
- SPOT_REST_API_TESTNET_URL: () => SPOT_REST_API_TESTNET_URL,
84
- SPOT_WS_API_PROD_URL: () => SPOT_WS_API_PROD_URL,
85
- SPOT_WS_API_TESTNET_URL: () => SPOT_WS_API_TESTNET_URL,
86
- SPOT_WS_STREAMS_MARKET_URL: () => SPOT_WS_STREAMS_MARKET_URL,
87
- SPOT_WS_STREAMS_PROD_URL: () => SPOT_WS_STREAMS_PROD_URL,
88
- SPOT_WS_STREAMS_TESTNET_URL: () => SPOT_WS_STREAMS_TESTNET_URL,
89
- STAKING_REST_API_PROD_URL: () => STAKING_REST_API_PROD_URL,
90
- SUB_ACCOUNT_REST_API_PROD_URL: () => SUB_ACCOUNT_REST_API_PROD_URL,
91
- ServerError: () => ServerError,
92
- TimeUnit: () => TimeUnit,
93
- TooManyRequestsError: () => TooManyRequestsError,
94
- UnauthorizedError: () => UnauthorizedError,
95
- VIP_LOAN_REST_API_PROD_URL: () => VIP_LOAN_REST_API_PROD_URL,
96
- WALLET_REST_API_PROD_URL: () => WALLET_REST_API_PROD_URL,
97
- WebsocketAPIBase: () => WebsocketAPIBase,
98
- WebsocketCommon: () => WebsocketCommon,
99
- WebsocketEventEmitter: () => WebsocketEventEmitter,
100
- WebsocketStreamsBase: () => WebsocketStreamsBase,
101
- assertParamExists: () => assertParamExists,
102
- buildQueryString: () => buildQueryString,
103
- buildUserAgent: () => buildUserAgent,
104
- buildWebsocketAPIMessage: () => buildWebsocketAPIMessage,
105
- clearSignerCache: () => clearSignerCache,
106
- createStreamHandler: () => createStreamHandler,
107
- delay: () => delay,
108
- getSignature: () => getSignature,
109
- getTimestamp: () => getTimestamp,
110
- httpRequestFunction: () => httpRequestFunction,
111
- normalizeScientificNumbers: () => normalizeScientificNumbers,
112
- parseCustomHeaders: () => parseCustomHeaders,
113
- parseRateLimitHeaders: () => parseRateLimitHeaders,
114
- randomString: () => randomString,
115
- removeEmptyValue: () => removeEmptyValue,
116
- replaceWebsocketStreamsPlaceholders: () => replaceWebsocketStreamsPlaceholders,
117
- sanitizeHeaderValue: () => sanitizeHeaderValue,
118
- sendRequest: () => sendRequest,
119
- setSearchParams: () => setSearchParams,
120
- shouldRetryRequest: () => shouldRetryRequest,
121
- sortObject: () => sortObject,
122
- toPathString: () => toPathString,
123
- validateTimeUnit: () => validateTimeUnit
124
- });
125
- module.exports = __toCommonJS(index_exports);
23
+ //#endregion
24
+ let crypto = require("crypto");
25
+ crypto = __toESM(crypto);
26
+ let fs = require("fs");
27
+ fs = __toESM(fs);
28
+ let https = require("https");
29
+ https = __toESM(https);
30
+ let json_with_bigint = require("json-with-bigint");
31
+ let os = require("os");
32
+ let axios = require("axios");
33
+ axios = __toESM(axios);
34
+ let events = require("events");
35
+ let ws = require("ws");
36
+ ws = __toESM(ws);
126
37
 
127
- // src/utils.ts
128
- var import_crypto = __toESM(require("crypto"));
129
- var import_fs = __toESM(require("fs"));
130
- var import_https = __toESM(require("https"));
131
- var import_json_with_bigint = require("json-with-bigint");
132
- var import_os = require("os");
133
- var import_axios = __toESM(require("axios"));
134
- var signerCache = /* @__PURE__ */ new WeakMap();
38
+ //#region src/utils.ts
39
+ /**
40
+ * A weak cache for storing RequestSigner instances based on configuration parameters.
41
+ *
42
+ * @remarks
43
+ * Uses a WeakMap to cache and reuse RequestSigner instances for configurations with
44
+ * apiSecret, privateKey, and privateKeyPassphrase, allowing efficient memory management.
45
+ */
46
+ let signerCache = /* @__PURE__ */ new WeakMap();
47
+ /**
48
+ * Represents a request signer for generating signatures using HMAC-SHA256 or asymmetric key signing.
49
+ *
50
+ * Supports two signing methods:
51
+ * 1. HMAC-SHA256 using an API secret
52
+ * 2. Asymmetric signing using RSA or ED25519 private keys
53
+ *
54
+ * @throws {Error} If neither API secret nor private key is provided, or if the private key is invalid
55
+ */
135
56
  var RequestSigner = class {
136
- constructor(configuration) {
137
- if (configuration.apiSecret && !configuration.privateKey) {
138
- this.apiSecret = configuration.apiSecret;
139
- return;
140
- }
141
- if (configuration.privateKey) {
142
- let privateKey = configuration.privateKey;
143
- if (typeof privateKey === "string" && import_fs.default.existsSync(privateKey)) {
144
- privateKey = import_fs.default.readFileSync(privateKey, "utf-8");
145
- }
146
- const keyInput = { key: privateKey };
147
- if (configuration.privateKeyPassphrase && typeof configuration.privateKeyPassphrase === "string") {
148
- keyInput.passphrase = configuration.privateKeyPassphrase;
149
- }
150
- try {
151
- this.keyObject = import_crypto.default.createPrivateKey(keyInput);
152
- this.keyType = this.keyObject.asymmetricKeyType;
153
- } catch {
154
- throw new Error(
155
- "Invalid private key. Please provide a valid RSA or ED25519 private key."
156
- );
157
- }
158
- return;
159
- }
160
- throw new Error("Either 'apiSecret' or 'privateKey' must be provided for signed requests.");
161
- }
162
- sign(queryParams) {
163
- const params = buildQueryString(queryParams);
164
- if (this.apiSecret)
165
- return import_crypto.default.createHmac("sha256", this.apiSecret).update(params).digest("hex");
166
- if (this.keyObject && this.keyType) {
167
- const data = Buffer.from(params);
168
- if (this.keyType === "rsa")
169
- return import_crypto.default.sign("RSA-SHA256", data, this.keyObject).toString("base64");
170
- if (this.keyType === "ed25519")
171
- return import_crypto.default.sign(null, data, this.keyObject).toString("base64");
172
- throw new Error("Unsupported private key type. Must be RSA or ED25519.");
173
- }
174
- throw new Error("Signer is not properly initialized.");
175
- }
57
+ constructor(configuration) {
58
+ if (configuration.apiSecret && !configuration.privateKey) {
59
+ this.apiSecret = configuration.apiSecret;
60
+ return;
61
+ }
62
+ if (configuration.privateKey) {
63
+ let privateKey = configuration.privateKey;
64
+ if (typeof privateKey === "string" && fs.default.existsSync(privateKey)) privateKey = fs.default.readFileSync(privateKey, "utf-8");
65
+ const keyInput = { key: privateKey };
66
+ if (configuration.privateKeyPassphrase && typeof configuration.privateKeyPassphrase === "string") keyInput.passphrase = configuration.privateKeyPassphrase;
67
+ try {
68
+ this.keyObject = crypto.default.createPrivateKey(keyInput);
69
+ this.keyType = this.keyObject.asymmetricKeyType;
70
+ } catch {
71
+ throw new Error("Invalid private key. Please provide a valid RSA or ED25519 private key.");
72
+ }
73
+ return;
74
+ }
75
+ throw new Error("Either 'apiSecret' or 'privateKey' must be provided for signed requests.");
76
+ }
77
+ sign(queryParams) {
78
+ const params = buildQueryString(queryParams);
79
+ if (this.apiSecret) return crypto.default.createHmac("sha256", this.apiSecret).update(params).digest("hex");
80
+ if (this.keyObject && this.keyType) {
81
+ const data = Buffer.from(params);
82
+ if (this.keyType === "rsa") return crypto.default.sign("RSA-SHA256", data, this.keyObject).toString("base64");
83
+ if (this.keyType === "ed25519") return crypto.default.sign(null, data, this.keyObject).toString("base64");
84
+ throw new Error("Unsupported private key type. Must be RSA or ED25519.");
85
+ }
86
+ throw new Error("Signer is not properly initialized.");
87
+ }
176
88
  };
177
- var clearSignerCache = function() {
178
- signerCache = /* @__PURE__ */ new WeakMap();
89
+ /**
90
+ * Resets the signer cache to a new empty WeakMap.
91
+ *
92
+ * This function clears the existing signer cache, creating a fresh WeakMap
93
+ * to store RequestSigner instances associated with configuration objects.
94
+ */
95
+ const clearSignerCache = function() {
96
+ signerCache = /* @__PURE__ */ new WeakMap();
179
97
  };
98
+ /**
99
+ * Serializes a value to a string representation.
100
+ *
101
+ * - If the value is `null` or `undefined`, returns an empty string.
102
+ * - If the value is an array or a non-null object, returns its JSON string representation.
103
+ * - Otherwise, converts the value to a string using `String()`.
104
+ *
105
+ * @param value - The value to serialize.
106
+ * @returns The serialized string representation of the value.
107
+ */
180
108
  function serializeValue(value) {
181
- if (value === null || value === void 0) return "";
182
- if (Array.isArray(value) || typeof value === "object" && value !== null)
183
- return JSON.stringify(value);
184
- return String(value);
109
+ if (value === null || value === void 0) return "";
110
+ if (Array.isArray(value) || typeof value === "object" && value !== null) return JSON.stringify(value);
111
+ return String(value);
185
112
  }
113
+ /**
114
+ * Builds a URL query string from the given parameters object.
115
+ *
116
+ * Iterates over the key-value pairs in the `params` object, serializes each value,
117
+ * and encodes it for use in a URL. Only keys with non-null and non-undefined values
118
+ * are included in the resulting query string.
119
+ *
120
+ * @param params - An object containing key-value pairs to be serialized into a query string.
121
+ * @returns A URL-encoded query string representing the provided parameters.
122
+ */
186
123
  function buildQueryString(params) {
187
- if (!params) return "";
188
- const pairs = [];
189
- Object.entries(params).forEach(([key, value]) => {
190
- if (value !== null && value !== void 0) {
191
- const serializedValue = serializeValue(value);
192
- pairs.push(`${key}=${encodeURIComponent(serializedValue)}`);
193
- }
194
- });
195
- return pairs.join("&");
124
+ if (!params) return "";
125
+ const pairs = [];
126
+ Object.entries(params).forEach(([key, value]) => {
127
+ if (value !== null && value !== void 0) {
128
+ const serializedValue = serializeValue(value);
129
+ pairs.push(`${key}=${encodeURIComponent(serializedValue)}`);
130
+ }
131
+ });
132
+ return pairs.join("&");
196
133
  }
134
+ /**
135
+ * Generates a random string of 16 hexadecimal characters.
136
+ *
137
+ * @returns A random string of 16 hexadecimal characters.
138
+ */
197
139
  function randomString() {
198
- return import_crypto.default.randomBytes(16).toString("hex");
140
+ return crypto.default.randomBytes(16).toString("hex");
199
141
  }
142
+ /**
143
+ * Validates the provided time unit string and returns it if it is either 'MILLISECOND' or 'MICROSECOND'.
144
+ *
145
+ * @param timeUnit - The time unit string to be validated.
146
+ * @returns The validated time unit string, or `undefined` if the input is falsy.
147
+ * @throws {Error} If the time unit is not 'MILLISECOND' or 'MICROSECOND'.
148
+ */
200
149
  function validateTimeUnit(timeUnit) {
201
- if (!timeUnit) {
202
- return;
203
- } else if (timeUnit !== TimeUnit.MILLISECOND && timeUnit !== TimeUnit.MICROSECOND && timeUnit !== TimeUnit.millisecond && timeUnit !== TimeUnit.microsecond) {
204
- throw new Error("timeUnit must be either 'MILLISECOND' or 'MICROSECOND'");
205
- }
206
- return timeUnit;
150
+ if (!timeUnit) return;
151
+ else if (timeUnit !== TimeUnit.MILLISECOND && timeUnit !== TimeUnit.MICROSECOND && timeUnit !== TimeUnit.millisecond && timeUnit !== TimeUnit.microsecond) throw new Error("timeUnit must be either 'MILLISECOND' or 'MICROSECOND'");
152
+ return timeUnit;
207
153
  }
154
+ /**
155
+ * Delays the execution of the current function for the specified number of milliseconds.
156
+ *
157
+ * @param ms - The number of milliseconds to delay the function.
158
+ * @returns A Promise that resolves after the specified delay.
159
+ */
208
160
  async function delay(ms) {
209
- return new Promise((resolve) => setTimeout(resolve, ms));
161
+ return new Promise((resolve) => setTimeout(resolve, ms));
210
162
  }
163
+ /**
164
+ * Generates the current timestamp in milliseconds.
165
+ *
166
+ * @returns The current timestamp in milliseconds.
167
+ */
211
168
  function getTimestamp() {
212
- return Date.now();
169
+ return Date.now();
213
170
  }
214
- var getSignature = function(configuration, queryParams) {
215
- let signer = signerCache.get(configuration);
216
- if (!signer) {
217
- signer = new RequestSigner(configuration);
218
- signerCache.set(configuration, signer);
219
- }
220
- return signer.sign(queryParams);
171
+ /**
172
+ * Generates a signature for the given configuration and query parameters using a cached request signer.
173
+ *
174
+ * @param configuration - Configuration object containing API secret, private key, and optional passphrase.
175
+ * @param queryParams - The query parameters to be signed.
176
+ * @returns A string representing the generated signature.
177
+ */
178
+ const getSignature = function(configuration, queryParams) {
179
+ let signer = signerCache.get(configuration);
180
+ if (!signer) {
181
+ signer = new RequestSigner(configuration);
182
+ signerCache.set(configuration, signer);
183
+ }
184
+ return signer.sign(queryParams);
221
185
  };
222
- var assertParamExists = function(functionName, paramName, paramValue) {
223
- if (paramValue === null || paramValue === void 0) {
224
- throw new RequiredError(
225
- paramName,
226
- `Required parameter ${paramName} was null or undefined when calling ${functionName}.`
227
- );
228
- }
186
+ /**
187
+ * Asserts that a function parameter exists and is not null or undefined.
188
+ *
189
+ * @param functionName - The name of the function that the parameter belongs to.
190
+ * @param paramName - The name of the parameter to check.
191
+ * @param paramValue - The value of the parameter to check.
192
+ * @throws {RequiredError} If the parameter is null or undefined.
193
+ */
194
+ const assertParamExists = function(functionName, paramName, paramValue) {
195
+ if (paramValue === null || paramValue === void 0) throw new RequiredError(paramName, `Required parameter ${paramName} was null or undefined when calling ${functionName}.`);
229
196
  };
197
+ /**
198
+ * Sets the search parameters of a given URL object based on the provided key-value pairs.
199
+ * Only parameters with non-null and non-undefined values are included.
200
+ * Values are serialized using the `serializeValue` function before being set.
201
+ *
202
+ * @param url - The URL object whose search parameters will be updated.
203
+ * @param params - An object containing key-value pairs to be set as search parameters.
204
+ */
230
205
  function setSearchParams(url, params) {
231
- const searchParams = new URLSearchParams();
232
- Object.entries(params).forEach(([key, value]) => {
233
- if (value !== null && value !== void 0) {
234
- const serializedValue = serializeValue(value);
235
- searchParams.set(key, serializedValue);
236
- }
237
- });
238
- url.search = searchParams.toString();
206
+ const searchParams = new URLSearchParams();
207
+ Object.entries(params).forEach(([key, value]) => {
208
+ if (value !== null && value !== void 0) {
209
+ const serializedValue = serializeValue(value);
210
+ searchParams.set(key, serializedValue);
211
+ }
212
+ });
213
+ url.search = searchParams.toString();
239
214
  }
240
- var toPathString = function(url) {
241
- return url.pathname + url.search + url.hash;
215
+ /**
216
+ * Converts a URL object to a full path string, including pathname, search parameters, and hash.
217
+ *
218
+ * @param url The URL object to convert to a path string.
219
+ * @returns A complete path string representation of the URL.
220
+ */
221
+ const toPathString = function(url) {
222
+ return url.pathname + url.search + url.hash;
242
223
  };
224
+ /**
225
+ * Normalizes scientific notation numbers in an object or array to a fixed number of decimal places.
226
+ *
227
+ * This function recursively processes objects, arrays, and numbers, converting scientific notation
228
+ * to a fixed decimal representation. Non-numeric values are left unchanged.
229
+ *
230
+ * @template T The type of the input object or value
231
+ * @param obj The object, array, or value to normalize
232
+ * @returns A new object or value with scientific notation numbers normalized
233
+ */
243
234
  function normalizeScientificNumbers(obj) {
244
- if (Array.isArray(obj)) {
245
- return obj.map((item) => normalizeScientificNumbers(item));
246
- } else if (typeof obj === "object" && obj !== null) {
247
- const result = {};
248
- for (const key of Object.keys(obj)) {
249
- result[key] = normalizeScientificNumbers(obj[key]);
250
- }
251
- return result;
252
- } else if (typeof obj === "number") {
253
- if (!Number.isFinite(obj)) return obj;
254
- const abs = Math.abs(obj);
255
- if (abs === 0 || abs >= 1e-6 && abs < 1e21) return String(obj);
256
- const isNegative = obj < 0;
257
- const [rawMantissa, rawExponent] = abs.toExponential().split("e");
258
- const exponent = +rawExponent;
259
- const digits = rawMantissa.replace(".", "");
260
- if (exponent < 0) {
261
- const zeros = "0".repeat(Math.abs(exponent) - 1);
262
- return (isNegative ? "-" : "") + "0." + zeros + digits;
263
- } else {
264
- const pad = exponent - (digits.length - 1);
265
- if (pad >= 0) {
266
- return (isNegative ? "-" : "") + digits + "0".repeat(pad);
267
- } else {
268
- const point = digits.length + pad;
269
- return (isNegative ? "-" : "") + digits.slice(0, point) + "." + digits.slice(point);
270
- }
271
- }
272
- } else {
273
- return obj;
274
- }
235
+ if (Array.isArray(obj)) return obj.map((item) => normalizeScientificNumbers(item));
236
+ else if (typeof obj === "object" && obj !== null) {
237
+ const result = {};
238
+ for (const key of Object.keys(obj)) result[key] = normalizeScientificNumbers(obj[key]);
239
+ return result;
240
+ } else if (typeof obj === "number") {
241
+ if (!Number.isFinite(obj)) return obj;
242
+ const abs = Math.abs(obj);
243
+ if (abs === 0 || abs >= 1e-6 && abs < 1e21) return String(obj);
244
+ const isNegative = obj < 0;
245
+ const [rawMantissa, rawExponent] = abs.toExponential().split("e");
246
+ const exponent = +rawExponent;
247
+ const digits = rawMantissa.replace(".", "");
248
+ if (exponent < 0) {
249
+ const zeros = "0".repeat(Math.abs(exponent) - 1);
250
+ return (isNegative ? "-" : "") + "0." + zeros + digits;
251
+ } else {
252
+ const pad = exponent - (digits.length - 1);
253
+ if (pad >= 0) return (isNegative ? "-" : "") + digits + "0".repeat(pad);
254
+ else {
255
+ const point = digits.length + pad;
256
+ return (isNegative ? "-" : "") + digits.slice(0, point) + "." + digits.slice(point);
257
+ }
258
+ }
259
+ } else return obj;
275
260
  }
276
- var shouldRetryRequest = function(error, method, retriesLeft) {
277
- const isRetriableMethod = ["GET", "DELETE"].includes(method ?? "");
278
- const isRetriableStatus = [500, 502, 503, 504].includes(
279
- error?.response?.status ?? 0
280
- );
281
- return (retriesLeft ?? 0) > 0 && isRetriableMethod && (isRetriableStatus || !error?.response);
261
+ /**
262
+ * Determines whether a request should be retried based on the provided error.
263
+ *
264
+ * This function checks the HTTP method, response status, and number of retries left to determine if a request should be retried.
265
+ *
266
+ * @param error The error object to check.
267
+ * @param method The HTTP method of the request (optional).
268
+ * @param retriesLeft The number of retries left (optional).
269
+ * @returns `true` if the request should be retried, `false` otherwise.
270
+ */
271
+ const shouldRetryRequest = function(error, method, retriesLeft) {
272
+ const isRetriableMethod = ["GET", "DELETE"].includes(method ?? "");
273
+ const isRetriableStatus = [
274
+ 500,
275
+ 502,
276
+ 503,
277
+ 504
278
+ ].includes(error?.response?.status ?? 0);
279
+ return (retriesLeft ?? 0) > 0 && isRetriableMethod && (isRetriableStatus || !error?.response);
282
280
  };
283
- var httpRequestFunction = async function(axiosArgs, configuration) {
284
- const axiosRequestArgs = {
285
- ...axiosArgs.options,
286
- url: (import_axios.default.defaults?.baseURL ? "" : configuration?.basePath ?? "") + axiosArgs.url
287
- };
288
- if (configuration?.keepAlive && !configuration?.baseOptions?.httpsAgent)
289
- axiosRequestArgs.httpsAgent = new import_https.default.Agent({ keepAlive: true });
290
- if (configuration?.compression)
291
- axiosRequestArgs.headers = {
292
- ...axiosRequestArgs.headers,
293
- "Accept-Encoding": "gzip, deflate, br"
294
- };
295
- const retries = configuration?.retries ?? 0;
296
- const backoff = configuration?.backoff ?? 0;
297
- let attempt = 0;
298
- let lastError;
299
- while (attempt <= retries) {
300
- try {
301
- const response = await import_axios.default.request({
302
- ...axiosRequestArgs,
303
- responseType: "text"
304
- });
305
- const rateLimits = parseRateLimitHeaders(response.headers);
306
- return {
307
- data: async () => {
308
- try {
309
- return (0, import_json_with_bigint.JSONParse)(response.data);
310
- } catch (err) {
311
- throw new Error(`Failed to parse JSON response: ${err}`);
312
- }
313
- },
314
- status: response.status,
315
- headers: response.headers,
316
- rateLimits
317
- };
318
- } catch (error) {
319
- attempt++;
320
- const axiosError = error;
321
- if (shouldRetryRequest(
322
- axiosError,
323
- axiosRequestArgs?.method?.toUpperCase(),
324
- retries - attempt
325
- )) {
326
- await delay(backoff * attempt);
327
- } else {
328
- if (axiosError.response && axiosError.response.status) {
329
- const status = axiosError.response?.status;
330
- const responseData = axiosError.response.data;
331
- let data = {};
332
- if (responseData && responseData !== null) {
333
- if (typeof responseData === "string" && responseData !== "")
334
- try {
335
- data = (0, import_json_with_bigint.JSONParse)(responseData);
336
- } catch {
337
- data = {};
338
- }
339
- else if (typeof responseData === "object")
340
- data = responseData;
341
- }
342
- const errorMsg = data.msg;
343
- switch (status) {
344
- case 400:
345
- throw new BadRequestError(errorMsg);
346
- case 401:
347
- throw new UnauthorizedError(errorMsg);
348
- case 403:
349
- throw new ForbiddenError(errorMsg);
350
- case 404:
351
- throw new NotFoundError(errorMsg);
352
- case 418:
353
- throw new RateLimitBanError(errorMsg);
354
- case 429:
355
- throw new TooManyRequestsError(errorMsg);
356
- default:
357
- if (status >= 500 && status < 600)
358
- throw new ServerError(`Server error: ${status}`, status);
359
- throw new ConnectorClientError(errorMsg);
360
- }
361
- } else {
362
- if (retries > 0 && attempt >= retries)
363
- lastError = new Error(`Request failed after ${retries} retries`);
364
- else lastError = new NetworkError("Network error or request timeout.");
365
- break;
366
- }
367
- }
368
- }
369
- }
370
- throw lastError;
281
+ /**
282
+ * Performs an HTTP request using the provided Axios instance and configuration.
283
+ *
284
+ * This function handles retries, rate limit handling, and error handling for the HTTP request.
285
+ *
286
+ * @param axiosArgs The request arguments to be passed to Axios.
287
+ * @param configuration The configuration options for the request.
288
+ * @returns A Promise that resolves to the API response, including the data and rate limit headers.
289
+ */
290
+ const httpRequestFunction = async function(axiosArgs, configuration) {
291
+ const axiosRequestArgs = {
292
+ ...axiosArgs.options,
293
+ url: (axios.default.defaults?.baseURL ? "" : configuration?.basePath ?? "") + axiosArgs.url
294
+ };
295
+ if (configuration?.keepAlive && !configuration?.baseOptions?.httpsAgent) axiosRequestArgs.httpsAgent = new https.default.Agent({ keepAlive: true });
296
+ if (configuration?.compression) axiosRequestArgs.headers = {
297
+ ...axiosRequestArgs.headers,
298
+ "Accept-Encoding": "gzip, deflate, br"
299
+ };
300
+ const retries = configuration?.retries ?? 0;
301
+ const backoff = configuration?.backoff ?? 0;
302
+ let attempt = 0;
303
+ let lastError;
304
+ while (attempt <= retries) try {
305
+ const response = await axios.default.request({
306
+ ...axiosRequestArgs,
307
+ responseType: "text"
308
+ });
309
+ const rateLimits = parseRateLimitHeaders(response.headers);
310
+ return {
311
+ data: async () => {
312
+ try {
313
+ return (0, json_with_bigint.JSONParse)(response.data);
314
+ } catch (err) {
315
+ throw new Error(`Failed to parse JSON response: ${err}`);
316
+ }
317
+ },
318
+ status: response.status,
319
+ headers: response.headers,
320
+ rateLimits
321
+ };
322
+ } catch (error) {
323
+ attempt++;
324
+ const axiosError = error;
325
+ if (shouldRetryRequest(axiosError, axiosRequestArgs?.method?.toUpperCase(), retries - attempt)) await delay(backoff * attempt);
326
+ else if (axiosError.response && axiosError.response.status) {
327
+ const status = axiosError.response?.status;
328
+ const responseData = axiosError.response.data;
329
+ let data = {};
330
+ if (responseData && responseData !== null) {
331
+ if (typeof responseData === "string" && responseData !== "") try {
332
+ data = (0, json_with_bigint.JSONParse)(responseData);
333
+ } catch {
334
+ data = {};
335
+ }
336
+ else if (typeof responseData === "object") data = responseData;
337
+ }
338
+ const errorMsg = data.msg;
339
+ switch (status) {
340
+ case 400: throw new BadRequestError(errorMsg);
341
+ case 401: throw new UnauthorizedError(errorMsg);
342
+ case 403: throw new ForbiddenError(errorMsg);
343
+ case 404: throw new NotFoundError(errorMsg);
344
+ case 418: throw new RateLimitBanError(errorMsg);
345
+ case 429: throw new TooManyRequestsError(errorMsg);
346
+ default:
347
+ if (status >= 500 && status < 600) throw new ServerError(`Server error: ${status}`, status);
348
+ throw new ConnectorClientError(errorMsg);
349
+ }
350
+ } else {
351
+ if (retries > 0 && attempt >= retries) lastError = /* @__PURE__ */ new Error(`Request failed after ${retries} retries`);
352
+ else lastError = new NetworkError("Network error or request timeout.");
353
+ break;
354
+ }
355
+ }
356
+ throw lastError;
371
357
  };
372
- var parseRateLimitHeaders = function(headers) {
373
- const rateLimits = [];
374
- const parseIntervalDetails = (key) => {
375
- const match = key.match(/x-mbx-used-weight-(\d+)([smhd])|x-mbx-order-count-(\d+)([smhd])/i);
376
- if (!match) return null;
377
- const intervalNum = parseInt(match[1] || match[3], 10);
378
- const intervalLetter = (match[2] || match[4])?.toUpperCase();
379
- let interval;
380
- switch (intervalLetter) {
381
- case "S":
382
- interval = "SECOND";
383
- break;
384
- case "M":
385
- interval = "MINUTE";
386
- break;
387
- case "H":
388
- interval = "HOUR";
389
- break;
390
- case "D":
391
- interval = "DAY";
392
- break;
393
- default:
394
- return null;
395
- }
396
- return { interval, intervalNum };
397
- };
398
- for (const [key, value] of Object.entries(headers)) {
399
- const normalizedKey = key.toLowerCase();
400
- if (value === void 0) continue;
401
- if (normalizedKey.startsWith("x-mbx-used-weight-")) {
402
- const details = parseIntervalDetails(normalizedKey);
403
- if (details) {
404
- rateLimits.push({
405
- rateLimitType: "REQUEST_WEIGHT",
406
- interval: details.interval,
407
- intervalNum: details.intervalNum,
408
- count: parseInt(value, 10)
409
- });
410
- }
411
- } else if (normalizedKey.startsWith("x-mbx-order-count-")) {
412
- const details = parseIntervalDetails(normalizedKey);
413
- if (details) {
414
- rateLimits.push({
415
- rateLimitType: "ORDERS",
416
- interval: details.interval,
417
- intervalNum: details.intervalNum,
418
- count: parseInt(value, 10)
419
- });
420
- }
421
- }
422
- }
423
- if (headers["retry-after"]) {
424
- const retryAfter = parseInt(headers["retry-after"], 10);
425
- for (const limit of rateLimits) {
426
- limit.retryAfter = retryAfter;
427
- }
428
- }
429
- return rateLimits;
358
+ /**
359
+ * Parses the rate limit headers from the Axios response headers and returns an array of `RestApiRateLimit` objects.
360
+ *
361
+ * @param headers - The Axios response headers.
362
+ * @returns An array of `RestApiRateLimit` objects containing the parsed rate limit information.
363
+ */
364
+ const parseRateLimitHeaders = function(headers) {
365
+ const rateLimits = [];
366
+ const parseIntervalDetails = (key) => {
367
+ const match = key.match(/x-mbx-used-weight-(\d+)([smhd])|x-mbx-order-count-(\d+)([smhd])/i);
368
+ if (!match) return null;
369
+ const intervalNum = parseInt(match[1] || match[3], 10);
370
+ const intervalLetter = (match[2] || match[4])?.toUpperCase();
371
+ let interval;
372
+ switch (intervalLetter) {
373
+ case "S":
374
+ interval = "SECOND";
375
+ break;
376
+ case "M":
377
+ interval = "MINUTE";
378
+ break;
379
+ case "H":
380
+ interval = "HOUR";
381
+ break;
382
+ case "D":
383
+ interval = "DAY";
384
+ break;
385
+ default: return null;
386
+ }
387
+ return {
388
+ interval,
389
+ intervalNum
390
+ };
391
+ };
392
+ for (const [key, value] of Object.entries(headers)) {
393
+ const normalizedKey = key.toLowerCase();
394
+ if (value === void 0) continue;
395
+ if (normalizedKey.startsWith("x-mbx-used-weight-")) {
396
+ const details = parseIntervalDetails(normalizedKey);
397
+ if (details) rateLimits.push({
398
+ rateLimitType: "REQUEST_WEIGHT",
399
+ interval: details.interval,
400
+ intervalNum: details.intervalNum,
401
+ count: parseInt(value, 10)
402
+ });
403
+ } else if (normalizedKey.startsWith("x-mbx-order-count-")) {
404
+ const details = parseIntervalDetails(normalizedKey);
405
+ if (details) rateLimits.push({
406
+ rateLimitType: "ORDERS",
407
+ interval: details.interval,
408
+ intervalNum: details.intervalNum,
409
+ count: parseInt(value, 10)
410
+ });
411
+ }
412
+ }
413
+ if (headers["retry-after"]) {
414
+ const retryAfter = parseInt(headers["retry-after"], 10);
415
+ for (const limit of rateLimits) limit.retryAfter = retryAfter;
416
+ }
417
+ return rateLimits;
430
418
  };
431
- var sendRequest = function(configuration, endpoint, method, params = {}, timeUnit, options = {}) {
432
- const localVarUrlObj = new URL(endpoint, configuration?.basePath);
433
- const localVarRequestOptions = {
434
- method,
435
- ...configuration?.baseOptions
436
- };
437
- const localVarQueryParameter = { ...normalizeScientificNumbers(params) };
438
- if (options.isSigned) {
439
- const timestamp = getTimestamp();
440
- localVarQueryParameter["timestamp"] = timestamp;
441
- const signature = getSignature(configuration, localVarQueryParameter);
442
- if (signature) {
443
- localVarQueryParameter["signature"] = signature;
444
- }
445
- }
446
- setSearchParams(localVarUrlObj, localVarQueryParameter);
447
- if (timeUnit && localVarRequestOptions.headers) {
448
- const _timeUnit = validateTimeUnit(timeUnit);
449
- localVarRequestOptions.headers = {
450
- ...localVarRequestOptions.headers,
451
- "X-MBX-TIME-UNIT": _timeUnit
452
- };
453
- }
454
- return httpRequestFunction(
455
- {
456
- url: toPathString(localVarUrlObj),
457
- options: localVarRequestOptions
458
- },
459
- configuration
460
- );
419
+ /**
420
+ * Generic function to send a request with optional API key and signature.
421
+ * @param endpoint - The API endpoint to call.
422
+ * @param method - HTTP method to use (GET, POST, DELETE, etc.).
423
+ * @param params - Query parameters for the request.
424
+ * @param timeUnit - The time unit for the request.
425
+ * @param options - Additional request options (isSigned).
426
+ * @returns A promise resolving to the response data object.
427
+ */
428
+ const sendRequest = function(configuration, endpoint, method, params = {}, timeUnit, options = {}) {
429
+ const localVarUrlObj = new URL(endpoint, configuration?.basePath);
430
+ const localVarRequestOptions = {
431
+ method,
432
+ ...configuration?.baseOptions
433
+ };
434
+ const localVarQueryParameter = { ...normalizeScientificNumbers(params) };
435
+ if (options.isSigned) {
436
+ localVarQueryParameter["timestamp"] = getTimestamp();
437
+ const signature = getSignature(configuration, localVarQueryParameter);
438
+ if (signature) localVarQueryParameter["signature"] = signature;
439
+ }
440
+ setSearchParams(localVarUrlObj, localVarQueryParameter);
441
+ if (timeUnit && localVarRequestOptions.headers) {
442
+ const _timeUnit = validateTimeUnit(timeUnit);
443
+ localVarRequestOptions.headers = {
444
+ ...localVarRequestOptions.headers,
445
+ "X-MBX-TIME-UNIT": _timeUnit
446
+ };
447
+ }
448
+ return httpRequestFunction({
449
+ url: toPathString(localVarUrlObj),
450
+ options: localVarRequestOptions
451
+ }, configuration);
461
452
  };
453
+ /**
454
+ * Removes any null, undefined, or empty string values from the provided object.
455
+ *
456
+ * @param obj - The object to remove empty values from.
457
+ * @returns A new object with empty values removed.
458
+ */
462
459
  function removeEmptyValue(obj) {
463
- if (!(obj instanceof Object)) return {};
464
- return Object.fromEntries(
465
- Object.entries(obj).filter(
466
- ([, value]) => value !== null && value !== void 0 && value !== ""
467
- )
468
- );
460
+ if (!(obj instanceof Object)) return {};
461
+ return Object.fromEntries(Object.entries(obj).filter(([, value]) => value !== null && value !== void 0 && value !== ""));
469
462
  }
463
+ /**
464
+ * Sorts the properties of the provided object in alphabetical order and returns a new object with the sorted properties.
465
+ *
466
+ * @param obj - The object to be sorted.
467
+ * @returns A new object with the properties sorted in alphabetical order.
468
+ */
470
469
  function sortObject(obj) {
471
- return Object.keys(obj).sort().reduce((res, key) => {
472
- res[key] = obj[key];
473
- return res;
474
- }, {});
470
+ return Object.keys(obj).sort().reduce((res, key) => {
471
+ res[key] = obj[key];
472
+ return res;
473
+ }, {});
475
474
  }
475
+ /**
476
+ * Replaces placeholders in the format <field> with corresponding values from the provided variables object.
477
+ *
478
+ * @param {string} str - The input string containing placeholders.
479
+ * @param {Object} variables - An object where keys correspond to placeholder names and values are the replacements.
480
+ * @returns {string} - The resulting string with placeholders replaced by their corresponding values.
481
+ */
476
482
  function replaceWebsocketStreamsPlaceholders(str, variables) {
477
- const normalizedVariables = Object.keys(variables).reduce(
478
- (acc, key) => {
479
- const normalizedKey = key.toLowerCase().replace(/[-_]/g, "");
480
- acc[normalizedKey] = variables[key];
481
- return acc;
482
- },
483
- {}
484
- );
485
- return str.replace(/(@)?<([^>]+)>/g, (match, precedingAt, fieldName) => {
486
- const normalizedFieldName = fieldName.toLowerCase().replace(/[-_]/g, "");
487
- if (Object.prototype.hasOwnProperty.call(normalizedVariables, normalizedFieldName) && normalizedVariables[normalizedFieldName] != null) {
488
- const value = normalizedVariables[normalizedFieldName];
489
- switch (normalizedFieldName) {
490
- case "symbol":
491
- case "windowsize":
492
- return value.toLowerCase();
493
- case "updatespeed":
494
- return `@${value}`;
495
- default:
496
- return (precedingAt || "") + value;
497
- }
498
- }
499
- return "";
500
- });
483
+ const normalizedVariables = Object.keys(variables).reduce((acc, key) => {
484
+ const normalizedKey = key.toLowerCase().replace(/[-_]/g, "");
485
+ acc[normalizedKey] = variables[key];
486
+ return acc;
487
+ }, {});
488
+ return str.replace(/(@)?<([^>]+)>/g, (match, precedingAt, fieldName) => {
489
+ const normalizedFieldName = fieldName.toLowerCase().replace(/[-_]/g, "");
490
+ if (Object.prototype.hasOwnProperty.call(normalizedVariables, normalizedFieldName) && normalizedVariables[normalizedFieldName] != null) {
491
+ const value = normalizedVariables[normalizedFieldName];
492
+ switch (normalizedFieldName) {
493
+ case "symbol":
494
+ case "windowsize": return value.toLowerCase();
495
+ case "updatespeed": return `@${value}`;
496
+ default: return (precedingAt || "") + value;
497
+ }
498
+ }
499
+ return "";
500
+ });
501
501
  }
502
+ /**
503
+ * Generates a standardized user agent string for the application.
504
+ *
505
+ * @param {string} packageName - The name of the package/application.
506
+ * @param {string} packageVersion - The version of the package/application.
507
+ * @returns {string} A formatted user agent string including package details, Node.js version, platform, and architecture.
508
+ */
502
509
  function buildUserAgent(packageName, packageVersion) {
503
- return `${packageName}/${packageVersion} (Node.js/${process.version}; ${(0, import_os.platform)()}; ${(0, import_os.arch)()})`;
510
+ return `${packageName}/${packageVersion} (Node.js/${process.version}; ${(0, os.platform)()}; ${(0, os.arch)()})`;
504
511
  }
512
+ /**
513
+ * Builds a WebSocket API message with optional authentication and signature.
514
+ *
515
+ * @param {ConfigurationWebsocketAPI} configuration - The WebSocket API configuration.
516
+ * @param {string} method - The method name for the WebSocket message.
517
+ * @param {WebsocketSendMsgOptions} payload - The payload data to be sent.
518
+ * @param {WebsocketSendMsgConfig} options - Configuration options for message sending.
519
+ * @param {boolean} [skipAuth=false] - Flag to skip authentication if needed.
520
+ * @returns {Object} A structured WebSocket message with id, method, and params.
521
+ */
505
522
  function buildWebsocketAPIMessage(configuration, method, payload, options, skipAuth = false) {
506
- const id = payload.id && /^[0-9a-f]{32}$/.test(payload.id) ? payload.id : randomString();
507
- delete payload.id;
508
- let params = normalizeScientificNumbers(removeEmptyValue(payload));
509
- if ((options.withApiKey || options.isSigned) && !skipAuth) params.apiKey = configuration.apiKey;
510
- if (options.isSigned) {
511
- params.timestamp = getTimestamp();
512
- params = sortObject(params);
513
- if (!skipAuth) params.signature = getSignature(configuration, params);
514
- }
515
- return { id, method, params };
523
+ const id = payload.id && /^[0-9a-f]{32}$/.test(payload.id) ? payload.id : randomString();
524
+ delete payload.id;
525
+ let params = normalizeScientificNumbers(removeEmptyValue(payload));
526
+ if ((options.withApiKey || options.isSigned) && !skipAuth) params.apiKey = configuration.apiKey;
527
+ if (options.isSigned) {
528
+ params.timestamp = getTimestamp();
529
+ params = sortObject(params);
530
+ if (!skipAuth) params.signature = getSignature(configuration, params);
531
+ }
532
+ return {
533
+ id,
534
+ method,
535
+ params
536
+ };
516
537
  }
538
+ /**
539
+ * Sanitizes a header value by checking for and preventing carriage return and line feed characters.
540
+ *
541
+ * @param {string | string[]} value - The header value or array of header values to sanitize.
542
+ * @returns {string | string[]} The sanitized header value(s).
543
+ * @throws {Error} If the header value contains CR/LF characters.
544
+ */
517
545
  function sanitizeHeaderValue(value) {
518
- const sanitizeOne = (v) => {
519
- if (/\r|\n/.test(v)) throw new Error(`Invalid header value (contains CR/LF): "${v}"`);
520
- return v;
521
- };
522
- return Array.isArray(value) ? value.map(sanitizeOne) : sanitizeOne(value);
546
+ const sanitizeOne = (v) => {
547
+ if (/\r|\n/.test(v)) throw new Error(`Invalid header value (contains CR/LF): "${v}"`);
548
+ return v;
549
+ };
550
+ return Array.isArray(value) ? value.map(sanitizeOne) : sanitizeOne(value);
523
551
  }
552
+ /**
553
+ * Parses and sanitizes custom headers, filtering out forbidden headers.
554
+ *
555
+ * @param {Record<string, string | string[]>} headers - The input headers to be parsed.
556
+ * @returns {Record<string, string | string[]>} A new object with sanitized and allowed headers.
557
+ * @description Removes forbidden headers like 'host', 'authorization', and 'cookie',
558
+ * and sanitizes remaining header values to prevent injection of carriage return or line feed characters.
559
+ */
524
560
  function parseCustomHeaders(headers) {
525
- if (!headers || Object.keys(headers).length === 0) return {};
526
- const forbidden = /* @__PURE__ */ new Set(["host", "authorization", "cookie", ":method", ":path"]);
527
- const parsedHeaders = {};
528
- for (const [rawName, rawValue] of Object.entries(headers || {})) {
529
- const name = rawName.trim();
530
- if (forbidden.has(name.toLowerCase())) {
531
- Logger.getInstance().warn(`Dropping forbidden header: ${name}`);
532
- continue;
533
- }
534
- try {
535
- parsedHeaders[name] = sanitizeHeaderValue(rawValue);
536
- } catch {
537
- continue;
538
- }
539
- }
540
- return parsedHeaders;
561
+ if (!headers || Object.keys(headers).length === 0) return {};
562
+ const forbidden = new Set([
563
+ "host",
564
+ "authorization",
565
+ "cookie",
566
+ ":method",
567
+ ":path"
568
+ ]);
569
+ const parsedHeaders = {};
570
+ for (const [rawName, rawValue] of Object.entries(headers || {})) {
571
+ const name = rawName.trim();
572
+ if (forbidden.has(name.toLowerCase())) {
573
+ Logger.getInstance().warn(`Dropping forbidden header: ${name}`);
574
+ continue;
575
+ }
576
+ try {
577
+ parsedHeaders[name] = sanitizeHeaderValue(rawValue);
578
+ } catch {
579
+ continue;
580
+ }
581
+ }
582
+ return parsedHeaders;
541
583
  }
542
584
 
543
- // src/configuration.ts
585
+ //#endregion
586
+ //#region src/configuration.ts
544
587
  var ConfigurationRestAPI = class {
545
- constructor(param = { apiKey: "" }) {
546
- this.apiKey = param.apiKey;
547
- this.apiSecret = param.apiSecret;
548
- this.basePath = param.basePath;
549
- this.keepAlive = param.keepAlive ?? true;
550
- this.compression = param.compression ?? true;
551
- this.retries = param.retries ?? 3;
552
- this.backoff = param.backoff ?? 1e3;
553
- this.privateKey = param.privateKey;
554
- this.privateKeyPassphrase = param.privateKeyPassphrase;
555
- this.timeUnit = param.timeUnit;
556
- this.baseOptions = {
557
- timeout: param.timeout ?? 1e3,
558
- proxy: param.proxy && {
559
- host: param.proxy.host,
560
- port: param.proxy.port,
561
- ...param.proxy.protocol && { protocol: param.proxy.protocol },
562
- ...param.proxy.auth && { auth: param.proxy.auth }
563
- },
564
- httpsAgent: param.httpsAgent ?? false,
565
- headers: {
566
- ...parseCustomHeaders(param.customHeaders || {}),
567
- "Content-Type": "application/json",
568
- "X-MBX-APIKEY": param.apiKey
569
- }
570
- };
571
- }
588
+ constructor(param = { apiKey: "" }) {
589
+ this.apiKey = param.apiKey;
590
+ this.apiSecret = param.apiSecret;
591
+ this.basePath = param.basePath;
592
+ this.keepAlive = param.keepAlive ?? true;
593
+ this.compression = param.compression ?? true;
594
+ this.retries = param.retries ?? 3;
595
+ this.backoff = param.backoff ?? 1e3;
596
+ this.privateKey = param.privateKey;
597
+ this.privateKeyPassphrase = param.privateKeyPassphrase;
598
+ this.timeUnit = param.timeUnit;
599
+ this.baseOptions = {
600
+ timeout: param.timeout ?? 1e3,
601
+ proxy: param.proxy && {
602
+ host: param.proxy.host,
603
+ port: param.proxy.port,
604
+ ...param.proxy.protocol && { protocol: param.proxy.protocol },
605
+ ...param.proxy.auth && { auth: param.proxy.auth }
606
+ },
607
+ httpsAgent: param.httpsAgent ?? false,
608
+ headers: {
609
+ ...parseCustomHeaders(param.customHeaders || {}),
610
+ "Content-Type": "application/json",
611
+ "X-MBX-APIKEY": param.apiKey
612
+ }
613
+ };
614
+ }
572
615
  };
573
- var ConfigurationWebsocketAPI2 = class {
574
- constructor(param = { apiKey: "" }) {
575
- this.apiKey = param.apiKey;
576
- this.apiSecret = param.apiSecret;
577
- this.wsURL = param.wsURL;
578
- this.timeout = param.timeout ?? 5e3;
579
- this.reconnectDelay = param.reconnectDelay ?? 5e3;
580
- this.compression = param.compression ?? true;
581
- this.agent = param.agent ?? false;
582
- this.mode = param.mode ?? "single";
583
- this.poolSize = param.poolSize ?? 1;
584
- this.privateKey = param.privateKey;
585
- this.privateKeyPassphrase = param.privateKeyPassphrase;
586
- this.timeUnit = param.timeUnit;
587
- this.autoSessionReLogon = param.autoSessionReLogon ?? true;
588
- }
616
+ var ConfigurationWebsocketAPI = class {
617
+ constructor(param = { apiKey: "" }) {
618
+ this.apiKey = param.apiKey;
619
+ this.apiSecret = param.apiSecret;
620
+ this.wsURL = param.wsURL;
621
+ this.timeout = param.timeout ?? 5e3;
622
+ this.reconnectDelay = param.reconnectDelay ?? 5e3;
623
+ this.compression = param.compression ?? true;
624
+ this.agent = param.agent ?? false;
625
+ this.mode = param.mode ?? "single";
626
+ this.poolSize = param.poolSize ?? 1;
627
+ this.privateKey = param.privateKey;
628
+ this.privateKeyPassphrase = param.privateKeyPassphrase;
629
+ this.timeUnit = param.timeUnit;
630
+ this.autoSessionReLogon = param.autoSessionReLogon ?? true;
631
+ }
589
632
  };
590
633
  var ConfigurationWebsocketStreams = class {
591
- constructor(param = {}) {
592
- this.wsURL = param.wsURL;
593
- this.reconnectDelay = param.reconnectDelay ?? 5e3;
594
- this.compression = param.compression ?? true;
595
- this.agent = param.agent ?? false;
596
- this.mode = param.mode ?? "single";
597
- this.poolSize = param.poolSize ?? 1;
598
- this.timeUnit = param.timeUnit;
599
- }
634
+ constructor(param = {}) {
635
+ this.wsURL = param.wsURL;
636
+ this.reconnectDelay = param.reconnectDelay ?? 5e3;
637
+ this.compression = param.compression ?? true;
638
+ this.agent = param.agent ?? false;
639
+ this.mode = param.mode ?? "single";
640
+ this.poolSize = param.poolSize ?? 1;
641
+ this.timeUnit = param.timeUnit;
642
+ }
600
643
  };
601
644
 
602
- // src/constants.ts
603
- var TimeUnit = {
604
- MILLISECOND: "MILLISECOND",
605
- millisecond: "millisecond",
606
- MICROSECOND: "MICROSECOND",
607
- microsecond: "microsecond"
645
+ //#endregion
646
+ //#region src/constants.ts
647
+ const TimeUnit = {
648
+ MILLISECOND: "MILLISECOND",
649
+ millisecond: "millisecond",
650
+ MICROSECOND: "MICROSECOND",
651
+ microsecond: "microsecond"
608
652
  };
609
- var ALGO_REST_API_PROD_URL = "https://api.binance.com";
610
- var C2C_REST_API_PROD_URL = "https://api.binance.com";
611
- var CONVERT_REST_API_PROD_URL = "https://api.binance.com";
612
- var COPY_TRADING_REST_API_PROD_URL = "https://api.binance.com";
613
- var CRYPTO_LOAN_REST_API_PROD_URL = "https://api.binance.com";
614
- var DERIVATIVES_TRADING_COIN_FUTURES_REST_API_PROD_URL = "https://dapi.binance.com";
615
- var DERIVATIVES_TRADING_COIN_FUTURES_REST_API_TESTNET_URL = "https://testnet.binancefuture.com";
616
- var DERIVATIVES_TRADING_COIN_FUTURES_WS_API_PROD_URL = "wss://ws-dapi.binance.com/ws-dapi/v1";
617
- var DERIVATIVES_TRADING_COIN_FUTURES_WS_API_TESTNET_URL = "wss://testnet.binancefuture.com/ws-dapi/v1";
618
- var DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_PROD_URL = "wss://dstream.binance.com";
619
- var DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_TESTNET_URL = "wss://dstream.binancefuture.com";
620
- var DERIVATIVES_TRADING_USDS_FUTURES_REST_API_PROD_URL = "https://fapi.binance.com";
621
- var DERIVATIVES_TRADING_USDS_FUTURES_REST_API_TESTNET_URL = "https://testnet.binancefuture.com";
622
- var DERIVATIVES_TRADING_USDS_FUTURES_WS_API_PROD_URL = "wss://ws-fapi.binance.com/ws-fapi/v1";
623
- var DERIVATIVES_TRADING_USDS_FUTURES_WS_API_TESTNET_URL = "wss://testnet.binancefuture.com/ws-fapi/v1";
624
- var DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_PROD_URL = "wss://fstream.binance.com";
625
- var DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_TESTNET_URL = "wss://stream.binancefuture.com";
626
- var DERIVATIVES_TRADING_OPTIONS_REST_API_PROD_URL = "https://eapi.binance.com";
627
- var DERIVATIVES_TRADING_OPTIONS_WS_STREAMS_PROD_URL = "wss://nbstream.binance.com/eoptions";
628
- var DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_PROD_URL = "https://papi.binance.com";
629
- var DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_TESTNET_URL = "https://testnet.binancefuture.com";
630
- var DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_PROD_URL = "wss://fstream.binance.com/pm";
631
- var DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_TESTNET_URL = "wss://fstream.binancefuture.com/pm";
632
- var DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_REST_API_PROD_URL = "https://api.binance.com";
633
- var DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_WS_STREAMS_PROD_URL = "wss://fstream.binance.com/pm-classic";
634
- var DUAL_INVESTMENT_REST_API_PROD_URL = "https://api.binance.com";
635
- var FIAT_REST_API_PROD_URL = "https://api.binance.com";
636
- var GIFT_CARD_REST_API_PROD_URL = "https://api.binance.com";
637
- var MARGIN_TRADING_REST_API_PROD_URL = "https://api.binance.com";
638
- var MARGIN_TRADING_WS_STREAMS_PROD_URL = "wss://stream.binance.com:9443";
639
- var MARGIN_TRADING_RISK_WS_STREAMS_PROD_URL = "wss://margin-stream.binance.com";
640
- var MINING_REST_API_PROD_URL = "https://api.binance.com";
641
- var NFT_REST_API_PROD_URL = "https://api.binance.com";
642
- var PAY_REST_API_PROD_URL = "https://api.binance.com";
643
- var REBATE_REST_API_PROD_URL = "https://api.binance.com";
644
- var SIMPLE_EARN_REST_API_PROD_URL = "https://api.binance.com";
645
- var SPOT_REST_API_PROD_URL = "https://api.binance.com";
646
- var SPOT_REST_API_TESTNET_URL = "https://testnet.binance.vision";
647
- var SPOT_WS_API_PROD_URL = "wss://ws-api.binance.com:443/ws-api/v3";
648
- var SPOT_WS_API_TESTNET_URL = "wss://ws-api.testnet.binance.vision/ws-api/v3";
649
- var SPOT_WS_STREAMS_PROD_URL = "wss://stream.binance.com:9443";
650
- var SPOT_WS_STREAMS_TESTNET_URL = "wss://stream.testnet.binance.vision";
651
- var SPOT_REST_API_MARKET_URL = "https://data-api.binance.vision";
652
- var SPOT_WS_STREAMS_MARKET_URL = "wss://data-stream.binance.vision";
653
- var STAKING_REST_API_PROD_URL = "https://api.binance.com";
654
- var SUB_ACCOUNT_REST_API_PROD_URL = "https://api.binance.com";
655
- var VIP_LOAN_REST_API_PROD_URL = "https://api.binance.com";
656
- var WALLET_REST_API_PROD_URL = "https://api.binance.com";
653
+ const ALGO_REST_API_PROD_URL = "https://api.binance.com";
654
+ const C2C_REST_API_PROD_URL = "https://api.binance.com";
655
+ const CONVERT_REST_API_PROD_URL = "https://api.binance.com";
656
+ const COPY_TRADING_REST_API_PROD_URL = "https://api.binance.com";
657
+ const CRYPTO_LOAN_REST_API_PROD_URL = "https://api.binance.com";
658
+ const DERIVATIVES_TRADING_COIN_FUTURES_REST_API_PROD_URL = "https://dapi.binance.com";
659
+ const DERIVATIVES_TRADING_COIN_FUTURES_REST_API_TESTNET_URL = "https://testnet.binancefuture.com";
660
+ const DERIVATIVES_TRADING_COIN_FUTURES_WS_API_PROD_URL = "wss://ws-dapi.binance.com/ws-dapi/v1";
661
+ const DERIVATIVES_TRADING_COIN_FUTURES_WS_API_TESTNET_URL = "wss://testnet.binancefuture.com/ws-dapi/v1";
662
+ const DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_PROD_URL = "wss://dstream.binance.com";
663
+ const DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_TESTNET_URL = "wss://dstream.binancefuture.com";
664
+ const DERIVATIVES_TRADING_USDS_FUTURES_REST_API_PROD_URL = "https://fapi.binance.com";
665
+ const DERIVATIVES_TRADING_USDS_FUTURES_REST_API_TESTNET_URL = "https://testnet.binancefuture.com";
666
+ const DERIVATIVES_TRADING_USDS_FUTURES_WS_API_PROD_URL = "wss://ws-fapi.binance.com/ws-fapi/v1";
667
+ const DERIVATIVES_TRADING_USDS_FUTURES_WS_API_TESTNET_URL = "wss://testnet.binancefuture.com/ws-fapi/v1";
668
+ const DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_PROD_URL = "wss://fstream.binance.com";
669
+ const DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_TESTNET_URL = "wss://stream.binancefuture.com";
670
+ const DERIVATIVES_TRADING_OPTIONS_REST_API_PROD_URL = "https://eapi.binance.com";
671
+ const DERIVATIVES_TRADING_OPTIONS_WS_STREAMS_PROD_URL = "wss://nbstream.binance.com/eoptions";
672
+ const DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_PROD_URL = "https://papi.binance.com";
673
+ const DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_TESTNET_URL = "https://testnet.binancefuture.com";
674
+ const DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_PROD_URL = "wss://fstream.binance.com/pm";
675
+ const DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_TESTNET_URL = "wss://fstream.binancefuture.com/pm";
676
+ const DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_REST_API_PROD_URL = "https://api.binance.com";
677
+ const DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_WS_STREAMS_PROD_URL = "wss://fstream.binance.com/pm-classic";
678
+ const DUAL_INVESTMENT_REST_API_PROD_URL = "https://api.binance.com";
679
+ const FIAT_REST_API_PROD_URL = "https://api.binance.com";
680
+ const GIFT_CARD_REST_API_PROD_URL = "https://api.binance.com";
681
+ const MARGIN_TRADING_REST_API_PROD_URL = "https://api.binance.com";
682
+ const MARGIN_TRADING_WS_STREAMS_PROD_URL = "wss://stream.binance.com:9443";
683
+ const MARGIN_TRADING_RISK_WS_STREAMS_PROD_URL = "wss://margin-stream.binance.com";
684
+ const MINING_REST_API_PROD_URL = "https://api.binance.com";
685
+ const NFT_REST_API_PROD_URL = "https://api.binance.com";
686
+ const PAY_REST_API_PROD_URL = "https://api.binance.com";
687
+ const REBATE_REST_API_PROD_URL = "https://api.binance.com";
688
+ const SIMPLE_EARN_REST_API_PROD_URL = "https://api.binance.com";
689
+ const SPOT_REST_API_PROD_URL = "https://api.binance.com";
690
+ const SPOT_REST_API_TESTNET_URL = "https://testnet.binance.vision";
691
+ const SPOT_WS_API_PROD_URL = "wss://ws-api.binance.com:443/ws-api/v3";
692
+ const SPOT_WS_API_TESTNET_URL = "wss://ws-api.testnet.binance.vision/ws-api/v3";
693
+ const SPOT_WS_STREAMS_PROD_URL = "wss://stream.binance.com:9443";
694
+ const SPOT_WS_STREAMS_TESTNET_URL = "wss://stream.testnet.binance.vision";
695
+ const SPOT_REST_API_MARKET_URL = "https://data-api.binance.vision";
696
+ const SPOT_WS_STREAMS_MARKET_URL = "wss://data-stream.binance.vision";
697
+ const STAKING_REST_API_PROD_URL = "https://api.binance.com";
698
+ const SUB_ACCOUNT_REST_API_PROD_URL = "https://api.binance.com";
699
+ const VIP_LOAN_REST_API_PROD_URL = "https://api.binance.com";
700
+ const WALLET_REST_API_PROD_URL = "https://api.binance.com";
657
701
 
658
- // src/errors.ts
659
- var ConnectorClientError = class _ConnectorClientError extends Error {
660
- constructor(msg) {
661
- super(msg || "An unexpected error occurred.");
662
- Object.setPrototypeOf(this, _ConnectorClientError.prototype);
663
- this.name = "ConnectorClientError";
664
- }
702
+ //#endregion
703
+ //#region src/errors.ts
704
+ /**
705
+ * Represents an error that occurred in the Connector client.
706
+ * @param msg - An optional error message.
707
+ */
708
+ var ConnectorClientError = class ConnectorClientError extends Error {
709
+ constructor(msg) {
710
+ super(msg || "An unexpected error occurred.");
711
+ Object.setPrototypeOf(this, ConnectorClientError.prototype);
712
+ this.name = "ConnectorClientError";
713
+ }
665
714
  };
666
- var RequiredError = class _RequiredError extends Error {
667
- constructor(field, msg) {
668
- super(msg || `Required parameter ${field} was null or undefined.`);
669
- this.field = field;
670
- Object.setPrototypeOf(this, _RequiredError.prototype);
671
- this.name = "RequiredError";
672
- }
715
+ /**
716
+ * Represents an error that occurs when a required parameter is missing or undefined.
717
+ * @param field - The name of the missing parameter.
718
+ * @param msg - An optional error message.
719
+ */
720
+ var RequiredError = class RequiredError extends Error {
721
+ constructor(field, msg) {
722
+ super(msg || `Required parameter ${field} was null or undefined.`);
723
+ this.field = field;
724
+ Object.setPrototypeOf(this, RequiredError.prototype);
725
+ this.name = "RequiredError";
726
+ }
673
727
  };
674
- var UnauthorizedError = class _UnauthorizedError extends Error {
675
- constructor(msg) {
676
- super(msg || "Unauthorized access. Authentication required.");
677
- Object.setPrototypeOf(this, _UnauthorizedError.prototype);
678
- this.name = "UnauthorizedError";
679
- }
728
+ /**
729
+ * Represents an error that occurs when a client is unauthorized to access a resource.
730
+ * @param msg - An optional error message.
731
+ */
732
+ var UnauthorizedError = class UnauthorizedError extends Error {
733
+ constructor(msg) {
734
+ super(msg || "Unauthorized access. Authentication required.");
735
+ Object.setPrototypeOf(this, UnauthorizedError.prototype);
736
+ this.name = "UnauthorizedError";
737
+ }
680
738
  };
681
- var ForbiddenError = class _ForbiddenError extends Error {
682
- constructor(msg) {
683
- super(msg || "Access to the requested resource is forbidden.");
684
- Object.setPrototypeOf(this, _ForbiddenError.prototype);
685
- this.name = "ForbiddenError";
686
- }
739
+ /**
740
+ * Represents an error that occurs when a resource is forbidden to the client.
741
+ * @param msg - An optional error message.
742
+ */
743
+ var ForbiddenError = class ForbiddenError extends Error {
744
+ constructor(msg) {
745
+ super(msg || "Access to the requested resource is forbidden.");
746
+ Object.setPrototypeOf(this, ForbiddenError.prototype);
747
+ this.name = "ForbiddenError";
748
+ }
687
749
  };
688
- var TooManyRequestsError = class _TooManyRequestsError extends Error {
689
- constructor(msg) {
690
- super(msg || "Too many requests. You are being rate-limited.");
691
- Object.setPrototypeOf(this, _TooManyRequestsError.prototype);
692
- this.name = "TooManyRequestsError";
693
- }
750
+ /**
751
+ * Represents an error that occurs when client is doing too many requests.
752
+ * @param msg - An optional error message.
753
+ */
754
+ var TooManyRequestsError = class TooManyRequestsError extends Error {
755
+ constructor(msg) {
756
+ super(msg || "Too many requests. You are being rate-limited.");
757
+ Object.setPrototypeOf(this, TooManyRequestsError.prototype);
758
+ this.name = "TooManyRequestsError";
759
+ }
694
760
  };
695
- var RateLimitBanError = class _RateLimitBanError extends Error {
696
- constructor(msg) {
697
- super(msg || "The IP address has been banned for exceeding rate limits.");
698
- Object.setPrototypeOf(this, _RateLimitBanError.prototype);
699
- this.name = "RateLimitBanError";
700
- }
761
+ /**
762
+ * Represents an error that occurs when client's IP has been banned.
763
+ * @param msg - An optional error message.
764
+ */
765
+ var RateLimitBanError = class RateLimitBanError extends Error {
766
+ constructor(msg) {
767
+ super(msg || "The IP address has been banned for exceeding rate limits.");
768
+ Object.setPrototypeOf(this, RateLimitBanError.prototype);
769
+ this.name = "RateLimitBanError";
770
+ }
701
771
  };
702
- var ServerError = class _ServerError extends Error {
703
- constructor(msg, statusCode) {
704
- super(msg || "An internal server error occurred.");
705
- this.statusCode = statusCode;
706
- Object.setPrototypeOf(this, _ServerError.prototype);
707
- this.name = "ServerError";
708
- }
772
+ /**
773
+ * Represents an error that occurs when there is an internal server error.
774
+ * @param msg - An optional error message.
775
+ * @param statusCode - An optional HTTP status code associated with the error.
776
+ */
777
+ var ServerError = class ServerError extends Error {
778
+ constructor(msg, statusCode) {
779
+ super(msg || "An internal server error occurred.");
780
+ this.statusCode = statusCode;
781
+ Object.setPrototypeOf(this, ServerError.prototype);
782
+ this.name = "ServerError";
783
+ }
709
784
  };
710
- var NetworkError = class _NetworkError extends Error {
711
- constructor(msg) {
712
- super(msg || "A network error occurred.");
713
- Object.setPrototypeOf(this, _NetworkError.prototype);
714
- this.name = "NetworkError";
715
- }
785
+ /**
786
+ * Represents an error that occurs when a network error occurs.
787
+ * @param msg - An optional error message.
788
+ */
789
+ var NetworkError = class NetworkError extends Error {
790
+ constructor(msg) {
791
+ super(msg || "A network error occurred.");
792
+ Object.setPrototypeOf(this, NetworkError.prototype);
793
+ this.name = "NetworkError";
794
+ }
716
795
  };
717
- var NotFoundError = class _NotFoundError extends Error {
718
- constructor(msg) {
719
- super(msg || "The requested resource was not found.");
720
- Object.setPrototypeOf(this, _NotFoundError.prototype);
721
- this.name = "NotFoundError";
722
- }
796
+ /**
797
+ * Represents an error that occurs when the requested resource was not found.
798
+ * @param msg - An optional error message.
799
+ */
800
+ var NotFoundError = class NotFoundError extends Error {
801
+ constructor(msg) {
802
+ super(msg || "The requested resource was not found.");
803
+ Object.setPrototypeOf(this, NotFoundError.prototype);
804
+ this.name = "NotFoundError";
805
+ }
723
806
  };
724
- var BadRequestError = class _BadRequestError extends Error {
725
- constructor(msg) {
726
- super(msg || "The request was invalid or cannot be otherwise served.");
727
- Object.setPrototypeOf(this, _BadRequestError.prototype);
728
- this.name = "BadRequestError";
729
- }
807
+ /**
808
+ * Represents an error that occurs when a request is invalid or cannot be otherwise served.
809
+ * @param msg - An optional error message.
810
+ */
811
+ var BadRequestError = class BadRequestError extends Error {
812
+ constructor(msg) {
813
+ super(msg || "The request was invalid or cannot be otherwise served.");
814
+ Object.setPrototypeOf(this, BadRequestError.prototype);
815
+ this.name = "BadRequestError";
816
+ }
730
817
  };
731
818
 
732
- // src/logger.ts
733
- var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
734
- LogLevel2["NONE"] = "";
735
- LogLevel2["DEBUG"] = "debug";
736
- LogLevel2["INFO"] = "info";
737
- LogLevel2["WARN"] = "warn";
738
- LogLevel2["ERROR"] = "error";
739
- return LogLevel2;
740
- })(LogLevel || {});
741
- var Logger = class _Logger {
742
- constructor() {
743
- this.minLogLevel = "info" /* INFO */;
744
- this.levelsOrder = [
745
- "" /* NONE */,
746
- "debug" /* DEBUG */,
747
- "info" /* INFO */,
748
- "warn" /* WARN */,
749
- "error" /* ERROR */
750
- ];
751
- const envLevel = process.env.LOG_LEVEL?.toLowerCase();
752
- this.minLogLevel = envLevel && this.isValidLogLevel(envLevel) ? envLevel : "info" /* INFO */;
753
- }
754
- static getInstance() {
755
- if (!_Logger.instance) _Logger.instance = new _Logger();
756
- return _Logger.instance;
757
- }
758
- setMinLogLevel(level) {
759
- if (!this.isValidLogLevel(level)) throw new Error(`Invalid log level: ${level}`);
760
- this.minLogLevel = level;
761
- }
762
- isValidLogLevel(level) {
763
- return this.levelsOrder.includes(level);
764
- }
765
- log(level, ...message) {
766
- if (level === "" /* NONE */ || !this.allowLevelLog(level)) return;
767
- const timestamp = (/* @__PURE__ */ new Date()).toISOString();
768
- console[level](`[${timestamp}] [${level.toLowerCase()}]`, ...message);
769
- }
770
- allowLevelLog(level) {
771
- if (!this.isValidLogLevel(level)) throw new Error(`Invalid log level: ${level}`);
772
- const currentLevelIndex = this.levelsOrder.indexOf(level);
773
- const minLevelIndex = this.levelsOrder.indexOf(this.minLogLevel);
774
- return currentLevelIndex >= minLevelIndex;
775
- }
776
- debug(...message) {
777
- this.log("debug" /* DEBUG */, ...message);
778
- }
779
- info(...message) {
780
- this.log("info" /* INFO */, ...message);
781
- }
782
- warn(...message) {
783
- this.log("warn" /* WARN */, ...message);
784
- }
785
- error(...message) {
786
- this.log("error" /* ERROR */, ...message);
787
- }
819
+ //#endregion
820
+ //#region src/logger.ts
821
+ let LogLevel = /* @__PURE__ */ function(LogLevel$1) {
822
+ LogLevel$1["NONE"] = "";
823
+ LogLevel$1["DEBUG"] = "debug";
824
+ LogLevel$1["INFO"] = "info";
825
+ LogLevel$1["WARN"] = "warn";
826
+ LogLevel$1["ERROR"] = "error";
827
+ return LogLevel$1;
828
+ }({});
829
+ var Logger = class Logger {
830
+ constructor() {
831
+ this.minLogLevel = LogLevel.INFO;
832
+ this.levelsOrder = [
833
+ LogLevel.NONE,
834
+ LogLevel.DEBUG,
835
+ LogLevel.INFO,
836
+ LogLevel.WARN,
837
+ LogLevel.ERROR
838
+ ];
839
+ const envLevel = process.env.LOG_LEVEL?.toLowerCase();
840
+ this.minLogLevel = envLevel && this.isValidLogLevel(envLevel) ? envLevel : LogLevel.INFO;
841
+ }
842
+ static getInstance() {
843
+ if (!Logger.instance) Logger.instance = new Logger();
844
+ return Logger.instance;
845
+ }
846
+ setMinLogLevel(level) {
847
+ if (!this.isValidLogLevel(level)) throw new Error(`Invalid log level: ${level}`);
848
+ this.minLogLevel = level;
849
+ }
850
+ isValidLogLevel(level) {
851
+ return this.levelsOrder.includes(level);
852
+ }
853
+ log(level, ...message) {
854
+ if (level === LogLevel.NONE || !this.allowLevelLog(level)) return;
855
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
856
+ console[level](`[${timestamp}] [${level.toLowerCase()}]`, ...message);
857
+ }
858
+ allowLevelLog(level) {
859
+ if (!this.isValidLogLevel(level)) throw new Error(`Invalid log level: ${level}`);
860
+ return this.levelsOrder.indexOf(level) >= this.levelsOrder.indexOf(this.minLogLevel);
861
+ }
862
+ debug(...message) {
863
+ this.log(LogLevel.DEBUG, ...message);
864
+ }
865
+ info(...message) {
866
+ this.log(LogLevel.INFO, ...message);
867
+ }
868
+ warn(...message) {
869
+ this.log(LogLevel.WARN, ...message);
870
+ }
871
+ error(...message) {
872
+ this.log(LogLevel.ERROR, ...message);
873
+ }
788
874
  };
789
875
 
790
- // src/websocket.ts
791
- var import_events = require("events");
792
- var import_ws = __toESM(require("ws"));
793
- var import_json_with_bigint2 = require("json-with-bigint");
876
+ //#endregion
877
+ //#region src/websocket.ts
794
878
  var WebsocketEventEmitter = class {
795
- constructor() {
796
- this.eventEmitter = new import_events.EventEmitter();
797
- }
798
- /* eslint-disable @typescript-eslint/no-explicit-any */
799
- on(event, listener) {
800
- this.eventEmitter.on(event, listener);
801
- }
802
- /* eslint-disable @typescript-eslint/no-explicit-any */
803
- off(event, listener) {
804
- this.eventEmitter.off(event, listener);
805
- }
806
- /* eslint-disable @typescript-eslint/no-explicit-any */
807
- emit(event, ...args) {
808
- this.eventEmitter.emit(event, ...args);
809
- }
879
+ constructor() {
880
+ this.eventEmitter = new events.EventEmitter();
881
+ }
882
+ on(event, listener) {
883
+ this.eventEmitter.on(event, listener);
884
+ }
885
+ off(event, listener) {
886
+ this.eventEmitter.off(event, listener);
887
+ }
888
+ emit(event, ...args) {
889
+ this.eventEmitter.emit(event, ...args);
890
+ }
810
891
  };
811
- var WebsocketCommon = class _WebsocketCommon extends WebsocketEventEmitter {
812
- constructor(configuration, connectionPool = []) {
813
- super();
814
- this.configuration = configuration;
815
- this.connectionQueue = [];
816
- this.queueProcessing = false;
817
- this.connectionTimers = /* @__PURE__ */ new Map();
818
- this.roundRobinIndex = 0;
819
- this.logger = Logger.getInstance();
820
- this.connectionPool = connectionPool;
821
- this.mode = this.configuration?.mode ?? "single";
822
- this.poolSize = this.mode === "pool" && this.configuration?.poolSize ? this.configuration.poolSize : 1;
823
- if (!connectionPool || connectionPool.length === 0) this.initializePool(this.poolSize);
824
- }
825
- static {
826
- this.MAX_CONNECTION_DURATION = 23 * 60 * 60 * 1e3;
827
- }
828
- /**
829
- * Initializes the WebSocket connection pool by creating a specified number of connection objects
830
- * and adding them to the `connectionPool` array. Each connection object has the following properties:
831
- * - `closeInitiated`: a boolean indicating whether the connection has been closed
832
- * - `reconnectionPending`: a boolean indicating whether a reconnection is pending
833
- * - `pendingRequests`: a Map that tracks pending requests for the connection
834
- * @param size - The number of connection objects to create and add to the pool.
835
- * @returns void
836
- */
837
- initializePool(size) {
838
- for (let i = 0; i < size; i++) {
839
- this.connectionPool.push({
840
- id: randomString(),
841
- closeInitiated: false,
842
- reconnectionPending: false,
843
- renewalPending: false,
844
- pendingRequests: /* @__PURE__ */ new Map(),
845
- pendingSubscriptions: []
846
- });
847
- }
848
- }
849
- /**
850
- * Retrieves available WebSocket connections based on the connection mode and readiness.
851
- * In 'single' mode, returns the first connection in the pool.
852
- * In 'pool' mode, filters and returns connections that are ready for use.
853
- * @param allowNonEstablishedWebsockets - Optional flag to include non-established WebSocket connections.
854
- * @returns An array of available WebSocket connections.
855
- */
856
- getAvailableConnections(allowNonEstablishedWebsockets = false) {
857
- if (this.mode === "single") return [this.connectionPool[0]];
858
- const availableConnections = this.connectionPool.filter(
859
- (connection) => this.isConnectionReady(connection, allowNonEstablishedWebsockets)
860
- );
861
- return availableConnections;
862
- }
863
- /**
864
- * Gets a WebSocket connection from the pool or single connection.
865
- * If the connection mode is 'single', it returns the first connection in the pool.
866
- * If the connection mode is 'pool', it returns an available connection from the pool,
867
- * using a round-robin selection strategy. If no available connections are found, it throws an error.
868
- * @param allowNonEstablishedWebsockets - A boolean indicating whether to allow connections that are not established.
869
- * @returns {WebsocketConnection} The selected WebSocket connection.
870
- */
871
- getConnection(allowNonEstablishedWebsockets = false) {
872
- const availableConnections = this.getAvailableConnections(allowNonEstablishedWebsockets);
873
- if (availableConnections.length === 0) {
874
- throw new Error("No available Websocket connections are ready.");
875
- }
876
- const selectedConnection = availableConnections[this.roundRobinIndex % availableConnections.length];
877
- this.roundRobinIndex = (this.roundRobinIndex + 1) % availableConnections.length;
878
- return selectedConnection;
879
- }
880
- /**
881
- * Checks if the provided WebSocket connection is ready for use.
882
- * A connection is considered ready if it is open, has no pending reconnection, and has not been closed.
883
- * @param connection - The WebSocket connection to check.
884
- * @param allowNonEstablishedWebsockets - An optional flag to allow non-established WebSocket connections.
885
- * @returns `true` if the connection is ready, `false` otherwise.
886
- */
887
- isConnectionReady(connection, allowNonEstablishedWebsockets = false) {
888
- return (allowNonEstablishedWebsockets || connection.ws?.readyState === import_ws.default.OPEN) && !connection.reconnectionPending && !connection.closeInitiated;
889
- }
890
- /**
891
- * Schedules a timer for a WebSocket connection and tracks it
892
- * @param connection WebSocket client instance
893
- * @param callback Function to execute when timer triggers
894
- * @param delay Time in milliseconds before callback execution
895
- * @param type Timer type ('timeout' or 'interval')
896
- * @returns Timer handle
897
- */
898
- scheduleTimer(connection, callback, delay2, type = "timeout") {
899
- let timers = this.connectionTimers.get(connection);
900
- if (!timers) {
901
- timers = /* @__PURE__ */ new Set();
902
- this.connectionTimers.set(connection, timers);
903
- }
904
- const timerRecord = { type };
905
- const wrappedTimeout = () => {
906
- try {
907
- callback();
908
- } finally {
909
- timers.delete(timerRecord);
910
- }
911
- };
912
- let timer;
913
- if (type === "timeout") timer = setTimeout(wrappedTimeout, delay2);
914
- else timer = setInterval(callback, delay2);
915
- timerRecord.timer = timer;
916
- timers.add(timerRecord);
917
- return timer;
918
- }
919
- /**
920
- * Clears all timers associated with a WebSocket connection.
921
- * @param connection - The WebSocket client instance to clear timers for.
922
- * @returns void
923
- */
924
- clearTimers(connection) {
925
- const timers = this.connectionTimers.get(connection);
926
- if (timers) {
927
- timers.forEach(({ timer, type }) => {
928
- if (type === "timeout") clearTimeout(timer);
929
- else if (type === "interval") clearInterval(timer);
930
- });
931
- this.connectionTimers.delete(connection);
932
- }
933
- }
934
- /**
935
- * Processes the connection queue, reconnecting or renewing connections as needed.
936
- * This method is responsible for iterating through the connection queue and initiating
937
- * the reconnection or renewal process for each connection in the queue. It throttles
938
- * the queue processing to avoid overwhelming the server with too many connection
939
- * requests at once.
940
- * @param throttleRate - The time in milliseconds to wait between processing each
941
- * connection in the queue.
942
- * @returns A Promise that resolves when the queue has been fully processed.
943
- */
944
- async processQueue(throttleRate = 1e3) {
945
- if (this.queueProcessing) return;
946
- this.queueProcessing = true;
947
- while (this.connectionQueue.length > 0) {
948
- const { connection, url, isRenewal } = this.connectionQueue.shift();
949
- this.initConnect(url, isRenewal, connection);
950
- await delay(throttleRate);
951
- }
952
- this.queueProcessing = false;
953
- }
954
- /**
955
- * Enqueues a reconnection or renewal for a WebSocket connection.
956
- * This method adds the connection, URL, and renewal flag to the connection queue,
957
- * and then calls the `processQueue` method to initiate the reconnection or renewal
958
- * process.
959
- * @param connection - The WebSocket connection to reconnect or renew.
960
- * @param url - The URL to use for the reconnection or renewal.
961
- * @param isRenewal - A flag indicating whether this is a renewal (true) or a reconnection (false).
962
- */
963
- enqueueReconnection(connection, url, isRenewal) {
964
- this.connectionQueue.push({ connection, url, isRenewal });
965
- this.processQueue();
966
- }
967
- /**
968
- * Gracefully closes a WebSocket connection after pending requests complete.
969
- * This method waits for any pending requests to complete before closing the connection.
970
- * It sets up a timeout to force-close the connection after 30 seconds if the pending requests
971
- * do not complete. Once all pending requests are completed, the connection is closed.
972
- * @param connectionToClose - The WebSocket client instance to close.
973
- * @param WebsocketConnectionToClose - The WebSocket connection to close.
974
- * @param connection - The WebSocket connection to close.
975
- * @returns Promise that resolves when the connection is closed.
976
- */
977
- async closeConnectionGracefully(WebsocketConnectionToClose, connection) {
978
- if (!WebsocketConnectionToClose || !connection) return;
979
- this.logger.debug(
980
- `Waiting for pending requests to complete before disconnecting websocket on connection ${connection.id}.`
981
- );
982
- const closePromise = new Promise((resolve) => {
983
- this.scheduleTimer(
984
- WebsocketConnectionToClose,
985
- () => {
986
- this.logger.warn(
987
- `Force-closing websocket connection after 30 seconds on connection ${connection.id}.`
988
- );
989
- resolve();
990
- },
991
- 3e4
992
- );
993
- this.scheduleTimer(
994
- WebsocketConnectionToClose,
995
- () => {
996
- if (connection.pendingRequests.size === 0) {
997
- this.logger.debug(
998
- `All pending requests completed, closing websocket connection on connection ${connection.id}.`
999
- );
1000
- resolve();
1001
- }
1002
- },
1003
- 1e3,
1004
- "interval"
1005
- );
1006
- });
1007
- await closePromise;
1008
- this.logger.info(`Closing Websocket connection on connection ${connection.id}.`);
1009
- WebsocketConnectionToClose.close();
1010
- this.cleanup(WebsocketConnectionToClose);
1011
- }
1012
- /**
1013
- * Attempts to re-establish a session for a WebSocket connection.
1014
- * If a session logon request exists and the connection is not already logged on,
1015
- * it sends an authentication request and updates the connection's logged-on status.
1016
- * @param connection - The WebSocket connection to re-authenticate.
1017
- * @private
1018
- */
1019
- async sessionReLogon(connection) {
1020
- const req = connection.sessionLogonReq;
1021
- if (req && !connection.isSessionLoggedOn) {
1022
- const data = buildWebsocketAPIMessage(
1023
- this.configuration,
1024
- req.method,
1025
- req.payload,
1026
- req.options
1027
- );
1028
- this.logger.debug(`Session re-logon on connection ${connection.id}`, data);
1029
- try {
1030
- await this.send(
1031
- JSON.stringify(data),
1032
- data.id,
1033
- true,
1034
- this.configuration.timeout,
1035
- connection
1036
- );
1037
- this.logger.debug(
1038
- `Session re-logon on connection ${connection.id} was successful.`
1039
- );
1040
- connection.isSessionLoggedOn = true;
1041
- } catch (err) {
1042
- this.logger.error(`Session re-logon on connection ${connection.id} failed:`, err);
1043
- }
1044
- }
1045
- }
1046
- /**
1047
- * Cleans up WebSocket connection resources.
1048
- * Removes all listeners and clears any associated timers for the provided WebSocket client.
1049
- * @param ws - The WebSocket client to clean up.
1050
- * @returns void
1051
- */
1052
- cleanup(ws) {
1053
- if (ws) {
1054
- ws.removeAllListeners();
1055
- this.clearTimers(ws);
1056
- }
1057
- }
1058
- /**
1059
- * Handles incoming WebSocket messages
1060
- * @param data Raw message data received
1061
- * @param connection Websocket connection
1062
- */
1063
- onMessage(data, connection) {
1064
- this.emit("message", data.toString(), connection);
1065
- }
1066
- /**
1067
- * Handles the opening of a WebSocket connection.
1068
- * @param url - The URL of the WebSocket server.
1069
- * @param targetConnection - The WebSocket connection being opened.
1070
- * @param oldWSConnection - The WebSocket client instance associated with the old connection.
1071
- */
1072
- onOpen(url, targetConnection, oldWSConnection) {
1073
- this.logger.info(
1074
- `Connected to the Websocket Server with id ${targetConnection.id}: ${url}`
1075
- );
1076
- if (targetConnection.renewalPending) {
1077
- targetConnection.renewalPending = false;
1078
- this.closeConnectionGracefully(oldWSConnection, targetConnection);
1079
- } else if (targetConnection.closeInitiated) {
1080
- this.closeConnectionGracefully(targetConnection.ws, targetConnection);
1081
- } else {
1082
- targetConnection.reconnectionPending = false;
1083
- this.emit("open", this);
1084
- }
1085
- this.sessionReLogon(targetConnection);
1086
- }
1087
- /**
1088
- * Returns the URL to use when reconnecting.
1089
- * Derived classes should override this to provide dynamic URLs.
1090
- * @param defaultURL The URL originally passed during the first connection.
1091
- * @param targetConnection The WebSocket connection being connected.
1092
- * @returns The URL to reconnect to.
1093
- */
1094
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
1095
- getReconnectURL(defaultURL, targetConnection) {
1096
- return defaultURL;
1097
- }
1098
- /**
1099
- * Connects all WebSocket connections in the pool
1100
- * @param url - The Websocket server URL.
1101
- * @returns A promise that resolves when all connections are established.
1102
- */
1103
- async connectPool(url) {
1104
- const connectPromises = this.connectionPool.map(
1105
- (connection) => new Promise((resolve, reject) => {
1106
- this.initConnect(url, false, connection);
1107
- connection.ws?.on("open", () => resolve());
1108
- connection.ws?.on("error", (err) => reject(err));
1109
- connection.ws?.on(
1110
- "close",
1111
- () => reject(new Error("Connection closed unexpectedly."))
1112
- );
1113
- })
1114
- );
1115
- await Promise.all(connectPromises);
1116
- }
1117
- /**
1118
- * Creates a new WebSocket client instance.
1119
- * @param url - The URL to connect to.
1120
- * @returns A new WebSocket client instance.
1121
- */
1122
- createWebSocket(url) {
1123
- const wsClientOptions = {
1124
- perMessageDeflate: this.configuration?.compression,
1125
- agent: this.configuration?.agent
1126
- };
1127
- if (this.configuration.userAgent)
1128
- wsClientOptions.headers = { "User-Agent": this.configuration.userAgent };
1129
- return new import_ws.default(url, wsClientOptions);
1130
- }
1131
- /**
1132
- * Initializes a WebSocket connection.
1133
- * @param url - The Websocket server URL.
1134
- * @param isRenewal - Whether this is a connection renewal.
1135
- * @param connection - An optional WebSocket connection to use.
1136
- * @returns The WebSocket connection.
1137
- */
1138
- initConnect(url, isRenewal = false, connection) {
1139
- const targetConnection = connection || this.getConnection();
1140
- if (targetConnection.renewalPending && isRenewal) {
1141
- this.logger.warn(
1142
- `Connection renewal with id ${targetConnection.id} is already in progress`
1143
- );
1144
- return;
1145
- }
1146
- if (targetConnection.ws && targetConnection.ws.readyState === import_ws.default.OPEN && !isRenewal) {
1147
- this.logger.warn(`Connection with id ${targetConnection.id} already exists`);
1148
- return;
1149
- }
1150
- const ws = this.createWebSocket(url);
1151
- this.logger.info(
1152
- `Establishing Websocket connection with id ${targetConnection.id} to: ${url}`
1153
- );
1154
- if (isRenewal) targetConnection.renewalPending = true;
1155
- else targetConnection.ws = ws;
1156
- targetConnection.isSessionLoggedOn = false;
1157
- this.scheduleTimer(
1158
- ws,
1159
- () => {
1160
- this.logger.info(`Renewing Websocket connection with id ${targetConnection.id}`);
1161
- targetConnection.isSessionLoggedOn = false;
1162
- this.enqueueReconnection(
1163
- targetConnection,
1164
- this.getReconnectURL(url, targetConnection),
1165
- true
1166
- );
1167
- },
1168
- _WebsocketCommon.MAX_CONNECTION_DURATION
1169
- );
1170
- ws.on("open", () => {
1171
- const oldWSConnection = targetConnection.ws;
1172
- if (targetConnection.renewalPending) targetConnection.ws = ws;
1173
- this.onOpen(url, targetConnection, oldWSConnection);
1174
- });
1175
- ws.on("message", (data) => {
1176
- this.onMessage(data.toString(), targetConnection);
1177
- });
1178
- ws.on("ping", () => {
1179
- this.logger.debug("Received PING from server");
1180
- this.emit("ping");
1181
- ws.pong();
1182
- this.logger.debug("Responded PONG to server's PING message");
1183
- });
1184
- ws.on("pong", () => {
1185
- this.logger.debug("Received PONG from server");
1186
- this.emit("pong");
1187
- });
1188
- ws.on("error", (err) => {
1189
- this.logger.error("Received error from server");
1190
- this.logger.error(err);
1191
- this.emit("error", err);
1192
- });
1193
- ws.on("close", (closeEventCode, reason) => {
1194
- this.emit("close", closeEventCode, reason);
1195
- if (!targetConnection.closeInitiated && !isRenewal) {
1196
- this.cleanup(ws);
1197
- this.logger.warn(
1198
- `Connection with id ${targetConnection.id} closed due to ${closeEventCode}: ${reason}`
1199
- );
1200
- this.scheduleTimer(
1201
- ws,
1202
- () => {
1203
- this.logger.info(
1204
- `Reconnecting conection with id ${targetConnection.id} to the server.`
1205
- );
1206
- targetConnection.isSessionLoggedOn = false;
1207
- targetConnection.reconnectionPending = true;
1208
- this.enqueueReconnection(
1209
- targetConnection,
1210
- this.getReconnectURL(url, targetConnection),
1211
- false
1212
- );
1213
- },
1214
- this.configuration?.reconnectDelay ?? 5e3
1215
- );
1216
- }
1217
- });
1218
- return targetConnection;
1219
- }
1220
- /**
1221
- * Checks if the WebSocket connection is currently open.
1222
- * @param connection - An optional WebSocket connection to check. If not provided, the entire connection pool is checked.
1223
- * @returns `true` if the connection is open, `false` otherwise.
1224
- */
1225
- isConnected(connection) {
1226
- const connectionPool = connection ? [connection] : this.connectionPool;
1227
- return connectionPool.some((connection2) => this.isConnectionReady(connection2));
1228
- }
1229
- /**
1230
- * Disconnects from the WebSocket server.
1231
- * If there is no active connection, a warning is logged.
1232
- * Otherwise, all connections in the connection pool are closed gracefully,
1233
- * and a message is logged indicating that the connection has been disconnected.
1234
- * @returns A Promise that resolves when all connections have been closed.
1235
- * @throws Error if the WebSocket client is not set.
1236
- */
1237
- async disconnect() {
1238
- if (!this.isConnected()) this.logger.warn("No connection to close.");
1239
- else {
1240
- this.connectionPool.forEach((connection) => {
1241
- connection.closeInitiated = true;
1242
- connection.isSessionLoggedOn = false;
1243
- connection.sessionLogonReq = void 0;
1244
- });
1245
- const disconnectPromises = this.connectionPool.map(
1246
- (connection) => this.closeConnectionGracefully(connection.ws, connection)
1247
- );
1248
- await Promise.all(disconnectPromises);
1249
- this.logger.info("Disconnected with Binance Websocket Server");
1250
- }
1251
- }
1252
- /**
1253
- * Sends a ping message to all connected Websocket servers in the pool.
1254
- * If no connections are ready, a warning is logged.
1255
- * For each active connection, the ping message is sent, and debug logs provide details.
1256
- * @throws Error if a Websocket client is not set for a connection.
1257
- */
1258
- pingServer() {
1259
- const connectedConnections = this.connectionPool.filter(
1260
- (connection) => this.isConnected(connection)
1261
- );
1262
- if (connectedConnections.length === 0) {
1263
- this.logger.warn("Ping only can be sent when connection is ready.");
1264
- return;
1265
- }
1266
- this.logger.debug("Sending PING to all connected Websocket servers.");
1267
- connectedConnections.forEach((connection) => {
1268
- if (connection.ws) {
1269
- connection.ws.ping();
1270
- this.logger.debug(`PING sent to connection with id ${connection.id}`);
1271
- } else {
1272
- this.logger.error("WebSocket Client not set for a connection.");
1273
- }
1274
- });
1275
- }
1276
- /**
1277
- * Sends a payload through the WebSocket connection.
1278
- * @param payload - Message to send.
1279
- * @param id - Optional request identifier.
1280
- * @param promiseBased - Whether to return a promise.
1281
- * @param timeout - Timeout duration in milliseconds.
1282
- * @param connection - The WebSocket connection to use.
1283
- * @returns A promise if `promiseBased` is true, void otherwise.
1284
- * @throws Error if not connected or WebSocket client is not set.
1285
- */
1286
- send(payload, id, promiseBased = true, timeout = 5e3, connection) {
1287
- if (!this.isConnected(connection)) {
1288
- const errorMsg = "Unable to send message \u2014 connection is not available.";
1289
- this.logger.warn(errorMsg);
1290
- if (promiseBased) return Promise.reject(new Error(errorMsg));
1291
- else throw new Error(errorMsg);
1292
- }
1293
- const connectionToUse = connection ?? this.getConnection();
1294
- if (!connectionToUse.ws) {
1295
- const errorMsg = "Websocket Client not set";
1296
- this.logger.error(errorMsg);
1297
- if (promiseBased) return Promise.reject(new Error(errorMsg));
1298
- else throw new Error(errorMsg);
1299
- }
1300
- connectionToUse.ws.send(payload);
1301
- if (promiseBased) {
1302
- return new Promise((resolve, reject) => {
1303
- if (!id) return reject(new Error("id is required for promise-based sending."));
1304
- const timeoutHandle = setTimeout(() => {
1305
- if (connectionToUse.pendingRequests.has(id)) {
1306
- connectionToUse.pendingRequests.delete(id);
1307
- reject(new Error(`Request timeout for id: ${id}`));
1308
- }
1309
- }, timeout);
1310
- connectionToUse.pendingRequests.set(id, {
1311
- resolve: (v) => {
1312
- clearTimeout(timeoutHandle);
1313
- resolve(v);
1314
- },
1315
- reject: (e) => {
1316
- clearTimeout(timeoutHandle);
1317
- reject(e);
1318
- }
1319
- });
1320
- });
1321
- }
1322
- }
892
+ var WebsocketCommon = class WebsocketCommon extends WebsocketEventEmitter {
893
+ static {
894
+ this.MAX_CONNECTION_DURATION = 1380 * 60 * 1e3;
895
+ }
896
+ constructor(configuration, connectionPool = []) {
897
+ super();
898
+ this.configuration = configuration;
899
+ this.connectionQueue = [];
900
+ this.queueProcessing = false;
901
+ this.connectionTimers = /* @__PURE__ */ new Map();
902
+ this.roundRobinIndex = 0;
903
+ this.logger = Logger.getInstance();
904
+ this.connectionPool = connectionPool;
905
+ this.mode = this.configuration?.mode ?? "single";
906
+ this.poolSize = this.mode === "pool" && this.configuration?.poolSize ? this.configuration.poolSize : 1;
907
+ if (!connectionPool || connectionPool.length === 0) this.initializePool(this.poolSize);
908
+ }
909
+ /**
910
+ * Initializes the WebSocket connection pool by creating a specified number of connection objects
911
+ * and adding them to the `connectionPool` array. Each connection object has the following properties:
912
+ * - `closeInitiated`: a boolean indicating whether the connection has been closed
913
+ * - `reconnectionPending`: a boolean indicating whether a reconnection is pending
914
+ * - `pendingRequests`: a Map that tracks pending requests for the connection
915
+ * @param size - The number of connection objects to create and add to the pool.
916
+ * @returns void
917
+ */
918
+ initializePool(size) {
919
+ for (let i = 0; i < size; i++) this.connectionPool.push({
920
+ id: randomString(),
921
+ closeInitiated: false,
922
+ reconnectionPending: false,
923
+ renewalPending: false,
924
+ pendingRequests: /* @__PURE__ */ new Map(),
925
+ pendingSubscriptions: []
926
+ });
927
+ }
928
+ /**
929
+ * Retrieves available WebSocket connections based on the connection mode and readiness.
930
+ * In 'single' mode, returns the first connection in the pool.
931
+ * In 'pool' mode, filters and returns connections that are ready for use.
932
+ * @param allowNonEstablishedWebsockets - Optional flag to include non-established WebSocket connections.
933
+ * @returns An array of available WebSocket connections.
934
+ */
935
+ getAvailableConnections(allowNonEstablishedWebsockets = false) {
936
+ if (this.mode === "single") return [this.connectionPool[0]];
937
+ return this.connectionPool.filter((connection) => this.isConnectionReady(connection, allowNonEstablishedWebsockets));
938
+ }
939
+ /**
940
+ * Gets a WebSocket connection from the pool or single connection.
941
+ * If the connection mode is 'single', it returns the first connection in the pool.
942
+ * If the connection mode is 'pool', it returns an available connection from the pool,
943
+ * using a round-robin selection strategy. If no available connections are found, it throws an error.
944
+ * @param allowNonEstablishedWebsockets - A boolean indicating whether to allow connections that are not established.
945
+ * @returns {WebsocketConnection} The selected WebSocket connection.
946
+ */
947
+ getConnection(allowNonEstablishedWebsockets = false) {
948
+ const availableConnections = this.getAvailableConnections(allowNonEstablishedWebsockets);
949
+ if (availableConnections.length === 0) throw new Error("No available Websocket connections are ready.");
950
+ const selectedConnection = availableConnections[this.roundRobinIndex % availableConnections.length];
951
+ this.roundRobinIndex = (this.roundRobinIndex + 1) % availableConnections.length;
952
+ return selectedConnection;
953
+ }
954
+ /**
955
+ * Checks if the provided WebSocket connection is ready for use.
956
+ * A connection is considered ready if it is open, has no pending reconnection, and has not been closed.
957
+ * @param connection - The WebSocket connection to check.
958
+ * @param allowNonEstablishedWebsockets - An optional flag to allow non-established WebSocket connections.
959
+ * @returns `true` if the connection is ready, `false` otherwise.
960
+ */
961
+ isConnectionReady(connection, allowNonEstablishedWebsockets = false) {
962
+ return (allowNonEstablishedWebsockets || connection.ws?.readyState === ws.default.OPEN) && !connection.reconnectionPending && !connection.closeInitiated;
963
+ }
964
+ /**
965
+ * Schedules a timer for a WebSocket connection and tracks it
966
+ * @param connection WebSocket client instance
967
+ * @param callback Function to execute when timer triggers
968
+ * @param delay Time in milliseconds before callback execution
969
+ * @param type Timer type ('timeout' or 'interval')
970
+ * @returns Timer handle
971
+ */
972
+ scheduleTimer(connection, callback, delay$1, type = "timeout") {
973
+ let timers = this.connectionTimers.get(connection);
974
+ if (!timers) {
975
+ timers = /* @__PURE__ */ new Set();
976
+ this.connectionTimers.set(connection, timers);
977
+ }
978
+ const timerRecord = { type };
979
+ const wrappedTimeout = () => {
980
+ try {
981
+ callback();
982
+ } finally {
983
+ timers.delete(timerRecord);
984
+ }
985
+ };
986
+ let timer;
987
+ if (type === "timeout") timer = setTimeout(wrappedTimeout, delay$1);
988
+ else timer = setInterval(callback, delay$1);
989
+ timerRecord.timer = timer;
990
+ timers.add(timerRecord);
991
+ return timer;
992
+ }
993
+ /**
994
+ * Clears all timers associated with a WebSocket connection.
995
+ * @param connection - The WebSocket client instance to clear timers for.
996
+ * @returns void
997
+ */
998
+ clearTimers(connection) {
999
+ const timers = this.connectionTimers.get(connection);
1000
+ if (timers) {
1001
+ timers.forEach(({ timer, type }) => {
1002
+ if (type === "timeout") clearTimeout(timer);
1003
+ else if (type === "interval") clearInterval(timer);
1004
+ });
1005
+ this.connectionTimers.delete(connection);
1006
+ }
1007
+ }
1008
+ /**
1009
+ * Processes the connection queue, reconnecting or renewing connections as needed.
1010
+ * This method is responsible for iterating through the connection queue and initiating
1011
+ * the reconnection or renewal process for each connection in the queue. It throttles
1012
+ * the queue processing to avoid overwhelming the server with too many connection
1013
+ * requests at once.
1014
+ * @param throttleRate - The time in milliseconds to wait between processing each
1015
+ * connection in the queue.
1016
+ * @returns A Promise that resolves when the queue has been fully processed.
1017
+ */
1018
+ async processQueue(throttleRate = 1e3) {
1019
+ if (this.queueProcessing) return;
1020
+ this.queueProcessing = true;
1021
+ while (this.connectionQueue.length > 0) {
1022
+ const { connection, url, isRenewal } = this.connectionQueue.shift();
1023
+ this.initConnect(url, isRenewal, connection);
1024
+ await delay(throttleRate);
1025
+ }
1026
+ this.queueProcessing = false;
1027
+ }
1028
+ /**
1029
+ * Enqueues a reconnection or renewal for a WebSocket connection.
1030
+ * This method adds the connection, URL, and renewal flag to the connection queue,
1031
+ * and then calls the `processQueue` method to initiate the reconnection or renewal
1032
+ * process.
1033
+ * @param connection - The WebSocket connection to reconnect or renew.
1034
+ * @param url - The URL to use for the reconnection or renewal.
1035
+ * @param isRenewal - A flag indicating whether this is a renewal (true) or a reconnection (false).
1036
+ */
1037
+ enqueueReconnection(connection, url, isRenewal) {
1038
+ this.connectionQueue.push({
1039
+ connection,
1040
+ url,
1041
+ isRenewal
1042
+ });
1043
+ this.processQueue();
1044
+ }
1045
+ /**
1046
+ * Gracefully closes a WebSocket connection after pending requests complete.
1047
+ * This method waits for any pending requests to complete before closing the connection.
1048
+ * It sets up a timeout to force-close the connection after 30 seconds if the pending requests
1049
+ * do not complete. Once all pending requests are completed, the connection is closed.
1050
+ * @param connectionToClose - The WebSocket client instance to close.
1051
+ * @param WebsocketConnectionToClose - The WebSocket connection to close.
1052
+ * @param connection - The WebSocket connection to close.
1053
+ * @returns Promise that resolves when the connection is closed.
1054
+ */
1055
+ async closeConnectionGracefully(WebsocketConnectionToClose, connection) {
1056
+ if (!WebsocketConnectionToClose || !connection) return;
1057
+ this.logger.debug(`Waiting for pending requests to complete before disconnecting websocket on connection ${connection.id}.`);
1058
+ await new Promise((resolve) => {
1059
+ this.scheduleTimer(WebsocketConnectionToClose, () => {
1060
+ this.logger.warn(`Force-closing websocket connection after 30 seconds on connection ${connection.id}.`);
1061
+ resolve();
1062
+ }, 3e4);
1063
+ this.scheduleTimer(WebsocketConnectionToClose, () => {
1064
+ if (connection.pendingRequests.size === 0) {
1065
+ this.logger.debug(`All pending requests completed, closing websocket connection on connection ${connection.id}.`);
1066
+ resolve();
1067
+ }
1068
+ }, 1e3, "interval");
1069
+ });
1070
+ this.logger.info(`Closing Websocket connection on connection ${connection.id}.`);
1071
+ WebsocketConnectionToClose.close();
1072
+ this.cleanup(WebsocketConnectionToClose);
1073
+ }
1074
+ /**
1075
+ * Attempts to re-establish a session for a WebSocket connection.
1076
+ * If a session logon request exists and the connection is not already logged on,
1077
+ * it sends an authentication request and updates the connection's logged-on status.
1078
+ * @param connection - The WebSocket connection to re-authenticate.
1079
+ * @private
1080
+ */
1081
+ async sessionReLogon(connection) {
1082
+ const req = connection.sessionLogonReq;
1083
+ if (req && !connection.isSessionLoggedOn) {
1084
+ const data = buildWebsocketAPIMessage(this.configuration, req.method, req.payload, req.options);
1085
+ this.logger.debug(`Session re-logon on connection ${connection.id}`, data);
1086
+ try {
1087
+ await this.send(JSON.stringify(data), data.id, true, this.configuration.timeout, connection);
1088
+ this.logger.debug(`Session re-logon on connection ${connection.id} was successful.`);
1089
+ connection.isSessionLoggedOn = true;
1090
+ } catch (err) {
1091
+ this.logger.error(`Session re-logon on connection ${connection.id} failed:`, err);
1092
+ }
1093
+ }
1094
+ }
1095
+ /**
1096
+ * Cleans up WebSocket connection resources.
1097
+ * Removes all listeners and clears any associated timers for the provided WebSocket client.
1098
+ * @param ws - The WebSocket client to clean up.
1099
+ * @returns void
1100
+ */
1101
+ cleanup(ws$1) {
1102
+ if (ws$1) {
1103
+ ws$1.removeAllListeners();
1104
+ this.clearTimers(ws$1);
1105
+ }
1106
+ }
1107
+ /**
1108
+ * Handles incoming WebSocket messages
1109
+ * @param data Raw message data received
1110
+ * @param connection Websocket connection
1111
+ */
1112
+ onMessage(data, connection) {
1113
+ this.emit("message", data.toString(), connection);
1114
+ }
1115
+ /**
1116
+ * Handles the opening of a WebSocket connection.
1117
+ * @param url - The URL of the WebSocket server.
1118
+ * @param targetConnection - The WebSocket connection being opened.
1119
+ * @param oldWSConnection - The WebSocket client instance associated with the old connection.
1120
+ */
1121
+ onOpen(url, targetConnection, oldWSConnection) {
1122
+ this.logger.info(`Connected to the Websocket Server with id ${targetConnection.id}: ${url}`);
1123
+ if (targetConnection.renewalPending) {
1124
+ targetConnection.renewalPending = false;
1125
+ this.closeConnectionGracefully(oldWSConnection, targetConnection);
1126
+ } else if (targetConnection.closeInitiated) this.closeConnectionGracefully(targetConnection.ws, targetConnection);
1127
+ else {
1128
+ targetConnection.reconnectionPending = false;
1129
+ this.emit("open", this);
1130
+ }
1131
+ this.sessionReLogon(targetConnection);
1132
+ }
1133
+ /**
1134
+ * Returns the URL to use when reconnecting.
1135
+ * Derived classes should override this to provide dynamic URLs.
1136
+ * @param defaultURL The URL originally passed during the first connection.
1137
+ * @param targetConnection The WebSocket connection being connected.
1138
+ * @returns The URL to reconnect to.
1139
+ */
1140
+ getReconnectURL(defaultURL, targetConnection) {
1141
+ return defaultURL;
1142
+ }
1143
+ /**
1144
+ * Connects all WebSocket connections in the pool
1145
+ * @param url - The Websocket server URL.
1146
+ * @returns A promise that resolves when all connections are established.
1147
+ */
1148
+ async connectPool(url) {
1149
+ const connectPromises = this.connectionPool.map((connection) => new Promise((resolve, reject) => {
1150
+ this.initConnect(url, false, connection);
1151
+ connection.ws?.on("open", () => resolve());
1152
+ connection.ws?.on("error", (err) => reject(err));
1153
+ connection.ws?.on("close", () => reject(/* @__PURE__ */ new Error("Connection closed unexpectedly.")));
1154
+ }));
1155
+ await Promise.all(connectPromises);
1156
+ }
1157
+ /**
1158
+ * Creates a new WebSocket client instance.
1159
+ * @param url - The URL to connect to.
1160
+ * @returns A new WebSocket client instance.
1161
+ */
1162
+ createWebSocket(url) {
1163
+ const wsClientOptions = {
1164
+ perMessageDeflate: this.configuration?.compression,
1165
+ agent: this.configuration?.agent
1166
+ };
1167
+ if (this.configuration.userAgent) wsClientOptions.headers = { "User-Agent": this.configuration.userAgent };
1168
+ return new ws.default(url, wsClientOptions);
1169
+ }
1170
+ /**
1171
+ * Initializes a WebSocket connection.
1172
+ * @param url - The Websocket server URL.
1173
+ * @param isRenewal - Whether this is a connection renewal.
1174
+ * @param connection - An optional WebSocket connection to use.
1175
+ * @returns The WebSocket connection.
1176
+ */
1177
+ initConnect(url, isRenewal = false, connection) {
1178
+ const targetConnection = connection || this.getConnection();
1179
+ if (targetConnection.renewalPending && isRenewal) {
1180
+ this.logger.warn(`Connection renewal with id ${targetConnection.id} is already in progress`);
1181
+ return;
1182
+ }
1183
+ if (targetConnection.ws && targetConnection.ws.readyState === ws.default.OPEN && !isRenewal) {
1184
+ this.logger.warn(`Connection with id ${targetConnection.id} already exists`);
1185
+ return;
1186
+ }
1187
+ const ws$1 = this.createWebSocket(url);
1188
+ this.logger.info(`Establishing Websocket connection with id ${targetConnection.id} to: ${url}`);
1189
+ if (isRenewal) targetConnection.renewalPending = true;
1190
+ else targetConnection.ws = ws$1;
1191
+ targetConnection.isSessionLoggedOn = false;
1192
+ this.scheduleTimer(ws$1, () => {
1193
+ this.logger.info(`Renewing Websocket connection with id ${targetConnection.id}`);
1194
+ targetConnection.isSessionLoggedOn = false;
1195
+ this.enqueueReconnection(targetConnection, this.getReconnectURL(url, targetConnection), true);
1196
+ }, WebsocketCommon.MAX_CONNECTION_DURATION);
1197
+ ws$1.on("open", () => {
1198
+ const oldWSConnection = targetConnection.ws;
1199
+ if (targetConnection.renewalPending) targetConnection.ws = ws$1;
1200
+ this.onOpen(url, targetConnection, oldWSConnection);
1201
+ });
1202
+ ws$1.on("message", (data) => {
1203
+ this.onMessage(data.toString(), targetConnection);
1204
+ });
1205
+ ws$1.on("ping", () => {
1206
+ this.logger.debug("Received PING from server");
1207
+ this.emit("ping");
1208
+ ws$1.pong();
1209
+ this.logger.debug("Responded PONG to server's PING message");
1210
+ });
1211
+ ws$1.on("pong", () => {
1212
+ this.logger.debug("Received PONG from server");
1213
+ this.emit("pong");
1214
+ });
1215
+ ws$1.on("error", (err) => {
1216
+ this.logger.error("Received error from server");
1217
+ this.logger.error(err);
1218
+ this.emit("error", err);
1219
+ });
1220
+ ws$1.on("close", (closeEventCode, reason) => {
1221
+ this.emit("close", closeEventCode, reason);
1222
+ if (!targetConnection.closeInitiated && !isRenewal) {
1223
+ this.cleanup(ws$1);
1224
+ this.logger.warn(`Connection with id ${targetConnection.id} closed due to ${closeEventCode}: ${reason}`);
1225
+ this.scheduleTimer(ws$1, () => {
1226
+ this.logger.info(`Reconnecting conection with id ${targetConnection.id} to the server.`);
1227
+ targetConnection.isSessionLoggedOn = false;
1228
+ targetConnection.reconnectionPending = true;
1229
+ this.enqueueReconnection(targetConnection, this.getReconnectURL(url, targetConnection), false);
1230
+ }, this.configuration?.reconnectDelay ?? 5e3);
1231
+ }
1232
+ });
1233
+ return targetConnection;
1234
+ }
1235
+ /**
1236
+ * Checks if the WebSocket connection is currently open.
1237
+ * @param connection - An optional WebSocket connection to check. If not provided, the entire connection pool is checked.
1238
+ * @returns `true` if the connection is open, `false` otherwise.
1239
+ */
1240
+ isConnected(connection) {
1241
+ return (connection ? [connection] : this.connectionPool).some((connection$1) => this.isConnectionReady(connection$1));
1242
+ }
1243
+ /**
1244
+ * Disconnects from the WebSocket server.
1245
+ * If there is no active connection, a warning is logged.
1246
+ * Otherwise, all connections in the connection pool are closed gracefully,
1247
+ * and a message is logged indicating that the connection has been disconnected.
1248
+ * @returns A Promise that resolves when all connections have been closed.
1249
+ * @throws Error if the WebSocket client is not set.
1250
+ */
1251
+ async disconnect() {
1252
+ if (!this.isConnected()) this.logger.warn("No connection to close.");
1253
+ else {
1254
+ this.connectionPool.forEach((connection) => {
1255
+ connection.closeInitiated = true;
1256
+ connection.isSessionLoggedOn = false;
1257
+ connection.sessionLogonReq = void 0;
1258
+ });
1259
+ const disconnectPromises = this.connectionPool.map((connection) => this.closeConnectionGracefully(connection.ws, connection));
1260
+ await Promise.all(disconnectPromises);
1261
+ this.logger.info("Disconnected with Binance Websocket Server");
1262
+ }
1263
+ }
1264
+ /**
1265
+ * Sends a ping message to all connected Websocket servers in the pool.
1266
+ * If no connections are ready, a warning is logged.
1267
+ * For each active connection, the ping message is sent, and debug logs provide details.
1268
+ * @throws Error if a Websocket client is not set for a connection.
1269
+ */
1270
+ pingServer() {
1271
+ const connectedConnections = this.connectionPool.filter((connection) => this.isConnected(connection));
1272
+ if (connectedConnections.length === 0) {
1273
+ this.logger.warn("Ping only can be sent when connection is ready.");
1274
+ return;
1275
+ }
1276
+ this.logger.debug("Sending PING to all connected Websocket servers.");
1277
+ connectedConnections.forEach((connection) => {
1278
+ if (connection.ws) {
1279
+ connection.ws.ping();
1280
+ this.logger.debug(`PING sent to connection with id ${connection.id}`);
1281
+ } else this.logger.error("WebSocket Client not set for a connection.");
1282
+ });
1283
+ }
1284
+ /**
1285
+ * Sends a payload through the WebSocket connection.
1286
+ * @param payload - Message to send.
1287
+ * @param id - Optional request identifier.
1288
+ * @param promiseBased - Whether to return a promise.
1289
+ * @param timeout - Timeout duration in milliseconds.
1290
+ * @param connection - The WebSocket connection to use.
1291
+ * @returns A promise if `promiseBased` is true, void otherwise.
1292
+ * @throws Error if not connected or WebSocket client is not set.
1293
+ */
1294
+ send(payload, id, promiseBased = true, timeout = 5e3, connection) {
1295
+ if (!this.isConnected(connection)) {
1296
+ const errorMsg = "Unable to send message — connection is not available.";
1297
+ this.logger.warn(errorMsg);
1298
+ if (promiseBased) return Promise.reject(new Error(errorMsg));
1299
+ else throw new Error(errorMsg);
1300
+ }
1301
+ const connectionToUse = connection ?? this.getConnection();
1302
+ if (!connectionToUse.ws) {
1303
+ const errorMsg = "Websocket Client not set";
1304
+ this.logger.error(errorMsg);
1305
+ if (promiseBased) return Promise.reject(new Error(errorMsg));
1306
+ else throw new Error(errorMsg);
1307
+ }
1308
+ connectionToUse.ws.send(payload);
1309
+ if (promiseBased) return new Promise((resolve, reject) => {
1310
+ if (!id) return reject(/* @__PURE__ */ new Error("id is required for promise-based sending."));
1311
+ const timeoutHandle = setTimeout(() => {
1312
+ if (connectionToUse.pendingRequests.has(id)) {
1313
+ connectionToUse.pendingRequests.delete(id);
1314
+ reject(/* @__PURE__ */ new Error(`Request timeout for id: ${id}`));
1315
+ }
1316
+ }, timeout);
1317
+ connectionToUse.pendingRequests.set(id, {
1318
+ resolve: (v) => {
1319
+ clearTimeout(timeoutHandle);
1320
+ resolve(v);
1321
+ },
1322
+ reject: (e) => {
1323
+ clearTimeout(timeoutHandle);
1324
+ reject(e);
1325
+ }
1326
+ });
1327
+ });
1328
+ }
1323
1329
  };
1324
1330
  var WebsocketAPIBase = class extends WebsocketCommon {
1325
- constructor(configuration, connectionPool = []) {
1326
- super(configuration, connectionPool);
1327
- this.isConnecting = false;
1328
- this.streamCallbackMap = /* @__PURE__ */ new Map();
1329
- this.logger = Logger.getInstance();
1330
- this.configuration = configuration;
1331
- }
1332
- /**
1333
- * Prepares the WebSocket URL by adding optional timeUnit parameter
1334
- * @param wsUrl The base WebSocket URL
1335
- * @returns The formatted WebSocket URL with parameters
1336
- */
1337
- prepareURL(wsUrl) {
1338
- let url = wsUrl;
1339
- if (this?.configuration.timeUnit) {
1340
- try {
1341
- const _timeUnit = validateTimeUnit(this.configuration.timeUnit);
1342
- url = `${url}${url.includes("?") ? "&" : "?"}timeUnit=${_timeUnit}`;
1343
- } catch (err) {
1344
- this.logger.error(err);
1345
- }
1346
- }
1347
- return url;
1348
- }
1349
- /**
1350
- * Processes incoming WebSocket messages
1351
- * @param data The raw message data received
1352
- */
1353
- onMessage(data, connection) {
1354
- try {
1355
- const message = (0, import_json_with_bigint2.JSONParse)(data);
1356
- const { id, status } = message;
1357
- if (id && connection.pendingRequests.has(id)) {
1358
- const request = connection.pendingRequests.get(id);
1359
- connection.pendingRequests.delete(id);
1360
- if (status && status >= 400) {
1361
- request?.reject(message.error);
1362
- } else {
1363
- const response = {
1364
- data: message.result ?? message.response,
1365
- ...message.rateLimits && { rateLimits: message.rateLimits }
1366
- };
1367
- request?.resolve(response);
1368
- }
1369
- } else if ("event" in message && "e" in message["event"] && this.streamCallbackMap.size > 0) {
1370
- this.streamCallbackMap.forEach(
1371
- (callbacks) => callbacks.forEach((callback) => callback(message["event"]))
1372
- );
1373
- } else {
1374
- this.logger.warn("Received response for unknown or timed-out request:", message);
1375
- }
1376
- } catch (error) {
1377
- this.logger.error("Failed to parse WebSocket message:", data, error);
1378
- }
1379
- super.onMessage(data, connection);
1380
- }
1381
- /**
1382
- * Establishes a WebSocket connection to Binance
1383
- * @returns Promise that resolves when connection is established
1384
- * @throws Error if connection times out
1385
- */
1386
- connect() {
1387
- if (this.isConnected()) {
1388
- this.logger.info("WebSocket connection already established");
1389
- return Promise.resolve();
1390
- }
1391
- return new Promise((resolve, reject) => {
1392
- if (this.isConnecting) return;
1393
- this.isConnecting = true;
1394
- const timeout = setTimeout(() => {
1395
- this.isConnecting = false;
1396
- reject(new Error("Websocket connection timed out"));
1397
- }, 1e4);
1398
- this.connectPool(this.prepareURL(this.configuration.wsURL)).then(() => {
1399
- this.isConnecting = false;
1400
- resolve();
1401
- }).catch((error) => {
1402
- this.isConnecting = false;
1403
- reject(error);
1404
- }).finally(() => {
1405
- clearTimeout(timeout);
1406
- });
1407
- });
1408
- }
1409
- async sendMessage(method, payload = {}, options = {}) {
1410
- if (!this.isConnected()) {
1411
- throw new Error("Not connected");
1412
- }
1413
- const isSessionReq = options.isSessionLogon || options.isSessionLogout;
1414
- const connections = isSessionReq ? this.getAvailableConnections() : [this.getConnection()];
1415
- const skipAuth = isSessionReq ? false : this.configuration.autoSessionReLogon && connections[0].isSessionLoggedOn;
1416
- const data = buildWebsocketAPIMessage(
1417
- this.configuration,
1418
- method,
1419
- payload,
1420
- options,
1421
- skipAuth
1422
- );
1423
- this.logger.debug("Send message to Binance WebSocket API Server:", data);
1424
- const responses = await Promise.all(
1425
- connections.map(
1426
- (connection) => this.send(
1427
- JSON.stringify(data),
1428
- data.id,
1429
- true,
1430
- this.configuration.timeout,
1431
- connection
1432
- )
1433
- )
1434
- );
1435
- if (isSessionReq && this.configuration.autoSessionReLogon) {
1436
- connections.forEach((connection) => {
1437
- if (options.isSessionLogon) {
1438
- connection.isSessionLoggedOn = true;
1439
- connection.sessionLogonReq = { method, payload, options };
1440
- } else {
1441
- connection.isSessionLoggedOn = false;
1442
- connection.sessionLogonReq = void 0;
1443
- }
1444
- });
1445
- }
1446
- return connections.length === 1 && !isSessionReq ? responses[0] : responses;
1447
- }
1331
+ constructor(configuration, connectionPool = []) {
1332
+ super(configuration, connectionPool);
1333
+ this.isConnecting = false;
1334
+ this.streamCallbackMap = /* @__PURE__ */ new Map();
1335
+ this.logger = Logger.getInstance();
1336
+ this.configuration = configuration;
1337
+ }
1338
+ /**
1339
+ * Prepares the WebSocket URL by adding optional timeUnit parameter
1340
+ * @param wsUrl The base WebSocket URL
1341
+ * @returns The formatted WebSocket URL with parameters
1342
+ */
1343
+ prepareURL(wsUrl) {
1344
+ let url = wsUrl;
1345
+ if (this?.configuration.timeUnit) try {
1346
+ const _timeUnit = validateTimeUnit(this.configuration.timeUnit);
1347
+ url = `${url}${url.includes("?") ? "&" : "?"}timeUnit=${_timeUnit}`;
1348
+ } catch (err) {
1349
+ this.logger.error(err);
1350
+ }
1351
+ return url;
1352
+ }
1353
+ /**
1354
+ * Processes incoming WebSocket messages
1355
+ * @param data The raw message data received
1356
+ */
1357
+ onMessage(data, connection) {
1358
+ try {
1359
+ const message = (0, json_with_bigint.JSONParse)(data);
1360
+ const { id, status } = message;
1361
+ if (id && connection.pendingRequests.has(id)) {
1362
+ const request = connection.pendingRequests.get(id);
1363
+ connection.pendingRequests.delete(id);
1364
+ if (status && status >= 400) request?.reject(message.error);
1365
+ else {
1366
+ const response = {
1367
+ data: message.result ?? message.response,
1368
+ ...message.rateLimits && { rateLimits: message.rateLimits }
1369
+ };
1370
+ request?.resolve(response);
1371
+ }
1372
+ } else if ("event" in message && "e" in message["event"] && this.streamCallbackMap.size > 0) this.streamCallbackMap.forEach((callbacks) => callbacks.forEach((callback) => callback(message["event"])));
1373
+ else this.logger.warn("Received response for unknown or timed-out request:", message);
1374
+ } catch (error) {
1375
+ this.logger.error("Failed to parse WebSocket message:", data, error);
1376
+ }
1377
+ super.onMessage(data, connection);
1378
+ }
1379
+ /**
1380
+ * Establishes a WebSocket connection to Binance
1381
+ * @returns Promise that resolves when connection is established
1382
+ * @throws Error if connection times out
1383
+ */
1384
+ connect() {
1385
+ if (this.isConnected()) {
1386
+ this.logger.info("WebSocket connection already established");
1387
+ return Promise.resolve();
1388
+ }
1389
+ return new Promise((resolve, reject) => {
1390
+ if (this.isConnecting) return;
1391
+ this.isConnecting = true;
1392
+ const timeout = setTimeout(() => {
1393
+ this.isConnecting = false;
1394
+ reject(/* @__PURE__ */ new Error("Websocket connection timed out"));
1395
+ }, 1e4);
1396
+ this.connectPool(this.prepareURL(this.configuration.wsURL)).then(() => {
1397
+ this.isConnecting = false;
1398
+ resolve();
1399
+ }).catch((error) => {
1400
+ this.isConnecting = false;
1401
+ reject(error);
1402
+ }).finally(() => {
1403
+ clearTimeout(timeout);
1404
+ });
1405
+ });
1406
+ }
1407
+ async sendMessage(method, payload = {}, options = {}) {
1408
+ if (!this.isConnected()) throw new Error("Not connected");
1409
+ const isSessionReq = options.isSessionLogon || options.isSessionLogout;
1410
+ const connections = isSessionReq ? this.getAvailableConnections() : [this.getConnection()];
1411
+ const skipAuth = isSessionReq ? false : this.configuration.autoSessionReLogon && connections[0].isSessionLoggedOn;
1412
+ const data = buildWebsocketAPIMessage(this.configuration, method, payload, options, skipAuth);
1413
+ this.logger.debug("Send message to Binance WebSocket API Server:", data);
1414
+ const responses = await Promise.all(connections.map((connection) => this.send(JSON.stringify(data), data.id, true, this.configuration.timeout, connection)));
1415
+ if (isSessionReq && this.configuration.autoSessionReLogon) connections.forEach((connection) => {
1416
+ if (options.isSessionLogon) {
1417
+ connection.isSessionLoggedOn = true;
1418
+ connection.sessionLogonReq = {
1419
+ method,
1420
+ payload,
1421
+ options
1422
+ };
1423
+ } else {
1424
+ connection.isSessionLoggedOn = false;
1425
+ connection.sessionLogonReq = void 0;
1426
+ }
1427
+ });
1428
+ return connections.length === 1 && !isSessionReq ? responses[0] : responses;
1429
+ }
1448
1430
  };
1449
1431
  var WebsocketStreamsBase = class extends WebsocketCommon {
1450
- constructor(configuration, connectionPool = []) {
1451
- super(configuration, connectionPool);
1452
- this.streamConnectionMap = /* @__PURE__ */ new Map();
1453
- this.streamCallbackMap = /* @__PURE__ */ new Map();
1454
- this.logger = Logger.getInstance();
1455
- this.configuration = configuration;
1456
- this.wsURL = configuration.wsURL;
1457
- }
1458
- /**
1459
- * Formats the WebSocket URL for a given stream or streams.
1460
- * @param streams - Array of stream names to include in the URL.
1461
- * @returns The formatted WebSocket URL with the provided streams.
1462
- */
1463
- prepareURL(streams = []) {
1464
- let url = `${this.wsURL}/stream?streams=${streams.join("/")}`;
1465
- if (this.configuration?.timeUnit) {
1466
- try {
1467
- const _timeUnit = validateTimeUnit(this.configuration.timeUnit);
1468
- url = `${url}${url.includes("?") ? "&" : "?"}timeUnit=${_timeUnit}`;
1469
- } catch (err) {
1470
- this.logger.error(err);
1471
- }
1472
- }
1473
- return url;
1474
- }
1475
- /**
1476
- * Formats the WebSocket URL with stream and configuration parameters to be used for reconnection.
1477
- * @param url - The base WebSocket URL.
1478
- * @param targetConnection - The target WebSocket connection.
1479
- * @returns The formatted WebSocket URL with streams and optional parameters.
1480
- */
1481
- getReconnectURL(url, targetConnection) {
1482
- const streams = Array.from(this.streamConnectionMap.keys()).filter(
1483
- (stream) => this.streamConnectionMap.get(stream) === targetConnection
1484
- );
1485
- return this.prepareURL(streams);
1486
- }
1487
- /**
1488
- * Handles subscription to streams and assigns them to specific connections
1489
- * @param streams Array of stream names to subscribe to
1490
- * @returns Map of connections to streams
1491
- */
1492
- handleStreamAssignment(streams) {
1493
- const connectionStreamMap = /* @__PURE__ */ new Map();
1494
- streams.forEach((stream) => {
1495
- if (!this.streamCallbackMap.has(stream)) this.streamCallbackMap.set(stream, /* @__PURE__ */ new Set());
1496
- let connection = this.streamConnectionMap.get(stream);
1497
- if (!connection || connection.closeInitiated || connection.reconnectionPending) {
1498
- connection = this.getConnection(true);
1499
- this.streamConnectionMap.set(stream, connection);
1500
- }
1501
- if (!connectionStreamMap.has(connection)) connectionStreamMap.set(connection, []);
1502
- connectionStreamMap.get(connection)?.push(stream);
1503
- });
1504
- return connectionStreamMap;
1505
- }
1506
- /**
1507
- * Sends a subscription payload for specified streams on a given connection.
1508
- * @param connection The WebSocket connection to use for sending the subscription.
1509
- * @param streams The streams to subscribe to.
1510
- * @param id Optional ID for the subscription.
1511
- */
1512
- sendSubscriptionPayload(connection, streams, id) {
1513
- const payload = {
1514
- method: "SUBSCRIBE",
1515
- params: streams,
1516
- id: id && /^[0-9a-f]{32}$/.test(id) ? id : randomString()
1517
- };
1518
- this.logger.debug("SUBSCRIBE", payload);
1519
- this.send(JSON.stringify(payload), void 0, false, 0, connection);
1520
- }
1521
- /**
1522
- * Processes pending subscriptions for a given connection.
1523
- * Sends all queued subscriptions in a single payload.
1524
- * @param connection The WebSocket connection to process.
1525
- */
1526
- processPendingSubscriptions(connection) {
1527
- if (connection.pendingSubscriptions && connection.pendingSubscriptions.length > 0) {
1528
- this.logger.info("Processing queued subscriptions for connection");
1529
- this.sendSubscriptionPayload(connection, connection.pendingSubscriptions);
1530
- connection.pendingSubscriptions = [];
1531
- }
1532
- }
1533
- /**
1534
- * Handles incoming WebSocket messages, parsing the data and invoking the appropriate callback function.
1535
- * If the message contains a stream name that is registered in the `streamCallbackMap`, the corresponding
1536
- * callback function is called with the message data.
1537
- * If the message cannot be parsed, an error is logged.
1538
- * @param data The raw WebSocket message data.
1539
- * @param connection The WebSocket connection that received the message.
1540
- */
1541
- onMessage(data, connection) {
1542
- try {
1543
- const parsedData = (0, import_json_with_bigint2.JSONParse)(data);
1544
- const streamName = parsedData?.stream;
1545
- if (streamName && this.streamCallbackMap.has(streamName))
1546
- this.streamCallbackMap.get(streamName)?.forEach((callback) => callback(parsedData.data));
1547
- } catch (error) {
1548
- this.logger.error("Failed to parse WebSocket message:", data, error);
1549
- }
1550
- super.onMessage(data, connection);
1551
- }
1552
- /**
1553
- * Called when the WebSocket connection is opened.
1554
- * Processes any pending subscriptions for the target connection.
1555
- * @param url The URL of the WebSocket connection.
1556
- * @param targetConnection The WebSocket connection that was opened.
1557
- * @param oldConnection The previous WebSocket connection, if any.
1558
- */
1559
- onOpen(url, targetConnection, oldWSConnection) {
1560
- this.processPendingSubscriptions(targetConnection);
1561
- super.onOpen(url, targetConnection, oldWSConnection);
1562
- }
1563
- /**
1564
- * Connects to the WebSocket server and subscribes to the specified streams.
1565
- * This method returns a Promise that resolves when the connection is established,
1566
- * or rejects with an error if the connection fails to be established within 10 seconds.
1567
- * @param stream - A single stream name or an array of stream names to subscribe to.
1568
- * @returns A Promise that resolves when the connection is established.
1569
- */
1570
- connect(stream = []) {
1571
- const streams = Array.isArray(stream) ? stream : [stream];
1572
- return new Promise((resolve, reject) => {
1573
- const timeout = setTimeout(() => {
1574
- reject(new Error("Websocket connection timed out"));
1575
- }, 1e4);
1576
- this.connectPool(this.prepareURL(streams)).then(() => resolve()).catch((error) => reject(error)).finally(() => clearTimeout(timeout));
1577
- });
1578
- }
1579
- /**
1580
- * Disconnects the WebSocket connection and clears the stream callback map.
1581
- * This method is called to clean up the connection and associated resources.
1582
- */
1583
- async disconnect() {
1584
- this.streamCallbackMap.clear();
1585
- this.streamConnectionMap.clear();
1586
- super.disconnect();
1587
- }
1588
- /**
1589
- * Subscribes to one or multiple WebSocket streams
1590
- * Handles both single and pool modes
1591
- * @param stream Single stream name or array of stream names to subscribe to
1592
- * @param id Optional subscription ID
1593
- * @returns void
1594
- */
1595
- subscribe(stream, id) {
1596
- const streams = (Array.isArray(stream) ? stream : [stream]).filter(
1597
- (stream2) => !this.streamConnectionMap.has(stream2)
1598
- );
1599
- const connectionStreamMap = this.handleStreamAssignment(streams);
1600
- connectionStreamMap.forEach((streams2, connection) => {
1601
- if (!this.isConnected(connection)) {
1602
- this.logger.info(
1603
- `Connection ${connection.id} is not ready. Queuing subscription for streams: ${streams2}`
1604
- );
1605
- connection.pendingSubscriptions?.push(...streams2);
1606
- return;
1607
- }
1608
- this.sendSubscriptionPayload(connection, streams2, id);
1609
- });
1610
- }
1611
- /**
1612
- * Unsubscribes from one or multiple WebSocket streams
1613
- * Handles both single and pool modes
1614
- * @param stream Single stream name or array of stream names to unsubscribe from
1615
- * @param id Optional unsubscription ID
1616
- * @returns void
1617
- */
1618
- unsubscribe(stream, id) {
1619
- const streams = Array.isArray(stream) ? stream : [stream];
1620
- streams.forEach((stream2) => {
1621
- const connection = this.streamConnectionMap.get(stream2);
1622
- if (!connection || !connection.ws || !this.isConnected(connection)) {
1623
- this.logger.warn(`Stream ${stream2} not associated with an active connection.`);
1624
- return;
1625
- }
1626
- if (!this.streamCallbackMap.has(stream2) || this.streamCallbackMap.get(stream2)?.size === 0) {
1627
- const payload = {
1628
- method: "UNSUBSCRIBE",
1629
- params: [stream2],
1630
- id: id && /^[0-9a-f]{32}$/.test(id) ? id : randomString()
1631
- };
1632
- this.logger.debug("UNSUBSCRIBE", payload);
1633
- this.send(JSON.stringify(payload), void 0, false, 0, connection);
1634
- this.streamConnectionMap.delete(stream2);
1635
- this.streamCallbackMap.delete(stream2);
1636
- }
1637
- });
1638
- }
1639
- /**
1640
- * Checks if the specified stream is currently subscribed.
1641
- * @param stream - The name of the stream to check.
1642
- * @returns `true` if the stream is currently subscribed, `false` otherwise.
1643
- */
1644
- isSubscribed(stream) {
1645
- return this.streamConnectionMap.has(stream);
1646
- }
1432
+ constructor(configuration, connectionPool = []) {
1433
+ super(configuration, connectionPool);
1434
+ this.streamConnectionMap = /* @__PURE__ */ new Map();
1435
+ this.streamCallbackMap = /* @__PURE__ */ new Map();
1436
+ this.logger = Logger.getInstance();
1437
+ this.configuration = configuration;
1438
+ this.wsURL = configuration.wsURL;
1439
+ }
1440
+ /**
1441
+ * Formats the WebSocket URL for a given stream or streams.
1442
+ * @param streams - Array of stream names to include in the URL.
1443
+ * @returns The formatted WebSocket URL with the provided streams.
1444
+ */
1445
+ prepareURL(streams = []) {
1446
+ let url = `${this.wsURL}/stream?streams=${streams.join("/")}`;
1447
+ if (this.configuration?.timeUnit) try {
1448
+ const _timeUnit = validateTimeUnit(this.configuration.timeUnit);
1449
+ url = `${url}${url.includes("?") ? "&" : "?"}timeUnit=${_timeUnit}`;
1450
+ } catch (err) {
1451
+ this.logger.error(err);
1452
+ }
1453
+ return url;
1454
+ }
1455
+ /**
1456
+ * Formats the WebSocket URL with stream and configuration parameters to be used for reconnection.
1457
+ * @param url - The base WebSocket URL.
1458
+ * @param targetConnection - The target WebSocket connection.
1459
+ * @returns The formatted WebSocket URL with streams and optional parameters.
1460
+ */
1461
+ getReconnectURL(url, targetConnection) {
1462
+ const streams = Array.from(this.streamConnectionMap.keys()).filter((stream) => this.streamConnectionMap.get(stream) === targetConnection);
1463
+ return this.prepareURL(streams);
1464
+ }
1465
+ /**
1466
+ * Handles subscription to streams and assigns them to specific connections
1467
+ * @param streams Array of stream names to subscribe to
1468
+ * @returns Map of connections to streams
1469
+ */
1470
+ handleStreamAssignment(streams) {
1471
+ const connectionStreamMap = /* @__PURE__ */ new Map();
1472
+ streams.forEach((stream) => {
1473
+ if (!this.streamCallbackMap.has(stream)) this.streamCallbackMap.set(stream, /* @__PURE__ */ new Set());
1474
+ let connection = this.streamConnectionMap.get(stream);
1475
+ if (!connection || connection.closeInitiated || connection.reconnectionPending) {
1476
+ connection = this.getConnection(true);
1477
+ this.streamConnectionMap.set(stream, connection);
1478
+ }
1479
+ if (!connectionStreamMap.has(connection)) connectionStreamMap.set(connection, []);
1480
+ connectionStreamMap.get(connection)?.push(stream);
1481
+ });
1482
+ return connectionStreamMap;
1483
+ }
1484
+ /**
1485
+ * Sends a subscription payload for specified streams on a given connection.
1486
+ * @param connection The WebSocket connection to use for sending the subscription.
1487
+ * @param streams The streams to subscribe to.
1488
+ * @param id Optional ID for the subscription.
1489
+ */
1490
+ sendSubscriptionPayload(connection, streams, id) {
1491
+ const payload = {
1492
+ method: "SUBSCRIBE",
1493
+ params: streams,
1494
+ id: id && /^[0-9a-f]{32}$/.test(id) ? id : randomString()
1495
+ };
1496
+ this.logger.debug("SUBSCRIBE", payload);
1497
+ this.send(JSON.stringify(payload), void 0, false, 0, connection);
1498
+ }
1499
+ /**
1500
+ * Processes pending subscriptions for a given connection.
1501
+ * Sends all queued subscriptions in a single payload.
1502
+ * @param connection The WebSocket connection to process.
1503
+ */
1504
+ processPendingSubscriptions(connection) {
1505
+ if (connection.pendingSubscriptions && connection.pendingSubscriptions.length > 0) {
1506
+ this.logger.info("Processing queued subscriptions for connection");
1507
+ this.sendSubscriptionPayload(connection, connection.pendingSubscriptions);
1508
+ connection.pendingSubscriptions = [];
1509
+ }
1510
+ }
1511
+ /**
1512
+ * Handles incoming WebSocket messages, parsing the data and invoking the appropriate callback function.
1513
+ * If the message contains a stream name that is registered in the `streamCallbackMap`, the corresponding
1514
+ * callback function is called with the message data.
1515
+ * If the message cannot be parsed, an error is logged.
1516
+ * @param data The raw WebSocket message data.
1517
+ * @param connection The WebSocket connection that received the message.
1518
+ */
1519
+ onMessage(data, connection) {
1520
+ try {
1521
+ const parsedData = (0, json_with_bigint.JSONParse)(data);
1522
+ const streamName = parsedData?.stream;
1523
+ if (streamName && this.streamCallbackMap.has(streamName)) this.streamCallbackMap.get(streamName)?.forEach((callback) => callback(parsedData.data));
1524
+ } catch (error) {
1525
+ this.logger.error("Failed to parse WebSocket message:", data, error);
1526
+ }
1527
+ super.onMessage(data, connection);
1528
+ }
1529
+ /**
1530
+ * Called when the WebSocket connection is opened.
1531
+ * Processes any pending subscriptions for the target connection.
1532
+ * @param url The URL of the WebSocket connection.
1533
+ * @param targetConnection The WebSocket connection that was opened.
1534
+ * @param oldConnection The previous WebSocket connection, if any.
1535
+ */
1536
+ onOpen(url, targetConnection, oldWSConnection) {
1537
+ this.processPendingSubscriptions(targetConnection);
1538
+ super.onOpen(url, targetConnection, oldWSConnection);
1539
+ }
1540
+ /**
1541
+ * Connects to the WebSocket server and subscribes to the specified streams.
1542
+ * This method returns a Promise that resolves when the connection is established,
1543
+ * or rejects with an error if the connection fails to be established within 10 seconds.
1544
+ * @param stream - A single stream name or an array of stream names to subscribe to.
1545
+ * @returns A Promise that resolves when the connection is established.
1546
+ */
1547
+ connect(stream = []) {
1548
+ const streams = Array.isArray(stream) ? stream : [stream];
1549
+ return new Promise((resolve, reject) => {
1550
+ const timeout = setTimeout(() => {
1551
+ reject(/* @__PURE__ */ new Error("Websocket connection timed out"));
1552
+ }, 1e4);
1553
+ this.connectPool(this.prepareURL(streams)).then(() => resolve()).catch((error) => reject(error)).finally(() => clearTimeout(timeout));
1554
+ });
1555
+ }
1556
+ /**
1557
+ * Disconnects the WebSocket connection and clears the stream callback map.
1558
+ * This method is called to clean up the connection and associated resources.
1559
+ */
1560
+ async disconnect() {
1561
+ this.streamCallbackMap.clear();
1562
+ this.streamConnectionMap.clear();
1563
+ super.disconnect();
1564
+ }
1565
+ /**
1566
+ * Subscribes to one or multiple WebSocket streams
1567
+ * Handles both single and pool modes
1568
+ * @param stream Single stream name or array of stream names to subscribe to
1569
+ * @param id Optional subscription ID
1570
+ * @returns void
1571
+ */
1572
+ subscribe(stream, id) {
1573
+ const streams = (Array.isArray(stream) ? stream : [stream]).filter((stream$1) => !this.streamConnectionMap.has(stream$1));
1574
+ this.handleStreamAssignment(streams).forEach((streams$1, connection) => {
1575
+ if (!this.isConnected(connection)) {
1576
+ this.logger.info(`Connection ${connection.id} is not ready. Queuing subscription for streams: ${streams$1}`);
1577
+ connection.pendingSubscriptions?.push(...streams$1);
1578
+ return;
1579
+ }
1580
+ this.sendSubscriptionPayload(connection, streams$1, id);
1581
+ });
1582
+ }
1583
+ /**
1584
+ * Unsubscribes from one or multiple WebSocket streams
1585
+ * Handles both single and pool modes
1586
+ * @param stream Single stream name or array of stream names to unsubscribe from
1587
+ * @param id Optional unsubscription ID
1588
+ * @returns void
1589
+ */
1590
+ unsubscribe(stream, id) {
1591
+ (Array.isArray(stream) ? stream : [stream]).forEach((stream$1) => {
1592
+ const connection = this.streamConnectionMap.get(stream$1);
1593
+ if (!connection || !connection.ws || !this.isConnected(connection)) {
1594
+ this.logger.warn(`Stream ${stream$1} not associated with an active connection.`);
1595
+ return;
1596
+ }
1597
+ if (!this.streamCallbackMap.has(stream$1) || this.streamCallbackMap.get(stream$1)?.size === 0) {
1598
+ const payload = {
1599
+ method: "UNSUBSCRIBE",
1600
+ params: [stream$1],
1601
+ id: id && /^[0-9a-f]{32}$/.test(id) ? id : randomString()
1602
+ };
1603
+ this.logger.debug("UNSUBSCRIBE", payload);
1604
+ this.send(JSON.stringify(payload), void 0, false, 0, connection);
1605
+ this.streamConnectionMap.delete(stream$1);
1606
+ this.streamCallbackMap.delete(stream$1);
1607
+ }
1608
+ });
1609
+ }
1610
+ /**
1611
+ * Checks if the specified stream is currently subscribed.
1612
+ * @param stream - The name of the stream to check.
1613
+ * @returns `true` if the stream is currently subscribed, `false` otherwise.
1614
+ */
1615
+ isSubscribed(stream) {
1616
+ return this.streamConnectionMap.has(stream);
1617
+ }
1647
1618
  };
1619
+ /**
1620
+ * Creates a WebSocket stream handler for managing stream subscriptions and callbacks.
1621
+ *
1622
+ * @template T The type of data expected in the stream messages
1623
+ * @param {WebsocketAPIBase | WebsocketStreamsBase} websocketBase The WebSocket base instance
1624
+ * @param {string} streamOrId The stream identifier
1625
+ * @param {string} [id] Optional additional identifier
1626
+ * @returns {WebsocketStream<T>} A stream handler with methods to register callbacks and unsubscribe
1627
+ */
1648
1628
  function createStreamHandler(websocketBase, streamOrId, id) {
1649
- if (websocketBase instanceof WebsocketStreamsBase) websocketBase.subscribe(streamOrId, id);
1650
- let registeredCallback;
1651
- return {
1652
- on: (event, callback) => {
1653
- if (event === "message") {
1654
- registeredCallback = (data) => {
1655
- Promise.resolve(callback(data)).catch((err) => {
1656
- websocketBase.logger.error(`Error in stream callback: ${err}`);
1657
- });
1658
- };
1659
- const callbackSet = websocketBase.streamCallbackMap.get(streamOrId) ?? /* @__PURE__ */ new Set();
1660
- callbackSet.add(registeredCallback);
1661
- websocketBase.streamCallbackMap.set(streamOrId, callbackSet);
1662
- }
1663
- },
1664
- unsubscribe: () => {
1665
- if (registeredCallback)
1666
- websocketBase.streamCallbackMap.get(streamOrId)?.delete(registeredCallback);
1667
- if (websocketBase instanceof WebsocketStreamsBase)
1668
- websocketBase.unsubscribe(streamOrId, id);
1669
- }
1670
- };
1629
+ if (websocketBase instanceof WebsocketStreamsBase) websocketBase.subscribe(streamOrId, id);
1630
+ let registeredCallback;
1631
+ return {
1632
+ on: (event, callback) => {
1633
+ if (event === "message") {
1634
+ registeredCallback = (data) => {
1635
+ Promise.resolve(callback(data)).catch((err) => {
1636
+ websocketBase.logger.error(`Error in stream callback: ${err}`);
1637
+ });
1638
+ };
1639
+ const callbackSet = websocketBase.streamCallbackMap.get(streamOrId) ?? /* @__PURE__ */ new Set();
1640
+ callbackSet.add(registeredCallback);
1641
+ websocketBase.streamCallbackMap.set(streamOrId, callbackSet);
1642
+ }
1643
+ },
1644
+ unsubscribe: () => {
1645
+ if (registeredCallback) websocketBase.streamCallbackMap.get(streamOrId)?.delete(registeredCallback);
1646
+ if (websocketBase instanceof WebsocketStreamsBase) websocketBase.unsubscribe(streamOrId, id);
1647
+ }
1648
+ };
1671
1649
  }
1672
- // Annotate the CommonJS export names for ESM import in node:
1673
- 0 && (module.exports = {
1674
- ALGO_REST_API_PROD_URL,
1675
- BadRequestError,
1676
- C2C_REST_API_PROD_URL,
1677
- CONVERT_REST_API_PROD_URL,
1678
- COPY_TRADING_REST_API_PROD_URL,
1679
- CRYPTO_LOAN_REST_API_PROD_URL,
1680
- ConfigurationRestAPI,
1681
- ConfigurationWebsocketAPI,
1682
- ConfigurationWebsocketStreams,
1683
- ConnectorClientError,
1684
- DERIVATIVES_TRADING_COIN_FUTURES_REST_API_PROD_URL,
1685
- DERIVATIVES_TRADING_COIN_FUTURES_REST_API_TESTNET_URL,
1686
- DERIVATIVES_TRADING_COIN_FUTURES_WS_API_PROD_URL,
1687
- DERIVATIVES_TRADING_COIN_FUTURES_WS_API_TESTNET_URL,
1688
- DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_PROD_URL,
1689
- DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_TESTNET_URL,
1690
- DERIVATIVES_TRADING_OPTIONS_REST_API_PROD_URL,
1691
- DERIVATIVES_TRADING_OPTIONS_WS_STREAMS_PROD_URL,
1692
- DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_REST_API_PROD_URL,
1693
- DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_WS_STREAMS_PROD_URL,
1694
- DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_PROD_URL,
1695
- DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_TESTNET_URL,
1696
- DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_PROD_URL,
1697
- DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_TESTNET_URL,
1698
- DERIVATIVES_TRADING_USDS_FUTURES_REST_API_PROD_URL,
1699
- DERIVATIVES_TRADING_USDS_FUTURES_REST_API_TESTNET_URL,
1700
- DERIVATIVES_TRADING_USDS_FUTURES_WS_API_PROD_URL,
1701
- DERIVATIVES_TRADING_USDS_FUTURES_WS_API_TESTNET_URL,
1702
- DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_PROD_URL,
1703
- DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_TESTNET_URL,
1704
- DUAL_INVESTMENT_REST_API_PROD_URL,
1705
- FIAT_REST_API_PROD_URL,
1706
- ForbiddenError,
1707
- GIFT_CARD_REST_API_PROD_URL,
1708
- LogLevel,
1709
- Logger,
1710
- MARGIN_TRADING_REST_API_PROD_URL,
1711
- MARGIN_TRADING_RISK_WS_STREAMS_PROD_URL,
1712
- MARGIN_TRADING_WS_STREAMS_PROD_URL,
1713
- MINING_REST_API_PROD_URL,
1714
- NFT_REST_API_PROD_URL,
1715
- NetworkError,
1716
- NotFoundError,
1717
- PAY_REST_API_PROD_URL,
1718
- REBATE_REST_API_PROD_URL,
1719
- RateLimitBanError,
1720
- RequiredError,
1721
- SIMPLE_EARN_REST_API_PROD_URL,
1722
- SPOT_REST_API_MARKET_URL,
1723
- SPOT_REST_API_PROD_URL,
1724
- SPOT_REST_API_TESTNET_URL,
1725
- SPOT_WS_API_PROD_URL,
1726
- SPOT_WS_API_TESTNET_URL,
1727
- SPOT_WS_STREAMS_MARKET_URL,
1728
- SPOT_WS_STREAMS_PROD_URL,
1729
- SPOT_WS_STREAMS_TESTNET_URL,
1730
- STAKING_REST_API_PROD_URL,
1731
- SUB_ACCOUNT_REST_API_PROD_URL,
1732
- ServerError,
1733
- TimeUnit,
1734
- TooManyRequestsError,
1735
- UnauthorizedError,
1736
- VIP_LOAN_REST_API_PROD_URL,
1737
- WALLET_REST_API_PROD_URL,
1738
- WebsocketAPIBase,
1739
- WebsocketCommon,
1740
- WebsocketEventEmitter,
1741
- WebsocketStreamsBase,
1742
- assertParamExists,
1743
- buildQueryString,
1744
- buildUserAgent,
1745
- buildWebsocketAPIMessage,
1746
- clearSignerCache,
1747
- createStreamHandler,
1748
- delay,
1749
- getSignature,
1750
- getTimestamp,
1751
- httpRequestFunction,
1752
- normalizeScientificNumbers,
1753
- parseCustomHeaders,
1754
- parseRateLimitHeaders,
1755
- randomString,
1756
- removeEmptyValue,
1757
- replaceWebsocketStreamsPlaceholders,
1758
- sanitizeHeaderValue,
1759
- sendRequest,
1760
- setSearchParams,
1761
- shouldRetryRequest,
1762
- sortObject,
1763
- toPathString,
1764
- validateTimeUnit
1765
- });
1650
+
1651
+ //#endregion
1652
+ exports.ALGO_REST_API_PROD_URL = ALGO_REST_API_PROD_URL;
1653
+ exports.BadRequestError = BadRequestError;
1654
+ exports.C2C_REST_API_PROD_URL = C2C_REST_API_PROD_URL;
1655
+ exports.CONVERT_REST_API_PROD_URL = CONVERT_REST_API_PROD_URL;
1656
+ exports.COPY_TRADING_REST_API_PROD_URL = COPY_TRADING_REST_API_PROD_URL;
1657
+ exports.CRYPTO_LOAN_REST_API_PROD_URL = CRYPTO_LOAN_REST_API_PROD_URL;
1658
+ exports.ConfigurationRestAPI = ConfigurationRestAPI;
1659
+ exports.ConfigurationWebsocketAPI = ConfigurationWebsocketAPI;
1660
+ exports.ConfigurationWebsocketStreams = ConfigurationWebsocketStreams;
1661
+ exports.ConnectorClientError = ConnectorClientError;
1662
+ exports.DERIVATIVES_TRADING_COIN_FUTURES_REST_API_PROD_URL = DERIVATIVES_TRADING_COIN_FUTURES_REST_API_PROD_URL;
1663
+ exports.DERIVATIVES_TRADING_COIN_FUTURES_REST_API_TESTNET_URL = DERIVATIVES_TRADING_COIN_FUTURES_REST_API_TESTNET_URL;
1664
+ exports.DERIVATIVES_TRADING_COIN_FUTURES_WS_API_PROD_URL = DERIVATIVES_TRADING_COIN_FUTURES_WS_API_PROD_URL;
1665
+ exports.DERIVATIVES_TRADING_COIN_FUTURES_WS_API_TESTNET_URL = DERIVATIVES_TRADING_COIN_FUTURES_WS_API_TESTNET_URL;
1666
+ exports.DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_PROD_URL = DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_PROD_URL;
1667
+ exports.DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_TESTNET_URL = DERIVATIVES_TRADING_COIN_FUTURES_WS_STREAMS_TESTNET_URL;
1668
+ exports.DERIVATIVES_TRADING_OPTIONS_REST_API_PROD_URL = DERIVATIVES_TRADING_OPTIONS_REST_API_PROD_URL;
1669
+ exports.DERIVATIVES_TRADING_OPTIONS_WS_STREAMS_PROD_URL = DERIVATIVES_TRADING_OPTIONS_WS_STREAMS_PROD_URL;
1670
+ exports.DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_REST_API_PROD_URL = DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_REST_API_PROD_URL;
1671
+ exports.DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_WS_STREAMS_PROD_URL = DERIVATIVES_TRADING_PORTFOLIO_MARGIN_PRO_WS_STREAMS_PROD_URL;
1672
+ exports.DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_PROD_URL = DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_PROD_URL;
1673
+ exports.DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_TESTNET_URL = DERIVATIVES_TRADING_PORTFOLIO_MARGIN_REST_API_TESTNET_URL;
1674
+ exports.DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_PROD_URL = DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_PROD_URL;
1675
+ exports.DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_TESTNET_URL = DERIVATIVES_TRADING_PORTFOLIO_MARGIN_WS_STREAMS_TESTNET_URL;
1676
+ exports.DERIVATIVES_TRADING_USDS_FUTURES_REST_API_PROD_URL = DERIVATIVES_TRADING_USDS_FUTURES_REST_API_PROD_URL;
1677
+ exports.DERIVATIVES_TRADING_USDS_FUTURES_REST_API_TESTNET_URL = DERIVATIVES_TRADING_USDS_FUTURES_REST_API_TESTNET_URL;
1678
+ exports.DERIVATIVES_TRADING_USDS_FUTURES_WS_API_PROD_URL = DERIVATIVES_TRADING_USDS_FUTURES_WS_API_PROD_URL;
1679
+ exports.DERIVATIVES_TRADING_USDS_FUTURES_WS_API_TESTNET_URL = DERIVATIVES_TRADING_USDS_FUTURES_WS_API_TESTNET_URL;
1680
+ exports.DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_PROD_URL = DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_PROD_URL;
1681
+ exports.DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_TESTNET_URL = DERIVATIVES_TRADING_USDS_FUTURES_WS_STREAMS_TESTNET_URL;
1682
+ exports.DUAL_INVESTMENT_REST_API_PROD_URL = DUAL_INVESTMENT_REST_API_PROD_URL;
1683
+ exports.FIAT_REST_API_PROD_URL = FIAT_REST_API_PROD_URL;
1684
+ exports.ForbiddenError = ForbiddenError;
1685
+ exports.GIFT_CARD_REST_API_PROD_URL = GIFT_CARD_REST_API_PROD_URL;
1686
+ exports.LogLevel = LogLevel;
1687
+ exports.Logger = Logger;
1688
+ exports.MARGIN_TRADING_REST_API_PROD_URL = MARGIN_TRADING_REST_API_PROD_URL;
1689
+ exports.MARGIN_TRADING_RISK_WS_STREAMS_PROD_URL = MARGIN_TRADING_RISK_WS_STREAMS_PROD_URL;
1690
+ exports.MARGIN_TRADING_WS_STREAMS_PROD_URL = MARGIN_TRADING_WS_STREAMS_PROD_URL;
1691
+ exports.MINING_REST_API_PROD_URL = MINING_REST_API_PROD_URL;
1692
+ exports.NFT_REST_API_PROD_URL = NFT_REST_API_PROD_URL;
1693
+ exports.NetworkError = NetworkError;
1694
+ exports.NotFoundError = NotFoundError;
1695
+ exports.PAY_REST_API_PROD_URL = PAY_REST_API_PROD_URL;
1696
+ exports.REBATE_REST_API_PROD_URL = REBATE_REST_API_PROD_URL;
1697
+ exports.RateLimitBanError = RateLimitBanError;
1698
+ exports.RequiredError = RequiredError;
1699
+ exports.SIMPLE_EARN_REST_API_PROD_URL = SIMPLE_EARN_REST_API_PROD_URL;
1700
+ exports.SPOT_REST_API_MARKET_URL = SPOT_REST_API_MARKET_URL;
1701
+ exports.SPOT_REST_API_PROD_URL = SPOT_REST_API_PROD_URL;
1702
+ exports.SPOT_REST_API_TESTNET_URL = SPOT_REST_API_TESTNET_URL;
1703
+ exports.SPOT_WS_API_PROD_URL = SPOT_WS_API_PROD_URL;
1704
+ exports.SPOT_WS_API_TESTNET_URL = SPOT_WS_API_TESTNET_URL;
1705
+ exports.SPOT_WS_STREAMS_MARKET_URL = SPOT_WS_STREAMS_MARKET_URL;
1706
+ exports.SPOT_WS_STREAMS_PROD_URL = SPOT_WS_STREAMS_PROD_URL;
1707
+ exports.SPOT_WS_STREAMS_TESTNET_URL = SPOT_WS_STREAMS_TESTNET_URL;
1708
+ exports.STAKING_REST_API_PROD_URL = STAKING_REST_API_PROD_URL;
1709
+ exports.SUB_ACCOUNT_REST_API_PROD_URL = SUB_ACCOUNT_REST_API_PROD_URL;
1710
+ exports.ServerError = ServerError;
1711
+ exports.TimeUnit = TimeUnit;
1712
+ exports.TooManyRequestsError = TooManyRequestsError;
1713
+ exports.UnauthorizedError = UnauthorizedError;
1714
+ exports.VIP_LOAN_REST_API_PROD_URL = VIP_LOAN_REST_API_PROD_URL;
1715
+ exports.WALLET_REST_API_PROD_URL = WALLET_REST_API_PROD_URL;
1716
+ exports.WebsocketAPIBase = WebsocketAPIBase;
1717
+ exports.WebsocketCommon = WebsocketCommon;
1718
+ exports.WebsocketEventEmitter = WebsocketEventEmitter;
1719
+ exports.WebsocketStreamsBase = WebsocketStreamsBase;
1720
+ exports.assertParamExists = assertParamExists;
1721
+ exports.buildQueryString = buildQueryString;
1722
+ exports.buildUserAgent = buildUserAgent;
1723
+ exports.buildWebsocketAPIMessage = buildWebsocketAPIMessage;
1724
+ exports.clearSignerCache = clearSignerCache;
1725
+ exports.createStreamHandler = createStreamHandler;
1726
+ exports.delay = delay;
1727
+ exports.getSignature = getSignature;
1728
+ exports.getTimestamp = getTimestamp;
1729
+ exports.httpRequestFunction = httpRequestFunction;
1730
+ exports.normalizeScientificNumbers = normalizeScientificNumbers;
1731
+ exports.parseCustomHeaders = parseCustomHeaders;
1732
+ exports.parseRateLimitHeaders = parseRateLimitHeaders;
1733
+ exports.randomString = randomString;
1734
+ exports.removeEmptyValue = removeEmptyValue;
1735
+ exports.replaceWebsocketStreamsPlaceholders = replaceWebsocketStreamsPlaceholders;
1736
+ exports.sanitizeHeaderValue = sanitizeHeaderValue;
1737
+ exports.sendRequest = sendRequest;
1738
+ exports.setSearchParams = setSearchParams;
1739
+ exports.shouldRetryRequest = shouldRetryRequest;
1740
+ exports.sortObject = sortObject;
1741
+ exports.toPathString = toPathString;
1742
+ exports.validateTimeUnit = validateTimeUnit;
1766
1743
  //# sourceMappingURL=index.js.map