@oxyhq/services
Version:
Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀
259 lines (237 loc) • 5.52 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.TTLCache = void 0;
exports.createCache = createCache;
exports.registerCacheForCleanup = registerCacheForCleanup;
exports.stopAllCleanupIntervals = stopAllCleanupIntervals;
exports.unregisterCacheFromCleanup = unregisterCacheFromCleanup;
/**
* Centralized cache utility with TTL support
*
* This is a production-ready cache implementation used across the codebase
* for consistent caching behavior and performance optimization.
*/
/**
* Cache entry with expiration tracking
*/
/**
* Cache statistics
*/
/**
* TTL-based cache implementation
*
* Features:
* - Automatic expiration based on TTL
* - Manual cleanup of expired entries
* - Statistics tracking (hits, misses, hit rate)
* - Type-safe generic interface
*
* @example
* ```typescript
* const cache = new TTLCache<string>(5 * 60 * 1000); // 5 minutes
*
* // Set with default TTL
* cache.set('key', 'value');
*
* // Set with custom TTL
* cache.set('key', 'value', 10 * 60 * 1000); // 10 minutes
*
* // Get value
* const value = cache.get('key');
*
* // Get statistics
* const stats = cache.getStats();
* ```
*/
class TTLCache {
cache = new Map();
hits = 0;
misses = 0;
/**
* Create a new TTL cache
* @param defaultTTL Default TTL in milliseconds (default: 5 minutes)
*/
constructor(defaultTTL = 5 * 60 * 1000) {
this.defaultTTL = defaultTTL;
}
/**
* Get a value from cache
* @param key Cache key
* @returns Cached value or null if not found or expired
*/
get(key) {
const entry = this.cache.get(key);
if (!entry) {
this.misses++;
return null;
}
const now = Date.now();
if (now > entry.expiresAt) {
this.cache.delete(key);
this.misses++;
return null;
}
this.hits++;
return entry.data;
}
/**
* Set a value in cache
* @param key Cache key
* @param data Data to cache
* @param ttl Optional TTL override (uses default if not provided)
*/
set(key, data, ttl) {
const now = Date.now();
const expiresAt = now + (ttl || this.defaultTTL);
this.cache.set(key, {
data,
timestamp: now,
expiresAt
});
}
/**
* Delete a specific cache entry
* @param key Cache key
* @returns true if entry was deleted, false if not found
*/
delete(key) {
return this.cache.delete(key);
}
/**
* Clear all cache entries
*/
clear() {
this.cache.clear();
this.hits = 0;
this.misses = 0;
}
/**
* Check if a key exists and is not expired
* @param key Cache key
* @returns true if key exists and is valid
*/
has(key) {
const entry = this.cache.get(key);
if (!entry) return false;
if (Date.now() > entry.expiresAt) {
this.cache.delete(key);
return false;
}
return true;
}
/**
* Get all valid cache keys
* @returns Array of valid cache keys
*/
keys() {
const now = Date.now();
const validKeys = [];
for (const [key, entry] of this.cache.entries()) {
if (now <= entry.expiresAt) {
validKeys.push(key);
} else {
this.cache.delete(key);
}
}
return validKeys;
}
/**
* Clean up expired entries
* @returns Number of entries removed
*/
cleanup() {
const now = Date.now();
let removed = 0;
for (const [key, entry] of this.cache.entries()) {
if (now > entry.expiresAt) {
this.cache.delete(key);
removed++;
}
}
return removed;
}
/**
* Get cache size (number of entries)
*/
size() {
return this.cache.size;
}
/**
* Get cache statistics
*/
getStats() {
const total = this.hits + this.misses;
return {
size: this.cache.size,
hits: this.hits,
misses: this.misses,
hitRate: total > 0 ? this.hits / total : 0
};
}
/**
* Reset statistics (keeps cache entries)
*/
resetStats() {
this.hits = 0;
this.misses = 0;
}
}
/**
* Create a TTL cache instance (convenience function)
*
* @example
* ```typescript
* const cache = createCache<string>(5 * 60 * 1000);
* ```
*/
exports.TTLCache = TTLCache;
function createCache(ttl = 5 * 60 * 1000) {
return new TTLCache(ttl);
}
/**
* Global cache cleanup interval (runs every minute)
* This helps prevent memory leaks from expired cache entries
*/
let cleanupInterval = null;
const activeCaches = new Set();
/**
* Register a cache for automatic cleanup
* @param cache Cache instance to register
*/
function registerCacheForCleanup(cache) {
activeCaches.add(cache);
// Start cleanup interval if not already running
if (!cleanupInterval) {
cleanupInterval = setInterval(() => {
for (const cache of activeCaches) {
cache.cleanup();
}
}, 60000); // Every minute
}
}
/**
* Unregister a cache from automatic cleanup
* @param cache Cache instance to unregister
*/
function unregisterCacheFromCleanup(cache) {
activeCaches.delete(cache);
// Stop cleanup interval if no caches are registered
if (activeCaches.size === 0 && cleanupInterval) {
clearInterval(cleanupInterval);
cleanupInterval = null;
}
}
/**
* Stop all cleanup intervals (useful for testing)
* This will clear the interval and unregister all caches
*/
function stopAllCleanupIntervals() {
if (cleanupInterval) {
clearInterval(cleanupInterval);
cleanupInterval = null;
}
activeCaches.clear();
}
//# sourceMappingURL=cache.js.map