@therealtinhtute/baroiplayer 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.
Files changed (244) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +384 -0
  3. package/dist/context/media.js +2 -0
  4. package/dist/context/media.js.map +1 -0
  5. package/dist/context/media.mjs +15 -0
  6. package/dist/context/media.mjs.map +1 -0
  7. package/dist/context/store.js +2 -0
  8. package/dist/context/store.js.map +1 -0
  9. package/dist/context/store.mjs +5 -0
  10. package/dist/context/store.mjs.map +1 -0
  11. package/dist/context-BMteAJDN.js +1140 -0
  12. package/dist/context-BMteAJDN.js.map +1 -0
  13. package/dist/context-DCJ_scmO.cjs +23 -0
  14. package/dist/context-DCJ_scmO.cjs.map +1 -0
  15. package/dist/core-events-B5W_j8yU.js +480 -0
  16. package/dist/core-events-B5W_j8yU.js.map +1 -0
  17. package/dist/core-events-CN0-Vhhq.cjs +2 -0
  18. package/dist/core-events-CN0-Vhhq.cjs.map +1 -0
  19. package/dist/core-plugins-Bq9KLany.js +696 -0
  20. package/dist/core-plugins-Bq9KLany.js.map +1 -0
  21. package/dist/core-plugins-CPDSHMiQ.cjs +2 -0
  22. package/dist/core-plugins-CPDSHMiQ.cjs.map +1 -0
  23. package/dist/core.js +37 -0
  24. package/dist/core.js.map +1 -0
  25. package/dist/core.mjs +1217 -0
  26. package/dist/core.mjs.map +1 -0
  27. package/dist/enhanced-components-DHv0MFvG.js +1073 -0
  28. package/dist/enhanced-components-DHv0MFvG.js.map +1 -0
  29. package/dist/enhanced-components-DTIObgB7.cjs +2 -0
  30. package/dist/enhanced-components-DTIObgB7.cjs.map +1 -0
  31. package/dist/hooks-C_r8wexn.cjs +4 -0
  32. package/dist/hooks-C_r8wexn.cjs.map +1 -0
  33. package/dist/hooks-Da2wVXlF.js +1630 -0
  34. package/dist/hooks-Da2wVXlF.js.map +1 -0
  35. package/dist/index.css +1 -0
  36. package/dist/index.js +112 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/index.mjs +6126 -0
  39. package/dist/index.mjs.map +1 -0
  40. package/dist/minimal-components-DSq5sQpy.js +2002 -0
  41. package/dist/minimal-components-DSq5sQpy.js.map +1 -0
  42. package/dist/minimal-components-DflWiihN.cjs +2 -0
  43. package/dist/minimal-components-DflWiihN.cjs.map +1 -0
  44. package/dist/minimal-components.css +1 -0
  45. package/dist/mona-loading.gif +0 -0
  46. package/dist/performance-monitor-CUW6f5ll.cjs +2 -0
  47. package/dist/performance-monitor-CUW6f5ll.cjs.map +1 -0
  48. package/dist/performance-monitor-rMW1RgV3.js +302 -0
  49. package/dist/performance-monitor-rMW1RgV3.js.map +1 -0
  50. package/dist/utils-BnscpYYd.js +2798 -0
  51. package/dist/utils-BnscpYYd.js.map +1 -0
  52. package/dist/utils-D_czS_0K.cjs +9 -0
  53. package/dist/utils-D_czS_0K.cjs.map +1 -0
  54. package/dist/utils.js +2 -0
  55. package/dist/utils.js.map +1 -0
  56. package/dist/utils.mjs +7 -0
  57. package/dist/utils.mjs.map +1 -0
  58. package/package.json +114 -0
  59. package/src/adapters/videojs-adapter.tsx +470 -0
  60. package/src/assets/mona-loading.gif +0 -0
  61. package/src/components/AnimateLoading.tsx +115 -0
  62. package/src/components/LoadingState.tsx +248 -0
  63. package/src/components/accessibility/keyboard-help-overlay.tsx +213 -0
  64. package/src/components/audio-player.tsx +85 -0
  65. package/src/components/basic/index.ts +37 -0
  66. package/src/components/enhanced/advanced-features.tsx +316 -0
  67. package/src/components/enhanced/debug-panel.tsx +422 -0
  68. package/src/components/enhanced/enhanced-controls.tsx +600 -0
  69. package/src/components/enhanced/enhanced-media-player.tsx +509 -0
  70. package/src/components/enhanced/error-handling.tsx +345 -0
  71. package/src/components/enhanced/index.ts +39 -0
  72. package/src/components/enhanced/media-player-enhanced.tsx +349 -0
  73. package/src/components/examples/basic-video-player.tsx +55 -0
  74. package/src/components/examples/custom-controls-example.tsx +63 -0
  75. package/src/components/examples/enhanced-controls-example.tsx +114 -0
  76. package/src/components/examples/enhanced-media-player-example.tsx +293 -0
  77. package/src/components/examples/loading-states-demo.tsx +142 -0
  78. package/src/components/examples/react-19-demo.tsx +381 -0
  79. package/src/components/examples/volume-orientation-demo.tsx +114 -0
  80. package/src/components/icon-demo.tsx +209 -0
  81. package/src/components/icons/index.tsx +166 -0
  82. package/src/components/icons/svg-icons/ArrowLeftIcon.tsx +18 -0
  83. package/src/components/icons/svg-icons/ArrowRightIcon.tsx +18 -0
  84. package/src/components/icons/svg-icons/AudioIcon.tsx +22 -0
  85. package/src/components/icons/svg-icons/BackwardIcon.tsx +12 -0
  86. package/src/components/icons/svg-icons/CameraIcon.tsx +19 -0
  87. package/src/components/icons/svg-icons/CheckIcon.tsx +19 -0
  88. package/src/components/icons/svg-icons/EnablePiPIcon.tsx +17 -0
  89. package/src/components/icons/svg-icons/ForwardIcon.tsx +12 -0
  90. package/src/components/icons/svg-icons/FullscreenEnterIcon.tsx +23 -0
  91. package/src/components/icons/svg-icons/FullscreenExitIcon.tsx +23 -0
  92. package/src/components/icons/svg-icons/LoadingIcon.tsx +32 -0
  93. package/src/components/icons/svg-icons/PauseIcon.tsx +23 -0
  94. package/src/components/icons/svg-icons/PlayIcon.tsx +21 -0
  95. package/src/components/icons/svg-icons/PlaybackSpeedIcon.tsx +23 -0
  96. package/src/components/icons/svg-icons/QualityIcon.tsx +21 -0
  97. package/src/components/icons/svg-icons/SettingsIcon.tsx +12 -0
  98. package/src/components/icons/svg-icons/SliderIcon.tsx +21 -0
  99. package/src/components/icons/svg-icons/SubtitleIcon.tsx +21 -0
  100. package/src/components/icons/svg-icons/SubtitleOffIcon.tsx +25 -0
  101. package/src/components/icons/svg-icons/UnPiPIcon.tsx +17 -0
  102. package/src/components/icons/svg-icons/VolumeMutedIcon.tsx +16 -0
  103. package/src/components/icons/svg-icons/VolumeOneIcon.tsx +22 -0
  104. package/src/components/icons/svg-icons/VolumeThreeIcon.tsx +16 -0
  105. package/src/components/icons/svg-icons/VolumeTwoIcon.tsx +16 -0
  106. package/src/components/legacy.tsx +93 -0
  107. package/src/components/media-metadata-display.tsx +100 -0
  108. package/src/components/media-player-audio.tsx +39 -0
  109. package/src/components/media-player-controls.tsx +226 -0
  110. package/src/components/media-player-error.tsx +163 -0
  111. package/src/components/media-player-fullscreen.tsx +109 -0
  112. package/src/components/media-player-hls.tsx +111 -0
  113. package/src/components/media-player-loading.tsx +76 -0
  114. package/src/components/media-player-play.tsx +75 -0
  115. package/src/components/media-player-root.tsx +340 -0
  116. package/src/components/media-player-seek.tsx +168 -0
  117. package/src/components/media-player-time.tsx +99 -0
  118. package/src/components/media-player-tooltip.tsx +58 -0
  119. package/src/components/media-player-video.tsx +39 -0
  120. package/src/components/media-player-volume.tsx +155 -0
  121. package/src/components/media-player.tsx +257 -0
  122. package/src/components/media-suspense.tsx +362 -0
  123. package/src/components/player-settings-form.tsx +163 -0
  124. package/src/components/react-player-wrapper.tsx +651 -0
  125. package/src/components/simple-video-player.tsx +60 -0
  126. package/src/components/ui/enhanced-seek-bar.tsx +235 -0
  127. package/src/components/ui/fullscreen-button.tsx +60 -0
  128. package/src/components/ui/media-resource-preloader.tsx +395 -0
  129. package/src/components/ui/play-button.tsx +107 -0
  130. package/src/components/ui/seek-bar.tsx +161 -0
  131. package/src/components/ui/time-display.tsx +40 -0
  132. package/src/components/ui/volume-control.tsx +143 -0
  133. package/src/components/unified/base-media-player.tsx +246 -0
  134. package/src/components/unified/media-player.tsx +75 -0
  135. package/src/components/variants/minimal/AccessibleMinimalPlayer.tsx +561 -0
  136. package/src/components/variants/minimal/EnhancedMinimalMediaPlayer.tsx +552 -0
  137. package/src/components/variants/minimal/MinimalControls.tsx +142 -0
  138. package/src/components/variants/minimal/MinimalLoading.tsx +32 -0
  139. package/src/components/variants/minimal/MinimalMediaPlayer.tsx +315 -0
  140. package/src/components/variants/minimal/MinimalOverlay.tsx +64 -0
  141. package/src/components/variants/minimal/MinimalPlayButton.tsx +65 -0
  142. package/src/components/variants/minimal/MinimalSeekBar.tsx +150 -0
  143. package/src/components/variants/minimal/MinimalVolumeControl.tsx +80 -0
  144. package/src/components/variants/minimal/SimpleMinimalPlayer.tsx +210 -0
  145. package/src/components/variants/minimal/components/CenterPlayButton.tsx +22 -0
  146. package/src/components/variants/minimal/components/ControlBar.tsx +146 -0
  147. package/src/components/variants/minimal/components/EnhancedSettingsMenu.tsx +361 -0
  148. package/src/components/variants/minimal/components/PlaybackControls.tsx +51 -0
  149. package/src/components/variants/minimal/components/RightControls.tsx +173 -0
  150. package/src/components/variants/minimal/components/SeekBar.tsx +131 -0
  151. package/src/components/variants/minimal/components/SettingsDropdown.tsx +70 -0
  152. package/src/components/variants/minimal/components/SubtitleDisplay.tsx +21 -0
  153. package/src/components/variants/minimal/components/TimeDisplay.tsx +18 -0
  154. package/src/components/variants/minimal/components/VideoElement.tsx +40 -0
  155. package/src/components/variants/minimal/components/VolumeControl.tsx +217 -0
  156. package/src/components/variants/minimal/index.ts +23 -0
  157. package/src/components/video-player.tsx +100 -0
  158. package/src/context/error-handling.ts +394 -0
  159. package/src/context/media-context.tsx +208 -0
  160. package/src/context/media-provider.tsx +265 -0
  161. package/src/context/media-store.ts +379 -0
  162. package/src/context/persistence.ts +453 -0
  163. package/src/core/events/event-system.ts +442 -0
  164. package/src/core/events/hooks.ts +302 -0
  165. package/src/core/events/index.ts +22 -0
  166. package/src/core/events/middleware.ts +269 -0
  167. package/src/core/index.ts +22 -0
  168. package/src/core/optimization/bundle-optimizer.tsx +308 -0
  169. package/src/core/performance/cicd.ts +96 -0
  170. package/src/core/performance/index.ts +38 -0
  171. package/src/core/performance/media-loading-optimizer.ts +538 -0
  172. package/src/core/performance/performance-budgets.ts +432 -0
  173. package/src/core/performance/performance-monitor.tsx +476 -0
  174. package/src/core/performance/performance-testing.ts +459 -0
  175. package/src/core/plugins/base-plugin.ts +224 -0
  176. package/src/core/plugins/enhanced-plugin-system.ts +411 -0
  177. package/src/core/plugins/examples/analytics-plugin.ts +242 -0
  178. package/src/core/plugins/index.ts +39 -0
  179. package/src/core/plugins/registry.ts +327 -0
  180. package/src/core/plugins/types.ts +159 -0
  181. package/src/core/plugins/utils.ts +316 -0
  182. package/src/core/progressive-loading.tsx +322 -0
  183. package/src/core/providers/enhanced-hls-provider.ts +339 -0
  184. package/src/core/providers/service-provider.tsx +168 -0
  185. package/src/core/utils.tsx +38 -0
  186. package/src/features/media-session/media-session-manager.ts +429 -0
  187. package/src/features/picture-in-picture/pip-manager.ts +429 -0
  188. package/src/features/playlist/playlist-manager.ts +606 -0
  189. package/src/features/subtitles/subtitle-manager.ts +441 -0
  190. package/src/features/subtitles/subtitle-parser.ts +280 -0
  191. package/src/features/thumbnails/thumbnail-generator.ts +519 -0
  192. package/src/hooks/accessibility/index.ts +27 -0
  193. package/src/hooks/accessibility/use-accessibility-preferences.ts +188 -0
  194. package/src/hooks/accessibility/use-focus-management.ts +176 -0
  195. package/src/hooks/accessibility/use-keyboard-shortcuts.ts +144 -0
  196. package/src/hooks/accessibility/use-screen-reader.ts +182 -0
  197. package/src/hooks/core/index.ts +47 -0
  198. package/src/hooks/legacy.ts +27 -0
  199. package/src/hooks/use-enhanced-media-player.ts +468 -0
  200. package/src/hooks/use-intelligent-preloading.ts +358 -0
  201. package/src/hooks/use-keyboard-controls.ts +115 -0
  202. package/src/hooks/use-media-actions.ts +175 -0
  203. package/src/hooks/use-media-metadata.ts +89 -0
  204. package/src/hooks/use-media-optimistic.ts +270 -0
  205. package/src/hooks/use-media-player.ts +312 -0
  206. package/src/hooks/use-media-state.ts +190 -0
  207. package/src/hooks/use-memory-optimization.ts +358 -0
  208. package/src/hooks/use-optimistic-controls.ts +92 -0
  209. package/src/hooks/use-optimized-media.ts +263 -0
  210. package/src/hooks/use-touch-gestures.ts +417 -0
  211. package/src/hooks/useEnhancedSubtitles.ts +258 -0
  212. package/src/hooks/useKeyboardShortcuts.ts +74 -0
  213. package/src/hooks/useMediaControls.ts +52 -0
  214. package/src/hooks/useScreenshot.ts +57 -0
  215. package/src/hooks/useSubtitles.ts +91 -0
  216. package/src/hooks/useVideoPlayer.ts +232 -0
  217. package/src/icons/components/FullscreenIcon.tsx +37 -0
  218. package/src/icons/components/SettingsIcon.tsx +38 -0
  219. package/src/icons/components/animated/LoadingIcon.tsx +43 -0
  220. package/src/icons/components/animated/PlayPauseIcon.tsx +50 -0
  221. package/src/icons/components/media/PauseIcon.tsx +39 -0
  222. package/src/icons/components/media/PlayIcon.tsx +38 -0
  223. package/src/icons/components/volume/VolumeIcon.tsx +39 -0
  224. package/src/icons/components/volume/VolumeOffIcon.tsx +39 -0
  225. package/src/icons/index.ts +56 -0
  226. package/src/icons/provider.tsx +64 -0
  227. package/src/icons/types.ts +40 -0
  228. package/src/index-enhanced.ts +35 -0
  229. package/src/index-legacy.ts +326 -0
  230. package/src/index.css +102 -0
  231. package/src/index.ts +216 -0
  232. package/src/lib/deprecation.ts +51 -0
  233. package/src/lib/format-time.ts +37 -0
  234. package/src/lib/index.ts +3 -0
  235. package/src/lib/utils.ts +6 -0
  236. package/src/styles/globals.css +462 -0
  237. package/src/styles/minimal-player.css +589 -0
  238. package/src/styles/pixel-art.css +46 -0
  239. package/src/styles/videojs.css +100 -0
  240. package/src/test/setup.ts +79 -0
  241. package/src/types/events.ts +47 -0
  242. package/src/types/formats.ts +95 -0
  243. package/src/types/index.ts +190 -0
  244. package/src/types/videojs-plugins.d.ts +14 -0
