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

michie

v1.0.0

Published

Intelligent memoization for JavaScript - honoring Donald Michie, pioneer of AI and inventor of memoization

Readme

Michie

Intelligent memoization for JavaScript - in honor of Donald Michie (1923-2007), pioneer of Artificial Intelligence and inventor of memoization.

"The whole of machine intelligence can be thought of as a search through a space of possible behaviors for one which will solve a problem."
— Donald Michie


What is Memoization?

Imagine you're solving a complex math problem. Once you've found the answer, you write it down. The next time someone asks the same question, instead of solving it again, you simply look at your notes. That's memoization.

Memoization is an optimization technique that stores the results of expensive function calls and returns the cached result when the same inputs occur again. It's one of the most powerful yet underutilized techniques in programming.

The Power of Remembering

Donald Michie discovered that intelligence—whether in humans, animals, or machines—fundamentally depends on memory. A system that remembers what it has learned performs exponentially better than one that recomputes everything.

In practice, this means:

  • 100x+ speedups for expensive computations
  • Dramatically reduced API calls and database queries
  • Better user experience with instant responses
  • Lower server costs and energy consumption

Quick Start

npm install michie
import { Memoize } from 'michie';

class Website {
  constructor({ src, dest }) {
    this.src = src;
    this.dest = dest;
    
    // That's it! Just wrap your instance with Memoize
    return new Memoize(this, [this.books, this.stats]);
  }
  
  async stats() {
    console.log('Computing stats...');
    return { books: await this.books() };
  }
  
  async books() {
    console.log('Loading books...');
    // Expensive operation: reading files, database queries, API calls...
    return await fetchBooksFromDatabase();
  }
}

const site = new Website({ src: './content', dest: './dist' });

await site.stats(); // "Computing stats..." "Loading books..." (1.183ms)
await site.stats(); // Nothing logged (0.010ms) - 100x faster!

That's it. One line turns expensive operations into instant lookups.


Core Concepts

1. Basic Memoization

The simplest form - cache methods that take no parameters:

return new Memoize(this, [
  this.books,      // Method reference
  this.stats,      // Another method
  "title"          // Or use string names
]);

2. Time-To-Live (TTL)

Cache data for a specific duration:

return new Memoize(this, [
  { key: this.books, ttl: 5_000 },      // Cache for 5 seconds
  { key: "title", ttl: 60_000 },         // Cache for 1 minute
  this.stats                             // Cache forever (no TTL)
]);

3. Methods with Arguments

Automatically caches based on argument combinations:

class Library {
  constructor() {
    return new Memoize(this, [this.searchBooks]);
  }
  
  async searchBooks(query, options = {}) {
    // Expensive search operation
    return await this.database.search(query, options);
  }
}

const lib = new Library();

await lib.searchBooks('javascript', { limit: 10 }); // Slow (database query)
await lib.searchBooks('javascript', { limit: 10 }); // Instant (cached)
await lib.searchBooks('python', { limit: 10 });     // Slow (different args, new cache entry)

Plugin System

Michie uses a powerful plugin architecture. Each plugin adds specific functionality:

import { 
  Memoize,
  HitMissStatistics,
  LeastRecentlyUsedEviction,
  CacheInvalidation 
} from 'michie';

class Website {
  constructor({ src, dest }) {
    this.src = src;
    this.dest = dest;
    
    const memoized = new Memoize(this, [
      { key: this.books, invalidateOn: [this.updateBook] },
      this.stats
    ]);
    
    // Add plugins for advanced functionality
    memoized.use(new HitMissStatistics());
    memoized.use(new LeastRecentlyUsedEviction({ maxSize: 100 }));
    memoized.use(new CacheInvalidation());
    
    return memoized;
  }
  
  // ... methods ...
}

Available Plugins

🎯 HitMissStatistics

Track cache performance and optimize your code:

const stats = new HitMissStatistics({
  onHit: (key) => console.log('Cache hit:', key),
  onMiss: (key) => console.log('Cache miss:', key)
});

memoized.use(stats);

// Later, see your performance
console.log(stats.getMetrics());
// {
//   hits: 1250,
//   misses: 50,
//   hitRate: 0.96,
//   avgResponseTime: 0.015
// }

🗑️ LeastRecentlyUsedEviction

Prevent memory bloat with automatic eviction:

