@@ -65,21 +65,29 @@ class ConfigLoader extends EventEmitter {
65
65
if ( ! fs . existsSync ( this . cacheDir ) ) {
66
66
try {
67
67
fs . mkdirSync ( this . cacheDir , { recursive : true } ) ;
68
+ console . log ( `Created cache directory at ${ this . cacheDir } ` ) ;
68
69
return true ;
69
70
} catch ( err ) {
70
71
console . error ( 'Failed to create cache directory:' , err ) ;
71
72
return false ;
72
73
}
73
74
}
75
+ console . log ( `Using cache directory at ${ this . cacheDir } ` ) ;
74
76
return true ;
75
77
}
76
78
77
79
async start ( ) {
78
80
const { configurationSources } = this . config ;
79
81
if ( ! configurationSources ?. enabled ) {
82
+ console . log ( 'Configuration sources are disabled' ) ;
80
83
return ;
81
84
}
82
85
86
+ console . log ( 'Configuration sources are enabled' ) ;
87
+ console . log (
88
+ `Sources: ${ JSON . stringify ( configurationSources . sources . filter ( ( s ) => s . enabled ) . map ( ( s ) => s . type ) ) } ` ,
89
+ ) ;
90
+
83
91
// Clear any existing interval before starting a new one
84
92
if ( this . reloadTimer ) {
85
93
clearInterval ( this . reloadTimer ) ;
@@ -88,6 +96,9 @@ class ConfigLoader extends EventEmitter {
88
96
89
97
// Start periodic reload if interval is set
90
98
if ( configurationSources . reloadIntervalSeconds > 0 ) {
99
+ console . log (
100
+ `Setting reload interval to ${ configurationSources . reloadIntervalSeconds } seconds` ,
101
+ ) ;
91
102
this . reloadTimer = setInterval (
92
103
( ) => this . reloadConfiguration ( ) ,
93
104
configurationSources . reloadIntervalSeconds * 1000 ,
@@ -106,34 +117,63 @@ class ConfigLoader extends EventEmitter {
106
117
}
107
118
108
119
async reloadConfiguration ( ) {
109
- if ( this . isReloading ) return ;
120
+ if ( this . isReloading ) {
121
+ console . log ( 'Configuration reload already in progress, skipping' ) ;
122
+ return ;
123
+ }
110
124
this . isReloading = true ;
125
+ console . log ( 'Starting configuration reload' ) ;
111
126
112
127
try {
113
128
const { configurationSources } = this . config ;
114
- if ( ! configurationSources ?. enabled ) return ;
129
+ if ( ! configurationSources ?. enabled ) {
130
+ console . log ( 'Configuration sources are disabled, skipping reload' ) ;
131
+ return ;
132
+ }
133
+
134
+ const enabledSources = configurationSources . sources . filter ( ( source ) => source . enabled ) ;
135
+ console . log ( `Found ${ enabledSources . length } enabled configuration sources` ) ;
115
136
116
137
const configs = await Promise . all (
117
- configurationSources . sources
118
- . filter ( ( source ) => source . enabled )
119
- . map ( ( source ) => this . loadFromSource ( source ) ) ,
138
+ enabledSources . map ( async ( source ) => {
139
+ try {
140
+ console . log ( `Loading configuration from ${ source . type } source` ) ;
141
+ return await this . loadFromSource ( source ) ;
142
+ } catch ( error ) {
143
+ console . error ( `Error loading from ${ source . type } source:` , error . message ) ;
144
+ return null ;
145
+ }
146
+ } ) ,
120
147
) ;
121
148
149
+ // Filter out null results from failed loads
150
+ const validConfigs = configs . filter ( ( config ) => config !== null ) ;
151
+
152
+ if ( validConfigs . length === 0 ) {
153
+ console . log ( 'No valid configurations loaded from any source' ) ;
154
+ return ;
155
+ }
156
+
122
157
// Use merge strategy based on configuration
123
158
const shouldMerge = configurationSources . merge ?? true ; // Default to true for backward compatibility
159
+ console . log ( `Using ${ shouldMerge ? 'merge' : 'override' } strategy for configuration` ) ;
160
+
124
161
const newConfig = shouldMerge
125
- ? configs . reduce (
162
+ ? validConfigs . reduce (
126
163
( acc , curr ) => {
127
164
return this . deepMerge ( acc , curr ) ;
128
165
} ,
129
166
{ ...this . config } ,
130
167
)
131
- : { ...this . config , ...configs [ configs . length - 1 ] } ; // Use last config for override
168
+ : { ...this . config , ...validConfigs [ validConfigs . length - 1 ] } ; // Use last config for override
132
169
133
170
// Emit change event if config changed
134
171
if ( JSON . stringify ( newConfig ) !== JSON . stringify ( this . config ) ) {
172
+ console . log ( 'Configuration has changed, updating and emitting change event' ) ;
135
173
this . config = newConfig ;
136
174
this . emit ( 'configurationChanged' , this . config ) ;
175
+ } else {
176
+ console . log ( 'Configuration has not changed, no update needed' ) ;
137
177
}
138
178
} catch ( error ) {
139
179
console . error ( 'Error reloading configuration:' , error ) ;
@@ -161,11 +201,13 @@ class ConfigLoader extends EventEmitter {
161
201
if ( ! isValidPath ( configPath ) ) {
162
202
throw new Error ( 'Invalid configuration file path' ) ;
163
203
}
204
+ console . log ( `Loading configuration from file: ${ configPath } ` ) ;
164
205
const content = await fs . promises . readFile ( configPath , 'utf8' ) ;
165
206
return JSON . parse ( content ) ;
166
207
}
167
208
168
209
async loadFromHttp ( source ) {
210
+ console . log ( `Loading configuration from HTTP: ${ source . url } ` ) ;
169
211
const headers = {
170
212
...source . headers ,
171
213
...( source . auth ?. type === 'bearer' ? { Authorization : `Bearer ${ source . auth . token } ` } : { } ) ,
@@ -176,6 +218,8 @@ class ConfigLoader extends EventEmitter {
176
218
}
177
219
178
220
async loadFromGit ( source ) {
221
+ console . log ( `Loading configuration from Git: ${ source . repository } ` ) ;
222
+
179
223
// Validate inputs
180
224
if ( ! source . repository || ! isValidGitUrl ( source . repository ) ) {
181
225
throw new Error ( 'Invalid repository URL format' ) ;
@@ -191,15 +235,25 @@ class ConfigLoader extends EventEmitter {
191
235
if ( ! isValidPath ( tempDir ) ) {
192
236
throw new Error ( 'Invalid temporary directory path' ) ;
193
237
}
238
+
239
+ console . log ( `Creating git cache directory at ${ tempDir } ` ) ;
194
240
await fs . promises . mkdir ( tempDir , { recursive : true } ) ;
195
241
196
- const repoDir = path . join ( tempDir , Buffer . from ( source . repository ) . toString ( 'base64' ) ) ;
242
+ // Create a safe directory name from the repository URL
243
+ const repoDirName = Buffer . from ( source . repository )
244
+ . toString ( 'base64' )
245
+ . replace ( / [ ^ a - z A - Z 0 - 9 ] / g, '_' ) ;
246
+ const repoDir = path . join ( tempDir , repoDirName ) ;
247
+
197
248
if ( ! isValidPath ( repoDir ) ) {
198
249
throw new Error ( 'Invalid repository directory path' ) ;
199
250
}
200
251
252
+ console . log ( `Using repository directory: ${ repoDir } ` ) ;
253
+
201
254
// Clone or pull repository
202
255
if ( ! fs . existsSync ( repoDir ) ) {
256
+ console . log ( `Cloning repository ${ source . repository } to ${ repoDir } ` ) ;
203
257
const execOptions = {
204
258
cwd : process . cwd ( ) ,
205
259
env : {
@@ -211,23 +265,57 @@ class ConfigLoader extends EventEmitter {
211
265
: { } ) ,
212
266
} ,
213
267
} ;
214
- await execFileAsync ( 'git' , [ 'clone' , source . repository , repoDir ] , execOptions ) ;
268
+
269
+ try {
270
+ await execFileAsync ( 'git' , [ 'clone' , source . repository , repoDir ] , execOptions ) ;
271
+ console . log ( 'Repository cloned successfully' ) ;
272
+ } catch ( error ) {
273
+ console . error ( 'Failed to clone repository:' , error . message ) ;
274
+ throw new Error ( `Failed to clone repository: ${ error . message } ` ) ;
275
+ }
215
276
} else {
216
- await execFileAsync ( 'git' , [ 'pull' ] , { cwd : repoDir } ) ;
277
+ console . log ( `Pulling latest changes from ${ source . repository } ` ) ;
278
+ try {
279
+ await execFileAsync ( 'git' , [ 'pull' ] , { cwd : repoDir } ) ;
280
+ console . log ( 'Repository pulled successfully' ) ;
281
+ } catch ( error ) {
282
+ console . error ( 'Failed to pull repository:' , error . message ) ;
283
+ throw new Error ( `Failed to pull repository: ${ error . message } ` ) ;
284
+ }
217
285
}
218
286
219
287
// Checkout specific branch if specified
220
288
if ( source . branch ) {
221
- await execFileAsync ( 'git' , [ 'checkout' , source . branch ] , { cwd : repoDir } ) ;
289
+ console . log ( `Checking out branch: ${ source . branch } ` ) ;
290
+ try {
291
+ await execFileAsync ( 'git' , [ 'checkout' , source . branch ] , { cwd : repoDir } ) ;
292
+ console . log ( `Branch ${ source . branch } checked out successfully` ) ;
293
+ } catch ( error ) {
294
+ console . error ( `Failed to checkout branch ${ source . branch } :` , error . message ) ;
295
+ throw new Error ( `Failed to checkout branch ${ source . branch } : ${ error . message } ` ) ;
296
+ }
222
297
}
223
298
224
299
// Read and parse config file
225
300
const configPath = path . join ( repoDir , source . path ) ;
226
301
if ( ! isValidPath ( configPath ) ) {
227
302
throw new Error ( 'Invalid configuration file path in repository' ) ;
228
303
}
229
- const content = await fs . promises . readFile ( configPath , 'utf8' ) ;
230
- return JSON . parse ( content ) ;
304
+
305
+ console . log ( `Reading configuration file: ${ configPath } ` ) ;
306
+ if ( ! fs . existsSync ( configPath ) ) {
307
+ throw new Error ( `Configuration file not found at ${ configPath } ` ) ;
308
+ }
309
+
310
+ try {
311
+ const content = await fs . promises . readFile ( configPath , 'utf8' ) ;
312
+ const config = JSON . parse ( content ) ;
313
+ console . log ( 'Configuration loaded successfully from Git' ) ;
314
+ return config ;
315
+ } catch ( error ) {
316
+ console . error ( 'Failed to read or parse configuration file:' , error . message ) ;
317
+ throw new Error ( `Failed to read or parse configuration file: ${ error . message } ` ) ;
318
+ }
231
319
}
232
320
233
321
deepMerge ( target , source ) {
@@ -249,6 +337,7 @@ class ConfigLoader extends EventEmitter {
249
337
}
250
338
}
251
339
340
+ // Helper function to check if a value is an object
252
341
function isObject ( item ) {
253
342
return item && typeof item === 'object' && ! Array . isArray ( item ) ;
254
343
}
0 commit comments