UNPKG

alfred-logger-sdk

Version:

Production-ready data collection SDK for feeding structured events to LLM Data Agents with auto-capture capabilities

125 lines (101 loc) 3.3 kB
const axios = require('axios'); class HttpClient { constructor(config) { this.endpoint = config.endpoint; this.apiKey = config.apiKey; this.timeout = Math.min(config.timeout || 10000, 30000); this.retryAttempts = Math.min(config.retryAttempts || 3, 5); this.retryDelay = Math.max(config.retryDelay || 1000, 500); this.maxRetryDelay = config.maxRetryDelay || 10000; this.requestCount = 0; this.lastRequestTime = 0; this.rateLimitDelay = config.rateLimitDelay || 100; } async sendBatch(events) { if (!events || events.length === 0) { return; } await this.enforceRateLimit(); const payload = { batchId: this.generateBatchId(), timestamp: new Date().toISOString(), eventCount: events.length, events }; const payloadSize = JSON.stringify(payload).length; if (payloadSize > 10 * 1024 * 1024) { throw new Error(`Payload too large: ${payloadSize} bytes`); } return await this.sendWithRetry(payload); } async sendWithRetry(payload) { const headers = { 'Content-Type': 'application/json', 'X-Data-Source': 'alfred-logger-sdk', 'X-Request-ID': this.generateBatchId() }; if (this.apiKey) { headers['Authorization'] = `Bearer ${this.apiKey}`; } let lastError; for (let attempt = 1; attempt <= this.retryAttempts; attempt++) { try { const response = await axios.post(this.endpoint, payload, { headers, timeout: this.timeout, validateStatus: (status) => status < 500 }); if (response.status >= 400) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return response.data; } catch (error) { lastError = error; if (this.isRetryableError(error) && attempt < this.retryAttempts) { const delay = Math.min(this.retryDelay * Math.pow(2, attempt - 1), this.maxRetryDelay); await this.delay(delay); continue; } break; } } this.handleFailedSend(payload, lastError); throw lastError; } generateBatchId() { return `batch_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; } isRetryableError(error) { if (error.code === 'ECONNABORTED' || error.code === 'ENOTFOUND') { return true; } if (error.response) { const status = error.response.status; return status >= 500 || status === 429; } return false; } async enforceRateLimit() { const now = Date.now(); const timeSinceLastRequest = now - this.lastRequestTime; if (timeSinceLastRequest < this.rateLimitDelay) { await this.delay(this.rateLimitDelay - timeSinceLastRequest); } this.lastRequestTime = Date.now(); this.requestCount++; } handleFailedSend(payload, error) { if (typeof window === 'undefined') { console.error('Failed to send data batch:', { batchId: payload.batchId, eventCount: payload.eventCount, error: error.message, timestamp: new Date().toISOString() }); } } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } module.exports = HttpClient;