@@ -5,8 +5,9 @@ import { execFile } from 'child_process';
55import { promisify } from 'util' ;
66import { EventEmitter } from 'events' ;
77import envPaths from 'env-paths' ;
8- import { GitProxyConfig , Convert } from './generated/config' ;
8+ import { GitProxyConfig } from './generated/config' ;
99import { Configuration , ConfigurationSource , FileSource , HttpSource , GitSource } from './types' ;
10+ import { loadConfig , validateConfig } from './validators' ;
1011
1112const execFileAsync = promisify ( execFile ) ;
1213
@@ -148,7 +149,7 @@ export class ConfigLoader extends EventEmitter {
148149 ) ;
149150 console . log ( `Found ${ enabledSources . length } enabled configuration sources` ) ;
150151
151- const configs = await Promise . all (
152+ const loadedConfigs = await Promise . all (
152153 enabledSources . map ( async ( source : ConfigurationSource ) => {
153154 try {
154155 console . log ( `Loading configuration from ${ source . type } source` ) ;
@@ -161,10 +162,12 @@ export class ConfigLoader extends EventEmitter {
161162 ) ;
162163
163164 // Filter out null results from failed loads
164- const validConfigs = configs . filter ( ( config ) : config is GitProxyConfig => config !== null ) ;
165+ const nonNullConfigs = loadedConfigs . filter (
166+ ( config ) : config is GitProxyConfig => config !== null ,
167+ ) ;
165168
166- if ( validConfigs . length === 0 ) {
167- console . log ( 'No valid configurations loaded from any source ' ) ;
169+ if ( nonNullConfigs . length === 0 ) {
170+ console . log ( 'All loaded configurations are empty, skipping reload ' ) ;
168171 return ;
169172 }
170173
@@ -173,15 +176,20 @@ export class ConfigLoader extends EventEmitter {
173176 console . log ( `Using ${ shouldMerge ? 'merge' : 'override' } strategy for configuration` ) ;
174177
175178 const newConfig = shouldMerge
176- ? validConfigs . reduce (
179+ ? nonNullConfigs . reduce (
177180 ( acc , curr ) => {
178181 return this . deepMerge ( acc , curr ) as Configuration ;
179182 } ,
180183 { ...this . config } ,
181184 )
182- : { ...this . config , ...validConfigs [ validConfigs . length - 1 ] } ; // Use last config for override
185+ : { ...this . config , ...nonNullConfigs [ nonNullConfigs . length - 1 ] } ; // Use last config for override
186+
187+ if ( ! validateConfig ( newConfig ) ) {
188+ console . error ( 'Invalid configuration, skipping reload' ) ;
189+ return ;
190+ }
183191
184- // Emit change event if config changed
192+ // Emit change event if config changed and is valid
185193 if ( JSON . stringify ( newConfig ) !== JSON . stringify ( this . config ) ) {
186194 console . log ( 'Configuration has changed, updating and emitting change event' ) ;
187195 this . config = newConfig ;
@@ -218,16 +226,7 @@ export class ConfigLoader extends EventEmitter {
218226 throw new Error ( 'Invalid configuration file path' ) ;
219227 }
220228 console . log ( `Loading configuration from file: ${ configPath } ` ) ;
221- const content = await fs . promises . readFile ( configPath , 'utf8' ) ;
222-
223- // Use QuickType to validate and parse the configuration
224- try {
225- return Convert . toGitProxyConfig ( content ) ;
226- } catch ( error ) {
227- throw new Error (
228- `Invalid configuration file format: ${ error instanceof Error ? error . message : 'Unknown error' } ` ,
229- ) ;
230- }
229+ return loadConfig ( `file: ${ configPath } ` , async ( ) => fs . promises . readFile ( configPath , 'utf8' ) ) ;
231230 }
232231
233232 async loadFromHttp ( source : HttpSource ) : Promise < GitProxyConfig > {
@@ -237,18 +236,10 @@ export class ConfigLoader extends EventEmitter {
237236 ...( source . auth ?. type === 'bearer' ? { Authorization : `Bearer ${ source . auth . token } ` } : { } ) ,
238237 } ;
239238
240- const response = await axios . get ( source . url , { headers } ) ;
241-
242- // Use QuickType to validate and parse the configuration from HTTP response
243- try {
244- const configJson =
245- typeof response . data === 'string' ? response . data : JSON . stringify ( response . data ) ;
246- return Convert . toGitProxyConfig ( configJson ) ;
247- } catch ( error ) {
248- throw new Error (
249- `Invalid configuration format from HTTP source: ${ error instanceof Error ? error . message : 'Unknown error' } ` ,
250- ) ;
251- }
239+ return loadConfig ( `HTTP: ${ source . url } ` , async ( ) => {
240+ const response = await axios . get ( source . url , { headers } ) ;
241+ return typeof response . data === 'string' ? response . data : JSON . stringify ( response . data ) ;
242+ } ) ;
252243 }
253244
254245 async loadFromGit ( source : GitSource ) : Promise < GitProxyConfig > {
@@ -344,17 +335,7 @@ export class ConfigLoader extends EventEmitter {
344335 throw new Error ( `Configuration file not found at ${ configPath } ` ) ;
345336 }
346337
347- try {
348- const content = await fs . promises . readFile ( configPath , 'utf8' ) ;
349-
350- // Use QuickType to validate and parse the configuration from Git
351- const config = Convert . toGitProxyConfig ( content ) ;
352- console . log ( 'Configuration loaded successfully from Git' ) ;
353- return config ;
354- } catch ( error : any ) {
355- console . error ( 'Failed to read or parse configuration file:' , error . message ) ;
356- throw new Error ( `Failed to read or parse configuration file: ${ error . message } ` ) ;
357- }
338+ return loadConfig ( `git: ${ configPath } ` , async ( ) => fs . promises . readFile ( configPath , 'utf8' ) ) ;
358339 }
359340
360341 deepMerge ( target : Record < string , any > , source : Record < string , any > ) : Record < string , any > {
0 commit comments