46 lines
1.4 KiB
TypeScript
46 lines
1.4 KiB
TypeScript
|
|
/**
|
||
|
|
* In-memory LRU cache for streaming listing results.
|
||
|
|
*
|
||
|
|
* Keyed by a deterministic hash of query parameters so that repeat visits
|
||
|
|
* to the same filter combination are instant (no network request).
|
||
|
|
*/
|
||
|
|
import type { PropertyFeature } from '@/types';
|
||
|
|
import type { ParameterValues } from '@/components/FilterPanel';
|
||
|
|
|
||
|
|
interface CacheEntry {
|
||
|
|
features: PropertyFeature[];
|
||
|
|
timestamp: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
const cache = new Map<string, CacheEntry>();
|
||
|
|
const MAX_ENTRIES = 5;
|
||
|
|
|
||
|
|
export function makeCacheKey(params: ParameterValues): string {
|
||
|
|
const sorted = Object.entries(params)
|
||
|
|
.filter(([, v]) => v !== undefined && v !== null && v !== '')
|
||
|
|
.sort(([a], [b]) => a.localeCompare(b))
|
||
|
|
.map(([k, v]) => `${k}=${v instanceof Date ? v.toISOString() : v}`);
|
||
|
|
return sorted.join('&');
|
||
|
|
}
|
||
|
|
|
||
|
|
export function getCached(params: ParameterValues): PropertyFeature[] | null {
|
||
|
|
const key = makeCacheKey(params);
|
||
|
|
const entry = cache.get(key);
|
||
|
|
if (!entry) return null;
|
||
|
|
return entry.features;
|
||
|
|
}
|
||
|
|
|
||
|
|
export function setCached(params: ParameterValues, features: PropertyFeature[]): void {
|
||
|
|
const key = makeCacheKey(params);
|
||
|
|
if (cache.size >= MAX_ENTRIES && !cache.has(key)) {
|
||
|
|
// Evict oldest entry (first inserted)
|
||
|
|
const oldest = cache.keys().next().value;
|
||
|
|
if (oldest) cache.delete(oldest);
|
||
|
|
}
|
||
|
|
cache.set(key, { features, timestamp: Date.now() });
|
||
|
|
}
|
||
|
|
|
||
|
|
export function invalidateAll(): void {
|
||
|
|
cache.clear();
|
||
|
|
}
|