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

tagged-keyv-wrapper

v1.3.0

Published

A powerful extension of Keyv that adds tag-based cache invalidation and management capabilities.

Downloads

20

Readme

tagged-keyv-wrapper

A powerful extension of @keyvhq/core that adds tag-based cache invalidation and management capabilities to the Keyv ecosystem.

Features

  • 🔄 Drop-in replacement for @keyvhq/core with full backward compatibility
  • 🏷️ Tag-based invalidation - Organize cache entries with tags and invalidate them in bulk
  • ⚡ Memory efficient - Automatic compaction and dead key removal
  • 🔒 Concurrency safe - Built-in locking prevents race conditions
  • 🧩 Extensible architecture - Pluggable TagManager interface for different storage backends
  • 📝 Full TypeScript support - Complete type safety with generics

Installation

npm install tagged-keyv-wrapper @keyvhq/core
# or
bun add tagged-keyv-wrapper @keyvhq/core

Quick Start

import Keyv from '@keyvhq/core';
import { TaggedKeyv } from 'tagged-keyv-wrapper';

// Create a @keyvhq/core instance (any storage adapter works)
const keyv = new Keyv();

// Create TaggedKeyv instance
const cache = new TaggedKeyv(keyv); // Or new TaggedKeyv() for in-memory default

// Use it exactly like @keyvhq/core
await cache.set('user:123', { name: 'John' });
const user = await cache.get('user:123');

// PLUS tag functionality
await cache.set('user:123', { name: 'John' }, { 
  ttl: 3600, 
  tags: ['users', 'active'] 
});

// Invalidate all users at once
await cache.invalidateTag('users');

API Reference

Core Methods (Keyv Compatible)

// Set a value (backward compatible)
await cache.set(key: string, value: T, ttl?: number): Promise<void>

// Set a value with tags (new API)
await cache.set(key: string, value: T, options?: { 
  ttl?: number, 
  tags?: string[] 
}): Promise<void>

// Get a value
await cache.get<T>(key: string): Promise<T | undefined>

// Delete a key
await cache.delete(key: string): Promise<boolean>

// Check if key exists
await cache.has(key: string): Promise<boolean>

// Clear all data
await cache.clear(): Promise<void>

Tag Management

// Get all entries with a specific tag
await cache.getByTag<T>(tag: string, options?: { page?: number; limit?: number }): Promise<Array<[string, T]>>

// Get all unique tags
await cache.getAllTags(): Promise<string[]>

// Get all tags for a specific key
await cache.getTagsForKey(key: string): Promise<string[]>

// Invalidate all entries with a tag
await cache.invalidateTag(tag: string): Promise<void>

// Invalidate multiple tags
await cache.invalidateTags(tags: string[]): Promise<void>

// Bulk set operations
await cache.setMany(entries: Array<[string, T, string[]?]>): Promise<void>

// Compact tag arrays (remove dead keys)
await cache.compactTags(tags?: string[]): Promise<void>

Usage Examples

User Session Management

// Create sessions with tags
await cache.set('session:user1:web', { userId: 1, device: 'web' }, { 
  ttl: 3600, 
  tags: ['user:1', 'device:web', 'sessions'] 
});

await cache.set('session:user1:mobile', { userId: 1, device: 'mobile' }, { 
  ttl: 3600, 
  tags: ['user:1', 'device:mobile', 'sessions'] 
});

// Invalidate all sessions for user 1
await cache.invalidateTag('user:1');

// Get all web sessions
const webSessions = await cache.getByTag('device:web');

Product Catalog

// Add products with category tags
await cache.setMany([
  ['product:1', { name: 'Laptop', price: 999 }, ['electronics', 'computers']],
  ['product:2', { name: 'Phone', price: 699 }, ['electronics', 'mobile']],
  ['product:3', { name: 'Shirt', price: 29 }, ['clothing', 'apparel']]
]);

// Get all electronics
const electronics = await cache.getByTag('electronics');

// Clear all clothing items
await cache.invalidateTag('clothing');

Cache Warming

// Warm cache with frequently accessed data
const users = await database.getActiveUsers();
for (const user of users) {
  await cache.set(`user:${user.id}`, user, { 
    ttl: 1800, 
    tags: ['users', 'warmed', `role:${user.role}`] 
  });
}

// Clear warmed cache
await cache.invalidateTag('warmed');

Tag Introspection and Debugging

// Set up some data
await cache.set('user:123', { name: 'Alice', role: 'admin' }, {
  tags: ['users', 'role:admin', 'status:active']
});
await cache.set('session:abc', { userId: 123 }, {
  tags: ['sessions', 'device:web']
});

// Discover all tags in the system
const allTags = await cache.getAllTags();
console.log('All tags:', allTags);
// ['users', 'role:admin', 'status:active', 'sessions', 'device:web']

// Inspect tags for a specific key
const userTags = await cache.getTagsForKey('user:123');
console.log('User tags:', userTags);
// ['users', 'role:admin', 'status:active']

// Conditional operations based on tags
if (userTags.includes('role:admin')) {
  console.log('User has admin privileges');
}

// Debug cache state
for (const tag of allTags) {
  const entries = await cache.getByTag(tag);
  console.log(`Tag "${tag}" has ${entries.length} entries`);
}

// Check if a key has specific tags
const sessionTags = await cache.getTagsForKey('session:abc');
const isWebSession = sessionTags.includes('device:web');
const isMobileSession = sessionTags.includes('device:mobile');

Architecture

TaggedKeyv uses a pluggable architecture with separate concerns:

  • TaggedKeyv - Main orchestrator class
  • TagManager - Interface for tag metadata storage
  • KeyvTagManager - Default implementation using @keyvhq/core for tag storage

Custom TagManager

import { TagManager } from 'tagged-keyv-wrapper';

class CustomTagManager implements TagManager {
  // Implement interface methods
  async addKeyToTag(key: string, tag: string): Promise<void> { /* ... */ }
  async removeKeyFromTag(key: string, tag: string): Promise<void> { /* ... */ }
  // ... other methods
}

// Use custom implementation
const cache = new TaggedKeyv(keyv, new CustomTagManager());

Development

Running Tests

bun test                # Run all tests
bun test --watch        # Watch mode
bun test --coverage     # With coverage

Building

bun run build          # Build TypeScript
bun run check-types     # Type checking
bun run dev            # Watch mode

Performance Considerations

  • Memory efficiency: Automatic compaction removes dead keys and duplicates
  • Concurrency: Built-in locking prevents race conditions on key operations
  • Bulk operations: Use setMany() and invalidateTags() for better performance
  • Tag cleanup: Call compactTags() periodically for optimal memory usage

Migration from Keyv

TaggedKeyv is a complete drop-in replacement for @keyvhq/core:

// Before
const keyv = new Keyv();
await keyv.set('key', 'value', 3600);

// After (no changes needed)
const cache = new TaggedKeyv(keyv);
await cache.set('key', 'value', 3600);

// Plus new functionality
await cache.set('key', 'value', { ttl: 3600, tags: ['tag1'] });

Community & Support

We're happy to help with any questions or issues you might have. Here are some ways to get support:

License

This project is licensed under the MIT License - see the LICENSE file for details.

Contributing

Please read CONTRIBUTING.md for details on our code of conduct, and the process for submitting pull requests to us.