create-fluxstack 1.8.3 → 1.10.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 (52) hide show
  1. package/LIVE_COMPONENTS_REVIEW.md +781 -0
  2. package/README.md +653 -275
  3. package/app/client/src/App.tsx +39 -43
  4. package/app/client/src/lib/eden-api.ts +2 -7
  5. package/app/client/src/live/FileUploadExample.tsx +359 -0
  6. package/app/client/src/live/MinimalLiveClock.tsx +47 -0
  7. package/app/client/src/live/QuickUploadTest.tsx +193 -0
  8. package/app/client/src/main.tsx +10 -10
  9. package/app/client/src/vite-env.d.ts +1 -1
  10. package/app/client/tsconfig.app.json +45 -44
  11. package/app/client/tsconfig.node.json +25 -25
  12. package/app/server/index.ts +30 -103
  13. package/app/server/live/LiveFileUploadComponent.ts +77 -0
  14. package/app/server/live/register-components.ts +19 -19
  15. package/core/build/bundler.ts +202 -55
  16. package/core/build/index.ts +126 -2
  17. package/core/build/live-components-generator.ts +68 -1
  18. package/core/cli/generators/plugin.ts +6 -6
  19. package/core/cli/index.ts +232 -4
  20. package/core/client/LiveComponentsProvider.tsx +3 -9
  21. package/core/client/hooks/AdaptiveChunkSizer.ts +215 -0
  22. package/core/client/hooks/useChunkedUpload.ts +112 -61
  23. package/core/client/hooks/useHybridLiveComponent.ts +80 -26
  24. package/core/client/hooks/useTypedLiveComponent.ts +133 -0
  25. package/core/client/hooks/useWebSocket.ts +4 -16
  26. package/core/client/index.ts +20 -2
  27. package/core/framework/server.ts +181 -8
  28. package/core/live/ComponentRegistry.ts +5 -1
  29. package/core/plugins/built-in/index.ts +8 -5
  30. package/core/plugins/built-in/live-components/commands/create-live-component.ts +55 -63
  31. package/core/plugins/built-in/vite/index.ts +75 -187
  32. package/core/plugins/built-in/vite/vite-dev.ts +88 -0
  33. package/core/plugins/registry.ts +54 -2
  34. package/core/plugins/types.ts +86 -2
  35. package/core/server/index.ts +1 -2
  36. package/core/server/live/ComponentRegistry.ts +14 -5
  37. package/core/server/live/FileUploadManager.ts +22 -25
  38. package/core/server/live/auto-generated-components.ts +29 -26
  39. package/core/server/live/websocket-plugin.ts +19 -5
  40. package/core/server/plugins/static-files-plugin.ts +49 -240
  41. package/core/server/plugins/swagger.ts +33 -33
  42. package/core/types/build.ts +22 -0
  43. package/core/types/plugin.ts +9 -1
  44. package/core/types/types.ts +137 -0
  45. package/core/utils/logger/startup-banner.ts +20 -4
  46. package/core/utils/version.ts +6 -6
  47. package/create-fluxstack.ts +7 -7
  48. package/eslint.config.js +23 -23
  49. package/package.json +3 -2
  50. package/plugins/crypto-auth/server/middlewares.ts +19 -19
  51. package/tsconfig.json +52 -51
  52. package/workspace.json +5 -5
