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

@chaisser/memoize

v1.0.1

Published

Function memoization with TTL and cache size limit

Readme

⚡ @chaisser/memoize

Function memoization with TTL, cache size limit, stats, async support, and more


✨ Features

  • 🎯 Type-safe - Full TypeScript support with generics
  • ⏱️ TTL support - Cache entries with time-to-live expiration
  • 📏 Size limits - LRU-style eviction when cache exceeds maxSize
  • 📊 Cache stats - Track hits, misses, and hit rate
  • 🔄 Async support - Deduplicate concurrent async calls
  • 🗂️ MemoMap class - Explicit key/value cache with full control
  • 🪶 Weak memoize - WeakMap-based caching for object arguments
  • Debounce memoize - Cache results for a debounce period
  • 🔧 Custom resolvers - Control cache key generation
  • 🪶 Zero dependencies - Lightweight and tree-shakeable
  • 🏎️ ESM + CJS - Dual module format support

📦 Installation

npm install @chaisser/memoize
# or
yarn add @chaisser/memoize
# or
pnpm add @chaisser/memoize

🚀 Quick Start

import {
  memoize,
  memoizeWithTTL,
  memoizeWithLimit,
  memoizeAsync,
  memoizeWithStats,
} from '@chaisser/memoize';

// Basic memoization
const double = memoize((x: number) => {
  console.log('computing...');
  return x * 2;
});
double(2); // computing... → 4
double(2); // → 4 (cached)

// With TTL (5 seconds)
const fetchUser = memoizeWithTTL((id: string) => {
  return db.findUser(id);
}, 5000);

// With cache limit (max 100 entries)
const compute = memoizeWithLimit((key: string) => {
  return expensiveCalculation(key);
}, 100);

// Async with deduplication
const fetchData = memoizeAsync(async (url: string) => {
  const res = await fetch(url);
  return res.json();
});

📖 What It Does

This package provides function memoization utilities for JavaScript and TypeScript. It supports TTL-based cache expiration, LRU-style size limits, async function deduplication, cache statistics tracking, explicit cache control via the MemoMap class, and WeakMap-based memoization — all with full TypeScript generics.


🎯 How It Works

The package provides:

  • Core - memoize, memoizeWithTTL, memoizeWithLimit
  • Async - memoizeAsync (deduplicates concurrent calls)
  • Stats - memoizeWithStats (tracks hits/misses/hitRate)
  • Cache class - MemoMap (explicit key/value cache with TTL and size limit)
  • Weak - weakMemoize (WeakMap-based for object arguments)
  • Debounce - memoizeDebounce (cache for a debounce period)
  • Utilities - clearCache, cacheSize, defaultResolver

🎨 What It's Useful For

  • Expensive computations - Cache results of costly calculations
  • API calls - Deduplicate and cache HTTP responses
  • Database queries - Avoid redundant lookups within a time window
  • React render optimization - Memoize selectors and derived data
  • Rate limiting - Prevent redundant work via debounce memoization
  • Performance monitoring - Track cache hit rates with stats

💡 Usage Examples

Basic Memoization

import { memoize } from '@chaisser/memoize';

const factorial = memoize((n: number): number => {
  if (n <= 1) return 1;
  return n * factorial(n - 1);
});

factorial(10); // computes
factorial(10); // cached
factorial(9);  // cached (was computed as part of factorial(10))

With TTL

import { memoizeWithTTL } from '@chaisser/memoize';

const getStockPrice = memoizeWithTTL((symbol: string) => {
  return fetchLatestPrice(symbol);
}, 30_000); // cache for 30 seconds

With Cache Size Limit

import { memoizeWithLimit } from '@chaisser/memoize';

const lookup = memoizeWithLimit((key: string) => {
  return expensiveLookup(key);
}, 50); // max 50 cached entries (LRU eviction)

Full Options

import { memoize } from '@chaisser/memoize';

const fn = memoize(
  (x: number, y: number) => x + y,
  {
    ttl: 10_000,     // expire after 10 seconds
    maxSize: 100,    // max 100 entries
    resolver: (x, y) => `${x},${y}`, // custom cache key
  }
);

Async with Deduplication

import { memoizeAsync } from '@chaisser/memoize';

const fetchUser = memoizeAsync(async (id: string) => {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
}, { ttl: 60_000 });

