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

@fullgream/heat-cache

v0.1.3

Published

LRU/LFU Cache

Readme

HeatCache

A lightweight in-memory cache with TTL support, hit-based scoring, priority weighting, and automatic garbage collection.

HeatCache ranks cached items by “heat”. Hotter items are kept longer, while colder or expired items are removed first when the cache exceeds its size limit.

Features

  • Generic key/value cache
  • Optional per-item TTL
  • Sliding TTL refresh on get()
  • Hit counter tracking
  • Priority-based retention
  • Automatic aging over time
  • Periodic cleanup of expired entries
  • Size-based eviction using heat score
  • Node.js-friendly timer cleanup with unref()

Installation

Install the module from npm:

npm i @fullgream/heat-cache

And connect to your project

import { HeatCache } from "@fullgream/heat-cache";

Basic Usage

const cache = new HeatCache<string, string>(100);

cache.set("user:1", "Alice");

const user = cache.get("user:1");

console.log(user);
// "Alice"

Creating a Cache

const cache = new HeatCache<K, V>(elementLimit, intervalPeriod);

Parameters

| Parameter | Type | Default | Description | | ---------------- | -------: | -------: | --------------------------------------------------------------------------- | | elementLimit | number | required | Maximum preferred number of items in the cache. | | intervalPeriod | number | 15000 | Interval in milliseconds used for aging items and removing expired entries. |

Example:

const cache = new HeatCache<string, number>(500, 10_000);

This creates a cache that tries to keep up to 500 items and runs its internal aging/cleanup cycle every 10 seconds.

Setting Values

cache.set(key, value, options);

Options

{
  priority?: number;
  ttl?: number;
}

| Option | Type | Default | Description | | ---------- | -------: | ----------: | ------------------------------------------------------------------- | | priority | number | 0 | Retention priority. Higher priority increases item heat. | | ttl | number | undefined | Time-to-live in milliseconds. Must be greater than 0 if provided. |

Example:

cache.set("session:abc", { userId: 123 }, {
  ttl: 60_000,
  priority: 2,
});

This stores the item for 60 seconds and gives it higher retention priority.

Getting Values

const value = cache.get(key);

Returns the cached value, or undefined if the key does not exist or the item has expired.

const session = cache.get("session:abc");

if (session) {
  console.log(session.userId);
}

Calling get() on a valid item:

  • refreshes its TTL if TTL is enabled
  • increments its hit counter
  • resets its recency category to 0
  • marks the eviction priority list as dirty

Checking Existence

cache.has(key);

Returns true if the key exists and is not expired.

if (cache.has("user:1")) {
  console.log("User is cached");
}

Expired items are deleted automatically when checked.

Deleting Values

cache.delete(key);

Returns true if the item existed and was deleted.

cache.delete("user:1");

Clearing the Cache

cache.clear();

Removes all cached items and resets the internal eviction priority list.

Destroying the Cache

cache.destroy();

Stops the internal timer and clears all cached data.

Use this when the cache is no longer needed, especially in tests, scripts, or short-lived processes.

cache.destroy();

Iterating Over Items

cache.forEach((value, key, cacheInstance) => {
  console.log(key, value);
});

Expired items are removed during iteration and are not passed to the callback.

Cache Size

cache.size;

Returns the number of non-expired items, based on the current eviction priority list.

console.log(cache.size);

TTL Behavior

TTL is optional per item.

cache.set("token", "abc", {
  ttl: 5_000,
});

The item expires after 5 seconds.

When get() is called before expiration, the TTL is refreshed:

cache.get("token");

This makes TTL behave like a sliding expiration window.

Invalid TTL

TTL must be greater than 0.

cache.set("bad", "value", {
  ttl: 0,
});

This throws:

TypeError: TTL must be more than 0 or null for disabling TTL

Heat Calculation

Each item has a computed heat score.

item.heat;

The score is based on:

  • recency category
  • number of hits
  • configured priority
  • expiration status

Expired items always have a heat score of -1.

Current formula:

heat = recency + frequency + priorityScore

Where:

recency = 100 - category * 20
frequency = Math.log1p(hits) * 10
priorityScore = priority * 20

