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

@lacspace/cache

v1.1.0

Published

A tiny in-memory cache — LRU/LFU eviction, per-entry TTL, stale-while-revalidate, tag invalidation and hit/miss stats, plus wrap()/getOrSet()/memoize() to cache any async function with single-flight de-duplication. Zero-dependency, isomorphic.

Readme

@lacspace/cache

LRU + TTL + stale-while-revalidate, and one-line async memoization. Zero dependencies.

npm version minzipped types license

An in-memory cache that does the things you actually want — bound the size (LRU or LFU), expire entries (TTL), and hide latency (stale-while-revalidate) — plus wrap()/getOrSet()/memoize() that cache any async function and de-duplicate concurrent calls. Tiny, typed, isomorphic.

New in 1.1.0 — LFU eviction (policy: "lfu"), getOrSet(), tag-based invalidation (tags + invalidateTag), deleteMany(), hit/miss/eviction stats(), purge(), an injectable clock for tests, and an onEvict listener. All additive — every 1.0 API is unchanged.

  • 📦 createCache({ max, ttl, policy, clock, onEvict }) — LRU/LFU + per-entry TTL, get/set/has/delete/clear/keys/size
  • 🔁 cache.wrap(key, fn) / cache.getOrSet(key, fn) — cache an async call and de-dupe concurrent callers into one fetch (single-flight)
  • staleWhileRevalidate — serve the stale value instantly, refresh in the background
  • 🏷️ tags + cache.invalidateTag(tag) / cache.deleteMany(pattern) — group and drop related entries
  • 📊 cache.stats() — hits · misses · sets · evictions · expirations · hitRate; cache.purge() sweeps expired
  • 🧠 memoize(fn) — one-line async memoization with a .cache handle
  • 🌍 Zero dependencies · isomorphic · fully typed

Install

npm i @lacspace/cache

Use it

import { createCache } from "@lacspace/cache";

const cache = createCache<User>({ max: 500, ttl: 60_000 });

cache.set("a", user);
cache.get("a");        // user  (or undefined once expired)
cache.has("a");        // true
cache.size;            // 1

Cache an async call — with de-duplication

// 100 concurrent callers → exactly ONE db call; the rest await the same promise.
const user = await cache.wrap(`user:${id}`, () => db.users.find(id), { ttl: 60_000 });

Stale-while-revalidate (serve instantly, refresh in the background)

const data = await cache.wrap("dashboard", fetchDashboard, {
  ttl: 30_000,               // fresh for 30s
  staleWhileRevalidate: 60_000, // then serve stale for up to 60s while refreshing
});
// After 30s the cached value is returned immediately AND a refresh kicks off.

Memoize any async function

import { memoize } from "@lacspace/cache";

const getUser = memoize(db.users.find, { ttl: 60_000, max: 1000 });
await getUser(42);   // fetches
await getUser(42);   // cached
getUser.cache.clear(); // full control when you need it

LFU eviction

// Keep the 500 hottest keys; the least-frequently-used are evicted first.
const cache = createCache({ max: 500, policy: "lfu" });

Tag related entries and drop them together

cache.set("user:1", user, 0, { tags: ["user", "team:9"] });
await cache.wrap("user:1:posts", loadPosts, { tags: ["user", "team:9"] });

cache.invalidateTag("team:9"); // → both entries gone; returns how many
cache.deleteMany("user:");     // prefix, RegExp, or predicate → count removed

Stats & a testable clock

const cache = createCache({ ttl: 1000, clock: () => myFakeNow() });
cache.get("miss");
cache.stats(); // { hits, misses, sets, evictions, expirations, size, hitRate }
cache.purge(); // sweep expired entries now → count removed

API

| | | | --- | --- | | createCache({ max, ttl, policy, clock, onEvict }) | get · set(k,v,ttl?,{tags}?) · has · delete · clear · keys · size · wrap · getOrSet · invalidateTag · deleteMany · purge · stats · resetStats | | cache.wrap(key, fn, { ttl, staleWhileRevalidate, tags }) | cached async, single-flight de-dup, optional SWR | | cache.getOrSet(key, factory, opts) | alias of wrap — cache-or-produce with single-flight | | cache.invalidateTag(tag) · cache.deleteMany(prefix\|RegExp\|fn) | drop grouped/matching entries → count | | cache.stats() · cache.purge() | hit/miss/eviction counters · sweep expired | | memoize(fn, { max, ttl, policy, clock, key, staleWhileRevalidate, tags }) | memoized fn + .cache handle |

policy is "lru" (default) or "lfu". clock defaults to Date.now. Eviction only happens once max (default 1000) is exceeded.

Pairs with @lacspace/retry for resilient, cached calls.

Licensing

Free under the Lacspace Free Licence — permissive freedoms. Use it in personal and commercial projects at no cost; just keep the notice. See the Lacspace Licence Centre.


The Lacspace Developer Platform

@lacspace/cache is part of 80+ zero-dependency, isomorphic TypeScript packages — one standard library for the modern web. Explore the ecosystem:

  • 📦 This package, documented — https://developer.lacspace.com/packages/cache
  • 🗂️ All 80+ packages — https://developer.lacspace.com/packages
  • 🧭 Developer handbook — guides & runnable recipes — https://developer.lacspace.com/handbook
  • 🧪 Live playground — run any package in your browser — https://developer.lacspace.com/playground
  • 🖥️ Finished app templates — https://templates.lacspace.com
  • 🚀 Scaffold a full appnpm create lacspace-app@latest

Free under the Lacspace Free Licence — a permissive, free-to-use licence.