@sc4rfurryx/proteusjs 1.0.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/API.md +438 -0
- package/FEATURES.md +286 -0
- package/LICENSE +21 -0
- package/README.md +645 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/proteus.cjs.js +16014 -0
- package/dist/proteus.cjs.js.map +1 -0
- package/dist/proteus.d.ts +3018 -0
- package/dist/proteus.esm.js +16005 -0
- package/dist/proteus.esm.js.map +1 -0
- package/dist/proteus.esm.min.js +8 -0
- package/dist/proteus.esm.min.js.map +1 -0
- package/dist/proteus.js +16020 -0
- package/dist/proteus.js.map +1 -0
- package/dist/proteus.min.js +8 -0
- package/dist/proteus.min.js.map +1 -0
- package/package.json +98 -0
- package/src/__tests__/mvp-integration.test.ts +518 -0
- package/src/accessibility/AccessibilityEngine.ts +2106 -0
- package/src/accessibility/ScreenReaderSupport.ts +444 -0
- package/src/accessibility/__tests__/ScreenReaderSupport.test.ts +435 -0
- package/src/animations/FLIPAnimationSystem.ts +491 -0
- package/src/compatibility/BrowserCompatibility.ts +1076 -0
- package/src/containers/BreakpointSystem.ts +347 -0
- package/src/containers/ContainerBreakpoints.ts +726 -0
- package/src/containers/ContainerManager.ts +370 -0
- package/src/containers/ContainerUnits.ts +336 -0
- package/src/containers/ContextIsolation.ts +394 -0
- package/src/containers/ElementQueries.ts +411 -0
- package/src/containers/SmartContainer.ts +536 -0
- package/src/containers/SmartContainers.ts +376 -0
- package/src/containers/__tests__/ContainerBreakpoints.test.ts +411 -0
- package/src/containers/__tests__/SmartContainers.test.ts +281 -0
- package/src/content/ResponsiveImages.ts +570 -0
- package/src/core/EventSystem.ts +147 -0
- package/src/core/MemoryManager.ts +321 -0
- package/src/core/PerformanceMonitor.ts +238 -0
- package/src/core/PluginSystem.ts +275 -0
- package/src/core/ProteusJS.test.ts +164 -0
- package/src/core/ProteusJS.ts +962 -0
- package/src/developer/PerformanceProfiler.ts +567 -0
- package/src/developer/VisualDebuggingTools.ts +656 -0
- package/src/developer/ZeroConfigSystem.ts +593 -0
- package/src/index.ts +35 -0
- package/src/integration.test.ts +227 -0
- package/src/layout/AdaptiveGrid.ts +429 -0
- package/src/layout/ContentReordering.ts +532 -0
- package/src/layout/FlexboxEnhancer.ts +406 -0
- package/src/layout/FlowLayout.ts +545 -0
- package/src/layout/SpacingSystem.ts +512 -0
- package/src/observers/IntersectionObserverPolyfill.ts +289 -0
- package/src/observers/ObserverManager.ts +299 -0
- package/src/observers/ResizeObserverPolyfill.ts +179 -0
- package/src/performance/BatchDOMOperations.ts +519 -0
- package/src/performance/CSSOptimizationEngine.ts +646 -0
- package/src/performance/CacheOptimizationSystem.ts +601 -0
- package/src/performance/EfficientEventHandler.ts +740 -0
- package/src/performance/LazyEvaluationSystem.ts +532 -0
- package/src/performance/MemoryManagementSystem.ts +497 -0
- package/src/performance/PerformanceMonitor.ts +931 -0
- package/src/performance/__tests__/BatchDOMOperations.test.ts +309 -0
- package/src/performance/__tests__/EfficientEventHandler.test.ts +268 -0
- package/src/performance/__tests__/PerformanceMonitor.test.ts +422 -0
- package/src/polyfills/BrowserPolyfills.ts +586 -0
- package/src/polyfills/__tests__/BrowserPolyfills.test.ts +328 -0
- package/src/test/setup.ts +115 -0
- package/src/theming/SmartThemeSystem.ts +591 -0
- package/src/types/index.ts +134 -0
- package/src/typography/ClampScaling.ts +356 -0
- package/src/typography/FluidTypography.ts +759 -0
- package/src/typography/LineHeightOptimization.ts +430 -0
- package/src/typography/LineHeightOptimizer.ts +326 -0
- package/src/typography/TextFitting.ts +355 -0
- package/src/typography/TypographicScale.ts +428 -0
- package/src/typography/VerticalRhythm.ts +369 -0
- package/src/typography/__tests__/FluidTypography.test.ts +432 -0
- package/src/typography/__tests__/LineHeightOptimization.test.ts +436 -0
- package/src/utils/Logger.ts +173 -0
- package/src/utils/debounce.ts +259 -0
- package/src/utils/performance.ts +371 -0
- package/src/utils/support.ts +106 -0
- package/src/utils/version.ts +24 -0
@@ -0,0 +1,321 @@
|
|
1
|
+
/**
|
2
|
+
* Memory Manager for ProteusJS
|
3
|
+
* Prevents memory leaks and manages resource cleanup
|
4
|
+
*/
|
5
|
+
|
6
|
+
export interface ManagedResource {
|
7
|
+
id: string;
|
8
|
+
type: 'observer' | 'listener' | 'timer' | 'animation' | 'cache';
|
9
|
+
cleanup: () => void;
|
10
|
+
element?: Element;
|
11
|
+
timestamp: number;
|
12
|
+
}
|
13
|
+
|
14
|
+
export class MemoryManager {
|
15
|
+
private resources: Map<string, ManagedResource> = new Map();
|
16
|
+
private elementResources: Map<Element, Set<string>> = new Map();
|
17
|
+
private mutationObserver: MutationObserver | null = null;
|
18
|
+
private cleanupInterval: number | null = null;
|
19
|
+
private isMonitoring: boolean = false;
|
20
|
+
|
21
|
+
constructor() {
|
22
|
+
this.setupDOMObserver();
|
23
|
+
this.startPeriodicCleanup();
|
24
|
+
}
|
25
|
+
|
26
|
+
/**
|
27
|
+
* Register a resource for automatic cleanup
|
28
|
+
*/
|
29
|
+
public register(resource: Omit<ManagedResource, 'timestamp'>): string {
|
30
|
+
const fullResource: ManagedResource = {
|
31
|
+
...resource,
|
32
|
+
timestamp: Date.now()
|
33
|
+
};
|
34
|
+
|
35
|
+
this.resources.set(resource.id, fullResource);
|
36
|
+
|
37
|
+
// Track element-specific resources
|
38
|
+
if (resource.element) {
|
39
|
+
if (!this.elementResources.has(resource.element)) {
|
40
|
+
this.elementResources.set(resource.element, new Set());
|
41
|
+
}
|
42
|
+
this.elementResources.get(resource.element)!.add(resource.id);
|
43
|
+
}
|
44
|
+
|
45
|
+
return resource.id;
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Unregister and cleanup a resource
|
50
|
+
*/
|
51
|
+
public unregister(resourceId: string): boolean {
|
52
|
+
const resource = this.resources.get(resourceId);
|
53
|
+
if (!resource) return false;
|
54
|
+
|
55
|
+
try {
|
56
|
+
resource.cleanup();
|
57
|
+
} catch (error) {
|
58
|
+
console.error(`ProteusJS: Error cleaning up resource ${resourceId}:`, error);
|
59
|
+
}
|
60
|
+
|
61
|
+
this.resources.delete(resourceId);
|
62
|
+
|
63
|
+
// Remove from element tracking
|
64
|
+
if (resource.element) {
|
65
|
+
const elementResources = this.elementResources.get(resource.element);
|
66
|
+
if (elementResources) {
|
67
|
+
elementResources.delete(resourceId);
|
68
|
+
if (elementResources.size === 0) {
|
69
|
+
this.elementResources.delete(resource.element);
|
70
|
+
}
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
return true;
|
75
|
+
}
|
76
|
+
|
77
|
+
/**
|
78
|
+
* Cleanup all resources associated with an element
|
79
|
+
*/
|
80
|
+
public cleanupElement(element: Element): number {
|
81
|
+
const resourceIds = this.elementResources.get(element);
|
82
|
+
if (!resourceIds) return 0;
|
83
|
+
|
84
|
+
let cleanedCount = 0;
|
85
|
+
resourceIds.forEach(resourceId => {
|
86
|
+
if (this.unregister(resourceId)) {
|
87
|
+
cleanedCount++;
|
88
|
+
}
|
89
|
+
});
|
90
|
+
|
91
|
+
return cleanedCount;
|
92
|
+
}
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Cleanup resources by type
|
96
|
+
*/
|
97
|
+
public cleanupByType(type: ManagedResource['type']): number {
|
98
|
+
let cleanedCount = 0;
|
99
|
+
const toCleanup: string[] = [];
|
100
|
+
|
101
|
+
this.resources.forEach((resource, id) => {
|
102
|
+
if (resource.type === type) {
|
103
|
+
toCleanup.push(id);
|
104
|
+
}
|
105
|
+
});
|
106
|
+
|
107
|
+
toCleanup.forEach(id => {
|
108
|
+
if (this.unregister(id)) {
|
109
|
+
cleanedCount++;
|
110
|
+
}
|
111
|
+
});
|
112
|
+
|
113
|
+
return cleanedCount;
|
114
|
+
}
|
115
|
+
|
116
|
+
/**
|
117
|
+
* Cleanup old resources based on age
|
118
|
+
*/
|
119
|
+
public cleanupOldResources(maxAge: number = 300000): number { // 5 minutes default
|
120
|
+
let cleanedCount = 0;
|
121
|
+
const now = Date.now();
|
122
|
+
const toCleanup: string[] = [];
|
123
|
+
|
124
|
+
this.resources.forEach((resource, id) => {
|
125
|
+
if (now - resource.timestamp > maxAge) {
|
126
|
+
toCleanup.push(id);
|
127
|
+
}
|
128
|
+
});
|
129
|
+
|
130
|
+
toCleanup.forEach(id => {
|
131
|
+
if (this.unregister(id)) {
|
132
|
+
cleanedCount++;
|
133
|
+
}
|
134
|
+
});
|
135
|
+
|
136
|
+
return cleanedCount;
|
137
|
+
}
|
138
|
+
|
139
|
+
/**
|
140
|
+
* Force garbage collection if available
|
141
|
+
*/
|
142
|
+
public forceGarbageCollection(): void {
|
143
|
+
if ('gc' in window && typeof (window as any).gc === 'function') {
|
144
|
+
try {
|
145
|
+
(window as any).gc();
|
146
|
+
} catch (error) {
|
147
|
+
// Ignore errors - gc might not be available
|
148
|
+
}
|
149
|
+
}
|
150
|
+
}
|
151
|
+
|
152
|
+
/**
|
153
|
+
* Get memory usage information
|
154
|
+
*/
|
155
|
+
public getMemoryInfo(): object {
|
156
|
+
const info: any = {
|
157
|
+
managedResources: this.resources.size,
|
158
|
+
trackedElements: this.elementResources.size,
|
159
|
+
resourcesByType: {} as Record<string, number>
|
160
|
+
};
|
161
|
+
|
162
|
+
// Count resources by type
|
163
|
+
this.resources.forEach(resource => {
|
164
|
+
info.resourcesByType[resource.type] = (info.resourcesByType[resource.type] || 0) + 1;
|
165
|
+
});
|
166
|
+
|
167
|
+
// Add browser memory info if available
|
168
|
+
if ('memory' in performance) {
|
169
|
+
const memory = (performance as any).memory;
|
170
|
+
info.browserMemory = {
|
171
|
+
usedJSHeapSize: memory.usedJSHeapSize,
|
172
|
+
totalJSHeapSize: memory.totalJSHeapSize,
|
173
|
+
jsHeapSizeLimit: memory.jsHeapSizeLimit
|
174
|
+
};
|
175
|
+
}
|
176
|
+
|
177
|
+
return info;
|
178
|
+
}
|
179
|
+
|
180
|
+
/**
|
181
|
+
* Check for potential memory leaks
|
182
|
+
*/
|
183
|
+
public detectLeaks(): string[] {
|
184
|
+
const warnings: string[] = [];
|
185
|
+
const now = Date.now();
|
186
|
+
|
187
|
+
// Check for too many resources
|
188
|
+
if (this.resources.size > 1000) {
|
189
|
+
warnings.push(`High number of managed resources: ${this.resources.size}`);
|
190
|
+
}
|
191
|
+
|
192
|
+
// Check for old resources
|
193
|
+
let oldResourceCount = 0;
|
194
|
+
this.resources.forEach(resource => {
|
195
|
+
if (now - resource.timestamp > 600000) { // 10 minutes
|
196
|
+
oldResourceCount++;
|
197
|
+
}
|
198
|
+
});
|
199
|
+
|
200
|
+
if (oldResourceCount > 50) {
|
201
|
+
warnings.push(`Many old resources detected: ${oldResourceCount}`);
|
202
|
+
}
|
203
|
+
|
204
|
+
// Check for orphaned element resources
|
205
|
+
let orphanedCount = 0;
|
206
|
+
this.elementResources.forEach((resourceIds, element) => {
|
207
|
+
if (!document.contains(element)) {
|
208
|
+
orphanedCount += resourceIds.size;
|
209
|
+
}
|
210
|
+
});
|
211
|
+
|
212
|
+
if (orphanedCount > 0) {
|
213
|
+
warnings.push(`Orphaned element resources detected: ${orphanedCount}`);
|
214
|
+
}
|
215
|
+
|
216
|
+
return warnings;
|
217
|
+
}
|
218
|
+
|
219
|
+
/**
|
220
|
+
* Cleanup all resources and stop monitoring
|
221
|
+
*/
|
222
|
+
public destroy(): void {
|
223
|
+
// Cleanup all resources
|
224
|
+
const resourceIds = Array.from(this.resources.keys());
|
225
|
+
resourceIds.forEach(id => this.unregister(id));
|
226
|
+
|
227
|
+
// Stop monitoring
|
228
|
+
this.stopMonitoring();
|
229
|
+
|
230
|
+
// Clear maps
|
231
|
+
this.resources.clear();
|
232
|
+
this.elementResources.clear();
|
233
|
+
}
|
234
|
+
|
235
|
+
/**
|
236
|
+
* Start monitoring for memory leaks
|
237
|
+
*/
|
238
|
+
public startMonitoring(): void {
|
239
|
+
if (this.isMonitoring) return;
|
240
|
+
this.isMonitoring = true;
|
241
|
+
}
|
242
|
+
|
243
|
+
/**
|
244
|
+
* Stop monitoring
|
245
|
+
*/
|
246
|
+
public stopMonitoring(): void {
|
247
|
+
if (!this.isMonitoring) return;
|
248
|
+
|
249
|
+
this.isMonitoring = false;
|
250
|
+
|
251
|
+
if (this.mutationObserver) {
|
252
|
+
this.mutationObserver.disconnect();
|
253
|
+
this.mutationObserver = null;
|
254
|
+
}
|
255
|
+
|
256
|
+
if (this.cleanupInterval) {
|
257
|
+
clearInterval(this.cleanupInterval);
|
258
|
+
this.cleanupInterval = null;
|
259
|
+
}
|
260
|
+
}
|
261
|
+
|
262
|
+
/**
|
263
|
+
* Setup DOM mutation observer to detect removed elements
|
264
|
+
*/
|
265
|
+
private setupDOMObserver(): void {
|
266
|
+
if (typeof MutationObserver === 'undefined') return;
|
267
|
+
|
268
|
+
this.mutationObserver = new MutationObserver((mutations) => {
|
269
|
+
mutations.forEach(mutation => {
|
270
|
+
if (mutation.type === 'childList') {
|
271
|
+
mutation.removedNodes.forEach(node => {
|
272
|
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
273
|
+
this.handleElementRemoval(node as Element);
|
274
|
+
}
|
275
|
+
});
|
276
|
+
}
|
277
|
+
});
|
278
|
+
});
|
279
|
+
|
280
|
+
this.mutationObserver.observe(document.body, {
|
281
|
+
childList: true,
|
282
|
+
subtree: true
|
283
|
+
});
|
284
|
+
}
|
285
|
+
|
286
|
+
/**
|
287
|
+
* Handle element removal from DOM
|
288
|
+
*/
|
289
|
+
private handleElementRemoval(element: Element): void {
|
290
|
+
// Cleanup resources for this element
|
291
|
+
this.cleanupElement(element);
|
292
|
+
|
293
|
+
// Also check descendants
|
294
|
+
const descendants = element.querySelectorAll('*');
|
295
|
+
descendants.forEach(descendant => {
|
296
|
+
this.cleanupElement(descendant);
|
297
|
+
});
|
298
|
+
}
|
299
|
+
|
300
|
+
/**
|
301
|
+
* Start periodic cleanup
|
302
|
+
*/
|
303
|
+
private startPeriodicCleanup(): void {
|
304
|
+
this.cleanupInterval = window.setInterval(() => {
|
305
|
+
// Cleanup orphaned resources
|
306
|
+
let orphanedCount = 0;
|
307
|
+
this.elementResources.forEach((resourceIds, element) => {
|
308
|
+
if (!document.contains(element)) {
|
309
|
+
orphanedCount += this.cleanupElement(element);
|
310
|
+
}
|
311
|
+
});
|
312
|
+
|
313
|
+
// Cleanup very old resources
|
314
|
+
const oldCount = this.cleanupOldResources(600000); // 10 minutes
|
315
|
+
|
316
|
+
if (orphanedCount > 0 || oldCount > 0) {
|
317
|
+
console.log(`ProteusJS: Cleaned up ${orphanedCount} orphaned and ${oldCount} old resources`);
|
318
|
+
}
|
319
|
+
}, 60000); // Run every minute
|
320
|
+
}
|
321
|
+
}
|
@@ -0,0 +1,238 @@
|
|
1
|
+
/**
|
2
|
+
* Performance Monitor for ProteusJS
|
3
|
+
* Tracks and optimizes performance metrics
|
4
|
+
*/
|
5
|
+
|
6
|
+
export interface PerformanceMetrics {
|
7
|
+
frameRate: number;
|
8
|
+
memoryUsage: number;
|
9
|
+
responseTime: number;
|
10
|
+
observerCount: number;
|
11
|
+
eventCount: number;
|
12
|
+
lastUpdate: number;
|
13
|
+
}
|
14
|
+
|
15
|
+
export class PerformanceMonitor {
|
16
|
+
private metrics: PerformanceMetrics;
|
17
|
+
private frameCount: number = 0;
|
18
|
+
private lastFrameTime: number = 0;
|
19
|
+
private frameRateHistory: number[] = [];
|
20
|
+
private responseTimeHistory: number[] = [];
|
21
|
+
private isMonitoring: boolean = false;
|
22
|
+
private animationFrameId: number | null = null;
|
23
|
+
private performanceLevel: 'low' | 'medium' | 'high';
|
24
|
+
|
25
|
+
constructor(performanceLevel: 'low' | 'medium' | 'high' = 'high') {
|
26
|
+
this.performanceLevel = performanceLevel;
|
27
|
+
this.metrics = {
|
28
|
+
frameRate: 0,
|
29
|
+
memoryUsage: 0,
|
30
|
+
responseTime: 0,
|
31
|
+
observerCount: 0,
|
32
|
+
eventCount: 0,
|
33
|
+
lastUpdate: Date.now()
|
34
|
+
};
|
35
|
+
}
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Start performance monitoring
|
39
|
+
*/
|
40
|
+
public start(): void {
|
41
|
+
if (this.isMonitoring) return;
|
42
|
+
|
43
|
+
this.isMonitoring = true;
|
44
|
+
this.lastFrameTime = performance.now();
|
45
|
+
this.scheduleFrame();
|
46
|
+
}
|
47
|
+
|
48
|
+
/**
|
49
|
+
* Stop performance monitoring
|
50
|
+
*/
|
51
|
+
public stop(): void {
|
52
|
+
if (!this.isMonitoring) return;
|
53
|
+
|
54
|
+
this.isMonitoring = false;
|
55
|
+
if (this.animationFrameId) {
|
56
|
+
cancelAnimationFrame(this.animationFrameId);
|
57
|
+
this.animationFrameId = null;
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
/**
|
62
|
+
* Get current performance metrics
|
63
|
+
*/
|
64
|
+
public getMetrics(): PerformanceMetrics {
|
65
|
+
return { ...this.metrics };
|
66
|
+
}
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Record response time for an operation
|
70
|
+
*/
|
71
|
+
public recordResponseTime(startTime: number): void {
|
72
|
+
const responseTime = performance.now() - startTime;
|
73
|
+
this.responseTimeHistory.push(responseTime);
|
74
|
+
|
75
|
+
// Keep only last 100 measurements
|
76
|
+
if (this.responseTimeHistory.length > 100) {
|
77
|
+
this.responseTimeHistory.shift();
|
78
|
+
}
|
79
|
+
|
80
|
+
// Calculate average response time
|
81
|
+
this.metrics.responseTime = this.responseTimeHistory.reduce((a, b) => a + b, 0) / this.responseTimeHistory.length;
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Update observer count
|
86
|
+
*/
|
87
|
+
public updateObserverCount(count: number): void {
|
88
|
+
this.metrics.observerCount = count;
|
89
|
+
}
|
90
|
+
|
91
|
+
/**
|
92
|
+
* Update event count
|
93
|
+
*/
|
94
|
+
public updateEventCount(count: number): void {
|
95
|
+
this.metrics.eventCount = count;
|
96
|
+
}
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Check if performance is within acceptable limits
|
100
|
+
*/
|
101
|
+
public isPerformanceGood(): boolean {
|
102
|
+
const targetFrameRate = this.getTargetFrameRate();
|
103
|
+
const targetResponseTime = this.getTargetResponseTime();
|
104
|
+
|
105
|
+
return this.metrics.frameRate >= targetFrameRate &&
|
106
|
+
this.metrics.responseTime <= targetResponseTime;
|
107
|
+
}
|
108
|
+
|
109
|
+
/**
|
110
|
+
* Get performance recommendations
|
111
|
+
*/
|
112
|
+
public getRecommendations(): string[] {
|
113
|
+
const recommendations: string[] = [];
|
114
|
+
|
115
|
+
if (this.metrics.frameRate < this.getTargetFrameRate()) {
|
116
|
+
recommendations.push('Frame rate is below target. Consider reducing animation complexity.');
|
117
|
+
}
|
118
|
+
|
119
|
+
if (this.metrics.responseTime > this.getTargetResponseTime()) {
|
120
|
+
recommendations.push('Response time is too high. Consider optimizing calculations.');
|
121
|
+
}
|
122
|
+
|
123
|
+
if (this.metrics.observerCount > this.getMaxObservers()) {
|
124
|
+
recommendations.push('Too many observers active. Consider lazy evaluation.');
|
125
|
+
}
|
126
|
+
|
127
|
+
if (this.metrics.memoryUsage > this.getMaxMemoryUsage()) {
|
128
|
+
recommendations.push('Memory usage is high. Check for memory leaks.');
|
129
|
+
}
|
130
|
+
|
131
|
+
return recommendations;
|
132
|
+
}
|
133
|
+
|
134
|
+
/**
|
135
|
+
* Get performance score (0-100)
|
136
|
+
*/
|
137
|
+
public getPerformanceScore(): number {
|
138
|
+
const frameRateScore = Math.min(this.metrics.frameRate / this.getTargetFrameRate(), 1) * 30;
|
139
|
+
const responseTimeScore = Math.max(1 - (this.metrics.responseTime / this.getTargetResponseTime()), 0) * 30;
|
140
|
+
const memoryScore = Math.max(1 - (this.metrics.memoryUsage / this.getMaxMemoryUsage()), 0) * 20;
|
141
|
+
const observerScore = Math.max(1 - (this.metrics.observerCount / this.getMaxObservers()), 0) * 20;
|
142
|
+
|
143
|
+
return Math.round(frameRateScore + responseTimeScore + memoryScore + observerScore);
|
144
|
+
}
|
145
|
+
|
146
|
+
/**
|
147
|
+
* Schedule next frame for monitoring
|
148
|
+
*/
|
149
|
+
private scheduleFrame(): void {
|
150
|
+
if (!this.isMonitoring) return;
|
151
|
+
|
152
|
+
this.animationFrameId = requestAnimationFrame((currentTime) => {
|
153
|
+
this.updateFrameRate(currentTime);
|
154
|
+
this.updateMemoryUsage();
|
155
|
+
this.metrics.lastUpdate = Date.now();
|
156
|
+
this.scheduleFrame();
|
157
|
+
});
|
158
|
+
}
|
159
|
+
|
160
|
+
/**
|
161
|
+
* Update frame rate calculation
|
162
|
+
*/
|
163
|
+
private updateFrameRate(currentTime: number): void {
|
164
|
+
const deltaTime = currentTime - this.lastFrameTime;
|
165
|
+
this.lastFrameTime = currentTime;
|
166
|
+
|
167
|
+
if (deltaTime > 0) {
|
168
|
+
const currentFrameRate = 1000 / deltaTime;
|
169
|
+
this.frameRateHistory.push(currentFrameRate);
|
170
|
+
|
171
|
+
// Keep only last 60 measurements (1 second at 60fps)
|
172
|
+
if (this.frameRateHistory.length > 60) {
|
173
|
+
this.frameRateHistory.shift();
|
174
|
+
}
|
175
|
+
|
176
|
+
// Calculate average frame rate
|
177
|
+
this.metrics.frameRate = this.frameRateHistory.reduce((a, b) => a + b, 0) / this.frameRateHistory.length;
|
178
|
+
}
|
179
|
+
}
|
180
|
+
|
181
|
+
/**
|
182
|
+
* Update memory usage
|
183
|
+
*/
|
184
|
+
private updateMemoryUsage(): void {
|
185
|
+
if ('memory' in performance) {
|
186
|
+
const memory = (performance as any).memory;
|
187
|
+
this.metrics.memoryUsage = memory.usedJSHeapSize / 1024 / 1024; // MB
|
188
|
+
}
|
189
|
+
}
|
190
|
+
|
191
|
+
/**
|
192
|
+
* Get target frame rate based on performance level
|
193
|
+
*/
|
194
|
+
private getTargetFrameRate(): number {
|
195
|
+
switch (this.performanceLevel) {
|
196
|
+
case 'low': return 30;
|
197
|
+
case 'medium': return 45;
|
198
|
+
case 'high': return 60;
|
199
|
+
default: return 60;
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
/**
|
204
|
+
* Get target response time based on performance level
|
205
|
+
*/
|
206
|
+
private getTargetResponseTime(): number {
|
207
|
+
switch (this.performanceLevel) {
|
208
|
+
case 'low': return 100; // 100ms
|
209
|
+
case 'medium': return 80; // 80ms
|
210
|
+
case 'high': return 60; // 60ms
|
211
|
+
default: return 60;
|
212
|
+
}
|
213
|
+
}
|
214
|
+
|
215
|
+
/**
|
216
|
+
* Get maximum allowed observers
|
217
|
+
*/
|
218
|
+
private getMaxObservers(): number {
|
219
|
+
switch (this.performanceLevel) {
|
220
|
+
case 'low': return 50;
|
221
|
+
case 'medium': return 100;
|
222
|
+
case 'high': return 200;
|
223
|
+
default: return 200;
|
224
|
+
}
|
225
|
+
}
|
226
|
+
|
227
|
+
/**
|
228
|
+
* Get maximum allowed memory usage (MB)
|
229
|
+
*/
|
230
|
+
private getMaxMemoryUsage(): number {
|
231
|
+
switch (this.performanceLevel) {
|
232
|
+
case 'low': return 50;
|
233
|
+
case 'medium': return 100;
|
234
|
+
case 'high': return 200;
|
235
|
+
default: return 200;
|
236
|
+
}
|
237
|
+
}
|
238
|
+
}
|