Registry / database / typescript-cacheable

typescript-cacheable

JSON →
library3.0.3jsnpmunverified

TypeScript Cacheable is an in-memory memoization decorator library designed to cache the results of expensive TypeScript methods or property accessors. Currently at version 3.0.3, it provides a stable API for enhancing application performance by storing computed values for subsequent retrieval. The library distinguishes itself by offering flexible caching scopes: 'GLOBAL' for application-wide caching, and 'LOCAL_STORAGE' which leverages Node.js's `AsyncLocalStorage` to provide request-scoped or call-chain-scoped caching, crucial for web applications. Cache keys can be automatically inferred from method parameters if they are JSON-serializable, or explicitly defined by implementing the `CacheableKey` interface for complex objects. While the library itself does not have a stated release cadence, its `3.x` version indicates active development and maintenance.

npm install typescript-cacheable
INSTALL
IMPORT
SIG · TYPESCRIPT-CACHEAB
T
typescript-cacheable
databasejavascriptv3.0.3
Install
Import
Disk
Pass rate
0/ 6
Env Coverage0 / 6
glibc
1822
musl
1822
Install & Compatibility
Where this runs
tested against v? · npm install
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
musl
node 18226 runs
build_error
glibc
node 18226 runs
build_error
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

Cacheable
import { Cacheable } from 'typescript-cacheable';
const Cacheable = require('typescript-cacheable');
The Cacheable decorator is a named export. ESM import syntax is standard. CommonJS `require` is generally not suitable for decorators or modern TypeScript modules.
CacheableKey
import { CacheableKey } from 'typescript-cacheable';
CacheableKey is a TypeScript interface, used for type-checking and defining custom cache key logic. It is a named export and disappears at runtime.
CacheableOptions (implicit)
import { Cacheable } from 'typescript-cacheable'; // Example usage with options @Cacheable({ scope: 'GLOBAL', ttl: 60000 })
Configuration options for the decorator are passed as an object to `Cacheable()`. While `CacheableOptions` is not directly imported as a type, its properties are part of the `Cacheable` decorator's API.

This quickstart demonstrates `typescript-cacheable` with global, parameterized, and `AsyncLocalStorage`-scoped caching. It shows how to apply the `@Cacheable` decorator, highlights automatic key inference for JSON-serializable arguments, and illustrates the setup required for request-scoped caching using Node.js's `AsyncLocalStorage` to ensure calls within the same 'request' hit the cache, while separate 'requests' compute new values.

