@@ -88,10 +88,9 @@ const NUM_SUPPORTED_MAJORS = 4;
88
88
export class BaseVersions implements Versions {
89
89
private readonly map = new Map < string , SemVer > ( ) ;
90
90
91
- public constructor ( val : unknown ) {
91
+ protected setVersions ( val : unknown ) {
92
92
// build the array
93
93
let parsed : Array < SemVer | null > = [ ] ;
94
-
95
94
if ( isArrayOfVersionObjects ( val ) ) {
96
95
parsed = val . map ( ( { version } ) => semverParse ( version ) ) ;
97
96
} else if ( isArrayOfStrings ( val ) ) {
@@ -101,7 +100,12 @@ export class BaseVersions implements Versions {
101
100
// insert them in sorted order
102
101
const semvers = parsed . filter ( ( sem ) => Boolean ( sem ) ) as SemVer [ ] ;
103
102
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 ) ;
105
109
}
106
110
107
111
public get prereleaseMajors ( ) : number [ ] {
@@ -186,32 +190,112 @@ export class BaseVersions implements Versions {
186
190
* This is generally what to use in production.
187
191
*/
188
192
export class ElectronVersions extends BaseVersions {
189
- private constructor ( values : unknown ) {
193
+ private static readonly freshnessMs = 4 * 60 * 60 * 1000 ; // cache for N hours
194
+
195
+ private constructor (
196
+ private readonly versionsCache : string ,
197
+ private mtimeMs : number ,
198
+ values : unknown ,
199
+ ) {
190
200
super ( values ) ;
191
201
}
192
202
203
+ private static async fetchVersions ( cacheFile : string ) : Promise < unknown > {
204
+ const d = debug ( 'fiddle-core:ElectronVersions:fetchVersions' ) ;
205
+ const url = 'https://releases.electronjs.org/releases.json' ;
206
+ d ( 'fetching releases list from' , url ) ;
207
+ const response = await fetch ( url ) ;
208
+ const json = ( await response . json ( ) ) as unknown ;
209
+ await fs . outputJson ( cacheFile , json ) ;
210
+ return json ;
211
+ }
212
+
193
213
public static async create (
194
214
paths : Partial < Paths > = { } ,
195
215
) : Promise < ElectronVersions > {
196
216
const d = debug ( 'fiddle-core:ElectronVersions:create' ) ;
197
217
const { versionsCache } = { ...DefaultPaths , ...paths } ;
218
+
219
+ let versions : unknown ;
220
+ const now = Date . now ( ) ;
198
221
try {
199
222
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 ) ) ;
223
+ if ( st . mtimeMs + ElectronVersions . freshnessMs >= now )
224
+ versions = ( await fs . readJson ( versionsCache ) ) as unknown ;
225
+ } catch ( err ) {
226
+ // cache file missing
227
+ }
228
+
229
+ if ( ! versions ) {
230
+ try {
231
+ versions = await ElectronVersions . fetchVersions ( versionsCache ) ;
232
+ } catch ( err ) {
233
+ d ( 'error fetching versions' , err ) ;
203
234
}
235
+ }
236
+
237
+ return new ElectronVersions ( versionsCache , now , versions ) ;
238
+ }
239
+
240
+ // upate the cache if it's too old
241
+ private async keepFresh ( ) : Promise < void > {
242
+ const d = debug ( 'fiddle-core:ElectronVersions:keepFresh' ) ;
243
+
244
+ // if it's still fresh, do nothing
245
+ const { mtimeMs, versionsCache } = this ;
246
+ const now = Date . now ( ) ;
247
+ if ( mtimeMs + ElectronVersions . freshnessMs >= now ) return ;
248
+
249
+ // update the cache
250
+ try {
251
+ this . mtimeMs = now ;
252
+ const versions = await ElectronVersions . fetchVersions ( versionsCache ) ;
253
+ this . setVersions ( versions ) ;
254
+ d ( `saved "${ versionsCache } "` ) ;
204
255
} catch ( err ) {
205
- // if no cache, fetch from electronjs.org
206
- d ( `unable to stat cache file " ${ versionsCache } "` , err ) ;
256
+ d ( 'error fetching versions' , err ) ;
257
+ this . mtimeMs = mtimeMs ;
207
258
}
259
+ }
208
260
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 ) ;
261
+ public override get prereleaseMajors ( ) : number [ ] {
262
+ void this . keepFresh ( ) ;
263
+ return super . prereleaseMajors ;
264
+ }
265
+ public override get stableMajors ( ) : number [ ] {
266
+ void this . keepFresh ( ) ;
267
+ return super . stableMajors ;
268
+ }
269
+ public override get supportedMajors ( ) : number [ ] {
270
+ void this . keepFresh ( ) ;
271
+ return super . supportedMajors ;
272
+ }
273
+ public override get obsoleteMajors ( ) : number [ ] {
274
+ void this . keepFresh ( ) ;
275
+ return super . obsoleteMajors ;
276
+ }
277
+ public override get versions ( ) : SemVer [ ] {
278
+ void this . keepFresh ( ) ;
279
+ return super . versions ;
280
+ }
281
+ public override get latest ( ) : SemVer | undefined {
282
+ void this . keepFresh ( ) ;
283
+ return super . latest ;
284
+ }
285
+ public override get latestStable ( ) : SemVer | undefined {
286
+ void this . keepFresh ( ) ;
287
+ return super . latestStable ;
288
+ }
289
+ public override isVersion ( ver : SemOrStr ) : boolean {
290
+ void this . keepFresh ( ) ;
291
+ return super . isVersion ( ver ) ;
292
+ }
293
+ public override inMajor ( major : number ) : SemVer [ ] {
294
+ void this . keepFresh ( ) ;
295
+ return super . inMajor ( major ) ;
296
+ }
297
+ public override inRange ( a : SemOrStr , b : SemOrStr ) : SemVer [ ] {
298
+ void this . keepFresh ( ) ;
299
+ return super . inRange ( a , b ) ;
216
300
}
217
301
}
0 commit comments