create-fluxstack 1.7.5 ā 1.8.3
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/.dockerignore +82 -0
- package/.env.example +19 -0
- package/Dockerfile +70 -0
- package/README.md +6 -3
- package/app/client/SIMPLIFICATION.md +140 -0
- package/app/client/frontend-only.ts +1 -1
- package/app/client/src/App.tsx +148 -283
- package/app/client/src/index.css +5 -20
- package/app/client/src/lib/eden-api.ts +53 -220
- package/app/client/src/main.tsx +2 -3
- package/app/server/app.ts +20 -5
- package/app/server/backend-only.ts +15 -12
- package/app/server/controllers/users.controller.ts +57 -31
- package/app/server/index.ts +86 -96
- package/app/server/live/register-components.ts +18 -7
- package/app/server/routes/env-test.ts +110 -0
- package/app/server/routes/index.ts +1 -8
- package/app/server/routes/users.routes.ts +192 -91
- package/config/app.config.ts +2 -54
- package/config/client.config.ts +95 -0
- package/config/fluxstack.config.ts +2 -2
- package/config/index.ts +57 -22
- package/config/monitoring.config.ts +114 -0
- package/config/plugins.config.ts +80 -0
- package/config/runtime.config.ts +0 -17
- package/config/server.config.ts +50 -30
- package/core/build/bundler.ts +17 -16
- package/core/build/flux-plugins-generator.ts +34 -23
- package/core/build/index.ts +32 -31
- package/core/build/live-components-generator.ts +44 -30
- package/core/build/optimizer.ts +37 -17
- package/core/cli/command-registry.ts +4 -14
- package/core/cli/commands/plugin-deps.ts +8 -8
- package/core/cli/generators/component.ts +3 -3
- package/core/cli/generators/controller.ts +4 -4
- package/core/cli/generators/index.ts +8 -8
- package/core/cli/generators/interactive.ts +4 -4
- package/core/cli/generators/plugin.ts +3 -3
- package/core/cli/generators/prompts.ts +1 -1
- package/core/cli/generators/route.ts +27 -11
- package/core/cli/generators/service.ts +5 -5
- package/core/cli/generators/template-engine.ts +1 -1
- package/core/cli/generators/types.ts +1 -1
- package/core/cli/index.ts +158 -189
- package/core/cli/plugin-discovery.ts +3 -3
- package/core/client/hooks/index.ts +2 -2
- package/core/client/hooks/state-validator.ts +1 -1
- package/core/client/hooks/useAuth.ts +1 -1
- package/core/client/hooks/useChunkedUpload.ts +1 -1
- package/core/client/hooks/useHybridLiveComponent.ts +1 -1
- package/core/client/hooks/useWebSocket.ts +1 -1
- package/core/config/env.ts +5 -1
- package/core/config/runtime-config.ts +12 -10
- package/core/config/schema.ts +33 -2
- package/core/framework/server.ts +30 -14
- package/core/framework/types.ts +2 -2
- package/core/index.ts +31 -23
- package/core/live/ComponentRegistry.ts +1 -1
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +1 -1
- package/core/plugins/built-in/live-components/index.ts +1 -1
- package/core/plugins/built-in/monitoring/index.ts +65 -161
- package/core/plugins/built-in/static/index.ts +75 -277
- package/core/plugins/built-in/swagger/index.ts +301 -231
- package/core/plugins/built-in/vite/index.ts +342 -377
- package/core/plugins/config.ts +2 -2
- package/core/plugins/dependency-manager.ts +2 -2
- package/core/plugins/discovery.ts +1 -1
- package/core/plugins/executor.ts +2 -2
- package/core/plugins/manager.ts +19 -4
- package/core/plugins/module-resolver.ts +1 -1
- package/core/plugins/registry.ts +25 -21
- package/core/plugins/types.ts +147 -5
- package/core/server/backend-entry.ts +51 -0
- package/core/server/framework.ts +2 -2
- package/core/server/live/ComponentRegistry.ts +9 -26
- package/core/server/live/FileUploadManager.ts +1 -1
- package/core/server/live/auto-generated-components.ts +26 -0
- package/core/server/live/websocket-plugin.ts +211 -19
- package/core/server/middleware/errorHandling.ts +1 -1
- package/core/server/middleware/index.ts +4 -4
- package/core/server/plugins/database.ts +1 -2
- package/core/server/plugins/static-files-plugin.ts +259 -231
- package/core/server/plugins/swagger.ts +1 -1
- package/core/server/services/BaseService.ts +1 -1
- package/core/server/services/ServiceContainer.ts +1 -1
- package/core/server/services/index.ts +4 -4
- package/core/server/standalone.ts +16 -1
- package/core/testing/index.ts +1 -1
- package/core/testing/setup.ts +1 -1
- package/core/types/plugin.ts +6 -0
- package/core/utils/build-logger.ts +324 -0
- package/core/utils/config-schema.ts +2 -6
- package/core/utils/helpers.ts +14 -9
- package/core/utils/logger/startup-banner.ts +7 -33
- package/core/utils/regenerate-files.ts +69 -0
- package/core/utils/version.ts +6 -6
- package/create-fluxstack.ts +68 -25
- package/fluxstack.config.ts +138 -252
- package/package.json +3 -18
- package/plugins/crypto-auth/index.ts +52 -47
- package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/helpers.ts +16 -1
- package/vitest.config.ts +17 -26
- package/app/client/src/App.css +0 -883
- package/app/client/src/components/ErrorBoundary.tsx +0 -107
- package/app/client/src/components/ErrorDisplay.css +0 -365
- package/app/client/src/components/ErrorDisplay.tsx +0 -258
- package/app/client/src/components/FluxStackConfig.tsx +0 -1321
- package/app/client/src/components/HybridLiveCounter.tsx +0 -140
- package/app/client/src/components/LiveClock.tsx +0 -286
- package/app/client/src/components/MainLayout.tsx +0 -388
- package/app/client/src/components/SidebarNavigation.tsx +0 -391
- package/app/client/src/components/StateDemo.tsx +0 -178
- package/app/client/src/components/SystemMonitor.tsx +0 -1044
- package/app/client/src/components/UserProfile.tsx +0 -809
- package/app/client/src/hooks/useAuth.ts +0 -39
- package/app/client/src/hooks/useNotifications.ts +0 -56
- package/app/client/src/lib/errors.ts +0 -340
- package/app/client/src/lib/hooks/useErrorHandler.ts +0 -258
- package/app/client/src/lib/index.ts +0 -45
- package/app/client/src/pages/ApiDocs.tsx +0 -182
- package/app/client/src/pages/CryptoAuthPage.tsx +0 -394
- package/app/client/src/pages/Demo.tsx +0 -174
- package/app/client/src/pages/HybridLive.tsx +0 -263
- package/app/client/src/pages/Overview.tsx +0 -155
- package/app/client/src/store/README.md +0 -43
- package/app/client/src/store/index.ts +0 -16
- package/app/client/src/store/slices/uiSlice.ts +0 -151
- package/app/client/src/store/slices/userSlice.ts +0 -161
- package/app/client/src/test/README.md +0 -257
- package/app/client/src/test/setup.ts +0 -70
- package/app/client/src/test/types.ts +0 -12
- package/app/server/live/CounterComponent.ts +0 -191
- package/app/server/live/FluxStackConfig.ts +0 -534
- package/app/server/live/SidebarNavigation.ts +0 -157
- package/app/server/live/SystemMonitor.ts +0 -595
- package/app/server/live/SystemMonitorIntegration.ts +0 -151
- package/app/server/live/UserProfileComponent.ts +0 -141
- package/app/server/middleware/auth.ts +0 -136
- package/app/server/middleware/errorHandling.ts +0 -252
- package/app/server/middleware/index.ts +0 -10
- package/app/server/middleware/rateLimit.ts +0 -193
- package/app/server/middleware/requestLogging.ts +0 -215
- package/app/server/middleware/validation.ts +0 -270
- package/app/server/routes/config.ts +0 -145
- package/app/server/routes/crypto-auth-demo.routes.ts +0 -167
- package/app/server/routes/example-with-crypto-auth.routes.ts +0 -235
- package/app/server/routes/exemplo-posts.routes.ts +0 -161
- package/app/server/routes/upload.ts +0 -92
- package/app/server/services/NotificationService.ts +0 -302
- package/app/server/services/UserService.ts +0 -222
- package/app/server/services/index.ts +0 -46
- package/app/server/types/index.ts +0 -1
- package/config/build.config.ts +0 -24
|
@@ -0,0 +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, icon: string = 'ā') {
|
|
110
|
+
console.log(colors.cyan + icon + ' ' + colors.reset + message)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Print a step message
|
|
115
|
+
*/
|
|
116
|
+
step(message: string, icon: string = 'āø') {
|
|
117
|
+
console.log(colors.dim + colors.gray + icon + ' ' + colors.reset + message)
|
|
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()
|
|
@@ -23,17 +23,13 @@
|
|
|
23
23
|
* default: 3000,
|
|
24
24
|
* validate: (value) => value > 0 && value < 65536
|
|
25
25
|
* },
|
|
26
|
-
*
|
|
27
|
-
* type: 'boolean',
|
|
28
|
-
* env: 'DEBUG',
|
|
29
|
-
* default: false
|
|
30
|
-
* }
|
|
26
|
+
* env: config.enum('NODE_ENV', ['development', 'production', 'test'] as const, 'development', true)
|
|
31
27
|
* })
|
|
32
28
|
*
|
|
33
29
|
* // Access with full type safety
|
|
34
30
|
* appConfig.name // string
|
|
35
31
|
* appConfig.port // number
|
|
36
|
-
* appConfig.
|
|
32
|
+
* appConfig.env // "development" | "production" | "test"
|
|
37
33
|
* ```
|
|
38
34
|
*/
|
|
39
35
|
|
package/core/utils/helpers.ts
CHANGED
|
@@ -86,22 +86,27 @@ export const throttle = <T extends (...args: any[]) => any>(
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/**
|
|
90
|
+
* Environment detection utilities
|
|
91
|
+
* Uses declarative config system instead of legacy env
|
|
92
|
+
*/
|
|
93
|
+
|
|
89
94
|
export const isProduction = (): boolean => {
|
|
90
|
-
//
|
|
91
|
-
const {
|
|
92
|
-
return env
|
|
95
|
+
// Lazy import to avoid circular dependency during module initialization
|
|
96
|
+
const { appConfig } = require('@/config/app.config')
|
|
97
|
+
return appConfig.env === 'production'
|
|
93
98
|
}
|
|
94
99
|
|
|
95
100
|
export const isDevelopment = (): boolean => {
|
|
96
|
-
//
|
|
97
|
-
const {
|
|
98
|
-
return env
|
|
101
|
+
// Lazy import to avoid circular dependency during module initialization
|
|
102
|
+
const { appConfig } = require('@/config/app.config')
|
|
103
|
+
return appConfig.env === 'development'
|
|
99
104
|
}
|
|
100
105
|
|
|
101
106
|
export const isTest = (): boolean => {
|
|
102
|
-
//
|
|
103
|
-
const {
|
|
104
|
-
return env
|
|
107
|
+
// Lazy import to avoid circular dependency during module initialization
|
|
108
|
+
const { appConfig } = require('@/config/app.config')
|
|
109
|
+
return appConfig.env === 'test'
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
export const deepMerge = <T extends Record<string, any>>(target: T, source: Partial<T>): T => {
|
|
@@ -33,43 +33,17 @@ export function displayStartupBanner(info: StartupInfo): void {
|
|
|
33
33
|
pluginCount = 0,
|
|
34
34
|
vitePort,
|
|
35
35
|
viteEmbedded = false,
|
|
36
|
-
swaggerPath
|
|
37
36
|
} = info
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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})`)}`)
|
|
38
|
+
// Display plugins in compact format
|
|
39
|
+
const plugins = (global as any).__fluxstackPlugins || []
|
|
40
|
+
if (plugins.length > 0) {
|
|
41
|
+
const pluginList = plugins.map((p: any) => `${p.name} (${p.details})`).join(', ')
|
|
42
|
+
console.log(`Plugins (${plugins.length}): ${pluginList}\n`)
|
|
70
43
|
}
|
|
71
44
|
|
|
72
|
-
|
|
45
|
+
// Simple ready message
|
|
46
|
+
console.log(chalk.green('Server ready!') + chalk.gray(` Environment: ${environment}${viteEmbedded ? ' | Vite: embedded' : ''}\n`))
|
|
73
47
|
}
|
|
74
48
|
|
|
75
49
|
/**
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Regeneration Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to regenerate critical application files that might be
|
|
5
|
+
* accidentally deleted by developers.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { join } from "path"
|
|
9
|
+
import { existsSync } from "fs"
|
|
10
|
+
|
|
11
|
+
const BACKEND_ONLY_TEMPLATE = `/**
|
|
12
|
+
* Backend Standalone Entry Point
|
|
13
|
+
*
|
|
14
|
+
* This is a minimal wrapper for starting the backend in standalone mode.
|
|
15
|
+
* The core logic is protected in @/core/server/backend-entry.ts
|
|
16
|
+
*
|
|
17
|
+
* You can customize the configuration here if needed.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { startBackend, createBackendConfig } from "@/core/server/backend-entry"
|
|
21
|
+
import { apiRoutes } from "./routes"
|
|
22
|
+
import { serverConfig } from "@/config/server.config"
|
|
23
|
+
|
|
24
|
+
// Create backend configuration from declarative config
|
|
25
|
+
const backendConfig = createBackendConfig(serverConfig)
|
|
26
|
+
|
|
27
|
+
// Start backend in standalone mode
|
|
28
|
+
startBackend(apiRoutes, backendConfig)
|
|
29
|
+
`
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if backend-only.ts exists, regenerate if missing
|
|
33
|
+
*/
|
|
34
|
+
export async function ensureBackendEntry(projectRoot: string = process.cwd()): Promise<boolean> {
|
|
35
|
+
const backendOnlyPath = join(projectRoot, "app/server/backend-only.ts")
|
|
36
|
+
|
|
37
|
+
if (!existsSync(backendOnlyPath)) {
|
|
38
|
+
console.log("ā ļø backend-only.ts not found, regenerating...")
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
await Bun.write(backendOnlyPath, BACKEND_ONLY_TEMPLATE)
|
|
42
|
+
console.log("ā
backend-only.ts regenerated successfully")
|
|
43
|
+
return true
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error("ā Failed to regenerate backend-only.ts:", error)
|
|
46
|
+
return false
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return true
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Regenerate backend-only.ts file
|
|
55
|
+
*/
|
|
56
|
+
export async function regenerateBackendEntry(projectRoot: string = process.cwd()): Promise<boolean> {
|
|
57
|
+
const backendOnlyPath = join(projectRoot, "app/server/backend-only.ts")
|
|
58
|
+
|
|
59
|
+
console.log("š Regenerating backend-only.ts...")
|
|
60
|
+
|
|
61
|
+
try {
|
|
62
|
+
await Bun.write(backendOnlyPath, BACKEND_ONLY_TEMPLATE)
|
|
63
|
+
console.log("ā
backend-only.ts regenerated successfully")
|
|
64
|
+
return true
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.error("ā Failed to regenerate backend-only.ts:", error)
|
|
67
|
+
return false
|
|
68
|
+
}
|
|
69
|
+
}
|
package/core/utils/version.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FluxStack Framework Version
|
|
3
|
-
* Single source of truth for version number
|
|
4
|
-
* Auto-synced with package.json
|
|
5
|
-
*/
|
|
6
|
-
export const FLUXSTACK_VERSION = '1.
|
|
1
|
+
/**
|
|
2
|
+
* FluxStack Framework Version
|
|
3
|
+
* Single source of truth for version number
|
|
4
|
+
* Auto-synced with package.json
|
|
5
|
+
*/
|
|
6
|
+
export const FLUXSTACK_VERSION = '1.8.3'
|
package/create-fluxstack.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
3
|
import { program } from 'commander'
|
|
4
|
-
import { resolve, join } from 'path'
|
|
5
|
-
import { existsSync, mkdirSync, cpSync, writeFileSync, readFileSync } from 'fs'
|
|
4
|
+
import { resolve, join, basename } from 'path'
|
|
5
|
+
import { existsSync, mkdirSync, cpSync, writeFileSync, readFileSync, readdirSync } from 'fs'
|
|
6
6
|
import chalk from 'chalk'
|
|
7
7
|
import ora from 'ora'
|
|
8
8
|
import { FLUXSTACK_VERSION } from './core/utils/version'
|
|
@@ -28,30 +28,62 @@ program
|
|
|
28
28
|
.action(async (projectName, options) => {
|
|
29
29
|
console.clear()
|
|
30
30
|
console.log(chalk.magenta(logo))
|
|
31
|
-
|
|
31
|
+
|
|
32
32
|
if (!projectName || projectName.trim().length === 0) {
|
|
33
33
|
console.log(chalk.red('ā Project name is required'))
|
|
34
|
-
console.log(chalk.gray('Usage:
|
|
34
|
+
console.log(chalk.gray('Usage: bunx create-fluxstack@latest my-app'))
|
|
35
|
+
console.log(chalk.gray(' or: bunx create-fluxstack@latest .'))
|
|
35
36
|
process.exit(1)
|
|
36
37
|
}
|
|
37
|
-
|
|
38
|
+
|
|
38
39
|
const currentDir = import.meta.dir
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
|
|
41
|
+
// Normalize path: remove trailing slashes (which may indicate current dir usage like path/.)
|
|
42
|
+
let normalizedName = projectName
|
|
43
|
+
const hasTrailingSlash = normalizedName.endsWith('/') || normalizedName.endsWith('\\')
|
|
44
|
+
|
|
45
|
+
if (hasTrailingSlash) {
|
|
46
|
+
normalizedName = normalizedName.slice(0, -1)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check if it's current directory
|
|
50
|
+
// - Explicit '.'
|
|
51
|
+
// - Path ending with /. or \. (e.g., /path/to/dir/.)
|
|
52
|
+
// - Path ending with / or \ (Bun normalizes path/. to path/)
|
|
53
|
+
const isCurrentDir = normalizedName === '.' ||
|
|
54
|
+
projectName.endsWith('/.') ||
|
|
55
|
+
projectName.endsWith('\\.') ||
|
|
56
|
+
hasTrailingSlash
|
|
57
|
+
|
|
58
|
+
const projectPath = resolve(normalizedName)
|
|
59
|
+
const displayName = isCurrentDir ? 'current directory' : projectName
|
|
60
|
+
|
|
61
|
+
// Check if directory already exists (skip for current dir)
|
|
62
|
+
if (!isCurrentDir && existsSync(projectPath)) {
|
|
43
63
|
console.log(chalk.red(`ā Directory ${projectName} already exists`))
|
|
44
64
|
process.exit(1)
|
|
45
65
|
}
|
|
46
|
-
|
|
47
|
-
|
|
66
|
+
|
|
67
|
+
// Check if current directory is not empty (when using '.')
|
|
68
|
+
if (isCurrentDir) {
|
|
69
|
+
const files = readdirSync(projectPath).filter(f => !f.startsWith('.'))
|
|
70
|
+
if (files.length > 0) {
|
|
71
|
+
console.log(chalk.yellow('ā ļø Current directory is not empty'))
|
|
72
|
+
console.log(chalk.gray(`Found ${files.length} file(s). FluxStack will be initialized here.`))
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
console.log(chalk.cyan(`\nš Creating FluxStack project: ${chalk.bold(displayName)}`))
|
|
48
77
|
console.log(chalk.gray(`š Location: ${projectPath}`))
|
|
49
78
|
|
|
50
79
|
// Create project directory
|
|
51
80
|
const spinner = ora('Creating project structure...').start()
|
|
52
|
-
|
|
81
|
+
|
|
53
82
|
try {
|
|
54
|
-
|
|
83
|
+
// Only create directory if not using current directory
|
|
84
|
+
if (!isCurrentDir) {
|
|
85
|
+
mkdirSync(projectPath, { recursive: true })
|
|
86
|
+
}
|
|
55
87
|
|
|
56
88
|
// Copy only essential FluxStack files (not node_modules, not test apps, etc.)
|
|
57
89
|
const frameworkDir = currentDir // Use current directory (framework root)
|
|
@@ -142,7 +174,14 @@ export class MyPlugin implements FluxStackPlugin {
|
|
|
142
174
|
// Intercept every request
|
|
143
175
|
async onRequest(context: PluginContext, request: Request): Promise<void> {
|
|
144
176
|
// Example: Add custom headers
|
|
145
|
-
const url =
|
|
177
|
+
const url = (() => {
|
|
178
|
+
try {
|
|
179
|
+
return new URL(request.url)
|
|
180
|
+
} catch {
|
|
181
|
+
const host = request.headers.get('host') || 'localhost'
|
|
182
|
+
return new URL(request.url, \`http://\${host}\`)
|
|
183
|
+
}
|
|
184
|
+
})()
|
|
146
185
|
console.log(\`[\${this.name}] Request to: \${url.pathname}\`)
|
|
147
186
|
|
|
148
187
|
// Example: Validate authentication
|
|
@@ -305,18 +344,20 @@ bun.lockb
|
|
|
305
344
|
|
|
306
345
|
// Customize package.json with project name
|
|
307
346
|
const packageJsonPath = join(projectPath, 'package.json')
|
|
347
|
+
const actualProjectName = isCurrentDir ? basename(projectPath) : normalizedName
|
|
348
|
+
|
|
308
349
|
if (existsSync(packageJsonPath)) {
|
|
309
350
|
const packageContent = readFileSync(packageJsonPath, 'utf-8')
|
|
310
351
|
const packageJson = JSON.parse(packageContent)
|
|
311
|
-
|
|
352
|
+
|
|
312
353
|
// Update project-specific fields
|
|
313
|
-
packageJson.name =
|
|
314
|
-
packageJson.description = `${
|
|
354
|
+
packageJson.name = actualProjectName
|
|
355
|
+
packageJson.description = `${actualProjectName} - FluxStack application`
|
|
315
356
|
packageJson.version = "1.0.0"
|
|
316
|
-
|
|
357
|
+
|
|
317
358
|
writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2))
|
|
318
359
|
}
|
|
319
|
-
|
|
360
|
+
|
|
320
361
|
// Create .env from .env.example and set development mode + project name
|
|
321
362
|
const envExamplePath = join(projectPath, '.env.example')
|
|
322
363
|
const envPath = join(projectPath, '.env')
|
|
@@ -325,14 +366,14 @@ bun.lockb
|
|
|
325
366
|
// Set development mode
|
|
326
367
|
envContent = envContent.replace('NODE_ENV=production', 'NODE_ENV=development')
|
|
327
368
|
// Customize app name to match project name
|
|
328
|
-
envContent = envContent.replace('VITE_APP_NAME=FluxStack', `VITE_APP_NAME=${
|
|
369
|
+
envContent = envContent.replace('VITE_APP_NAME=FluxStack', `VITE_APP_NAME=${actualProjectName}`)
|
|
329
370
|
writeFileSync(envPath, envContent)
|
|
330
371
|
}
|
|
331
|
-
|
|
372
|
+
|
|
332
373
|
// Customize README.md
|
|
333
374
|
const readmePath = join(projectPath, 'README.md')
|
|
334
375
|
if (existsSync(readmePath)) {
|
|
335
|
-
const readmeContent = `# ${
|
|
376
|
+
const readmeContent = `# ${actualProjectName}
|
|
336
377
|
|
|
337
378
|
ā” **FluxStack Application** - Modern full-stack TypeScript framework
|
|
338
379
|
|
|
@@ -352,7 +393,7 @@ bun run start
|
|
|
352
393
|
## š Project Structure
|
|
353
394
|
|
|
354
395
|
\`\`\`
|
|
355
|
-
${
|
|
396
|
+
${actualProjectName}/
|
|
356
397
|
āāā core/ # FluxStack framework (don't modify)
|
|
357
398
|
āāā app/ # Your application code
|
|
358
399
|
ā āāā server/ # Backend API routes
|
|
@@ -471,7 +512,7 @@ Built with ā¤ļø using FluxStack
|
|
|
471
512
|
})
|
|
472
513
|
await addProc.exited
|
|
473
514
|
|
|
474
|
-
const commitProc = Bun.spawn(['git', 'commit', '-m', `feat: initial ${
|
|
515
|
+
const commitProc = Bun.spawn(['git', 'commit', '-m', `feat: initial ${actualProjectName} with FluxStack`], {
|
|
475
516
|
cwd: projectPath,
|
|
476
517
|
stdio: ['ignore', 'pipe', 'pipe']
|
|
477
518
|
})
|
|
@@ -487,7 +528,9 @@ Built with ā¤ļø using FluxStack
|
|
|
487
528
|
// Success message
|
|
488
529
|
console.log(chalk.green('\nš Project created successfully!'))
|
|
489
530
|
console.log(chalk.cyan('\nNext steps:'))
|
|
490
|
-
|
|
531
|
+
if (!isCurrentDir) {
|
|
532
|
+
console.log(chalk.white(` cd ${projectName}`))
|
|
533
|
+
}
|
|
491
534
|
if (!options.install) {
|
|
492
535
|
console.log(chalk.white(` bun install`))
|
|
493
536
|
}
|