152 lines
4.1 KiB
TypeScript
152 lines
4.1 KiB
TypeScript
import * as plugins from './smartupdate.plugins.js';
|
|
import type { ICacheStatus, ICacheOptions, TCacheStrategy, TCachedUpdateStatus } from './smartupdate.interfaces.js';
|
|
|
|
type TCacheStoreData = Record<string, ICacheStatus>;
|
|
|
|
/**
|
|
* Manages caching of update check results
|
|
*/
|
|
export class UpdateCacheManager {
|
|
public readonly kvStore: plugins.npmextra.KeyValueStore<TCacheStoreData>;
|
|
private cacheDurationMs: number;
|
|
|
|
constructor(options: ICacheOptions) {
|
|
this.cacheDurationMs = options.durationMs;
|
|
this.kvStore = new plugins.npmextra.KeyValueStore<TCacheStoreData>({
|
|
typeArg: options.storeType || 'userHomeDir',
|
|
identityArg: options.storeIdentifier || 'global_smartupdate',
|
|
customPath: options.customPath,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Get the cache duration in milliseconds
|
|
*/
|
|
public getCacheDuration(): number {
|
|
return this.cacheDurationMs;
|
|
}
|
|
|
|
/**
|
|
* Get cached status for a package
|
|
*/
|
|
public async getCached(packageName: string): Promise<ICacheStatus | null> {
|
|
return (await this.kvStore.readKey(packageName)) || null;
|
|
}
|
|
|
|
/**
|
|
* Set cache status for a package
|
|
*/
|
|
public async setCached(packageName: string, status: ICacheStatus): Promise<void> {
|
|
await this.kvStore.writeKey(packageName, status);
|
|
}
|
|
|
|
/**
|
|
* Clear cache for a specific package or all packages
|
|
*/
|
|
public async clearCache(packageName?: string): Promise<void> {
|
|
if (packageName) {
|
|
const cacheData = await this.kvStore.readAll();
|
|
delete cacheData[packageName];
|
|
await this.kvStore.wipe();
|
|
await this.kvStore.writeAll(cacheData);
|
|
} else {
|
|
await this.kvStore.wipe();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if we should check the registry or use cache
|
|
* @returns Object with shouldCheck flag and optional nextCheckTime
|
|
*/
|
|
public async shouldCheckRegistry(
|
|
packageName: string,
|
|
strategy: TCacheStrategy = 'time-based',
|
|
cacheContext: {
|
|
currentVersion?: string;
|
|
registryUrl?: string;
|
|
} = {}
|
|
): 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 || !this.cacheMatchesContext(cacheStatus, cacheContext)) {
|
|
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,
|
|
metadata: {
|
|
currentVersion?: string;
|
|
registryUrl?: string;
|
|
status?: TCachedUpdateStatus;
|
|
} = {}
|
|
): ICacheStatus {
|
|
return {
|
|
lastCheck: Date.now(),
|
|
latestVersion,
|
|
currentVersion: metadata.currentVersion,
|
|
registryUrl: metadata.registryUrl,
|
|
status: metadata.status,
|
|
performedUpgrade,
|
|
};
|
|
}
|
|
|
|
private cacheMatchesContext(
|
|
cacheStatus: ICacheStatus,
|
|
cacheContext: {
|
|
currentVersion?: string;
|
|
registryUrl?: string;
|
|
}
|
|
): boolean {
|
|
if (cacheContext.currentVersion && cacheStatus.currentVersion !== cacheContext.currentVersion) {
|
|
return false;
|
|
}
|
|
if (cacheContext.registryUrl && cacheStatus.registryUrl !== cacheContext.registryUrl) {
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|