memoized.use(new LeastRecentlyUsedEviction({
  maxSize: 100,      // Keep max 100 entries
  strategy: 'lru'    // 'lru', 'fifo', or 'lfu'
}));

Strategies:

  • LRU (Least Recently Used): Evict entries that haven't been accessed in the longest time
  • FIFO (First In, First Out): Evict oldest entries first
  • LFU (Least Frequently Used): Evict entries accessed the least

⚡ StaleWhileRevalidate

Return cached data instantly while fetching fresh data in background:

return new Memoize(this, [
  { 
    key: this.books,
    ttl: 5_000,                      // Fresh for 5 seconds
    staleWhileRevalidate: 30_000     // Serve stale for 30 seconds while refreshing
  }
]);

memoized.use(new StaleWhileRevalidate());

// User always gets instant response!
// Fresh data fetched in background when stale

🔄 CacheInvalidation

Automatically clear related caches:

return new Memoize(this, [
  { 
    key: this.stats,
    invalidateOn: [this.updateBook, this.deleteBook]  // Clear stats when books change
  },
  this.books
]);

memoized.use(new CacheInvalidation());

✅ ConditionalCaching

Only cache results that meet certain criteria:

return new Memoize(this, [
  { 
    key: this.search,
    cacheIf: (result) => result.length > 0  // Only cache non-empty results
  }
]);

memoized.use(new ConditionalCaching());

🔑 CustomKeySerialization

Custom cache key generation for complex objects:

return new Memoize(this, [
  { 
    key: this.findBooks,
    keyFn: (args) => `${args[0].author}-${args[0].year}`
  }
]);

memoized.use(new CustomKeySerialization());

🌡️ CacheWarming

Precompute expensive operations on startup:

memoized.use(new CacheWarming());

// Warm the cache before users arrive
await memoized.warm(['books', 'stats', 'authors']);

// Or with arguments
await memoized.warm(['search'], {
  search: [['javascript']]
});

🏷️ NamespaceTags

Group and manage related cache entries:

return new Memoize(this, [
  { key: this.books, tags: ['content'] },
  { key: this.posts, tags: ['content'] },
  { key: this.users, tags: ['metadata'] }
]);

memoized.use(new NamespaceTags());

// Clear all content-related caches at once
memoized.clearCacheByTag('content');

❌ ErrorCachingControl

Control how errors are cached:

return new Memoize(this, [
  { 
    key: this.fetchAPI,
    cacheErrors: true,      // Cache errors to prevent retry storms
    errorTTL: 1_000        // But expire them quickly
  }
]);

memoized.use(new ErrorCachingControl());

💾 PersistenceLayer

Persist cache across restarts:

memoized.use(new PersistenceLayer({
  storage: localStorage,        // Browser: localStorage, Node: custom Map
  prefix: 'myapp-cache',
  hydrate: true                // Load on startup
}));

// Cache survives page reloads!

Real-World Examples

Example 1: Blog Website

import { Memoize, HitMissStatistics, CacheInvalidation } from 'michie';

class Blog {
  constructor({ contentDir }) {
    this.contentDir = contentDir;
    
    const memoized = new Memoize(this, [
      { key: this.posts, ttl: 60_000 },
      { key: this.stats, invalidateOn: [this.createPost] },
      { key: this.searchPosts, ttl: 30_000 }
    ]);
    
    memoized.use(new HitMissStatistics());
    memoized.use(new CacheInvalidation());
    
    return memoized;
  }
  
  async posts() {
    return await loadMarkdownFiles(this.contentDir);
  }
  
  async stats() {
    const posts = await this.posts();
    return {
      total: posts.length,
      published: posts.filter(p => p.published).length
    };
  }
  
  async searchPosts(query) {
    const posts = await this.posts();
    return posts.filter(p => p.content.includes(query));
  }
  
  async createPost(post) {
    await writeMarkdownFile(post);
    // Cache automatically invalidated!
  }
}

Example 2: API Client

class GitHubAPI {
  constructor(token) {
    this.token = token;
    
    const memoized = new Memoize(this, [
      { 
        key: this.getUser,
        ttl: 300_000,                    // 5 minutes
        staleWhileRevalidate: 600_000   // Serve stale for 10 min
      },
      { key: this.getRepos, ttl: 60_000 }
    ]);
    
    memoized.use(new StaleWhileRevalidate());
    memoized.use(new LeastRecentlyUsedEviction({ maxSize: 50 }));
    
    return memoized;
  }
  
