@dynamicu/chromedebug-mcp 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +344 -0
- package/LICENSE +21 -0
- package/README.md +250 -0
- package/chrome-extension/README.md +41 -0
- package/chrome-extension/background.js +3917 -0
- package/chrome-extension/chrome-session-manager.js +706 -0
- package/chrome-extension/content.css +181 -0
- package/chrome-extension/content.js +3022 -0
- package/chrome-extension/data-buffer.js +435 -0
- package/chrome-extension/dom-tracker.js +411 -0
- package/chrome-extension/extension-config.js +78 -0
- package/chrome-extension/firebase-client.js +278 -0
- package/chrome-extension/firebase-config.js +32 -0
- package/chrome-extension/firebase-config.module.js +22 -0
- package/chrome-extension/firebase-config.module.template.js +27 -0
- package/chrome-extension/firebase-config.template.js +36 -0
- package/chrome-extension/frame-capture.js +407 -0
- package/chrome-extension/icon128.png +1 -0
- package/chrome-extension/icon16.png +1 -0
- package/chrome-extension/icon48.png +1 -0
- package/chrome-extension/license-helper.js +181 -0
- package/chrome-extension/logger.js +23 -0
- package/chrome-extension/manifest.json +73 -0
- package/chrome-extension/network-tracker.js +510 -0
- package/chrome-extension/offscreen.html +10 -0
- package/chrome-extension/options.html +203 -0
- package/chrome-extension/options.js +282 -0
- package/chrome-extension/pako.min.js +2 -0
- package/chrome-extension/performance-monitor.js +533 -0
- package/chrome-extension/pii-redactor.js +405 -0
- package/chrome-extension/popup.html +532 -0
- package/chrome-extension/popup.js +2446 -0
- package/chrome-extension/upload-manager.js +323 -0
- package/chrome-extension/web-vitals.iife.js +1 -0
- package/config/api-keys.json +11 -0
- package/config/chrome-pilot-config.json +45 -0
- package/package.json +126 -0
- package/scripts/cleanup-processes.js +109 -0
- package/scripts/config-manager.js +280 -0
- package/scripts/generate-extension-config.js +53 -0
- package/scripts/setup-security.js +64 -0
- package/src/capture/architecture.js +426 -0
- package/src/capture/error-handling-tests.md +38 -0
- package/src/capture/error-handling-types.ts +360 -0
- package/src/capture/index.js +508 -0
- package/src/capture/interfaces.js +625 -0
- package/src/capture/memory-manager.js +713 -0
- package/src/capture/types.js +342 -0
- package/src/chrome-controller.js +2658 -0
- package/src/cli.js +19 -0
- package/src/config-loader.js +303 -0
- package/src/database.js +2178 -0
- package/src/firebase-license-manager.js +462 -0
- package/src/firebase-privacy-guard.js +397 -0
- package/src/http-server.js +1516 -0
- package/src/index-direct.js +157 -0
- package/src/index-modular.js +219 -0
- package/src/index-monolithic-backup.js +2230 -0
- package/src/index.js +305 -0
- package/src/legacy/chrome-controller-old.js +1406 -0
- package/src/legacy/index-express.js +625 -0
- package/src/legacy/index-old.js +977 -0
- package/src/legacy/routes.js +260 -0
- package/src/legacy/shared-storage.js +101 -0
- package/src/logger.js +10 -0
- package/src/mcp/handlers/chrome-tool-handler.js +306 -0
- package/src/mcp/handlers/element-tool-handler.js +51 -0
- package/src/mcp/handlers/frame-tool-handler.js +957 -0
- package/src/mcp/handlers/request-handler.js +104 -0
- package/src/mcp/handlers/workflow-tool-handler.js +636 -0
- package/src/mcp/server.js +68 -0
- package/src/mcp/tools/index.js +701 -0
- package/src/middleware/auth.js +371 -0
- package/src/middleware/security.js +267 -0
- package/src/port-discovery.js +258 -0
- package/src/routes/admin.js +182 -0
- package/src/services/browser-daemon.js +494 -0
- package/src/services/chrome-service.js +375 -0
- package/src/services/failover-manager.js +412 -0
- package/src/services/git-safety-service.js +675 -0
- package/src/services/heartbeat-manager.js +200 -0
- package/src/services/http-client.js +195 -0
- package/src/services/process-manager.js +318 -0
- package/src/services/process-tracker.js +574 -0
- package/src/services/profile-manager.js +449 -0
- package/src/services/project-manager.js +415 -0
- package/src/services/session-manager.js +497 -0
- package/src/services/session-registry.js +491 -0
- package/src/services/unified-session-manager.js +678 -0
- package/src/shared-storage-old.js +267 -0
- package/src/standalone-server.js +53 -0
- package/src/utils/extension-path.js +145 -0
- package/src/utils.js +187 -0
- package/src/validation/log-transformer.js +125 -0
- package/src/validation/schemas.js +391 -0
|
@@ -0,0 +1,713 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified Interaction Capture System - Memory Management & Performance
|
|
3
|
+
*
|
|
4
|
+
* This module provides memory management and performance optimization
|
|
5
|
+
* for the capture system. It handles:
|
|
6
|
+
*
|
|
7
|
+
* 1. Memory usage monitoring and limits
|
|
8
|
+
* 2. Automatic cleanup and garbage collection
|
|
9
|
+
* 3. Event buffering and streaming
|
|
10
|
+
* 4. Performance optimization strategies
|
|
11
|
+
* 5. Backpressure handling
|
|
12
|
+
*
|
|
13
|
+
* MEMORY STRATEGY:
|
|
14
|
+
* ================
|
|
15
|
+
* • Hot Path: Recent events in memory for instant access
|
|
16
|
+
* • Warm Path: Session events in database for quick queries
|
|
17
|
+
* • Cold Path: Archived events in file system for long-term storage
|
|
18
|
+
* • Size-based routing: Small events cached, large events streamed
|
|
19
|
+
*
|
|
20
|
+
* PERFORMANCE STRATEGY:
|
|
21
|
+
* =====================
|
|
22
|
+
* • Async pipelines with backpressure
|
|
23
|
+
* • Batch processing for efficiency
|
|
24
|
+
* • Circular buffers for high-frequency events
|
|
25
|
+
* • Lazy loading for large datasets
|
|
26
|
+
* • Compression for storage optimization
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { ICaptureMemoryManager } from './interfaces.js';
|
|
30
|
+
import { ARCHITECTURE_CONFIG } from './architecture.js';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Memory-efficient circular buffer for high-frequency events
|
|
34
|
+
*/
|
|
35
|
+
export class CircularEventBuffer {
|
|
36
|
+
constructor(maxSize = 1000) {
|
|
37
|
+
this.buffer = new Array(maxSize);
|
|
38
|
+
this.maxSize = maxSize;
|
|
39
|
+
this.head = 0;
|
|
40
|
+
this.tail = 0;
|
|
41
|
+
this.count = 0;
|
|
42
|
+
this.overflowCount = 0;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Add event to buffer
|
|
47
|
+
* @param {CaptureEvent} event - Event to add
|
|
48
|
+
* @returns {boolean} True if added, false if buffer full
|
|
49
|
+
*/
|
|
50
|
+
add(event) {
|
|
51
|
+
this.buffer[this.tail] = event;
|
|
52
|
+
this.tail = (this.tail + 1) % this.maxSize;
|
|
53
|
+
|
|
54
|
+
if (this.count < this.maxSize) {
|
|
55
|
+
this.count++;
|
|
56
|
+
return true;
|
|
57
|
+
} else {
|
|
58
|
+
// Buffer full, overwrite oldest
|
|
59
|
+
this.head = (this.head + 1) % this.maxSize;
|
|
60
|
+
this.overflowCount++;
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Get events from buffer
|
|
67
|
+
* @param {number} [limit] - Maximum events to return
|
|
68
|
+
* @returns {CaptureEvent[]} Events from buffer
|
|
69
|
+
*/
|
|
70
|
+
getEvents(limit = this.count) {
|
|
71
|
+
const events = [];
|
|
72
|
+
const actualLimit = Math.min(limit, this.count);
|
|
73
|
+
|
|
74
|
+
for (let i = 0; i < actualLimit; i++) {
|
|
75
|
+
const index = (this.head + i) % this.maxSize;
|
|
76
|
+
events.push(this.buffer[index]);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return events;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Remove events from buffer
|
|
84
|
+
* @param {number} count - Number of events to remove
|
|
85
|
+
*/
|
|
86
|
+
remove(count) {
|
|
87
|
+
const actualCount = Math.min(count, this.count);
|
|
88
|
+
this.head = (this.head + actualCount) % this.maxSize;
|
|
89
|
+
this.count -= actualCount;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Clear all events from buffer
|
|
94
|
+
*/
|
|
95
|
+
clear() {
|
|
96
|
+
this.head = 0;
|
|
97
|
+
this.tail = 0;
|
|
98
|
+
this.count = 0;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Get buffer statistics
|
|
103
|
+
* @returns {Object} Buffer stats
|
|
104
|
+
*/
|
|
105
|
+
getStats() {
|
|
106
|
+
return {
|
|
107
|
+
count: this.count,
|
|
108
|
+
maxSize: this.maxSize,
|
|
109
|
+
utilization: this.count / this.maxSize,
|
|
110
|
+
overflowCount: this.overflowCount,
|
|
111
|
+
memoryUsageBytes: this.estimateMemoryUsage()
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Estimate memory usage of buffer
|
|
117
|
+
* @returns {number} Estimated bytes
|
|
118
|
+
*/
|
|
119
|
+
estimateMemoryUsage() {
|
|
120
|
+
// Rough estimate: 1KB per event on average
|
|
121
|
+
return this.count * 1024;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Event batch processor for efficient database writes
|
|
127
|
+
*/
|
|
128
|
+
export class EventBatchProcessor {
|
|
129
|
+
constructor(storage, batchSize = 100, flushInterval = 5000) {
|
|
130
|
+
this.storage = storage;
|
|
131
|
+
this.batchSize = batchSize;
|
|
132
|
+
this.flushInterval = flushInterval;
|
|
133
|
+
this.batch = [];
|
|
134
|
+
this.flushTimer = null;
|
|
135
|
+
this.processing = false;
|
|
136
|
+
this.stats = {
|
|
137
|
+
batchesProcessed: 0,
|
|
138
|
+
eventsProcessed: 0,
|
|
139
|
+
errors: 0,
|
|
140
|
+
avgBatchSize: 0
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Add event to batch
|
|
146
|
+
* @param {CaptureEvent} event - Event to add
|
|
147
|
+
* @returns {Promise<void>}
|
|
148
|
+
*/
|
|
149
|
+
async addEvent(event) {
|
|
150
|
+
this.batch.push(event);
|
|
151
|
+
|
|
152
|
+
// Flush if batch is full
|
|
153
|
+
if (this.batch.length >= this.batchSize) {
|
|
154
|
+
await this.flush();
|
|
155
|
+
} else if (!this.flushTimer) {
|
|
156
|
+
// Start flush timer if not already running
|
|
157
|
+
this.flushTimer = setTimeout(() => this.flush(), this.flushInterval);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Flush current batch to storage
|
|
163
|
+
* @returns {Promise<void>}
|
|
164
|
+
*/
|
|
165
|
+
async flush() {
|
|
166
|
+
if (this.processing || this.batch.length === 0) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
this.processing = true;
|
|
171
|
+
|
|
172
|
+
if (this.flushTimer) {
|
|
173
|
+
clearTimeout(this.flushTimer);
|
|
174
|
+
this.flushTimer = null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const currentBatch = this.batch.splice(0);
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
await this.storage.storeEvents(currentBatch);
|
|
181
|
+
|
|
182
|
+
// Update statistics
|
|
183
|
+
this.stats.batchesProcessed++;
|
|
184
|
+
this.stats.eventsProcessed += currentBatch.length;
|
|
185
|
+
this.stats.avgBatchSize = this.stats.eventsProcessed / this.stats.batchesProcessed;
|
|
186
|
+
|
|
187
|
+
} catch (error) {
|
|
188
|
+
this.stats.errors++;
|
|
189
|
+
console.error('Error flushing event batch:', error);
|
|
190
|
+
|
|
191
|
+
// Re-add events to batch for retry
|
|
192
|
+
this.batch.unshift(...currentBatch);
|
|
193
|
+
} finally {
|
|
194
|
+
this.processing = false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Stop processor and flush remaining events
|
|
200
|
+
* @returns {Promise<void>}
|
|
201
|
+
*/
|
|
202
|
+
async stop() {
|
|
203
|
+
if (this.flushTimer) {
|
|
204
|
+
clearTimeout(this.flushTimer);
|
|
205
|
+
this.flushTimer = null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
await this.flush();
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Get processor statistics
|
|
213
|
+
* @returns {Object} Processor stats
|
|
214
|
+
*/
|
|
215
|
+
getStats() {
|
|
216
|
+
return {
|
|
217
|
+
...this.stats,
|
|
218
|
+
pendingEvents: this.batch.length,
|
|
219
|
+
processing: this.processing
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Memory usage monitor
|
|
226
|
+
*/
|
|
227
|
+
export class MemoryMonitor {
|
|
228
|
+
constructor() {
|
|
229
|
+
this.components = new Map();
|
|
230
|
+
this.limits = {
|
|
231
|
+
maxMemoryMB: ARCHITECTURE_CONFIG.MEMORY.MAX_MEMORY_MB,
|
|
232
|
+
warningThresholdMB: ARCHITECTURE_CONFIG.MEMORY.WARNING_THRESHOLD_MB,
|
|
233
|
+
criticalThresholdMB: ARCHITECTURE_CONFIG.MEMORY.CRITICAL_THRESHOLD_MB
|
|
234
|
+
};
|
|
235
|
+
this.callbacks = {
|
|
236
|
+
warning: [],
|
|
237
|
+
critical: [],
|
|
238
|
+
cleanup: []
|
|
239
|
+
};
|
|
240
|
+
this.monitoring = false;
|
|
241
|
+
this.monitorInterval = null;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Register component for monitoring
|
|
246
|
+
* @param {string} componentId - Component identifier
|
|
247
|
+
* @param {Object} component - Component with getMemoryUsage method
|
|
248
|
+
*/
|
|
249
|
+
registerComponent(componentId, component) {
|
|
250
|
+
this.components.set(componentId, component);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Unregister component
|
|
255
|
+
* @param {string} componentId - Component identifier
|
|
256
|
+
*/
|
|
257
|
+
unregisterComponent(componentId) {
|
|
258
|
+
this.components.delete(componentId);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Start memory monitoring
|
|
263
|
+
* @param {number} [intervalMs=30000] - Monitoring interval
|
|
264
|
+
*/
|
|
265
|
+
startMonitoring(intervalMs = 30000) {
|
|
266
|
+
if (this.monitoring) return;
|
|
267
|
+
|
|
268
|
+
this.monitoring = true;
|
|
269
|
+
this.monitorInterval = setInterval(() => {
|
|
270
|
+
this.checkMemoryUsage();
|
|
271
|
+
}, intervalMs);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Stop memory monitoring
|
|
276
|
+
*/
|
|
277
|
+
stopMonitoring() {
|
|
278
|
+
if (!this.monitoring) return;
|
|
279
|
+
|
|
280
|
+
this.monitoring = false;
|
|
281
|
+
if (this.monitorInterval) {
|
|
282
|
+
clearInterval(this.monitorInterval);
|
|
283
|
+
this.monitorInterval = null;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Check current memory usage
|
|
289
|
+
* @returns {Promise<Object>} Memory usage statistics
|
|
290
|
+
*/
|
|
291
|
+
async checkMemoryUsage() {
|
|
292
|
+
const usage = await this.getMemoryUsage();
|
|
293
|
+
const totalMB = usage.totalBytes / (1024 * 1024);
|
|
294
|
+
|
|
295
|
+
// Trigger callbacks based on usage
|
|
296
|
+
if (totalMB >= this.limits.criticalThresholdMB) {
|
|
297
|
+
this.triggerCallbacks('critical', usage);
|
|
298
|
+
} else if (totalMB >= this.limits.warningThresholdMB) {
|
|
299
|
+
this.triggerCallbacks('warning', usage);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return usage;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Get comprehensive memory usage
|
|
307
|
+
* @returns {Promise<Object>} Memory usage data
|
|
308
|
+
*/
|
|
309
|
+
async getMemoryUsage() {
|
|
310
|
+
const componentUsage = new Map();
|
|
311
|
+
let totalBytes = 0;
|
|
312
|
+
|
|
313
|
+
// Get usage from registered components
|
|
314
|
+
for (const [id, component] of this.components) {
|
|
315
|
+
try {
|
|
316
|
+
let usage = 0;
|
|
317
|
+
if (typeof component.getMemoryUsage === 'function') {
|
|
318
|
+
usage = await component.getMemoryUsage();
|
|
319
|
+
} else if (typeof component.estimateMemoryUsage === 'function') {
|
|
320
|
+
usage = component.estimateMemoryUsage();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
componentUsage.set(id, usage);
|
|
324
|
+
totalBytes += usage;
|
|
325
|
+
} catch (error) {
|
|
326
|
+
console.warn(`Error getting memory usage for component ${id}:`, error);
|
|
327
|
+
componentUsage.set(id, 0);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Get Node.js process memory usage
|
|
332
|
+
const processUsage = process.memoryUsage();
|
|
333
|
+
|
|
334
|
+
return {
|
|
335
|
+
totalBytes,
|
|
336
|
+
totalMB: totalBytes / (1024 * 1024),
|
|
337
|
+
componentUsage: Object.fromEntries(componentUsage),
|
|
338
|
+
processUsage,
|
|
339
|
+
limits: this.limits,
|
|
340
|
+
timestamp: Date.now()
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Register callback for memory events
|
|
346
|
+
* @param {string} event - Event type (warning, critical, cleanup)
|
|
347
|
+
* @param {Function} callback - Callback function
|
|
348
|
+
*/
|
|
349
|
+
onMemoryEvent(event, callback) {
|
|
350
|
+
if (!this.callbacks[event]) {
|
|
351
|
+
this.callbacks[event] = [];
|
|
352
|
+
}
|
|
353
|
+
this.callbacks[event].push(callback);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Trigger callbacks for event type
|
|
358
|
+
* @param {string} event - Event type
|
|
359
|
+
* @param {Object} data - Event data
|
|
360
|
+
*/
|
|
361
|
+
triggerCallbacks(event, data) {
|
|
362
|
+
const callbacks = this.callbacks[event] || [];
|
|
363
|
+
for (const callback of callbacks) {
|
|
364
|
+
try {
|
|
365
|
+
callback(data);
|
|
366
|
+
} catch (error) {
|
|
367
|
+
console.error(`Error in memory event callback for ${event}:`, error);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Request immediate cleanup
|
|
374
|
+
* @param {string} [priority='normal'] - Cleanup priority
|
|
375
|
+
* @returns {Promise<Object>} Cleanup results
|
|
376
|
+
*/
|
|
377
|
+
async requestCleanup(priority = 'normal') {
|
|
378
|
+
const results = {
|
|
379
|
+
beforeUsage: await this.getMemoryUsage(),
|
|
380
|
+
componentsProcessed: 0,
|
|
381
|
+
bytesFreed: 0,
|
|
382
|
+
errors: []
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
// Trigger cleanup callbacks
|
|
386
|
+
this.triggerCallbacks('cleanup', { priority, results });
|
|
387
|
+
|
|
388
|
+
// Call cleanup on registered components
|
|
389
|
+
for (const [id, component] of this.components) {
|
|
390
|
+
try {
|
|
391
|
+
if (typeof component.cleanup === 'function') {
|
|
392
|
+
const beforeBytes = component.estimateMemoryUsage?.() || 0;
|
|
393
|
+
await component.cleanup(priority);
|
|
394
|
+
const afterBytes = component.estimateMemoryUsage?.() || 0;
|
|
395
|
+
|
|
396
|
+
results.componentsProcessed++;
|
|
397
|
+
results.bytesFreed += Math.max(0, beforeBytes - afterBytes);
|
|
398
|
+
}
|
|
399
|
+
} catch (error) {
|
|
400
|
+
console.error(`Error cleaning up component ${id}:`, error);
|
|
401
|
+
results.errors.push({ component: id, error: error.message });
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
results.afterUsage = await this.getMemoryUsage();
|
|
406
|
+
|
|
407
|
+
return results;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Backpressure manager for flow control
|
|
413
|
+
*/
|
|
414
|
+
export class BackpressureManager {
|
|
415
|
+
constructor() {
|
|
416
|
+
this.thresholds = {
|
|
417
|
+
warning: 0.7, // 70% capacity
|
|
418
|
+
critical: 0.9, // 90% capacity
|
|
419
|
+
max: 1.0 // 100% capacity
|
|
420
|
+
};
|
|
421
|
+
this.components = new Map();
|
|
422
|
+
this.state = 'normal'; // normal, warning, critical, blocked
|
|
423
|
+
this.callbacks = new Map();
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Register component for backpressure monitoring
|
|
428
|
+
* @param {string} componentId - Component identifier
|
|
429
|
+
* @param {Object} component - Component with capacity info
|
|
430
|
+
*/
|
|
431
|
+
registerComponent(componentId, component) {
|
|
432
|
+
this.components.set(componentId, {
|
|
433
|
+
component,
|
|
434
|
+
capacity: component.getCapacity?.() || 1.0,
|
|
435
|
+
utilization: 0
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Update component utilization
|
|
441
|
+
* @param {string} componentId - Component identifier
|
|
442
|
+
* @param {number} utilization - Current utilization (0-1)
|
|
443
|
+
*/
|
|
444
|
+
updateUtilization(componentId, utilization) {
|
|
445
|
+
const info = this.components.get(componentId);
|
|
446
|
+
if (info) {
|
|
447
|
+
info.utilization = utilization;
|
|
448
|
+
this.checkBackpressure();
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Check if backpressure should be applied
|
|
454
|
+
*/
|
|
455
|
+
checkBackpressure() {
|
|
456
|
+
let maxUtilization = 0;
|
|
457
|
+
|
|
458
|
+
for (const [id, info] of this.components) {
|
|
459
|
+
maxUtilization = Math.max(maxUtilization, info.utilization);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const newState = this.calculateState(maxUtilization);
|
|
463
|
+
|
|
464
|
+
if (newState !== this.state) {
|
|
465
|
+
const oldState = this.state;
|
|
466
|
+
this.state = newState;
|
|
467
|
+
this.notifyStateChange(oldState, newState, maxUtilization);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Calculate backpressure state
|
|
473
|
+
* @param {number} utilization - Maximum utilization
|
|
474
|
+
* @returns {string} New state
|
|
475
|
+
*/
|
|
476
|
+
calculateState(utilization) {
|
|
477
|
+
if (utilization >= this.thresholds.max) {
|
|
478
|
+
return 'blocked';
|
|
479
|
+
} else if (utilization >= this.thresholds.critical) {
|
|
480
|
+
return 'critical';
|
|
481
|
+
} else if (utilization >= this.thresholds.warning) {
|
|
482
|
+
return 'warning';
|
|
483
|
+
} else {
|
|
484
|
+
return 'normal';
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Notify components of state change
|
|
490
|
+
* @param {string} oldState - Previous state
|
|
491
|
+
* @param {string} newState - New state
|
|
492
|
+
* @param {number} utilization - Current utilization
|
|
493
|
+
*/
|
|
494
|
+
notifyStateChange(oldState, newState, utilization) {
|
|
495
|
+
const callbacks = this.callbacks.get(newState) || [];
|
|
496
|
+
for (const callback of callbacks) {
|
|
497
|
+
try {
|
|
498
|
+
callback({ oldState, newState, utilization });
|
|
499
|
+
} catch (error) {
|
|
500
|
+
console.error('Error in backpressure callback:', error);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Register callback for state changes
|
|
507
|
+
* @param {string} state - State to listen for
|
|
508
|
+
* @param {Function} callback - Callback function
|
|
509
|
+
*/
|
|
510
|
+
onStateChange(state, callback) {
|
|
511
|
+
if (!this.callbacks.has(state)) {
|
|
512
|
+
this.callbacks.set(state, []);
|
|
513
|
+
}
|
|
514
|
+
this.callbacks.get(state).push(callback);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/**
|
|
518
|
+
* Check if system should throttle input
|
|
519
|
+
* @returns {boolean} True if should throttle
|
|
520
|
+
*/
|
|
521
|
+
shouldThrottle() {
|
|
522
|
+
return this.state === 'critical' || this.state === 'blocked';
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
/**
|
|
526
|
+
* Get suggested delay for throttling
|
|
527
|
+
* @returns {number} Delay in milliseconds
|
|
528
|
+
*/
|
|
529
|
+
getThrottleDelay() {
|
|
530
|
+
switch (this.state) {
|
|
531
|
+
case 'warning': return 10;
|
|
532
|
+
case 'critical': return 100;
|
|
533
|
+
case 'blocked': return 1000;
|
|
534
|
+
default: return 0;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Main memory manager implementation
|
|
541
|
+
*/
|
|
542
|
+
export class CaptureMemoryManager extends ICaptureMemoryManager {
|
|
543
|
+
constructor() {
|
|
544
|
+
super();
|
|
545
|
+
this.monitor = new MemoryMonitor();
|
|
546
|
+
this.backpressure = new BackpressureManager();
|
|
547
|
+
this.buffers = new Map();
|
|
548
|
+
this.processors = new Map();
|
|
549
|
+
this.initialized = false;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Initialize the memory manager
|
|
554
|
+
* @param {Object} config - Memory management configuration
|
|
555
|
+
* @returns {Promise<void>}
|
|
556
|
+
*/
|
|
557
|
+
async initialize(config = {}) {
|
|
558
|
+
if (this.initialized) return;
|
|
559
|
+
|
|
560
|
+
// Apply configuration
|
|
561
|
+
Object.assign(this.monitor.limits, config.limits || {});
|
|
562
|
+
Object.assign(this.backpressure.thresholds, config.thresholds || {});
|
|
563
|
+
|
|
564
|
+
// Set up memory monitoring
|
|
565
|
+
this.monitor.onMemoryEvent('critical', async (usage) => {
|
|
566
|
+
console.warn('Critical memory usage detected:', usage.totalMB, 'MB');
|
|
567
|
+
await this.requestCleanup('aggressive');
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
this.monitor.onMemoryEvent('warning', (usage) => {
|
|
571
|
+
console.info('High memory usage detected:', usage.totalMB, 'MB');
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
// Set up backpressure monitoring
|
|
575
|
+
this.backpressure.onStateChange('critical', ({ utilization }) => {
|
|
576
|
+
console.warn('Critical backpressure detected, utilization:', utilization);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
this.backpressure.onStateChange('blocked', ({ utilization }) => {
|
|
580
|
+
console.error('System blocked due to backpressure, utilization:', utilization);
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
// Start monitoring
|
|
584
|
+
this.monitor.startMonitoring();
|
|
585
|
+
|
|
586
|
+
this.initialized = true;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* Check current memory usage
|
|
591
|
+
* @returns {Promise<Object>} Memory usage statistics
|
|
592
|
+
*/
|
|
593
|
+
async getMemoryUsage() {
|
|
594
|
+
return await this.monitor.getMemoryUsage();
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
/**
|
|
598
|
+
* Request memory cleanup
|
|
599
|
+
* @param {string} [priority='normal'] - Cleanup priority
|
|
600
|
+
* @returns {Promise<Object>} Cleanup results
|
|
601
|
+
*/
|
|
602
|
+
async requestCleanup(priority = 'normal') {
|
|
603
|
+
return await this.monitor.requestCleanup(priority);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Set memory limits
|
|
608
|
+
* @param {Object} limits - Memory limits configuration
|
|
609
|
+
* @returns {Promise<void>}
|
|
610
|
+
*/
|
|
611
|
+
async setLimits(limits) {
|
|
612
|
+
Object.assign(this.monitor.limits, limits);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/**
|
|
616
|
+
* Register component for memory monitoring
|
|
617
|
+
* @param {string} componentId - Component identifier
|
|
618
|
+
* @param {Object} component - Component to monitor
|
|
619
|
+
* @returns {Promise<void>}
|
|
620
|
+
*/
|
|
621
|
+
async registerComponent(componentId, component) {
|
|
622
|
+
this.monitor.registerComponent(componentId, component);
|
|
623
|
+
|
|
624
|
+
if (component.getCapacity) {
|
|
625
|
+
this.backpressure.registerComponent(componentId, component);
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
/**
|
|
630
|
+
* Create optimized event buffer
|
|
631
|
+
* @param {string} bufferId - Buffer identifier
|
|
632
|
+
* @param {Object} options - Buffer options
|
|
633
|
+
* @returns {CircularEventBuffer} Created buffer
|
|
634
|
+
*/
|
|
635
|
+
createBuffer(bufferId, options = {}) {
|
|
636
|
+
const buffer = new CircularEventBuffer(options.maxSize);
|
|
637
|
+
this.buffers.set(bufferId, buffer);
|
|
638
|
+
this.monitor.registerComponent(`buffer_${bufferId}`, buffer);
|
|
639
|
+
return buffer;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Create batch processor
|
|
644
|
+
* @param {string} processorId - Processor identifier
|
|
645
|
+
* @param {Object} storage - Storage backend
|
|
646
|
+
* @param {Object} options - Processor options
|
|
647
|
+
* @returns {EventBatchProcessor} Created processor
|
|
648
|
+
*/
|
|
649
|
+
createBatchProcessor(processorId, storage, options = {}) {
|
|
650
|
+
const processor = new EventBatchProcessor(
|
|
651
|
+
storage,
|
|
652
|
+
options.batchSize,
|
|
653
|
+
options.flushInterval
|
|
654
|
+
);
|
|
655
|
+
this.processors.set(processorId, processor);
|
|
656
|
+
this.monitor.registerComponent(`processor_${processorId}`, processor);
|
|
657
|
+
return processor;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
/**
|
|
661
|
+
* Get system performance metrics
|
|
662
|
+
* @returns {Promise<Object>} Performance metrics
|
|
663
|
+
*/
|
|
664
|
+
async getPerformanceMetrics() {
|
|
665
|
+
const memoryUsage = await this.getMemoryUsage();
|
|
666
|
+
const bufferStats = {};
|
|
667
|
+
const processorStats = {};
|
|
668
|
+
|
|
669
|
+
for (const [id, buffer] of this.buffers) {
|
|
670
|
+
bufferStats[id] = buffer.getStats();
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
for (const [id, processor] of this.processors) {
|
|
674
|
+
processorStats[id] = processor.getStats();
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
return {
|
|
678
|
+
memory: memoryUsage,
|
|
679
|
+
backpressure: {
|
|
680
|
+
state: this.backpressure.state,
|
|
681
|
+
shouldThrottle: this.backpressure.shouldThrottle(),
|
|
682
|
+
throttleDelay: this.backpressure.getThrottleDelay()
|
|
683
|
+
},
|
|
684
|
+
buffers: bufferStats,
|
|
685
|
+
processors: processorStats,
|
|
686
|
+
timestamp: Date.now()
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
/**
|
|
691
|
+
* Shutdown memory manager
|
|
692
|
+
* @returns {Promise<void>}
|
|
693
|
+
*/
|
|
694
|
+
async shutdown() {
|
|
695
|
+
this.monitor.stopMonitoring();
|
|
696
|
+
|
|
697
|
+
// Stop all processors
|
|
698
|
+
const stopPromises = [];
|
|
699
|
+
for (const processor of this.processors.values()) {
|
|
700
|
+
stopPromises.push(processor.stop());
|
|
701
|
+
}
|
|
702
|
+
await Promise.all(stopPromises);
|
|
703
|
+
|
|
704
|
+
// Clear all buffers
|
|
705
|
+
for (const buffer of this.buffers.values()) {
|
|
706
|
+
buffer.clear();
|
|
707
|
+
}
|
|
708
|
+
|
|
709
|
+
this.initialized = false;
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
export default CaptureMemoryManager;
|