Skip to content

Dodilanne/cachedts

Repository files navigation

cachedts – Simple Function-Level Caching for TypeScript APIs

cachedts is a lightweight utility for function-level memoization of object methods in TypeScript. It wraps an object and automatically caches the results of its functions based on input arguments.

Perfect for avoiding repeated computations, expensive API calls, or just improving performance with minimal setup.


Features

  • Zero-dependency
  • Memoizes function results by arguments
  • Optional TTL-based expiration
  • LRU cache eviction with configurable maxSize
  • Per-function or global settings
  • Debug logging
  • Custom cache key generation

Quick Start

import { cached } from "cachedts";

const mathApi = {
  slowSquare(x: number) {
    console.log("Calculating...");
    return x * x;
  },
};

const cachedMath = cached(mathApi);

cachedMath.slowSquare(5); // logs "Calculating..." and returns 25
cachedMath.slowSquare(5); // returns 25 instantly (from cache)

Options

const cachedApi = cached(apiObject, {
  debug: true, // Log cache hits/misses
  settings: {
    enabled: true,
    ttl: 5000, // Cache expires after 5 seconds
    maxSize: 100, // Keep at most 100 entries per function (LRU eviction)
  },
  overrides: {
    // Per-method override
    expensiveMethod: { ttl: 10000 },
  },
  getCacheKey(methodName, args) {
    return `${methodName}-${JSON.stringify(args)}`;
  },
});
Option Type Description
cache Map Provide a shared cache instance
debug boolean Enable logging for cache hits/misses
settings { enabled?: boolean; ttl?: number; maxSize?: number } Global cache settings
overrides Partial<Record<keyof TApi, CacheSettings>> Per-method cache control
getCacheKey (methodName, args) => string | symbol Custom key generator for cache entries

Example with TTL

const api = {
  greet(name: string) {
    return `Hello, ${name}`;
  },
};

const cachedApi = cached(api, {
  settings: { ttl: 1000 },
});

cachedApi.greet("Alice"); // cache miss
setTimeout(() => {
  cachedApi.greet("Alice"); // cache hit
}, 500);

setTimeout(() => {
  cachedApi.greet("Alice"); // cache expired, miss
}, 1500);

LRU Cache Eviction

Limit the number of cached entries per function with maxSize. When the limit is exceeded, the least recently used entry is evicted.

const cachedApi = cached(api, {
  settings: { maxSize: 2 },
});

cachedApi.greet("Alice"); // cached
cachedApi.greet("Bob");   // cached
cachedApi.greet("Eve");   // cached, "Alice" evicted (least recently used)

maxSize can be combined with ttl and set per-method via overrides.


Pruning Expired Entries

When ttl is set, expired entries are automatically pruned from a function's cache on every cache miss. This ensures expired entries don't accumulate and, when combined with maxSize, prevents LRU eviction from evicting valid entries over expired ones. Cache hits do not trigger pruning.

For bulk cleanup across all functions, use prune():

import { cached, prune } from "cachedts";

const cachedApi = cached(api, { settings: { ttl: 5000 } });

// Remove all expired entries across all functions
prune(cachedApi);

Pruning only sweeps the cache of the function being called, not all functions. Both automatic and manual pruning respect per-function TTL overrides.


Cache Invalidation

You can clear cached results using the invalidate function. It allows three levels of granularity:

Invalidate the entire cache

import { invalidate } from "cachedts";

invalidate(cachedApi); // Clears all cached entries for all methods

Invalidate a specific method's cache

invalidate(cachedApi, "getParams"); // Clears all cache entries for `getParams`

Invalidate a specific method call with given arguments

invalidate(cachedApi, "getParams", "user123", 42); // Clears cache entry for `getParams("user123", 42)`

This is useful when the underlying data changes and a fresh fetch is needed for specific arguments.

Notes

  • No effect is applied if the method is not a function or has no cached entries.
  • Cache key computation uses the same mechanism as the one used by cached(), including custom getCacheKey if provided.

Advanced Usage: Custom Cache Key

By default, cache keys are generated based on the function name and serialized arguments.

To customize:

const cachedApi = cached(api, {
  getCacheKey(methodName, args) {
    if (methodName === "fetchUser") {
      return args[0]; // Use user ID directly
    }
  },
});

Accessing Cache Internals

You can introspect the wrapped object via the cacheStateKey:

import { cacheStateKey } from "cachedts";

const state = cachedApi[cacheStateKey];
console.log(state.cache); // underlying Map

Or by using the getCacheState function:

import { getCacheState } from "cachedts";

const state = getCacheState(cachedApi);
console.log(state.cache); // nderlying Map

Disabling Caching

Disable globally or per-method:

cached(api, {
  settings: { enabled: false }, // globally off
  overrides: {
    compute: { enabled: true }, // override for `compute` method
  },
});

Installation

npm install cachedts

License

MIT

About

Simple transparent caching utils for TypeScript

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors