import { assertMethodDecorator, isDecoratorCall, } from "../common/decorators.js"; import type { AsyncMethod } from "../common/types.js"; import { isWeakMapKey } from "../common/utils.js"; import { ThrottleAsyncExecutor } from "./throttle-async-executor.js"; type ThrottleAsyncDecorator = ( value: AsyncMethod, context: ClassMethodDecoratorContext>, ) => AsyncMethod; export function createThrottledAsyncMethod( originalMethod: AsyncMethod, parallelCalls = 1, ): AsyncMethod { const executorsByInstance = new WeakMap>(); let fallbackExecutor: ThrottleAsyncExecutor | undefined; const getExecutor = (instance: This): ThrottleAsyncExecutor => { if (!isWeakMapKey(instance)) { fallbackExecutor ??= new ThrottleAsyncExecutor(originalMethod, parallelCalls); return fallbackExecutor; } const instanceKey = instance as object; const existingExecutor = executorsByInstance.get(instanceKey); if (existingExecutor !== undefined) { return existingExecutor; } const executor = new ThrottleAsyncExecutor(originalMethod, parallelCalls); executorsByInstance.set(instanceKey, executor); return executor; }; return function(this: This, ...args: Args): Promise { return getExecutor(this).exec(this, args); }; } export function throttleAsync( value: AsyncMethod, context: ClassMethodDecoratorContext>, ): AsyncMethod; export function throttleAsync(parallelCalls?: number): ThrottleAsyncDecorator; export function throttleAsync(inputOrValue?: unknown, context?: unknown): unknown { const decorate = ( value: AsyncMethod, decoratorContext: ClassMethodDecoratorContext>, parallelCalls = 1, ): AsyncMethod => { assertMethodDecorator("throttleAsync", value, decoratorContext); return createThrottledAsyncMethod(value, parallelCalls); }; if (arguments.length === 2 && isDecoratorCall(context)) { return decorate( inputOrValue as AsyncMethod, context as ClassMethodDecoratorContext>, ); } return ( value: AsyncMethod, decoratorContext: ClassMethodDecoratorContext>, ): AsyncMethod => { return decorate(value, decoratorContext, inputOrValue as number | undefined); }; }