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

starveil

v1.1.1

Published

LocalStorage with expiration, namespace, and advanced features for web applications

Readme

License: MIT npm version Downloads TypeScript Node.js Zero Dependencies

Version 1.1.1 - Production Release 🎉

View on npmjs.com


Table of Contents


About

Starveil is a lightweight, zero-dependency localStorage wrapper that enhances browser storage with powerful features like automatic expiration, namespace isolation, size management, and event-driven notifications. Built with TypeScript from the ground up, it provides type-safe storage operations for modern web applications.


Features

  • TTL Support: Time-to-live with human-readable format ('1h', '30m', '2d', '1w')
  • Namespace: Optional prefix to avoid key collisions across applications
  • Size Management: Auto-cleanup oldest items when storage is full
  • Custom Response: { starveilInfo: 'expired', starveilStatus: false } for expired data
  • Events: expired, warning, full events for reactive programming
  • TypeScript Support: Full type definitions and generic types included
  • Cross-Tab Sync: Synchronize storage changes across browser tabs
  • Metadata Access: Get expiration time, TTL, and storage info
  • Validation: Built-in data validation before storage
  • Zero Dependencies: Pure TypeScript implementation, no external dependencies

Installation

npm install starveil
# or
yarn add starveil
# or
pnpm add starveil
# or
bun add starveil

Quick Start

Usage Examples

import Starveil from 'starveil';

// 1. Default settings (namespace='Starveil', no expiration)
const storage = new Starveil();

// 2. String argument (namespace='myapp', no expiration)
const storage = new Starveil('myapp');

// 3. Options with name and expire (new API)
const storage = new Starveil({
  name: 'myapp',
  expire: '1h'
});

// 4. Backward compatible with namespace and defaultTTL
const storage = new Starveil({
  namespace: 'myapp',
  defaultTTL: '1h'
});

storage.set('user', { id: 1, name: 'John' });
const user = storage.get('user');
console.log(user);

Interface include

import Starveil from 'starveil';

interface User {
  id: number;
  name: string;
}

const storage = new Starveil<User>({
  name: 'myapp',
  expire: '1h'
});

const user: User = storage.get('user');
console.log(user.name);

API Reference

Constructor

new Starveil<T = any>(optionsOrNamespace?: StarveilOptions | string)

Constructor Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | namespace | string | null | Prefix for all keys to avoid collisions (backward compatible) | | name | string | null | Alias for namespace (new API) | | defaultTTL | string | null | Default time-to-live for all items (backward compatible) | | expire | string | null | Alias for defaultTTL (new API) | | maxSize | number | 5242880 | Maximum storage size in bytes (5MB) |


TTL Formats

  • '1s' - 1 second
  • '1m' - 1 minute
  • '1h' - 1 hour
  • '1d' - 1 day
  • '1w' - 1 week
  • '2h30m' - 2 hours and 30 minutes
  • '1d12h' - 1 day and 12 hours

Namespace

const app1 = new Starveil({ namespace: 'app1' });
const app2 = new Starveil({ namespace: 'app2' });

app1.set('user', { id: 1 });
app2.set('user', { id: 2 });

app1.get('user'); // { id: 1 }
app2.get('user'); // { id: 2 }

// Keys are stored as:
// 'app1:user' and 'app2:user'

Browser Compatibility

| Browser | Minimum Version | |---------|-----------------| | Chrome | 90+ | | Firefox | 88+ | | Safari | 14+ | | Edge | 90+ |


Methods

| Method | Description | |--------|-------------| | set() | Store data with optional TTL | | get() | Retrieve data from storage | | remove() | Remove a specific key | | clear() | Remove all items from storage | | getAll() | Get all items from storage | | getInfo() | Get storage information | | setMaxSize() | Set maximum storage size | | on() | Register event listeners | | off() | Remove event listeners |


set()

Store data with optional time-to-live (TTL).

Basic Usage:

