@rakeyshgidwani/roger-ui-bank-theme-stan-design 0.1.3 → 0.1.5
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/CHANGELOG.md +1 -1
- package/dist/index.d.ts +131 -131
- package/dist/index.esm.js +148 -148
- package/dist/index.js +148 -148
- package/dist/styles.css +1 -1
- package/package.json +1 -1
- package/src/components/ui/accessibility-demo.tsx +271 -0
- package/src/components/ui/advanced-component-architecture-demo.tsx +916 -0
- package/src/components/ui/advanced-transition-system-demo.tsx +670 -0
- package/src/components/ui/advanced-transition-system.tsx +395 -0
- package/src/components/ui/animation/animated-container.tsx +166 -0
- package/src/components/ui/animation/index.ts +19 -0
- package/src/components/ui/animation/staggered-container.tsx +68 -0
- package/src/components/ui/animation-demo.tsx +250 -0
- package/src/components/ui/badge.tsx +33 -0
- package/src/components/ui/battery-conscious-animation-demo.tsx +568 -0
- package/src/components/ui/border-radius-shadow-demo.tsx +187 -0
- package/src/components/ui/button.tsx +36 -0
- package/src/components/ui/card.tsx +207 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/color-preview.tsx +411 -0
- package/src/components/ui/data-display/chart.tsx +653 -0
- package/src/components/ui/data-display/data-grid-simple.tsx +76 -0
- package/src/components/ui/data-display/data-grid.tsx +680 -0
- package/src/components/ui/data-display/list.tsx +456 -0
- package/src/components/ui/data-display/table.tsx +482 -0
- package/src/components/ui/data-display/timeline.tsx +441 -0
- package/src/components/ui/data-display/tree.tsx +602 -0
- package/src/components/ui/data-display/types.ts +536 -0
- package/src/components/ui/enterprise-mobile-experience-demo.tsx +749 -0
- package/src/components/ui/enterprise-mobile-experience.tsx +464 -0
- package/src/components/ui/feedback/alert.tsx +157 -0
- package/src/components/ui/feedback/progress.tsx +292 -0
- package/src/components/ui/feedback/skeleton.tsx +185 -0
- package/src/components/ui/feedback/toast.tsx +280 -0
- package/src/components/ui/feedback/types.ts +125 -0
- package/src/components/ui/font-preview.tsx +288 -0
- package/src/components/ui/form-demo.tsx +553 -0
- package/src/components/ui/hardware-acceleration-demo.tsx +547 -0
- package/src/components/ui/input.tsx +35 -0
- package/src/components/ui/label.tsx +16 -0
- package/src/components/ui/layout-demo.tsx +367 -0
- package/src/components/ui/layouts/adaptive-layout.tsx +139 -0
- package/src/components/ui/layouts/desktop-layout.tsx +224 -0
- package/src/components/ui/layouts/index.ts +10 -0
- package/src/components/ui/layouts/mobile-layout.tsx +162 -0
- package/src/components/ui/layouts/tablet-layout.tsx +197 -0
- package/src/components/ui/mobile-form-validation.tsx +451 -0
- package/src/components/ui/mobile-input-demo.tsx +201 -0
- package/src/components/ui/mobile-input.tsx +281 -0
- package/src/components/ui/mobile-skeleton-loading-demo.tsx +638 -0
- package/src/components/ui/navigation/breadcrumb.tsx +158 -0
- package/src/components/ui/navigation/index.ts +36 -0
- package/src/components/ui/navigation/menu.tsx +374 -0
- package/src/components/ui/navigation/navigation-demo.tsx +324 -0
- package/src/components/ui/navigation/pagination.tsx +272 -0
- package/src/components/ui/navigation/sidebar.tsx +383 -0
- package/src/components/ui/navigation/stepper.tsx +303 -0
- package/src/components/ui/navigation/tabs.tsx +205 -0
- package/src/components/ui/navigation/types.ts +299 -0
- package/src/components/ui/overlay/backdrop.tsx +81 -0
- package/src/components/ui/overlay/focus-manager.tsx +143 -0
- package/src/components/ui/overlay/index.ts +36 -0
- package/src/components/ui/overlay/modal.tsx +270 -0
- package/src/components/ui/overlay/overlay-manager.tsx +110 -0
- package/src/components/ui/overlay/popover.tsx +462 -0
- package/src/components/ui/overlay/portal.tsx +79 -0
- package/src/components/ui/overlay/tooltip.tsx +303 -0
- package/src/components/ui/overlay/types.ts +196 -0
- package/src/components/ui/performance-demo.tsx +596 -0
- package/src/components/ui/semantic-input-system-demo.tsx +502 -0
- package/src/components/ui/semantic-input-system-demo.tsx.disabled +873 -0
- package/src/components/ui/tablet-layout.tsx +192 -0
- package/src/components/ui/theme-customizer.tsx +386 -0
- package/src/components/ui/theme-preview.tsx +310 -0
- package/src/components/ui/theme-switcher.tsx +264 -0
- package/src/components/ui/theme-toggle.tsx +38 -0
- package/src/components/ui/token-demo.tsx +195 -0
- package/src/components/ui/touch-demo.tsx +462 -0
- package/src/components/ui/touch-friendly-interface-demo.tsx +519 -0
- package/src/components/ui/touch-friendly-interface.tsx +296 -0
- package/src/hooks/index.ts +190 -0
- package/src/hooks/use-accessibility-support.ts +518 -0
- package/src/hooks/use-adaptive-layout.ts +289 -0
- package/src/hooks/use-advanced-patterns.ts +294 -0
- package/src/hooks/use-advanced-transition-system.ts +393 -0
- package/src/hooks/use-animation-profile.ts +288 -0
- package/src/hooks/use-battery-animations.ts +384 -0
- package/src/hooks/use-battery-conscious-loading.ts +475 -0
- package/src/hooks/use-battery-optimization.ts +330 -0
- package/src/hooks/use-battery-status.ts +299 -0
- package/src/hooks/use-component-performance.ts +344 -0
- package/src/hooks/use-device-loading-states.ts +459 -0
- package/src/hooks/use-device.tsx +110 -0
- package/src/hooks/use-enterprise-mobile-experience.ts +488 -0
- package/src/hooks/use-form-feedback.ts +403 -0
- package/src/hooks/use-form-performance.ts +513 -0
- package/src/hooks/use-frame-rate.ts +251 -0
- package/src/hooks/use-gestures.ts +338 -0
- package/src/hooks/use-hardware-acceleration.ts +341 -0
- package/src/hooks/use-input-accessibility.ts +455 -0
- package/src/hooks/use-input-performance.ts +506 -0
- package/src/hooks/use-layout-performance.ts +319 -0
- package/src/hooks/use-loading-accessibility.ts +535 -0
- package/src/hooks/use-loading-performance.ts +473 -0
- package/src/hooks/use-memory-usage.ts +287 -0
- package/src/hooks/use-mobile-form-layout.ts +464 -0
- package/src/hooks/use-mobile-form-validation.ts +518 -0
- package/src/hooks/use-mobile-keyboard-optimization.ts +472 -0
- package/src/hooks/use-mobile-layout.ts +302 -0
- package/src/hooks/use-mobile-optimization.ts +406 -0
- package/src/hooks/use-mobile-skeleton.ts +402 -0
- package/src/hooks/use-mobile-touch.ts +414 -0
- package/src/hooks/use-performance-throttling.ts +348 -0
- package/src/hooks/use-performance.ts +316 -0
- package/src/hooks/use-reusable-architecture.ts +414 -0
- package/src/hooks/use-semantic-input-types.ts +357 -0
- package/src/hooks/use-semantic-input.ts +565 -0
- package/src/hooks/use-tablet-layout.ts +384 -0
- package/src/hooks/use-touch-friendly-input.ts +524 -0
- package/src/hooks/use-touch-friendly-interface.ts +331 -0
- package/src/hooks/use-touch-optimization.ts +375 -0
- package/src/index.ts +279 -279
- package/src/lib/utils.ts +6 -0
- package/src/themes/README.md +272 -0
- package/src/themes/ThemeContext.tsx +31 -0
- package/src/themes/ThemeProvider.tsx +232 -0
- package/src/themes/accessibility/index.ts +27 -0
- package/src/themes/accessibility.ts +259 -0
- package/src/themes/aria-patterns.ts +420 -0
- package/src/themes/base-themes.ts +55 -0
- package/src/themes/colorManager.ts +380 -0
- package/src/themes/examples/dark-theme.ts +154 -0
- package/src/themes/examples/minimal-theme.ts +108 -0
- package/src/themes/focus-management.ts +701 -0
- package/src/themes/fontLoader.ts +201 -0
- package/src/themes/high-contrast.ts +621 -0
- package/src/themes/index.ts +19 -0
- package/src/themes/inheritance.ts +227 -0
- package/src/themes/keyboard-navigation.ts +550 -0
- package/src/themes/motion-reduction.ts +662 -0
- package/src/themes/navigation.ts +238 -0
- package/src/themes/screen-reader.ts +645 -0
- package/src/themes/systemThemeDetector.ts +182 -0
- package/src/themes/themeCSSUpdater.ts +262 -0
- package/src/themes/themePersistence.ts +238 -0
- package/src/themes/themes/default.ts +586 -0
- package/src/themes/themes/harvey.ts +554 -0
- package/src/themes/themes/stan-design.ts +683 -0
- package/src/themes/types.ts +460 -0
- package/src/themes/useSystemTheme.ts +48 -0
- package/src/themes/useTheme.ts +87 -0
- package/src/themes/validation.ts +462 -0
- package/src/tokens/index.ts +34 -0
- package/src/tokens/tokenExporter.ts +397 -0
- package/src/tokens/tokenGenerator.ts +276 -0
- package/src/tokens/tokenManager.ts +248 -0
- package/src/tokens/tokenValidator.ts +543 -0
- package/src/tokens/types.ts +78 -0
- package/src/utils/bundle-analyzer.ts +260 -0
- package/src/utils/bundle-splitting.ts +483 -0
- package/src/utils/lazy-loading.ts +441 -0
- package/src/utils/performance-monitor.ts +513 -0
- package/src/utils/tree-shaking.ts +274 -0
|
@@ -0,0 +1,513 @@
|
|
|
1
|
+
// Performance monitoring utilities for tracking various metrics
|
|
2
|
+
export interface PerformanceMetric {
|
|
3
|
+
name: string;
|
|
4
|
+
value: number;
|
|
5
|
+
unit: string;
|
|
6
|
+
timestamp: number;
|
|
7
|
+
metadata?: Record<string, any>;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface PerformanceThreshold {
|
|
11
|
+
name: string;
|
|
12
|
+
warning: number;
|
|
13
|
+
critical: number;
|
|
14
|
+
unit: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PerformanceReport {
|
|
18
|
+
metrics: PerformanceMetric[];
|
|
19
|
+
thresholds: PerformanceThreshold[];
|
|
20
|
+
violations: Array<{
|
|
21
|
+
metric: string;
|
|
22
|
+
value: number;
|
|
23
|
+
threshold: number;
|
|
24
|
+
severity: 'warning' | 'critical';
|
|
25
|
+
}>;
|
|
26
|
+
summary: {
|
|
27
|
+
totalMetrics: number;
|
|
28
|
+
warnings: number;
|
|
29
|
+
criticals: number;
|
|
30
|
+
averagePerformance: number;
|
|
31
|
+
};
|
|
32
|
+
timestamp: number;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface PerformanceMonitorConfig {
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
autoCollect: boolean;
|
|
38
|
+
collectionInterval: number;
|
|
39
|
+
maxMetrics: number;
|
|
40
|
+
thresholds: PerformanceThreshold[];
|
|
41
|
+
enableWebVitals: boolean;
|
|
42
|
+
enableMemoryMonitoring: boolean;
|
|
43
|
+
enableNetworkMonitoring: boolean;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class PerformanceMonitor {
|
|
47
|
+
private config: PerformanceMonitorConfig;
|
|
48
|
+
private metrics: PerformanceMetric[];
|
|
49
|
+
private thresholds: Map<string, PerformanceThreshold>;
|
|
50
|
+
private observers: Map<string, PerformanceObserver>;
|
|
51
|
+
private collectionTimer: NodeJS.Timeout | null;
|
|
52
|
+
private startTime: number;
|
|
53
|
+
|
|
54
|
+
constructor(config: PerformanceMonitorConfig = {
|
|
55
|
+
enabled: true,
|
|
56
|
+
autoCollect: true,
|
|
57
|
+
collectionInterval: 5000, // 5 seconds
|
|
58
|
+
maxMetrics: 1000,
|
|
59
|
+
thresholds: [],
|
|
60
|
+
enableWebVitals: true,
|
|
61
|
+
enableMemoryMonitoring: true,
|
|
62
|
+
enableNetworkMonitoring: true
|
|
63
|
+
}) {
|
|
64
|
+
this.config = config;
|
|
65
|
+
this.metrics = [];
|
|
66
|
+
this.thresholds = new Map();
|
|
67
|
+
this.observers = new Map();
|
|
68
|
+
this.collectionTimer = null;
|
|
69
|
+
this.startTime = Date.now();
|
|
70
|
+
|
|
71
|
+
// Initialize thresholds
|
|
72
|
+
this.config.thresholds.forEach(threshold => {
|
|
73
|
+
this.thresholds.set(threshold.name, threshold);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
// Set up default thresholds if none provided
|
|
77
|
+
if (this.config.thresholds.length === 0) {
|
|
78
|
+
this.setupDefaultThresholds();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Start auto-collection if enabled
|
|
82
|
+
if (this.config.autoCollect) {
|
|
83
|
+
this.startAutoCollection();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Set up performance observers
|
|
87
|
+
if (this.config.enableWebVitals) {
|
|
88
|
+
this.setupWebVitalsObserver();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Set up default performance thresholds
|
|
93
|
+
private setupDefaultThresholds(): void {
|
|
94
|
+
const defaultThresholds: PerformanceThreshold[] = [
|
|
95
|
+
{ name: 'themeSwitchTime', warning: 100, critical: 300, unit: 'ms' },
|
|
96
|
+
{ name: 'componentRenderTime', warning: 50, critical: 150, unit: 'ms' },
|
|
97
|
+
{ name: 'bundleLoadTime', warning: 2000, critical: 5000, unit: 'ms' },
|
|
98
|
+
{ name: 'memoryUsage', warning: 100, critical: 500, unit: 'MB' },
|
|
99
|
+
{ name: 'firstContentfulPaint', warning: 1000, critical: 3000, unit: 'ms' },
|
|
100
|
+
{ name: 'largestContentfulPaint', warning: 2500, critical: 4000, unit: 'ms' }
|
|
101
|
+
];
|
|
102
|
+
|
|
103
|
+
defaultThresholds.forEach(threshold => {
|
|
104
|
+
this.thresholds.set(threshold.name, threshold);
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Set up Web Vitals observer
|
|
109
|
+
private setupWebVitalsObserver(): void {
|
|
110
|
+
if (!('PerformanceObserver' in window)) return;
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
// First Contentful Paint
|
|
114
|
+
const fcpObserver = new PerformanceObserver((list) => {
|
|
115
|
+
const entries = list.getEntries();
|
|
116
|
+
entries.forEach(entry => {
|
|
117
|
+
this.recordMetric('firstContentfulPaint', entry.startTime, 'ms', {
|
|
118
|
+
entryType: entry.entryType,
|
|
119
|
+
name: entry.name
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
fcpObserver.observe({ entryTypes: ['paint'] });
|
|
124
|
+
|
|
125
|
+
// Largest Contentful Paint
|
|
126
|
+
const lcpObserver = new PerformanceObserver((list) => {
|
|
127
|
+
const entries = list.getEntries();
|
|
128
|
+
entries.forEach(entry => {
|
|
129
|
+
this.recordMetric('largestContentfulPaint', entry.startTime, 'ms', {
|
|
130
|
+
entryType: entry.entryType,
|
|
131
|
+
name: entry.name
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
lcpObserver.observe({ entryTypes: ['largest-contentful-paint'] });
|
|
136
|
+
|
|
137
|
+
// First Input Delay
|
|
138
|
+
const fidObserver = new PerformanceObserver((list) => {
|
|
139
|
+
const entries = list.getEntries();
|
|
140
|
+
entries.forEach(entry => {
|
|
141
|
+
if ('processingStart' in entry) {
|
|
142
|
+
const fidEntry = entry as any;
|
|
143
|
+
this.recordMetric('firstInputDelay', fidEntry.processingStart - fidEntry.startTime, 'ms', {
|
|
144
|
+
entryType: entry.entryType,
|
|
145
|
+
name: entry.name
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
fidObserver.observe({ entryTypes: ['first-input'] });
|
|
151
|
+
|
|
152
|
+
// Cumulative Layout Shift
|
|
153
|
+
const clsObserver = new PerformanceObserver((list) => {
|
|
154
|
+
const entries = list.getEntries();
|
|
155
|
+
entries.forEach(entry => {
|
|
156
|
+
if ('value' in entry) {
|
|
157
|
+
const clsEntry = entry as any;
|
|
158
|
+
this.recordMetric('cumulativeLayoutShift', clsEntry.value, 'score', {
|
|
159
|
+
entryType: entry.entryType,
|
|
160
|
+
name: entry.name
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
clsObserver.observe({ entryTypes: ['layout-shift'] });
|
|
166
|
+
|
|
167
|
+
this.observers.set('webVitals', fcpObserver);
|
|
168
|
+
} catch (error) {
|
|
169
|
+
console.warn('Failed to set up Web Vitals observer:', error);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Record a performance metric
|
|
174
|
+
recordMetric(name: string, value: number, unit: string, metadata?: Record<string, any>): void {
|
|
175
|
+
if (!this.config.enabled) return;
|
|
176
|
+
|
|
177
|
+
const metric: PerformanceMetric = {
|
|
178
|
+
name,
|
|
179
|
+
value,
|
|
180
|
+
unit,
|
|
181
|
+
timestamp: Date.now(),
|
|
182
|
+
metadata
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
this.metrics.push(metric);
|
|
186
|
+
|
|
187
|
+
// Enforce max metrics limit
|
|
188
|
+
if (this.metrics.length > this.config.maxMetrics) {
|
|
189
|
+
this.metrics = this.metrics.slice(-this.config.maxMetrics);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Check thresholds and emit warnings
|
|
193
|
+
this.checkThresholds(metric);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Check if a metric violates any thresholds
|
|
197
|
+
private checkThresholds(metric: PerformanceMetric): void {
|
|
198
|
+
const threshold = this.thresholds.get(metric.name);
|
|
199
|
+
if (!threshold) return;
|
|
200
|
+
|
|
201
|
+
if (metric.value >= threshold.critical) {
|
|
202
|
+
this.emitThresholdViolation(metric, threshold, 'critical');
|
|
203
|
+
} else if (metric.value >= threshold.warning) {
|
|
204
|
+
this.emitThresholdViolation(metric, threshold, 'warning');
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Emit threshold violation event
|
|
209
|
+
private emitThresholdViolation(metric: PerformanceMetric, threshold: PerformanceThreshold, severity: 'warning' | 'critical'): void {
|
|
210
|
+
const event = new CustomEvent('performanceThresholdViolation', {
|
|
211
|
+
detail: {
|
|
212
|
+
metric: metric.name,
|
|
213
|
+
value: metric.value,
|
|
214
|
+
threshold: threshold[severity],
|
|
215
|
+
severity,
|
|
216
|
+
unit: threshold.unit,
|
|
217
|
+
timestamp: metric.timestamp
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
window.dispatchEvent(event);
|
|
222
|
+
|
|
223
|
+
// Log to console
|
|
224
|
+
const logMethod = severity === 'critical' ? 'error' : 'warn';
|
|
225
|
+
console[logMethod](`Performance ${severity}: ${metric.name} = ${metric.value}${metric.unit} (threshold: ${threshold[severity]}${threshold.unit})`);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Measure theme switch performance
|
|
229
|
+
measureThemeSwitch(callback: () => void): number {
|
|
230
|
+
const startTime = performance.now();
|
|
231
|
+
callback();
|
|
232
|
+
const endTime = performance.now();
|
|
233
|
+
const duration = endTime - startTime;
|
|
234
|
+
|
|
235
|
+
this.recordMetric('themeSwitchTime', duration, 'ms');
|
|
236
|
+
return duration;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Measure component render performance
|
|
240
|
+
measureComponentRender(callback: () => void): number {
|
|
241
|
+
const startTime = performance.now();
|
|
242
|
+
callback();
|
|
243
|
+
const endTime = performance.now();
|
|
244
|
+
const duration = endTime - startTime;
|
|
245
|
+
|
|
246
|
+
this.recordMetric('componentRenderTime', duration, 'ms');
|
|
247
|
+
return duration;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Measure bundle load performance
|
|
251
|
+
measureBundleLoad(callback: () => Promise<any>): Promise<number> {
|
|
252
|
+
const startTime = performance.now();
|
|
253
|
+
return callback().then(() => {
|
|
254
|
+
const endTime = performance.now();
|
|
255
|
+
const duration = endTime - startTime;
|
|
256
|
+
this.recordMetric('bundleLoadTime', duration, 'ms');
|
|
257
|
+
return duration;
|
|
258
|
+
});
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Measure memory usage
|
|
262
|
+
measureMemoryUsage(): number | null {
|
|
263
|
+
if (!this.config.enableMemoryMonitoring || !('memory' in performance)) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const memory = (performance as any).memory;
|
|
268
|
+
const usedMB = memory.usedJSHeapSize / 1024 / 1024;
|
|
269
|
+
|
|
270
|
+
this.recordMetric('memoryUsage', usedMB, 'MB', {
|
|
271
|
+
totalJSHeapSize: memory.totalJSHeapSize / 1024 / 1024,
|
|
272
|
+
jsHeapSizeLimit: memory.jsHeapSizeLimit / 1024 / 1024
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
return usedMB;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Measure network performance
|
|
279
|
+
measureNetworkPerformance(): void {
|
|
280
|
+
if (!this.config.enableNetworkMonitoring || !('getEntriesByType' in performance)) return;
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
const navigationEntries = performance.getEntriesByType('navigation');
|
|
284
|
+
if (navigationEntries.length > 0) {
|
|
285
|
+
const nav = navigationEntries[0] as PerformanceNavigationTiming;
|
|
286
|
+
|
|
287
|
+
this.recordMetric('domContentLoaded', nav.domContentLoadedEventEnd - nav.domContentLoadedEventStart, 'ms');
|
|
288
|
+
this.recordMetric('loadComplete', nav.loadEventEnd - nav.loadEventStart, 'ms');
|
|
289
|
+
this.recordMetric('domInteractive', nav.domInteractive - nav.fetchStart, 'ms');
|
|
290
|
+
this.recordMetric('firstByte', nav.responseStart - nav.fetchStart, 'ms');
|
|
291
|
+
}
|
|
292
|
+
} catch (error) {
|
|
293
|
+
console.warn('Failed to measure network performance:', error);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Start automatic metric collection
|
|
298
|
+
startAutoCollection(): void {
|
|
299
|
+
if (this.collectionTimer) return;
|
|
300
|
+
|
|
301
|
+
this.collectionTimer = setInterval(() => {
|
|
302
|
+
// Collect memory usage
|
|
303
|
+
this.measureMemoryUsage();
|
|
304
|
+
|
|
305
|
+
// Collect network performance
|
|
306
|
+
this.measureNetworkPerformance();
|
|
307
|
+
|
|
308
|
+
// Collect custom metrics
|
|
309
|
+
this.collectCustomMetrics();
|
|
310
|
+
}, this.config.collectionInterval);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Stop automatic metric collection
|
|
314
|
+
stopAutoCollection(): void {
|
|
315
|
+
if (this.collectionTimer) {
|
|
316
|
+
clearInterval(this.collectionTimer);
|
|
317
|
+
this.collectionTimer = null;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Collect custom metrics
|
|
322
|
+
private collectCustomMetrics(): void {
|
|
323
|
+
// Record uptime
|
|
324
|
+
const uptime = Date.now() - this.startTime;
|
|
325
|
+
this.recordMetric('uptime', uptime, 'ms');
|
|
326
|
+
|
|
327
|
+
// Record metric count
|
|
328
|
+
this.recordMetric('metricCount', this.metrics.length, 'count');
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Get metrics by name
|
|
332
|
+
getMetricsByName(name: string): PerformanceMetric[] {
|
|
333
|
+
return this.metrics.filter(metric => metric.name === name);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Get latest metric by name
|
|
337
|
+
getLatestMetric(name: string): PerformanceMetric | null {
|
|
338
|
+
const metrics = this.getMetricsByName(name);
|
|
339
|
+
return metrics.length > 0 ? metrics[metrics.length - 1] : null;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// Get metrics within time range
|
|
343
|
+
getMetricsInRange(startTime: number, endTime: number): PerformanceMetric[] {
|
|
344
|
+
return this.metrics.filter(metric =>
|
|
345
|
+
metric.timestamp >= startTime && metric.timestamp <= endTime
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Calculate average for a metric
|
|
350
|
+
getAverageMetric(name: string, timeRange?: { start: number; end: number }): number {
|
|
351
|
+
let metrics = this.getMetricsByName(name);
|
|
352
|
+
|
|
353
|
+
if (timeRange) {
|
|
354
|
+
metrics = this.getMetricsInRange(timeRange.start, timeRange.end);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (metrics.length === 0) return 0;
|
|
358
|
+
|
|
359
|
+
const sum = metrics.reduce((total, metric) => total + metric.value, 0);
|
|
360
|
+
return sum / metrics.length;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Get performance report
|
|
364
|
+
getPerformanceReport(): PerformanceReport {
|
|
365
|
+
const violations: Array<{
|
|
366
|
+
metric: string;
|
|
367
|
+
value: number;
|
|
368
|
+
threshold: number;
|
|
369
|
+
severity: 'warning' | 'critical';
|
|
370
|
+
}> = [];
|
|
371
|
+
|
|
372
|
+
// Check all metrics against thresholds
|
|
373
|
+
this.metrics.forEach(metric => {
|
|
374
|
+
const threshold = this.thresholds.get(metric.name);
|
|
375
|
+
if (threshold) {
|
|
376
|
+
if (metric.value >= threshold.critical) {
|
|
377
|
+
violations.push({
|
|
378
|
+
metric: metric.name,
|
|
379
|
+
value: metric.value,
|
|
380
|
+
threshold: threshold.critical,
|
|
381
|
+
severity: 'critical'
|
|
382
|
+
});
|
|
383
|
+
} else if (metric.value >= threshold.warning) {
|
|
384
|
+
violations.push({
|
|
385
|
+
metric: metric.name,
|
|
386
|
+
value: metric.value,
|
|
387
|
+
threshold: threshold.warning,
|
|
388
|
+
severity: 'warning'
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
const warnings = violations.filter(v => v.severity === 'warning').length;
|
|
395
|
+
const criticals = violations.filter(v => v.severity === 'critical').length;
|
|
396
|
+
|
|
397
|
+
// Calculate average performance (lower is better for time-based metrics)
|
|
398
|
+
const timeMetrics = this.metrics.filter(m => m.unit === 'ms');
|
|
399
|
+
const averagePerformance = timeMetrics.length > 0
|
|
400
|
+
? timeMetrics.reduce((sum, m) => sum + m.value, 0) / timeMetrics.length
|
|
401
|
+
: 0;
|
|
402
|
+
|
|
403
|
+
return {
|
|
404
|
+
metrics: [...this.metrics],
|
|
405
|
+
thresholds: Array.from(this.thresholds.values()),
|
|
406
|
+
violations,
|
|
407
|
+
summary: {
|
|
408
|
+
totalMetrics: this.metrics.length,
|
|
409
|
+
warnings,
|
|
410
|
+
criticals,
|
|
411
|
+
averagePerformance
|
|
412
|
+
},
|
|
413
|
+
timestamp: Date.now()
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Add custom threshold
|
|
418
|
+
addThreshold(threshold: PerformanceThreshold): void {
|
|
419
|
+
this.thresholds.set(threshold.name, threshold);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Remove threshold
|
|
423
|
+
removeThreshold(name: string): void {
|
|
424
|
+
this.thresholds.delete(name);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Clear all metrics
|
|
428
|
+
clearMetrics(): void {
|
|
429
|
+
this.metrics = [];
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Export metrics to JSON
|
|
433
|
+
exportMetrics(): string {
|
|
434
|
+
return JSON.stringify({
|
|
435
|
+
config: this.config,
|
|
436
|
+
metrics: this.metrics,
|
|
437
|
+
thresholds: Array.from(this.thresholds.values()),
|
|
438
|
+
report: this.getPerformanceReport(),
|
|
439
|
+
timestamp: new Date().toISOString()
|
|
440
|
+
}, null, 2);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Get configuration
|
|
444
|
+
getConfig(): PerformanceMonitorConfig {
|
|
445
|
+
return { ...this.config };
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Update configuration
|
|
449
|
+
updateConfig(newConfig: Partial<PerformanceMonitorConfig>): void {
|
|
450
|
+
this.config = { ...this.config, ...newConfig };
|
|
451
|
+
|
|
452
|
+
// Restart auto-collection if interval changed
|
|
453
|
+
if (newConfig.collectionInterval && this.collectionTimer) {
|
|
454
|
+
this.stopAutoCollection();
|
|
455
|
+
this.startAutoCollection();
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// Cleanup resources
|
|
460
|
+
destroy(): void {
|
|
461
|
+
this.stopAutoCollection();
|
|
462
|
+
|
|
463
|
+
// Disconnect observers
|
|
464
|
+
this.observers.forEach(observer => {
|
|
465
|
+
observer.disconnect();
|
|
466
|
+
});
|
|
467
|
+
this.observers.clear();
|
|
468
|
+
|
|
469
|
+
// Clear metrics
|
|
470
|
+
this.clearMetrics();
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
// Create and export default instance
|
|
475
|
+
export const performanceMonitor = new PerformanceMonitor();
|
|
476
|
+
|
|
477
|
+
// Utility function for measuring async operations
|
|
478
|
+
export async function measureAsync<T>(
|
|
479
|
+
name: string,
|
|
480
|
+
operation: () => Promise<T>,
|
|
481
|
+
monitor: PerformanceMonitor = performanceMonitor
|
|
482
|
+
): Promise<T> {
|
|
483
|
+
const startTime = performance.now();
|
|
484
|
+
try {
|
|
485
|
+
const result = await operation();
|
|
486
|
+
const duration = performance.now() - startTime;
|
|
487
|
+
monitor.recordMetric(name, duration, 'ms');
|
|
488
|
+
return result;
|
|
489
|
+
} catch (error) {
|
|
490
|
+
const duration = performance.now() - startTime;
|
|
491
|
+
monitor.recordMetric(name, duration, 'ms');
|
|
492
|
+
throw error;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Utility function for measuring sync operations
|
|
497
|
+
export function measureSync<T>(
|
|
498
|
+
name: string,
|
|
499
|
+
operation: () => T,
|
|
500
|
+
monitor: PerformanceMonitor = performanceMonitor
|
|
501
|
+
): T {
|
|
502
|
+
const startTime = performance.now();
|
|
503
|
+
try {
|
|
504
|
+
const result = operation();
|
|
505
|
+
const duration = performance.now() - startTime;
|
|
506
|
+
monitor.recordMetric(name, duration, 'ms');
|
|
507
|
+
return result;
|
|
508
|
+
} catch (error) {
|
|
509
|
+
const duration = performance.now() - startTime;
|
|
510
|
+
monitor.recordMetric(name, duration, 'ms');
|
|
511
|
+
throw error;
|
|
512
|
+
}
|
|
513
|
+
}
|