@@ -12,9 +12,9 @@ import type {
12
12
 
13
13
  export class FileUploadManager {
14
14
  private activeUploads = new Map<string, ActiveUpload>()
15
- private readonly maxUploadSize = 50 * 1024 * 1024 // 50MB max
15
+ private readonly maxUploadSize = 500 * 1024 * 1024 // 500MB max (aceita qualquer arquivo)
16
16
  private readonly chunkTimeout = 30000 // 30 seconds timeout per chunk
17
- private readonly allowedTypes = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif']
17
+ private readonly allowedTypes: string[] = [] // Array vazio = aceita todos os tipos de arquivo
18
18
 
19
19
  constructor() {
20
20
  // Cleanup stale uploads every 5 minutes
@@ -25,12 +25,7 @@ export class FileUploadManager {
25
25
  try {
26
26
  const { uploadId, componentId, filename, fileType, fileSize, chunkSize = 64 * 1024 } = message
27
27
 
28
- // Validate file type
29
- if (!this.allowedTypes.includes(fileType)) {
30
- throw new Error(`Invalid file type: ${fileType}. Allowed: ${this.allowedTypes.join(', ')}`)
31
- }
32
-
33
- // Validate file size
28
+ // Validate file size (sem restrição de tipo)
34
29
  if (fileSize > this.maxUploadSize) {
35
30
  throw new Error(`File too large: ${fileSize} bytes. Max: ${this.maxUploadSize} bytes`)
36
31
  }
@@ -52,6 +47,7 @@ export class FileUploadManager {
52
47
  fileSize,
53
48
  totalChunks,
54
49
  receivedChunks: new Map(),
50
+ bytesReceived: 0, // Track actual bytes for adaptive chunking
55
51
  startTime: Date.now(),
56
52
  lastChunkTime: Date.now()
57
53
  }
@@ -97,16 +93,20 @@ export class FileUploadManager {
97
93
  upload.receivedChunks.set(chunkIndex, data)
98
94
  upload.lastChunkTime = Date.now()
99
95
 
100
- console.log(`📦 Received chunk ${chunkIndex + 1}/${totalChunks} for upload ${uploadId}`)
96
+ // Track actual bytes received (decode base64 to get real size)
97
+ const chunkBytes = Buffer.from(data, 'base64').length
98
+ upload.bytesReceived += chunkBytes
99
+
100
+ console.log(`📦 Received chunk ${chunkIndex + 1}/${totalChunks} for upload ${uploadId} (${chunkBytes} bytes, total: ${upload.bytesReceived}/${upload.fileSize})`)
101
101
  }
102
102
 
103
- // Calculate progress
104
- const progress = (upload.receivedChunks.size / totalChunks) * 100
105
- const bytesUploaded = upload.receivedChunks.size * (upload.fileSize / totalChunks)
103
+ // Calculate progress based on actual bytes received (supports adaptive chunking)
104
+ const progress = (upload.bytesReceived / upload.fileSize) * 100
105
+ const bytesUploaded = upload.bytesReceived
106
106
 
107
- // Check if upload is complete
108
- if (upload.receivedChunks.size === totalChunks) {
109
- await this.finalizeUpload(upload)
107
+ // Log completion status (but don't finalize until COMPLETE message)
108
+ if (upload.bytesReceived >= upload.fileSize) {
109
+ console.log(`✅ All bytes received for upload ${uploadId} (${upload.bytesReceived}/${upload.fileSize}), waiting for COMPLETE message`)
110
110
  }
111
111
 
112
112
  return {
@@ -152,19 +152,16 @@ export class FileUploadManager {
152
152
  throw new Error(`Upload ${uploadId} not found`)
153
153
  }
154
154
 
155
- console.log(`✅ Upload completed: ${uploadId}`)
155
+ console.log(`✅ Upload completion requested: ${uploadId}`)
156
156
 
157
- // Check for missing chunks
158
- const missingChunks: number[] = []
159
- for (let i = 0; i < upload.totalChunks; i++) {
160
- if (!upload.receivedChunks.has(i)) {
161
- missingChunks.push(i)
162
- }
157
+ // Validate bytes received (supports adaptive chunking)
158
+ if (upload.bytesReceived !== upload.fileSize) {
159
+ const bytesShort = upload.fileSize - upload.bytesReceived
160
+ throw new Error(`Incomplete upload: received ${upload.bytesReceived}/${upload.fileSize} bytes (${bytesShort} bytes short)`)
163
161
  }
164
162
 
165
- if (missingChunks.length > 0) {
166
- throw new Error(`Missing chunks: ${missingChunks.join(', ')}`)
167
- }
163
+ console.log(`✅ Upload validation passed: ${uploadId} (${upload.bytesReceived} bytes)`)
164
+
168
165
 
169
166
  // Assemble file from chunks
170
167
  const fileUrl = await this.assembleFile(upload)
@@ -1,26 +1,29 @@
1
- // 🔥 Auto-generated Live Components Registration
2
- // This file is automatically generated during build time - DO NOT EDIT MANUALLY
3
- // Generated at: 2025-11-12T22:57:08.521Z
4
-
5
- import { LiveClockComponent } from "@/app/server/live/LiveClockComponent"
6
- import { componentRegistry } from "@/core/server/live/ComponentRegistry"
7
-
8
- // Register all components statically for production bundle
9
- function registerAllComponents() {
10
- try {
11
- // Auto-generated component registrations
12
- componentRegistry.registerComponentClass('LiveClock', LiveClockComponent)
13
-
14
- console.log('📝 Live components registered successfully! (1 components)')
15
- } catch (error) {
16
- console.warn('⚠️ Error registering components:', error)
17
- }
18
- }
19
-
20
- // Auto-register components
21
- registerAllComponents()
22
-
23
- // Export all components to ensure they're included in the bundle
24
- export {
25
- LiveClockComponent
26
- }
1
+ // 🔥 Auto-generated Live Components Registration
2
+ // This file is automatically generated during build time - DO NOT EDIT MANUALLY
3
+ // Generated at: 2025-11-23T11:09:30.419Z
4
+
5
+ import { LiveClockComponent } from "@/app/server/live/LiveClockComponent"
6
+ import { LiveFileUploadComponent } from "@/app/server/live/LiveFileUploadComponent"
7
+ import { componentRegistry } from "@/core/server/live/ComponentRegistry"
8
+
9
+ // Register all components statically for production bundle
10
+ function registerAllComponents() {
11
+ try {
12
+ // Auto-generated component registrations
13
+ componentRegistry.registerComponentClass('LiveClock', LiveClockComponent)
14
+ componentRegistry.registerComponentClass('LiveFileUpload', LiveFileUploadComponent)
15
+
16
+ console.log('📝 Live components registered successfully! (2 components)')
17
+ } catch (error) {
18
+ console.warn('⚠️ Error registering components:', error)
19
+ }
20
+ }
21
+
22
+ // Auto-register components
23
+ registerAllComponents()
24
+
25
+ // Export all components to ensure they're included in the bundle
26
+ export {
27
+ LiveClockComponent,
28
+ LiveFileUploadComponent
29
+ }
@@ -619,11 +619,17 @@ async function handleFileUploadStart(ws: any, message: FileUploadStartMessage) {
619
619
 
620
620
  async function handleFileUploadChunk(ws: any, message: FileUploadChunkMessage) {
621
621
  console.log(`📦 Receiving chunk ${message.chunkIndex + 1} for upload ${message.uploadId}`)
622
-
622
+
623
623
  const progressResponse = await fileUploadManager.receiveChunk(message, ws)
624
-
624
+
625
625
  if (progressResponse) {
626
- ws.send(JSON.stringify(progressResponse))
626
+ // Add requestId to response so client can correlate it
627
+ const responseWithRequestId = {
628
+ ...progressResponse,
629
+ requestId: message.requestId,
630
+ success: true
631
+ }
632
+ ws.send(JSON.stringify(responseWithRequestId))
627
633
  } else {
628
634
  // Send error response
629
635
  const errorResponse = {
@@ -632,6 +638,7 @@ async function handleFileUploadChunk(ws: any, message: FileUploadChunkMessage) {
632
638
  uploadId: message.uploadId,
633
639
  error: 'Failed to process chunk',
634
640
  requestId: message.requestId,
641
+ success: false,
635
642
  timestamp: Date.now()
636
643
  }
637
644
  ws.send(JSON.stringify(errorResponse))
@@ -640,7 +647,14 @@ async function handleFileUploadChunk(ws: any, message: FileUploadChunkMessage) {
640
647
 
641
648
  async function handleFileUploadComplete(ws: any, message: FileUploadCompleteMessage) {
642
649
  console.log('✅ Completing file upload:', message.uploadId)
643
-
650
+
644
651
  const completeResponse = await fileUploadManager.completeUpload(message)
645
- ws.send(JSON.stringify(completeResponse))
652
+
653
+ // Add requestId to response so client can correlate it
654
+ const responseWithRequestId = {
655
+ ...completeResponse,
656
+ requestId: message.requestId
657
+ }
658
+
659
+ ws.send(JSON.stringify(responseWithRequestId))
646
660
  }
@@ -1,260 +1,69 @@
1
- // 🔥 FluxStack Static Files Plugin - Serve Public Files
1
+ // 🔥 FluxStack Static Files Plugin - Serve Public Files & Uploads
2
2
 
3
3
  import { existsSync, statSync } from 'fs'
4
- import { join, extname, resolve } from 'path'
5
- import type { FluxStack, PluginContext, Plugin } from '../../plugins/types'
6
- import { t } from 'elysia'
7
-
8
- // Response schema for static files info endpoint
9
- const StaticFilesInfoSchema = t.Object({
10
- success: t.Boolean(),
11
- config: t.Object({
12
- publicDir: t.String(),
13
- uploadsDir: t.String(),
14
- enablePublic: t.Boolean(),
15
- enableUploads: t.Boolean(),
16
- cacheMaxAge: t.Number()
17
- }),
18
- paths: t.Object({
19
- publicPath: t.String(),
20
- uploadsPath: t.String(),
21
- publicUrl: t.String(),
22
- uploadsUrl: t.String()
23
- }),
24
- timestamp: t.String()
25
- }, {
26
- description: 'Static files configuration and paths information'
27
- })
28
-
29
- export interface StaticFilesConfig {
30
- publicDir?: string // Default: 'public'
31
- uploadsDir?: string // Default: 'uploads'
32
- cacheMaxAge?: number // Default: 1 year in seconds
33
- enableUploads?: boolean // Default: true
34
- enablePublic?: boolean // Default: true
35
- publicRoute?: string // Default: '/public' (can be '/static' in dev)
36
- uploadsRoute?: string // Default: '/uploads'
37
- }
4
+ import { mkdir } from 'fs/promises'
5
+ import { resolve } from 'path'
6
+ import type { Plugin, PluginContext } from '../../plugins/types'
38
7
 
39
8
  export const staticFilesPlugin: Plugin = {
40
9
  name: 'static-files',
41
- description: 'Serve static files and uploads with proper caching and security',
10
+ description: 'Serve static files and uploads',
42
11
  author: 'FluxStack Team',
43
12
  priority: 'normal',
44
13
  category: 'core',
45
- tags: ['static', 'files', 'uploads', 'public'],
46
-
14
+ tags: ['static', 'files', 'uploads'],
15
+
47
16
  setup: async (context: PluginContext) => {
48
- context.logger.debug('📁 Setting up Static Files plugin...')
49
-
50
- const config: StaticFilesConfig = {
51
- publicDir: 'public',
52
- uploadsDir: 'uploads',
53
- cacheMaxAge: 31536000, // 1 year
54
- enableUploads: true,
55
- enablePublic: true,
56
- publicRoute: '/api/static', // Use /api/static in dev to avoid Vite conflicts
57
- uploadsRoute: '/api/uploads',
58
- ...context.config.staticFiles
59
- }
60
-
61
17
  const projectRoot = process.cwd()
62
- const publicPath = resolve(projectRoot, config.publicDir!)
63
- const uploadsPath = resolve(projectRoot, config.uploadsDir!)
64
-
65
- // MIME types mapping
66
- const getMimeType = (extension: string): string => {
67
- const mimeTypes: Record<string, string> = {
68
- // Images
69
- '.jpg': 'image/jpeg',
70
- '.jpeg': 'image/jpeg',
71
- '.png': 'image/png',
72
- '.gif': 'image/gif',
73
- '.webp': 'image/webp',
74
- '.svg': 'image/svg+xml',
75
- '.ico': 'image/x-icon',
76
-
77
- // Documents
78
- '.pdf': 'application/pdf',
79
- '.txt': 'text/plain',
80
- '.json': 'application/json',
81
- '.xml': 'application/xml',
82
-
83
- // Web assets
84
- '.css': 'text/css',
85
- '.js': 'application/javascript',
86
- '.html': 'text/html',
87
- '.htm': 'text/html',
88
-
89
- // Fonts
90
- '.woff': 'font/woff',
91
- '.woff2': 'font/woff2',
92
- '.ttf': 'font/ttf',
93
- '.otf': 'font/otf',
94
-
95
- // Audio/Video
96
- '.mp3': 'audio/mpeg',
97
- '.mp4': 'video/mp4',
98
- '.webm': 'video/webm',
99
- '.ogg': 'audio/ogg'
18
+ const publicDir = resolve(projectRoot, 'public')
19
+ const uploadsDir = resolve(projectRoot, 'uploads')
20
+
21
+ // Create directories if they don't exist
22
+ await mkdir(publicDir, { recursive: true })
23
+ await mkdir(uploadsDir, { recursive: true })
24
+ await mkdir(resolve(uploadsDir, 'avatars'), { recursive: true })
25
+
26
+ // Helper to serve files from a directory
27
+ const serveFile = (baseDir: string) => ({ params, set }: any) => {
28
+ const requestedPath = params['*'] || ''
29
+ const filePath = resolve(baseDir, requestedPath)
30
+
31
+ // Path traversal protection
32
+ if (!filePath.startsWith(baseDir)) {
33
+ set.status = 400
34
+ return { error: 'Invalid path' }
100
35
  }
101
-
102
- return mimeTypes[extension.toLowerCase()] || 'application/octet-stream'
103
- }
104
-
105
- // Security check for path traversal
106
- const isPathSafe = (filePath: string, basePath: string): boolean => {
107
- const resolvedPath = resolve(basePath, filePath)
108
- return resolvedPath.startsWith(basePath)
109
- }
110
-
111
- // Generic file serving function
112
- const serveFile = async (filePath: string, set: any) => {
36
+
37
+ // Check if file exists
38
+ if (!existsSync(filePath)) {
39
+ set.status = 404
40
+ return { error: 'File not found' }
41
+ }
42
+
43
+ // Check if it's a file (not directory)
113
44
  try {
114
- if (!existsSync(filePath)) {
115
- set.status = 404
116
- return {
117
- error: 'File not found',
118
- path: filePath.replace(projectRoot, ''),
119
- timestamp: new Date().toISOString()
120
- }
121
- }
122
-
123
- const stats = statSync(filePath)
124
- if (!stats.isFile()) {
45
+ if (!statSync(filePath).isFile()) {
125
46
  set.status = 404
126
47
  return { error: 'Not a file' }
127
48
  }
128
-
129
- // Set appropriate headers
130
- const extension = extname(filePath).toLowerCase()
131
- const mimeType = getMimeType(extension)
132
-
133
- set.headers['content-type'] = mimeType
134
- set.headers['content-length'] = stats.size.toString()
135
- set.headers['last-modified'] = stats.mtime.toUTCString()
136
- set.headers['cache-control'] = `public, max-age=${config.cacheMaxAge}`
137
- set.headers['etag'] = `"${stats.mtime.getTime()}-${stats.size}"`
138
-
139
- // Security headers for images
140
- if (mimeType.startsWith('image/')) {
141
- set.headers['x-content-type-options'] = 'nosniff'
142
- }
143
-
144
- context.logger.debug(`📁 Serving file: ${filePath.replace(projectRoot, '')}`, {
145
- size: stats.size,
146
- mimeType,
147
- lastModified: stats.mtime
148
- })
149
-
150
- return Bun.file(filePath)
151
-
152
- } catch (error: any) {
153
- context.logger.error('❌ File serving error:', error.message)
154
- set.status = 500
155
- return { error: 'Failed to serve file' }
156
- }
157
- }
158
-
159
- // Add static file routes
160
- if (config.enablePublic) {
161
- const publicRoutePattern = `${config.publicRoute}/*`
162
- context.app.get(publicRoutePattern, ({ params, set }) => {
163
- const filePath = params['*'] || ''
164
-
165
- if (!isPathSafe(filePath, publicPath)) {
166
- set.status = 400
167
- return { error: 'Invalid file path' }
168
- }
169
-
170
- const fullPath = join(publicPath, filePath)
171
- return serveFile(fullPath, set)
172
- })
173
-
174
- context.logger.debug(`📁 Public files route enabled: ${publicRoutePattern} → ${config.publicDir}`)
175
- }
176
-
177
- if (config.enableUploads) {
178
- const uploadsRoutePattern = `${config.uploadsRoute}/*`
179
- context.app.get(uploadsRoutePattern, ({ params, set }) => {
180
- const filePath = params['*'] || ''
181
-
182
- if (!isPathSafe(filePath, uploadsPath)) {
183
- set.status = 400
184
- return { error: 'Invalid file path' }
185
- }
186
-
187
- const fullPath = join(uploadsPath, filePath)
188
- return serveFile(fullPath, set)
189
- })
190
-
191
- context.logger.debug(`📁 Uploads route enabled: ${uploadsRoutePattern} → ${config.uploadsDir}`)
192
- }
193
-
194
- // Static files info endpoint
195
- context.app.get('/api/static/info', () => {
196
- return {
197
- success: true,
198
- config: {
199
- publicDir: config.publicDir,
200
- uploadsDir: config.uploadsDir,
201
- enablePublic: config.enablePublic,
202
- enableUploads: config.enableUploads,
203
- cacheMaxAge: config.cacheMaxAge
204
- },
205
- paths: {
206
- publicPath,
207
- uploadsPath,
208
- publicUrl: config.publicRoute,
209
- uploadsUrl: config.uploadsRoute
210
- },
211
- timestamp: new Date().toISOString()
49
+ } catch {
50
+ set.status = 404
51
+ return { error: 'File not found' }
212
52
  }
213
- }, {
214
- detail: {
215
- summary: 'Static Files Configuration',
216
- description: 'Returns configuration and paths for static files and uploads serving',
217
- tags: ['Static Files', 'Configuration']
218
- },
219
- response: StaticFilesInfoSchema
220
- })
221
-
222
- // Create directories if they don't exist
223
- const { mkdir } = await import('fs/promises')
224
-
225
- if (config.enablePublic && !existsSync(publicPath)) {
226
- await mkdir(publicPath, { recursive: true })
227
- context.logger.debug(`📁 Created public directory: ${publicPath}`)
228
- }
229
-
230
- if (config.enableUploads && !existsSync(uploadsPath)) {
231
- await mkdir(uploadsPath, { recursive: true })
232
- await mkdir(join(uploadsPath, 'avatars'), { recursive: true })
233
- context.logger.debug(`📁 Created uploads directory: ${uploadsPath}`)
234
- }
235
-
236
- context.logger.debug('📁 Static Files plugin setup complete', {
237
- publicEnabled: config.enablePublic,
238
- uploadsEnabled: config.enableUploads,
239
- publicPath: config.enablePublic ? publicPath : 'disabled',
240
- uploadsPath: config.enableUploads ? uploadsPath : 'disabled'
241
- })
242
- },
243
53
 
244
- onServerStart: async (context: PluginContext) => {
245
- const config = {
246
- enablePublic: true,
247
- enableUploads: true,
248
- publicRoute: '/api/static',
249
- uploadsRoute: '/api/uploads',
250
- ...context.config.staticFiles
54
+ // Set cache header (1 year)
55
+ set.headers['cache-control'] = 'public, max-age=31536000'
56
+
57
+ // Bun.file() handles: content-type, content-length, streaming
58
+ return Bun.file(filePath)
251
59
  }
252
- context.logger.debug('📁 Static Files plugin ready', {
253
- routes: [
254
- config.enablePublic ? `${config.publicRoute}/*` : null,
255
- config.enableUploads ? `${config.uploadsRoute}/*` : null,
256
- '/api/static/info'
257
- ].filter(Boolean)
60
+
61
+ // Register routes
62
+ context.app.get('/api/static/*', serveFile(publicDir))
63
+ context.app.get('/api/uploads/*', serveFile(uploadsDir))
64
+
65
+ context.logger.debug('📁 Static files plugin ready', {
66
+ routes: ['/api/static/*', '/api/uploads/*']
258
67
  })
259
68
  }
260
- }
69
+ }
@@ -1,34 +1,34 @@
1
- import { swagger } from '@elysiajs/swagger'
2
- import type { Plugin, PluginContext } from '@/core/plugins/types'
3
-
4
- export const swaggerPlugin: Plugin = {
5
- name: 'swagger',
6
- setup(context: PluginContext) {
7
- context.app.use(swagger({
8
- path: '/swagger',
9
- documentation: {
10
- info: {
11
- title: 'FluxStack API',
12
- version: '1.7.4',
13
- description: 'Modern full-stack TypeScript framework with type-safe API endpoints'
14
- },
15
- tags: [
16
- {
17
- name: 'Health',
18
- description: 'Health check endpoints'
19
- },
20
- {
21
- name: 'Users',
22
- description: 'User management endpoints'
23
- }
24
- ],
25
- servers: [
26
- {
27
- url: `http://localhost:${context.config.server?.port || 3000}`,
28
- description: 'Development server'
29
- }
30
- ]
31
- }
32
- }))
33
- }
1
+ import { swagger } from '@elysiajs/swagger'
2
+ import type { Plugin, PluginContext } from '@/core/plugins/types'
3
+
4
+ export const swaggerPlugin: Plugin = {
5
+ name: 'swagger',
6
+ setup(context: PluginContext) {
7
+ context.app.use(swagger({
8
+ path: '/swagger',
9
+ documentation: {
10
+ info: {
11
+ title: 'FluxStack API',
12
+ version: '1.7.4',
13
+ description: 'Modern full-stack TypeScript framework with type-safe API endpoints'
14
+ },
15
+ tags: [
16
+ {
17
+ name: 'Health',
18
+ description: 'Health check endpoints'
19
+ },
20
+ {
21
+ name: 'Users',
22
+ description: 'User management endpoints'
23
+ }
24
+ ],
25
+ servers: [
26
+ {
27
+ url: `http://localhost:${context.config.server?.port || 3000}`,
28
+ description: 'Development server'
29
+ }
30
+ ]
31
+ }
32
+ }))
33
+ }
34
34
  }
@@ -83,6 +83,28 @@ export interface BundleOptions {
83
83
  external?: string[]
84
84
  minify?: boolean
85
85
  sourceMaps?: boolean
86
+ target?: string
87
+ executable?: ExecutableOptions
88
+ }
89
+
90
+ /**
91
+ * Options for compiling standalone executables
92
+ * Note: Cross-platform compilation is limited - executables are built for the current platform.
93
+ * To build for different platforms, run the build on that platform.
94
+ */
95
+ export interface ExecutableOptions {
96
+ // Windows-specific options
97
+ windows?: {
98
+ hideConsole?: boolean
99
+ icon?: string
100
+ title?: string
101
+ publisher?: string
102
+ version?: string
103
+ description?: string
104
+ copyright?: string
105
+ }
106
+ // Additional custom build arguments
107
+ customArgs?: string[]
86
108
  }
87
109
 
88
110
  export interface OptimizationOptions {
@@ -13,7 +13,15 @@ export type {
13
13
  PluginUtils,
14
14
  RequestContext,
15
15
  ResponseContext,
16
- ErrorContext
16
+ ErrorContext,
17
+ BuildContext,
18
+ ConfigLoadContext,
19
+ RouteContext,
20
+ ValidationContext,
21
+ TransformContext,
22
+ BuildAssetContext,
23
+ BuildErrorContext,
24
+ PluginEventContext
17
25
  } from "../plugins/types"
18
26
 
19
27
  // Export Plugin as a standalone type for convenience