Sign In

@cacheable/memory

Package Overview
Dependencies
Maintainers
1
Versions
14
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@cacheable/memory - npm Package Compare versions

Comparing version
2.0.9
to
2.2.0
+412
dist/index.d.mts
import { CacheableItem, CacheableItem as CacheableItem$1, CacheableStoreItem, CacheableStoreItem as CacheableStoreItem$1, GetOrSetFunctionOptions, GetOrSetFunctionOptions as GetOrSetFunctionOptions$1, GetOrSetSyncKey, GetOrSetSyncKey as GetOrSetSyncKey$1, GetOrSetSyncOptions, HashAlgorithm, HashAlgorithm as HashAlgorithm$1, Stats, Stats as Stats$1, StatsOptions, StatsSnapshot, WrapFunctionOptions, getOrSetSync, hash, hashToNumber } from "@cacheable/utils";
import { Hookified } from "hookified";
import { Keyv, KeyvStoreAdapter, StoredData } from "keyv";
//#region src/keyv-memory.d.ts
type KeyvCacheableMemoryOptions = CacheableMemoryOptions & {
namespace?: string;
};
declare class KeyvCacheableMemory implements KeyvStoreAdapter {
opts: CacheableMemoryOptions;
private readonly _defaultCache;
private readonly _nCache;
private _namespace?;
constructor(options?: KeyvCacheableMemoryOptions);
get namespace(): string | undefined;
set namespace(value: string | undefined);
get store(): CacheableMemory;
get<Value>(key: string): Promise<StoredData<Value> | undefined>;
getMany<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>>;
set(key: string, value: any, ttl?: number): Promise<void>;
setMany(values: Array<{
key: string;
value: any;
ttl?: number;
}>): Promise<void>;
delete(key: string): Promise<boolean>;
deleteMany?(key: string[]): Promise<boolean>;
clear(): Promise<void>;
has?(key: string): Promise<boolean>;
on(event: string, listener: (...arguments_: any[]) => void): this;
getStore(namespace?: string): CacheableMemory;
}
/**
* Creates a new Keyv instance with a new KeyvCacheableMemory store. This also removes the serialize/deserialize methods from the Keyv instance for optimization.
* @param options
* @returns
*/
declare function createKeyv(options?: KeyvCacheableMemoryOptions): Keyv;
//#endregion
//#region src/index.d.ts
/**
* Lifecycle hooks fired by {@link CacheableMemory}. Register handlers with the inherited
* `onHook(hook, handler)` method. Hooks are dispatched synchronously via `hookSync`, which skips
* `async` handler functions entirely — register only synchronous handlers.
*/
declare enum CacheableMemoryHooks {
BEFORE_SET = "BEFORE_SET",
AFTER_SET = "AFTER_SET",
BEFORE_SET_MANY = "BEFORE_SET_MANY",
AFTER_SET_MANY = "AFTER_SET_MANY",
BEFORE_GET = "BEFORE_GET",
AFTER_GET = "AFTER_GET",
BEFORE_GET_MANY = "BEFORE_GET_MANY",
AFTER_GET_MANY = "AFTER_GET_MANY",
BEFORE_DELETE = "BEFORE_DELETE",
AFTER_DELETE = "AFTER_DELETE",
BEFORE_DELETE_MANY = "BEFORE_DELETE_MANY",
AFTER_DELETE_MANY = "AFTER_DELETE_MANY",
BEFORE_CLEAR = "BEFORE_CLEAR",
AFTER_CLEAR = "AFTER_CLEAR"
}
type StoreHashAlgorithmFunction = (key: string, storeHashSize: number) => number;
/**
* @typedef {Object} CacheableMemoryOptions
* @property {number|string} [ttl] - Time to Live - If you set a number it is miliseconds, if you set a string it is a human-readable
* format such as `1s` for 1 second or `1h` for 1 hour. Setting undefined means that it will use the default time-to-live. If both are
* undefined then it will not have a time-to-live.
* @property {number|string} [maxTtl] - Maximum Time to Live - The upper bound for any TTL set on a cache entry. If a TTL (whether from the
* default or per-entry) exceeds this value, the entry's TTL is capped to maxTtl. Can be a number in milliseconds or a human-readable
* format such as `1s`, `1m`, `1h`, `1d`. Default is `undefined` (no maximum).
* @property {boolean} [useClone] - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
* @property {number} [lruSize] - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
* @property {number} [checkInterval] - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
* @property {number} [storeHashSize] - The number of how many Map stores we have for the hash. Default is 10.
* @property {boolean} [stats] - If true, it will track statistics such as hits, misses, gets, sets, and deletes for this
* instance. Statistics are accessible via the `stats` property. Default is `false`.
*/
type CacheableMemoryOptions = {
ttl?: number | string;
maxTtl?: number | string;
useClone?: boolean;
lruSize?: number;
checkInterval?: number;
storeHashSize?: number;
storeHashAlgorithm?: HashAlgorithm$1 | ((key: string, storeHashSize: number) => number);
stats?: boolean;
};
type SetOptions = {
ttl?: number | string;
expire?: number | Date;
};
/**
* The payload passed to the `BEFORE_SET` and `AFTER_SET` hooks. Inside a `BEFORE_SET` handler
* you can reassign `key`, `value`, or `ttl` to change what gets stored.
*/
type CacheableMemoryHookItem<T = unknown> = {
key: string;
value: T;
ttl?: number | string | SetOptions;
};
/** The payload passed to the `AFTER_GET` hook. `result` is `undefined` on a cache miss. */
type CacheableMemoryAfterGetItem<T = unknown> = {
key: string;
result: T | undefined;
};
/**
* The payload passed to the `AFTER_GET_MANY` hook. Entries are `undefined` for keys that were
* missing or expired, mirroring what `getMany` collects.
*/
type CacheableMemoryAfterGetManyItem<T = unknown> = {
keys: string[];
result: Array<T | undefined>;
};
declare const defaultStoreHashSize = 16;
declare const maximumMapSize = 16777216;
declare class CacheableMemory extends Hookified {
private _lru;
private _storeHashSize;
private _storeHashAlgorithm;
private _store;
private _ttl;
private _maxTtl;
private _useClone;
private _lruSize;
private _checkInterval;
private _interval;
private readonly _stats;
/**
* @constructor
* @param {CacheableMemoryOptions} [options] - The options for the CacheableMemory
*/
constructor(options?: CacheableMemoryOptions);
/**
* Gets the time-to-live
* @returns {number|string|undefined} - The time-to-live in miliseconds or a human-readable format. If undefined, it will not have a time-to-live.
*/
get ttl(): number | string | undefined;
/**
* Sets the time-to-live
* @param {number|string|undefined} value - The time-to-live in miliseconds or a human-readable format (example '1s' = 1 second, '1h' = 1 hour). If undefined, it will not have a time-to-live.
*/
set ttl(value: number | string | undefined);
/**
* Gets the maximum time-to-live. When set, any TTL that exceeds this value is capped to maxTtl.
* Entries with no TTL will also be capped to maxTtl. Default is `undefined` (no maximum).
* @returns {number|string|undefined} - The maximum TTL in milliseconds, human-readable format, or undefined.
*/
get maxTtl(): number | string | undefined;
/**
* Sets the maximum time-to-live. When set, any TTL that exceeds this value is capped to maxTtl.
* Entries with no TTL will also be capped to maxTtl.
* @param {number|string|undefined} value - The maximum TTL in milliseconds or human-readable format (e.g. '1s', '1h'). If undefined, no maximum is enforced.
*/
set maxTtl(value: number | string | undefined);
/**
* Gets whether to use clone
* @returns {boolean} - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
get useClone(): boolean;
/**
* Sets whether to use clone
* @param {boolean} value - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
set useClone(value: boolean);
/**
* Gets the size of the LRU cache
* @returns {number} - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
get lruSize(): number;
/**
* Sets the size of the LRU cache
* @param {number} value - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
set lruSize(value: number);
/**
* Gets the check interval
* @returns {number} - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
get checkInterval(): number;
/**
* Sets the check interval
* @param {number} value - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
set checkInterval(value: number);
/**
* Gets the size of the cache
* @returns {number} - The size of the cache
*/
get size(): number;
/**
* Gets the statistics of the cache. Statistics track aggregate counters such as `hits`, `misses`,
* `gets`, `sets`, `deletes`, `clears`, `count`, `ksize`, and `vsize`. They are disabled by default;
* enable them via the `stats` option or by setting `cache.stats.enabled = true`.
* @returns {Stats} - The statistics for this CacheableMemory instance
*/
get stats(): Stats$1;
/**
* Gets the number of hash stores
* @returns {number} - The number of hash stores
*/
get storeHashSize(): number;
/**
* Sets the number of hash stores. This will recreate the store and all data will be cleared
* @param {number} value - The number of hash stores
*/
set storeHashSize(value: number);
/**
* Gets the store hash algorithm
* @returns {HashAlgorithm | StoreHashAlgorithmFunction} - The store hash algorithm
*/
get storeHashAlgorithm(): HashAlgorithm$1 | StoreHashAlgorithmFunction;
/**
* Sets the store hash algorithm. This will recreate the store and all data will be cleared
* @param {HashAlgorithm | HashAlgorithmFunction} value - The store hash algorithm
*/
set storeHashAlgorithm(value: HashAlgorithm$1 | StoreHashAlgorithmFunction);
/**
* Gets the keys
* @returns {IterableIterator<string>} - The keys
*/
get keys(): IterableIterator<string>;
/**
* Gets the items
* @returns {IterableIterator<CacheableStoreItem>} - The items
*/
get items(): IterableIterator<CacheableStoreItem$1>;
/**
* Gets the store
* @returns {Array<Map<string, CacheableStoreItem>>} - The store
*/
get store(): Array<Map<string, CacheableStoreItem$1>>;
/**
* Gets the value of the key
* @param {string} key - The key to get the value
* @returns {T | undefined} - The value of the key
*/
get<T>(key: string): T | undefined;
/**
* Gets the values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {T[]} - The values of the keys
*/
getMany<T>(keys: string[]): T[];
/**
* Gets the raw value of the key
* @param {string} key - The key to get the value
* @returns {CacheableStoreItem | undefined} - The raw value of the key
*/
getRaw(key: string): CacheableStoreItem$1 | undefined;
/**
* Gets the raw values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {CacheableStoreItem[]} - The raw values of the keys
*/
getManyRaw(keys: string[]): Array<CacheableStoreItem$1 | undefined>;
/**
* Sets the value of the key
* @param {string} key - The key to set the value
* @param {any} value - The value to set
* @param {number|string|SetOptions} [ttl] - Time to Live - If you set a number it is miliseconds, if you set a string it is a human-readable.
* If you want to set expire directly you can do that by setting the expire property in the SetOptions.
* If you set undefined, it will use the default time-to-live. If both are undefined then it will not have a time-to-live.
* @returns {void}
*/
set(key: string, value: any, ttl?: number | string | SetOptions): void;
/**
* Sets the values of the keys
* @param {CacheableItem[]} items - The items to set
* @returns {void}
*/
setMany(items: CacheableItem$1[]): void;
/**
* Checks if the key exists
* @param {string} key - The key to check
* @returns {boolean} - If true, the key exists. If false, the key does not exist.
*/
has(key: string): boolean;
/**
* @function hasMany
* @param {string[]} keys - The keys to check
* @returns {boolean[]} - If true, the key exists. If false, the key does not exist.
*/
hasMany(keys: string[]): boolean[];
/**
* Take will get the key and delete the entry from cache
* @param {string} key - The key to take
* @returns {T | undefined} - The value of the key
*/
take<T>(key: string): T | undefined;
/**
* TakeMany will get the keys and delete the entries from cache
* @param {string[]} keys - The keys to take
* @returns {T[]} - The values of the keys
*/
takeMany<T>(keys: string[]): T[];
/**
* Delete the key
* @param {string} key - The key to delete
* @returns {void}
*/
delete(key: string): void;
/**
* Delete the keys
* @param {string[]} keys - The keys to delete
* @returns {void}
*/
deleteMany(keys: string[]): void;
/**
* Clear the cache
* @returns {void}
*/
clear(): void;
/**
* Get the store based on the key (internal use)
* @param {string} key - The key to get the store
* @returns {CacheableHashStore} - The store
*/
getStore(key: string): Map<string, CacheableStoreItem$1>;
/**
* Hash the key for which store to go to (internal use)
* @param {string} key - The key to hash
* Available algorithms are: SHA256, SHA1, MD5, and djb2Hash.
* @returns {number} - The hashed key as a number
*/
getKeyStoreHash(key: string): number;
/**
* Clone the value. This is for internal use
* @param {any} value - The value to clone
* @returns {any} - The cloned value
*/
clone(value: any): any;
/**
* Add to the front of the LRU cache. This is for internal use
* @param {string} key - The key to add to the front
* @returns {void}
*/
lruAddToFront(key: string): void;
/**
* Move to the front of the LRU cache. This is for internal use
* @param {string} key - The key to move to the front
* @returns {void}
*/
lruMoveToFront(key: string): void;
/**
* Remove a key from the LRU cache. This is for internal use
* @param {string} key - The key to remove
* @returns {void}
*/
lruRemove(key: string): void;
/**
* Resize the LRU cache. This is for internal use.
* @returns {void}
*/
lruResize(): void;
/**
* Check for expiration. This is for internal use
* @returns {void}
*/
checkExpiration(): void;
/**
* Start the interval check. This is for internal use
* @returns {void}
*/
startIntervalCheck(): void;
/**
* Stop the interval check. This is for internal use
* @returns {void}
*/
stopIntervalCheck(): void;
/**
* Wrap the function for caching
* @param {Function} function_ - The function to wrap
* @param {Object} [options] - The options to wrap
* @returns {Function} - The wrapped function
*/
wrap<T, Arguments extends any[]>(function_: (...arguments_: Arguments) => T, options?: WrapFunctionOptions): (...arguments_: Arguments) => T;
/**
* Gets the value of the key, or computes and stores it on a cache miss. This is the synchronous
* cache-aside helper: if the key is present its value is returned, otherwise `function_` is
* invoked, its result is stored, and that result is returned.
*
* The value is stored using `options.ttl`, falling back to the instance default `ttl`. Because
* the cache is synchronous there is no request coalescing — concurrent callers cannot stampede
* the setter the way they can with an async cache.
* @param {GetOrSetSyncKey} key - The key to get or set. Can also be a function that returns the key.
* @param {() => T} function_ - The function that computes the value on a cache miss.
* @param {GetOrSetFunctionOptions} [options] - Options such as `ttl`, `cacheErrors`, and `throwErrors`.
* @returns {T | undefined} - The cached or freshly computed value
*/
getOrSet<T>(key: GetOrSetSyncKey$1, function_: () => T, options?: GetOrSetFunctionOptions$1): T | undefined;
/**
* Records a single read against the statistics counters. Each read increments `gets` and either
* `hits` or `misses`. No-op when statistics are disabled. This is for internal use.
* @param {boolean} hit - Whether the read found a (non-expired) value
* @returns {void}
*/
private recordRead;
/**
* Decrements the size statistics (`count`, `ksize`, and `vsize`) for an entry that is being removed
* because it expired. Expirations are not counted as `deletes` since they are not user-initiated.
* No-op when statistics are disabled. This is for internal use.
* @param {CacheableStoreItem} item - The expired item being removed from the store
* @returns {void}
*/
private recordExpiration;
private isPrimitive;
private setTtl;
private setMaxTtl;
private hasExpired;
}
//#endregion
export { type CacheableItem, CacheableMemory, CacheableMemoryAfterGetItem, CacheableMemoryAfterGetManyItem, CacheableMemoryHookItem, CacheableMemoryHooks, CacheableMemoryOptions, type CacheableStoreItem, type GetOrSetFunctionOptions, type GetOrSetSyncKey, type GetOrSetSyncOptions, HashAlgorithm, KeyvCacheableMemory, type KeyvCacheableMemoryOptions, SetOptions, Stats, type StatsOptions, type StatsSnapshot, StoreHashAlgorithmFunction, createKeyv, defaultStoreHashSize, getOrSetSync, hash, hashToNumber, maximumMapSize };
import { HashAlgorithm, HashAlgorithm as HashAlgorithm$1, Stats, Stats as Stats$1, getOrSetSync, getOrSetSync as getOrSetSync$1, hash, hashToNumber, hashToNumberSync, shorthandToTime, wrapSync } from "@cacheable/utils";
import { Hookified } from "hookified";
import { Keyv } from "keyv";
//#region src/memory-lru.ts
var ListNode = class {
value;
prev = void 0;
next = void 0;
constructor(value) {
this.value = value;
}
};
var DoublyLinkedList = class {
head = void 0;
tail = void 0;
nodesMap = /* @__PURE__ */ new Map();
addToFront(value) {
const newNode = new ListNode(value);
if (this.head) {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
} else this.head = this.tail = newNode;
this.nodesMap.set(value, newNode);
}
moveToFront(value) {
const node = this.nodesMap.get(value);
if (!node || this.head === node) return;
/* v8 ignore next -- @preserve */
if (node.prev) node.prev.next = node.next;
/* v8 ignore next -- @preserve */
if (node.next) node.next.prev = node.prev;
/* v8 ignore next -- @preserve */
if (node === this.tail) this.tail = node.prev;
node.prev = void 0;
node.next = this.head;
/* v8 ignore next -- @preserve */
if (this.head) this.head.prev = node;
this.head = node;
this.tail ??= node;
}
getOldest() {
/* v8 ignore next -- @preserve */
return this.tail ? this.tail.value : void 0;
}
removeOldest() {
/* v8 ignore next -- @preserve */
if (!this.tail) return;
const oldValue = this.tail.value;
/* v8 ignore next -- @preserve */
if (this.tail.prev) {
this.tail = this.tail.prev;
this.tail.next = void 0;
} else
/* v8 ignore next -- @preserve */
this.head = this.tail = void 0;
this.nodesMap.delete(oldValue);
return oldValue;
}
remove(value) {
const node = this.nodesMap.get(value);
if (!node) return false;
if (node.prev) node.prev.next = node.next;
else {
this.head = node.next;
if (this.head) this.head.prev = void 0;
}
if (node.next) node.next.prev = node.prev;
else {
this.tail = node.prev;
if (this.tail) this.tail.next = void 0;
}
this.nodesMap.delete(value);
return true;
}
get size() {
return this.nodesMap.size;
}
};
//#endregion
//#region src/keyv-memory.ts
var KeyvCacheableMemory = class {
opts = {
ttl: 0,
useClone: true,
lruSize: 0,
checkInterval: 0
};
_defaultCache = new CacheableMemory();
_nCache = /* @__PURE__ */ new Map();
_namespace;
constructor(options) {
if (options) {
this.opts = options;
this._defaultCache = new CacheableMemory(options);
if (options.namespace) {
this._namespace = options.namespace;
this._nCache.set(this._namespace, new CacheableMemory(options));
}
}
}
get namespace() {
return this._namespace;
}
set namespace(value) {
this._namespace = value;
}
get store() {
return this.getStore(this._namespace);
}
async get(key) {
const result = this.getStore(this._namespace).get(key);
if (result) return result;
}
async getMany(keys) {
return this.getStore(this._namespace).getMany(keys);
}
async set(key, value, ttl) {
this.getStore(this._namespace).set(key, value, ttl);
}
async setMany(values) {
this.getStore(this._namespace).setMany(values);
}
async delete(key) {
this.getStore(this._namespace).delete(key);
return true;
}
async deleteMany(key) {
this.getStore(this._namespace).deleteMany(key);
return true;
}
async clear() {
this.getStore(this._namespace).clear();
}
async has(key) {
return this.getStore(this._namespace).has(key);
}
on(event, listener) {
this.getStore(this._namespace).on(event, listener);
return this;
}
getStore(namespace) {
if (!namespace) return this._defaultCache;
if (!this._nCache.has(namespace)) this._nCache.set(namespace, new CacheableMemory(this.opts));
return this._nCache.get(namespace);
}
};
/**
* Creates a new Keyv instance with a new KeyvCacheableMemory store. This also removes the serialize/deserialize methods from the Keyv instance for optimization.
* @param options
* @returns
*/
function createKeyv(options) {
const store = new KeyvCacheableMemory(options);
const namespace = options?.namespace;
let ttl;
/* v8 ignore next -- @preserve */
if (options?.ttl && Number.isInteger(options.ttl)) ttl = options?.ttl;
const keyv = new Keyv({
store,
namespace,
ttl
});
keyv.serialize = void 0;
keyv.deserialize = void 0;
return keyv;
}
//#endregion
//#region src/index.ts
/**
* Lifecycle hooks fired by {@link CacheableMemory}. Register handlers with the inherited
* `onHook(hook, handler)` method. Hooks are dispatched synchronously via `hookSync`, which skips
* `async` handler functions entirely — register only synchronous handlers.
*/
let CacheableMemoryHooks = /* @__PURE__ */ function(CacheableMemoryHooks) {
CacheableMemoryHooks["BEFORE_SET"] = "BEFORE_SET";
CacheableMemoryHooks["AFTER_SET"] = "AFTER_SET";
CacheableMemoryHooks["BEFORE_SET_MANY"] = "BEFORE_SET_MANY";
CacheableMemoryHooks["AFTER_SET_MANY"] = "AFTER_SET_MANY";
CacheableMemoryHooks["BEFORE_GET"] = "BEFORE_GET";
CacheableMemoryHooks["AFTER_GET"] = "AFTER_GET";
CacheableMemoryHooks["BEFORE_GET_MANY"] = "BEFORE_GET_MANY";
CacheableMemoryHooks["AFTER_GET_MANY"] = "AFTER_GET_MANY";
CacheableMemoryHooks["BEFORE_DELETE"] = "BEFORE_DELETE";
CacheableMemoryHooks["AFTER_DELETE"] = "AFTER_DELETE";
CacheableMemoryHooks["BEFORE_DELETE_MANY"] = "BEFORE_DELETE_MANY";
CacheableMemoryHooks["AFTER_DELETE_MANY"] = "AFTER_DELETE_MANY";
CacheableMemoryHooks["BEFORE_CLEAR"] = "BEFORE_CLEAR";
CacheableMemoryHooks["AFTER_CLEAR"] = "AFTER_CLEAR";
return CacheableMemoryHooks;
}({});
const defaultStoreHashSize = 16;
const maximumMapSize = 16777216;
var CacheableMemory = class extends Hookified {
_lru = new DoublyLinkedList();
_storeHashSize = 16;
_storeHashAlgorithm = HashAlgorithm$1.DJB2;
_store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
_ttl;
_maxTtl;
_useClone = true;
_lruSize = 0;
_checkInterval = 0;
_interval = 0;
_stats = new Stats$1({ enabled: false });
/**
* @constructor
* @param {CacheableMemoryOptions} [options] - The options for the CacheableMemory
*/
constructor(options) {
super();
if (options?.ttl) this.setTtl(options.ttl);
if (options?.maxTtl !== void 0) this.setMaxTtl(options.maxTtl);
if (options?.useClone !== void 0) this._useClone = options.useClone;
if (options?.stats) this._stats.enabled = options.stats;
if (options?.storeHashSize && options.storeHashSize > 0) this._storeHashSize = options.storeHashSize;
if (options?.lruSize) if (options.lruSize > 16777216) this.emit("error", /* @__PURE__ */ new Error(`LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`));
else this._lruSize = options.lruSize;
if (options?.checkInterval) this._checkInterval = options.checkInterval;
if (options?.storeHashAlgorithm) this._storeHashAlgorithm = options.storeHashAlgorithm;
this._store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
this.startIntervalCheck();
}
/**
* Gets the time-to-live
* @returns {number|string|undefined} - The time-to-live in miliseconds or a human-readable format. If undefined, it will not have a time-to-live.
*/
get ttl() {
return this._ttl;
}
/**
* Sets the time-to-live
* @param {number|string|undefined} value - The time-to-live in miliseconds or a human-readable format (example '1s' = 1 second, '1h' = 1 hour). If undefined, it will not have a time-to-live.
*/
set ttl(value) {
this.setTtl(value);
}
/**
* Gets the maximum time-to-live. When set, any TTL that exceeds this value is capped to maxTtl.
* Entries with no TTL will also be capped to maxTtl. Default is `undefined` (no maximum).
* @returns {number|string|undefined} - The maximum TTL in milliseconds, human-readable format, or undefined.
*/
get maxTtl() {
return this._maxTtl;
}
/**
* Sets the maximum time-to-live. When set, any TTL that exceeds this value is capped to maxTtl.
* Entries with no TTL will also be capped to maxTtl.
* @param {number|string|undefined} value - The maximum TTL in milliseconds or human-readable format (e.g. '1s', '1h'). If undefined, no maximum is enforced.
*/
set maxTtl(value) {
this.setMaxTtl(value);
}
/**
* Gets whether to use clone
* @returns {boolean} - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
get useClone() {
return this._useClone;
}
/**
* Sets whether to use clone
* @param {boolean} value - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
set useClone(value) {
this._useClone = value;
}
/**
* Gets the size of the LRU cache
* @returns {number} - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
get lruSize() {
return this._lruSize;
}
/**
* Sets the size of the LRU cache
* @param {number} value - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
set lruSize(value) {
if (value > 16777216) {
this.emit("error", /* @__PURE__ */ new Error(`LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`));
return;
}
this._lruSize = value;
if (this._lruSize === 0) {
this._lru = new DoublyLinkedList();
return;
}
this.lruResize();
}
/**
* Gets the check interval
* @returns {number} - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
get checkInterval() {
return this._checkInterval;
}
/**
* Sets the check interval
* @param {number} value - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
set checkInterval(value) {
this._checkInterval = value;
}
/**
* Gets the size of the cache
* @returns {number} - The size of the cache
*/
get size() {
let size = 0;
for (const store of this._store) size += store.size;
return size;
}
/**
* Gets the statistics of the cache. Statistics track aggregate counters such as `hits`, `misses`,
* `gets`, `sets`, `deletes`, `clears`, `count`, `ksize`, and `vsize`. They are disabled by default;
* enable them via the `stats` option or by setting `cache.stats.enabled = true`.
* @returns {Stats} - The statistics for this CacheableMemory instance
*/
get stats() {
return this._stats;
}
/**
* Gets the number of hash stores
* @returns {number} - The number of hash stores
*/
get storeHashSize() {
return this._storeHashSize;
}
/**
* Sets the number of hash stores. This will recreate the store and all data will be cleared
* @param {number} value - The number of hash stores
*/
set storeHashSize(value) {
if (value === this._storeHashSize) return;
this._storeHashSize = value;
this._store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
if (this._stats.enabled) this._stats.resetStoreValues();
}
/**
* Gets the store hash algorithm
* @returns {HashAlgorithm | StoreHashAlgorithmFunction} - The store hash algorithm
*/
get storeHashAlgorithm() {
return this._storeHashAlgorithm;
}
/**
* Sets the store hash algorithm. This will recreate the store and all data will be cleared
* @param {HashAlgorithm | HashAlgorithmFunction} value - The store hash algorithm
*/
set storeHashAlgorithm(value) {
this._storeHashAlgorithm = value;
}
/**
* Gets the keys
* @returns {IterableIterator<string>} - The keys
*/
get keys() {
const keys = [];
for (const store of this._store) for (const key of store.keys()) {
const item = store.get(key);
if (item && this.hasExpired(item)) {
this.recordExpiration(item);
store.delete(key);
this.lruRemove(key);
continue;
}
keys.push(key);
}
return keys.values();
}
/**
* Gets the items
* @returns {IterableIterator<CacheableStoreItem>} - The items
*/
get items() {
const items = [];
for (const store of this._store) for (const item of store.values()) {
if (this.hasExpired(item)) {
this.recordExpiration(item);
store.delete(item.key);
this.lruRemove(item.key);
continue;
}
items.push(item);
}
return items.values();
}
/**
* Gets the store
* @returns {Array<Map<string, CacheableStoreItem>>} - The store
*/
get store() {
return this._store;
}
/**
* Gets the value of the key
* @param {string} key - The key to get the value
* @returns {T | undefined} - The value of the key
*/
get(key) {
this.hookSync("BEFORE_GET", key);
const store = this.getStore(key);
const item = store.get(key);
if (!item) {
this.recordRead(false);
this.hookSync("AFTER_GET", {
key,
result: void 0
});
return;
}
if (item.expires && Date.now() > item.expires) {
this.recordExpiration(item);
store.delete(key);
this.lruRemove(key);
this.recordRead(false);
this.hookSync("AFTER_GET", {
key,
result: void 0
});
return;
}
this.lruMoveToFront(key);
let result;
if (!this._useClone) result = item.value;
else result = this.clone(item.value);
this.recordRead(true);
this.hookSync("AFTER_GET", {
key,
result
});
return result;
}
/**
* Gets the values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {T[]} - The values of the keys
*/
getMany(keys) {
this.hookSync("BEFORE_GET_MANY", keys);
const result = [];
for (const key of keys) result.push(this.get(key));
this.hookSync("AFTER_GET_MANY", {
keys,
result
});
return result;
}
/**
* Gets the raw value of the key
* @param {string} key - The key to get the value
* @returns {CacheableStoreItem | undefined} - The raw value of the key
*/
getRaw(key) {
const store = this.getStore(key);
const item = store.get(key);
if (!item) {
this.recordRead(false);
return;
}
if (item.expires && Date.now() > item.expires) {
this.recordExpiration(item);
store.delete(key);
this.lruRemove(key);
this.recordRead(false);
return;
}
this.lruMoveToFront(key);
this.recordRead(true);
return item;
}
/**
* Gets the raw values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {CacheableStoreItem[]} - The raw values of the keys
*/
getManyRaw(keys) {
const result = [];
for (const key of keys) result.push(this.getRaw(key));
return result;
}
/**
* Sets the value of the key
* @param {string} key - The key to set the value
* @param {any} value - The value to set
* @param {number|string|SetOptions} [ttl] - Time to Live - If you set a number it is miliseconds, if you set a string it is a human-readable.
* If you want to set expire directly you can do that by setting the expire property in the SetOptions.
* If you set undefined, it will use the default time-to-live. If both are undefined then it will not have a time-to-live.
* @returns {void}
*/
set(key, value, ttl) {
const hookItem = {
key,
value,
ttl
};
this.hookSync("BEFORE_SET", hookItem);
const store = this.getStore(hookItem.key);
let expires;
const effectiveTtl = hookItem.ttl;
if (effectiveTtl !== void 0 || this._ttl !== void 0) if (typeof effectiveTtl === "object") {
if (effectiveTtl.expire) expires = typeof effectiveTtl.expire === "number" ? effectiveTtl.expire : effectiveTtl.expire.getTime();
if (effectiveTtl.ttl) {
const finalTtl = shorthandToTime(effectiveTtl.ttl);
/* v8 ignore next -- @preserve */
if (finalTtl !== void 0) expires = finalTtl;
}
} else {
const finalTtl = shorthandToTime(effectiveTtl ?? this._ttl);
/* v8 ignore next -- @preserve */
if (finalTtl !== void 0) expires = finalTtl;
}
if (this._maxTtl !== void 0) {
const maxExpires = shorthandToTime(this._maxTtl);
if (expires === void 0) expires = maxExpires;
else if (expires > maxExpires) expires = maxExpires;
}
if (this._lruSize > 0) if (store.has(hookItem.key)) this.lruMoveToFront(hookItem.key);
else {
this.lruAddToFront(hookItem.key);
if (this._lru.size > this._lruSize) {
const oldestKey = this._lru.getOldest();
/* v8 ignore next -- @preserve */
if (oldestKey) {
this._lru.removeOldest();
this.delete(oldestKey);
}
}
}
if (this._stats.enabled) {
const existing = store.get(hookItem.key);
if (existing) this._stats.decreaseVSize(existing.value);
else {
this._stats.incrementKSize(hookItem.key);
this._stats.incrementCount();
}
this._stats.incrementVSize(hookItem.value);
this._stats.incrementSets();
}
const item = {
key: hookItem.key,
value: hookItem.value,
expires
};
store.set(hookItem.key, item);
this.hookSync("AFTER_SET", hookItem);
}
/**
* Sets the values of the keys
* @param {CacheableItem[]} items - The items to set
* @returns {void}
*/
setMany(items) {
this.hookSync("BEFORE_SET_MANY", items);
for (const item of items) this.set(item.key, item.value, item.ttl);
this.hookSync("AFTER_SET_MANY", items);
}
/**
* Checks if the key exists
* @param {string} key - The key to check
* @returns {boolean} - If true, the key exists. If false, the key does not exist.
*/
has(key) {
const item = this.get(key);
return Boolean(item);
}
/**
* @function hasMany
* @param {string[]} keys - The keys to check
* @returns {boolean[]} - If true, the key exists. If false, the key does not exist.
*/
hasMany(keys) {
const result = [];
for (const key of keys) {
const item = this.get(key);
result.push(Boolean(item));
}
return result;
}
/**
* Take will get the key and delete the entry from cache
* @param {string} key - The key to take
* @returns {T | undefined} - The value of the key
*/
take(key) {
const item = this.get(key);
if (!item) return;
this.delete(key);
return item;
}
/**
* TakeMany will get the keys and delete the entries from cache
* @param {string[]} keys - The keys to take
* @returns {T[]} - The values of the keys
*/
takeMany(keys) {
const result = [];
for (const key of keys) result.push(this.take(key));
return result;
}
/**
* Delete the key
* @param {string} key - The key to delete
* @returns {void}
*/
delete(key) {
this.hookSync("BEFORE_DELETE", key);
const store = this.getStore(key);
if (this._stats.enabled) {
const item = store.get(key);
if (item) {
this._stats.decreaseKSize(key);
this._stats.decreaseVSize(item.value);
this._stats.decreaseCount();
this._stats.incrementDeletes();
}
}
store.delete(key);
this.lruRemove(key);
this.hookSync("AFTER_DELETE", key);
}
/**
* Delete the keys
* @param {string[]} keys - The keys to delete
* @returns {void}
*/
deleteMany(keys) {
this.hookSync("BEFORE_DELETE_MANY", keys);
for (const key of keys) this.delete(key);
this.hookSync("AFTER_DELETE_MANY", keys);
}
/**
* Clear the cache
* @returns {void}
*/
clear() {
this.hookSync("BEFORE_CLEAR");
this._store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
this._lru = new DoublyLinkedList();
if (this._stats.enabled) {
this._stats.resetStoreValues();
this._stats.incrementClears();
}
this.hookSync("AFTER_CLEAR");
}
/**
* Get the store based on the key (internal use)
* @param {string} key - The key to get the store
* @returns {CacheableHashStore} - The store
*/
getStore(key) {
const hash = this.getKeyStoreHash(key);
this._store[hash] ||= /* @__PURE__ */ new Map();
return this._store[hash];
}
/**
* Hash the key for which store to go to (internal use)
* @param {string} key - The key to hash
* Available algorithms are: SHA256, SHA1, MD5, and djb2Hash.
* @returns {number} - The hashed key as a number
*/
getKeyStoreHash(key) {
if (this._store.length === 1) return 0;
if (typeof this._storeHashAlgorithm === "function") return this._storeHashAlgorithm(key, this._storeHashSize);
return hashToNumberSync(key, {
min: 0,
max: this._storeHashSize - 1,
algorithm: this._storeHashAlgorithm
});
}
/**
* Clone the value. This is for internal use
* @param {any} value - The value to clone
* @returns {any} - The cloned value
*/
clone(value) {
if (this.isPrimitive(value)) return value;
return structuredClone(value);
}
/**
* Add to the front of the LRU cache. This is for internal use
* @param {string} key - The key to add to the front
* @returns {void}
*/
lruAddToFront(key) {
if (this._lruSize === 0) return;
this._lru.addToFront(key);
}
/**
* Move to the front of the LRU cache. This is for internal use
* @param {string} key - The key to move to the front
* @returns {void}
*/
lruMoveToFront(key) {
if (this._lruSize === 0) return;
this._lru.moveToFront(key);
}
/**
* Remove a key from the LRU cache. This is for internal use
* @param {string} key - The key to remove
* @returns {void}
*/
lruRemove(key) {
if (this._lruSize === 0) return;
this._lru.remove(key);
}
/**
* Resize the LRU cache. This is for internal use.
* @returns {void}
*/
lruResize() {
while (this._lru.size > this._lruSize) {
const oldestKey = this._lru.getOldest();
/* v8 ignore next -- @preserve */
if (oldestKey) {
this._lru.removeOldest();
this.delete(oldestKey);
}
}
}
/**
* Check for expiration. This is for internal use
* @returns {void}
*/
checkExpiration() {
for (const store of this._store) for (const item of store.values()) if (item.expires && Date.now() > item.expires) {
this.recordExpiration(item);
store.delete(item.key);
this.lruRemove(item.key);
}
}
/**
* Start the interval check. This is for internal use
* @returns {void}
*/
startIntervalCheck() {
if (this._checkInterval > 0) {
/* v8 ignore next -- @preserve */
if (this._interval)
/* v8 ignore next -- @preserve */
clearInterval(this._interval);
this._interval = setInterval(() => {
this.checkExpiration();
}, this._checkInterval).unref();
}
}
/**
* Stop the interval check. This is for internal use
* @returns {void}
*/
stopIntervalCheck() {
/* v8 ignore next -- @preserve */
if (this._interval) clearInterval(this._interval);
this._interval = 0;
this._checkInterval = 0;
}
/**
* Wrap the function for caching
* @param {Function} function_ - The function to wrap
* @param {Object} [options] - The options to wrap
* @returns {Function} - The wrapped function
*/
wrap(function_, options) {
return wrapSync(function_, {
ttl: options?.ttl ?? this._ttl,
keyPrefix: options?.keyPrefix,
createKey: options?.createKey,
cache: this
});
}
/**
* Gets the value of the key, or computes and stores it on a cache miss. This is the synchronous
* cache-aside helper: if the key is present its value is returned, otherwise `function_` is
* invoked, its result is stored, and that result is returned.
*
* The value is stored using `options.ttl`, falling back to the instance default `ttl`. Because
* the cache is synchronous there is no request coalescing — concurrent callers cannot stampede
* the setter the way they can with an async cache.
* @param {GetOrSetSyncKey} key - The key to get or set. Can also be a function that returns the key.
* @param {() => T} function_ - The function that computes the value on a cache miss.
* @param {GetOrSetFunctionOptions} [options] - Options such as `ttl`, `cacheErrors`, and `throwErrors`.
* @returns {T | undefined} - The cached or freshly computed value
*/
getOrSet(key, function_, options) {
return getOrSetSync$1(key, function_, {
cache: this,
ttl: options?.ttl ?? this._ttl,
cacheErrors: options?.cacheErrors,
throwErrors: options?.throwErrors
});
}
/**
* Records a single read against the statistics counters. Each read increments `gets` and either
* `hits` or `misses`. No-op when statistics are disabled. This is for internal use.
* @param {boolean} hit - Whether the read found a (non-expired) value
* @returns {void}
*/
recordRead(hit) {
if (!this._stats.enabled) return;
if (hit) this._stats.incrementHits();
else this._stats.incrementMisses();
this._stats.incrementGets();
}
/**
* Decrements the size statistics (`count`, `ksize`, and `vsize`) for an entry that is being removed
* because it expired. Expirations are not counted as `deletes` since they are not user-initiated.
* No-op when statistics are disabled. This is for internal use.
* @param {CacheableStoreItem} item - The expired item being removed from the store
* @returns {void}
*/
recordExpiration(item) {
if (!this._stats.enabled) return;
this._stats.decreaseKSize(item.key);
this._stats.decreaseVSize(item.value);
this._stats.decreaseCount();
}
isPrimitive(value) {
const result = false;
/* v8 ignore next -- @preserve */
if (value === null || value === void 0) return true;
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return true;
return result;
}
setTtl(ttl) {
if (typeof ttl === "string" || ttl === void 0) this._ttl = ttl;
else if (ttl > 0) this._ttl = ttl;
else this._ttl = void 0;
}
setMaxTtl(maxTtl) {
if (typeof maxTtl === "string" || maxTtl === void 0) this._maxTtl = maxTtl;
else if (maxTtl > 0) this._maxTtl = maxTtl;
else this._maxTtl = void 0;
}
hasExpired(item) {
if (item.expires && Date.now() > item.expires) return true;
return false;
}
};
//#endregion
export { CacheableMemory, CacheableMemoryHooks, HashAlgorithm, KeyvCacheableMemory, Stats, createKeyv, defaultStoreHashSize, getOrSetSync, hash, hashToNumber, maximumMapSize };
+867
-820

