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

@imirfanul/ttl-lru-cache

v1.0.0

Published

A lightweight O(1) TTL LRU Cache with Decorator support

Downloads

3

Readme

TTL LRU Cache

A lightweight, high-performance in-memory caching library for Node.js and TypeScript. It combines a Hash Map with a Doubly Linked List to ensure O(1) performance for all operations.

Includes support for Time-To-Live (TTL) expiration, Least Recently Used (LRU) eviction, and Method Decorators for effortless caching in class-based services.

Features

  • O(1) Complexity: Constant time for get, set, and eviction.
  • Dual Eviction Strategy:
    • LRU: Removes the least recently accessed item when capacity is reached.
    • TTL: Automatically expires items after a specified duration (Lazy expiration).
  • TypeScript Native: Written in TS with full Generic <K, V> support.
  • Event Driven: Emits events when items are evicted (useful for telemetry).
  • @Cacheable Decorator: AOP-style caching for class methods (Services, Repositories).

Installation

Install ttl-lru-cache with npm

  npm install ttl-lru-cache

Usage / Examples

1. Basic Caching

import { TTLLRUCache } from 'ttl-lru-cache';

// Initialize with types <KeyType, ValueType>
const cache = new TTLLRUCache<string, number>({
  capacity: 100, // Max items
  ttl: 60        // Default TTL in seconds
});

// Set items
cache.set('user_count', 42);
cache.set('active_sessions', 10, 300); // Custom TTL of 300s for this item

// Get items
const count = cache.get('user_count'); // Returns 42 or undefined

// Listen for drops
cache.on('evict', (event) => {
  console.log(`Item evicted: ${event.key}. Reason: ${event.reason}`);
});

2. Using the @Cacheable Decorator

Automatically cache the result of asynchronous class methods. The cache key is generated based on the arguments passed to the method.

import { Cacheable } from 'ttl-lru-cache';

class UserService {
  
  // Caches result for 60 seconds. 
  // Cache key is automatically generated from arguments (e.g., '["123"]')
  @Cacheable({ ttl: 60, capacity: 50 })
  async getUserProfile(userId: string) {
    console.log('Fetching from Database...');
    return await db.findUser(userId);
  }

  // Custom key generator example
  @Cacheable({ 
    ttl: 300,
    keyGenerator: (id, version) => `product:${id}:v${version}`
  })
  async getProduct(id: string, version: number) {
    // ... complex logic
  }
}

⚙️ Configuration

Cache Options

| Option | Type | Default | Description | | :--- | :--- | :--- | :--- | | capacity | number | 100 | Maximum number of items in the cache. | | ttl | number | 0 | Default Time-To-Live in seconds (0 = no expiry). |

Decorator Options

| Option | Type | Required | Description | | :--- | :--- | :--- | :--- | | ttl | number | Yes | Time-To-Live for cached method results (seconds). | | capacity | number | No | Capacity specific to this method's cache (default 100). | | keyGenerator | Function | No | Custom function to generate cache keys from arguments. |

🧪 Performance

This library uses a Doubly Linked List for order management.

  • Set: O(1) - Appends to Head.
  • Get: O(1) - Moves node to Head.
  • Evict: O(1) - Removes Tail node.

2. Comprehensive Usage Examples (usage_examples.ts)

You can use this file to test the library or show your team how to use it.

import { TTLLRUCache, Cacheable } from './src';

// ==========================================
// SCENARIO 1: The "Raw" Cache (Manual Control)
// ==========================================
async function manualCacheDemo() {
  console.log('--- 1. Manual Cache Demo ---');

  // Define a type for our value
  type SessionData = { userId: number; token: string };

  const sessionCache = new TTLLRUCache<string, SessionData>({
    capacity: 3, // Small capacity to force eviction
    ttl: 2       // Short TTL to force expiration
  });

  // telemetry logging
  sessionCache.on('evict', ({ key, reason }) => {
    console.warn(`[LOG] Session killed: ${key} (Reason: ${reason})`);
  });

  console.log('1. Adding users A, B, C...');
  sessionCache.set('user_A', { userId: 1, token: 'abc' });
  sessionCache.set('user_B', { userId: 2, token: 'def' });
  sessionCache.set('user_C', { userId: 3, token: 'ghi' });

  console.log('2. Accessing user_A (moves it to "Most Recently Used")');
  sessionCache.get('user_A');

  console.log('3. Adding user_D (Triggers capacity eviction of LRU item)');
  // Since A was accessed, B is now the LRU (Least Recently Used) and should be dropped
  sessionCache.set('user_D', { userId: 4, token: 'jkl' });

  console.log('4. Waiting for TTL expiration (3 seconds)...');
  await new Promise(r => setTimeout(r, 3000));

  const result = sessionCache.get('user_D');
  console.log(`Get user_D after timeout: ${result}`); // Should be undefined
}

// ==========================================
// SCENARIO 2: The Decorator (Service Layer)
// ==========================================

// Mock Database Call
const fakeDbCall = (id: string) => 
  new Promise<string>(resolve => {
    setTimeout(() => resolve(`Product_Data_${id}`), 500); // 500ms delay
  });

class ProductService {
  
  @Cacheable({ ttl: 5 })
  async getProduct(id: string) {
    console.log(`\x1b[33m[DB HIT] Fetching product ${id} from database...\x1b[0m`);
    return await fakeDbCall(id);
  }

  // Example: Cache search results based on query string
  @Cacheable({ ttl: 10, capacity: 50 })
  async searchProducts(query: string) {
    console.log(`\x1b[33m[DB HIT] Searching for "${query}"...\x1b[0m`);
    return [`Result 1 for ${query}`, `Result 2 for ${query}`];
  }
}

async function decoratorDemo() {
  console.log('\n--- 2. Decorator Demo ---');
  const service = new ProductService();

  console.log('Call 1: (Slow)');
  console.time('Time');
  const p1 = await service.getProduct('101');
  console.timeEnd('Time');
  console.log(`Value: ${p1}`);

  console.log('Call 2: (Fast - Cached)');
  console.time('Time');
  const p2 = await service.getProduct('101');
  console.timeEnd('Time');
  console.log(`Value: ${p2}`);

  console.log('Call 3: (Slow - Different Arg)');
  await service.getProduct('999');
}

// ==========================================
// RUNNER
// ==========================================
(async () => {
  await manualCacheDemo();
  await decoratorDemo();
})();

📄 License

MIT