storage.set('key', 'value');
storage.set('user', { id: 1, name: 'John' });
storage.set('config', { theme: 'dark' }, { ttl: '1h' });

Parameters:

  • key (string): Storage key
  • value (any): Data to store
  • options (object, optional):
    • ttl (string): Time-to-live (e.g., '1h', '30m', '2d')

Returns: boolean - Success status

Real-life Example - Session Token:

const storage = new Starveil();

// Store user session that expires in 2 hours
const sessionToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';

storage.set('session', sessionToken, { ttl: '2h' });

// Later retrieve the session
const token = storage.get('session');
if (token) {
  console.log('User is logged in');
} else {
  console.log('Session expired');
}

Real-life Example - Shopping Cart:

const cart = {
  items: [
    { id: 1, name: 'Product A', price: 29.99, quantity: 2 },
    { id: 2, name: 'Product B', price: 49.99, quantity: 1 }
  ],
  total: 109.97
};

// Cart expires in 24 hours (abandoned cart)
storage.set('cart', cart, { ttl: '1d' });

// Check if cart still exists
const savedCart = storage.get('cart');
if (savedCart) {
  displayCart(savedCart);
} else {
  showEmptyCartMessage();
}

get()

Retrieve data from storage. Optionally include metadata.

Basic Usage:

const value = storage.get('key');
const withMeta = storage.get('key', { includeMeta: true });

Parameters:

  • key (string): Storage key
  • options (object, optional):
    • includeMeta (boolean): Include metadata in response

Returns: Data value, expired response, or metadata object

Expired Response:

{
  starveilInfo: 'expired',
  starveilStatus: false
}

Response with Metadata:

{
  value: { id: 1, name: 'John' },
  expiresAt: 1737369600000,
  ttl: '1h',
  size: 156
}

Real-life Example - Countdown Timer:

const session = storage.get('session', { includeMeta: true });

if (session?.expiresAt) {
  const timeLeft = session.expiresAt - Date.now();
  const minutesLeft = Math.floor(timeLeft / 1000 / 60);

  console.log(`Session expires in ${minutesLeft} minutes`);

  if (minutesLeft < 5) {
    console.warn('Session expiring soon!');
    showRenewSessionDialog();
  }
}

Real-life Example - Cache Validation:

const cacheData = storage.get('api-data', { includeMeta: true });

if (cacheData) {
  const age = Date.now() - (cacheData.expiresAt || 0);
  const ageInMinutes = Math.abs(age) / 1000 / 60;

  if (ageInMinutes > 30) {
    console.log('Cache is too old, refreshing...');
    fetchFreshData();
  } else {
    console.log(`Using cache (${ageInMinutes.toFixed(0)} min old)`);
    renderData(cacheData.value);
  }
}

remove()

Remove a specific key from storage.

Basic Usage:

storage.remove('user');
storage.remove('session');

Parameters:

  • key (string): Storage key

Returns: boolean - Success status

Real-life Example - User Logout:

function logout() {
  storage.remove('session');
  storage.remove('user');
  storage.remove('preferences');
  window.location.href = '/login';
}

// Call when user clicks logout
document.getElementById('logout-btn').addEventListener('click', logout);

Real-life Example - Clear Specific Cache:

function clearUserData() {
  const keysToRemove = ['user', 'cart', 'wishlist', 'recently-viewed'];
  keysToRemove.forEach(key => storage.remove(key));
  console.log('User data cleared');
}

clear()

Remove all items from storage in the current namespace.

Basic Usage:

storage.clear();

Returns: void

Real-life Example - Reset App:

function factoryReset() {
  if (confirm('This will delete all your data. Continue?')) {
    storage.clear();
    console.log('All data cleared');
    window.location.reload();
  }
}

document.getElementById('reset-btn').addEventListener('click', factoryReset);

Real-life Example - Clear on Logout:

function logout() {
  storage.clear();
  showNotification('Logged out successfully');
  navigateToLogin();
}

getAll()

Get all items from storage with their metadata.

