import { CipherView } from "../view/cipherView"; const CacheTTL = 3000; export class SortedCiphersCache { private readonly sortedCiphersByUrl: Map = new Map(); private readonly timeouts: Map = new Map(); constructor(private readonly comparator: (a: CipherView, b: CipherView) => number) {} isCached(url: string) { return this.sortedCiphersByUrl.has(url); } addCiphers(url: string, ciphers: CipherView[]) { ciphers.sort(this.comparator); this.sortedCiphersByUrl.set(url, new Ciphers(ciphers)); this.resetTimer(url); } getLastUsed(url: string) { this.resetTimer(url); return this.isCached(url) ? this.sortedCiphersByUrl.get(url).getLastUsed() : null; } getLastLaunched(url: string) { return this.isCached(url) ? this.sortedCiphersByUrl.get(url).getLastLaunched() : null; } getNext(url: string) { this.resetTimer(url); return this.isCached(url) ? this.sortedCiphersByUrl.get(url).getNext() : null; } updateLastUsedIndex(url: string) { if (this.isCached(url)) { this.sortedCiphersByUrl.get(url).updateLastUsedIndex(); } } clear() { this.sortedCiphersByUrl.clear(); this.timeouts.clear(); } private resetTimer(url: string) { clearTimeout(this.timeouts.get(url)); this.timeouts.set( url, setTimeout(() => { this.sortedCiphersByUrl.delete(url); this.timeouts.delete(url); }, CacheTTL) ); } } class Ciphers { lastUsedIndex = -1; constructor(private readonly ciphers: CipherView[]) {} getLastUsed() { this.lastUsedIndex = Math.max(this.lastUsedIndex, 0); return this.ciphers[this.lastUsedIndex]; } getLastLaunched() { const usedCiphers = this.ciphers.filter((cipher) => cipher.localData?.lastLaunched); const sortedCiphers = usedCiphers.sort( (x, y) => y.localData.lastLaunched.valueOf() - x.localData.lastLaunched.valueOf() ); return sortedCiphers[0]; } getNextIndex() { return (this.lastUsedIndex + 1) % this.ciphers.length; } getNext() { return this.ciphers[this.getNextIndex()]; } updateLastUsedIndex() { this.lastUsedIndex = this.getNextIndex(); } }