create-fluxstack 1.1.0 → 1.4.1

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 (64) hide show
  1. package/app/server/backend-only.ts +5 -5
  2. package/app/server/index.ts +63 -54
  3. package/app/server/live/FluxStackConfig.ts +43 -39
  4. package/app/server/live/SystemMonitorIntegration.ts +2 -2
  5. package/app/server/live/register-components.ts +1 -1
  6. package/app/server/middleware/errorHandling.ts +6 -4
  7. package/app/server/routes/config.ts +145 -0
  8. package/app/server/routes/index.ts +5 -3
  9. package/config/app.config.ts +113 -0
  10. package/config/build.config.ts +24 -0
  11. package/config/database.config.ts +99 -0
  12. package/config/index.ts +68 -0
  13. package/config/logger.config.ts +27 -0
  14. package/config/runtime.config.ts +92 -0
  15. package/config/server.config.ts +46 -0
  16. package/config/services.config.ts +130 -0
  17. package/config/system.config.ts +105 -0
  18. package/core/build/index.ts +10 -4
  19. package/core/cli/generators/index.ts +5 -2
  20. package/core/cli/generators/plugin.ts +290 -0
  21. package/core/cli/index.ts +117 -15
  22. package/core/config/env.ts +37 -95
  23. package/core/config/runtime-config.ts +61 -58
  24. package/core/config/schema.ts +4 -0
  25. package/core/framework/server.ts +22 -10
  26. package/core/plugins/built-in/index.ts +7 -17
  27. package/core/plugins/built-in/swagger/index.ts +228 -228
  28. package/core/plugins/built-in/vite/index.ts +374 -358
  29. package/core/plugins/dependency-manager.ts +5 -5
  30. package/core/plugins/manager.ts +12 -12
  31. package/core/plugins/registry.ts +3 -3
  32. package/core/server/index.ts +0 -1
  33. package/core/server/live/ComponentRegistry.ts +34 -8
  34. package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
  35. package/core/server/live/websocket-plugin.ts +434 -434
  36. package/core/server/middleware/README.md +488 -0
  37. package/core/server/middleware/elysia-helpers.ts +227 -0
  38. package/core/server/middleware/index.ts +25 -9
  39. package/core/server/plugins/static-files-plugin.ts +231 -231
  40. package/core/utils/config-schema.ts +484 -0
  41. package/core/utils/env.ts +306 -0
  42. package/core/utils/helpers.ts +4 -4
  43. package/core/utils/logger/colors.ts +114 -0
  44. package/core/utils/logger/config.ts +35 -0
  45. package/core/utils/logger/formatter.ts +82 -0
  46. package/core/utils/logger/group-logger.ts +101 -0
  47. package/core/utils/logger/index.ts +199 -250
  48. package/core/utils/logger/stack-trace.ts +92 -0
  49. package/core/utils/logger/startup-banner.ts +92 -0
  50. package/core/utils/logger/winston-logger.ts +152 -0
  51. package/core/utils/version.ts +5 -0
  52. package/create-fluxstack.ts +118 -8
  53. package/fluxstack.config.ts +2 -2
  54. package/package.json +117 -115
  55. package/core/config/env-dynamic.ts +0 -326
  56. package/core/plugins/built-in/logger/index.ts +0 -180
  57. package/core/server/plugins/logger.ts +0 -47
  58. package/core/utils/env-runtime-v2.ts +0 -232
  59. package/core/utils/env-runtime.ts +0 -259
  60. package/core/utils/logger/formatters.ts +0 -222
  61. package/core/utils/logger/middleware.ts +0 -253
  62. package/core/utils/logger/performance.ts +0 -384
  63. package/core/utils/logger/transports.ts +0 -365
  64. package/core/utils/logger.ts +0 -106
