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

web-kv

v1.0.7

Published

Modern key-value storage for the web with IndexedDB and localStorage support

Readme

web-kv

A modern, performant key-value storage library designed for web applications. Built with TypeScript and optimized for both development experience and production performance.

Table of Contents


Installation

npm install web-kv
yarn add web-kv
bun add web-kv

Quick Start

import kv from 'web-kv';

// Store a value
await kv.set('user', { name: 'John', email: '[email protected]' });

// Retrieve a value
const user = await kv.get('user');

// Check existence
const exists = await kv.has('user');

// Delete a value
await kv.delete('user');

Core Concepts

Storage Engines

web-kv automatically selects the optimal storage engine for your environment:

| Engine | Storage Limit | Performance | Use Case | |--------|---------------|-------------|----------| | IndexedDB | ~50% of disk | Excellent | Primary storage, large datasets | | localStorage | ~5-10 MB | Good | Fallback, legacy browsers |

The library defaults to IndexedDB when available, with automatic fallback to localStorage.

Namespaces

Isolate data using namespaces to prevent key collisions:

const userStore = await kv.create({ namespace: 'users' });
const cacheStore = await kv.create({ namespace: 'cache' });

// These are completely isolated
await userStore.set('data', { type: 'user' });
await cacheStore.set('data', { type: 'cache' });

Time-To-Live (TTL)

Automatically expire entries after a specified duration:

// Expire after 1 hour (3600 seconds)
await kv.set('session', token, { ttl: 3600 });

// Check remaining time
const ttlInfo = await kv.ttl('session');
console.log(`Expires in ${ttlInfo?.remaining} seconds`);

// Extend TTL
await kv.expire('session', 7200); // Extend to 2 hours

// Remove TTL (make permanent)
await kv.persist('session');

LRU Eviction

Prevent unbounded storage growth with Least Recently Used eviction:

const cache = await kv.create({
  namespace: 'api-cache',
  maxItems: 1000,      // Maximum 1000 entries
  maxSize: 10485760,   // Maximum 10 MB
  lru: true,           // Enable LRU eviction
});

When limits are exceeded, the least recently accessed items are automatically removed.


API Reference

Basic Operations

get<T>(key: string): Promise<T | undefined>

Retrieve a value by key.

const user = await kv.get<User>('user:123');

set<T>(key: string, value: T, options?): Promise<void>

Store a value with optional TTL and priority.

await kv.set('key', value, {
  ttl: 3600,       // Expires in 1 hour
  priority: 8,     // Higher priority (1-10, higher = kept longer)
});

delete(key: string): Promise<boolean>

Remove a key. Returns true if the key existed.

const wasDeleted = await kv.delete('key');

has(key: string): Promise<boolean>

Check if a key exists.

if (await kv.has('key')) {
  // Key exists
}

Batch Operations

Perform multiple operations efficiently in a single transaction.

getMany<T>(keys: string[]): Promise<Map<string, T | undefined>>

const results = await kv.getMany(['key1', 'key2', 'key3']);

setMany<T>(entries: Entry<T>[], options?): Promise<void>

await kv.setMany([
  { key: 'a', value: 1 },
  { key: 'b', value: 2 },
  { key: 'c', value: 3, options: { ttl: 60 } },
]);

deleteMany(keys: string[]): Promise<Map<string, boolean>>

const results = await kv.deleteMany(['key1', 'key2']);

Query Operations

keys(options?): Promise<string[]>

Get all keys in the current namespace.

const allKeys = await kv.keys();
const limitedKeys = await kv.keys({ limit: 10, offset: 0 });

values<T>(options?): Promise<T[]>

Get all values.

const allValues = await kv.values<User>();

entries<T>(options?): Promise<Entry<T>[]>

Get all key-value pairs.

const entries = await kv.entries<User>();
entries.forEach(({ key, value }) => {
  console.log(key, value);
});

Search & Filtering

prefix<T>(prefix: string, options?): Promise<Entry<T>[]>

Find entries by key prefix.

const userEntries = await kv.prefix('user:');

suffix<T>(suffix: string, options?): Promise<Entry<T>[]>

Find entries by key suffix.

const jsonEntries = await kv.suffix('.json');

search<T>(pattern: string | RegExp, options?): Promise<Entry<T>[]>

Search using regular expressions.

const matches = await kv.search(/^cache:api:.*/);
const caseInsensitive = await kv.search('user', { caseInsensitive: true });

Pagination

list<T>(options): Promise<PaginatedResult<T>>

Paginate through large datasets.

const page1 = await kv.list({
  limit: 20,
  offset: 0,
  sortBy: 'createdAt',
  order: 'desc',
});

console.log(`Total: ${page1.total}`);
console.log(`Has more: ${page1.hasMore}`);

Iteration

forEach<T>(callback, options?): Promise<void>

Iterate over all entries.

