voko-sdk
Version:
Process payments with ease
126 lines (125 loc) • 4.11 kB
JavaScript
;
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;