@@ -1,830 +0,877 @@

"use strict";
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
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 __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
CacheableMemory: () => CacheableMemory,
HashAlgorithm: () => import_utils2.HashAlgorithm,
KeyvCacheableMemory: () => KeyvCacheableMemory,
createKeyv: () => createKeyv,
defaultStoreHashSize: () => defaultStoreHashSize,
hash: () => import_utils2.hash,
hashToNumber: () => import_utils2.hashToNumber,
maximumMapSize: () => maximumMapSize
});
module.exports = __toCommonJS(index_exports);
var import_utils = require("@cacheable/utils");
var import_hookified = require("hookified");
// src/memory-lru.ts
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
let _cacheable_utils = require("@cacheable/utils");
let hookified = require("hookified");
let keyv = require("keyv");
//#region src/memory-lru.ts
var ListNode = class {
value;
prev = void 0;
next = void 0;
constructor(value) {
this.value = value;
}
value;
prev = void 0;
next = void 0;
constructor(value) {
this.value = value;
}
};
var DoublyLinkedList = class {
head = void 0;
tail = void 0;
nodesMap = /* @__PURE__ */ new Map();
// Add a new node to the front (most recently used)
addToFront(value) {
const newNode = new ListNode(value);
if (this.head) {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
} else {
this.head = this.tail = newNode;
}
this.nodesMap.set(value, newNode);
}
// Move an existing node to the front (most recently used)
moveToFront(value) {
const node = this.nodesMap.get(value);
if (!node || this.head === node) {
return;
}
if (node.prev) {
node.prev.next = node.next;
}
if (node.next) {
node.next.prev = node.prev;
}
if (node === this.tail) {
this.tail = node.prev;
}
node.prev = void 0;
node.next = this.head;
if (this.head) {
this.head.prev = node;
}
this.head = node;
this.tail ??= node;
}
// Get the oldest node (tail)
getOldest() {
return this.tail ? this.tail.value : void 0;
}
// Remove the oldest node (tail)
removeOldest() {
if (!this.tail) {
return void 0;
}
const oldValue = this.tail.value;
if (this.tail.prev) {
this.tail = this.tail.prev;
this.tail.next = void 0;
} else {
this.head = this.tail = void 0;
}
this.nodesMap.delete(oldValue);
return oldValue;
}
// Remove a specific node by value
remove(value) {
const node = this.nodesMap.get(value);
if (!node) {
return false;
}
if (node.prev) {
node.prev.next = node.next;
} else {
this.head = node.next;
if (this.head) {
this.head.prev = void 0;
}
}
if (node.next) {
node.next.prev = node.prev;
} else {
this.tail = node.prev;
if (this.tail) {
this.tail.next = void 0;
}
}
this.nodesMap.delete(value);
return true;
}
get size() {
return this.nodesMap.size;
}
head = void 0;
tail = void 0;
nodesMap = /* @__PURE__ */ new Map();
addToFront(value) {
const newNode = new ListNode(value);
if (this.head) {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
} else this.head = this.tail = newNode;
this.nodesMap.set(value, newNode);
}
moveToFront(value) {
const node = this.nodesMap.get(value);
if (!node || this.head === node) return;
/* v8 ignore next -- @preserve */
if (node.prev) node.prev.next = node.next;
/* v8 ignore next -- @preserve */
if (node.next) node.next.prev = node.prev;
/* v8 ignore next -- @preserve */
if (node === this.tail) this.tail = node.prev;
node.prev = void 0;
node.next = this.head;
/* v8 ignore next -- @preserve */
if (this.head) this.head.prev = node;
this.head = node;
this.tail ??= node;
}
getOldest() {
/* v8 ignore next -- @preserve */
return this.tail ? this.tail.value : void 0;
}
removeOldest() {
/* v8 ignore next -- @preserve */
if (!this.tail) return;
const oldValue = this.tail.value;
/* v8 ignore next -- @preserve */
if (this.tail.prev) {
this.tail = this.tail.prev;
this.tail.next = void 0;
} else
/* v8 ignore next -- @preserve */
this.head = this.tail = void 0;
this.nodesMap.delete(oldValue);
return oldValue;
}
remove(value) {
const node = this.nodesMap.get(value);
if (!node) return false;
if (node.prev) node.prev.next = node.next;
else {
this.head = node.next;
if (this.head) this.head.prev = void 0;
}
if (node.next) node.next.prev = node.prev;
else {
this.tail = node.prev;
if (this.tail) this.tail.next = void 0;
}
this.nodesMap.delete(value);
return true;
}
get size() {
return this.nodesMap.size;
}
};
// src/index.ts
var import_utils2 = require("@cacheable/utils");
// src/keyv-memory.ts
var import_keyv = require("keyv");
//#endregion
//#region src/keyv-memory.ts
var KeyvCacheableMemory = class {
opts = {
ttl: 0,
useClone: true,
lruSize: 0,
checkInterval: 0
};
_defaultCache = new CacheableMemory();
_nCache = /* @__PURE__ */ new Map();
_namespace;
constructor(options) {
if (options) {
this.opts = options;
this._defaultCache = new CacheableMemory(options);
if (options.namespace) {
this._namespace = options.namespace;
this._nCache.set(this._namespace, new CacheableMemory(options));
}
}
}
get namespace() {
return this._namespace;
}
set namespace(value) {
this._namespace = value;
}
get store() {
return this.getStore(this._namespace);
}
async get(key) {
const result = this.getStore(this._namespace).get(key);
if (result) {
return result;
}
return void 0;
}
async getMany(keys) {
const result = this.getStore(this._namespace).getMany(keys);
return result;
}
// biome-ignore lint/suspicious/noExplicitAny: type format
async set(key, value, ttl) {
this.getStore(this._namespace).set(key, value, ttl);
}
async setMany(values) {
this.getStore(this._namespace).setMany(values);
}
async delete(key) {
this.getStore(this._namespace).delete(key);
return true;
}
async deleteMany(key) {
this.getStore(this._namespace).deleteMany(key);
return true;
}
async clear() {
this.getStore(this._namespace).clear();
}
async has(key) {
return this.getStore(this._namespace).has(key);
}
// biome-ignore lint/suspicious/noExplicitAny: type format
on(event, listener) {
this.getStore(this._namespace).on(event, listener);
return this;
}
getStore(namespace) {
if (!namespace) {
return this._defaultCache;
}
if (!this._nCache.has(namespace)) {
this._nCache.set(namespace, new CacheableMemory(this.opts));
}
return this._nCache.get(namespace);
}
opts = {
ttl: 0,
useClone: true,
lruSize: 0,
checkInterval: 0
};
_defaultCache = new CacheableMemory();
_nCache = /* @__PURE__ */ new Map();
_namespace;
constructor(options) {
if (options) {
this.opts = options;
this._defaultCache = new CacheableMemory(options);
if (options.namespace) {
this._namespace = options.namespace;
this._nCache.set(this._namespace, new CacheableMemory(options));
}
}
}
get namespace() {
return this._namespace;
}
set namespace(value) {
this._namespace = value;
}
get store() {
return this.getStore(this._namespace);
}
async get(key) {
const result = this.getStore(this._namespace).get(key);
if (result) return result;
}
async getMany(keys) {
return this.getStore(this._namespace).getMany(keys);
}
async set(key, value, ttl) {
this.getStore(this._namespace).set(key, value, ttl);
}
async setMany(values) {
this.getStore(this._namespace).setMany(values);
}
async delete(key) {
this.getStore(this._namespace).delete(key);
return true;
}
async deleteMany(key) {
this.getStore(this._namespace).deleteMany(key);
return true;
}
async clear() {
this.getStore(this._namespace).clear();
}
async has(key) {
return this.getStore(this._namespace).has(key);
}
on(event, listener) {
this.getStore(this._namespace).on(event, listener);
return this;
}
getStore(namespace) {
if (!namespace) return this._defaultCache;
if (!this._nCache.has(namespace)) this._nCache.set(namespace, new CacheableMemory(this.opts));
return this._nCache.get(namespace);
}
};
/**
* Creates a new Keyv instance with a new KeyvCacheableMemory store. This also removes the serialize/deserialize methods from the Keyv instance for optimization.
* @param options
* @returns
*/
function createKeyv(options) {
const store = new KeyvCacheableMemory(options);
const namespace = options?.namespace;
let ttl;
if (options?.ttl && Number.isInteger(options.ttl)) {
ttl = options?.ttl;
}
const keyv = new import_keyv.Keyv({ store, namespace, ttl });
keyv.serialize = void 0;
keyv.deserialize = void 0;
return keyv;
const store = new KeyvCacheableMemory(options);
const namespace = options?.namespace;
let ttl;
/* v8 ignore next -- @preserve */
if (options?.ttl && Number.isInteger(options.ttl)) ttl = options?.ttl;
const keyv$1 = new keyv.Keyv({
store,
namespace,
ttl
});
keyv$1.serialize = void 0;
keyv$1.deserialize = void 0;
return keyv$1;
}
// src/index.ts
var defaultStoreHashSize = 16;
var maximumMapSize = 16777216;
var CacheableMemory = class extends import_hookified.Hookified {
_lru = new DoublyLinkedList();
_storeHashSize = defaultStoreHashSize;
_storeHashAlgorithm = import_utils.HashAlgorithm.DJB2;
// Default is djb2Hash
_store = Array.from(
{ length: this._storeHashSize },
() => /* @__PURE__ */ new Map()
);
_ttl;
// Turned off by default
_useClone = true;
// Turned on by default
_lruSize = 0;
// Turned off by default
_checkInterval = 0;
// Turned off by default
_interval = 0;
// Turned off by default
/**
* @constructor
* @param {CacheableMemoryOptions} [options] - The options for the CacheableMemory
*/
constructor(options) {
super();
if (options?.ttl) {
this.setTtl(options.ttl);
}
if (options?.useClone !== void 0) {
this._useClone = options.useClone;
}
if (options?.storeHashSize && options.storeHashSize > 0) {
this._storeHashSize = options.storeHashSize;
}
if (options?.lruSize) {
if (options.lruSize > maximumMapSize) {
this.emit(
"error",
new Error(
`LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`
)
);
} else {
this._lruSize = options.lruSize;
}
}
if (options?.checkInterval) {
this._checkInterval = options.checkInterval;
}
if (options?.storeHashAlgorithm) {
this._storeHashAlgorithm = options.storeHashAlgorithm;
}
this._store = Array.from(
{ length: this._storeHashSize },
() => /* @__PURE__ */ new Map()
);
this.startIntervalCheck();
}
/**
* Gets the time-to-live
* @returns {number|string|undefined} - The time-to-live in miliseconds or a human-readable format. If undefined, it will not have a time-to-live.
*/
get ttl() {
return this._ttl;
}
/**
* Sets the time-to-live
* @param {number|string|undefined} value - The time-to-live in miliseconds or a human-readable format (example '1s' = 1 second, '1h' = 1 hour). If undefined, it will not have a time-to-live.
*/
set ttl(value) {
this.setTtl(value);
}
/**
* Gets whether to use clone
* @returns {boolean} - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
get useClone() {
return this._useClone;
}
/**
* Sets whether to use clone
* @param {boolean} value - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
set useClone(value) {
this._useClone = value;
}
/**
* Gets the size of the LRU cache
* @returns {number} - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
get lruSize() {
return this._lruSize;
}
/**
* Sets the size of the LRU cache
* @param {number} value - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
set lruSize(value) {
if (value > maximumMapSize) {
this.emit(
"error",
new Error(
`LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`
)
);
return;
}
this._lruSize = value;
if (this._lruSize === 0) {
this._lru = new DoublyLinkedList();
return;
}
this.lruResize();
}
/**
* Gets the check interval
* @returns {number} - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
get checkInterval() {
return this._checkInterval;
}
/**
* Sets the check interval
* @param {number} value - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
set checkInterval(value) {
this._checkInterval = value;
}
/**
* Gets the size of the cache
* @returns {number} - The size of the cache
*/
get size() {
let size = 0;
for (const store of this._store) {
size += store.size;
}
return size;
}
/**
* Gets the number of hash stores
* @returns {number} - The number of hash stores
*/
get storeHashSize() {
return this._storeHashSize;
}
/**
* Sets the number of hash stores. This will recreate the store and all data will be cleared
* @param {number} value - The number of hash stores
*/
set storeHashSize(value) {
if (value === this._storeHashSize) {
return;
}
this._storeHashSize = value;
this._store = Array.from(
{ length: this._storeHashSize },
() => /* @__PURE__ */ new Map()
);
}
/**
* Gets the store hash algorithm
* @returns {HashAlgorithm | StoreHashAlgorithmFunction} - The store hash algorithm
*/
get storeHashAlgorithm() {
return this._storeHashAlgorithm;
}
/**
* Sets the store hash algorithm. This will recreate the store and all data will be cleared
* @param {HashAlgorithm | HashAlgorithmFunction} value - The store hash algorithm
*/
set storeHashAlgorithm(value) {
this._storeHashAlgorithm = value;
}
/**
* Gets the keys
* @returns {IterableIterator<string>} - The keys
*/
get keys() {
const keys = [];
for (const store of this._store) {
for (const key of store.keys()) {
const item = store.get(key);
if (item && this.hasExpired(item)) {
store.delete(key);
this.lruRemove(key);
continue;
}
keys.push(key);
}
}
return keys.values();
}
/**
* Gets the items
* @returns {IterableIterator<CacheableStoreItem>} - The items
*/
get items() {
const items = [];
for (const store of this._store) {
for (const item of store.values()) {
if (this.hasExpired(item)) {
store.delete(item.key);
this.lruRemove(item.key);
continue;
}
items.push(item);
}
}
return items.values();
}
/**
* Gets the store
* @returns {Array<Map<string, CacheableStoreItem>>} - The store
*/
get store() {
return this._store;
}
/**
* Gets the value of the key
* @param {string} key - The key to get the value
* @returns {T | undefined} - The value of the key
*/
get(key) {
const store = this.getStore(key);
const item = store.get(key);
if (!item) {
return void 0;
}
if (item.expires && Date.now() > item.expires) {
store.delete(key);
this.lruRemove(key);
return void 0;
}
this.lruMoveToFront(key);
if (!this._useClone) {
return item.value;
}
return this.clone(item.value);
}
/**
* Gets the values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {T[]} - The values of the keys
*/
getMany(keys) {
const result = [];
for (const key of keys) {
result.push(this.get(key));
}
return result;
}
/**
* Gets the raw value of the key
* @param {string} key - The key to get the value
* @returns {CacheableStoreItem | undefined} - The raw value of the key
*/
getRaw(key) {
const store = this.getStore(key);
const item = store.get(key);
if (!item) {
return void 0;
}
if (item.expires && item.expires && Date.now() > item.expires) {
store.delete(key);
this.lruRemove(key);
return void 0;
}
this.lruMoveToFront(key);
return item;
}
/**
* Gets the raw values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {CacheableStoreItem[]} - The raw values of the keys
*/
getManyRaw(keys) {
const result = [];
for (const key of keys) {
result.push(this.getRaw(key));
}
return result;
}
/**
* Sets the value of the key
* @param {string} key - The key to set the value
* @param {any} value - The value to set
* @param {number|string|SetOptions} [ttl] - Time to Live - If you set a number it is miliseconds, if you set a string it is a human-readable.
* If you want to set expire directly you can do that by setting the expire property in the SetOptions.
* If you set undefined, it will use the default time-to-live. If both are undefined then it will not have a time-to-live.
* @returns {void}
*/
set(key, value, ttl) {
const store = this.getStore(key);
let expires;
if (ttl !== void 0 || this._ttl !== void 0) {
if (typeof ttl === "object") {
if (ttl.expire) {
expires = typeof ttl.expire === "number" ? ttl.expire : ttl.expire.getTime();
}
if (ttl.ttl) {
const finalTtl = (0, import_utils.shorthandToTime)(ttl.ttl);
if (finalTtl !== void 0) {
expires = finalTtl;
}
}
} else {
const finalTtl = (0, import_utils.shorthandToTime)(ttl ?? this._ttl);
if (finalTtl !== void 0) {
expires = finalTtl;
}
}
}
if (this._lruSize > 0) {
if (store.has(key)) {
this.lruMoveToFront(key);
} else {
this.lruAddToFront(key);
if (this._lru.size > this._lruSize) {
const oldestKey = this._lru.getOldest();
if (oldestKey) {
this._lru.removeOldest();
this.delete(oldestKey);
}
}
}
}
const item = { key, value, expires };
store.set(key, item);
}
/**
* Sets the values of the keys
* @param {CacheableItem[]} items - The items to set
* @returns {void}
*/
setMany(items) {
for (const item of items) {
this.set(item.key, item.value, item.ttl);
}
}
/**
* Checks if the key exists
* @param {string} key - The key to check
* @returns {boolean} - If true, the key exists. If false, the key does not exist.
*/
has(key) {
const item = this.get(key);
return Boolean(item);
}
/**
* @function hasMany
* @param {string[]} keys - The keys to check
* @returns {boolean[]} - If true, the key exists. If false, the key does not exist.
*/
hasMany(keys) {
const result = [];
for (const key of keys) {
const item = this.get(key);
result.push(Boolean(item));
}
return result;
}
/**
* Take will get the key and delete the entry from cache
* @param {string} key - The key to take
* @returns {T | undefined} - The value of the key
*/
take(key) {
const item = this.get(key);
if (!item) {
return void 0;
}
this.delete(key);
return item;
}
/**
* TakeMany will get the keys and delete the entries from cache
* @param {string[]} keys - The keys to take
* @returns {T[]} - The values of the keys
*/
takeMany(keys) {
const result = [];
for (const key of keys) {
result.push(this.take(key));
}
return result;
}
/**
* Delete the key
* @param {string} key - The key to delete
* @returns {void}
*/
delete(key) {
const store = this.getStore(key);
store.delete(key);
this.lruRemove(key);
}
/**
* Delete the keys
* @param {string[]} keys - The keys to delete
* @returns {void}
*/
deleteMany(keys) {
for (const key of keys) {
this.delete(key);
}
}
/**
* Clear the cache
* @returns {void}
*/
clear() {
this._store = Array.from(
{ length: this._storeHashSize },
() => /* @__PURE__ */ new Map()
);
this._lru = new DoublyLinkedList();
}
/**
* Get the store based on the key (internal use)
* @param {string} key - The key to get the store
* @returns {CacheableHashStore} - The store
*/
getStore(key) {
const hash2 = this.getKeyStoreHash(key);
this._store[hash2] ||= /* @__PURE__ */ new Map();
return this._store[hash2];
}
/**
* Hash the key for which store to go to (internal use)
* @param {string} key - The key to hash
* Available algorithms are: SHA256, SHA1, MD5, and djb2Hash.
* @returns {number} - The hashed key as a number
*/
getKeyStoreHash(key) {
if (this._store.length === 1) {
return 0;
}
if (typeof this._storeHashAlgorithm === "function") {
return this._storeHashAlgorithm(key, this._storeHashSize);
}
const storeHashSize = this._storeHashSize - 1;
const hash2 = (0, import_utils.hashToNumberSync)(key, {
min: 0,
max: storeHashSize,
algorithm: this._storeHashAlgorithm
});
return hash2;
}
/**
* Clone the value. This is for internal use
* @param {any} value - The value to clone
* @returns {any} - The cloned value
*/
// biome-ignore lint/suspicious/noExplicitAny: type format
clone(value) {
if (this.isPrimitive(value)) {
return value;
}
return structuredClone(value);
}
/**
* Add to the front of the LRU cache. This is for internal use
* @param {string} key - The key to add to the front
* @returns {void}
*/
lruAddToFront(key) {
if (this._lruSize === 0) {
return;
}
this._lru.addToFront(key);
}
/**
* Move to the front of the LRU cache. This is for internal use
* @param {string} key - The key to move to the front
* @returns {void}
*/
lruMoveToFront(key) {
if (this._lruSize === 0) {
return;
}
this._lru.moveToFront(key);
}
/**
* Remove a key from the LRU cache. This is for internal use
* @param {string} key - The key to remove
* @returns {void}
*/
lruRemove(key) {
if (this._lruSize === 0) {
return;
}
this._lru.remove(key);
}
/**
* Resize the LRU cache. This is for internal use.
* @returns {void}
*/
lruResize() {
while (this._lru.size > this._lruSize) {
const oldestKey = this._lru.getOldest();
if (oldestKey) {
this._lru.removeOldest();
this.delete(oldestKey);
}
}
}
/**
* Check for expiration. This is for internal use
* @returns {void}
*/
checkExpiration() {
for (const store of this._store) {
for (const item of store.values()) {
if (item.expires && Date.now() > item.expires) {
store.delete(item.key);
this.lruRemove(item.key);
}
}
}
}
/**
* Start the interval check. This is for internal use
* @returns {void}
*/
startIntervalCheck() {
if (this._checkInterval > 0) {
if (this._interval) {
clearInterval(this._interval);
}
this._interval = setInterval(() => {
this.checkExpiration();
}, this._checkInterval).unref();
}
}
/**
* Stop the interval check. This is for internal use
* @returns {void}
*/
stopIntervalCheck() {
if (this._interval) {
clearInterval(this._interval);
}
this._interval = 0;
this._checkInterval = 0;
}
/**
* Wrap the function for caching
* @param {Function} function_ - The function to wrap
* @param {Object} [options] - The options to wrap
* @returns {Function} - The wrapped function
*/
// biome-ignore lint/suspicious/noExplicitAny: type format
wrap(function_, options) {
const wrapOptions = {
ttl: options?.ttl ?? this._ttl,
keyPrefix: options?.keyPrefix,
createKey: options?.createKey,
cache: this
};
return (0, import_utils.wrapSync)(function_, wrapOptions);
}
// biome-ignore lint/suspicious/noExplicitAny: type format
isPrimitive(value) {
const result = false;
if (value === null || value === void 0) {
return true;
}
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
return true;
}
return result;
}
setTtl(ttl) {
if (typeof ttl === "string" || ttl === void 0) {
this._ttl = ttl;
} else if (ttl > 0) {
this._ttl = ttl;
} else {
this._ttl = void 0;
}
}
hasExpired(item) {
if (item.expires && Date.now() > item.expires) {
return true;
}
return false;
}
//#endregion
//#region src/index.ts
/**
* Lifecycle hooks fired by {@link CacheableMemory}. Register handlers with the inherited
* `onHook(hook, handler)` method. Hooks are dispatched synchronously via `hookSync`, which skips
* `async` handler functions entirely — register only synchronous handlers.
*/
let CacheableMemoryHooks = /* @__PURE__ */ function(CacheableMemoryHooks) {
CacheableMemoryHooks["BEFORE_SET"] = "BEFORE_SET";
CacheableMemoryHooks["AFTER_SET"] = "AFTER_SET";
CacheableMemoryHooks["BEFORE_SET_MANY"] = "BEFORE_SET_MANY";
CacheableMemoryHooks["AFTER_SET_MANY"] = "AFTER_SET_MANY";
CacheableMemoryHooks["BEFORE_GET"] = "BEFORE_GET";
CacheableMemoryHooks["AFTER_GET"] = "AFTER_GET";
CacheableMemoryHooks["BEFORE_GET_MANY"] = "BEFORE_GET_MANY";
CacheableMemoryHooks["AFTER_GET_MANY"] = "AFTER_GET_MANY";
CacheableMemoryHooks["BEFORE_DELETE"] = "BEFORE_DELETE";
CacheableMemoryHooks["AFTER_DELETE"] = "AFTER_DELETE";
CacheableMemoryHooks["BEFORE_DELETE_MANY"] = "BEFORE_DELETE_MANY";
CacheableMemoryHooks["AFTER_DELETE_MANY"] = "AFTER_DELETE_MANY";
CacheableMemoryHooks["BEFORE_CLEAR"] = "BEFORE_CLEAR";
CacheableMemoryHooks["AFTER_CLEAR"] = "AFTER_CLEAR";
return CacheableMemoryHooks;
}({});
const defaultStoreHashSize = 16;
const maximumMapSize = 16777216;
var CacheableMemory = class extends hookified.Hookified {
_lru = new DoublyLinkedList();
_storeHashSize = 16;
_storeHashAlgorithm = _cacheable_utils.HashAlgorithm.DJB2;
_store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
_ttl;
_maxTtl;
_useClone = true;
_lruSize = 0;
_checkInterval = 0;
_interval = 0;
_stats = new _cacheable_utils.Stats({ enabled: false });
/**
* @constructor
* @param {CacheableMemoryOptions} [options] - The options for the CacheableMemory
*/
constructor(options) {
super();
if (options?.ttl) this.setTtl(options.ttl);
if (options?.maxTtl !== void 0) this.setMaxTtl(options.maxTtl);
if (options?.useClone !== void 0) this._useClone = options.useClone;
if (options?.stats) this._stats.enabled = options.stats;
if (options?.storeHashSize && options.storeHashSize > 0) this._storeHashSize = options.storeHashSize;
if (options?.lruSize) if (options.lruSize > 16777216) this.emit("error", /* @__PURE__ */ new Error(`LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`));
else this._lruSize = options.lruSize;
if (options?.checkInterval) this._checkInterval = options.checkInterval;
if (options?.storeHashAlgorithm) this._storeHashAlgorithm = options.storeHashAlgorithm;
this._store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
this.startIntervalCheck();
}
/**
* Gets the time-to-live
* @returns {number|string|undefined} - The time-to-live in miliseconds or a human-readable format. If undefined, it will not have a time-to-live.
*/
get ttl() {
return this._ttl;
}
/**
* Sets the time-to-live
* @param {number|string|undefined} value - The time-to-live in miliseconds or a human-readable format (example '1s' = 1 second, '1h' = 1 hour). If undefined, it will not have a time-to-live.
*/
set ttl(value) {
this.setTtl(value);
}
/**
* Gets the maximum time-to-live. When set, any TTL that exceeds this value is capped to maxTtl.
* Entries with no TTL will also be capped to maxTtl. Default is `undefined` (no maximum).
* @returns {number|string|undefined} - The maximum TTL in milliseconds, human-readable format, or undefined.
*/
get maxTtl() {
return this._maxTtl;
}
/**
* Sets the maximum time-to-live. When set, any TTL that exceeds this value is capped to maxTtl.
* Entries with no TTL will also be capped to maxTtl.
* @param {number|string|undefined} value - The maximum TTL in milliseconds or human-readable format (e.g. '1s', '1h'). If undefined, no maximum is enforced.
*/
set maxTtl(value) {
this.setMaxTtl(value);
}
/**
* Gets whether to use clone
* @returns {boolean} - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
get useClone() {
return this._useClone;
}
/**
* Sets whether to use clone
* @param {boolean} value - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
set useClone(value) {
this._useClone = value;
}
/**
* Gets the size of the LRU cache
* @returns {number} - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
get lruSize() {
return this._lruSize;
}
/**
* Sets the size of the LRU cache
* @param {number} value - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
set lruSize(value) {
if (value > 16777216) {
this.emit("error", /* @__PURE__ */ new Error(`LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`));
return;
}
this._lruSize = value;
if (this._lruSize === 0) {
this._lru = new DoublyLinkedList();
return;
}
this.lruResize();
}
/**
* Gets the check interval
* @returns {number} - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
get checkInterval() {
return this._checkInterval;
}
/**
* Sets the check interval
* @param {number} value - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
set checkInterval(value) {
this._checkInterval = value;
}
/**
* Gets the size of the cache
* @returns {number} - The size of the cache
*/
get size() {
let size = 0;
for (const store of this._store) size += store.size;
return size;
}
/**
* Gets the statistics of the cache. Statistics track aggregate counters such as `hits`, `misses`,
* `gets`, `sets`, `deletes`, `clears`, `count`, `ksize`, and `vsize`. They are disabled by default;
* enable them via the `stats` option or by setting `cache.stats.enabled = true`.
* @returns {Stats} - The statistics for this CacheableMemory instance
*/
get stats() {
return this._stats;
}
/**
* Gets the number of hash stores
* @returns {number} - The number of hash stores
*/
get storeHashSize() {
return this._storeHashSize;
}
/**
* Sets the number of hash stores. This will recreate the store and all data will be cleared
* @param {number} value - The number of hash stores
*/
set storeHashSize(value) {
if (value === this._storeHashSize) return;
this._storeHashSize = value;
this._store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
if (this._stats.enabled) this._stats.resetStoreValues();
}
/**
* Gets the store hash algorithm
* @returns {HashAlgorithm | StoreHashAlgorithmFunction} - The store hash algorithm
*/
get storeHashAlgorithm() {
return this._storeHashAlgorithm;
}
/**
* Sets the store hash algorithm. This will recreate the store and all data will be cleared
* @param {HashAlgorithm | HashAlgorithmFunction} value - The store hash algorithm
*/
set storeHashAlgorithm(value) {
this._storeHashAlgorithm = value;
}
/**
* Gets the keys
* @returns {IterableIterator<string>} - The keys
*/
get keys() {
const keys = [];
for (const store of this._store) for (const key of store.keys()) {
const item = store.get(key);
if (item && this.hasExpired(item)) {
this.recordExpiration(item);
store.delete(key);
this.lruRemove(key);
continue;
}
keys.push(key);
}
return keys.values();
}
/**
* Gets the items
* @returns {IterableIterator<CacheableStoreItem>} - The items
*/
get items() {
const items = [];
for (const store of this._store) for (const item of store.values()) {
if (this.hasExpired(item)) {
this.recordExpiration(item);
store.delete(item.key);
this.lruRemove(item.key);
continue;
}
items.push(item);
}
return items.values();
}
/**
* Gets the store
* @returns {Array<Map<string, CacheableStoreItem>>} - The store
*/
get store() {
return this._store;
}
/**
* Gets the value of the key
* @param {string} key - The key to get the value
* @returns {T | undefined} - The value of the key
*/
get(key) {
this.hookSync("BEFORE_GET", key);
const store = this.getStore(key);
const item = store.get(key);
if (!item) {
this.recordRead(false);
this.hookSync("AFTER_GET", {
key,
result: void 0
});
return;
}
if (item.expires && Date.now() > item.expires) {
this.recordExpiration(item);
store.delete(key);
this.lruRemove(key);
this.recordRead(false);
this.hookSync("AFTER_GET", {
key,
result: void 0
});
return;
}
this.lruMoveToFront(key);
let result;
if (!this._useClone) result = item.value;
else result = this.clone(item.value);
this.recordRead(true);
this.hookSync("AFTER_GET", {
key,
result
});
return result;
}
/**
* Gets the values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {T[]} - The values of the keys
*/
getMany(keys) {
this.hookSync("BEFORE_GET_MANY", keys);
const result = [];
for (const key of keys) result.push(this.get(key));
this.hookSync("AFTER_GET_MANY", {
keys,
result
});
return result;
}
/**
* Gets the raw value of the key
* @param {string} key - The key to get the value
* @returns {CacheableStoreItem | undefined} - The raw value of the key
*/
getRaw(key) {
const store = this.getStore(key);
const item = store.get(key);
if (!item) {
this.recordRead(false);
return;
}
if (item.expires && Date.now() > item.expires) {
this.recordExpiration(item);
store.delete(key);
this.lruRemove(key);
this.recordRead(false);
return;
}
this.lruMoveToFront(key);
this.recordRead(true);
return item;
}
/**
* Gets the raw values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {CacheableStoreItem[]} - The raw values of the keys
*/
getManyRaw(keys) {
const result = [];
for (const key of keys) result.push(this.getRaw(key));
return result;
}
/**
* Sets the value of the key
* @param {string} key - The key to set the value
* @param {any} value - The value to set
* @param {number|string|SetOptions} [ttl] - Time to Live - If you set a number it is miliseconds, if you set a string it is a human-readable.
* If you want to set expire directly you can do that by setting the expire property in the SetOptions.
* If you set undefined, it will use the default time-to-live. If both are undefined then it will not have a time-to-live.
* @returns {void}
*/
set(key, value, ttl) {
const hookItem = {
key,
value,
ttl
};
this.hookSync("BEFORE_SET", hookItem);
const store = this.getStore(hookItem.key);
let expires;
const effectiveTtl = hookItem.ttl;
if (effectiveTtl !== void 0 || this._ttl !== void 0) if (typeof effectiveTtl === "object") {
if (effectiveTtl.expire) expires = typeof effectiveTtl.expire === "number" ? effectiveTtl.expire : effectiveTtl.expire.getTime();
if (effectiveTtl.ttl) {
const finalTtl = (0, _cacheable_utils.shorthandToTime)(effectiveTtl.ttl);
/* v8 ignore next -- @preserve */
if (finalTtl !== void 0) expires = finalTtl;
}
} else {
const finalTtl = (0, _cacheable_utils.shorthandToTime)(effectiveTtl ?? this._ttl);
/* v8 ignore next -- @preserve */
if (finalTtl !== void 0) expires = finalTtl;
}
if (this._maxTtl !== void 0) {
const maxExpires = (0, _cacheable_utils.shorthandToTime)(this._maxTtl);
if (expires === void 0) expires = maxExpires;
else if (expires > maxExpires) expires = maxExpires;
}
if (this._lruSize > 0) if (store.has(hookItem.key)) this.lruMoveToFront(hookItem.key);
else {
this.lruAddToFront(hookItem.key);
if (this._lru.size > this._lruSize) {
const oldestKey = this._lru.getOldest();
/* v8 ignore next -- @preserve */
if (oldestKey) {
this._lru.removeOldest();
this.delete(oldestKey);
}
}
}
if (this._stats.enabled) {
const existing = store.get(hookItem.key);
if (existing) this._stats.decreaseVSize(existing.value);
else {
this._stats.incrementKSize(hookItem.key);
this._stats.incrementCount();
}
this._stats.incrementVSize(hookItem.value);
this._stats.incrementSets();
}
const item = {
key: hookItem.key,
value: hookItem.value,
expires
};
store.set(hookItem.key, item);
this.hookSync("AFTER_SET", hookItem);
}
/**
* Sets the values of the keys
* @param {CacheableItem[]} items - The items to set
* @returns {void}
*/
setMany(items) {
this.hookSync("BEFORE_SET_MANY", items);
for (const item of items) this.set(item.key, item.value, item.ttl);
this.hookSync("AFTER_SET_MANY", items);
}
/**
* Checks if the key exists
* @param {string} key - The key to check
* @returns {boolean} - If true, the key exists. If false, the key does not exist.
*/
has(key) {
const item = this.get(key);
return Boolean(item);
}
/**
* @function hasMany
* @param {string[]} keys - The keys to check
* @returns {boolean[]} - If true, the key exists. If false, the key does not exist.
*/
hasMany(keys) {
const result = [];
for (const key of keys) {
const item = this.get(key);
result.push(Boolean(item));
}
return result;
}
/**
* Take will get the key and delete the entry from cache
* @param {string} key - The key to take
* @returns {T | undefined} - The value of the key
*/
take(key) {
const item = this.get(key);
if (!item) return;
this.delete(key);
return item;
}
/**
* TakeMany will get the keys and delete the entries from cache
* @param {string[]} keys - The keys to take
* @returns {T[]} - The values of the keys
*/
takeMany(keys) {
const result = [];
for (const key of keys) result.push(this.take(key));
return result;
}
/**
* Delete the key
* @param {string} key - The key to delete
* @returns {void}
*/
delete(key) {
this.hookSync("BEFORE_DELETE", key);
const store = this.getStore(key);
if (this._stats.enabled) {
const item = store.get(key);
if (item) {
this._stats.decreaseKSize(key);
this._stats.decreaseVSize(item.value);
this._stats.decreaseCount();
this._stats.incrementDeletes();
}
}
store.delete(key);
this.lruRemove(key);
this.hookSync("AFTER_DELETE", key);
}
/**
* Delete the keys
* @param {string[]} keys - The keys to delete
* @returns {void}
*/
deleteMany(keys) {
this.hookSync("BEFORE_DELETE_MANY", keys);
for (const key of keys) this.delete(key);
this.hookSync("AFTER_DELETE_MANY", keys);
}
/**
* Clear the cache
* @returns {void}
*/
clear() {
this.hookSync("BEFORE_CLEAR");
this._store = Array.from({ length: this._storeHashSize }, () => /* @__PURE__ */ new Map());
this._lru = new DoublyLinkedList();
if (this._stats.enabled) {
this._stats.resetStoreValues();
this._stats.incrementClears();
}
this.hookSync("AFTER_CLEAR");
}
/**
* Get the store based on the key (internal use)
* @param {string} key - The key to get the store
* @returns {CacheableHashStore} - The store
*/
getStore(key) {
const hash = this.getKeyStoreHash(key);
this._store[hash] ||= /* @__PURE__ */ new Map();
return this._store[hash];
}
/**
* Hash the key for which store to go to (internal use)
* @param {string} key - The key to hash
* Available algorithms are: SHA256, SHA1, MD5, and djb2Hash.
* @returns {number} - The hashed key as a number
*/
getKeyStoreHash(key) {
if (this._store.length === 1) return 0;
if (typeof this._storeHashAlgorithm === "function") return this._storeHashAlgorithm(key, this._storeHashSize);
return (0, _cacheable_utils.hashToNumberSync)(key, {
min: 0,
max: this._storeHashSize - 1,
algorithm: this._storeHashAlgorithm
});
}
/**
* Clone the value. This is for internal use
* @param {any} value - The value to clone
* @returns {any} - The cloned value
*/
clone(value) {
if (this.isPrimitive(value)) return value;
return structuredClone(value);
}
/**
* Add to the front of the LRU cache. This is for internal use
* @param {string} key - The key to add to the front
* @returns {void}
*/
lruAddToFront(key) {
if (this._lruSize === 0) return;
this._lru.addToFront(key);
}
/**
* Move to the front of the LRU cache. This is for internal use
* @param {string} key - The key to move to the front
* @returns {void}
*/
lruMoveToFront(key) {
if (this._lruSize === 0) return;
this._lru.moveToFront(key);
}
/**
* Remove a key from the LRU cache. This is for internal use
* @param {string} key - The key to remove
* @returns {void}
*/
lruRemove(key) {
if (this._lruSize === 0) return;
this._lru.remove(key);
}
/**
* Resize the LRU cache. This is for internal use.
* @returns {void}
*/
lruResize() {
while (this._lru.size > this._lruSize) {
const oldestKey = this._lru.getOldest();
/* v8 ignore next -- @preserve */
if (oldestKey) {
this._lru.removeOldest();
this.delete(oldestKey);
}
}
}
/**
* Check for expiration. This is for internal use
* @returns {void}
*/
checkExpiration() {
for (const store of this._store) for (const item of store.values()) if (item.expires && Date.now() > item.expires) {
this.recordExpiration(item);
store.delete(item.key);
this.lruRemove(item.key);
}
}
/**
* Start the interval check. This is for internal use
* @returns {void}
*/
startIntervalCheck() {
if (this._checkInterval > 0) {
/* v8 ignore next -- @preserve */
if (this._interval)
/* v8 ignore next -- @preserve */
clearInterval(this._interval);
this._interval = setInterval(() => {
this.checkExpiration();
}, this._checkInterval).unref();
}
}
/**
* Stop the interval check. This is for internal use
* @returns {void}
*/
stopIntervalCheck() {
/* v8 ignore next -- @preserve */
if (this._interval) clearInterval(this._interval);
this._interval = 0;
this._checkInterval = 0;
}
/**
* Wrap the function for caching
* @param {Function} function_ - The function to wrap
* @param {Object} [options] - The options to wrap
* @returns {Function} - The wrapped function
*/
wrap(function_, options) {
return (0, _cacheable_utils.wrapSync)(function_, {
ttl: options?.ttl ?? this._ttl,
keyPrefix: options?.keyPrefix,
createKey: options?.createKey,
cache: this
});
}
/**
* Gets the value of the key, or computes and stores it on a cache miss. This is the synchronous
* cache-aside helper: if the key is present its value is returned, otherwise `function_` is
* invoked, its result is stored, and that result is returned.
*
* The value is stored using `options.ttl`, falling back to the instance default `ttl`. Because
* the cache is synchronous there is no request coalescing — concurrent callers cannot stampede
* the setter the way they can with an async cache.
* @param {GetOrSetSyncKey} key - The key to get or set. Can also be a function that returns the key.
* @param {() => T} function_ - The function that computes the value on a cache miss.
* @param {GetOrSetFunctionOptions} [options] - Options such as `ttl`, `cacheErrors`, and `throwErrors`.
* @returns {T | undefined} - The cached or freshly computed value
*/
getOrSet(key, function_, options) {
return (0, _cacheable_utils.getOrSetSync)(key, function_, {
cache: this,
ttl: options?.ttl ?? this._ttl,
cacheErrors: options?.cacheErrors,
throwErrors: options?.throwErrors
});
}
/**
* Records a single read against the statistics counters. Each read increments `gets` and either
* `hits` or `misses`. No-op when statistics are disabled. This is for internal use.
* @param {boolean} hit - Whether the read found a (non-expired) value
* @returns {void}
*/
recordRead(hit) {
if (!this._stats.enabled) return;
if (hit) this._stats.incrementHits();
else this._stats.incrementMisses();
this._stats.incrementGets();
}
/**
* Decrements the size statistics (`count`, `ksize`, and `vsize`) for an entry that is being removed
* because it expired. Expirations are not counted as `deletes` since they are not user-initiated.
* No-op when statistics are disabled. This is for internal use.
* @param {CacheableStoreItem} item - The expired item being removed from the store
* @returns {void}
*/
recordExpiration(item) {
if (!this._stats.enabled) return;
this._stats.decreaseKSize(item.key);
this._stats.decreaseVSize(item.value);
this._stats.decreaseCount();
}
isPrimitive(value) {
const result = false;
/* v8 ignore next -- @preserve */
if (value === null || value === void 0) return true;
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return true;
return result;
}
setTtl(ttl) {
if (typeof ttl === "string" || ttl === void 0) this._ttl = ttl;
else if (ttl > 0) this._ttl = ttl;
else this._ttl = void 0;
}
setMaxTtl(maxTtl) {
if (typeof maxTtl === "string" || maxTtl === void 0) this._maxTtl = maxTtl;
else if (maxTtl > 0) this._maxTtl = maxTtl;
else this._maxTtl = void 0;
}
hasExpired(item) {
if (item.expires && Date.now() > item.expires) return true;
return false;
}
};
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
CacheableMemory,
HashAlgorithm,
KeyvCacheableMemory,
createKeyv,
defaultStoreHashSize,
hash,
hashToNumber,
maximumMapSize
//#endregion
exports.CacheableMemory = CacheableMemory;
exports.CacheableMemoryHooks = CacheableMemoryHooks;
Object.defineProperty(exports, "HashAlgorithm", {
enumerable: true,
get: function() {
return _cacheable_utils.HashAlgorithm;
}
});
/* v8 ignore next -- @preserve */
exports.KeyvCacheableMemory = KeyvCacheableMemory;
Object.defineProperty(exports, "Stats", {
enumerable: true,
get: function() {
return _cacheable_utils.Stats;
}
});
exports.createKeyv = createKeyv;
exports.defaultStoreHashSize = defaultStoreHashSize;
Object.defineProperty(exports, "getOrSetSync", {
enumerable: true,
get: function() {
return _cacheable_utils.getOrSetSync;
}
});
Object.defineProperty(exports, "hash", {
enumerable: true,
get: function() {
return _cacheable_utils.hash;
}
});
Object.defineProperty(exports, "hashToNumber", {
enumerable: true,
get: function() {
return _cacheable_utils.hashToNumber;
}
});
exports.maximumMapSize = maximumMapSize;

