/* Spacebar: A FOSS re-implementation and extension of the Discord.com backend. Copyright (C) 2026 Spacebar and Spacebar Contributors This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import { TimeSpan } from "./Timespan"; import { sleep } from "./Sleep"; export class SingletonCache { private expiry: TimeSpan; private lastUpdated: Date = new Date(0); private isLocked: boolean = false; private cachedValue: T; constructor(expiry: TimeSpan) { this.expiry = expiry; } async getOrUpdate(factory: () => Promise): Promise { if (new TimeSpan(this.lastUpdated.getTime(), Date.now()).totalMillis < this.expiry.totalMillis) return this.cachedValue; if (this.isLocked) { // avoid running the factory twice (async) while (this.isLocked) await sleep(50); return this.cachedValue; } this.isLocked = true; try { const result = await factory(); this.cachedValue = result; return result; } catch (e) { console.error(`[SingletonCache] Factory method failed, returning stale value:`, e); return this.cachedValue; } finally { this.isLocked = false; } } }