import type { EqualityFn } from './types' ; const NOT_FOUND = 'NOT_FOUND' ; type NOT_FOUND_TYPE = typeof NOT_FOUND ; interface Entry { key : unknown ; value : unknown ; } interface Cache { get ( key ) : unknown | NOT_FOUND_TYPE ; put ( key , value ) : void ; getEntries ( ) : Entry [ ] ; clear ( ) : void ; } function createSingletonCache ( equals ) { let entry ; return { get ( key ) { if ( entry && equals ( entry . key , key ) ) { return entry . value ; } return NOT_FOUND ; } , put ( key , value ) { entry = { key , value } ; } , getEntries ( ) { return entry ? [ entry ] : [ ] ; } , clear ( ) { entry = undefined ; } } ; } function createLruCache ( maxSize , equals ) { let entries = [ ] ; function get ( key ) { const cacheIndex = entries . findIndex ( entry => equals ( key , entry . key ) ) ; if ( cacheIndex > - 1 ) { const entry = entries [ cacheIndex ] ; if ( cacheIndex > 0 ) { entries . splice ( cacheIndex , 1 ) ; entries . unshift ( entry ) ; } return entry . value ; } return NOT_FOUND ; } function put ( key , value ) { if ( get ( key ) === NOT_FOUND ) { entries . unshift ( { key , value } ) ; if ( entries . length > maxSize ) { entries . pop ( ) ; } } } function getEntries ( ) { return entries ; } function clear ( ) { entries = [ ] ; } return { get , put , getEntries , clear } ; } export const defaultEqualityCheck = ( a , b ) : boolean => { return a === b ; } ; export function createCacheKeyComparator ( equalityCheck ) { return function areArgumentsShallowlyEqual ( prev , next ) : boolean { if ( prev === null || next === null || prev . length !== next . length ) { return false ; } const length = prev . length ; for ( let i = 0 ; i < length ; i ++ ) { if ( ! equalityCheck ( prev [ i ] , next [ i ] ) ) { return false ; } } return true ; } ; } export interface DefaultMemoizeOptions { equalityCheck ? : EqualityFn ; resultEqualityCheck ? : EqualityFn ; maxSize ? : number ; } export function defaultMemoize < F extends ( ... args : any [ ] ) => any > ( func , equalityCheckOrOptions ? ) { const providedOptions = typeof equalityCheckOrOptions === 'object' ? equalityCheckOrOptions : { equalityCheck : equalityCheckOrOptions } ; const { equalityCheck = defaultEqualityCheck , maxSize = 1 , resultEqualityCheck } = providedOptions ; const comparator = createCacheKeyComparator ( equalityCheck ) ; const cache = maxSize === 1 ? createSingletonCache ( comparator ) : createLruCache ( maxSize , comparator ) ; function memoized ( ) { let value = cache . get ( arguments ) ; if ( value === NOT_FOUND ) { value = func . apply ( null , arguments ) ; if ( resultEqualityCheck ) { const entries = cache . getEntries ( ) ; const matchingEntry = entries . find ( entry => resultEqualityCheck ( entry . value , value ) ) ; if ( matchingEntry ) { value = matchingEntry . value ; } } cache . put ( arguments , value ) ; } return value ; } memoized . clearCache = ( ) => cache . clear ( ) ; return memoized as F & { clearCache : ( ) => void ; } ; }