Yassine Mhirsi
Add caching support for analysis data in AnalysisPage
6c8fc4b
/**
* 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<T extends (...args: any[]) => any>(
func: T,
wait: number
): (...args: Parameters<T>) => void {
let timeout: NodeJS.Timeout | null = null;
return function executedFunction(...args: Parameters<T>) {
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<void> {
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 !== '')
),
};
}