UNPKG

smart-track

Version:

A TypeScript SDK for integrating with tracking services using the Beckn protocol. Provides adapters for package tracking with built-in error handling, retry logic, and type safety.

132 lines 4.85 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BaseAdapter = void 0; const error_1 = require("./utils/error"); const payload_types_1 = require("../types/payload.types"); class BaseAdapter { constructor(config) { this.baseUrl = config.baseURL.replace(/\/$/, ""); this.timeout = config.timeout || 5000; this.headers = config.headers || {}; this.retryAttempts = config.retryAttempts || 3; this.beckn_metadata = payload_types_1.becknMetadataSchema.parse({ ...payload_types_1.becknMetadataSchema.parse({}), ...config.beckn_metadata, }); } async request(method, endpoint, data, options = {}) { const url = `${this.baseUrl}${endpoint}`; const config = { method, headers: new Headers({ "Content-Type": "application/json", ...this.cleanHeaders(this.headers), ...this.cleanHeaders(options.headers), }), signal: options.signal || null, }; // Handle different types of request bodies if (data) { config.body = JSON.stringify({ context: this.beckn_metadata, message: data, }); } return this.executeWithRetry(() => this.makeRequest(url, config, options.timeout || this.timeout)); } cleanHeaders(headers = {}) { const cleaned = {}; for (const [key, value] of Object.entries(headers)) { if (typeof value === "string") { cleaned[key] = value; } else if (Array.isArray(value)) { cleaned[key] = value.join(", "); } } return cleaned; } async makeRequest(url, config, timeout) { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { const response = await fetch(url, { ...config, signal: controller.signal, }); clearTimeout(timeoutId); if (!response.ok) { await this.handleErrorResponse(response); } return await this.parseResponse(response); } catch (error) { clearTimeout(timeoutId); const err = error; if (err.name === "AbortError") { throw new error_1.TimeoutError(timeout); } if (error instanceof error_1.AdapterError) { throw error; } throw new error_1.NetworkError("Network request failed", err); } } // Handles error responses and creates appropriate error objects async handleErrorResponse(response) { let errorData; try { const contentType = response.headers.get("content-type"); if (contentType?.includes("application/json")) { errorData = await response.json(); } else { errorData = await response.text(); } } catch { errorData = null; } const message = errorData?.message || errorData?.error || `HTTP ${response.status}: ${response.statusText}`; throw new error_1.AdapterError(message, response.status, errorData, response.status >= 500 || response.status === 429); } // Parses response based on content type async parseResponse(response) { const contentType = response.headers.get("content-type"); if (contentType?.includes("application/json")) { return response.json(); } return response.text(); } // Implements retry logic with exponential backoff async executeWithRetry(operation) { let lastError; for (let attempt = 1; attempt <= this.retryAttempts; attempt++) { try { return await operation(); } catch (error) { lastError = error; // Only retry if error is retryable if (!(error instanceof error_1.AdapterError) || !error.isRetryable) { throw error; } if (attempt === this.retryAttempts) { throw error; } // Exponential backoff with jitter to prevent thundering herd const delay = Math.pow(2, attempt) * 1000 + Math.random() * 1000; await new Promise((resolve) => setTimeout(resolve, delay)); } } throw lastError; } // Basic health check endpoint async healthCheck() { return this.request("GET", "/health"); } } exports.BaseAdapter = BaseAdapter; //# sourceMappingURL=base.adapter.js.map