await kv.forEach((value, key, metadata) => {
  console.log(key, value, metadata.createdAt);
  
  // Return false to stop iteration
  if (key === 'stop') return false;
});

Utility Methods

| Method | Description | |--------|-------------| | count(options?) | Count entries in the namespace | | size() | Get total storage size in bytes | | clear() | Remove all entries in the namespace | | stats() | Get storage statistics | | ttl(key) | Get TTL info for a key | | expire(key, ttl) | Set/update TTL for a key | | persist(key) | Remove TTL from a key | | create(options?) | Create new instance with custom config |


Advanced Usage

Encryption

Protect sensitive data with AES-256-GCM encryption:

const secureStore = await kv.create({
  namespace: 'sensitive',
  encryption: {
    enabled: true,
    key: 'your-strong-password',
  },
});

// Data is encrypted at rest
await secureStore.set('api-key', 'sk-secret-key-here');

Events

Subscribe to storage events:

const store = await kv.create();

store.on('set', (key, value, metadata) => {
  console.log(`Set: ${key}`);
});

store.on('delete', (key, success) => {
  console.log(`Deleted: ${key}`);
});

store.on('expired', (key) => {
  console.log(`Expired: ${key}`);
});

store.on('evicted', (key, reason) => {
  console.log(`Evicted: ${key} due to ${reason}`);
});

store.on('error', (error) => {
  console.error('Storage error:', error);
});

Supported Data Types

All JavaScript types are fully supported with automatic serialization:

| Type | Example | |------|---------| | Primitives | string, number, boolean, null, undefined | | Objects | { nested: { data: true } } | | Arrays | [1, 2, 3], [[nested]] | | Date | new Date() | | RegExp | /pattern/gi | | Map | new Map([['key', 'value']]) | | Set | new Set([1, 2, 3]) | | BigInt | BigInt(9007199254740991) | | TypedArrays | Uint8Array, Float32Array, etc. | | Error | new Error('message') |


Configuration

Instance Options

When creating a new instance with kv.create(options), the following options are available:

Storage Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | namespace | string | "default" | Unique identifier to isolate this instance's data from others. Use different namespaces to separate data between features or modules. | | engine | "indexeddb" | "localstorage" | "auto" | "auto" | Storage backend to use. "auto" prefers IndexedDB with localStorage fallback. |

Capacity Limits

| Option | Type | Default | Description | |--------|------|---------|-------------| | maxItems | number | 0 | Maximum number of entries allowed. Set to 0 for unlimited. When exceeded, LRU eviction removes oldest items. | | maxSize | number | 0 | Maximum total storage size in bytes. Set to 0 for unlimited. When exceeded, LRU eviction removes oldest items. | | lru | boolean | false | Enable Least Recently Used eviction. When true, oldest accessed items are removed when limits are exceeded. | | priority | number | 5 | Default priority for new entries (1-10). Higher priority items are kept longer during LRU eviction. |

Time-To-Live

| Option | Type | Default | Description | |--------|------|---------|-------------| | ttl | number | 0 | Default expiration time in seconds for new entries. Set to 0 for no expiration. Can be overridden per-entry. | | autoCleanup | boolean | true | Automatically remove expired entries in the background. Disable for manual cleanup control. | | cleanupInterval | number | 60000 | Interval in milliseconds between automatic cleanup runs. Lower values = more frequent cleanup. |

Encryption

| Option | Type | Default | Description | |--------|------|---------|-------------| | encryption.enabled | boolean | false | Enable AES-256-GCM encryption for all stored values. | | encryption.key | string | - | Password for encryption. Required when enabled is true. Use a strong, unique password. |

Performance

| Option | Type | Default | Description | |--------|------|---------|-------------| | batchSize | number | 100 | Number of items to process in a single batch during bulk operations. Larger = faster but more memory. |

Set Options

When storing values with kv.set(key, value, options):

| Option | Type | Description | |--------|------|-------------| | ttl | number | Time-to-live in seconds. Overrides default ttl for this entry. | | priority | number | Priority level 1-10. Overrides default priority for this entry. | | metadata | object | Custom metadata to store alongside the value. |

Query Options

When querying with keys(), values(), entries(), count():

| Option | Type | Description | |--------|------|-------------| | prefix | string | Filter keys starting with this string. | | suffix | string | Filter keys ending with this string. | | limit | number | Maximum number of results to return. | | offset | number | Number of results to skip (for pagination). | | includeExpired | boolean | Include expired entries in results (default: false). |


Tree Shaking

This package is fully tree-shakable. Only import what you need:

// Full default instance
import kv from 'web-kv';

// Named exports for advanced usage
import { KV, EventEmitter, serialize, deserialize } from 'web-kv';

// Only type imports (zero runtime cost)
import type { KVOptions, Entry, StorageStats } from 'web-kv';

Browser Support

| Browser | Minimum Version | |---------|-----------------| | Chrome | 60+ | | Firefox | 60+ | | Safari | 11+ | | Edge | 79+ |