npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@simpill/cache.utils

v1.0.0

Published

In-memory LRU and TTL cache with memoize; small and tree-shakeable (Node and Edge).

Downloads

118

Readme

Features: Type-safe · Node & Edge · Tree-shakeable · Lightweight


Installation

From npm

npm install @simpill/cache.utils

From GitHub

To use this package from the monorepo source:

git clone https://github.com/SkinnnyJay/simpill.git
cd simpill/utils/@simpill-cache.utils
npm install && npm run build

In your project you can then install from the local path: npm install /path/to/simpill/utils/@simpill-cache.utils or use npm link from the package directory.


Quick Start

import { LRUMap, memoize, InMemoryCache } from "@simpill/cache.utils";

const lru = new LRUMap<string, number>(100);
lru.set("key", 42);
const value = lru.get("key");

const fn = memoize((x: number) => expensive(x), { cache: new LRUMap(50) });

Features

| Feature | Description | |---------|-------------| | LRUMap | In-memory LRU map with max size | | InMemoryCache | TTL-capable in-memory cache (no max size; use defaultTtlMs or bounded keys to avoid unbounded growth) | | memoize | Memoize with optional custom cache (e.g. LRUMap) | | memoizeAsync | Memoize async functions with optional custom cache | | TTLCache | TTL cache with max size (server) | | RedisCache | Redis-backed cache via adapter interface (server) |


Import Paths

import { ... } from "@simpill/cache.utils";         // Everything
import { ... } from "@simpill/cache.utils/client"; // Client
import { ... } from "@simpill/cache.utils/server"; // Server
import { ... } from "@simpill/cache.utils/shared"; // Shared only

API Reference

  • LRUMap<K, V>(maxSize) — get, set, has, size, clear
  • InMemoryCache — get, set, has, delete, clear, size. Optional defaultTtlMs and maxSize (LRU eviction); without both the cache is unbounded.
  • memoize(fn, options?) — options: key, cache (MemoizeCache). Default cache is unbounded; for long-lived processes pass a bounded cache (e.g. LRUMap or InMemoryCache with maxSize).
  • memoizeAsync(fn, options?) — async memoize with custom key/cache. Same unbounded-default note; use a bounded cache to avoid unbounded growth.
  • TTLCache — server TTL cache with max size
  • RedisCache — server cache wrapper (requires RedisCacheAdapter)

memoizeAsync

Memoizes async functions: same arguments return the same promise (cached). Options: key (custom key function), cache (custom cache instance), ttlMs (default TTL when using the default InMemoryCache). Unlike sync memoize, concurrent calls with the same key share one in-flight promise when the cache is empty — no built-in “in-flight dedupe” flag; the default behavior reuses the same promise until it settles and is stored. Use a bounded cache (e.g. InMemoryCache({ maxSize: 500, defaultTtlMs: 60000 })) to avoid unbounded growth.

Invalidation and eviction

  • LRUMap / TTLCache: Eviction is automatic (oldest-first when max size is reached; TTL expiry for TTLCache). No manual invalidation API beyond clear() or delete(key) where available.
  • InMemoryCache: Supports delete(key), clear(), and optional TTL + maxSize; eviction when full is LRU.
  • RedisCache: Use adapter delete(key) or key patterns for invalidation; TTL is typically set per key by the adapter.

Bounded growth (InMemoryCache)

InMemoryCache is unbounded unless you set maxSize and/or defaultTtlMs. For long-running processes, prefer e.g. new InMemoryCache({ maxSize: 1000, defaultTtlMs: 60_000 }) so entries expire and size is capped (LRU eviction when maxSize is reached).

RedisCache: serialization and keys

Values are stored as JSON (serialized on set, parsed on get). get(key) returns the parsed value; the generic type is not validated at runtime — validate at use site (e.g. Zod) if you need a guaranteed shape. Use a key prefix (e.g. myapp:user:${id}) to namespace keys and avoid collisions. The adapter is responsible for encoding; ensure your Redis client stores strings (UTF-8). See server example below.

Stale-while-revalidate (SWR)

This package does not implement SWR. To serve stale and revalidate in the background, wrap your fetch in logic that returns cached value immediately and triggers an async refresh; then update the cache when the refresh completes.

What we don't provide

  • Hit/miss counters or metrics — Caches expose size, get, set, delete, clear only. For hit/miss stats wrap the cache or use a backend (e.g. Redis) that provides them.
  • SWR — No stale-while-revalidate; implement with immediate cache return plus background refresh and cache update.
  • getMany / deleteMany — No batch get or delete; call get/delete in a loop or use an adapter that supports batching.

Server: TTLCache and RedisCache examples

import { TTLCache, RedisCache } from "@simpill/cache.utils/server";

// TTL in-memory cache (5 min TTL; entries pruned on access)
const ttl = new TTLCache<string, User>(300_000);
ttl.set("user:1", user);
const u = ttl.get("user:1");

// Redis: pass an adapter implementing get(key), set(key, value, { px }), del(key)
const redisAdapter = {
  async get(key: string) { return client.get(key); },
  async set(key: string, value: string, opts?: { px?: number }) {
    if (opts?.px) await client.set(key, value, "PX", opts.px);
    else await client.set(key, value);
  },
  async del(key: string) { await client.del(key); },
};
const redis = new RedisCache<User>(redisAdapter, { keyPrefix: "myapp", defaultTtlMs: 60_000 });
await redis.set("user:1", user);
const u2 = await redis.get("user:1");

Cache stats and metrics

This package does not expose hit/miss counters, eviction counts, or metrics. LRUMap, InMemoryCache, and TTLCache provide size (and get/ set/ delete/ clear); they do not track hits or misses. For metrics, wrap the cache: e.g. a thin wrapper that increments hits on get when a value is found and misses when not, or use your monitoring (e.g. log cache size periodically). RedisCache delegates to the adapter; if your Redis client or adapter exposes stats, use those.


Examples

npx ts-node examples/01-basic-usage.ts

| Example | Description | |---------|-------------| | 01-basic-usage.ts | LRUMap, memoize, InMemoryCache |


Development

npm install
npm test
npm run build
npm run verify

Documentation


License

ISC