export const sanitizeText = (text: string) => { return text .replace(/"/g, '"') .replace(/'/g, ''') .replace(//g, '>'); }; export const unescapeText = (text: string) => { return text .replace(/"/g, '"') .replace(/'/g, "'") .replace(/</g, '<') .replace(/>/g, '>') .replace(/&/g, '&'); }; export const truncateWithEllipsis = (str: string, maxLength: number): string => { const segmenter = new Intl.Segmenter('en', { granularity: 'grapheme' }); const segments = segmenter.segment(str); let truncated = ''; let length = 0; for (const segment of segments) { if (length + segment.segment.length > maxLength) { break; } truncated += segment.segment; length += segment.segment.length; } return truncated.length < str.length ? truncated + '…' : truncated; }; export async function withTimeout( asyncTask: (signal: AbortSignal) => Promise, timeout: number = 3500, retries: number = 3 ): Promise { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeout); try { const result = await asyncTask(controller.signal); clearTimeout(timeoutId); return result; } catch (error) { if ((error as Error).name === 'AbortError') { if (retries > 0) { // Try again, reducing the retries count return withTimeout(asyncTask, timeout, retries - 1); } throw new Error('Request has timed out too many times'); } else { throw error as Error; } } } export const formatNumber = (num: number) => { if (num >= 1e6) { return (num / 1e6).toFixed(2) + 'M'; } else if (num >= 1e3) { return (num / 1e3).toFixed(1) + 'K'; } else { return num.toString(); } };