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

simple-in-memory-cache

v0.5.1

Published

A simple in-memory cache, for nodejs and the browser, with time based expiration policies.

Readme

simple-in-memory-cache

test publish

A simple, typed, in-memory cache for nodejs and the browser with time-based expiration policies.

install

npm install --save simple-in-memory-cache

usage

set and get

import { createCache } from 'simple-in-memory-cache';

const { set, get } = createCache();
set('purpose of life', 42);
const purpose = get('purpose of life'); // returns 42

expiration

items in the cache expire after 5 minutes by default.

change the default expiration on cache creation:

const { set, get } = createCache({ expiration: { minutes: 10 } });

override expiration per item:

set('ice cream state', 'solid', { expiration: { seconds: 30 } });

set an item to never expire:

set('speed of light', 299792458, { expiration: null });

invalidation

invalidate a cached item with set(key, undefined):

set('purpose of life', 42);
get('purpose of life'); // returns 42

set('purpose of life', undefined);
get('purpose of life'); // returns undefined

keys

list all non-expired keys in the cache:

const { set, keys } = createCache();
set('a', 1);
set('b', 2);
keys(); // returns ['a', 'b']

conditional writes (put-if-absent + compare-and-set)

gate a write on a version precondition, so it succeeds only when the key is in the state you expect. on a precondition miss the write throws SimpleCacheConditionError (never a silent clobber). this makes the cache usable as a coordination primitive (locks, leases, stampede control, optimistic concurrency).

read the current opaque version token for a key with version(key):

import { createCache, SimpleCacheConditionError } from 'simple-in-memory-cache';

const { set, get, version } = createCache<string>();

put-if-absent — write only if no live entry exists (condition: { version: null }):

set('lock', 'worker-a', { condition: { version: null } }); // ✓ wins — key was open

try {
  set('lock', 'worker-b', { condition: { version: null } }); // ✋ throws — key held
} catch (error) {
  if (!(error instanceof SimpleCacheConditionError)) throw error;
  // worker-b lost the race, loudly — no silent overwrite
}

compare-and-set — write only if the stored version matches a token from a prior observation. version() returns string | undefined while a condition wants string | null, so bridge an absent read with ?? null (absent → put-if-absent, present → compare-and-set):

set('counter', '1');
const v = version('counter'); // capture the token now

set('counter', '2', { condition: { version: v ?? null } }); // ✓ still at v
// ✋ if someone else wrote 'counter' since you read v, this throws

version-checked get — read the value only if it is still the version you last saw:

const v = version('counter');
const current = get('counter', { condition: { version: v ?? null } });
// ✓ returns the value IF it is still at version v
// ✋ throws SimpleCacheConditionError if the version drifted (the value you were about to
//    act on is stale)

compare-and-delete — release a lock only if it is still yours (set(key, undefined, …)):

set('lock', 'worker-a', { condition: { version: null } });
const mine = version('lock');
set('lock', undefined, { condition: { version: mine ?? null } }); // release, only if still mine

⚠️ the token must come from a prior observation (a get/version/acquire taken before the write). a fresh version(key) read taken immediately before its own conditional write always matches the current token and so guards no state — the whole point of a condition is to compare against a value you saw earlier.

⚠️ a successful conditional write also mints a fresh token, so the token you used to authorize it is immediately dead. in a renew-loop (mutex renewal), re-observe version(key) before each renewal — do not carry the token you acquired at lock time into the second renewal, or it will throw:

set('lock', me, { condition: { version: null } }); // acquire
let held = version('lock'); // token now
set('lock', me, { condition: { version: held ?? null } }); // renew #1 → mints a new token
held = version('lock'); // re-observe before the next renewal
set('lock', me, { condition: { version: held ?? null } }); // renew #2 ✓ (the old token would throw)

⚠️ always bridge an absent read with ?? null. version() yields string | undefined, but a condition wants string | null. typescript callers are safe — a bare { version: v } where v: string | undefined fails to compile against the condition type. but plain-js/browser callers get no such guard: a { condition: { version: someUndefinedVar } } meant as put-if-absent falls into the compare-and-set branch and always throws, since an undefined token never equals the found version. use { version: v ?? null } so an absent read means put-if-absent, not a guaranteed miss.

types

the cache is fully typed:

import { createCache, SimpleInMemoryCache } from 'simple-in-memory-cache';

const cache: SimpleInMemoryCache<number> = createCache<number>();
cache.set('answer', 42);
const answer: number | undefined = cache.get('answer');

api

createCache<T>(options?)

creates a new cache instance.

options:

  • expiration?: IsoDuration | null — default expiration for items (default: { minutes: 5 })

returns: SimpleInMemoryCache<T>

SimpleInMemoryCache<T>

  • get(key, options?): T | undefined — retrieve an item (returns undefined if absent or expired). with options.condition, verify the stored version before it yields the value; on a mismatch throw SimpleCacheConditionError (version-checked get).
  • set(key, value, options?): void — store an item (or invalidate with value: undefined). with options.condition, gate the write: { version: null } = put-if-absent, { version: '<token>' } = compare-and-set; on a precondition miss throw SimpleCacheConditionError.
  • version(key): string | undefined — read the current opaque version token for a live key (undefined if absent or expired). treat the token as equality-only; never parse or order it.
  • keys(): string[] — list all non-expired keys

options (both get and set):

  • condition?: { version: string | null } — the version precondition (see conditional writes above)

options (set only):

  • expiration?: IsoDuration | null — override the item's expiration (null = never expire)

SimpleCacheConditionError

thrown by get/set when a condition.version precondition is not met. extends ConstraintError (from helpful-errors) — a caller-must-fix constraint (exit code 2). carries { key, condition, found } metadata for diagnosis.