UNPKG

voko-sdk

Version:
126 lines (125 loc) 4.11 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HttpClient = void 0; class HttpClient { timeout; retries; constructor(config = {}) { this.timeout = config.timeout || 30000; this.retries = config.retries || 3; } async request(config) { let lastError; for (let attempt = 0; attempt <= this.retries; attempt++) { try { return await this.executeRequest(config); } catch (error) { lastError = error; if (attempt === this.retries) { throw lastError; } await this.sleep(Math.pow(2, attempt) * 1000); } } throw lastError; } async executeRequest(config) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.timeout); try { const url = this.buildUrl(config.url, config.params); const body = this.prepareBody(config.data, config.headers); const headers = this.prepareHeaders(config.headers, config.data); const response = await fetch(url, { method: config.method, headers, body, signal: controller.signal, }); clearTimeout(timeoutId); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } const data = await this.parseResponse(response); return { data, status: response.status, headers: this.parseHeaders(response.headers), }; } catch (error) { clearTimeout(timeoutId); if (error instanceof Error && error.name === 'AbortError') { throw new Error(`Request timeout after ${this.timeout}ms`); } throw error; } } buildUrl(baseUrl, params) { if (!params) return baseUrl; const url = new URL(baseUrl); Object.entries(params).forEach(([key, value]) => { if (value != null) { url.searchParams.append(key, String(value)); } }); return url.toString(); } prepareBody(data, headers) { if (!data) return null; const contentType = this.getContentType(headers); if (contentType.includes('application/x-www-form-urlencoded')) { if (typeof data === 'string') return data; const formData = new URLSearchParams(); Object.entries(data).forEach(([key, value]) => { if (value != null) { formData.append(key, String(value)); } }); return formData.toString(); } return typeof data === 'string' ? data : JSON.stringify(data); } prepareHeaders(headers, data) { const result = { ...headers }; if (data && !this.hasContentType(result)) { result['Content-Type'] = 'application/json'; } return result; } async parseResponse(response) { const text = await response.text(); if (!text) return null; try { return JSON.parse(text); } catch { return text; } } parseHeaders(headers) { const result = {}; headers.forEach((value, key) => { result[key] = value; }); return result; } getContentType(headers) { if (!headers) return ''; const entry = Object.entries(headers).find(([key]) => key.toLowerCase() === 'content-type'); return entry ? entry[1] : ''; } hasContentType(headers) { return Object.keys(headers).some((key) => key.toLowerCase() === 'content-type'); } sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } } exports.HttpClient = HttpClient;