create-fluxstack 1.5.5 → 1.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +172 -215
- package/app/client/src/App.tsx +45 -19
- package/app/client/src/components/FluxStackConfig.tsx +1 -1
- package/app/client/src/components/HybridLiveCounter.tsx +1 -1
- package/app/client/src/components/LiveClock.tsx +1 -1
- package/app/client/src/components/MainLayout.tsx +0 -2
- package/app/client/src/components/SidebarNavigation.tsx +1 -1
- package/app/client/src/components/SystemMonitor.tsx +16 -10
- package/app/client/src/components/UserProfile.tsx +1 -1
- package/app/server/live/FluxStackConfig.ts +2 -1
- package/app/server/live/LiveClockComponent.ts +8 -7
- package/app/server/live/SidebarNavigation.ts +2 -1
- package/app/server/live/SystemMonitor.ts +1 -0
- package/app/server/live/UserProfileComponent.ts +36 -30
- package/config/server.config.ts +1 -0
- package/core/build/index.ts +14 -0
- package/core/cli/command-registry.ts +10 -10
- package/core/cli/commands/plugin-deps.ts +13 -5
- package/core/cli/plugin-discovery.ts +1 -1
- package/core/client/LiveComponentsProvider.tsx +414 -0
- package/core/client/hooks/useHybridLiveComponent.ts +194 -530
- package/core/client/index.ts +16 -0
- package/core/framework/server.ts +144 -63
- package/core/index.ts +4 -1
- package/core/plugins/built-in/monitoring/index.ts +1 -1
- package/core/plugins/built-in/static/index.ts +1 -1
- package/core/plugins/built-in/swagger/index.ts +1 -1
- package/core/plugins/built-in/vite/index.ts +1 -1
- package/core/plugins/config.ts +1 -1
- package/core/plugins/discovery.ts +1 -1
- package/core/plugins/executor.ts +1 -1
- package/core/plugins/index.ts +1 -0
- package/core/server/live/ComponentRegistry.ts +3 -1
- package/core/server/live/WebSocketConnectionManager.ts +14 -4
- package/core/server/live/websocket-plugin.ts +453 -434
- package/core/server/middleware/elysia-helpers.ts +3 -5
- package/core/server/plugins/database.ts +1 -1
- package/core/server/plugins/static-files-plugin.ts +1 -1
- package/core/templates/create-project.ts +1 -0
- package/core/types/index.ts +1 -1
- package/core/types/plugin.ts +1 -1
- package/core/types/types.ts +6 -2
- package/core/utils/logger/colors.ts +4 -4
- package/core/utils/logger/index.ts +37 -4
- package/core/utils/logger/winston-logger.ts +1 -1
- package/core/utils/sync-version.ts +67 -0
- package/core/utils/version.ts +6 -5
- package/package.json +3 -2
- package/plugins/crypto-auth/server/middlewares/cryptoAuthAdmin.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthOptional.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthPermissions.ts +1 -1
- package/plugins/crypto-auth/server/middlewares/cryptoAuthRequired.ts +1 -1
- package/vite.config.ts +8 -0
- package/.dockerignore +0 -50
- package/CRYPTO-AUTH-MIDDLEWARE-GUIDE.md +0 -475
- package/CRYPTO-AUTH-MIDDLEWARES.md +0 -473
- package/CRYPTO-AUTH-USAGE.md +0 -491
- package/EXEMPLO-ROTA-PROTEGIDA.md +0 -347
- package/QUICK-START-CRYPTO-AUTH.md +0 -221
- package/app/client/src/components/Teste.tsx +0 -104
- package/app/server/live/TesteComponent.ts +0 -87
- package/test-crypto-auth.ts +0 -101
package/core/client/index.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
// 🔥 FluxStack Client Core - Main Export
|
|
2
2
|
|
|
3
|
+
// Live Components Provider (Singleton WebSocket Connection)
|
|
4
|
+
export {
|
|
5
|
+
LiveComponentsProvider,
|
|
6
|
+
useLiveComponents,
|
|
7
|
+
// Deprecated exports for backward compatibility
|
|
8
|
+
WebSocketProvider,
|
|
9
|
+
useWebSocketContext
|
|
10
|
+
} from './LiveComponentsProvider'
|
|
11
|
+
export type {
|
|
12
|
+
LiveComponentsProviderProps,
|
|
13
|
+
LiveComponentsContextValue,
|
|
14
|
+
// Deprecated types for backward compatibility
|
|
15
|
+
WebSocketProviderProps,
|
|
16
|
+
WebSocketContextValue
|
|
17
|
+
} from './LiveComponentsProvider'
|
|
18
|
+
|
|
3
19
|
// Hooks
|
|
4
20
|
export { useWebSocket } from './hooks/useWebSocket'
|
|
5
21
|
export { useHybridLiveComponent } from './hooks/useHybridLiveComponent'
|
package/core/framework/server.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Elysia } from "elysia"
|
|
2
2
|
import type { FluxStackConfig, FluxStackContext } from "../types"
|
|
3
|
-
import type {
|
|
3
|
+
import type { FluxStack, PluginContext, PluginUtils } from "../plugins/types"
|
|
4
4
|
import { PluginRegistry } from "../plugins/registry"
|
|
5
5
|
import { PluginManager } from "../plugins/manager"
|
|
6
6
|
import { getConfigSync, getEnvironmentInfo } from "../config"
|
|
@@ -16,6 +16,7 @@ export class FluxStackFramework {
|
|
|
16
16
|
private pluginManager: PluginManager
|
|
17
17
|
private pluginContext: PluginContext
|
|
18
18
|
private isStarted: boolean = false
|
|
19
|
+
private requestTimings: Map<string, number> = new Map()
|
|
19
20
|
|
|
20
21
|
constructor(config?: Partial<FluxStackConfig>) {
|
|
21
22
|
// Load the full configuration
|
|
@@ -32,7 +33,7 @@ export class FluxStackFramework {
|
|
|
32
33
|
|
|
33
34
|
this.app = new Elysia()
|
|
34
35
|
this.pluginRegistry = new PluginRegistry()
|
|
35
|
-
|
|
36
|
+
|
|
36
37
|
|
|
37
38
|
|
|
38
39
|
// Create plugin utilities
|
|
@@ -68,22 +69,33 @@ export class FluxStackFramework {
|
|
|
68
69
|
}
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
// Create
|
|
72
|
-
|
|
73
|
-
debug: (message: string, meta?:
|
|
74
|
-
info: (message: string, meta?:
|
|
75
|
-
warn: (message: string, meta?:
|
|
76
|
-
error: (message: string, meta?:
|
|
77
|
-
child: (context:
|
|
78
|
-
time: (label: string) =>
|
|
79
|
-
timeEnd: (label: string) =>
|
|
72
|
+
// Create plugin-compatible logger interface
|
|
73
|
+
interface PluginLogger {
|
|
74
|
+
debug: (message: string, meta?: unknown) => void
|
|
75
|
+
info: (message: string, meta?: unknown) => void
|
|
76
|
+
warn: (message: string, meta?: unknown) => void
|
|
77
|
+
error: (message: string, meta?: unknown) => void
|
|
78
|
+
child: (context: Record<string, unknown>) => PluginLogger
|
|
79
|
+
time: (label: string) => void
|
|
80
|
+
timeEnd: (label: string) => void
|
|
81
|
+
request: (method: string, path: string, status?: number, duration?: number) => void
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const pluginLogger: PluginLogger = {
|
|
85
|
+
debug: (message: string, meta?: unknown) => logger.debug(message, meta),
|
|
86
|
+
info: (message: string, meta?: unknown) => logger.info(message, meta),
|
|
87
|
+
warn: (message: string, meta?: unknown) => logger.warn(message, meta),
|
|
88
|
+
error: (message: string, meta?: unknown) => logger.error(message, meta),
|
|
89
|
+
child: (context: Record<string, unknown>) => pluginLogger,
|
|
90
|
+
time: (label: string) => logger.time(label),
|
|
91
|
+
timeEnd: (label: string) => logger.timeEnd(label),
|
|
80
92
|
request: (method: string, path: string, status?: number, duration?: number) =>
|
|
81
93
|
logger.request(method, path, status, duration)
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
this.pluginContext = {
|
|
85
97
|
config: fullConfig,
|
|
86
|
-
logger: pluginLogger,
|
|
98
|
+
logger: pluginLogger as any,
|
|
87
99
|
app: this.app,
|
|
88
100
|
utils: pluginUtils
|
|
89
101
|
}
|
|
@@ -97,6 +109,7 @@ export class FluxStackFramework {
|
|
|
97
109
|
|
|
98
110
|
this.setupCors()
|
|
99
111
|
this.setupHeadHandler()
|
|
112
|
+
this.setupElysiaHeadBugFilter()
|
|
100
113
|
this.setupHooks()
|
|
101
114
|
this.setupErrorHandling()
|
|
102
115
|
|
|
@@ -104,7 +117,7 @@ export class FluxStackFramework {
|
|
|
104
117
|
environment: envInfo.name,
|
|
105
118
|
port: fullConfig.server.port
|
|
106
119
|
})
|
|
107
|
-
|
|
120
|
+
|
|
108
121
|
// Initialize automatic plugin discovery in background
|
|
109
122
|
this.initializeAutomaticPlugins().catch(error => {
|
|
110
123
|
logger.error('Failed to initialize automatic plugins', { error })
|
|
@@ -147,7 +160,7 @@ export class FluxStackFramework {
|
|
|
147
160
|
// Global HEAD handler to prevent Elysia's automatic HEAD conversion bug
|
|
148
161
|
this.app.head("*", ({ request, set }) => {
|
|
149
162
|
const url = new URL(request.url)
|
|
150
|
-
|
|
163
|
+
|
|
151
164
|
// Handle API routes
|
|
152
165
|
if (url.pathname.startsWith(this.context.config.server.apiPrefix)) {
|
|
153
166
|
set.status = 200
|
|
@@ -155,7 +168,7 @@ export class FluxStackFramework {
|
|
|
155
168
|
set.headers['Content-Length'] = '0'
|
|
156
169
|
return ""
|
|
157
170
|
}
|
|
158
|
-
|
|
171
|
+
|
|
159
172
|
// Handle static files (assume they're HTML if no extension)
|
|
160
173
|
const isStatic = url.pathname === '/' || !url.pathname.includes('.')
|
|
161
174
|
if (isStatic) {
|
|
@@ -165,7 +178,7 @@ export class FluxStackFramework {
|
|
|
165
178
|
set.headers['Cache-Control'] = 'no-cache'
|
|
166
179
|
return ""
|
|
167
180
|
}
|
|
168
|
-
|
|
181
|
+
|
|
169
182
|
// Handle other file types
|
|
170
183
|
set.status = 200
|
|
171
184
|
set.headers['Content-Type'] = 'application/octet-stream'
|
|
@@ -174,12 +187,61 @@ export class FluxStackFramework {
|
|
|
174
187
|
})
|
|
175
188
|
}
|
|
176
189
|
|
|
190
|
+
private setupElysiaHeadBugFilter() {
|
|
191
|
+
// Only filter in development mode to avoid affecting production logs
|
|
192
|
+
if (process.env.NODE_ENV !== 'development') {
|
|
193
|
+
return
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Store original stderr.write to restore if needed
|
|
197
|
+
const originalStderrWrite = process.stderr.write
|
|
198
|
+
|
|
199
|
+
// Override stderr.write to filter Elysia HEAD bug errors
|
|
200
|
+
process.stderr.write = function (
|
|
201
|
+
chunk: string | Uint8Array,
|
|
202
|
+
encoding?: BufferEncoding | ((error?: Error) => void),
|
|
203
|
+
callback?: (error?: Error) => void
|
|
204
|
+
): boolean {
|
|
205
|
+
const str = chunk.toString()
|
|
206
|
+
|
|
207
|
+
// Filter out known Elysia HEAD bug error patterns
|
|
208
|
+
if (str.includes("TypeError: undefined is not an object (evaluating '_res.headers.set')") ||
|
|
209
|
+
str.includes("HEAD - / failed") ||
|
|
210
|
+
(str.includes("HEAD - ") && str.includes(" failed"))) {
|
|
211
|
+
// Silently ignore these specific errors
|
|
212
|
+
if (typeof encoding === 'function') {
|
|
213
|
+
encoding() // encoding is actually the callback
|
|
214
|
+
} else if (callback) {
|
|
215
|
+
callback()
|
|
216
|
+
}
|
|
217
|
+
return true
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Pass through all other stderr output
|
|
221
|
+
if (typeof encoding === 'function') {
|
|
222
|
+
return originalStderrWrite.call(process.stderr, chunk, encoding)
|
|
223
|
+
} else {
|
|
224
|
+
return originalStderrWrite.call(process.stderr, chunk, encoding, callback)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Store reference to restore original behavior if needed
|
|
229
|
+
; (this as any)._originalStderrWrite = originalStderrWrite
|
|
230
|
+
}
|
|
231
|
+
|
|
177
232
|
private setupHooks() {
|
|
178
233
|
// Setup onRequest hook and onBeforeRoute hook
|
|
179
234
|
this.app.onRequest(async ({ request, set }) => {
|
|
180
235
|
const startTime = Date.now()
|
|
181
236
|
const url = new URL(request.url)
|
|
182
|
-
|
|
237
|
+
|
|
238
|
+
// Store start time for duration calculation (using request URL as key)
|
|
239
|
+
const requestKey = `${request.method}-${url.pathname}-${startTime}`
|
|
240
|
+
this.requestTimings.set(requestKey, startTime)
|
|
241
|
+
|
|
242
|
+
// Store key in set.headers for retrieval in onAfterHandle
|
|
243
|
+
set.headers['x-request-timing-key'] = requestKey
|
|
244
|
+
|
|
183
245
|
const requestContext = {
|
|
184
246
|
request,
|
|
185
247
|
path: url.pathname,
|
|
@@ -197,24 +259,33 @@ export class FluxStackFramework {
|
|
|
197
259
|
handled: false,
|
|
198
260
|
response: undefined
|
|
199
261
|
}
|
|
200
|
-
|
|
262
|
+
|
|
201
263
|
// Execute onRequest hooks for all plugins first (logging, auth, etc.)
|
|
202
264
|
await this.executePluginHooks('onRequest', requestContext)
|
|
203
|
-
|
|
265
|
+
|
|
204
266
|
// Execute onBeforeRoute hooks - allow plugins to handle requests before routing
|
|
205
267
|
const handledResponse = await this.executePluginBeforeRouteHooks(requestContext)
|
|
206
|
-
|
|
268
|
+
|
|
207
269
|
// If a plugin handled the request, return the response
|
|
208
270
|
if (handledResponse) {
|
|
209
271
|
return handledResponse
|
|
210
272
|
}
|
|
211
273
|
})
|
|
212
274
|
|
|
213
|
-
// Setup onResponse hook
|
|
275
|
+
// Setup onResponse hook
|
|
214
276
|
this.app.onAfterHandle(async ({ request, response, set }) => {
|
|
215
|
-
const startTime = Date.now()
|
|
216
277
|
const url = new URL(request.url)
|
|
217
|
-
|
|
278
|
+
|
|
279
|
+
// Retrieve start time using the timing key
|
|
280
|
+
const requestKey = set.headers['x-request-timing-key']
|
|
281
|
+
const startTime = requestKey ? this.requestTimings.get(String(requestKey)) : undefined
|
|
282
|
+
const duration = startTime ? Date.now() - startTime : 0
|
|
283
|
+
|
|
284
|
+
// Clean up timing entry
|
|
285
|
+
if (requestKey) {
|
|
286
|
+
this.requestTimings.delete(String(requestKey))
|
|
287
|
+
}
|
|
288
|
+
|
|
218
289
|
const responseContext = {
|
|
219
290
|
request,
|
|
220
291
|
path: url.pathname,
|
|
@@ -229,11 +300,21 @@ export class FluxStackFramework {
|
|
|
229
300
|
query: Object.fromEntries(url.searchParams.entries()),
|
|
230
301
|
params: {},
|
|
231
302
|
response,
|
|
232
|
-
statusCode: (response as any)?.status || set.status || 200,
|
|
233
|
-
duration
|
|
303
|
+
statusCode: Number((response as any)?.status || set.status || 200),
|
|
304
|
+
duration,
|
|
234
305
|
startTime
|
|
235
306
|
}
|
|
236
|
-
|
|
307
|
+
|
|
308
|
+
// Log the request automatically (if not disabled in config)
|
|
309
|
+
if (this.context.config.server.enableRequestLogging !== false) {
|
|
310
|
+
// Ensure status is always a number (HTTP status code)
|
|
311
|
+
const status = typeof responseContext.statusCode === 'number'
|
|
312
|
+
? responseContext.statusCode
|
|
313
|
+
: Number(set.status) || 200
|
|
314
|
+
|
|
315
|
+
logger.request(request.method, url.pathname, status, duration)
|
|
316
|
+
}
|
|
317
|
+
|
|
237
318
|
// Execute onResponse hooks for all plugins
|
|
238
319
|
await this.executePluginHooks('onResponse', responseContext)
|
|
239
320
|
})
|
|
@@ -248,7 +329,7 @@ export class FluxStackFramework {
|
|
|
248
329
|
this.app.onError(async ({ error, request, path, set }) => {
|
|
249
330
|
const startTime = Date.now()
|
|
250
331
|
const url = new URL(request.url)
|
|
251
|
-
|
|
332
|
+
|
|
252
333
|
const errorContext = {
|
|
253
334
|
request,
|
|
254
335
|
path: url.pathname,
|
|
@@ -267,17 +348,17 @@ export class FluxStackFramework {
|
|
|
267
348
|
handled: false,
|
|
268
349
|
startTime
|
|
269
350
|
}
|
|
270
|
-
|
|
351
|
+
|
|
271
352
|
// Execute onError hooks for all plugins - allow them to handle the error
|
|
272
353
|
const handledResponse = await this.executePluginErrorHooks(errorContext)
|
|
273
|
-
|
|
354
|
+
|
|
274
355
|
// If a plugin handled the error, return the response
|
|
275
356
|
if (handledResponse) {
|
|
276
357
|
return handledResponse
|
|
277
358
|
}
|
|
278
|
-
|
|
359
|
+
|
|
279
360
|
// Vite proxy logic is now handled by the Vite plugin via onBeforeRoute hook
|
|
280
|
-
|
|
361
|
+
|
|
281
362
|
// Convert Elysia error to standard Error if needed
|
|
282
363
|
const standardError = error instanceof Error ? error : new Error(String(error))
|
|
283
364
|
return errorHandler(standardError, request, path)
|
|
@@ -286,18 +367,18 @@ export class FluxStackFramework {
|
|
|
286
367
|
|
|
287
368
|
private async executePluginHooks(hookName: string, context: any): Promise<void> {
|
|
288
369
|
const loadOrder = this.pluginRegistry.getLoadOrder()
|
|
289
|
-
|
|
370
|
+
|
|
290
371
|
for (const pluginName of loadOrder) {
|
|
291
372
|
const plugin = this.pluginRegistry.get(pluginName)
|
|
292
373
|
if (!plugin) continue
|
|
293
|
-
|
|
374
|
+
|
|
294
375
|
const hookFn = (plugin as any)[hookName]
|
|
295
376
|
if (typeof hookFn === 'function') {
|
|
296
377
|
try {
|
|
297
378
|
await hookFn(context)
|
|
298
379
|
} catch (error) {
|
|
299
|
-
logger.error(`Plugin '${pluginName}' ${hookName} hook failed`, {
|
|
300
|
-
error: (error as Error).message
|
|
380
|
+
logger.error(`Plugin '${pluginName}' ${hookName} hook failed`, {
|
|
381
|
+
error: (error as Error).message
|
|
301
382
|
})
|
|
302
383
|
}
|
|
303
384
|
}
|
|
@@ -306,86 +387,86 @@ export class FluxStackFramework {
|
|
|
306
387
|
|
|
307
388
|
private async executePluginBeforeRouteHooks(requestContext: any): Promise<Response | null> {
|
|
308
389
|
const loadOrder = this.pluginRegistry.getLoadOrder()
|
|
309
|
-
|
|
390
|
+
|
|
310
391
|
for (const pluginName of loadOrder) {
|
|
311
392
|
const plugin = this.pluginRegistry.get(pluginName)
|
|
312
393
|
if (!plugin) continue
|
|
313
|
-
|
|
394
|
+
|
|
314
395
|
const onBeforeRouteFn = (plugin as any).onBeforeRoute
|
|
315
396
|
if (typeof onBeforeRouteFn === 'function') {
|
|
316
397
|
try {
|
|
317
398
|
await onBeforeRouteFn(requestContext)
|
|
318
|
-
|
|
399
|
+
|
|
319
400
|
// If this plugin handled the request, return the response
|
|
320
401
|
if (requestContext.handled && requestContext.response) {
|
|
321
402
|
return requestContext.response
|
|
322
403
|
}
|
|
323
404
|
} catch (error) {
|
|
324
|
-
logger.error(`Plugin '${pluginName}' onBeforeRoute hook failed`, {
|
|
325
|
-
error: (error as Error).message
|
|
405
|
+
logger.error(`Plugin '${pluginName}' onBeforeRoute hook failed`, {
|
|
406
|
+
error: (error as Error).message
|
|
326
407
|
})
|
|
327
408
|
}
|
|
328
409
|
}
|
|
329
410
|
}
|
|
330
|
-
|
|
411
|
+
|
|
331
412
|
return null
|
|
332
413
|
}
|
|
333
414
|
|
|
334
415
|
private async executePluginErrorHooks(errorContext: any): Promise<Response | null> {
|
|
335
416
|
const loadOrder = this.pluginRegistry.getLoadOrder()
|
|
336
|
-
|
|
417
|
+
|
|
337
418
|
for (const pluginName of loadOrder) {
|
|
338
419
|
const plugin = this.pluginRegistry.get(pluginName)
|
|
339
420
|
if (!plugin) continue
|
|
340
|
-
|
|
421
|
+
|
|
341
422
|
const onErrorFn = (plugin as any).onError
|
|
342
423
|
if (typeof onErrorFn === 'function') {
|
|
343
424
|
try {
|
|
344
425
|
await onErrorFn(errorContext)
|
|
345
|
-
|
|
426
|
+
|
|
346
427
|
// If this plugin handled the error, check if it provides a response
|
|
347
428
|
if (errorContext.handled) {
|
|
348
429
|
// For Vite plugin, we'll handle the proxy here
|
|
349
430
|
if (pluginName === 'vite' && errorContext.error.constructor.name === 'NotFoundError') {
|
|
350
431
|
return await this.handleViteProxy(errorContext)
|
|
351
432
|
}
|
|
352
|
-
|
|
433
|
+
|
|
353
434
|
// For other plugins, return a basic success response
|
|
354
435
|
return new Response('OK', { status: 200 })
|
|
355
436
|
}
|
|
356
437
|
} catch (error) {
|
|
357
|
-
logger.error(`Plugin '${pluginName}' onError hook failed`, {
|
|
358
|
-
error: (error as Error).message
|
|
438
|
+
logger.error(`Plugin '${pluginName}' onError hook failed`, {
|
|
439
|
+
error: (error as Error).message
|
|
359
440
|
})
|
|
360
441
|
}
|
|
361
442
|
}
|
|
362
443
|
}
|
|
363
|
-
|
|
444
|
+
|
|
364
445
|
return null
|
|
365
446
|
}
|
|
366
447
|
|
|
367
448
|
private async handleViteProxy(errorContext: any): Promise<Response> {
|
|
368
449
|
const vitePort = this.context.config.client?.port || 5173
|
|
369
450
|
const url = new URL(errorContext.request.url)
|
|
370
|
-
|
|
451
|
+
|
|
371
452
|
try {
|
|
372
453
|
const viteUrl = `http://localhost:${vitePort}${url.pathname}${url.search}`
|
|
373
|
-
|
|
454
|
+
|
|
374
455
|
// Forward request to Vite
|
|
375
456
|
const response = await fetch(viteUrl, {
|
|
376
457
|
method: errorContext.method,
|
|
377
458
|
headers: errorContext.headers
|
|
378
459
|
})
|
|
379
|
-
|
|
460
|
+
|
|
380
461
|
// Return a proper Response object with all headers and status
|
|
381
462
|
const body = await response.arrayBuffer()
|
|
382
|
-
|
|
463
|
+
|
|
383
464
|
return new Response(body, {
|
|
384
465
|
status: response.status,
|
|
385
466
|
statusText: response.statusText,
|
|
386
467
|
headers: response.headers
|
|
387
468
|
})
|
|
388
|
-
|
|
469
|
+
|
|
389
470
|
} catch (viteError) {
|
|
390
471
|
// If Vite fails, return error response
|
|
391
472
|
return new Response(`Vite server not ready on port ${vitePort}. Error: ${viteError}`, {
|
|
@@ -401,16 +482,16 @@ export class FluxStackFramework {
|
|
|
401
482
|
if (this.pluginRegistry.has(plugin.name)) {
|
|
402
483
|
throw new Error(`Plugin '${plugin.name}' is already registered`)
|
|
403
484
|
}
|
|
404
|
-
|
|
485
|
+
|
|
405
486
|
// Store plugin without calling setup - setup will be called in start()
|
|
406
487
|
// We need to manually set the plugin since register() is async but we need sync
|
|
407
488
|
(this.pluginRegistry as any).plugins.set(plugin.name, plugin)
|
|
408
|
-
|
|
489
|
+
|
|
409
490
|
// Update dependencies tracking
|
|
410
|
-
if (plugin.dependencies) {
|
|
411
|
-
(this.pluginRegistry as any).dependencies.set(plugin.name, plugin.dependencies)
|
|
491
|
+
if ((plugin as FluxStack.Plugin).dependencies) {
|
|
492
|
+
(this.pluginRegistry as any).dependencies.set(plugin.name, (plugin as FluxStack.Plugin).dependencies)
|
|
412
493
|
}
|
|
413
|
-
|
|
494
|
+
|
|
414
495
|
// Update load order by calling the private method
|
|
415
496
|
try {
|
|
416
497
|
(this.pluginRegistry as any).updateLoadOrder()
|
|
@@ -418,12 +499,12 @@ export class FluxStackFramework {
|
|
|
418
499
|
// Fallback: create basic load order
|
|
419
500
|
const plugins = (this.pluginRegistry as any).plugins as Map<string, Plugin>
|
|
420
501
|
const loadOrder = Array.from(plugins.keys())
|
|
421
|
-
|
|
502
|
+
; (this.pluginRegistry as any).loadOrder = loadOrder
|
|
422
503
|
}
|
|
423
|
-
|
|
504
|
+
|
|
424
505
|
logger.debug(`Plugin '${plugin.name}' registered`, {
|
|
425
|
-
version: plugin.version,
|
|
426
|
-
dependencies: plugin.dependencies
|
|
506
|
+
version: (plugin as FluxStack.Plugin).version,
|
|
507
|
+
dependencies: (plugin as FluxStack.Plugin).dependencies
|
|
427
508
|
})
|
|
428
509
|
return this
|
|
429
510
|
} catch (error) {
|
|
@@ -445,7 +526,7 @@ export class FluxStackFramework {
|
|
|
445
526
|
|
|
446
527
|
try {
|
|
447
528
|
// Validate plugin dependencies before starting
|
|
448
|
-
const plugins = (this.pluginRegistry as any).plugins as Map<string, Plugin>
|
|
529
|
+
const plugins = (this.pluginRegistry as any).plugins as Map<string, FluxStack.Plugin>
|
|
449
530
|
for (const [pluginName, plugin] of plugins) {
|
|
450
531
|
if (plugin.dependencies) {
|
|
451
532
|
for (const depName of plugin.dependencies) {
|
package/core/index.ts
CHANGED
|
@@ -8,6 +8,7 @@ export * from './server/services/index.js'
|
|
|
8
8
|
export * from './server/middleware/index.js'
|
|
9
9
|
|
|
10
10
|
// Client exports
|
|
11
|
+
export * from './client/index.js'
|
|
11
12
|
export * from './client/state/index.js'
|
|
12
13
|
export * from './client/hooks/index.js'
|
|
13
14
|
|
|
@@ -22,4 +23,6 @@ export * from './framework/server.js'
|
|
|
22
23
|
export * from './framework/client.js'
|
|
23
24
|
export * from './plugins/index.js'
|
|
24
25
|
export * from './utils/logger/index.js'
|
|
25
|
-
export * from './utils/errors/index.js'
|
|
26
|
+
export * from './utils/errors/index.js'// Ve
|
|
27
|
+
rsion exports
|
|
28
|
+
export { FLUXSTACK_VERSION, getVersion, getVersionInfo } from './utils/version.js'
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Provides performance monitoring, metrics collection, and system monitoring
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { FluxStack, PluginContext, RequestContext, ResponseContext, ErrorContext } from "../../types"
|
|
7
7
|
import { MetricsCollector } from "../../../utils/monitoring"
|
|
8
8
|
import * as os from 'os'
|
|
9
9
|
import * as fs from 'fs'
|
package/core/plugins/config.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Handles plugin-specific configuration validation and management
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { FluxStack, PluginConfigSchema, PluginValidationResult } from "./types"
|
|
7
7
|
import type { FluxStackConfig } from "../config/schema"
|
|
8
8
|
import type { Logger } from "../utils/logger/index"
|
|
9
9
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Handles automatic discovery and loading of plugins from various sources
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type {
|
|
6
|
+
import type { FluxStack, PluginManifest, PluginLoadResult, PluginDiscoveryOptions } from "./types"
|
|
7
7
|
import type { Logger } from "../utils/logger/index"
|
|
8
8
|
import { readdir, readFile } from "fs/promises"
|
|
9
9
|
import { join, resolve } from "path"
|
package/core/plugins/executor.ts
CHANGED
package/core/plugins/index.ts
CHANGED
|
@@ -794,12 +794,14 @@ export class ComponentRegistry {
|
|
|
794
794
|
}
|
|
795
795
|
|
|
796
796
|
// Update component activity
|
|
797
|
-
updateComponentActivity(componentId: string):
|
|
797
|
+
updateComponentActivity(componentId: string): boolean {
|
|
798
798
|
const metadata = this.metadata.get(componentId)
|
|
799
799
|
if (metadata) {
|
|
800
800
|
metadata.lastActivity = new Date()
|
|
801
801
|
metadata.state = 'active'
|
|
802
|
+
return true
|
|
802
803
|
}
|
|
804
|
+
return false
|
|
803
805
|
}
|
|
804
806
|
|
|
805
807
|
// Record component metrics
|
|
@@ -134,7 +134,17 @@ export class WebSocketConnectionManager extends EventEmitter {
|
|
|
134
134
|
if (!metrics) return
|
|
135
135
|
|
|
136
136
|
// Handle incoming messages
|
|
137
|
-
|
|
137
|
+
const addListener = (event: string, handler: (...args: any[]) => void) => {
|
|
138
|
+
if (typeof ws.on === 'function') {
|
|
139
|
+
ws.on(event, handler)
|
|
140
|
+
} else if (typeof ws.addEventListener === 'function') {
|
|
141
|
+
ws.addEventListener(event as any, handler as any)
|
|
142
|
+
} else if (typeof ws.addListener === 'function') {
|
|
143
|
+
ws.addListener(event, handler)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
addListener('message', (data: any) => {
|
|
138
148
|
metrics.messagesReceived++
|
|
139
149
|
metrics.lastActivity = new Date()
|
|
140
150
|
metrics.bytesTransferred += Buffer.byteLength(data)
|
|
@@ -143,20 +153,20 @@ export class WebSocketConnectionManager extends EventEmitter {
|
|
|
143
153
|
})
|
|
144
154
|
|
|
145
155
|
// Handle connection close
|
|
146
|
-
|
|
156
|
+
addListener('close', () => {
|
|
147
157
|
metrics.status = 'disconnected'
|
|
148
158
|
this.handleConnectionClose(connectionId)
|
|
149
159
|
})
|
|
150
160
|
|
|
151
161
|
// Handle connection errors
|
|
152
|
-
|
|
162
|
+
addListener('error', (error: Error) => {
|
|
153
163
|
metrics.errorCount++
|
|
154
164
|
metrics.status = 'error'
|
|
155
165
|
this.handleConnectionError(connectionId, error)
|
|
156
166
|
})
|
|
157
167
|
|
|
158
168
|
// Handle pong responses for latency measurement
|
|
159
|
-
|
|
169
|
+
addListener('pong', () => {
|
|
160
170
|
const now = Date.now()
|
|
161
171
|
const pingTime = (ws as any)._pingTime
|
|
162
172
|
if (pingTime) {
|