UNPKG

morpheus-node

Version:

Official Node.js SDK for the Morpheus API Gateway - Connect to the Morpheus-Lumerin AI Marketplace

180 lines (179 loc) 6.92 kB
"use strict"; /** * HTTP Client for Morpheus Node SDK */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpClient = void 0; const cross_fetch_1 = __importDefault(require("cross-fetch")); const errors_1 = require("./errors"); class HttpClient { constructor(config) { this.config = config; this.accessToken = config.accessToken; } setAccessToken(token) { this.accessToken = token; } async delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } shouldRetry(error, attempt) { if (attempt >= this.config.maxRetries) return false; // Retry on network errors if (error instanceof TypeError || error instanceof errors_1.MorpheusNetworkError) { return true; } // Retry on timeout if (error instanceof errors_1.MorpheusTimeoutError) { return true; } // Retry on specific status codes if (error instanceof errors_1.MorpheusError && error.status) { // Retry on 429 (rate limit), 502, 503, 504 return [429, 502, 503, 504].includes(error.status); } return false; } getRetryDelay(attempt, error) { // If we have a rate limit error with retry-after header if (error?.status === 429 && error.details?.retryAfter) { return error.details.retryAfter * 1000; } // Exponential backoff: 1s, 2s, 4s, 8s... return Math.min(this.config.retryDelay * Math.pow(2, attempt), 30000); } async request(method, path, data, options) { const url = `${this.config.baseURL}${path}`; let lastError; for (let attempt = 0; attempt <= this.config.maxRetries; attempt++) { try { const response = await this.makeRequest(method, url, data, options); return await this.handleResponse(response); } catch (error) { lastError = error; if (!this.shouldRetry(error, attempt)) { throw error; } const delay = this.getRetryDelay(attempt, error); console.warn(`Request failed, retrying in ${delay}ms...`, { attempt: attempt + 1, error: error instanceof Error ? error.message : 'Unknown error' }); await this.delay(delay); } } throw lastError || new errors_1.MorpheusError('Max retries exceeded'); } async makeRequest(method, url, data, options) { const controller = new AbortController(); const timeout = options?.timeout || this.config.timeout; const timeoutId = setTimeout(() => { controller.abort(); }, timeout); try { const headers = { 'Content-Type': 'application/json', 'User-Agent': 'morpheus-node/1.0.0', ...this.config.headers, ...options?.headers }; // Add authorization header if (this.accessToken) { headers['Authorization'] = `Bearer ${this.accessToken}`; } else if (this.config.apiKey) { headers['Authorization'] = `Bearer ${this.config.apiKey}`; } const fetchOptions = { method, headers, signal: options?.signal || controller.signal }; if (data && ['POST', 'PUT', 'PATCH'].includes(method)) { fetchOptions.body = JSON.stringify(data); } const response = await (0, cross_fetch_1.default)(url, fetchOptions); clearTimeout(timeoutId); return response; } catch (error) { clearTimeout(timeoutId); if (error instanceof Error) { if (error.name === 'AbortError') { throw new errors_1.MorpheusTimeoutError(`Request timeout after ${timeout}ms`); } throw new errors_1.MorpheusNetworkError(error.message, error); } throw new errors_1.MorpheusNetworkError('Unknown network error'); } } async handleResponse(response) { let responseData; try { const contentType = response.headers.get('content-type'); if (contentType?.includes('application/json')) { responseData = await response.json(); } else { responseData = await response.text(); } } catch (error) { // If we can't parse the response, use the status text responseData = response.statusText; } if (!response.ok) { // Check if it's a structured error response if (responseData?.error) { throw errors_1.MorpheusError.fromResponse(responseData, response.status); } // Create error based on status code throw (0, errors_1.createErrorFromStatus)(response.status, typeof responseData === 'string' ? responseData : 'Request failed', responseData); } return responseData; } async *stream(method, path, data, options) { const url = `${this.config.baseURL}${path}`; const response = await this.makeRequest(method, url, data, options); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw (0, errors_1.createErrorFromStatus)(response.status, errorData.message || response.statusText, errorData); } const reader = response.body?.getReader(); if (!reader) { throw new errors_1.MorpheusError('Failed to get response reader'); } const decoder = new TextDecoder(); let buffer = ''; try { while (true) { const { done, value } = await reader.read(); if (done) break; buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); buffer = lines.pop() || ''; for (const line of lines) { if (line.trim() === '') continue; if (line.startsWith('data: ')) { const data = line.slice(6).trim(); if (data === '[DONE]') { return; } yield data; } } } } finally { reader.releaseLock(); } } } exports.HttpClient = HttpClient;