@@ -0,0 +1,432 @@
1
+ // Performance budget configuration
2
+ export interface PerformanceBudget {
3
+ name: string;
4
+ category: 'bundle' | 'runtime' | 'network' | 'memory';
5
+ limit: number;
6
+ unit: 'bytes' | 'ms' | 'score' | 'percent';
7
+ severity: 'error' | 'warning' | 'info';
8
+ description: string;
9
+ }
10
+
11
+ // Performance budget violation
12
+ export interface BudgetViolation {
13
+ budget: PerformanceBudget;
14
+ actualValue: number;
15
+ overageAmount: number;
16
+ overagePercentage: number;
17
+ timestamp: number;
18
+ }
19
+
20
+ // Alert configuration
21
+ export interface AlertConfig {
22
+ enabled: boolean;
23
+ channels: AlertChannel[];
24
+ threshold: number; // Number of violations before alerting
25
+ cooldown: number; // Minimum time between alerts (ms)
26
+ }
27
+
28
+ export interface AlertChannel {
29
+ type: 'console' | 'webhook' | 'email' | 'slack';
30
+ config: {
31
+ url?: string;
32
+ email?: string;
33
+ token?: string;
34
+ channel?: string;
35
+ };
36
+ }
37
+
38
+ // Default performance budgets for media player
39
+ export const defaultPerformanceBudgets: PerformanceBudget[] = [
40
+ // Bundle size budgets
41
+ {
42
+ name: 'Main Bundle Size (ESM)',
43
+ category: 'bundle',
44
+ limit: 250 * 1024, // 250KB
45
+ unit: 'bytes',
46
+ severity: 'error',
47
+ description: 'Main bundle should stay under 250KB for fast initial loading'
48
+ },
49
+ {
50
+ name: 'Main Bundle Size (CJS)',
51
+ category: 'bundle',
52
+ limit: 200 * 1024, // 200KB
53
+ unit: 'bytes',
54
+ severity: 'error',
55
+ description: 'CJS bundle should be smaller due to better compression'
56
+ },
57
+ {
58
+ name: 'Core Module Size',
59
+ category: 'bundle',
60
+ limit: 15 * 1024, // 15KB
61
+ unit: 'bytes',
62
+ severity: 'warning',
63
+ description: 'Core architecture should remain lightweight'
64
+ },
65
+ {
66
+ name: 'Enhanced Components Size',
67
+ category: 'bundle',
68
+ limit: 40 * 1024, // 40KB
69
+ unit: 'bytes',
70
+ severity: 'warning',
71
+ description: 'Enhanced components bundle size'
72
+ },
73
+
74
+ // Runtime performance budgets
75
+ {
76
+ name: 'Component Render Time',
77
+ category: 'runtime',
78
+ limit: 16, // 16ms for 60fps
79
+ unit: 'ms',
80
+ severity: 'error',
81
+ description: 'Component rendering should complete within one frame'
82
+ },
83
+ {
84
+ name: 'First Input Delay',
85
+ category: 'runtime',
86
+ limit: 100, // 100ms
87
+ unit: 'ms',
88
+ severity: 'error',
89
+ description: 'First Input Delay should be under 100ms for good UX'
90
+ },
91
+ {
92
+ name: 'Largest Contentful Paint',
93
+ category: 'runtime',
94
+ limit: 2500, // 2.5s
95
+ unit: 'ms',
96
+ severity: 'warning',
97
+ description: 'LCP should be under 2.5s for good Core Web Vitals score'
98
+ },
99
+ {
100
+ name: 'Cumulative Layout Shift',
101
+ category: 'runtime',
102
+ limit: 0.1,
103
+ unit: 'score',
104
+ severity: 'warning',
105
+ description: 'CLS should be under 0.1 for good visual stability'
106
+ },
107
+
108
+ // Memory budgets
109
+ {
110
+ name: 'Memory Usage',
111
+ category: 'memory',
112
+ limit: 50 * 1024 * 1024, // 50MB
113
+ unit: 'bytes',
114
+ severity: 'warning',
115
+ description: 'Memory usage should stay under 50MB for good performance'
116
+ },
117
+ {
118
+ name: 'Memory Increase Per Operation',
119
+ category: 'memory',
120
+ limit: 1024 * 1024, // 1MB
121
+ unit: 'bytes',
122
+ severity: 'error',
123
+ description: 'Each operation should not increase memory by more than 1MB'
124
+ },
125
+
126
+ // Network budgets (for media loading)
127
+ {
128
+ name: 'Media Load Time',
129
+ category: 'network',
130
+ limit: 3000, // 3s
131
+ unit: 'ms',
132
+ severity: 'warning',
133
+ description: 'Media should start loading within 3 seconds'
134
+ }
135
+ ];
136
+
137
+ // Performance budget monitor
138
+ export class PerformanceBudgetMonitor {
139
+ private budgets: Map<string, PerformanceBudget> = new Map();
140
+ private violations: BudgetViolation[] = [];
141
+ private alertConfig: AlertConfig;
142
+ private lastAlertTime = 0;
143
+
144
+ constructor(
145
+ budgets: PerformanceBudget[] = defaultPerformanceBudgets,
146
+ alertConfig: AlertConfig = {
147
+ enabled: true,
148
+ channels: [{ type: 'console', config: {} }],
149
+ threshold: 1,
150
+ cooldown: 5 * 60 * 1000 // 5 minutes
151
+ }
152
+ ) {
153
+ budgets.forEach(budget => {
154
+ this.budgets.set(budget.name, budget);
155
+ });
156
+ this.alertConfig = alertConfig;
157
+ }
158
+
159
+ // Check a single metric against budgets
160
+ checkMetric(
161
+ metricName: string,
162
+ value: number,
163
+ category: PerformanceBudget['category'] = 'runtime'
164
+ ): BudgetViolation[] {
165
+ const violations: BudgetViolation[] = [];
166
+
167
+ for (const [name, budget] of this.budgets) {
168
+ if (budget.category === category &&
169
+ (name.includes(metricName) || metricName.includes(name.split(' ')[0]?.toLowerCase() || ''))) {
170
+
171
+ if (value > budget.limit) {
172
+ const violation: BudgetViolation = {
173
+ budget,
174
+ actualValue: value,
175
+ overageAmount: value - budget.limit,
176
+ overagePercentage: ((value - budget.limit) / budget.limit) * 100,
177
+ timestamp: Date.now()
178
+ };
179
+
180
+ violations.push(violation);
181
+ this.violations.push(violation);
182
+ }
183
+ }
184
+ }
185
+
186
+ if (violations.length > 0) {
187
+ this.handleViolations(violations);
188
+ }
189
+
190
+ return violations;
191
+ }
192
+
193
+ // Check bundle sizes
194
+ async checkBundleSizes(distPath: string): Promise<BudgetViolation[]> {
195
+ const violations: BudgetViolation[] = [];
196
+
197
+ // Only check bundle sizes in Node.js environment
198
+ if (typeof window !== 'undefined' || typeof process === 'undefined') {
199
+ console.warn('Bundle size checking is only available in Node.js environment');
200
+ return violations;
201
+ }
202
+
203
+ // Bundle size checking is disabled in browser environments
204
+ console.warn('Bundle size checking disabled - use Node.js cicdIntegration from @rp/baroiplayer/core/performance/cicd for CI/CD environments');
205
+
206
+ return violations;
207
+ }
208
+
209
+ // Check runtime performance
210
+ checkRuntimeMetrics(metrics: {
211
+ renderTime?: number;
212
+ fid?: number;
213
+ lcp?: number;
214
+ cls?: number;
215
+ memoryUsage?: number;
216
+ mediaLoadTime?: number;
217
+ }): BudgetViolation[] {
218
+ const allViolations: BudgetViolation[] = [];
219
+
220
+ if (metrics.renderTime !== undefined) {
221
+ allViolations.push(...this.checkMetric('Component Render Time', metrics.renderTime, 'runtime'));
222
+ }
223
+
224
+ if (metrics.fid !== undefined) {
225
+ allViolations.push(...this.checkMetric('First Input Delay', metrics.fid, 'runtime'));
226
+ }
227
+
228
+ if (metrics.lcp !== undefined) {
229
+ allViolations.push(...this.checkMetric('Largest Contentful Paint', metrics.lcp, 'runtime'));
230
+ }
231
+
232
+ if (metrics.cls !== undefined) {
233
+ allViolations.push(...this.checkMetric('Cumulative Layout Shift', metrics.cls, 'runtime'));
234
+ }
235
+
236
+ if (metrics.memoryUsage !== undefined) {
237
+ allViolations.push(...this.checkMetric('Memory Usage', metrics.memoryUsage, 'memory'));
238
+ }
239
+
240
+ if (metrics.mediaLoadTime !== undefined) {
241
+ allViolations.push(...this.checkMetric('Media Load Time', metrics.mediaLoadTime, 'network'));
242
+ }
243
+
244
+ return allViolations;
245
+ }
246
+
247
+ // Handle budget violations
248
+ private async handleViolations(violations: BudgetViolation[]) {
249
+ if (!this.alertConfig.enabled) return;
250
+
251
+ const now = Date.now();
252
+ const errorViolations = violations.filter(v => v.budget.severity === 'error');
253
+
254
+ // Check if we should send an alert based on threshold and cooldown
255
+ if (errorViolations.length >= this.alertConfig.threshold &&
256
+ now - this.lastAlertTime >= this.alertConfig.cooldown) {
257
+
258
+ await this.sendAlerts(violations);
259
+ this.lastAlertTime = now;
260
+ }
261
+ }
262
+
263
+ // Send alerts through configured channels
264
+ private async sendAlerts(violations: BudgetViolation[]) {
265
+ const message = this.formatAlertMessage(violations);
266
+
267
+ for (const channel of this.alertConfig.channels) {
268
+ try {
269
+ await this.sendAlert(channel, message);
270
+ } catch (error) {
271
+ console.error(`Failed to send alert via ${channel.type}:`, error);
272
+ }
273
+ }
274
+ }
275
+
276
+ // Format alert message
277
+ private formatAlertMessage(violations: BudgetViolation[]): string {
278
+ let message = `🚨 Performance Budget Violations Detected!\n\n`;
279
+
280
+ violations.forEach(violation => {
281
+ const { budget, actualValue, overageAmount, overagePercentage } = violation;
282
+ const unit = this.getUnitDisplay(budget.unit);
283
+
284
+ message += `❌ ${budget.name}\n`;
285
+ message += ` Limit: ${this.formatValue(budget.limit, budget.unit)}\n`;
286
+ message += ` Actual: ${this.formatValue(actualValue, budget.unit)}\n`;
287
+ message += ` Overage: ${this.formatValue(overageAmount, budget.unit)} (+${overagePercentage.toFixed(1)}%)\n`;
288
+ message += ` Severity: ${budget.severity.toUpperCase()}\n\n`;
289
+ });
290
+
291
+ message += `Time: ${new Date().toISOString()}\n`;
292
+ message += `Total Violations: ${violations.length}`;
293
+
294
+ return message;
295
+ }
296
+
297
+ // Send alert to specific channel
298
+ private async sendAlert(channel: AlertChannel, message: string) {
299
+ switch (channel.type) {
300
+ case 'console':
301
+ console.error(message);
302
+ break;
303
+
304
+ case 'webhook':
305
+ if (channel.config.url) {
306
+ const response = await fetch(channel.config.url, {
307
+ method: 'POST',
308
+ headers: { 'Content-Type': 'application/json' },
309
+ body: JSON.stringify({ text: message, timestamp: Date.now() })
310
+ });
311
+ if (!response.ok) {
312
+ throw new Error(`Webhook failed: ${response.status}`);
313
+ }
314
+ }
315
+ break;
316
+
317
+ case 'slack':
318
+ if (channel.config.url) {
319
+ const response = await fetch(channel.config.url, {
320
+ method: 'POST',
321
+ headers: { 'Content-Type': 'application/json' },
322
+ body: JSON.stringify({
323
+ text: message,
324
+ channel: channel.config.channel,
325
+ username: 'BaroiPlayer Performance Monitor'
326
+ })
327
+ });
328
+ if (!response.ok) {
329
+ throw new Error(`Slack webhook failed: ${response.status}`);
330
+ }
331
+ }
332
+ break;
333
+
334
+ default:
335
+ console.warn(`Unsupported alert channel: ${channel.type}`);
336
+ }
337
+ }
338
+
339
+ // Helper methods
340
+ private formatValue(value: number, unit: PerformanceBudget['unit']): string {
341
+ switch (unit) {
342
+ case 'bytes':
343
+ return this.formatBytes(value);
344
+ case 'ms':
345
+ return `${value.toFixed(2)}ms`;
346
+ case 'score':
347
+ return value.toFixed(3);
348
+ case 'percent':
349
+ return `${value.toFixed(1)}%`;
350
+ default:
351
+ return value.toString();
352
+ }
353
+ }
354
+
355
+ private formatBytes(bytes: number): string {
356
+ if (bytes >= 1024 * 1024) {
357
+ return `${(bytes / 1024 / 1024).toFixed(2)}MB`;
358
+ }
359
+ if (bytes >= 1024) {
360
+ return `${(bytes / 1024).toFixed(2)}KB`;
361
+ }
362
+ return `${bytes}B`;
363
+ }
364
+
365
+ private getUnitDisplay(unit: PerformanceBudget['unit']): string {
366
+ const displays = {
367
+ 'bytes': 'bytes',
368
+ 'ms': 'milliseconds',
369
+ 'score': 'score',
370
+ 'percent': 'percent'
371
+ };
372
+ return displays[unit] || unit;
373
+ }
374
+
375
+ // Management methods
376
+ addBudget(budget: PerformanceBudget) {
377
+ this.budgets.set(budget.name, budget);
378
+ }
379
+
380
+ removeBudget(name: string) {
381
+ this.budgets.delete(name);
382
+ }
383
+
384
+ updateBudget(name: string, updates: Partial<PerformanceBudget>) {
385
+ const existing = this.budgets.get(name);
386
+ if (existing) {
387
+ this.budgets.set(name, { ...existing, ...updates });
388
+ }
389
+ }
390
+
391
+ getBudgets(): PerformanceBudget[] {
392
+ return Array.from(this.budgets.values());
393
+ }
394
+
395
+ getViolations(since?: number): BudgetViolation[] {
396
+ if (since) {
397
+ return this.violations.filter(v => v.timestamp >= since);
398
+ }
399
+ return [...this.violations];
400
+ }
401
+
402
+ clearViolations() {
403
+ this.violations = [];
404
+ }
405
+
406
+ getViolationSummary(): {
407
+ total: number;
408
+ byCategory: Record<string, number>;
409
+ bySeverity: Record<string, number>;
410
+ } {
411
+ const byCategory: Record<string, number> = {};
412
+ const bySeverity: Record<string, number> = {};
413
+
414
+ this.violations.forEach(violation => {
415
+ const category = violation.budget.category;
416
+ const severity = violation.budget.severity;
417
+
418
+ byCategory[category] = (byCategory[category] || 0) + 1;
419
+ bySeverity[severity] = (bySeverity[severity] || 0) + 1;
420
+ });
421
+
422
+ return {
423
+ total: this.violations.length,
424
+ byCategory,
425
+ bySeverity
426
+ };
427
+ }
428
+ }
429
+
430
+ // Global performance budget monitor
431
+ export const performanceBudgetMonitor = new PerformanceBudgetMonitor();
432
+