Files
paseo/packages/website/src/github-cache.ts
T
Mohamed BoudraandGitHub 319d9017c2 Publish the latest Android version code (#2085)
* feat(website): publish latest Android version code

* fix(website): select latest Android APK release
2026-07-15 21:38:52 +08:00

73 lines
1.7 KiB
TypeScript

export const GITHUB_CACHE_TTL_MS = 5 * 60 * 1000;
export interface WebsiteCacheContext {
cache: KVNamespace | null;
waitUntil: (promise: Promise<unknown>) => void;
}
interface CachedValue<T> {
fetchedAt: number;
value: T;
}
type Validator<T> = (value: unknown) => value is T;
function isCachedValue<T>(value: unknown, isValue: Validator<T>): value is CachedValue<T> {
if (typeof value !== "object" || value === null) return false;
const record = value as Record<string, unknown>;
return typeof record.fetchedAt === "number" && isValue(record.value);
}
async function readCachedValue<T>(
cache: KVNamespace | null,
key: string,
isValue: Validator<T>,
): Promise<CachedValue<T> | null> {
if (!cache) return null;
const cached = await cache.get(key, { cacheTtl: 60, type: "json" });
return isCachedValue(cached, isValue) ? cached : null;
}
async function writeCachedValue<T>(
cache: KVNamespace | null,
key: string,
value: T,
): Promise<void> {
if (!cache) return;
await cache.put(
key,
JSON.stringify({
fetchedAt: Date.now(),
value,
}),
);
}
export async function getBlockingColdCache<T>({
context,
key,
isValue,
fetchFresh,
}: {
context: WebsiteCacheContext;
key: string;
isValue: Validator<T>;
fetchFresh: () => Promise<T>;
}): Promise<T> {
const cached = await readCachedValue(context.cache, key, isValue);
if (cached) {
if (Date.now() - cached.fetchedAt > GITHUB_CACHE_TTL_MS) {
context.waitUntil(
fetchFresh()
.then((fresh) => writeCachedValue(context.cache, key, fresh))
.catch(() => undefined),
);
}
return cached.value;
}
const fresh = await fetchFresh();
await writeCachedValue(context.cache, key, fresh);
return fresh;
}