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
@@ -3,14 +3,30 @@
3
3
  * FluxStack middleware infrastructure exports
4
4
  */
5
5
 
6
- export {
7
- errorHandlingMiddleware,
8
- notFoundMiddleware,
9
- createError,
10
- asyncHandler
6
+ export {
7
+ errorHandlingMiddleware,
8
+ notFoundMiddleware,
9
+ createError,
10
+ asyncHandler
11
11
  } from './errorHandling.js'
12
12
 
13
- export type {
14
- ErrorHandlingOptions,
15
- FluxStackError
16
- } from './errorHandling.js'
13
+ export type {
14
+ ErrorHandlingOptions,
15
+ FluxStackError
16
+ } from './errorHandling.js'
17
+
18
+ // Elysia Middleware Helpers
19
+ export {
20
+ createMiddleware,
21
+ createDerive,
22
+ createGuard,
23
+ createRateLimit,
24
+ composeMiddleware,
25
+ isDevelopment,
26
+ isProduction,
27
+ isTest
28
+ } from './elysia-helpers.js'
29
+
30
+ export type {
31
+ MiddlewareOptions
32
+ } from './elysia-helpers.js'
@@ -1,232 +1,232 @@
1
- // 🔥 FluxStack Static Files Plugin - Serve Public Files
2
-
3
- import { existsSync, statSync } from 'fs'
4
- import { join, extname, resolve } from 'path'
5
- import type { Plugin, PluginContext } from '../plugins/types'
6
-
7
- export interface StaticFilesConfig {
8
- publicDir?: string // Default: 'public'
9
- uploadsDir?: string // Default: 'uploads'
10
- cacheMaxAge?: number // Default: 1 year in seconds
11
- enableUploads?: boolean // Default: true
12
- enablePublic?: boolean // Default: true
13
- publicRoute?: string // Default: '/public' (can be '/static' in dev)
14
- uploadsRoute?: string // Default: '/uploads'
15
- }
16
-
17
- export const staticFilesPlugin: Plugin = {
18
- name: 'static-files',
19
- version: '1.0.0',
20
- description: 'Serve static files and uploads with proper caching and security',
21
- author: 'FluxStack Team',
22
- priority: 'normal',
23
- category: 'core',
24
- tags: ['static', 'files', 'uploads', 'public'],
25
-
26
- setup: async (context: PluginContext) => {
27
- context.logger.info('📁 Setting up Static Files plugin...')
28
-
29
- const config: StaticFilesConfig = {
30
- publicDir: 'public',
31
- uploadsDir: 'uploads',
32
- cacheMaxAge: 31536000, // 1 year
33
- enableUploads: true,
34
- enablePublic: true,
35
- publicRoute: '/api/static', // Use /api/static in dev to avoid Vite conflicts
36
- uploadsRoute: '/api/uploads',
37
- ...context.config.staticFiles
38
- }
39
-
40
- const projectRoot = process.cwd()
41
- const publicPath = resolve(projectRoot, config.publicDir!)
42
- const uploadsPath = resolve(projectRoot, config.uploadsDir!)
43
-
44
- // MIME types mapping
45
- const getMimeType = (extension: string): string => {
46
- const mimeTypes: Record<string, string> = {
47
- // Images
48
- '.jpg': 'image/jpeg',
49
- '.jpeg': 'image/jpeg',
50
- '.png': 'image/png',
51
- '.gif': 'image/gif',
52
- '.webp': 'image/webp',
53
- '.svg': 'image/svg+xml',
54
- '.ico': 'image/x-icon',
55
-
56
- // Documents
57
- '.pdf': 'application/pdf',
58
- '.txt': 'text/plain',
59
- '.json': 'application/json',
60
- '.xml': 'application/xml',
61
-
62
- // Web assets
63
- '.css': 'text/css',
64
- '.js': 'application/javascript',
65
- '.html': 'text/html',
66
- '.htm': 'text/html',
67
-
68
- // Fonts
69
- '.woff': 'font/woff',
70
- '.woff2': 'font/woff2',
71
- '.ttf': 'font/ttf',
72
- '.otf': 'font/otf',
73
-
74
- // Audio/Video
75
- '.mp3': 'audio/mpeg',
76
- '.mp4': 'video/mp4',
77
- '.webm': 'video/webm',
78
- '.ogg': 'audio/ogg'
79
- }
80
-
81
- return mimeTypes[extension.toLowerCase()] || 'application/octet-stream'
82
- }
83
-
84
- // Security check for path traversal
85
- const isPathSafe = (filePath: string, basePath: string): boolean => {
86
- const resolvedPath = resolve(basePath, filePath)
87
- return resolvedPath.startsWith(basePath)
88
- }
89
-
90
- // Generic file serving function
91
- const serveFile = async (filePath: string, set: any) => {
92
- try {
93
- if (!existsSync(filePath)) {
94
- set.status = 404
95
- return {
96
- error: 'File not found',
97
- path: filePath.replace(projectRoot, ''),
98
- timestamp: new Date().toISOString()
99
- }
100
- }
101
-
102
- const stats = statSync(filePath)
103
- if (!stats.isFile()) {
104
- set.status = 404
105
- return { error: 'Not a file' }
106
- }
107
-
108
- // Set appropriate headers
109
- const extension = extname(filePath).toLowerCase()
110
- const mimeType = getMimeType(extension)
111
-
112
- set.headers['content-type'] = mimeType
113
- set.headers['content-length'] = stats.size.toString()
114
- set.headers['last-modified'] = stats.mtime.toUTCString()
115
- set.headers['cache-control'] = `public, max-age=${config.cacheMaxAge}`
116
- set.headers['etag'] = `"${stats.mtime.getTime()}-${stats.size}"`
117
-
118
- // Security headers for images
119
- if (mimeType.startsWith('image/')) {
120
- set.headers['x-content-type-options'] = 'nosniff'
121
- }
122
-
123
- context.logger.debug(`📁 Serving file: ${filePath.replace(projectRoot, '')}`, {
124
- size: stats.size,
125
- mimeType,
126
- lastModified: stats.mtime
127
- })
128
-
129
- return Bun.file(filePath)
130
-
131
- } catch (error: any) {
132
- context.logger.error('❌ File serving error:', error.message)
133
- set.status = 500
134
- return { error: 'Failed to serve file' }
135
- }
136
- }
137
-
138
- // Add static file routes
139
- if (config.enablePublic) {
140
- const publicRoutePattern = `${config.publicRoute}/*`
141
- context.app.get(publicRoutePattern, ({ params, set }) => {
142
- const filePath = params['*'] || ''
143
-
144
- if (!isPathSafe(filePath, publicPath)) {
145
- set.status = 400
146
- return { error: 'Invalid file path' }
147
- }
148
-
149
- const fullPath = join(publicPath, filePath)
150
- return serveFile(fullPath, set)
151
- })
152
-
153
- context.logger.info(`📁 Public files route enabled: ${publicRoutePattern} → ${config.publicDir}`)
154
- }
155
-
156
- if (config.enableUploads) {
157
- const uploadsRoutePattern = `${config.uploadsRoute}/*`
158
- context.app.get(uploadsRoutePattern, ({ params, set }) => {
159
- const filePath = params['*'] || ''
160
-
161
- if (!isPathSafe(filePath, uploadsPath)) {
162
- set.status = 400
163
- return { error: 'Invalid file path' }
164
- }
165
-
166
- const fullPath = join(uploadsPath, filePath)
167
- return serveFile(fullPath, set)
168
- })
169
-
170
- context.logger.info(`📁 Uploads route enabled: ${uploadsRoutePattern} → ${config.uploadsDir}`)
171
- }
172
-
173
- // Static files info endpoint
174
- context.app.get('/api/static/info', () => {
175
- return {
176
- success: true,
177
- config: {
178
- publicDir: config.publicDir,
179
- uploadsDir: config.uploadsDir,
180
- enablePublic: config.enablePublic,
181
- enableUploads: config.enableUploads,
182
- cacheMaxAge: config.cacheMaxAge
183
- },
184
- paths: {
185
- publicPath,
186
- uploadsPath,
187
- publicUrl: config.publicRoute,
188
- uploadsUrl: config.uploadsRoute
189
- },
190
- timestamp: new Date().toISOString()
191
- }
192
- })
193
-
194
- // Create directories if they don't exist
195
- const { mkdir } = await import('fs/promises')
196
-
197
- if (config.enablePublic && !existsSync(publicPath)) {
198
- await mkdir(publicPath, { recursive: true })
199
- context.logger.info(`📁 Created public directory: ${publicPath}`)
200
- }
201
-
202
- if (config.enableUploads && !existsSync(uploadsPath)) {
203
- await mkdir(uploadsPath, { recursive: true })
204
- await mkdir(join(uploadsPath, 'avatars'), { recursive: true })
205
- context.logger.info(`📁 Created uploads directory: ${uploadsPath}`)
206
- }
207
-
208
- context.logger.info('📁 Static Files plugin setup complete', {
209
- publicEnabled: config.enablePublic,
210
- uploadsEnabled: config.enableUploads,
211
- publicPath: config.enablePublic ? publicPath : 'disabled',
212
- uploadsPath: config.enableUploads ? uploadsPath : 'disabled'
213
- })
214
- },
215
-
216
- onServerStart: async (context: PluginContext) => {
217
- const config = {
218
- enablePublic: true,
219
- enableUploads: true,
220
- publicRoute: '/api/static',
221
- uploadsRoute: '/api/uploads',
222
- ...context.config.staticFiles
223
- }
224
- context.logger.info('📁 Static Files plugin ready', {
225
- routes: [
226
- config.enablePublic ? `${config.publicRoute}/*` : null,
227
- config.enableUploads ? `${config.uploadsRoute}/*` : null,
228
- '/api/static/info'
229
- ].filter(Boolean)
230
- })
231
- }
1
+ // 🔥 FluxStack Static Files Plugin - Serve Public Files
2
+
3
+ import { existsSync, statSync } from 'fs'
4
+ import { join, extname, resolve } from 'path'
5
+ import type { Plugin, PluginContext } from '../plugins/types'
6
+
7
+ export interface StaticFilesConfig {
8
+ publicDir?: string // Default: 'public'
9
+ uploadsDir?: string // Default: 'uploads'
10
+ cacheMaxAge?: number // Default: 1 year in seconds
11
+ enableUploads?: boolean // Default: true
12
+ enablePublic?: boolean // Default: true
13
+ publicRoute?: string // Default: '/public' (can be '/static' in dev)
14
+ uploadsRoute?: string // Default: '/uploads'
15
+ }
16
+
17
+ export const staticFilesPlugin: Plugin = {
18
+ name: 'static-files',
19
+ version: '1.0.0',
20
+ description: 'Serve static files and uploads with proper caching and security',
21
+ author: 'FluxStack Team',
22
+ priority: 'normal',
23
+ category: 'core',
24
+ tags: ['static', 'files', 'uploads', 'public'],
25
+
26
+ setup: async (context: PluginContext) => {
27
+ context.logger.debug('📁 Setting up Static Files plugin...')
28
+
29
+ const config: StaticFilesConfig = {
30
+ publicDir: 'public',
31
+ uploadsDir: 'uploads',
32
+ cacheMaxAge: 31536000, // 1 year
33
+ enableUploads: true,
34
+ enablePublic: true,
35
+ publicRoute: '/api/static', // Use /api/static in dev to avoid Vite conflicts
36
+ uploadsRoute: '/api/uploads',
37
+ ...context.config.staticFiles
38
+ }
39
+
40
+ const projectRoot = process.cwd()
41
+ const publicPath = resolve(projectRoot, config.publicDir!)
42
+ const uploadsPath = resolve(projectRoot, config.uploadsDir!)
43
+
44
+ // MIME types mapping
45
+ const getMimeType = (extension: string): string => {
46
+ const mimeTypes: Record<string, string> = {
47
+ // Images
48
+ '.jpg': 'image/jpeg',
49
+ '.jpeg': 'image/jpeg',
50
+ '.png': 'image/png',
51
+ '.gif': 'image/gif',
52
+ '.webp': 'image/webp',
53
+ '.svg': 'image/svg+xml',
54
+ '.ico': 'image/x-icon',
55
+
56
+ // Documents
57
+ '.pdf': 'application/pdf',
58
+ '.txt': 'text/plain',
59
+ '.json': 'application/json',
60
+ '.xml': 'application/xml',
61
+
62
+ // Web assets
63
+ '.css': 'text/css',
64
+ '.js': 'application/javascript',
65
+ '.html': 'text/html',
66
+ '.htm': 'text/html',
67
+
68
+ // Fonts
69
+ '.woff': 'font/woff',
70
+ '.woff2': 'font/woff2',
71
+ '.ttf': 'font/ttf',
72
+ '.otf': 'font/otf',
73
+
74
+ // Audio/Video
75
+ '.mp3': 'audio/mpeg',
76
+ '.mp4': 'video/mp4',
77
+ '.webm': 'video/webm',
78
+ '.ogg': 'audio/ogg'
79
+ }
80
+
81
+ return mimeTypes[extension.toLowerCase()] || 'application/octet-stream'
82
+ }
83
+
84
+ // Security check for path traversal
85
+ const isPathSafe = (filePath: string, basePath: string): boolean => {
86
+ const resolvedPath = resolve(basePath, filePath)
87
+ return resolvedPath.startsWith(basePath)
88
+ }
89
+
90
+ // Generic file serving function
91
+ const serveFile = async (filePath: string, set: any) => {
92
+ try {
93
+ if (!existsSync(filePath)) {
94
+ set.status = 404
95
+ return {
96
+ error: 'File not found',
97
+ path: filePath.replace(projectRoot, ''),
98
+ timestamp: new Date().toISOString()
99
+ }
100
+ }
101
+
102
+ const stats = statSync(filePath)
103
+ if (!stats.isFile()) {
104
+ set.status = 404
105
+ return { error: 'Not a file' }
106
+ }
107
+
108
+ // Set appropriate headers
109
+ const extension = extname(filePath).toLowerCase()
110
+ const mimeType = getMimeType(extension)
111
+
112
+ set.headers['content-type'] = mimeType
113
+ set.headers['content-length'] = stats.size.toString()
114
+ set.headers['last-modified'] = stats.mtime.toUTCString()
115
+ set.headers['cache-control'] = `public, max-age=${config.cacheMaxAge}`
116
+ set.headers['etag'] = `"${stats.mtime.getTime()}-${stats.size}"`
117
+
118
+ // Security headers for images
119
+ if (mimeType.startsWith('image/')) {
120
+ set.headers['x-content-type-options'] = 'nosniff'
121
+ }
122
+
123
+ context.logger.debug(`📁 Serving file: ${filePath.replace(projectRoot, '')}`, {
124
+ size: stats.size,
125
+ mimeType,
126
+ lastModified: stats.mtime
127
+ })
128
+
129
+ return Bun.file(filePath)
130
+
131
+ } catch (error: any) {
132
+ context.logger.error('❌ File serving error:', error.message)
133
+ set.status = 500
134
+ return { error: 'Failed to serve file' }
135
+ }
136
+ }
137
+
138
+ // Add static file routes
139
+ if (config.enablePublic) {
140
+ const publicRoutePattern = `${config.publicRoute}/*`
141
+ context.app.get(publicRoutePattern, ({ params, set }) => {
142
+ const filePath = params['*'] || ''
143
+
144
+ if (!isPathSafe(filePath, publicPath)) {
145
+ set.status = 400
146
+ return { error: 'Invalid file path' }
147
+ }
148
+
149
+ const fullPath = join(publicPath, filePath)
150
+ return serveFile(fullPath, set)
151
+ })
152
+
153
+ context.logger.debug(`📁 Public files route enabled: ${publicRoutePattern} → ${config.publicDir}`)
154
+ }
155
+
156
+ if (config.enableUploads) {
157
+ const uploadsRoutePattern = `${config.uploadsRoute}/*`
158
+ context.app.get(uploadsRoutePattern, ({ params, set }) => {
159
+ const filePath = params['*'] || ''
160
+
161
+ if (!isPathSafe(filePath, uploadsPath)) {
162
+ set.status = 400
163
+ return { error: 'Invalid file path' }
164
+ }
165
+
166
+ const fullPath = join(uploadsPath, filePath)
167
+ return serveFile(fullPath, set)
168
+ })
169
+
170
+ context.logger.debug(`📁 Uploads route enabled: ${uploadsRoutePattern} → ${config.uploadsDir}`)
171
+ }
172
+
173
+ // Static files info endpoint
174
+ context.app.get('/api/static/info', () => {
175
+ return {
176
+ success: true,
177
+ config: {
178
+ publicDir: config.publicDir,
179
+ uploadsDir: config.uploadsDir,
180
+ enablePublic: config.enablePublic,
181
+ enableUploads: config.enableUploads,
182
+ cacheMaxAge: config.cacheMaxAge
183
+ },
184
+ paths: {
185
+ publicPath,
186
+ uploadsPath,
187
+ publicUrl: config.publicRoute,
188
+ uploadsUrl: config.uploadsRoute
189
+ },
190
+ timestamp: new Date().toISOString()
191
+ }
192
+ })
193
+
194
+ // Create directories if they don't exist
195
+ const { mkdir } = await import('fs/promises')
196
+
197
+ if (config.enablePublic && !existsSync(publicPath)) {
198
+ await mkdir(publicPath, { recursive: true })
199
+ context.logger.debug(`📁 Created public directory: ${publicPath}`)
200
+ }
201
+
202
+ if (config.enableUploads && !existsSync(uploadsPath)) {
203
+ await mkdir(uploadsPath, { recursive: true })
204
+ await mkdir(join(uploadsPath, 'avatars'), { recursive: true })
205
+ context.logger.debug(`📁 Created uploads directory: ${uploadsPath}`)
206
+ }
207
+
208
+ context.logger.debug('📁 Static Files plugin setup complete', {
209
+ publicEnabled: config.enablePublic,
210
+ uploadsEnabled: config.enableUploads,
211
+ publicPath: config.enablePublic ? publicPath : 'disabled',
212
+ uploadsPath: config.enableUploads ? uploadsPath : 'disabled'
213
+ })
214
+ },
215
+
216
+ onServerStart: async (context: PluginContext) => {
217
+ const config = {
218
+ enablePublic: true,
219
+ enableUploads: true,
220
+ publicRoute: '/api/static',
221
+ uploadsRoute: '/api/uploads',
222
+ ...context.config.staticFiles
223
+ }
224
+ context.logger.debug('📁 Static Files plugin ready', {
225
+ routes: [
226
+ config.enablePublic ? `${config.publicRoute}/*` : null,
227
+ config.enableUploads ? `${config.uploadsRoute}/*` : null,
228
+ '/api/static/info'
229
+ ].filter(Boolean)
230
+ })
231
+ }
232
232
  }