import * as plugins from './smartupdate.plugins.js'; import type { ICacheStatus, ICacheOptions } from './smartupdate.interfaces.js'; /** * Manages caching of update check results */ export class UpdateCacheManager { private kvStore: plugins.npmextra.KeyValueStore; private cacheDurationMs: number; constructor(options: ICacheOptions) { this.cacheDurationMs = options.durationMs; this.kvStore = new plugins.npmextra.KeyValueStore({ typeArg: 'userHomeDir', identityArg: options.storeIdentifier || 'global_smartupdate', }); } /** * Get the cache duration in milliseconds */ public getCacheDuration(): number { return this.cacheDurationMs; } /** * Get cached status for a package */ public async getCached(packageName: string): Promise { return await this.kvStore.readKey(packageName); } /** * Set cache status for a package */ public async setCached(packageName: string, status: ICacheStatus): Promise { await this.kvStore.writeKey(packageName, status); } /** * Clear cache for a specific package or all packages */ public async clearCache(packageName?: string): Promise { if (packageName) { await this.kvStore.deleteKey(packageName); } else { // Clear all keys - this requires reading all keys first // For now, we'll skip implementing full cache clear as it requires more kvStore API throw new Error('Clearing all cache entries is not yet implemented'); } } /** * Check if we should check the registry or use cache * @returns Object with shouldCheck flag and optional nextCheckTime */ public async shouldCheckRegistry( packageName: string, strategy: 'always' | 'never' | 'time-based' = 'time-based' ): Promise<{ shouldCheck: boolean; cacheStatus?: ICacheStatus; nextCheckTime?: Date; minutesUntilNextCheck?: number; }> { // Never use cache if (strategy === 'never') { return { shouldCheck: true }; } // Get cached data const cacheStatus = await this.getCached(packageName); // No cache exists if (!cacheStatus) { return { shouldCheck: true }; } // Always use cache if available if (strategy === 'always') { return { shouldCheck: false, cacheStatus }; } // Time-based strategy: check if cache is still valid const now = Date.now(); const lastCheckTime = cacheStatus.lastCheck; const timeSinceLastCheck = now - lastCheckTime; // Cache is still valid if (timeSinceLastCheck < this.cacheDurationMs) { const nextCheckTime = new Date(lastCheckTime + this.cacheDurationMs); const minutesUntilNextCheck = (this.cacheDurationMs - timeSinceLastCheck) / 60000; return { shouldCheck: false, cacheStatus, nextCheckTime, minutesUntilNextCheck, }; } // Cache is expired return { shouldCheck: true, cacheStatus }; } /** * Create a new cache status object */ public createCacheStatus(latestVersion: string, performedUpgrade: boolean = false): ICacheStatus { return { lastCheck: Date.now(), latestVersion, performedUpgrade, }; } }