import type { TracePayload } from "./models.js"; export interface BatchTransportOptions { apiKey: string; endpoint: string; maxBatchSize?: number; flushInterval?: number; } export class BatchTransport { private readonly apiKey: string; private readonly endpoint: string; private readonly maxBatchSize: number; private readonly flushInterval: number; private buffer: TracePayload[] = []; private timer: ReturnType | null = null; constructor(options: BatchTransportOptions) { this.apiKey = options.apiKey; this.endpoint = options.endpoint.replace(/\/+$/, ""); this.maxBatchSize = options.maxBatchSize ?? 10; this.flushInterval = options.flushInterval ?? 5_000; this.timer = setInterval(() => { void this._doFlush(); }, this.flushInterval); } add(trace: TracePayload): void { this.buffer.push(trace); if (this.buffer.length >= this.maxBatchSize) { void this._doFlush(); } } async flush(): Promise { await this._doFlush(); } async shutdown(): Promise { if (this.timer !== null) { clearInterval(this.timer); this.timer = null; } await this._doFlush(); } private async _doFlush(): Promise { if (this.buffer.length === 0) { return; } const batch = this.buffer.splice(0, this.buffer.length); try { const response = await fetch(`${this.endpoint}/api/traces`, { method: "POST", headers: { Authorization: `Bearer ${this.apiKey}`, "Content-Type": "application/json", }, body: JSON.stringify({ traces: batch }), }); if (!response.ok) { const text = await response.text().catch(() => ""); console.warn( `AgentLens: Failed to send traces (HTTP ${response.status}): ${text.slice(0, 200)}`, ); } } catch (error: unknown) { const message = error instanceof Error ? error.message : String(error); console.warn(`AgentLens: Failed to send traces: ${message}`); } } }