@morojs/moro 1.5.5 → 1.5.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/config/config-manager.d.ts +44 -0
- package/dist/core/config/config-manager.js +114 -0
- package/dist/core/config/config-manager.js.map +1 -0
- package/dist/core/config/config-sources.d.ts +21 -0
- package/dist/core/config/config-sources.js +314 -0
- package/dist/core/config/config-sources.js.map +1 -0
- package/dist/core/config/config-validator.d.ts +21 -0
- package/dist/core/config/config-validator.js +744 -0
- package/dist/core/config/config-validator.js.map +1 -0
- package/dist/core/config/file-loader.d.ts +0 -5
- package/dist/core/config/file-loader.js +0 -171
- package/dist/core/config/file-loader.js.map +1 -1
- package/dist/core/config/index.d.ts +39 -10
- package/dist/core/config/index.js +66 -29
- package/dist/core/config/index.js.map +1 -1
- package/dist/core/config/schema.js +29 -31
- package/dist/core/config/schema.js.map +1 -1
- package/dist/core/config/utils.d.ts +9 -2
- package/dist/core/config/utils.js +19 -32
- package/dist/core/config/utils.js.map +1 -1
- package/dist/core/framework.d.ts +4 -7
- package/dist/core/framework.js +38 -12
- package/dist/core/framework.js.map +1 -1
- package/dist/core/http/http-server.d.ts +12 -0
- package/dist/core/http/http-server.js +56 -0
- package/dist/core/http/http-server.js.map +1 -1
- package/dist/core/http/router.d.ts +12 -0
- package/dist/core/http/router.js +114 -36
- package/dist/core/http/router.js.map +1 -1
- package/dist/core/logger/index.d.ts +1 -1
- package/dist/core/logger/index.js +2 -1
- package/dist/core/logger/index.js.map +1 -1
- package/dist/core/logger/logger.d.ts +9 -1
- package/dist/core/logger/logger.js +36 -3
- package/dist/core/logger/logger.js.map +1 -1
- package/dist/core/routing/index.d.ts +20 -0
- package/dist/core/routing/index.js +109 -11
- package/dist/core/routing/index.js.map +1 -1
- package/dist/moro.d.ts +7 -20
- package/dist/moro.js +115 -200
- package/dist/moro.js.map +1 -1
- package/dist/types/config.d.ts +46 -2
- package/dist/types/core.d.ts +22 -39
- package/dist/types/logger.d.ts +4 -0
- package/package.json +1 -1
- package/src/core/config/config-manager.ts +133 -0
- package/src/core/config/config-sources.ts +384 -0
- package/src/core/config/config-validator.ts +1042 -0
- package/src/core/config/file-loader.ts +0 -233
- package/src/core/config/index.ts +77 -32
- package/src/core/config/schema.ts +29 -31
- package/src/core/config/utils.ts +22 -29
- package/src/core/framework.ts +51 -18
- package/src/core/http/http-server.ts +66 -0
- package/src/core/http/router.ts +127 -38
- package/src/core/logger/index.ts +1 -0
- package/src/core/logger/logger.ts +43 -4
- package/src/core/routing/index.ts +116 -12
- package/src/moro.ts +127 -233
- package/src/types/config.ts +47 -2
- package/src/types/core.ts +32 -43
- package/src/types/logger.ts +6 -0
- package/dist/core/config/loader.d.ts +0 -7
- package/dist/core/config/loader.js +0 -269
- package/dist/core/config/loader.js.map +0 -1
- package/dist/core/config/validation.d.ts +0 -17
- package/dist/core/config/validation.js +0 -131
- package/dist/core/config/validation.js.map +0 -1
- package/src/core/config/loader.ts +0 -633
- package/src/core/config/validation.ts +0 -140
|
@@ -172,236 +172,3 @@ async function setupTypeScriptLoader(): Promise<void> {
|
|
|
172
172
|
// the TypeScript transpilation is already handled by those tools.
|
|
173
173
|
logger.debug('TypeScript config loading delegated to runtime environment');
|
|
174
174
|
}
|
|
175
|
-
|
|
176
|
-
/**
|
|
177
|
-
* Convert a configuration object to environment variable mappings
|
|
178
|
-
* This function flattens the config object and sets corresponding environment variables
|
|
179
|
-
*/
|
|
180
|
-
export function applyConfigAsEnvironmentVariables(config: Partial<AppConfig>): void {
|
|
181
|
-
if (!config || typeof config !== 'object') {
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Apply server configuration
|
|
186
|
-
if (config.server) {
|
|
187
|
-
setEnvIfNotSet('PORT', config.server.port?.toString());
|
|
188
|
-
setEnvIfNotSet('HOST', config.server.host);
|
|
189
|
-
setEnvIfNotSet('NODE_ENV', config.server.environment);
|
|
190
|
-
setEnvIfNotSet('MAX_CONNECTIONS', config.server.maxConnections?.toString());
|
|
191
|
-
setEnvIfNotSet('REQUEST_TIMEOUT', config.server.timeout?.toString());
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
// Apply database configuration
|
|
195
|
-
if (config.database) {
|
|
196
|
-
setEnvIfNotSet('DATABASE_URL', config.database.url);
|
|
197
|
-
|
|
198
|
-
if (config.database.redis) {
|
|
199
|
-
setEnvIfNotSet('REDIS_URL', config.database.redis.url);
|
|
200
|
-
setEnvIfNotSet('REDIS_MAX_RETRIES', config.database.redis.maxRetries?.toString());
|
|
201
|
-
setEnvIfNotSet('REDIS_RETRY_DELAY', config.database.redis.retryDelay?.toString());
|
|
202
|
-
setEnvIfNotSet('REDIS_KEY_PREFIX', config.database.redis.keyPrefix);
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
if (config.database.mysql) {
|
|
206
|
-
setEnvIfNotSet('MYSQL_HOST', config.database.mysql.host);
|
|
207
|
-
setEnvIfNotSet('MYSQL_PORT', config.database.mysql.port?.toString());
|
|
208
|
-
setEnvIfNotSet('MYSQL_DATABASE', config.database.mysql.database);
|
|
209
|
-
setEnvIfNotSet('MYSQL_USERNAME', config.database.mysql.username);
|
|
210
|
-
setEnvIfNotSet('MYSQL_PASSWORD', config.database.mysql.password);
|
|
211
|
-
setEnvIfNotSet('MYSQL_CONNECTION_LIMIT', config.database.mysql.connectionLimit?.toString());
|
|
212
|
-
setEnvIfNotSet('MYSQL_ACQUIRE_TIMEOUT', config.database.mysql.acquireTimeout?.toString());
|
|
213
|
-
setEnvIfNotSet('MYSQL_TIMEOUT', config.database.mysql.timeout?.toString());
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Apply service discovery configuration
|
|
218
|
-
if (config.serviceDiscovery) {
|
|
219
|
-
setEnvIfNotSet('SERVICE_DISCOVERY_ENABLED', config.serviceDiscovery.enabled?.toString());
|
|
220
|
-
setEnvIfNotSet('DISCOVERY_TYPE', config.serviceDiscovery.type);
|
|
221
|
-
setEnvIfNotSet('CONSUL_URL', config.serviceDiscovery.consulUrl);
|
|
222
|
-
setEnvIfNotSet('K8S_NAMESPACE', config.serviceDiscovery.kubernetesNamespace);
|
|
223
|
-
setEnvIfNotSet(
|
|
224
|
-
'HEALTH_CHECK_INTERVAL',
|
|
225
|
-
config.serviceDiscovery.healthCheckInterval?.toString()
|
|
226
|
-
);
|
|
227
|
-
setEnvIfNotSet('DISCOVERY_RETRY_ATTEMPTS', config.serviceDiscovery.retryAttempts?.toString());
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Apply logging configuration
|
|
231
|
-
if (config.logging) {
|
|
232
|
-
setEnvIfNotSet('LOG_LEVEL', config.logging.level);
|
|
233
|
-
setEnvIfNotSet('LOG_FORMAT', config.logging.format);
|
|
234
|
-
setEnvIfNotSet('LOG_COLORS', config.logging.enableColors?.toString());
|
|
235
|
-
setEnvIfNotSet('LOG_TIMESTAMP', config.logging.enableTimestamp?.toString());
|
|
236
|
-
setEnvIfNotSet('LOG_CONTEXT', config.logging.enableContext?.toString());
|
|
237
|
-
|
|
238
|
-
if (config.logging.outputs) {
|
|
239
|
-
setEnvIfNotSet('LOG_CONSOLE', config.logging.outputs.console?.toString());
|
|
240
|
-
|
|
241
|
-
if (config.logging.outputs.file) {
|
|
242
|
-
setEnvIfNotSet('LOG_FILE_ENABLED', config.logging.outputs.file.enabled?.toString());
|
|
243
|
-
setEnvIfNotSet('LOG_FILE_PATH', config.logging.outputs.file.path);
|
|
244
|
-
setEnvIfNotSet('LOG_FILE_MAX_SIZE', config.logging.outputs.file.maxSize);
|
|
245
|
-
setEnvIfNotSet('LOG_FILE_MAX_FILES', config.logging.outputs.file.maxFiles?.toString());
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
if (config.logging.outputs.webhook) {
|
|
249
|
-
setEnvIfNotSet('LOG_WEBHOOK_ENABLED', config.logging.outputs.webhook.enabled?.toString());
|
|
250
|
-
setEnvIfNotSet('LOG_WEBHOOK_URL', config.logging.outputs.webhook.url);
|
|
251
|
-
if (config.logging.outputs.webhook.headers) {
|
|
252
|
-
setEnvIfNotSet(
|
|
253
|
-
'LOG_WEBHOOK_HEADERS',
|
|
254
|
-
JSON.stringify(config.logging.outputs.webhook.headers)
|
|
255
|
-
);
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// Apply module defaults
|
|
262
|
-
if (config.modules) {
|
|
263
|
-
if (config.modules.cache) {
|
|
264
|
-
setEnvIfNotSet('CACHE_ENABLED', config.modules.cache.enabled?.toString());
|
|
265
|
-
setEnvIfNotSet('DEFAULT_CACHE_TTL', config.modules.cache.defaultTtl?.toString());
|
|
266
|
-
setEnvIfNotSet('CACHE_MAX_SIZE', config.modules.cache.maxSize?.toString());
|
|
267
|
-
setEnvIfNotSet('CACHE_STRATEGY', config.modules.cache.strategy);
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
if (config.modules.rateLimit) {
|
|
271
|
-
setEnvIfNotSet('RATE_LIMIT_ENABLED', config.modules.rateLimit.enabled?.toString());
|
|
272
|
-
setEnvIfNotSet(
|
|
273
|
-
'DEFAULT_RATE_LIMIT_REQUESTS',
|
|
274
|
-
config.modules.rateLimit.defaultRequests?.toString()
|
|
275
|
-
);
|
|
276
|
-
setEnvIfNotSet(
|
|
277
|
-
'DEFAULT_RATE_LIMIT_WINDOW',
|
|
278
|
-
config.modules.rateLimit.defaultWindow?.toString()
|
|
279
|
-
);
|
|
280
|
-
setEnvIfNotSet(
|
|
281
|
-
'RATE_LIMIT_SKIP_SUCCESS',
|
|
282
|
-
config.modules.rateLimit.skipSuccessfulRequests?.toString()
|
|
283
|
-
);
|
|
284
|
-
setEnvIfNotSet(
|
|
285
|
-
'RATE_LIMIT_SKIP_FAILED',
|
|
286
|
-
config.modules.rateLimit.skipFailedRequests?.toString()
|
|
287
|
-
);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
if (config.modules.validation) {
|
|
291
|
-
setEnvIfNotSet('VALIDATION_ENABLED', config.modules.validation.enabled?.toString());
|
|
292
|
-
setEnvIfNotSet(
|
|
293
|
-
'VALIDATION_STRIP_UNKNOWN',
|
|
294
|
-
config.modules.validation.stripUnknown?.toString()
|
|
295
|
-
);
|
|
296
|
-
setEnvIfNotSet('VALIDATION_ABORT_EARLY', config.modules.validation.abortEarly?.toString());
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
// Apply security configuration
|
|
301
|
-
if (config.security) {
|
|
302
|
-
if (config.security.cors) {
|
|
303
|
-
setEnvIfNotSet('CORS_ENABLED', config.security.cors.enabled?.toString());
|
|
304
|
-
if (typeof config.security.cors.origin === 'string') {
|
|
305
|
-
setEnvIfNotSet('CORS_ORIGIN', config.security.cors.origin);
|
|
306
|
-
} else if (Array.isArray(config.security.cors.origin)) {
|
|
307
|
-
setEnvIfNotSet('CORS_ORIGIN', config.security.cors.origin.join(','));
|
|
308
|
-
} else if (typeof config.security.cors.origin === 'boolean') {
|
|
309
|
-
setEnvIfNotSet('CORS_ORIGIN', config.security.cors.origin.toString());
|
|
310
|
-
}
|
|
311
|
-
setEnvIfNotSet('CORS_METHODS', config.security.cors.methods?.join(','));
|
|
312
|
-
setEnvIfNotSet('CORS_HEADERS', config.security.cors.allowedHeaders?.join(','));
|
|
313
|
-
setEnvIfNotSet('CORS_CREDENTIALS', config.security.cors.credentials?.toString());
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
if (config.security.helmet) {
|
|
317
|
-
setEnvIfNotSet('HELMET_ENABLED', config.security.helmet.enabled?.toString());
|
|
318
|
-
setEnvIfNotSet('HELMET_CSP', config.security.helmet.contentSecurityPolicy?.toString());
|
|
319
|
-
setEnvIfNotSet('HELMET_HSTS', config.security.helmet.hsts?.toString());
|
|
320
|
-
setEnvIfNotSet('HELMET_NO_SNIFF', config.security.helmet.noSniff?.toString());
|
|
321
|
-
setEnvIfNotSet('HELMET_FRAMEGUARD', config.security.helmet.frameguard?.toString());
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
if (config.security.rateLimit?.global) {
|
|
325
|
-
setEnvIfNotSet(
|
|
326
|
-
'GLOBAL_RATE_LIMIT_ENABLED',
|
|
327
|
-
config.security.rateLimit.global.enabled?.toString()
|
|
328
|
-
);
|
|
329
|
-
setEnvIfNotSet(
|
|
330
|
-
'GLOBAL_RATE_LIMIT_REQUESTS',
|
|
331
|
-
config.security.rateLimit.global.requests?.toString()
|
|
332
|
-
);
|
|
333
|
-
setEnvIfNotSet(
|
|
334
|
-
'GLOBAL_RATE_LIMIT_WINDOW',
|
|
335
|
-
config.security.rateLimit.global.window?.toString()
|
|
336
|
-
);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
// Apply external services configuration
|
|
341
|
-
if (config.external) {
|
|
342
|
-
if (config.external.stripe) {
|
|
343
|
-
setEnvIfNotSet('STRIPE_SECRET_KEY', config.external.stripe.secretKey);
|
|
344
|
-
setEnvIfNotSet('STRIPE_PUBLISHABLE_KEY', config.external.stripe.publishableKey);
|
|
345
|
-
setEnvIfNotSet('STRIPE_WEBHOOK_SECRET', config.external.stripe.webhookSecret);
|
|
346
|
-
setEnvIfNotSet('STRIPE_API_VERSION', config.external.stripe.apiVersion);
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (config.external.paypal) {
|
|
350
|
-
setEnvIfNotSet('PAYPAL_CLIENT_ID', config.external.paypal.clientId);
|
|
351
|
-
setEnvIfNotSet('PAYPAL_CLIENT_SECRET', config.external.paypal.clientSecret);
|
|
352
|
-
setEnvIfNotSet('PAYPAL_WEBHOOK_ID', config.external.paypal.webhookId);
|
|
353
|
-
setEnvIfNotSet('PAYPAL_ENVIRONMENT', config.external.paypal.environment);
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
if (config.external.smtp) {
|
|
357
|
-
setEnvIfNotSet('SMTP_HOST', config.external.smtp.host);
|
|
358
|
-
setEnvIfNotSet('SMTP_PORT', config.external.smtp.port?.toString());
|
|
359
|
-
setEnvIfNotSet('SMTP_SECURE', config.external.smtp.secure?.toString());
|
|
360
|
-
setEnvIfNotSet('SMTP_USERNAME', config.external.smtp.username);
|
|
361
|
-
setEnvIfNotSet('SMTP_PASSWORD', config.external.smtp.password);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// Apply performance configuration
|
|
366
|
-
if (config.performance) {
|
|
367
|
-
if (config.performance.compression) {
|
|
368
|
-
setEnvIfNotSet('COMPRESSION_ENABLED', config.performance.compression.enabled?.toString());
|
|
369
|
-
setEnvIfNotSet('COMPRESSION_LEVEL', config.performance.compression.level?.toString());
|
|
370
|
-
setEnvIfNotSet('COMPRESSION_THRESHOLD', config.performance.compression.threshold?.toString());
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
if (config.performance.circuitBreaker) {
|
|
374
|
-
setEnvIfNotSet(
|
|
375
|
-
'CIRCUIT_BREAKER_ENABLED',
|
|
376
|
-
config.performance.circuitBreaker.enabled?.toString()
|
|
377
|
-
);
|
|
378
|
-
setEnvIfNotSet(
|
|
379
|
-
'CIRCUIT_BREAKER_THRESHOLD',
|
|
380
|
-
config.performance.circuitBreaker.failureThreshold?.toString()
|
|
381
|
-
);
|
|
382
|
-
setEnvIfNotSet(
|
|
383
|
-
'CIRCUIT_BREAKER_RESET',
|
|
384
|
-
config.performance.circuitBreaker.resetTimeout?.toString()
|
|
385
|
-
);
|
|
386
|
-
setEnvIfNotSet(
|
|
387
|
-
'CIRCUIT_BREAKER_MONITOR',
|
|
388
|
-
config.performance.circuitBreaker.monitoringPeriod?.toString()
|
|
389
|
-
);
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
if (config.performance.clustering) {
|
|
393
|
-
setEnvIfNotSet('CLUSTERING_ENABLED', config.performance.clustering.enabled?.toString());
|
|
394
|
-
setEnvIfNotSet('CLUSTER_WORKERS', config.performance.clustering.workers?.toString());
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
/**
|
|
400
|
-
* Set environment variable only if it's not already set
|
|
401
|
-
* This ensures environment variables take precedence over config file values
|
|
402
|
-
*/
|
|
403
|
-
function setEnvIfNotSet(key: string, value: string | undefined): void {
|
|
404
|
-
if (value !== undefined && process.env[key] === undefined) {
|
|
405
|
-
process.env[key] = value;
|
|
406
|
-
}
|
|
407
|
-
}
|
package/src/core/config/index.ts
CHANGED
|
@@ -1,60 +1,105 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Configuration System - Immutable Config with createApp Override Support
|
|
3
|
+
*
|
|
4
|
+
* This is the main entry point for the MoroJS configuration system.
|
|
5
|
+
* It provides a clean, immutable configuration that is locked at createApp() time.
|
|
6
|
+
*
|
|
7
|
+
* Key Features:
|
|
8
|
+
* - Immutable configuration after initialization
|
|
9
|
+
* - Clear precedence: Env Vars > createApp Options > Config File > Defaults
|
|
10
|
+
* - Type-safe validation
|
|
11
|
+
* - Single source of truth
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
// Export types and core components
|
|
2
15
|
export * from './schema';
|
|
3
|
-
export * from './
|
|
4
|
-
export * from './
|
|
16
|
+
export * from './config-sources';
|
|
17
|
+
export * from './config-validator';
|
|
5
18
|
export * from './file-loader';
|
|
6
19
|
|
|
7
|
-
//
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
20
|
+
// Export specific functions from config-manager to avoid conflicts
|
|
21
|
+
export { initializeAndLockConfig, isConfigLocked, resetConfigForTesting } from './config-manager';
|
|
22
|
+
|
|
23
|
+
// Export utilities for backward compatibility
|
|
24
|
+
export * from './utils';
|
|
25
|
+
|
|
26
|
+
import { MoroOptions } from '../../types/core';
|
|
27
|
+
import { AppConfig } from '../../types/config';
|
|
28
|
+
import { loadConfigFromAllSources } from './config-sources';
|
|
29
|
+
import {
|
|
30
|
+
initializeAndLockConfig,
|
|
31
|
+
getGlobalConfig as getConfig,
|
|
32
|
+
isConfigLocked,
|
|
33
|
+
resetConfigForTesting,
|
|
34
|
+
} from './config-manager';
|
|
35
|
+
import { createFrameworkLogger } from '../logger';
|
|
11
36
|
|
|
12
|
-
|
|
13
|
-
let globalConfig: AppConfig | null = null;
|
|
37
|
+
const logger = createFrameworkLogger('ConfigSystem');
|
|
14
38
|
|
|
15
39
|
/**
|
|
16
|
-
* Initialize
|
|
17
|
-
* This
|
|
40
|
+
* Initialize configuration system with createApp options
|
|
41
|
+
* This is the main entry point called by createApp()
|
|
42
|
+
*
|
|
43
|
+
* @param options - createApp options that can override config file and defaults
|
|
44
|
+
* @returns Immutable, validated configuration object
|
|
18
45
|
*/
|
|
19
|
-
export function initializeConfig(): AppConfig {
|
|
20
|
-
if (
|
|
21
|
-
|
|
46
|
+
export function initializeConfig(options?: MoroOptions): Readonly<AppConfig> {
|
|
47
|
+
if (isConfigLocked()) {
|
|
48
|
+
logger.debug('Configuration already locked, returning existing config');
|
|
49
|
+
return getConfig();
|
|
22
50
|
}
|
|
23
51
|
|
|
24
|
-
|
|
52
|
+
logger.debug('Initializing configuration system');
|
|
53
|
+
|
|
54
|
+
// Load configuration from all sources with proper precedence
|
|
55
|
+
const config = loadConfigFromAllSources(options);
|
|
25
56
|
|
|
26
|
-
//
|
|
27
|
-
|
|
57
|
+
// Lock the configuration to prevent further changes
|
|
58
|
+
initializeAndLockConfig(config);
|
|
28
59
|
|
|
29
|
-
|
|
60
|
+
logger.info(
|
|
61
|
+
`Configuration system initialized and locked: ${process.env.NODE_ENV || 'development'}:${config.server.port} (sources: env + file + options + defaults)`
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
return config;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Load configuration without locking (for testing and utilities)
|
|
69
|
+
* This maintains backward compatibility with existing code
|
|
70
|
+
*/
|
|
71
|
+
export function loadConfig(): AppConfig {
|
|
72
|
+
return loadConfigFromAllSources();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Load configuration with createApp options (for testing and utilities)
|
|
77
|
+
* This maintains backward compatibility with existing code
|
|
78
|
+
*/
|
|
79
|
+
export function loadConfigWithOptions(options: MoroOptions): AppConfig {
|
|
80
|
+
return loadConfigFromAllSources(options);
|
|
30
81
|
}
|
|
31
82
|
|
|
32
83
|
/**
|
|
33
84
|
* Get the current global configuration
|
|
34
|
-
*
|
|
85
|
+
* Alias for getGlobalConfig() for backward compatibility
|
|
35
86
|
*/
|
|
36
|
-
export function getGlobalConfig(): AppConfig {
|
|
37
|
-
|
|
38
|
-
throw new Error('Configuration not initialized. Call initializeConfig() first.');
|
|
39
|
-
}
|
|
40
|
-
return globalConfig;
|
|
87
|
+
export function getGlobalConfig(): Readonly<AppConfig> {
|
|
88
|
+
return getConfig();
|
|
41
89
|
}
|
|
42
90
|
|
|
43
91
|
/**
|
|
44
|
-
* Check if configuration has been initialized
|
|
92
|
+
* Check if configuration has been initialized and locked
|
|
93
|
+
* Alias for isConfigLocked() for backward compatibility
|
|
45
94
|
*/
|
|
46
95
|
export function isConfigInitialized(): boolean {
|
|
47
|
-
return
|
|
96
|
+
return isConfigLocked();
|
|
48
97
|
}
|
|
49
98
|
|
|
50
99
|
/**
|
|
51
|
-
* Reset
|
|
100
|
+
* Reset configuration state (for testing only)
|
|
52
101
|
* @internal
|
|
53
102
|
*/
|
|
54
103
|
export function resetConfig(): void {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
// Also reset the utils config (by setting it to null via direct access)
|
|
58
|
-
const { setConfig } = require('./utils');
|
|
59
|
-
setConfig(null as any);
|
|
104
|
+
resetConfigForTesting();
|
|
60
105
|
}
|
|
@@ -2,14 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
import { AppConfig } from '../../types/config';
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// Minimal default configuration - performance-focused, most things opt-in
|
|
6
6
|
export const DEFAULT_CONFIG: AppConfig = {
|
|
7
7
|
server: {
|
|
8
8
|
port: 3001,
|
|
9
9
|
host: 'localhost',
|
|
10
|
-
environment: 'development',
|
|
11
10
|
maxConnections: 1000,
|
|
12
11
|
timeout: 30000,
|
|
12
|
+
bodySizeLimit: '10mb',
|
|
13
|
+
requestTracking: {
|
|
14
|
+
enabled: true, // Enable by default for debugging
|
|
15
|
+
},
|
|
16
|
+
errorBoundary: {
|
|
17
|
+
enabled: true, // Always enabled for safety
|
|
18
|
+
},
|
|
13
19
|
},
|
|
14
20
|
serviceDiscovery: {
|
|
15
21
|
enabled: false,
|
|
@@ -19,30 +25,23 @@ export const DEFAULT_CONFIG: AppConfig = {
|
|
|
19
25
|
healthCheckInterval: 30000,
|
|
20
26
|
retryAttempts: 3,
|
|
21
27
|
},
|
|
22
|
-
database: {
|
|
23
|
-
redis: {
|
|
24
|
-
url: 'redis://localhost:6379',
|
|
25
|
-
maxRetries: 3,
|
|
26
|
-
retryDelay: 1000,
|
|
27
|
-
keyPrefix: 'moro:',
|
|
28
|
-
},
|
|
29
|
-
},
|
|
28
|
+
database: {},
|
|
30
29
|
modules: {
|
|
31
30
|
cache: {
|
|
32
|
-
enabled:
|
|
31
|
+
enabled: false, // Opt-in for better performance
|
|
33
32
|
defaultTtl: 300,
|
|
34
33
|
maxSize: 1000,
|
|
35
34
|
strategy: 'lru',
|
|
36
35
|
},
|
|
37
36
|
rateLimit: {
|
|
38
|
-
enabled:
|
|
37
|
+
enabled: false, // Opt-in to avoid unnecessary overhead
|
|
39
38
|
defaultRequests: 100,
|
|
40
39
|
defaultWindow: 60000,
|
|
41
40
|
skipSuccessfulRequests: false,
|
|
42
41
|
skipFailedRequests: false,
|
|
43
42
|
},
|
|
44
43
|
validation: {
|
|
45
|
-
enabled:
|
|
44
|
+
enabled: false,
|
|
46
45
|
stripUnknown: true,
|
|
47
46
|
abortEarly: false,
|
|
48
47
|
},
|
|
@@ -69,14 +68,14 @@ export const DEFAULT_CONFIG: AppConfig = {
|
|
|
69
68
|
},
|
|
70
69
|
security: {
|
|
71
70
|
cors: {
|
|
72
|
-
enabled:
|
|
71
|
+
enabled: false, // Opt-in for better performance
|
|
73
72
|
origin: '*',
|
|
74
73
|
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'],
|
|
75
74
|
allowedHeaders: ['Content-Type', 'Authorization'],
|
|
76
75
|
credentials: false,
|
|
77
76
|
},
|
|
78
77
|
helmet: {
|
|
79
|
-
enabled:
|
|
78
|
+
enabled: false, // Opt-in for better performance
|
|
80
79
|
contentSecurityPolicy: true,
|
|
81
80
|
hsts: true,
|
|
82
81
|
noSniff: true,
|
|
@@ -90,26 +89,15 @@ export const DEFAULT_CONFIG: AppConfig = {
|
|
|
90
89
|
},
|
|
91
90
|
},
|
|
92
91
|
},
|
|
93
|
-
external: {
|
|
94
|
-
stripe: {
|
|
95
|
-
apiVersion: '2023-10-16',
|
|
96
|
-
},
|
|
97
|
-
paypal: {
|
|
98
|
-
environment: 'sandbox',
|
|
99
|
-
},
|
|
100
|
-
smtp: {
|
|
101
|
-
port: 587,
|
|
102
|
-
secure: false,
|
|
103
|
-
},
|
|
104
|
-
},
|
|
92
|
+
external: {},
|
|
105
93
|
performance: {
|
|
106
94
|
compression: {
|
|
107
|
-
enabled:
|
|
95
|
+
enabled: false, // Opt-in to avoid overhead
|
|
108
96
|
level: 6,
|
|
109
97
|
threshold: 1024,
|
|
110
98
|
},
|
|
111
99
|
circuitBreaker: {
|
|
112
|
-
enabled:
|
|
100
|
+
enabled: false, // Opt-in to avoid overhead
|
|
113
101
|
failureThreshold: 5,
|
|
114
102
|
resetTimeout: 60000,
|
|
115
103
|
monitoringPeriod: 10000,
|
|
@@ -117,13 +105,23 @@ export const DEFAULT_CONFIG: AppConfig = {
|
|
|
117
105
|
clustering: {
|
|
118
106
|
enabled: false,
|
|
119
107
|
workers: 1,
|
|
108
|
+
memoryPerWorkerGB: undefined,
|
|
120
109
|
},
|
|
121
110
|
},
|
|
111
|
+
websocket: {
|
|
112
|
+
enabled: false, // Opt-in - user must explicitly enable WebSockets
|
|
113
|
+
},
|
|
122
114
|
};
|
|
123
115
|
|
|
124
|
-
//
|
|
116
|
+
// Schema validation is now handled by config-validator.ts
|
|
117
|
+
// This export is kept for backward compatibility only
|
|
118
|
+
// Note: For actual validation, use validateConfig() from config-validator.ts directly
|
|
125
119
|
export const ConfigSchema = {
|
|
126
|
-
parse: (data: any): AppConfig =>
|
|
120
|
+
parse: (data: any): AppConfig => {
|
|
121
|
+
// Simple pass-through for backward compatibility
|
|
122
|
+
// Real validation happens in the config loading pipeline
|
|
123
|
+
return data as AppConfig;
|
|
124
|
+
},
|
|
127
125
|
};
|
|
128
126
|
|
|
129
127
|
// Re-export types for backward compatibility
|
package/src/core/config/utils.ts
CHANGED
|
@@ -1,28 +1,26 @@
|
|
|
1
1
|
// Configuration Utilities for Modules and Environment Handling
|
|
2
2
|
import { AppConfig } from './schema';
|
|
3
3
|
import { createFrameworkLogger } from '../logger';
|
|
4
|
+
import { getGlobalConfig } from './config-manager';
|
|
4
5
|
|
|
5
6
|
const logger = createFrameworkLogger('ConfigUtils');
|
|
6
7
|
|
|
7
|
-
// Global configuration store
|
|
8
|
-
let appConfig: AppConfig | null = null;
|
|
9
|
-
|
|
10
8
|
/**
|
|
11
|
-
* Set the global configuration (
|
|
9
|
+
* Set the global configuration (deprecated - for backward compatibility only)
|
|
10
|
+
* @deprecated Use the new immutable config system instead
|
|
12
11
|
*/
|
|
13
|
-
export function setConfig(
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
export function setConfig(_config: AppConfig): void {
|
|
13
|
+
logger.warn(
|
|
14
|
+
'setConfig() is deprecated. Configuration is now immutable after createApp() initialization.'
|
|
15
|
+
);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Get the global configuration
|
|
20
|
+
* This now delegates to the new config manager
|
|
20
21
|
*/
|
|
21
22
|
export function getConfig(): AppConfig {
|
|
22
|
-
|
|
23
|
-
throw new Error('Configuration not initialized. Call loadConfig() first.');
|
|
24
|
-
}
|
|
25
|
-
return appConfig;
|
|
23
|
+
return getGlobalConfig();
|
|
26
24
|
}
|
|
27
25
|
|
|
28
26
|
/**
|
|
@@ -63,6 +61,7 @@ function coerceEnvironmentValue(value: string): any {
|
|
|
63
61
|
|
|
64
62
|
/**
|
|
65
63
|
* Create module-specific configuration with environment override support
|
|
64
|
+
* This now uses the new immutable config system
|
|
66
65
|
*/
|
|
67
66
|
export function createModuleConfig<T>(
|
|
68
67
|
schema: { parse: (data: any) => T },
|
|
@@ -72,10 +71,12 @@ export function createModuleConfig<T>(
|
|
|
72
71
|
// Try to get global config, but don't fail if not initialized
|
|
73
72
|
let globalConfig = {};
|
|
74
73
|
try {
|
|
75
|
-
const { getGlobalConfig } = require('./index');
|
|
76
74
|
globalConfig = getGlobalConfig();
|
|
77
75
|
} catch {
|
|
78
76
|
// Global config not initialized - use empty object (module config can still work independently)
|
|
77
|
+
logger.debug(
|
|
78
|
+
`Global config not available for module config with prefix ${envPrefix}, using defaults only`
|
|
79
|
+
);
|
|
79
80
|
globalConfig = {};
|
|
80
81
|
}
|
|
81
82
|
|
|
@@ -104,7 +105,7 @@ export function createModuleConfig<T>(
|
|
|
104
105
|
// Priority: environment variables > global config > default config
|
|
105
106
|
const mergedConfig = {
|
|
106
107
|
...defaultConfig,
|
|
107
|
-
...globalConfig, // Now
|
|
108
|
+
...globalConfig, // Now uses the new immutable config system
|
|
108
109
|
...envConfig,
|
|
109
110
|
};
|
|
110
111
|
|
|
@@ -208,9 +209,10 @@ export function envVar(prefix: string, name: string): string {
|
|
|
208
209
|
|
|
209
210
|
/**
|
|
210
211
|
* Get configuration value with dot notation
|
|
212
|
+
* This now delegates to the new config manager
|
|
211
213
|
*/
|
|
212
214
|
export function getConfigValue(path: string): any {
|
|
213
|
-
const config =
|
|
215
|
+
const config = getGlobalConfig();
|
|
214
216
|
|
|
215
217
|
return path.split('.').reduce((obj, key) => {
|
|
216
218
|
return obj && obj[key] !== undefined ? obj[key] : undefined;
|
|
@@ -219,33 +221,24 @@ export function getConfigValue(path: string): any {
|
|
|
219
221
|
|
|
220
222
|
/**
|
|
221
223
|
* Check if we're in development environment
|
|
224
|
+
* Now reads NODE_ENV directly for consistency with Node.js ecosystem
|
|
222
225
|
*/
|
|
223
226
|
export function isDevelopment(): boolean {
|
|
224
|
-
|
|
225
|
-
return getConfig().server.environment === 'development';
|
|
226
|
-
} catch {
|
|
227
|
-
return process.env.NODE_ENV === 'development';
|
|
228
|
-
}
|
|
227
|
+
return process.env.NODE_ENV === 'development';
|
|
229
228
|
}
|
|
230
229
|
|
|
231
230
|
/**
|
|
232
231
|
* Check if we're in production environment
|
|
232
|
+
* Now reads NODE_ENV directly for consistency with Node.js ecosystem
|
|
233
233
|
*/
|
|
234
234
|
export function isProduction(): boolean {
|
|
235
|
-
|
|
236
|
-
return getConfig().server.environment === 'production';
|
|
237
|
-
} catch {
|
|
238
|
-
return process.env.NODE_ENV === 'production';
|
|
239
|
-
}
|
|
235
|
+
return process.env.NODE_ENV === 'production';
|
|
240
236
|
}
|
|
241
237
|
|
|
242
238
|
/**
|
|
243
239
|
* Check if we're in staging environment
|
|
240
|
+
* Now reads NODE_ENV directly for consistency with Node.js ecosystem
|
|
244
241
|
*/
|
|
245
242
|
export function isStaging(): boolean {
|
|
246
|
-
|
|
247
|
-
return getConfig().server.environment === 'staging';
|
|
248
|
-
} catch {
|
|
249
|
-
return process.env.NODE_ENV === 'staging';
|
|
250
|
-
}
|
|
243
|
+
return process.env.NODE_ENV === 'staging';
|
|
251
244
|
}
|