+75
-41
@@ -616,2 +616,3 @@ "use strict"; | ||
| store.delete(key); | ||
| this._hashCache.delete(key); | ||
| } | ||
@@ -701,3 +702,3 @@ /** | ||
| const cacheHashNumber = this._hashCache.get(key); | ||
| if (cacheHashNumber) { | ||
| if (typeof cacheHashNumber === "number") { | ||
| return cacheHashNumber; | ||
@@ -1152,2 +1153,31 @@ } | ||
| // 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 | ||
@@ -1164,2 +1194,3 @@ var import_keyv3 = require("keyv"); | ||
| CacheableHooks2["AFTER_GET_MANY"] = "AFTER_GET_MANY"; | ||
| CacheableHooks2["BEFORE_SECONDARY_SETS_PRIMARY"] = "BEFORE_SECONDARY_SETS_PRIMARY"; | ||
| return CacheableHooks2; | ||
@@ -1373,33 +1404,22 @@ })(CacheableHooks || {}); | ||
| } | ||
| /** | ||
| * Gets the value of the key. If the key does not exist in the primary store then it will check the secondary store. | ||
| * @param {string} key The key to get the value of | ||
| * @returns {Promise<T | undefined>} The value of the key or undefined if the key does not exist | ||
| */ | ||
| async get(key) { | ||
| 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); | ||
| result = await this._primary.get(key, { raw: true }); | ||
| let ttl; | ||
| if (!result && this._secondary) { | ||
| const rawResult = await this._secondary.get(key, { raw: true }); | ||
| if (rawResult) { | ||
| result = rawResult.value; | ||
| let finalTtl; | ||
| let expired = false; | ||
| if (rawResult.expires) { | ||
| const now = Date.now(); | ||
| finalTtl = rawResult.expires - now; | ||
| if (finalTtl <= 0) { | ||
| expired = true; | ||
| } | ||
| } | ||
| if (expired) { | ||
| result = void 0; | ||
| } else { | ||
| await this._primary.set(key, result, finalTtl); | ||
| } | ||
| 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 }); | ||
| await this.hook("AFTER_GET" /* AFTER_GET */, { key, result, ttl }); | ||
| } catch (error) { | ||
@@ -1416,14 +1436,10 @@ this.emit("error" /* ERROR */, error); | ||
| } | ||
| return result; | ||
| return raw ? result : result?.value; | ||
| } | ||
| /** | ||
| * Gets the values of the keys. If the key does not exist in the primary store then it will check the secondary store. | ||
| * @param {string[]} keys The keys to get the values of | ||
| * @returns {Promise<Array<T | undefined>>} The values of the keys or undefined if the key does not exist | ||
| */ | ||
| async getMany(keys) { | ||
| 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); | ||
| result = await this._primary.get(keys, { raw: true }); | ||
| if (this._secondary) { | ||
@@ -1436,8 +1452,12 @@ const missingKeys = []; | ||
| } | ||
| const secondaryResult = await this._secondary.get(missingKeys); | ||
| for (const [i, key] of keys.entries()) { | ||
| if (!result[i] && secondaryResult[i]) { | ||
| result[i] = secondaryResult[i]; | ||
| const finalTtl = shorthandToMilliseconds(this._ttl); | ||
| await this._primary.set(key, secondaryResult[i], finalTtl); | ||
| 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); | ||
| const expires = secondaryResults[i].expires; | ||
| 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); | ||
| } | ||
@@ -1460,3 +1480,3 @@ } | ||
| } | ||
| return result; | ||
| return raw ? result : result.map((item) => item?.value); | ||
| } | ||
@@ -1700,2 +1720,16 @@ /** | ||
| } | ||
| async getSecondaryRawResults(key) { | ||
| let result; | ||
| if (this._secondary) { | ||
| result = await this._secondary.get(key, { raw: true }); | ||
| } | ||
| return result; | ||
| } | ||
| async getManySecondaryRawResults(keys) { | ||
| let result = new Array(); | ||
| if (this._secondary) { | ||
| result = await this._secondary.get(keys, { raw: true }); | ||
| } | ||
| return result; | ||
| } | ||
| async deleteManyKeyv(keyv, keys) { | ||
@@ -1702,0 +1736,0 @@ const promises = []; |
+44
-12
@@ -1,2 +0,2 @@ | ||
| import { KeyvStoreAdapter, StoredData, Keyv } from 'keyv'; | ||
| import { KeyvStoreAdapter, StoredData, Keyv, StoredDataRaw } from 'keyv'; | ||
| export { Keyv, KeyvHooks, KeyvOptions, KeyvStoreAdapter } from 'keyv'; | ||
@@ -426,3 +426,4 @@ import { Hookified } from 'hookified'; | ||
| BEFORE_GET_MANY = "BEFORE_GET_MANY", | ||
| AFTER_GET_MANY = "AFTER_GET_MANY" | ||
| AFTER_GET_MANY = "AFTER_GET_MANY", | ||
| BEFORE_SECONDARY_SETS_PRIMARY = "BEFORE_SECONDARY_SETS_PRIMARY" | ||
| } | ||
@@ -597,13 +598,42 @@ declare enum CacheableEvents { | ||
| /** | ||
| * Gets the value of the key. If the key does not exist in the primary store then it will check the secondary store. | ||
| * @param {string} key The key to get the value of | ||
| * @returns {Promise<T | undefined>} The value of the key or undefined if the key does not exist | ||
| */ | ||
| get<T>(key: string): Promise<T | undefined>; | ||
| * Retrieves an entry from the cache, with an optional “raw” mode. | ||
| * | ||
| * 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 {{ 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`. | ||
| */ | ||
| get<T>(key: string, options?: { | ||
| raw?: false; | ||
| }): Promise<T | undefined>; | ||
| get<T>(key: string, options: { | ||
| raw: true; | ||
| }): Promise<StoredDataRaw<T>>; | ||
| /** | ||
| * Gets the values of the keys. If the key does not exist in the primary store then it will check the secondary store. | ||
| * @param {string[]} keys The keys to get the values of | ||
| * @returns {Promise<Array<T | undefined>>} The values of the keys or undefined if the key does not exist | ||
| */ | ||
| getMany<T>(keys: string[]): Promise<Array<T | undefined>>; | ||
| * Retrieves multiple entries from the cache. | ||
| * 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 {{ 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`. | ||
| */ | ||
| getMany<T>(keys: string[], options?: { | ||
| raw?: false; | ||
| }): Promise<Array<T | undefined>>; | ||
| getMany<T>(keys: string[], options: { | ||
| raw: true; | ||
| }): Promise<Array<StoredDataRaw<T>>>; | ||
| /** | ||
@@ -686,2 +716,4 @@ * 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; | ||
@@ -688,0 +720,0 @@ private setManyKeyv; |
+44
-12
@@ -1,2 +0,2 @@ | ||
| import { KeyvStoreAdapter, StoredData, Keyv } from 'keyv'; | ||
| import { KeyvStoreAdapter, StoredData, Keyv, StoredDataRaw } from 'keyv'; | ||
| export { Keyv, KeyvHooks, KeyvOptions, KeyvStoreAdapter } from 'keyv'; | ||
@@ -426,3 +426,4 @@ import { Hookified } from 'hookified'; | ||
| BEFORE_GET_MANY = "BEFORE_GET_MANY", | ||
| AFTER_GET_MANY = "AFTER_GET_MANY" | ||
| AFTER_GET_MANY = "AFTER_GET_MANY", | ||
| BEFORE_SECONDARY_SETS_PRIMARY = "BEFORE_SECONDARY_SETS_PRIMARY" | ||
| } | ||
@@ -597,13 +598,42 @@ declare enum CacheableEvents { | ||
| /** | ||
| * Gets the value of the key. If the key does not exist in the primary store then it will check the secondary store. | ||
| * @param {string} key The key to get the value of | ||
| * @returns {Promise<T | undefined>} The value of the key or undefined if the key does not exist | ||
| */ | ||
| get<T>(key: string): Promise<T | undefined>; | ||
| * Retrieves an entry from the cache, with an optional “raw” mode. | ||
| * | ||
| * 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 {{ 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`. | ||
| */ | ||
| get<T>(key: string, options?: { | ||
| raw?: false; | ||
| }): Promise<T | undefined>; | ||
| get<T>(key: string, options: { | ||
| raw: true; | ||
| }): Promise<StoredDataRaw<T>>; | ||
| /** | ||
| * Gets the values of the keys. If the key does not exist in the primary store then it will check the secondary store. | ||
| * @param {string[]} keys The keys to get the values of | ||
| * @returns {Promise<Array<T | undefined>>} The values of the keys or undefined if the key does not exist | ||
| */ | ||
| getMany<T>(keys: string[]): Promise<Array<T | undefined>>; | ||
| * Retrieves multiple entries from the cache. | ||
| * 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 {{ 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`. | ||
| */ | ||
| getMany<T>(keys: string[], options?: { | ||
| raw?: false; | ||
| }): Promise<Array<T | undefined>>; | ||
| getMany<T>(keys: string[], options: { | ||
| raw: true; | ||
| }): Promise<Array<StoredDataRaw<T>>>; | ||
| /** | ||
@@ -686,2 +716,4 @@ * 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; | ||
@@ -688,0 +720,0 @@ private setManyKeyv; |
+75
-41
@@ -572,2 +572,3 @@ // src/index.ts | ||
| store.delete(key); | ||
| this._hashCache.delete(key); | ||
| } | ||
@@ -657,3 +658,3 @@ /** | ||
| const cacheHashNumber = this._hashCache.get(key); | ||
| if (cacheHashNumber) { | ||
| if (typeof cacheHashNumber === "number") { | ||
| return cacheHashNumber; | ||
@@ -1108,2 +1109,31 @@ } | ||
| // 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 | ||
@@ -1123,2 +1153,3 @@ import { | ||
| CacheableHooks2["AFTER_GET_MANY"] = "AFTER_GET_MANY"; | ||
| CacheableHooks2["BEFORE_SECONDARY_SETS_PRIMARY"] = "BEFORE_SECONDARY_SETS_PRIMARY"; | ||
| return CacheableHooks2; | ||
@@ -1332,33 +1363,22 @@ })(CacheableHooks || {}); | ||
| } | ||
| /** | ||
| * Gets the value of the key. If the key does not exist in the primary store then it will check the secondary store. | ||
| * @param {string} key The key to get the value of | ||
| * @returns {Promise<T | undefined>} The value of the key or undefined if the key does not exist | ||
| */ | ||
| async get(key) { | ||
| 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); | ||
| result = await this._primary.get(key, { raw: true }); | ||
| let ttl; | ||
| if (!result && this._secondary) { | ||
| const rawResult = await this._secondary.get(key, { raw: true }); | ||
| if (rawResult) { | ||
| result = rawResult.value; | ||
| let finalTtl; | ||
| let expired = false; | ||
| if (rawResult.expires) { | ||
| const now = Date.now(); | ||
| finalTtl = rawResult.expires - now; | ||
| if (finalTtl <= 0) { | ||
| expired = true; | ||
| } | ||
| } | ||
| if (expired) { | ||
| result = void 0; | ||
| } else { | ||
| await this._primary.set(key, result, finalTtl); | ||
| } | ||
| 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 }); | ||
| await this.hook("AFTER_GET" /* AFTER_GET */, { key, result, ttl }); | ||
| } catch (error) { | ||
@@ -1375,14 +1395,10 @@ this.emit("error" /* ERROR */, error); | ||
| } | ||
| return result; | ||
| return raw ? result : result?.value; | ||
| } | ||
| /** | ||
| * Gets the values of the keys. If the key does not exist in the primary store then it will check the secondary store. | ||
| * @param {string[]} keys The keys to get the values of | ||
| * @returns {Promise<Array<T | undefined>>} The values of the keys or undefined if the key does not exist | ||
| */ | ||
| async getMany(keys) { | ||
| 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); | ||
| result = await this._primary.get(keys, { raw: true }); | ||
| if (this._secondary) { | ||
@@ -1395,8 +1411,12 @@ const missingKeys = []; | ||
| } | ||
| const secondaryResult = await this._secondary.get(missingKeys); | ||
| for (const [i, key] of keys.entries()) { | ||
| if (!result[i] && secondaryResult[i]) { | ||
| result[i] = secondaryResult[i]; | ||
| const finalTtl = shorthandToMilliseconds(this._ttl); | ||
| await this._primary.set(key, secondaryResult[i], finalTtl); | ||
| 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); | ||
| const expires = secondaryResults[i].expires; | ||
| 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); | ||
| } | ||
@@ -1419,3 +1439,3 @@ } | ||
| } | ||
| return result; | ||
| return raw ? result : result.map((item) => item?.value); | ||
| } | ||
@@ -1659,2 +1679,16 @@ /** | ||
| } | ||
| async getSecondaryRawResults(key) { | ||
| let result; | ||
| if (this._secondary) { | ||
| result = await this._secondary.get(key, { raw: true }); | ||
| } | ||
| return result; | ||
| } | ||
| async getManySecondaryRawResults(keys) { | ||
| let result = new Array(); | ||
| if (this._secondary) { | ||
| result = await this._secondary.get(keys, { raw: true }); | ||
| } | ||
| return result; | ||
| } | ||
| async deleteManyKeyv(keyv, keys) { | ||
@@ -1661,0 +1695,0 @@ const promises = []; |
+9
-9
| { | ||
| "name": "cacheable", | ||
| "version": "1.8.10", | ||
| "version": "1.9.0", | ||
| "description": "High Performance Layer 1 / Layer 2 Caching with Keyv Storage", | ||
@@ -24,16 +24,16 @@ "type": "module", | ||
| "devDependencies": { | ||
| "@faker-js/faker": "^9.6.0", | ||
| "@keyv/redis": "^4.3.2", | ||
| "@types/node": "^22.14.0", | ||
| "@vitest/coverage-v8": "^3.1.1", | ||
| "@faker-js/faker": "^9.7.0", | ||
| "@keyv/redis": "^4.4.0", | ||
| "@types/node": "^22.15.3", | ||
| "@vitest/coverage-v8": "^3.1.3", | ||
| "lru-cache": "^11.1.0", | ||
| "rimraf": "^6.0.1", | ||
| "tsup": "^8.4.0", | ||
| "typescript": "^5.8.2", | ||
| "vitest": "^3.1.1", | ||
| "typescript": "^5.8.3", | ||
| "vitest": "^3.1.3", | ||
| "xo": "^0.60.0" | ||
| }, | ||
| "dependencies": { | ||
| "hookified": "^1.8.1", | ||
| "keyv": "^5.3.2" | ||
| "hookified": "^1.8.2", | ||
| "keyv": "^5.3.3" | ||
| }, | ||
@@ -40,0 +40,0 @@ "keywords": [ |
+108
-15
@@ -32,2 +32,3 @@ [<img align="center" src="https://cacheable.org/logo.svg" alt="Cacheable" />](https://github.com/jaredwray/cacheable) | ||
| * [Storage Tiering and Caching](#storage-tiering-and-caching) | ||
| * [TTL Propagation and Storage Tiering](#ttl-propagation-and-storage-tiering) | ||
| * [Shorthand for Time to Live (ttl)](#shorthand-for-time-to-live-ttl) | ||
@@ -42,4 +43,4 @@ * [Non-Blocking Operations](#non-blocking-operations) | ||
| * [CacheableMemory - API](#cacheablememory---api) | ||
| * [Keyv Storage Adapter - KeyvCacheableMemory](#keyv-storage-adapter---keyvcacheablememory) | ||
| * [Wrap / Memoization for Sync and Async Functions](#wrap--memoization-for-sync-and-async-functions) | ||
| * [Keyv Storage Adapter - KeyvCacheableMemory](#keyv-storage-adapter---keyvcacheablememory) | ||
| * [How to Contribute](#how-to-contribute) | ||
@@ -103,2 +104,3 @@ * [License and Copyright](#license-and-copyright) | ||
| * `AFTER_GET_MANY`: This is called after the `getMany()` method is called. | ||
| * `BEFORE_SECONDARY_SETS_PRIMARY`: This is called when the secondary store sets the value in the primary store. | ||
@@ -116,2 +118,15 @@ An example of how to use these hooks: | ||
| Here is an example of how to use `BEFORE_SECONDARY_SETS_PRIMARY` hook: | ||
| ```javascript | ||
| import { Cacheable, CacheableHooks } from 'cacheable'; | ||
| import KeyvRedis from '@keyv/redis'; | ||
| const secondary = new KeyvRedis('redis://user:pass@localhost:6379'); | ||
| const cache = new Cacheable({secondary}); | ||
| cache.onHook(CacheableHooks.BEFORE_SECONDARY_SETS_PRIMARY, (data) => { | ||
| console.log(`before secondary sets primary: ${data.key} ${data.value} ${data.ttl}`); | ||
| }); | ||
| ``` | ||
| This is called when the secondary store sets the value in the primary store. This is useful if you want to do something before the value is set in the primary store such as manipulating the ttl or the value. | ||
| # Storage Tiering and Caching | ||
@@ -126,2 +141,44 @@ | ||
| When `Getting Data` if the value does not exist in the primary store it will try to get it from the secondary store. If the secondary store returns the value it will set it in the primary store. Because we use [TTL Propagation](#ttl-propagation-and-storage-tiering) the value will be set in the primary store with the TTL of the secondary store unless the time to live (TTL) is greater than the primary store which will then use the TTL of the primary store. An example of this is: | ||
| ```javascript | ||
| import { Cacheable } from 'cacheable'; | ||
| import KeyvRedis from '@keyv/redis'; | ||
| const secondary = new KeyvRedis('redis://user:pass@localhost:6379', { ttl: 1000 }); | ||
| const cache = new Cacheable({secondary, ttl: 100}); | ||
| await cache.set('key', 'value'); // sets the value in the primary store with a ttl of 100 ms and secondary store with a ttl of 1000 ms | ||
| await sleep(500); // wait for .5 seconds | ||
| const value = await cache.get('key'); // gets the value from the secondary store and now sets the value in the primary store with a ttl of 500 ms which is what is left from the secondary store | ||
| ``` | ||
| In this example the primary store has a ttl of `100 ms` and the secondary store has a ttl of `1000 ms`. Because the ttl is greater in the secondary store it will default to setting ttl value in the primary store. | ||
| ```javascript | ||
| import { Cacheable } from 'cacheable'; | ||
| import {Keyv} from 'keyv'; | ||
| import KeyvRedis from '@keyv/redis'; | ||
| const primary = new Keyv({ ttl: 200 }); | ||
| const secondary = new KeyvRedis('redis://user:pass@localhost:6379', { ttl: 1000 }); | ||
| const cache = new Cacheable({primary, secondary}); | ||
| await cache.set('key', 'value'); // sets the value in the primary store with a ttl of 100 ms and secondary store with a ttl of 1000 ms | ||
| await sleep(200); // wait for .2 seconds | ||
| const value = await cache.get('key'); // gets the value from the secondary store and now sets the value in the primary store with a ttl of 200 ms which is what the primary store is set with | ||
| ``` | ||
| # TTL Propagation and Storage Tiering | ||
| Cacheable TTL propagation is a feature that allows you to set a time to live (TTL) for the cache. By default the TTL is set in the following order: | ||
| ``` | ||
| ttl = set at the function ?? storage adapter ttl ?? cacheable ttl | ||
| ``` | ||
| This means that if you set a TTL at the function level it will override the storage adapter TTL and the cacheable TTL. If you do not set a TTL at the function level it will use the storage adapter TTL and then the cacheable TTL. If you do not set a TTL at all it will use the default TTL of `undefined` which is disabled. | ||
| # Shorthand for Time to Live (ttl) | ||
@@ -165,2 +222,36 @@ | ||
| ## Retrieving raw cache entries | ||
| The `get` and `getMany` methods support a `raw` option, which returns the full stored metadata (`StoredDataRaw<T>`) instead of just the value: | ||
| ```typescript | ||
| import { Cacheable } from 'cacheable'; | ||
| const cache = new Cacheable(); | ||
| // store a value | ||
| await cache.set('user:1', { name: 'Alice' }); | ||
| // default: only the value | ||
| const user = await cache.get<{ name: string }>('user:1'); | ||
| console.log(user); // { name: 'Alice' } | ||
| // with raw: full record including expiration | ||
| const raw = await cache.get<{ name: string }>('user:1', { raw: true }); | ||
| console.log(raw.value); // { name: 'Alice' } | ||
| console.log(raw.expires); // e.g. 1677628495000 or null | ||
| ``` | ||
| ```typescript | ||
| // getMany with raw option | ||
| await cache.set('a', 1); | ||
| await cache.set('b', 2); | ||
| const raws = await cache.getMany<number>(['a', 'b'], { raw: true }); | ||
| raws.forEach((entry, idx) => { | ||
| console.log(`key=${['a','b'][idx]}, value=${entry?.value}, expires=${entry?.expires}`); | ||
| }); | ||
| ``` | ||
| # Non-Blocking Operations | ||
@@ -224,3 +315,5 @@ | ||
| * `get(key)`: Gets a value from the cache. | ||
| * `get(key, { raw: true })`: Gets a raw value from the cache. | ||
| * `getMany([keys])`: Gets multiple values from the cache. | ||
| * `getMany([keys], { raw: true })`: Gets multiple raw values from the cache. | ||
| * `has(key)`: Checks if a value exists in the cache. | ||
@@ -299,2 +392,16 @@ * `hasMany([keys])`: Checks if multiple values exist in the cache. | ||
| # 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 | ||
@@ -374,16 +481,2 @@ | ||
| # 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 | ||
| ``` | ||
| # How to Contribute | ||
@@ -390,0 +483,0 @@ |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
182120
5.88%4198
2.39%478
24.16%4
Infinity%Updated
Updated