flight-planner
Version:
Plan and route VFR flights
149 lines (148 loc) • 4.29 kB
JavaScript
/**
* A generic LRU cache service with TTL support.
*/
export class CacheService {
cache = new Map();
accessOrder = [];
maxCacheSize;
defaultTTL;
/**
* Creates a new instance of the CacheService class.
*
* @param maxCacheSize - Maximum number of items to keep in the cache (default: 1000).
* @param defaultTTL - Default time-to-live in milliseconds, or null for no expiry (default: null).
*/
constructor(maxCacheSize = 1_000, defaultTTL = null) {
this.maxCacheSize = Math.max(1, maxCacheSize);
this.defaultTTL = defaultTTL;
}
/**
* Retrieves an item from the cache.
*
* @param key - The key of the item to retrieve.
* @returns The cached item, or undefined if not found or expired.
*/
get(key) {
const entry = this.cache.get(key);
if (!entry) {
return undefined;
}
if (entry.expiry !== null && entry.expiry < Date.now()) {
this.delete(key);
return undefined;
}
this.updateAccessOrder(key);
return entry.value;
}
/**
* Adds or updates an item in the cache.
*
* @param key - The key of the item to add or update.
* @param value - The item to cache.
* @param ttl - Time-to-live in milliseconds, or null for no expiry. Uses defaultTTL if not specified.
*/
set(key, value, ttl) {
let expiry = null;
if (ttl !== undefined) {
expiry = ttl !== null ? Date.now() + ttl : null;
}
else if (this.defaultTTL !== null) {
expiry = Date.now() + this.defaultTTL;
}
this.cache.set(key, { value, expiry });
this.updateAccessOrder(key);
this.enforceCacheLimit();
}
/**
* Checks if an item exists in the cache and is not expired.
*
* @param key - The key of the item to check.
* @returns True if the item exists and is not expired, false otherwise.
*/
has(key) {
const entry = this.cache.get(key);
if (!entry) {
return false;
}
if (entry.expiry !== null && entry.expiry < Date.now()) {
this.delete(key);
return false;
}
return true;
}
/**
* Deletes an item from the cache.
*
* @param key - The key of the item to delete.
*/
delete(key) {
this.cache.delete(key);
const index = this.accessOrder.indexOf(key);
if (index !== -1) {
this.accessOrder.splice(index, 1);
}
}
/**
* Returns an array of keys in the cache.
* This does not check for expired items.
*
* @returns An array of keys.
*/
keys() {
return Array.from(this.cache.keys());
}
/**
* Returns an array of values in the cache.
* This does not check for expired items.
*
* @returns An array of values.
*/
values() {
return Array.from(this.cache.values()).map(entry => entry.value);
}
/**
* Clears all items from the cache.
*/
clear() {
this.cache.clear();
this.accessOrder = [];
}
/**
* Removes all expired items from the cache.
*
* @returns The number of items removed.
*/
cleanExpired() {
const now = Date.now();
let count = 0;
for (const [key, entry] of this.cache.entries()) {
if (entry.expiry !== null && entry.expiry < now) {
this.delete(key);
count++;
}
}
return count;
}
/**
* Updates the access order for the LRU cache.
* @param key - The key that was accessed.
*/
updateAccessOrder(key) {
const index = this.accessOrder.indexOf(key);
if (index !== -1) {
this.accessOrder.splice(index, 1);
}
this.accessOrder.push(key);
}
/**
* Enforces the cache size limit by removing least recently used items.
*/
enforceCacheLimit() {
while (this.cache.size > this.maxCacheSize && this.accessOrder.length > 0) {
const leastUsedKey = this.accessOrder.shift();
if (leastUsedKey) {
this.cache.delete(leastUsedKey);
}
}
}
}