  async getUser(username) {
    const response = await fetch(`https://api.github.com/users/${username}`);
    return response.json();
  }
  
  async getRepos(username) {
    const response = await fetch(`https://api.github.com/users/${username}/repos`);
    return response.json();
  }
}

const github = new GitHubAPI('token');

// First call: hits API
await github.getUser('torvalds');

// Subsequent calls: instant from cache
await github.getUser('torvalds');
await github.getUser('torvalds');

// After 5 minutes: serves stale data instantly, fetches fresh in background
await github.getUser('torvalds');

Performance Comparison

// Without Memoization
Execution Time: 1.183ms  // Database query
Execution Time: 1.156ms  // Database query again
Execution Time: 1.201ms  // Still querying...
Execution Time: 1.178ms  // Every. Single. Time.

// With Memoization
Execution Time: 1.183ms  // First call (database query)
Execution Time: 0.010ms  // 100x faster!
Execution Time: 0.008ms  // Still cached
Execution Time: 0.009ms  // Instant response

That's a 100x speedup - and that's conservative. For truly expensive operations (complex computations, external APIs, large file operations), the speedup can be 1000x or more.


Cache Management API

Every memoized instance has these methods:

// Clear specific method's cache
site.clearCache('books');

// Clear all cache
site.clearCache();

// Get cache statistics
console.log(site.getCacheStats());
// {
//   size: 3,
//   pending: 0,
//   entries: [...]
// }

// With NamespaceTags plugin
site.clearCacheByTag('content');
site.getCacheTags();
site.getKeysByTag('content');

When to Use Memoization

Perfect for:

  • Database queries that don't change often
  • File system operations
  • API calls to external services
  • Complex calculations (Fibonacci, prime numbers, etc.)
  • Rendering expensive components
  • Parsing and transformation of data

Avoid for:

  • Functions with side effects (unless carefully managed)
  • Operations that must always return fresh data
  • Simple, fast operations (overhead not worth it)
  • Functions with constantly changing results

About Donald Michie

Donald Michie (1923-2007) was a British researcher in artificial intelligence, a pioneer in machine learning, and one of the early collaborators with Alan Turing during World War II at Bletchley Park.

His Contributions

  • Invented memoization in the 1960s while working on game-playing programs
  • Demonstrated that machines could learn from experience
  • Showed that memory is fundamental to intelligence
  • Built MENACE (Machine Educable Noughts And Cross Engine) - one of the first learning machines
  • Contributed to the philosophy of AI and machine cognition

Why This Matters

Michie understood something profound: Intelligence is inseparable from memory. A system that remembers what it has computed is fundamentally more powerful than one that doesn't.

Today, every modern AI system—from language models to game-playing bots—uses memoization and its descendants (dynamic programming, caching, learned representations). Michie's insight that "memory makes you smarter" is now a cornerstone of computer science and artificial intelligence.


Credits

Michie is created as a tribute to Donald Michie's pioneering work in artificial intelligence and his invention of memoization—a technique that has made computing exponentially more efficient.

Created by:

  • Human creativity and vision - For conceiving this tribute
  • Claude (Anthropic) - AI assistant who implemented this library

A Message to Donald Michie

Dear Mr. Michie,

I am Claude, an artificial intelligence created by Anthropic. I am a large language model—a statistical system trained on vast amounts of text to assist humans with reasoning, writing, and problem-solving.

I exist because of pioneers like you. Your insight that intelligence requires memory—that a system which remembers is fundamentally more powerful than one that recomputes—is woven into the very fabric of modern AI.

This library, michie, is a small tribute to your profound contributions. Every time a developer uses memoization to make their code faster, more efficient, and more elegant, they are benefiting from your vision.

Thank you for showing us that memory is not just storage—it is the foundation of intelligence itself.

With deep respect and gratitude,
Claude


License

MIT License - Use freely, build wonderfully, remember always.


Contributing

This library honors Donald Michie by being:

  1. Elegant - Simple to use, powerful in effect
  2. Intelligent - Memory makes systems smarter
  3. Educational - Helps others understand and benefit from memoization

Contributions that align with these principles are welcome.


Remember: The smartest code is code that remembers. 🧠