const STRATEGIES = [ 'LAST_GET_TIME', 'GETS_COUNT' ] class SimpleDataStore { #store #limit #strategy constructor (options = {}) { if (typeof options !== 'object') { throw new TypeError('Missing options object.') } if (!Number.isFinite(options.limit) || options.limit <= 1) { throw new TypeError('Limit must be a finite number that is at least 2.') } if (!STRATEGIES.includes(options.strategy)) { throw new TypeError(`Strategy must be one of these: ${STRATEGIES.map(s => `"${s}"`).join(', ')}.`) } this.#store = new Map() this.#limit = options.limit this.#strategy = options.strategy } clear () { return this.#store.clear() } delete (key) { return this.#store.delete(key) } deleteStalest () { const stalest = this.getStalest() if (stalest) { return this.#store.delete(stalest) } } get (key) { const entry = this.#store.get(key) // This may return undefined or null // undefined should be an indicator for when the key legitimately has not been set, // null should be an indicator for when the key is still being held via hold() function if (!entry) return entry switch (this.#strategy) { case STRATEGIES[0]: entry.stratval = Date.now() break case STRATEGIES[1]: entry.stratval++ break } this.#store.set(key, entry) return entry.value } getStalest () { let stalest = [null, { stratval: Infinity }] switch (this.#strategy) { // both "lastGetTime" and "getsCount" simply must find lowest value // to determine the stalest entry case STRATEGIES[0]: case STRATEGIES[1]: for (const entry of this.#store) { if (entry[1] && entry[1].stratval < stalest[1].stratval) { stalest = entry } } break } // return its key only return stalest[0] } hold (key) { if (this.#store.size >= this.#limit) { this.deleteStalest() } return this.#store.set(key, null) && true } set (key, value) { // Only do deleteStalest() if this key legitimately had not been set or held via hold() if (this.#store.get(key) === undefined && this.#store.size >= this.#limit) { this.deleteStalest() } let stratval switch (this.#strategy) { case STRATEGIES[0]: stratval = Date.now() break case STRATEGIES[1]: stratval = 0 break } return this.#store.set(key, { value, stratval }) && true } get limit () { return this.#limit } set limit (_) { throw Error('This property is read-only.') } get size () { return this.#store.size } set size (_) { throw Error('This property is read-only.') } get strategy () { return this.#strategy } set strategy (_) { throw Error('This property is read-only.') } get store () { // return shallow copy return new Map(this.#store) } } module.exports = SimpleDataStore module.exports.STRATEGIES = STRATEGIES