UNPKG

@maximai/maxim-js

Version:

Maxim AI JS SDK. Visit https://getmaxim.ai for more info.

245 lines 10.2 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MaximAPI = void 0; const axios_1 = __importStar(require("axios")); const axios_retry_1 = __importDefault(require("axios-retry")); const platform_1 = require("../platform"); // Network error codes that should trigger retries const RETRIABLE_ERROR_CODES = [ "ECONNRESET", "ENOTFOUND", "ECONNREFUSED", "ETIMEDOUT", "ECONNABORTED", "EPIPE", "EAI_AGAIN", "EHOSTUNREACH", "ENETUNREACH", "ENETDOWN", "EHOSTDOWN", ]; // HTTP status codes that indicate temporary server issues const RETRIABLE_STATUS_CODES = [408, 429, 500, 502, 503, 504, 507, 508, 510, 511, 520, 521, 522, 523, 524, 525, 526, 527, 529, 530]; class MaximAPI { constructor(baseUrl, apiKey, isDebug) { this.activeControllers = new Set(); this.apiKey = apiKey; this.isDebug = isDebug; // Create axios instance with optimal configuration const axiosConfig = { baseURL: baseUrl, timeout: 30000, // 30 second timeout headers: { "User-Agent": "Maxim-SDK/1.0", Accept: "application/json", Connection: "keep-alive", }, // Handle both localhost and production environments validateStatus: (status) => status < 600, // Don't throw on any status code, let us handle it }; // Add agents only if platform supports them const httpAgent = platform_1.platform.net.httpAgent({ keepAlive: true, keepAliveMsecs: 30000, maxSockets: 100, maxFreeSockets: 10, timeout: 30000, }); const httpsAgent = platform_1.platform.net.httpsAgent({ keepAlive: true, keepAliveMsecs: 30000, maxSockets: 100, maxFreeSockets: 10, timeout: 30000, }); if (httpAgent) axiosConfig.httpAgent = httpAgent; if (httpsAgent) axiosConfig.httpsAgent = httpsAgent; this.axiosInstance = axios_1.default.create(axiosConfig); // Configure comprehensive retry logic (0, axios_retry_1.default)(this.axiosInstance, { retries: 5, // Maximum retry attempts // Exponential backoff with jitter to prevent thundering herd retryDelay: (retryCount, error) => { var _a; // Respect Retry-After header if present const retryAfter = (_a = error.response) === null || _a === void 0 ? void 0 : _a.headers["retry-after"]; if (retryAfter && !isNaN(Number.parseInt(retryAfter))) { return Number.parseInt(retryAfter) * 1000; } // Exponential backoff: 1s, 2s, 4s, 8s, 16s with jitter const delay = Math.min(Math.pow(2, retryCount) * 1000, 16000); const jitter = Math.random() * 0.1 * delay; // 10% jitter return delay + jitter; }, // Enhanced retry conditions retryCondition: (error) => { var _a; // Network errors (ECONNRESET, EPIPE, etc.) if (error.code && RETRIABLE_ERROR_CODES.includes(error.code)) { return true; } // Timeout errors if (error.code === "ECONNABORTED" && ((_a = error.message) === null || _a === void 0 ? void 0 : _a.includes("timeout"))) { return true; } // No response received (network issues) if (!error.response) { return true; } // Server errors and rate limiting if (error.response.status && RETRIABLE_STATUS_CODES.includes(error.response.status)) { return true; } // Client errors should not be retried return false; }, // Reset timeout on each retry attempt shouldResetTimeout: true, // Retry callback for logging onRetry: (retryCount, error, requestConfig) => { var _a; const errorInfo = { attempt: retryCount, error: error.code || error.message, status: (_a = error.response) === null || _a === void 0 ? void 0 : _a.status, url: requestConfig.url, }; if (this.isDebug) { console.warn(`[Maxim-SDK] Retrying request (attempt ${retryCount}/5):`, errorInfo); } }, // Max retry time exceeded callback onMaxRetryTimesExceeded: (error, retryCount) => { var _a, _b; console.error(`[Maxim-SDK] Max retries (${retryCount}) exceeded for request:`, { error: error.code || error.message, status: (_a = error.response) === null || _a === void 0 ? void 0 : _a.status, url: (_b = error.config) === null || _b === void 0 ? void 0 : _b.url, }); }, }); // Request interceptor to add API key and handle special cases this.axiosInstance.interceptors.request.use((config) => { // Ensure headers object exists if (!config.headers) { config.headers = new axios_1.AxiosHeaders(); } // Add API key header config.headers["x-maxim-api-key"] = this.apiKey; return config; }, (error) => { return Promise.reject(error); }); // Response interceptor for enhanced error handling this.axiosInstance.interceptors.response.use((response) => { return response; }, (error) => { if (error && typeof error === "object" && "error" in error) { return Promise.reject(error.error); } return Promise.reject(error); }); } async fetch(relativeUrl, { method = "GET", headers = {}, body, responseType = "json", } = {}) { var _a; const controller = new AbortController(); this.activeControllers.add(controller); const config = { url: relativeUrl, method: method.toLowerCase(), headers: { ...headers }, responseType: responseType, signal: controller.signal, }; // Add request body if provided if (body) { config.data = body; // Ensure headers object exists and get reference const configHeaders = (_a = config.headers) !== null && _a !== void 0 ? _a : new axios_1.AxiosHeaders(); config.headers = configHeaders; // Set content-type if not already set if (!configHeaders["Content-Type"] && !configHeaders["content-type"]) { configHeaders["Content-Type"] = "application/json"; } } try { const response = await this.axiosInstance.request(config); // For successful responses, return the data if (response.status >= 200 && response.status < 300) { return response.data; } // For non-2xx responses that didn't trigger axios errors if (response.data && typeof response.data === "object" && "error" in response.data) { const { error } = response.data; if (typeof error === "string") { throw error; } if (error && typeof error === "object" && "message" in error) { throw error.message; } throw JSON.stringify(error, null, 2); } throw response.data; } finally { this.activeControllers.delete(controller); } } /** * Destroys the HTTP and HTTPS agents, closing all sockets */ destroyAgents() { // Axios doesn't expose agents directly, but we can ensure all pending requests are cancelled // and connection pools are cleaned up if (this.axiosInstance) { // Abort all active requests for (const controller of this.activeControllers) { controller.abort(); } this.activeControllers.clear(); // Clear interceptors this.axiosInstance.interceptors.request.clear(); this.axiosInstance.interceptors.response.clear(); } } } exports.MaximAPI = MaximAPI; //# sourceMappingURL=maxim.js.map