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.
Files changed (154) hide show
  1. package/.dockerignore +82 -0
  2. package/.env.example +19 -0
  3. package/Dockerfile +70 -0
  4. package/README.md +6 -3
  5. package/app/client/SIMPLIFICATION.md +140 -0
  6. package/app/client/frontend-only.ts +1 -1
  7. package/app/client/src/App.tsx +148 -283
  8. package/app/client/src/index.css +5 -20
  9. package/app/client/src/lib/eden-api.ts +53 -220
  10. package/app/client/src/main.tsx +2 -3
  11. package/app/server/app.ts +20 -5
  12. package/app/server/backend-only.ts +15 -12
  13. package/app/server/controllers/users.controller.ts +57 -31
  14. package/app/server/index.ts +86 -96
  15. package/app/server/live/register-components.ts +18 -7
  16. package/app/server/routes/env-test.ts +110 -0
  17. package/app/server/routes/index.ts +1 -8
  18. package/app/server/routes/users.routes.ts +192 -91
  19. package/config/app.config.ts +2 -54
  20. package/config/client.config.ts +95 -0
  21. package/config/fluxstack.config.ts +2 -2
  22. package/config/index.ts +57 -22
  23. package/config/monitoring.config.ts +114 -0
  24. package/config/plugins.config.ts +80 -0
  25. package/config/runtime.config.ts +0 -17
  26. package/config/server.config.ts +50 -30
  27. package/core/build/bundler.ts +17 -16
  28. package/core/build/flux-plugins-generator.ts +34 -23
  29. package/core/build/index.ts +32 -31
  30. package/core/build/live-components-generator.ts +44 -30
  31. package/core/build/optimizer.ts +37 -17
  32. package/core/cli/command-registry.ts +4 -14
  33. package/core/cli/commands/plugin-deps.ts +8 -8
  34. package/core/cli/generators/component.ts +3 -3
  35. package/core/cli/generators/controller.ts +4 -4
  36. package/core/cli/generators/index.ts +8 -8
  37. package/core/cli/generators/interactive.ts +4 -4
  38. package/core/cli/generators/plugin.ts +3 -3
  39. package/core/cli/generators/prompts.ts +1 -1
  40. package/core/cli/generators/route.ts +27 -11
  41. package/core/cli/generators/service.ts +5 -5
  42. package/core/cli/generators/template-engine.ts +1 -1
  43. package/core/cli/generators/types.ts +1 -1
  44. package/core/cli/index.ts +158 -189
  45. package/core/cli/plugin-discovery.ts +3 -3
  46. package/core/client/hooks/index.ts +2 -2
  47. package/core/client/hooks/state-validator.ts +1 -1
  48. package/core/client/hooks/useAuth.ts +1 -1
  49. package/core/client/hooks/useChunkedUpload.ts +1 -1
  50. package/core/client/hooks/useHybridLiveComponent.ts +1 -1
  51. package/core/client/hooks/useWebSocket.ts +1 -1
  52. package/core/config/env.ts +5 -1
  53. package/core/config/runtime-config.ts +12 -10
  54. package/core/config/schema.ts +33 -2
  55. package/core/framework/server.ts +30 -14
  56. package/core/framework/types.ts +2 -2
  57. package/core/index.ts +31 -23
  58. package/core/live/ComponentRegistry.ts +1 -1
  59. package/core/plugins/built-in/live-components/commands/create-live-component.ts +1 -1
  60. package/core/plugins/built-in/live-components/index.ts +1 -1
  61. package/core/plugins/built-in/monitoring/index.ts +65 -161
  62. package/core/plugins/built-in/static/index.ts +75 -277
  63. package/core/plugins/built-in/swagger/index.ts +301 -231
  64. package/core/plugins/built-in/vite/index.ts +342 -377
  65. package/core/plugins/config.ts +2 -2
  66. package/core/plugins/dependency-manager.ts +2 -2
  67. package/core/plugins/discovery.ts +1 -1
  68. package/core/plugins/executor.ts +2 -2
  69. package/core/plugins/manager.ts +19 -4
  70. package/core/plugins/module-resolver.ts +1 -1
  71. package/core/plugins/registry.ts +25 -21
  72. package/core/plugins/types.ts +147 -5
  73. package/core/server/backend-entry.ts +51 -0
  74. package/core/server/framework.ts +2 -2
  75. package/core/server/live/ComponentRegistry.ts +9 -26
  76. package/core/server/live/FileUploadManager.ts +1 -1
  77. package/core/server/live/auto-generated-components.ts +26 -0
  78. package/core/server/live/websocket-plugin.ts +211 -19
  79. package/core/server/middleware/errorHandling.ts +1 -1
  80. package/core/server/middleware/index.ts +4 -4
  81. package/core/server/plugins/database.ts +1 -2
  82. package/core/server/plugins/static-files-plugin.ts +259 -231
  83. package/core/server/plugins/swagger.ts +1 -1
  84. package/core/server/services/BaseService.ts +1 -1
  85. package/core/server/services/ServiceContainer.ts +1 -1
  86. package/core/server/services/index.ts +4 -4
  87. package/core/server/standalone.ts +16 -1
  88. package/core/testing/index.ts +1 -1
  89. package/core/testing/setup.ts +1 -1
  90. package/core/types/plugin.ts +6 -0
  91. package/core/utils/build-logger.ts +324 -0
  92. package/core/utils/config-schema.ts +2 -6
  93. package/core/utils/helpers.ts +14 -9
  94. package/core/utils/logger/startup-banner.ts +7 -33
  95. package/core/utils/regenerate-files.ts +69 -0
  96. package/core/utils/version.ts +6 -6
  97. package/create-fluxstack.ts +68 -25
  98. package/fluxstack.config.ts +138 -252
  99. package/package.json +3 -18
  100. package/plugins/crypto-auth/index.ts +52 -47
  101. package/plugins/crypto-auth/server/AuthMiddleware.ts +1 -1
  102. package/plugins/crypto-auth/server/middlewares/helpers.ts +16 -1
  103. package/vitest.config.ts +17 -26
  104. package/app/client/src/App.css +0 -883
  105. package/app/client/src/components/ErrorBoundary.tsx +0 -107
  106. package/app/client/src/components/ErrorDisplay.css +0 -365
  107. package/app/client/src/components/ErrorDisplay.tsx +0 -258
  108. package/app/client/src/components/FluxStackConfig.tsx +0 -1321
  109. package/app/client/src/components/HybridLiveCounter.tsx +0 -140
  110. package/app/client/src/components/LiveClock.tsx +0 -286
  111. package/app/client/src/components/MainLayout.tsx +0 -388
  112. package/app/client/src/components/SidebarNavigation.tsx +0 -391
  113. package/app/client/src/components/StateDemo.tsx +0 -178
  114. package/app/client/src/components/SystemMonitor.tsx +0 -1044
  115. package/app/client/src/components/UserProfile.tsx +0 -809
  116. package/app/client/src/hooks/useAuth.ts +0 -39
  117. package/app/client/src/hooks/useNotifications.ts +0 -56
  118. package/app/client/src/lib/errors.ts +0 -340
  119. package/app/client/src/lib/hooks/useErrorHandler.ts +0 -258
  120. package/app/client/src/lib/index.ts +0 -45
  121. package/app/client/src/pages/ApiDocs.tsx +0 -182
  122. package/app/client/src/pages/CryptoAuthPage.tsx +0 -394
  123. package/app/client/src/pages/Demo.tsx +0 -174
  124. package/app/client/src/pages/HybridLive.tsx +0 -263
  125. package/app/client/src/pages/Overview.tsx +0 -155
  126. package/app/client/src/store/README.md +0 -43
  127. package/app/client/src/store/index.ts +0 -16
  128. package/app/client/src/store/slices/uiSlice.ts +0 -151
  129. package/app/client/src/store/slices/userSlice.ts +0 -161
  130. package/app/client/src/test/README.md +0 -257
  131. package/app/client/src/test/setup.ts +0 -70
  132. package/app/client/src/test/types.ts +0 -12
  133. package/app/server/live/CounterComponent.ts +0 -191
  134. package/app/server/live/FluxStackConfig.ts +0 -534
  135. package/app/server/live/SidebarNavigation.ts +0 -157
  136. package/app/server/live/SystemMonitor.ts +0 -595
  137. package/app/server/live/SystemMonitorIntegration.ts +0 -151
  138. package/app/server/live/UserProfileComponent.ts +0 -141
  139. package/app/server/middleware/auth.ts +0 -136
  140. package/app/server/middleware/errorHandling.ts +0 -252
  141. package/app/server/middleware/index.ts +0 -10
  142. package/app/server/middleware/rateLimit.ts +0 -193
  143. package/app/server/middleware/requestLogging.ts +0 -215
  144. package/app/server/middleware/validation.ts +0 -270
  145. package/app/server/routes/config.ts +0 -145
  146. package/app/server/routes/crypto-auth-demo.routes.ts +0 -167
  147. package/app/server/routes/example-with-crypto-auth.routes.ts +0 -235
  148. package/app/server/routes/exemplo-posts.routes.ts +0 -161
  149. package/app/server/routes/upload.ts +0 -92
  150. package/app/server/services/NotificationService.ts +0 -302
  151. package/app/server/services/UserService.ts +0 -222
  152. package/app/server/services/index.ts +0 -46
  153. package/app/server/types/index.ts +0 -1
  154. package/config/build.config.ts +0 -24