@@ -1,32 +0,32 @@

import { HashAlgorithm, CacheableStoreItem, CacheableItem, WrapFunctionOptions } from '@cacheable/utils';
export { CacheableItem, CacheableStoreItem, HashAlgorithm, hash, hashToNumber } from '@cacheable/utils';
import { Hookified } from 'hookified';
import { KeyvStoreAdapter, StoredData, Keyv } from 'keyv';
import { CacheableItem, CacheableItem as CacheableItem$1, CacheableStoreItem, CacheableStoreItem as CacheableStoreItem$1, GetOrSetFunctionOptions, GetOrSetFunctionOptions as GetOrSetFunctionOptions$1, GetOrSetSyncKey, GetOrSetSyncKey as GetOrSetSyncKey$1, GetOrSetSyncOptions, HashAlgorithm, HashAlgorithm as HashAlgorithm$1, Stats, Stats as Stats$1, StatsOptions, StatsSnapshot, WrapFunctionOptions, getOrSetSync, hash, hashToNumber } from "@cacheable/utils";
import { Hookified } from "hookified";
import { Keyv, KeyvStoreAdapter, StoredData } from "keyv";
//#region src/keyv-memory.d.ts
type KeyvCacheableMemoryOptions = CacheableMemoryOptions & {
namespace?: string;
namespace?: string;
};
declare class KeyvCacheableMemory implements KeyvStoreAdapter {
opts: CacheableMemoryOptions;
private readonly _defaultCache;
private readonly _nCache;
private _namespace?;
constructor(options?: KeyvCacheableMemoryOptions);
get namespace(): string | undefined;
set namespace(value: string | undefined);
get store(): CacheableMemory;
get<Value>(key: string): Promise<StoredData<Value> | undefined>;
getMany<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>>;
set(key: string, value: any, ttl?: number): Promise<void>;
setMany(values: Array<{
key: string;
value: any;
ttl?: number;
}>): Promise<void>;
delete(key: string): Promise<boolean>;
deleteMany?(key: string[]): Promise<boolean>;
clear(): Promise<void>;
has?(key: string): Promise<boolean>;
on(event: string, listener: (...arguments_: any[]) => void): this;
getStore(namespace?: string): CacheableMemory;
opts: CacheableMemoryOptions;
private readonly _defaultCache;
private readonly _nCache;
private _namespace?;
constructor(options?: KeyvCacheableMemoryOptions);
get namespace(): string | undefined;
set namespace(value: string | undefined);
get store(): CacheableMemory;
get<Value>(key: string): Promise<StoredData<Value> | undefined>;
getMany<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>>;
set(key: string, value: any, ttl?: number): Promise<void>;
setMany(values: Array<{
key: string;
value: any;
ttl?: number;
}>): Promise<void>;
delete(key: string): Promise<boolean>;
deleteMany?(key: string[]): Promise<boolean>;
clear(): Promise<void>;
has?(key: string): Promise<boolean>;
on(event: string, listener: (...arguments_: any[]) => void): this;
getStore(namespace?: string): CacheableMemory;
}

