utils-decorators
Star
Installation

This library was highly inspired by lodash but uses decorators to implement its util methods. The lib can be used both in node and in web application, it is built to be tree-shakable so you can use it even if you need a specific decorator. To install the lib via npm please follow the command bellow:

  npm i utils-decorators
After
Description:

Invocation of the decorated method will cause execution of the provided function method after the invocation of the decorated method.

Configuration:
  interface AfterConfig<T = any, D = any> {
    // the function (or the method name within the context) to invoke after the invocation of the decorated method
    func: keyof T | (args: ...any[]) => <D> ;

    // in case of a returned promise of the decorated method, should the after function wait for it to be resolved
    wait?: boolean;
  }
Arguments to provided method:
  interface AfterParams<D = any> {
    // the arguments that were provided to the decorated method
    args: any[];

    // the response data of the decorated method
    response: D;
  }
Decorator Example 1:
  import { after } from 'utils-decorators';

  class Example1 {

    @after({
      func: 'after',
    })
    foo(x: number): number {
      return 42;
    }

    after(respData: AfterParams<number>): void {
      console.log(respData.args, respData.response);
    }
  }
Function Example 1:
  import { afterify } from 'utils-decorators';

  function foo = () => {
    console.log('original method called');
  }

  const fooWithAfter = afterify(foo, {
    func: (respData: AfterParams<number>) => {
        console.log(respData.args, respData.response);
    };
  });
Decorator Example 2:
  import { after } from 'utils-decorators';

  class Example2 {

    @after({
      func: (respData: AfterParams<number>) => console.log(respData.args, respData.response),
      wait: true,
    })
    foo(x: number): Promise<number> {
      return this.numberProvider.getNumber(x);
    }
  }
Before
Description:

Invocation of the decorated method will cause an execution of the provided function method before the invocation of the decorated method.

Configuration:
  interface BeforeConfig<T = any, D = any> {
    // the function (or the method name within the context) to invoke before the invocation of the decorated method
    func: keyof T | (args: ...any[]) => <D> ;

    // in case of a returned promise of the decorated method, should the before function wait for it to be resolved
    wait?: boolean;
  }
Decorator Example 1:
  import { before } from 'utils-decorators';

  class Example1 {

    @before({
      func: 'before',
    })
    foo(x: number): number {
      return 42;
    }

    before(): void {
      console.log('this will be invoked before');
    }
  }
Function Example 1:
  import { beforify } from 'utils-decorators';

  function foo = () => {
    console.log('original method called');
  }

  const fooWithBefore = beforify(foo, {
    func: () => {
        console.log('this will be invoked before');
    };
  });
Decorator Example 2:
  import { before } from 'utils-decorators';

  class Example2 {

    @after({
      func: () => console.log('this will be invoked before');,
      wait: true,
    })
    foo(x: number): Promise<number> {
      return this.numberProvider.getNumber(x);
    }
  }
Cancel Previous
Description:

Invocation of the decorated method will cause the a rejection of the previous invocation with an error of CancelPromise type.

Decorator Example 1:
  import { cancelPrevious } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider.service';

  class Example1 {

    constructor(private readonly dataProvider: DataProvider) { }

    @cancelPrevious()
    getData(): Promise<DataDto> {
      return this.dataProvider.getData();
    }
  }
Function Example 1:
  import { cancelPreviousify } from 'utils-decorators';

  const dataProvider = cancelPreviousify(() => {
    return fetch(...);
  }));
Debounce
Description:

Causes a delay in the invocation of the decorated method by given time (in ms), if during the delay another invocation will happen, the delay will be restarted.

Configuration:
  // the time (in milliseconds) to wait before invoke
  delay: number;
            
Decorator Example 1:
  import { debounce } from 'utils-decorators';

  class Example1 {

    @debounce(1000)
    foo(x: number): number {
      return 42;
    }
  }
Function Example 1:
  import { debouncify } from 'utils-decorators';

  function foo(): void {
    console.log('will be debounced')
  }

  const debouncedFoo = debouncify(foo, 1000);
Delay
Description:

Causes a delay in the invocation of the decorated method by given time (in ms).

Configuration:
  // the time (in milliseconds) to wait before invoke
  delay: number;
            
