"use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( // If the importer is in node compatibility mode or this is not an ESM // file that has been converted to a CommonJS file using a Babel- // compatible transform (i.e. "__esModule" has not been set), then set // "default" to the CommonJS "module.exports" for node compatibility. isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // src/index.ts var src_exports = {}; __export(src_exports, { NodeCacheErrors: () => NodeCacheErrors, NodeCacheStore: () => NodeCacheStore, default: () => NodeCache }); module.exports = __toCommonJS(src_exports); var import_eventemitter3 = __toESM(require("eventemitter3"), 1); var import_cacheable2 = require("cacheable"); // src/store.ts var import_cacheable = require("cacheable"); var import_keyv = require("keyv"); var NodeCacheStore = class { _maxKeys = 0; _cache = new import_cacheable.Cacheable({ primary: new import_keyv.Keyv({ store: new import_cacheable.CacheableMemory() }) }); constructor(options) { if (options) { const cacheOptions = { ttl: options.ttl, primary: options.primary, secondary: options.secondary }; this._cache = new import_cacheable.Cacheable(cacheOptions); if (options.maxKeys) { this._maxKeys = options.maxKeys; if (this._maxKeys > 0) { this._cache.stats.enabled = true; } } } } /** * Cacheable instance. * @returns {Cacheable} * @readonly */ get cache() { return this._cache; } /** * Time to live in milliseconds. * @returns {number | string | undefined} * @readonly */ get ttl() { return this._cache.ttl; } /** * Time to live in milliseconds. * @param {number | string | undefined} ttl */ set ttl(ttl) { this._cache.ttl = ttl; } /** * Primary cache store. * @returns {Keyv} * @readonly */ get primary() { return this._cache.primary; } /** * Primary cache store. * @param {Keyv} primary */ set primary(primary) { this._cache.primary = primary; } /** * Secondary cache store. Learn more about the secondary cache store in the * [cacheable](https://github.com/jaredwray/cacheable/tree/main/packages/cacheable#storage-tiering-and-caching) documentation. * @returns {Keyv | undefined} */ get secondary() { return this._cache.secondary; } /** * Secondary cache store. Learn more about the secondary cache store in the * [cacheable](https://github.com/jaredwray/cacheable/tree/main/packages/cacheable#storage-tiering-and-caching) documentation. * @param {Keyv | undefined} secondary */ set secondary(secondary) { this._cache.secondary = secondary; } /** * Maximum number of keys to store in the cache. if this is set to a value greater than 0, * the cache will keep track of the number of keys and will not store more than the specified number of keys. * @returns {number} * @readonly */ get maxKeys() { return this._maxKeys; } /** * Maximum number of keys to store in the cache. if this is set to a value greater than 0, * the cache will keep track of the number of keys and will not store more than the specified number of keys. * @param {number} maxKeys */ set maxKeys(maxKeys) { this._maxKeys = maxKeys; if (this._maxKeys > 0) { this._cache.stats.enabled = true; } } /** * Set a key/value pair in the cache. * @param {string | number} key * @param {any} value * @param {number} [ttl] * @returns {boolean} */ async set(key, value, ttl) { if (this._maxKeys > 0) { if (this._cache.stats.count >= this._maxKeys) { return false; } } await this._cache.set(key.toString(), value, ttl); return true; } /** * Set multiple key/value pairs in the cache. * @param {NodeCacheItem[]} list * @returns {void} */ async mset(list) { const items = new Array(); for (const item of list) { items.push({ key: item.key.toString(), value: item.value, ttl: item.ttl }); } await this._cache.setMany(items); } /** * Get a value from the cache. * @param {string | number} key * @returns {any | undefined} */ async get(key) { return this._cache.get(key.toString()); } /** * Get multiple values from the cache. * @param {Array} keys * @returns {Record} */ async mget(keys) { const result = {}; for (const key of keys) { result[key.toString()] = await this._cache.get(key.toString()); } return result; } /** * Delete a key from the cache. * @param {string | number} key * @returns {boolean} */ async del(key) { return this._cache.delete(key.toString()); } /** * Delete multiple keys from the cache. * @param {Array} keys * @returns {boolean} */ async mdel(keys) { return this._cache.deleteMany(keys.map((key) => key.toString())); } /** * Clear the cache. * @returns {void} */ async clear() { return this._cache.clear(); } /** * Check if a key exists in the cache. * @param {string | number} key * @returns {boolean} */ async setTtl(key, ttl) { const item = await this._cache.get(key.toString()); if (item) { await this._cache.set(key.toString(), item, ttl); return true; } return false; } /** * Check if a key exists in the cache. If it does exist it will get the value and delete the item from the cache. * @param {string | number} key * @returns {any | undefined} */ async take(key) { return this._cache.take(key.toString()); } /** * Disconnect from the cache. * @returns {void} */ async disconnect() { await this._cache.disconnect(); } }; // src/index.ts var NodeCacheErrors = /* @__PURE__ */ ((NodeCacheErrors2) => { NodeCacheErrors2["ECACHEFULL"] = "Cache max keys amount exceeded"; NodeCacheErrors2["EKEYTYPE"] = "The key argument has to be of type `string` or `number`. Found: `__key`"; NodeCacheErrors2["EKEYSTYPE"] = "The keys argument has to be an array."; NodeCacheErrors2["ETTLTYPE"] = "The ttl argument has to be a number."; return NodeCacheErrors2; })(NodeCacheErrors || {}); var NodeCache = class extends import_eventemitter3.default { options = { // eslint-disable-next-line @typescript-eslint/naming-convention stdTTL: 0, checkperiod: 600, useClones: true, deleteOnExpire: true, maxKeys: -1 }; store = /* @__PURE__ */ new Map(); _stats = new import_cacheable2.CacheableStats({ enabled: true }); _cacheable = new import_cacheable2.CacheableMemory(); intervalId = 0; constructor(options) { super(); if (options) { this.options = { ...this.options, ...options }; } this.startInterval(); } /** * Sets a key value pair. It is possible to define a ttl (in seconds). Returns true on success. * @param {string | number} key - it will convert the key to a string * @param {any} value * @param {number} [ttl] - this is in seconds and undefined will use the default ttl * @returns {boolean} */ set(key, value, ttl) { if (typeof key !== "string" && typeof key !== "number") { throw this.createError("The key argument has to be of type `string` or `number`. Found: `__key`" /* EKEYTYPE */, key); } if (ttl && typeof ttl !== "number") { throw this.createError("The ttl argument has to be a number." /* ETTLTYPE */, this.formatKey(key)); } const keyValue = this.formatKey(key); const ttlValue = ttl ?? this.options.stdTTL; let expirationTimestamp = 0; if (ttlValue && ttlValue > 0) { expirationTimestamp = this.getExpirationTimestamp(ttlValue); } if (this.options.maxKeys) { const maxKeys = this.options.maxKeys; if (maxKeys > -1 && this.store.size >= maxKeys) { throw this.createError("Cache max keys amount exceeded" /* ECACHEFULL */, this.formatKey(key)); } } this.store.set(keyValue, { key: keyValue, value, ttl: expirationTimestamp }); this.emit("set", keyValue, value, ttlValue); this._stats.incrementKSize(keyValue); this._stats.incrementVSize(value); this._stats.setCount(this.store.size); return true; } /** * Sets multiple key val pairs. It is possible to define a ttl (seconds). Returns true on success. * @param {NodeCacheItem[]} data an array of key value pairs with optional ttl * @returns {boolean} */ mset(data) { if (!Array.isArray(data)) { throw this.createError("The keys argument has to be an array." /* EKEYSTYPE */); } for (const item of data) { this.set(item.key, item.value, item.ttl); } return true; } /** * Gets a saved value from the cache. Returns a undefined if not found or expired. If the value was found it returns the value. * @param {string | number} key if the key is a number it will convert it to a string * @returns {T} the value or undefined */ get(key) { const result = this.store.get(this.formatKey(key)); if (result) { if (result.ttl > 0) { if (result.ttl < Date.now()) { if (this.options.deleteOnExpire) { this.del(key); } this._stats.incrementMisses(); this.emit("expired", this.formatKey(key), result.value); return void 0; } this._stats.incrementHits(); if (this.options.useClones) { return this._cacheable.clone(result.value); } return result.value; } this._stats.incrementHits(); if (this.options.useClones) { return this._cacheable.clone(result.value); } return result.value; } this._stats.incrementMisses(); return void 0; } /** * Gets multiple saved values from the cache. Returns an empty object {} if not found or expired. * If the value was found it returns an object with the key value pair. * @param {Array} an object with the key as a property and the value as the value */ mget(keys) { const result = {}; for (const key of keys) { const value = this.get(key); if (value) { result[this.formatKey(key)] = value; } } return result; } /** * Get the cached value and remove the key from the cache. Equivalent to calling get(key) + del(key). * Useful for implementing single use mechanism such as OTP, where once a value is read it will become obsolete. * @param {string | number} key * @returns {T | undefined} the value or undefined */ take(key) { const result = this.get(key); if (result) { this.del(key); if (this.options.useClones) { return this._cacheable.clone(result); } return result; } return void 0; } /** * Delete a key. Returns the number of deleted entries. A delete will never fail. * @param {string | number | Array} key if the key is a number it will convert it to a string. if an array is passed it will delete all keys in the array. * @returns {number} if it was successful it will return the count that was deleted */ del(key) { if (Array.isArray(key)) { return this.mdel(key); } const result = this.store.get(this.formatKey(key)); if (result) { const keyValue = this.formatKey(key); this.store.delete(keyValue); this.emit("del", keyValue, result.value); this._stats.decreaseKSize(keyValue); this._stats.decreaseVSize(result.value); this._stats.setCount(this.store.size); return 1; } return 0; } /** * Delete all keys in Array that exist. Returns the number of deleted entries. * @param {Array} keys an array of keys * @returns {number} the count of deleted keys */ mdel(keys) { let result = 0; for (const key of keys) { result += this.del(key); } return result; } /** * Redefine the ttl of a key. Returns true if the key has been found and changed. * Otherwise returns false. If the ttl-argument isn't passed the default-TTL will be used. * @param {string | number} key if the key is a number it will convert it to a string * @param {number} [ttl] the ttl in seconds * @returns {boolean} true if the key has been found and changed. Otherwise returns false. */ ttl(key, ttl) { const result = this.store.get(this.formatKey(key)); if (result) { const ttlValue = ttl ?? this.options.stdTTL; result.ttl = this.getExpirationTimestamp(ttlValue); this.store.set(this.formatKey(key), result); return true; } return false; } /** * Receive the ttl of a key. * @param {string | number} key if the key is a number it will convert it to a string * @returns {number | undefined} 0 if this key has no ttl, undefined if this key is not in the cache, * a timestamp in ms representing the time at which this key will expire */ getTtl(key) { const result = this.store.get(this.formatKey(key)); if (result) { if (result.ttl === 0) { return 0; } return result.ttl; } return void 0; } /** * Returns an array of all existing keys. [ "all", "my", "keys", "foo", "bar" ] * @returns {string[]} an array of all keys */ keys() { const result = []; for (const key of this.store.keys()) { result.push(key); } return result; } /** * Returns boolean indicating if the key is cached. * @param {string | number} key if the key is a number it will convert it to a string * @returns {boolean} true if the key is cached */ has(key) { return this.store.has(this.formatKey(key)); } /** * Gets the stats of the cache * @returns {NodeCacheStats} the stats of the cache */ getStats() { const stats = { keys: this._stats.count, hits: this._stats.hits, misses: this._stats.misses, ksize: this._stats.ksize, vsize: this._stats.vsize }; return stats; } /** * Flush the whole data. * @returns {void} */ flushAll() { this.store.clear(); this.flushStats(); this.emit("flush"); } /** * Flush the stats. * @returns {void} */ flushStats() { this._stats = new import_cacheable2.CacheableStats({ enabled: true }); this.emit("flush_stats"); } /** * Close the cache. This will clear the interval timeout which is set on check period option. * @returns {void} */ close() { this.stopInterval(); } /** * Get the interval id * @returns {number | NodeJS.Timeout} the interval id */ getIntervalId() { return this.intervalId; } formatKey(key) { return key.toString(); } getExpirationTimestamp(ttlInSeconds) { const currentTimestamp = Date.now(); const ttlInMilliseconds = ttlInSeconds * 1e3; const expirationTimestamp = currentTimestamp + ttlInMilliseconds; return expirationTimestamp; } startInterval() { if (this.options.checkperiod && this.options.checkperiod > 0) { const checkPeriodinSeconds = this.options.checkperiod * 1e3; this.intervalId = setInterval(() => { this.checkData(); }, checkPeriodinSeconds); return; } this.intervalId = 0; } checkData() { for (const [key, value] of this.store.entries()) { if (value.ttl > 0 && value.ttl < Date.now()) { this.del(key); } } } stopInterval() { if (this.intervalId !== 0) { clearInterval(this.intervalId); this.intervalId = 0; } } createError(errorCode, key) { let error = errorCode; if (key) { error = error.replace("__key", key); } return new Error(error); } }; // Annotate the CommonJS export names for ESM import in node: 0 && (module.exports = { NodeCacheErrors, NodeCacheStore });