// Two concurrent calls with same id → only one fetch
const [user1, user2] = await Promise.all([
  fetchUser('123'),
  fetchUser('123'),
]);

Cache Statistics

import { memoizeWithStats } from '@chaisser/memoize';

const compute = memoizeWithStats((x: number) => x * 2);

compute(1); // miss
compute(1); // hit
compute(2); // miss

const stats = compute.stats();
// { hits: 1, misses: 2, hitRate: 0.333, size: 2 }

MemoMap (Explicit Cache Control)

import { MemoMap } from '@chaisser/memoize';

const cache = new MemoMap<{ data: string }>({ ttl: 5000, maxSize: 100 });

cache.set('user:1', { data: 'Alice' });
cache.get('user:1');     // { data: 'Alice' }
cache.has('user:1');     // true
cache.size;              // 1
cache.keys();            // ['user:1']
cache.values();          // [{ data: 'Alice' }]
cache.entries();         // [['user:1', { data: 'Alice' }]]
cache.delete('user:1');  // true
cache.clear();

Weak Memoize

import { weakMemoize } from '@chaisser/memoize';

const getMetadata = weakMemoize((obj: Record<string, unknown>) => {
  return Object.keys(obj);
});

const obj = { a: 1, b: 2 };
getMetadata(obj); // ['a', 'b'] — computed
getMetadata(obj); // ['a', 'b'] — cached by reference

Debounce Memoize

import { memoizeDebounce } from '@chaisser/memoize';

const search = memoizeDebounce((query: string) => {
  return performSearch(query);
}, 5000); // cache results for 5 seconds

search('hello'); // computed
search('hello'); // cached
// after 5 seconds, cache expires

Cache Utilities

import { memoize, clearCache, cacheSize } from '@chaisser/memoize';

const fn = memoize((x: number) => x * 2);
fn(1);
fn(2);

cacheSize(fn);  // 2
clearCache(fn);
cacheSize(fn);  // 0

📚 API Reference

Core Memoization

| Function | Signature | Description | |---|---|---| | memoize(fn, options?) | (Fn, MemoOptions?) → Fn & { cache } | Memoize with optional TTL, maxSize, resolver | | memoizeWithTTL(fn, ttl, resolver?) | (Fn, number, Resolver?) → Fn & { cache } | Shorthand for TTL-only memoization | | memoizeWithLimit(fn, maxSize, resolver?) | (Fn, number, Resolver?) → Fn & { cache } | Shorthand for size-limited memoization |

MemoOptions

| Option | Type | Default | Description | |---|---|---|---| | ttl | number | Infinity | Time-to-live in milliseconds | | maxSize | number | Infinity | Maximum cache entries (LRU eviction) | | resolver | (...args) => string | defaultResolver | Custom cache key function |

Async

| Function | Description | |---|---| | memoizeAsync(fn, options?) | Memoize async functions, deduplicates concurrent calls |

Stats

| Function | Description | |---|---| | memoizeWithStats(fn, options?) | Memoize with hit/miss tracking via .stats() |

Returns { hits, misses, hitRate, size } from .stats().

MemoMap

const cache = new MemoMap<T>({ ttl?, maxSize? });
cache.get(key);       // T | undefined
cache.set(key, value); // void
cache.has(key);        // boolean
cache.delete(key);     // boolean
cache.clear();         // void
cache.size;            // number
cache.keys();          // string[]
cache.values();        // T[]
cache.entries();       // [string, T][]

Specialized

| Function | Description | |---|---| | weakMemoize(fn) | WeakMap-based cache for object arguments | | memoizeDebounce(fn, delay, resolver?) | Cache results for a debounce period |

Utilities

| Function | Description | |---|---| | clearCache(fn) | Clear a memoized function's cache | | cacheSize(fn) | Get the cache size of a memoized function | | defaultResolver(...args) | Default cache key serializer |


🔗 Related Packages

Explore our other utility packages in the @chaisser namespace:


🔒 License

MIT - Free to use in personal and commercial projects


👨 Developed by

Doruk Karaboncuk [email protected]


📄 Repository


🤝 Contributing

Contributions are welcome! Feel free to:

  • Report bugs
  • Suggest new features
  • Submit pull requests
  • Improve documentation

📞 Support

For issues, questions, or suggestions, please reach out through:


Made with ❤️ by @chaisser