@@ -39,3 +39,25 @@ /**

declare function createKeyv(options?: KeyvCacheableMemoryOptions): Keyv;
//#endregion
//#region src/index.d.ts
/**
* Lifecycle hooks fired by {@link CacheableMemory}. Register handlers with the inherited
* `onHook(hook, handler)` method. Hooks are dispatched synchronously via `hookSync`, which skips
* `async` handler functions entirely — register only synchronous handlers.
*/
declare enum CacheableMemoryHooks {
BEFORE_SET = "BEFORE_SET",
AFTER_SET = "AFTER_SET",
BEFORE_SET_MANY = "BEFORE_SET_MANY",
AFTER_SET_MANY = "AFTER_SET_MANY",
BEFORE_GET = "BEFORE_GET",
AFTER_GET = "AFTER_GET",
BEFORE_GET_MANY = "BEFORE_GET_MANY",
AFTER_GET_MANY = "AFTER_GET_MANY",
BEFORE_DELETE = "BEFORE_DELETE",
AFTER_DELETE = "AFTER_DELETE",
BEFORE_DELETE_MANY = "BEFORE_DELETE_MANY",
AFTER_DELETE_MANY = "AFTER_DELETE_MANY",
BEFORE_CLEAR = "BEFORE_CLEAR",
AFTER_CLEAR = "AFTER_CLEAR"
}
type StoreHashAlgorithmFunction = (key: string, storeHashSize: number) => number;

