import type { RequestHandler } from 'express'; import type { MemorizeStoreLike } from '../MemorizeStoreLike'; import type { CacheInfo } from './CacheInfo'; import type { MemorizeBatchOptions } from './MemorizeBatchOptions'; import type { MemorizeCallOptions } from './MemorizeCallOptions'; import type { MemorizeInspectionOptions, MemorizeInspectionPage } from './MemorizeInspection'; export interface DeleteMatchingOptions { /** When `true`, only the exact key is deleted (no child keys). Default `false`. */ exactMatch?: boolean; } /** * Options accepted by {@link Memorize.set}, {@link Memorize.setAsync}, * {@link Memorize.remember}, and {@link Memorize.rememberAsync} in place of a * plain TTL number. */ export interface MemorizeSetOptions { /** Time-to-live in milliseconds. Defaults to the global TTL. */ ttl?: number; /** * Invalidation tags attached to the entry. All entries carrying a tag can be * removed at once with {@link Memorize.deleteByTag}. */ tags?: string[]; /** * Length of the stale window in milliseconds (stale-while-revalidate). * * After `ttl` elapses the entry becomes *stale* but remains servable for this * many additional milliseconds. During the stale window, {@link Memorize.remember} * and {@link Memorize.rememberAsync} return the stale value immediately and run * the factory **in the background** to refresh the entry. After the window * closes the entry is evicted and the next read is a regular miss. * * Only meaningful with a finite `ttl`. */ staleWhileRevalidate?: number; } import type { MemorizeDeleteEvent } from './MemorizeDeleteEvent'; import type { MemorizeEmptyEvent } from './MemorizeEmptyEvent'; import type { MemorizeEventType } from './MemorizeEventType'; import type { MemorizeEvictEvent } from './MemorizeEvictEvent'; import type { MemorizeExpireEvent } from './MemorizeExpireEvent'; import type { MemorizeSetEvent } from './MemorizeSetEvent'; import type { MemorizeStats } from './MemorizeStats'; /** * The cache instance returned by {@link memorize}. * * It is both a callable that produces Express middleware **and** a namespace for * cache management methods and event hooks. All middleware created from the same * `Memorize` instance share a single underlying store. * * @example * ```ts * const cache = memorize({ ttl: 30_000 }); * * // Per-route middleware * app.get('/users', cache(), handler); * * // Global middleware — caches every GET route automatically * app.use(cache()); * * // Direct service-level usage * const users = await cache.remember('users:list', () => userService.findAll()); * * // Cache management * cache.delete('/users'); * cache.clear(); * ``` */ export interface Memorize { /** * Returns an Express `RequestHandler` that caches `GET` responses with a `2xx` * status code. Alias of {@link Memorize.express} kept for backwards compatibility. * * @param options - Optional per-route options (e.g. TTL override). */ (options?: MemorizeCallOptions): RequestHandler; /** * Returns an Express `RequestHandler` that caches `GET` responses with a `2xx` * status code. * * - On a **cache miss** the request proceeds normally. The response is intercepted * and stored after being sent. Sets `X-Cache: MISS`. * - On a **cache hit** the stored response is returned immediately without calling * downstream handlers. Sets `X-Cache: HIT`. * - Non-`GET` requests are forwarded to `next()` unchanged. * * @param options - Optional per-route options (e.g. TTL override). * * @example * ```ts * app.get('/users', cache.express(), handler); // global TTL * app.get('/products', cache.express({ ttl: 5_000 }), handler); // 5-second override * app.use(cache.express()); // global middleware * ``` */ express(options?: MemorizeCallOptions): RequestHandler; /** * Stores an arbitrary value in the cache under the given key. * * The value is serialized with `JSON.stringify`. Retrieve it with {@link getValue}. * * @param key - Cache key. * @param value - Value to cache. * @param ttlOrOptions - Time-to-live in milliseconds, or a {@link MemorizeSetOptions} * object (`ttl`, `tags`, `staleWhileRevalidate`). Defaults to the global TTL. * * @example * ```ts * cache.set('config', { theme: 'dark' }); * cache.set('config', { theme: 'dark' }, 60_000); * cache.set('users:1', user, { ttl: 60_000, tags: ['users'] }); * ``` */ set(key: string, value: T, ttlOrOptions?: number | MemorizeSetOptions): void; /** * Async variant of {@link set}. It yields back to the event loop before * serializing and storing the value, which helps when many direct-cache writes * are processed with `await` in a loop. * * @param key - Cache key. * @param value - Value to cache. * @param ttlOrOptions - Time-to-live in milliseconds, or a {@link MemorizeSetOptions} object. */ setAsync(key: string, value: T, ttlOrOptions?: number | MemorizeSetOptions): Promise; /** * Returns the cached value for the given key, or `undefined` if the key does * not exist or has expired. * * Values stored via {@link set} or {@link remember} are deserialized with * `JSON.parse`. * * @param key - Cache key. * * @example * ```ts * const config = cache.getValue('config'); * ``` */ getValue(key: string): T | undefined; /** * Async variant of {@link getValue}. It yields back to the event loop before * deserializing the cached value. * * @param key - Cache key. */ getValueAsync(key: string): Promise; /** * Returns the cached value for the given key if it exists, otherwise calls * `factory`, caches the result, and returns it. * * With `staleWhileRevalidate`, an expired-but-stale entry is returned * immediately and the factory runs in the background to refresh it * (concurrent stale reads trigger a single refresh). * * @param key - Cache key. * @param factory - Async or sync function that produces the value on a cache miss. * @param ttlOrOptions - Time-to-live in milliseconds, or a {@link MemorizeSetOptions} * object (`ttl`, `tags`, `staleWhileRevalidate`). Defaults to the global TTL. * * @example * ```ts * const users = await cache.remember('users:list', () => userService.findAll()); * const users = await cache.remember('users:list', () => userService.findAll(), 30_000); * const users = await cache.remember('users:list', () => userService.findAll(), { * ttl: 30_000, * staleWhileRevalidate: 60_000, * tags: ['users'], * }); * ``` */ remember(key: string, factory: () => T | Promise, ttlOrOptions?: number | MemorizeSetOptions): Promise; /** * Async variant of {@link remember}. It uses {@link getValueAsync} and * {@link setAsync} for cooperative yielding around direct-cache * serialization/deserialization. * * @param key - Cache key. * @param factory - Async or sync function that produces the value on a cache miss. * @param ttlOrOptions - Time-to-live in milliseconds, or a {@link MemorizeSetOptions} object. */ rememberAsync(key: string, factory: () => T | Promise, ttlOrOptions?: number | MemorizeSetOptions): Promise; /** * Returns the {@link CacheInfo} for a specific cache key, or `null` if the key * does not exist or has expired. * * @param key - The full request URL used as the cache key (e.g. `/users?page=1`). * * @example * ```ts * const info = cache.get('/users'); * if (info) console.log(`expires in ${info.remainingTtl}ms`); * ``` */ get(key: string): CacheInfo | null; /** * Returns all active (non-expired) cache entries as a plain object keyed by URL. * * @example * ```ts * console.log(Object.keys(cache.getAll())); // ['/users', '/products'] * ``` */ getAll(): Record; /** * Async variant of {@link getAll}. It scans entries in batches and yields * back to the event loop between batches, which reduces long synchronous * pauses on large stores. * * @param options - Batch options. * @returns All active cache entries keyed by cache key. * * @example * ```ts * const entries = await cache.getAllAsync({ batchSize: 500 }); * ``` */ getAllAsync(options?: MemorizeBatchOptions): Promise>; /** * Returns a paginated list of cache metadata without cached bodies. The scan * yields between batches and does not affect LRU order, hits, or misses. * * @example * ```ts * const page = await cache.inspectAsync({ offset: 0, limit: 100, batchSize: 500 }); * ``` */ inspectAsync(options?: MemorizeInspectionOptions): Promise; /** * Removes a single entry from the cache and emits a {@link MemorizeEventType.Delete} event. * * @param key - The full request URL to invalidate. * @returns `true` if the entry existed and was removed, `false` otherwise. * * @example * ```ts * app.post('/users', (req, res) => { * users.push(req.body); * cache.delete('/users'); * res.status(201).json(req.body); * }); * ``` */ delete(key: string): boolean; /** * Removes all cache entries carrying at least one of the given tags and emits * a {@link MemorizeEventType.Delete} event for each removed entry. * * Tags are attached at write time via {@link MemorizeSetOptions.tags} or the * middleware `tags` call option. * * @param tag - A tag or list of tags. * @returns The number of entries removed. * * @example * ```ts * cache.set('users:1', alice, { tags: ['users'] }); * cache.set('users:2', bob, { tags: ['users'] }); * cache.deleteByTag('users'); // → 2 * ``` */ deleteByTag(tag: string | string[]): number; /** * Async variant of {@link deleteByTag}. It removes matching entries in * batches and yields back to the event loop between batches, which reduces * long synchronous pauses on large stores. * * @param tag - A tag or list of tags. * @param options - Batch options. * @returns The number of entries removed. */ deleteByTagAsync(tag: string | string[], options?: MemorizeBatchOptions): Promise; /** * Removes all cache entries whose keys match the given glob pattern. * Emits a {@link MemorizeEventType.Delete} event for each removed entry. * * Glob rules: * - `**` — matches any character sequence **across** path segments (crosses `/`). * - `*` — matches any character sequence **within** a single path segment (does not cross `/`). * - `?` — matches any single character except `/`. * * @param pattern - Glob pattern to match against cache keys. * @returns The number of entries removed. * * @example * ```ts * // Invalidate all cached variants of a user regardless of query params. * // Build the pattern with join to avoid the closing-comment sequence in source. * app.put('/users/:id', (req, res) => { * users.update(req.params.id, req.body); * const pattern = ['**', 'users', req.params.id + '*'].join('/'); * cache.deleteMatching(pattern); // e.g. ** /users/abc123* (no space) * res.json({ ok: true }); * }); * ``` */ deleteMatching(pattern: string | Array, options?: DeleteMatchingOptions): number; /** * Async variant of {@link deleteMatching}. It removes matching entries in * batches and yields back to the event loop between batches, which reduces * long synchronous pauses on large stores. * * @param pattern - Glob pattern to match against cache keys. * @param options - Batch options. * @returns The number of entries removed. * * @example * ```ts * await cache.deleteMatchingAsync('/api/users/*', { batchSize: 500 }); * ``` */ deleteMatchingAsync(pattern: string | Array, options?: DeleteMatchingOptions & MemorizeBatchOptions): Promise; /** * Removes **all** entries from the cache and emits a {@link MemorizeEventType.Delete} * event for each. * * @example * ```ts * cache.clear(); * ``` */ clear(): void; /** * Async variant of {@link clear}. It removes entries in batches and yields * back to the event loop between batches, which reduces long synchronous * pauses on large stores. * * @param options - Batch options. * @returns The number of entries removed. * * @example * ```ts * await cache.clearAsync({ batchSize: 500 }); * ``` */ clearAsync(options?: MemorizeBatchOptions): Promise; /** * Registers a listener for cache events. * * | Event | When | * |-------|------| * | `MemorizeEventType.Set` | A response is stored | * | `MemorizeEventType.Delete` | An entry is removed via `delete()` or `clear()` | * | `MemorizeEventType.Expire` | An entry's TTL elapses | * | `MemorizeEventType.Empty` | The last entry is removed, cache is now empty | * * Returns a function that unregisters the listener. * * @example * ```ts * cache.on(MemorizeEventType.Set, (e) => console.log('stored', e.key)); * cache.on(MemorizeEventType.Delete, (e) => console.log('deleted', e.key)); * cache.on(MemorizeEventType.Expire, (e) => console.log('expired', e.key)); * cache.on(MemorizeEventType.Empty, () => console.log('cache is empty')); * * const unsubscribe = cache.on(MemorizeEventType.Set, handler); * unsubscribe(); // same as cache.off(MemorizeEventType.Set, handler) * ``` */ on(event: MemorizeEventType.Set, handler: (e: MemorizeSetEvent) => void): () => void; on(event: MemorizeEventType.Delete, handler: (e: MemorizeDeleteEvent) => void): () => void; on(event: MemorizeEventType.Expire, handler: (e: MemorizeExpireEvent) => void): () => void; on(event: MemorizeEventType.Empty, handler: (e: MemorizeEmptyEvent) => void): () => void; on(event: MemorizeEventType.Evict, handler: (e: MemorizeEvictEvent) => void): () => void; /** * Unregisters a listener previously registered with {@link on}. The handler * must be the same function reference. Unknown handlers are ignored. */ off(event: MemorizeEventType.Set, handler: (e: MemorizeSetEvent) => void): void; off(event: MemorizeEventType.Delete, handler: (e: MemorizeDeleteEvent) => void): void; off(event: MemorizeEventType.Expire, handler: (e: MemorizeExpireEvent) => void): void; off(event: MemorizeEventType.Empty, handler: (e: MemorizeEmptyEvent) => void): void; off(event: MemorizeEventType.Evict, handler: (e: MemorizeEvictEvent) => void): void; /** * Releases every resource held by the cache instance: the expiry timer, all * event listeners, worker threads used by `asyncSerializer: 'worker'`, and — * for SQLite storage — the database handle (persisted entries are kept on disk). * * Call it in tests and graceful shutdowns. The instance must not be used * after disposal. * * @example * ```ts * afterEach(() => cache.dispose()); * ``` */ dispose(): void; /** * Returns the number of active (non-expired) cache entries. * * @example * ```ts * console.log(`${cache.size()} entries in cache`); * ``` */ size(): number; /** * Returns the approximate total byte size of all cached bodies. * * The value is an estimate based on UTF-8 encoding for strings and * `byteLength` for buffers. It may not reflect actual memory usage. * * @example * ```ts * console.log(`~${cache.byteSize()} bytes cached`); * ``` */ byteSize(): number; /** * Returns aggregate cache statistics. * * @example * ```ts * const { entries, maxEntries, byteSize } = cache.getStats(); * ``` */ getStats(): MemorizeStats; /** * The underlying store. Intended for use by framework adapters only. * @internal */ _store: MemorizeStoreLike; /** * Global TTL configured through `memorize({ ttl })`. Intended for use by * framework adapters only. * @internal */ _ttl?: number; } //# sourceMappingURL=Memorize.d.ts.map