UNPKG

@kaifronsdal/transcript-viewer

Version:

A web-based viewer for AI conversation transcripts with rollback support

148 lines (129 loc) 4.01 kB
import Ajv from 'ajv'; import addFormats from 'ajv-formats'; import transcriptSchema from './transcript-schema.json'; // Create AJV instance with proper configuration const ajv = new Ajv({ allErrors: true, // Collect all errors, not just the first one verbose: true, // Include schema and data information in errors strict: false // Allow unknown keywords (for Pydantic-generated schemas) }); // Add standard formats (including date-time) addFormats(ajv); // Add a more lenient date-time format that accepts various formats ajv.addFormat('date-time', { type: 'string', validate: function(dateTimeString: string) { // Accept ISO 8601 format if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?$/.test(dateTimeString)) { return !isNaN(Date.parse(dateTimeString)); } // Accept space-separated format (common in transcript files) if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(\.\d+)?$/.test(dateTimeString)) { return !isNaN(Date.parse(dateTimeString.replace(' ', 'T'))); } return false; } }); // Compile the transcript schema const validateTranscript = ajv.compile(transcriptSchema); export interface ValidationError { message: string; path: string; value: any; schema: any; } export interface ValidationResult { valid: boolean; errors: ValidationError[]; data?: any; } /** * Validate transcript data against the JSON schema */ export function validateTranscriptData(data: any): ValidationResult { const valid = validateTranscript(data); if (valid) { return { valid: true, errors: [], data }; } const errors: ValidationError[] = (validateTranscript.errors || []).map(error => ({ message: error.message || 'Unknown validation error', path: error.instancePath || error.schemaPath || 'unknown', value: error.data, schema: error.schema })); return { valid: false, errors, data }; } /** * Format validation errors into a human-readable message */ export function formatValidationErrors(errors: ValidationError[]): string { if (errors.length === 0) return 'No validation errors'; const errorMessages = errors.map(error => { const path = error.path || 'root'; return `• ${path}: ${error.message}`; }); return `Schema validation failed:\n${errorMessages.join('\n')}`; } /** * Validate and parse transcript file content */ export function validateAndParseTranscript(content: string, filename?: string): ValidationResult { try { // First, try to parse as JSON const data = JSON.parse(content); // Then validate against schema const result = validateTranscriptData(data); if (!result.valid) { console.error(`Transcript validation failed${filename ? ` for ${filename}` : ''}: ${result.errors.length} validation errors. ${result.errors.map(error => error.message).join('\n')}`); } return result; } catch (parseError) { return { valid: false, errors: [{ message: `JSON parsing failed: ${parseError instanceof Error ? parseError.message : 'Unknown parse error'}`, path: 'root', value: content, schema: null }], data: null }; } } /** * Check if data looks like a transcript (basic structure check) */ export function isTranscriptLike(data: any): boolean { return ( data && typeof data === 'object' && data.metadata && Array.isArray(data.events) ); } /** * Extract basic info from potentially invalid transcript data */ export function extractTranscriptInfo(data: any): { hasMetadata: boolean; hasEvents: boolean; eventCount: number; version: string | null; sessionId: string | null; } { return { hasMetadata: !!(data && data.metadata), hasEvents: !!(data && Array.isArray(data.events)), eventCount: (data && Array.isArray(data.events)) ? data.events.length : 0, version: data?.metadata?.version || null, sessionId: data?.metadata?.session_id || null }; }