Decorator Example 1:
  import { delay } from 'utils-decorators';

  class Example1 {

    @delay(1000)
    foo(x: number): number {
      return 42;
    }
  }
Function Example 1:
  import { delayfy } from 'utils-decorators';

  function foo(): void {
    console.log('will be delayed')
  }

  const delayedFoo = delayfy(foo, 1000);
Delegate
Description:

For a given input, if within the time period of the resolving of the promise of the first invocation the decorated method was invoked multiple times (with the same input) the response would be the promise that was generated by the first invocation. This way this decorator reduces the amount of calls to the implementation of the decorated method, for example accessing the database.

Configuration:
  // an optional function (or a function name within the context) that maps the arguments to a specific key.
  // The default mapping function is JSON.stringify(args).
  keyResolver?: string | (...args: any[]) => string;
            
Decorator Example 1:
  import { delegate } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider.service';

  class Example1 {

    constructor(private readonly dataProvider: DataProvider) { }

    @delegate()
    getData(): Promise<DataDto> {
      return this.dataProvider.getData();
    }
  }
Function Example 1:
  import { delegatify } from 'utils-decorators';

  function getData(): Promise<DataDto> {
    return dataProvider.getData();
  }

  const delegatedFoo = delegatify(getData);
Decorator Example 2 (with custom mapper):
  import { delegate } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider.service';

  class Example2 {

    constructor(private readonly dataProvider: DataProvider) { }

    @delegate((x, y) => `${x}_${y}`)
    getData(x, y): Promise<DataDto> {
      return this.dataProvider.getData(x, y);
    }
  }
Function Example 2 (with custom mapper):
  import { delegatify } from 'utils-decorators';

  function getData(): Promise<DataDto> {
    return dataProvider.getData();
  }

  const delegatedFoo = delegatify(getData, (x, y) => `${x}_${y}`);
Exec-Time
Description:

Measures the time that takes for the decorated method to response. By default will log the result using console.info() but this can be changed by providing your own reporter function.

Arguments to the reporter function:
  interface ExactTimeReportData = {
    args: any[];
    result: any;
    execTime: number;
  };
Configuration:
  // an optional function (or a function name within the context) that receives ExactTimeReportData
  // (see details above) as an argument. If the decorated method returns a Promise,
  // the decorator will wait for it to be resolved/rejected.
  // The default report function is (data: ExactTimeReportData) => console.info(data.execTime)
  reporter?: string | (data: ExactTimeReportData) => any;
            
Decorator Example 1:
  import { execTime } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider.service';

  class Example1 {

    constructor(private readonly dataProvider: DataProvider) { }

    @execTime()
    getData(): Promise<DataDto> {
      return this.dataProvider.getData();
    }
  }
Function Example 1:
  import { execTimify } from 'utils-decorators';

  function getData(): Promise<DataDto> {
    return dataProvider.getData();
  }

  const measuredFoo = execTimify(getData);
Decorator Example 2 (with custom reporter):
  import { execTime } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider.service';

  class Example2 {

    constructor(private readonly dataProvider: DataProvider) { }

    @execTime((data: ExactTimeReportData) => {
        console.log(data.args, data.result, data.execTime);
    })
    getData(x, y): Promise<DataDto> {
      return this.dataProvider.getData(x, y);
    }
  }
Function Example 2 (with custom reporter):
  import { delegatify } from 'utils-decorators';

  function getData(): Promise<DataDto> {
    return dataProvider.getData();
  }

  const measuredFoo = execTimify(getData, (data: ExactTimeReportData) => {
    console.log(data.args, data.result, data.execTime);
  });
Memoize
Description:

Memoizes the response that is being returned by the decorated method. Be default the key of the cached value will be the serialized JSON.stringify value of the provided arguments. You can supply your own key resolver. Also, you can provide your own cache, it has to implement the Cache<D> interface (see bellow), by default the decorator is using a simple Map<string, D>.