@@ -47,2 +69,5 @@ /**

* undefined then it will not have a time-to-live.
* @property {number|string} [maxTtl] - Maximum Time to Live - The upper bound for any TTL set on a cache entry. If a TTL (whether from the
* default or per-entry) exceeds this value, the entry's TTL is capped to maxTtl. Can be a number in milliseconds or a human-readable
* format such as `1s`, `1m`, `1h`, `1d`. Default is `undefined` (no maximum).
* @property {boolean} [useClone] - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.

@@ -52,262 +77,339 @@ * @property {number} [lruSize] - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.

* @property {number} [storeHashSize] - The number of how many Map stores we have for the hash. Default is 10.
* @property {boolean} [stats] - If true, it will track statistics such as hits, misses, gets, sets, and deletes for this
* instance. Statistics are accessible via the `stats` property. Default is `false`.
*/
type CacheableMemoryOptions = {
ttl?: number | string;
useClone?: boolean;
lruSize?: number;
checkInterval?: number;
storeHashSize?: number;
storeHashAlgorithm?: HashAlgorithm | ((key: string, storeHashSize: number) => number);
ttl?: number | string;
maxTtl?: number | string;
useClone?: boolean;
lruSize?: number;
checkInterval?: number;
storeHashSize?: number;
storeHashAlgorithm?: HashAlgorithm$1 | ((key: string, storeHashSize: number) => number);
stats?: boolean;
};
type SetOptions = {
ttl?: number | string;
expire?: number | Date;
ttl?: number | string;
expire?: number | Date;
};
/**
* The payload passed to the `BEFORE_SET` and `AFTER_SET` hooks. Inside a `BEFORE_SET` handler
* you can reassign `key`, `value`, or `ttl` to change what gets stored.
*/
type CacheableMemoryHookItem<T = unknown> = {
key: string;
value: T;
ttl?: number | string | SetOptions;
};
/** The payload passed to the `AFTER_GET` hook. `result` is `undefined` on a cache miss. */
type CacheableMemoryAfterGetItem<T = unknown> = {
key: string;
result: T | undefined;
};
/**
* The payload passed to the `AFTER_GET_MANY` hook. Entries are `undefined` for keys that were
* missing or expired, mirroring what `getMany` collects.
*/
type CacheableMemoryAfterGetManyItem<T = unknown> = {
keys: string[];
result: Array<T | undefined>;
};
declare const defaultStoreHashSize = 16;
declare const maximumMapSize = 16777216;
declare class CacheableMemory extends Hookified {
private _lru;
private _storeHashSize;
private _storeHashAlgorithm;
private _store;
private _ttl;
private _useClone;
private _lruSize;
private _checkInterval;
private _interval;
/**
* @constructor
* @param {CacheableMemoryOptions} [options] - The options for the CacheableMemory
*/
constructor(options?: CacheableMemoryOptions);
/**
* Gets the time-to-live
* @returns {number|string|undefined} - The time-to-live in miliseconds or a human-readable format. If undefined, it will not have a time-to-live.
*/
get ttl(): number | string | undefined;
/**
* Sets the time-to-live
* @param {number|string|undefined} value - The time-to-live in miliseconds or a human-readable format (example '1s' = 1 second, '1h' = 1 hour). If undefined, it will not have a time-to-live.
*/
set ttl(value: number | string | undefined);
/**
* Gets whether to use clone
* @returns {boolean} - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
get useClone(): boolean;
/**
* Sets whether to use clone
* @param {boolean} value - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
set useClone(value: boolean);
/**
* Gets the size of the LRU cache
* @returns {number} - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
get lruSize(): number;
/**
* Sets the size of the LRU cache
* @param {number} value - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
set lruSize(value: number);
/**
* Gets the check interval
* @returns {number} - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
get checkInterval(): number;
/**
* Sets the check interval
* @param {number} value - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
set checkInterval(value: number);
/**
* Gets the size of the cache
* @returns {number} - The size of the cache
*/
get size(): number;
/**
* Gets the number of hash stores
* @returns {number} - The number of hash stores
*/
get storeHashSize(): number;
/**
* Sets the number of hash stores. This will recreate the store and all data will be cleared
* @param {number} value - The number of hash stores
*/
set storeHashSize(value: number);
/**
* Gets the store hash algorithm
* @returns {HashAlgorithm | StoreHashAlgorithmFunction} - The store hash algorithm
*/
get storeHashAlgorithm(): HashAlgorithm | StoreHashAlgorithmFunction;
/**
* Sets the store hash algorithm. This will recreate the store and all data will be cleared
* @param {HashAlgorithm | HashAlgorithmFunction} value - The store hash algorithm
*/
set storeHashAlgorithm(value: HashAlgorithm | StoreHashAlgorithmFunction);
/**
* Gets the keys
* @returns {IterableIterator<string>} - The keys
*/
get keys(): IterableIterator<string>;
/**
* Gets the items
* @returns {IterableIterator<CacheableStoreItem>} - The items
*/
get items(): IterableIterator<CacheableStoreItem>;
/**
* Gets the store
* @returns {Array<Map<string, CacheableStoreItem>>} - The store
*/
get store(): Array<Map<string, CacheableStoreItem>>;
/**
* Gets the value of the key
* @param {string} key - The key to get the value
* @returns {T | undefined} - The value of the key
*/
get<T>(key: string): T | undefined;
/**
* Gets the values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {T[]} - The values of the keys
*/
getMany<T>(keys: string[]): T[];
/**
* Gets the raw value of the key
* @param {string} key - The key to get the value
* @returns {CacheableStoreItem | undefined} - The raw value of the key
*/
getRaw(key: string): CacheableStoreItem | undefined;
/**
* Gets the raw values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {CacheableStoreItem[]} - The raw values of the keys
*/
getManyRaw(keys: string[]): Array<CacheableStoreItem | undefined>;
/**
* Sets the value of the key
* @param {string} key - The key to set the value
* @param {any} value - The value to set
* @param {number|string|SetOptions} [ttl] - Time to Live - If you set a number it is miliseconds, if you set a string it is a human-readable.
* If you want to set expire directly you can do that by setting the expire property in the SetOptions.
* If you set undefined, it will use the default time-to-live. If both are undefined then it will not have a time-to-live.
* @returns {void}
*/
set(key: string, value: any, ttl?: number | string | SetOptions): void;
/**
* Sets the values of the keys
* @param {CacheableItem[]} items - The items to set
* @returns {void}
*/
setMany(items: CacheableItem[]): void;
/**
* Checks if the key exists
* @param {string} key - The key to check
* @returns {boolean} - If true, the key exists. If false, the key does not exist.
*/
has(key: string): boolean;
/**
* @function hasMany
* @param {string[]} keys - The keys to check
* @returns {boolean[]} - If true, the key exists. If false, the key does not exist.
*/
hasMany(keys: string[]): boolean[];
/**
* Take will get the key and delete the entry from cache
* @param {string} key - The key to take
* @returns {T | undefined} - The value of the key
*/
take<T>(key: string): T | undefined;
/**
* TakeMany will get the keys and delete the entries from cache
* @param {string[]} keys - The keys to take
* @returns {T[]} - The values of the keys
*/
takeMany<T>(keys: string[]): T[];
/**
* Delete the key
* @param {string} key - The key to delete
* @returns {void}
*/
delete(key: string): void;
/**
* Delete the keys
* @param {string[]} keys - The keys to delete
* @returns {void}
*/
deleteMany(keys: string[]): void;
/**
* Clear the cache
* @returns {void}
*/
clear(): void;
/**
* Get the store based on the key (internal use)
* @param {string} key - The key to get the store
* @returns {CacheableHashStore} - The store
*/
getStore(key: string): Map<string, CacheableStoreItem>;
/**
* Hash the key for which store to go to (internal use)
* @param {string} key - The key to hash
* Available algorithms are: SHA256, SHA1, MD5, and djb2Hash.
* @returns {number} - The hashed key as a number
*/
getKeyStoreHash(key: string): number;
/**
* Clone the value. This is for internal use
* @param {any} value - The value to clone
* @returns {any} - The cloned value
*/
clone(value: any): any;
/**
* Add to the front of the LRU cache. This is for internal use
* @param {string} key - The key to add to the front
* @returns {void}
*/
lruAddToFront(key: string): void;
/**
* Move to the front of the LRU cache. This is for internal use
* @param {string} key - The key to move to the front
* @returns {void}
*/
lruMoveToFront(key: string): void;
/**
* Remove a key from the LRU cache. This is for internal use
* @param {string} key - The key to remove
* @returns {void}
*/
lruRemove(key: string): void;
/**
* Resize the LRU cache. This is for internal use.
* @returns {void}
*/
lruResize(): void;
/**
* Check for expiration. This is for internal use
* @returns {void}
*/
checkExpiration(): void;
/**
* Start the interval check. This is for internal use
* @returns {void}
*/
startIntervalCheck(): void;
/**
* Stop the interval check. This is for internal use
* @returns {void}
*/
stopIntervalCheck(): void;
/**
* Wrap the function for caching
* @param {Function} function_ - The function to wrap
* @param {Object} [options] - The options to wrap
* @returns {Function} - The wrapped function
*/
wrap<T, Arguments extends any[]>(function_: (...arguments_: Arguments) => T, options?: WrapFunctionOptions): (...arguments_: Arguments) => T;
private isPrimitive;
private setTtl;
private hasExpired;
private _lru;
private _storeHashSize;
private _storeHashAlgorithm;
private _store;
private _ttl;
private _maxTtl;
private _useClone;
private _lruSize;
private _checkInterval;
private _interval;
private readonly _stats;
/**
* @constructor
* @param {CacheableMemoryOptions} [options] - The options for the CacheableMemory
*/
constructor(options?: CacheableMemoryOptions);
/**
* Gets the time-to-live
* @returns {number|string|undefined} - The time-to-live in miliseconds or a human-readable format. If undefined, it will not have a time-to-live.
*/
get ttl(): number | string | undefined;
/**
* Sets the time-to-live
* @param {number|string|undefined} value - The time-to-live in miliseconds or a human-readable format (example '1s' = 1 second, '1h' = 1 hour). If undefined, it will not have a time-to-live.
*/
set ttl(value: number | string | undefined);
/**
* Gets the maximum time-to-live. When set, any TTL that exceeds this value is capped to maxTtl.
* Entries with no TTL will also be capped to maxTtl. Default is `undefined` (no maximum).
* @returns {number|string|undefined} - The maximum TTL in milliseconds, human-readable format, or undefined.
*/
get maxTtl(): number | string | undefined;
/**
* Sets the maximum time-to-live. When set, any TTL that exceeds this value is capped to maxTtl.
* Entries with no TTL will also be capped to maxTtl.
* @param {number|string|undefined} value - The maximum TTL in milliseconds or human-readable format (e.g. '1s', '1h'). If undefined, no maximum is enforced.
*/
set maxTtl(value: number | string | undefined);
/**
* Gets whether to use clone
* @returns {boolean} - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
get useClone(): boolean;
/**
* Sets whether to use clone
* @param {boolean} value - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
set useClone(value: boolean);
/**
* Gets the size of the LRU cache
* @returns {number} - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
get lruSize(): number;
/**
* Sets the size of the LRU cache
* @param {number} value - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
set lruSize(value: number);
/**
* Gets the check interval
* @returns {number} - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
get checkInterval(): number;
/**
* Sets the check interval
* @param {number} value - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
set checkInterval(value: number);
/**
* Gets the size of the cache
* @returns {number} - The size of the cache
*/
get size(): number;
/**
* Gets the statistics of the cache. Statistics track aggregate counters such as `hits`, `misses`,
* `gets`, `sets`, `deletes`, `clears`, `count`, `ksize`, and `vsize`. They are disabled by default;
* enable them via the `stats` option or by setting `cache.stats.enabled = true`.
* @returns {Stats} - The statistics for this CacheableMemory instance
*/
get stats(): Stats$1;
/**
* Gets the number of hash stores
* @returns {number} - The number of hash stores
*/
get storeHashSize(): number;
/**
* Sets the number of hash stores. This will recreate the store and all data will be cleared
* @param {number} value - The number of hash stores
*/
set storeHashSize(value: number);
/**
* Gets the store hash algorithm
* @returns {HashAlgorithm | StoreHashAlgorithmFunction} - The store hash algorithm
*/
get storeHashAlgorithm(): HashAlgorithm$1 | StoreHashAlgorithmFunction;
/**
* Sets the store hash algorithm. This will recreate the store and all data will be cleared
* @param {HashAlgorithm | HashAlgorithmFunction} value - The store hash algorithm
*/
set storeHashAlgorithm(value: HashAlgorithm$1 | StoreHashAlgorithmFunction);
/**
* Gets the keys
* @returns {IterableIterator<string>} - The keys
*/
get keys(): IterableIterator<string>;
/**
* Gets the items
* @returns {IterableIterator<CacheableStoreItem>} - The items
*/
get items(): IterableIterator<CacheableStoreItem$1>;
/**
* Gets the store
* @returns {Array<Map<string, CacheableStoreItem>>} - The store
*/
get store(): Array<Map<string, CacheableStoreItem$1>>;
/**
* Gets the value of the key
* @param {string} key - The key to get the value
* @returns {T | undefined} - The value of the key
*/
get<T>(key: string): T | undefined;
/**
* Gets the values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {T[]} - The values of the keys
*/
getMany<T>(keys: string[]): T[];
/**
* Gets the raw value of the key
* @param {string} key - The key to get the value
* @returns {CacheableStoreItem | undefined} - The raw value of the key
*/
getRaw(key: string): CacheableStoreItem$1 | undefined;
/**
* Gets the raw values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {CacheableStoreItem[]} - The raw values of the keys
*/
getManyRaw(keys: string[]): Array<CacheableStoreItem$1 | undefined>;
/**
* Sets the value of the key
* @param {string} key - The key to set the value
* @param {any} value - The value to set
* @param {number|string|SetOptions} [ttl] - Time to Live - If you set a number it is miliseconds, if you set a string it is a human-readable.
* If you want to set expire directly you can do that by setting the expire property in the SetOptions.
* If you set undefined, it will use the default time-to-live. If both are undefined then it will not have a time-to-live.
* @returns {void}
*/
set(key: string, value: any, ttl?: number | string | SetOptions): void;
/**
* Sets the values of the keys
* @param {CacheableItem[]} items - The items to set
* @returns {void}
*/
setMany(items: CacheableItem$1[]): void;
/**
* Checks if the key exists
* @param {string} key - The key to check
* @returns {boolean} - If true, the key exists. If false, the key does not exist.
*/
has(key: string): boolean;
/**
* @function hasMany
* @param {string[]} keys - The keys to check
* @returns {boolean[]} - If true, the key exists. If false, the key does not exist.
*/
hasMany(keys: string[]): boolean[];
/**
* Take will get the key and delete the entry from cache
* @param {string} key - The key to take
* @returns {T | undefined} - The value of the key
*/
take<T>(key: string): T | undefined;
/**
* TakeMany will get the keys and delete the entries from cache
* @param {string[]} keys - The keys to take
* @returns {T[]} - The values of the keys
*/
takeMany<T>(keys: string[]): T[];
/**
* Delete the key
* @param {string} key - The key to delete
* @returns {void}
*/
delete(key: string): void;
/**
* Delete the keys
* @param {string[]} keys - The keys to delete
* @returns {void}
*/
deleteMany(keys: string[]): void;
/**
* Clear the cache
* @returns {void}
*/
clear(): void;
/**
* Get the store based on the key (internal use)
* @param {string} key - The key to get the store
* @returns {CacheableHashStore} - The store
*/
getStore(key: string): Map<string, CacheableStoreItem$1>;
/**
* Hash the key for which store to go to (internal use)
* @param {string} key - The key to hash
* Available algorithms are: SHA256, SHA1, MD5, and djb2Hash.
* @returns {number} - The hashed key as a number
*/
getKeyStoreHash(key: string): number;
/**
* Clone the value. This is for internal use
* @param {any} value - The value to clone
* @returns {any} - The cloned value
*/
clone(value: any): any;
/**
* Add to the front of the LRU cache. This is for internal use
* @param {string} key - The key to add to the front
* @returns {void}
*/
lruAddToFront(key: string): void;
/**
* Move to the front of the LRU cache. This is for internal use
* @param {string} key - The key to move to the front
* @returns {void}
*/
lruMoveToFront(key: string): void;
/**
* Remove a key from the LRU cache. This is for internal use
* @param {string} key - The key to remove
* @returns {void}
*/
lruRemove(key: string): void;
/**
* Resize the LRU cache. This is for internal use.
* @returns {void}
*/
lruResize(): void;
/**
* Check for expiration. This is for internal use
* @returns {void}
*/
checkExpiration(): void;
/**
* Start the interval check. This is for internal use
* @returns {void}
*/
startIntervalCheck(): void;
/**
* Stop the interval check. This is for internal use
* @returns {void}
*/
stopIntervalCheck(): void;
/**
* Wrap the function for caching
* @param {Function} function_ - The function to wrap
* @param {Object} [options] - The options to wrap
* @returns {Function} - The wrapped function
*/
wrap<T, Arguments extends any[]>(function_: (...arguments_: Arguments) => T, options?: WrapFunctionOptions): (...arguments_: Arguments) => T;
/**
* Gets the value of the key, or computes and stores it on a cache miss. This is the synchronous
* cache-aside helper: if the key is present its value is returned, otherwise `function_` is
* invoked, its result is stored, and that result is returned.
*
* The value is stored using `options.ttl`, falling back to the instance default `ttl`. Because
* the cache is synchronous there is no request coalescing — concurrent callers cannot stampede
* the setter the way they can with an async cache.
* @param {GetOrSetSyncKey} key - The key to get or set. Can also be a function that returns the key.
* @param {() => T} function_ - The function that computes the value on a cache miss.
* @param {GetOrSetFunctionOptions} [options] - Options such as `ttl`, `cacheErrors`, and `throwErrors`.
* @returns {T | undefined} - The cached or freshly computed value
*/
getOrSet<T>(key: GetOrSetSyncKey$1, function_: () => T, options?: GetOrSetFunctionOptions$1): T | undefined;
/**
* Records a single read against the statistics counters. Each read increments `gets` and either
* `hits` or `misses`. No-op when statistics are disabled. This is for internal use.
* @param {boolean} hit - Whether the read found a (non-expired) value
* @returns {void}
*/
private recordRead;
/**
* Decrements the size statistics (`count`, `ksize`, and `vsize`) for an entry that is being removed
* because it expired. Expirations are not counted as `deletes` since they are not user-initiated.
* No-op when statistics are disabled. This is for internal use.
* @param {CacheableStoreItem} item - The expired item being removed from the store
* @returns {void}
*/
private recordExpiration;
private isPrimitive;
private setTtl;
private setMaxTtl;
private hasExpired;
}
export { CacheableMemory, type CacheableMemoryOptions, KeyvCacheableMemory, type KeyvCacheableMemoryOptions, type SetOptions, type StoreHashAlgorithmFunction, createKeyv, defaultStoreHashSize, maximumMapSize };
//#endregion
export { type CacheableItem, CacheableMemory, CacheableMemoryAfterGetItem, CacheableMemoryAfterGetManyItem, CacheableMemoryHookItem, CacheableMemoryHooks, CacheableMemoryOptions, type CacheableStoreItem, type GetOrSetFunctionOptions, type GetOrSetSyncKey, type GetOrSetSyncOptions, HashAlgorithm, KeyvCacheableMemory, type KeyvCacheableMemoryOptions, SetOptions, Stats, type StatsOptions, type StatsSnapshot, StoreHashAlgorithmFunction, createKeyv, defaultStoreHashSize, getOrSetSync, hash, hashToNumber, maximumMapSize };
{
"name": "@cacheable/memory",
"version": "2.0.9",
"version": "2.2.0",
"description": "High Performance In-Memory Cache for Node.js",
"type": "module",
"main": "./dist/index.js",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"main": "./dist/index.cjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.mts",
"exports": {
".": {
"import": {
"types": "./dist/index.d.ts",
"default": "./dist/index.js"
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},

@@ -30,3 +30,3 @@ "require": {

"devDependencies": {
"tsup": "^8.5.1",
"tsdown": "^0.22.0",
"typescript": "^5.9.3"

@@ -38,3 +38,3 @@ },

"keyv": "^5.6.0",
"@cacheable/utils": "^2.4.1"
"@cacheable/utils": "^2.5.0"
},

@@ -61,3 +61,3 @@ "keywords": [

"scripts": {
"build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean",
"build": "rimraf ./dist && tsdown src/index.ts --format cjs,esm --dts --clean",
"lint": "biome check --write --error-on-warnings",

@@ -64,0 +64,0 @@ "test": "pnpm lint && vitest run --coverage",

+177
-0

@@ -32,2 +32,4 @@ [<img align="center" src="https://cacheable.org/logo.svg" alt="Cacheable" />](https://github.com/jaredwray/cacheable)

* [CacheableMemory Performance](#cacheablememory-performance)
* [CacheableMemory Statistics](#cacheablememory-statistics)
* [CacheableMemory Hooks and Events](#cacheablememory-hooks-and-events)
* [CacheableMemory Options](#cacheablememory-options)

@@ -230,5 +232,128 @@ * [CacheableMemory - API](#cacheablememory---api)

## Maximum Time to Live (maxTtl)
You can set a `maxTtl` option to enforce an upper bound on any TTL in the cache. When `maxTtl` is set:
- Any per-entry TTL that exceeds `maxTtl` will be capped to `maxTtl`.
- Entries with no TTL (that would otherwise live indefinitely) will be capped to `maxTtl`.
- The default TTL is still respected if it is within the `maxTtl` limit.
This is useful when you want to guarantee that no cache entry lives longer than a certain duration, regardless of what TTL is passed to individual `set()` calls.
```javascript
import { CacheableMemory } from '@cacheable/memory';
// No entry can live longer than 1 hour
const cache = new CacheableMemory({ maxTtl: '1h' });
cache.set('key1', 'value1', '2h'); // capped to 1 hour
cache.set('key2', 'value2'); // also capped to 1 hour (would otherwise be indefinite)
cache.set('key3', 'value3', '30m'); // 30 minutes is within maxTtl, so it stays as-is
```
You can also set `maxTtl` after construction:
```javascript
const cache = new CacheableMemory();
cache.maxTtl = 5000; // 5 seconds max
cache.maxTtl = '10m'; // 10 minutes max
cache.maxTtl = undefined; // disable maxTtl (no upper bound)
```
## CacheableMemory Statistics
`CacheableMemory` can track runtime statistics using the shared [`Stats`](https://cacheable.org/docs/utils/) implementation from `@cacheable/utils` (the same engine used by `cacheable` and `@cacheable/node-cache`). Statistics are disabled by default. Enable them with the `stats` option or by setting `cache.stats.enabled = true` at any time:
```javascript
import { CacheableMemory } from '@cacheable/memory';
const cache = new CacheableMemory({ stats: true });
cache.set('key', 'value');
cache.get('key'); // hit
cache.get('missing'); // miss
console.log(cache.stats.hits); // 1
console.log(cache.stats.misses); // 1
console.log(cache.stats.gets); // 2
console.log(cache.stats.sets); // 1
console.log(cache.stats.count); // 1
console.log(cache.stats.hitRate); // 0.5
```
The `stats` property exposes the following counters:
* `hits`: The number of reads that found a (non-expired) value.
* `misses`: The number of reads that did not find a value.
* `gets`: The number of read operations. Every key read counts as one get, so `getMany(['a', 'b'])` records two gets.
* `sets`: The number of writes. Every key written counts as one set, including overwrites.
* `deletes`: The number of keys removed via `delete`/`deleteMany`/`take`, as well as keys evicted by the LRU.
* `clears`: The number of times `clear()` was called.
* `count`: The number of keys currently tracked in the cache.
* `ksize`: The estimated byte size of the keys in the cache.
* `vsize`: The estimated byte size of the values in the cache.
* `hitRate` / `missRate`: The ratio of hits / misses to total lookups.
You can get a plain-object snapshot via `cache.stats.toJSON()` and reset all counters with `cache.stats.reset()`.
The `count`, `ksize`, and `vsize` values are kept in sync as entries are added, removed, overwritten, and lazily expired, so they reflect the current contents of the cache. (Expired entries are not counted as `deletes`, since their removal is not user-initiated.) Methods that perform a read internally — such as `has()`, `take()`, and the `wrap()` / `getOrSet()` memoization helpers — flow through `get`/`set`, so they update the statistics as well.
For accurate size counters, enable statistics before populating the cache: `count`/`ksize`/`vsize` only account for entries written while statistics were enabled, and are clamped at `0` so they never go negative if you enable stats after the cache already has data. Changing `storeHashSize` recreates the underlying stores and clears all entries, so the size counters are reset to `0` accordingly.
## CacheableMemory Hooks and Events
`CacheableMemory` extends [`Hookified`](https://github.com/jaredwray/hookified), so you can register handlers that run around cache operations via the `CacheableMemoryHooks` enum and the `onHook()` method:
* `BEFORE_SET`: Called before `set()`. The handler receives `{ key, value, ttl }` and can reassign any of them to change what gets stored.
* `AFTER_SET`: Called after `set()` with the (possibly modified) `{ key, value, ttl }`.
* `BEFORE_SET_MANY`: Called before `setMany()` with the array of `CacheableItem`s. Items can be mutated.
* `AFTER_SET_MANY`: Called after `setMany()` with the array of items.
* `BEFORE_GET`: Called before `get()` with the `key`.
* `AFTER_GET`: Called after `get()` with `{ key, result }` (`result` is `undefined` on a cache miss).
* `BEFORE_GET_MANY`: Called before `getMany()` with the array of `keys`.
* `AFTER_GET_MANY`: Called after `getMany()` with `{ keys, result }`.
* `BEFORE_DELETE`: Called before `delete()` with the `key`.
* `AFTER_DELETE`: Called after `delete()` with the `key`.
* `BEFORE_DELETE_MANY`: Called before `deleteMany()` with the array of `keys`.
* `AFTER_DELETE_MANY`: Called after `deleteMany()` with the array of `keys`.
* `BEFORE_CLEAR`: Called before `clear()`.
* `AFTER_CLEAR`: Called after `clear()`.
An example of how to use these hooks:
```javascript
import { CacheableMemory, CacheableMemoryHooks } from '@cacheable/memory';
const cache = new CacheableMemory();
cache.onHook(CacheableMemoryHooks.BEFORE_SET, (item) => {
console.log(`before set: ${item.key} ${item.value}`);
});
cache.onHook(CacheableMemoryHooks.AFTER_GET, (item) => {
console.log(`after get: ${item.key} = ${item.result}`);
});
```
A `BEFORE_SET` handler can change the `key`, `value`, or `ttl` before the entry is stored. The `ttl` accepts a number (milliseconds), a [shorthand string](#shorthand-for-time-to-live-ttl), or a `SetOptions` object (`{ ttl, expire }`):
```javascript
cache.onHook(CacheableMemoryHooks.BEFORE_SET, (item) => {
item.key = `user:${item.key}`;
item.ttl = '1h';
});
```
Hooks are dispatched synchronously via `hookSync`, which **skips `async` handler functions entirely** — an `async` handler will not run at all (not merely run un-awaited), so register only synchronous handlers.
> **TypeScript:** the hook payload types are exported so you can annotate your handlers — `CacheableMemoryHookItem`, `CacheableMemoryAfterGetItem`, and `CacheableMemoryAfterGetManyItem`. For example:
> ```ts
> cache.onHook(CacheableMemoryHooks.BEFORE_SET, (item: CacheableMemoryHookItem) => {
> item.ttl = '1h';
> });
> ```
## CacheableMemory Options
* `ttl`: The time to live for the cache in milliseconds. Default is `undefined` which is means indefinitely.
* `maxTtl`: The maximum time to live for any cache entry. When set, TTLs exceeding this value are capped. Default is `undefined` (no maximum).
* `useClones`: If the cache should use clones for the values. Default is `true`.

@@ -239,2 +364,3 @@ * `lruSize`: The size of the LRU cache. Default is `0`, which disables the LRU cache (no LRU eviction is performed). Maximum is `16,777,216 (2^24)`.

* `storeHashAlgorithm`: The hashing algorithm to use for the cache. Default is `djb2`. Supported: DJB2, FNV1, MURMER, CRC32.
* `stats`: Whether to track runtime statistics (`hits`, `misses`, `gets`, `sets`, `deletes`, `clears`, `count`, `ksize`, `vsize`). Default is `false`.

@@ -257,3 +383,5 @@ ## CacheableMemory - API

* `clear()`: Clears the cache.
* `onHook(hook, handler)`: Registers a handler for a `CacheableMemoryHooks` event. See [CacheableMemory Hooks and Events](#cacheablememory-hooks-and-events).
* `ttl`: The default time to live for the cache in milliseconds. Default is `undefined` which is disabled.
* `maxTtl`: The maximum time to live for any cache entry. When set, TTLs exceeding this value are capped. Default is `undefined` (no maximum).
* `useClones`: If the cache should use clones for the values. Default is `true`.

@@ -265,2 +393,3 @@ * `lruSize`: The size of the LRU cache. Default is `0`, which disables the LRU cache (no LRU eviction is performed). Maximum is `16,777,216 (2^24)`.

* `storeHashAlgorithm`: The hashing algorithm to use for the cache. Default is `djb2`. Supported: DJB2, FNV1, MURMER, CRC32.
* `stats`: The statistics for this instance which includes `hits`, `misses`, `gets`, `sets`, `deletes`, `clears`, `count`, `vsize`, and `ksize`. Disabled by default; enable via the `stats` option or `cache.stats.enabled = true`.
* `keys`: Get the keys in the cache. Not able to be set.

@@ -340,2 +469,50 @@ * `items`: Get the items in the cache as `CacheableStoreItem` example `{ key, value, expires? }`.

# Get Or Set Memoization Function
`CacheableMemory` also has a `getOrSet` method that implements the cache-aside pattern in a single synchronous call. It attempts to retrieve a value from the cache, and if it is not found it calls the provided function to compute the value, stores it, and returns it. This is the synchronous counterpart to the `getOrSet` method on `cacheable` and is backed by `getOrSetSync` from [@cacheable/utils](https://cacheable.org/docs/utils/).
```javascript
import { CacheableMemory } from '@cacheable/memory';
const cache = new CacheableMemory();
const getUser = () => ({ id: 1, name: 'Alice' });
// First call computes the value and stores it
const user1 = cache.getOrSet('user:1', getUser, { ttl: '1h' });
// Second call returns the cached value without calling getUser again
const user2 = cache.getOrSet('user:1', getUser, { ttl: '1h' });
console.log(user1); // { id: 1, name: 'Alice' }
console.log(user1 === user2); // true (served from cache)
```
The third argument accepts the following options:
```typescript
export type GetOrSetFunctionOptions = {
ttl?: number | string;
cacheErrors?: boolean;
throwErrors?: boolean | 'function' | 'store';
};
```
* `ttl`: The time to live for the stored value. If omitted it falls back to the instance default `ttl`. Accepts milliseconds or the [shorthand](#shorthand-for-time-to-live-ttl) format such as `1h`.
* `cacheErrors`: When `true`, errors thrown by the function are cached so the function is not retried until the entry expires. Default is `false`.
* `throwErrors`: Controls whether errors are rethrown. `false` (default) emits errors on the `error` event and returns `undefined`; `true` rethrows any error; `'function'` only rethrows errors from the provided function; `'store'` only rethrows errors from reading/writing the cache.
Because `CacheableMemory` is synchronous there is no request coalescing — synchronous code runs to completion without interleaving, so concurrent callers cannot stampede the setter the way they can with the async `getOrSet` on `cacheable`.
You can also pass a function to compute the key:
```javascript
import { CacheableMemory, GetOrSetSyncKey } from '@cacheable/memory';
const cache = new CacheableMemory();
const generateKey: GetOrSetSyncKey = (options) => `user:${options?.ttl}`;
const value = cache.getOrSet(generateKey, () => Math.random() * 100, { ttl: '1h' });
```
# How to Contribute

@@ -342,0 +519,0 @@

import { HashAlgorithm, CacheableStoreItem, CacheableItem, WrapFunctionOptions } from '@cacheable/utils';
export { CacheableItem, CacheableStoreItem, HashAlgorithm, hash, hashToNumber } from '@cacheable/utils';
import { Hookified } from 'hookified';
import { KeyvStoreAdapter, StoredData, Keyv } from 'keyv';
type KeyvCacheableMemoryOptions = CacheableMemoryOptions & {
namespace?: string;
};
declare class KeyvCacheableMemory implements KeyvStoreAdapter {
opts: CacheableMemoryOptions;
private readonly _defaultCache;
private readonly _nCache;
private _namespace?;
constructor(options?: KeyvCacheableMemoryOptions);
get namespace(): string | undefined;
set namespace(value: string | undefined);
get store(): CacheableMemory;
get<Value>(key: string): Promise<StoredData<Value> | undefined>;
getMany<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>>;
set(key: string, value: any, ttl?: number): Promise<void>;
setMany(values: Array<{
key: string;
value: any;
ttl?: number;
}>): Promise<void>;
delete(key: string): Promise<boolean>;
deleteMany?(key: string[]): Promise<boolean>;
clear(): Promise<void>;
has?(key: string): Promise<boolean>;
on(event: string, listener: (...arguments_: any[]) => void): this;
getStore(namespace?: string): CacheableMemory;
}
/**
* Creates a new Keyv instance with a new KeyvCacheableMemory store. This also removes the serialize/deserialize methods from the Keyv instance for optimization.
* @param options
* @returns
*/
declare function createKeyv(options?: KeyvCacheableMemoryOptions): Keyv;
type StoreHashAlgorithmFunction = (key: string, storeHashSize: number) => number;
/**
* @typedef {Object} CacheableMemoryOptions
* @property {number|string} [ttl] - Time to Live - If you set a number it is miliseconds, if you set a string it is a human-readable
* format such as `1s` for 1 second or `1h` for 1 hour. Setting undefined means that it will use the default time-to-live. If both are
* undefined then it will not have a time-to-live.
* @property {boolean} [useClone] - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
* @property {number} [lruSize] - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
* @property {number} [checkInterval] - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
* @property {number} [storeHashSize] - The number of how many Map stores we have for the hash. Default is 10.
*/
type CacheableMemoryOptions = {
ttl?: number | string;
useClone?: boolean;
lruSize?: number;
checkInterval?: number;
storeHashSize?: number;
storeHashAlgorithm?: HashAlgorithm | ((key: string, storeHashSize: number) => number);
};
type SetOptions = {
ttl?: number | string;
expire?: number | Date;
};
declare const defaultStoreHashSize = 16;
declare const maximumMapSize = 16777216;
declare class CacheableMemory extends Hookified {
private _lru;
private _storeHashSize;
private _storeHashAlgorithm;
private _store;
private _ttl;
private _useClone;
private _lruSize;
private _checkInterval;
private _interval;
/**
* @constructor
* @param {CacheableMemoryOptions} [options] - The options for the CacheableMemory
*/
constructor(options?: CacheableMemoryOptions);
/**
* Gets the time-to-live
* @returns {number|string|undefined} - The time-to-live in miliseconds or a human-readable format. If undefined, it will not have a time-to-live.
*/
get ttl(): number | string | undefined;
/**
* Sets the time-to-live
* @param {number|string|undefined} value - The time-to-live in miliseconds or a human-readable format (example '1s' = 1 second, '1h' = 1 hour). If undefined, it will not have a time-to-live.
*/
set ttl(value: number | string | undefined);
/**
* Gets whether to use clone
* @returns {boolean} - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
get useClone(): boolean;
/**
* Sets whether to use clone
* @param {boolean} value - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
set useClone(value: boolean);
/**
* Gets the size of the LRU cache
* @returns {number} - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
get lruSize(): number;
/**
* Sets the size of the LRU cache
* @param {number} value - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
set lruSize(value: number);
/**
* Gets the check interval
* @returns {number} - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
get checkInterval(): number;
/**
* Sets the check interval
* @param {number} value - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
set checkInterval(value: number);
/**
* Gets the size of the cache
* @returns {number} - The size of the cache
*/
get size(): number;
/**
* Gets the number of hash stores
* @returns {number} - The number of hash stores
*/
get storeHashSize(): number;
/**
* Sets the number of hash stores. This will recreate the store and all data will be cleared
* @param {number} value - The number of hash stores
*/
set storeHashSize(value: number);
/**
* Gets the store hash algorithm
* @returns {HashAlgorithm | StoreHashAlgorithmFunction} - The store hash algorithm
*/
get storeHashAlgorithm(): HashAlgorithm | StoreHashAlgorithmFunction;
/**
* Sets the store hash algorithm. This will recreate the store and all data will be cleared
* @param {HashAlgorithm | HashAlgorithmFunction} value - The store hash algorithm
*/
set storeHashAlgorithm(value: HashAlgorithm | StoreHashAlgorithmFunction);
/**
* Gets the keys
* @returns {IterableIterator<string>} - The keys
*/
get keys(): IterableIterator<string>;
/**
* Gets the items
* @returns {IterableIterator<CacheableStoreItem>} - The items
*/
get items(): IterableIterator<CacheableStoreItem>;
/**
* Gets the store
* @returns {Array<Map<string, CacheableStoreItem>>} - The store
*/
get store(): Array<Map<string, CacheableStoreItem>>;
/**
* Gets the value of the key
* @param {string} key - The key to get the value
* @returns {T | undefined} - The value of the key
*/
get<T>(key: string): T | undefined;
/**
* Gets the values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {T[]} - The values of the keys
*/
getMany<T>(keys: string[]): T[];
/**
* Gets the raw value of the key
* @param {string} key - The key to get the value
* @returns {CacheableStoreItem | undefined} - The raw value of the key
*/
getRaw(key: string): CacheableStoreItem | undefined;
/**
* Gets the raw values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {CacheableStoreItem[]} - The raw values of the keys
*/
getManyRaw(keys: string[]): Array<CacheableStoreItem | undefined>;
/**
* Sets the value of the key
* @param {string} key - The key to set the value
* @param {any} value - The value to set
* @param {number|string|SetOptions} [ttl] - Time to Live - If you set a number it is miliseconds, if you set a string it is a human-readable.
* If you want to set expire directly you can do that by setting the expire property in the SetOptions.
* If you set undefined, it will use the default time-to-live. If both are undefined then it will not have a time-to-live.
* @returns {void}
*/
set(key: string, value: any, ttl?: number | string | SetOptions): void;
/**
* Sets the values of the keys
* @param {CacheableItem[]} items - The items to set
* @returns {void}
*/
setMany(items: CacheableItem[]): void;
/**
* Checks if the key exists
* @param {string} key - The key to check
* @returns {boolean} - If true, the key exists. If false, the key does not exist.
*/
has(key: string): boolean;
/**
* @function hasMany
* @param {string[]} keys - The keys to check
* @returns {boolean[]} - If true, the key exists. If false, the key does not exist.
*/
hasMany(keys: string[]): boolean[];
/**
* Take will get the key and delete the entry from cache
* @param {string} key - The key to take
* @returns {T | undefined} - The value of the key
*/
take<T>(key: string): T | undefined;
/**
* TakeMany will get the keys and delete the entries from cache
* @param {string[]} keys - The keys to take
* @returns {T[]} - The values of the keys
*/
takeMany<T>(keys: string[]): T[];
/**
* Delete the key
* @param {string} key - The key to delete
* @returns {void}
*/
delete(key: string): void;
/**
* Delete the keys
* @param {string[]} keys - The keys to delete
* @returns {void}
*/
deleteMany(keys: string[]): void;
/**
* Clear the cache
* @returns {void}
*/
clear(): void;
/**
* Get the store based on the key (internal use)
* @param {string} key - The key to get the store
* @returns {CacheableHashStore} - The store
*/
getStore(key: string): Map<string, CacheableStoreItem>;
/**
* Hash the key for which store to go to (internal use)
* @param {string} key - The key to hash
* Available algorithms are: SHA256, SHA1, MD5, and djb2Hash.
* @returns {number} - The hashed key as a number
*/
getKeyStoreHash(key: string): number;
/**
* Clone the value. This is for internal use
* @param {any} value - The value to clone
* @returns {any} - The cloned value
*/
clone(value: any): any;
/**
* Add to the front of the LRU cache. This is for internal use
* @param {string} key - The key to add to the front
* @returns {void}
*/
lruAddToFront(key: string): void;
/**
* Move to the front of the LRU cache. This is for internal use
* @param {string} key - The key to move to the front
* @returns {void}
*/
lruMoveToFront(key: string): void;
/**
* Remove a key from the LRU cache. This is for internal use
* @param {string} key - The key to remove
* @returns {void}
*/
lruRemove(key: string): void;
/**
* Resize the LRU cache. This is for internal use.
* @returns {void}
*/
lruResize(): void;
/**
* Check for expiration. This is for internal use
* @returns {void}
*/
checkExpiration(): void;
/**
* Start the interval check. This is for internal use
* @returns {void}
*/
startIntervalCheck(): void;
/**
* Stop the interval check. This is for internal use
* @returns {void}
*/
stopIntervalCheck(): void;
/**
* Wrap the function for caching
* @param {Function} function_ - The function to wrap
* @param {Object} [options] - The options to wrap
* @returns {Function} - The wrapped function
*/
wrap<T, Arguments extends any[]>(function_: (...arguments_: Arguments) => T, options?: WrapFunctionOptions): (...arguments_: Arguments) => T;
private isPrimitive;
private setTtl;
private hasExpired;
}
export { CacheableMemory, type CacheableMemoryOptions, KeyvCacheableMemory, type KeyvCacheableMemoryOptions, type SetOptions, type StoreHashAlgorithmFunction, createKeyv, defaultStoreHashSize, maximumMapSize };
// src/index.ts
import {
HashAlgorithm,
hashToNumberSync,
shorthandToTime,
wrapSync
} from "@cacheable/utils";
import { Hookified } from "hookified";
// src/memory-lru.ts
var ListNode = class {
value;
prev = void 0;
next = void 0;
constructor(value) {
this.value = value;
}
};
var DoublyLinkedList = class {
head = void 0;
tail = void 0;
nodesMap = /* @__PURE__ */ new Map();
// Add a new node to the front (most recently used)
addToFront(value) {
const newNode = new ListNode(value);
if (this.head) {
newNode.next = this.head;
this.head.prev = newNode;
this.head = newNode;
} else {
this.head = this.tail = newNode;
}
this.nodesMap.set(value, newNode);
}
// Move an existing node to the front (most recently used)
moveToFront(value) {
const node = this.nodesMap.get(value);
if (!node || this.head === node) {
return;
}
if (node.prev) {
node.prev.next = node.next;
}
if (node.next) {
node.next.prev = node.prev;
}
if (node === this.tail) {
this.tail = node.prev;
}
node.prev = void 0;
node.next = this.head;
if (this.head) {
this.head.prev = node;
}
this.head = node;
this.tail ??= node;
}
// Get the oldest node (tail)
getOldest() {
return this.tail ? this.tail.value : void 0;
}
// Remove the oldest node (tail)
removeOldest() {
if (!this.tail) {
return void 0;
}
const oldValue = this.tail.value;
if (this.tail.prev) {
this.tail = this.tail.prev;
this.tail.next = void 0;
} else {
this.head = this.tail = void 0;
}
this.nodesMap.delete(oldValue);
return oldValue;
}
// Remove a specific node by value
remove(value) {
const node = this.nodesMap.get(value);
if (!node) {
return false;
}
if (node.prev) {
node.prev.next = node.next;
} else {
this.head = node.next;
if (this.head) {
this.head.prev = void 0;
}
}
if (node.next) {
node.next.prev = node.prev;
} else {
this.tail = node.prev;
if (this.tail) {
this.tail.next = void 0;
}
}
this.nodesMap.delete(value);
return true;
}
get size() {
return this.nodesMap.size;
}
};
// src/index.ts
import {
HashAlgorithm as HashAlgorithm2,
hash,
hashToNumber
} from "@cacheable/utils";
// src/keyv-memory.ts
import { Keyv } from "keyv";
var KeyvCacheableMemory = class {
opts = {
ttl: 0,
useClone: true,
lruSize: 0,
checkInterval: 0
};
_defaultCache = new CacheableMemory();
_nCache = /* @__PURE__ */ new Map();
_namespace;
constructor(options) {
if (options) {
this.opts = options;
this._defaultCache = new CacheableMemory(options);
if (options.namespace) {
this._namespace = options.namespace;
this._nCache.set(this._namespace, new CacheableMemory(options));
}
}
}
get namespace() {
return this._namespace;
}
set namespace(value) {
this._namespace = value;
}
get store() {
return this.getStore(this._namespace);
}
async get(key) {
const result = this.getStore(this._namespace).get(key);
if (result) {
return result;
}
return void 0;
}
async getMany(keys) {
const result = this.getStore(this._namespace).getMany(keys);
return result;
}
// biome-ignore lint/suspicious/noExplicitAny: type format
async set(key, value, ttl) {
this.getStore(this._namespace).set(key, value, ttl);
}
async setMany(values) {
this.getStore(this._namespace).setMany(values);
}
async delete(key) {
this.getStore(this._namespace).delete(key);
return true;
}
async deleteMany(key) {
this.getStore(this._namespace).deleteMany(key);
return true;
}
async clear() {
this.getStore(this._namespace).clear();
}
async has(key) {
return this.getStore(this._namespace).has(key);
}
// biome-ignore lint/suspicious/noExplicitAny: type format
on(event, listener) {
this.getStore(this._namespace).on(event, listener);
return this;
}
getStore(namespace) {
if (!namespace) {
return this._defaultCache;
}
if (!this._nCache.has(namespace)) {
this._nCache.set(namespace, new CacheableMemory(this.opts));
}
return this._nCache.get(namespace);
}
};
function createKeyv(options) {
const store = new KeyvCacheableMemory(options);
const namespace = options?.namespace;
let ttl;
if (options?.ttl && Number.isInteger(options.ttl)) {
ttl = options?.ttl;
}
const keyv = new Keyv({ store, namespace, ttl });
keyv.serialize = void 0;
keyv.deserialize = void 0;
return keyv;
}
// src/index.ts
var defaultStoreHashSize = 16;
var maximumMapSize = 16777216;
var CacheableMemory = class extends Hookified {
_lru = new DoublyLinkedList();
_storeHashSize = defaultStoreHashSize;
_storeHashAlgorithm = HashAlgorithm.DJB2;
// Default is djb2Hash
_store = Array.from(
{ length: this._storeHashSize },
() => /* @__PURE__ */ new Map()
);
_ttl;
// Turned off by default
_useClone = true;
// Turned on by default
_lruSize = 0;
// Turned off by default
_checkInterval = 0;
// Turned off by default
_interval = 0;
// Turned off by default
/**
* @constructor
* @param {CacheableMemoryOptions} [options] - The options for the CacheableMemory
*/
constructor(options) {
super();
if (options?.ttl) {
this.setTtl(options.ttl);
}
if (options?.useClone !== void 0) {
this._useClone = options.useClone;
}
if (options?.storeHashSize && options.storeHashSize > 0) {
this._storeHashSize = options.storeHashSize;
}
if (options?.lruSize) {
if (options.lruSize > maximumMapSize) {
this.emit(
"error",
new Error(
`LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`
)
);
} else {
this._lruSize = options.lruSize;
}
}
if (options?.checkInterval) {
this._checkInterval = options.checkInterval;
}
if (options?.storeHashAlgorithm) {
this._storeHashAlgorithm = options.storeHashAlgorithm;
}
this._store = Array.from(
{ length: this._storeHashSize },
() => /* @__PURE__ */ new Map()
);
this.startIntervalCheck();
}
/**
* Gets the time-to-live
* @returns {number|string|undefined} - The time-to-live in miliseconds or a human-readable format. If undefined, it will not have a time-to-live.
*/
get ttl() {
return this._ttl;
}
/**
* Sets the time-to-live
* @param {number|string|undefined} value - The time-to-live in miliseconds or a human-readable format (example '1s' = 1 second, '1h' = 1 hour). If undefined, it will not have a time-to-live.
*/
set ttl(value) {
this.setTtl(value);
}
/**
* Gets whether to use clone
* @returns {boolean} - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
get useClone() {
return this._useClone;
}
/**
* Sets whether to use clone
* @param {boolean} value - If true, it will clone the value before returning it. If false, it will return the value directly. Default is true.
*/
set useClone(value) {
this._useClone = value;
}
/**
* Gets the size of the LRU cache
* @returns {number} - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
get lruSize() {
return this._lruSize;
}
/**
* Sets the size of the LRU cache
* @param {number} value - The size of the LRU cache. If set to 0, it will not use LRU cache. Default is 0. If you are using LRU then the limit is based on Map() size 17mm.
*/
set lruSize(value) {
if (value > maximumMapSize) {
this.emit(
"error",
new Error(
`LRU size cannot be larger than ${maximumMapSize} due to Map limitations.`
)
);
return;
}
this._lruSize = value;
if (this._lruSize === 0) {
this._lru = new DoublyLinkedList();
return;
}
this.lruResize();
}
/**
* Gets the check interval
* @returns {number} - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
get checkInterval() {
return this._checkInterval;
}
/**
* Sets the check interval
* @param {number} value - The interval to check for expired items. If set to 0, it will not check for expired items. Default is 0.
*/
set checkInterval(value) {
this._checkInterval = value;
}
/**
* Gets the size of the cache
* @returns {number} - The size of the cache
*/
get size() {
let size = 0;
for (const store of this._store) {
size += store.size;
}
return size;
}
/**
* Gets the number of hash stores
* @returns {number} - The number of hash stores
*/
get storeHashSize() {
return this._storeHashSize;
}
/**
* Sets the number of hash stores. This will recreate the store and all data will be cleared
* @param {number} value - The number of hash stores
*/
set storeHashSize(value) {
if (value === this._storeHashSize) {
return;
}
this._storeHashSize = value;
this._store = Array.from(
{ length: this._storeHashSize },
() => /* @__PURE__ */ new Map()
);
}
/**
* Gets the store hash algorithm
* @returns {HashAlgorithm | StoreHashAlgorithmFunction} - The store hash algorithm
*/
get storeHashAlgorithm() {
return this._storeHashAlgorithm;
}
/**
* Sets the store hash algorithm. This will recreate the store and all data will be cleared
* @param {HashAlgorithm | HashAlgorithmFunction} value - The store hash algorithm
*/
set storeHashAlgorithm(value) {
this._storeHashAlgorithm = value;
}
/**
* Gets the keys
* @returns {IterableIterator<string>} - The keys
*/
get keys() {
const keys = [];
for (const store of this._store) {
for (const key of store.keys()) {
const item = store.get(key);
if (item && this.hasExpired(item)) {
store.delete(key);
this.lruRemove(key);
continue;
}
keys.push(key);
}
}
return keys.values();
}
/**
* Gets the items
* @returns {IterableIterator<CacheableStoreItem>} - The items
*/
get items() {
const items = [];
for (const store of this._store) {
for (const item of store.values()) {
if (this.hasExpired(item)) {
store.delete(item.key);
this.lruRemove(item.key);
continue;
}
items.push(item);
}
}
return items.values();
}
/**
* Gets the store
* @returns {Array<Map<string, CacheableStoreItem>>} - The store
*/
get store() {
return this._store;
}
/**
* Gets the value of the key
* @param {string} key - The key to get the value
* @returns {T | undefined} - The value of the key
*/
get(key) {
const store = this.getStore(key);
const item = store.get(key);
if (!item) {
return void 0;
}
if (item.expires && Date.now() > item.expires) {
store.delete(key);
this.lruRemove(key);
return void 0;
}
this.lruMoveToFront(key);
if (!this._useClone) {
return item.value;
}
return this.clone(item.value);
}
/**
* Gets the values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {T[]} - The values of the keys
*/
getMany(keys) {
const result = [];
for (const key of keys) {
result.push(this.get(key));
}
return result;
}
/**
* Gets the raw value of the key
* @param {string} key - The key to get the value
* @returns {CacheableStoreItem | undefined} - The raw value of the key
*/
getRaw(key) {
const store = this.getStore(key);
const item = store.get(key);
if (!item) {
return void 0;
}
if (item.expires && item.expires && Date.now() > item.expires) {
store.delete(key);
this.lruRemove(key);
return void 0;
}
this.lruMoveToFront(key);
return item;
}
/**
* Gets the raw values of the keys
* @param {string[]} keys - The keys to get the values
* @returns {CacheableStoreItem[]} - The raw values of the keys
*/
getManyRaw(keys) {
const result = [];
for (const key of keys) {
result.push(this.getRaw(key));
}
return result;
}
/**
* Sets the value of the key
* @param {string} key - The key to set the value
* @param {any} value - The value to set
* @param {number|string|SetOptions} [ttl] - Time to Live - If you set a number it is miliseconds, if you set a string it is a human-readable.
* If you want to set expire directly you can do that by setting the expire property in the SetOptions.
* If you set undefined, it will use the default time-to-live. If both are undefined then it will not have a time-to-live.
* @returns {void}
*/
set(key, value, ttl) {
const store = this.getStore(key);
let expires;
if (ttl !== void 0 || this._ttl !== void 0) {
if (typeof ttl === "object") {
if (ttl.expire) {
expires = typeof ttl.expire === "number" ? ttl.expire : ttl.expire.getTime();
}
if (ttl.ttl) {
const finalTtl = shorthandToTime(ttl.ttl);
if (finalTtl !== void 0) {
expires = finalTtl;
}
}
} else {
const finalTtl = shorthandToTime(ttl ?? this._ttl);
if (finalTtl !== void 0) {
expires = finalTtl;
}
}
}
if (this._lruSize > 0) {
if (store.has(key)) {
this.lruMoveToFront(key);
} else {
this.lruAddToFront(key);
if (this._lru.size > this._lruSize) {
const oldestKey = this._lru.getOldest();
if (oldestKey) {
this._lru.removeOldest();
this.delete(oldestKey);
}
}
}
}
const item = { key, value, expires };
store.set(key, item);
}
/**
* Sets the values of the keys
* @param {CacheableItem[]} items - The items to set
* @returns {void}
*/
setMany(items) {
for (const item of items) {
this.set(item.key, item.value, item.ttl);
}
}
/**
* Checks if the key exists
* @param {string} key - The key to check
* @returns {boolean} - If true, the key exists. If false, the key does not exist.
*/
has(key) {
const item = this.get(key);
return Boolean(item);
}
/**
* @function hasMany
* @param {string[]} keys - The keys to check
* @returns {boolean[]} - If true, the key exists. If false, the key does not exist.
*/
hasMany(keys) {
const result = [];
for (const key of keys) {
const item = this.get(key);
result.push(Boolean(item));
}
return result;
}
/**
* Take will get the key and delete the entry from cache
* @param {string} key - The key to take
* @returns {T | undefined} - The value of the key
*/
take(key) {
const item = this.get(key);
if (!item) {
return void 0;
}
this.delete(key);
return item;
}
/**
* TakeMany will get the keys and delete the entries from cache
* @param {string[]} keys - The keys to take
* @returns {T[]} - The values of the keys
*/
takeMany(keys) {
const result = [];
for (const key of keys) {
result.push(this.take(key));
}
return result;
}
/**
* Delete the key
* @param {string} key - The key to delete
* @returns {void}
*/
delete(key) {
const store = this.getStore(key);
store.delete(key);
this.lruRemove(key);
}
/**
* Delete the keys
* @param {string[]} keys - The keys to delete
* @returns {void}
*/
deleteMany(keys) {
for (const key of keys) {
this.delete(key);
}
}
/**
* Clear the cache
* @returns {void}
*/
clear() {
this._store = Array.from(
{ length: this._storeHashSize },
() => /* @__PURE__ */ new Map()
);
this._lru = new DoublyLinkedList();
}
/**
* Get the store based on the key (internal use)
* @param {string} key - The key to get the store
* @returns {CacheableHashStore} - The store
*/
getStore(key) {
const hash2 = this.getKeyStoreHash(key);
this._store[hash2] ||= /* @__PURE__ */ new Map();
return this._store[hash2];
}
/**
* Hash the key for which store to go to (internal use)
* @param {string} key - The key to hash
* Available algorithms are: SHA256, SHA1, MD5, and djb2Hash.
* @returns {number} - The hashed key as a number
*/
getKeyStoreHash(key) {
if (this._store.length === 1) {
return 0;
}
if (typeof this._storeHashAlgorithm === "function") {
return this._storeHashAlgorithm(key, this._storeHashSize);
}
const storeHashSize = this._storeHashSize - 1;
const hash2 = hashToNumberSync(key, {
min: 0,
max: storeHashSize,
algorithm: this._storeHashAlgorithm
});
return hash2;
}
/**
* Clone the value. This is for internal use
* @param {any} value - The value to clone
* @returns {any} - The cloned value
*/
// biome-ignore lint/suspicious/noExplicitAny: type format
clone(value) {
if (this.isPrimitive(value)) {
return value;
}
return structuredClone(value);
}
/**
* Add to the front of the LRU cache. This is for internal use
* @param {string} key - The key to add to the front
* @returns {void}
*/
lruAddToFront(key) {
if (this._lruSize === 0) {
return;
}
this._lru.addToFront(key);
}
/**
* Move to the front of the LRU cache. This is for internal use
* @param {string} key - The key to move to the front
* @returns {void}
*/
lruMoveToFront(key) {
if (this._lruSize === 0) {
return;
}
this._lru.moveToFront(key);
}
/**
* Remove a key from the LRU cache. This is for internal use
* @param {string} key - The key to remove
* @returns {void}
*/
lruRemove(key) {
if (this._lruSize === 0) {
return;
}
this._lru.remove(key);
}
/**
* Resize the LRU cache. This is for internal use.
* @returns {void}
*/
lruResize() {
while (this._lru.size > this._lruSize) {
const oldestKey = this._lru.getOldest();
if (oldestKey) {
this._lru.removeOldest();
this.delete(oldestKey);
}
}
}
/**
* Check for expiration. This is for internal use
* @returns {void}
*/
checkExpiration() {
for (const store of this._store) {
for (const item of store.values()) {
if (item.expires && Date.now() > item.expires) {
store.delete(item.key);
this.lruRemove(item.key);
}
}
}
}
/**
* Start the interval check. This is for internal use
* @returns {void}
*/
startIntervalCheck() {
if (this._checkInterval > 0) {
if (this._interval) {
clearInterval(this._interval);
}
this._interval = setInterval(() => {
this.checkExpiration();
}, this._checkInterval).unref();
}
}
/**
* Stop the interval check. This is for internal use
* @returns {void}
*/
stopIntervalCheck() {
if (this._interval) {
clearInterval(this._interval);
}
this._interval = 0;
this._checkInterval = 0;
}
/**
* Wrap the function for caching
* @param {Function} function_ - The function to wrap
* @param {Object} [options] - The options to wrap
* @returns {Function} - The wrapped function
*/
// biome-ignore lint/suspicious/noExplicitAny: type format
wrap(function_, options) {
const wrapOptions = {
ttl: options?.ttl ?? this._ttl,
keyPrefix: options?.keyPrefix,
createKey: options?.createKey,
cache: this
};
return wrapSync(function_, wrapOptions);
}
// biome-ignore lint/suspicious/noExplicitAny: type format
isPrimitive(value) {
const result = false;
if (value === null || value === void 0) {
return true;
}
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
return true;
}
return result;
}
setTtl(ttl) {
if (typeof ttl === "string" || ttl === void 0) {
this._ttl = ttl;
} else if (ttl > 0) {
this._ttl = ttl;
} else {
this._ttl = void 0;
}
}
hasExpired(item) {
if (item.expires && Date.now() > item.expires) {
return true;
}
return false;
}
};
export {
CacheableMemory,
HashAlgorithm2 as HashAlgorithm,
KeyvCacheableMemory,
createKeyv,
defaultStoreHashSize,
hash,
hashToNumber,
maximumMapSize
};
/* v8 ignore next -- @preserve */