@@ -3,61 +3,67 @@ import * as path from 'path';
33import { promisify } from 'util' ;
44
55import * as getBrowserLauncherCb from '@httptoolkit/browser-launcher' ;
6- import { LaunchOptions , Launch , BrowserInstance , Browser } from '@httptoolkit/browser-launcher' ;
6+ import {
7+ LaunchOptions ,
8+ Launch ,
9+ BrowserInstance ,
10+ Browser ,
11+ update as updateBrowserCacheCb
12+ } from '@httptoolkit/browser-launcher' ;
713
814import { reportError } from './error-tracking' ;
9- import { readFile , statFile , deleteFile } from './util' ;
15+ import { readFile , deleteFile , delay } from './util' ;
1016
1117const getBrowserLauncher = promisify ( getBrowserLauncherCb ) ;
18+ const updateBrowserCache : ( configPath : string ) => Promise < unknown > = promisify ( updateBrowserCacheCb ) ;
1219
1320const browserConfigPath = ( configPath : string ) => path . join ( configPath , 'browsers.json' ) ;
1421
1522export { BrowserInstance , Browser } ;
1623
1724export async function checkBrowserConfig ( configPath : string ) {
1825 // It's not clear why, but sometimes the browser config can become corrupted, so it's not valid JSON
19- // If that happens JBL doesn't catch it, so we crash . To avoid that, we check it here on startup.
26+ // If that happens browser-launcher can hit issues . To avoid that entirely , we check it here on startup.
2027
2128 const browserConfig = browserConfigPath ( configPath ) ;
22- return Promise . all ( [
23- // Check the file is readable and parseable
24- readFile ( browserConfig , 'utf8' )
25- . then ( ( contents ) => JSON . parse ( contents ) ) ,
26-
27- // Check the file is relatively recent
28- statFile ( browserConfig )
29- . then ( ( stats ) => {
30- if ( Date . now ( ) - stats . mtime . valueOf ( ) > 1000 * 60 * 60 * 24 ) {
31- return deleteFile ( browserConfig ) . catch ( ( err ) => {
32- console . error ( 'Failed to clear outdated config file' ) ;
33- reportError ( err ) ;
34- } ) ;
35- } ;
36- } )
37- ] )
38- . catch ( ( error ) => {
29+
30+ try {
31+ const rawConfig = await readFile ( browserConfig , 'utf8' ) ;
32+ JSON . parse ( rawConfig ) ;
33+ } catch ( error ) {
3934 if ( error . code === 'ENOENT' ) return ;
35+ console . warn ( `Failed to read browser config cache from ${ browserConfig } , clearing.` , error ) ;
4036
41- console . warn ( 'Failed to read browser config on startup' , error ) ;
4237 return deleteFile ( browserConfig ) . catch ( ( err ) => {
43- // We can have a race if the file was invalid AND old, which case we can get a parse error
44- // and have deleted the file already. Regardless: it's now gone, so we're all good.
38+ // There may be possible races around here - as long as the file's gone, we're happy
4539 if ( err . code === 'ENOENT' ) return ;
46-
4740 console . error ( 'Failed to clear broken config file:' , err ) ;
4841 reportError ( err ) ;
4942 } ) ;
50- } )
43+ }
5144}
5245
5346let launcher : Promise < Launch > | undefined ;
5447
5548function getLauncher ( configPath : string ) {
5649 if ( ! launcher ) {
57- const start = Date . now ( ) ;
58- console . log ( 'Getting launcher...' ) ;
59- launcher = getBrowserLauncher ( browserConfigPath ( configPath ) ) ;
60- launcher . then ( ( ) => console . log ( `Got launcher, took ${ Date . now ( ) - start } ms` ) ) ;
50+ const browserConfig = browserConfigPath ( configPath ) ;
51+ launcher = getBrowserLauncher ( browserConfig ) ;
52+
53+ launcher . then ( async ( ) => {
54+ // Async after first creating the launcher, we trigger a background cache update.
55+ // This can be *synchronously* expensive (spawns 10s of procs, 10+ms sync per
56+ // spawn on unix-based OSs) so defer briefly.
57+ await delay ( 2000 ) ;
58+ try {
59+ await updateBrowserCache ( browserConfig ) ;
60+ console . log ( 'Browser cache updated' ) ;
61+ // Need to reload the launcher after updating the cache:
62+ launcher = getBrowserLauncher ( browserConfig ) ;
63+ } catch ( e ) {
64+ reportError ( e )
65+ }
66+ } ) ;
6167
6268 // Reset & retry if this fails somehow:
6369 launcher . catch ( ( e ) => {
0 commit comments