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 { const idx = this.buffer.findIndex((t) => t.id === trace.id); if (idx !== -1) { this.buffer[idx] = trace; } else { 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) { await response.text().catch(() => ""); } } catch { } } }