Skip to content

qanteSm/NanoStorage

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

8 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

๐Ÿ—œ๏ธ 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

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Packages

No packages published