UNPKG

mexc-futures-sdk

Version:

Unofficial TypeScript SDK for MEXC Futures trading with maintenance bypass. Uses browser session tokens to work 24/7 even during API downtime.

246 lines 9.48 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MexcRateLimitError = exports.MexcSignatureError = exports.MexcValidationError = exports.MexcNetworkError = exports.MexcApiError = exports.MexcAuthenticationError = exports.MexcFuturesError = void 0; exports.redactAxiosError = redactAxiosError; exports.parseAxiosError = parseAxiosError; exports.formatErrorForLogging = formatErrorForLogging; /** * Base error class for MEXC Futures SDK */ class MexcFuturesError extends Error { constructor(message, code, statusCode, originalError) { super(message); this.name = this.constructor.name; this.code = code; this.statusCode = statusCode; this.originalError = originalError; this.timestamp = new Date(); // Ensure proper prototype chain for instanceof checks Object.setPrototypeOf(this, new.target.prototype); } /** * Get a user-friendly error message */ getUserFriendlyMessage() { return this.message; } /** * Get error details for logging */ getDetails() { return { name: this.name, message: this.message, code: this.code, statusCode: this.statusCode, timestamp: this.timestamp.toISOString(), }; } } exports.MexcFuturesError = MexcFuturesError; /** * Authentication related errors */ class MexcAuthenticationError extends MexcFuturesError { constructor(message, originalError) { const defaultMessage = "Authentication failed. Please check your authorization token."; super(message || defaultMessage, "AUTH_ERROR", 401, originalError); } getUserFriendlyMessage() { switch (this.code) { case 401: return "Authentication failed. Your authorization token may be expired or invalid. Please update your WEB token from browser Developer Tools."; default: return `Authentication error: ${this.message}`; } } } exports.MexcAuthenticationError = MexcAuthenticationError; /** * API related errors (4xx, 5xx responses) */ class MexcApiError extends MexcFuturesError { constructor(message, code, statusCode, endpoint, method, responseData, originalError) { super(message, code, statusCode, originalError); this.endpoint = endpoint; this.method = method; this.responseData = responseData; } getUserFriendlyMessage() { switch (this.statusCode) { case 400: return `Bad Request: ${this.message}. Please check your request parameters.`; case 401: return `Unauthorized: ${this.message}. Your authorization token may be expired.`; case 403: return `Forbidden: ${this.message}. You don't have permission for this operation.`; case 404: return `Not Found: ${this.message}. The requested resource was not found.`; case 429: return `Rate Limit Exceeded: ${this.message}. Please reduce request frequency.`; case 500: return `Server Error: ${this.message}. MEXC server is experiencing issues.`; case 502: case 503: case 504: return `Service Unavailable: ${this.message}. MEXC service is temporarily unavailable.`; default: return `API Error (${this.statusCode}): ${this.message}`; } } getDetails() { return { ...super.getDetails(), endpoint: this.endpoint, method: this.method, responseData: this.responseData, }; } } exports.MexcApiError = MexcApiError; /** * Network related errors (timeouts, connection issues) */ class MexcNetworkError extends MexcFuturesError { constructor(message, originalError) { super(message, "NETWORK_ERROR", undefined, originalError); } getUserFriendlyMessage() { if (this.message.includes("timeout")) { return "Request timeout. Please check your internet connection and try again."; } if (this.message.includes("ENOTFOUND") || this.message.includes("ECONNREFUSED")) { return "Connection failed. Please check your internet connection."; } return `Network error: ${this.message}`; } } exports.MexcNetworkError = MexcNetworkError; /** * Validation errors for request parameters */ class MexcValidationError extends MexcFuturesError { constructor(message, field) { super(message, "VALIDATION_ERROR"); this.field = field; } getUserFriendlyMessage() { if (this.field) { return `Validation error for field '${this.field}': ${this.message}`; } return `Validation error: ${this.message}`; } } exports.MexcValidationError = MexcValidationError; /** * Signature related errors */ class MexcSignatureError extends MexcFuturesError { constructor(message, originalError) { const defaultMessage = "Request signature verification failed"; super(message || defaultMessage, "SIGNATURE_ERROR", 602, originalError); } getUserFriendlyMessage() { return "Signature verification failed. This usually means your authorization token is invalid or expired. Please get a fresh WEB token from your browser."; } } exports.MexcSignatureError = MexcSignatureError; /** * Rate limiting errors */ class MexcRateLimitError extends MexcFuturesError { constructor(message, retryAfter, originalError) { super(message, "RATE_LIMIT", 429, originalError); this.retryAfter = retryAfter; } getUserFriendlyMessage() { const retryMsg = this.retryAfter ? ` Please retry after ${this.retryAfter} seconds.` : ""; return `Rate limit exceeded: ${this.message}.${retryMsg}`; } } exports.MexcRateLimitError = MexcRateLimitError; /** * Strip request secrets from a raw axios error before it is retained on `originalError`. * The axios error carries the WEB token and signature in `config.headers` (and embedded in the * raw `request` object), so a consumer doing `console.log(err)` / `JSON.stringify(err)` would * otherwise leak the credential. Best-effort, mutates the about-to-be-wrapped error in place. */ const SENSITIVE_HEADERS = ["authorization", "x-mxc-sign", "x-mxc-nonce"]; function redactAxiosError(error) { if (!error || typeof error !== "object") return error; const e = error; const scrub = (headers) => { if (headers && typeof headers === "object") { for (const key of Object.keys(headers)) { if (SENSITIVE_HEADERS.includes(key.toLowerCase())) headers[key] = "[REDACTED]"; } } }; try { scrub(e.config?.headers); scrub(e.response?.config?.headers); // The raw ClientRequest embeds the Authorization line in `_header` (a string) and is circular; // drop BOTH copies — `e.request` and `e.response.request` (the response-bearing error is the one // retained as originalError on 4xx/5xx, so its `response.request._header` would re-expose the token). if (e.request) delete e.request; if (e.response?.request) delete e.response.request; } catch { /* best-effort redaction; never throw from error handling */ } return error; } /** * Parse axios error and convert to appropriate MEXC error */ function parseAxiosError(error, endpoint, method) { // Redact request secrets (WEB token / signature) before this error is retained as originalError. redactAxiosError(error); // Network errors (no response) if (error.code === "ENOTFOUND" || error.code === "ECONNREFUSED") { return new MexcNetworkError(error.message, error); } // Timeout errors if (error.code === "ECONNABORTED" || error.message?.includes("timeout")) { return new MexcNetworkError("Request timeout", error); } // Response errors if (error.response) { const { status, data } = error.response; const message = data?.message || error.message || "Unknown API error"; const code = data?.code || status; // Specific error types switch (status) { case 401: return new MexcAuthenticationError(message, error); case 429: const retryAfter = error.response.headers?.["retry-after"]; return new MexcRateLimitError(message, retryAfter ? parseInt(retryAfter) : undefined, error); default: // Check for signature error by code if (code === 602 || message.includes("signature") || message.includes("Signature")) { return new MexcSignatureError(message, error); } return new MexcApiError(message, code, status, endpoint, method, data, error); } } // Fallback for unknown errors return new MexcFuturesError(error.message || "Unknown error", "UNKNOWN_ERROR", undefined, error); } /** * Format error for logging */ function formatErrorForLogging(error) { const details = error.getDetails(); return `${error.getUserFriendlyMessage()}\nDetails: ${JSON.stringify(details, null, 2)}`; } //# sourceMappingURL=errors.js.map