UNPKG

@elusion-sdk/briq

Version:

A modern TypeScript SDK for Briq SMS API integration

170 lines 6.69 kB
import { BaseClient } from "./BaseClient"; import { AuthenticationError, AuthorizationError, BriqError, NetworkError, NotFoundError, RateLimitError, ServerError, TimeoutError, ValidationError, } from "../utils/errors"; import { HTTP_STATUS, RETRY_CONFIG } from "../utils/constants"; import { calculateBackoffDelay, delay, isRetryableError, } from "../utils/helpers"; class ConsoleLogger { error(message, meta) { console.error(`[BRIQ-SDK-ERROR] ${message}`, meta || ""); } warn(message, meta) { console.warn(`[BRIQ-SDK-WARN] ${message}`, meta || ""); } info(message, meta) { console.info(`[BRIQ-SDK-INFO] ${message}`, meta || ""); } debug(message, meta) { console.debug(`[BRIQ-SDK-DEBUG] ${message}`, meta || ""); } } export class HttpClient extends BaseClient { logger; constructor(config) { super(config); this.logger = config.logger || new ConsoleLogger(); } async executeRequest(config) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), this.config.timeout); try { const fetchConfig = { method: config.method, signal: controller.signal, }; if (config.headers) { fetchConfig.headers = config.headers; } if (config.data && config.method !== "GET") { fetchConfig.body = JSON.stringify(config.data); } let url = config.url; if (config.params && Object.keys(config.params).length > 0) { const searchParams = new URLSearchParams(); Object.entries(config.params).forEach(([key, value]) => { if (value !== undefined && value !== null) { searchParams.append(key, String(value)); } }); url += `?${searchParams.toString()}`; } const response = await fetch(url, fetchConfig); clearTimeout(timeoutId); return response; } catch (error) { clearTimeout(timeoutId); if (error.name === "AbortError") { const timeoutError = new TimeoutError(this.config.timeout); this.logger.error("Request timeout", { timeout: this.config.timeout, url: config.url, errorCode: timeoutError.code, }); throw timeoutError; } const networkError = new NetworkError("Network request failed", { originalError: error.message, }); throw networkError; } } async handleResponse(response) { let responseData; try { const text = await response.text(); responseData = text ? JSON.parse(text) : {}; } catch { throw new ServerError("Invalid JSON response from server", response.status); } if (response.ok) { if (responseData.success !== undefined) { return responseData; } else { return { success: true, data: responseData, }; } } throw this.createErrorFromResponse(response, responseData); } handleError(error) { if (error.name?.includes("Error")) { return error; } return new NetworkError("Unexpected error occurred", { originalError: error, }); } createErrorFromResponse(response, data) { const message = data?.message || data?.error || `HTTP ${response.status} error`; const details = { statusCode: response.status, statusText: response.statusText, response: data, }; switch (response.status) { case HTTP_STATUS.BAD_REQUEST: return new ValidationError(message, details); case HTTP_STATUS.UNAUTHORIZED: return new AuthenticationError(message, details); case HTTP_STATUS.FORBIDDEN: return new AuthorizationError(message, details); case HTTP_STATUS.NOT_FOUND: return new NotFoundError("Resource", undefined); case HTTP_STATUS.TOO_MANY_REQUESTS: const retryAfter = response.headers.get("Retry-After"); return new RateLimitError(message, retryAfter ? parseInt(retryAfter) : undefined); case HTTP_STATUS.INTERNAL_SERVER_ERROR: case HTTP_STATUS.BAD_GATEWAY: case HTTP_STATUS.SERVICE_UNAVAILABLE: case HTTP_STATUS.GATEWAY_TIMEOUT: return new ServerError(message, response.status, details); default: return new ServerError(message, response.status, details); } } async request(config) { let lastError; for (let attempt = 1; attempt <= this.config.retries; attempt++) { try { const response = await super.request(config); return response; } catch (error) { lastError = error; if (attempt === this.config.retries || !isRetryableError(error)) { if (attempt === this.config.retries) { this.logger.error("All retry attempts exhausted", { method: config.method, url: config.url, totalAttempts: this.config.retries, finalErrorCode: error instanceof BriqError ? error.code : "UNKNOWN", }); } else { this.logger.info("Error is not retryable, failing immediately", { method: config.method, url: config.url, errorCode: error instanceof BriqError ? error.code : "UNKNOWN", details: error.details || "", }); } break; } const delayMs = calculateBackoffDelay(attempt, RETRY_CONFIG.INITIAL_DELAY); await delay(delayMs); } } throw lastError; } getLogger() { return this.logger; } setLogger(logger) { this.logger = logger; this.logger.info("Custom logger set for HTTP client"); } } //# sourceMappingURL=HttpClient.js.map