+1
-1908
@@ -1,1908 +0,1 @@ | ||
| "use strict"; | ||
| var __create = Object.create; | ||
| var __defProp = Object.defineProperty; | ||
| var __getOwnPropDesc = Object.getOwnPropertyDescriptor; | ||
| var __getOwnPropNames = Object.getOwnPropertyNames; | ||
| var __getProtoOf = Object.getPrototypeOf; | ||
| var __hasOwnProp = Object.prototype.hasOwnProperty; | ||
| var __export = (target, all) => { | ||
| for (var name in all) | ||
| __defProp(target, name, { get: all[name], enumerable: true }); | ||
| }; | ||
| var __copyProps = (to, from, except, desc) => { | ||
| if (from && typeof from === "object" || typeof from === "function") { | ||
| for (let key of __getOwnPropNames(from)) | ||
| if (!__hasOwnProp.call(to, key) && key !== except) | ||
| __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); | ||
| } | ||
| return to; | ||
| }; | ||
| var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( | ||
| // If the importer is in node compatibility mode or this is not an ESM | ||
| // file that has been converted to a CommonJS file using a Babel- | ||
| // compatible transform (i.e. "__esModule" has not been set), then set | ||
| // "default" to the CommonJS "module.exports" for node compatibility. | ||
| isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, | ||
| mod | ||
| )); | ||
| var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); | ||
| // src/index.ts | ||
| var index_exports = {}; | ||
| __export(index_exports, { | ||
| Cacheable: () => Cacheable, | ||
| CacheableEvents: () => CacheableEvents, | ||
| CacheableHooks: () => CacheableHooks, | ||
| CacheableMemory: () => CacheableMemory, | ||
| CacheableStats: () => CacheableStats, | ||
| Keyv: () => import_keyv3.Keyv, | ||
| KeyvCacheableMemory: () => KeyvCacheableMemory, | ||
| KeyvHooks: () => import_keyv3.KeyvHooks, | ||
| createKeyv: () => createKeyv, | ||
| getOrSet: () => getOrSet, | ||
| shorthandToMilliseconds: () => shorthandToMilliseconds, | ||
| shorthandToTime: () => shorthandToTime, | ||
| wrap: () => wrap, | ||
| wrapSync: () => wrapSync | ||
| }); | ||
| module.exports = __toCommonJS(index_exports); | ||
| var import_hookified2 = require("hookified"); | ||
| var import_keyv2 = require("keyv"); | ||
| // src/hash.ts | ||
| var crypto = __toESM(require("crypto"), 1); | ||
| function hash(object, algorithm = "sha256") { | ||
| const objectString = JSON.stringify(object); | ||
| if (!crypto.getHashes().includes(algorithm)) { | ||
| throw new Error(`Unsupported hash algorithm: '${algorithm}'`); | ||
| } | ||
| const hasher = crypto.createHash(algorithm); | ||
| hasher.update(objectString); | ||
| return hasher.digest("hex"); | ||
| } | ||
| function hashToNumber(object, min = 0, max = 10, algorithm = "sha256") { | ||
| const objectString = JSON.stringify(object); | ||
| if (!crypto.getHashes().includes(algorithm)) { | ||
| throw new Error(`Unsupported hash algorithm: '${algorithm}'`); | ||
| } | ||
| const hasher = crypto.createHash(algorithm); | ||
| hasher.update(objectString); | ||
| const hashHex = hasher.digest("hex"); | ||
| const hashNumber = Number.parseInt(hashHex, 16); | ||
| const range = max - min + 1; | ||
| return min + hashNumber % range; | ||
| } | ||
| function djb2Hash(string_, min = 0, max = 10) { | ||
| let hash2 = 5381; | ||
| for (let i = 0; i < string_.length; i++) { | ||
| hash2 = hash2 * 33 ^ string_.charCodeAt(i); | ||
| } | ||
| const range = max - min + 1; | ||
| return min + Math.abs(hash2) % range; | ||
| } | ||
| // src/keyv-memory.ts | ||
| var import_keyv = require("keyv"); | ||
| // src/memory.ts | ||
| var import_hookified = require("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; | ||
| } | ||
| get size() { | ||
| return this.nodesMap.size; | ||
| } | ||
| }; | ||
| // src/shorthand-time.ts | ||
| var shorthandToMilliseconds = (shorthand) => { | ||
| let milliseconds; | ||
| if (shorthand === void 0) { | ||
| return void 0; | ||
| } | ||
| if (typeof shorthand === "number") { | ||
| milliseconds = shorthand; | ||
| } else if (typeof shorthand === "string") { | ||
| shorthand = shorthand.trim(); | ||
| if (Number.isNaN(Number(shorthand))) { | ||
| const match = /^([\d.]+)\s*(ms|s|m|h|hr|d)$/i.exec(shorthand); | ||
| if (!match) { | ||
| throw new Error( | ||
| `Unsupported time format: "${shorthand}". Use 'ms', 's', 'm', 'h', 'hr', or 'd'.` | ||
| ); | ||
| } | ||
| const [, value, unit] = match; | ||
| const numericValue = Number.parseFloat(value); | ||
| const unitLower = unit.toLowerCase(); | ||
| switch (unitLower) { | ||
| case "ms": { | ||
| milliseconds = numericValue; | ||
| break; | ||
| } | ||
| case "s": { | ||
| milliseconds = numericValue * 1e3; | ||
| break; | ||
| } | ||
| case "m": { | ||
| milliseconds = numericValue * 1e3 * 60; | ||
| break; | ||
| } | ||
| case "h": { | ||
| milliseconds = numericValue * 1e3 * 60 * 60; | ||
| break; | ||
| } | ||
| case "hr": { | ||
| milliseconds = numericValue * 1e3 * 60 * 60; | ||
| break; | ||
| } | ||
| case "d": { | ||
| milliseconds = numericValue * 1e3 * 60 * 60 * 24; | ||
| break; | ||
| } | ||
| /* c8 ignore next 3 */ | ||
| default: { | ||
| milliseconds = Number(shorthand); | ||
| } | ||
| } | ||
| } else { | ||
| milliseconds = Number(shorthand); | ||
| } | ||
| } else { | ||
| throw new TypeError("Time must be a string or a number."); | ||
| } | ||
| return milliseconds; | ||
| }; | ||
| var shorthandToTime = (shorthand, fromDate) => { | ||
| fromDate ??= /* @__PURE__ */ new Date(); | ||
| const milliseconds = shorthandToMilliseconds(shorthand); | ||
| if (milliseconds === void 0) { | ||
| return fromDate.getTime(); | ||
| } | ||
| return fromDate.getTime() + milliseconds; | ||
| }; | ||
| // src/coalesce-async.ts | ||
| var callbacks = /* @__PURE__ */ new Map(); | ||
| function hasKey(key) { | ||
| return callbacks.has(key); | ||
| } | ||
| function addKey(key) { | ||
| callbacks.set(key, []); | ||
| } | ||
| function removeKey(key) { | ||
| callbacks.delete(key); | ||
| } | ||
| function addCallbackToKey(key, callback) { | ||
| const stash = getCallbacksByKey(key); | ||
| stash.push(callback); | ||
| callbacks.set(key, stash); | ||
| } | ||
| function getCallbacksByKey(key) { | ||
| return callbacks.get(key) ?? []; | ||
| } | ||
| async function enqueue(key) { | ||
| return new Promise((resolve, reject) => { | ||
| const callback = { resolve, reject }; | ||
| addCallbackToKey(key, callback); | ||
| }); | ||
| } | ||
| function dequeue(key) { | ||
| const stash = getCallbacksByKey(key); | ||
| removeKey(key); | ||
| return stash; | ||
| } | ||
| function coalesce(options) { | ||
| const { key, error, result } = options; | ||
| for (const callback of dequeue(key)) { | ||
| if (error) { | ||
| callback.reject(error); | ||
| } else { | ||
| callback.resolve(result); | ||
| } | ||
| } | ||
| } | ||
| async function coalesceAsync(key, fnc) { | ||
| if (!hasKey(key)) { | ||
| addKey(key); | ||
| try { | ||
| const result = await Promise.resolve(fnc()); | ||
| coalesce({ key, result }); | ||
| return result; | ||
| } catch (error) { | ||
| coalesce({ key, error }); | ||
| throw error; | ||
| } | ||
| } | ||
| return enqueue(key); | ||
| } | ||
| // src/wrap.ts | ||
| function wrapSync(function_, options) { | ||
| const { ttl, keyPrefix, cache } = options; | ||
| return (...arguments_) => { | ||
| let cacheKey = createWrapKey(function_, arguments_, keyPrefix); | ||
| if (options.createKey) { | ||
| cacheKey = options.createKey(function_, arguments_, options); | ||
| } | ||
| let value = cache.get(cacheKey); | ||
| if (value === void 0) { | ||
| try { | ||
| value = function_(...arguments_); | ||
| cache.set(cacheKey, value, ttl); | ||
| } catch (error) { | ||
| cache.emit("error", error); | ||
| if (options.cacheErrors) { | ||
| cache.set(cacheKey, error, ttl); | ||
| } | ||
| } | ||
| } | ||
| return value; | ||
| }; | ||
| } | ||
| async function getOrSet(key, function_, options) { | ||
| const keyString = typeof key === "function" ? key(options) : key; | ||
| let value = await options.cache.get(keyString); | ||
| if (value === void 0) { | ||
| const cacheId = options.cacheId ?? "default"; | ||
| const coalesceKey = `${cacheId}::${keyString}`; | ||
| value = await coalesceAsync(coalesceKey, async () => { | ||
| try { | ||
| const result = await function_(); | ||
| await options.cache.set(keyString, result, options.ttl); | ||
| return result; | ||
| } catch (error) { | ||
| options.cache.emit("error", error); | ||
| if (options.cacheErrors) { | ||
| await options.cache.set(keyString, error, options.ttl); | ||
| } | ||
| if (options.throwErrors) { | ||
| throw error; | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| return value; | ||
| } | ||
| function wrap(function_, options) { | ||
| const { keyPrefix, cache } = options; | ||
| return async (...arguments_) => { | ||
| let cacheKey = createWrapKey(function_, arguments_, keyPrefix); | ||
| if (options.createKey) { | ||
| cacheKey = options.createKey(function_, arguments_, options); | ||
| } | ||
| return cache.getOrSet( | ||
| cacheKey, | ||
| async () => function_(...arguments_), | ||
| options | ||
| ); | ||
| }; | ||
| } | ||
| function createWrapKey(function_, arguments_, keyPrefix) { | ||
| if (!keyPrefix) { | ||
| return `${function_.name}::${hash(arguments_)}`; | ||
| } | ||
| return `${keyPrefix}::${function_.name}::${hash(arguments_)}`; | ||
| } | ||
| // src/memory.ts | ||
| var defaultStoreHashSize = 16; | ||
| var maximumMapSize = 16777216; | ||
| var CacheableMemory = class extends import_hookified.Hookified { | ||
| _lru = new DoublyLinkedList(); | ||
| _storeHashSize = defaultStoreHashSize; | ||
| _storeHashAlgorithm = "djb2Hash" /* djb2Hash */; | ||
| // 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 {StoreHashAlgorithm | 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 {StoreHashAlgorithm | StoreHashAlgorithmFunction} 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); | ||
| 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); | ||
| 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); | ||
| 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); | ||
| 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); | ||
| } | ||
| /** | ||
| * 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 (this._storeHashAlgorithm === "djb2Hash" /* djb2Hash */) { | ||
| return djb2Hash(key, 0, this._storeHashSize); | ||
| } | ||
| if (typeof this._storeHashAlgorithm === "function") { | ||
| return this._storeHashAlgorithm(key, this._storeHashSize); | ||
| } | ||
| return hashToNumber(key, 0, this._storeHashSize, 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); | ||
| } | ||
| /** | ||
| * 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); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * 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 | ||
| */ | ||
| wrap(function_, options) { | ||
| const wrapOptions = { | ||
| ttl: options?.ttl ?? this._ttl, | ||
| keyPrefix: options?.keyPrefix, | ||
| cache: this | ||
| }; | ||
| return wrapSync(function_, wrapOptions); | ||
| } | ||
| 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; | ||
| } | ||
| }; | ||
| // 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; | ||
| } | ||
| 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); | ||
| } | ||
| }; | ||
| 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; | ||
| } | ||
| // src/stats.ts | ||
| var CacheableStats = class { | ||
| _hits = 0; | ||
| _misses = 0; | ||
| _gets = 0; | ||
| _sets = 0; | ||
| _deletes = 0; | ||
| _clears = 0; | ||
| _vsize = 0; | ||
| _ksize = 0; | ||
| _count = 0; | ||
| _enabled = false; | ||
| constructor(options) { | ||
| if (options?.enabled) { | ||
| this._enabled = options.enabled; | ||
| } | ||
| } | ||
| /** | ||
| * @returns {boolean} - Whether the stats are enabled | ||
| */ | ||
| get enabled() { | ||
| return this._enabled; | ||
| } | ||
| /** | ||
| * @param {boolean} enabled - Whether to enable the stats | ||
| */ | ||
| set enabled(enabled) { | ||
| this._enabled = enabled; | ||
| } | ||
| /** | ||
| * @returns {number} - The number of hits | ||
| * @readonly | ||
| */ | ||
| get hits() { | ||
| return this._hits; | ||
| } | ||
| /** | ||
| * @returns {number} - The number of misses | ||
| * @readonly | ||
| */ | ||
| get misses() { | ||
| return this._misses; | ||
| } | ||
| /** | ||
| * @returns {number} - The number of gets | ||
| * @readonly | ||
| */ | ||
| get gets() { | ||
| return this._gets; | ||
| } | ||
| /** | ||
| * @returns {number} - The number of sets | ||
| * @readonly | ||
| */ | ||
| get sets() { | ||
| return this._sets; | ||
| } | ||
| /** | ||
| * @returns {number} - The number of deletes | ||
| * @readonly | ||
| */ | ||
| get deletes() { | ||
| return this._deletes; | ||
| } | ||
| /** | ||
| * @returns {number} - The number of clears | ||
| * @readonly | ||
| */ | ||
| get clears() { | ||
| return this._clears; | ||
| } | ||
| /** | ||
| * @returns {number} - The vsize (value size) of the cache instance | ||
| * @readonly | ||
| */ | ||
| get vsize() { | ||
| return this._vsize; | ||
| } | ||
| /** | ||
| * @returns {number} - The ksize (key size) of the cache instance | ||
| * @readonly | ||
| */ | ||
| get ksize() { | ||
| return this._ksize; | ||
| } | ||
| /** | ||
| * @returns {number} - The count of the cache instance | ||
| * @readonly | ||
| */ | ||
| get count() { | ||
| return this._count; | ||
| } | ||
| incrementHits() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._hits++; | ||
| } | ||
| incrementMisses() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._misses++; | ||
| } | ||
| incrementGets() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._gets++; | ||
| } | ||
| incrementSets() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._sets++; | ||
| } | ||
| incrementDeletes() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._deletes++; | ||
| } | ||
| incrementClears() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._clears++; | ||
| } | ||
| incrementVSize(value) { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._vsize += this.roughSizeOfObject(value); | ||
| } | ||
| decreaseVSize(value) { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._vsize -= this.roughSizeOfObject(value); | ||
| } | ||
| incrementKSize(key) { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._ksize += this.roughSizeOfString(key); | ||
| } | ||
| decreaseKSize(key) { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._ksize -= this.roughSizeOfString(key); | ||
| } | ||
| incrementCount() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._count++; | ||
| } | ||
| decreaseCount() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._count--; | ||
| } | ||
| setCount(count) { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._count = count; | ||
| } | ||
| roughSizeOfString(value) { | ||
| return value.length * 2; | ||
| } | ||
| roughSizeOfObject(object) { | ||
| const objectList = []; | ||
| const stack = [object]; | ||
| let bytes = 0; | ||
| while (stack.length > 0) { | ||
| const value = stack.pop(); | ||
| if (typeof value === "boolean") { | ||
| bytes += 4; | ||
| } else if (typeof value === "string") { | ||
| bytes += value.length * 2; | ||
| } else if (typeof value === "number") { | ||
| bytes += 8; | ||
| } else if (typeof value === "object" && value !== null && !objectList.includes(value)) { | ||
| objectList.push(value); | ||
| for (const key in value) { | ||
| bytes += key.length * 2; | ||
| stack.push(value[key]); | ||
| } | ||
| } | ||
| } | ||
| return bytes; | ||
| } | ||
| reset() { | ||
| this._hits = 0; | ||
| this._misses = 0; | ||
| this._gets = 0; | ||
| this._sets = 0; | ||
| this._deletes = 0; | ||
| this._clears = 0; | ||
| this._vsize = 0; | ||
| this._ksize = 0; | ||
| this._count = 0; | ||
| } | ||
| resetStoreValues() { | ||
| this._vsize = 0; | ||
| this._ksize = 0; | ||
| this._count = 0; | ||
| } | ||
| }; | ||
| // src/ttl.ts | ||
| function getTtlFromExpires(expires) { | ||
| if (expires === void 0 || expires === null) { | ||
| return void 0; | ||
| } | ||
| const now = Date.now(); | ||
| if (expires < now) { | ||
| return void 0; | ||
| } | ||
| return expires - now; | ||
| } | ||
| function getCascadingTtl(cacheableTtl, primaryTtl, secondaryTtl) { | ||
| return secondaryTtl ?? primaryTtl ?? shorthandToMilliseconds(cacheableTtl); | ||
| } | ||
| function calculateTtlFromExpiration(ttl, expires) { | ||
| const ttlFromExpires = getTtlFromExpires(expires); | ||
| const expiresFromTtl = ttl ? Date.now() + ttl : void 0; | ||
| if (ttlFromExpires === void 0) { | ||
| return ttl; | ||
| } | ||
| if (expiresFromTtl === void 0) { | ||
| return ttlFromExpires; | ||
| } | ||
| if (expires > expiresFromTtl) { | ||
| return ttl; | ||
| } | ||
| return ttlFromExpires; | ||
| } | ||
| // src/index.ts | ||
| var import_keyv3 = require("keyv"); | ||
| var CacheableHooks = /* @__PURE__ */ ((CacheableHooks2) => { | ||
| CacheableHooks2["BEFORE_SET"] = "BEFORE_SET"; | ||
| CacheableHooks2["AFTER_SET"] = "AFTER_SET"; | ||
| CacheableHooks2["BEFORE_SET_MANY"] = "BEFORE_SET_MANY"; | ||
| CacheableHooks2["AFTER_SET_MANY"] = "AFTER_SET_MANY"; | ||
| CacheableHooks2["BEFORE_GET"] = "BEFORE_GET"; | ||
| CacheableHooks2["AFTER_GET"] = "AFTER_GET"; | ||
| CacheableHooks2["BEFORE_GET_MANY"] = "BEFORE_GET_MANY"; | ||
| CacheableHooks2["AFTER_GET_MANY"] = "AFTER_GET_MANY"; | ||
| CacheableHooks2["BEFORE_SECONDARY_SETS_PRIMARY"] = "BEFORE_SECONDARY_SETS_PRIMARY"; | ||
| return CacheableHooks2; | ||
| })(CacheableHooks || {}); | ||
| var CacheableEvents = /* @__PURE__ */ ((CacheableEvents2) => { | ||
| CacheableEvents2["ERROR"] = "error"; | ||
| return CacheableEvents2; | ||
| })(CacheableEvents || {}); | ||
| var Cacheable = class extends import_hookified2.Hookified { | ||
| _primary = createKeyv(); | ||
| _secondary; | ||
| _nonBlocking = false; | ||
| _ttl; | ||
| _stats = new CacheableStats({ enabled: false }); | ||
| _namespace; | ||
| _cacheId = Math.random().toString(36).slice(2); | ||
| /** | ||
| * Creates a new cacheable instance | ||
| * @param {CacheableOptions} [options] The options for the cacheable instance | ||
| */ | ||
| constructor(options) { | ||
| super(); | ||
| if (options?.primary) { | ||
| this.setPrimary(options.primary); | ||
| } | ||
| if (options?.secondary) { | ||
| this.setSecondary(options.secondary); | ||
| } | ||
| if (options?.nonBlocking) { | ||
| this._nonBlocking = options.nonBlocking; | ||
| } | ||
| if (options?.stats) { | ||
| this._stats.enabled = options.stats; | ||
| } | ||
| if (options?.ttl) { | ||
| this.setTtl(options.ttl); | ||
| } | ||
| if (options?.cacheId) { | ||
| this._cacheId = options.cacheId; | ||
| } | ||
| if (options?.namespace) { | ||
| this._namespace = options.namespace; | ||
| this._primary.namespace = this.getNameSpace(); | ||
| if (this._secondary) { | ||
| this._secondary.namespace = this.getNameSpace(); | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * The namespace for the cacheable instance | ||
| * @returns {string | (() => string) | undefined} The namespace for the cacheable instance | ||
| */ | ||
| get namespace() { | ||
| return this._namespace; | ||
| } | ||
| /** | ||
| * Sets the namespace for the cacheable instance | ||
| * @param {string | (() => string) | undefined} namespace The namespace for the cacheable instance | ||
| * @returns {void} | ||
| */ | ||
| set namespace(namespace) { | ||
| this._namespace = namespace; | ||
| this._primary.namespace = this.getNameSpace(); | ||
| if (this._secondary) { | ||
| this._secondary.namespace = this.getNameSpace(); | ||
| } | ||
| } | ||
| /** | ||
| * The statistics for the cacheable instance | ||
| * @returns {CacheableStats} The statistics for the cacheable instance | ||
| */ | ||
| get stats() { | ||
| return this._stats; | ||
| } | ||
| /** | ||
| * The primary store for the cacheable instance | ||
| * @returns {Keyv} The primary store for the cacheable instance | ||
| */ | ||
| get primary() { | ||
| return this._primary; | ||
| } | ||
| /** | ||
| * Sets the primary store for the cacheable instance | ||
| * @param {Keyv} primary The primary store for the cacheable instance | ||
| */ | ||
| set primary(primary) { | ||
| this._primary = primary; | ||
| } | ||
| /** | ||
| * The secondary store for the cacheable instance | ||
| * @returns {Keyv | undefined} The secondary store for the cacheable instance | ||
| */ | ||
| get secondary() { | ||
| return this._secondary; | ||
| } | ||
| /** | ||
| * Sets the secondary store for the cacheable instance. If it is set to undefined then the secondary store is disabled. | ||
| * @param {Keyv | undefined} secondary The secondary store for the cacheable instance | ||
| * @returns {void} | ||
| */ | ||
| set secondary(secondary) { | ||
| this._secondary = secondary; | ||
| } | ||
| /** | ||
| * Gets whether the secondary store is non-blocking mode. It is set to false by default. | ||
| * If it is set to true then the secondary store will not block the primary store. | ||
| * | ||
| * [Learn more about non-blocking mode](https://cacheable.org/docs/cacheable/#non-blocking-operations). | ||
| * | ||
| * @returns {boolean} Whether the cacheable instance is non-blocking | ||
| */ | ||
| get nonBlocking() { | ||
| return this._nonBlocking; | ||
| } | ||
| /** | ||
| * Sets whether the secondary store is non-blocking mode. It is set to false by default. | ||
| * If it is set to true then the secondary store will not block the primary store. | ||
| * | ||
| * [Learn more about non-blocking mode](https://cacheable.org/docs/cacheable/#non-blocking-operations). | ||
| * | ||
| * @param {boolean} nonBlocking Whether the cacheable instance is non-blocking | ||
| * @returns {void} | ||
| */ | ||
| set nonBlocking(nonBlocking) { | ||
| this._nonBlocking = nonBlocking; | ||
| } | ||
| /** | ||
| * The time-to-live for the cacheable instance and will be used as the default value. | ||
| * can be a number in milliseconds or a human-readable format such as `1s` for 1 second or `1h` for 1 hour | ||
| * or undefined if there is no time-to-live. | ||
| * | ||
| * [Learn more about time-to-live](https://cacheable.org/docs/cacheable/#shorthand-for-time-to-live-ttl). | ||
| * | ||
| * @returns {number | string | undefined} The time-to-live for the cacheable instance in milliseconds, human-readable format or undefined | ||
| * @example | ||
| * ```typescript | ||
| * const cacheable = new Cacheable({ ttl: '1h' }); | ||
| * console.log(cacheable.ttl); // 1h | ||
| * ``` | ||
| */ | ||
| get ttl() { | ||
| return this._ttl; | ||
| } | ||
| /** | ||
| * Sets the time-to-live for the cacheable instance and will be used as the default value. | ||
| * 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 | ||
| * there is no time-to-live. | ||
| * | ||
| * [Learn more about time-to-live](https://cacheable.org/docs/cacheable/#shorthand-for-time-to-live-ttl). | ||
| * | ||
| * @param {number | string | undefined} ttl The time-to-live for the cacheable instance | ||
| * @example | ||
| * ```typescript | ||
| * const cacheable = new Cacheable(); | ||
| * cacheable.ttl = '1h'; // Set the time-to-live to 1 hour | ||
| * ``` | ||
| * or setting the time-to-live in milliseconds | ||
| * ```typescript | ||
| * const cacheable = new Cacheable(); | ||
| * cacheable.ttl = 3600000; // Set the time-to-live to 1 hour | ||
| * ``` | ||
| */ | ||
| set ttl(ttl) { | ||
| this.setTtl(ttl); | ||
| } | ||
| /** | ||
| * The cacheId for the cacheable instance. This is primarily used for the wrap function to not have conflicts. | ||
| * If it is not set then it will be a random string that is generated | ||
| * @returns {string} The cacheId for the cacheable instance | ||
| */ | ||
| get cacheId() { | ||
| return this._cacheId; | ||
| } | ||
| /** | ||
| * Sets the cacheId for the cacheable instance. This is primarily used for the wrap function to not have conflicts. | ||
| * If it is not set then it will be a random string that is generated | ||
| * @param {string} cacheId The cacheId for the cacheable instance | ||
| */ | ||
| set cacheId(cacheId) { | ||
| this._cacheId = cacheId; | ||
| } | ||
| /** | ||
| * Sets the primary store for the cacheable instance | ||
| * @param {Keyv | KeyvStoreAdapter} primary The primary store for the cacheable instance | ||
| * @returns {void} | ||
| */ | ||
| setPrimary(primary) { | ||
| if (this.isKeyvInstance(primary)) { | ||
| this._primary = primary; | ||
| } else { | ||
| this._primary = new import_keyv2.Keyv(primary); | ||
| } | ||
| this._primary.on("error", (error) => { | ||
| this.emit("error" /* ERROR */, error); | ||
| }); | ||
| } | ||
| /** | ||
| * Sets the secondary store for the cacheable instance. If it is set to undefined then the secondary store is disabled. | ||
| * @param {Keyv | KeyvStoreAdapter} secondary The secondary store for the cacheable instance | ||
| * @returns {void} | ||
| */ | ||
| setSecondary(secondary) { | ||
| if (this.isKeyvInstance(secondary)) { | ||
| this._secondary = secondary; | ||
| } else { | ||
| this._secondary = new import_keyv2.Keyv(secondary); | ||
| } | ||
| this._secondary.on("error", (error) => { | ||
| this.emit("error" /* ERROR */, error); | ||
| }); | ||
| } | ||
| // biome-ignore lint/suspicious/noExplicitAny: type format | ||
| isKeyvInstance(keyv) { | ||
| if (keyv instanceof import_keyv2.Keyv) { | ||
| return true; | ||
| } | ||
| const keyvMethods = [ | ||
| "generateIterator", | ||
| "get", | ||
| "getMany", | ||
| "set", | ||
| "setMany", | ||
| "delete", | ||
| "deleteMany", | ||
| "has", | ||
| "hasMany", | ||
| "clear", | ||
| "disconnect", | ||
| "serialize", | ||
| "deserialize" | ||
| ]; | ||
| return keyvMethods.every((method) => typeof keyv[method] === "function"); | ||
| } | ||
| getNameSpace() { | ||
| if (typeof this._namespace === "function") { | ||
| return this._namespace(); | ||
| } | ||
| return this._namespace; | ||
| } | ||
| async get(key, options = {}) { | ||
| let result; | ||
| const { raw = false } = options; | ||
| try { | ||
| await this.hook("BEFORE_GET" /* BEFORE_GET */, key); | ||
| result = await this._primary.get(key, { raw: true }); | ||
| let ttl; | ||
| if (!result && this._secondary) { | ||
| const secondaryResult = await this.getSecondaryRawResults(key); | ||
| if (secondaryResult?.value) { | ||
| result = secondaryResult; | ||
| const cascadeTtl = getCascadingTtl(this._ttl, this._primary.ttl); | ||
| const expires = secondaryResult.expires ?? void 0; | ||
| ttl = calculateTtlFromExpiration(cascadeTtl, expires); | ||
| const setItem = { key, value: result.value, ttl }; | ||
| await this.hook( | ||
| "BEFORE_SECONDARY_SETS_PRIMARY" /* BEFORE_SECONDARY_SETS_PRIMARY */, | ||
| setItem | ||
| ); | ||
| await this._primary.set(setItem.key, setItem.value, setItem.ttl); | ||
| } | ||
| } | ||
| await this.hook("AFTER_GET" /* AFTER_GET */, { key, result, ttl }); | ||
| } catch (error) { | ||
| this.emit("error" /* ERROR */, error); | ||
| } | ||
| if (this.stats.enabled) { | ||
| if (result) { | ||
| this._stats.incrementHits(); | ||
| } else { | ||
| this._stats.incrementMisses(); | ||
| } | ||
| this.stats.incrementGets(); | ||
| } | ||
| return raw ? result : result?.value; | ||
| } | ||
| async getMany(keys, options = {}) { | ||
| let result = []; | ||
| const { raw = false } = options; | ||
| try { | ||
| await this.hook("BEFORE_GET_MANY" /* BEFORE_GET_MANY */, keys); | ||
| result = await this._primary.get(keys, { raw: true }); | ||
| if (this._secondary) { | ||
| const missingKeys = []; | ||
| for (const [i, key] of keys.entries()) { | ||
| if (!result[i]) { | ||
| missingKeys.push(key); | ||
| } | ||
| } | ||
| const secondaryResults = await this.getManySecondaryRawResults(missingKeys); | ||
| for await (const [i, key] of keys.entries()) { | ||
| if (!result[i] && secondaryResults[i]) { | ||
| result[i] = secondaryResults[i]; | ||
| const cascadeTtl = getCascadingTtl(this._ttl, this._primary.ttl); | ||
| let { expires } = secondaryResults[i]; | ||
| if (expires === null) { | ||
| expires = void 0; | ||
| } | ||
| const ttl = calculateTtlFromExpiration(cascadeTtl, expires); | ||
| const setItem = { key, value: result[i].value, ttl }; | ||
| await this.hook( | ||
| "BEFORE_SECONDARY_SETS_PRIMARY" /* BEFORE_SECONDARY_SETS_PRIMARY */, | ||
| setItem | ||
| ); | ||
| await this._primary.set(setItem.key, setItem.value, setItem.ttl); | ||
| } | ||
| } | ||
| } | ||
| await this.hook("AFTER_GET_MANY" /* AFTER_GET_MANY */, { keys, result }); | ||
| } catch (error) { | ||
| this.emit("error" /* ERROR */, error); | ||
| } | ||
| if (this.stats.enabled) { | ||
| for (const item of result) { | ||
| if (item) { | ||
| this._stats.incrementHits(); | ||
| } else { | ||
| this._stats.incrementMisses(); | ||
| } | ||
| } | ||
| this.stats.incrementGets(); | ||
| } | ||
| return raw ? result : result.map((item) => item?.value); | ||
| } | ||
| /** | ||
| * Sets the value of the key. If the secondary store is set then it will also set the value in the secondary store. | ||
| * @param {string} key the key to set the value of | ||
| * @param {T} value The value to set | ||
| * @param {number | string} [ttl] set a number it is miliseconds, 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. | ||
| * @returns {boolean} Whether the value was set | ||
| */ | ||
| async set(key, value, ttl) { | ||
| let result = false; | ||
| const finalTtl = shorthandToMilliseconds(ttl ?? this._ttl); | ||
| try { | ||
| const item = { key, value, ttl: finalTtl }; | ||
| await this.hook("BEFORE_SET" /* BEFORE_SET */, item); | ||
| const promises = []; | ||
| promises.push(this._primary.set(item.key, item.value, item.ttl)); | ||
| if (this._secondary) { | ||
| promises.push(this._secondary.set(item.key, item.value, item.ttl)); | ||
| } | ||
| if (this._nonBlocking) { | ||
| result = await Promise.race(promises); | ||
| } else { | ||
| const results = await Promise.all(promises); | ||
| result = results[0]; | ||
| } | ||
| await this.hook("AFTER_SET" /* AFTER_SET */, item); | ||
| } catch (error) { | ||
| this.emit("error" /* ERROR */, error); | ||
| } | ||
| if (this.stats.enabled) { | ||
| this.stats.incrementKSize(key); | ||
| this.stats.incrementCount(); | ||
| this.stats.incrementVSize(value); | ||
| this.stats.incrementSets(); | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Sets the values of the keys. If the secondary store is set then it will also set the values in the secondary store. | ||
| * @param {CacheableItem[]} items The items to set | ||
| * @returns {boolean} Whether the values were set | ||
| */ | ||
| async setMany(items) { | ||
| let result = false; | ||
| try { | ||
| await this.hook("BEFORE_SET_MANY" /* BEFORE_SET_MANY */, items); | ||
| result = await this.setManyKeyv(this._primary, items); | ||
| if (this._secondary) { | ||
| if (this._nonBlocking) { | ||
| this.setManyKeyv(this._secondary, items); | ||
| } else { | ||
| await this.setManyKeyv(this._secondary, items); | ||
| } | ||
| } | ||
| await this.hook("AFTER_SET_MANY" /* AFTER_SET_MANY */, items); | ||
| } catch (error) { | ||
| this.emit("error" /* ERROR */, error); | ||
| } | ||
| if (this.stats.enabled) { | ||
| for (const item of items) { | ||
| this.stats.incrementKSize(item.key); | ||
| this.stats.incrementCount(); | ||
| this.stats.incrementVSize(item.value); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Takes the value of the key and deletes the key. If the key does not exist then it will return undefined. | ||
| * @param {string} key The key to take the value of | ||
| * @returns {Promise<T | undefined>} The value of the key or undefined if the key does not exist | ||
| */ | ||
| async take(key) { | ||
| const result = await this.get(key); | ||
| await this.delete(key); | ||
| return result; | ||
| } | ||
| /** | ||
| * Takes the values of the keys and deletes the keys. If the key does not exist then it will return undefined. | ||
| * @param {string[]} keys The keys to take the values of | ||
| * @returns {Promise<Array<T | undefined>>} The values of the keys or undefined if the key does not exist | ||
| */ | ||
| async takeMany(keys) { | ||
| const result = await this.getMany(keys); | ||
| await this.deleteMany(keys); | ||
| return result; | ||
| } | ||
| /** | ||
| * Checks if the key exists in the primary store. If it does not exist then it will check the secondary store. | ||
| * @param {string} key The key to check | ||
| * @returns {Promise<boolean>} Whether the key exists | ||
| */ | ||
| async has(key) { | ||
| const promises = []; | ||
| promises.push(this._primary.has(key)); | ||
| if (this._secondary) { | ||
| promises.push(this._secondary.has(key)); | ||
| } | ||
| const resultAll = await Promise.all(promises); | ||
| for (const result of resultAll) { | ||
| if (result) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| /** | ||
| * Checks if the keys exist in the primary store. If it does not exist then it will check the secondary store. | ||
| * @param {string[]} keys The keys to check | ||
| * @returns {Promise<boolean[]>} Whether the keys exist | ||
| */ | ||
| async hasMany(keys) { | ||
| const result = await this.hasManyKeyv(this._primary, keys); | ||
| const missingKeys = []; | ||
| for (const [i, key] of keys.entries()) { | ||
| if (!result[i] && this._secondary) { | ||
| missingKeys.push(key); | ||
| } | ||
| } | ||
| if (missingKeys.length > 0 && this._secondary) { | ||
| const secondary = await this.hasManyKeyv(this._secondary, keys); | ||
| for (const [i, _key] of keys.entries()) { | ||
| if (!result[i] && secondary[i]) { | ||
| result[i] = secondary[i]; | ||
| } | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Deletes the key from the primary store. If the secondary store is set then it will also delete the key from the secondary store. | ||
| * @param {string} key The key to delete | ||
| * @returns {Promise<boolean>} Whether the key was deleted | ||
| */ | ||
| async delete(key) { | ||
| let result = false; | ||
| const promises = []; | ||
| if (this.stats.enabled) { | ||
| const statResult = await this._primary.get(key); | ||
| if (statResult) { | ||
| this.stats.decreaseKSize(key); | ||
| this.stats.decreaseVSize(statResult); | ||
| this.stats.decreaseCount(); | ||
| this.stats.incrementDeletes(); | ||
| } | ||
| } | ||
| promises.push(this._primary.delete(key)); | ||
| if (this._secondary) { | ||
| promises.push(this._secondary.delete(key)); | ||
| } | ||
| if (this.nonBlocking) { | ||
| result = await Promise.race(promises); | ||
| } else { | ||
| const resultAll = await Promise.all(promises); | ||
| result = resultAll[0]; | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Deletes the keys from the primary store. If the secondary store is set then it will also delete the keys from the secondary store. | ||
| * @param {string[]} keys The keys to delete | ||
| * @returns {Promise<boolean>} Whether the keys were deleted | ||
| */ | ||
| async deleteMany(keys) { | ||
| if (this.stats.enabled) { | ||
| const statResult = await this._primary.get(keys); | ||
| for (const key of keys) { | ||
| this.stats.decreaseKSize(key); | ||
| this.stats.decreaseVSize(statResult); | ||
| this.stats.decreaseCount(); | ||
| this.stats.incrementDeletes(); | ||
| } | ||
| } | ||
| const result = await this.deleteManyKeyv(this._primary, keys); | ||
| if (this._secondary) { | ||
| if (this._nonBlocking) { | ||
| this.deleteManyKeyv(this._secondary, keys); | ||
| } else { | ||
| await this.deleteManyKeyv(this._secondary, keys); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Clears the primary store. If the secondary store is set then it will also clear the secondary store. | ||
| * @returns {Promise<void>} | ||
| */ | ||
| async clear() { | ||
| const promises = []; | ||
| promises.push(this._primary.clear()); | ||
| if (this._secondary) { | ||
| promises.push(this._secondary.clear()); | ||
| } | ||
| await (this._nonBlocking ? Promise.race(promises) : Promise.all(promises)); | ||
| if (this.stats.enabled) { | ||
| this._stats.resetStoreValues(); | ||
| this._stats.incrementClears(); | ||
| } | ||
| } | ||
| /** | ||
| * Disconnects the primary store. If the secondary store is set then it will also disconnect the secondary store. | ||
| * @returns {Promise<void>} | ||
| */ | ||
| async disconnect() { | ||
| const promises = []; | ||
| promises.push(this._primary.disconnect()); | ||
| if (this._secondary) { | ||
| promises.push(this._secondary.disconnect()); | ||
| } | ||
| await (this._nonBlocking ? Promise.race(promises) : Promise.all(promises)); | ||
| } | ||
| /** | ||
| * Wraps a function with caching | ||
| * | ||
| * [Learn more about wrapping functions](https://cacheable.org/docs/cacheable/#wrap--memoization-for-sync-and-async-functions). | ||
| * @param {Function} function_ The function to wrap | ||
| * @param {WrapOptions} [options] The options for the wrap function | ||
| * @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, | ||
| cache: this, | ||
| cacheId: this._cacheId | ||
| }; | ||
| return wrap(function_, wrapOptions); | ||
| } | ||
| /** | ||
| * Retrieves the value associated with the given key from the cache. If the key is not found, | ||
| * invokes the provided function to calculate the value, stores it in the cache, and then returns it. | ||
| * | ||
| * @param {GetOrSetKey} key - The key to retrieve or set in the cache. This can also be a function that returns a string key. | ||
| * If a function is provided, it will be called with the cache options to generate the key. | ||
| * @param {() => Promise<T>} function_ - The asynchronous function that computes the value to be cached if the key does not exist. | ||
| * @param {GetOrSetFunctionOptions} [options] - Optional settings for caching, such as the time to live (TTL) or whether to cache errors. | ||
| * @return {Promise<T | undefined>} - A promise that resolves to the cached or newly computed value, or undefined if an error occurs and caching is not configured for errors. | ||
| */ | ||
| async getOrSet(key, function_, options) { | ||
| const getOrSetOptions = { | ||
| cache: this, | ||
| cacheId: this._cacheId, | ||
| ttl: options?.ttl ?? this._ttl, | ||
| cacheErrors: options?.cacheErrors, | ||
| throwErrors: options?.throwErrors | ||
| }; | ||
| return getOrSet(key, function_, getOrSetOptions); | ||
| } | ||
| /** | ||
| * Will hash an object using the specified algorithm. The default algorithm is 'sha256'. | ||
| * @param {any} object the object to hash | ||
| * @param {string} algorithm the hash algorithm to use. The default is 'sha256' | ||
| * @returns {string} the hash of the object | ||
| */ | ||
| // biome-ignore lint/suspicious/noExplicitAny: type format | ||
| hash(object, algorithm = "sha256") { | ||
| return hash(object, algorithm); | ||
| } | ||
| async getSecondaryRawResults(key) { | ||
| let result; | ||
| if (this._secondary) { | ||
| result = await this._secondary.get(key, { raw: true }); | ||
| } | ||
| return result; | ||
| } | ||
| async getManySecondaryRawResults(keys) { | ||
| let result = []; | ||
| if (this._secondary) { | ||
| result = await this._secondary.get(keys, { raw: true }); | ||
| } | ||
| return result; | ||
| } | ||
| async deleteManyKeyv(keyv, keys) { | ||
| const promises = []; | ||
| for (const key of keys) { | ||
| promises.push(keyv.delete(key)); | ||
| } | ||
| await Promise.all(promises); | ||
| return true; | ||
| } | ||
| async setManyKeyv(keyv, items) { | ||
| const promises = []; | ||
| for (const item of items) { | ||
| const finalTtl = shorthandToMilliseconds(item.ttl ?? this._ttl); | ||
| promises.push(keyv.set(item.key, item.value, finalTtl)); | ||
| } | ||
| await Promise.all(promises); | ||
| return true; | ||
| } | ||
| async hasManyKeyv(keyv, keys) { | ||
| const promises = []; | ||
| for (const key of keys) { | ||
| promises.push(keyv.has(key)); | ||
| } | ||
| return Promise.all(promises); | ||
| } | ||
| setTtl(ttl) { | ||
| if (typeof ttl === "string" || ttl === void 0) { | ||
| this._ttl = ttl; | ||
| } else if (ttl > 0) { | ||
| this._ttl = ttl; | ||
| } else { | ||
| this._ttl = void 0; | ||
| } | ||
| } | ||
| }; | ||
| // Annotate the CommonJS export names for ESM import in node: | ||
| 0 && (module.exports = { | ||
| Cacheable, | ||
| CacheableEvents, | ||
| CacheableHooks, | ||
| CacheableMemory, | ||
| CacheableStats, | ||
| Keyv, | ||
| KeyvCacheableMemory, | ||
| KeyvHooks, | ||
| createKeyv, | ||
| getOrSet, | ||
| shorthandToMilliseconds, | ||
| shorthandToTime, | ||
| wrap, | ||
| wrapSync | ||
| }); | ||
| "use strict";var A=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var F=Object.getOwnPropertyNames;var B=Object.prototype.hasOwnProperty;var I=(u,t)=>{for(var i in t)A(u,i,{get:t[i],enumerable:!0})},G=(u,t,i,e)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of F(t))!B.call(u,s)&&s!==i&&A(u,s,{get:()=>t[s],enumerable:!(e=K(t,s))||e.enumerable});return u};var P=u=>G(A({},"__esModule",{value:!0}),u);var C={};I(C,{Cacheable:()=>w,CacheableEvents:()=>R,CacheableHooks:()=>E,CacheableMemory:()=>m.CacheableMemory,CacheableStats:()=>h.Stats,HashAlgorithm:()=>h.HashAlgorithm,Keyv:()=>T.Keyv,KeyvCacheableMemory:()=>m.KeyvCacheableMemory,KeyvHooks:()=>T.KeyvHooks,calculateTtlFromExpiration:()=>h.calculateTtlFromExpiration,createKeyv:()=>m.createKeyv,getCascadingTtl:()=>h.getCascadingTtl,getOrSet:()=>_.getOrSet,hash:()=>h.hash,shorthandToMilliseconds:()=>h.shorthandToMilliseconds,shorthandToTime:()=>h.shorthandToTime,wrap:()=>_.wrap,wrapSync:()=>_.wrapSync});module.exports=P(C);var g=require("@cacheable/memoize"),O=require("@cacheable/memory"),l=require("@cacheable/utils"),M=require("hookified"),f=require("keyv");var E=(o=>(o.BEFORE_SET="BEFORE_SET",o.AFTER_SET="AFTER_SET",o.BEFORE_SET_MANY="BEFORE_SET_MANY",o.AFTER_SET_MANY="AFTER_SET_MANY",o.BEFORE_GET="BEFORE_GET",o.AFTER_GET="AFTER_GET",o.BEFORE_GET_MANY="BEFORE_GET_MANY",o.AFTER_GET_MANY="AFTER_GET_MANY",o.BEFORE_SECONDARY_SETS_PRIMARY="BEFORE_SECONDARY_SETS_PRIMARY",o))(E||{}),R=(e=>(e.ERROR="error",e.CACHE_HIT="cache:hit",e.CACHE_MISS="cache:miss",e))(R||{});var _=require("@cacheable/memoize"),m=require("@cacheable/memory"),h=require("@cacheable/utils"),T=require("keyv"),w=class extends M.Hookified{_primary=(0,O.createKeyv)();_secondary;_nonBlocking=!1;_ttl;_stats=new l.Stats({enabled:!1});_namespace;_cacheId=Math.random().toString(36).slice(2);constructor(t){super(),t?.primary&&this.setPrimary(t.primary),t?.secondary&&this.setSecondary(t.secondary),t?.nonBlocking&&(this._nonBlocking=t.nonBlocking),t?.stats&&(this._stats.enabled=t.stats),t?.ttl&&this.setTtl(t.ttl),t?.cacheId&&(this._cacheId=t.cacheId),t?.namespace&&(this._namespace=t.namespace,this._primary.namespace=this.getNameSpace(),this._secondary&&(this._secondary.namespace=this.getNameSpace()))}get namespace(){return this._namespace}set namespace(t){this._namespace=t,this._primary.namespace=this.getNameSpace(),this._secondary&&(this._secondary.namespace=this.getNameSpace())}get stats(){return this._stats}get primary(){return this._primary}set primary(t){this._primary=t}get secondary(){return this._secondary}set secondary(t){this._secondary=t}get nonBlocking(){return this._nonBlocking}set nonBlocking(t){this._nonBlocking=t}get ttl(){return this._ttl}set ttl(t){this.setTtl(t)}get cacheId(){return this._cacheId}set cacheId(t){this._cacheId=t}setPrimary(t){this.isKeyvInstance(t)?this._primary=t:this._primary=new f.Keyv(t),this._primary.on("error",i=>{this.emit("error",i)})}setSecondary(t){this.isKeyvInstance(t)?this._secondary=t:this._secondary=new f.Keyv(t),this._secondary.on("error",i=>{this.emit("error",i)})}isKeyvInstance(t){return t instanceof f.Keyv?!0:["generateIterator","get","getMany","set","setMany","delete","deleteMany","has","hasMany","clear","disconnect","serialize","deserialize"].every(e=>typeof t[e]=="function")}getNameSpace(){return typeof this._namespace=="function"?this._namespace():this._namespace}async get(t,i){return(await this.getRaw(t,i))?.value}async getRaw(t,i){let e;try{await this.hook("BEFORE_GET",t),e=await this._primary.getRaw(t);let s;e?this.emit("cache:hit",{key:t,value:e.value,store:"primary"}):this.emit("cache:miss",{key:t,store:"primary"});let n=i?.nonBlocking??this._nonBlocking;if(!e&&this._secondary){let a;n?a=await this.processSecondaryForGetRawNonBlocking(this._primary,this._secondary,t):a=await this.processSecondaryForGetRaw(this._primary,this._secondary,t),a&&(e=a.result,s=a.ttl)}await this.hook("AFTER_GET",{key:t,result:e,ttl:s})}catch(s){this.emit("error",s)}return this.stats.enabled&&(e?this._stats.incrementHits():this._stats.incrementMisses(),this.stats.incrementGets()),e}async getManyRaw(t,i){let e=[];try{await this.hook("BEFORE_GET_MANY",t),e=await this._primary.getManyRaw(t);for(let[n,a]of t.entries())e[n]?this.emit("cache:hit",{key:a,value:e[n].value,store:"primary"}):this.emit("cache:miss",{key:a,store:"primary"});let s=i?.nonBlocking??this._nonBlocking;this._secondary&&(s?await this.processSecondaryForGetManyRawNonBlocking(this._primary,this._secondary,t,e):await this.processSecondaryForGetManyRaw(this._primary,this._secondary,t,e)),await this.hook("AFTER_GET_MANY",{keys:t,result:e})}catch(s){this.emit("error",s)}if(this.stats.enabled){for(let s of e)s?this._stats.incrementHits():this._stats.incrementMisses();this.stats.incrementGets()}return e}async getMany(t,i){return(await this.getManyRaw(t,i)).map(s=>s?.value)}async set(t,i,e){let s=!1,n=(0,l.shorthandToMilliseconds)(e??this._ttl);try{let a={key:t,value:i,ttl:n};await this.hook("BEFORE_SET",a);let r=[];if(r.push(this._primary.set(a.key,a.value,a.ttl)),this._secondary&&r.push(this._secondary.set(a.key,a.value,a.ttl)),this._nonBlocking){s=await Promise.race(r);for(let c of r)c.catch(o=>{this.emit("error",o)})}else s=(await Promise.all(r))[0];await this.hook("AFTER_SET",a)}catch(a){this.emit("error",a)}return this.stats.enabled&&(this.stats.incrementKSize(t),this.stats.incrementCount(),this.stats.incrementVSize(i),this.stats.incrementSets()),s}async setMany(t){let i=!1;try{await this.hook("BEFORE_SET_MANY",t),i=await this.setManyKeyv(this._primary,t),this._secondary&&(this._nonBlocking?this.setManyKeyv(this._secondary,t).catch(e=>{this.emit("error",e)}):await this.setManyKeyv(this._secondary,t)),await this.hook("AFTER_SET_MANY",t)}catch(e){this.emit("error",e)}if(this.stats.enabled)for(let e of t)this.stats.incrementKSize(e.key),this.stats.incrementCount(),this.stats.incrementVSize(e.value);return i}async take(t){let i=await this.get(t);return await this.delete(t),i}async takeMany(t){let i=await this.getMany(t);return await this.deleteMany(t),i}async has(t){let i=[];i.push(this._primary.has(t)),this._secondary&&i.push(this._secondary.has(t));let e=await Promise.all(i);for(let s of e)if(s)return!0;return!1}async hasMany(t){let i=await this.hasManyKeyv(this._primary,t),e=[];for(let[s,n]of t.entries())!i[s]&&this._secondary&&e.push(n);if(e.length>0&&this._secondary){let s=await this.hasManyKeyv(this._secondary,t);for(let[n,a]of t.entries())!i[n]&&s[n]&&(i[n]=s[n])}return i}async delete(t){let i=!1,e=[];if(this.stats.enabled){let s=await this._primary.get(t);s&&(this.stats.decreaseKSize(t),this.stats.decreaseVSize(s),this.stats.decreaseCount(),this.stats.incrementDeletes())}if(e.push(this._primary.delete(t)),this._secondary&&e.push(this._secondary.delete(t)),this.nonBlocking){i=await Promise.race(e);for(let s of e)s.catch(n=>{this.emit("error",n)})}else i=(await Promise.all(e))[0];return i}async deleteMany(t){if(this.stats.enabled){let e=await this._primary.get(t);for(let s of t)this.stats.decreaseKSize(s),this.stats.decreaseVSize(e),this.stats.decreaseCount(),this.stats.incrementDeletes()}let i=await this._primary.deleteMany(t);return this._secondary&&(this._nonBlocking?this._secondary.deleteMany(t).catch(e=>{this.emit("error",e)}):await this._secondary.deleteMany(t)),i}async clear(){let t=[];t.push(this._primary.clear()),this._secondary&&t.push(this._secondary.clear()),await(this._nonBlocking?Promise.race(t):Promise.all(t)),this.stats.enabled&&(this._stats.resetStoreValues(),this._stats.incrementClears())}async disconnect(){let t=[];t.push(this._primary.disconnect()),this._secondary&&t.push(this._secondary.disconnect()),await(this._nonBlocking?Promise.race(t):Promise.all(t))}wrap(t,i){let e={get:async n=>this.get(n),has:async n=>this.has(n),set:async(n,a,r)=>{await this.set(n,a,r)},on:(n,a)=>{this.on(n,a)},emit:(n,...a)=>this.emit(n,...a)},s={ttl:i?.ttl??this._ttl,keyPrefix:i?.keyPrefix,createKey:i?.createKey,cacheErrors:i?.cacheErrors,cache:e,cacheId:this._cacheId,serialize:i?.serialize};return(0,g.wrap)(t,s)}async getOrSet(t,i,e){let n={cache:{get:async a=>this.get(a),has:async a=>this.has(a),set:async(a,r,c)=>{await this.set(a,r,c)},on:(a,r)=>{this.on(a,r)},emit:(a,...r)=>this.emit(a,...r)},cacheId:this._cacheId,ttl:e?.ttl??this._ttl,cacheErrors:e?.cacheErrors,throwErrors:e?.throwErrors};return(0,g.getOrSet)(t,i,n)}hash(t,i=l.HashAlgorithm.SHA256){let e=Object.values(l.HashAlgorithm).includes(i)?i:l.HashAlgorithm.SHA256;return(0,l.hash)(t,{algorithm:e})}async setManyKeyv(t,i){let e=[];for(let s of i){let n=(0,l.shorthandToMilliseconds)(s.ttl??this._ttl);e.push({key:s.key,value:s.value,ttl:n})}return await t.setMany(e),!0}async hasManyKeyv(t,i){let e=[];for(let s of i)e.push(t.has(s));return Promise.all(e)}async processSecondaryForGetRaw(t,i,e){let s=await i.getRaw(e);if(s?.value){this.emit("cache:hit",{key:e,value:s.value,store:"secondary"});let n=(0,l.getCascadingTtl)(this._ttl,this._primary.ttl),a=s.expires??void 0,r=(0,l.calculateTtlFromExpiration)(n,a),c={key:e,value:s.value,ttl:r};return await this.hook("BEFORE_SECONDARY_SETS_PRIMARY",c),await t.set(c.key,c.value,c.ttl),{result:s,ttl:r}}else{this.emit("cache:miss",{key:e,store:"secondary"});return}}async processSecondaryForGetRawNonBlocking(t,i,e){let s=await i.getRaw(e);if(s?.value){this.emit("cache:hit",{key:e,value:s.value,store:"secondary"});let n=(0,l.getCascadingTtl)(this._ttl,this._primary.ttl),a=s.expires??void 0,r=(0,l.calculateTtlFromExpiration)(n,a),c={key:e,value:s.value,ttl:r};return this.hook("BEFORE_SECONDARY_SETS_PRIMARY",c).then(async()=>{await t.set(c.key,c.value,c.ttl)}).catch(o=>{this.emit("error",o)}),{result:s,ttl:r}}else{this.emit("cache:miss",{key:e,store:"secondary"});return}}async processSecondaryForGetManyRaw(t,i,e,s){let n=[];for(let[c,o]of e.entries())s[c]||n.push(o);let a=await i.getManyRaw(n),r=0;for await(let[c,o]of e.entries())if(!s[c]){let y=a[r];if(y&&y.value!==void 0){s[c]=y,this.emit("cache:hit",{key:o,value:y.value,store:"secondary"});let S=(0,l.getCascadingTtl)(this._ttl,this._primary.ttl),{expires:d}=y;d===null&&(d=void 0);let v=(0,l.calculateTtlFromExpiration)(S,d),p={key:o,value:y.value,ttl:v};await this.hook("BEFORE_SECONDARY_SETS_PRIMARY",p),await t.set(p.key,p.value,p.ttl)}else this.emit("cache:miss",{key:o,store:"secondary"});r++}}async processSecondaryForGetManyRawNonBlocking(t,i,e,s){let n=[];for(let[c,o]of e.entries())s[c]||n.push(o);let a=await i.getManyRaw(n),r=0;for await(let[c,o]of e.entries())if(!s[c]){let y=a[r];if(y&&y.value!==void 0){s[c]=y,this.emit("cache:hit",{key:o,value:y.value,store:"secondary"});let S=(0,l.getCascadingTtl)(this._ttl,this._primary.ttl),{expires:d}=y;d===null&&(d=void 0);let v=(0,l.calculateTtlFromExpiration)(S,d),p={key:o,value:y.value,ttl:v};this.hook("BEFORE_SECONDARY_SETS_PRIMARY",p).then(async()=>{await t.set(p.key,p.value,p.ttl)}).catch(b=>{this.emit("error",b)})}else this.emit("cache:miss",{key:o,store:"secondary"});r++}}setTtl(t){typeof t=="string"||t===void 0?this._ttl=t:t>0?this._ttl=t:this._ttl=void 0}};0&&(module.exports={Cacheable,CacheableEvents,CacheableHooks,CacheableMemory,CacheableStats,HashAlgorithm,Keyv,KeyvCacheableMemory,KeyvHooks,calculateTtlFromExpiration,createKeyv,getCascadingTtl,getOrSet,hash,shorthandToMilliseconds,shorthandToTime,wrap,wrapSync}); |
+104
-487
@@ -0,460 +1,10 @@ | ||
| import { WrapFunctionOptions, GetOrSetKey, GetOrSetFunctionOptions } from '@cacheable/memoize'; | ||
| export { GetOrSetFunctionOptions, GetOrSetKey, GetOrSetOptions, WrapOptions, WrapSyncOptions, getOrSet, wrap, wrapSync } from '@cacheable/memoize'; | ||
| import { Stats, CacheableItem, HashAlgorithm } from '@cacheable/utils'; | ||
| export { CacheableItem, Stats as CacheableStats, HashAlgorithm, calculateTtlFromExpiration, getCascadingTtl, hash, shorthandToMilliseconds, shorthandToTime } from '@cacheable/utils'; | ||
| import { Hookified } from 'hookified'; | ||
| import { Keyv, KeyvStoreAdapter, StoredData, StoredDataRaw } from 'keyv'; | ||
| import { Keyv, KeyvStoreAdapter, StoredDataRaw } from 'keyv'; | ||
| export { Keyv, KeyvHooks, KeyvOptions, KeyvStoreAdapter } from 'keyv'; | ||
| export { CacheableMemory, CacheableMemoryOptions, KeyvCacheableMemory, KeyvCacheableMemoryOptions, createKeyv } from '@cacheable/memory'; | ||
| /** | ||
| * CacheableItem | ||
| * @typedef {Object} CacheableItem | ||
| * @property {string} key - The key of the cacheable item | ||
| * @property {any} value - The value of the cacheable item | ||
| * @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. | ||
| */ | ||
| type CacheableItem = { | ||
| key: string; | ||
| value: any; | ||
| ttl?: number | string; | ||
| }; | ||
| type CacheableStoreItem = { | ||
| key: string; | ||
| value: any; | ||
| expires?: number; | ||
| }; | ||
| type CacheableOptions$1 = { | ||
| enabled?: boolean; | ||
| }; | ||
| declare class CacheableStats { | ||
| private _hits; | ||
| private _misses; | ||
| private _gets; | ||
| private _sets; | ||
| private _deletes; | ||
| private _clears; | ||
| private _vsize; | ||
| private _ksize; | ||
| private _count; | ||
| private _enabled; | ||
| constructor(options?: CacheableOptions$1); | ||
| /** | ||
| * @returns {boolean} - Whether the stats are enabled | ||
| */ | ||
| get enabled(): boolean; | ||
| /** | ||
| * @param {boolean} enabled - Whether to enable the stats | ||
| */ | ||
| set enabled(enabled: boolean); | ||
| /** | ||
| * @returns {number} - The number of hits | ||
| * @readonly | ||
| */ | ||
| get hits(): number; | ||
| /** | ||
| * @returns {number} - The number of misses | ||
| * @readonly | ||
| */ | ||
| get misses(): number; | ||
| /** | ||
| * @returns {number} - The number of gets | ||
| * @readonly | ||
| */ | ||
| get gets(): number; | ||
| /** | ||
| * @returns {number} - The number of sets | ||
| * @readonly | ||
| */ | ||
| get sets(): number; | ||
| /** | ||
| * @returns {number} - The number of deletes | ||
| * @readonly | ||
| */ | ||
| get deletes(): number; | ||
| /** | ||
| * @returns {number} - The number of clears | ||
| * @readonly | ||
| */ | ||
| get clears(): number; | ||
| /** | ||
| * @returns {number} - The vsize (value size) of the cache instance | ||
| * @readonly | ||
| */ | ||
| get vsize(): number; | ||
| /** | ||
| * @returns {number} - The ksize (key size) of the cache instance | ||
| * @readonly | ||
| */ | ||
| get ksize(): number; | ||
| /** | ||
| * @returns {number} - The count of the cache instance | ||
| * @readonly | ||
| */ | ||
| get count(): number; | ||
| incrementHits(): void; | ||
| incrementMisses(): void; | ||
| incrementGets(): void; | ||
| incrementSets(): void; | ||
| incrementDeletes(): void; | ||
| incrementClears(): void; | ||
| incrementVSize(value: any): void; | ||
| decreaseVSize(value: any): void; | ||
| incrementKSize(key: string): void; | ||
| decreaseKSize(key: string): void; | ||
| incrementCount(): void; | ||
| decreaseCount(): void; | ||
| setCount(count: number): void; | ||
| roughSizeOfString(value: string): number; | ||
| roughSizeOfObject(object: any): number; | ||
| reset(): void; | ||
| resetStoreValues(): void; | ||
| } | ||
| type GetOrSetKey = string | ((options?: GetOrSetOptions) => string); | ||
| type GetOrSetFunctionOptions = { | ||
| ttl?: number | string; | ||
| cacheErrors?: boolean; | ||
| throwErrors?: boolean; | ||
| }; | ||
| type GetOrSetOptions = GetOrSetFunctionOptions & { | ||
| cacheId?: string; | ||
| cache: Cacheable; | ||
| }; | ||
| type CreateWrapKey = (function_: AnyFunction, arguments_: any[], options?: WrapFunctionOptions) => string; | ||
| type WrapFunctionOptions = { | ||
| ttl?: number | string; | ||
| keyPrefix?: string; | ||
| createKey?: CreateWrapKey; | ||
| cacheErrors?: boolean; | ||
| cacheId?: string; | ||
| }; | ||
| type WrapOptions = WrapFunctionOptions & { | ||
| cache: Cacheable; | ||
| }; | ||
| type WrapSyncOptions = WrapFunctionOptions & { | ||
| cache: CacheableMemory; | ||
| }; | ||
| type AnyFunction = (...arguments_: any[]) => any; | ||
| declare function wrapSync<T>(function_: AnyFunction, options: WrapSyncOptions): AnyFunction; | ||
| declare function getOrSet<T>(key: GetOrSetKey, function_: () => Promise<T>, options: GetOrSetOptions): Promise<T | undefined>; | ||
| declare function wrap<T>(function_: AnyFunction, options: WrapOptions): AnyFunction; | ||
| declare enum StoreHashAlgorithm { | ||
| SHA256 = "sha256", | ||
| SHA1 = "sha1", | ||
| MD5 = "md5", | ||
| djb2Hash = "djb2Hash" | ||
| } | ||
| 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?: StoreHashAlgorithm | ((key: string, storeHashSize: number) => number); | ||
| }; | ||
| type SetOptions = { | ||
| ttl?: number | string; | ||
| expire?: number | Date; | ||
| }; | ||
| 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 {StoreHashAlgorithm | StoreHashAlgorithmFunction} - The store hash algorithm | ||
| */ | ||
| get storeHashAlgorithm(): StoreHashAlgorithm | StoreHashAlgorithmFunction; | ||
| /** | ||
| * Sets the store hash algorithm. This will recreate the store and all data will be cleared | ||
| * @param {StoreHashAlgorithm | StoreHashAlgorithmFunction} value - The store hash algorithm | ||
| */ | ||
| set storeHashAlgorithm(value: StoreHashAlgorithm | 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; | ||
| /** | ||
| * 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; | ||
| } | ||
| 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; | ||
| declare const shorthandToMilliseconds: (shorthand?: string | number) => number | undefined; | ||
| declare const shorthandToTime: (shorthand?: string | number, fromDate?: Date) => number; | ||
| declare enum CacheableHooks { | ||
| 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_SECONDARY_SETS_PRIMARY = "BEFORE_SECONDARY_SETS_PRIMARY" | ||
| } | ||
| declare enum CacheableEvents { | ||
| ERROR = "error" | ||
| } | ||
| type CacheableOptions = { | ||
@@ -494,2 +44,27 @@ /** | ||
| }; | ||
| type GetOptions = { | ||
| /** | ||
| * If set, this will bypass the instances nonBlocking setting. | ||
| * @type {boolean} | ||
| */ | ||
| nonBlocking?: boolean; | ||
| }; | ||
| declare enum CacheableHooks { | ||
| 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_SECONDARY_SETS_PRIMARY = "BEFORE_SECONDARY_SETS_PRIMARY" | ||
| } | ||
| declare enum CacheableEvents { | ||
| ERROR = "error", | ||
| CACHE_HIT = "cache:hit", | ||
| CACHE_MISS = "cache:miss" | ||
| } | ||
| declare class Cacheable extends Hookified { | ||
@@ -523,3 +98,3 @@ private _primary; | ||
| */ | ||
| get stats(): CacheableStats; | ||
| get stats(): Stats; | ||
| /** | ||
@@ -628,3 +203,3 @@ * The primary store for the cacheable instance | ||
| /** | ||
| * Retrieves an entry from the cache, with an optional “raw” mode. | ||
| * Retrieves an entry from the cache. | ||
| * | ||
@@ -636,15 +211,34 @@ * Checks the primary store first; if not found and a secondary store is configured, | ||
| * @param {string} key - The cache key to retrieve. | ||
| * @param {{ raw?: boolean }} [opts] - Options for retrieval. | ||
| * @param {boolean} [opts.raw=false] - If `true`, returns the full raw data object | ||
| * (`StoredDataRaw<T>`); otherwise returns just the value. | ||
| * @returns {Promise<T | StoredDataRaw<T> | undefined>} | ||
| * A promise that resolves to the cached value (or raw data) if found, or `undefined`. | ||
| * @param {GetOptions} - options such as to bypass `nonBlocking` for this call | ||
| * @returns {Promise<T | undefined>} | ||
| * A promise that resolves to the cached value if found, or `undefined`. | ||
| */ | ||
| get<T>(key: string, options?: { | ||
| raw?: false; | ||
| }): Promise<T | undefined>; | ||
| get<T>(key: string, options: { | ||
| raw: true; | ||
| }): Promise<StoredDataRaw<T>>; | ||
| get<T>(key: string, options?: GetOptions): Promise<T | undefined>; | ||
| /** | ||
| * Retrieves the raw entry from the cache including metadata like expiration. | ||
| * | ||
| * Checks the primary store first; if not found and a secondary store is configured, | ||
| * it will fetch from the secondary, repopulate the primary, and return the result. | ||
| * | ||
| * @typeParam T - The expected type of the stored value. | ||
| * @param {string} key - The cache key to retrieve. | ||
| * @param {GetOptions} - options such as to bypass `nonBlocking` for this call | ||
| * @returns {Promise<StoredDataRaw<T>>} | ||
| * A promise that resolves to the full raw data object if found, or undefined. | ||
| */ | ||
| getRaw<T>(key: string, options?: GetOptions): Promise<StoredDataRaw<T>>; | ||
| /** | ||
| * Retrieves multiple raw entries from the cache including metadata like expiration. | ||
| * | ||
| * Checks the primary store for each key; if a key is missing and a secondary store is configured, | ||
| * it will fetch from the secondary store, repopulate the primary store, and return the results. | ||
| * | ||
| * @typeParam T - The expected type of the stored values. | ||
| * @param {string[]} keys - The cache keys to retrieve. | ||
| * @param {GetOptions} - options such as to bypass `nonBlocking` on this call | ||
| * @returns {Promise<Array<StoredDataRaw<T>>>} | ||
| * A promise that resolves to an array of raw data objects. | ||
| */ | ||
| getManyRaw<T>(keys: string[], options?: GetOptions): Promise<Array<StoredDataRaw<T>>>; | ||
| /** | ||
| * Retrieves multiple entries from the cache. | ||
@@ -656,16 +250,7 @@ * Checks the primary store for each key; if a key is missing and a secondary store is configured, | ||
| * @param {string[]} keys - The cache keys to retrieve. | ||
| * @param {{ raw?: boolean }} [options] - Options for retrieval. | ||
| * @param {boolean} [options.raw=false] - When `true`, returns an array of raw data objects (`StoredDataRaw<T>`); | ||
| * when `false`, returns an array of unwrapped values (`T`) or `undefined` for misses. | ||
| * @returns {Promise<Array<T | undefined>> | Promise<Array<StoredDataRaw<T>>>} | ||
| * A promise that resolves to: | ||
| * - `Array<T | undefined>` if `raw` is `false` (default). | ||
| * - `Array<StoredDataRaw<T>>` if `raw` is `true`. | ||
| * @param {GetOptions} - options such as to bypass `nonBlocking` on this call | ||
| * @returns {Promise<Array<T | undefined>>} | ||
| * A promise that resolves to an array of cached values or `undefined` for misses. | ||
| */ | ||
| getMany<T>(keys: string[], options?: { | ||
| raw?: false; | ||
| }): Promise<Array<T | undefined>>; | ||
| getMany<T>(keys: string[], options: { | ||
| raw: true; | ||
| }): Promise<Array<StoredDataRaw<T>>>; | ||
| getMany<T>(keys: string[], options?: GetOptions): Promise<Array<T | undefined>>; | ||
| /** | ||
@@ -758,11 +343,43 @@ * Sets the value of the key. If the secondary store is set then it will also set the value in the secondary store. | ||
| */ | ||
| hash(object: any, algorithm?: string): string; | ||
| private getSecondaryRawResults; | ||
| private getManySecondaryRawResults; | ||
| private deleteManyKeyv; | ||
| hash(object: any, algorithm?: HashAlgorithm): string; | ||
| private setManyKeyv; | ||
| private hasManyKeyv; | ||
| /** | ||
| * Processes a single key from secondary store for getRaw operation | ||
| * @param primary - the primary store to use | ||
| * @param secondary - the secondary store to use | ||
| * @param key - The key to retrieve from secondary store | ||
| * @returns Promise containing the result and TTL information | ||
| */ | ||
| private processSecondaryForGetRaw; | ||
| /** | ||
| * Processes a single key from secondary store for getRaw operation in non-blocking mode | ||
| * Non-blocking mode means we don't wait for secondary operations that update primary store | ||
| * @param primary - the primary store to use | ||
| * @param secondary - the secondary store to use | ||
| * @param key - The key to retrieve from secondary store | ||
| * @returns Promise containing the result and TTL information | ||
| */ | ||
| private processSecondaryForGetRawNonBlocking; | ||
| /** | ||
| * Processes missing keys from secondary store for getManyRaw operation | ||
| * @param primary - the primary store to use | ||
| * @param secondary - the secondary store to use | ||
| * @param keys - The original array of keys requested | ||
| * @param result - The result array from primary store (will be modified) | ||
| * @returns Promise<void> | ||
| */ | ||
| private processSecondaryForGetManyRaw; | ||
| /** | ||
| * Processes missing keys from secondary store for getManyRaw operation in non-blocking mode | ||
| * Non-blocking mode means we don't wait for secondary operations that update primary store | ||
| * @param secondary - the secondary store to use | ||
| * @param keys - The original array of keys requested | ||
| * @param result - The result array from primary store (will be modified) | ||
| * @returns Promise<void> | ||
| */ | ||
| private processSecondaryForGetManyRawNonBlocking; | ||
| private setTtl; | ||
| } | ||
| export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableMemoryOptions, type CacheableOptions, CacheableStats, type GetOrSetFunctionOptions, type GetOrSetKey, type GetOrSetOptions, KeyvCacheableMemory, type WrapOptions, type WrapSyncOptions, createKeyv, getOrSet, shorthandToMilliseconds, shorthandToTime, wrap, wrapSync }; | ||
| export { Cacheable, CacheableEvents, CacheableHooks, type CacheableOptions }; |
+104
-487
@@ -0,460 +1,10 @@ | ||
| import { WrapFunctionOptions, GetOrSetKey, GetOrSetFunctionOptions } from '@cacheable/memoize'; | ||
| export { GetOrSetFunctionOptions, GetOrSetKey, GetOrSetOptions, WrapOptions, WrapSyncOptions, getOrSet, wrap, wrapSync } from '@cacheable/memoize'; | ||
| import { Stats, CacheableItem, HashAlgorithm } from '@cacheable/utils'; | ||
| export { CacheableItem, Stats as CacheableStats, HashAlgorithm, calculateTtlFromExpiration, getCascadingTtl, hash, shorthandToMilliseconds, shorthandToTime } from '@cacheable/utils'; | ||
| import { Hookified } from 'hookified'; | ||
| import { Keyv, KeyvStoreAdapter, StoredData, StoredDataRaw } from 'keyv'; | ||
| import { Keyv, KeyvStoreAdapter, StoredDataRaw } from 'keyv'; | ||
| export { Keyv, KeyvHooks, KeyvOptions, KeyvStoreAdapter } from 'keyv'; | ||
| export { CacheableMemory, CacheableMemoryOptions, KeyvCacheableMemory, KeyvCacheableMemoryOptions, createKeyv } from '@cacheable/memory'; | ||
| /** | ||
| * CacheableItem | ||
| * @typedef {Object} CacheableItem | ||
| * @property {string} key - The key of the cacheable item | ||
| * @property {any} value - The value of the cacheable item | ||
| * @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. | ||
| */ | ||
| type CacheableItem = { | ||
| key: string; | ||
| value: any; | ||
| ttl?: number | string; | ||
| }; | ||
| type CacheableStoreItem = { | ||
| key: string; | ||
| value: any; | ||
| expires?: number; | ||
| }; | ||
| type CacheableOptions$1 = { | ||
| enabled?: boolean; | ||
| }; | ||
| declare class CacheableStats { | ||
| private _hits; | ||
| private _misses; | ||
| private _gets; | ||
| private _sets; | ||
| private _deletes; | ||
| private _clears; | ||
| private _vsize; | ||
| private _ksize; | ||
| private _count; | ||
| private _enabled; | ||
| constructor(options?: CacheableOptions$1); | ||
| /** | ||
| * @returns {boolean} - Whether the stats are enabled | ||
| */ | ||
| get enabled(): boolean; | ||
| /** | ||
| * @param {boolean} enabled - Whether to enable the stats | ||
| */ | ||
| set enabled(enabled: boolean); | ||
| /** | ||
| * @returns {number} - The number of hits | ||
| * @readonly | ||
| */ | ||
| get hits(): number; | ||
| /** | ||
| * @returns {number} - The number of misses | ||
| * @readonly | ||
| */ | ||
| get misses(): number; | ||
| /** | ||
| * @returns {number} - The number of gets | ||
| * @readonly | ||
| */ | ||
| get gets(): number; | ||
| /** | ||
| * @returns {number} - The number of sets | ||
| * @readonly | ||
| */ | ||
| get sets(): number; | ||
| /** | ||
| * @returns {number} - The number of deletes | ||
| * @readonly | ||
| */ | ||
| get deletes(): number; | ||
| /** | ||
| * @returns {number} - The number of clears | ||
| * @readonly | ||
| */ | ||
| get clears(): number; | ||
| /** | ||
| * @returns {number} - The vsize (value size) of the cache instance | ||
| * @readonly | ||
| */ | ||
| get vsize(): number; | ||
| /** | ||
| * @returns {number} - The ksize (key size) of the cache instance | ||
| * @readonly | ||
| */ | ||
| get ksize(): number; | ||
| /** | ||
| * @returns {number} - The count of the cache instance | ||
| * @readonly | ||
| */ | ||
| get count(): number; | ||
| incrementHits(): void; | ||
| incrementMisses(): void; | ||
| incrementGets(): void; | ||
| incrementSets(): void; | ||
| incrementDeletes(): void; | ||
| incrementClears(): void; | ||
| incrementVSize(value: any): void; | ||
| decreaseVSize(value: any): void; | ||
| incrementKSize(key: string): void; | ||
| decreaseKSize(key: string): void; | ||
| incrementCount(): void; | ||
| decreaseCount(): void; | ||
| setCount(count: number): void; | ||
| roughSizeOfString(value: string): number; | ||
| roughSizeOfObject(object: any): number; | ||
| reset(): void; | ||
| resetStoreValues(): void; | ||
| } | ||
| type GetOrSetKey = string | ((options?: GetOrSetOptions) => string); | ||
| type GetOrSetFunctionOptions = { | ||
| ttl?: number | string; | ||
| cacheErrors?: boolean; | ||
| throwErrors?: boolean; | ||
| }; | ||
| type GetOrSetOptions = GetOrSetFunctionOptions & { | ||
| cacheId?: string; | ||
| cache: Cacheable; | ||
| }; | ||
| type CreateWrapKey = (function_: AnyFunction, arguments_: any[], options?: WrapFunctionOptions) => string; | ||
| type WrapFunctionOptions = { | ||
| ttl?: number | string; | ||
| keyPrefix?: string; | ||
| createKey?: CreateWrapKey; | ||
| cacheErrors?: boolean; | ||
| cacheId?: string; | ||
| }; | ||
| type WrapOptions = WrapFunctionOptions & { | ||
| cache: Cacheable; | ||
| }; | ||
| type WrapSyncOptions = WrapFunctionOptions & { | ||
| cache: CacheableMemory; | ||
| }; | ||
| type AnyFunction = (...arguments_: any[]) => any; | ||
| declare function wrapSync<T>(function_: AnyFunction, options: WrapSyncOptions): AnyFunction; | ||
| declare function getOrSet<T>(key: GetOrSetKey, function_: () => Promise<T>, options: GetOrSetOptions): Promise<T | undefined>; | ||
| declare function wrap<T>(function_: AnyFunction, options: WrapOptions): AnyFunction; | ||
| declare enum StoreHashAlgorithm { | ||
| SHA256 = "sha256", | ||
| SHA1 = "sha1", | ||
| MD5 = "md5", | ||
| djb2Hash = "djb2Hash" | ||
| } | ||
| 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?: StoreHashAlgorithm | ((key: string, storeHashSize: number) => number); | ||
| }; | ||
| type SetOptions = { | ||
| ttl?: number | string; | ||
| expire?: number | Date; | ||
| }; | ||
| 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 {StoreHashAlgorithm | StoreHashAlgorithmFunction} - The store hash algorithm | ||
| */ | ||
| get storeHashAlgorithm(): StoreHashAlgorithm | StoreHashAlgorithmFunction; | ||
| /** | ||
| * Sets the store hash algorithm. This will recreate the store and all data will be cleared | ||
| * @param {StoreHashAlgorithm | StoreHashAlgorithmFunction} value - The store hash algorithm | ||
| */ | ||
| set storeHashAlgorithm(value: StoreHashAlgorithm | 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; | ||
| /** | ||
| * 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; | ||
| } | ||
| 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; | ||
| declare const shorthandToMilliseconds: (shorthand?: string | number) => number | undefined; | ||
| declare const shorthandToTime: (shorthand?: string | number, fromDate?: Date) => number; | ||
| declare enum CacheableHooks { | ||
| 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_SECONDARY_SETS_PRIMARY = "BEFORE_SECONDARY_SETS_PRIMARY" | ||
| } | ||
| declare enum CacheableEvents { | ||
| ERROR = "error" | ||
| } | ||
| type CacheableOptions = { | ||
@@ -494,2 +44,27 @@ /** | ||
| }; | ||
| type GetOptions = { | ||
| /** | ||
| * If set, this will bypass the instances nonBlocking setting. | ||
| * @type {boolean} | ||
| */ | ||
| nonBlocking?: boolean; | ||
| }; | ||
| declare enum CacheableHooks { | ||
| 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_SECONDARY_SETS_PRIMARY = "BEFORE_SECONDARY_SETS_PRIMARY" | ||
| } | ||
| declare enum CacheableEvents { | ||
| ERROR = "error", | ||
| CACHE_HIT = "cache:hit", | ||
| CACHE_MISS = "cache:miss" | ||
| } | ||
| declare class Cacheable extends Hookified { | ||
@@ -523,3 +98,3 @@ private _primary; | ||
| */ | ||
| get stats(): CacheableStats; | ||
| get stats(): Stats; | ||
| /** | ||
@@ -628,3 +203,3 @@ * The primary store for the cacheable instance | ||
| /** | ||
| * Retrieves an entry from the cache, with an optional “raw” mode. | ||
| * Retrieves an entry from the cache. | ||
| * | ||
@@ -636,15 +211,34 @@ * Checks the primary store first; if not found and a secondary store is configured, | ||
| * @param {string} key - The cache key to retrieve. | ||
| * @param {{ raw?: boolean }} [opts] - Options for retrieval. | ||
| * @param {boolean} [opts.raw=false] - If `true`, returns the full raw data object | ||
| * (`StoredDataRaw<T>`); otherwise returns just the value. | ||
| * @returns {Promise<T | StoredDataRaw<T> | undefined>} | ||
| * A promise that resolves to the cached value (or raw data) if found, or `undefined`. | ||
| * @param {GetOptions} - options such as to bypass `nonBlocking` for this call | ||
| * @returns {Promise<T | undefined>} | ||
| * A promise that resolves to the cached value if found, or `undefined`. | ||
| */ | ||
| get<T>(key: string, options?: { | ||
| raw?: false; | ||
| }): Promise<T | undefined>; | ||
| get<T>(key: string, options: { | ||
| raw: true; | ||
| }): Promise<StoredDataRaw<T>>; | ||
| get<T>(key: string, options?: GetOptions): Promise<T | undefined>; | ||
| /** | ||
| * Retrieves the raw entry from the cache including metadata like expiration. | ||
| * | ||
| * Checks the primary store first; if not found and a secondary store is configured, | ||
| * it will fetch from the secondary, repopulate the primary, and return the result. | ||
| * | ||
| * @typeParam T - The expected type of the stored value. | ||
| * @param {string} key - The cache key to retrieve. | ||
| * @param {GetOptions} - options such as to bypass `nonBlocking` for this call | ||
| * @returns {Promise<StoredDataRaw<T>>} | ||
| * A promise that resolves to the full raw data object if found, or undefined. | ||
| */ | ||
| getRaw<T>(key: string, options?: GetOptions): Promise<StoredDataRaw<T>>; | ||
| /** | ||
| * Retrieves multiple raw entries from the cache including metadata like expiration. | ||
| * | ||
| * Checks the primary store for each key; if a key is missing and a secondary store is configured, | ||
| * it will fetch from the secondary store, repopulate the primary store, and return the results. | ||
| * | ||
| * @typeParam T - The expected type of the stored values. | ||
| * @param {string[]} keys - The cache keys to retrieve. | ||
| * @param {GetOptions} - options such as to bypass `nonBlocking` on this call | ||
| * @returns {Promise<Array<StoredDataRaw<T>>>} | ||
| * A promise that resolves to an array of raw data objects. | ||
| */ | ||
| getManyRaw<T>(keys: string[], options?: GetOptions): Promise<Array<StoredDataRaw<T>>>; | ||
| /** | ||
| * Retrieves multiple entries from the cache. | ||
@@ -656,16 +250,7 @@ * Checks the primary store for each key; if a key is missing and a secondary store is configured, | ||
| * @param {string[]} keys - The cache keys to retrieve. | ||
| * @param {{ raw?: boolean }} [options] - Options for retrieval. | ||
| * @param {boolean} [options.raw=false] - When `true`, returns an array of raw data objects (`StoredDataRaw<T>`); | ||
| * when `false`, returns an array of unwrapped values (`T`) or `undefined` for misses. | ||
| * @returns {Promise<Array<T | undefined>> | Promise<Array<StoredDataRaw<T>>>} | ||
| * A promise that resolves to: | ||
| * - `Array<T | undefined>` if `raw` is `false` (default). | ||
| * - `Array<StoredDataRaw<T>>` if `raw` is `true`. | ||
| * @param {GetOptions} - options such as to bypass `nonBlocking` on this call | ||
| * @returns {Promise<Array<T | undefined>>} | ||
| * A promise that resolves to an array of cached values or `undefined` for misses. | ||
| */ | ||
| getMany<T>(keys: string[], options?: { | ||
| raw?: false; | ||
| }): Promise<Array<T | undefined>>; | ||
| getMany<T>(keys: string[], options: { | ||
| raw: true; | ||
| }): Promise<Array<StoredDataRaw<T>>>; | ||
| getMany<T>(keys: string[], options?: GetOptions): Promise<Array<T | undefined>>; | ||
| /** | ||
@@ -758,11 +343,43 @@ * Sets the value of the key. If the secondary store is set then it will also set the value in the secondary store. | ||
| */ | ||
| hash(object: any, algorithm?: string): string; | ||
| private getSecondaryRawResults; | ||
| private getManySecondaryRawResults; | ||
| private deleteManyKeyv; | ||
| hash(object: any, algorithm?: HashAlgorithm): string; | ||
| private setManyKeyv; | ||
| private hasManyKeyv; | ||
| /** | ||
| * Processes a single key from secondary store for getRaw operation | ||
| * @param primary - the primary store to use | ||
| * @param secondary - the secondary store to use | ||
| * @param key - The key to retrieve from secondary store | ||
| * @returns Promise containing the result and TTL information | ||
| */ | ||
| private processSecondaryForGetRaw; | ||
| /** | ||
| * Processes a single key from secondary store for getRaw operation in non-blocking mode | ||
| * Non-blocking mode means we don't wait for secondary operations that update primary store | ||
| * @param primary - the primary store to use | ||
| * @param secondary - the secondary store to use | ||
| * @param key - The key to retrieve from secondary store | ||
| * @returns Promise containing the result and TTL information | ||
| */ | ||
| private processSecondaryForGetRawNonBlocking; | ||
| /** | ||
| * Processes missing keys from secondary store for getManyRaw operation | ||
| * @param primary - the primary store to use | ||
| * @param secondary - the secondary store to use | ||
| * @param keys - The original array of keys requested | ||
| * @param result - The result array from primary store (will be modified) | ||
| * @returns Promise<void> | ||
| */ | ||
| private processSecondaryForGetManyRaw; | ||
| /** | ||
| * Processes missing keys from secondary store for getManyRaw operation in non-blocking mode | ||
| * Non-blocking mode means we don't wait for secondary operations that update primary store | ||
| * @param secondary - the secondary store to use | ||
| * @param keys - The original array of keys requested | ||
| * @param result - The result array from primary store (will be modified) | ||
| * @returns Promise<void> | ||
| */ | ||
| private processSecondaryForGetManyRawNonBlocking; | ||
| private setTtl; | ||
| } | ||
| export { Cacheable, CacheableEvents, CacheableHooks, type CacheableItem, CacheableMemory, type CacheableMemoryOptions, type CacheableOptions, CacheableStats, type GetOrSetFunctionOptions, type GetOrSetKey, type GetOrSetOptions, KeyvCacheableMemory, type WrapOptions, type WrapSyncOptions, createKeyv, getOrSet, shorthandToMilliseconds, shorthandToTime, wrap, wrapSync }; | ||
| export { Cacheable, CacheableEvents, CacheableHooks, type CacheableOptions }; |
+1
-1860
@@ -1,1860 +0,1 @@ | ||
| // src/index.ts | ||
| import { Hookified as Hookified2 } from "hookified"; | ||
| import { Keyv as Keyv2 } from "keyv"; | ||
| // src/hash.ts | ||
| import * as crypto from "crypto"; | ||
| function hash(object, algorithm = "sha256") { | ||
| const objectString = JSON.stringify(object); | ||
| if (!crypto.getHashes().includes(algorithm)) { | ||
| throw new Error(`Unsupported hash algorithm: '${algorithm}'`); | ||
| } | ||
| const hasher = crypto.createHash(algorithm); | ||
| hasher.update(objectString); | ||
| return hasher.digest("hex"); | ||
| } | ||
| function hashToNumber(object, min = 0, max = 10, algorithm = "sha256") { | ||
| const objectString = JSON.stringify(object); | ||
| if (!crypto.getHashes().includes(algorithm)) { | ||
| throw new Error(`Unsupported hash algorithm: '${algorithm}'`); | ||
| } | ||
| const hasher = crypto.createHash(algorithm); | ||
| hasher.update(objectString); | ||
| const hashHex = hasher.digest("hex"); | ||
| const hashNumber = Number.parseInt(hashHex, 16); | ||
| const range = max - min + 1; | ||
| return min + hashNumber % range; | ||
| } | ||
| function djb2Hash(string_, min = 0, max = 10) { | ||
| let hash2 = 5381; | ||
| for (let i = 0; i < string_.length; i++) { | ||
| hash2 = hash2 * 33 ^ string_.charCodeAt(i); | ||
| } | ||
| const range = max - min + 1; | ||
| return min + Math.abs(hash2) % range; | ||
| } | ||
| // src/keyv-memory.ts | ||
| import { Keyv } from "keyv"; | ||
| // src/memory.ts | ||
| 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; | ||
| } | ||
| get size() { | ||
| return this.nodesMap.size; | ||
| } | ||
| }; | ||
| // src/shorthand-time.ts | ||
| var shorthandToMilliseconds = (shorthand) => { | ||
| let milliseconds; | ||
| if (shorthand === void 0) { | ||
| return void 0; | ||
| } | ||
| if (typeof shorthand === "number") { | ||
| milliseconds = shorthand; | ||
| } else if (typeof shorthand === "string") { | ||
| shorthand = shorthand.trim(); | ||
| if (Number.isNaN(Number(shorthand))) { | ||
| const match = /^([\d.]+)\s*(ms|s|m|h|hr|d)$/i.exec(shorthand); | ||
| if (!match) { | ||
| throw new Error( | ||
| `Unsupported time format: "${shorthand}". Use 'ms', 's', 'm', 'h', 'hr', or 'd'.` | ||
| ); | ||
| } | ||
| const [, value, unit] = match; | ||
| const numericValue = Number.parseFloat(value); | ||
| const unitLower = unit.toLowerCase(); | ||
| switch (unitLower) { | ||
| case "ms": { | ||
| milliseconds = numericValue; | ||
| break; | ||
| } | ||
| case "s": { | ||
| milliseconds = numericValue * 1e3; | ||
| break; | ||
| } | ||
| case "m": { | ||
| milliseconds = numericValue * 1e3 * 60; | ||
| break; | ||
| } | ||
| case "h": { | ||
| milliseconds = numericValue * 1e3 * 60 * 60; | ||
| break; | ||
| } | ||
| case "hr": { | ||
| milliseconds = numericValue * 1e3 * 60 * 60; | ||
| break; | ||
| } | ||
| case "d": { | ||
| milliseconds = numericValue * 1e3 * 60 * 60 * 24; | ||
| break; | ||
| } | ||
| /* c8 ignore next 3 */ | ||
| default: { | ||
| milliseconds = Number(shorthand); | ||
| } | ||
| } | ||
| } else { | ||
| milliseconds = Number(shorthand); | ||
| } | ||
| } else { | ||
| throw new TypeError("Time must be a string or a number."); | ||
| } | ||
| return milliseconds; | ||
| }; | ||
| var shorthandToTime = (shorthand, fromDate) => { | ||
| fromDate ??= /* @__PURE__ */ new Date(); | ||
| const milliseconds = shorthandToMilliseconds(shorthand); | ||
| if (milliseconds === void 0) { | ||
| return fromDate.getTime(); | ||
| } | ||
| return fromDate.getTime() + milliseconds; | ||
| }; | ||
| // src/coalesce-async.ts | ||
| var callbacks = /* @__PURE__ */ new Map(); | ||
| function hasKey(key) { | ||
| return callbacks.has(key); | ||
| } | ||
| function addKey(key) { | ||
| callbacks.set(key, []); | ||
| } | ||
| function removeKey(key) { | ||
| callbacks.delete(key); | ||
| } | ||
| function addCallbackToKey(key, callback) { | ||
| const stash = getCallbacksByKey(key); | ||
| stash.push(callback); | ||
| callbacks.set(key, stash); | ||
| } | ||
| function getCallbacksByKey(key) { | ||
| return callbacks.get(key) ?? []; | ||
| } | ||
| async function enqueue(key) { | ||
| return new Promise((resolve, reject) => { | ||
| const callback = { resolve, reject }; | ||
| addCallbackToKey(key, callback); | ||
| }); | ||
| } | ||
| function dequeue(key) { | ||
| const stash = getCallbacksByKey(key); | ||
| removeKey(key); | ||
| return stash; | ||
| } | ||
| function coalesce(options) { | ||
| const { key, error, result } = options; | ||
| for (const callback of dequeue(key)) { | ||
| if (error) { | ||
| callback.reject(error); | ||
| } else { | ||
| callback.resolve(result); | ||
| } | ||
| } | ||
| } | ||
| async function coalesceAsync(key, fnc) { | ||
| if (!hasKey(key)) { | ||
| addKey(key); | ||
| try { | ||
| const result = await Promise.resolve(fnc()); | ||
| coalesce({ key, result }); | ||
| return result; | ||
| } catch (error) { | ||
| coalesce({ key, error }); | ||
| throw error; | ||
| } | ||
| } | ||
| return enqueue(key); | ||
| } | ||
| // src/wrap.ts | ||
| function wrapSync(function_, options) { | ||
| const { ttl, keyPrefix, cache } = options; | ||
| return (...arguments_) => { | ||
| let cacheKey = createWrapKey(function_, arguments_, keyPrefix); | ||
| if (options.createKey) { | ||
| cacheKey = options.createKey(function_, arguments_, options); | ||
| } | ||
| let value = cache.get(cacheKey); | ||
| if (value === void 0) { | ||
| try { | ||
| value = function_(...arguments_); | ||
| cache.set(cacheKey, value, ttl); | ||
| } catch (error) { | ||
| cache.emit("error", error); | ||
| if (options.cacheErrors) { | ||
| cache.set(cacheKey, error, ttl); | ||
| } | ||
| } | ||
| } | ||
| return value; | ||
| }; | ||
| } | ||
| async function getOrSet(key, function_, options) { | ||
| const keyString = typeof key === "function" ? key(options) : key; | ||
| let value = await options.cache.get(keyString); | ||
| if (value === void 0) { | ||
| const cacheId = options.cacheId ?? "default"; | ||
| const coalesceKey = `${cacheId}::${keyString}`; | ||
| value = await coalesceAsync(coalesceKey, async () => { | ||
| try { | ||
| const result = await function_(); | ||
| await options.cache.set(keyString, result, options.ttl); | ||
| return result; | ||
| } catch (error) { | ||
| options.cache.emit("error", error); | ||
| if (options.cacheErrors) { | ||
| await options.cache.set(keyString, error, options.ttl); | ||
| } | ||
| if (options.throwErrors) { | ||
| throw error; | ||
| } | ||
| } | ||
| }); | ||
| } | ||
| return value; | ||
| } | ||
| function wrap(function_, options) { | ||
| const { keyPrefix, cache } = options; | ||
| return async (...arguments_) => { | ||
| let cacheKey = createWrapKey(function_, arguments_, keyPrefix); | ||
| if (options.createKey) { | ||
| cacheKey = options.createKey(function_, arguments_, options); | ||
| } | ||
| return cache.getOrSet( | ||
| cacheKey, | ||
| async () => function_(...arguments_), | ||
| options | ||
| ); | ||
| }; | ||
| } | ||
| function createWrapKey(function_, arguments_, keyPrefix) { | ||
| if (!keyPrefix) { | ||
| return `${function_.name}::${hash(arguments_)}`; | ||
| } | ||
| return `${keyPrefix}::${function_.name}::${hash(arguments_)}`; | ||
| } | ||
| // src/memory.ts | ||
| var defaultStoreHashSize = 16; | ||
| var maximumMapSize = 16777216; | ||
| var CacheableMemory = class extends Hookified { | ||
| _lru = new DoublyLinkedList(); | ||
| _storeHashSize = defaultStoreHashSize; | ||
| _storeHashAlgorithm = "djb2Hash" /* djb2Hash */; | ||
| // 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 {StoreHashAlgorithm | 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 {StoreHashAlgorithm | StoreHashAlgorithmFunction} 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); | ||
| 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); | ||
| 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); | ||
| 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); | ||
| 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); | ||
| } | ||
| /** | ||
| * 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 (this._storeHashAlgorithm === "djb2Hash" /* djb2Hash */) { | ||
| return djb2Hash(key, 0, this._storeHashSize); | ||
| } | ||
| if (typeof this._storeHashAlgorithm === "function") { | ||
| return this._storeHashAlgorithm(key, this._storeHashSize); | ||
| } | ||
| return hashToNumber(key, 0, this._storeHashSize, 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); | ||
| } | ||
| /** | ||
| * 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); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * 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 | ||
| */ | ||
| wrap(function_, options) { | ||
| const wrapOptions = { | ||
| ttl: options?.ttl ?? this._ttl, | ||
| keyPrefix: options?.keyPrefix, | ||
| cache: this | ||
| }; | ||
| return wrapSync(function_, wrapOptions); | ||
| } | ||
| 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; | ||
| } | ||
| }; | ||
| // 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; | ||
| } | ||
| 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); | ||
| } | ||
| }; | ||
| 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/stats.ts | ||
| var CacheableStats = class { | ||
| _hits = 0; | ||
| _misses = 0; | ||
| _gets = 0; | ||
| _sets = 0; | ||
| _deletes = 0; | ||
| _clears = 0; | ||
| _vsize = 0; | ||
| _ksize = 0; | ||
| _count = 0; | ||
| _enabled = false; | ||
| constructor(options) { | ||
| if (options?.enabled) { | ||
| this._enabled = options.enabled; | ||
| } | ||
| } | ||
| /** | ||
| * @returns {boolean} - Whether the stats are enabled | ||
| */ | ||
| get enabled() { | ||
| return this._enabled; | ||
| } | ||
| /** | ||
| * @param {boolean} enabled - Whether to enable the stats | ||
| */ | ||
| set enabled(enabled) { | ||
| this._enabled = enabled; | ||
| } | ||
| /** | ||
| * @returns {number} - The number of hits | ||
| * @readonly | ||
| */ | ||
| get hits() { | ||
| return this._hits; | ||
| } | ||
| /** | ||
| * @returns {number} - The number of misses | ||
| * @readonly | ||
| */ | ||
| get misses() { | ||
| return this._misses; | ||
| } | ||
| /** | ||
| * @returns {number} - The number of gets | ||
| * @readonly | ||
| */ | ||
| get gets() { | ||
| return this._gets; | ||
| } | ||
| /** | ||
| * @returns {number} - The number of sets | ||
| * @readonly | ||
| */ | ||
| get sets() { | ||
| return this._sets; | ||
| } | ||
| /** | ||
| * @returns {number} - The number of deletes | ||
| * @readonly | ||
| */ | ||
| get deletes() { | ||
| return this._deletes; | ||
| } | ||
| /** | ||
| * @returns {number} - The number of clears | ||
| * @readonly | ||
| */ | ||
| get clears() { | ||
| return this._clears; | ||
| } | ||
| /** | ||
| * @returns {number} - The vsize (value size) of the cache instance | ||
| * @readonly | ||
| */ | ||
| get vsize() { | ||
| return this._vsize; | ||
| } | ||
| /** | ||
| * @returns {number} - The ksize (key size) of the cache instance | ||
| * @readonly | ||
| */ | ||
| get ksize() { | ||
| return this._ksize; | ||
| } | ||
| /** | ||
| * @returns {number} - The count of the cache instance | ||
| * @readonly | ||
| */ | ||
| get count() { | ||
| return this._count; | ||
| } | ||
| incrementHits() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._hits++; | ||
| } | ||
| incrementMisses() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._misses++; | ||
| } | ||
| incrementGets() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._gets++; | ||
| } | ||
| incrementSets() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._sets++; | ||
| } | ||
| incrementDeletes() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._deletes++; | ||
| } | ||
| incrementClears() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._clears++; | ||
| } | ||
| incrementVSize(value) { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._vsize += this.roughSizeOfObject(value); | ||
| } | ||
| decreaseVSize(value) { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._vsize -= this.roughSizeOfObject(value); | ||
| } | ||
| incrementKSize(key) { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._ksize += this.roughSizeOfString(key); | ||
| } | ||
| decreaseKSize(key) { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._ksize -= this.roughSizeOfString(key); | ||
| } | ||
| incrementCount() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._count++; | ||
| } | ||
| decreaseCount() { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._count--; | ||
| } | ||
| setCount(count) { | ||
| if (!this._enabled) { | ||
| return; | ||
| } | ||
| this._count = count; | ||
| } | ||
| roughSizeOfString(value) { | ||
| return value.length * 2; | ||
| } | ||
| roughSizeOfObject(object) { | ||
| const objectList = []; | ||
| const stack = [object]; | ||
| let bytes = 0; | ||
| while (stack.length > 0) { | ||
| const value = stack.pop(); | ||
| if (typeof value === "boolean") { | ||
| bytes += 4; | ||
| } else if (typeof value === "string") { | ||
| bytes += value.length * 2; | ||
| } else if (typeof value === "number") { | ||
| bytes += 8; | ||
| } else if (typeof value === "object" && value !== null && !objectList.includes(value)) { | ||
| objectList.push(value); | ||
| for (const key in value) { | ||
| bytes += key.length * 2; | ||
| stack.push(value[key]); | ||
| } | ||
| } | ||
| } | ||
| return bytes; | ||
| } | ||
| reset() { | ||
| this._hits = 0; | ||
| this._misses = 0; | ||
| this._gets = 0; | ||
| this._sets = 0; | ||
| this._deletes = 0; | ||
| this._clears = 0; | ||
| this._vsize = 0; | ||
| this._ksize = 0; | ||
| this._count = 0; | ||
| } | ||
| resetStoreValues() { | ||
| this._vsize = 0; | ||
| this._ksize = 0; | ||
| this._count = 0; | ||
| } | ||
| }; | ||
| // src/ttl.ts | ||
| function getTtlFromExpires(expires) { | ||
| if (expires === void 0 || expires === null) { | ||
| return void 0; | ||
| } | ||
| const now = Date.now(); | ||
| if (expires < now) { | ||
| return void 0; | ||
| } | ||
| return expires - now; | ||
| } | ||
| function getCascadingTtl(cacheableTtl, primaryTtl, secondaryTtl) { | ||
| return secondaryTtl ?? primaryTtl ?? shorthandToMilliseconds(cacheableTtl); | ||
| } | ||
| function calculateTtlFromExpiration(ttl, expires) { | ||
| const ttlFromExpires = getTtlFromExpires(expires); | ||
| const expiresFromTtl = ttl ? Date.now() + ttl : void 0; | ||
| if (ttlFromExpires === void 0) { | ||
| return ttl; | ||
| } | ||
| if (expiresFromTtl === void 0) { | ||
| return ttlFromExpires; | ||
| } | ||
| if (expires > expiresFromTtl) { | ||
| return ttl; | ||
| } | ||
| return ttlFromExpires; | ||
| } | ||
| // src/index.ts | ||
| import { Keyv as Keyv3, KeyvHooks } from "keyv"; | ||
| var CacheableHooks = /* @__PURE__ */ ((CacheableHooks2) => { | ||
| CacheableHooks2["BEFORE_SET"] = "BEFORE_SET"; | ||
| CacheableHooks2["AFTER_SET"] = "AFTER_SET"; | ||
| CacheableHooks2["BEFORE_SET_MANY"] = "BEFORE_SET_MANY"; | ||
| CacheableHooks2["AFTER_SET_MANY"] = "AFTER_SET_MANY"; | ||
| CacheableHooks2["BEFORE_GET"] = "BEFORE_GET"; | ||
| CacheableHooks2["AFTER_GET"] = "AFTER_GET"; | ||
| CacheableHooks2["BEFORE_GET_MANY"] = "BEFORE_GET_MANY"; | ||
| CacheableHooks2["AFTER_GET_MANY"] = "AFTER_GET_MANY"; | ||
| CacheableHooks2["BEFORE_SECONDARY_SETS_PRIMARY"] = "BEFORE_SECONDARY_SETS_PRIMARY"; | ||
| return CacheableHooks2; | ||
| })(CacheableHooks || {}); | ||
| var CacheableEvents = /* @__PURE__ */ ((CacheableEvents2) => { | ||
| CacheableEvents2["ERROR"] = "error"; | ||
| return CacheableEvents2; | ||
| })(CacheableEvents || {}); | ||
| var Cacheable = class extends Hookified2 { | ||
| _primary = createKeyv(); | ||
| _secondary; | ||
| _nonBlocking = false; | ||
| _ttl; | ||
| _stats = new CacheableStats({ enabled: false }); | ||
| _namespace; | ||
| _cacheId = Math.random().toString(36).slice(2); | ||
| /** | ||
| * Creates a new cacheable instance | ||
| * @param {CacheableOptions} [options] The options for the cacheable instance | ||
| */ | ||
| constructor(options) { | ||
| super(); | ||
| if (options?.primary) { | ||
| this.setPrimary(options.primary); | ||
| } | ||
| if (options?.secondary) { | ||
| this.setSecondary(options.secondary); | ||
| } | ||
| if (options?.nonBlocking) { | ||
| this._nonBlocking = options.nonBlocking; | ||
| } | ||
| if (options?.stats) { | ||
| this._stats.enabled = options.stats; | ||
| } | ||
| if (options?.ttl) { | ||
| this.setTtl(options.ttl); | ||
| } | ||
| if (options?.cacheId) { | ||
| this._cacheId = options.cacheId; | ||
| } | ||
| if (options?.namespace) { | ||
| this._namespace = options.namespace; | ||
| this._primary.namespace = this.getNameSpace(); | ||
| if (this._secondary) { | ||
| this._secondary.namespace = this.getNameSpace(); | ||
| } | ||
| } | ||
| } | ||
| /** | ||
| * The namespace for the cacheable instance | ||
| * @returns {string | (() => string) | undefined} The namespace for the cacheable instance | ||
| */ | ||
| get namespace() { | ||
| return this._namespace; | ||
| } | ||
| /** | ||
| * Sets the namespace for the cacheable instance | ||
| * @param {string | (() => string) | undefined} namespace The namespace for the cacheable instance | ||
| * @returns {void} | ||
| */ | ||
| set namespace(namespace) { | ||
| this._namespace = namespace; | ||
| this._primary.namespace = this.getNameSpace(); | ||
| if (this._secondary) { | ||
| this._secondary.namespace = this.getNameSpace(); | ||
| } | ||
| } | ||
| /** | ||
| * The statistics for the cacheable instance | ||
| * @returns {CacheableStats} The statistics for the cacheable instance | ||
| */ | ||
| get stats() { | ||
| return this._stats; | ||
| } | ||
| /** | ||
| * The primary store for the cacheable instance | ||
| * @returns {Keyv} The primary store for the cacheable instance | ||
| */ | ||
| get primary() { | ||
| return this._primary; | ||
| } | ||
| /** | ||
| * Sets the primary store for the cacheable instance | ||
| * @param {Keyv} primary The primary store for the cacheable instance | ||
| */ | ||
| set primary(primary) { | ||
| this._primary = primary; | ||
| } | ||
| /** | ||
| * The secondary store for the cacheable instance | ||
| * @returns {Keyv | undefined} The secondary store for the cacheable instance | ||
| */ | ||
| get secondary() { | ||
| return this._secondary; | ||
| } | ||
| /** | ||
| * Sets the secondary store for the cacheable instance. If it is set to undefined then the secondary store is disabled. | ||
| * @param {Keyv | undefined} secondary The secondary store for the cacheable instance | ||
| * @returns {void} | ||
| */ | ||
| set secondary(secondary) { | ||
| this._secondary = secondary; | ||
| } | ||
| /** | ||
| * Gets whether the secondary store is non-blocking mode. It is set to false by default. | ||
| * If it is set to true then the secondary store will not block the primary store. | ||
| * | ||
| * [Learn more about non-blocking mode](https://cacheable.org/docs/cacheable/#non-blocking-operations). | ||
| * | ||
| * @returns {boolean} Whether the cacheable instance is non-blocking | ||
| */ | ||
| get nonBlocking() { | ||
| return this._nonBlocking; | ||
| } | ||
| /** | ||
| * Sets whether the secondary store is non-blocking mode. It is set to false by default. | ||
| * If it is set to true then the secondary store will not block the primary store. | ||
| * | ||
| * [Learn more about non-blocking mode](https://cacheable.org/docs/cacheable/#non-blocking-operations). | ||
| * | ||
| * @param {boolean} nonBlocking Whether the cacheable instance is non-blocking | ||
| * @returns {void} | ||
| */ | ||
| set nonBlocking(nonBlocking) { | ||
| this._nonBlocking = nonBlocking; | ||
| } | ||
| /** | ||
| * The time-to-live for the cacheable instance and will be used as the default value. | ||
| * can be a number in milliseconds or a human-readable format such as `1s` for 1 second or `1h` for 1 hour | ||
| * or undefined if there is no time-to-live. | ||
| * | ||
| * [Learn more about time-to-live](https://cacheable.org/docs/cacheable/#shorthand-for-time-to-live-ttl). | ||
| * | ||
| * @returns {number | string | undefined} The time-to-live for the cacheable instance in milliseconds, human-readable format or undefined | ||
| * @example | ||
| * ```typescript | ||
| * const cacheable = new Cacheable({ ttl: '1h' }); | ||
| * console.log(cacheable.ttl); // 1h | ||
| * ``` | ||
| */ | ||
| get ttl() { | ||
| return this._ttl; | ||
| } | ||
| /** | ||
| * Sets the time-to-live for the cacheable instance and will be used as the default value. | ||
| * 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 | ||
| * there is no time-to-live. | ||
| * | ||
| * [Learn more about time-to-live](https://cacheable.org/docs/cacheable/#shorthand-for-time-to-live-ttl). | ||
| * | ||
| * @param {number | string | undefined} ttl The time-to-live for the cacheable instance | ||
| * @example | ||
| * ```typescript | ||
| * const cacheable = new Cacheable(); | ||
| * cacheable.ttl = '1h'; // Set the time-to-live to 1 hour | ||
| * ``` | ||
| * or setting the time-to-live in milliseconds | ||
| * ```typescript | ||
| * const cacheable = new Cacheable(); | ||
| * cacheable.ttl = 3600000; // Set the time-to-live to 1 hour | ||
| * ``` | ||
| */ | ||
| set ttl(ttl) { | ||
| this.setTtl(ttl); | ||
| } | ||
| /** | ||
| * The cacheId for the cacheable instance. This is primarily used for the wrap function to not have conflicts. | ||
| * If it is not set then it will be a random string that is generated | ||
| * @returns {string} The cacheId for the cacheable instance | ||
| */ | ||
| get cacheId() { | ||
| return this._cacheId; | ||
| } | ||
| /** | ||
| * Sets the cacheId for the cacheable instance. This is primarily used for the wrap function to not have conflicts. | ||
| * If it is not set then it will be a random string that is generated | ||
| * @param {string} cacheId The cacheId for the cacheable instance | ||
| */ | ||
| set cacheId(cacheId) { | ||
| this._cacheId = cacheId; | ||
| } | ||
| /** | ||
| * Sets the primary store for the cacheable instance | ||
| * @param {Keyv | KeyvStoreAdapter} primary The primary store for the cacheable instance | ||
| * @returns {void} | ||
| */ | ||
| setPrimary(primary) { | ||
| if (this.isKeyvInstance(primary)) { | ||
| this._primary = primary; | ||
| } else { | ||
| this._primary = new Keyv2(primary); | ||
| } | ||
| this._primary.on("error", (error) => { | ||
| this.emit("error" /* ERROR */, error); | ||
| }); | ||
| } | ||
| /** | ||
| * Sets the secondary store for the cacheable instance. If it is set to undefined then the secondary store is disabled. | ||
| * @param {Keyv | KeyvStoreAdapter} secondary The secondary store for the cacheable instance | ||
| * @returns {void} | ||
| */ | ||
| setSecondary(secondary) { | ||
| if (this.isKeyvInstance(secondary)) { | ||
| this._secondary = secondary; | ||
| } else { | ||
| this._secondary = new Keyv2(secondary); | ||
| } | ||
| this._secondary.on("error", (error) => { | ||
| this.emit("error" /* ERROR */, error); | ||
| }); | ||
| } | ||
| // biome-ignore lint/suspicious/noExplicitAny: type format | ||
| isKeyvInstance(keyv) { | ||
| if (keyv instanceof Keyv2) { | ||
| return true; | ||
| } | ||
| const keyvMethods = [ | ||
| "generateIterator", | ||
| "get", | ||
| "getMany", | ||
| "set", | ||
| "setMany", | ||
| "delete", | ||
| "deleteMany", | ||
| "has", | ||
| "hasMany", | ||
| "clear", | ||
| "disconnect", | ||
| "serialize", | ||
| "deserialize" | ||
| ]; | ||
| return keyvMethods.every((method) => typeof keyv[method] === "function"); | ||
| } | ||
| getNameSpace() { | ||
| if (typeof this._namespace === "function") { | ||
| return this._namespace(); | ||
| } | ||
| return this._namespace; | ||
| } | ||
| async get(key, options = {}) { | ||
| let result; | ||
| const { raw = false } = options; | ||
| try { | ||
| await this.hook("BEFORE_GET" /* BEFORE_GET */, key); | ||
| result = await this._primary.get(key, { raw: true }); | ||
| let ttl; | ||
| if (!result && this._secondary) { | ||
| const secondaryResult = await this.getSecondaryRawResults(key); | ||
| if (secondaryResult?.value) { | ||
| result = secondaryResult; | ||
| const cascadeTtl = getCascadingTtl(this._ttl, this._primary.ttl); | ||
| const expires = secondaryResult.expires ?? void 0; | ||
| ttl = calculateTtlFromExpiration(cascadeTtl, expires); | ||
| const setItem = { key, value: result.value, ttl }; | ||
| await this.hook( | ||
| "BEFORE_SECONDARY_SETS_PRIMARY" /* BEFORE_SECONDARY_SETS_PRIMARY */, | ||
| setItem | ||
| ); | ||
| await this._primary.set(setItem.key, setItem.value, setItem.ttl); | ||
| } | ||
| } | ||
| await this.hook("AFTER_GET" /* AFTER_GET */, { key, result, ttl }); | ||
| } catch (error) { | ||
| this.emit("error" /* ERROR */, error); | ||
| } | ||
| if (this.stats.enabled) { | ||
| if (result) { | ||
| this._stats.incrementHits(); | ||
| } else { | ||
| this._stats.incrementMisses(); | ||
| } | ||
| this.stats.incrementGets(); | ||
| } | ||
| return raw ? result : result?.value; | ||
| } | ||
| async getMany(keys, options = {}) { | ||
| let result = []; | ||
| const { raw = false } = options; | ||
| try { | ||
| await this.hook("BEFORE_GET_MANY" /* BEFORE_GET_MANY */, keys); | ||
| result = await this._primary.get(keys, { raw: true }); | ||
| if (this._secondary) { | ||
| const missingKeys = []; | ||
| for (const [i, key] of keys.entries()) { | ||
| if (!result[i]) { | ||
| missingKeys.push(key); | ||
| } | ||
| } | ||
| const secondaryResults = await this.getManySecondaryRawResults(missingKeys); | ||
| for await (const [i, key] of keys.entries()) { | ||
| if (!result[i] && secondaryResults[i]) { | ||
| result[i] = secondaryResults[i]; | ||
| const cascadeTtl = getCascadingTtl(this._ttl, this._primary.ttl); | ||
| let { expires } = secondaryResults[i]; | ||
| if (expires === null) { | ||
| expires = void 0; | ||
| } | ||
| const ttl = calculateTtlFromExpiration(cascadeTtl, expires); | ||
| const setItem = { key, value: result[i].value, ttl }; | ||
| await this.hook( | ||
| "BEFORE_SECONDARY_SETS_PRIMARY" /* BEFORE_SECONDARY_SETS_PRIMARY */, | ||
| setItem | ||
| ); | ||
| await this._primary.set(setItem.key, setItem.value, setItem.ttl); | ||
| } | ||
| } | ||
| } | ||
| await this.hook("AFTER_GET_MANY" /* AFTER_GET_MANY */, { keys, result }); | ||
| } catch (error) { | ||
| this.emit("error" /* ERROR */, error); | ||
| } | ||
| if (this.stats.enabled) { | ||
| for (const item of result) { | ||
| if (item) { | ||
| this._stats.incrementHits(); | ||
| } else { | ||
| this._stats.incrementMisses(); | ||
| } | ||
| } | ||
| this.stats.incrementGets(); | ||
| } | ||
| return raw ? result : result.map((item) => item?.value); | ||
| } | ||
| /** | ||
| * Sets the value of the key. If the secondary store is set then it will also set the value in the secondary store. | ||
| * @param {string} key the key to set the value of | ||
| * @param {T} value The value to set | ||
| * @param {number | string} [ttl] set a number it is miliseconds, 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. | ||
| * @returns {boolean} Whether the value was set | ||
| */ | ||
| async set(key, value, ttl) { | ||
| let result = false; | ||
| const finalTtl = shorthandToMilliseconds(ttl ?? this._ttl); | ||
| try { | ||
| const item = { key, value, ttl: finalTtl }; | ||
| await this.hook("BEFORE_SET" /* BEFORE_SET */, item); | ||
| const promises = []; | ||
| promises.push(this._primary.set(item.key, item.value, item.ttl)); | ||
| if (this._secondary) { | ||
| promises.push(this._secondary.set(item.key, item.value, item.ttl)); | ||
| } | ||
| if (this._nonBlocking) { | ||
| result = await Promise.race(promises); | ||
| } else { | ||
| const results = await Promise.all(promises); | ||
| result = results[0]; | ||
| } | ||
| await this.hook("AFTER_SET" /* AFTER_SET */, item); | ||
| } catch (error) { | ||
| this.emit("error" /* ERROR */, error); | ||
| } | ||
| if (this.stats.enabled) { | ||
| this.stats.incrementKSize(key); | ||
| this.stats.incrementCount(); | ||
| this.stats.incrementVSize(value); | ||
| this.stats.incrementSets(); | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Sets the values of the keys. If the secondary store is set then it will also set the values in the secondary store. | ||
| * @param {CacheableItem[]} items The items to set | ||
| * @returns {boolean} Whether the values were set | ||
| */ | ||
| async setMany(items) { | ||
| let result = false; | ||
| try { | ||
| await this.hook("BEFORE_SET_MANY" /* BEFORE_SET_MANY */, items); | ||
| result = await this.setManyKeyv(this._primary, items); | ||
| if (this._secondary) { | ||
| if (this._nonBlocking) { | ||
| this.setManyKeyv(this._secondary, items); | ||
| } else { | ||
| await this.setManyKeyv(this._secondary, items); | ||
| } | ||
| } | ||
| await this.hook("AFTER_SET_MANY" /* AFTER_SET_MANY */, items); | ||
| } catch (error) { | ||
| this.emit("error" /* ERROR */, error); | ||
| } | ||
| if (this.stats.enabled) { | ||
| for (const item of items) { | ||
| this.stats.incrementKSize(item.key); | ||
| this.stats.incrementCount(); | ||
| this.stats.incrementVSize(item.value); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Takes the value of the key and deletes the key. If the key does not exist then it will return undefined. | ||
| * @param {string} key The key to take the value of | ||
| * @returns {Promise<T | undefined>} The value of the key or undefined if the key does not exist | ||
| */ | ||
| async take(key) { | ||
| const result = await this.get(key); | ||
| await this.delete(key); | ||
| return result; | ||
| } | ||
| /** | ||
| * Takes the values of the keys and deletes the keys. If the key does not exist then it will return undefined. | ||
| * @param {string[]} keys The keys to take the values of | ||
| * @returns {Promise<Array<T | undefined>>} The values of the keys or undefined if the key does not exist | ||
| */ | ||
| async takeMany(keys) { | ||
| const result = await this.getMany(keys); | ||
| await this.deleteMany(keys); | ||
| return result; | ||
| } | ||
| /** | ||
| * Checks if the key exists in the primary store. If it does not exist then it will check the secondary store. | ||
| * @param {string} key The key to check | ||
| * @returns {Promise<boolean>} Whether the key exists | ||
| */ | ||
| async has(key) { | ||
| const promises = []; | ||
| promises.push(this._primary.has(key)); | ||
| if (this._secondary) { | ||
| promises.push(this._secondary.has(key)); | ||
| } | ||
| const resultAll = await Promise.all(promises); | ||
| for (const result of resultAll) { | ||
| if (result) { | ||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
| /** | ||
| * Checks if the keys exist in the primary store. If it does not exist then it will check the secondary store. | ||
| * @param {string[]} keys The keys to check | ||
| * @returns {Promise<boolean[]>} Whether the keys exist | ||
| */ | ||
| async hasMany(keys) { | ||
| const result = await this.hasManyKeyv(this._primary, keys); | ||
| const missingKeys = []; | ||
| for (const [i, key] of keys.entries()) { | ||
| if (!result[i] && this._secondary) { | ||
| missingKeys.push(key); | ||
| } | ||
| } | ||
| if (missingKeys.length > 0 && this._secondary) { | ||
| const secondary = await this.hasManyKeyv(this._secondary, keys); | ||
| for (const [i, _key] of keys.entries()) { | ||
| if (!result[i] && secondary[i]) { | ||
| result[i] = secondary[i]; | ||
| } | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Deletes the key from the primary store. If the secondary store is set then it will also delete the key from the secondary store. | ||
| * @param {string} key The key to delete | ||
| * @returns {Promise<boolean>} Whether the key was deleted | ||
| */ | ||
| async delete(key) { | ||
| let result = false; | ||
| const promises = []; | ||
| if (this.stats.enabled) { | ||
| const statResult = await this._primary.get(key); | ||
| if (statResult) { | ||
| this.stats.decreaseKSize(key); | ||
| this.stats.decreaseVSize(statResult); | ||
| this.stats.decreaseCount(); | ||
| this.stats.incrementDeletes(); | ||
| } | ||
| } | ||
| promises.push(this._primary.delete(key)); | ||
| if (this._secondary) { | ||
| promises.push(this._secondary.delete(key)); | ||
| } | ||
| if (this.nonBlocking) { | ||
| result = await Promise.race(promises); | ||
| } else { | ||
| const resultAll = await Promise.all(promises); | ||
| result = resultAll[0]; | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Deletes the keys from the primary store. If the secondary store is set then it will also delete the keys from the secondary store. | ||
| * @param {string[]} keys The keys to delete | ||
| * @returns {Promise<boolean>} Whether the keys were deleted | ||
| */ | ||
| async deleteMany(keys) { | ||
| if (this.stats.enabled) { | ||
| const statResult = await this._primary.get(keys); | ||
| for (const key of keys) { | ||
| this.stats.decreaseKSize(key); | ||
| this.stats.decreaseVSize(statResult); | ||
| this.stats.decreaseCount(); | ||
| this.stats.incrementDeletes(); | ||
| } | ||
| } | ||
| const result = await this.deleteManyKeyv(this._primary, keys); | ||
| if (this._secondary) { | ||
| if (this._nonBlocking) { | ||
| this.deleteManyKeyv(this._secondary, keys); | ||
| } else { | ||
| await this.deleteManyKeyv(this._secondary, keys); | ||
| } | ||
| } | ||
| return result; | ||
| } | ||
| /** | ||
| * Clears the primary store. If the secondary store is set then it will also clear the secondary store. | ||
| * @returns {Promise<void>} | ||
| */ | ||
| async clear() { | ||
| const promises = []; | ||
| promises.push(this._primary.clear()); | ||
| if (this._secondary) { | ||
| promises.push(this._secondary.clear()); | ||
| } | ||
| await (this._nonBlocking ? Promise.race(promises) : Promise.all(promises)); | ||
| if (this.stats.enabled) { | ||
| this._stats.resetStoreValues(); | ||
| this._stats.incrementClears(); | ||
| } | ||
| } | ||
| /** | ||
| * Disconnects the primary store. If the secondary store is set then it will also disconnect the secondary store. | ||
| * @returns {Promise<void>} | ||
| */ | ||
| async disconnect() { | ||
| const promises = []; | ||
| promises.push(this._primary.disconnect()); | ||
| if (this._secondary) { | ||
| promises.push(this._secondary.disconnect()); | ||
| } | ||
| await (this._nonBlocking ? Promise.race(promises) : Promise.all(promises)); | ||
| } | ||
| /** | ||
| * Wraps a function with caching | ||
| * | ||
| * [Learn more about wrapping functions](https://cacheable.org/docs/cacheable/#wrap--memoization-for-sync-and-async-functions). | ||
| * @param {Function} function_ The function to wrap | ||
| * @param {WrapOptions} [options] The options for the wrap function | ||
| * @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, | ||
| cache: this, | ||
| cacheId: this._cacheId | ||
| }; | ||
| return wrap(function_, wrapOptions); | ||
| } | ||
| /** | ||
| * Retrieves the value associated with the given key from the cache. If the key is not found, | ||
| * invokes the provided function to calculate the value, stores it in the cache, and then returns it. | ||
| * | ||
| * @param {GetOrSetKey} key - The key to retrieve or set in the cache. This can also be a function that returns a string key. | ||
| * If a function is provided, it will be called with the cache options to generate the key. | ||
| * @param {() => Promise<T>} function_ - The asynchronous function that computes the value to be cached if the key does not exist. | ||
| * @param {GetOrSetFunctionOptions} [options] - Optional settings for caching, such as the time to live (TTL) or whether to cache errors. | ||
| * @return {Promise<T | undefined>} - A promise that resolves to the cached or newly computed value, or undefined if an error occurs and caching is not configured for errors. | ||
| */ | ||
| async getOrSet(key, function_, options) { | ||
| const getOrSetOptions = { | ||
| cache: this, | ||
| cacheId: this._cacheId, | ||
| ttl: options?.ttl ?? this._ttl, | ||
| cacheErrors: options?.cacheErrors, | ||
| throwErrors: options?.throwErrors | ||
| }; | ||
| return getOrSet(key, function_, getOrSetOptions); | ||
| } | ||
| /** | ||
| * Will hash an object using the specified algorithm. The default algorithm is 'sha256'. | ||
| * @param {any} object the object to hash | ||
| * @param {string} algorithm the hash algorithm to use. The default is 'sha256' | ||
| * @returns {string} the hash of the object | ||
| */ | ||
| // biome-ignore lint/suspicious/noExplicitAny: type format | ||
| hash(object, algorithm = "sha256") { | ||
| return hash(object, algorithm); | ||
| } | ||
| async getSecondaryRawResults(key) { | ||
| let result; | ||
| if (this._secondary) { | ||
| result = await this._secondary.get(key, { raw: true }); | ||
| } | ||
| return result; | ||
| } | ||
| async getManySecondaryRawResults(keys) { | ||
| let result = []; | ||
| if (this._secondary) { | ||
| result = await this._secondary.get(keys, { raw: true }); | ||
| } | ||
| return result; | ||
| } | ||
| async deleteManyKeyv(keyv, keys) { | ||
| const promises = []; | ||
| for (const key of keys) { | ||
| promises.push(keyv.delete(key)); | ||
| } | ||
| await Promise.all(promises); | ||
| return true; | ||
| } | ||
| async setManyKeyv(keyv, items) { | ||
| const promises = []; | ||
| for (const item of items) { | ||
| const finalTtl = shorthandToMilliseconds(item.ttl ?? this._ttl); | ||
| promises.push(keyv.set(item.key, item.value, finalTtl)); | ||
| } | ||
| await Promise.all(promises); | ||
| return true; | ||
| } | ||
| async hasManyKeyv(keyv, keys) { | ||
| const promises = []; | ||
| for (const key of keys) { | ||
| promises.push(keyv.has(key)); | ||
| } | ||
| return Promise.all(promises); | ||
| } | ||
| setTtl(ttl) { | ||
| if (typeof ttl === "string" || ttl === void 0) { | ||
| this._ttl = ttl; | ||
| } else if (ttl > 0) { | ||
| this._ttl = ttl; | ||
| } else { | ||
| this._ttl = void 0; | ||
| } | ||
| } | ||
| }; | ||
| export { | ||
| Cacheable, | ||
| CacheableEvents, | ||
| CacheableHooks, | ||
| CacheableMemory, | ||
| CacheableStats, | ||
| Keyv3 as Keyv, | ||
| KeyvCacheableMemory, | ||
| KeyvHooks, | ||
| createKeyv, | ||
| getOrSet, | ||
| shorthandToMilliseconds, | ||
| shorthandToTime, | ||
| wrap, | ||
| wrapSync | ||
| }; | ||
| import{getOrSet as v,wrap as A}from"@cacheable/memoize";import{createKeyv as w}from"@cacheable/memory";import{Stats as O,calculateTtlFromExpiration as p,getCascadingTtl as u,HashAlgorithm as R,hash as M,shorthandToMilliseconds as g}from"@cacheable/utils";import{Hookified as b}from"hookified";import{Keyv as f}from"keyv";var m=(o=>(o.BEFORE_SET="BEFORE_SET",o.AFTER_SET="AFTER_SET",o.BEFORE_SET_MANY="BEFORE_SET_MANY",o.AFTER_SET_MANY="AFTER_SET_MANY",o.BEFORE_GET="BEFORE_GET",o.AFTER_GET="AFTER_GET",o.BEFORE_GET_MANY="BEFORE_GET_MANY",o.AFTER_GET_MANY="AFTER_GET_MANY",o.BEFORE_SECONDARY_SETS_PRIMARY="BEFORE_SECONDARY_SETS_PRIMARY",o))(m||{}),E=(e=>(e.ERROR="error",e.CACHE_HIT="cache:hit",e.CACHE_MISS="cache:miss",e))(E||{});import{getOrSet as D,wrap as H,wrapSync as z}from"@cacheable/memoize";import{CacheableMemory as W,createKeyv as j,KeyvCacheableMemory as q}from"@cacheable/memory";import{calculateTtlFromExpiration as L,getCascadingTtl as Q,HashAlgorithm as U,hash as X,Stats as Z,shorthandToMilliseconds as $,shorthandToTime as tt}from"@cacheable/utils";import{Keyv as st,KeyvHooks as it}from"keyv";var T=class extends b{_primary=w();_secondary;_nonBlocking=!1;_ttl;_stats=new O({enabled:!1});_namespace;_cacheId=Math.random().toString(36).slice(2);constructor(t){super(),t?.primary&&this.setPrimary(t.primary),t?.secondary&&this.setSecondary(t.secondary),t?.nonBlocking&&(this._nonBlocking=t.nonBlocking),t?.stats&&(this._stats.enabled=t.stats),t?.ttl&&this.setTtl(t.ttl),t?.cacheId&&(this._cacheId=t.cacheId),t?.namespace&&(this._namespace=t.namespace,this._primary.namespace=this.getNameSpace(),this._secondary&&(this._secondary.namespace=this.getNameSpace()))}get namespace(){return this._namespace}set namespace(t){this._namespace=t,this._primary.namespace=this.getNameSpace(),this._secondary&&(this._secondary.namespace=this.getNameSpace())}get stats(){return this._stats}get primary(){return this._primary}set primary(t){this._primary=t}get secondary(){return this._secondary}set secondary(t){this._secondary=t}get nonBlocking(){return this._nonBlocking}set nonBlocking(t){this._nonBlocking=t}get ttl(){return this._ttl}set ttl(t){this.setTtl(t)}get cacheId(){return this._cacheId}set cacheId(t){this._cacheId=t}setPrimary(t){this.isKeyvInstance(t)?this._primary=t:this._primary=new f(t),this._primary.on("error",s=>{this.emit("error",s)})}setSecondary(t){this.isKeyvInstance(t)?this._secondary=t:this._secondary=new f(t),this._secondary.on("error",s=>{this.emit("error",s)})}isKeyvInstance(t){return t instanceof f?!0:["generateIterator","get","getMany","set","setMany","delete","deleteMany","has","hasMany","clear","disconnect","serialize","deserialize"].every(e=>typeof t[e]=="function")}getNameSpace(){return typeof this._namespace=="function"?this._namespace():this._namespace}async get(t,s){return(await this.getRaw(t,s))?.value}async getRaw(t,s){let e;try{await this.hook("BEFORE_GET",t),e=await this._primary.getRaw(t);let i;e?this.emit("cache:hit",{key:t,value:e.value,store:"primary"}):this.emit("cache:miss",{key:t,store:"primary"});let n=s?.nonBlocking??this._nonBlocking;if(!e&&this._secondary){let a;n?a=await this.processSecondaryForGetRawNonBlocking(this._primary,this._secondary,t):a=await this.processSecondaryForGetRaw(this._primary,this._secondary,t),a&&(e=a.result,i=a.ttl)}await this.hook("AFTER_GET",{key:t,result:e,ttl:i})}catch(i){this.emit("error",i)}return this.stats.enabled&&(e?this._stats.incrementHits():this._stats.incrementMisses(),this.stats.incrementGets()),e}async getManyRaw(t,s){let e=[];try{await this.hook("BEFORE_GET_MANY",t),e=await this._primary.getManyRaw(t);for(let[n,a]of t.entries())e[n]?this.emit("cache:hit",{key:a,value:e[n].value,store:"primary"}):this.emit("cache:miss",{key:a,store:"primary"});let i=s?.nonBlocking??this._nonBlocking;this._secondary&&(i?await this.processSecondaryForGetManyRawNonBlocking(this._primary,this._secondary,t,e):await this.processSecondaryForGetManyRaw(this._primary,this._secondary,t,e)),await this.hook("AFTER_GET_MANY",{keys:t,result:e})}catch(i){this.emit("error",i)}if(this.stats.enabled){for(let i of e)i?this._stats.incrementHits():this._stats.incrementMisses();this.stats.incrementGets()}return e}async getMany(t,s){return(await this.getManyRaw(t,s)).map(i=>i?.value)}async set(t,s,e){let i=!1,n=g(e??this._ttl);try{let a={key:t,value:s,ttl:n};await this.hook("BEFORE_SET",a);let r=[];if(r.push(this._primary.set(a.key,a.value,a.ttl)),this._secondary&&r.push(this._secondary.set(a.key,a.value,a.ttl)),this._nonBlocking){i=await Promise.race(r);for(let c of r)c.catch(o=>{this.emit("error",o)})}else i=(await Promise.all(r))[0];await this.hook("AFTER_SET",a)}catch(a){this.emit("error",a)}return this.stats.enabled&&(this.stats.incrementKSize(t),this.stats.incrementCount(),this.stats.incrementVSize(s),this.stats.incrementSets()),i}async setMany(t){let s=!1;try{await this.hook("BEFORE_SET_MANY",t),s=await this.setManyKeyv(this._primary,t),this._secondary&&(this._nonBlocking?this.setManyKeyv(this._secondary,t).catch(e=>{this.emit("error",e)}):await this.setManyKeyv(this._secondary,t)),await this.hook("AFTER_SET_MANY",t)}catch(e){this.emit("error",e)}if(this.stats.enabled)for(let e of t)this.stats.incrementKSize(e.key),this.stats.incrementCount(),this.stats.incrementVSize(e.value);return s}async take(t){let s=await this.get(t);return await this.delete(t),s}async takeMany(t){let s=await this.getMany(t);return await this.deleteMany(t),s}async has(t){let s=[];s.push(this._primary.has(t)),this._secondary&&s.push(this._secondary.has(t));let e=await Promise.all(s);for(let i of e)if(i)return!0;return!1}async hasMany(t){let s=await this.hasManyKeyv(this._primary,t),e=[];for(let[i,n]of t.entries())!s[i]&&this._secondary&&e.push(n);if(e.length>0&&this._secondary){let i=await this.hasManyKeyv(this._secondary,t);for(let[n,a]of t.entries())!s[n]&&i[n]&&(s[n]=i[n])}return s}async delete(t){let s=!1,e=[];if(this.stats.enabled){let i=await this._primary.get(t);i&&(this.stats.decreaseKSize(t),this.stats.decreaseVSize(i),this.stats.decreaseCount(),this.stats.incrementDeletes())}if(e.push(this._primary.delete(t)),this._secondary&&e.push(this._secondary.delete(t)),this.nonBlocking){s=await Promise.race(e);for(let i of e)i.catch(n=>{this.emit("error",n)})}else s=(await Promise.all(e))[0];return s}async deleteMany(t){if(this.stats.enabled){let e=await this._primary.get(t);for(let i of t)this.stats.decreaseKSize(i),this.stats.decreaseVSize(e),this.stats.decreaseCount(),this.stats.incrementDeletes()}let s=await this._primary.deleteMany(t);return this._secondary&&(this._nonBlocking?this._secondary.deleteMany(t).catch(e=>{this.emit("error",e)}):await this._secondary.deleteMany(t)),s}async clear(){let t=[];t.push(this._primary.clear()),this._secondary&&t.push(this._secondary.clear()),await(this._nonBlocking?Promise.race(t):Promise.all(t)),this.stats.enabled&&(this._stats.resetStoreValues(),this._stats.incrementClears())}async disconnect(){let t=[];t.push(this._primary.disconnect()),this._secondary&&t.push(this._secondary.disconnect()),await(this._nonBlocking?Promise.race(t):Promise.all(t))}wrap(t,s){let e={get:async n=>this.get(n),has:async n=>this.has(n),set:async(n,a,r)=>{await this.set(n,a,r)},on:(n,a)=>{this.on(n,a)},emit:(n,...a)=>this.emit(n,...a)},i={ttl:s?.ttl??this._ttl,keyPrefix:s?.keyPrefix,createKey:s?.createKey,cacheErrors:s?.cacheErrors,cache:e,cacheId:this._cacheId,serialize:s?.serialize};return A(t,i)}async getOrSet(t,s,e){let n={cache:{get:async a=>this.get(a),has:async a=>this.has(a),set:async(a,r,c)=>{await this.set(a,r,c)},on:(a,r)=>{this.on(a,r)},emit:(a,...r)=>this.emit(a,...r)},cacheId:this._cacheId,ttl:e?.ttl??this._ttl,cacheErrors:e?.cacheErrors,throwErrors:e?.throwErrors};return v(t,s,n)}hash(t,s=R.SHA256){let e=Object.values(R).includes(s)?s:R.SHA256;return M(t,{algorithm:e})}async setManyKeyv(t,s){let e=[];for(let i of s){let n=g(i.ttl??this._ttl);e.push({key:i.key,value:i.value,ttl:n})}return await t.setMany(e),!0}async hasManyKeyv(t,s){let e=[];for(let i of s)e.push(t.has(i));return Promise.all(e)}async processSecondaryForGetRaw(t,s,e){let i=await s.getRaw(e);if(i?.value){this.emit("cache:hit",{key:e,value:i.value,store:"secondary"});let n=u(this._ttl,this._primary.ttl),a=i.expires??void 0,r=p(n,a),c={key:e,value:i.value,ttl:r};return await this.hook("BEFORE_SECONDARY_SETS_PRIMARY",c),await t.set(c.key,c.value,c.ttl),{result:i,ttl:r}}else{this.emit("cache:miss",{key:e,store:"secondary"});return}}async processSecondaryForGetRawNonBlocking(t,s,e){let i=await s.getRaw(e);if(i?.value){this.emit("cache:hit",{key:e,value:i.value,store:"secondary"});let n=u(this._ttl,this._primary.ttl),a=i.expires??void 0,r=p(n,a),c={key:e,value:i.value,ttl:r};return this.hook("BEFORE_SECONDARY_SETS_PRIMARY",c).then(async()=>{await t.set(c.key,c.value,c.ttl)}).catch(o=>{this.emit("error",o)}),{result:i,ttl:r}}else{this.emit("cache:miss",{key:e,store:"secondary"});return}}async processSecondaryForGetManyRaw(t,s,e,i){let n=[];for(let[c,o]of e.entries())i[c]||n.push(o);let a=await s.getManyRaw(n),r=0;for await(let[c,o]of e.entries())if(!i[c]){let l=a[r];if(l&&l.value!==void 0){i[c]=l,this.emit("cache:hit",{key:o,value:l.value,store:"secondary"});let d=u(this._ttl,this._primary.ttl),{expires:y}=l;y===null&&(y=void 0);let _=p(d,y),h={key:o,value:l.value,ttl:_};await this.hook("BEFORE_SECONDARY_SETS_PRIMARY",h),await t.set(h.key,h.value,h.ttl)}else this.emit("cache:miss",{key:o,store:"secondary"});r++}}async processSecondaryForGetManyRawNonBlocking(t,s,e,i){let n=[];for(let[c,o]of e.entries())i[c]||n.push(o);let a=await s.getManyRaw(n),r=0;for await(let[c,o]of e.entries())if(!i[c]){let l=a[r];if(l&&l.value!==void 0){i[c]=l,this.emit("cache:hit",{key:o,value:l.value,store:"secondary"});let d=u(this._ttl,this._primary.ttl),{expires:y}=l;y===null&&(y=void 0);let _=p(d,y),h={key:o,value:l.value,ttl:_};this.hook("BEFORE_SECONDARY_SETS_PRIMARY",h).then(async()=>{await t.set(h.key,h.value,h.ttl)}).catch(S=>{this.emit("error",S)})}else this.emit("cache:miss",{key:o,store:"secondary"});r++}}setTtl(t){typeof t=="string"||t===void 0?this._ttl=t:t>0?this._ttl=t:this._ttl=void 0}};export{T as Cacheable,E as CacheableEvents,m as CacheableHooks,W as CacheableMemory,Z as CacheableStats,U as HashAlgorithm,st as Keyv,q as KeyvCacheableMemory,it as KeyvHooks,L as calculateTtlFromExpiration,j as createKeyv,Q as getCascadingTtl,D as getOrSet,X as hash,$ as shorthandToMilliseconds,tt as shorthandToTime,H as wrap,z as wrapSync}; |
+9
-5
| { | ||
| "name": "cacheable", | ||
| "version": "1.10.4", | ||
| "version": "2.0.0", | ||
| "description": "High Performance Layer 1 / Layer 2 Caching with Keyv Storage", | ||
@@ -38,3 +38,6 @@ "type": "module", | ||
| "hookified": "^1.11.0", | ||
| "keyv": "^5.5.0" | ||
| "keyv": "^5.5.0", | ||
| "@cacheable/memoize": "^2.0.0", | ||
| "@cacheable/memory": "^2.0.0", | ||
| "@cacheable/utils": "^2.0.0" | ||
| }, | ||
@@ -72,8 +75,9 @@ "keywords": [ | ||
| "scripts": { | ||
| "build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean", | ||
| "build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --clean --minify", | ||
| "prepublish": "pnpm build", | ||
| "test": "biome check --write && vitest run --coverage", | ||
| "test:ci": "biome check && vitest run --coverage", | ||
| "lint": "biome check --write --error-on-warnings", | ||
| "test": "pnpm lint && vitest run --coverage", | ||
| "test:ci": "biome check --error-on-warnings && vitest run --coverage", | ||
| "clean": "rimraf ./dist ./coverage ./node_modules" | ||
| } | ||
| } |
+58
-159
@@ -29,2 +29,3 @@ [<img align="center" src="https://cacheable.org/logo.svg" alt="Cacheable" />](https://github.com/jaredwray/cacheable) | ||
| * [Getting Started](#getting-started) | ||
| * [v1 to v2 Changes](#v1-to-v2-changes) | ||
| * [Basic Usage](#basic-usage) | ||
@@ -134,2 +135,33 @@ * [Hooks and Events](#hooks-and-events) | ||
| The following events are provided: | ||
| - `error`: Emitted when an error occurs. | ||
| - `cache:hit`: Emitted when a cache hit occurs. | ||
| - `cache:miss`: Emitted when a cache miss occurs. | ||
| Here is an example of using the `error` event: | ||
| ```javascript | ||
| import { Cacheable, CacheableEvents } from 'cacheable'; | ||
| const cacheable = new Cacheable(); | ||
| cacheable.on(CacheableEvents.ERROR, (error) => { | ||
| console.error(`Cacheable error: ${error.message}`); | ||
| }); | ||
| ``` | ||
| We also offer `cache:hit` and `cache:miss` events. These events are emitted when a cache hit or miss occurs, respectively. Here is how to use them: | ||
| ```javascript | ||
| import { Cacheable, CacheableEvents } from 'cacheable'; | ||
| const cacheable = new Cacheable(); | ||
| cacheable.on(CacheableEvents.CACHE_HIT, (data) => { | ||
| console.log(`Cache hit: ${data.key} ${data.value} ${data.store}`); // the store will say primary or secondary | ||
| }); | ||
| cacheable.on(CacheableEvents.CACHE_MISS, (data) => { | ||
| console.log(`Cache miss: ${data.key} ${data.store}`); // the store will say primary or secondary | ||
| }); | ||
| ``` | ||
| # Storage Tiering and Caching | ||
@@ -260,4 +292,13 @@ | ||
| If you want your layer 2 (secondary) store to be non-blocking you can set the `nonBlocking` property to `true` in the options. This will make the secondary store non-blocking and will not wait for the secondary store to respond on `setting data`, `deleting data`, or `clearing data`. This is useful if you want to have a faster response time and not wait for the secondary store to respond. | ||
| If you want your layer 2 (secondary) store to be non-blocking you can set the `nonBlocking` property to `true` in the options. This will make the secondary store non-blocking and will not wait for the secondary store to respond on `setting data`, `deleting data`, or `clearing data`. This is useful if you want to have a faster response time and not wait for the secondary store to respond. Here is a full list of what each method does in nonBlocking mode: | ||
| * `set` - in non-blocking mode it will set at the `primary` storage and then in the background update `secondary` | ||
| * `get` - in non-blocking mode it will only check the primary storage but then in the background look to see if there is a value in the `secondary` and update the primary | ||
| * `getMany` - in non-blocking mode it will only check the primary storage but then in the background look to see if there is a value in the `secondary` and update the primary | ||
| * `getRaw` - in non-blocking mode it will only check the primary storage but then in the background look to see if there is a value in the `secondary` and update the primary | ||
| * `getManyRaw` - in non-blocking mode it will only check the primary storage but then in the background look to see if there is a value in the `secondary` and update the primary | ||
| # Non-Blocking with @keyv/redis | ||
@@ -401,3 +442,3 @@ | ||
| `cacheable` comes with a built-in in-memory cache called `CacheableMemory`. This is a simple in-memory cache that is used as the primary store for `cacheable`. You can use this as a standalone cache or as a primary store for `cacheable`. Here is an example of how to use `CacheableMemory`: | ||
| `cacheable` comes with a built-in in-memory cache called `CacheableMemory` from `@cacheable/memory`. This is a simple in-memory cache that is used as the primary store for `cacheable`. You can use this as a standalone cache or as a primary store for `cacheable`. Here is an example of how to use `CacheableMemory`: | ||
@@ -416,161 +457,7 @@ ```javascript | ||
| You can use `CacheableMemory` as a standalone cache or as a primary store for `cacheable`. You can also set the `useClones` property to `false` if you want to use the same reference for the values. This is useful if you are using large objects and want to save memory. The `lruSize` property is the size of the LRU cache and is set to `0` by default which is unlimited. When setting the `lruSize` property it will limit the number of keys in the cache. | ||
| To learn more go to [@cacheable/memory](https://cacheable.org/docs/memory/) | ||
| This simple in-memory cache uses multiple Map objects and a with `expiration` and `lru` policies if set to manage the in memory cache at scale. | ||
| By default we use lazy expiration deletion which means on `get` and `getMany` type functions we look if it is expired and then delete it. If you want to have a more aggressive expiration policy you can set the `checkInterval` property to a value greater than `0` which will check for expired keys at the interval you set. | ||
| Here are some of the main features of `CacheableMemory`: | ||
| * High performance in-memory cache with a robust API and feature set. 🚀 | ||
| * Can scale past the `16,777,216 (2^24) keys` limit of a single `Map` via `hashStoreSize`. Default is `16` Map objects. | ||
| * LRU (Least Recently Used) cache feature to limit the number of keys in the cache via `lruSize`. Limit to `16,777,216 (2^24) keys` total. | ||
| * Expiration policy to delete expired keys with lazy deletion or aggressive deletion via `checkInterval`. | ||
| * `Wrap` feature to memoize `sync` and `async` functions with stampede protection. | ||
| * Ability to do many operations at once such as `setMany`, `getMany`, `deleteMany`, and `takeMany`. | ||
| * Supports `raw` data retrieval with `getRaw` and `getManyRaw` methods to get the full metadata of the cache entry. | ||
| ## CacheableMemory Store Hashing | ||
| `CacheableMemory` uses `Map` objects to store the keys and values. To make this scale past the `16,777,216 (2^24) keys` limit of a single `Map` we use a hash to balance the data across multiple `Map` objects. This is done by hashing the key and using the hash to determine which `Map` object to use. The default hashing algorithm is `djb2Hash` but you can change it by setting the `storeHashAlgorithm` property in the options. By default we set the amount of `Map` objects to `16`. | ||
| NOTE: if you are using the LRU cache feature the `lruSize` no matter how many `Map` objects you have it will be limited to the `16,777,216 (2^24) keys` limit of a single `Map` object. This is because we use a double linked list to manage the LRU cache and it is not possible to have more than `16,777,216 (2^24) keys` in a single `Map` object. | ||
| Here is an example of how to set the number of `Map` objects and the hashing algorithm: | ||
| ```javascript | ||
| import { CacheableMemory } from 'cacheable'; | ||
| const cache = new CacheableMemory({ | ||
| storeSize: 32, // set the number of Map objects to 32 | ||
| }); | ||
| cache.set('key', 'value'); | ||
| const value = cache.get('key'); // value | ||
| ``` | ||
| Here is an example of how to use the `storeHashAlgorithm` property: | ||
| ```javascript | ||
| import { CacheableMemory } from 'cacheable'; | ||
| const cache = new CacheableMemory({ storeHashAlgorithm: 'sha256' }); | ||
| cache.set('key', 'value'); | ||
| const value = cache.get('key'); // value | ||
| ``` | ||
| If you want to provide your own hashing function you can set the `storeHashAlgorithm` property to a function that takes an object and returns a `number` that is in the range of the amount of `Map` stores you have. | ||
| ```javascript | ||
| import { CacheableMemory } from 'cacheable'; | ||
| /** | ||
| * Custom hash function that takes a key and the size of the store | ||
| * and returns a number between 0 and storeHashSize - 1. | ||
| * @param {string} key - The key to hash. | ||
| * @param {number} storeHashSize - The size of the store (number of Map objects). | ||
| * @returns {number} - A number between 0 and storeHashSize - 1. | ||
| */ | ||
| const customHash = (key, storeHashSize) => { | ||
| // custom hashing logic | ||
| return key.length % storeHashSize; // returns a number between 0 and 31 for 32 Map objects | ||
| }; | ||
| const cache = new CacheableMemory({ storeHashAlgorithm: customHash, storeSize: 32 }); | ||
| cache.set('key', 'value'); | ||
| const value = cache.get('key'); // value | ||
| ``` | ||
| ## CacheableMemory LRU Feature | ||
| You can enable the LRU (Least Recently Used) feature in `CacheableMemory` by setting the `lruSize` property in the options. This will limit the number of keys in the cache to the size you set. When the cache reaches the limit it will remove the least recently used keys from the cache. This is useful if you want to limit the memory usage of the cache. | ||
| When you set the `lruSize` we use a double linked list to manage the LRU cache and also set the `hashStoreSize` to `1` which means we will only use a single `Map` object for the LRU cache. This is because the LRU cache is managed by the double linked list and it is not possible to have more than `16,777,216 (2^24) keys` in a single `Map` object. | ||
| ```javascript | ||
| import { CacheableMemory } from 'cacheable'; | ||
| const cache = new CacheableMemory({ lruSize: 1 }); // sets the LRU cache size to 1000 keys and hashStoreSize to 1 | ||
| cache.set('key1', 'value1'); | ||
| cache.set('key2', 'value2'); | ||
| const value1 = cache.get('key1'); | ||
| console.log(value1); // undefined if the cache is full and key1 is the least recently used | ||
| const value2 = cache.get('key2'); | ||
| console.log(value2); // value2 if key2 is still in the cache | ||
| console.log(cache.size()); // 1 | ||
| ``` | ||
| NOTE: if you set the `lruSize` property to `0` after it was enabled it will disable the LRU cache feature and will not limit the number of keys in the cache. This will remove the `16,777,216 (2^24) keys` limit of a single `Map` object and will allow you to store more keys in the cache. | ||
| ## CacheableMemory Performance | ||
| Our goal with `cacheable` and `CacheableMemory` is to provide a high performance caching engine that is simple to use and has a robust API. We test it against other cacheing engines such that are less feature rich to make sure there is little difference. Here are some of the benchmarks we have run: | ||
| *Memory Benchmark Results:* | ||
| | name | summary | ops/sec | time/op | margin | samples | | ||
| |------------------------------------------|:---------:|----------:|----------:|:--------:|----------:| | ||
| | Map (v22) - set / get | 🥇 | 117K | 9µs | ±1.29% | 110K | | ||
| | Cacheable Memory (v1.10.0) - set / get | -1.3% | 116K | 9µs | ±0.77% | 110K | | ||
| | Node Cache - set / get | -4.1% | 112K | 9µs | ±1.34% | 107K | | ||
| | bentocache (v1.4.0) - set / get | -45% | 65K | 17µs | ±1.10% | 100K | | ||
| *Memory LRU Benchmark Results:* | ||
| | name | summary | ops/sec | time/op | margin | samples | | ||
| |------------------------------------------|:---------:|----------:|----------:|:--------:|----------:| | ||
| | quick-lru (v7.0.1) - set / get | 🥇 | 118K | 9µs | ±0.85% | 112K | | ||
| | Map (v22) - set / get | -0.56% | 117K | 9µs | ±1.35% | 110K | | ||
| | lru.min (v1.1.2) - set / get | -1.7% | 116K | 9µs | ±0.90% | 110K | | ||
| | Cacheable Memory (v1.10.0) - set / get | -3.3% | 114K | 9µs | ±1.16% | 108K | | ||
| As you can see from the benchmarks `CacheableMemory` is on par with other caching engines such as `Map`, `Node Cache`, and `bentocache`. We have also tested it against other LRU caching engines such as `quick-lru` and `lru.min` and it performs well against them too. | ||
| ## CacheableMemory Options | ||
| * `ttl`: The time to live for the cache in milliseconds. Default is `undefined` which is means indefinitely. | ||
| * `useClones`: If the cache should use clones for the values. Default is `true`. | ||
| * `lruSize`: The size of the LRU cache. Default is `0` which is unlimited. | ||
| * `checkInterval`: The interval to check for expired keys in milliseconds. Default is `0` which is disabled. | ||
| * `storeHashSize`: The number of `Map` objects to use for the cache. Default is `16`. | ||
| * `storeHashAlgorithm`: The hashing algorithm to use for the cache. Default is `djb2Hash`. | ||
| ## CacheableMemory - API | ||
| * `set(key, value, ttl?)`: Sets a value in the cache. | ||
| * `setMany([{key, value, ttl?}])`: Sets multiple values in the cache from `CacheableItem`. | ||
| * `get(key)`: Gets a value from the cache. | ||
| * `getMany([keys])`: Gets multiple values from the cache. | ||
| * `getRaw(key)`: Gets a value from the cache as `CacheableStoreItem`. | ||
| * `getManyRaw([keys])`: Gets multiple values from the cache as `CacheableStoreItem`. | ||
| * `has(key)`: Checks if a value exists in the cache. | ||
| * `hasMany([keys])`: Checks if multiple values exist in the cache. | ||
| * `delete(key)`: Deletes a value from the cache. | ||
| * `deleteMany([keys])`: Deletes multiple values from the cache. | ||
| * `take(key)`: Takes a value from the cache and deletes it. | ||
| * `takeMany([keys])`: Takes multiple values from the cache and deletes them. | ||
| * `wrap(function, WrapSyncOptions)`: Wraps a `sync` function in a cache. | ||
| * `clear()`: Clears the cache. | ||
| * `ttl`: The default time to live for the cache in milliseconds. Default is `undefined` which is disabled. | ||
| * `useClones`: If the cache should use clones for the values. Default is `true`. | ||
| * `lruSize`: The size of the LRU cache. Default is `0` which is unlimited. | ||
| * `size`: The number of keys in the cache. | ||
| * `checkInterval`: The interval to check for expired keys in milliseconds. Default is `0` which is disabled. | ||
| * `storeHashSize`: The number of `Map` objects to use for the cache. Default is `16`. | ||
| * `storeHashAlgorithm`: The hashing algorithm to use for the cache. Default is `djb2Hash`. | ||
| * `keys`: Get the keys in the cache. Not able to be set. | ||
| * `items`: Get the items in the cache as `CacheableStoreItem` example `{ key, value, expires? }`. | ||
| * `store`: The hash store for the cache which is an array of `Map` objects. | ||
| * `checkExpired()`: Checks for expired keys in the cache. This is used by the `checkInterval` property. | ||
| * `startIntervalCheck()`: Starts the interval check for expired keys if `checkInterval` is above 0 ms. | ||
| * `stopIntervalCheck()`: Stops the interval check for expired keys. | ||
| # Keyv Storage Adapter - KeyvCacheableMemory | ||
| `cacheable` comes with a built-in storage adapter for Keyv called `KeyvCacheableMemory`. This takes `CacheableMemory` and creates a storage adapter for Keyv. This is useful if you want to use `CacheableMemory` as a storage adapter for Keyv. Here is an example of how to use `KeyvCacheableMemory`: | ||
| ```javascript | ||
| import { Keyv } from 'keyv'; | ||
| import { KeyvCacheableMemory } from 'cacheable'; | ||
| const keyv = new Keyv({ store: new KeyvCacheableMemory() }); | ||
| await keyv.set('foo', 'bar'); | ||
| const value = await keyv.get('foo'); | ||
| console.log(value); // bar | ||
| ``` | ||
| # Wrap / Memoization for Sync and Async Functions | ||
| `Cacheable` and `CacheableMemory` has a feature called `wrap` that allows you to wrap a function in a cache. This is useful for memoization and caching the results of a function. You can wrap a `sync` or `async` function in a cache. Here is an example of how to use the `wrap` function: | ||
| `Cacheable` and `CacheableMemory` has a feature called `wrap` that comes from [@cacheable/memoize](https://cacheable.org/docs/memoize/) and allows you to wrap a function in a cache. This is useful for memoization and caching the results of a function. You can wrap a `sync` or `async` function in a cache. Here is an example of how to use the `wrap` function: | ||
@@ -668,5 +555,7 @@ ```javascript | ||
| To learn more visit [@cacheable/memoize](https://cacheable.org/docs/memoize/) | ||
| # Get Or Set Memoization Function | ||
| The `getOrSet` method provides a convenient way to implement the cache-aside pattern. It attempts to retrieve a value from cache, and if not found, calls the provided function to compute the value and store it in cache before returning it. Here are the options: | ||
| The `getOrSet` method that comes from [@cacheable/memoize](https://cacheable.org/docs/memoize/) provides a convenient way to implement the cache-aside pattern. It attempts to retrieve a value from cache, and if not found, calls the provided function to compute the value and store it in cache before returning it. Here are the options: | ||
@@ -707,4 +596,14 @@ ```typescript | ||
| To learn more go to [@cacheable/memoize](https://cacheable.org/docs/memoize/) | ||
| # v1 to v2 Changes | ||
| `cacheable` is now using `@cacheable/utils`, `@cacheable/memoize`, and `@cacheable/memory` for its core functionality as we are moving to this modular architecture and plan to eventually have these modules across `cache-manager` and `flat-cache`. In addition there are some breaking changes: | ||
| * `get()` and `getMany()` no longer have the `raw` option but instead we have built out `getRaw()` and `getManyRaw()` to use. | ||
| * All `get` related functions now support `nonBlocking` which means if `nonBlocking: true` the primary store will return what it has and then in the background will work to sync from secondary storage for any misses. You can disable this by setting at the `get` function level the option `nonBlocking: false` which will look for any missing keys in the secondary. | ||
| * `Keyv` v5.5+ is now the recommended supported version as we are using its native `getMany*` and `getRaw*` | ||
| * `Wrap` and `getOrSet` have been updated with more robust options including the ability to use your own `serialize` function for creating the key in `wrap`. | ||
| * `hash` has now been updated with robust options and also an enum for setting the algorithm. | ||
| # How to Contribute | ||
@@ -711,0 +610,0 @@ |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
90368
-56.65%5
150%459
-89.8%3
200%608
-14.25%9
125%+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added
+ Added