create-fluxstack 1.0.22 → 1.4.0

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