Configuration:
  interface MemoizeConfig<T, D> {
    // an optional cache object which should implement the Cache interface.
    // Internally the decorator is using Map<string, D>
    cache?: Cache<D>;

    // an optional function (or a function name within the context) that maps the arguments to a specific key.
    // The default mapping function is JSON.stringify(args).
    keyResolver?: string | (...args: any[]) => string;

    // An optional TTL (time to leave in milliseconds) for the entry to be removed from the cache.
    // If not provided the cache will be never cleared.
    expirationTimeMs?: number;
  }
Decorator Example 1 (cache with no ttl):
  import { memoize } from 'utils-decorators';

  class Example1 {

    @memoize()
    fibo(n: number): number; {
      if (n < 2) return 1;

      return this.fibo(n - 1) + this.fibo(n - 2);
    }
  }
Function Example 1 (cache with no ttl):
  import { memoizify } from 'utils-decorators';

  function fibo(n: number): number {
    if (n < 2) return 1;

     return fibo(n - 1) + fibo(n - 2);
  );

  const memoizedFibo = memoizify(fibo);
Decorator Example 2 (with a ttl only):
  import { memoize, Cache } from 'utils-decorators';

  class Example2 {

    // a one hour ttl
    @memoize(1000 * 60 * 60)
    fibo(n: number): number; {
      if (n < 2) return 1;

      return this.fibo(n - 1) + this.fibo(n - 2);
    }
  }
Function Example 2 (with a ttl only):
  import { memoizify } from 'utils-decorators';

  function fibo(n: number): number {
    if (n < 2) return 1;

     return fibo(n - 1) + fibo(n - 2);
  );

  // a one hour ttl
  const memoizedFibo = memoizeify(fibo, 1000 * 60 * 60);
Decorator Example 3 (with a custom cache):
  import { memoize, Cache } from 'utils-decorators';

  class CustomCache implements Cache<number> {
    set: (key: string, value: number) => {...};
    get: (key: string) => number | null => {...}
    delete: (key: string) => void => {...}
    has: (key: string) => boolean => {...}
  }

  const customCache = new CustomCache();

  class Example3 {

    @memoize({
      cache: customCache
    })
    fibo(n: number): number; {
      if (n < 2) return 1;

      return this.fibo(n - 1) + this.fibo(n - 2);
    }
  }
Function Example 3 (with a custom cache):
  import { memoizify } from 'utils-decorators';

  const customCache: Cache<number> = {
    set: (key: string, value: number) => {...};
    get: (key: string) => number | null => {...}
    delete: (key: string) => void => {...}
    has: (key: string) => boolean => {...}
  }

  function fibo(n: number): number {
    if (n < 2) return 1;

     return fibo(n - 1) + fibo(n - 2);
  );

  const memoizedFibo = memoizeify(fibo, {
    cache: customCache,
  });
Memoize Async
Description:

Memoizes the promise that being returned by the decorated method. If the promise would be rejected, the promise won't be memoized. Another great feature of this decorator is that it delegates requests, for example if the same method has been called more than one time after the promise was resolved, only one invocation of the decorated method will be invoked. Be default the key of the cached value will be the serialized JSON.stringify value of the provided arguments. You can supply your own key resolver. Also, you can provide your own cache, it has to implement the GetterSetter<D> interface, by default the decorator is using a simple Map<string, Promise<D>>. For further readying please read this blog post.

Configuration:
  interface MemoizeAsyncConfig<T, D> {
    // an optional cache object which should implement the Cache or the AsyncCache interface.
    // Internally the decorator is using Map<string, D>
    cache?: Cache<D>;

    // an optional function (or a function name within the context) that maps the arguments to a specific key.
    // The default mapping function is JSON.stringify(args).
    keyResolver?: string | (...args: any[]) => string;

    // An optional TTL (time to leave in milliseconds) for the entry to be removed from the cache.
    // If not provided the cache will be never cleared.
    expirationTimeMs?: number;
  }
Decorator Example 1 (cache with no ttl):
  import { memoizeAsync } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider';

  class Example1 {

    constructor(private readonly dataProvider: DataProvider) { }

    @memoizeAsync()
    getData(): Promise<DataDto>; {
      return this.dataProvider.getData();
    }
  }
Function Example 1 (cache with no ttl):
  import { memoizeAsyncify } from 'utils-decorators';
  import { dataProvider } from './data-provider';

  const memoizedDataProvider = memoizeAsyncify(dataProvider);