@@ -1,378 +1,343 @@
1
- import type { FluxStack, PluginContext, RequestContext } from "../../types"
2
- import { createServer, type ViteDevServer } from 'vite'
3
- import { FLUXSTACK_VERSION } from "../../../utils/version"
4
-
5
- type Plugin = FluxStack.Plugin
6
-
7
- let viteServer: ViteDevServer | null = null
8
-
9
- export const vitePlugin: Plugin = {
10
- name: "vite",
11
- version: FLUXSTACK_VERSION,
12
- description: "Enhanced Vite integration plugin for FluxStack with improved error handling and monitoring",
13
- author: "FluxStack Team",
14
- priority: 800, // Should run early to setup proxying
15
- category: "development",
16
- tags: ["vite", "development", "hot-reload"],
17
- dependencies: [], // No dependencies
18
-
19
- configSchema: {
20
- type: "object",
21
- properties: {
22
- enabled: {
23
- type: "boolean",
24
- description: "Enable Vite integration"
25
- },
26
- port: {
27
- type: "number",
28
- minimum: 1,
29
- maximum: 65535,
30
- description: "Vite development server port"
31
- },
32
- host: {
33
- type: "string",
34
- description: "Vite development server host"
35
- },
36
- checkInterval: {
37
- type: "number",
38
- minimum: 100,
39
- description: "Interval to check if Vite is running (ms)"
40
- },
41
- maxRetries: {
42
- type: "number",
43
- minimum: 1,
44
- description: "Maximum retries to connect to Vite"
45
- },
46
- timeout: {
47
- type: "number",
48
- minimum: 100,
49
- description: "Timeout for Vite requests (ms)"
50
- },
51
- proxyPaths: {
52
- type: "array",
53
- items: { type: "string" },
54
- description: "Paths to proxy to Vite (defaults to all non-API paths)"
55
- },
56
- excludePaths: {
57
- type: "array",
58
- items: { type: "string" },
59
- description: "Paths to exclude from Vite proxying"
60
- }
61
- },
62
- additionalProperties: false
63
- },
64
-
65
- defaultConfig: {
66
- enabled: true,
67
- port: 5173,
68
- host: "localhost",
69
- checkInterval: 2000,
70
- maxRetries: 10,
71
- timeout: 5000,
72
- proxyPaths: [],
73
- excludePaths: []
74
- },
75
-
76
- setup: async (context: PluginContext) => {
77
- const config = getPluginConfig(context)
78
-
79
- if (!config.enabled || !context.config.client) {
80
- context.logger.debug('Vite plugin disabled or no client configuration found')
81
- return
82
- }
83
-
84
- const vitePort = config.port || context.config.client.port || 5173
85
- const viteHost = config.host || "localhost"
86
-
87
- try {
88
- const { startGroup, endGroup, logInGroup } = await import('../../../utils/logger/group-logger')
89
-
90
- startGroup({
91
- title: 'Vite Development Server',
92
- icon: '🎨',
93
- color: 'magenta',
94
- collapsed: true
95
- })
96
-
97
- logInGroup(`Starting on ${viteHost}:${vitePort}`, '📍')
98
-
99
- // Start Vite dev server programmatically
100
- viteServer = await createServer({
101
- configFile: './vite.config.ts',
102
- // Don't override root - let vite.config.ts handle it
103
- server: {
104
- port: vitePort,
105
- host: viteHost,
106
- strictPort: true
107
- },
108
- logLevel: 'warn' // Suppress Vite's verbose logs
109
- })
110
-
111
- await viteServer.listen()
112
-
113
- // Custom URL display instead of viteServer.printUrls()
114
- logInGroup(`Local: http://${viteHost}:${vitePort}/`, '✅')
115
- logInGroup('Hot reload coordination active', '🔄')
116
-
117
- endGroup()
118
- console.log('') // Separator line
119
-
120
- // Store Vite config in context for later use
121
- ; (context as any).viteConfig = {
122
- port: vitePort,
123
- host: viteHost,
124
- ...config,
125
- server: viteServer
126
- }
127
-
128
- // Setup cleanup on process exit
129
- const cleanup = async () => {
130
- if (viteServer) {
131
- context.logger.debug('🛑 Stopping Vite server...')
132
- await viteServer.close()
133
- viteServer = null
134
- }
135
- }
136
-
137
- process.on('SIGINT', cleanup)
138
- process.on('SIGTERM', cleanup)
139
- process.on('exit', cleanup)
140
-
141
- } catch (error) {
142
- context.logger.error('❌ Failed to start Vite server programmatically:', error)
143
- context.logger.debug('⚠️ Falling back to monitoring mode...')
144
-
145
- // Fallback to monitoring if programmatic start fails
146
- ; (context as any).viteConfig = {
147
- port: vitePort,
148
- host: viteHost,
149
- ...config
150
- }
151
- monitorVite(context, viteHost, vitePort, config)
152
- }
153
- },
154
-
155
- onServerStart: async (context: PluginContext) => {
156
- const config = getPluginConfig(context)
157
- const viteConfig = (context as any).viteConfig
158
-
159
- if (config.enabled && viteConfig) {
160
- context.logger.debug(`Vite integration active - monitoring ${viteConfig.host}:${viteConfig.port}`)
161
- }
162
- },
163
-
164
- onBeforeRoute: async (requestContext: RequestContext) => {
165
- // Skip API routes and swagger - let them be handled by backend
166
- if (requestContext.path.startsWith("/api") || requestContext.path.startsWith("/swagger")) {
167
- return
168
- }
169
-
170
- // For Vite internal routes, proxy directly to Vite server
171
- if (requestContext.path.startsWith("/@") || // All Vite internal routes (/@vite/, /@fs/, /@react-refresh, etc.)
172
- requestContext.path.startsWith("/__vite") || // Vite HMR and dev routes
173
- requestContext.path.startsWith("/node_modules") || // Direct node_modules access
174
- requestContext.path.includes("/.vite/") || // Vite cache and deps
175
- requestContext.path.endsWith(".js.map") || // Source maps
176
- requestContext.path.endsWith(".css.map")) { // CSS source maps
177
-
178
- // Use fixed configuration for Vite proxy
179
- const viteHost = "localhost"
180
- const vitePort = 5173
181
-
182
- try {
183
- const url = new URL(requestContext.request.url)
184
- const viteUrl = `http://${viteHost}:${vitePort}${requestContext.path}${url.search}`
185
-
186
- // Forward request to Vite
187
- const response = await fetch(viteUrl, {
188
- method: requestContext.method,
189
- headers: requestContext.headers,
190
- body: requestContext.method !== 'GET' && requestContext.method !== 'HEAD' ? requestContext.request.body : undefined
191
- })
192
-
193
- // Return the Vite response
194
- const body = await response.arrayBuffer()
195
-
196
- requestContext.handled = true
197
- requestContext.response = new Response(body, {
198
- status: response.status,
199
- statusText: response.statusText,
200
- headers: response.headers
201
- })
202
-
203
- } catch (viteError) {
204
- // If Vite fails, let the request continue to normal routing (will become 404)
205
- // Only log if explicitly enabled for debugging
206
- const { serverConfig } = await import('@/config/server.config')
207
- if (serverConfig.enableViteProxyLogs) {
208
- console.warn(`Vite proxy error: ${viteError}`)
209
- }
210
- }
211
- return
212
- }
213
-
214
- // Use fixed configuration for simplicity - Vite should be running on port 5173
215
- const viteHost = "localhost"
216
- const vitePort = 5173
217
-
218
- try {
219
- const url = new URL(requestContext.request.url)
220
- const viteUrl = `http://${viteHost}:${vitePort}${requestContext.path}${url.search}`
221
-
222
- // Forward request to Vite
223
- const response = await fetch(viteUrl, {
224
- method: requestContext.method,
225
- headers: requestContext.headers,
226
- body: requestContext.method !== 'GET' && requestContext.method !== 'HEAD' ? requestContext.request.body : undefined
227
- })
228
-
229
- // If Vite responds successfully, handle the request
230
- if (response.ok || response.status < 500) {
231
- // Return a proper Response object with all headers and status
232
- const body = await response.arrayBuffer()
233
-
234
- requestContext.handled = true
235
- requestContext.response = new Response(body, {
236
- status: response.status,
237
- statusText: response.statusText,
238
- headers: response.headers
239
- })
240
- }
241
-
242
- } catch (viteError) {
243
- // If Vite fails, let the request continue to normal routing (will become 404)
244
- // Only log if explicitly enabled for debugging
245
- const { serverConfig } = await import('@/config/server.config')
246
- if (serverConfig.enableViteProxyLogs) {
247
- console.warn(`Vite proxy error: ${viteError}`)
248
- }
249
- }
250
- }
251
- }
252
-
253
- // Helper function to get plugin config
254
- function getPluginConfig(context: PluginContext) {
255
- const pluginConfig = context.config.plugins.config?.vite || {}
256
- return { ...vitePlugin.defaultConfig, ...pluginConfig }
257
- }
258
-
259
- // Monitor Vite server status with automatic port detection
260
- async function monitorVite(
261
- context: PluginContext,
262
- host: string,
263
- initialPort: number,
264
- config: any
265
- ) {
266
- let retries = 0
267
- let isConnected = false
268
- let actualPort = initialPort
269
- let portDetected = false
270
-
271
- const checkVite = async () => {
272
- try {
273
- // If we haven't found the correct port yet, try to detect it
274
- if (!portDetected) {
275
- const detectedPort = await detectVitePort(host, initialPort)
276
- if (detectedPort !== null) {
277
- actualPort = detectedPort
278
- portDetected = true
279
- // Update the context with the detected port
280
- if ((context as any).viteConfig) {
281
- ; (context as any).viteConfig.port = actualPort
282
- }
283
- }
284
- }
285
-
286
- const isRunning = await checkViteRunning(host, actualPort, config.timeout)
287
-
288
- if (isRunning && !isConnected) {
289
- isConnected = true
290
- retries = 0
291
- if (actualPort !== initialPort) {
292
- context.logger.debug(`✓ Vite server detected on ${host}:${actualPort} (auto-detected from port ${initialPort})`)
293
- } else {
294
- context.logger.debug(`✓ Vite server detected on ${host}:${actualPort}`)
295
- }
296
- context.logger.debug("Hot reload coordination active")
297
- } else if (!isRunning && isConnected) {
298
- isConnected = false
299
- context.logger.warn(`✗ Vite server disconnected from ${host}:${actualPort}`)
300
- // Reset port detection when disconnected
301
- portDetected = false
302
- actualPort = initialPort
303
- } else if (!isRunning) {
304
- retries++
305
- if (retries <= config.maxRetries) {
306
- if (portDetected) {
307
- context.logger.debug(`Waiting for Vite server on ${host}:${actualPort}... (${retries}/${config.maxRetries})`)
308
- } else {
309
- context.logger.debug(`Detecting Vite server port... (${retries}/${config.maxRetries})`)
310
- }
311
- } else if (retries === config.maxRetries + 1) {
312
- context.logger.warn(`Vite server not found after ${config.maxRetries} attempts. Development features may be limited.`)
313
- }
314
- }
315
- } catch (error) {
316
- if (isConnected) {
317
- context.logger.error('Error checking Vite server status', { error })
318
- }
319
- }
320
-
321
- // Continue monitoring
322
- setTimeout(checkVite, config.checkInterval)
323
- }
324
-
325
- // Start monitoring after a brief delay
326
- setTimeout(checkVite, 1000)
327
- }
328
-
329
- // Auto-detect Vite port by trying common ports
330
- async function detectVitePort(host: string, startPort: number): Promise<number | null> {
331
- // Try the initial port first, then common alternatives
332
- const portsToTry = [
333
- startPort,
334
- startPort + 1,
335
- startPort + 2,
336
- startPort + 3,
337
- 5174, // Common Vite alternative
338
- 5175,
339
- 5176,
340
- 3000, // Sometimes Vite might use this
341
- 4173 // Another common alternative
342
- ]
343
-
344
- for (const port of portsToTry) {
345
- try {
346
- const isRunning = await checkViteRunning(host, port, 1000)
347
- if (isRunning) {
348
- return port
349
- }
350
- } catch (error) {
351
- // Continue trying other ports
352
- }
353
- }
354
-
355
- return null
356
- }
357
-
358
- // Check if Vite is running
359
- async function checkViteRunning(host: string, port: number, timeout: number = 1000): Promise<boolean> {
360
- try {
361
- const controller = new AbortController()
362
- const timeoutId = setTimeout(() => controller.abort(), timeout)
363
-
364
- const response = await fetch(`http://${host}:${port}`, {
365
- signal: controller.signal,
366
- method: 'HEAD' // Use HEAD to minimize data transfer
367
- })
368
-
369
- clearTimeout(timeoutId)
370
- return response.status >= 200 && response.status < 500
371
- } catch (error) {
372
- return false
373
- }
374
- }
375
-
376
- // Note: Proxy logic is now handled directly in the onBeforeRoute hook above
377
-
1
+ import type { FluxStack, PluginContext, RequestContext } from "@/core/plugins/types"
2
+ import { createServer, type ViteDevServer } from 'vite'
3
+ import { FLUXSTACK_VERSION } from "@/core/utils/version"
4
+ import { clientConfig } from '@/config/client.config'
5
+
6
+ type Plugin = FluxStack.Plugin
7
+
8
+ let viteServer: ViteDevServer | null = null
9
+
10
+ // Default configuration values (uses clientConfig from /config)
11
+ const DEFAULTS = {
12
+ enabled: true,
13
+ port: clientConfig.vite.port,
14
+ host: "localhost",
15
+ checkInterval: 2000,
16
+ maxRetries: 10,
17
+ timeout: 5000,
18
+ proxyPaths: [] as string[],
19
+ excludePaths: [] as string[]
20
+ }
21
+
22
+ /**
23
+ * Helper to safely parse request.url which might be relative or absolute
24
+ */
25
+ function parseRequestURL(request: Request): URL {
26
+ try {
27
+ // Try parsing as absolute URL first
28
+ return new URL(request.url)
29
+ } catch {
30
+ // If relative, use host from headers or default to localhost
31
+ const host = request.headers.get('host') || 'localhost'
32
+ const protocol = request.headers.get('x-forwarded-proto') || 'http'
33
+ return new URL(request.url, `${protocol}://${host}`)
34
+ }
35
+ }
36
+
37
+ export const vitePlugin: Plugin = {
38
+ name: "vite",
39
+ version: FLUXSTACK_VERSION,
40
+ description: "Enhanced Vite integration plugin for FluxStack with improved error handling and monitoring",
41
+ author: "FluxStack Team",
42
+ priority: 800, // Should run early to setup proxying
43
+ category: "development",
44
+ tags: ["vite", "development", "hot-reload"],
45
+ dependencies: [],
46
+
47
+ setup: async (context: PluginContext) => {
48
+ if (!DEFAULTS.enabled) {
49
+ context.logger.debug('Vite plugin disabled or no client configuration found')
50
+ return
51
+ }
52
+
53
+ const vitePort = DEFAULTS.port || clientConfig.vite.port || 5173
54
+ const viteHost = DEFAULTS.host || "localhost"
55
+
56
+ // Import group logger utilities
57
+ const { startGroup, endGroup, logInGroup } = await import('@/core/utils/logger/group-logger')
58
+
59
+ try {
60
+ // Start Vite dev server programmatically (silently)
61
+ viteServer = await createServer({
62
+ configFile: './vite.config.ts',
63
+ // Don't override root - let vite.config.ts handle it
64
+ server: {
65
+ port: vitePort,
66
+ host: viteHost,
67
+ strictPort: true
68
+ },
69
+ logLevel: 'silent' // Suppress all Vite logs
70
+ })
71
+
72
+ await viteServer.listen()
73
+
74
+ context.logger.debug(`Vite server started on ${viteHost}:${vitePort} (internal proxy)`)
75
+ context.logger.debug('Hot reload coordination active')
76
+
77
+ // Store Vite config in context for later use
78
+ ; (context as any).viteConfig = {
79
+ port: vitePort,
80
+ host: viteHost,
81
+ server: viteServer
82
+ }
83
+
84
+ // Setup cleanup on process exit
85
+ const cleanup = async () => {
86
+ if (viteServer) {
87
+ context.logger.debug('🛑 Stopping Vite server...')
88
+ await viteServer.close()
89
+ viteServer = null
90
+ }
91
+ }
92
+
93
+ process.on('SIGINT', cleanup)
94
+ process.on('SIGTERM', cleanup)
95
+ process.on('exit', cleanup)
96
+
97
+ } catch (error) {
98
+ // Check if error is related to port already in use
99
+ const errorMessage = error instanceof Error ? error.message : String(error)
100
+ const isPortInUse = errorMessage.includes('EADDRINUSE') ||
101
+ errorMessage.includes('address already in use') ||
102
+ errorMessage.includes('Port') && errorMessage.includes('is in use')
103
+
104
+ if (isPortInUse) {
105
+ endGroup()
106
+ console.log('') // Separator line
107
+ context.logger.error(`❌ Failed to start Vite: Port ${vitePort} is already in use`)
108
+ context.logger.info(`💡 Try one of these solutions:`)
109
+ context.logger.info(` 1. Stop the process using port ${vitePort}`)
110
+ context.logger.info(` 2. Change VITE_PORT in your .env file`)
111
+ context.logger.info(` 3. Kill the process: ${process.platform === 'win32' ? `netstat -ano | findstr :${vitePort}` : `lsof -ti:${vitePort} | xargs kill -9`}`)
112
+ process.exit(1)
113
+ } else {
114
+ context.logger.error('❌ Failed to start Vite server:', errorMessage)
115
+ context.logger.debug('Full error:', error)
116
+ context.logger.debug('⚠️ Falling back to monitoring mode...')
117
+
118
+ // Fallback to monitoring if programmatic start fails
119
+ ; (context as any).viteConfig = {
120
+ port: vitePort,
121
+ host: viteHost
122
+ }
123
+ monitorVite(context, viteHost, vitePort)
124
+ }
125
+ }
126
+ },
127
+
128
+ onServerStart: async (context: PluginContext) => {
129
+ const viteConfig = (context as any).viteConfig
130
+
131
+ if (DEFAULTS.enabled && viteConfig) {
132
+ context.logger.debug(`Vite integration active - monitoring ${viteConfig.host}:${viteConfig.port}`)
133
+ }
134
+ },
135
+
136
+ onBeforeRoute: async (requestContext: RequestContext) => {
137
+ // Skip API routes and swagger - let them be handled by backend
138
+ if (requestContext.path.startsWith("/api") || requestContext.path.startsWith("/swagger")) {
139
+ return
140
+ }
141
+
142
+ // For Vite internal routes, proxy directly to Vite server
143
+ if (requestContext.path.startsWith("/@") || // All Vite internal routes (/@vite/, /@fs/, /@react-refresh, etc.)
144
+ requestContext.path.startsWith("/__vite") || // Vite HMR and dev routes
145
+ requestContext.path.startsWith("/node_modules") || // Direct node_modules access
146
+ requestContext.path.includes("/.vite/") || // Vite cache and deps
147
+ requestContext.path.endsWith(".js.map") || // Source maps
148
+ requestContext.path.endsWith(".css.map")) { // CSS source maps
149
+
150
+ // Use fixed configuration for Vite proxy
151
+ const viteHost = "localhost"
152
+ const vitePort = 5173
153
+
154
+ try {
155
+ const url = parseRequestURL(requestContext.request)
156
+ const viteUrl = `http://${viteHost}:${vitePort}${requestContext.path}${url.search}`
157
+
158
+ // Forward request to Vite
159
+ const response = await fetch(viteUrl, {
160
+ method: requestContext.method,
161
+ headers: requestContext.headers,
162
+ body: requestContext.method !== 'GET' && requestContext.method !== 'HEAD' ? requestContext.request.body : undefined
163
+ })
164
+
165
+ // Return the Vite response
166
+ const body = await response.arrayBuffer()
167
+
168
+ requestContext.handled = true
169
+ requestContext.response = new Response(body, {
170
+ status: response.status,
171
+ statusText: response.statusText,
172
+ headers: response.headers
173
+ })
174
+
175
+ } catch (viteError) {
176
+ // If Vite fails, let the request continue to normal routing (will become 404)
177
+ // Only log if explicitly enabled for debugging
178
+ const { clientConfig } = await import('@/config/client.config')
179
+ if (clientConfig.vite.enableLogging) {
180
+ console.warn(`Vite proxy error: ${viteError}`)
181
+ }
182
+ }
183
+ return
184
+ }
185
+
186
+ // Use fixed configuration for simplicity - Vite should be running on port 5173
187
+ const viteHost = "localhost"
188
+ const vitePort = 5173
189
+
190
+ try {
191
+ const url = parseRequestURL(requestContext.request)
192
+ const viteUrl = `http://${viteHost}:${vitePort}${requestContext.path}${url.search}`
193
+
194
+ // Forward request to Vite
195
+ const response = await fetch(viteUrl, {
196
+ method: requestContext.method,
197
+ headers: requestContext.headers,
198
+ body: requestContext.method !== 'GET' && requestContext.method !== 'HEAD' ? requestContext.request.body : undefined
199
+ })
200
+
201
+ // If Vite responds successfully, handle the request
202
+ if (response.ok || response.status < 500) {
203
+ // Return a proper Response object with all headers and status
204
+ const body = await response.arrayBuffer()
205
+
206
+ requestContext.handled = true
207
+ requestContext.response = new Response(body, {
208
+ status: response.status,
209
+ statusText: response.statusText,
210
+ headers: response.headers
211
+ })
212
+ }
213
+
214
+ } catch (viteError) {
215
+ // If Vite fails, let the request continue to normal routing (will become 404)
216
+ // Only log if explicitly enabled for debugging
217
+ const { clientConfig } = await import('@/config/client.config')
218
+ if (clientConfig.vite.enableLogging) {
219
+ console.warn(`Vite proxy error: ${viteError}`)
220
+ }
221
+ }
222
+ }
223
+ }
224
+
225
+ // Monitor Vite server status with automatic port detection
226
+ async function monitorVite(
227
+ context: PluginContext,
228
+ host: string,
229
+ initialPort: number
230
+ ) {
231
+ let retries = 0
232
+ let isConnected = false
233
+ let actualPort = initialPort
234
+ let portDetected = false
235
+
236
+ const checkVite = async () => {
237
+ try {
238
+ // If we haven't found the correct port yet, try to detect it
239
+ if (!portDetected) {
240
+ const detectedPort = await detectVitePort(host, initialPort)
241
+ if (detectedPort !== null) {
242
+ actualPort = detectedPort
243
+ portDetected = true
244
+ // Update the context with the detected port
245
+ if ((context as any).viteConfig) {
246
+ ; (context as any).viteConfig.port = actualPort
247
+ }
248
+ }
249
+ }
250
+
251
+ const isRunning = await checkViteRunning(host, actualPort, DEFAULTS.timeout)
252
+
253
+ if (isRunning && !isConnected) {
254
+ isConnected = true
255
+ retries = 0
256
+ if (actualPort !== initialPort) {
257
+ context.logger.debug(`✓ Vite server detected on ${host}:${actualPort} (auto-detected from port ${initialPort})`)
258
+ } else {
259
+ context.logger.debug(`✓ Vite server detected on ${host}:${actualPort}`)
260
+ }
261
+ context.logger.debug("Hot reload coordination active")
262
+ } else if (!isRunning && isConnected) {
263
+ isConnected = false
264
+ context.logger.warn(`✗ Vite server disconnected from ${host}:${actualPort}`)
265
+ // Reset port detection when disconnected
266
+ portDetected = false
267
+ actualPort = initialPort
268
+ } else if (!isRunning) {
269
+ retries++
270
+ if (retries <= DEFAULTS.maxRetries) {
271
+ if (portDetected) {
272
+ context.logger.debug(`Waiting for Vite server on ${host}:${actualPort}... (${retries}/${DEFAULTS.maxRetries})`)
273
+ } else {
274
+ context.logger.debug(`Detecting Vite server port... (${retries}/${DEFAULTS.maxRetries})`)
275
+ }
276
+ } else if (retries === DEFAULTS.maxRetries + 1) {
277
+ context.logger.warn(`Vite server not found after ${DEFAULTS.maxRetries} attempts. Development features may be limited.`)
278
+ }
279
+ }
280
+ } catch (error) {
281
+ if (isConnected) {
282
+ context.logger.error('Error checking Vite server status', { error })
283
+ }
284
+ }
285
+
286
+ // Continue monitoring
287
+ setTimeout(checkVite, DEFAULTS.checkInterval)
288
+ }
289
+
290
+ // Start monitoring after a brief delay
291
+ setTimeout(checkVite, 1000)
292
+ }
293
+
294
+ // Auto-detect Vite port by trying common ports
295
+ async function detectVitePort(host: string, startPort: number): Promise<number | null> {
296
+ // Try the initial port first, then common alternatives
297
+ const portsToTry = [
298
+ startPort,
299
+ startPort + 1,
300
+ startPort + 2,
301
+ startPort + 3,
302
+ 5174, // Common Vite alternative
303
+ 5175,
304
+ 5176,
305
+ 3000, // Sometimes Vite might use this
306
+ 4173 // Another common alternative
307
+ ]
308
+
309
+ for (const port of portsToTry) {
310
+ try {
311
+ const isRunning = await checkViteRunning(host, port, 1000)
312
+ if (isRunning) {
313
+ return port
314
+ }
315
+ } catch (error) {
316
+ // Continue trying other ports
317
+ }
318
+ }
319
+
320
+ return null
321
+ }
322
+
323
+ // Check if Vite is running
324
+ async function checkViteRunning(host: string, port: number, timeout: number = 1000): Promise<boolean> {
325
+ try {
326
+ const controller = new AbortController()
327
+ const timeoutId = setTimeout(() => controller.abort(), timeout)
328
+
329
+ const response = await fetch(`http://${host}:${port}`, {
330
+ signal: controller.signal,
331
+ method: 'HEAD' // Use HEAD to minimize data transfer
332
+ })
333
+
334
+ clearTimeout(timeoutId)
335
+ return response.status >= 200 && response.status < 500
336
+ } catch (error) {
337
+ return false
338
+ }
339
+ }
340
+
341
+ // Note: Proxy logic is now handled directly in the onBeforeRoute hook above
342
+
378
343
  export default vitePlugin