create-fluxstack 1.0.0

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.
Files changed (101) hide show
  1. package/.env +30 -0
  2. package/LICENSE +21 -0
  3. package/README.md +214 -0
  4. package/app/client/README.md +69 -0
  5. package/app/client/frontend-only.ts +12 -0
  6. package/app/client/index.html +13 -0
  7. package/app/client/public/vite.svg +1 -0
  8. package/app/client/src/App.css +883 -0
  9. package/app/client/src/App.tsx +669 -0
  10. package/app/client/src/assets/react.svg +1 -0
  11. package/app/client/src/components/TestPage.tsx +453 -0
  12. package/app/client/src/index.css +51 -0
  13. package/app/client/src/lib/eden-api.ts +110 -0
  14. package/app/client/src/main.tsx +10 -0
  15. package/app/client/src/vite-env.d.ts +1 -0
  16. package/app/client/tsconfig.app.json +43 -0
  17. package/app/client/tsconfig.json +7 -0
  18. package/app/client/tsconfig.node.json +25 -0
  19. package/app/server/app.ts +10 -0
  20. package/app/server/backend-only.ts +15 -0
  21. package/app/server/controllers/users.controller.ts +69 -0
  22. package/app/server/index.ts +104 -0
  23. package/app/server/routes/index.ts +25 -0
  24. package/app/server/routes/users.routes.ts +121 -0
  25. package/app/server/types/index.ts +1 -0
  26. package/app/shared/types/index.ts +18 -0
  27. package/bun.lock +1053 -0
  28. package/core/__tests__/integration.test.ts +227 -0
  29. package/core/build/index.ts +186 -0
  30. package/core/cli/command-registry.ts +334 -0
  31. package/core/cli/index.ts +394 -0
  32. package/core/cli/plugin-discovery.ts +200 -0
  33. package/core/client/standalone.ts +57 -0
  34. package/core/config/__tests__/config-loader.test.ts +591 -0
  35. package/core/config/__tests__/config-merger.test.ts +657 -0
  36. package/core/config/__tests__/env-converter.test.ts +372 -0
  37. package/core/config/__tests__/env-processor.test.ts +431 -0
  38. package/core/config/__tests__/env.test.ts +452 -0
  39. package/core/config/__tests__/integration.test.ts +418 -0
  40. package/core/config/__tests__/loader.test.ts +331 -0
  41. package/core/config/__tests__/schema.test.ts +129 -0
  42. package/core/config/__tests__/validator.test.ts +318 -0
  43. package/core/config/env-dynamic.ts +326 -0
  44. package/core/config/env.ts +597 -0
  45. package/core/config/index.ts +317 -0
  46. package/core/config/loader.ts +546 -0
  47. package/core/config/runtime-config.ts +322 -0
  48. package/core/config/schema.ts +694 -0
  49. package/core/config/validator.ts +540 -0
  50. package/core/framework/__tests__/server.test.ts +233 -0
  51. package/core/framework/client.ts +132 -0
  52. package/core/framework/index.ts +8 -0
  53. package/core/framework/server.ts +501 -0
  54. package/core/framework/types.ts +63 -0
  55. package/core/plugins/__tests__/built-in.test.ts.disabled +366 -0
  56. package/core/plugins/__tests__/manager.test.ts +398 -0
  57. package/core/plugins/__tests__/monitoring.test.ts +401 -0
  58. package/core/plugins/__tests__/registry.test.ts +335 -0
  59. package/core/plugins/built-in/index.ts +142 -0
  60. package/core/plugins/built-in/logger/index.ts +180 -0
  61. package/core/plugins/built-in/monitoring/README.md +193 -0
  62. package/core/plugins/built-in/monitoring/index.ts +912 -0
  63. package/core/plugins/built-in/static/index.ts +289 -0
  64. package/core/plugins/built-in/swagger/index.ts +229 -0
  65. package/core/plugins/built-in/vite/index.ts +316 -0
  66. package/core/plugins/config.ts +348 -0
  67. package/core/plugins/discovery.ts +350 -0
  68. package/core/plugins/executor.ts +351 -0
  69. package/core/plugins/index.ts +195 -0
  70. package/core/plugins/manager.ts +583 -0
  71. package/core/plugins/registry.ts +424 -0
  72. package/core/plugins/types.ts +254 -0
  73. package/core/server/framework.ts +123 -0
  74. package/core/server/index.ts +8 -0
  75. package/core/server/plugins/database.ts +182 -0
  76. package/core/server/plugins/logger.ts +47 -0
  77. package/core/server/plugins/swagger.ts +34 -0
  78. package/core/server/standalone.ts +91 -0
  79. package/core/templates/create-project.ts +455 -0
  80. package/core/types/api.ts +169 -0
  81. package/core/types/build.ts +174 -0
  82. package/core/types/config.ts +68 -0
  83. package/core/types/index.ts +127 -0
  84. package/core/types/plugin.ts +94 -0
  85. package/core/utils/__tests__/errors.test.ts +139 -0
  86. package/core/utils/__tests__/helpers.test.ts +297 -0
  87. package/core/utils/__tests__/logger.test.ts +141 -0
  88. package/core/utils/env-runtime-v2.ts +232 -0
  89. package/core/utils/env-runtime.ts +252 -0
  90. package/core/utils/errors/codes.ts +115 -0
  91. package/core/utils/errors/handlers.ts +63 -0
  92. package/core/utils/errors/index.ts +81 -0
  93. package/core/utils/helpers.ts +180 -0
  94. package/core/utils/index.ts +18 -0
  95. package/core/utils/logger/index.ts +161 -0
  96. package/core/utils/logger.ts +106 -0
  97. package/core/utils/monitoring/index.ts +212 -0
  98. package/create-fluxstack.ts +231 -0
  99. package/package.json +43 -0
  100. package/tsconfig.json +51 -0
  101. package/vite.config.ts +42 -0