Decorator Example 2 (with a ttl only):
  import { memoizeAsync } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider';

  class Example2 {

    constructor(private readonly dataProvider: DataProvider) { }

    // a one hour ttl
    @memoizeAsync(1000 * 60 * 60)
    getData(): Promise<DataDto>; {
      return this.dataProvider.getData();
    }
  }
Function Example 2 (with a ttl only):
  import { memoizeAsyncify, Cache } from 'utils-decorators';
  import { dataProvider } from './data-provider';

  // a one hour ttl
  const memoizedDataProvider = memoizeAsyncify(dataProvider, 1000 * 60 * 60);
Decorator Example 3 (with a custom cache):
  import { memoizeAsync, Cache } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider';

  class CustomCache implements Cache<number> {
    set: (key: string, value: number) => {...};
    get: (key: string) => number | null => {...}
    delete: (key: string) => void => {...}
    has: (key: string) => boolean => {...}
  }

  const customCache = new CustomCache();

  class Example3 {

    constructor(private readonly dataProvider: DataProvider) { }

    @memoizeAsync({
        cache: customCache,
        expirationTimeMs: 1000 * 60 * 60,
    })
    getData(): Promise<DataDto>; {
      return this.dataProvider.getData();
    }
  }
Function Example 3 (with a custom cache):
  import { memoizeAsyncify } from 'utils-decorators';
  import { dataProvider } from './data-provider';

  const customCache: Cache<number> = {
    set: (key: string, value: number) => {...};
    get: (key: string) => number | null => {...}
    delete: (key: string) => void => {...}
    has: (key: string) => boolean => {...}
  }

  const memoizedDataProvider = memoizeAsyncify(dataProvider, {
      cache: customCache,
      expirationTimeMs: 1000 * 60 * 60,
  });
Decorator Example 4 (with an async cache):
  import { memoizeAsync, AsyncCache } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider';

  class CustomCache implements AsyncCache<number> {
    set: (key: string, value: number): Promise<void> => {...};
    get: (key: string) => Promise<number | null> => {...}
    delete: (key: string) => Promise<void> => {...}
    has: (key: string) => Promise<boolean> => {...}
  }

  const customCache = new CustomCache();

  class Example4 {

    constructor(private readonly dataProvider: DataProvider) { }

    @memoizeAsync({
        cache: customCache,
        expirationTimeMs: 1000 * 60 * 60,
    })
    getData(): Promise<DataDto>; {
      return this.dataProvider.getData();
    }
  }
Function Example 4 (with an async cache):
  import { memoizeAsyncify, AsyncCache } from 'utils-decorators';
  import { dataProvider } from './data-provider';

  const customCache: AsyncCache<number> = {
    set: (key: string, value: number): Promise<void> => {...};
    get: (key: string) => Promise<number | null> => {...}
    delete: (key: string) => Promise<void> => {...}
    has: (key: string) => Promise<boolean> => {...}
  }

  const memoizedDataProvider = memoizeAsyncify(dataProvider, {
      cache: customCache,
      expirationTimeMs: 1000 * 60 * 60,
  });
Multi Dispatch
Description:

Will invoke the decorated method the amount of provided time in parallel. This decorator is useful when you want to increase the chance of the decorated method to return a value. Note that the fastest resolved promise will resolve the decorated method and in case where all dispatched will fail the last error would be of the last rejected promise.

Configuration:
  // the amount of invocations to generate simultaneously
  dispatchesAmount: number;
            
Decorator Example 1:
  import { multiDispatch } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider';

  class Example1 {

    constructor(private readonly dataProvider: DataProvider) { }

    @multiDispatch(2)
    getData(): Promise<DataDto> {
      return this.dataProvider.getData();
    }
  }
Function Example 1:
  import { multiDispatchify } from 'utils-decorators';
  import { dataProvider } from './data-provider';

  const multiDispatchedDataProvider = multiDispatchify(dataProvider.getData, 2);
On Error
Description:

This decorator will catch errors thrown from the decorated method and invoke the provided `func` function. If the decorated method returns a Promise the wait property should be set to true in order to handle the promise rejection correctly.

