import { KeyType } from '../key-type' ; import { Cache } from '../cache' ; import { CacheSPI } from '../cache-spi' ; import { AbstractCache } from '../abstract' ; import { RemovalListener } from '../removal-listener' ; import { RemovalReason } from '../removal-reason' ; import { Metrics } from '../metrics/metrics' ; import { ON_REMOVE , ON_MAINTENANCE , TRIGGER_REMOVE , MAINTENANCE } from '../symbols' ; const DATA = Symbol ( 'boundlessData' ) ; const EVICTION_DELAY = 5000 ; /**
 * Options for a boundless cache.
 */ export interface BoundlessCacheOptions < K extends KeyType , V > { /**
     * Listener that triggers when a cached value is removed.
     */ removalListener ? : RemovalListener < K , V > | undefined | null ; } /**
 * Data as used by the boundless cache.
 */ interface BoundlessCacheData < K extends KeyType , V > { values : Map < K , V > ; removalListener : RemovalListener < K , V > | null ; evictionTimeout : any ; } /**
 * Boundless cache.
 */ export class BoundlessCache < K extends KeyType , V > extends AbstractCache < K , V > implements Cache < K , V > , CacheSPI < K , V > { private [ DATA ] : BoundlessCacheData < K , V > ; public [ ON_REMOVE ] ? : RemovalListener < K , V > ; public [ ON_MAINTENANCE ] ? : ( ) => void ; constructor ( options ) { super ( ) ; this [ DATA ] = { values : new Map ( ) , removalListener : options . removalListener || null , evictionTimeout : null } ; } /**
     * Get the maximum size this cache can be.
     */ get maxSize ( ) { return - 1 ; } /**
     * Get the current size of the cache.
     */ get size ( ) { return this [ DATA ] . values . size ; } get weightedSize ( ) { return this . size ; } /**
     * Cache and associate a value with the given key.
     */ public set ( key , value ) { const data = this [ DATA ] ; const old = data . values . get ( key ) ; data . values . set ( key , value ) ; if ( ! data . evictionTimeout ) { data . evictionTimeout = setTimeout ( ( ) => this [ MAINTENANCE ] ( ) , EVICTION_DELAY ) ; } if ( old !== undefined ) { this [ TRIGGER_REMOVE ] ( key , old , RemovalReason . REPLACED ) ; return old ; } else { return null ; } } /**
     * Get a value from this cache if it has been previously cached.
     */ public getIfPresent ( key ) { const data = this [ DATA ] ; const value = data . values . get ( key ) ; return value === undefined ? null : value ; } public peek ( key ) { const data = this [ DATA ] ; const value = data . values . get ( key ) ; return value === undefined ? null : value ; } /**
     * Delete any value associated with the given key from the cache.
     */ public delete ( key ) { const data = this [ DATA ] ; const old = data . values . get ( key ) ; data . values . delete ( key ) ; if ( old !== undefined ) { this [ TRIGGER_REMOVE ] ( key , old , RemovalReason . EXPLICIT ) ; if ( ! data . evictionTimeout ) { data . evictionTimeout = setTimeout ( ( ) => this [ MAINTENANCE ] ( ) , EVICTION_DELAY ) ; } return old ; } else { return null ; } } /**
     * Check if a certain value exists in the cache.
     */ public has ( key ) { const data = this [ DATA ] ; return data . values . has ( key ) ; } /**
     * Clear all of the cached data.
     */ public clear ( ) { const data = this [ DATA ] ; const oldValues = data . values ; data . values = new Map ( ) ; for ( const [ key , value ] of oldValues . entries ( ) ) { this [ TRIGGER_REMOVE ] ( key , value , RemovalReason . EXPLICIT ) ; } } /**
     * Get all of the keys currently in the cache.
     */ public keys ( ) { this [ MAINTENANCE ] ( ) ; return Array . from ( this [ DATA ] . values . keys ( ) ) ; } /**
     * Clean up.
     */ public cleanUp ( ) { this [ MAINTENANCE ] ( ) ; } get metrics ( ) : Metrics { throw new Error ( 'Metrics are not supported by this cache' ) ; } private [ TRIGGER_REMOVE ] ( key , value , reason ) { const data = this [ DATA ] ; const onRemove = this [ ON_REMOVE ] ; if ( onRemove ) { onRemove ( key , value , reason ) ; } if ( data . removalListener ) { data . removalListener ( key , value as V , reason ) ; } } private [ MAINTENANCE ] ( ) { const onEvict = this [ ON_MAINTENANCE ] ; if ( onEvict ) { onEvict ( ) ; } const data = this [ DATA ] ; if ( data . evictionTimeout ) { clearTimeout ( data . evictionTimeout ) ; data . evictionTimeout = null ; } } }