Skip to main content

Compression API

Automatic data compression to reduce storage size and optimize performance.

Overview​

Strata Storage includes built-in compression using a pure JavaScript implementation of the LZ-string algorithm. This reduces storage footprint for large objects while maintaining fast performance.

Configuration​

Global Compression​

Enable compression for all data:

import { defineStorage } from 'strata-storage';

const storage = defineStorage({
compression: {
enabled: true,
threshold: 1024 // Only compress data larger than 1KB
}
});

// All large objects are now automatically compressed
await storage.set('largeData', bigObject);

Per-Operation Compression​

Compress specific values:

import { defineStorage } from 'strata-storage';

const storage = defineStorage({ compression: { enabled: true } });

// Skip compression for a specific value.
await storage.set('small', 'value', { compress: false });

// Allow compression for a specific value when it reduces size.
await storage.set('large', bigObject, {
compress: true
});

The per-operation flag controls a configured compressor. compress: true on an instance without compression.enabled: true does not initialize one.

API Reference​

CompressionConfig​

Configuration options for compression:

interface CompressionConfig {
enabled?: boolean; // Enable/disable compression
threshold?: number; // Minimum size in bytes to compress (default: 1024)
algorithm?: 'lz'; // Compression algorithm (only the bundled zero-dependency LZ codec is implemented)
}

SetOptions.compress​

Per-operation compression flag:

interface SetOptions {
compress?: boolean; // Override global compression setting
// ... other options
}

Methods​

Automatic Compression/Decompression​

Compression and decompression happen automatically:

// Data is compressed when stored
await storage.set('data', largeObject, { compress: true });

// Data is decompressed when retrieved
const data = await storage.get('data'); // Returns original object

Manual Compression​

Use the exported CompressionManager class directly. Its compress/decompress methods are async; compress() returns a CompressedData object, or the original value when it is below the threshold or would not get smaller:

import { CompressionManager } from 'strata-storage';

const manager = new CompressionManager({ threshold: 1024 });
const data = 'Large string data...'.repeat(1000);

// Compress -> CompressedData | original value
const compressed = await manager.compress(data);

if (manager.isCompressedData(compressed)) {
console.log(`Original: ${compressed.originalSize} bytes`);
console.log(`Compressed: ${compressed.compressedSize} bytes`);

// Decompress -> original value
const decompressed = await manager.decompress(compressed);
console.log(decompressed === data); // true
}

Inspect Compression Results​

CompressionManager reports whether a value compressed and by how much:

import { CompressionManager } from 'strata-storage';

const manager = new CompressionManager({ threshold: 1024 });
const result = await manager.compress(largeObject);

if (manager.isCompressedData(result)) {
console.log(result.originalSize); // Size before compression (bytes)
console.log(result.compressedSize); // Size after compression (bytes)
console.log(manager.getCompressionRatio(result)); // e.g. 0.3
console.log(manager.getSavingsPercentage(result)); // e.g. 70
}

Examples​

Basic Compression​

const storage = defineStorage({
compression: {
enabled: true,
threshold: 1024 // Compress data > 1KB
}
});

// Small data - not compressed
await storage.set('small', 'Hello'); // 5 bytes, not compressed

// Large data - compressed
await storage.set('large', 'x'.repeat(2000), {
compress: true
}); // 2000 bytes, compressed

Selective Compression​

const storage = defineStorage({ compression: { enabled: true } });

// Skip a small value deliberately.
await storage.set('config', { theme: 'dark', lang: 'en' }, { compress: false });

// Ask the configured compressor to examine larger values.
await storage.set('article', longArticleText, { compress: true });
await storage.set('logs', largeLogArray, { compress: true });

The compressor may keep an input uncompressed when compression would not reduce its size.

Compression with Encryption​

Compression runs before encryption when both are configured. Supply the password at runtime from your application's authentication or a user-entered secret; do not embed it in a browser bundle or store it beside the encrypted value.

function createPrivateStorage(encryptionPassword: string) {
return defineStorage({
compression: { enabled: true, threshold: 512 },
encryption: { enabled: true, password: encryptionPassword },
});
}

Choosing a Threshold​

The threshold is chosen when the storage instance is created. Test representative values on your target devices and set a threshold that avoids compressing small writes. navigator.storage.estimate() reports an origin's estimated storage budget; it does not establish a reliable localStorage capacity or an ideal compression threshold.

Compression Measurement​

Measure a representative value with the exported manager. This reports the codec's result for that sample; it is not a measurement of the complete adapter write or of every value stored by an instance.

import { CompressionManager } from 'strata-storage';

const sample = { entries: Array.from({ length: 100 }, () => 'repeated text') };
const compressor = new CompressionManager({ threshold: 1024 });
const start = performance.now();
const result = await compressor.compress(sample);
const elapsedMs = performance.now() - start;

if (compressor.isCompressedData(result)) {
console.log({
originalBytes: result.originalSize,
compressedBytes: result.compressedSize,
elapsedMs,
});
} else {
console.log({ compressed: false, elapsedMs });
}

Performance Considerations​

When to Use Compression​

✅ Good compression candidates:

  • Text content (articles, logs, JSON strings)
  • Repetitive data
  • Large arrays with similar items
  • Base64-encoded data

❌ Poor compression candidates:

  • Already compressed data (images, videos)
  • Encrypted data (no patterns to compress)
  • Very small data (<1KB overhead not worth it)
  • Highly random data

Compression Impact​

Compression ratio and time depend on the input and device. Measure with your actual values on the platforms you support. Repetitive text often compresses well; already-compressed images and random bytes often do not. The bundled compressor does not promise a fixed reduction or latency.

Best Practices​

1. Set Appropriate Threshold​

Set a threshold for the value sizes you actually write. A very low threshold spends time checking small values even when the compressor decides to leave them unchanged.

const storage = defineStorage({
compression: { enabled: true, threshold: 1024 }
});

2. Combine with Right Storage Adapter​

// Skip compression when the value is already small enough for your use case.
await storage.set('largeData', data, {
storage: 'indexedDB',
compress: false
});

// Compression can reduce repetitive values before a browser storage write.
await storage.set('largeData', data, {
storage: 'localStorage',
compress: true
});

3. Don't Compress Already Compressed Data​

// ❌ BAD - Double compression
const imageBase64 = '...'; // Already compressed
await storage.set('image', imageBase64, { compress: true });

// ✅ GOOD - Skip compression
await storage.set('image', imageBase64, { compress: false });

Error Handling​

import { CompressionError } from 'strata-storage';

try {
await storage.set('data', largeData, { compress: true });
} catch (error) {
if (error instanceof CompressionError) {
console.error('Compression failed:', error.message);
// Fallback: Store without compression
await storage.set('data', largeData, { compress: false });
}
}

See Also​