Configuration:
  interface OnErrorConfig<T = any, D = any> {
    // the function (or the method name within the context) to invoke on an error of the decorated method
    func: keyof T | (args: ...any[]) => <D> ;
  }
Decorator Example 1:
  import { onError } from 'utils-decorators';

  class Example1 {

    @onError({
      func: 'errorHandler',
    })
    foo(x: number): Promise<number> {
      return doSomething();
    }

    errorHandler(e: Error): void {
      console.error(e);
    }
  }
Function Example 1:
  import { onErrorify } from 'utils-decorators';

  const wrappedFunction = onErrorify(
    () => {
      doSomething();
    },
    {
      func: console.error,
    }
  );
Rate Limit
Description:

Limits the amount of calls to the decorated method in a given time period. This decorator can support both local (in-memory) counter or a distributed one.

Configuration:
  interface RateLimitConfigs<T = any> {
    // the time window for which the rate-limit should hold the counts (time in milliseconds)
    timeSpanMs: number;

    // the amount of call allowed in the provided timeSpanMs.
    allowedCalls: number;

    // Sometimes you want to group your calls and only then apply the rate-limit.
    // By providing the keyResolver you can group your calls by the provided arguments.
    // You should supply here an optional function (or the method name within the context) which will be invoked to get
    // in order to get a key
    keyResolver?: keyof T | (...args: any[]) => string;

    // An optional custom rate limit counter, note that you can only provide rateLimitCounter
    // or rateLimitAsyncCounter - not both
    rateLimitCounter?: RateLimitCounter;

    // An optional custom async rate limit counter, note that you can only provide rateLimitCounter
    // or rateLimitAsyncCounter - not both
    rateLimitAsyncCounter?: RateLimitAsyncCounter;

    // An optional custom handler which will be invoked when the amount of calls has exceeds,
    // By default the decorator will throw an Error
    exceedHandler?: () => void;
  }
Decorator Example 1:
  import { rateLimit } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider.service';

  class Example1 {

    constructor(private readonly dataProvider: DataProvider) { }

    @rateLimit({
      timeSpanMs: 2000,
      allowedCalls: 10,
    })
    getData(): Promise<DataDto> {
      return this.dataProvider.getData();
    }
  }
Function Example 1:
  import { rateLimitify } from 'utils-decorators';
  import { dataProvider } from './data-provider';

  const delegatedFoo = rateLimitify(dataProvider.getData, {
    timeSpanMs: 2000,
    allowedCalls: 10,
  });
Decorator Example 2 (with custom keyResolver):
  import { rateLimit } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider.service';

  class Example1 {

    constructor(private readonly dataProvider: DataProvider) { }

    @rateLimit({
      timeSpanMs: 2000,
      allowedCalls: 10,
      keyResolver: 'groupCalls',
    })
    getData(userId: number): Promise<DataDto> {
      return this.dataProvider.getUserData(userId);
    }

    groupCalls(userId: number): string {
      return userId.toString();
    }
  }
Function Example 2 (with custom keyResolver):
  import { rateLimitify } from 'utils-decorators';
  import { dataProvider } from './data-provider';

  const delegatedFoo = rateLimitify(dataProvider.getUserData, {
    timeSpanMs: 2000,
    allowedCalls: 10,
    keyResolver: (userId: number) => {
      return userId.toString();
    },
  });
Decorator Example 3 (with custom rate limit counter):
  import { rateLimit, RateLimitCounter } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider.service';

  class CustomCounter implements RateLimitCounter {
    inc: (key: string): void => { ... };
    dec: (key: string): void => { ... };
    getCount: (key: string): number => { ... };
  }

  const customCounter = new CustomCounter();

  class Example3 {

    constructor(private readonly dataProvider: DataProvider) { }

    @rateLimit({
      timeSpanMs: 2000,
      allowedCalls: 10,
      rateLimitCounter: customCounter,
    })
    getData(): Promise<DataDto> {
      return this.dataProvider.getData();
    }
  }
