Comparing version 1.8.1 to 1.8.2
@@ -112,2 +112,20 @@ import { KeyvStoreAdapter, StoredData, Keyv } from 'keyv'; | ||
type WrapOptions = { | ||
ttl?: number | string; | ||
keyPrefix?: string; | ||
cache: Cacheable; | ||
}; | ||
type WrapSyncOptions = { | ||
ttl?: number | string; | ||
keyPrefix?: string; | ||
cache: CacheableMemory; | ||
}; | ||
type WrapFunctionOptions = { | ||
ttl?: number | string; | ||
keyPrefix: string; | ||
}; | ||
type AnyFunction = (...arguments_: any[]) => any; | ||
declare function wrapSync<T>(function_: AnyFunction, options: WrapSyncOptions): AnyFunction; | ||
declare function wrap<T>(function_: AnyFunction, options: WrapOptions): AnyFunction; | ||
/** | ||
@@ -129,2 +147,3 @@ * @typedef {Object} CacheableMemoryOptions | ||
declare class CacheableMemory { | ||
private _lru; | ||
private readonly _hashCache; | ||
@@ -141,3 +160,2 @@ private readonly _hash0; | ||
private readonly _hash9; | ||
private readonly _lru; | ||
private _ttl; | ||
@@ -289,14 +307,20 @@ private _useClone; | ||
/** | ||
* Hash the key. this is used to determine which store to use (internal use) | ||
* @param {string} key - The key to hash | ||
* @returns {number} - The hash number | ||
*/ | ||
hashKey(key: string): number; | ||
/** | ||
* Get the store based on the key (internal use) | ||
* @param {string} key - The key to get the store | ||
* @returns {Map<string, any>} - The store | ||
* @returns {CacheableHashStore} - The store | ||
*/ | ||
getStore(key: string): Map<string, any>; | ||
getStore(key: string): Map<string, CacheableStoreItem>; | ||
/** | ||
* Get the store based on the hash (internal use) | ||
* @param {number} hash | ||
* @returns {Map<string, CacheableStoreItem>} | ||
*/ | ||
getStoreFromHash(hash: number): Map<string, CacheableStoreItem>; | ||
/** | ||
* Hash the key (internal use) | ||
* @param key | ||
* @returns {number} from 0 to 9 | ||
*/ | ||
hashKey(key: string): number; | ||
/** | ||
* Clone the value. This is for internal use | ||
@@ -352,6 +376,3 @@ * @param {any} value - The value to clone | ||
*/ | ||
wrap<T>(function_: (...arguments_: any[]) => T, options?: { | ||
ttl?: number; | ||
key?: string; | ||
}): (...arguments_: any[]) => T; | ||
wrap<T>(function_: (...arguments_: any[]) => T, options: WrapFunctionOptions): (...arguments_: any[]) => T; | ||
private isPrimitive; | ||
@@ -362,7 +383,14 @@ private concatStores; | ||
type KeyvCacheableMemoryOptions = CacheableMemoryOptions & { | ||
namespace?: string; | ||
}; | ||
declare class KeyvCacheableMemory implements KeyvStoreAdapter { | ||
opts: CacheableMemoryOptions; | ||
namespace?: string | undefined; | ||
private readonly _cache; | ||
constructor(options?: 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>; | ||
@@ -381,2 +409,3 @@ getMany<Value>(keys: string[]): Promise<Array<StoredData<Value | undefined>>>; | ||
on(event: string, listener: (...arguments_: any[]) => void): this; | ||
getStore(namespace?: string): CacheableMemory; | ||
} | ||
@@ -387,16 +416,2 @@ | ||
type WrapOptions = { | ||
ttl?: number | string; | ||
key?: string; | ||
cache: Cacheable; | ||
}; | ||
type WrapSyncOptions = { | ||
ttl?: number | string; | ||
key?: string; | ||
cache: CacheableMemory; | ||
}; | ||
type AnyFunction = (...arguments_: any[]) => any; | ||
declare function wrapSync<T>(function_: AnyFunction, options: WrapSyncOptions): AnyFunction; | ||
declare function wrap<T>(function_: AnyFunction, options: WrapOptions): AnyFunction; | ||
declare enum CacheableHooks { | ||
@@ -421,2 +436,3 @@ BEFORE_SET = "BEFORE_SET", | ||
ttl?: number | string; | ||
namespace?: string | (() => string); | ||
}; | ||
@@ -429,2 +445,3 @@ declare class Cacheable extends Hookified { | ||
private readonly _stats; | ||
private _namespace?; | ||
/** | ||
@@ -436,2 +453,13 @@ * Creates a new cacheable instance | ||
/** | ||
* The namespace for the cacheable instance | ||
* @returns {string | (() => string) | undefined} The namespace for the cacheable instance | ||
*/ | ||
get namespace(): string | (() => string) | undefined; | ||
/** | ||
* Sets the namespace for the cacheable instance | ||
* @param {string | (() => string) | undefined} namespace The namespace for the cacheable instance | ||
* @returns {void} | ||
*/ | ||
set namespace(namespace: string | (() => string) | undefined); | ||
/** | ||
* The statistics for the cacheable instance | ||
@@ -529,2 +557,3 @@ * @returns {CacheableStats} The statistics for the cacheable instance | ||
setSecondary(secondary: Keyv | KeyvStoreAdapter): void; | ||
getNameSpace(): string | undefined; | ||
/** | ||
@@ -546,5 +575,4 @@ * Gets the value of the key. If the key does not exist in the primary store then it will check the secondary store. | ||
* @param {T} value The value to set | ||
* @param {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. | ||
* @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 | ||
@@ -613,6 +641,3 @@ */ | ||
*/ | ||
wrap<T>(function_: (...arguments_: any[]) => T, options?: { | ||
ttl?: number; | ||
key?: string; | ||
}): (...arguments_: any[]) => T; | ||
wrap<T>(function_: (...arguments_: any[]) => T, options: WrapFunctionOptions): (...arguments_: any[]) => T; | ||
/** | ||
@@ -619,0 +644,0 @@ * Will hash an object using the specified algorithm. The default algorithm is 'sha256'. |
@@ -72,7 +72,19 @@ // src/index.ts | ||
// src/hash.ts | ||
import * as crypto from "node: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"); | ||
} | ||
// src/wrap.ts | ||
function wrapSync(function_, options) { | ||
const { ttl, key, cache } = options; | ||
const { ttl, keyPrefix, cache } = options; | ||
return function(...arguments_) { | ||
const cacheKey = key ?? cache.hash(arguments_); | ||
const cacheKey = createWrapKey(function_, arguments_, keyPrefix); | ||
let value = cache.get(cacheKey); | ||
@@ -87,5 +99,5 @@ if (value === void 0) { | ||
function wrap(function_, options) { | ||
const { ttl, key, cache } = options; | ||
const { ttl, keyPrefix, cache } = options; | ||
return async function(...arguments_) { | ||
const cacheKey = key ?? cache.hash(arguments_); | ||
const cacheKey = createWrapKey(function_, arguments_, keyPrefix); | ||
let value = await cache.get(cacheKey); | ||
@@ -99,2 +111,8 @@ if (value === void 0) { | ||
} | ||
function createWrapKey(function_, arguments_, keyPrefix) { | ||
if (!keyPrefix) { | ||
return `${function_.name}::${hash(arguments_)}`; | ||
} | ||
return `${keyPrefix}::${function_.name}::${hash(arguments_)}`; | ||
} | ||
@@ -174,16 +192,5 @@ // src/memory-lru.ts | ||
// src/hash.ts | ||
import * as crypto from "node: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"); | ||
} | ||
// src/memory.ts | ||
var CacheableMemory = class { | ||
_lru = new DoublyLinkedList(); | ||
_hashCache = /* @__PURE__ */ new Map(); | ||
@@ -200,3 +207,2 @@ _hash0 = /* @__PURE__ */ new Map(); | ||
_hash9 = /* @__PURE__ */ new Map(); | ||
_lru = new DoublyLinkedList(); | ||
_ttl; | ||
@@ -403,8 +409,7 @@ // Turned off by default | ||
} | ||
store.set(key, { | ||
const item = { key, value, expires }; | ||
store.set( | ||
key, | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment | ||
value, | ||
expires | ||
}); | ||
item | ||
); | ||
} | ||
@@ -503,30 +508,20 @@ /** | ||
this._hashCache.clear(); | ||
this._lru = new DoublyLinkedList(); | ||
} | ||
/** | ||
* Hash the key. this is used to determine which store to use (internal use) | ||
* @param {string} key - The key to hash | ||
* @returns {number} - The hash number | ||
* Get the store based on the key (internal use) | ||
* @param {string} key - The key to get the store | ||
* @returns {CacheableHashStore} - The store | ||
*/ | ||
hashKey(key) { | ||
const cacheHashNumber = this._hashCache.get(key); | ||
if (cacheHashNumber) { | ||
return cacheHashNumber; | ||
} | ||
let hash2 = 0; | ||
const primeMultiplier = 31; | ||
for (let i = 0; i < key.length; i++) { | ||
hash2 = hash2 * primeMultiplier + key.charCodeAt(i); | ||
} | ||
const result = Math.abs(hash2) % 10; | ||
this._hashCache.set(key, result); | ||
return result; | ||
getStore(key) { | ||
const hash2 = this.hashKey(key); | ||
return this.getStoreFromHash(hash2); | ||
} | ||
/** | ||
* Get the store based on the key (internal use) | ||
* @param {string} key - The key to get the store | ||
* @returns {Map<string, any>} - The store | ||
* Get the store based on the hash (internal use) | ||
* @param {number} hash | ||
* @returns {Map<string, CacheableStoreItem>} | ||
*/ | ||
getStore(key) { | ||
const hashKey = this.hashKey(key); | ||
switch (hashKey) { | ||
getStoreFromHash(hash2) { | ||
switch (hash2) { | ||
case 1: { | ||
@@ -565,2 +560,21 @@ return this._hash1; | ||
/** | ||
* Hash the key (internal use) | ||
* @param key | ||
* @returns {number} from 0 to 9 | ||
*/ | ||
hashKey(key) { | ||
const cacheHashNumber = this._hashCache.get(key); | ||
if (cacheHashNumber) { | ||
return cacheHashNumber; | ||
} | ||
let hash2 = 0; | ||
const primeMultiplier = 31; | ||
for (let i = 0; i < key.length; i++) { | ||
hash2 = hash2 * primeMultiplier + key.charCodeAt(i); | ||
} | ||
const result = Math.abs(hash2) % 10; | ||
this._hashCache.set(key, result); | ||
return result; | ||
} | ||
/** | ||
* Clone the value. This is for internal use | ||
@@ -663,6 +677,6 @@ * @param {any} value - The value to clone | ||
*/ | ||
wrap(function_, options = {}) { | ||
wrap(function_, options) { | ||
const wrapOptions = { | ||
ttl: options.ttl, | ||
key: options.key, | ||
keyPrefix: options.keyPrefix, | ||
cache: this | ||
@@ -683,4 +697,3 @@ }; | ||
concatStores() { | ||
const result = new Map([...this._hash0, ...this._hash1, ...this._hash2, ...this._hash3, ...this._hash4, ...this._hash5, ...this._hash6, ...this._hash7, ...this._hash8, ...this._hash9]); | ||
return result; | ||
return new Map([...this._hash0, ...this._hash1, ...this._hash2, ...this._hash3, ...this._hash4, ...this._hash5, ...this._hash6, ...this._hash7, ...this._hash8, ...this._hash9]); | ||
} | ||
@@ -706,12 +719,26 @@ setTtl(ttl) { | ||
}; | ||
namespace; | ||
_cache = new CacheableMemory(); | ||
_defaultCache = new CacheableMemory(); | ||
_nCache = /* @__PURE__ */ new Map(); | ||
_namespace; | ||
constructor(options) { | ||
if (options) { | ||
this.opts = options; | ||
this._cache = new CacheableMemory(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._cache.get(key); | ||
const result = this.getStore(this._namespace).get(key); | ||
if (result) { | ||
@@ -723,24 +750,24 @@ return result; | ||
async getMany(keys) { | ||
const result = this._cache.getMany(keys); | ||
const result = this.getStore(this._namespace).getMany(keys); | ||
return result; | ||
} | ||
async set(key, value, ttl) { | ||
this._cache.set(key, value, ttl); | ||
this.getStore(this._namespace).set(key, value, ttl); | ||
} | ||
async setMany(values) { | ||
this._cache.setMany(values); | ||
this.getStore(this._namespace).setMany(values); | ||
} | ||
async delete(key) { | ||
this._cache.delete(key); | ||
this.getStore(this._namespace).delete(key); | ||
return true; | ||
} | ||
async deleteMany(key) { | ||
this._cache.deleteMany(key); | ||
this.getStore(this._namespace).deleteMany(key); | ||
return true; | ||
} | ||
async clear() { | ||
this._cache.clear(); | ||
this.getStore(this._namespace).clear(); | ||
} | ||
async has(key) { | ||
return this._cache.has(key); | ||
return this.getStore(this._namespace).has(key); | ||
} | ||
@@ -750,2 +777,11 @@ on(event, listener) { | ||
} | ||
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); | ||
} | ||
}; | ||
@@ -996,2 +1032,3 @@ | ||
_stats = new CacheableStats({ enabled: false }); | ||
_namespace; | ||
/** | ||
@@ -1018,4 +1055,30 @@ * Creates a new cacheable instance | ||
} | ||
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 | ||
@@ -1135,2 +1198,8 @@ * @returns {CacheableStats} The statistics for the cacheable instance | ||
} | ||
getNameSpace() { | ||
if (typeof this._namespace === "function") { | ||
return this._namespace(); | ||
} | ||
return this._namespace; | ||
} | ||
/** | ||
@@ -1213,5 +1282,4 @@ * Gets the value of the key. If the key does not exist in the primary store then it will check the secondary store. | ||
* @param {T} value The value to set | ||
* @param {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. | ||
* @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 | ||
@@ -1430,6 +1498,6 @@ */ | ||
*/ | ||
wrap(function_, options = {}) { | ||
wrap(function_, options) { | ||
const wrapOptions = { | ||
ttl: options.ttl, | ||
key: options.key, | ||
keyPrefix: options.keyPrefix, | ||
cache: this | ||
@@ -1436,0 +1504,0 @@ }; |
{ | ||
"name": "cacheable", | ||
"version": "1.8.1", | ||
"version": "1.8.2", | ||
"description": "Simple Caching Engine using Keyv", | ||
@@ -25,14 +25,14 @@ "type": "module", | ||
"@keyv/redis": "^3.0.1", | ||
"@types/node": "^22.7.4", | ||
"@vitest/coverage-v8": "^2.1.1", | ||
"@types/node": "^22.8.1", | ||
"@vitest/coverage-v8": "^2.1.3", | ||
"lru-cache": "^11.0.1", | ||
"rimraf": "^6.0.1", | ||
"tsup": "^8.3.0", | ||
"typescript": "^5.6.2", | ||
"vitest": "^2.1.1", | ||
"tsup": "^8.3.5", | ||
"typescript": "^5.6.3", | ||
"vitest": "^2.1.3", | ||
"xo": "^0.59.3" | ||
}, | ||
"dependencies": { | ||
"hookified": "^1.1.0", | ||
"keyv": "^5.0.3" | ||
"hookified": "^1.4.0", | ||
"keyv": "^5.1.2" | ||
}, | ||
@@ -39,0 +39,0 @@ "keywords": [ |
@@ -195,2 +195,3 @@ [<img align="center" src="https://cacheable.org/logo.svg" alt="Cacheable" />](https://github.com/jaredwray/cacheable) | ||
* `ttl`: The default time to live for the cache in milliseconds. Default is `undefined` which is disabled. | ||
* `namespace`: The namespace for the cache. Default is `undefined`. | ||
@@ -237,2 +238,3 @@ # Cacheable Statistics (Instance Only) | ||
* `secondary`: The secondary store for the cache (layer 2) usually a persistent cache by Keyv. | ||
* `namespace`: The namespace for the cache. Default is `undefined`. This will set the namespace for the primary and secondary stores. | ||
* `nonBlocking`: If the secondary store is non-blocking. Default is `false`. | ||
@@ -301,7 +303,11 @@ * `stats`: The statistics for this instance which includes `hits`, `misses`, `sets`, `deletes`, `clears`, `errors`, `count`, `vsize`, `ksize`. | ||
const asyncFunction = async (value: number) => { | ||
return value * 2; | ||
return Math.random() * value; | ||
}; | ||
const cache = new Cacheable(); | ||
const wrappedFunction = cache.wrap(asyncFunction, { ttl: '1h' }); | ||
const options = { | ||
ttl: '1h', // 1 hour | ||
keyPrefix: 'p1', // key prefix. This is used if you have multiple functions and need to set a unique prefix. | ||
} | ||
const wrappedFunction = cache.wrap(asyncFunction, options); | ||
console.log(await wrappedFunction(2)); // 4 | ||
@@ -308,0 +314,0 @@ console.log(await wrappedFunction(2)); // 4 from cache |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
157286
3750
350
Updatedhookified@^1.4.0
Updatedkeyv@^5.1.2