@@ -0,0 +1,597 @@
1
+ /**
2
+ * Enhanced Environment Configuration System for FluxStack
3
+ * Handles environment variable processing and precedence
4
+ */
5
+
6
+ import type { FluxStackConfig, LogLevel, BuildTarget, LogFormat } from './schema'
7
+
8
+ export interface EnvironmentInfo {
9
+ name: string
10
+ isDevelopment: boolean
11
+ isProduction: boolean
12
+ isTest: boolean
13
+ nodeEnv: string
14
+ }
15
+
16
+ export interface ConfigPrecedence {
17
+ source: 'default' | 'file' | 'environment' | 'override'
18
+ path: string
19
+ value: any
20
+ priority: number
21
+ }
22
+
23
+ /**
24
+ * Get current environment information
25
+ */
26
+ export function getEnvironmentInfo(): EnvironmentInfo {
27
+ const nodeEnv = process.env.NODE_ENV || 'development'
28
+
29
+ return {
30
+ name: nodeEnv,
31
+ isDevelopment: nodeEnv === 'development',
32
+ isProduction: nodeEnv === 'production',
33
+ isTest: nodeEnv === 'test',
34
+ nodeEnv
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Environment variable type conversion utilities
40
+ */
41
+ export class EnvConverter {
42
+ static toNumber(value: string | undefined, defaultValue: number): number {
43
+ if (!value) return defaultValue
44
+ const parsed = parseInt(value, 10)
45
+ return isNaN(parsed) ? defaultValue : parsed
46
+ }
47
+
48
+ static toBoolean(value: string | undefined, defaultValue: boolean): boolean {
49
+ if (!value) return defaultValue
50
+ return ['true', '1', 'yes', 'on'].includes(value.toLowerCase())
51
+ }
52
+
53
+ static toArray(value: string | undefined, defaultValue: string[] = []): string[] {
54
+ if (!value) return defaultValue
55
+ return value.split(',').map(v => v.trim()).filter(Boolean)
56
+ }
57
+
58
+ static toLogLevel(value: string | undefined, defaultValue: LogLevel): LogLevel {
59
+ if (!value) return defaultValue
60
+ const level = value.toLowerCase() as LogLevel
61
+ return ['debug', 'info', 'warn', 'error'].includes(level) ? level : defaultValue
62
+ }
63
+
64
+ static toBuildTarget(value: string | undefined, defaultValue: BuildTarget): BuildTarget {
65
+ if (!value) return defaultValue
66
+ const target = value.toLowerCase() as BuildTarget
67
+ return ['bun', 'node', 'docker'].includes(target) ? target : defaultValue
68
+ }
69
+
70
+ static toLogFormat(value: string | undefined, defaultValue: LogFormat): LogFormat {
71
+ if (!value) return defaultValue
72
+ const format = value.toLowerCase() as LogFormat
73
+ return ['json', 'pretty'].includes(format) ? format : defaultValue
74
+ }
75
+
76
+ static toObject<T = any>(value: string | undefined, defaultValue: T): T {
77
+ if (!value) return defaultValue
78
+ try {
79
+ return JSON.parse(value)
80
+ } catch {
81
+ return defaultValue
82
+ }
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Environment variable processor with precedence handling
88
+ */
89
+ export class EnvironmentProcessor {
90
+ private precedenceMap: Map<string, ConfigPrecedence> = new Map()
91
+
92
+ /**
93
+ * Process environment variables with type conversion and precedence tracking
94
+ */
95
+ processEnvironmentVariables(): Partial<FluxStackConfig> {
96
+ const config: any = {}
97
+
98
+ // App configuration
99
+ this.setConfigValue(config, 'app.name',
100
+ process.env.FLUXSTACK_APP_NAME || process.env.APP_NAME, 'string')
101
+ this.setConfigValue(config, 'app.version',
102
+ process.env.FLUXSTACK_APP_VERSION || process.env.APP_VERSION, 'string')
103
+ this.setConfigValue(config, 'app.description',
104
+ process.env.FLUXSTACK_APP_DESCRIPTION || process.env.APP_DESCRIPTION, 'string')
105
+
106
+ // Server configuration
107
+ this.setConfigValue(config, 'server.port',
108
+ process.env.PORT || process.env.FLUXSTACK_PORT, 'number')
109
+ this.setConfigValue(config, 'server.host',
110
+ process.env.HOST || process.env.FLUXSTACK_HOST, 'string')
111
+ this.setConfigValue(config, 'server.apiPrefix',
112
+ process.env.FLUXSTACK_API_PREFIX || process.env.API_PREFIX, 'string')
113
+
114
+ // CORS configuration
115
+ this.setConfigValue(config, 'server.cors.origins',
116
+ process.env.CORS_ORIGINS || process.env.FLUXSTACK_CORS_ORIGINS, 'array')
117
+ this.setConfigValue(config, 'server.cors.methods',
118
+ process.env.CORS_METHODS || process.env.FLUXSTACK_CORS_METHODS, 'array')
119
+ this.setConfigValue(config, 'server.cors.headers',
120
+ process.env.CORS_HEADERS || process.env.FLUXSTACK_CORS_HEADERS, 'array')
121
+ this.setConfigValue(config, 'server.cors.credentials',
122
+ process.env.CORS_CREDENTIALS || process.env.FLUXSTACK_CORS_CREDENTIALS, 'boolean')
123
+ this.setConfigValue(config, 'server.cors.maxAge',
124
+ process.env.CORS_MAX_AGE || process.env.FLUXSTACK_CORS_MAX_AGE, 'number')
125
+
126
+ // Client configuration
127
+ this.setConfigValue(config, 'client.port',
128
+ process.env.VITE_PORT || process.env.CLIENT_PORT || process.env.FLUXSTACK_CLIENT_PORT, 'number')
129
+ this.setConfigValue(config, 'client.proxy.target',
130
+ process.env.VITE_API_URL || process.env.API_URL || process.env.FLUXSTACK_PROXY_TARGET, 'string')
131
+ this.setConfigValue(config, 'client.build.sourceMaps',
132
+ process.env.FLUXSTACK_CLIENT_SOURCEMAPS, 'boolean')
133
+ this.setConfigValue(config, 'client.build.minify',
134
+ process.env.FLUXSTACK_CLIENT_MINIFY, 'boolean')
135
+
136
+ // Build configuration
137
+ this.setConfigValue(config, 'build.target',
138
+ process.env.BUILD_TARGET || process.env.FLUXSTACK_BUILD_TARGET, 'buildTarget')
139
+ this.setConfigValue(config, 'build.outDir',
140
+ process.env.BUILD_OUTDIR || process.env.FLUXSTACK_BUILD_OUTDIR, 'string')
141
+ this.setConfigValue(config, 'build.sourceMaps',
142
+ process.env.BUILD_SOURCEMAPS || process.env.FLUXSTACK_BUILD_SOURCEMAPS, 'boolean')
143
+ this.setConfigValue(config, 'build.clean',
144
+ process.env.BUILD_CLEAN || process.env.FLUXSTACK_BUILD_CLEAN, 'boolean')
145
+
146
+ // Build optimization
147
+ this.setConfigValue(config, 'build.optimization.minify',
148
+ process.env.BUILD_MINIFY || process.env.FLUXSTACK_BUILD_MINIFY, 'boolean')
149
+ this.setConfigValue(config, 'build.optimization.treeshake',
150
+ process.env.BUILD_TREESHAKE || process.env.FLUXSTACK_BUILD_TREESHAKE, 'boolean')
151
+ this.setConfigValue(config, 'build.optimization.compress',
152
+ process.env.BUILD_COMPRESS || process.env.FLUXSTACK_BUILD_COMPRESS, 'boolean')
153
+ this.setConfigValue(config, 'build.optimization.splitChunks',
154
+ process.env.BUILD_SPLIT_CHUNKS || process.env.FLUXSTACK_BUILD_SPLIT_CHUNKS, 'boolean')
155
+ this.setConfigValue(config, 'build.optimization.bundleAnalyzer',
156
+ process.env.BUILD_ANALYZER || process.env.FLUXSTACK_BUILD_ANALYZER, 'boolean')
157
+
158
+ // Logging configuration
159
+ this.setConfigValue(config, 'logging.level',
160
+ process.env.LOG_LEVEL || process.env.FLUXSTACK_LOG_LEVEL, 'logLevel')
161
+ this.setConfigValue(config, 'logging.format',
162
+ process.env.LOG_FORMAT || process.env.FLUXSTACK_LOG_FORMAT, 'logFormat')
163
+
164
+ // Monitoring configuration
165
+ this.setConfigValue(config, 'monitoring.enabled',
166
+ process.env.MONITORING_ENABLED || process.env.FLUXSTACK_MONITORING_ENABLED, 'boolean')
167
+ this.setConfigValue(config, 'monitoring.metrics.enabled',
168
+ process.env.METRICS_ENABLED || process.env.FLUXSTACK_METRICS_ENABLED, 'boolean')
169
+ this.setConfigValue(config, 'monitoring.metrics.collectInterval',
170
+ process.env.METRICS_INTERVAL || process.env.FLUXSTACK_METRICS_INTERVAL, 'number')
171
+ this.setConfigValue(config, 'monitoring.profiling.enabled',
172
+ process.env.PROFILING_ENABLED || process.env.FLUXSTACK_PROFILING_ENABLED, 'boolean')
173
+ this.setConfigValue(config, 'monitoring.profiling.sampleRate',
174
+ process.env.PROFILING_SAMPLE_RATE || process.env.FLUXSTACK_PROFILING_SAMPLE_RATE, 'number')
175
+
176
+ // Database configuration
177
+ this.setConfigValue(config, 'database.url', process.env.DATABASE_URL, 'string')
178
+ this.setConfigValue(config, 'database.host', process.env.DATABASE_HOST, 'string')
179
+ this.setConfigValue(config, 'database.port', process.env.DATABASE_PORT, 'number')
180
+ this.setConfigValue(config, 'database.database', process.env.DATABASE_NAME, 'string')
181
+ this.setConfigValue(config, 'database.user', process.env.DATABASE_USER, 'string')
182
+ this.setConfigValue(config, 'database.password', process.env.DATABASE_PASSWORD, 'string')
183
+ this.setConfigValue(config, 'database.ssl', process.env.DATABASE_SSL, 'boolean')
184
+ this.setConfigValue(config, 'database.poolSize', process.env.DATABASE_POOL_SIZE, 'number')
185
+
186
+ // Auth configuration
187
+ this.setConfigValue(config, 'auth.secret', process.env.JWT_SECRET, 'string')
188
+ this.setConfigValue(config, 'auth.expiresIn', process.env.JWT_EXPIRES_IN, 'string')
189
+ this.setConfigValue(config, 'auth.algorithm', process.env.JWT_ALGORITHM, 'string')
190
+ this.setConfigValue(config, 'auth.issuer', process.env.JWT_ISSUER, 'string')
191
+
192
+ // Email configuration
193
+ this.setConfigValue(config, 'email.host', process.env.SMTP_HOST, 'string')
194
+ this.setConfigValue(config, 'email.port', process.env.SMTP_PORT, 'number')
195
+ this.setConfigValue(config, 'email.user', process.env.SMTP_USER, 'string')
196
+ this.setConfigValue(config, 'email.password', process.env.SMTP_PASSWORD, 'string')
197
+ this.setConfigValue(config, 'email.secure', process.env.SMTP_SECURE, 'boolean')
198
+ this.setConfigValue(config, 'email.from', process.env.SMTP_FROM, 'string')
199
+
200
+ // Storage configuration
201
+ this.setConfigValue(config, 'storage.uploadPath', process.env.UPLOAD_PATH, 'string')
202
+ this.setConfigValue(config, 'storage.maxFileSize', process.env.MAX_FILE_SIZE, 'number')
203
+ this.setConfigValue(config, 'storage.provider', process.env.STORAGE_PROVIDER, 'string')
204
+
205
+ // Plugin configuration
206
+ this.setConfigValue(config, 'plugins.enabled',
207
+ process.env.FLUXSTACK_PLUGINS_ENABLED, 'array')
208
+ this.setConfigValue(config, 'plugins.disabled',
209
+ process.env.FLUXSTACK_PLUGINS_DISABLED, 'array')
210
+
211
+ return this.cleanEmptyObjects(config)
212
+ }
213
+
214
+ private setConfigValue(
215
+ config: any,
216
+ path: string,
217
+ value: string | undefined,
218
+ type: string
219
+ ): void {
220
+ if (value === undefined) return
221
+
222
+ const convertedValue = this.convertValue(value, type)
223
+ if (convertedValue !== undefined) {
224
+ this.setNestedProperty(config, path, convertedValue)
225
+
226
+ // Track precedence
227
+ this.precedenceMap.set(path, {
228
+ source: 'environment',
229
+ path,
230
+ value: convertedValue,
231
+ priority: 3 // Environment variables have high priority
232
+ })
233
+ }
234
+ }
235
+
236
+ private convertValue(value: string, type: string): any {
237
+ switch (type) {
238
+ case 'string':
239
+ return value
240
+ case 'number':
241
+ return EnvConverter.toNumber(value, 0)
242
+ case 'boolean':
243
+ const boolValue = EnvConverter.toBoolean(value, false)
244
+ return boolValue
245
+ case 'array':
246
+ return EnvConverter.toArray(value)
247
+ case 'logLevel':
248
+ return EnvConverter.toLogLevel(value, 'info')
249
+ case 'buildTarget':
250
+ return EnvConverter.toBuildTarget(value, 'bun')
251
+ case 'logFormat':
252
+ return EnvConverter.toLogFormat(value, 'pretty')
253
+ case 'object':
254
+ return EnvConverter.toObject(value, {})
255
+ default:
256
+ return value
257
+ }
258
+ }
259
+
260
+ private setNestedProperty(obj: any, path: string, value: any): void {
261
+ const keys = path.split('.')
262
+ let current = obj
263
+
264
+ for (let i = 0; i < keys.length - 1; i++) {
265
+ const key = keys[i]
266
+ if (!(key in current) || typeof current[key] !== 'object') {
267
+ current[key] = {}
268
+ }
269
+ current = current[key]
270
+ }
271
+
272
+ current[keys[keys.length - 1]] = value
273
+ }
274
+
275
+ private cleanEmptyObjects(obj: any): any {
276
+ if (typeof obj !== 'object' || obj === null) return obj
277
+
278
+ const cleaned: any = {}
279
+
280
+ for (const [key, value] of Object.entries(obj)) {
281
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
282
+ const cleanedValue = this.cleanEmptyObjects(value)
283
+ if (Object.keys(cleanedValue).length > 0) {
284
+ cleaned[key] = cleanedValue
285
+ }
286
+ } else if (value !== undefined && value !== null) {
287
+ cleaned[key] = value
288
+ }
289
+ }
290
+
291
+ return cleaned
292
+ }
293
+
294
+ /**
295
+ * Get precedence information for configuration values
296
+ */
297
+ getPrecedenceInfo(): Map<string, ConfigPrecedence> {
298
+ return new Map(this.precedenceMap)
299
+ }
300
+
301
+ /**
302
+ * Clear precedence tracking
303
+ */
304
+ clearPrecedence(): void {
305
+ this.precedenceMap.clear()
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Configuration merger with precedence handling
311
+ */
312
+ export class ConfigMerger {
313
+ private precedenceOrder = ['default', 'file', 'environment', 'override']
314
+
315
+ /**
316
+ * Merge configurations with precedence handling
317
+ * Higher precedence values override lower ones
318
+ */
319
+ merge(...configs: Array<{ config: Partial<FluxStackConfig>, source: string }>): FluxStackConfig {
320
+ let result: any = {}
321
+ const precedenceMap: Map<string, ConfigPrecedence> = new Map()
322
+
323
+ // Process configs in precedence order
324
+ for (const { config, source } of configs) {
325
+ this.deepMergeWithPrecedence(result, config, source, precedenceMap)
326
+ }
327
+
328
+ return result as FluxStackConfig
329
+ }
330
+
331
+ private deepMergeWithPrecedence(
332
+ target: any,
333
+ source: any,
334
+ sourceName: string,
335
+ precedenceMap: Map<string, ConfigPrecedence>,
336
+ currentPath = ''
337
+ ): void {
338
+ if (!source || typeof source !== 'object') return
339
+
340
+ for (const [key, value] of Object.entries(source)) {
341
+ const fullPath = currentPath ? `${currentPath}.${key}` : key
342
+ const sourcePriority = this.precedenceOrder.indexOf(sourceName)
343
+
344
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
345
+ // Ensure target has the nested object
346
+ if (!(key in target) || typeof target[key] !== 'object') {
347
+ target[key] = {}
348
+ }
349
+
350
+ // Recursively merge nested objects
351
+ this.deepMergeWithPrecedence(target[key], value, sourceName, precedenceMap, fullPath)
352
+ } else {
353
+ // Check precedence before overriding
354
+ const existingPrecedence = precedenceMap.get(fullPath)
355
+
356
+ if (!existingPrecedence || sourcePriority >= existingPrecedence.priority) {
357
+ target[key] = value
358
+ precedenceMap.set(fullPath, {
359
+ source: sourceName as any,
360
+ path: fullPath,
361
+ value,
362
+ priority: sourcePriority
363
+ })
364
+ }
365
+ }
366
+ }
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Environment-specific configuration applier
372
+ */
373
+ export class EnvironmentConfigApplier {
374
+ /**
375
+ * Apply environment-specific configuration overrides
376
+ */
377
+ applyEnvironmentConfig(
378
+ baseConfig: FluxStackConfig,
379
+ environment: string
380
+ ): FluxStackConfig {
381
+ const envConfig = baseConfig.environments?.[environment]
382
+
383
+ if (!envConfig) {
384
+ return baseConfig
385
+ }
386
+
387
+ const merger = new ConfigMerger()
388
+ return merger.merge(
389
+ { config: baseConfig, source: 'base' },
390
+ { config: envConfig, source: `environment:${environment}` }
391
+ )
392
+ }
393
+
394
+ /**
395
+ * Get available environments from configuration
396
+ */
397
+ getAvailableEnvironments(config: FluxStackConfig): string[] {
398
+ return config.environments ? Object.keys(config.environments) : []
399
+ }
400
+
401
+ /**
402
+ * Validate environment-specific configuration
403
+ */
404
+ validateEnvironmentConfig(
405
+ config: FluxStackConfig,
406
+ environment: string
407
+ ): { valid: boolean; errors: string[] } {
408
+ const envConfig = config.environments?.[environment]
409
+
410
+ if (!envConfig) {
411
+ return { valid: true, errors: [] }
412
+ }
413
+
414
+ const errors: string[] = []
415
+
416
+ // Check for conflicting configurations
417
+ if (envConfig.server?.port === config.server.port && environment !== 'development') {
418
+ errors.push(`Environment ${environment} uses same port as base configuration`)
419
+ }
420
+
421
+ // Check for missing required overrides in production
422
+ if (environment === 'production') {
423
+ if (!envConfig.logging?.level || envConfig.logging.level === 'debug') {
424
+ errors.push('Production environment should not use debug logging')
425
+ }
426
+
427
+ if (!envConfig.monitoring?.enabled) {
428
+ errors.push('Production environment should enable monitoring')
429
+ }
430
+ }
431
+
432
+ return {
433
+ valid: errors.length === 0,
434
+ errors
435
+ }
436
+ }
437
+ }
438
+
439
+ // Singleton instances for global use
440
+ export const environmentProcessor = new EnvironmentProcessor()
441
+ export const configMerger = new ConfigMerger()
442
+ export const environmentConfigApplier = new EnvironmentConfigApplier()
443
+
444
+ /**
445
+ * Utility functions for backward compatibility
446
+ */
447
+ export function isDevelopment(): boolean {
448
+ return getEnvironmentInfo().isDevelopment
449
+ }
450
+
451
+ export function isProduction(): boolean {
452
+ return getEnvironmentInfo().isProduction
453
+ }
454
+
455
+ export function isTest(): boolean {
456
+ return getEnvironmentInfo().isTest
457
+ }
458
+
459
+ /**
460
+ * Get environment-specific configuration recommendations
461
+ */
462
+ export function getEnvironmentRecommendations(environment: string): Partial<FluxStackConfig> {
463
+ switch (environment) {
464
+ case 'development':
465
+ return {
466
+ logging: {
467
+ level: 'debug' as const,
468
+ format: 'pretty' as const,
469
+ transports: [{ type: 'console' as const, level: 'debug' as const, format: 'pretty' as const }]
470
+ },
471
+ build: {
472
+ target: 'bun' as const,
473
+ outDir: 'dist',
474
+ clean: true,
475
+ optimization: {
476
+ minify: false,
477
+ compress: false,
478
+ treeshake: false,
479
+ splitChunks: false,
480
+ bundleAnalyzer: false
481
+ },
482
+ sourceMaps: true
483
+ },
484
+ monitoring: {
485
+ enabled: false,
486
+ metrics: {
487
+ enabled: false,
488
+ collectInterval: 60000,
489
+ httpMetrics: true,
490
+ systemMetrics: true,
491
+ customMetrics: false
492
+ },
493
+ profiling: {
494
+ enabled: false,
495
+ sampleRate: 0.1,
496
+ memoryProfiling: false,
497
+ cpuProfiling: false
498
+ },
499
+ exporters: []
500
+ }
501
+ }
502
+
503
+ case 'production':
504
+ return {
505
+ logging: {
506
+ level: 'warn' as const,
507
+ format: 'json' as const,
508
+ transports: [
509
+ { type: 'console' as const, level: 'warn' as const, format: 'json' as const },
510
+ { type: 'file' as const, level: 'warn' as const, format: 'json' as const, options: { filename: 'app.log' } }
511
+ ]
512
+ },
513
+ build: {
514
+ target: 'bun' as const,
515
+ outDir: 'dist',
516
+ clean: true,
517
+ optimization: {
518
+ minify: true,
519
+ compress: true,
520
+ treeshake: true,
521
+ splitChunks: true,
522
+ bundleAnalyzer: false
523
+ },
524
+ sourceMaps: false
525
+ },
526
+ monitoring: {
527
+ enabled: true,
528
+ metrics: {
529
+ enabled: true,
530
+ collectInterval: 30000,
531
+ httpMetrics: true,
532
+ systemMetrics: true,
533
+ customMetrics: false
534
+ },
535
+ profiling: {
536
+ enabled: true,
537
+ sampleRate: 0.01,
538
+ memoryProfiling: true,
539
+ cpuProfiling: true
540
+ },
541
+ exporters: ['prometheus']
542
+ }
543
+ }
544
+
545
+ case 'test':
546
+ return {
547
+ logging: {
548
+ level: 'error' as const,
549
+ format: 'json' as const,
550
+ transports: [{ type: 'console' as const, level: 'error' as const, format: 'json' as const }]
551
+ },
552
+ server: {
553
+ port: 0, // Random port
554
+ host: 'localhost',
555
+ apiPrefix: '/api',
556
+ cors: {
557
+ origins: ['*'],
558
+ methods: ['GET', 'POST', 'PUT', 'DELETE'],
559
+ headers: ['Content-Type', 'Authorization'],
560
+ credentials: false,
561
+ maxAge: 86400
562
+ },
563
+ middleware: []
564
+ },
565
+ client: {
566
+ port: 0,
567
+ proxy: { target: 'http://localhost:3000' },
568
+ build: {
569
+ target: 'es2020' as const,
570
+ outDir: 'dist/client',
571
+ sourceMaps: false,
572
+ minify: false
573
+ }
574
+ },
575
+ monitoring: {
576
+ enabled: false,
577
+ metrics: {
578
+ enabled: false,
579
+ collectInterval: 60000,
580
+ httpMetrics: true,
581
+ systemMetrics: true,
582
+ customMetrics: false
583
+ },
584
+ profiling: {
585
+ enabled: false,
586
+ sampleRate: 0.1,
587
+ memoryProfiling: false,
588
+ cpuProfiling: false
589
+ },
590
+ exporters: []
591
+ }
592
+ }
593
+
594
+ default:
595
+ return {}
596
+ }
597
+ }