mastercontroller 1.2.12 → 1.2.13
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/CSPConfig.js +319 -0
- package/EventHandlerValidator.js +464 -0
- package/MasterAction.js +296 -72
- package/MasterBackendErrorHandler.js +769 -0
- package/MasterBenchmark.js +89 -0
- package/MasterBuildOptimizer.js +376 -0
- package/MasterBundleAnalyzer.js +108 -0
- package/MasterCache.js +400 -0
- package/MasterControl.js +76 -6
- package/MasterErrorHandler.js +487 -0
- package/MasterErrorLogger.js +360 -0
- package/MasterErrorMiddleware.js +407 -0
- package/MasterHtml.js +101 -14
- package/MasterMemoryMonitor.js +188 -0
- package/MasterProfiler.js +409 -0
- package/MasterRouter.js +273 -66
- package/MasterSanitizer.js +429 -0
- package/MasterTemplate.js +96 -3
- package/MasterValidator.js +546 -0
- package/README.md +0 -44
- package/SecurityMiddleware.js +486 -0
- package/SessionSecurity.js +416 -0
- package/package.json +3 -3
- package/ssr/ErrorBoundary.js +353 -0
- package/ssr/HTMLUtils.js +15 -0
- package/ssr/HydrationMismatch.js +265 -0
- package/ssr/PerformanceMonitor.js +233 -0
- package/ssr/SSRErrorHandler.js +273 -0
- package/ssr/hydration-client.js +93 -0
- package/ssr/runtime-ssr.cjs +553 -0
- package/ssr/ssr-shims.js +73 -0
- package/examples/FileServingExample.js +0 -88
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// version 1.0.0
|
|
2
|
+
// MasterController Memory Monitor - Memory Leak Detection
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Memory monitor for detecting memory leaks
|
|
6
|
+
* - Heap usage tracking
|
|
7
|
+
* - Memory leak detection
|
|
8
|
+
* - Garbage collection monitoring
|
|
9
|
+
* - Memory alerts
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const { logger } = require('./MasterErrorLogger');
|
|
13
|
+
|
|
14
|
+
class MasterMemoryMonitor {
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.enabled = options.enabled !== false;
|
|
17
|
+
this.checkInterval = options.checkInterval || 30000; // 30 seconds
|
|
18
|
+
this.leakThreshold = options.leakThreshold || 50; // 50MB growth
|
|
19
|
+
this.alertThreshold = options.alertThreshold || 500; // 500MB
|
|
20
|
+
|
|
21
|
+
this.snapshots = [];
|
|
22
|
+
this.maxSnapshots = 100;
|
|
23
|
+
this.intervalId = null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Start monitoring
|
|
28
|
+
*/
|
|
29
|
+
start() {
|
|
30
|
+
if (!this.enabled || this.intervalId) return;
|
|
31
|
+
|
|
32
|
+
this.takeSnapshot();
|
|
33
|
+
|
|
34
|
+
this.intervalId = setInterval(() => {
|
|
35
|
+
this.takeSnapshot();
|
|
36
|
+
this.checkForLeaks();
|
|
37
|
+
}, this.checkInterval);
|
|
38
|
+
|
|
39
|
+
logger.info({
|
|
40
|
+
code: 'MC_MEMORY_MONITOR_START',
|
|
41
|
+
message: 'Memory monitoring started',
|
|
42
|
+
interval: `${this.checkInterval}ms`
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Stop monitoring
|
|
48
|
+
*/
|
|
49
|
+
stop() {
|
|
50
|
+
if (this.intervalId) {
|
|
51
|
+
clearInterval(this.intervalId);
|
|
52
|
+
this.intervalId = null;
|
|
53
|
+
|
|
54
|
+
logger.info({
|
|
55
|
+
code: 'MC_MEMORY_MONITOR_STOP',
|
|
56
|
+
message: 'Memory monitoring stopped'
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Take memory snapshot
|
|
63
|
+
*/
|
|
64
|
+
takeSnapshot() {
|
|
65
|
+
const usage = process.memoryUsage();
|
|
66
|
+
|
|
67
|
+
const snapshot = {
|
|
68
|
+
timestamp: Date.now(),
|
|
69
|
+
heapUsed: usage.heapUsed,
|
|
70
|
+
heapTotal: usage.heapTotal,
|
|
71
|
+
external: usage.external,
|
|
72
|
+
rss: usage.rss
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
this.snapshots.push(snapshot);
|
|
76
|
+
|
|
77
|
+
// Keep only last N snapshots
|
|
78
|
+
if (this.snapshots.length > this.maxSnapshots) {
|
|
79
|
+
this.snapshots.shift();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Alert if memory usage is high
|
|
83
|
+
const heapUsedMB = usage.heapUsed / 1024 / 1024;
|
|
84
|
+
if (heapUsedMB > this.alertThreshold) {
|
|
85
|
+
logger.warn({
|
|
86
|
+
code: 'MC_MEMORY_HIGH',
|
|
87
|
+
message: 'High memory usage detected',
|
|
88
|
+
heapUsed: `${heapUsedMB.toFixed(2)} MB`,
|
|
89
|
+
threshold: `${this.alertThreshold} MB`
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return snapshot;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Check for memory leaks
|
|
98
|
+
*/
|
|
99
|
+
checkForLeaks() {
|
|
100
|
+
if (this.snapshots.length < 10) return;
|
|
101
|
+
|
|
102
|
+
// Compare first 5 and last 5 snapshots
|
|
103
|
+
const oldSnapshots = this.snapshots.slice(0, 5);
|
|
104
|
+
const newSnapshots = this.snapshots.slice(-5);
|
|
105
|
+
|
|
106
|
+
const oldAvg = oldSnapshots.reduce((sum, s) => sum + s.heapUsed, 0) / oldSnapshots.length;
|
|
107
|
+
const newAvg = newSnapshots.reduce((sum, s) => sum + s.heapUsed, 0) / newSnapshots.length;
|
|
108
|
+
|
|
109
|
+
const growthBytes = newAvg - oldAvg;
|
|
110
|
+
const growthMB = growthBytes / 1024 / 1024;
|
|
111
|
+
|
|
112
|
+
if (growthMB > this.leakThreshold) {
|
|
113
|
+
logger.warn({
|
|
114
|
+
code: 'MC_MEMORY_LEAK_DETECTED',
|
|
115
|
+
message: 'Potential memory leak detected',
|
|
116
|
+
growth: `${growthMB.toFixed(2)} MB`,
|
|
117
|
+
oldAvg: `${(oldAvg / 1024 / 1024).toFixed(2)} MB`,
|
|
118
|
+
newAvg: `${(newAvg / 1024 / 1024).toFixed(2)} MB`,
|
|
119
|
+
suggestion: 'Review component lifecycle and event listener cleanup'
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Get current memory usage
|
|
126
|
+
*/
|
|
127
|
+
getCurrentUsage() {
|
|
128
|
+
const usage = process.memoryUsage();
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
heapUsed: `${(usage.heapUsed / 1024 / 1024).toFixed(2)} MB`,
|
|
132
|
+
heapTotal: `${(usage.heapTotal / 1024 / 1024).toFixed(2)} MB`,
|
|
133
|
+
external: `${(usage.external / 1024 / 1024).toFixed(2)} MB`,
|
|
134
|
+
rss: `${(usage.rss / 1024 / 1024).toFixed(2)} MB`
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Print memory report
|
|
140
|
+
*/
|
|
141
|
+
printReport() {
|
|
142
|
+
if (this.snapshots.length === 0) {
|
|
143
|
+
console.log('No memory snapshots available');
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const current = this.snapshots[this.snapshots.length - 1];
|
|
148
|
+
const first = this.snapshots[0];
|
|
149
|
+
|
|
150
|
+
const growth = current.heapUsed - first.heapUsed;
|
|
151
|
+
const growthPercent = (growth / first.heapUsed * 100).toFixed(2);
|
|
152
|
+
|
|
153
|
+
console.log('\n═══════════════════════════════════════════════════');
|
|
154
|
+
console.log('💾 MasterController Memory Report');
|
|
155
|
+
console.log('═══════════════════════════════════════════════════');
|
|
156
|
+
|
|
157
|
+
console.log('\nCurrent Usage:');
|
|
158
|
+
console.log(` Heap Used: ${(current.heapUsed / 1024 / 1024).toFixed(2)} MB`);
|
|
159
|
+
console.log(` Heap Total: ${(current.heapTotal / 1024 / 1024).toFixed(2)} MB`);
|
|
160
|
+
console.log(` External: ${(current.external / 1024 / 1024).toFixed(2)} MB`);
|
|
161
|
+
console.log(` RSS: ${(current.rss / 1024 / 1024).toFixed(2)} MB`);
|
|
162
|
+
|
|
163
|
+
console.log('\nMemory Growth:');
|
|
164
|
+
console.log(` Initial: ${(first.heapUsed / 1024 / 1024).toFixed(2)} MB`);
|
|
165
|
+
console.log(` Current: ${(current.heapUsed / 1024 / 1024).toFixed(2)} MB`);
|
|
166
|
+
console.log(` Growth: ${(growth / 1024 / 1024).toFixed(2)} MB (${growthPercent}%)`);
|
|
167
|
+
|
|
168
|
+
console.log(`\nSnapshots: ${this.snapshots.length}`);
|
|
169
|
+
console.log(`Duration: ${((current.timestamp - first.timestamp) / 1000 / 60).toFixed(2)} minutes`);
|
|
170
|
+
|
|
171
|
+
console.log('═══════════════════════════════════════════════════\n');
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Create singleton
|
|
176
|
+
const memoryMonitor = new MasterMemoryMonitor({
|
|
177
|
+
enabled: process.env.MC_MEMORY_MONITOR === 'true',
|
|
178
|
+
checkInterval: 30000,
|
|
179
|
+
leakThreshold: 50,
|
|
180
|
+
alertThreshold: 500
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Auto-start in development
|
|
184
|
+
if (process.env.NODE_ENV === 'development') {
|
|
185
|
+
memoryMonitor.start();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
module.exports = { MasterMemoryMonitor, memoryMonitor };
|
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
// version 1.0.0
|
|
2
|
+
// MasterController Performance Profiler - Component and Request Profiling
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Performance profiler for MasterController
|
|
6
|
+
* - Component render time tracking
|
|
7
|
+
* - Slow component detection
|
|
8
|
+
* - Request profiling
|
|
9
|
+
* - Performance bottleneck identification
|
|
10
|
+
* - Detailed performance reports
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { logger } = require('./MasterErrorLogger');
|
|
14
|
+
|
|
15
|
+
class MasterProfiler {
|
|
16
|
+
constructor(options = {}) {
|
|
17
|
+
this.enabled = options.enabled !== false;
|
|
18
|
+
this.slowThreshold = options.slowThreshold || 100; // 100ms
|
|
19
|
+
this.verySlowThreshold = options.verySlowThreshold || 500; // 500ms
|
|
20
|
+
|
|
21
|
+
// Performance data
|
|
22
|
+
this.componentMetrics = new Map();
|
|
23
|
+
this.requestMetrics = [];
|
|
24
|
+
this.currentRequest = null;
|
|
25
|
+
|
|
26
|
+
// Marks
|
|
27
|
+
this.marks = new Map();
|
|
28
|
+
|
|
29
|
+
// Statistics
|
|
30
|
+
this.totalComponents = 0;
|
|
31
|
+
this.slowComponents = 0;
|
|
32
|
+
this.verySlowComponents = 0;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Start profiling a component render
|
|
37
|
+
*/
|
|
38
|
+
startComponentRender(componentName, props = {}) {
|
|
39
|
+
if (!this.enabled) return null;
|
|
40
|
+
|
|
41
|
+
const id = `${componentName}-${Date.now()}-${Math.random()}`;
|
|
42
|
+
|
|
43
|
+
this.mark(`${id}:start`);
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
id,
|
|
47
|
+
componentName,
|
|
48
|
+
props,
|
|
49
|
+
startTime: Date.now()
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* End profiling a component render
|
|
55
|
+
*/
|
|
56
|
+
endComponentRender(profile) {
|
|
57
|
+
if (!this.enabled || !profile) return;
|
|
58
|
+
|
|
59
|
+
this.mark(`${profile.id}:end`);
|
|
60
|
+
|
|
61
|
+
const duration = this.measure(
|
|
62
|
+
`${profile.id}:render`,
|
|
63
|
+
`${profile.id}:start`,
|
|
64
|
+
`${profile.id}:end`
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Store metrics
|
|
68
|
+
if (!this.componentMetrics.has(profile.componentName)) {
|
|
69
|
+
this.componentMetrics.set(profile.componentName, {
|
|
70
|
+
componentName: profile.componentName,
|
|
71
|
+
renders: [],
|
|
72
|
+
totalRenders: 0,
|
|
73
|
+
totalTime: 0,
|
|
74
|
+
avgTime: 0,
|
|
75
|
+
minTime: Infinity,
|
|
76
|
+
maxTime: 0,
|
|
77
|
+
slowRenders: 0,
|
|
78
|
+
verySlowRenders: 0
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const metrics = this.componentMetrics.get(profile.componentName);
|
|
83
|
+
metrics.renders.push({
|
|
84
|
+
duration,
|
|
85
|
+
timestamp: profile.startTime,
|
|
86
|
+
props: profile.props
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
metrics.totalRenders++;
|
|
90
|
+
metrics.totalTime += duration;
|
|
91
|
+
metrics.avgTime = metrics.totalTime / metrics.totalRenders;
|
|
92
|
+
metrics.minTime = Math.min(metrics.minTime, duration);
|
|
93
|
+
metrics.maxTime = Math.max(metrics.maxTime, duration);
|
|
94
|
+
|
|
95
|
+
if (duration > this.slowThreshold) {
|
|
96
|
+
metrics.slowRenders++;
|
|
97
|
+
this.slowComponents++;
|
|
98
|
+
|
|
99
|
+
if (duration > this.verySlowThreshold) {
|
|
100
|
+
metrics.verySlowRenders++;
|
|
101
|
+
this.verySlowComponents++;
|
|
102
|
+
|
|
103
|
+
logger.warn({
|
|
104
|
+
code: 'MC_PERF_VERY_SLOW_COMPONENT',
|
|
105
|
+
message: `Very slow component render detected: ${profile.componentName}`,
|
|
106
|
+
duration: `${duration}ms`,
|
|
107
|
+
threshold: `${this.verySlowThreshold}ms`
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
this.totalComponents++;
|
|
113
|
+
|
|
114
|
+
// Keep only last 100 renders per component
|
|
115
|
+
if (metrics.renders.length > 100) {
|
|
116
|
+
metrics.renders.shift();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Start profiling a request
|
|
122
|
+
*/
|
|
123
|
+
startRequest(path, method = 'GET') {
|
|
124
|
+
if (!this.enabled) return null;
|
|
125
|
+
|
|
126
|
+
const id = `request-${Date.now()}-${Math.random()}`;
|
|
127
|
+
|
|
128
|
+
this.mark(`${id}:start`);
|
|
129
|
+
|
|
130
|
+
this.currentRequest = {
|
|
131
|
+
id,
|
|
132
|
+
path,
|
|
133
|
+
method,
|
|
134
|
+
startTime: Date.now(),
|
|
135
|
+
components: []
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
return this.currentRequest;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* End profiling a request
|
|
143
|
+
*/
|
|
144
|
+
endRequest(requestProfile) {
|
|
145
|
+
if (!this.enabled || !requestProfile) return;
|
|
146
|
+
|
|
147
|
+
this.mark(`${requestProfile.id}:end`);
|
|
148
|
+
|
|
149
|
+
const duration = this.measure(
|
|
150
|
+
`${requestProfile.id}:request`,
|
|
151
|
+
`${requestProfile.id}:start`,
|
|
152
|
+
`${requestProfile.id}:end`
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
requestProfile.duration = duration;
|
|
156
|
+
requestProfile.endTime = Date.now();
|
|
157
|
+
|
|
158
|
+
this.requestMetrics.push(requestProfile);
|
|
159
|
+
|
|
160
|
+
// Keep only last 1000 requests
|
|
161
|
+
if (this.requestMetrics.length > 1000) {
|
|
162
|
+
this.requestMetrics.shift();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Log slow requests
|
|
166
|
+
if (duration > 1000) {
|
|
167
|
+
logger.warn({
|
|
168
|
+
code: 'MC_PERF_SLOW_REQUEST',
|
|
169
|
+
message: `Slow request detected: ${requestProfile.method} ${requestProfile.path}`,
|
|
170
|
+
duration: `${duration}ms`
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
this.currentRequest = null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Create a performance mark
|
|
179
|
+
*/
|
|
180
|
+
mark(name) {
|
|
181
|
+
if (!this.enabled) return;
|
|
182
|
+
|
|
183
|
+
this.marks.set(name, {
|
|
184
|
+
name,
|
|
185
|
+
timestamp: Date.now()
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Measure duration between two marks
|
|
191
|
+
*/
|
|
192
|
+
measure(name, startMark, endMark) {
|
|
193
|
+
if (!this.enabled) return 0;
|
|
194
|
+
|
|
195
|
+
const start = this.marks.get(startMark);
|
|
196
|
+
const end = this.marks.get(endMark);
|
|
197
|
+
|
|
198
|
+
if (!start || !end) {
|
|
199
|
+
return 0;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return end.timestamp - start.timestamp;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Get component performance metrics
|
|
207
|
+
*/
|
|
208
|
+
getComponentMetrics(componentName = null) {
|
|
209
|
+
if (componentName) {
|
|
210
|
+
return this.componentMetrics.get(componentName) || null;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return Array.from(this.componentMetrics.values());
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Get request performance metrics
|
|
218
|
+
*/
|
|
219
|
+
getRequestMetrics(limit = 100) {
|
|
220
|
+
return this.requestMetrics.slice(-limit);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Get slow components
|
|
225
|
+
*/
|
|
226
|
+
getSlowComponents(threshold = null) {
|
|
227
|
+
threshold = threshold || this.slowThreshold;
|
|
228
|
+
|
|
229
|
+
return Array.from(this.componentMetrics.values())
|
|
230
|
+
.filter(m => m.avgTime > threshold)
|
|
231
|
+
.sort((a, b) => b.avgTime - a.avgTime);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Get slow requests
|
|
236
|
+
*/
|
|
237
|
+
getSlowRequests(threshold = 1000, limit = 100) {
|
|
238
|
+
return this.requestMetrics
|
|
239
|
+
.filter(r => r.duration > threshold)
|
|
240
|
+
.slice(-limit)
|
|
241
|
+
.sort((a, b) => b.duration - a.duration);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Generate performance report
|
|
246
|
+
*/
|
|
247
|
+
generateReport() {
|
|
248
|
+
const components = this.getComponentMetrics();
|
|
249
|
+
const slowComponents = this.getSlowComponents();
|
|
250
|
+
const requests = this.getRequestMetrics();
|
|
251
|
+
const slowRequests = this.getSlowRequests();
|
|
252
|
+
|
|
253
|
+
// Calculate statistics
|
|
254
|
+
const totalRenderTime = components.reduce((sum, c) => sum + c.totalTime, 0);
|
|
255
|
+
const avgRenderTime = components.length > 0
|
|
256
|
+
? totalRenderTime / components.reduce((sum, c) => sum + c.totalRenders, 0)
|
|
257
|
+
: 0;
|
|
258
|
+
|
|
259
|
+
const totalRequestTime = requests.reduce((sum, r) => sum + r.duration, 0);
|
|
260
|
+
const avgRequestTime = requests.length > 0 ? totalRequestTime / requests.length : 0;
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
summary: {
|
|
264
|
+
totalComponents: this.totalComponents,
|
|
265
|
+
uniqueComponents: components.length,
|
|
266
|
+
slowComponents: this.slowComponents,
|
|
267
|
+
verySlowComponents: this.verySlowComponents,
|
|
268
|
+
totalRequests: requests.length,
|
|
269
|
+
slowRequests: slowRequests.length,
|
|
270
|
+
avgRenderTime: Math.round(avgRenderTime),
|
|
271
|
+
avgRequestTime: Math.round(avgRequestTime)
|
|
272
|
+
},
|
|
273
|
+
components: {
|
|
274
|
+
all: components,
|
|
275
|
+
slow: slowComponents.slice(0, 10)
|
|
276
|
+
},
|
|
277
|
+
requests: {
|
|
278
|
+
recent: requests.slice(-10),
|
|
279
|
+
slow: slowRequests.slice(0, 10)
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Print performance report
|
|
286
|
+
*/
|
|
287
|
+
printReport() {
|
|
288
|
+
const report = this.generateReport();
|
|
289
|
+
|
|
290
|
+
console.log('\n═══════════════════════════════════════════════════');
|
|
291
|
+
console.log('⚡ MasterController Performance Report');
|
|
292
|
+
console.log('═══════════════════════════════════════════════════');
|
|
293
|
+
|
|
294
|
+
console.log('\n📊 Summary:');
|
|
295
|
+
console.log(` Total Component Renders: ${report.summary.totalComponents}`);
|
|
296
|
+
console.log(` Unique Components: ${report.summary.uniqueComponents}`);
|
|
297
|
+
console.log(` Slow Components (>${this.slowThreshold}ms): ${report.summary.slowComponents}`);
|
|
298
|
+
console.log(` Very Slow Components (>${this.verySlowThreshold}ms): ${report.summary.verySlowComponents}`);
|
|
299
|
+
console.log(` Average Render Time: ${report.summary.avgRenderTime}ms`);
|
|
300
|
+
console.log(` Total Requests: ${report.summary.totalRequests}`);
|
|
301
|
+
console.log(` Slow Requests (>1000ms): ${report.summary.slowRequests}`);
|
|
302
|
+
console.log(` Average Request Time: ${report.summary.avgRequestTime}ms`);
|
|
303
|
+
|
|
304
|
+
if (report.components.slow.length > 0) {
|
|
305
|
+
console.log('\n🐌 Slowest Components:');
|
|
306
|
+
report.components.slow.forEach((comp, i) => {
|
|
307
|
+
console.log(` ${i + 1}. ${comp.componentName}`);
|
|
308
|
+
console.log(` Avg: ${Math.round(comp.avgTime)}ms | Max: ${Math.round(comp.maxTime)}ms | Renders: ${comp.totalRenders}`);
|
|
309
|
+
console.log(` Slow Renders: ${comp.slowRenders} | Very Slow: ${comp.verySlowRenders}`);
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (report.requests.slow.length > 0) {
|
|
314
|
+
console.log('\n🐌 Slowest Requests:');
|
|
315
|
+
report.requests.slow.forEach((req, i) => {
|
|
316
|
+
console.log(` ${i + 1}. ${req.method} ${req.path}`);
|
|
317
|
+
console.log(` Duration: ${Math.round(req.duration)}ms`);
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
console.log('\n💡 Recommendations:');
|
|
322
|
+
if (report.summary.verySlowComponents > 0) {
|
|
323
|
+
console.log(' ⚠️ Some components are very slow (>500ms)');
|
|
324
|
+
console.log(' - Consider code splitting');
|
|
325
|
+
console.log(' - Optimize expensive operations');
|
|
326
|
+
console.log(' - Use memoization for computed values');
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
if (report.summary.slowComponents > 10) {
|
|
330
|
+
console.log(' ⚠️ Many slow components detected');
|
|
331
|
+
console.log(' - Review component implementations');
|
|
332
|
+
console.log(' - Enable render caching');
|
|
333
|
+
console.log(' - Consider lazy loading');
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (report.summary.slowRequests > 5) {
|
|
337
|
+
console.log(' ⚠️ Multiple slow requests detected');
|
|
338
|
+
console.log(' - Review database queries');
|
|
339
|
+
console.log(' - Add caching');
|
|
340
|
+
console.log(' - Optimize expensive operations');
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
console.log('═══════════════════════════════════════════════════\n');
|
|
344
|
+
|
|
345
|
+
return report;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Reset profiler
|
|
350
|
+
*/
|
|
351
|
+
reset() {
|
|
352
|
+
this.componentMetrics.clear();
|
|
353
|
+
this.requestMetrics = [];
|
|
354
|
+
this.marks.clear();
|
|
355
|
+
this.totalComponents = 0;
|
|
356
|
+
this.slowComponents = 0;
|
|
357
|
+
this.verySlowComponents = 0;
|
|
358
|
+
this.currentRequest = null;
|
|
359
|
+
|
|
360
|
+
logger.info({
|
|
361
|
+
code: 'MC_PERF_RESET',
|
|
362
|
+
message: 'Profiler reset'
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Enable profiler
|
|
368
|
+
*/
|
|
369
|
+
enable() {
|
|
370
|
+
this.enabled = true;
|
|
371
|
+
logger.info({
|
|
372
|
+
code: 'MC_PERF_ENABLED',
|
|
373
|
+
message: 'Profiler enabled'
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Disable profiler
|
|
379
|
+
*/
|
|
380
|
+
disable() {
|
|
381
|
+
this.enabled = false;
|
|
382
|
+
logger.info({
|
|
383
|
+
code: 'MC_PERF_DISABLED',
|
|
384
|
+
message: 'Profiler disabled'
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Create singleton instance
|
|
390
|
+
const profiler = new MasterProfiler({
|
|
391
|
+
enabled: process.env.NODE_ENV === 'development' || process.env.MC_PROFILER_ENABLED === 'true',
|
|
392
|
+
slowThreshold: parseInt(process.env.MC_SLOW_THRESHOLD) || 100,
|
|
393
|
+
verySlowThreshold: parseInt(process.env.MC_VERY_SLOW_THRESHOLD) || 500
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
// Auto-print report every 5 minutes in development
|
|
397
|
+
if (process.env.NODE_ENV === 'development') {
|
|
398
|
+
setInterval(() => {
|
|
399
|
+
const report = profiler.generateReport();
|
|
400
|
+
if (report.summary.totalComponents > 0) {
|
|
401
|
+
profiler.printReport();
|
|
402
|
+
}
|
|
403
|
+
}, 300000); // 5 minutes
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
module.exports = {
|
|
407
|
+
MasterProfiler,
|
|
408
|
+
profiler
|
|
409
|
+
};
|