@@ -1,232 +0,0 @@
1
- /**
2
- * Runtime Environment Loader V2 - Simplified API
3
- * Mais elegante com casting automático e acesso direto
4
- */
5
-
6
- /**
7
- * Enhanced environment variable loader with smart casting
8
- */
9
- class SmartEnvLoader {
10
- private envAccessor: () => Record<string, string | undefined>
11
-
12
- constructor() {
13
- this.envAccessor = this.createDynamicAccessor()
14
- }
15
-
16
- private createDynamicAccessor(): () => Record<string, string | undefined> {
17
- const globalScope = globalThis as any
18
-
19
- return () => {
20
- // Try Bun.env first (most reliable in Bun runtime)
21
- if (globalScope['Bun'] && globalScope['Bun']['env']) {
22
- return globalScope['Bun']['env']
23
- }
24
-
25
- // Fallback to process.env with dynamic access
26
- if (globalScope['process'] && globalScope['process']['env']) {
27
- return globalScope['process']['env']
28
- }
29
-
30
- // Final fallback
31
- const proc = eval('typeof process !== "undefined" ? process : null')
32
- return proc?.env || {}
33
- }
34
- }
35
-
36
- /**
37
- * Smart get with automatic type conversion based on default value
38
- */
39
- get<T>(key: string, defaultValue?: T): T {
40
- const env = this.envAccessor()
41
- const value = env[key]
42
-
43
- if (!value || value === '') {
44
- return defaultValue as T
45
- }
46
-
47
- // Auto-detect type from default value
48
- if (typeof defaultValue === 'number') {
49
- const parsed = parseInt(value, 10)
50
- return (isNaN(parsed) ? defaultValue : parsed) as T
51
- }
52
-
53
- if (typeof defaultValue === 'boolean') {
54
- return ['true', '1', 'yes', 'on'].includes(value.toLowerCase()) as T
55
- }
56
-
57
- if (Array.isArray(defaultValue)) {
58
- return value.split(',').map(v => v.trim()).filter(Boolean) as T
59
- }
60
-
61
- if (typeof defaultValue === 'object' && defaultValue !== null) {
62
- try {
63
- return JSON.parse(value) as T
64
- } catch {
65
- return defaultValue
66
- }
67
- }
68
-
69
- return value as T
70
- }
71
-
72
- /**
73
- * Check if environment variable exists
74
- */
75
- has(key: string): boolean {
76
- const env = this.envAccessor()
77
- return key in env && env[key] !== undefined && env[key] !== ''
78
- }
79
-
80
- /**
81
- * Get all environment variables
82
- */
83
- all(): Record<string, string> {
84
- const env = this.envAccessor()
85
- const result: Record<string, string> = {}
86
-
87
- for (const [key, value] of Object.entries(env)) {
88
- if (value !== undefined && value !== '') {
89
- result[key] = value
90
- }
91
- }
92
-
93
- return result
94
- }
95
- }
96
-
97
- // Create singleton instance
98
- const smartEnv = new SmartEnvLoader()
99
-
100
- /**
101
- * Simplified env API with smart casting
102
- */
103
- export const env = {
104
- /**
105
- * Smart get - automatically casts based on default value type
106
- * Usage:
107
- * env.get('PORT', 3000) -> number
108
- * env.get('DEBUG', false) -> boolean
109
- * env.get('ORIGINS', ['*']) -> string[]
110
- * env.get('HOST', 'localhost') -> string
111
- */
112
- get: <T>(key: string, defaultValue?: T): T => smartEnv.get(key, defaultValue),
113
-
114
- /**
115
- * Check if env var exists
116
- */
117
- has: (key: string) => smartEnv.has(key),
118
-
119
- /**
120
- * Get all env vars
121
- */
122
- all: () => smartEnv.all(),
123
-
124
- // Common environment variables as properties with smart defaults
125
- get NODE_ENV() { return this.get('NODE_ENV', 'development') },
126
- get PORT() { return this.get('PORT', 3000) },
127
- get HOST() { return this.get('HOST', 'localhost') },
128
- get DEBUG() { return this.get('DEBUG', false) },
129
- get LOG_LEVEL() { return this.get('LOG_LEVEL', 'info') },
130
- get DATABASE_URL() { return this.get('DATABASE_URL', '') },
131
- get JWT_SECRET() { return this.get('JWT_SECRET', '') },
132
- get CORS_ORIGINS() { return this.get('CORS_ORIGINS', ['*']) },
133
- get VITE_PORT() { return this.get('VITE_PORT', 5173) },
134
- get API_PREFIX() { return this.get('API_PREFIX', '/api') },
135
-
136
- // App specific
137
- get FLUXSTACK_APP_NAME() { return this.get('FLUXSTACK_APP_NAME', 'FluxStack') },
138
- get FLUXSTACK_APP_VERSION() { return this.get('FLUXSTACK_APP_VERSION', '1.0.0') },
139
-
140
- // Monitoring
141
- get ENABLE_MONITORING() { return this.get('ENABLE_MONITORING', false) },
142
- get ENABLE_SWAGGER() { return this.get('ENABLE_SWAGGER', true) },
143
- get ENABLE_METRICS() { return this.get('ENABLE_METRICS', false) },
144
-
145
- // Database
146
- get DB_HOST() { return this.get('DB_HOST', 'localhost') },
147
- get DB_PORT() { return this.get('DB_PORT', 5432) },
148
- get DB_NAME() { return this.get('DB_NAME', '') },
149
- get DB_USER() { return this.get('DB_USER', '') },
150
- get DB_PASSWORD() { return this.get('DB_PASSWORD', '') },
151
- get DB_SSL() { return this.get('DB_SSL', false) },
152
-
153
- // SMTP
154
- get SMTP_HOST() { return this.get('SMTP_HOST', '') },
155
- get SMTP_PORT() { return this.get('SMTP_PORT', 587) },
156
- get SMTP_USER() { return this.get('SMTP_USER', '') },
157
- get SMTP_PASSWORD() { return this.get('SMTP_PASSWORD', '') },
158
- get SMTP_SECURE() { return this.get('SMTP_SECURE', false) }
159
- }
160
-
161
- /**
162
- * Create namespaced environment access
163
- * Usage: const db = createNamespace('DATABASE_')
164
- * db.get('URL') -> reads DATABASE_URL
165
- */
166
- export function createNamespace(prefix: string) {
167
- return {
168
- get: <T>(key: string, defaultValue?: T): T =>
169
- smartEnv.get(`${prefix}${key}`, defaultValue),
170
-
171
- has: (key: string) => smartEnv.has(`${prefix}${key}`),
172
-
173
- all: () => {
174
- const allEnv = smartEnv.all()
175
- const namespaced: Record<string, string> = {}
176
-
177
- for (const [key, value] of Object.entries(allEnv)) {
178
- if (key.startsWith(prefix)) {
179
- namespaced[key.slice(prefix.length)] = value
180
- }
181
- }
182
-
183
- return namespaced
184
- }
185
- }
186
- }
187
-
188
- /**
189
- * Environment validation
190
- */
191
- export const validate = {
192
- require(keys: string[]): void {
193
- const missing = keys.filter(key => !smartEnv.has(key))
194
- if (missing.length > 0) {
195
- throw new Error(`Missing required environment variables: ${missing.join(', ')}`)
196
- }
197
- },
198
-
199
- oneOf(key: string, validValues: string[]): void {
200
- const value = smartEnv.get(key, '')
201
- if (value && !validValues.includes(value)) {
202
- throw new Error(`${key} must be one of: ${validValues.join(', ')}, got: ${value}`)
203
- }
204
- }
205
- }
206
-
207
- /**
208
- * Convenience functions
209
- */
210
- export const helpers = {
211
- isDevelopment: () => env.NODE_ENV === 'development',
212
- isProduction: () => env.NODE_ENV === 'production',
213
- isTest: () => env.NODE_ENV === 'test',
214
-
215
- getDatabaseUrl: () => {
216
- const url = env.DATABASE_URL
217
- if (url) return url
218
-
219
- const { DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD } = env
220
- if (DB_HOST && DB_NAME) {
221
- const auth = DB_USER ? `${DB_USER}:${DB_PASSWORD}@` : ''
222
- return `postgres://${auth}${DB_HOST}:${DB_PORT}/${DB_NAME}`
223
- }
224
-
225
- return null
226
- },
227
-
228
- getServerUrl: () => `http://${env.HOST}:${env.PORT}`,
229
- getClientUrl: () => `http://${env.HOST}:${env.VITE_PORT}`
230
- }
231
-
232
- export default env
@@ -1,259 +0,0 @@
1
- /**
2
- * Runtime Environment Loader V2 - Simplified API
3
- * Mais elegante com casting automático e acesso direto
4
- */
5
-
6
- /**
7
- * Enhanced environment variable loader with smart casting
8
- */
9
- class SmartEnvLoader {
10
- private envAccessor: () => Record<string, string | undefined>
11
-
12
- constructor() {
13
- this.envAccessor = this.createDynamicAccessor()
14
- }
15
-
16
- private createDynamicAccessor(): () => Record<string, string | undefined> {
17
- const globalScope = globalThis as any
18
-
19
- return () => {
20
- // Try Bun.env first (most reliable in Bun runtime)
21
- if (globalScope['Bun'] && globalScope['Bun']['env']) {
22
- return globalScope['Bun']['env']
23
- }
24
-
25
- // Fallback to process.env with dynamic access
26
- if (globalScope['process'] && globalScope['process']['env']) {
27
- return globalScope['process']['env']
28
- }
29
-
30
- // Final fallback
31
- const proc = eval('typeof process !== "undefined" ? process : null')
32
- return proc?.env || {}
33
- }
34
- }
35
-
36
- /**
37
- * Smart get with automatic type conversion based on default value
38
- */
39
- get<T>(key: string, defaultValue?: T): T {
40
- const env = this.envAccessor()
41
- const value = env[key]
42
-
43
- if (!value || value === '') {
44
- return defaultValue as T
45
- }
46
-
47
- // Auto-detect type from default value
48
- if (typeof defaultValue === 'number') {
49
- const parsed = parseInt(value, 10)
50
- return (isNaN(parsed) ? defaultValue : parsed) as T
51
- }
52
-
53
- if (typeof defaultValue === 'boolean') {
54
- return ['true', '1', 'yes', 'on'].includes(value.toLowerCase()) as T
55
- }
56
-
57
- if (Array.isArray(defaultValue)) {
58
- return value.split(',').map(v => v.trim()).filter(Boolean) as T
59
- }
60
-
61
- if (typeof defaultValue === 'object' && defaultValue !== null) {
62
- try {
63
- return JSON.parse(value) as T
64
- } catch {
65
- return defaultValue
66
- }
67
- }
68
-
69
- return value as T
70
- }
71
-
72
- /**
73
- * Check if environment variable exists
74
- */
75
- has(key: string): boolean {
76
- const env = this.envAccessor()
77
- return key in env && env[key] !== undefined && env[key] !== ''
78
- }
79
-
80
- /**
81
- * Get all environment variables
82
- */
83
- all(): Record<string, string> {
84
- const env = this.envAccessor()
85
- const result: Record<string, string> = {}
86
-
87
- for (const [key, value] of Object.entries(env)) {
88
- if (value !== undefined && value !== '') {
89
- result[key] = value
90
- }
91
- }
92
-
93
- return result
94
- }
95
- }
96
-
97
- // Create singleton instance
98
- const smartEnv = new SmartEnvLoader()
99
-
100
- /**
101
- * Simplified env API with smart casting
102
- */
103
- export const env = {
104
- /**
105
- * Smart get - automatically casts based on default value type
106
- * Usage:
107
- * env.get('PORT', 3000) -> number
108
- * env.get('DEBUG', false) -> boolean
109
- * env.get('ORIGINS', ['*']) -> string[]
110
- * env.get('HOST', 'localhost') -> string
111
- */
112
- get: <T>(key: string, defaultValue?: T): T => smartEnv.get(key, defaultValue),
113
-
114
- /**
115
- * Check if env var exists
116
- */
117
- has: (key: string) => smartEnv.has(key),
118
-
119
- /**
120
- * Get number value
121
- */
122
- num: (key: string, defaultValue?: number) => Number(smartEnv.get(key, defaultValue?.toString() || '0')),
123
-
124
- /**
125
- * Get boolean value
126
- */
127
- bool: (key: string, defaultValue?: boolean) => smartEnv.get(key, defaultValue?.toString() || 'false') === 'true',
128
-
129
- /**
130
- * Get array value
131
- */
132
- array: (key: string, defaultValue?: string[]) => smartEnv.get(key, defaultValue?.join(',') || '').split(',').filter(Boolean),
133
-
134
- /**
135
- * Get all env vars
136
- */
137
- all: () => smartEnv.all(),
138
-
139
- // Common environment variables as properties with smart defaults
140
- get NODE_ENV() { return this.get('NODE_ENV', 'development') },
141
- get PORT() { return this.get('PORT', 3000) },
142
- get HOST() { return this.get('HOST', 'localhost') },
143
- get DEBUG() { return this.get('DEBUG', false) },
144
- get LOG_LEVEL() { return this.get('LOG_LEVEL', 'info') },
145
- get DATABASE_URL() { return this.get('DATABASE_URL', '') },
146
- get JWT_SECRET() { return this.get('JWT_SECRET', '') },
147
- get CORS_ORIGINS() { return this.get('CORS_ORIGINS', ['*']) },
148
- get VITE_PORT() { return this.get('VITE_PORT', 5173) },
149
- get API_PREFIX() { return this.get('API_PREFIX', '/api') },
150
-
151
- // App specific
152
- get FLUXSTACK_APP_NAME() { return this.get('FLUXSTACK_APP_NAME', 'FluxStack') },
153
- get FLUXSTACK_APP_VERSION() { return this.get('FLUXSTACK_APP_VERSION', '1.0.0') },
154
-
155
- // Monitoring
156
- get ENABLE_MONITORING() { return this.get('ENABLE_MONITORING', false) },
157
- get ENABLE_SWAGGER() { return this.get('ENABLE_SWAGGER', true) },
158
- get ENABLE_METRICS() { return this.get('ENABLE_METRICS', false) },
159
-
160
- // Database
161
- get DB_HOST() { return this.get('DB_HOST', 'localhost') },
162
- get DB_PORT() { return this.get('DB_PORT', 5432) },
163
- get DB_NAME() { return this.get('DB_NAME', '') },
164
- get DB_USER() { return this.get('DB_USER', '') },
165
- get DB_PASSWORD() { return this.get('DB_PASSWORD', '') },
166
- get DB_SSL() { return this.get('DB_SSL', false) },
167
-
168
- // SMTP
169
- get SMTP_HOST() { return this.get('SMTP_HOST', '') },
170
- get SMTP_PORT() { return this.get('SMTP_PORT', 587) },
171
- get SMTP_USER() { return this.get('SMTP_USER', '') },
172
- get SMTP_PASSWORD() { return this.get('SMTP_PASSWORD', '') },
173
- get SMTP_SECURE() { return this.get('SMTP_SECURE', false) }
174
- }
175
-
176
- /**
177
- * Create namespaced environment access
178
- * Usage: const db = createNamespace('DATABASE_')
179
- * db.get('URL') -> reads DATABASE_URL
180
- */
181
- export function createNamespace(prefix: string) {
182
- return {
183
- get: <T>(key: string, defaultValue?: T): T =>
184
- smartEnv.get(`${prefix}${key}`, defaultValue),
185
-
186
- has: (key: string) => smartEnv.has(`${prefix}${key}`),
187
-
188
- all: () => {
189
- const allEnv = smartEnv.all()
190
- const namespaced: Record<string, string> = {}
191
-
192
- for (const [key, value] of Object.entries(allEnv)) {
193
- if (key.startsWith(prefix)) {
194
- namespaced[key.slice(prefix.length)] = value
195
- }
196
- }
197
-
198
- return namespaced
199
- }
200
- }
201
- }
202
-
203
- /**
204
- * Environment validation
205
- */
206
- export const validate = {
207
- require(keys: string[]): void {
208
- const missing = keys.filter(key => !smartEnv.has(key))
209
- if (missing.length > 0) {
210
- throw new Error(`Missing required environment variables: ${missing.join(', ')}`)
211
- }
212
- },
213
-
214
- oneOf(key: string, validValues: string[]): void {
215
- const value = smartEnv.get(key, '')
216
- if (value && !validValues.includes(value)) {
217
- throw new Error(`${key} must be one of: ${validValues.join(', ')}, got: ${value}`)
218
- }
219
- },
220
-
221
- validate(key: string, validator: (value: string) => boolean, errorMessage: string): void {
222
- const value = smartEnv.get(key, '')
223
- if (value && !validator(value)) {
224
- throw new Error(`${key}: ${errorMessage}`)
225
- }
226
- }
227
- }
228
-
229
- /**
230
- * Convenience functions
231
- */
232
- export const helpers = {
233
- isDevelopment: () => env.NODE_ENV === 'development',
234
- isProduction: () => env.NODE_ENV === 'production',
235
- isTest: () => env.NODE_ENV === 'test',
236
-
237
- getDatabaseUrl: () => {
238
- const url = env.DATABASE_URL
239
- if (url) return url
240
-
241
- const { DB_HOST, DB_PORT, DB_NAME, DB_USER, DB_PASSWORD } = env
242
- if (DB_HOST && DB_NAME) {
243
- const auth = DB_USER ? `${DB_USER}:${DB_PASSWORD}@` : ''
244
- return `postgres://${auth}${DB_HOST}:${DB_PORT}/${DB_NAME}`
245
- }
246
-
247
- return null
248
- },
249
-
250
- getServerUrl: () => `http://${env.HOST}:${env.PORT}`,
251
- getClientUrl: () => `http://${env.HOST}:${env.VITE_PORT}`
252
- }
253
-
254
- export default env
255
-
256
- // Legacy exports for compatibility
257
- export const runtimeEnv = env
258
- export const envValidation = validate
259
- export const createEnvNamespace = createNamespace
@@ -1,222 +0,0 @@
1
- /**
2
- * FluxStack Logger Formatters
3
- * Different formatting strategies for log output
4
- */
5
-
6
- import type { LogEntry, LogLevel } from './transports'
7
-
8
- export interface LogFormatter {
9
- format(entry: LogEntry): string
10
- }
11
-
12
- /**
13
- * Pretty formatter for development with colors and readable layout
14
- */
15
- export class PrettyFormatter implements LogFormatter {
16
- private colors: boolean
17
- private timestamp: boolean
18
-
19
- private colorMap = {
20
- debug: '\x1b[36m', // cyan
21
- info: '\x1b[32m', // green
22
- warn: '\x1b[33m', // yellow
23
- error: '\x1b[31m', // red
24
- reset: '\x1b[0m',
25
- gray: '\x1b[90m'
26
- }
27
-
28
- constructor(options: { colors?: boolean; timestamp?: boolean } = {}) {
29
- this.colors = options.colors !== false
30
- this.timestamp = options.timestamp !== false
31
- }
32
-
33
- format(entry: LogEntry): string {
34
- const { timestamp, level, message, meta, context } = entry
35
-
36
- let formatted = ''
37
-
38
- // Add timestamp
39
- if (this.timestamp) {
40
- const color = this.colors ? this.colorMap.gray : ''
41
- const reset = this.colors ? this.colorMap.reset : ''
42
- formatted += `${color}[${timestamp}]${reset} `
43
- }
44
-
45
- // Add level with color and icon
46
- const levelColor = this.colors ? this.colorMap[level] : ''
47
- const reset = this.colors ? this.colorMap.reset : ''
48
- const levelIcon = this.getLevelIcon(level)
49
- const levelStr = level.toUpperCase().padEnd(5)
50
- formatted += `${levelColor}${levelIcon} ${levelStr}${reset} `
51
-
52
- // Add context if available
53
- if (context && Object.keys(context).length > 0) {
54
- const contextStr = Object.entries(context)
55
- .map(([key, value]) => `${key}=${value}`)
56
- .join(' ')
57
- const contextColor = this.colors ? this.colorMap.gray : ''
58
- formatted += `${contextColor}[${contextStr}]${reset} `
59
- }
60
-
61
- // Add message
62
- formatted += message
63
-
64
- // Add meta if available
65
- if (meta && typeof meta === 'object') {
66
- const metaColor = this.colors ? this.colorMap.gray : ''
67
- const metaStr = this.formatMeta(meta)
68
- formatted += ` ${metaColor}${metaStr}${reset}`
69
- } else if (meta !== undefined) {
70
- formatted += ` ${meta}`
71
- }
72
-
73
- return formatted
74
- }
75
-
76
- private getLevelIcon(level: LogLevel): string {
77
- switch (level) {
78
- case 'debug': return '🔍'
79
- case 'info': return 'ℹ️ '
80
- case 'warn': return '⚠️ '
81
- case 'error': return '❌'
82
- default: return ' '
83
- }
84
- }
85
-
86
- private formatMeta(meta: any): string {
87
- if (typeof meta === 'object' && meta !== null) {
88
- // Pretty print objects with indentation
89
- return JSON.stringify(meta, null, 2)
90
- .split('\n')
91
- .map((line, index) => index === 0 ? line : ` ${line}`)
92
- .join('\n')
93
- }
94
- return String(meta)
95
- }
96
- }
97
-
98
- /**
99
- * JSON formatter for production with structured output
100
- */
101
- export class JSONFormatter implements LogFormatter {
102
- private pretty: boolean
103
-
104
- constructor(options: { pretty?: boolean } = {}) {
105
- this.pretty = options.pretty || false
106
- }
107
-
108
- format(entry: LogEntry): string {
109
- const jsonEntry = {
110
- '@timestamp': entry.timestamp,
111
- level: entry.level,
112
- message: entry.message,
113
- ...(entry.context && { context: entry.context }),
114
- ...(entry.meta && { meta: entry.meta })
115
- }
116
-
117
- return this.pretty
118
- ? JSON.stringify(jsonEntry, null, 2)
119
- : JSON.stringify(jsonEntry)
120
- }
121
- }
122
-
123
- /**
124
- * Simple formatter for basic text output
125
- */
126
- export class SimpleFormatter implements LogFormatter {
127
- private timestamp: boolean
128
-
129
- constructor(options: { timestamp?: boolean } = {}) {
130
- this.timestamp = options.timestamp !== false
131
- }
132
-
133
- format(entry: LogEntry): string {
134
- const { timestamp, level, message, meta, context } = entry
135
-
136
- let formatted = ''
137
-
138
- // Add timestamp
139
- if (this.timestamp) {
140
- formatted += `[${timestamp}] `
141
- }
142
-
143
- // Add level
144
- formatted += `${level.toUpperCase().padEnd(5)} `
145
-
146
- // Add context if available
147
- if (context && Object.keys(context).length > 0) {
148
- const contextStr = Object.entries(context)
149
- .map(([key, value]) => `${key}=${value}`)
150
- .join(' ')
151
- formatted += `[${contextStr}] `
152
- }
153
-
154
- // Add message
155
- formatted += message
156
-
157
- // Add meta if available
158
- if (meta && typeof meta === 'object') {
159
- formatted += ` ${JSON.stringify(meta)}`
160
- } else if (meta !== undefined) {
161
- formatted += ` ${meta}`
162
- }
163
-
164
- return formatted
165
- }
166
- }
167
-
168
- /**
169
- * Syslog formatter for system logging
170
- */
171
- export class SyslogFormatter implements LogFormatter {
172
- private facility: number
173
- private hostname: string
174
-
175
- constructor(options: { facility?: number; hostname?: string } = {}) {
176
- this.facility = options.facility || 16 // local0
177
- this.hostname = options.hostname || require('os').hostname()
178
- }
179
-
180
- format(entry: LogEntry): string {
181
- const { timestamp, level, message, meta, context } = entry
182
-
183
- // Calculate priority (facility * 8 + severity)
184
- const severity = this.getLevelSeverity(level)
185
- const priority = this.facility * 8 + severity
186
-
187
- // Format timestamp in RFC3339
188
- const syslogTime = new Date(timestamp).toISOString()
189
-
190
- // Build syslog message
191
- let syslogMessage = `<${priority}>${syslogTime} ${this.hostname} fluxstack: `
192
-
193
- // Add context if available
194
- if (context && Object.keys(context).length > 0) {
195
- const contextStr = Object.entries(context)
196
- .map(([key, value]) => `${key}=${value}`)
197
- .join(' ')
198
- syslogMessage += `[${contextStr}] `
199
- }
200
-
201
- syslogMessage += message
202
-
203
- // Add meta if available
204
- if (meta && typeof meta === 'object') {
205
- syslogMessage += ` ${JSON.stringify(meta)}`
206
- } else if (meta !== undefined) {
207
- syslogMessage += ` ${meta}`
208
- }
209
-
210
- return syslogMessage
211
- }
212
-
213
- private getLevelSeverity(level: LogLevel): number {
214
- switch (level) {
215
- case 'debug': return 7 // debug
216
- case 'info': return 6 // info
217
- case 'warn': return 4 // warning
218
- case 'error': return 3 // error
219
- default: return 6
220
- }
221
- }
222
- }