create-fluxstack 1.0.13 โ 1.0.14
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/.env.example +29 -29
- package/app/client/README.md +69 -69
- package/app/client/index.html +14 -13
- package/app/client/src/App.tsx +157 -524
- package/app/client/src/components/ErrorBoundary.tsx +107 -0
- package/app/client/src/components/ErrorDisplay.css +365 -0
- package/app/client/src/components/ErrorDisplay.tsx +258 -0
- package/app/client/src/components/FluxStackConfig.tsx +1321 -0
- package/app/client/src/components/HybridLiveCounter.tsx +140 -0
- package/app/client/src/components/LiveClock.tsx +286 -0
- package/app/client/src/components/MainLayout.tsx +390 -0
- package/app/client/src/components/SidebarNavigation.tsx +391 -0
- package/app/client/src/components/StateDemo.tsx +178 -0
- package/app/client/src/components/SystemMonitor.tsx +1038 -0
- package/app/client/src/components/Teste.tsx +104 -0
- package/app/client/src/components/UserProfile.tsx +809 -0
- package/app/client/src/hooks/useAuth.ts +39 -0
- package/app/client/src/hooks/useNotifications.ts +56 -0
- package/app/client/src/lib/eden-api.ts +189 -53
- package/app/client/src/lib/errors.ts +340 -0
- package/app/client/src/lib/hooks/useErrorHandler.ts +258 -0
- package/app/client/src/lib/index.ts +45 -0
- package/app/client/src/main.tsx +3 -2
- package/app/client/src/pages/ApiDocs.tsx +182 -0
- package/app/client/src/pages/Demo.tsx +174 -0
- package/app/client/src/pages/HybridLive.tsx +263 -0
- package/app/client/src/pages/Overview.tsx +155 -0
- package/app/client/src/store/README.md +43 -0
- package/app/client/src/store/index.ts +16 -0
- package/app/client/src/store/slices/uiSlice.ts +151 -0
- package/app/client/src/store/slices/userSlice.ts +161 -0
- package/app/client/src/test/README.md +257 -0
- package/app/client/src/test/setup.ts +70 -0
- package/app/client/src/test/types.ts +12 -0
- package/app/client/src/vite-env.d.ts +1 -1
- package/app/client/tsconfig.app.json +44 -43
- package/app/client/tsconfig.json +7 -7
- package/app/client/tsconfig.node.json +25 -25
- package/app/client/zustand-setup.md +65 -0
- package/app/server/controllers/users.controller.ts +68 -68
- package/app/server/index.ts +9 -1
- package/app/server/live/CounterComponent.ts +191 -0
- package/app/server/live/FluxStackConfig.ts +529 -0
- package/app/server/live/LiveClockComponent.ts +214 -0
- package/app/server/live/SidebarNavigation.ts +156 -0
- package/app/server/live/SystemMonitor.ts +594 -0
- package/app/server/live/SystemMonitorIntegration.ts +151 -0
- package/app/server/live/TesteComponent.ts +87 -0
- package/app/server/live/UserProfileComponent.ts +135 -0
- package/app/server/live/register-components.ts +28 -0
- package/app/server/middleware/auth.ts +136 -0
- package/app/server/middleware/errorHandling.ts +250 -0
- package/app/server/middleware/index.ts +10 -0
- package/app/server/middleware/rateLimit.ts +193 -0
- package/app/server/middleware/requestLogging.ts +215 -0
- package/app/server/middleware/validation.ts +270 -0
- package/app/server/routes/index.ts +14 -2
- package/app/server/routes/upload.ts +92 -0
- package/app/server/routes/users.routes.ts +2 -9
- package/app/server/services/NotificationService.ts +302 -0
- package/app/server/services/UserService.ts +222 -0
- package/app/server/services/index.ts +46 -0
- package/core/cli/commands/plugin-deps.ts +263 -0
- package/core/cli/generators/README.md +339 -0
- package/core/cli/generators/component.ts +770 -0
- package/core/cli/generators/controller.ts +299 -0
- package/core/cli/generators/index.ts +144 -0
- package/core/cli/generators/interactive.ts +228 -0
- package/core/cli/generators/prompts.ts +83 -0
- package/core/cli/generators/route.ts +513 -0
- package/core/cli/generators/service.ts +465 -0
- package/core/cli/generators/template-engine.ts +154 -0
- package/core/cli/generators/types.ts +71 -0
- package/core/cli/generators/utils.ts +192 -0
- package/core/cli/index.ts +69 -0
- package/core/cli/plugin-discovery.ts +16 -85
- package/core/client/fluxstack.ts +17 -0
- package/core/client/hooks/index.ts +7 -0
- package/core/client/hooks/state-validator.ts +130 -0
- package/core/client/hooks/useAuth.ts +49 -0
- package/core/client/hooks/useChunkedUpload.ts +258 -0
- package/core/client/hooks/useHybridLiveComponent.ts +967 -0
- package/core/client/hooks/useWebSocket.ts +373 -0
- package/core/client/index.ts +47 -0
- package/core/client/state/createStore.ts +193 -0
- package/core/client/state/index.ts +15 -0
- package/core/config/env-dynamic.ts +1 -1
- package/core/config/env.ts +2 -1
- package/core/config/runtime-config.ts +3 -3
- package/core/config/schema.ts +84 -49
- package/core/framework/server.ts +30 -0
- package/core/index.ts +25 -0
- package/core/live/ComponentRegistry.ts +399 -0
- package/core/live/types.ts +164 -0
- package/core/plugins/built-in/live-components/commands/create-live-component.ts +1201 -0
- package/core/plugins/built-in/live-components/index.ts +27 -0
- package/core/plugins/built-in/logger/index.ts +1 -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/dependency-manager.ts +384 -0
- package/core/plugins/index.ts +5 -1
- package/core/plugins/manager.ts +7 -3
- package/core/plugins/registry.ts +88 -10
- package/core/plugins/types.ts +11 -11
- package/core/server/framework.ts +43 -0
- package/core/server/index.ts +11 -1
- package/core/server/live/ComponentRegistry.ts +1017 -0
- package/core/server/live/FileUploadManager.ts +272 -0
- package/core/server/live/LiveComponentPerformanceMonitor.ts +930 -0
- package/core/server/live/SingleConnectionManager.ts +0 -0
- package/core/server/live/StateSignature.ts +644 -0
- package/core/server/live/WebSocketConnectionManager.ts +688 -0
- package/core/server/live/websocket-plugin.ts +435 -0
- package/core/server/middleware/errorHandling.ts +141 -0
- package/core/server/middleware/index.ts +16 -0
- package/core/server/plugins/static-files-plugin.ts +232 -0
- package/core/server/services/BaseService.ts +95 -0
- package/core/server/services/ServiceContainer.ts +144 -0
- package/core/server/services/index.ts +9 -0
- package/core/templates/create-project.ts +46 -2
- package/core/testing/index.ts +10 -0
- package/core/testing/setup.ts +74 -0
- package/core/types/build.ts +38 -14
- package/core/types/types.ts +319 -0
- package/core/utils/env-runtime.ts +7 -0
- package/core/utils/errors/handlers.ts +264 -39
- package/core/utils/errors/index.ts +528 -18
- package/core/utils/errors/middleware.ts +114 -0
- package/core/utils/logger/formatters.ts +222 -0
- package/core/utils/logger/index.ts +167 -48
- package/core/utils/logger/middleware.ts +253 -0
- package/core/utils/logger/performance.ts +384 -0
- package/core/utils/logger/transports.ts +365 -0
- package/create-fluxstack.ts +296 -296
- package/fluxstack.config.ts +17 -1
- package/package-template.json +66 -66
- package/package.json +31 -6
- package/public/README.md +16 -0
- package/vite.config.ts +29 -14
- package/.claude/settings.local.json +0 -74
- package/.github/workflows/ci-build-tests.yml +0 -480
- package/.github/workflows/dependency-management.yml +0 -324
- package/.github/workflows/release-validation.yml +0 -355
- package/.kiro/specs/fluxstack-architecture-optimization/design.md +0 -700
- package/.kiro/specs/fluxstack-architecture-optimization/requirements.md +0 -127
- package/.kiro/specs/fluxstack-architecture-optimization/tasks.md +0 -330
- package/CLAUDE.md +0 -200
- package/Dockerfile +0 -58
- package/Dockerfile.backend +0 -52
- package/Dockerfile.frontend +0 -54
- package/README-Docker.md +0 -85
- package/ai-context/00-QUICK-START.md +0 -86
- package/ai-context/README.md +0 -88
- package/ai-context/development/eden-treaty-guide.md +0 -362
- package/ai-context/development/patterns.md +0 -382
- package/ai-context/development/plugins-guide.md +0 -572
- package/ai-context/examples/crud-complete.md +0 -626
- package/ai-context/project/architecture.md +0 -399
- package/ai-context/project/overview.md +0 -213
- package/ai-context/recent-changes/eden-treaty-refactor.md +0 -281
- package/ai-context/recent-changes/type-inference-fix.md +0 -223
- package/ai-context/reference/environment-vars.md +0 -384
- package/ai-context/reference/troubleshooting.md +0 -407
- package/app/client/src/components/TestPage.tsx +0 -453
- package/bun.lock +0 -1063
- package/bunfig.toml +0 -16
- package/core/__tests__/integration.test.ts +0 -227
- package/core/build/index.ts +0 -186
- package/core/config/__tests__/config-loader.test.ts +0 -554
- package/core/config/__tests__/config-merger.test.ts +0 -657
- package/core/config/__tests__/env-converter.test.ts +0 -372
- package/core/config/__tests__/env-processor.test.ts +0 -431
- package/core/config/__tests__/env.test.ts +0 -452
- package/core/config/__tests__/integration.test.ts +0 -418
- package/core/config/__tests__/loader.test.ts +0 -331
- package/core/config/__tests__/schema.test.ts +0 -129
- package/core/config/__tests__/validator.test.ts +0 -318
- package/core/framework/__tests__/server.test.ts +0 -233
- package/core/plugins/__tests__/built-in.test.ts.disabled +0 -366
- package/core/plugins/__tests__/manager.test.ts +0 -398
- package/core/plugins/__tests__/monitoring.test.ts +0 -401
- package/core/plugins/__tests__/registry.test.ts +0 -335
- package/core/utils/__tests__/errors.test.ts +0 -139
- package/core/utils/__tests__/helpers.test.ts +0 -297
- package/core/utils/__tests__/logger.test.ts +0 -141
- package/create-test-app.ts +0 -156
- package/docker-compose.microservices.yml +0 -75
- package/docker-compose.simple.yml +0 -57
- package/docker-compose.yml +0 -71
- package/eslint.config.js +0 -23
- package/flux-cli.ts +0 -214
- package/nginx-lb.conf +0 -37
- package/publish.sh +0 -63
- package/run-clean.ts +0 -26
- package/run-env-tests.ts +0 -313
- package/tailwind.config.js +0 -34
- package/tests/__mocks__/api.ts +0 -56
- package/tests/fixtures/users.ts +0 -69
- package/tests/integration/api/users.routes.test.ts +0 -221
- package/tests/setup.ts +0 -29
- package/tests/unit/app/client/App-simple.test.tsx +0 -56
- package/tests/unit/app/client/App.test.tsx.skip +0 -237
- package/tests/unit/app/client/eden-api.test.ts +0 -186
- package/tests/unit/app/client/simple.test.tsx +0 -23
- package/tests/unit/app/controllers/users.controller.test.ts +0 -150
- package/tests/unit/core/create-project.test.ts.skip +0 -95
- package/tests/unit/core/framework.test.ts +0 -144
- package/tests/unit/core/plugins/logger.test.ts.skip +0 -268
- package/tests/unit/core/plugins/vite.test.ts.disabled +0 -188
- package/tests/utils/test-helpers.ts +0 -61
- package/vitest.config.ts +0 -50
- package/workspace.json +0 -6
|
@@ -0,0 +1,930 @@
|
|
|
1
|
+
// ๐ FluxStack Live Component Performance Monitor
|
|
2
|
+
// Advanced performance monitoring, metrics collection, and optimization suggestions
|
|
3
|
+
|
|
4
|
+
import { EventEmitter } from 'events'
|
|
5
|
+
|
|
6
|
+
export interface PerformanceMetrics {
|
|
7
|
+
componentId: string
|
|
8
|
+
componentName: string
|
|
9
|
+
renderMetrics: RenderMetrics
|
|
10
|
+
actionMetrics: ActionMetrics
|
|
11
|
+
memoryMetrics: MemoryMetrics
|
|
12
|
+
networkMetrics: NetworkMetrics
|
|
13
|
+
userInteractionMetrics: UserInteractionMetrics
|
|
14
|
+
timestamp: Date
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface RenderMetrics {
|
|
18
|
+
totalRenders: number
|
|
19
|
+
averageRenderTime: number
|
|
20
|
+
minRenderTime: number
|
|
21
|
+
maxRenderTime: number
|
|
22
|
+
lastRenderTime: number
|
|
23
|
+
renderTimeHistory: number[]
|
|
24
|
+
slowRenderCount: number // renders > threshold
|
|
25
|
+
renderErrorCount: number
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ActionMetrics {
|
|
29
|
+
totalActions: number
|
|
30
|
+
averageActionTime: number
|
|
31
|
+
minActionTime: number
|
|
32
|
+
maxActionTime: number
|
|
33
|
+
actionsByType: Record<string, ActionTypeMetrics>
|
|
34
|
+
failedActions: number
|
|
35
|
+
timeoutActions: number
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ActionTypeMetrics {
|
|
39
|
+
count: number
|
|
40
|
+
averageTime: number
|
|
41
|
+
minTime: number
|
|
42
|
+
maxTime: number
|
|
43
|
+
errorCount: number
|
|
44
|
+
lastExecuted: Date
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface MemoryMetrics {
|
|
48
|
+
currentUsage: number // bytes
|
|
49
|
+
peakUsage: number
|
|
50
|
+
averageUsage: number
|
|
51
|
+
memoryLeakDetected: boolean
|
|
52
|
+
gcCount: number
|
|
53
|
+
stateSize: number // serialized state size
|
|
54
|
+
stateSizeHistory: number[]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface NetworkMetrics {
|
|
58
|
+
messagesSent: number
|
|
59
|
+
messagesReceived: number
|
|
60
|
+
bytesTransferred: number
|
|
61
|
+
averageLatency: number
|
|
62
|
+
connectionDrops: number
|
|
63
|
+
reconnectCount: number
|
|
64
|
+
queuedMessages: number
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface UserInteractionMetrics {
|
|
68
|
+
clickCount: number
|
|
69
|
+
inputChangeCount: number
|
|
70
|
+
formSubmissions: number
|
|
71
|
+
averageInteractionTime: number
|
|
72
|
+
bounceRate: number // percentage of single interactions
|
|
73
|
+
engagementScore: number // calculated engagement metric
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface PerformanceAlert {
|
|
77
|
+
id: string
|
|
78
|
+
componentId: string
|
|
79
|
+
type: 'warning' | 'critical'
|
|
80
|
+
category: 'render' | 'action' | 'memory' | 'network' | 'interaction'
|
|
81
|
+
message: string
|
|
82
|
+
threshold: number
|
|
83
|
+
currentValue: number
|
|
84
|
+
timestamp: Date
|
|
85
|
+
resolved: boolean
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface OptimizationSuggestion {
|
|
89
|
+
id: string
|
|
90
|
+
componentId: string
|
|
91
|
+
type: 'render' | 'memory' | 'network' | 'state' | 'action'
|
|
92
|
+
priority: 'low' | 'medium' | 'high' | 'critical'
|
|
93
|
+
title: string
|
|
94
|
+
description: string
|
|
95
|
+
impact: string
|
|
96
|
+
implementation: string
|
|
97
|
+
estimatedImprovement: string
|
|
98
|
+
timestamp: Date
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export interface PerformanceDashboard {
|
|
102
|
+
overview: {
|
|
103
|
+
totalComponents: number
|
|
104
|
+
healthyComponents: number
|
|
105
|
+
degradedComponents: number
|
|
106
|
+
unhealthyComponents: number
|
|
107
|
+
averageRenderTime: number
|
|
108
|
+
averageMemoryUsage: number
|
|
109
|
+
totalAlerts: number
|
|
110
|
+
criticalAlerts: number
|
|
111
|
+
}
|
|
112
|
+
topPerformers: PerformanceMetrics[]
|
|
113
|
+
worstPerformers: PerformanceMetrics[]
|
|
114
|
+
recentAlerts: PerformanceAlert[]
|
|
115
|
+
suggestions: OptimizationSuggestion[]
|
|
116
|
+
trends: {
|
|
117
|
+
renderTimetrend: number[] // last 24 hours
|
|
118
|
+
memoryUsageTrend: number[]
|
|
119
|
+
actionTimeTrend: number[]
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface MonitoringConfig {
|
|
124
|
+
enabled: boolean
|
|
125
|
+
sampleRate: number // 0-1, percentage of operations to monitor
|
|
126
|
+
renderTimeThreshold: number // ms
|
|
127
|
+
memoryThreshold: number // bytes
|
|
128
|
+
actionTimeThreshold: number // ms
|
|
129
|
+
alertCooldown: number // ms between same type alerts
|
|
130
|
+
historyRetention: number // ms to keep historical data
|
|
131
|
+
dashboardUpdateInterval: number // ms
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export class LiveComponentPerformanceMonitor extends EventEmitter {
|
|
135
|
+
private metrics = new Map<string, PerformanceMetrics>()
|
|
136
|
+
private alerts = new Map<string, PerformanceAlert[]>() // componentId -> alerts
|
|
137
|
+
private suggestions = new Map<string, OptimizationSuggestion[]>()
|
|
138
|
+
private config: MonitoringConfig
|
|
139
|
+
private dashboardUpdateInterval: NodeJS.Timeout
|
|
140
|
+
private alertCooldowns = new Map<string, number>() // alertType -> lastAlertTime
|
|
141
|
+
private performanceObserver?: any
|
|
142
|
+
|
|
143
|
+
constructor(config?: Partial<MonitoringConfig>) {
|
|
144
|
+
super()
|
|
145
|
+
|
|
146
|
+
this.config = {
|
|
147
|
+
enabled: true,
|
|
148
|
+
sampleRate: 1.0, // Monitor 100% by default
|
|
149
|
+
renderTimeThreshold: 100, // 100ms
|
|
150
|
+
memoryThreshold: 50 * 1024 * 1024, // 50MB
|
|
151
|
+
actionTimeThreshold: 1000, // 1 second
|
|
152
|
+
alertCooldown: 60000, // 1 minute
|
|
153
|
+
historyRetention: 24 * 60 * 60 * 1000, // 24 hours
|
|
154
|
+
dashboardUpdateInterval: 30000, // 30 seconds
|
|
155
|
+
...config
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (this.config.enabled) {
|
|
159
|
+
this.setupPerformanceObserver()
|
|
160
|
+
this.setupDashboardUpdates()
|
|
161
|
+
this.setupCleanupTasks()
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Setup performance observer for native performance monitoring
|
|
167
|
+
*/
|
|
168
|
+
private setupPerformanceObserver(): void {
|
|
169
|
+
try {
|
|
170
|
+
// Use Node.js performance hooks if available
|
|
171
|
+
const { PerformanceObserver, performance } = require('perf_hooks')
|
|
172
|
+
|
|
173
|
+
this.performanceObserver = new PerformanceObserver((list: any) => {
|
|
174
|
+
const entries = list.getEntries()
|
|
175
|
+
for (const entry of entries) {
|
|
176
|
+
if (entry.name.startsWith('live-component-')) {
|
|
177
|
+
this.processPerformanceEntry(entry)
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
})
|
|
181
|
+
|
|
182
|
+
this.performanceObserver.observe({ entryTypes: ['measure', 'mark'] })
|
|
183
|
+
console.log('๐ Performance observer initialized')
|
|
184
|
+
} catch (error) {
|
|
185
|
+
console.warn('โ ๏ธ Performance observer not available:', error)
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Process performance entry from observer
|
|
191
|
+
*/
|
|
192
|
+
private processPerformanceEntry(entry: any): void {
|
|
193
|
+
const [, componentId, operation] = entry.name.split('-')
|
|
194
|
+
|
|
195
|
+
if (operation === 'render') {
|
|
196
|
+
this.recordRenderTime(componentId, entry.duration)
|
|
197
|
+
} else if (operation === 'action') {
|
|
198
|
+
this.recordActionTime(componentId, 'unknown', entry.duration)
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Setup dashboard update interval
|
|
204
|
+
*/
|
|
205
|
+
private setupDashboardUpdates(): void {
|
|
206
|
+
this.dashboardUpdateInterval = setInterval(() => {
|
|
207
|
+
this.updateDashboard()
|
|
208
|
+
}, this.config.dashboardUpdateInterval)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Setup cleanup tasks
|
|
213
|
+
*/
|
|
214
|
+
private setupCleanupTasks(): void {
|
|
215
|
+
// Clean up old data every hour
|
|
216
|
+
setInterval(() => {
|
|
217
|
+
this.cleanupOldData()
|
|
218
|
+
}, 60 * 60 * 1000)
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Initialize metrics for a component
|
|
223
|
+
*/
|
|
224
|
+
initializeComponent(componentId: string, componentName: string): void {
|
|
225
|
+
if (!this.config.enabled || !this.shouldSample()) return
|
|
226
|
+
|
|
227
|
+
const metrics: PerformanceMetrics = {
|
|
228
|
+
componentId,
|
|
229
|
+
componentName,
|
|
230
|
+
renderMetrics: {
|
|
231
|
+
totalRenders: 0,
|
|
232
|
+
averageRenderTime: 0,
|
|
233
|
+
minRenderTime: Infinity,
|
|
234
|
+
maxRenderTime: 0,
|
|
235
|
+
lastRenderTime: 0,
|
|
236
|
+
renderTimeHistory: [],
|
|
237
|
+
slowRenderCount: 0,
|
|
238
|
+
renderErrorCount: 0
|
|
239
|
+
},
|
|
240
|
+
actionMetrics: {
|
|
241
|
+
totalActions: 0,
|
|
242
|
+
averageActionTime: 0,
|
|
243
|
+
minActionTime: Infinity,
|
|
244
|
+
maxActionTime: 0,
|
|
245
|
+
actionsByType: {},
|
|
246
|
+
failedActions: 0,
|
|
247
|
+
timeoutActions: 0
|
|
248
|
+
},
|
|
249
|
+
memoryMetrics: {
|
|
250
|
+
currentUsage: 0,
|
|
251
|
+
peakUsage: 0,
|
|
252
|
+
averageUsage: 0,
|
|
253
|
+
memoryLeakDetected: false,
|
|
254
|
+
gcCount: 0,
|
|
255
|
+
stateSize: 0,
|
|
256
|
+
stateSizeHistory: []
|
|
257
|
+
},
|
|
258
|
+
networkMetrics: {
|
|
259
|
+
messagesSent: 0,
|
|
260
|
+
messagesReceived: 0,
|
|
261
|
+
bytesTransferred: 0,
|
|
262
|
+
averageLatency: 0,
|
|
263
|
+
connectionDrops: 0,
|
|
264
|
+
reconnectCount: 0,
|
|
265
|
+
queuedMessages: 0
|
|
266
|
+
},
|
|
267
|
+
userInteractionMetrics: {
|
|
268
|
+
clickCount: 0,
|
|
269
|
+
inputChangeCount: 0,
|
|
270
|
+
formSubmissions: 0,
|
|
271
|
+
averageInteractionTime: 0,
|
|
272
|
+
bounceRate: 0,
|
|
273
|
+
engagementScore: 0
|
|
274
|
+
},
|
|
275
|
+
timestamp: new Date()
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
this.metrics.set(componentId, metrics)
|
|
279
|
+
this.alerts.set(componentId, [])
|
|
280
|
+
this.suggestions.set(componentId, [])
|
|
281
|
+
|
|
282
|
+
console.log(`๐ Performance monitoring initialized for component: ${componentId}`)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Record render performance
|
|
287
|
+
*/
|
|
288
|
+
recordRenderTime(componentId: string, renderTime: number, error?: Error): void {
|
|
289
|
+
if (!this.config.enabled || !this.shouldSample()) return
|
|
290
|
+
|
|
291
|
+
const metrics = this.metrics.get(componentId)
|
|
292
|
+
if (!metrics) return
|
|
293
|
+
|
|
294
|
+
const renderMetrics = metrics.renderMetrics
|
|
295
|
+
|
|
296
|
+
if (error) {
|
|
297
|
+
renderMetrics.renderErrorCount++
|
|
298
|
+
this.createAlert(componentId, 'warning', 'render', `Render error: ${error.message}`, 0, 1)
|
|
299
|
+
return
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// Update render metrics
|
|
303
|
+
renderMetrics.totalRenders++
|
|
304
|
+
renderMetrics.lastRenderTime = renderTime
|
|
305
|
+
renderMetrics.minRenderTime = Math.min(renderMetrics.minRenderTime, renderTime)
|
|
306
|
+
renderMetrics.maxRenderTime = Math.max(renderMetrics.maxRenderTime, renderTime)
|
|
307
|
+
|
|
308
|
+
// Update average
|
|
309
|
+
renderMetrics.averageRenderTime =
|
|
310
|
+
(renderMetrics.averageRenderTime * (renderMetrics.totalRenders - 1) + renderTime) / renderMetrics.totalRenders
|
|
311
|
+
|
|
312
|
+
// Track history (keep last 100 renders)
|
|
313
|
+
renderMetrics.renderTimeHistory.push(renderTime)
|
|
314
|
+
if (renderMetrics.renderTimeHistory.length > 100) {
|
|
315
|
+
renderMetrics.renderTimeHistory.shift()
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Check for slow renders
|
|
319
|
+
if (renderTime > this.config.renderTimeThreshold) {
|
|
320
|
+
renderMetrics.slowRenderCount++
|
|
321
|
+
|
|
322
|
+
if (renderTime > this.config.renderTimeThreshold * 2) {
|
|
323
|
+
this.createAlert(componentId, 'warning', 'render',
|
|
324
|
+
`Slow render detected: ${renderTime.toFixed(2)}ms`,
|
|
325
|
+
this.config.renderTimeThreshold, renderTime)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Generate optimization suggestions
|
|
330
|
+
this.analyzeRenderPerformance(componentId, metrics)
|
|
331
|
+
|
|
332
|
+
metrics.timestamp = new Date()
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Record action performance
|
|
337
|
+
*/
|
|
338
|
+
recordActionTime(componentId: string, actionName: string, actionTime: number, error?: Error): void {
|
|
339
|
+
if (!this.config.enabled || !this.shouldSample()) return
|
|
340
|
+
|
|
341
|
+
const metrics = this.metrics.get(componentId)
|
|
342
|
+
if (!metrics) return
|
|
343
|
+
|
|
344
|
+
const actionMetrics = metrics.actionMetrics
|
|
345
|
+
|
|
346
|
+
if (error) {
|
|
347
|
+
actionMetrics.failedActions++
|
|
348
|
+
this.createAlert(componentId, 'warning', 'action',
|
|
349
|
+
`Action failed: ${actionName} - ${error.message}`, 0, 1)
|
|
350
|
+
return
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Update overall action metrics
|
|
354
|
+
actionMetrics.totalActions++
|
|
355
|
+
actionMetrics.minActionTime = Math.min(actionMetrics.minActionTime, actionTime)
|
|
356
|
+
actionMetrics.maxActionTime = Math.max(actionMetrics.maxActionTime, actionTime)
|
|
357
|
+
actionMetrics.averageActionTime =
|
|
358
|
+
(actionMetrics.averageActionTime * (actionMetrics.totalActions - 1) + actionTime) / actionMetrics.totalActions
|
|
359
|
+
|
|
360
|
+
// Update action type metrics
|
|
361
|
+
if (!actionMetrics.actionsByType[actionName]) {
|
|
362
|
+
actionMetrics.actionsByType[actionName] = {
|
|
363
|
+
count: 0,
|
|
364
|
+
averageTime: 0,
|
|
365
|
+
minTime: Infinity,
|
|
366
|
+
maxTime: 0,
|
|
367
|
+
errorCount: 0,
|
|
368
|
+
lastExecuted: new Date()
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const typeMetrics = actionMetrics.actionsByType[actionName]
|
|
373
|
+
typeMetrics.count++
|
|
374
|
+
typeMetrics.minTime = Math.min(typeMetrics.minTime, actionTime)
|
|
375
|
+
typeMetrics.maxTime = Math.max(typeMetrics.maxTime, actionTime)
|
|
376
|
+
typeMetrics.averageTime =
|
|
377
|
+
(typeMetrics.averageTime * (typeMetrics.count - 1) + actionTime) / typeMetrics.count
|
|
378
|
+
typeMetrics.lastExecuted = new Date()
|
|
379
|
+
|
|
380
|
+
// Check for slow actions
|
|
381
|
+
if (actionTime > this.config.actionTimeThreshold) {
|
|
382
|
+
this.createAlert(componentId, 'warning', 'action',
|
|
383
|
+
`Slow action detected: ${actionName} took ${actionTime.toFixed(2)}ms`,
|
|
384
|
+
this.config.actionTimeThreshold, actionTime)
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
// Generate optimization suggestions
|
|
388
|
+
this.analyzeActionPerformance(componentId, actionName, metrics)
|
|
389
|
+
|
|
390
|
+
metrics.timestamp = new Date()
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Record memory usage
|
|
395
|
+
*/
|
|
396
|
+
recordMemoryUsage(componentId: string, memoryUsage: number, stateSize?: number): void {
|
|
397
|
+
if (!this.config.enabled || !this.shouldSample()) return
|
|
398
|
+
|
|
399
|
+
const metrics = this.metrics.get(componentId)
|
|
400
|
+
if (!metrics) return
|
|
401
|
+
|
|
402
|
+
const memoryMetrics = metrics.memoryMetrics
|
|
403
|
+
|
|
404
|
+
// Update memory metrics
|
|
405
|
+
memoryMetrics.currentUsage = memoryUsage
|
|
406
|
+
memoryMetrics.peakUsage = Math.max(memoryMetrics.peakUsage, memoryUsage)
|
|
407
|
+
|
|
408
|
+
// Calculate average (simple moving average)
|
|
409
|
+
const sampleCount = Math.min(100, memoryMetrics.gcCount + 1)
|
|
410
|
+
memoryMetrics.averageUsage =
|
|
411
|
+
(memoryMetrics.averageUsage * (sampleCount - 1) + memoryUsage) / sampleCount
|
|
412
|
+
|
|
413
|
+
if (stateSize !== undefined) {
|
|
414
|
+
memoryMetrics.stateSize = stateSize
|
|
415
|
+
memoryMetrics.stateSizeHistory.push(stateSize)
|
|
416
|
+
|
|
417
|
+
// Keep last 100 state sizes
|
|
418
|
+
if (memoryMetrics.stateSizeHistory.length > 100) {
|
|
419
|
+
memoryMetrics.stateSizeHistory.shift()
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Check for memory issues
|
|
424
|
+
if (memoryUsage > this.config.memoryThreshold) {
|
|
425
|
+
this.createAlert(componentId, 'critical', 'memory',
|
|
426
|
+
`High memory usage: ${(memoryUsage / 1024 / 1024).toFixed(2)}MB`,
|
|
427
|
+
this.config.memoryThreshold, memoryUsage)
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Detect memory leaks (increasing trend over time)
|
|
431
|
+
if (memoryMetrics.stateSizeHistory.length >= 10) {
|
|
432
|
+
const recentSizes = memoryMetrics.stateSizeHistory.slice(-10)
|
|
433
|
+
const trend = this.calculateTrend(recentSizes)
|
|
434
|
+
|
|
435
|
+
if (trend > 0.1) { // 10% increase trend
|
|
436
|
+
memoryMetrics.memoryLeakDetected = true
|
|
437
|
+
this.createAlert(componentId, 'critical', 'memory',
|
|
438
|
+
'Potential memory leak detected - state size increasing', 0, trend)
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Generate optimization suggestions
|
|
443
|
+
this.analyzeMemoryPerformance(componentId, metrics)
|
|
444
|
+
|
|
445
|
+
metrics.timestamp = new Date()
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Record network metrics
|
|
450
|
+
*/
|
|
451
|
+
recordNetworkActivity(componentId: string, type: 'sent' | 'received', bytes: number, latency?: number): void {
|
|
452
|
+
if (!this.config.enabled || !this.shouldSample()) return
|
|
453
|
+
|
|
454
|
+
const metrics = this.metrics.get(componentId)
|
|
455
|
+
if (!metrics) return
|
|
456
|
+
|
|
457
|
+
const networkMetrics = metrics.networkMetrics
|
|
458
|
+
|
|
459
|
+
if (type === 'sent') {
|
|
460
|
+
networkMetrics.messagesSent++
|
|
461
|
+
} else {
|
|
462
|
+
networkMetrics.messagesReceived++
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
networkMetrics.bytesTransferred += bytes
|
|
466
|
+
|
|
467
|
+
if (latency !== undefined) {
|
|
468
|
+
const totalMessages = networkMetrics.messagesSent + networkMetrics.messagesReceived
|
|
469
|
+
networkMetrics.averageLatency =
|
|
470
|
+
(networkMetrics.averageLatency * (totalMessages - 1) + latency) / totalMessages
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
metrics.timestamp = new Date()
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Record user interaction
|
|
478
|
+
*/
|
|
479
|
+
recordUserInteraction(componentId: string, type: 'click' | 'input' | 'submit', interactionTime?: number): void {
|
|
480
|
+
if (!this.config.enabled || !this.shouldSample()) return
|
|
481
|
+
|
|
482
|
+
const metrics = this.metrics.get(componentId)
|
|
483
|
+
if (!metrics) return
|
|
484
|
+
|
|
485
|
+
const interactionMetrics = metrics.userInteractionMetrics
|
|
486
|
+
|
|
487
|
+
switch (type) {
|
|
488
|
+
case 'click':
|
|
489
|
+
interactionMetrics.clickCount++
|
|
490
|
+
break
|
|
491
|
+
case 'input':
|
|
492
|
+
interactionMetrics.inputChangeCount++
|
|
493
|
+
break
|
|
494
|
+
case 'submit':
|
|
495
|
+
interactionMetrics.formSubmissions++
|
|
496
|
+
break
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
if (interactionTime !== undefined) {
|
|
500
|
+
const totalInteractions = interactionMetrics.clickCount +
|
|
501
|
+
interactionMetrics.inputChangeCount +
|
|
502
|
+
interactionMetrics.formSubmissions
|
|
503
|
+
|
|
504
|
+
interactionMetrics.averageInteractionTime =
|
|
505
|
+
(interactionMetrics.averageInteractionTime * (totalInteractions - 1) + interactionTime) / totalInteractions
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
// Calculate engagement score
|
|
509
|
+
interactionMetrics.engagementScore = this.calculateEngagementScore(interactionMetrics)
|
|
510
|
+
|
|
511
|
+
metrics.timestamp = new Date()
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Calculate engagement score based on interactions
|
|
516
|
+
*/
|
|
517
|
+
private calculateEngagementScore(metrics: UserInteractionMetrics): number {
|
|
518
|
+
const totalInteractions = metrics.clickCount + metrics.inputChangeCount + metrics.formSubmissions
|
|
519
|
+
const timeWeight = Math.min(metrics.averageInteractionTime / 1000, 10) // Cap at 10 seconds
|
|
520
|
+
const diversityBonus = (metrics.clickCount > 0 ? 1 : 0) +
|
|
521
|
+
(metrics.inputChangeCount > 0 ? 1 : 0) +
|
|
522
|
+
(metrics.formSubmissions > 0 ? 1 : 0)
|
|
523
|
+
|
|
524
|
+
return Math.min(100, (totalInteractions * timeWeight * diversityBonus) / 10)
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
/**
|
|
528
|
+
* Create performance alert
|
|
529
|
+
*/
|
|
530
|
+
private createAlert(
|
|
531
|
+
componentId: string,
|
|
532
|
+
type: 'warning' | 'critical',
|
|
533
|
+
category: 'render' | 'action' | 'memory' | 'network' | 'interaction',
|
|
534
|
+
message: string,
|
|
535
|
+
threshold: number,
|
|
536
|
+
currentValue: number
|
|
537
|
+
): void {
|
|
538
|
+
const alertKey = `${componentId}-${category}-${type}`
|
|
539
|
+
const now = Date.now()
|
|
540
|
+
|
|
541
|
+
// Check cooldown
|
|
542
|
+
const lastAlert = this.alertCooldowns.get(alertKey)
|
|
543
|
+
if (lastAlert && (now - lastAlert) < this.config.alertCooldown) {
|
|
544
|
+
return
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const alert: PerformanceAlert = {
|
|
548
|
+
id: `alert-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
549
|
+
componentId,
|
|
550
|
+
type,
|
|
551
|
+
category,
|
|
552
|
+
message,
|
|
553
|
+
threshold,
|
|
554
|
+
currentValue,
|
|
555
|
+
timestamp: new Date(),
|
|
556
|
+
resolved: false
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
const componentAlerts = this.alerts.get(componentId) || []
|
|
560
|
+
componentAlerts.push(alert)
|
|
561
|
+
this.alerts.set(componentId, componentAlerts)
|
|
562
|
+
|
|
563
|
+
this.alertCooldowns.set(alertKey, now)
|
|
564
|
+
|
|
565
|
+
console.warn(`โ ๏ธ Performance alert [${type}]: ${message}`)
|
|
566
|
+
this.emit('performanceAlert', alert)
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Analyze render performance and generate suggestions
|
|
571
|
+
*/
|
|
572
|
+
private analyzeRenderPerformance(componentId: string, metrics: PerformanceMetrics): void {
|
|
573
|
+
const renderMetrics = metrics.renderMetrics
|
|
574
|
+
|
|
575
|
+
// Check for consistently slow renders
|
|
576
|
+
if (renderMetrics.averageRenderTime > this.config.renderTimeThreshold * 0.8) {
|
|
577
|
+
this.createSuggestion(componentId, 'render', 'medium',
|
|
578
|
+
'Optimize Render Performance',
|
|
579
|
+
'Component renders are consistently slow. Consider optimizing state updates and reducing re-renders.',
|
|
580
|
+
'Improved user experience and responsiveness',
|
|
581
|
+
'Use React.memo, useMemo, or useCallback to prevent unnecessary re-renders. Optimize state structure.',
|
|
582
|
+
`${((this.config.renderTimeThreshold - renderMetrics.averageRenderTime) / renderMetrics.averageRenderTime * 100).toFixed(1)}% faster renders`
|
|
583
|
+
)
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Check for render time variance
|
|
587
|
+
if (renderMetrics.renderTimeHistory.length >= 10) {
|
|
588
|
+
const variance = this.calculateVariance(renderMetrics.renderTimeHistory)
|
|
589
|
+
if (variance > renderMetrics.averageRenderTime * 0.5) {
|
|
590
|
+
this.createSuggestion(componentId, 'render', 'low',
|
|
591
|
+
'Reduce Render Time Variance',
|
|
592
|
+
'Render times are inconsistent, which can cause jank.',
|
|
593
|
+
'Smoother user experience',
|
|
594
|
+
'Identify and optimize conditional rendering logic. Consider lazy loading heavy components.',
|
|
595
|
+
'More consistent performance'
|
|
596
|
+
)
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/**
|
|
602
|
+
* Analyze action performance and generate suggestions
|
|
603
|
+
*/
|
|
604
|
+
private analyzeActionPerformance(componentId: string, actionName: string, metrics: PerformanceMetrics): void {
|
|
605
|
+
const actionMetrics = metrics.actionMetrics
|
|
606
|
+
const typeMetrics = actionMetrics.actionsByType[actionName]
|
|
607
|
+
|
|
608
|
+
// Check for slow actions
|
|
609
|
+
if (typeMetrics.averageTime > this.config.actionTimeThreshold * 0.8) {
|
|
610
|
+
this.createSuggestion(componentId, 'action', 'high',
|
|
611
|
+
`Optimize ${actionName} Action`,
|
|
612
|
+
`The ${actionName} action is taking longer than expected to complete.`,
|
|
613
|
+
'Better user experience and responsiveness',
|
|
614
|
+
'Consider adding loading states, optimizing database queries, or implementing caching.',
|
|
615
|
+
`${((this.config.actionTimeThreshold - typeMetrics.averageTime) / typeMetrics.averageTime * 100).toFixed(1)}% faster actions`
|
|
616
|
+
)
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
// Check for high error rate
|
|
620
|
+
const errorRate = typeMetrics.errorCount / typeMetrics.count
|
|
621
|
+
if (errorRate > 0.1) { // 10% error rate
|
|
622
|
+
this.createSuggestion(componentId, 'action', 'critical',
|
|
623
|
+
`Fix ${actionName} Action Errors`,
|
|
624
|
+
`High error rate detected for ${actionName} action (${(errorRate * 100).toFixed(1)}%).`,
|
|
625
|
+
'Improved reliability and user experience',
|
|
626
|
+
'Add proper error handling, input validation, and retry mechanisms.',
|
|
627
|
+
`${((1 - errorRate) * 100).toFixed(1)}% success rate improvement`
|
|
628
|
+
)
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Analyze memory performance and generate suggestions
|
|
634
|
+
*/
|
|
635
|
+
private analyzeMemoryPerformance(componentId: string, metrics: PerformanceMetrics): void {
|
|
636
|
+
const memoryMetrics = metrics.memoryMetrics
|
|
637
|
+
|
|
638
|
+
// Check for large state size
|
|
639
|
+
if (memoryMetrics.stateSize > 100 * 1024) { // 100KB
|
|
640
|
+
this.createSuggestion(componentId, 'memory', 'medium',
|
|
641
|
+
'Optimize State Size',
|
|
642
|
+
'Component state is larger than recommended, which can impact performance.',
|
|
643
|
+
'Reduced memory usage and faster serialization',
|
|
644
|
+
'Consider normalizing state structure, removing unused data, or implementing state compression.',
|
|
645
|
+
`${((memoryMetrics.stateSize - 50 * 1024) / 1024).toFixed(1)}KB reduction potential`
|
|
646
|
+
)
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
// Check for memory leak
|
|
650
|
+
if (memoryMetrics.memoryLeakDetected) {
|
|
651
|
+
this.createSuggestion(componentId, 'memory', 'critical',
|
|
652
|
+
'Fix Memory Leak',
|
|
653
|
+
'Potential memory leak detected - memory usage is continuously increasing.',
|
|
654
|
+
'Prevent application crashes and improve stability',
|
|
655
|
+
'Review event listeners, timers, and subscriptions for proper cleanup. Use weak references where appropriate.',
|
|
656
|
+
'Stable memory usage'
|
|
657
|
+
)
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Create optimization suggestion
|
|
663
|
+
*/
|
|
664
|
+
private createSuggestion(
|
|
665
|
+
componentId: string,
|
|
666
|
+
type: 'render' | 'memory' | 'network' | 'state' | 'action',
|
|
667
|
+
priority: 'low' | 'medium' | 'high' | 'critical',
|
|
668
|
+
title: string,
|
|
669
|
+
description: string,
|
|
670
|
+
impact: string,
|
|
671
|
+
implementation: string,
|
|
672
|
+
estimatedImprovement: string
|
|
673
|
+
): void {
|
|
674
|
+
const suggestions = this.suggestions.get(componentId) || []
|
|
675
|
+
|
|
676
|
+
// Check if similar suggestion already exists
|
|
677
|
+
const existingSuggestion = suggestions.find(s => s.title === title && !s.timestamp ||
|
|
678
|
+
(Date.now() - s.timestamp.getTime()) < 24 * 60 * 60 * 1000) // 24 hours
|
|
679
|
+
|
|
680
|
+
if (existingSuggestion) return
|
|
681
|
+
|
|
682
|
+
const suggestion: OptimizationSuggestion = {
|
|
683
|
+
id: `suggestion-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
|
684
|
+
componentId,
|
|
685
|
+
type,
|
|
686
|
+
priority,
|
|
687
|
+
title,
|
|
688
|
+
description,
|
|
689
|
+
impact,
|
|
690
|
+
implementation,
|
|
691
|
+
estimatedImprovement,
|
|
692
|
+
timestamp: new Date()
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
suggestions.push(suggestion)
|
|
696
|
+
this.suggestions.set(componentId, suggestions)
|
|
697
|
+
|
|
698
|
+
console.log(`๐ก Optimization suggestion for ${componentId}: ${title}`)
|
|
699
|
+
this.emit('optimizationSuggestion', suggestion)
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/**
|
|
703
|
+
* Calculate trend from array of numbers
|
|
704
|
+
*/
|
|
705
|
+
private calculateTrend(values: number[]): number {
|
|
706
|
+
if (values.length < 2) return 0
|
|
707
|
+
|
|
708
|
+
const n = values.length
|
|
709
|
+
const sumX = (n * (n - 1)) / 2
|
|
710
|
+
const sumY = values.reduce((sum, val) => sum + val, 0)
|
|
711
|
+
const sumXY = values.reduce((sum, val, index) => sum + (index * val), 0)
|
|
712
|
+
const sumX2 = (n * (n - 1) * (2 * n - 1)) / 6
|
|
713
|
+
|
|
714
|
+
const slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX)
|
|
715
|
+
return slope / (sumY / n) // Normalize by average
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
/**
|
|
719
|
+
* Calculate variance from array of numbers
|
|
720
|
+
*/
|
|
721
|
+
private calculateVariance(values: number[]): number {
|
|
722
|
+
if (values.length < 2) return 0
|
|
723
|
+
|
|
724
|
+
const mean = values.reduce((sum, val) => sum + val, 0) / values.length
|
|
725
|
+
const squaredDiffs = values.map(val => Math.pow(val - mean, 2))
|
|
726
|
+
return squaredDiffs.reduce((sum, val) => sum + val, 0) / values.length
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
/**
|
|
730
|
+
* Should sample this operation based on sample rate
|
|
731
|
+
*/
|
|
732
|
+
private shouldSample(): boolean {
|
|
733
|
+
return Math.random() < this.config.sampleRate
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
/**
|
|
737
|
+
* Update dashboard data
|
|
738
|
+
*/
|
|
739
|
+
private updateDashboard(): void {
|
|
740
|
+
const dashboard = this.generateDashboard()
|
|
741
|
+
this.emit('dashboardUpdate', dashboard)
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
/**
|
|
745
|
+
* Generate performance dashboard
|
|
746
|
+
*/
|
|
747
|
+
generateDashboard(): PerformanceDashboard {
|
|
748
|
+
const allMetrics = Array.from(this.metrics.values())
|
|
749
|
+
const allAlerts = Array.from(this.alerts.values()).flat()
|
|
750
|
+
const allSuggestions = Array.from(this.suggestions.values()).flat()
|
|
751
|
+
|
|
752
|
+
// Calculate overview stats
|
|
753
|
+
const totalComponents = allMetrics.length
|
|
754
|
+
const healthyComponents = allMetrics.filter(m => this.isComponentHealthy(m)).length
|
|
755
|
+
const degradedComponents = allMetrics.filter(m => this.isComponentDegraded(m)).length
|
|
756
|
+
const unhealthyComponents = totalComponents - healthyComponents - degradedComponents
|
|
757
|
+
|
|
758
|
+
const averageRenderTime = allMetrics.reduce((sum, m) => sum + m.renderMetrics.averageRenderTime, 0) / totalComponents || 0
|
|
759
|
+
const averageMemoryUsage = allMetrics.reduce((sum, m) => sum + m.memoryMetrics.currentUsage, 0) / totalComponents || 0
|
|
760
|
+
|
|
761
|
+
const totalAlerts = allAlerts.filter(a => !a.resolved).length
|
|
762
|
+
const criticalAlerts = allAlerts.filter(a => a.type === 'critical' && !a.resolved).length
|
|
763
|
+
|
|
764
|
+
// Get top and worst performers
|
|
765
|
+
const sortedByRender = [...allMetrics].sort((a, b) => a.renderMetrics.averageRenderTime - b.renderMetrics.averageRenderTime)
|
|
766
|
+
const topPerformers = sortedByRender.slice(0, 5)
|
|
767
|
+
const worstPerformers = sortedByRender.slice(-5).reverse()
|
|
768
|
+
|
|
769
|
+
// Get recent alerts
|
|
770
|
+
const recentAlerts = allAlerts
|
|
771
|
+
.filter(a => !a.resolved)
|
|
772
|
+
.sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime())
|
|
773
|
+
.slice(0, 10)
|
|
774
|
+
|
|
775
|
+
// Get top suggestions
|
|
776
|
+
const topSuggestions = allSuggestions
|
|
777
|
+
.sort((a, b) => {
|
|
778
|
+
const priorityOrder = { critical: 4, high: 3, medium: 2, low: 1 }
|
|
779
|
+
return priorityOrder[b.priority] - priorityOrder[a.priority]
|
|
780
|
+
})
|
|
781
|
+
.slice(0, 10)
|
|
782
|
+
|
|
783
|
+
// Generate trends (simplified - would need historical data storage for real trends)
|
|
784
|
+
const renderTimeHistory = allMetrics.map(m => m.renderMetrics.averageRenderTime)
|
|
785
|
+
const memoryUsageHistory = allMetrics.map(m => m.memoryMetrics.currentUsage)
|
|
786
|
+
const actionTimeHistory = allMetrics.map(m => m.actionMetrics.averageActionTime)
|
|
787
|
+
|
|
788
|
+
return {
|
|
789
|
+
overview: {
|
|
790
|
+
totalComponents,
|
|
791
|
+
healthyComponents,
|
|
792
|
+
degradedComponents,
|
|
793
|
+
unhealthyComponents,
|
|
794
|
+
averageRenderTime,
|
|
795
|
+
averageMemoryUsage,
|
|
796
|
+
totalAlerts,
|
|
797
|
+
criticalAlerts
|
|
798
|
+
},
|
|
799
|
+
topPerformers,
|
|
800
|
+
worstPerformers,
|
|
801
|
+
recentAlerts,
|
|
802
|
+
suggestions: topSuggestions,
|
|
803
|
+
trends: {
|
|
804
|
+
renderTimetrend: renderTimeHistory,
|
|
805
|
+
memoryUsageTrend: memoryUsageHistory,
|
|
806
|
+
actionTimeTrend: actionTimeHistory
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
/**
|
|
812
|
+
* Check if component is healthy
|
|
813
|
+
*/
|
|
814
|
+
private isComponentHealthy(metrics: PerformanceMetrics): boolean {
|
|
815
|
+
return metrics.renderMetrics.averageRenderTime < this.config.renderTimeThreshold * 0.5 &&
|
|
816
|
+
metrics.memoryMetrics.currentUsage < this.config.memoryThreshold * 0.5 &&
|
|
817
|
+
metrics.actionMetrics.failedActions / Math.max(metrics.actionMetrics.totalActions, 1) < 0.05
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/**
|
|
821
|
+
* Check if component is degraded
|
|
822
|
+
*/
|
|
823
|
+
private isComponentDegraded(metrics: PerformanceMetrics): boolean {
|
|
824
|
+
return !this.isComponentHealthy(metrics) && !this.isComponentUnhealthy(metrics)
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
/**
|
|
828
|
+
* Check if component is unhealthy
|
|
829
|
+
*/
|
|
830
|
+
private isComponentUnhealthy(metrics: PerformanceMetrics): boolean {
|
|
831
|
+
return metrics.renderMetrics.averageRenderTime > this.config.renderTimeThreshold ||
|
|
832
|
+
metrics.memoryMetrics.currentUsage > this.config.memoryThreshold ||
|
|
833
|
+
metrics.actionMetrics.failedActions / Math.max(metrics.actionMetrics.totalActions, 1) > 0.2 ||
|
|
834
|
+
metrics.memoryMetrics.memoryLeakDetected
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
/**
|
|
838
|
+
* Get component metrics
|
|
839
|
+
*/
|
|
840
|
+
getComponentMetrics(componentId: string): PerformanceMetrics | null {
|
|
841
|
+
return this.metrics.get(componentId) || null
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
/**
|
|
845
|
+
* Get component alerts
|
|
846
|
+
*/
|
|
847
|
+
getComponentAlerts(componentId: string): PerformanceAlert[] {
|
|
848
|
+
return this.alerts.get(componentId) || []
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
* Get component suggestions
|
|
853
|
+
*/
|
|
854
|
+
getComponentSuggestions(componentId: string): OptimizationSuggestion[] {
|
|
855
|
+
return this.suggestions.get(componentId) || []
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* Resolve alert
|
|
860
|
+
*/
|
|
861
|
+
resolveAlert(alertId: string): boolean {
|
|
862
|
+
for (const alerts of this.alerts.values()) {
|
|
863
|
+
const alert = alerts.find(a => a.id === alertId)
|
|
864
|
+
if (alert) {
|
|
865
|
+
alert.resolved = true
|
|
866
|
+
console.log(`โ
Alert resolved: ${alertId}`)
|
|
867
|
+
return true
|
|
868
|
+
}
|
|
869
|
+
}
|
|
870
|
+
return false
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
/**
|
|
874
|
+
* Clean up old data
|
|
875
|
+
*/
|
|
876
|
+
private cleanupOldData(): void {
|
|
877
|
+
const now = Date.now()
|
|
878
|
+
const cutoff = now - this.config.historyRetention
|
|
879
|
+
|
|
880
|
+
// Clean up old alerts
|
|
881
|
+
for (const [componentId, alerts] of this.alerts) {
|
|
882
|
+
const validAlerts = alerts.filter(alert => alert.timestamp.getTime() > cutoff)
|
|
883
|
+
this.alerts.set(componentId, validAlerts)
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// Clean up old suggestions
|
|
887
|
+
for (const [componentId, suggestions] of this.suggestions) {
|
|
888
|
+
const validSuggestions = suggestions.filter(suggestion => suggestion.timestamp.getTime() > cutoff)
|
|
889
|
+
this.suggestions.set(componentId, validSuggestions)
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
console.log('๐งน Performance monitoring data cleanup completed')
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Remove component from monitoring
|
|
897
|
+
*/
|
|
898
|
+
removeComponent(componentId: string): void {
|
|
899
|
+
this.metrics.delete(componentId)
|
|
900
|
+
this.alerts.delete(componentId)
|
|
901
|
+
this.suggestions.delete(componentId)
|
|
902
|
+
|
|
903
|
+
console.log(`๐ Performance monitoring removed for component: ${componentId}`)
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Shutdown performance monitor
|
|
908
|
+
*/
|
|
909
|
+
shutdown(): void {
|
|
910
|
+
console.log('๐ Shutting down Performance Monitor...')
|
|
911
|
+
|
|
912
|
+
if (this.dashboardUpdateInterval) {
|
|
913
|
+
clearInterval(this.dashboardUpdateInterval)
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
if (this.performanceObserver) {
|
|
917
|
+
this.performanceObserver.disconnect()
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
this.metrics.clear()
|
|
921
|
+
this.alerts.clear()
|
|
922
|
+
this.suggestions.clear()
|
|
923
|
+
this.alertCooldowns.clear()
|
|
924
|
+
|
|
925
|
+
console.log('โ
Performance Monitor shutdown complete')
|
|
926
|
+
}
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
// Global performance monitor instance
|
|
930
|
+
export const performanceMonitor = new LiveComponentPerformanceMonitor()
|