/** * Diagnostics Client Library * Admin dashboard client for debug sessions, traces, and logs */ import { createApiClient } from '@bytelyst/api-client'; import type { ApiClient } from '@bytelyst/api-client'; // ============================================================================= // Types // ============================================================================= export interface DebugSession { id: string; productId: string; targetUserId?: string; targetAnonymousId?: string; targetDeviceId?: string; targetSessionId?: string; status: 'pending' | 'active' | 'paused' | 'completed' | 'cancelled'; collectionLevel: 'standard' | 'debug' | 'trace'; captureLogs: boolean; captureNetwork: boolean; captureScreenshots: boolean; screenshotOnError: boolean; maxDurationMinutes: number; createdAt: string; updatedAt: string; startedAt?: string; endedAt?: string; expiresAt: string; logCount: number; traceCount: number; screenshotCount: number; createdBy: string; updatedBy?: string; userConsent?: { consentedAt: string; consentMethod: 'prompt' | 'pre_consent' | 'auto'; }; } export interface CreateSessionRequest { targetUserId?: string; targetAnonymousId?: string; targetDeviceId?: string; targetSessionId?: string; collectionLevel: 'standard' | 'debug' | 'trace'; captureLogs?: boolean; captureNetwork?: boolean; captureScreenshots?: boolean; screenshotOnError?: boolean; maxDurationMinutes?: number; } export interface UpdateSessionRequest { status?: 'active' | 'paused' | 'completed' | 'cancelled'; collectionLevel?: 'standard' | 'debug' | 'trace'; captureLogs?: boolean; captureNetwork?: boolean; captureScreenshots?: boolean; screenshotOnError?: boolean; maxDurationMinutes?: number; } export interface QuerySessionsOptions { productId: string; status?: string; targetUserId?: string; targetDeviceId?: string; startDate?: string; endDate?: string; limit?: number; offset?: number; } export interface QuerySessionsResult { sessions: DebugSession[]; total: number; limit: number; offset: number; } export interface TraceSpan { id: string; sessionId: string; productId: string; traceId: string; parentId?: string; spanId: string; name: string; kind?: 'internal' | 'server' | 'client' | 'producer' | 'consumer'; startTime: string; endTime?: string; durationMs?: number; attributes: Record; status: 'ok' | 'error' | 'unset'; statusMessage?: string; events?: Array<{ name: string; timestamp: string; attributes?: Record; }>; } export interface LogEntry { id: string; sessionId: string; productId: string; level: 'debug' | 'info' | 'warn' | 'error' | 'fatal'; message: string; messageHash?: string; timestamp: string; receivedAt?: string; module: string; file?: string; line?: number; function?: string; threadId?: string; correlationId?: string; context: Record; redaction?: { fieldsRedacted: string[]; patternsMatched: string[]; }; } export interface QueryTracesOptions { sessionId: string; productId: string; startTime?: string; endTime?: string; name?: string; status?: string; limit?: number; offset?: number; } export interface QueryLogsOptions { sessionId: string; productId: string; levels?: ('debug' | 'info' | 'warn' | 'error' | 'fatal')[]; module?: string; search?: string; startTime?: string; endTime?: string; limit?: number; offset?: number; } // ============================================================================= // Client Factory // ============================================================================= export interface DiagnosticsClientConfig { baseUrl: string; productId: string; getAuthToken: () => string | null; } export function createDiagnosticsClient(config: DiagnosticsClientConfig) { const client = createApiClient({ baseUrl: config.baseUrl, getToken: config.getAuthToken, }) as ApiClient; return { // ------------------------------------------------------------------------- // Sessions // ------------------------------------------------------------------------- async querySessions(options: QuerySessionsOptions): Promise { const params = new URLSearchParams(); params.set('productId', options.productId); if (options.status) params.set('status', options.status); if (options.targetUserId) params.set('targetUserId', options.targetUserId); if (options.targetDeviceId) params.set('targetDeviceId', options.targetDeviceId); if (options.startDate) params.set('startDate', options.startDate); if (options.endDate) params.set('endDate', options.endDate); if (options.limit) params.set('limit', options.limit.toString()); if (options.offset) params.set('offset', options.offset.toString()); const result = await client.safeFetch(`/api/diagnostics/sessions?${params.toString()}`); if (result.error) throw new Error(result.error); return result.data!; }, async createSession(request: CreateSessionRequest): Promise { const result = await client.safeFetch('/api/diagnostics/sessions', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(request), }); if (result.error) throw new Error(result.error); return result.data!; }, async getSession(sessionId: string): Promise { const result = await client.safeFetch(`/api/diagnostics/sessions/${sessionId}`); if (result.error) throw new Error(result.error); return result.data!; }, async updateSession(sessionId: string, request: UpdateSessionRequest): Promise { const result = await client.safeFetch(`/api/diagnostics/sessions/${sessionId}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(request), }); if (result.error) throw new Error(result.error); return result.data!; }, async cancelSession(sessionId: string, reason?: string): Promise { const result = await client.safeFetch(`/api/diagnostics/sessions/${sessionId}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ reason }), }); if (result.error) throw new Error(result.error); }, // ------------------------------------------------------------------------- // Traces // ------------------------------------------------------------------------- async getTraces(options: QueryTracesOptions): Promise<{ traces: TraceSpan[]; total: number }> { const params = new URLSearchParams(); params.set('productId', options.productId); if (options.startTime) params.set('startTime', options.startTime); if (options.endTime) params.set('endTime', options.endTime); if (options.name) params.set('name', options.name); if (options.status) params.set('status', options.status); if (options.limit) params.set('limit', options.limit.toString()); if (options.offset) params.set('offset', options.offset.toString()); const result = await client.safeFetch<{ traces: TraceSpan[]; total: number }>( `/api/diagnostics/sessions/${options.sessionId}/traces?${params.toString()}` ); if (result.error) throw new Error(result.error); return result.data!; }, // ------------------------------------------------------------------------- // Logs // ------------------------------------------------------------------------- async getLogs(options: QueryLogsOptions): Promise<{ logs: LogEntry[]; total: number }> { const params = new URLSearchParams(); params.set('productId', options.productId); if (options.levels) params.set('levels', options.levels.join(',')); if (options.module) params.set('module', options.module); if (options.search) params.set('search', options.search); if (options.startTime) params.set('startTime', options.startTime); if (options.endTime) params.set('endTime', options.endTime); if (options.limit) params.set('limit', options.limit.toString()); if (options.offset) params.set('offset', options.offset.toString()); const result = await client.safeFetch<{ logs: LogEntry[]; total: number }>( `/api/diagnostics/sessions/${options.sessionId}/logs?${params.toString()}` ); if (result.error) throw new Error(result.error); return result.data!; }, // ------------------------------------------------------------------------- // Screenshots // ------------------------------------------------------------------------- async getScreenshots(sessionId: string, productId: string): Promise< Array<{ id: string; blobUrl: string; capturedAt: string; trigger: string; width: number; height: number; }> > { const params = new URLSearchParams(); params.set('productId', productId); const result = await client.safeFetch< Array<{ id: string; blobUrl: string; capturedAt: string; trigger: string; width: number; height: number; }> >(`/api/diagnostics/sessions/${sessionId}/screenshots?${params.toString()}`); if (result.error) throw new Error(result.error); return result.data!; }, }; } export type DiagnosticsClient = ReturnType;