safe-memory-layer
v1.3.3
Published
Secure, lightweight, dependency-free in-memory storage with TTL support, automatic cleanup, and memory leak prevention for Node.js and browsers
Downloads
1,035
Maintainers
Readme
Safe Memory Layer
Secure, lightweight, dependency-free in-memory storage with TTL support, automatic cleanup, and memory leak prevention for Node.js and modern browsers.
Features
- Memory Safety: Automatic cleanup of expired entries, leak prevention, and safe resource management
- TTL Support: Time-based expiration with lazy and active cleanup
- Automatic Cleanup: Background scheduler that stops when empty to save CPU
- WeakMap Support: Object-only storage with automatic garbage collection
- LRU/FIFO Eviction: Configurable strategies when reaching max entries
- Event Callbacks: Monitor expiration, deletion, and cleanup events
- Statistics: Track entries, expired count, deleted count, and uptime
- Auto-Dispose: Automatically dispose store after being empty for a configurable time
- Zero Dependencies: No external runtime dependencies
- TypeScript: Full generic typing with strict mode compatibility
- Tree-Shakeable ESM: Optimized bundle size
- Browser & Node.js: Works in both environments
Installation
npm install safe-memory-layerQuick Start
import { MemoryStore } from "safe-memory-layer";
// Create a store
const store = new MemoryStore<string, User>();
// Set values with optional TTL
store.set("user:123", user, { ttl: 60000 }); // Expires in 60 seconds
// Get values
const user = store.get("user:123");
// Check existence
if (store.has("user:123")) {
console.log("User exists");
}
// Clean up when done
store.dispose();API Reference
MemoryStore
The main in-memory store with TTL support and automatic cleanup.
Constructor
const store = new MemoryStore<K, V>(options?: MemoryStoreOptions<K, V>);Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| defaultTTL | number \| undefined | undefined | Default TTL in milliseconds for all entries |
| cleanupInterval | number | 10000 | Interval between automatic cleanup runs (ms) |
| autoCleanup | boolean | true | Enable automatic cleanup |
| autoDispose | boolean | false | Auto-dispose after being empty for autoDisposeDelay |
| autoDisposeDelay | number | 60000 | Delay before auto-dispose (ms) |
| maxEntries | number \| undefined | undefined | Maximum number of entries |
| maxEntriesStrategy | "reject" \| "FIFO" \| "LRU" | "reject" | Strategy when max entries is reached |
| onExpire | (key: K, value: V) => void | undefined | Callback when entry expires |
| onDelete | (key: K, value: V) => void | undefined | Callback when entry is deleted |
| onCleanup | (stats: CleanupStats) => void | undefined | Callback after cleanup cycle |
Methods
set(key, value, options?)
Stores a value with the given key.
store.set("key", value, { ttl: 60000 });Returns: boolean - true if stored, false if rejected (max entries reached)
get(key)
Retrieves a value by key.
const value = store.get("key");Returns: V | undefined - The value or undefined if not found/expired
has(key)
Checks if a key exists and is not expired.
if (store.has("key")) {
// Key exists
}Returns: boolean
delete(key)
Deletes a value by key.
const deleted = store.delete("key");Returns: boolean - true if key was present
clear()
Removes all entries.
store.clear();size
Returns the number of non-expired entries.
const count = store.size;Returns: number
keys()
Returns an iterator of all keys.
for (const key of store.keys()) {
console.log(key);
}Returns: IterableIterator<K>
values()
Returns an iterator of all non-expired values.
for (const value of store.values()) {
console.log(value);
}Returns: IterableIterator<V>
entries()
Returns an iterator of all [key, value] pairs.
for (const [key, value] of store.entries()) {
console.log(key, value);
}Returns: IterableIterator<[K, V]>
cleanup()
Removes all expired entries.
const removed = store.cleanup();Returns: number - Number of entries removed
compact()
Alias for cleanup().
const removed = store.compact();stats()
Returns statistics about the store.
const stats = store.stats();
console.log(stats);Returns: StoreStats
interface StoreStats {
entries: number; // Current number of entries
expired: number; // Total expired entries
deleted: number; // Total deleted entries
cleaned: number; // Total cleanup cycles
uptime: number; // Uptime in milliseconds
memoryEstimate: number; // Estimated memory usage in bytes
}dispose()
Disposes the store, stopping all timers and releasing references.
store.dispose();After disposal, the store cannot be used.
disposed
Checks if the store has been disposed.
if (store.disposed) {
console.log("Store is disposed");
}Returns: boolean
WeakMemoryStore
Object-only storage using WeakMap for automatic garbage collection.
Constructor
const weakStore = new WeakMemoryStore<V>(options?: WeakMemoryStoreOptions<V>);Options:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| onDelete | (value: V) => void | undefined | Callback when entry is deleted |
Methods
set(key, value)
Stores a value with an object key.
const obj = { id: 1 };
weakStore.set(obj, { name: "Alice" });Returns: boolean
get(key)
Retrieves a value by object key.
const value = weakStore.get(obj);Returns: V | undefined
has(key)
Checks if a key exists.
if (weakStore.has(obj)) {
// Key exists
}Returns: boolean
delete(key)
Deletes a value by object key.
const deleted = weakStore.delete(obj);Returns: boolean
clear()
Removes all entries.
weakStore.clear();deleted
Returns the number of deleted entries.
const count = weakStore.deleted;Returns: number
stats()
Returns statistics.
const stats = weakStore.stats();Returns: { deleted: number; uptime: number }
dispose()
Disposes the store.
weakStore.dispose();disposed
Checks if the store has been disposed.
if (weakStore.disposed) {
console.log("Store is disposed");
}Returns: boolean
Examples
Basic Cache
const cache = new MemoryStore<string, string>();
cache.set("key", "value");
console.log(cache.get("key")); // "value"
cache.dispose();TTL Cache
const ttlCache = new MemoryStore<string, User>({
defaultTTL: 60000, // 1 minute
cleanupInterval: 10000,
});
ttlCache.set("user:1", user, { ttl: 30000 }); // 30 secondsSession Storage
const sessionStore = new MemoryStore<string, Session>({
defaultTTL: 30 * 60 * 1000, // 30 minutes
maxEntries: 1000,
maxEntriesStrategy: "LRU",
});
sessionStore.set(sessionId, sessionData);Token Storage
const authStore = new MemoryStore<string, TokenData>({
defaultTTL: 3600000, // 1 hour
maxEntries: 5000,
maxEntriesStrategy: "FIFO",
});
authStore.set(`token:${token}`, tokenData);Weak Object Cache
const weakStore = new WeakMemoryStore<UserData>();
const user = { id: 1 };
weakStore.set(user, { name: "Alice" });
// When 'user' is garbage collected, entry is automatically removedEvent Monitoring
const store = new MemoryStore<string, Data>({
onExpire: (key, value) => {
console.log(`Expired: ${key}`);
},
onDelete: (key, value) => {
console.log(`Deleted: ${key}`);
},
onCleanup: (stats) => {
console.log(`Cleaned ${stats.removed} entries`);
},
});Auto-Dispose
const store = new MemoryStore<string, Data>({
autoDispose: true,
autoDisposeDelay: 60000, // 1 minute
});
store.set("key", value);
store.delete("key");
// Store will auto-dispose after 1 minute of being emptyPerformance Optimization
Safe Memory Layer supports disabling features to reduce overhead when they're not needed. This makes the library extremely lightweight and tree-shakeable.
Disable Automatic Cleanup
If you want to call cleanup() manually on your own schedule:
const store = new MemoryStore({
autoCleanup: false,
cleanupInterval: 0, // Disables the scheduler
});
// Call cleanup manually when needed
store.cleanup();Disable Statistics
Disable statistics tracking to reduce overhead:
const store = new MemoryStore({
statistics: false,
});
// stats() will return basic computed values
const stats = store.stats();Disable Events
Disable event callbacks to reduce overhead:
const store = new MemoryStore({
events: false,
});
// No event callbacks will be firedMinimal Configuration Example
// Ultra-minimal store with no overhead
const minimalStore = new MemoryStore({
autoCleanup: false,
statistics: false,
events: false,
cleanupInterval: 0,
});
// Use with manual cleanup
minimalStore.set("key", value);
// ... later ...
minimalStore.cleanup();
minimalStore.dispose();Performance Comparison
| Configuration | Memory Overhead | CPU Overhead | Use Case | |---------------|----------------|--------------|----------| | Full features (default) | ~200 bytes/entry | Low (periodic cleanup) | General purpose | | No statistics | ~150 bytes/entry | Minimal | When you don't need stats | | No events | ~180 bytes/entry | Minimal | When you don't need callbacks | | Manual cleanup | ~100 bytes/entry | Zero (until cleanup called) | Maximum performance | | Minimal (all disabled) | ~100 bytes/entry | Zero (until cleanup called) | Ultimate performance |
Tree-Shaking
The library is fully tree-shakeable. When using a bundler like webpack, Rollup, or esbuild, unused features are automatically removed from the final bundle.
// Import only what you need
import { MemoryStore } from "safe-memory-layer";
// Stats, Events, and Scheduler modules are tree-shaken if not usedBest Practices
- Always dispose stores: Call
dispose()when done to release timers and references - Use appropriate TTLs: Set TTLs based on data freshness requirements
- Configure cleanup interval: Balance between cleanup frequency and CPU usage
- Use LRU for caches: LRU eviction works well for cache scenarios
- Use FIFO for queues: FIFO eviction works well for queue-like data
- Monitor with callbacks: Use event callbacks to track store behavior
- Avoid storing large objects: Keep values small for better performance
- Use WeakMemoryStore for object metadata: When keys are objects that may be GC'd
- Disable unused features: Set
statistics: falseandevents: falseto reduce overhead - Manual cleanup for performance: Use
autoCleanup: falseand callcleanup()manually for maximum performance
Performance
- O(1) get, set, delete operations
- O(n) cleanup (iterates all entries)
- O(n) size (counts non-expired entries)
- Minimal memory allocations
- Single timer for all cleanup operations
- Automatic scheduler pause when empty
Browser Compatibility
Works in all modern browsers that support:
- ES2022
- WeakMap
- Map
- setTimeout/setInterval
No Node.js-specific APIs are used.
Electron Support
Safe Memory Layer works perfectly in Electron applications, both in the main process and renderer process.
Main Process
Use MemoryStore in Electron's main process for:
- Window state management
- IPC message caching
- Application state management
- Session data storage
import { MemoryStore } from "safe-memory-layer";
// Window state store
const windowStateStore = new MemoryStore<string, WindowState>({
defaultTTL: 5 * 60 * 1000, // 5 minutes
cleanupInterval: 30_000,
autoCleanup: true,
});
// Save window state
windowStateStore.set(`window:${windowId}`, {
bounds: window.getBounds(),
isFocused: window.isFocused(),
});Renderer Process
Use MemoryStore in Electron's renderer process (browser environment) for:
- UI component state
- API response caching
- DOM element metadata (with WeakMemoryStore)
- User preferences
import { MemoryStore, WeakMemoryStore } from "safe-memory-layer";
// UI state store
const uiStateStore = new MemoryStore<string, UIState>({
defaultTTL: 10 * 60 * 1000, // 10 minutes
autoCleanup: true,
});
// DOM metadata store (auto-cleaned by GC)
const domStore = new WeakMemoryStore<ElementMetadata>();
const element = document.querySelector("#my-element");
domStore.set(element, { interactions: 0, lastClick: Date.now() });Electron Best Practices
- Dispose on app quit: Always dispose stores when the app quits
- Use TTLs: Set appropriate TTLs for cached data
- Separate stores: Use different stores for different concerns
- Cleanup on window close: Dispose stores when windows are closed
- IPC caching: Cache IPC responses to reduce main process load
- Lazy load modules: Only import safe-memory-layer when actually needed using dynamic
import() - Completely close windows: Call
win.close()notwin.hide()to free renderer processes - Remove listeners: Always remove event listeners before closing windows
- Dispose before closing: Dispose memory stores before closing windows
Lazy Loading Pattern
For optimal performance in Electron, use lazy loading:
// Instead of static import at the top
// import { MemoryStore } from "safe-memory-layer";
// Use dynamic import when needed
async function getStore() {
const { MemoryStore } = await import("safe-memory-layer");
return new MemoryStore({
statistics: false,
events: false,
});
}
// Use in your code
const store = await getStore();
store.set("key", value);Window Disposal Pattern
Always completely close windows to free renderer processes:
win.on("closed", () => {
// Dispose memory store
store.dispose();
// Remove all listeners
win.removeAllListeners();
win.webContents.removeAllListeners();
// Clear references
windowStores.delete(win);
});See examples/electron-lazy-loading.ts for a complete example.
TypeScript Support
Full TypeScript support with generics:
interface User {
id: string;
name: string;
email: string;
}
const store = new MemoryStore<string, User>();
store.set("user:1", { id: "1", name: "Alice", email: "[email protected]" });
const user = store.get("user:1"); // Type: User | undefinedJavaScript Support
The library works seamlessly with plain JavaScript (no TypeScript required). Since it's compiled to both ESM and CommonJS, you can use it in any JavaScript project.
ESM (Modern JavaScript)
import { MemoryStore, WeakMemoryStore } from "safe-memory-layer";
const cache = new MemoryStore();
cache.set("key", "value", { ttl: 60000 });
const value = cache.get("key");
cache.dispose();CommonJS (Node.js)
const { MemoryStore, WeakMemoryStore } = require("safe-memory-layer");
const cache = new MemoryStore();
cache.set("key", "value", { ttl: 60000 });
const value = cache.get("key");
cache.dispose();JavaScript Features
- No TypeScript required
- Works with ESM and CommonJS
- Full API access (same as TypeScript)
- No type annotations needed
- Dynamic typing support
See examples/javascript-usage.js for a complete JavaScript example.
License
MIT
Contributing
Contributions are welcome! Please ensure all tests pass before submitting a PR.
npm testChangelog
1.0.0
- Initial release
- MemoryStore with TTL support
- WeakMemoryStore with WeakMap
- Automatic cleanup scheduler
- LRU/FIFO eviction strategies
- Event callbacks
- Statistics tracking
- Auto-dispose feature
- Full TypeScript support
1.1.2
- support Electron desktop (Framework)
1.2.2
- support JS
1.3.2
- New option
API:
const store = new MemoryStore({
ttl: 60000,
autoCleanup: false,
statistics: false,
events: false,
cleanupInterval: 0
});- Electron Lazy Loading and Window Management
