import { assertMethodDecorator, isDecoratorCall, } from "../common/decorators.js"; import type { Method } from "../common/types.js"; import { isPromise, resolveCallable, } from "../common/utils.js"; export type ReportFunction = (data: ExactTimeReportData) => unknown; export interface ExactTimeReportData { args: Args; result: Result; execTime: number; } const defaultReporter: ReportFunction = (data): void => { console.info(data.execTime); }; type ExecTimeDecorator = ( value: Method, context: ClassMethodDecoratorContext>, ) => Method; export function createExecTimeMethod( originalMethod: Method, arg?: ReportFunction, Args> | keyof This, ): Method { const input = arg ?? defaultReporter; return function(this: This, ...args: Args): Return { const reporter = resolveCallable(this, input); const start = Date.now(); const result = originalMethod.apply(this, args); if (isPromise(result)) { return Promise.resolve(result).then((resolvedResult) => { reporter({ args, result: resolvedResult, execTime: Date.now() - start, }); return resolvedResult; }) as Return; } reporter({ args, result, execTime: Date.now() - start, }); return result; }; } export function execTime( value: Method, context: ClassMethodDecoratorContext>, ): Method; export function execTime( arg?: ReportFunction, Args> | keyof This, ): ExecTimeDecorator; export function execTime(inputOrValue?: unknown, context?: unknown): unknown { const decorate = ( value: Method, decoratorContext: ClassMethodDecoratorContext>, reporterArg?: ReportFunction, Args> | keyof This, ): Method => { assertMethodDecorator("execTime", value, decoratorContext); return createExecTimeMethod(value, reporterArg); }; if (arguments.length === 2 && isDecoratorCall(context)) { return decorate( inputOrValue as Method, context as ClassMethodDecoratorContext>, ); } const reporterArg = inputOrValue as ReportFunction | PropertyKey | undefined; return ( value: Method, decoratorContext: ClassMethodDecoratorContext>, ): Method => { return decorate(value, decoratorContext, reporterArg as ReportFunction, Args> | keyof This | undefined); }; }