UNPKG

@eklabdev/blingts4

Version:

A comprehensive TypeScript decorators library implementing Stage 2 decorators for TS 4.9+

201 lines (200 loc) 7.91 kB
/** * Debounce decorator for both sync and async methods * @param delay The delay in milliseconds */ /** * Debounce decorator for synchronous methods * @param delay The delay in milliseconds */ export function DebounceSync(delay) { // Global map to store timeouts by class and method name const timeoutMap = new Map(); return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; const methodName = propertyKey; descriptor.value = function (...args) { // Create a key using class name and method name const key = `${this.constructor.name}.${methodName}`; // Clear the previous timeout const existingTimeout = timeoutMap.get(key); if (existingTimeout) { clearTimeout(existingTimeout); } return new Promise(resolve => { const newTimeout = setTimeout(() => { // Execute the original method const result = originalMethod.apply(this, args); resolve(result); }, delay); // Store the new timeout ID timeoutMap.set(key, newTimeout); }); }; return descriptor; }; } /** * Debounce decorator for asynchronous methods * @param delay The delay in milliseconds */ export function DebounceAsync(delay) { // Global map to store timeouts by class and method name const timeoutMap = new Map(); return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; const methodName = propertyKey; descriptor.value = function (...args) { // Create a key using class name and method name const key = `${this.constructor.name}.${methodName}`; // Clear the previous timeout const existingTimeout = timeoutMap.get(key); if (existingTimeout) { clearTimeout(existingTimeout); } // Create a new promise that will resolve when the debounce completes const debouncePromise = new Promise(resolve => { const newTimeout = setTimeout(() => { resolve(null); }, delay); // Store the new timeout ID timeoutMap.set(key, newTimeout); }); // Chain the original method after the debounce completes return debouncePromise.then(() => { return originalMethod.apply(this, args); }); }; return descriptor; }; } /** * Throttle decorator for synchronous methods * @param interval The interval in milliseconds */ export function ThrottleSync(interval) { // Global map to store last call times by class and method name const lastCallMap = new Map(); return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; const methodName = propertyKey; descriptor.value = function (...args) { const now = Date.now(); const key = `${this.constructor.name}.${methodName}`; const lastCall = lastCallMap.get(key) || 0; if (now - lastCall >= interval) { lastCallMap.set(key, now); return originalMethod.apply(this, args); } return undefined; }; return descriptor; }; } /** * Throttle decorator for asynchronous methods * @param interval The interval in milliseconds */ export function ThrottleAsync(interval) { // Global maps to store state by class and method name const lastCallMap = new Map(); const lastResultMap = new Map(); return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; const methodName = propertyKey; descriptor.value = function (...args) { const now = Date.now(); const key = `${this.constructor.name}.${methodName}`; const lastCall = lastCallMap.get(key) || 0; if (now - lastCall >= interval) { const result = originalMethod.apply(this, args); lastCallMap.set(key, now); lastResultMap.set(key, result); return result; } const lastResult = lastResultMap.get(key); return lastResult || Promise.resolve(undefined); }; return descriptor; }; } /** * Timed decorator for both sync and async methods */ export function Timed() { return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; const methodName = propertyKey; descriptor.value = function (...args) { const start = performance.now(); try { const result = originalMethod.apply(this, args); if (result instanceof Promise) { return result.then(value => { const duration = performance.now() - start; console.log(`${this.constructor.name}.${methodName} took ${duration.toFixed(2)}ms`); return value; }, error => { const duration = performance.now() - start; console.log(`${this.constructor.name}.${methodName} failed after ${duration.toFixed(2)}ms`); throw error; }); } const duration = performance.now() - start; console.log(`${this.constructor.name}.${methodName} took ${duration.toFixed(2)}ms`); return result; } catch (error) { const duration = performance.now() - start; console.log(`${this.constructor.name}.${methodName} failed after ${duration.toFixed(2)}ms`); throw error; } }; return descriptor; }; } /** * Measure decorator for both sync and async methods * @param options Configuration options for measurement */ export function Measure(options = {}) { return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; const { memory = false, logger = console.log } = options; const methodName = propertyKey; descriptor.value = function (...args) { const start = performance.now(); const startMemory = memory && typeof process !== 'undefined' ? process.memoryUsage() : null; try { const result = originalMethod.apply(this, args); const logMetrics = () => { const duration = performance.now() - start; const metrics = { duration: Number(duration.toFixed(2)), }; if (memory && startMemory) { const endMemory = process.memoryUsage(); metrics.memory = endMemory.heapUsed - startMemory.heapUsed; } logger(`${this.constructor.name}.${methodName} metrics:`, metrics); }; if (result instanceof Promise) { return result.then(value => { logMetrics(); return value; }, error => { logMetrics(); throw error; }); } logMetrics(); return result; } catch (error) { const duration = performance.now() - start; logger(`${this.constructor.name}.${methodName} failed after ${duration.toFixed(2)}ms`); throw error; } }; return descriptor; }; }