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

@qantesm/nanostorage

v1.0.2

Published

High-performance LocalStorage compression using native CompressionStream API. Zero-dependency, < 1KB, non-blocking.

Readme

🗜️ NanoStorage

npm version Bundle Size License: MIT

High-performance LocalStorage compression using native CompressionStream API.

Store up to 10x more data in LocalStorage with browser-native GZIP compression. Zero dependencies, under 1KB, non-blocking.


✨ Features

| Feature | Description | |---------|-------------| | 🚀 Native Speed | Uses browser's C++ compression engine, not JavaScript | | 📦 < 1KB | Minimal footprint, zero dependencies | | ⚡ Non-Blocking | Stream-based async API prevents UI freezing | | 🔧 Smart Threshold | Auto-skips compression for small data | | 📝 TypeScript | Full type definitions included | | 🎯 Simple API | Just setItem and getItem |


📊 Performance Comparison

Benchmark Results (5 MB JSON, Chrome)

| Metric | NanoStorage | lz-string | Winner | |--------|-------------|-----------|--------| | Compress Time | 95 ms | 1.3 s | 🏆 NanoStorage (14x) | | Decompress Time | 57 ms | 67 ms | 🏆 NanoStorage | | Compressed Size | 70 KB | 168 KB | 🏆 NanoStorage (2.4x) | | Compression Ratio | 98.6% | 96.6% | 🏆 NanoStorage |

💡 4/4 categories won! 5 MB JSON → 70 KB in 95ms with faster decompression.

Why So Fast?

| Feature | lz-string | NanoStorage | |---------|-----------|-------------| | Engine | JavaScript (Main Thread) | C++ (Browser Native) | | UI Blocking | ❌ Yes, freezes on big data | ✅ No, async streams | | Bundle Size | ~18 KB | < 1 KB | | Algorithm | LZW (1984) | GZIP/Deflate (Industry Standard) |

Real-World Example

📁 Original:     1 MB JSON
   ↓ GZIP:       ~100 KB
   ↓ Base64:     ~133 KB
💾 Final:        133 KB (87% savings!)

📦 Installation

npm install @qantesm/nanostorage
yarn add @qantesm/nanostorage
pnpm add @qantesm/nanostorage

🚀 Quick Start

import { nanoStorage } from '@qantesm/nanostorage';

// Store data (automatically compressed)
await nanoStorage.setItem('user', {
  name: 'Muhammet',
  preferences: { theme: 'dark', language: 'tr' },
  history: [...largeArray]
});

// Retrieve data (automatically decompressed)
const user = await nanoStorage.getItem('user');
console.log(user.name); // 'Muhammet'

📖 API Reference

Default Instance

import { nanoStorage } from '@qantesm/nanostorage';

A pre-configured instance ready to use.

Create Custom Instance

import { createStorage } from '@qantesm/nanostorage';

const storage = createStorage({
  threshold: 500,      // Bytes. Skip compression for smaller data
  algorithm: 'gzip',   // 'gzip' or 'deflate'
  keyPrefix: 'myapp:', // Prefix for all keys
});

Methods

setItem<T>(key: string, value: T): Promise<void>

Store any JSON-serializable value with automatic compression.

await storage.setItem('settings', { theme: 'dark' });
await storage.setItem('items', [1, 2, 3, 4, 5]);
await storage.setItem('count', 42);

getItem<T>(key: string): Promise<T | null>

Retrieve and decompress a stored value.

const settings = await storage.getItem<Settings>('settings');
if (settings) {
  console.log(settings.theme);
}

removeItem(key: string): Promise<void>

Remove an item from storage.

await storage.removeItem('settings');

hasItem(key: string): Promise<boolean>

Check if a key exists.

if (await storage.hasItem('user')) {
  // User data exists
}

keys(): Promise<string[]>

Get all stored keys.

const allKeys = await storage.keys();
// ['user', 'settings', 'cache']

clear(): Promise<void>

Remove all items managed by this instance.

await storage.clear();

getStats(): Promise<StorageStats>

Get compression statistics.

const stats = await storage.getStats();
console.log(`Compression ratio: ${(1 - stats.compressionRatio) * 100}%`);
// "Compression ratio: 85%"

Low-Level Functions

For advanced use cases, you can use the compression functions directly:

