@@ -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,115 @@ 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 constructor (
194
+ private readonly versionsCache : string ,
195
+ private mtimeMs : number ,
196
+ values : unknown ,
197
+ ) {
190
198
super ( values ) ;
191
199
}
192
200
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
+
193
216
public static async create (
194
217
paths : Partial < Paths > = { } ,
195
218
) : Promise < ElectronVersions > {
196
219
const d = debug ( 'fiddle-core:ElectronVersions:create' ) ;
197
220
const { versionsCache } = { ...DefaultPaths , ...paths } ;
221
+
222
+ let versions : unknown ;
223
+ const now = Date . now ( ) ;
198
224
try {
199
225
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 ) ;
203
237
}
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 } "` ) ;
204
258
} 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 ;
207
261
}
262
+ }
208
263
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 ) ;
216
303
}
217
304
}
0 commit comments