import functionDouble from "function-double" ; import { createWeakStorage } from "./weakStorage" ; import { WeakStorage } from "./types" ; type WeakStorageCreator = ( ) => WeakStorage ; const cacheStack = [ ] ; let cacheOverride ; const pushCache = ( cache ) => { cacheStack . push ( cache ) ; cacheOverride = cache ; } ; const popCache = ( cache ) => { const popped = cacheStack . pop ( ) ; if ( cache !== popped ) { console . error ( { expected : cache , given : popped , stack : cacheStack , } ) ; throw new Error ( 'kashe synchronization failed' ) ; } cacheOverride = cacheStack [ cacheStack . length - 1 ] ; } ; const addKashePrefix = ( name ) => `kashe-${ name }` ; const getCacheFor = ( fn , cacheCreator ) => { if ( ! cacheOverride ) { return ; } const cache = cacheOverride . get ( [ fn ] ) ; if ( cache ) { return cache . value ; } return cacheOverride . set ( [ fn ] , cacheCreator ( ) ) ; } ; export function weakMemoizeCreator ( cacheCreator = createWeakStorage , mapper ? ) { /**
     * memoizes a function
     */ return function kashe < Arg extends object , T extends any [ ] , Return > ( func , cache = cacheCreator ( ) ) : ( x : Arg , ... rest : T ) => Return { const _this_ = { func } ; return functionDouble ( ( ... args ) => { const localCache = getCacheFor ( _this_ , cacheCreator ) || cache ; const usedArgs = mapper ? args . map ( mapper ) : args ; const test = localCache . get ( usedArgs ) ; if ( test ) { return test . value ; } return localCache . set ( usedArgs , func ( ... args ) ) ; } , func , { name : addKashePrefix as any } ) ; } ; } param { Object } argument0 param argument1 see https example export const kashe = weakMemoizeCreator ( createWeakStorage ) ; /**
 * a special version of  kashe which does not strictly checks arg1+.
 * Could be used to bypass equality check, however use with caution
 *
 *  see https example export const weakKashe = weakMemoizeCreator ( createWeakStorage , ( arg , i ) => i > 0 ? String ( arg ) : arg ) ; function weakKasheFactory < T extends any [ ] , Return > ( func , indexId = 0 ) { const cache = createWeakStorage ( ) ; return function kasheFactory ( ... args ) { const localCache = cacheOverride || cache ; const cacheArg = [ args [ indexId ] ] ; const test = localCache . get ( cacheArg ) ; if ( test ) { return test . value ; } return localCache . set ( cacheArg , func ( ... args ) ) ; } ; } export function swap < T , K , R > ( fn ) { return ( k , t ) => fn ( t , k ) ; } type BoxedCall < T extends any [ ] , K > = ( state : object , ... rest : T ) => K ; param { Function } fn see https see inboxed - for nested caches.
 *
 *  example export function boxed < T extends any [ ] , K > ( fn ) { return kashe ( ( _ , ... rest ) => fn ( ... rest ) ) ; } const localCacheCreator = kashe ( ( _ ) => createWeakStorage ( ) ) ; /**
 * Prepends with additional cache-key, which will be used for any other  kashe call made inside.
 * inboxed scopes all the nested caches behind a first argument, creating a "sub cache".
 *
 * inboxed is about __isolation__. Or creating parallel caches.
 *
 *  param { Function } fn see boxed for non-nested caches.
 *  see https example export function inboxed < T extends any [ ] , K > ( fn ) { const factory = weakKasheFactory ( cacheVariation => { const cache = localCacheCreator ( cacheVariation ) ; return ( ... rest ) => { try { pushCache ( cache ) ; return fn ( ... rest ) ; } finally { popCache ( cache ) ; } } ; } ) ; return ( _ , ... rest ) => factory ( _ ) ( ... rest ) ; } param fn param options param options . singleton see inboxed for argument based cache separation
 *  see https export function fork < T extends any [ ] , K > ( fn , options ? ) { const cache = localCacheCreator ( { } ) ; const genLocalCache = ( ) => localCacheCreator ( { } ) ; return ( ... rest ) => { const cacheOverride = ( ( ! options || ! options . singleton ) ? getCacheFor ( cache , genLocalCache ) : null ) || cache ; try { pushCache ( cacheOverride ) ; return fn ( ... rest ) ; } finally { popCache ( cacheOverride ) ; } } ; }