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.
- package/app/server/backend-only.ts +5 -5
- package/app/server/index.ts +63 -54
- package/app/server/live/FluxStackConfig.ts +43 -39
- package/app/server/live/SystemMonitorIntegration.ts +2 -2
- package/app/server/live/register-components.ts +1 -1
- package/app/server/middleware/errorHandling.ts +6 -4
- package/app/server/routes/config.ts +145 -0
- package/app/server/routes/index.ts +5 -3
- package/config/app.config.ts +113 -0
- package/config/build.config.ts +24 -0
- package/config/database.config.ts +99 -0
- package/config/index.ts +68 -0
- package/config/logger.config.ts +27 -0
- package/config/runtime.config.ts +92 -0
- package/config/server.config.ts +46 -0
- package/config/services.config.ts +130 -0
- package/config/system.config.ts +105 -0
- package/core/build/index.ts +10 -4
- package/core/cli/generators/index.ts +5 -2
- package/core/cli/generators/plugin.ts +290 -0
- package/core/cli/index.ts +117 -15
- package/core/config/env.ts +37 -95
- package/core/config/runtime-config.ts +61 -58
- package/core/config/schema.ts +4 -0
- package/core/framework/server.ts +22 -10
- package/core/plugins/built-in/index.ts +7 -17
- package/core/plugins/built-in/swagger/index.ts +228 -228
- package/core/plugins/built-in/vite/index.ts +374 -358
- package/core/plugins/dependency-manager.ts +5 -5
- package/core/plugins/manager.ts +12 -12
- package/core/plugins/registry.ts +3 -3
- package/core/server/index.ts +0 -1
- package/core/server/live/ComponentRegistry.ts +34 -8
- package/core/server/live/LiveComponentPerformanceMonitor.ts +1 -1
- package/core/server/live/websocket-plugin.ts +434 -434
- package/core/server/middleware/README.md +488 -0
- package/core/server/middleware/elysia-helpers.ts +227 -0
- package/core/server/middleware/index.ts +25 -9
- package/core/server/plugins/static-files-plugin.ts +231 -231
- package/core/utils/config-schema.ts +484 -0
- package/core/utils/env.ts +306 -0
- package/core/utils/helpers.ts +4 -4
- package/core/utils/logger/colors.ts +114 -0
- package/core/utils/logger/config.ts +35 -0
- package/core/utils/logger/formatter.ts +82 -0
- package/core/utils/logger/group-logger.ts +101 -0
- package/core/utils/logger/index.ts +199 -250
- package/core/utils/logger/stack-trace.ts +92 -0
- package/core/utils/logger/startup-banner.ts +92 -0
- package/core/utils/logger/winston-logger.ts +152 -0
- package/core/utils/version.ts +5 -0
- package/create-fluxstack.ts +118 -8
- package/fluxstack.config.ts +2 -2
- package/package.json +117 -115
- package/core/config/env-dynamic.ts +0 -326
- package/core/plugins/built-in/logger/index.ts +0 -180
- package/core/server/plugins/logger.ts +0 -47
- package/core/utils/env-runtime-v2.ts +0 -232
- package/core/utils/env-runtime.ts +0 -259
- package/core/utils/logger/formatters.ts +0 -222
- package/core/utils/logger/middleware.ts +0 -253
- package/core/utils/logger/performance.ts +0 -384
- package/core/utils/logger/transports.ts +0 -365
- package/core/utils/logger.ts +0 -106
|
@@ -1,280 +1,229 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* FluxStack Logger
|
|
3
|
-
*
|
|
2
|
+
* FluxStack Logger - Main Entry Point
|
|
3
|
+
* Unified logging system based on Winston with automatic module detection
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import
|
|
9
|
-
import
|
|
6
|
+
import chalk from 'chalk'
|
|
7
|
+
import { getCallerInfo, formatCallerInfo } from './stack-trace'
|
|
8
|
+
import { getColorForModule } from './colors'
|
|
9
|
+
import { getLoggerForModule } from './winston-logger'
|
|
10
|
+
import {
|
|
11
|
+
formatMessage,
|
|
12
|
+
formatSection,
|
|
13
|
+
formatImportant,
|
|
14
|
+
formatOperationStart,
|
|
15
|
+
formatOperationSuccess
|
|
16
|
+
} from './formatter'
|
|
17
|
+
|
|
18
|
+
// Re-export types and utilities
|
|
19
|
+
export { LOGGER_CONFIG } from './config'
|
|
20
|
+
export type { LoggerConfig } from './config'
|
|
21
|
+
export type { CallerInfo } from './stack-trace'
|
|
22
|
+
export { clearColorCache } from './colors'
|
|
23
|
+
export { clearCallerCache } from './stack-trace'
|
|
24
|
+
export { clearLoggerCache } from './winston-logger'
|
|
25
|
+
|
|
26
|
+
// Re-export banner utilities for custom banners
|
|
27
|
+
export { displayStartupBanner, type StartupInfo } from './startup-banner'
|
|
10
28
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
// Cleanup
|
|
35
|
-
close(): Promise<void>
|
|
29
|
+
/**
|
|
30
|
+
* Core log function that handles all log levels
|
|
31
|
+
*/
|
|
32
|
+
function logMessage(level: 'debug' | 'info' | 'warn' | 'error', message: unknown, ...args: unknown[]): void {
|
|
33
|
+
const { file: callerFile, line: callerLine, function: callerFunction } = getCallerInfo()
|
|
34
|
+
const logger = getLoggerForModule(callerFile)
|
|
35
|
+
const moduleColor = getColorForModule(callerFile)
|
|
36
|
+
|
|
37
|
+
// Format caller context
|
|
38
|
+
const context = formatCallerInfo(
|
|
39
|
+
{ file: callerFile, line: callerLine, function: callerFunction },
|
|
40
|
+
(str) => moduleColor(str)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
// Format the message
|
|
44
|
+
const finalMessage = formatMessage(message, args)
|
|
45
|
+
|
|
46
|
+
// Log with appropriate level
|
|
47
|
+
logger.log({
|
|
48
|
+
level,
|
|
49
|
+
message: `${context} ${finalMessage}`
|
|
50
|
+
})
|
|
36
51
|
}
|
|
37
52
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
53
|
+
/**
|
|
54
|
+
* Log info message
|
|
55
|
+
*/
|
|
56
|
+
export function LOG(message: unknown, ...args: unknown[]): void {
|
|
57
|
+
logMessage('info', message, ...args)
|
|
42
58
|
}
|
|
43
59
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
private defaultMeta: any = {}
|
|
51
|
-
|
|
52
|
-
constructor(config?: LoggerConfig) {
|
|
53
|
-
this.logLevel = config?.level || (process.env.LOG_LEVEL as LogLevel) || 'info'
|
|
54
|
-
this.context = {}
|
|
55
|
-
this.defaultMeta = config?.defaultMeta || {}
|
|
56
|
-
|
|
57
|
-
// Setup default transports if none provided
|
|
58
|
-
if (config?.transports) {
|
|
59
|
-
config.transports.forEach(transport => {
|
|
60
|
-
this.transports.set(transport.name, transport)
|
|
61
|
-
})
|
|
62
|
-
} else {
|
|
63
|
-
this.setupDefaultTransports()
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
static getInstance(config?: LoggerConfig): FluxStackLogger {
|
|
68
|
-
if (FluxStackLogger.instance === null) {
|
|
69
|
-
FluxStackLogger.instance = new FluxStackLogger(config)
|
|
70
|
-
}
|
|
71
|
-
return FluxStackLogger.instance
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
private setupDefaultTransports(): void {
|
|
75
|
-
const isDevelopment = process.env.NODE_ENV !== 'production'
|
|
76
|
-
|
|
77
|
-
if (isDevelopment) {
|
|
78
|
-
// Development: Pretty console output
|
|
79
|
-
this.transports.set('console', new ConsoleTransport({
|
|
80
|
-
level: this.logLevel,
|
|
81
|
-
colors: true,
|
|
82
|
-
timestamp: true
|
|
83
|
-
}))
|
|
84
|
-
} else {
|
|
85
|
-
// Production: JSON output for structured logging
|
|
86
|
-
this.transports.set('json', new JSONTransport({
|
|
87
|
-
level: this.logLevel,
|
|
88
|
-
pretty: false
|
|
89
|
-
}))
|
|
90
|
-
|
|
91
|
-
// Also add file transport for production
|
|
92
|
-
this.transports.set('file', new FileTransport({
|
|
93
|
-
level: this.logLevel,
|
|
94
|
-
filename: 'logs/fluxstack.log',
|
|
95
|
-
maxSize: 10 * 1024 * 1024, // 10MB
|
|
96
|
-
maxFiles: 5,
|
|
97
|
-
compress: true
|
|
98
|
-
}))
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
private shouldLog(level: LogLevel): boolean {
|
|
103
|
-
const levels: Record<LogLevel, number> = {
|
|
104
|
-
debug: 0,
|
|
105
|
-
info: 1,
|
|
106
|
-
warn: 2,
|
|
107
|
-
error: 3
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
return levels[level] >= levels[this.logLevel]
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
private async writeToTransports(level: LogLevel, message: string, meta?: any): Promise<void> {
|
|
114
|
-
if (!this.shouldLog(level)) return
|
|
115
|
-
|
|
116
|
-
const entry: LogEntry = {
|
|
117
|
-
timestamp: new Date().toISOString(),
|
|
118
|
-
level,
|
|
119
|
-
message,
|
|
120
|
-
meta: { ...this.defaultMeta, ...meta },
|
|
121
|
-
context: Object.keys(this.context).length > 0 ? this.context : undefined
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Write to all transports
|
|
125
|
-
const writePromises = Array.from(this.transports.values()).map(async transport => {
|
|
126
|
-
try {
|
|
127
|
-
await transport.write(entry)
|
|
128
|
-
} catch (error) {
|
|
129
|
-
// Fallback to console if transport fails
|
|
130
|
-
console.error(`Transport ${transport.name} failed:`, error)
|
|
131
|
-
}
|
|
132
|
-
})
|
|
133
|
-
|
|
134
|
-
await Promise.all(writePromises)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
debug(message: string, meta?: any): void {
|
|
138
|
-
this.writeToTransports('debug', message, meta).catch(err => {
|
|
139
|
-
console.error('Logger error:', err)
|
|
140
|
-
})
|
|
141
|
-
}
|
|
60
|
+
/**
|
|
61
|
+
* Log warning message
|
|
62
|
+
*/
|
|
63
|
+
export function WARN(message: unknown, ...args: unknown[]): void {
|
|
64
|
+
logMessage('warn', message, ...args)
|
|
65
|
+
}
|
|
142
66
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
67
|
+
/**
|
|
68
|
+
* Log error message
|
|
69
|
+
*/
|
|
70
|
+
export function ERROR(message: unknown, ...args: unknown[]): void {
|
|
71
|
+
logMessage('error', message, ...args)
|
|
72
|
+
}
|
|
148
73
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
74
|
+
/**
|
|
75
|
+
* Log debug message
|
|
76
|
+
*/
|
|
77
|
+
export function DEBUG(message: unknown, ...args: unknown[]): void {
|
|
78
|
+
logMessage('debug', message, ...args)
|
|
79
|
+
}
|
|
154
80
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
81
|
+
/**
|
|
82
|
+
* Log operation start
|
|
83
|
+
*/
|
|
84
|
+
export function START(operation: string, details?: unknown): void {
|
|
85
|
+
const message = formatOperationStart(operation)
|
|
86
|
+
if (details) {
|
|
87
|
+
logMessage('info', message, details)
|
|
88
|
+
} else {
|
|
89
|
+
logMessage('info', message)
|
|
159
90
|
}
|
|
91
|
+
}
|
|
160
92
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Log operation success
|
|
95
|
+
*/
|
|
96
|
+
export function SUCCESS(operation: string, details?: unknown): void {
|
|
97
|
+
const message = formatOperationSuccess(operation)
|
|
98
|
+
if (details) {
|
|
99
|
+
logMessage('info', message, details)
|
|
100
|
+
} else {
|
|
101
|
+
logMessage('info', message)
|
|
170
102
|
}
|
|
103
|
+
}
|
|
171
104
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Log important information
|
|
107
|
+
*/
|
|
108
|
+
export function IMPORTANT(title: string, message: unknown): void {
|
|
109
|
+
const formattedTitle = chalk.bold.cyan(formatImportant(title))
|
|
110
|
+
logMessage('info', `${formattedTitle}\n${formatMessage(message)}`)
|
|
111
|
+
}
|
|
176
112
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
113
|
+
/**
|
|
114
|
+
* Log a section (group of related logs)
|
|
115
|
+
*/
|
|
116
|
+
export function SECTION(sectionName: string, callback: () => void): void {
|
|
117
|
+
const sectionTitle = chalk.bold.cyan(formatSection(sectionName))
|
|
118
|
+
logMessage('info', sectionTitle)
|
|
119
|
+
callback()
|
|
120
|
+
logMessage('info', chalk.bold.cyan(formatSection(`FIM: ${sectionName}`)))
|
|
121
|
+
}
|
|
185
122
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
this.info(`${method} ${path}${statusStr}${durationStr}`, meta)
|
|
195
|
-
}
|
|
123
|
+
/**
|
|
124
|
+
* HTTP request logging (compatibility with Elysia)
|
|
125
|
+
*/
|
|
126
|
+
export function request(method: string, path: string, status?: number, duration?: number): void {
|
|
127
|
+
const statusStr = status ? ` ${status}` : ''
|
|
128
|
+
const durationStr = duration ? ` (${duration}ms)` : ''
|
|
129
|
+
LOG(`${method} ${path}${statusStr}${durationStr}`)
|
|
130
|
+
}
|
|
196
131
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
132
|
+
/**
|
|
133
|
+
* Plugin logging (compatibility with plugin system)
|
|
134
|
+
*/
|
|
135
|
+
export function plugin(pluginName: string, message: string, meta?: unknown): void {
|
|
136
|
+
DEBUG(`[${pluginName}] ${message}`, meta)
|
|
137
|
+
}
|
|
201
138
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
this.transports.delete(name)
|
|
211
|
-
}
|
|
139
|
+
/**
|
|
140
|
+
* Framework logging (compatibility with framework)
|
|
141
|
+
*/
|
|
142
|
+
export function framework(message: string, meta?: unknown): void {
|
|
143
|
+
LOG(`[FluxStack] ${message}`, meta)
|
|
144
|
+
}
|
|
212
145
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
.map(transport => transport.close!())
|
|
218
|
-
|
|
219
|
-
await Promise.all(closePromises)
|
|
220
|
-
this.transports.clear()
|
|
221
|
-
}
|
|
146
|
+
/**
|
|
147
|
+
* Performance timing
|
|
148
|
+
*/
|
|
149
|
+
const timers = new Map<string, number>()
|
|
222
150
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
151
|
+
export function time(label: string): void {
|
|
152
|
+
timers.set(label, Date.now())
|
|
153
|
+
}
|
|
227
154
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
155
|
+
export function timeEnd(label: string): void {
|
|
156
|
+
const startTime = timers.get(label)
|
|
157
|
+
if (startTime) {
|
|
158
|
+
const duration = Date.now() - startTime
|
|
159
|
+
LOG(`Timer ${label}: ${duration}ms`)
|
|
160
|
+
timers.delete(label)
|
|
161
|
+
} else {
|
|
162
|
+
WARN(`Timer not found: ${label}`)
|
|
231
163
|
}
|
|
232
164
|
}
|
|
233
165
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
// Export middleware utilities
|
|
247
|
-
export {
|
|
248
|
-
createElysiaLoggerMiddleware,
|
|
249
|
-
createDatabaseLoggerMiddleware,
|
|
250
|
-
createPluginLoggerMiddleware,
|
|
251
|
-
createBuildLoggerMiddleware
|
|
252
|
-
} from './middleware'
|
|
166
|
+
/**
|
|
167
|
+
* Clear all caches (useful for testing)
|
|
168
|
+
*/
|
|
169
|
+
export function clearCache(): void {
|
|
170
|
+
const { clearColorCache } = require('./colors')
|
|
171
|
+
const { clearCallerCache } = require('./stack-trace')
|
|
172
|
+
const { clearLoggerCache } = require('./winston-logger')
|
|
173
|
+
|
|
174
|
+
clearColorCache()
|
|
175
|
+
clearCallerCache()
|
|
176
|
+
clearLoggerCache()
|
|
177
|
+
}
|
|
253
178
|
|
|
254
|
-
|
|
255
|
-
|
|
179
|
+
/**
|
|
180
|
+
* Legacy compatibility - logger object with methods
|
|
181
|
+
*/
|
|
182
|
+
export const logger = {
|
|
183
|
+
debug: DEBUG,
|
|
184
|
+
info: LOG,
|
|
185
|
+
warn: WARN,
|
|
186
|
+
error: ERROR,
|
|
187
|
+
request,
|
|
188
|
+
plugin,
|
|
189
|
+
framework,
|
|
190
|
+
time,
|
|
191
|
+
timeEnd
|
|
192
|
+
}
|
|
256
193
|
|
|
257
|
-
|
|
194
|
+
/**
|
|
195
|
+
* Convenience log object (similar to old implementation)
|
|
196
|
+
*/
|
|
258
197
|
export const log = {
|
|
259
|
-
debug:
|
|
260
|
-
info:
|
|
261
|
-
warn:
|
|
262
|
-
error:
|
|
263
|
-
request
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
logger.framework(message, meta),
|
|
269
|
-
child: (context: any) => logger.child(context),
|
|
270
|
-
time: (label: string) => logger.time(label),
|
|
271
|
-
timeEnd: (label: string) => logger.timeEnd(label),
|
|
272
|
-
addTransport: (transport: LogTransport) => logger.addTransport(transport),
|
|
273
|
-
removeTransport: (name: string) => logger.removeTransport(name),
|
|
274
|
-
close: () => logger.close()
|
|
198
|
+
debug: DEBUG,
|
|
199
|
+
info: LOG,
|
|
200
|
+
warn: WARN,
|
|
201
|
+
error: ERROR,
|
|
202
|
+
request,
|
|
203
|
+
plugin,
|
|
204
|
+
framework,
|
|
205
|
+
time,
|
|
206
|
+
timeEnd
|
|
275
207
|
}
|
|
276
208
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
209
|
+
/**
|
|
210
|
+
* Default export for easy importing
|
|
211
|
+
*/
|
|
212
|
+
export default {
|
|
213
|
+
LOG,
|
|
214
|
+
WARN,
|
|
215
|
+
ERROR,
|
|
216
|
+
DEBUG,
|
|
217
|
+
START,
|
|
218
|
+
SUCCESS,
|
|
219
|
+
IMPORTANT,
|
|
220
|
+
SECTION,
|
|
221
|
+
request,
|
|
222
|
+
plugin,
|
|
223
|
+
framework,
|
|
224
|
+
time,
|
|
225
|
+
timeEnd,
|
|
226
|
+
logger,
|
|
227
|
+
log,
|
|
228
|
+
clearCache
|
|
229
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FluxStack Logger - Stack Trace Analysis
|
|
3
|
+
* Extracts caller information from stack traces
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { relative } from 'path'
|
|
7
|
+
|
|
8
|
+
export interface CallerInfo {
|
|
9
|
+
file: string
|
|
10
|
+
line: number
|
|
11
|
+
function: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Cache for caller information
|
|
15
|
+
const callerCache = new Map<string, CallerInfo>()
|
|
16
|
+
const MAX_CACHE_SIZE = 1000
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Get information about the caller (file, line, function)
|
|
20
|
+
*/
|
|
21
|
+
export function getCallerInfo(): CallerInfo {
|
|
22
|
+
const originalFunc = Error.prepareStackTrace
|
|
23
|
+
let callerInfo: CallerInfo = { file: 'unknown', line: 0, function: 'unknown' }
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
const err = new Error()
|
|
27
|
+
let stack: NodeJS.CallSite[] = []
|
|
28
|
+
|
|
29
|
+
Error.prepareStackTrace = (_: Error, stackTraces: NodeJS.CallSite[]) => stackTraces
|
|
30
|
+
stack = err.stack as unknown as NodeJS.CallSite[]
|
|
31
|
+
|
|
32
|
+
// Find the first frame that is not from the logger
|
|
33
|
+
for (let i = 2; i < stack.length; i++) {
|
|
34
|
+
const callSite = stack[i]
|
|
35
|
+
const fileName = callSite.getFileName()
|
|
36
|
+
|
|
37
|
+
if (
|
|
38
|
+
fileName &&
|
|
39
|
+
!fileName.includes('logger') &&
|
|
40
|
+
!fileName.includes('node_modules') &&
|
|
41
|
+
!fileName.includes('bun:') &&
|
|
42
|
+
!fileName.includes('<anonymous>')
|
|
43
|
+
) {
|
|
44
|
+
const relativeFile = relative(process.cwd(), fileName)
|
|
45
|
+
const lineNumber = callSite.getLineNumber() || 0
|
|
46
|
+
const cacheKey = `${relativeFile}:${lineNumber}`
|
|
47
|
+
|
|
48
|
+
// Check cache
|
|
49
|
+
if (callerCache.has(cacheKey)) {
|
|
50
|
+
return callerCache.get(cacheKey)!
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
callerInfo = {
|
|
54
|
+
file: relativeFile,
|
|
55
|
+
line: lineNumber,
|
|
56
|
+
function: callSite.getFunctionName() || 'anonymous'
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Cache the result
|
|
60
|
+
if (callerCache.size >= MAX_CACHE_SIZE) {
|
|
61
|
+
// Remove oldest entry
|
|
62
|
+
const firstKey = callerCache.keys().next().value
|
|
63
|
+
callerCache.delete(firstKey)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
callerCache.set(cacheKey, callerInfo)
|
|
67
|
+
break
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
} catch (error) {
|
|
71
|
+
// Silently fail - return default caller info
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
Error.prepareStackTrace = originalFunc
|
|
75
|
+
return callerInfo
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Clear caller cache (useful for testing)
|
|
80
|
+
*/
|
|
81
|
+
export function clearCallerCache(): void {
|
|
82
|
+
callerCache.clear()
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Format caller info for display
|
|
87
|
+
*/
|
|
88
|
+
export function formatCallerInfo(info: CallerInfo, colorFn: (str: string) => string): string {
|
|
89
|
+
const fileInfo = colorFn(`[${info.file}:${info.line}]`)
|
|
90
|
+
const functionInfo = info.function !== 'unknown' ? colorFn(`[${info.function}]`) : ''
|
|
91
|
+
return functionInfo ? `${fileInfo} ${functionInfo}` : fileInfo
|
|
92
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FluxStack Logger - Startup Banner
|
|
3
|
+
* Clean and beautiful startup display
|
|
4
|
+
*
|
|
5
|
+
* Developers can customize the banner by:
|
|
6
|
+
* 1. Setting showBanner: false in server config
|
|
7
|
+
* 2. Using displayStartupBanner() in app.listen() callback
|
|
8
|
+
* 3. Creating completely custom banners with chalk
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import chalk from 'chalk'
|
|
12
|
+
import { LOG } from './index'
|
|
13
|
+
import { FLUXSTACK_VERSION } from '../version'
|
|
14
|
+
|
|
15
|
+
export interface StartupInfo {
|
|
16
|
+
port: number
|
|
17
|
+
apiPrefix?: string
|
|
18
|
+
environment: string
|
|
19
|
+
pluginCount?: number
|
|
20
|
+
vitePort?: number
|
|
21
|
+
viteEmbedded?: boolean // true when Vite runs programmatically with backend
|
|
22
|
+
swaggerPath?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Display clean startup banner
|
|
27
|
+
*/
|
|
28
|
+
export function displayStartupBanner(info: StartupInfo): void {
|
|
29
|
+
const {
|
|
30
|
+
port,
|
|
31
|
+
apiPrefix = '/api',
|
|
32
|
+
environment,
|
|
33
|
+
pluginCount = 0,
|
|
34
|
+
vitePort,
|
|
35
|
+
viteEmbedded = false,
|
|
36
|
+
swaggerPath
|
|
37
|
+
} = info
|
|
38
|
+
|
|
39
|
+
console.log('\n' + chalk.cyan.bold('⥠FluxStack') + chalk.gray(` v${FLUXSTACK_VERSION}\n`))
|
|
40
|
+
|
|
41
|
+
// Server info
|
|
42
|
+
console.log(chalk.bold('đ Server'))
|
|
43
|
+
console.log(` ${chalk.gray('â')} http://localhost:${port}`)
|
|
44
|
+
console.log(` ${chalk.gray('â')} API: http://localhost:${port}${apiPrefix}`)
|
|
45
|
+
console.log(` ${chalk.gray('â')} Health: http://localhost:${port}${apiPrefix}/health`)
|
|
46
|
+
|
|
47
|
+
// Frontend info (only if Vite is running standalone, NOT embedded)
|
|
48
|
+
if (vitePort && !viteEmbedded) {
|
|
49
|
+
console.log('')
|
|
50
|
+
console.log(chalk.bold('âī¸ Frontend'))
|
|
51
|
+
console.log(` ${chalk.gray('â')} http://localhost:${vitePort}`)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Swagger docs (if enabled)
|
|
55
|
+
if (swaggerPath) {
|
|
56
|
+
console.log('')
|
|
57
|
+
console.log(chalk.bold('đ Documentation'))
|
|
58
|
+
console.log(` ${chalk.gray('â')} Swagger: http://localhost:${port}${swaggerPath}`)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Environment and plugins
|
|
62
|
+
console.log('')
|
|
63
|
+
console.log(chalk.bold('âšī¸ Info'))
|
|
64
|
+
console.log(` ${chalk.gray('â')} Environment: ${chalk.green(environment)}`)
|
|
65
|
+
console.log(` ${chalk.gray('â')} Plugins: ${chalk.yellow(pluginCount)}`)
|
|
66
|
+
|
|
67
|
+
// Show Vite embedded status when applicable
|
|
68
|
+
if (viteEmbedded && vitePort) {
|
|
69
|
+
console.log(` ${chalk.gray('â')} Vite: ${chalk.magenta('embedded')} ${chalk.gray(`(port ${vitePort})`)}`)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log('\n' + chalk.green('⨠Ready!') + chalk.gray(' Press Ctrl+C to stop\n'))
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Display simple plugin loaded message
|
|
77
|
+
*/
|
|
78
|
+
export function logPluginLoaded(name: string, version?: string): void {
|
|
79
|
+
const versionStr = version ? chalk.gray(`v${version}`) : ''
|
|
80
|
+
LOG(`${chalk.green('â')} Plugin loaded: ${chalk.cyan(name)} ${versionStr}`)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Display plugin count summary
|
|
85
|
+
*/
|
|
86
|
+
export function logPluginsSummary(count: number): void {
|
|
87
|
+
if (count === 0) {
|
|
88
|
+
LOG(chalk.yellow('â No plugins loaded'))
|
|
89
|
+
} else {
|
|
90
|
+
LOG(chalk.green(`â ${count} plugin${count > 1 ? 's' : ''} loaded successfully`))
|
|
91
|
+
}
|
|
92
|
+
}
|