export function debounce(fn, delay = 300) { let timeoutId; return function (...args) { clearTimeout(timeoutId); timeoutId = setTimeout(() => fn.apply(this, args), delay); }; } export function throttle(fn, delay = 300) { let lastCall = 0; let timeoutId; return function (...args) { const now = Date.now(); if (now - lastCall >= delay) { lastCall = now; fn.apply(this, args); } else { clearTimeout(timeoutId); timeoutId = setTimeout(() => { lastCall = Date.now(); fn.apply(this, args); }, delay - (now - lastCall)); } }; } export function once(fn) { let called = false; return function (...args) { if (!called) { called = true; return fn.apply(this, args); } }; } export function compose(...fns) { return (value) => fns.reduceRight((acc, fn) => fn(acc), value); } export function pipe(...fns) { return (value) => fns.reduce((acc, fn) => fn(acc), value); } export async function retry(fn, maxAttempts = 3, delay = 1000) { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (error) { if (attempt === maxAttempts) throw error; await new Promise(resolve => setTimeout(resolve, delay * attempt)); } } } export function deepClone(obj) { if (obj === null || typeof obj !== 'object') return obj; if (obj instanceof Date) return new Date(obj.getTime()); if (obj instanceof Array) return obj.map(item => deepClone(item)); if (obj instanceof Object) { const cloned = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { cloned[key] = deepClone(obj[key]); } } return cloned; } }