create-fluxstack 1.16.0 → 1.17.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/CHANGELOG.md +80 -0
- package/app/client/src/App.tsx +8 -0
- package/app/client/src/live/AuthDemo.tsx +4 -4
- package/core/build/bundler.ts +40 -26
- package/core/build/flux-plugins-generator.ts +325 -325
- package/core/build/index.ts +92 -21
- package/core/cli/command-registry.ts +44 -46
- package/core/cli/commands/build.ts +11 -6
- package/core/cli/commands/create.ts +7 -5
- package/core/cli/commands/dev.ts +6 -5
- package/core/cli/commands/help.ts +3 -2
- package/core/cli/commands/make-plugin.ts +8 -7
- package/core/cli/commands/plugin-add.ts +60 -43
- package/core/cli/commands/plugin-deps.ts +73 -57
- package/core/cli/commands/plugin-list.ts +44 -41
- package/core/cli/commands/plugin-remove.ts +33 -22
- package/core/cli/generators/component.ts +770 -769
- package/core/cli/generators/controller.ts +9 -8
- package/core/cli/generators/index.ts +148 -146
- package/core/cli/generators/interactive.ts +228 -227
- package/core/cli/generators/plugin.ts +11 -10
- package/core/cli/generators/prompts.ts +83 -82
- package/core/cli/generators/route.ts +7 -6
- package/core/cli/generators/service.ts +10 -9
- package/core/cli/generators/template-engine.ts +2 -1
- package/core/cli/generators/types.ts +7 -7
- package/core/cli/generators/utils.ts +191 -191
- package/core/cli/index.ts +9 -8
- package/core/cli/plugin-discovery.ts +2 -2
- package/core/client/hooks/useAuth.ts +48 -48
- package/core/client/standalone.ts +18 -17
- package/core/client/state/createStore.ts +192 -192
- package/core/client/state/index.ts +14 -14
- package/core/config/index.ts +1 -0
- package/core/framework/client.ts +131 -131
- package/core/framework/index.ts +7 -7
- package/core/framework/server.ts +73 -113
- package/core/framework/types.ts +2 -2
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +6 -3
- package/core/plugins/built-in/monitoring/index.ts +110 -68
- package/core/plugins/built-in/static/index.ts +2 -2
- package/core/plugins/built-in/swagger/index.ts +9 -9
- package/core/plugins/built-in/vite/index.ts +3 -3
- package/core/plugins/built-in/vite/vite-dev.ts +3 -3
- package/core/plugins/config.ts +50 -47
- package/core/plugins/discovery.ts +10 -4
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/index.ts +206 -203
- package/core/plugins/manager.ts +21 -20
- package/core/plugins/registry.ts +76 -12
- package/core/plugins/types.ts +14 -14
- package/core/server/framework.ts +3 -189
- package/core/server/live/auto-generated-components.ts +11 -29
- package/core/server/live/index.ts +41 -31
- package/core/server/live/websocket-plugin.ts +11 -1
- package/core/server/middleware/elysia-helpers.ts +16 -15
- package/core/server/middleware/errorHandling.ts +14 -14
- package/core/server/middleware/index.ts +31 -31
- package/core/server/plugins/database.ts +181 -180
- package/core/server/plugins/static-files-plugin.ts +4 -3
- package/core/server/plugins/swagger.ts +11 -8
- package/core/server/rooms/RoomBroadcaster.ts +11 -10
- package/core/server/rooms/RoomSystem.ts +14 -11
- package/core/server/services/BaseService.ts +7 -7
- package/core/server/services/ServiceContainer.ts +5 -5
- package/core/server/services/index.ts +8 -8
- package/core/templates/create-project.ts +28 -27
- package/core/testing/index.ts +9 -9
- package/core/testing/setup.ts +73 -73
- package/core/types/api.ts +168 -168
- package/core/types/config.ts +5 -5
- package/core/types/index.ts +1 -1
- package/core/types/plugin.ts +2 -2
- package/core/types/types.ts +3 -3
- package/core/utils/build-logger.ts +324 -324
- package/core/utils/config-schema.ts +480 -480
- package/core/utils/env.ts +10 -8
- package/core/utils/errors/codes.ts +114 -114
- package/core/utils/errors/handlers.ts +30 -20
- package/core/utils/errors/index.ts +54 -46
- package/core/utils/errors/middleware.ts +113 -113
- package/core/utils/helpers.ts +19 -16
- package/core/utils/logger/colors.ts +114 -114
- package/core/utils/logger/config.ts +2 -2
- package/core/utils/logger/formatter.ts +82 -82
- package/core/utils/logger/group-logger.ts +101 -101
- package/core/utils/logger/index.ts +13 -3
- package/core/utils/logger/startup-banner.ts +2 -2
- package/core/utils/logger/winston-logger.ts +152 -152
- package/core/utils/monitoring/index.ts +211 -211
- package/core/utils/sync-version.ts +67 -66
- package/core/utils/version.ts +1 -1
- package/package.json +104 -100
- package/playwright-report/index.html +85 -0
- package/playwright.config.ts +31 -0
- package/plugins/crypto-auth/client/CryptoAuthClient.ts +302 -302
- package/plugins/crypto-auth/client/components/index.ts +11 -11
- package/plugins/crypto-auth/client/index.ts +11 -11
- package/plugins/crypto-auth/package.json +65 -65
- package/plugins/crypto-auth/server/CryptoAuthService.ts +185 -185
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +6 -5
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +6 -5
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +3 -3
- package/plugins/crypto-auth/server/middlewares/index.ts +22 -22
- package/plugins/crypto-auth/server/middlewares.ts +19 -19
- package/vite.config.ts +13 -0
- package/app/client/.live-stubs/LiveAdminPanel.js +0 -5
- package/app/client/.live-stubs/LiveCounter.js +0 -9
- package/app/client/.live-stubs/LiveForm.js +0 -11
- package/app/client/.live-stubs/LiveLocalCounter.js +0 -8
- package/app/client/.live-stubs/LivePingPong.js +0 -10
- package/app/client/.live-stubs/LiveRoomChat.js +0 -11
- package/app/client/.live-stubs/LiveSharedCounter.js +0 -10
- package/app/client/.live-stubs/LiveUpload.js +0 -15
- package/app/server/live/register-components.ts +0 -19
- package/core/build/live-components-generator.ts +0 -321
- package/core/live/ComponentRegistry.ts +0 -403
- package/core/live/types.ts +0 -241
- package/workspace.json +0 -6
|
@@ -1,324 +1,324 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FluxStack Build Logger - Beautiful terminal output for build process
|
|
3
|
-
* Provides formatted tables, boxes, and colored output
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
// ANSI Color codes
|
|
7
|
-
const colors = {
|
|
8
|
-
reset: '\x1b[0m',
|
|
9
|
-
bright: '\x1b[1m',
|
|
10
|
-
dim: '\x1b[2m',
|
|
11
|
-
|
|
12
|
-
// Text colors
|
|
13
|
-
cyan: '\x1b[36m',
|
|
14
|
-
blue: '\x1b[34m',
|
|
15
|
-
green: '\x1b[32m',
|
|
16
|
-
yellow: '\x1b[33m',
|
|
17
|
-
red: '\x1b[31m',
|
|
18
|
-
magenta: '\x1b[35m',
|
|
19
|
-
white: '\x1b[37m',
|
|
20
|
-
gray: '\x1b[90m',
|
|
21
|
-
|
|
22
|
-
// Background colors
|
|
23
|
-
bgCyan: '\x1b[46m',
|
|
24
|
-
bgBlue: '\x1b[44m',
|
|
25
|
-
bgGreen: '\x1b[42m',
|
|
26
|
-
bgYellow: '\x1b[43m',
|
|
27
|
-
bgRed: '\x1b[41m',
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Box drawing characters
|
|
31
|
-
const box = {
|
|
32
|
-
topLeft: '╭',
|
|
33
|
-
topRight: '╮',
|
|
34
|
-
bottomLeft: '╰',
|
|
35
|
-
bottomRight: '╯',
|
|
36
|
-
horizontal: '─',
|
|
37
|
-
vertical: '│',
|
|
38
|
-
verticalRight: '├',
|
|
39
|
-
verticalLeft: '┤',
|
|
40
|
-
horizontalDown: '┬',
|
|
41
|
-
horizontalUp: '┴',
|
|
42
|
-
cross: '┼',
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export interface TableColumn {
|
|
46
|
-
header: string
|
|
47
|
-
key: string
|
|
48
|
-
width?: number
|
|
49
|
-
align?: 'left' | 'right' | 'center'
|
|
50
|
-
color?: keyof typeof colors
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface TableRow {
|
|
54
|
-
[key: string]: string | number
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export class BuildLogger {
|
|
58
|
-
private indent = ''
|
|
59
|
-
private startTime = Date.now()
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Print a beautiful header banner
|
|
63
|
-
*/
|
|
64
|
-
header(title: string) {
|
|
65
|
-
const width = 60
|
|
66
|
-
const padding = Math.floor((width - title.length - 2) / 2)
|
|
67
|
-
const paddingRight = width - title.length - 2 - padding
|
|
68
|
-
|
|
69
|
-
console.log()
|
|
70
|
-
console.log(colors.cyan + colors.bright + box.topLeft + box.horizontal.repeat(width) + box.topRight + colors.reset)
|
|
71
|
-
console.log(colors.cyan + box.vertical + ' '.repeat(padding) + colors.bright + colors.white + title + colors.cyan + ' '.repeat(paddingRight) + box.vertical + colors.reset)
|
|
72
|
-
console.log(colors.cyan + box.bottomLeft + box.horizontal.repeat(width) + box.bottomRight + colors.reset)
|
|
73
|
-
console.log()
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Print a section header
|
|
78
|
-
*/
|
|
79
|
-
section(title: string, icon: string = '📦') {
|
|
80
|
-
console.log()
|
|
81
|
-
console.log(colors.bright + colors.blue + `${icon} ${title}` + colors.reset)
|
|
82
|
-
console.log(colors.dim + colors.gray + box.horizontal.repeat(50) + colors.reset)
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Print a success message
|
|
87
|
-
*/
|
|
88
|
-
success(message: string) {
|
|
89
|
-
console.log(colors.green + '✓ ' + colors.reset + message)
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Print an error message
|
|
94
|
-
*/
|
|
95
|
-
error(message: string) {
|
|
96
|
-
console.log(colors.red + '✗ ' + colors.reset + message)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Print a warning message
|
|
101
|
-
*/
|
|
102
|
-
warn(message: string) {
|
|
103
|
-
console.log(colors.yellow + '⚠ ' + colors.reset + message)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Print an info message
|
|
108
|
-
*/
|
|
109
|
-
info(message: string,
|
|
110
|
-
console.log(colors.cyan +
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Print a step message
|
|
115
|
-
*/
|
|
116
|
-
step(message: string,
|
|
117
|
-
console.log(colors.dim + colors.gray +
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Print a table
|
|
122
|
-
*/
|
|
123
|
-
table(columns: TableColumn[], rows: TableRow[]) {
|
|
124
|
-
if (rows.length === 0) {
|
|
125
|
-
this.warn('No data to display')
|
|
126
|
-
return
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
// Calculate column widths
|
|
130
|
-
const widths = columns.map(col => {
|
|
131
|
-
if (col.width) return col.width
|
|
132
|
-
const maxContentWidth = Math.max(
|
|
133
|
-
col.header.length,
|
|
134
|
-
...rows.map(row => String(row[col.key] || '').length)
|
|
135
|
-
)
|
|
136
|
-
return Math.min(maxContentWidth, 40) // Max 40 chars per column
|
|
137
|
-
})
|
|
138
|
-
|
|
139
|
-
const totalWidth = widths.reduce((sum, w) => sum + w, 0) + (columns.length - 1) * 3 + 4
|
|
140
|
-
|
|
141
|
-
// Print top border
|
|
142
|
-
console.log(
|
|
143
|
-
colors.gray + box.topLeft +
|
|
144
|
-
widths.map((w, i) =>
|
|
145
|
-
box.horizontal.repeat(w + 2) + (i < widths.length - 1 ? box.horizontalDown : '')
|
|
146
|
-
).join('') +
|
|
147
|
-
box.topRight + colors.reset
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
// Print header
|
|
151
|
-
const headerRow = columns.map((col, i) => {
|
|
152
|
-
const content = this.padContent(col.header, widths[i], 'center')
|
|
153
|
-
return colors.bright + colors.white + content + colors.reset
|
|
154
|
-
}).join(colors.gray + ' │ ' + colors.reset)
|
|
155
|
-
|
|
156
|
-
console.log(colors.gray + box.vertical + ' ' + colors.reset + headerRow + colors.gray + ' ' + box.vertical + colors.reset)
|
|
157
|
-
|
|
158
|
-
// Print header separator
|
|
159
|
-
console.log(
|
|
160
|
-
colors.gray + box.verticalRight +
|
|
161
|
-
widths.map((w, i) =>
|
|
162
|
-
box.horizontal.repeat(w + 2) + (i < widths.length - 1 ? box.cross : '')
|
|
163
|
-
).join('') +
|
|
164
|
-
box.verticalLeft + colors.reset
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
// Print rows
|
|
168
|
-
rows.forEach((row, rowIndex) => {
|
|
169
|
-
const rowContent = columns.map((col, i) => {
|
|
170
|
-
const value = String(row[col.key] || '')
|
|
171
|
-
const content = this.padContent(value, widths[i], col.align || 'left')
|
|
172
|
-
const color = col.color ? colors[col.color] : ''
|
|
173
|
-
return color + content + colors.reset
|
|
174
|
-
}).join(colors.gray + ' │ ' + colors.reset)
|
|
175
|
-
|
|
176
|
-
console.log(colors.gray + box.vertical + ' ' + colors.reset + rowContent + colors.gray + ' ' + box.vertical + colors.reset)
|
|
177
|
-
})
|
|
178
|
-
|
|
179
|
-
// Print bottom border
|
|
180
|
-
console.log(
|
|
181
|
-
colors.gray + box.bottomLeft +
|
|
182
|
-
widths.map((w, i) =>
|
|
183
|
-
box.horizontal.repeat(w + 2) + (i < widths.length - 1 ? box.horizontalUp : '')
|
|
184
|
-
).join('') +
|
|
185
|
-
box.bottomRight + colors.reset
|
|
186
|
-
)
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
/**
|
|
190
|
-
* Print a simple info box
|
|
191
|
-
*/
|
|
192
|
-
box(title: string, items: Array<{ label: string; value: string | number; color?: keyof typeof colors }>) {
|
|
193
|
-
const maxLabelWidth = Math.max(...items.map(i => i.label.length))
|
|
194
|
-
const maxValueWidth = Math.max(...items.map(i => String(i.value).length))
|
|
195
|
-
const contentWidth = maxLabelWidth + maxValueWidth + 3
|
|
196
|
-
const boxWidth = Math.max(contentWidth, title.length) + 4
|
|
197
|
-
|
|
198
|
-
// Top border with title
|
|
199
|
-
console.log()
|
|
200
|
-
console.log(colors.cyan + box.topLeft + box.horizontal.repeat(2) + colors.bright + colors.white + title + colors.cyan + box.horizontal.repeat(boxWidth - title.length - 2) + box.topRight + colors.reset)
|
|
201
|
-
|
|
202
|
-
// Content
|
|
203
|
-
items.forEach(item => {
|
|
204
|
-
const label = item.label.padEnd(maxLabelWidth)
|
|
205
|
-
const value = String(item.value)
|
|
206
|
-
const valueColor = item.color ? colors[item.color] : colors.white
|
|
207
|
-
console.log(
|
|
208
|
-
colors.cyan + box.vertical + ' ' + colors.reset +
|
|
209
|
-
colors.gray + label + colors.reset +
|
|
210
|
-
colors.dim + ' : ' + colors.reset +
|
|
211
|
-
valueColor + colors.bright + value + colors.reset +
|
|
212
|
-
' '.repeat(boxWidth - label.length - value.length - 3) +
|
|
213
|
-
colors.cyan + box.vertical + colors.reset
|
|
214
|
-
)
|
|
215
|
-
})
|
|
216
|
-
|
|
217
|
-
// Bottom border
|
|
218
|
-
console.log(colors.cyan + box.bottomLeft + box.horizontal.repeat(boxWidth) + box.bottomRight + colors.reset)
|
|
219
|
-
console.log()
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Print a progress indicator
|
|
224
|
-
*/
|
|
225
|
-
progress(current: number, total: number, label: string) {
|
|
226
|
-
const percentage = Math.round((current / total) * 100)
|
|
227
|
-
const barLength = 30
|
|
228
|
-
const filled = Math.round((percentage / 100) * barLength)
|
|
229
|
-
const empty = barLength - filled
|
|
230
|
-
|
|
231
|
-
const bar = colors.green + '█'.repeat(filled) + colors.gray + '░'.repeat(empty) + colors.reset
|
|
232
|
-
console.log(`${label} [${bar}] ${percentage}% (${current}/${total})`)
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Start a timer
|
|
237
|
-
*/
|
|
238
|
-
startTimer(label?: string) {
|
|
239
|
-
this.startTime = Date.now()
|
|
240
|
-
if (label) {
|
|
241
|
-
this.info(label, '⏱')
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
/**
|
|
246
|
-
* End timer and print elapsed time
|
|
247
|
-
*/
|
|
248
|
-
endTimer(label: string = 'Completed') {
|
|
249
|
-
const elapsed = Date.now() - this.startTime
|
|
250
|
-
const seconds = (elapsed / 1000).toFixed(2)
|
|
251
|
-
this.success(`${label} in ${colors.bright}${seconds}s${colors.reset}`)
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
/**
|
|
255
|
-
* Print a summary box
|
|
256
|
-
*/
|
|
257
|
-
summary(title: string, stats: Array<{ label: string; value: string | number; highlight?: boolean }>) {
|
|
258
|
-
console.log()
|
|
259
|
-
console.log(colors.bright + colors.green + '╔═══════════════════════════════════════════════════════════╗' + colors.reset)
|
|
260
|
-
console.log(colors.bright + colors.green + '║' + colors.reset + colors.bright + colors.white + ` ${title}`.padEnd(60) + colors.bright + colors.green + '║' + colors.reset)
|
|
261
|
-
console.log(colors.bright + colors.green + '╠═══════════════════════════════════════════════════════════╣' + colors.reset)
|
|
262
|
-
|
|
263
|
-
stats.forEach(stat => {
|
|
264
|
-
const label = ` ${stat.label}:`
|
|
265
|
-
const value = String(stat.value)
|
|
266
|
-
const valueColor = stat.highlight ? colors.yellow + colors.bright : colors.white
|
|
267
|
-
const padding = 60 - label.length - value.length - 1
|
|
268
|
-
console.log(
|
|
269
|
-
colors.bright + colors.green + '║' + colors.reset +
|
|
270
|
-
colors.cyan + label + colors.reset +
|
|
271
|
-
' '.repeat(Math.max(padding, 1)) +
|
|
272
|
-
valueColor + value + colors.reset +
|
|
273
|
-
colors.bright + colors.green + ' ║' + colors.reset
|
|
274
|
-
)
|
|
275
|
-
})
|
|
276
|
-
|
|
277
|
-
console.log(colors.bright + colors.green + '╚═══════════════════════════════════════════════════════════╝' + colors.reset)
|
|
278
|
-
console.log()
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
* Pad content based on alignment
|
|
283
|
-
*/
|
|
284
|
-
private padContent(content: string, width: number, align: 'left' | 'right' | 'center' = 'left'): string {
|
|
285
|
-
if (content.length >= width) {
|
|
286
|
-
return content.slice(0, width)
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const padding = width - content.length
|
|
290
|
-
|
|
291
|
-
switch (align) {
|
|
292
|
-
case 'right':
|
|
293
|
-
return ' '.repeat(padding) + content
|
|
294
|
-
case 'center':
|
|
295
|
-
const leftPad = Math.floor(padding / 2)
|
|
296
|
-
const rightPad = padding - leftPad
|
|
297
|
-
return ' '.repeat(leftPad) + content + ' '.repeat(rightPad)
|
|
298
|
-
default:
|
|
299
|
-
return content + ' '.repeat(padding)
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Format file size
|
|
305
|
-
*/
|
|
306
|
-
formatSize(bytes: number): string {
|
|
307
|
-
if (bytes === 0) return '0 B'
|
|
308
|
-
const k = 1024
|
|
309
|
-
const sizes = ['B', 'KB', 'MB', 'GB']
|
|
310
|
-
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
311
|
-
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Format duration
|
|
316
|
-
*/
|
|
317
|
-
formatDuration(ms: number): string {
|
|
318
|
-
if (ms < 1000) return `${ms}ms`
|
|
319
|
-
return `${(ms / 1000).toFixed(2)}s`
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
// Export singleton instance
|
|
324
|
-
export const buildLogger = new BuildLogger()
|
|
1
|
+
/**
|
|
2
|
+
* FluxStack Build Logger - Beautiful terminal output for build process
|
|
3
|
+
* Provides formatted tables, boxes, and colored output
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ANSI Color codes
|
|
7
|
+
const colors = {
|
|
8
|
+
reset: '\x1b[0m',
|
|
9
|
+
bright: '\x1b[1m',
|
|
10
|
+
dim: '\x1b[2m',
|
|
11
|
+
|
|
12
|
+
// Text colors
|
|
13
|
+
cyan: '\x1b[36m',
|
|
14
|
+
blue: '\x1b[34m',
|
|
15
|
+
green: '\x1b[32m',
|
|
16
|
+
yellow: '\x1b[33m',
|
|
17
|
+
red: '\x1b[31m',
|
|
18
|
+
magenta: '\x1b[35m',
|
|
19
|
+
white: '\x1b[37m',
|
|
20
|
+
gray: '\x1b[90m',
|
|
21
|
+
|
|
22
|
+
// Background colors
|
|
23
|
+
bgCyan: '\x1b[46m',
|
|
24
|
+
bgBlue: '\x1b[44m',
|
|
25
|
+
bgGreen: '\x1b[42m',
|
|
26
|
+
bgYellow: '\x1b[43m',
|
|
27
|
+
bgRed: '\x1b[41m',
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Box drawing characters
|
|
31
|
+
const box = {
|
|
32
|
+
topLeft: '╭',
|
|
33
|
+
topRight: '╮',
|
|
34
|
+
bottomLeft: '╰',
|
|
35
|
+
bottomRight: '╯',
|
|
36
|
+
horizontal: '─',
|
|
37
|
+
vertical: '│',
|
|
38
|
+
verticalRight: '├',
|
|
39
|
+
verticalLeft: '┤',
|
|
40
|
+
horizontalDown: '┬',
|
|
41
|
+
horizontalUp: '┴',
|
|
42
|
+
cross: '┼',
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface TableColumn {
|
|
46
|
+
header: string
|
|
47
|
+
key: string
|
|
48
|
+
width?: number
|
|
49
|
+
align?: 'left' | 'right' | 'center'
|
|
50
|
+
color?: keyof typeof colors
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface TableRow {
|
|
54
|
+
[key: string]: string | number
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export class BuildLogger {
|
|
58
|
+
private indent = ''
|
|
59
|
+
private startTime = Date.now()
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Print a beautiful header banner
|
|
63
|
+
*/
|
|
64
|
+
header(title: string) {
|
|
65
|
+
const width = 60
|
|
66
|
+
const padding = Math.floor((width - title.length - 2) / 2)
|
|
67
|
+
const paddingRight = width - title.length - 2 - padding
|
|
68
|
+
|
|
69
|
+
console.log()
|
|
70
|
+
console.log(colors.cyan + colors.bright + box.topLeft + box.horizontal.repeat(width) + box.topRight + colors.reset)
|
|
71
|
+
console.log(colors.cyan + box.vertical + ' '.repeat(padding) + colors.bright + colors.white + title + colors.cyan + ' '.repeat(paddingRight) + box.vertical + colors.reset)
|
|
72
|
+
console.log(colors.cyan + box.bottomLeft + box.horizontal.repeat(width) + box.bottomRight + colors.reset)
|
|
73
|
+
console.log()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Print a section header
|
|
78
|
+
*/
|
|
79
|
+
section(title: string, icon: string = '📦') {
|
|
80
|
+
console.log()
|
|
81
|
+
console.log(colors.bright + colors.blue + `${icon} ${title}` + colors.reset)
|
|
82
|
+
console.log(colors.dim + colors.gray + box.horizontal.repeat(50) + colors.reset)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Print a success message
|
|
87
|
+
*/
|
|
88
|
+
success(message: string, ...args: unknown[]) {
|
|
89
|
+
console.log(colors.green + '✓ ' + colors.reset + message, ...args)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Print an error message
|
|
94
|
+
*/
|
|
95
|
+
error(message: string, ...args: unknown[]) {
|
|
96
|
+
console.log(colors.red + '✗ ' + colors.reset + message, ...args)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Print a warning message
|
|
101
|
+
*/
|
|
102
|
+
warn(message: string, ...args: unknown[]) {
|
|
103
|
+
console.log(colors.yellow + '⚠ ' + colors.reset + message, ...args)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Print an info message
|
|
108
|
+
*/
|
|
109
|
+
info(message: string, ...args: unknown[]) {
|
|
110
|
+
console.log(colors.cyan + '→ ' + colors.reset + message, ...args)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Print a step message
|
|
115
|
+
*/
|
|
116
|
+
step(message: string, ...args: unknown[]) {
|
|
117
|
+
console.log(colors.dim + colors.gray + '▸ ' + colors.reset + message, ...args)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Print a table
|
|
122
|
+
*/
|
|
123
|
+
table(columns: TableColumn[], rows: TableRow[]) {
|
|
124
|
+
if (rows.length === 0) {
|
|
125
|
+
this.warn('No data to display')
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// Calculate column widths
|
|
130
|
+
const widths = columns.map(col => {
|
|
131
|
+
if (col.width) return col.width
|
|
132
|
+
const maxContentWidth = Math.max(
|
|
133
|
+
col.header.length,
|
|
134
|
+
...rows.map(row => String(row[col.key] || '').length)
|
|
135
|
+
)
|
|
136
|
+
return Math.min(maxContentWidth, 40) // Max 40 chars per column
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
const totalWidth = widths.reduce((sum, w) => sum + w, 0) + (columns.length - 1) * 3 + 4
|
|
140
|
+
|
|
141
|
+
// Print top border
|
|
142
|
+
console.log(
|
|
143
|
+
colors.gray + box.topLeft +
|
|
144
|
+
widths.map((w, i) =>
|
|
145
|
+
box.horizontal.repeat(w + 2) + (i < widths.length - 1 ? box.horizontalDown : '')
|
|
146
|
+
).join('') +
|
|
147
|
+
box.topRight + colors.reset
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
// Print header
|
|
151
|
+
const headerRow = columns.map((col, i) => {
|
|
152
|
+
const content = this.padContent(col.header, widths[i], 'center')
|
|
153
|
+
return colors.bright + colors.white + content + colors.reset
|
|
154
|
+
}).join(colors.gray + ' │ ' + colors.reset)
|
|
155
|
+
|
|
156
|
+
console.log(colors.gray + box.vertical + ' ' + colors.reset + headerRow + colors.gray + ' ' + box.vertical + colors.reset)
|
|
157
|
+
|
|
158
|
+
// Print header separator
|
|
159
|
+
console.log(
|
|
160
|
+
colors.gray + box.verticalRight +
|
|
161
|
+
widths.map((w, i) =>
|
|
162
|
+
box.horizontal.repeat(w + 2) + (i < widths.length - 1 ? box.cross : '')
|
|
163
|
+
).join('') +
|
|
164
|
+
box.verticalLeft + colors.reset
|
|
165
|
+
)
|
|
166
|
+
|
|
167
|
+
// Print rows
|
|
168
|
+
rows.forEach((row, rowIndex) => {
|
|
169
|
+
const rowContent = columns.map((col, i) => {
|
|
170
|
+
const value = String(row[col.key] || '')
|
|
171
|
+
const content = this.padContent(value, widths[i], col.align || 'left')
|
|
172
|
+
const color = col.color ? colors[col.color] : ''
|
|
173
|
+
return color + content + colors.reset
|
|
174
|
+
}).join(colors.gray + ' │ ' + colors.reset)
|
|
175
|
+
|
|
176
|
+
console.log(colors.gray + box.vertical + ' ' + colors.reset + rowContent + colors.gray + ' ' + box.vertical + colors.reset)
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
// Print bottom border
|
|
180
|
+
console.log(
|
|
181
|
+
colors.gray + box.bottomLeft +
|
|
182
|
+
widths.map((w, i) =>
|
|
183
|
+
box.horizontal.repeat(w + 2) + (i < widths.length - 1 ? box.horizontalUp : '')
|
|
184
|
+
).join('') +
|
|
185
|
+
box.bottomRight + colors.reset
|
|
186
|
+
)
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Print a simple info box
|
|
191
|
+
*/
|
|
192
|
+
box(title: string, items: Array<{ label: string; value: string | number; color?: keyof typeof colors }>) {
|
|
193
|
+
const maxLabelWidth = Math.max(...items.map(i => i.label.length))
|
|
194
|
+
const maxValueWidth = Math.max(...items.map(i => String(i.value).length))
|
|
195
|
+
const contentWidth = maxLabelWidth + maxValueWidth + 3
|
|
196
|
+
const boxWidth = Math.max(contentWidth, title.length) + 4
|
|
197
|
+
|
|
198
|
+
// Top border with title
|
|
199
|
+
console.log()
|
|
200
|
+
console.log(colors.cyan + box.topLeft + box.horizontal.repeat(2) + colors.bright + colors.white + title + colors.cyan + box.horizontal.repeat(boxWidth - title.length - 2) + box.topRight + colors.reset)
|
|
201
|
+
|
|
202
|
+
// Content
|
|
203
|
+
items.forEach(item => {
|
|
204
|
+
const label = item.label.padEnd(maxLabelWidth)
|
|
205
|
+
const value = String(item.value)
|
|
206
|
+
const valueColor = item.color ? colors[item.color] : colors.white
|
|
207
|
+
console.log(
|
|
208
|
+
colors.cyan + box.vertical + ' ' + colors.reset +
|
|
209
|
+
colors.gray + label + colors.reset +
|
|
210
|
+
colors.dim + ' : ' + colors.reset +
|
|
211
|
+
valueColor + colors.bright + value + colors.reset +
|
|
212
|
+
' '.repeat(boxWidth - label.length - value.length - 3) +
|
|
213
|
+
colors.cyan + box.vertical + colors.reset
|
|
214
|
+
)
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
// Bottom border
|
|
218
|
+
console.log(colors.cyan + box.bottomLeft + box.horizontal.repeat(boxWidth) + box.bottomRight + colors.reset)
|
|
219
|
+
console.log()
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Print a progress indicator
|
|
224
|
+
*/
|
|
225
|
+
progress(current: number, total: number, label: string) {
|
|
226
|
+
const percentage = Math.round((current / total) * 100)
|
|
227
|
+
const barLength = 30
|
|
228
|
+
const filled = Math.round((percentage / 100) * barLength)
|
|
229
|
+
const empty = barLength - filled
|
|
230
|
+
|
|
231
|
+
const bar = colors.green + '█'.repeat(filled) + colors.gray + '░'.repeat(empty) + colors.reset
|
|
232
|
+
console.log(`${label} [${bar}] ${percentage}% (${current}/${total})`)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Start a timer
|
|
237
|
+
*/
|
|
238
|
+
startTimer(label?: string) {
|
|
239
|
+
this.startTime = Date.now()
|
|
240
|
+
if (label) {
|
|
241
|
+
this.info(label, '⏱')
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* End timer and print elapsed time
|
|
247
|
+
*/
|
|
248
|
+
endTimer(label: string = 'Completed') {
|
|
249
|
+
const elapsed = Date.now() - this.startTime
|
|
250
|
+
const seconds = (elapsed / 1000).toFixed(2)
|
|
251
|
+
this.success(`${label} in ${colors.bright}${seconds}s${colors.reset}`)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Print a summary box
|
|
256
|
+
*/
|
|
257
|
+
summary(title: string, stats: Array<{ label: string; value: string | number; highlight?: boolean }>) {
|
|
258
|
+
console.log()
|
|
259
|
+
console.log(colors.bright + colors.green + '╔═══════════════════════════════════════════════════════════╗' + colors.reset)
|
|
260
|
+
console.log(colors.bright + colors.green + '║' + colors.reset + colors.bright + colors.white + ` ${title}`.padEnd(60) + colors.bright + colors.green + '║' + colors.reset)
|
|
261
|
+
console.log(colors.bright + colors.green + '╠═══════════════════════════════════════════════════════════╣' + colors.reset)
|
|
262
|
+
|
|
263
|
+
stats.forEach(stat => {
|
|
264
|
+
const label = ` ${stat.label}:`
|
|
265
|
+
const value = String(stat.value)
|
|
266
|
+
const valueColor = stat.highlight ? colors.yellow + colors.bright : colors.white
|
|
267
|
+
const padding = 60 - label.length - value.length - 1
|
|
268
|
+
console.log(
|
|
269
|
+
colors.bright + colors.green + '║' + colors.reset +
|
|
270
|
+
colors.cyan + label + colors.reset +
|
|
271
|
+
' '.repeat(Math.max(padding, 1)) +
|
|
272
|
+
valueColor + value + colors.reset +
|
|
273
|
+
colors.bright + colors.green + ' ║' + colors.reset
|
|
274
|
+
)
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
console.log(colors.bright + colors.green + '╚═══════════════════════════════════════════════════════════╝' + colors.reset)
|
|
278
|
+
console.log()
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Pad content based on alignment
|
|
283
|
+
*/
|
|
284
|
+
private padContent(content: string, width: number, align: 'left' | 'right' | 'center' = 'left'): string {
|
|
285
|
+
if (content.length >= width) {
|
|
286
|
+
return content.slice(0, width)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const padding = width - content.length
|
|
290
|
+
|
|
291
|
+
switch (align) {
|
|
292
|
+
case 'right':
|
|
293
|
+
return ' '.repeat(padding) + content
|
|
294
|
+
case 'center':
|
|
295
|
+
const leftPad = Math.floor(padding / 2)
|
|
296
|
+
const rightPad = padding - leftPad
|
|
297
|
+
return ' '.repeat(leftPad) + content + ' '.repeat(rightPad)
|
|
298
|
+
default:
|
|
299
|
+
return content + ' '.repeat(padding)
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Format file size
|
|
305
|
+
*/
|
|
306
|
+
formatSize(bytes: number): string {
|
|
307
|
+
if (bytes === 0) return '0 B'
|
|
308
|
+
const k = 1024
|
|
309
|
+
const sizes = ['B', 'KB', 'MB', 'GB']
|
|
310
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
311
|
+
return `${(bytes / Math.pow(k, i)).toFixed(2)} ${sizes[i]}`
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Format duration
|
|
316
|
+
*/
|
|
317
|
+
formatDuration(ms: number): string {
|
|
318
|
+
if (ms < 1000) return `${ms}ms`
|
|
319
|
+
return `${(ms / 1000).toFixed(2)}s`
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Export singleton instance
|
|
324
|
+
export const buildLogger = new BuildLogger()
|