import { Cacheable } from 'typescript-cacheable'; import { AsyncLocalStorage } from 'async_hooks'; interface Dwarf { name: string; lastName: string; } // Simulate a context for AsyncLocalStorage (e.g., an HTTP request context) class Context { constructor(public requestId: string) {} } const als = new AsyncLocalStorage<Context>(); // Helper to get the store for LOCAL_STORAGE scope export const getStore = (): unknown => als.getStore(); class DwarfService { private callCount: number = 0; // Caching globally without parameters @Cacheable() public async findHappiest(): Promise<Dwarf> { this.callCount++; return new Promise((resolve) => { setTimeout(() => { resolve({ name: 'Huck', lastName: 'Finn' }); }, 100); // Simulate expensive operation }); } // Caching with parameters, inferring key from JSON-serializable args @Cacheable() public async countByLastName(name: string): Promise<number> { this.callCount++; return new Promise((resolve) => { setTimeout(() => { resolve(name.length * 5); }, 50); // Simulate expensive operation }); } // Caching with AsyncLocalStorage (request scope) @Cacheable({ scope: 'LOCAL_STORAGE', getStore: getStore }) public async getRequestScopedValue(): Promise<string> { const store = als.getStore(); const requestId = store ? store.requestId : 'no-request-id'; this.callCount++; return new Promise((resolve) => { setTimeout(() => { resolve(`Value for req ${requestId}, call ${this.callCount}`); }, 20); // Simulate expensive operation }); } public getCallCount(): number { return this.callCount; } } // Example Usage async function runExamples() { const service = new DwarfService(); console.log('--- Global Cache Example ---'); const dwarf1 = await service.findHappiest(); console.log('First call (global):', dwarf1, 'Call Count:', service.getCallCount()); const dwarf2 = await service.findHappiest(); console.log('Second call (global, cached):', dwarf2, 'Call Count:', service.getCallCount()); console.log('\n--- Parameterized Cache Example ---'); service['callCount'] = 0; // Reset for demonstration const count1 = await service.countByLastName('Snow'); console.log('First call (params):', count1, 'Call Count:', service.getCallCount()); const count2 = await service.countByLastName('Snow'); console.log('Second call (params, cached):', count2, 'Call Count:', service.getCallCount()); const count3 = await service.countByLastName('White'); console.log('Third call (params, new arg):', count3, 'Call Count:', service.getCallCount()); console.log('\n--- Local Storage Cache Example (Simulated Request) ---'); service['callCount'] = 0; // Reset for demonstration await als.run(new Context('req-123'), async () => { const valA1 = await service.getRequestScopedValue(); console.log('Req 123 - Call 1:', valA1, 'Call Count:', service.getCallCount()); const valA2 = await service.getRequestScopedValue(); console.log('Req 123 - Call 2 (cached):', valA2, 'Call Count:', service.getCallCount()); }); await als.run(new Context('req-456'), async () => { const valB1 = await service.getRequestScopedValue(); console.log('Req 456 - Call 1:', valB1, 'Call Count:', service.getCallCount()); const valB2 = await service.getRequestScopedValue(); console.log('Req 456 - Call 2 (cached):', valB2, 'Call Count:', service.getCallCount()); }); } runExamples();
Debug
Known issues
gotchaWhen using `GLOBAL` scope with methods that accept a wide range of unique, non-primitive parameters, the in-memory cache can grow indefinitely, leading to high memory consumption and potential memory leaks if not managed.
fix
Implement the `CacheableKey` interface for complex parameters to generate a controlled, unique cache key, or ensure parameters result in a finite set of cache entries. Consider using `ttl` (Time-To-Live) options to automatically expire cached entries.
affects: >=1.0.0
breakingDecorators, while widely used, are still an 'experimental' TypeScript feature. For `typescript-cacheable` to function correctly, `experimentalDecorators` and `emitDecoratorMetadata` must be enabled in your `tsconfig.json` compiler options. Future TypeScript versions might introduce changes to the decorator API.
fix
Add the following to your `tsconfig.json` under `compilerOptions`: `"experimentalDecorators": true, "emitDecoratorMetadata": true`.
affects: >=1.0.0
gotchaThe `LOCAL_STORAGE` caching scope relies on Node.js's `AsyncLocalStorage`. It is critical to correctly bind `AsyncLocalStorage` in your application's call chain (e.g., using middleware in an Express app) and ensure the `getStore` function provided to `@Cacheable` returns the *exact same* `AsyncLocalStorage` instance used for binding. Incorrect setup will result in the cache not being isolated per request or not being used at all.
fix
Review the `AsyncLocalStorage` integration example in the documentation. Ensure `als.run()` wraps the entire request context and that the `AsyncLocalStorage` instance is consistently used across your application for binding and retrieval within the decorator.
affects: >=1.0.0
gotchaAutomatic cache key inference for method parameters relies on JSON serialization. Parameters with circular references, `undefined` values within objects (top-level `undefined` arguments are supported), or non-serializable types will lead to errors or unexpected cache behavior.
fix
For complex or non-JSON-serializable parameters, implement the `CacheableKey` interface on the parameter class and provide a `cacheKey()` method that returns a unique string identifier. Alternatively, use a custom `keyComposer` option in the `@Cacheable` decorator.
affects: >=1.0.0
breakingMajor version updates (e.g., from v2.x to v3.x) for decorator-based libraries often involve breaking changes due to evolutions in TypeScript's decorator specification or internal implementation details. Always review the changelog when upgrading between major versions.
fix
Consult the official release notes and changelog for `typescript-cacheable` when migrating between major versions to understand specific API changes, deprecations, or required configuration updates.
affects: ~2.x to >=3.0.0
Errors
Common errors & fixes
Error: Decorators are not enabled. You must enable 'experimentalDecorators' in your tsconfig.json.
The TypeScript compiler is not configured to support experimental decorators, which are required for `@Cacheable` to function.
fix
Add or update the `compilerOptions` in your `tsconfig.json` file: `"experimentalDecorators": true, "emitDecoratorMetadata": true`.
TypeError: Cannot convert circular structure to JSON
A method decorated with `@Cacheable` received an argument that contains circular references, and the library attempted to JSON-serialize it to generate a cache key.
fix
For the problematic parameter, implement the `CacheableKey` interface on its class, providing a `cacheKey()` method that generates a unique, non-circular string. Alternatively, pass a custom `keyComposer` function to the `@Cacheable` decorator options.
Always re-computing value when using LOCAL_STORAGE scope / Cache is not working as expected with AsyncLocalStorage.
The `AsyncLocalStorage` instance is not correctly bound in the call chain, or the `getStore` function provided to `@Cacheable` does not return the same `AsyncLocalStorage` instance that was used to create the execution context.
fix
Verify that `AsyncLocalStorage.run()` is used to wrap the code path where caching should occur (e.g., in Express middleware) and that the `getStore` function consistently retrieves the correct `AsyncLocalStorage` instance. Refer to the `AsyncLocalStorage` example in the documentation.
Cannot apply a decorator to a non-method or non-accessor.
The `@Cacheable` decorator was applied to a class property directly, a class itself, or another non-callable member, instead of a method or property accessor (getter/setter).
fix
Ensure `@Cacheable` is only used on class methods or property getters/setters, as decorators in TypeScript modify the behavior of callable members.
Upgrade
Version history
3.0.3latest on npm
Audit
Dependencies

No dependency data recorded yet.

Agent activity
30 hits · last 30 days
node
24
Meta
2
OpenAI (training)
1
Resources