diff --git a/CHANGELOG.md b/CHANGELOG.md index aa2cde3..c5131cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # cringe lorg +## 10.1.0 + +- add `cache.info(key)` to get value as well as ttl and size + information. + ## 10.0.0 - `cache.fetch()` return type is now `Promise` diff --git a/README.md b/README.md index a12c0cc..0dd87df 100644 --- a/README.md +++ b/README.md @@ -607,6 +607,21 @@ If the key is not found, `get()` will return `undefined`. For the usage of the `status` option, see **Status Tracking** below. +### `info(key) => Entry | undefined` + +Return an `Entry` object containing the currently cached value, +as well as ttl and size information if available. Returns +undefined if the key is not found in the cache. + +Unlike `dump()` (which is designed to be portable and survive +serialization), the `start` value is always the current +timestamp, and the `ttl` is a calculated remaining time to live +(negative if expired). + +Note that stale values are always returned, rather than being +pruned and treated as if they were not in the cache. If you wish +to exclude stale entries, guard against a negative `ttl` value. + ### `async fetch(key, options = {}) => Promise` The following options are supported: diff --git a/src/index.ts b/src/index.ts index d930c53..58b6c4c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -130,6 +130,7 @@ class ZeroArray extends Array { } } export type { ZeroArray } +export type { Stack } export type StackLike = Stack | Index[] class Stack { @@ -164,7 +165,6 @@ class Stack { return this.heap[--this.length] as Index } } -export type { Stack } /** * Promise representing an in-progress {@link LRUCache#fetch} call @@ -792,7 +792,8 @@ export namespace LRUCache { | OptionsTTLLimit /** - * Entry objects used by {@link LRUCache#load} and {@link LRUCache#dump} + * Entry objects used by {@link LRUCache#load} and {@link LRUCache#dump}, + * and returned by {@link LRUCache#info}. */ export interface Entry { value: V @@ -1563,6 +1564,36 @@ export class LRUCache { return deleted } + /** + * Get the extended info about a given entry, to get its value, size, and + * TTL info simultaneously. Like {@link LRUCache#dump}, but just for a + * single key. Always returns stale values, if their info is found in the + * cache, so be sure to check for expired TTLs if relevant. + */ + info(key: K): LRUCache.Entry | undefined { + const i = this.#keyMap.get(key) + if (i === undefined) return undefined + const v = this.#valList[i] + const value: V | undefined = this.#isBackgroundFetch(v) + ? v.__staleWhileFetching + : v + if (value === undefined) return undefined + const entry: LRUCache.Entry = { value } + if (this.#ttls && this.#starts) { + const ttl = this.#ttls[i] + const start = this.#starts[i] + if (ttl && start) { + const remain = ttl - (perf.now() - start) + entry.ttl = remain + entry.start = Date.now() + } + } + if (this.#sizes) { + entry.size = this.#sizes[i] + } + return entry + } + /** * Return an array of [key, {@link LRUCache.Entry}] tuples which can be * passed to cache.load() diff --git a/test/info.ts b/test/info.ts new file mode 100644 index 0000000..69d5626 --- /dev/null +++ b/test/info.ts @@ -0,0 +1,38 @@ +import t from 'tap' +import { LRUCache } from '../dist/esm/index.js' + +t.test('just kv', t => { + const c = new LRUCache({ max: 2 }) + c.set(1, 10) + c.set(2, 20) + c.set(3, 30) + t.equal(c.info(1), undefined) + t.strictSame(c.info(2), { value: 20 }) + t.strictSame(c.info(3), { value: 30 }) + t.end() +}) + +t.test('other info', t => { + const c = new LRUCache({ + max: 2, + ttl: 1000, + maxSize: 10000, + }) + c.set(1, 10, { size: 100 }) + c.set(2, 20, { size: 200 }) + c.set(3, 30, { size: 300 }) + t.equal(c.info(1), undefined) + t.match(c.info(2), { + value: 20, + size: 200, + ttl: Number, + start: Number, + }) + t.match(c.info(3), { + value: 30, + size: 300, + ttl: Number, + start: Number, + }) + t.end() +})