UNPKG

@catbee/utils

Version:

A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.

316 lines (313 loc) 13.5 kB
/* * The MIT License * * Copyright (c) 2026 Catbee Technologies. https://catbee.in/license * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /** * Delays execution for a specified number of milliseconds. * * @param {number} ms - The number of milliseconds to sleep. * @returns {Promise<void>} A Promise that resolves after the given time. */ declare function sleep(ms: number): Promise<void>; /** * Creates a debounced version of a function that delays its execution. * Provides `.cancel()` and `.flush()` methods. * * @template T * @param {T} fn - The function to debounce. * @param {number} delay - Delay in milliseconds. * @returns {T & { cancel: () => void; flush: () => void }} A debounced function. */ declare function debounce<T extends (...args: any[]) => void>(fn: T, delay: number): { (...args: Parameters<T>): void; cancel(): void; flush(): void; } & { cancel: () => void; flush: () => void; }; /** * Creates a throttled version of a function that limits its execution rate. * Allows control over leading/trailing invocation. * * @template T * @param {T} fn - The function to throttle. * @param {number} limit - Minimum time between calls in milliseconds. * @param {{ leading?: boolean, trailing?: boolean }} [opts] - Options for leading/trailing edge throttling. * @returns {(...args: Parameters<T>) => void} A throttled function. */ declare function throttle<T extends (...args: any[]) => void>(fn: T, limit: number, opts?: { leading?: boolean; trailing?: boolean; }): (...args: Parameters<T>) => void; /** * Retries an asynchronous function a given number of times with optional delay/backoff. * * @template T * @param {() => Promise<T>} fn - The async function to retry. * @param {number} [retries=3] - Number of retry attempts. * @param {number} [delay=500] - Delay in milliseconds between retries. * @param {boolean} [backoff=false] - Use exponential backoff between attempts. * @param {(error: unknown, attempt: number) => void} [onRetry] - Callback for each retry attempt. * @returns {Promise<T>} The result of the async function if successful. * @throws {*} The last encountered error if all retries fail. */ declare function retry<T>(fn: () => Promise<T>, retries?: number, delay?: number, backoff?: boolean, onRetry?: (error: unknown, attempt: number) => void): Promise<T>; /** * Wraps a promise and rejects it if it doesn't resolve within the specified timeout. * * @template T * @param {Promise<T>} promise - The original promise. * @param {number} ms - Timeout in milliseconds. * @param {string} [message="Operation timed out"] - Optional timeout message. * @returns {Promise<T>} A promise that resolves or rejects within the timeout. */ declare function withTimeout<T>(promise: Promise<T>, ms: number, message?: string): Promise<T>; /** * Executes async tasks in true batches. * Each batch runs in parallel, but batches run sequentially. * All tasks in a batch start at the same time, next batch waits for full completion. * NOTE: For more granular concurrency, use a "queue" or "pooled" approach. * * @template T * @param {Array<() => Promise<T>>} tasks - An array of functions that return Promises. * @param {number} limit - Number of tasks to run in parallel per batch. * @returns {Promise<T[]>} A promise that resolves to an array of resolved values. */ declare function runInBatches<T>(tasks: (() => Promise<T>)[], limit: number): Promise<T[]>; /** * Wraps a function and ensures it is only called once at a time. * Calls made while one is in progress will wait for the same Promise. * Optionally, new calls can be dropped while in progress (drop=true). * * @template TArgs * @template TResult * @param {(...args: TArgs) => Promise<TResult>} fn - The async function to wrap. * @param {boolean} [drop=false] - If true, new calls while one is pending are rejected. * @returns {(...args: TArgs) => Promise<TResult>} A wrapped function with singleton behavior. */ declare function singletonAsync<TArgs extends unknown[], TResult>(fn: (...args: TArgs) => Promise<TResult>, drop?: boolean): (...args: TArgs) => Promise<TResult>; /** * Resolves a list of async tasks in parallel, returning both resolved and rejected results. * * @template T * @param {Array<() => Promise<T>>} tasks - Array of promise-returning functions. * @returns {Promise<PromiseSettledResult<T>[]>} Results including status and value/reason. */ declare function settleAll<T>(tasks: (() => Promise<T>)[]): Promise<PromiseSettledResult<T>[]>; interface TaskQueue { <T>(taskFn: () => Promise<T>): Promise<T>; pause: () => void; resume: () => void; readonly length: number; readonly isPaused: boolean; } /** * A simple task queue that executes async tasks with a concurrency limit. * Exposes pause, resume, and queue length getters. * * @param {number} limit - Maximum number of concurrent tasks. * @returns {function & { pause: () => void, resume: () => void, length: number, isPaused: boolean }} * Enqueue function plus queue controls. */ declare function createTaskQueue(limit: number): TaskQueue; /** * Executes async functions sequentially and collects results. * Useful when order matters or tasks depend on each other. * * @template T * @param {Array<() => Promise<T>>} tasks - Array of promise-returning functions. * @returns {Promise<T[]>} Array of resolved values. */ declare function runInSeries<T>(tasks: (() => Promise<T>)[]): Promise<T[]>; /** * Memoizes an async function, caching results for repeated calls with identical arguments. * Optional TTL (time-to-live) for cached entries. * * @template T Function return type * @template Args Function arguments types * @param {(...args: Args) => Promise<T>} fn - The async function to memoize * @param {object} [options] - Memoization options * @param {number} [options.ttl] - Cache TTL in milliseconds (optional) * @param {(args: Args) => string} [options.keyFn] - Custom key generator function * @returns {(...args: Args) => Promise<T>} Memoized function */ declare function memoizeAsync<T, Args extends any[]>(fn: (...args: Args) => Promise<T>, options?: { ttl?: number; keyFn?: (args: Args) => string; }): (...args: Args) => Promise<T>; /** * Creates an abortable version of a promise that can be cancelled using an AbortController. * * @template T * @param {Promise<T>} promise - The promise to make abortable * @param {AbortSignal} signal - AbortSignal from AbortController * @param {any} [abortValue] - Value to use when rejecting on abort * @returns {Promise<T>} Promise that rejects if the signal is aborted */ declare function abortable<T>(promise: Promise<T>, signal: AbortSignal, abortValue?: any): Promise<T>; /** * Creates a promise with external resolve/reject functions. * Useful for creating promises that can be resolved or rejected from outside. * * @template T * @returns {[Promise<T>, (value: T | PromiseLike<T>) => void, (reason?: any) => void]} * Tuple of [promise, resolve, reject] */ declare function createDeferred<T>(): [Promise<T>, (value: T | PromiseLike<T>) => void, (reason?: any) => void]; /** * Chains a series of async functions, passing the result of each to the next. * Similar to function composition but for async functions. * * @template T * @param {Array<(input: any) => Promise<any>>} fns - Array of async functions to compose * @returns {(input: any) => Promise<T>} Composed function */ declare function waterfall<T>(fns: Array<(input: any) => Promise<any>>): (initialValue: any) => Promise<T>; /** * Creates a rate limiter that ensures functions aren't called more than * a specified number of times per interval. * * @template T * @param {(...args: any[]) => Promise<T>} fn - Function to rate limit * @param {number} maxCalls - Maximum calls allowed per interval * @param {number} interval - Time interval in milliseconds * @returns {(...args: any[]) => Promise<T>} Rate limited function */ declare function rateLimit<T>(fn: (...args: any[]) => Promise<T>, maxCalls: number, interval: number): (...args: any[]) => Promise<T>; /** * Circuit breaker pattern implementation for protecting against cascading failures. * Tracks failures and prevents calling the function when too many failures occur. * * @param fn - Function to protect with circuit breaker * @param options - Circuit breaker options * @returns Function wrapped with circuit breaker logic * * @example * ```typescript * const protectedFetch = circuitBreaker( * async (url) => { * const response = await fetch(url); * if (!response.ok) throw new Error(`HTTP error: ${response.status}`); * return response.json(); * }, * { * failureThreshold: 3, * resetTimeout: 30000, * onOpen: () => console.log('Circuit breaker opened'), * onClose: () => console.log('Circuit breaker closed') * } * ); * * // Will throw CircuitBreakerOpenError after failureThreshold consecutive failures * try { * const data = await protectedFetch('https://api.example.com'); * } catch (error) { * if (error instanceof CircuitBreakerOpenError) { * console.log('Service is currently unavailable, please try again later'); * } * } * ``` */ declare class CircuitBreakerOpenError extends Error { constructor(message?: string); } /** * Circuit breaker states */ declare enum CircuitBreakerState { CLOSED = "CLOSED", OPEN = "OPEN", HALF_OPEN = "HALF_OPEN" } interface CircuitBreakerOptions { /** Number of consecutive failures before opening circuit (default: 5) */ failureThreshold?: number; /** Time in milliseconds to wait before trying again (default: 10000) */ resetTimeout?: number; /** Number of successful calls to close the circuit again (default: 1) */ successThreshold?: number; /** Callback when circuit opens */ onOpen?: () => void; /** Callback when circuit closes */ onClose?: () => void; /** Callback when circuit enters half-open state */ onHalfOpen?: () => void; } declare function circuitBreaker<T, Args extends any[]>(fn: (...args: Args) => Promise<T>, options?: CircuitBreakerOptions): (...args: Args) => Promise<T>; /** * Run multiple async tasks with concurrency control. * * @param tasks - Array of async tasks to run * @param options - Concurrency options * @returns Promise that resolves when all tasks are complete * * @example * ```typescript * const urls = ['https://example.com/1', 'https://example.com/2', many more ]; * * // Process up to 5 requests at a time, with progress reporting * const results = await runWithConcurrency( * urls.map(url => () => fetch(url).then(res => res.json())), * { * concurrency: 5, * onProgress: (completed, total) => { * console.log(`Progress: ${completed}/${total}`); * } * } * ); * ``` */ declare function runWithConcurrency<T>(tasks: Array<() => Promise<T>>, options?: { /** Maximum number of tasks to run at once (default: 3) */ concurrency?: number; /** Called whenever a task completes */ onProgress?: (completed: number, total: number) => void; /** Abort signal to cancel execution */ signal?: AbortSignal; }): Promise<T[]>; /** * Optionally requires a module, returning null if not found. * * @template T * @param {string} name - The name of the module to require. * @returns {T | null} The required module or null if not found. */ declare function optionalRequire<T = any>(name: string): T | null; /** * Races promises and returns the first fulfilled value with its index. * If all promises reject, the last rejection is thrown. * * @template T * @param {Promise<T>[]} promises - Array of promises. * @returns {Promise<{ value: T; index: number }>} First fulfilled value with index. * * @example * const result = await raceWithValue([fetchA(), fetchB(), fetchC()]); * console.log(result.value, result.index); // First fulfilled promise result */ declare function raceWithValue<T>(promises: Promise<T>[]): Promise<{ value: T; index: number; }>; export { CircuitBreakerOpenError, CircuitBreakerState, abortable, circuitBreaker, createDeferred, createTaskQueue, debounce, memoizeAsync, optionalRequire, raceWithValue, rateLimit, retry, runInBatches, runInSeries, runWithConcurrency, settleAll, singletonAsync, sleep, throttle, waterfall, withTimeout }; export type { CircuitBreakerOptions, TaskQueue };