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

@sunacchi/cache-kit

v1.0.2

Published

Universal cache library with pluggable adapters for Node, Bun, Deno, and browsers

Downloads

69

Readme

@sunacchi/cache-kit

Core cache library with a unified async API, pluggable adapters, and built-in MemoryAdapter.

Part of the Cache Kit monorepo.

Install

npm install @sunacchi/cache-kit

Quick Start

import { Cache } from '@sunacchi/cache-kit';

const cache = new Cache<string>();

await cache.set('key', 'value');
const val = await cache.get('key'); // 'value'

Constructor Options

const cache = new Cache<MyType>({
  adapter: new MemoryAdapter(), // default — swap for any CacheAdapter
  defaultTtl: 60_000,          // ms, undefined = no expiry
  staleWhileRevalidate: 30_000,// ms grace period after TTL
  maxSize: 1000,               // max entries before eviction
  eviction: 'lru',             // 'lru' | 'fifo'
  namespace: 'users',          // key prefix
  serialize: (entry) => JSON.stringify(entry),
  deserialize: (raw) => JSON.parse(raw),
  middleware: [],               // initial middleware stack
});

API

Core Methods

| Method | Returns | Description | |--------|---------|-------------| | get(key) | Promise<T \| undefined> | Retrieve a value. Returns undefined on miss or expiry. | | set(key, value, ttl?, swr?) | Promise<void> | Store a value with optional per-call TTL and SWR. | | has(key) | Promise<boolean> | Check existence (respects TTL). | | delete(key) | Promise<boolean> | Remove a key. | | clear() | Promise<void> | Remove all entries. | | keys() | AsyncIterable<string> | Iterate over stored keys. | | size() | Promise<number> | Count of stored entries. |

Read-Through

// Single key — factory only called on miss, with stampede protection
const user = await cache.getOrCreate('user:42', async () => {
  return await db.users.findById(42);
}, { ttl: 30_000 });

// Batch — only missed keys hit the factory
const users = await cache.getOrCreateMany(
  ['user:1', 'user:2', 'user:3'],
  async (missed) => {
    const rows = await db.users.findByIds(missed);
    return new Map(rows.map(u => [u.id, u]));
  },
);

// Function wrapper — transparent caching
const getUser = cache.wrap(
  (id: number) => `user:${id}`,
  (id: number) => db.users.findById(id),
  { ttl: 60_000 },
);

Events

cache.on('hit', ({ key, value }) => { /* ... */ });
cache.on('miss', ({ key }) => { /* ... */ });
cache.on('set', ({ key, value }) => { /* ... */ });
cache.on('delete', ({ key }) => { /* ... */ });
cache.on('evict', ({ key, reason }) => { /* ... */ });
cache.on('stale', ({ key, value }) => { /* ... */ });

const unsub = cache.on('hit', handler);
unsub(); // unsubscribe

Statistics

const s = cache.stats();
// { hits, misses, sets, deletes, evictions, staleHits, hitRate }

cache.resetStats();

Middleware

cache.use({
  before(ctx) {
    // ctx: { key, operation, value?, ttl? }
    return true; // false aborts the operation
  },
  after(ctx, result) {
    // runs after the operation completes
  },
});

MemoryAdapter

The default adapter. Uses a Map<string, string> under the hood — synchronous, zero-config.

import { MemoryAdapter } from '@sunacchi/cache-kit';

const adapter = new MemoryAdapter();

Writing a Custom Adapter

Implement the CacheAdapter interface. Adapters store and return strings — serialization is handled by the Cache class.

import type { CacheAdapter } from '@sunacchi/cache-kit';

export class MyAdapter implements CacheAdapter {
  async get(key: string): Promise<string | undefined> { /* ... */ }
  async set(key: string, value: string, ttlMs?: number): Promise<void> { /* ... */ }
  async delete(key: string): Promise<boolean> { /* ... */ }
  async clear(): Promise<void> { /* ... */ }
  async has(key: string): Promise<boolean> { /* ... */ }
  async *keys(): AsyncIterable<string> { /* ... */ }
  async size(): Promise<number> { /* ... */ }
}

Exports

// Classes
export { Cache } from './cache.js';
export { MemoryAdapter } from './memory-adapter.js';
export { defaultSerialize, defaultDeserialize } from './serialization.js';

// Types
export type {
  CacheAdapter,
  CacheEntry,
  CacheOptions,
  GetOrCreateOptions,
  CacheEventType,
  CacheEventMap,
  CacheListener,
  CacheStats,
  CacheMiddleware,
  MiddlewareContext,
} from './types.js';

License

MIT