#!/usr/bin/env node
import { c as logger } from "./logger-4ntIRWmf.js";
import { f as spansTable, g as getDb, m as tracesTable } from "./tables-BD_diFhY.js";
import { asc, eq, sql } from "drizzle-orm";
//#region src/tracing/store.ts
const SENSITIVE_ATTRIBUTE_KEYS = [
	"authorization",
	"cookie",
	"set-cookie",
	"token",
	"api_key",
	"apikey",
	"secret",
	"password",
	"passphrase"
];
const NORMALIZED_SENSITIVE_ATTRIBUTE_KEYS = SENSITIVE_ATTRIBUTE_KEYS.map((key) => key.replace(/[^a-z0-9]/g, ""));
const SAFE_TOKEN_ATTRIBUTE_KEYS = new Set([
	"gen_ai.request.max_tokens",
	"gen_ai.usage.input_tokens",
	"gen_ai.usage.output_tokens",
	"gen_ai.usage.total_tokens",
	"gen_ai.usage.cached_tokens",
	"gen_ai.usage.reasoning_tokens",
	"gen_ai.usage.accepted_prediction_tokens",
	"gen_ai.usage.rejected_prediction_tokens",
	"gen_ai.usage.cache_read_input_tokens",
	"gen_ai.usage.cache_creation_input_tokens"
]);
function isSensitiveAttributeKey(key) {
	const lowerKey = key.toLowerCase();
	if (SAFE_TOKEN_ATTRIBUTE_KEYS.has(lowerKey)) return false;
	const normalizedKey = lowerKey.replace(/[^a-z0-9]/g, "");
	return SENSITIVE_ATTRIBUTE_KEYS.some((sensitiveKey, index) => {
		return lowerKey.includes(sensitiveKey) || normalizedKey.includes(NORMALIZED_SENSITIVE_ATTRIBUTE_KEYS[index]);
	});
}
function sanitizeAttributes(attributes) {
	if (!attributes) return {};
	const sanitizeValue = (value) => {
		if (typeof value === "string") return value.length > 400 ? `${value.slice(0, 400)}…` : value;
		if (Array.isArray(value)) return value.map(sanitizeValue);
		if (value && typeof value === "object") return sanitizeAttributes(value);
		return value;
	};
	const sanitized = {};
	for (const [key, value] of Object.entries(attributes)) {
		if (isSensitiveAttributeKey(key)) {
			sanitized[key] = "<redacted>";
			continue;
		}
		sanitized[key] = sanitizeValue(value);
	}
	return sanitized;
}
function serializeSpan(span, shouldSanitizeAttributes = true) {
	const rawAttributes = span.attributes ?? void 0;
	return {
		spanId: span.spanId,
		parentSpanId: span.parentSpanId ?? void 0,
		name: span.name,
		startTime: span.startTime,
		endTime: span.endTime ?? void 0,
		attributes: rawAttributes ? shouldSanitizeAttributes ? sanitizeAttributes(rawAttributes) : rawAttributes : void 0,
		statusCode: span.statusCode ?? void 0,
		statusMessage: span.statusMessage ?? void 0
	};
}
function sqliteTimestampFromMs(timestampMs) {
	return new Date(timestampMs).toISOString().slice(0, 19).replace("T", " ");
}
function traceCreatedBefore(cutoffTime) {
	const sqliteTimestampCutoff = sqliteTimestampFromMs(cutoffTime);
	return sql`(
    (
      typeof(${tracesTable.createdAt}) in ('integer', 'real')
      and ${tracesTable.createdAt} < ${cutoffTime}
    )
    or (
      typeof(${tracesTable.createdAt}) = 'text'
      and (
        (
          cast(${tracesTable.createdAt} as integer) > 1000000000000
          and cast(${tracesTable.createdAt} as integer) < ${cutoffTime}
        )
        or datetime(${tracesTable.createdAt}) < datetime(${sqliteTimestampCutoff})
      )
    )
  )`;
}
function computeDepth(span, spanMap, depthCache) {
	if (depthCache.has(span.spanId)) return depthCache.get(span.spanId);
	if (!span.parentSpanId || !spanMap.has(span.parentSpanId)) {
		depthCache.set(span.spanId, 0);
		return 0;
	}
	const currentDepth = computeDepth(spanMap.get(span.parentSpanId), spanMap, depthCache) + 1;
	depthCache.set(span.spanId, currentDepth);
	return currentDepth;
}
function deriveSpanKind(span) {
	const attributes = span.attributes || {};
	const attributeKind = attributes["span.kind"] || attributes["otel.span.kind"] || attributes["spanKind"];
	if (typeof attributeKind === "string") return attributeKind.toLowerCase();
	return "internal";
}
var TraceStore = class {
	db = null;
	async getDatabase() {
		if (!this.db) {
			logger.debug("[TraceStore] Initializing database connection");
			this.db = await getDb();
		}
		return this.db;
	}
	async createTrace(trace) {
		try {
			logger.debug(`[TraceStore] Creating trace ${trace.traceId} for evaluation ${trace.evaluationId}`);
			await (await this.getDatabase()).insert(tracesTable).values({
				id: crypto.randomUUID(),
				traceId: trace.traceId,
				evaluationId: trace.evaluationId,
				testCaseId: trace.testCaseId,
				createdAt: Date.now(),
				metadata: trace.metadata
			}).onConflictDoNothing({ target: tracesTable.traceId }).run();
			logger.debug(`[TraceStore] Successfully created or found existing trace ${trace.traceId}`);
		} catch (error) {
			logger.error(`[TraceStore] Failed to create trace: ${error}`);
			throw error;
		}
	}
	async addSpans(traceId, spans, options) {
		try {
			logger.debug(`[TraceStore] Adding ${spans.length} spans to trace ${traceId}`);
			const db = await this.getDatabase();
			if (options?.skipTraceCheck) logger.debug(`[TraceStore] Skipping trace existence check for OTLP scenario`);
			else {
				logger.debug(`[TraceStore] Verifying trace ${traceId} exists`);
				if ((await db.select().from(tracesTable).where(eq(tracesTable.traceId, traceId)).limit(1)).length === 0) {
					const message = `[TraceStore] Trace ${traceId} not found, skipping ${spans.length} spans. This may indicate spans arrived before trace was created.`;
					if (options?.warnIfMissingTrace === false) logger.debug(message);
					else logger.warn(message);
					return {
						stored: false,
						reason: `Trace ${traceId} not found`
					};
				}
				logger.debug(`[TraceStore] Trace ${traceId} found, proceeding with span insertion`);
			}
			const spanRecords = spans.map((span) => {
				logger.debug(`[TraceStore] Preparing span ${span.spanId} (${span.name}) for insertion`);
				return {
					id: crypto.randomUUID(),
					traceId,
					spanId: span.spanId,
					parentSpanId: span.parentSpanId,
					name: span.name,
					startTime: span.startTime,
					endTime: span.endTime,
					attributes: span.attributes,
					statusCode: span.statusCode,
					statusMessage: span.statusMessage
				};
			});
			if (spanRecords.length === 0) return { stored: true };
			await db.insert(spansTable).values(spanRecords).run();
			logger.debug(`[TraceStore] Successfully added ${spans.length} spans to trace ${traceId}`);
			return { stored: true };
		} catch (error) {
			logger.error(`[TraceStore] Failed to add spans: ${error}`);
			throw error;
		}
	}
	async getTracesByEvaluation(evaluationId, options = {}) {
		const { sanitizeAttributes: shouldSanitize = true } = options;
		try {
			logger.debug(`[TraceStore] Fetching traces for evaluation ${evaluationId}`);
			const db = await this.getDatabase();
			const traces = await db.select().from(tracesTable).where(eq(tracesTable.evaluationId, evaluationId));
			logger.debug(`[TraceStore] Found ${traces.length} traces for evaluation ${evaluationId}`);
			const tracesWithSpans = await Promise.all(traces.map(async (trace) => {
				logger.debug(`[TraceStore] Fetching spans for trace ${trace.traceId}`);
				const spans = await db.select().from(spansTable).where(eq(spansTable.traceId, trace.traceId));
				logger.debug(`[TraceStore] Found ${spans.length} spans for trace ${trace.traceId}`);
				return {
					traceId: trace.traceId,
					evaluationId: trace.evaluationId,
					testCaseId: trace.testCaseId,
					metadata: trace.metadata ?? void 0,
					spans: spans.map((span) => serializeSpan(span, shouldSanitize))
				};
			}));
			logger.debug(`[TraceStore] Returning ${tracesWithSpans.length} traces with spans`);
			return tracesWithSpans;
		} catch (error) {
			logger.error(`[TraceStore] Failed to get traces for evaluation: ${error}`);
			throw error;
		}
	}
	async getTrace(traceId, options = {}) {
		const { sanitizeAttributes: shouldSanitize = true } = options;
		try {
			logger.debug(`[TraceStore] Fetching trace ${traceId}`);
			const db = await this.getDatabase();
			const traces = await db.select().from(tracesTable).where(eq(tracesTable.traceId, traceId)).limit(1);
			if (traces.length === 0) {
				logger.debug(`[TraceStore] Trace ${traceId} not found`);
				return null;
			}
			const trace = traces[0];
			logger.debug(`[TraceStore] Found trace ${traceId}, fetching spans`);
			const spans = await db.select().from(spansTable).where(eq(spansTable.traceId, traceId));
			logger.debug(`[TraceStore] Found ${spans.length} spans for trace ${traceId}`);
			return {
				traceId: trace.traceId,
				evaluationId: trace.evaluationId,
				testCaseId: trace.testCaseId,
				metadata: trace.metadata ?? void 0,
				spans: spans.map((span) => serializeSpan(span, shouldSanitize))
			};
		} catch (error) {
			logger.error(`[TraceStore] Failed to get trace: ${error}`);
			throw error;
		}
	}
	async getTraceMetadata(traceId) {
		try {
			logger.debug(`[TraceStore] Fetching metadata for trace ${traceId}`);
			const traces = await (await this.getDatabase()).select({ metadata: tracesTable.metadata }).from(tracesTable).where(eq(tracesTable.traceId, traceId)).limit(1);
			return traces.length > 0 ? traces[0].metadata ?? {} : void 0;
		} catch (error) {
			logger.error(`[TraceStore] Failed to get trace metadata: ${error}`);
			throw error;
		}
	}
	async deleteOldTraces(retentionDays) {
		try {
			logger.debug(`[TraceStore] Deleting traces older than ${retentionDays} days`);
			const db = await this.getDatabase();
			const cutoffCondition = traceCreatedBefore(Date.now() - retentionDays * 24 * 60 * 60 * 1e3);
			await db.transaction(async (tx) => {
				await tx.delete(spansTable).where(sql`${spansTable.traceId} in (
              select ${tracesTable.traceId}
              from ${tracesTable}
              where ${cutoffCondition}
            )`).run();
				await tx.delete(tracesTable).where(cutoffCondition).run();
			});
			logger.debug(`[TraceStore] Successfully deleted traces older than ${retentionDays} days`);
		} catch (error) {
			logger.error(`[TraceStore] Failed to delete old traces: ${error}`);
			throw error;
		}
	}
	async getSpans(traceId, options = {}) {
		const { earliestStartTime, maxSpans, maxDepth, includeInternalSpans = true, spanFilter, sanitizeAttributes: shouldSanitize = true } = options;
		try {
			logger.debug(`[TraceStore] Fetching spans for trace ${traceId}`);
			const rows = await (await this.getDatabase()).select().from(spansTable).where(eq(spansTable.traceId, traceId)).orderBy(asc(spansTable.startTime));
			const spanMap = /* @__PURE__ */ new Map();
			const depthCache = /* @__PURE__ */ new Map();
			for (const row of rows) {
				if (earliestStartTime && row.startTime < earliestStartTime) continue;
				const rawAttributes = row.attributes ?? {};
				const spanData = {
					spanId: row.spanId,
					parentSpanId: row.parentSpanId ?? void 0,
					name: row.name,
					startTime: row.startTime,
					endTime: row.endTime ?? void 0,
					attributes: shouldSanitize ? sanitizeAttributes(rawAttributes) : rawAttributes,
					statusCode: row.statusCode ?? void 0,
					statusMessage: row.statusMessage ?? void 0
				};
				const spanKind = deriveSpanKind({
					...spanData,
					attributes: rawAttributes
				});
				if (!includeInternalSpans && spanKind === "internal") continue;
				if (spanFilter && spanFilter.length > 0) {
					if (!spanFilter.some((filterName) => spanData.name.toLowerCase().includes(filterName.toLowerCase()))) continue;
				}
				spanMap.set(spanData.spanId, spanData);
			}
			let spans = Array.from(spanMap.values());
			if (maxDepth !== void 0) spans = spans.filter((span) => computeDepth(span, spanMap, depthCache) < maxDepth);
			if (maxSpans !== void 0) spans = spans.slice(0, maxSpans);
			logger.debug(`[TraceStore] Returning ${spans.length} spans for trace ${traceId}`);
			return spans;
		} catch (error) {
			logger.error(`[TraceStore] Failed to fetch spans for trace ${traceId}: ${error}`);
			throw error;
		}
	}
};
let traceStore = null;
function getTraceStore() {
	if (!traceStore) {
		logger.debug("[TraceStore] Creating new TraceStore instance");
		traceStore = new TraceStore();
	}
	return traceStore;
}
//#endregion
export { getTraceStore as n, TraceStore as t };

//# sourceMappingURL=store-BIk-OZNR.js.map