claude-code-templates 1.8.0 → 1.8.1

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.
@@ -0,0 +1,455 @@
1
+ /**
2
+ * PerformanceMonitor - Monitors and tracks system performance
3
+ * Phase 4: Performance monitoring and optimization
4
+ */
5
+ const fs = require('fs-extra');
6
+ const path = require('path');
7
+ const chalk = require('chalk');
8
+
9
+ class PerformanceMonitor {
10
+ constructor(options = {}) {
11
+ this.options = {
12
+ enabled: options.enabled !== false,
13
+ logInterval: options.logInterval || 60000, // 1 minute
14
+ metricsRetention: options.metricsRetention || 3600000, // 1 hour
15
+ memoryThreshold: options.memoryThreshold || 100 * 1024 * 1024, // 100MB
16
+ ...options
17
+ };
18
+
19
+ this.metrics = {
20
+ memory: [],
21
+ cpu: [],
22
+ requests: [],
23
+ cache: [],
24
+ errors: [],
25
+ websocket: []
26
+ };
27
+
28
+ this.timers = {};
29
+ this.counters = {};
30
+ this.startTime = Date.now();
31
+ this.logInterval = null;
32
+
33
+ if (this.options.enabled) {
34
+ this.startMonitoring();
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Start performance monitoring
40
+ */
41
+ startMonitoring() {
42
+ console.log(chalk.blue('šŸ“Š Starting performance monitoring...'));
43
+
44
+ // Start periodic logging
45
+ this.logInterval = setInterval(() => {
46
+ this.collectSystemMetrics();
47
+ this.logPerformanceReport();
48
+ this.cleanupOldMetrics();
49
+ }, this.options.logInterval);
50
+
51
+ // Monitor process events
52
+ this.setupProcessMonitoring();
53
+ }
54
+
55
+ /**
56
+ * Stop performance monitoring
57
+ */
58
+ stopMonitoring() {
59
+ if (this.logInterval) {
60
+ clearInterval(this.logInterval);
61
+ this.logInterval = null;
62
+ }
63
+
64
+ console.log(chalk.yellow('šŸ“Š Performance monitoring stopped'));
65
+ }
66
+
67
+ /**
68
+ * Setup process monitoring
69
+ */
70
+ setupProcessMonitoring() {
71
+ // Monitor memory usage
72
+ process.on('warning', (warning) => {
73
+ this.recordError('process_warning', warning.message, {
74
+ name: warning.name,
75
+ code: warning.code
76
+ });
77
+ });
78
+
79
+ // Monitor uncaught exceptions
80
+ process.on('uncaughtException', (error) => {
81
+ this.recordError('uncaught_exception', error.message, {
82
+ stack: error.stack
83
+ });
84
+ });
85
+
86
+ // Monitor unhandled rejections
87
+ process.on('unhandledRejection', (reason, promise) => {
88
+ this.recordError('unhandled_rejection', reason.toString(), {
89
+ promise: promise.toString()
90
+ });
91
+ });
92
+ }
93
+
94
+ /**
95
+ * Start timing an operation
96
+ * @param {string} name - Timer name
97
+ */
98
+ startTimer(name) {
99
+ this.timers[name] = {
100
+ start: process.hrtime.bigint(),
101
+ startTime: Date.now()
102
+ };
103
+ }
104
+
105
+ /**
106
+ * End timing an operation
107
+ * @param {string} name - Timer name
108
+ * @param {Object} metadata - Additional metadata
109
+ */
110
+ endTimer(name, metadata = {}) {
111
+ if (!this.timers[name]) {
112
+ console.warn(`Timer ${name} was not started`);
113
+ return 0;
114
+ }
115
+
116
+ const timer = this.timers[name];
117
+ const endTime = process.hrtime.bigint();
118
+ const duration = Number(endTime - timer.start) / 1000000; // Convert to milliseconds
119
+
120
+ this.recordMetric('performance', {
121
+ operation: name,
122
+ duration,
123
+ timestamp: Date.now(),
124
+ ...metadata
125
+ });
126
+
127
+ delete this.timers[name];
128
+ return duration;
129
+ }
130
+
131
+ /**
132
+ * Increment a counter
133
+ * @param {string} name - Counter name
134
+ * @param {number} value - Increment value
135
+ */
136
+ incrementCounter(name, value = 1) {
137
+ this.counters[name] = (this.counters[name] || 0) + value;
138
+ }
139
+
140
+ /**
141
+ * Record a metric
142
+ * @param {string} category - Metric category
143
+ * @param {Object} data - Metric data
144
+ */
145
+ recordMetric(category, data) {
146
+ if (!this.options.enabled) return;
147
+
148
+ if (!this.metrics[category]) {
149
+ this.metrics[category] = [];
150
+ }
151
+
152
+ this.metrics[category].push({
153
+ ...data,
154
+ timestamp: data.timestamp || Date.now()
155
+ });
156
+
157
+ // Keep array size manageable
158
+ if (this.metrics[category].length > 1000) {
159
+ this.metrics[category].shift();
160
+ }
161
+ }
162
+
163
+ /**
164
+ * Record an error
165
+ * @param {string} type - Error type
166
+ * @param {string} message - Error message
167
+ * @param {Object} metadata - Additional metadata
168
+ */
169
+ recordError(type, message, metadata = {}) {
170
+ this.recordMetric('errors', {
171
+ type,
172
+ message,
173
+ ...metadata
174
+ });
175
+
176
+ console.error(chalk.red(`āŒ Error recorded: ${type} - ${message}`));
177
+ }
178
+
179
+ /**
180
+ * Record API request metrics
181
+ * @param {string} endpoint - API endpoint
182
+ * @param {number} duration - Request duration
183
+ * @param {number} statusCode - HTTP status code
184
+ * @param {Object} metadata - Additional metadata
185
+ */
186
+ recordRequest(endpoint, duration, statusCode, metadata = {}) {
187
+ this.recordMetric('requests', {
188
+ endpoint,
189
+ duration,
190
+ statusCode,
191
+ success: statusCode >= 200 && statusCode < 400,
192
+ ...metadata
193
+ });
194
+
195
+ this.incrementCounter('total_requests');
196
+ if (statusCode >= 400) {
197
+ this.incrementCounter('error_requests');
198
+ }
199
+ }
200
+
201
+ /**
202
+ * Record cache metrics
203
+ * @param {string} operation - Cache operation (hit, miss, set, delete)
204
+ * @param {string} key - Cache key
205
+ * @param {number} duration - Operation duration
206
+ */
207
+ recordCache(operation, key, duration = 0) {
208
+ this.recordMetric('cache', {
209
+ operation,
210
+ key,
211
+ duration
212
+ });
213
+
214
+ this.incrementCounter(`cache_${operation}`);
215
+ }
216
+
217
+ /**
218
+ * Record WebSocket metrics
219
+ * @param {string} event - WebSocket event
220
+ * @param {Object} data - Event data
221
+ */
222
+ recordWebSocket(event, data = {}) {
223
+ this.recordMetric('websocket', {
224
+ event,
225
+ ...data
226
+ });
227
+
228
+ this.incrementCounter(`websocket_${event}`);
229
+ }
230
+
231
+ /**
232
+ * Collect system metrics
233
+ */
234
+ collectSystemMetrics() {
235
+ const memUsage = process.memoryUsage();
236
+ const cpuUsage = process.cpuUsage();
237
+
238
+ this.recordMetric('memory', {
239
+ rss: memUsage.rss,
240
+ heapUsed: memUsage.heapUsed,
241
+ heapTotal: memUsage.heapTotal,
242
+ external: memUsage.external,
243
+ arrayBuffers: memUsage.arrayBuffers
244
+ });
245
+
246
+ this.recordMetric('cpu', {
247
+ user: cpuUsage.user,
248
+ system: cpuUsage.system
249
+ });
250
+
251
+ // Check memory threshold
252
+ if (memUsage.heapUsed > this.options.memoryThreshold) {
253
+ this.recordError('memory_threshold',
254
+ `Memory usage exceeded threshold: ${Math.round(memUsage.heapUsed / 1024 / 1024)}MB`
255
+ );
256
+ }
257
+ }
258
+
259
+ /**
260
+ * Get performance statistics
261
+ * @param {number} timeframe - Timeframe in milliseconds
262
+ * @returns {Object} Performance statistics
263
+ */
264
+ getStats(timeframe = 300000) { // Default 5 minutes
265
+ const cutoff = Date.now() - timeframe;
266
+
267
+ const filterRecent = (metrics) =>
268
+ metrics.filter(metric => metric.timestamp > cutoff);
269
+
270
+ const recentRequests = filterRecent(this.metrics.requests || []);
271
+ const recentErrors = filterRecent(this.metrics.errors || []);
272
+ const recentMemory = filterRecent(this.metrics.memory || []);
273
+ const recentCache = filterRecent(this.metrics.cache || []);
274
+
275
+ return {
276
+ uptime: Date.now() - this.startTime,
277
+ requests: {
278
+ total: recentRequests.length,
279
+ successful: recentRequests.filter(r => r.success).length,
280
+ errors: recentRequests.filter(r => !r.success).length,
281
+ averageResponseTime: this.calculateAverage(recentRequests, 'duration'),
282
+ endpointStats: this.groupBy(recentRequests, 'endpoint')
283
+ },
284
+ errors: {
285
+ total: recentErrors.length,
286
+ byType: this.groupBy(recentErrors, 'type')
287
+ },
288
+ memory: {
289
+ current: recentMemory.length > 0 ? recentMemory[recentMemory.length - 1] : null,
290
+ average: this.calculateAverageMemory(recentMemory),
291
+ peak: this.calculatePeakMemory(recentMemory)
292
+ },
293
+ cache: {
294
+ operations: recentCache.length,
295
+ hitRate: this.calculateCacheHitRate(recentCache),
296
+ operationStats: this.groupBy(recentCache, 'operation')
297
+ },
298
+ counters: { ...this.counters },
299
+ activeTimers: Object.keys(this.timers).length
300
+ };
301
+ }
302
+
303
+ /**
304
+ * Calculate average value
305
+ * @param {Array} items - Array of items
306
+ * @param {string} field - Field to average
307
+ * @returns {number} Average value
308
+ */
309
+ calculateAverage(items, field) {
310
+ if (items.length === 0) return 0;
311
+ const sum = items.reduce((acc, item) => acc + (item[field] || 0), 0);
312
+ return Math.round(sum / items.length * 100) / 100;
313
+ }
314
+
315
+ /**
316
+ * Calculate average memory usage
317
+ * @param {Array} memoryMetrics - Memory metrics
318
+ * @returns {Object} Average memory usage
319
+ */
320
+ calculateAverageMemory(memoryMetrics) {
321
+ if (memoryMetrics.length === 0) return null;
322
+
323
+ const avgRss = this.calculateAverage(memoryMetrics, 'rss');
324
+ const avgHeapUsed = this.calculateAverage(memoryMetrics, 'heapUsed');
325
+ const avgHeapTotal = this.calculateAverage(memoryMetrics, 'heapTotal');
326
+
327
+ return { rss: avgRss, heapUsed: avgHeapUsed, heapTotal: avgHeapTotal };
328
+ }
329
+
330
+ /**
331
+ * Calculate peak memory usage
332
+ * @param {Array} memoryMetrics - Memory metrics
333
+ * @returns {Object} Peak memory usage
334
+ */
335
+ calculatePeakMemory(memoryMetrics) {
336
+ if (memoryMetrics.length === 0) return null;
337
+
338
+ const maxRss = Math.max(...memoryMetrics.map(m => m.rss));
339
+ const maxHeapUsed = Math.max(...memoryMetrics.map(m => m.heapUsed));
340
+
341
+ return { rss: maxRss, heapUsed: maxHeapUsed };
342
+ }
343
+
344
+ /**
345
+ * Calculate cache hit rate
346
+ * @param {Array} cacheMetrics - Cache metrics
347
+ * @returns {number} Cache hit rate percentage
348
+ */
349
+ calculateCacheHitRate(cacheMetrics) {
350
+ const hits = cacheMetrics.filter(m => m.operation === 'hit').length;
351
+ const misses = cacheMetrics.filter(m => m.operation === 'miss').length;
352
+ const total = hits + misses;
353
+
354
+ return total > 0 ? Math.round((hits / total) * 100 * 100) / 100 : 0;
355
+ }
356
+
357
+ /**
358
+ * Group items by field
359
+ * @param {Array} items - Items to group
360
+ * @param {string} field - Field to group by
361
+ * @returns {Object} Grouped items
362
+ */
363
+ groupBy(items, field) {
364
+ return items.reduce((acc, item) => {
365
+ const key = item[field] || 'unknown';
366
+ acc[key] = (acc[key] || 0) + 1;
367
+ return acc;
368
+ }, {});
369
+ }
370
+
371
+ /**
372
+ * Clean up old metrics
373
+ */
374
+ cleanupOldMetrics() {
375
+ const cutoff = Date.now() - this.options.metricsRetention;
376
+
377
+ Object.keys(this.metrics).forEach(category => {
378
+ this.metrics[category] = this.metrics[category].filter(
379
+ metric => metric.timestamp > cutoff
380
+ );
381
+ });
382
+ }
383
+
384
+ /**
385
+ * Log performance report
386
+ */
387
+ logPerformanceReport() {
388
+ const stats = this.getStats();
389
+
390
+ console.log(chalk.cyan('\nšŸ“Š Performance Report:'));
391
+ console.log(chalk.gray(`Uptime: ${Math.round(stats.uptime / 1000)}s`));
392
+
393
+ if (stats.requests.total > 0) {
394
+ console.log(chalk.green(`Requests: ${stats.requests.total} (${stats.requests.successful} success, ${stats.requests.errors} errors)`));
395
+ console.log(chalk.blue(`Avg Response Time: ${stats.requests.averageResponseTime}ms`));
396
+ }
397
+
398
+ if (stats.memory.current) {
399
+ const memMB = Math.round(stats.memory.current.heapUsed / 1024 / 1024);
400
+ console.log(chalk.yellow(`Memory: ${memMB}MB heap used`));
401
+ }
402
+
403
+ if (stats.cache.operations > 0) {
404
+ console.log(chalk.magenta(`Cache: ${stats.cache.hitRate}% hit rate (${stats.cache.operations} ops)`));
405
+ }
406
+
407
+ if (stats.errors.total > 0) {
408
+ console.log(chalk.red(`Errors: ${stats.errors.total} in last 5 minutes`));
409
+ }
410
+ }
411
+
412
+ /**
413
+ * Export metrics to file
414
+ * @param {string} filePath - Export file path
415
+ */
416
+ async exportMetrics(filePath) {
417
+ const stats = this.getStats(3600000); // Last hour
418
+ const exportData = {
419
+ timestamp: new Date().toISOString(),
420
+ stats,
421
+ rawMetrics: this.metrics
422
+ };
423
+
424
+ await fs.writeJson(filePath, exportData, { spaces: 2 });
425
+ console.log(chalk.green(`šŸ“Š Metrics exported to ${filePath}`));
426
+ }
427
+
428
+ /**
429
+ * Create performance middleware for Express
430
+ * @returns {Function} Express middleware
431
+ */
432
+ createExpressMiddleware() {
433
+ return (req, res, next) => {
434
+ const start = Date.now();
435
+
436
+ res.on('finish', () => {
437
+ const duration = Date.now() - start;
438
+ this.recordRequest(
439
+ `${req.method} ${req.route?.path || req.url}`,
440
+ duration,
441
+ res.statusCode,
442
+ {
443
+ method: req.method,
444
+ userAgent: req.get('User-Agent'),
445
+ ip: req.ip
446
+ }
447
+ );
448
+ });
449
+
450
+ next();
451
+ };
452
+ }
453
+ }
454
+
455
+ module.exports = PerformanceMonitor;