import { compress, decompress, isSupported } from '@qantesm/nanostorage';

// Check browser support
if (!isSupported()) {
  console.warn('CompressionStream not available');
}

// Direct compression
const result = await compress({ data: 'large payload' });
console.log(result.data);           // Compressed string
console.log(result.originalSize);   // Original byte size
console.log(result.compressedSize); // Compressed byte size
console.log(result.wasCompressed);  // true if compression was applied

// Direct decompression
const original = await decompress(result.data);

🔧 Configuration Options

| Option | Type | Default | Description | |--------|------|---------|-------------| | threshold | number | 500 | Minimum bytes to trigger compression. Smaller data is stored raw. | | algorithm | 'gzip' \| 'deflate' | 'gzip' | Compression algorithm to use. | | keyPrefix | string | 'ns:' | Prefix added to all storage keys. |

Why Threshold?

GZIP adds ~18 bytes of header overhead. For tiny data like { theme: 'dark' }, compression would actually increase size. The threshold ensures only beneficial compressions occur.


🌐 Browser Support

| Browser | Version | Status | |---------|---------|--------| | Chrome | 80+ | ✅ Supported | | Edge | 80+ | ✅ Supported | | Firefox | 113+ | ✅ Supported | | Safari | 16.4+ | ✅ Supported | | Opera | 67+ | ✅ Supported | | IE | All | ❌ Not Supported |


💡 Use Cases

🎮 Game Save Data

await nanoStorage.setItem('gameState', {
  level: 42,
  inventory: [...hundredsOfItems],
  achievements: [...],
  settings: {...}
});

📝 Form Draft Auto-Save

// Save draft as user types
await nanoStorage.setItem('formDraft', formData);

// Restore on page reload
const draft = await nanoStorage.getItem('formDraft');
if (draft) {
  restoreForm(draft);
}

🛒 E-Commerce Cart

await nanoStorage.setItem('cart', {
  items: cartItems,
  lastUpdated: Date.now()
});

📊 Dashboard State (Redux/Vuex)

// Persist state
store.subscribe(() => {
  nanoStorage.setItem('appState', store.getState());
});

// Hydrate on load
const savedState = await nanoStorage.getItem('appState');
if (savedState) {
  store.dispatch({ type: 'HYDRATE', payload: savedState });
}

⚠️ Important Notes

Async API

Unlike native localStorage.getItem() which is synchronous, NanoStorage uses Promises:

// ❌ Won't work
const data = nanoStorage.getItem('key');

// ✅ Correct
const data = await nanoStorage.getItem('key');

This is intentional - async operations prevent UI blocking during compression.

Data Must Be JSON-Serializable

// ✅ These work
await storage.setItem('obj', { a: 1 });
await storage.setItem('arr', [1, 2, 3]);
await storage.setItem('str', 'hello');
await storage.setItem('num', 42);
await storage.setItem('bool', true);
await storage.setItem('null', null);

// ❌ These won't work
await storage.setItem('fn', () => {}); // Functions
await storage.setItem('date', new Date()); // Dates (use .toISOString())
await storage.setItem('map', new Map()); // Map/Set (convert to array)

🔬 Technical Details

Compression Pipeline

┌─────────────┐     ┌──────────────┐     ┌─────────────────┐     ┌─────────┐
│ JavaScript  │ ──► │ TextEncoder  │ ──► │ CompressionStream│ ──► │ Base64  │
│ Object      │     │ (UTF-8)      │     │ (Native GZIP)   │     │ String  │
└─────────────┘     └──────────────┘     └─────────────────┘     └─────────┘

Decompression Pipeline

┌─────────┐     ┌───────────────────┐     ┌──────────────┐     ┌─────────────┐
│ Base64  │ ──► │ DecompressionStream│ ──► │ TextDecoder  │ ──► │ JavaScript  │
│ String  │     │ (Native GZIP)     │     │ (UTF-8)      │     │ Object      │
└─────────┘     └───────────────────┘     └──────────────┘     └─────────────┘

Storage Format

Compressed data is prefixed with a marker byte:

  • R - Raw (uncompressed) data
  • G - GZIP compressed
  • D - Deflate compressed

📄 License

MIT © Muhammet Ali Büyük


🤝 Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing)
  5. Open a Pull Request

🔗 Links