Skip to content

Commit ef29daf

Browse files
authored
Merge pull request #19 from electron/fix/keep-version-cache-refreshed
fix: keep ElectronVersions' version cache current.
2 parents 7480f56 + 2851efb commit ef29daf

File tree

1 file changed

+103
-16
lines changed

1 file changed

+103
-16
lines changed

src/versions.ts

Lines changed: 103 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,9 @@ const NUM_SUPPORTED_MAJORS = 4;
8888
export class BaseVersions implements Versions {
8989
private readonly map = new Map<string, SemVer>();
9090

91-
public constructor(val: unknown) {
91+
protected setVersions(val: unknown) {
9292
// build the array
9393
let parsed: Array<SemVer | null> = [];
94-
9594
if (isArrayOfVersionObjects(val)) {
9695
parsed = val.map(({ version }) => semverParse(version));
9796
} else if (isArrayOfStrings(val)) {
@@ -101,7 +100,12 @@ export class BaseVersions implements Versions {
101100
// insert them in sorted order
102101
const semvers = parsed.filter((sem) => Boolean(sem)) as SemVer[];
103102
semvers.sort((a, b) => compareVersions(a, b));
104-
this.map = new Map(semvers.map((sem) => [sem.version, sem]));
103+
this.map.clear();
104+
for (const sem of semvers) this.map.set(sem.version, sem);
105+
}
106+
107+
public constructor(versions: unknown) {
108+
this.setVersions(versions);
105109
}
106110

107111
public get prereleaseMajors(): number[] {
@@ -186,32 +190,115 @@ export class BaseVersions implements Versions {
186190
* This is generally what to use in production.
187191
*/
188192
export class ElectronVersions extends BaseVersions {
189-
private constructor(values: unknown) {
193+
private constructor(
194+
private readonly versionsCache: string,
195+
private mtimeMs: number,
196+
values: unknown,
197+
) {
190198
super(values);
191199
}
192200

201+
private static async fetchVersions(cacheFile: string): Promise<unknown> {
202+
const d = debug('fiddle-core:ElectronVersions:fetchVersions');
203+
const url = 'https://releases.electronjs.org/releases.json';
204+
d('fetching releases list from', url);
205+
const response = await fetch(url);
206+
const json = (await response.json()) as unknown;
207+
await fs.outputJson(cacheFile, json);
208+
return json;
209+
}
210+
211+
private static isCacheFresh(cacheTimeMs: number, now: number): boolean {
212+
const VERSION_CACHE_TTL_MS = 4 * 60 * 60 * 1000; // cache for N hours
213+
return now <= cacheTimeMs + VERSION_CACHE_TTL_MS;
214+
}
215+
193216
public static async create(
194217
paths: Partial<Paths> = {},
195218
): Promise<ElectronVersions> {
196219
const d = debug('fiddle-core:ElectronVersions:create');
197220
const { versionsCache } = { ...DefaultPaths, ...paths };
221+
222+
let versions: unknown;
223+
const now = Date.now();
198224
try {
199225
const st = await fs.stat(versionsCache);
200-
const cacheIntervalMs = 4 * 60 * 60 * 1000; // re-fetch after 4 hours
201-
if (st.mtime.getTime() + cacheIntervalMs > Date.now()) {
202-
return new ElectronVersions(await fs.readJson(versionsCache));
226+
if (ElectronVersions.isCacheFresh(st.mtimeMs, now))
227+
versions = (await fs.readJson(versionsCache)) as unknown;
228+
} catch (err) {
229+
// cache file missing or cannot be read
230+
}
231+
232+
if (!versions) {
233+
try {
234+
versions = await ElectronVersions.fetchVersions(versionsCache);
235+
} catch (err) {
236+
d('error fetching versions', err);
203237
}
238+
}
239+
240+
return new ElectronVersions(versionsCache, now, versions);
241+
}
242+
243+
// upate the cache if it's too old
244+
private async keepFresh(): Promise<void> {
245+
const d = debug('fiddle-core:ElectronVersions:keepFresh');
246+
247+
// if it's still fresh, do nothing
248+
const { mtimeMs, versionsCache } = this;
249+
const now = Date.now();
250+
if (ElectronVersions.isCacheFresh(mtimeMs, now)) return;
251+
252+
// update the cache
253+
try {
254+
this.mtimeMs = now;
255+
const versions = await ElectronVersions.fetchVersions(versionsCache);
256+
this.setVersions(versions);
257+
d(`saved "${versionsCache}"`);
204258
} catch (err) {
205-
// if no cache, fetch from electronjs.org
206-
d(`unable to stat cache file "${versionsCache}"`, err);
259+
d('error fetching versions', err);
260+
this.mtimeMs = mtimeMs;
207261
}
262+
}
208263

209-
const url = 'https://releases.electronjs.org/releases.json';
210-
d('fetching releases list from', url);
211-
const response = await fetch(url);
212-
const json = (await response.json()) as unknown;
213-
await fs.outputJson(versionsCache, json);
214-
d(`saved "${versionsCache}"`);
215-
return new ElectronVersions(json);
264+
public override get prereleaseMajors(): number[] {
265+
void this.keepFresh();
266+
return super.prereleaseMajors;
267+
}
268+
public override get stableMajors(): number[] {
269+
void this.keepFresh();
270+
return super.stableMajors;
271+
}
272+
public override get supportedMajors(): number[] {
273+
void this.keepFresh();
274+
return super.supportedMajors;
275+
}
276+
public override get obsoleteMajors(): number[] {
277+
void this.keepFresh();
278+
return super.obsoleteMajors;
279+
}
280+
public override get versions(): SemVer[] {
281+
void this.keepFresh();
282+
return super.versions;
283+
}
284+
public override get latest(): SemVer | undefined {
285+
void this.keepFresh();
286+
return super.latest;
287+
}
288+
public override get latestStable(): SemVer | undefined {
289+
void this.keepFresh();
290+
return super.latestStable;
291+
}
292+
public override isVersion(ver: SemOrStr): boolean {
293+
void this.keepFresh();
294+
return super.isVersion(ver);
295+
}
296+
public override inMajor(major: number): SemVer[] {
297+
void this.keepFresh();
298+
return super.inMajor(major);
299+
}
300+
public override inRange(a: SemOrStr, b: SemOrStr): SemVer[] {
301+
void this.keepFresh();
302+
return super.inRange(a, b);
216303
}
217304
}

0 commit comments

Comments
 (0)