@maximai/maxim-js
Version:
Maxim AI JS SDK. Visit https://getmaxim.ai for more info.
245 lines • 10.2 kB
JavaScript
;
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