Function Example 3 (with custom rate limit counter):
  import { rateLimitify, RateLimitCounter } from 'utils-decorators';
  import { dataProvider } from './data-provider';

  const customCounter: RateLimitCounter = {
    inc: (key: string): void => { ... };
    dec: (key: string): void => { ... };
    getCount: (key: string): number => { ... };
  }

  const delegatedFoo = rateLimitify(dataProvider.getData, {
    timeSpanMs: 2000,
    allowedCalls: 10,
    rateLimitCounter: customCounter,
  });
Decorator Example 4 (with custom async rate limit counter):
  import { rateLimit, RateLimitAsyncCounter } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider.service';

  class CustomCounter implements RateLimitAsyncCounter {
    inc: (key: string): Promise<void> => { ... };
    dec: (key: string): Promise<void> => { ... };
    getCount: (key: string): Promise<number> => { ... };
  }

  const customCounter = new CustomCounter();

  class Example4 {

    constructor(private readonly dataProvider: DataProvider) { }

    @rateLimit({
      timeSpanMs: 2000,
      allowedCalls: 10,
      rateLimitAsyncCounter: customCounter,
    })
    getData(): Promise<DataDto> {
      return this.dataProvider.getData();
    }
  }
Function Example 4 (with custom async rate limit counter):
  import { rateLimitify, RateLimitAsyncCounter } from 'utils-decorators';
  import { dataProvider } from './data-provider';

  const customCounter: RateLimitAsyncCounter = {
    inc: (key: string): Promise<void> => { ... },
    dec: (key: string): Promise<void> => { ... },
    getCount: (key: string): Promise<number> => { ... },
  }

  const delegatedFoo = rateLimitify(dataProvider.getData, {
    timeSpanMs: 2000,
    allowedCalls: 10,
    rateLimitAsyncCounter: customCounter,
  });
Decorator Example 5 (with custom exceedHandler):
  import { rateLimit } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider.service';

  class Example1 {

    constructor(private readonly dataProvider: DataProvider) { }

    @rateLimit({
      timeSpanMs: 2000,
      allowedCalls: 10,
      exceedHandler: 'handler',
    })
    getData(userId: number): Promise<DataDto> {
      return this.dataProvider.getUserData(userId);
    }

    handler(): void {
      throw new Error('too much call in allowed window');
    }
  }
Function Example 5 (with custom exceedHandler):
  import { rateLimitify } from 'utils-decorators';
  import { dataProvider } from './data-provider';

  const delegatedFoo = rateLimitify(dataProvider.getUserData, {
    timeSpanMs: 2000,
    allowedCalls: 10,
    exceedHandler: (userId: number) => {
      throw new Error('too much call in allowed window');
    },
  });
Readonly
Description:

This decorator prevents setting new values to decorated property.

Decorator Example 1:
  import { readonly } from 'utils-decorators';

  class Example1 {

    @readonly()
    userId: number = 100;
  }
Refreshable
Description:

This decorator provides an ability to access a property which value is being updated over and over in a given interval (in ms) by the returned value of the provided method. Note that the method can also return a promise which will be resolved after each interval. In order to cancel the refreshment of the data you need to set the decorated value to null (this is very important note to prevent memory leaks). Setting any other value will be ignored. Another important note is that the initial value of the decorated attribute must not be null. Any other value should be fine.

Configuration:
  // the amount of invocations to generate simultaneously
  interface RefreshableConfig<D> {
    // the function that will provide the data to the decorated attribute
    dataProvider: AsyncMethod<D>;

    // the time interval (in milliseconds) in which the data will be refreshed
    intervalMs: number;
  }
            
Decorator Example 1:
  import { refreshable } from 'utils-decorators';
  import { userProvider, UserDto } from 'user.service';

  class Example1 {

    @refreshable({
        dataProvider: userProvider.getUsers,

        // 10 minuts
        intervalMs: 1000 * 60 * 10
    })
    users: UserDto[];
  }
Retry
Description:

Retries execution of the decorated method. The method will be invoked extra x + 1 (where x is the retries values) in the worst case (when all invocation failed). You can provide you own delay time (or delay times array) between the invocations - see details below. Note that the default delay between retries is 1000ms. You can also provide an onRetry callback function (or name of the method in the decorated class). This callback will be invoked before every retry call. Note that if you are providing a RetryInputConfig, you can pass retries and delay (if not provided than will use the default 1000ms delay) or delaysArray but not both.