Basic Usage:

const all = storage.getAll();
console.log(all);

Returns: Object with all items and metadata

{
  'user': {
    value: { id: 1, name: 'John' },
    expiresAt: 1737369600000,
    ttl: '1h',
    size: 156
  },
  'cart': {
    value: { items: [], total: 0 },
    expiresAt: 1737369600000,
    ttl: '1d',
    size: 89
  }
}

Real-life Example - Debug Dashboard:

function showDebugInfo() {
  const allData = storage.getAll();
  console.table(allData);

  const totalSize = Object.values(allData).reduce((sum, item) => sum + item.size, 0);
  const sizeMB = (totalSize / 1024 / 1024).toFixed(2);
  const itemCount = Object.keys(allData).length;

  console.log(`Storage: ${itemCount} items, ${sizeMB}MB total`);
}

showDebugInfo();

Real-life Example - Export Data:

function exportUserData() {
  const allData = storage.getAll();
  const exportData = {
    exportedAt: new Date().toISOString(),
    data: {}
  };

  for (const [key, item] of Object.entries(allData)) {
    exportData.data[key] = item.value;
  }

  const blob = new Blob([JSON.stringify(exportData, null, 2)], { type: 'application/json' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'starveil-export.json';
  a.click();
}

exportUserData();

getInfo()

Get storage statistics and usage information.

Basic Usage:

const info = storage.getInfo();
console.log(info);

Returns: Storage information

{
  usedSpace: 1024,
  freeSpace: 5241856,
  itemCount: 2,
  maxSize: 5242880
}

Real-life Example - Storage Health Monitor:

function checkStorageHealth() {
  const info = storage.getInfo();
  const usagePercent = ((info.usedSpace / info.maxSize) * 100).toFixed(1);
  const usedMB = (info.usedSpace / 1024 / 1024).toFixed(2);
  const freeMB = (info.freeSpace / 1024 / 1024).toFixed(2);

  console.log(`Storage: ${usedMB}MB used / ${freeMB}MB free (${usagePercent}%)`);

  if (parseFloat(usagePercent) > 80) {
    console.warn('Storage nearly full!');
    suggestCleanup();
  }
}

setInterval(checkStorageHealth, 60000);

Real-life Example - Storage Quota Warning:

function showStorageUsage() {
  const info = storage.getInfo();
  const usagePercent = Math.floor((info.usedSpace / info.maxSize) * 100);

  const element = document.getElementById('storage-indicator');
  element.textContent = `${usagePercent}% storage used`;

  if (usagePercent < 50) {
    element.style.color = 'green';
  } else if (usagePercent < 80) {
    element.style.color = 'orange';
  } else {
    element.style.color = 'red';
    element.textContent += ' - Consider cleaning up';
  }
}

showStorageUsage();

setMaxSize()

Set maximum storage size dynamically. Accepts number or human-readable string.

Basic Usage:

storage.setMaxSize(10 * 1024 * 1024);
storage.setMaxSize('10MB');
storage.setMaxSize('1GB');
storage.setMaxSize('512KB');

Parameters:

  • size (number | string): Maximum size in bytes or human-readable format

Returns: void

Real-life Example - User Preference:

const storageSettings = document.getElementById('storage-settings');

storageSettings.addEventListener('change', (e) => {
  const sizeMB = parseInt(e.target.value);
  storage.setMaxSize(`${sizeMB}MB`);
  console.log(`Storage limit set to ${sizeMB}MB`);
});

Real-life Example - Dynamic Quota:

function applyStorageQuota(userTier) {
  const quotas = {
    'free': '5MB',
    'premium': '50MB',
    'enterprise': '500MB'
  };

  const quota = quotas[userTier] || '5MB';
  storage.setMaxSize(quota);
  console.log(`Applied ${quota} storage quota for ${userTier} users`);
}

applyStorageQuota('premium');

on()

Register event listeners for storage events.

Basic Usage:

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

Parameters:

  • event (string): Event name ('expired', 'warning', 'full')
  • callback (function): Event handler function

Returns: void

Real-life Example - Auto-Refresh Session:

const storage = new Starveil();

storage.on('expired', (key, value) => {
  if (key === 'session') {
    console.log('Session expired, redirecting to login...');
    showNotification('Your session has expired. Please log in again.');
    setTimeout(() => {
      window.location.href = '/login';
    }, 2000);
  }
});

storage.set('session', 'token-abc123', { ttl: '1h' });

Real-life Example - Automatic Cleanup:

storage.on('warning', (message) => {
  console.log(`Warning: ${message}`);
  showNotification(`Storage warning: ${message}`, 'warning');

  const allData = storage.getAll();
  const oldKeys = Object.entries(allData)
    .filter(([_, item]) => item.expiresAt && item.expiresAt < Date.now())
    .map(([key]) => key);

  oldKeys.forEach(key => storage.remove(key));
  console.log(`Cleaned up ${oldKeys.length} expired items`);
});

storage.on('full', () => {
  console.log('Storage is full! Cleaning up...');
  showNotification('Storage full. Cleaning up old data...', 'error');

  const allData = storage.getAll();
  const sortedKeys = Object.entries(allData)
    .sort((a, b) => a[1].expiresAt - b[1].expiresAt)
    .slice(0, 5)
    .map(([key]) => key);

  sortedKeys.forEach(key => storage.remove(key));
  console.log(`Removed ${sortedKeys.length} oldest items`);
});

Real-life Example - Analytics Tracking:

storage.on('expired', (key, value) => {
  analytics.track('item_expired', {
    key: key,
    keyType: typeof value,
    timestamp: new Date().toISOString()
  });
});

storage.on('full', () => {
  if (typeof window.Sentry !== 'undefined') {
    Sentry.captureMessage('Storage is full', {
      level: 'warning',
      extra: { itemCount: storage.getInfo().itemCount }
    });
  }
});

off()

Remove event listeners to prevent memory leaks.

Basic Usage:

const handler = (key, value) => {
  console.log(`${key} expired:`, value);
};

storage.on('expired', handler);

storage.off('expired', handler);

Parameters:

  • event (string): Event name
  • callback (function): Event handler function to remove

Returns: void

Real-life Example - Component Cleanup:

class UserProfile extends React.Component {
  constructor(props) {
    super(props);
    this.handleExpiration = this.handleExpiration.bind(this);
  }

  componentDidMount() {
    storage.on('expired', this.handleExpiration);
  }

  componentWillUnmount() {
    storage.off('expired', this.handleExpiration);
  }

  handleExpiration(key, value) {
    if (key === 'user') {
      this.setState({ user: null });
      showNotification('User session expired');
    }
  }
}

Real-life Example - Conditional Monitoring:

let monitorActive = false;

function startMonitoring() {
  if (monitorActive) return;

  storage.on('warning', handleWarning);
  storage.on('full', handleFull);
  monitorActive = true;
  console.log('Storage monitoring started');
}

function stopMonitoring() {
  storage.off('warning', handleWarning);
  storage.off('full', handleFull);
  monitorActive = false;
  console.log('Storage monitoring stopped');
}

function handleWarning(message) {
  console.log(`Warning: ${message}`);
}

function handleFull() {
  console.log('Storage is full!');
}

startMonitoring();
stopMonitoring();

Contributing

Contributions are welcome! Please see AGENTS.md for guidelines.

Development Setup

git clone https://github.com/dotjumpdot/starveil.git
cd starveil
bun install

Development Commands

bun run build          # Build TypeScript
bun run test           # Run tests
bun run test:watch     # Run tests in watch mode
bun run test:coverage  # Run tests with coverage

License

MIT License - Copyright (c) 2026 DotJumpDot


Changelog

See CHANGELOG.md for version history.


Support


Made with ❤️ by DotJumpDot

⬆ Back to Top