Lower heat means the item is more likely to be removed first.

Aging

Each cache item has a category from 0 to 5.

  • 0 means most recent
  • 5 means oldest/coldest

On every internal interval tick:

  • expired items are deleted
  • non-expired items move one category older
  • items already in category 5 stay there

Calling get() resets the item category back to 0.

Eviction Behavior

When the number of stored items exceeds elementLimit, the cache sorts items by heat in ascending order.

The coldest items are deleted first.

Expired items are preferred for deletion.

Example:

const cache = new HeatCache<string, string>(2);

cache.set("a", "A");
cache.set("b", "B");
cache.set("c", "C");

After adding "c", the cache evicts the coldest item to keep the size near the configured limit.

Priority

Priority increases an item’s heat score.

cache.set("important", "value", {
  priority: 5,
});

Higher-priority items are less likely to be evicted.

Priority does not prevent expiration. If an item expires, it is removed regardless of priority.

Replacing Existing Keys

Calling set() with an existing key creates a new HeatCacheItem.

cache.set("user:1", "Alice");
cache.set("user:1", "Bob");

The new item replaces the old one and resets:

  • hits
  • category
  • TTL
  • priority

This is intentional behavior in the current implementation.

API Reference

HeatCache<K, V>

Constructor

new HeatCache<K, V>(elementLimit: number, intervalPeriod?: number)

Creates a new cache.

set()

set(
  key: K,
  value: V,
  options?: {
    priority?: number;
    ttl?: number;
  }
): this

Stores a value in the cache.

get()

get(key: K): V | undefined

Returns the cached value or undefined.

has()

has(key: K): boolean

Checks whether a non-expired item exists.

delete()

delete(key: K): boolean

Deletes an item from the cache.

clear()

clear(): void

Removes all items.

destroy()

destroy(): void

Stops the internal timer and clears the cache.

forEach()

forEach(
  callbackfn: (this: any, value: V, key: K, map: HeatCache<K, V>) => void,
  thisArg?: any
): void

Iterates over non-expired items.

size

get size(): number

Returns the approximate number of non-expired items.

HeatCacheItem<K, V>

Represents a single cache entry.

Properties

| Property | Type | Description | | ----------- | ---------------------------: | --------------------------------------------------- | | key | K | Item key. | | value | V | Stored value. | | priority | number | Retention priority. | | category | 0 \| 1 \| 2 \| 3 \| 4 \| 5 | Recency category. | | hits | number | Number of successful reads. | | expiresAt | number \| null | Expiration timestamp, or null if TTL is disabled. | | heat | number | Computed heat score. | | isExpired | boolean | Whether the item has expired. |

Example: Session Cache

type Session = {
  userId: number;
  role: string;
};

const sessions = new HeatCache<string, Session>(10_000);

sessions.set("session:abc", {
  userId: 1,
  role: "admin",
}, {
  ttl: 30 * 60 * 1000,
  priority: 3,
});

const session = sessions.get("session:abc");

if (session) {
  console.log(session.userId);
}

Example: Small Hot Cache

const cache = new HeatCache<string, string>(3);

cache.set("a", "A");
cache.set("b", "B");
cache.set("c", "C");

cache.get("a");
cache.get("a");
cache.get("b");

cache.set("d", "D");

In this example, "a" and "b" are more likely to stay because they have more hits and therefore higher heat.

Notes and Caveats

size depends on the GC priority list

The size getter subtracts expired items detected at the beginning of the current GC priority list. It may not always force a full scan of the map.

For an exact count, iterate through the cache or clean expired entries first.

TTL refresh only happens on get()

Calling has() checks expiration but does not refresh TTL.

ttl: undefined disables TTL

TTL is only active when a positive number is provided.

ttl: null is accepted by HeatCacheItem

Internally, HeatCacheItem supports null TTL to disable expiration.

The cache is in-memory only

Data is lost when the process exits.

Not designed for distributed caching

This cache is local to a single JavaScript/TypeScript runtime.

Recommended Cleanup

Always call destroy() when the cache lifecycle ends:

cache.destroy();

This clears the interval timer and removes all stored items.