@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,497 @@
|
|
1
|
+
/**
|
2
|
+
* Memory Management System for ProteusJS
|
3
|
+
* Automatic observer cleanup, event listener lifecycle management, and memory optimization
|
4
|
+
*/
|
5
|
+
|
6
|
+
export interface MemoryConfig {
|
7
|
+
autoCleanup: boolean;
|
8
|
+
cleanupInterval: number;
|
9
|
+
memoryThreshold: number;
|
10
|
+
gcOptimization: boolean;
|
11
|
+
leakDetection: boolean;
|
12
|
+
performanceMonitoring: boolean;
|
13
|
+
}
|
14
|
+
|
15
|
+
export interface MemoryMetrics {
|
16
|
+
totalObservers: number;
|
17
|
+
activeObservers: number;
|
18
|
+
totalEventListeners: number;
|
19
|
+
activeEventListeners: number;
|
20
|
+
memoryUsage: number;
|
21
|
+
gcCollections: number;
|
22
|
+
leaksDetected: number;
|
23
|
+
cleanupOperations: number;
|
24
|
+
}
|
25
|
+
|
26
|
+
export interface ManagedResource {
|
27
|
+
id: string;
|
28
|
+
type: 'observer' | 'listener' | 'timer' | 'animation' | 'cache';
|
29
|
+
element?: Element;
|
30
|
+
cleanup: () => void;
|
31
|
+
timestamp: number;
|
32
|
+
lastAccessed: number;
|
33
|
+
memorySize: number;
|
34
|
+
}
|
35
|
+
|
36
|
+
export class MemoryManagementSystem {
|
37
|
+
private config: Required<MemoryConfig>;
|
38
|
+
private resources: Map<string, ManagedResource> = new Map();
|
39
|
+
private metrics: MemoryMetrics;
|
40
|
+
private cleanupTimer: number | null = null;
|
41
|
+
private performanceObserver: PerformanceObserver | null = null;
|
42
|
+
private weakRefs: Set<WeakRef<any>> = new Set();
|
43
|
+
|
44
|
+
constructor(config: Partial<MemoryConfig> = {}) {
|
45
|
+
this.config = {
|
46
|
+
autoCleanup: true,
|
47
|
+
cleanupInterval: 30000, // 30 seconds
|
48
|
+
memoryThreshold: 50 * 1024 * 1024, // 50MB
|
49
|
+
gcOptimization: true,
|
50
|
+
leakDetection: true,
|
51
|
+
performanceMonitoring: true,
|
52
|
+
...config
|
53
|
+
};
|
54
|
+
|
55
|
+
this.metrics = this.createInitialMetrics();
|
56
|
+
this.setupMemoryMonitoring();
|
57
|
+
this.startAutoCleanup();
|
58
|
+
}
|
59
|
+
|
60
|
+
/**
|
61
|
+
* Register a managed resource
|
62
|
+
*/
|
63
|
+
public register(
|
64
|
+
type: ManagedResource['type'],
|
65
|
+
cleanup: () => void,
|
66
|
+
element?: Element,
|
67
|
+
estimatedSize: number = 1024
|
68
|
+
): string {
|
69
|
+
const id = this.generateResourceId();
|
70
|
+
const resource: ManagedResource = {
|
71
|
+
id,
|
72
|
+
type,
|
73
|
+
...(element && { element }),
|
74
|
+
cleanup,
|
75
|
+
timestamp: performance.now(),
|
76
|
+
lastAccessed: performance.now(),
|
77
|
+
memorySize: estimatedSize
|
78
|
+
};
|
79
|
+
|
80
|
+
this.resources.set(id, resource);
|
81
|
+
this.updateMetrics(type, 'register');
|
82
|
+
|
83
|
+
// Create weak reference for leak detection
|
84
|
+
if (element && this.config.leakDetection) {
|
85
|
+
this.weakRefs.add(new WeakRef(element));
|
86
|
+
}
|
87
|
+
|
88
|
+
return id;
|
89
|
+
}
|
90
|
+
|
91
|
+
/**
|
92
|
+
* Unregister and cleanup a resource
|
93
|
+
*/
|
94
|
+
public unregister(id: string): void {
|
95
|
+
const resource = this.resources.get(id);
|
96
|
+
if (resource) {
|
97
|
+
try {
|
98
|
+
resource.cleanup();
|
99
|
+
this.updateMetrics(resource.type, 'unregister');
|
100
|
+
} catch (error) {
|
101
|
+
console.error('Error during resource cleanup:', error);
|
102
|
+
}
|
103
|
+
|
104
|
+
this.resources.delete(id);
|
105
|
+
this.metrics.cleanupOperations++;
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
/**
|
110
|
+
* Register ResizeObserver with automatic cleanup
|
111
|
+
*/
|
112
|
+
public registerResizeObserver(
|
113
|
+
observer: ResizeObserver,
|
114
|
+
elements: Element[]
|
115
|
+
): string {
|
116
|
+
return this.register(
|
117
|
+
'observer',
|
118
|
+
() => {
|
119
|
+
observer.disconnect();
|
120
|
+
},
|
121
|
+
elements[0],
|
122
|
+
elements.length * 512 // Estimate memory per observed element
|
123
|
+
);
|
124
|
+
}
|
125
|
+
|
126
|
+
/**
|
127
|
+
* Register IntersectionObserver with automatic cleanup
|
128
|
+
*/
|
129
|
+
public registerIntersectionObserver(
|
130
|
+
observer: IntersectionObserver,
|
131
|
+
elements: Element[]
|
132
|
+
): string {
|
133
|
+
return this.register(
|
134
|
+
'observer',
|
135
|
+
() => {
|
136
|
+
observer.disconnect();
|
137
|
+
},
|
138
|
+
elements[0],
|
139
|
+
elements.length * 256
|
140
|
+
);
|
141
|
+
}
|
142
|
+
|
143
|
+
/**
|
144
|
+
* Register event listener with automatic cleanup
|
145
|
+
*/
|
146
|
+
public registerEventListener(
|
147
|
+
element: Element,
|
148
|
+
event: string,
|
149
|
+
listener: EventListener,
|
150
|
+
options?: AddEventListenerOptions
|
151
|
+
): string {
|
152
|
+
return this.register(
|
153
|
+
'listener',
|
154
|
+
() => {
|
155
|
+
element.removeEventListener(event, listener, options);
|
156
|
+
},
|
157
|
+
element,
|
158
|
+
128 // Estimate memory per listener
|
159
|
+
);
|
160
|
+
}
|
161
|
+
|
162
|
+
/**
|
163
|
+
* Register timer with automatic cleanup
|
164
|
+
*/
|
165
|
+
public registerTimer(timerId: number, type: 'timeout' | 'interval' = 'timeout'): string {
|
166
|
+
return this.register(
|
167
|
+
'timer',
|
168
|
+
() => {
|
169
|
+
if (type === 'timeout') {
|
170
|
+
clearTimeout(timerId);
|
171
|
+
} else {
|
172
|
+
clearInterval(timerId);
|
173
|
+
}
|
174
|
+
},
|
175
|
+
undefined,
|
176
|
+
64
|
177
|
+
);
|
178
|
+
}
|
179
|
+
|
180
|
+
/**
|
181
|
+
* Register animation with automatic cleanup
|
182
|
+
*/
|
183
|
+
public registerAnimation(animation: Animation): string {
|
184
|
+
return this.register(
|
185
|
+
'animation',
|
186
|
+
() => {
|
187
|
+
animation.cancel();
|
188
|
+
},
|
189
|
+
(animation.effect as any)?.target instanceof Element ? (animation.effect as any).target : undefined,
|
190
|
+
256
|
191
|
+
);
|
192
|
+
}
|
193
|
+
|
194
|
+
/**
|
195
|
+
* Register cache entry with automatic cleanup
|
196
|
+
*/
|
197
|
+
public registerCacheEntry(
|
198
|
+
cache: Map<any, any> | WeakMap<any, any>,
|
199
|
+
key: any,
|
200
|
+
estimatedSize: number = 1024
|
201
|
+
): string {
|
202
|
+
return this.register(
|
203
|
+
'cache',
|
204
|
+
() => {
|
205
|
+
cache.delete(key);
|
206
|
+
},
|
207
|
+
undefined,
|
208
|
+
estimatedSize
|
209
|
+
);
|
210
|
+
}
|
211
|
+
|
212
|
+
/**
|
213
|
+
* Force cleanup of all resources
|
214
|
+
*/
|
215
|
+
public cleanup(): void {
|
216
|
+
const resourceIds = Array.from(this.resources.keys());
|
217
|
+
resourceIds.forEach(id => this.unregister(id));
|
218
|
+
}
|
219
|
+
|
220
|
+
/**
|
221
|
+
* Cleanup resources by type
|
222
|
+
*/
|
223
|
+
public cleanupByType(type: ManagedResource['type']): void {
|
224
|
+
const resourceIds = Array.from(this.resources.entries())
|
225
|
+
.filter(([, resource]) => resource.type === type)
|
226
|
+
.map(([id]) => id);
|
227
|
+
|
228
|
+
resourceIds.forEach(id => this.unregister(id));
|
229
|
+
}
|
230
|
+
|
231
|
+
/**
|
232
|
+
* Cleanup stale resources
|
233
|
+
*/
|
234
|
+
public cleanupStale(maxAge: number = 300000): void { // 5 minutes default
|
235
|
+
const now = performance.now();
|
236
|
+
const staleIds = Array.from(this.resources.entries())
|
237
|
+
.filter(([, resource]) => now - resource.lastAccessed > maxAge)
|
238
|
+
.map(([id]) => id);
|
239
|
+
|
240
|
+
staleIds.forEach(id => this.unregister(id));
|
241
|
+
}
|
242
|
+
|
243
|
+
/**
|
244
|
+
* Cleanup resources for removed elements
|
245
|
+
*/
|
246
|
+
public cleanupOrphanedResources(): void {
|
247
|
+
const orphanedIds: string[] = [];
|
248
|
+
|
249
|
+
this.resources.forEach((resource, id) => {
|
250
|
+
if (resource.element && !document.contains(resource.element)) {
|
251
|
+
orphanedIds.push(id);
|
252
|
+
}
|
253
|
+
});
|
254
|
+
|
255
|
+
orphanedIds.forEach(id => this.unregister(id));
|
256
|
+
}
|
257
|
+
|
258
|
+
/**
|
259
|
+
* Get current memory metrics
|
260
|
+
*/
|
261
|
+
public getMetrics(): MemoryMetrics {
|
262
|
+
this.updateMemoryUsage();
|
263
|
+
return { ...this.metrics };
|
264
|
+
}
|
265
|
+
|
266
|
+
/**
|
267
|
+
* Get resource count by type
|
268
|
+
*/
|
269
|
+
public getResourceCount(type?: ManagedResource['type']): number {
|
270
|
+
if (!type) {
|
271
|
+
return this.resources.size;
|
272
|
+
}
|
273
|
+
|
274
|
+
return Array.from(this.resources.values())
|
275
|
+
.filter(resource => resource.type === type).length;
|
276
|
+
}
|
277
|
+
|
278
|
+
/**
|
279
|
+
* Check for memory leaks
|
280
|
+
*/
|
281
|
+
public detectLeaks(): string[] {
|
282
|
+
const leaks: string[] = [];
|
283
|
+
|
284
|
+
// Check for orphaned weak references
|
285
|
+
this.weakRefs.forEach(weakRef => {
|
286
|
+
if (weakRef.deref() === undefined) {
|
287
|
+
// Element was garbage collected but we might still have resources
|
288
|
+
this.cleanupOrphanedResources();
|
289
|
+
}
|
290
|
+
});
|
291
|
+
|
292
|
+
// Check for excessive resource accumulation
|
293
|
+
const resourceCounts = new Map<string, number>();
|
294
|
+
this.resources.forEach(resource => {
|
295
|
+
const count = resourceCounts.get(resource.type) || 0;
|
296
|
+
resourceCounts.set(resource.type, count + 1);
|
297
|
+
});
|
298
|
+
|
299
|
+
resourceCounts.forEach((count, type) => {
|
300
|
+
const threshold = this.getThresholdForType(type);
|
301
|
+
if (count > threshold) {
|
302
|
+
leaks.push(`Excessive ${type} resources: ${count} (threshold: ${threshold})`);
|
303
|
+
}
|
304
|
+
});
|
305
|
+
|
306
|
+
this.metrics.leaksDetected = leaks.length;
|
307
|
+
return leaks;
|
308
|
+
}
|
309
|
+
|
310
|
+
/**
|
311
|
+
* Optimize garbage collection
|
312
|
+
*/
|
313
|
+
public optimizeGC(): void {
|
314
|
+
if (!this.config.gcOptimization) return;
|
315
|
+
|
316
|
+
// Force cleanup of stale resources
|
317
|
+
this.cleanupStale();
|
318
|
+
|
319
|
+
// Clear weak references to collected objects
|
320
|
+
const validWeakRefs = new Set<WeakRef<any>>();
|
321
|
+
this.weakRefs.forEach(weakRef => {
|
322
|
+
if (weakRef.deref() !== undefined) {
|
323
|
+
validWeakRefs.add(weakRef);
|
324
|
+
}
|
325
|
+
});
|
326
|
+
this.weakRefs = validWeakRefs;
|
327
|
+
|
328
|
+
// Suggest garbage collection if available
|
329
|
+
if ('gc' in window && typeof window.gc === 'function') {
|
330
|
+
window.gc();
|
331
|
+
this.metrics.gcCollections++;
|
332
|
+
}
|
333
|
+
}
|
334
|
+
|
335
|
+
/**
|
336
|
+
* Destroy memory management system
|
337
|
+
*/
|
338
|
+
public destroy(): void {
|
339
|
+
this.stopAutoCleanup();
|
340
|
+
this.stopMemoryMonitoring();
|
341
|
+
this.cleanup();
|
342
|
+
this.weakRefs.clear();
|
343
|
+
}
|
344
|
+
|
345
|
+
/**
|
346
|
+
* Setup memory monitoring
|
347
|
+
*/
|
348
|
+
private setupMemoryMonitoring(): void {
|
349
|
+
if (!this.config.performanceMonitoring) return;
|
350
|
+
|
351
|
+
// Monitor memory usage
|
352
|
+
if ('memory' in performance) {
|
353
|
+
setInterval(() => {
|
354
|
+
this.updateMemoryUsage();
|
355
|
+
this.checkMemoryThreshold();
|
356
|
+
}, 5000);
|
357
|
+
}
|
358
|
+
|
359
|
+
// Monitor performance entries
|
360
|
+
if (window.PerformanceObserver) {
|
361
|
+
try {
|
362
|
+
this.performanceObserver = new PerformanceObserver((list) => {
|
363
|
+
const entries = list.getEntries();
|
364
|
+
entries.forEach(entry => {
|
365
|
+
if (entry.entryType === 'measure' && entry.name.includes('proteus')) {
|
366
|
+
// Track ProteusJS-related performance measures
|
367
|
+
}
|
368
|
+
});
|
369
|
+
});
|
370
|
+
|
371
|
+
this.performanceObserver.observe({ entryTypes: ['measure', 'navigation'] });
|
372
|
+
} catch (error) {
|
373
|
+
console.warn('PerformanceObserver not fully supported:', error);
|
374
|
+
}
|
375
|
+
}
|
376
|
+
}
|
377
|
+
|
378
|
+
/**
|
379
|
+
* Start automatic cleanup
|
380
|
+
*/
|
381
|
+
private startAutoCleanup(): void {
|
382
|
+
if (!this.config.autoCleanup) return;
|
383
|
+
|
384
|
+
this.cleanupTimer = window.setInterval(() => {
|
385
|
+
this.cleanupStale();
|
386
|
+
this.cleanupOrphanedResources();
|
387
|
+
this.detectLeaks();
|
388
|
+
|
389
|
+
if (this.config.gcOptimization) {
|
390
|
+
this.optimizeGC();
|
391
|
+
}
|
392
|
+
}, this.config.cleanupInterval);
|
393
|
+
}
|
394
|
+
|
395
|
+
/**
|
396
|
+
* Stop automatic cleanup
|
397
|
+
*/
|
398
|
+
private stopAutoCleanup(): void {
|
399
|
+
if (this.cleanupTimer) {
|
400
|
+
clearInterval(this.cleanupTimer);
|
401
|
+
this.cleanupTimer = null;
|
402
|
+
}
|
403
|
+
}
|
404
|
+
|
405
|
+
/**
|
406
|
+
* Stop memory monitoring
|
407
|
+
*/
|
408
|
+
private stopMemoryMonitoring(): void {
|
409
|
+
if (this.performanceObserver) {
|
410
|
+
this.performanceObserver.disconnect();
|
411
|
+
this.performanceObserver = null;
|
412
|
+
}
|
413
|
+
}
|
414
|
+
|
415
|
+
/**
|
416
|
+
* Update memory usage metrics
|
417
|
+
*/
|
418
|
+
private updateMemoryUsage(): void {
|
419
|
+
if ('memory' in performance) {
|
420
|
+
const memory = (performance as any).memory;
|
421
|
+
this.metrics.memoryUsage = memory.usedJSHeapSize || 0;
|
422
|
+
}
|
423
|
+
}
|
424
|
+
|
425
|
+
/**
|
426
|
+
* Check memory threshold and trigger cleanup if needed
|
427
|
+
*/
|
428
|
+
private checkMemoryThreshold(): void {
|
429
|
+
if (this.metrics.memoryUsage > this.config.memoryThreshold) {
|
430
|
+
console.warn('Memory threshold exceeded, triggering cleanup');
|
431
|
+
this.cleanupStale(60000); // Cleanup resources older than 1 minute
|
432
|
+
this.optimizeGC();
|
433
|
+
}
|
434
|
+
}
|
435
|
+
|
436
|
+
/**
|
437
|
+
* Update metrics for resource operations
|
438
|
+
*/
|
439
|
+
private updateMetrics(type: ManagedResource['type'], operation: 'register' | 'unregister'): void {
|
440
|
+
switch (type) {
|
441
|
+
case 'observer':
|
442
|
+
if (operation === 'register') {
|
443
|
+
this.metrics.totalObservers++;
|
444
|
+
this.metrics.activeObservers++;
|
445
|
+
} else {
|
446
|
+
this.metrics.activeObservers--;
|
447
|
+
}
|
448
|
+
break;
|
449
|
+
case 'listener':
|
450
|
+
if (operation === 'register') {
|
451
|
+
this.metrics.totalEventListeners++;
|
452
|
+
this.metrics.activeEventListeners++;
|
453
|
+
} else {
|
454
|
+
this.metrics.activeEventListeners--;
|
455
|
+
}
|
456
|
+
break;
|
457
|
+
}
|
458
|
+
}
|
459
|
+
|
460
|
+
/**
|
461
|
+
* Get threshold for resource type
|
462
|
+
*/
|
463
|
+
private getThresholdForType(type: string): number {
|
464
|
+
const thresholds: Record<string, number> = {
|
465
|
+
observer: 100,
|
466
|
+
listener: 500,
|
467
|
+
timer: 50,
|
468
|
+
animation: 20,
|
469
|
+
cache: 1000
|
470
|
+
};
|
471
|
+
|
472
|
+
return thresholds[type] || 100;
|
473
|
+
}
|
474
|
+
|
475
|
+
/**
|
476
|
+
* Generate unique resource ID
|
477
|
+
*/
|
478
|
+
private generateResourceId(): string {
|
479
|
+
return `mem-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
480
|
+
}
|
481
|
+
|
482
|
+
/**
|
483
|
+
* Create initial metrics
|
484
|
+
*/
|
485
|
+
private createInitialMetrics(): MemoryMetrics {
|
486
|
+
return {
|
487
|
+
totalObservers: 0,
|
488
|
+
activeObservers: 0,
|
489
|
+
totalEventListeners: 0,
|
490
|
+
activeEventListeners: 0,
|
491
|
+
memoryUsage: 0,
|
492
|
+
gcCollections: 0,
|
493
|
+
leaksDetected: 0,
|
494
|
+
cleanupOperations: 0
|
495
|
+
};
|
496
|
+
}
|
497
|
+
}
|