Files
Sebastian Poxhofer 914c64afbd test(linter): zod schema and infered type naming rule (#43736)
* ci: add linting rule for schema and infered types

* refactor: remove usages of Schema and differently named types
2026-06-02 12:07:35 +00:00

115 lines
3.4 KiB
TypeScript

import { isNonEmptyString, isString } from '@sindresorhus/is';
import { GlobalConfig } from '../../../../config/global.ts';
import { logger } from '../../../../logger/index.ts';
import { compressToBase64, decompressFromBase64 } from '../../../compress.ts';
import { hash } from '../../../hash.ts';
import { safeStringify } from '../../../stringify.ts';
import { CACHE_REVISION } from '../common.ts';
import { cleanupHttpCache } from '../http-cache.ts';
import { RepoCacheRecord } from '../schema.ts';
import type { RepoCache, RepoCacheData } from '../types.ts';
export abstract class RepoCacheBase implements RepoCache {
protected platform = GlobalConfig.get('platform');
private oldHash: string | null = null;
private data: RepoCacheData = {};
protected readonly repository: string;
protected readonly fingerprint: string;
protected constructor(repository: string, fingerprint: string) {
this.repository = repository;
this.fingerprint = fingerprint;
}
protected abstract read(): Promise<string | null>;
protected abstract write(data: RepoCacheRecord): Promise<void>;
private static parseData(input: string): RepoCacheData {
const data: RepoCacheData = JSON.parse(input);
// istanbul ignore next
if (data.branches) {
for (const branch of data.branches) {
if (branch.branchFingerprint) {
branch.commitFingerprint = branch.branchFingerprint;
delete branch.branchFingerprint;
}
}
}
return data;
}
private async restore(oldCache: RepoCacheRecord): Promise<void> {
if (oldCache.fingerprint !== this.fingerprint) {
logger.debug('Repository cache fingerprint is invalid');
return;
}
const jsonStr = await decompressFromBase64(oldCache.payload);
this.data = RepoCacheBase.parseData(jsonStr);
this.oldHash = oldCache.hash;
}
async load(): Promise<void> {
try {
const oldCache = await this.read();
if (!isString(oldCache)) {
logger.debug(
`RepoCacheBase.load() - expecting data of type 'string' received '${typeof oldCache}' instead - skipping`,
);
return;
}
if (!isNonEmptyString(oldCache)) {
logger.debug('RepoCacheBase.load() - cache file is empty - skipping');
return;
}
const cacheV13 = RepoCacheRecord.safeParse(oldCache);
if (cacheV13.success) {
await this.restore(cacheV13.data);
logger.debug('Repository cache is restored from revision 13');
return;
}
logger.warn({ err: cacheV13.error }, 'Repository cache is invalid');
} catch (err) /* istanbul ignore next: not easily testable */ {
logger.debug({ err }, 'Error reading repository cache');
}
}
async save(): Promise<void> {
cleanupHttpCache(this.data);
const jsonStr = safeStringify(this.data);
const hashedJsonStr = hash(jsonStr);
if (hashedJsonStr === this.oldHash) {
return;
}
const revision = CACHE_REVISION;
const repository = this.repository;
const fingerprint = this.fingerprint;
const payload = await compressToBase64(jsonStr);
await this.write({
revision,
repository,
fingerprint,
payload,
hash: hashedJsonStr,
});
}
getData(): RepoCacheData {
return this.data;
}
isModified(): boolean | undefined {
if (!this.oldHash) {
return undefined;
}
const jsonStr = safeStringify(this.data);
return hash(jsonStr) !== this.oldHash;
}
}