Configuration:
  type RetryInput = number | number[] | RetryInputConfig;

  export interface RetryInputConfig {
    delaysArray?: number[];
    retries?: number;
    delay: number;
    onRetry?: OnRetry;
  }

  export type OnRetry = (error: Error, retriesCount: number) => void | string;
            
Decorator Example 1:
  import { retry } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider';

  class Example1 {

    constructor(private readonly dataProvider: DataProvider) { }

    @retry(3)
    getData: number): DataDto {
      return this.dataProvider.getData();
    }
  }
Function Example 1:
  import { retrify } from 'utils-decorators';
  import { getData } from './data-provider';

  const debouncedFoo = retrify(getData, 3);
Decorator Example 2 (with delays array):
  import { retry } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider';

  class Example2 {

    constructor(private readonly dataProvider: DataProvider) { }

    @retry([1000, 2000, 3000])
    getData: number): DataDto {
      return this.dataProvider.getData();
    }
  }
Function Example 2 (with delays array):
  import { retrify } from 'utils-decorators';
  import { getData } from './data-provider';

  const debouncedFoo = retrify(getData, [1000, 2000, 3000]);
Decorator Example 3 (with configuration object):
  import { retry } from 'utils-decorators';
  import { DataProvider, DataDto } from './data-provider';

  class Example2 {

    constructor(private readonly dataProvider: DataProvider) { }

    @retry({
      retries: 3,
      delay: 1500,
      onError: (e, retriesCount) => console.log(e, retriesCount),
    })
    getData: number): DataDto {
      return this.dataProvider.getData();
    }

    onRetry(e: Error, retriesCount: number) {
      console.log(e, retriesCount)
    }
  }
Function Example 3 (with configuration object):
  import { retrify } from 'utils-decorators';
  import { getData } from './data-provider';

  const debouncedFoo = retrify(getData, {
    retries: 3,
    delay: 1500,
    onError: (e, retriesCount) => console.log(e, retriesCount),
  });
Throttle
Description:

Invocation of the decorated method will happen immediately, but if another invocation of this method will happen during the provided time (in ms) it will be ignored.

Configuration:
  // the time (in milliseconds) to delay the invocation between the calls
  delayMs: number;
            
Decorator Example 1:
  import { throttle } from 'utils-decorators';

  class Example1 {

    @throttle(1000)
    foo(x: number): number {
      return 42;
    }
  }
Function Example 1:
  import { throttlify } from 'utils-decorators';

  function foo(): void {
    console.log('will be throttled')
  }

  const delayedFoo = throttlify(foo, 1000);
Throttle Async
Description:

For a given limit, the decorator will invoke the decorated method jus the allowed times. Once one of the promises will be resolved the next call in queue will be invoked,

Configuration:
  // The limit of allowed concurrent calls
  limit: number;
            
Decorator Example 1:
  import { throttleAsync } from 'utils-decorators';
  import { DataProvider, DataDto }

  class Example1 {
    constructor(private readonly dataProvider: DataProvider) { }

    @throttleAsync(2)
    getData(): DataDto {
      return this.dataProvider.getData();
    }
  }
Function Example 1:
  import { throttleAsyncify } from 'utils-decorators';
  import { dataProvider } from './data-provider';

  const throttledDataProvider = throttleAsyncify(dataProvider.getData, 3);
Timeout
Description:

Will throw an error (TimeoutError) if the decorated method returned promise won't be resolved withing the provided timeout (timeout is in milliseconds).

Configuration:
  // time in milliseconds for waiting for the promise to be resolved
  ms: number;
            
Decorator Example 1:
  import { timeout } from 'utils-decorators';
  import { DataProvider, DataDto }

  class Example1 {
    constructor(private readonly dataProvider: DataProvider) { }

    @timeout(2000)
    getData(): DataDto {
      return this.dataProvider.getData();
    }
  }
Function Example 1:
  import { timeoutify } from 'utils-decorators';
  import { dataProvider } from './data-provider';

  const dataProviderWithTimeout = timeoutify(dataProvider.getData, 2000);