/** * Utility functions and helpers */ import { CsvParseResult, CsvRow } from '../types/index.ts'; // Export user utilities export * from './user.utils.ts'; // Export analysis utilities export * from './analysis.utils.ts'; // Export cache utilities export * from './cache.utils.ts'; /** * Debounce function - delays execution until after wait time */ export function debounce any>( func: T, wait: number ): (...args: Parameters) => void { let timeout: NodeJS.Timeout | null = null; return function executedFunction(...args: Parameters) { const later = () => { timeout = null; func(...args); }; if (timeout) clearTimeout(timeout); timeout = setTimeout(later, wait); }; } /** * Format error message from API error or string */ export function formatError(error: unknown): string { if (typeof error === 'string') return error; if (error && typeof error === 'object' && 'message' in error) { return String(error.message); } return 'An unexpected error occurred'; } /** * Sleep/delay utility */ export function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } /** * Check if value is empty (null, undefined, empty string, empty array, empty object) */ export function isEmpty(value: unknown): boolean { if (value == null) return true; if (typeof value === 'string') return value.trim().length === 0; if (Array.isArray(value)) return value.length === 0; if (typeof value === 'object') return Object.keys(value).length === 0; return false; } /** * Parse a CSV string into headers and rows. * Supports quoted fields and trims whitespace around values. */ export function parseCsv(content: string): CsvParseResult { const lines = content.split(/\r?\n/).filter((line) => line.trim().length > 0); if (lines.length === 0) { return { headers: [], rows: [] }; } const parseLine = (line: string): string[] => { const cells: string[] = []; let current = ''; let inQuotes = false; for (let i = 0; i < line.length; i += 1) { const char = line[i]; const nextChar = line[i + 1]; if (char === '"') { if (inQuotes && nextChar === '"') { current += '"'; i += 1; } else { inQuotes = !inQuotes; } } else if (char === ',' && !inQuotes) { cells.push(current.trim()); current = ''; } else { current += char; } } cells.push(current.trim()); return cells; }; const headers = parseLine(lines[0]).map((header, index) => header ? header : `column_${index + 1}` ); const rows: CsvRow[] = lines.slice(1).map((line) => { const values = parseLine(line); const row: CsvRow = {}; headers.forEach((header, idx) => { row[header] = values[idx]?.trim() ?? ''; }); return row; }); return { headers, rows: rows.filter((row) => Object.values(row).some((value) => value !== undefined && value !== '') ), }; }