-
Notifications
You must be signed in to change notification settings - Fork 2.1k
/
metadata-cache.ts
62 lines (55 loc) · 1.86 KB
/
metadata-cache.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import hasha from 'hasha';
import * as packageCache from '../../../util/cache/package';
import type { Http } from '../../../util/http';
import { AsyncResult, Result } from '../../../util/result';
import { parseUrl } from '../../../util/url';
import type { ReleaseResult } from '../types';
import { getV1Releases } from './common';
interface CacheRecord {
hash: string;
data: ReleaseResult;
}
export class MetadataCache {
constructor(private readonly http: Http) {}
async getRelease(
registryUrl: string,
packageName: string,
versions: string[]
): Promise<ReleaseResult> {
const cacheNs = `datasource-rubygems`;
const cacheKey = `metadata-cache:${registryUrl}:${packageName}`;
const hash = hasha(versions, { algorithm: 'sha256' });
const loadCache = (): AsyncResult<ReleaseResult, unknown> =>
Result.wrapNullable(
packageCache.get<CacheRecord>(cacheNs, cacheKey),
'cache-not-found'
).transform((cache) => {
return hash === cache.hash
? Result.ok(cache.data)
: Result.err('cache-outdated');
});
const saveCache = async (data: ReleaseResult): Promise<ReleaseResult> => {
const registryHostname = parseUrl(registryUrl)?.hostname;
if (registryHostname === 'rubygems.org') {
const newCache: CacheRecord = { hash, data };
const ttlMinutes = 100 * 24 * 60;
const ttlRandomDelta = Math.floor(Math.random() * 10 * 24 * 60);
await packageCache.set(
cacheNs,
cacheKey,
newCache,
ttlMinutes + ttlRandomDelta
);
}
return data;
};
return await loadCache()
.catch(() =>
getV1Releases(this.http, registryUrl, packageName).transform(saveCache)
)
.catch(() =>
Result.ok({ releases: versions.map((version) => ({ version })) })
)
.unwrapOrThrow();
}
}