my website at ewancroft.uk
at main 107 lines 2.9 kB view raw
1/** 2 * Validation and text processing utilities 3 */ 4 5/** 6 * Validates AT Protocol TID (Timestamp Identifier) format 7 * @param tid - The TID to validate 8 * @returns True if valid TID format 9 */ 10export function isValidTid(tid: string): boolean { 11 // TIDs are base32-encoded timestamps, 12-16 alphanumeric characters 12 const tidPattern = /^[a-zA-Z0-9]{12,16}$/; 13 return tidPattern.test(tid); 14} 15 16/** 17 * Validates AT Protocol DID format 18 * @param did - The DID to validate 19 * @returns True if valid DID format 20 */ 21export function isValidDid(did: string): boolean { 22 // DID format: did:method:identifier 23 const didPattern = /^did:[a-z]+:[a-zA-Z0-9._:-]+$/; 24 return didPattern.test(did); 25} 26 27/** 28 * Truncates text to a specified length with ellipsis 29 * @param text - The text to truncate 30 * @param maxLength - Maximum length before truncation 31 * @param ellipsis - String to append when truncated (default: "...") 32 * @returns Truncated text 33 */ 34export function truncateText(text: string, maxLength: number, ellipsis = '...'): string { 35 if (text.length <= maxLength) return text; 36 return text.slice(0, maxLength - ellipsis.length).trim() + ellipsis; 37} 38 39/** 40 * Safely escapes HTML to prevent XSS attacks 41 * @param text - The text to escape 42 * @returns HTML-safe text 43 */ 44export function escapeHtml(text: string): string { 45 const div = typeof document !== 'undefined' ? document.createElement('div') : null; 46 if (div) { 47 div.textContent = text; 48 return div.innerHTML; 49 } 50 // Server-side fallback 51 return text 52 .replace(/&/g, '&amp;') 53 .replace(/</g, '&lt;') 54 .replace(/>/g, '&gt;') 55 .replace(/"/g, '&quot;') 56 .replace(/'/g, '&#039;'); 57} 58 59/** 60 * Generates initials from a name (max 2 characters) 61 * @param name - The name to generate initials from 62 * @returns Uppercase initials 63 */ 64export function getInitials(name: string): string { 65 const words = name.trim().split(/\s+/); 66 if (words.length === 1) { 67 return words[0].charAt(0).toUpperCase(); 68 } 69 return (words[0].charAt(0) + words[words.length - 1].charAt(0)).toUpperCase(); 70} 71 72/** 73 * Debounces a function call 74 * @param func - The function to debounce 75 * @param delay - Delay in milliseconds 76 * @returns Debounced function 77 */ 78export function debounce<T extends (...args: any[]) => any>( 79 func: T, 80 delay: number 81): (...args: Parameters<T>) => void { 82 let timeoutId: ReturnType<typeof setTimeout>; 83 return (...args: Parameters<T>) => { 84 clearTimeout(timeoutId); 85 timeoutId = setTimeout(() => func(...args), delay); 86 }; 87} 88 89/** 90 * Throttles a function call 91 * @param func - The function to throttle 92 * @param limit - Time limit in milliseconds 93 * @returns Throttled function 94 */ 95export function throttle<T extends (...args: any[]) => any>( 96 func: T, 97 limit: number 98): (...args: Parameters<T>) => void { 99 let inThrottle: boolean; 100 return (...args: Parameters<T>) => { 101 if (!inThrottle) { 102 func(...args); 103 inThrottle = true; 104 setTimeout(() => (inThrottle = false), limit); 105 } 106 }; 107}