export type Handler = ( parameters: DecoratorParameters ) => Promise; export type Method = (this: T, ...args: any[]) => Promise; export type Decorator = ( target: U, key: keyof U, descriptor: TypedPropertyDescriptor> ) => TypedPropertyDescriptor>; export interface DecoratorParameters { /** * Current call arguments. */ args: U; /** * A callback to call the decorated method with the current arguments. */ callback(): unknown; /** * Current call context. */ instance: T; } /** * Applies decorating function to intercept decorated method calls. * @param fn - The decorating function. */ export function decorate(fn: Handler): Decorator { const result: Decorator = ( target, key, /* TODO: Can descriptor be undefined ? */ descriptor = Object.getOwnPropertyDescriptor(target, key) ?? { enumerable: true, configurable: true, writable: true, } ) => { const { value } = descriptor; return Object.assign(descriptor, { value(this: T, ...args: any[]) { return fn({ args, instance: this, callback: () => value?.apply(this, args), }); }, }); }; return result; }