All files cache.ts

100% Statements 27/27
100% Branches 16/16
100% Functions 7/7
100% Lines 22/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 658x                                                         8x       3x 2x 2x   7x     3x       7x 3x 3x 8x 8x 8x 8x 4x 4x 1x 1x 1x     3x 3x   4x      
import { hash } from './hash';
 
export interface CacheMechanism<R> {
  set: (key: string, data: R) => void;
  get: (key: string) => R | undefined;
}
export declare type ArgsType<T> = T extends (...args: infer U) => any ? U : [];
export interface CacheOptions<I extends any[], R> {
  cacheKeyFn?: (...args: I) => string;
  cacheMechanism?: CacheMechanism<R>;
}
 
/**
 * Function middleware that caches function output based on input
 * @param fn - target function
 * @param cacheFn - function that receives and return cache key
 * @signature
 *    P.cache(fn, options)
 * @signature
 *    P.cache(options)(fn)
 * @example
 *    const request = (url: string) => axios.get(url)
 *    const requestWithCache = P.cache(request, (url) => url)
 * @category Utility, Pipe
 */
export function cache<I extends (...args: any[]) => any>(
  fn: I,
  options?: CacheOptions<ArgsType<I>, ReturnType<I>>
): I;
export function cache<I extends any[], R>(
  fn: (...args: I) => R,
  options?: CacheOptions<I, R>
): (...args: I) => R {
  const defaultCache = (): CacheMechanism<R> => {
    const cache: Record<string, R> = {};
    return {
      get: key => {
        return cache[key];
      },
      set: (key, data) => {
        cache[key] = data;
      },
    };
  };
  const defaultCacheFn = (...args: I) => hash(JSON.stringify(args));
  const cacheFnF = options?.cacheKeyFn || defaultCacheFn;
  const cacheMechanism = options?.cacheMechanism || defaultCache();
  return (...args: I) => {
    const cacheId = cacheFnF(...args);
    const cached = cacheMechanism.get(cacheId);
    if (cached == null) {
      const result = fn(...args);
      if (result instanceof Promise) {
        return (result.then(r => {
          cacheMechanism.set(cacheId, r);
          return r;
        }) as unknown) as R;
      }
      cacheMechanism.set(cacheId, result);
      return result as R;
    }
    return cached;
  };
}