@livepeer-frameworks/player-core 0.0.3

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 (120) hide show
  1. package/dist/cjs/index.js +19493 -0
  2. package/dist/cjs/index.js.map +1 -0
  3. package/dist/esm/index.js +19398 -0
  4. package/dist/esm/index.js.map +1 -0
  5. package/dist/player.css +2140 -0
  6. package/dist/types/core/ABRController.d.ts +164 -0
  7. package/dist/types/core/CodecUtils.d.ts +54 -0
  8. package/dist/types/core/Disposable.d.ts +61 -0
  9. package/dist/types/core/EventEmitter.d.ts +73 -0
  10. package/dist/types/core/GatewayClient.d.ts +144 -0
  11. package/dist/types/core/InteractionController.d.ts +121 -0
  12. package/dist/types/core/LiveDurationProxy.d.ts +102 -0
  13. package/dist/types/core/MetaTrackManager.d.ts +220 -0
  14. package/dist/types/core/MistReporter.d.ts +163 -0
  15. package/dist/types/core/MistSignaling.d.ts +148 -0
  16. package/dist/types/core/PlayerController.d.ts +665 -0
  17. package/dist/types/core/PlayerInterface.d.ts +230 -0
  18. package/dist/types/core/PlayerManager.d.ts +182 -0
  19. package/dist/types/core/PlayerRegistry.d.ts +27 -0
  20. package/dist/types/core/QualityMonitor.d.ts +184 -0
  21. package/dist/types/core/ScreenWakeLockManager.d.ts +70 -0
  22. package/dist/types/core/SeekingUtils.d.ts +142 -0
  23. package/dist/types/core/StreamStateClient.d.ts +108 -0
  24. package/dist/types/core/SubtitleManager.d.ts +111 -0
  25. package/dist/types/core/TelemetryReporter.d.ts +79 -0
  26. package/dist/types/core/TimeFormat.d.ts +97 -0
  27. package/dist/types/core/TimerManager.d.ts +83 -0
  28. package/dist/types/core/UrlUtils.d.ts +81 -0
  29. package/dist/types/core/detector.d.ts +149 -0
  30. package/dist/types/core/index.d.ts +49 -0
  31. package/dist/types/core/scorer.d.ts +167 -0
  32. package/dist/types/core/selector.d.ts +9 -0
  33. package/dist/types/index.d.ts +45 -0
  34. package/dist/types/lib/utils.d.ts +2 -0
  35. package/dist/types/players/DashJsPlayer.d.ts +102 -0
  36. package/dist/types/players/HlsJsPlayer.d.ts +70 -0
  37. package/dist/types/players/MewsWsPlayer/SourceBufferManager.d.ts +119 -0
  38. package/dist/types/players/MewsWsPlayer/WebSocketManager.d.ts +60 -0
  39. package/dist/types/players/MewsWsPlayer/index.d.ts +220 -0
  40. package/dist/types/players/MewsWsPlayer/types.d.ts +89 -0
  41. package/dist/types/players/MistPlayer.d.ts +25 -0
  42. package/dist/types/players/MistWebRTCPlayer/index.d.ts +133 -0
  43. package/dist/types/players/NativePlayer.d.ts +143 -0
  44. package/dist/types/players/VideoJsPlayer.d.ts +59 -0
  45. package/dist/types/players/WebCodecsPlayer/JitterBuffer.d.ts +118 -0
  46. package/dist/types/players/WebCodecsPlayer/LatencyProfiles.d.ts +64 -0
  47. package/dist/types/players/WebCodecsPlayer/RawChunkParser.d.ts +63 -0
  48. package/dist/types/players/WebCodecsPlayer/SyncController.d.ts +174 -0
  49. package/dist/types/players/WebCodecsPlayer/WebSocketController.d.ts +164 -0
  50. package/dist/types/players/WebCodecsPlayer/index.d.ts +149 -0
  51. package/dist/types/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.d.ts +105 -0
  52. package/dist/types/players/WebCodecsPlayer/types.d.ts +395 -0
  53. package/dist/types/players/WebCodecsPlayer/worker/decoder.worker.d.ts +13 -0
  54. package/dist/types/players/WebCodecsPlayer/worker/types.d.ts +197 -0
  55. package/dist/types/players/index.d.ts +14 -0
  56. package/dist/types/styles/index.d.ts +11 -0
  57. package/dist/types/types.d.ts +363 -0
  58. package/dist/types/vanilla/FrameWorksPlayer.d.ts +143 -0
  59. package/dist/types/vanilla/index.d.ts +19 -0
  60. package/dist/workers/decoder.worker.js +989 -0
  61. package/dist/workers/decoder.worker.js.map +1 -0
  62. package/package.json +80 -0
  63. package/src/core/ABRController.ts +550 -0
  64. package/src/core/CodecUtils.ts +257 -0
  65. package/src/core/Disposable.ts +120 -0
  66. package/src/core/EventEmitter.ts +113 -0
  67. package/src/core/GatewayClient.ts +439 -0
  68. package/src/core/InteractionController.ts +712 -0
  69. package/src/core/LiveDurationProxy.ts +270 -0
  70. package/src/core/MetaTrackManager.ts +753 -0
  71. package/src/core/MistReporter.ts +543 -0
  72. package/src/core/MistSignaling.ts +346 -0
  73. package/src/core/PlayerController.ts +2829 -0
  74. package/src/core/PlayerInterface.ts +432 -0
  75. package/src/core/PlayerManager.ts +900 -0
  76. package/src/core/PlayerRegistry.ts +149 -0
  77. package/src/core/QualityMonitor.ts +597 -0
  78. package/src/core/ScreenWakeLockManager.ts +163 -0
  79. package/src/core/SeekingUtils.ts +364 -0
  80. package/src/core/StreamStateClient.ts +457 -0
  81. package/src/core/SubtitleManager.ts +297 -0
  82. package/src/core/TelemetryReporter.ts +308 -0
  83. package/src/core/TimeFormat.ts +205 -0
  84. package/src/core/TimerManager.ts +209 -0
  85. package/src/core/UrlUtils.ts +179 -0
  86. package/src/core/detector.ts +382 -0
  87. package/src/core/index.ts +140 -0
  88. package/src/core/scorer.ts +553 -0
  89. package/src/core/selector.ts +16 -0
  90. package/src/global.d.ts +11 -0
  91. package/src/index.ts +75 -0
  92. package/src/lib/utils.ts +6 -0
  93. package/src/players/DashJsPlayer.ts +642 -0
  94. package/src/players/HlsJsPlayer.ts +483 -0
  95. package/src/players/MewsWsPlayer/SourceBufferManager.ts +572 -0
  96. package/src/players/MewsWsPlayer/WebSocketManager.ts +241 -0
  97. package/src/players/MewsWsPlayer/index.ts +1065 -0
  98. package/src/players/MewsWsPlayer/types.ts +106 -0
  99. package/src/players/MistPlayer.ts +188 -0
  100. package/src/players/MistWebRTCPlayer/index.ts +703 -0
  101. package/src/players/NativePlayer.ts +820 -0
  102. package/src/players/VideoJsPlayer.ts +643 -0
  103. package/src/players/WebCodecsPlayer/JitterBuffer.ts +299 -0
  104. package/src/players/WebCodecsPlayer/LatencyProfiles.ts +151 -0
  105. package/src/players/WebCodecsPlayer/RawChunkParser.ts +151 -0
  106. package/src/players/WebCodecsPlayer/SyncController.ts +456 -0
  107. package/src/players/WebCodecsPlayer/WebSocketController.ts +564 -0
  108. package/src/players/WebCodecsPlayer/index.ts +1650 -0
  109. package/src/players/WebCodecsPlayer/polyfills/MediaStreamTrackGenerator.ts +379 -0
  110. package/src/players/WebCodecsPlayer/types.ts +542 -0
  111. package/src/players/WebCodecsPlayer/worker/decoder.worker.ts +1360 -0
  112. package/src/players/WebCodecsPlayer/worker/types.ts +276 -0
  113. package/src/players/index.ts +22 -0
  114. package/src/styles/animations.css +21 -0
  115. package/src/styles/index.ts +52 -0
  116. package/src/styles/player.css +2126 -0
  117. package/src/styles/tailwind.css +1015 -0
  118. package/src/types.ts +421 -0
  119. package/src/vanilla/FrameWorksPlayer.ts +367 -0
  120. package/src/vanilla/index.ts +22 -0
@@ -0,0 +1,257 @@
1
+ /**
2
+ * CodecUtils - Codec string translation utilities
3
+ *
4
+ * Based on MistMetaPlayer's MistUtil.tracks.translateCodec functionality.
5
+ * Translates MistServer codec names to browser-compatible codec strings.
6
+ */
7
+
8
+ export interface TrackInfo {
9
+ type: string; // 'video' | 'audio' | 'meta' - loosened for compatibility
10
+ codec: string;
11
+ init?: string;
12
+ codecstring?: string;
13
+ width?: number;
14
+ height?: number;
15
+ bps?: number;
16
+ fpks?: number;
17
+ }
18
+
19
+ /**
20
+ * Translate a MistServer codec name to a browser-compatible codec string
21
+ *
22
+ * @param track - Track info from MistServer
23
+ * @returns Browser-compatible codec string (e.g., "avc1.64001f")
24
+ *
25
+ * @example
26
+ * ```ts
27
+ * translateCodec({ codec: 'H264', type: 'video' })
28
+ * // => 'avc1.42E01E' (baseline profile, level 3.0 default)
29
+ *
30
+ * translateCodec({ codec: 'AAC', type: 'audio' })
31
+ * // => 'mp4a.40.2'
32
+ * ```
33
+ */
34
+ export function translateCodec(track: TrackInfo): string {
35
+ const codec = track.codec.toUpperCase();
36
+
37
+ // Use codecstring if available (MistServer provides this for some tracks)
38
+ if (track.codecstring) {
39
+ return track.codecstring;
40
+ }
41
+
42
+ // Audio codecs
43
+ if (track.type === 'audio') {
44
+ switch (codec) {
45
+ case 'AAC':
46
+ case 'MP4A':
47
+ return 'mp4a.40.2'; // AAC-LC
48
+ case 'MP3':
49
+ return 'mp4a.40.34'; // MP3 in MP4 container
50
+ case 'AC3':
51
+ case 'AC-3':
52
+ return 'ac-3';
53
+ case 'EAC3':
54
+ case 'EC3':
55
+ case 'E-AC3':
56
+ case 'EC-3':
57
+ return 'ec-3';
58
+ case 'OPUS':
59
+ return 'opus';
60
+ case 'VORBIS':
61
+ return 'vorbis';
62
+ case 'FLAC':
63
+ return 'flac';
64
+ case 'PCM':
65
+ case 'PCMS16LE':
66
+ return 'pcm';
67
+ default:
68
+ return codec.toLowerCase();
69
+ }
70
+ }
71
+
72
+ // Video codecs
73
+ if (track.type === 'video') {
74
+ switch (codec) {
75
+ case 'H264':
76
+ case 'AVC':
77
+ case 'AVC1': {
78
+ // Try to extract profile/level from init data
79
+ const profileLevel = extractH264Profile(track.init);
80
+ return profileLevel || 'avc1.42E01E'; // Default: Baseline Profile, Level 3.0
81
+ }
82
+ case 'H265':
83
+ case 'HEVC':
84
+ case 'HEV1':
85
+ case 'HVC1': {
86
+ // Try to extract profile/level from init data
87
+ const profileLevel = extractHEVCProfile(track.init);
88
+ return profileLevel || 'hev1.1.6.L93.B0'; // Default: Main Profile, Level 3.1
89
+ }
90
+ case 'VP8':
91
+ return 'vp8';
92
+ case 'VP9':
93
+ return 'vp09.00.10.08'; // Profile 0, Level 1.0, 8-bit
94
+ case 'AV1':
95
+ return 'av01.0.01M.08'; // Main Profile, Level 2.1, 8-bit
96
+ case 'THEORA':
97
+ return 'theora';
98
+ default:
99
+ return codec.toLowerCase();
100
+ }
101
+ }
102
+
103
+ return codec.toLowerCase();
104
+ }
105
+
106
+ /**
107
+ * Extract H264 profile and level from init data (SPS)
108
+ * The init data contains the SPS NAL unit which has profile/level info
109
+ *
110
+ * @param init - Base64 encoded init data from MistServer
111
+ * @returns Codec string like "avc1.64001f" or null
112
+ */
113
+ function extractH264Profile(init?: string): string | null {
114
+ if (!init) return null;
115
+
116
+ try {
117
+ // Decode base64 init data
118
+ const bytes = base64ToBytes(init);
119
+
120
+ // Look for SPS NAL unit (starts with 0x67 for H264)
121
+ // Format: NAL type (1 byte) + profile_idc (1 byte) + constraint flags (1 byte) + level_idc (1 byte)
122
+ for (let i = 0; i < bytes.length - 4; i++) {
123
+ // Check for NAL start code (0x00 0x00 0x01 or 0x00 0x00 0x00 0x01)
124
+ if (bytes[i] === 0x00 && bytes[i + 1] === 0x00) {
125
+ let offset = i + 2;
126
+ if (bytes[offset] === 0x00) offset++;
127
+ if (bytes[offset] === 0x01) offset++;
128
+
129
+ // Check if this is SPS (NAL type 7)
130
+ const nalType = bytes[offset] & 0x1f;
131
+ if (nalType === 7 && offset + 3 < bytes.length) {
132
+ const profileIdc = bytes[offset + 1];
133
+ const constraintFlags = bytes[offset + 2];
134
+ const levelIdc = bytes[offset + 3];
135
+
136
+ return `avc1.${toHex(profileIdc)}${toHex(constraintFlags)}${toHex(levelIdc)}`;
137
+ }
138
+ }
139
+ }
140
+
141
+ // If no NAL start code found, try raw format
142
+ if (bytes.length >= 4) {
143
+ // Assume first bytes are profile/constraint/level
144
+ const profileIdc = bytes[0];
145
+ const constraintFlags = bytes[1];
146
+ const levelIdc = bytes[2];
147
+
148
+ // Validate reasonable values
149
+ if (profileIdc > 0 && profileIdc < 255 && levelIdc > 0 && levelIdc < 100) {
150
+ return `avc1.${toHex(profileIdc)}${toHex(constraintFlags)}${toHex(levelIdc)}`;
151
+ }
152
+ }
153
+ } catch {
154
+ // Ignore parsing errors
155
+ }
156
+
157
+ return null;
158
+ }
159
+
160
+ /**
161
+ * Extract HEVC profile and level from init data (VPS/SPS)
162
+ *
163
+ * @param init - Base64 encoded init data from MistServer
164
+ * @returns Codec string like "hev1.1.6.L93.B0" or null
165
+ */
166
+ function extractHEVCProfile(init?: string): string | null {
167
+ if (!init) return null;
168
+
169
+ try {
170
+ // Decode base64 init data
171
+ const bytes = base64ToBytes(init);
172
+
173
+ // HEVC profile/level extraction is more complex
174
+ // For now, return a sensible default based on data presence
175
+ if (bytes.length > 0) {
176
+ // Look for profile_tier_level in VPS/SPS
177
+ // This is a simplified extraction - full parsing would be more complex
178
+ for (let i = 0; i < bytes.length - 3; i++) {
179
+ // Look for general_profile_idc (usually in first few bytes after NAL header)
180
+ const profileIdc = bytes[i];
181
+ if (profileIdc >= 1 && profileIdc <= 5) {
182
+ // Valid profile IDC (1=Main, 2=Main10, 3=MainStill, 4=Range Extensions, 5=High Throughput)
183
+ const tierFlag = 0; // Assume main tier
184
+ const levelIdc = bytes[i + 1] || 93; // Default to level 3.1
185
+
186
+ // Format: hev1.{profile}.{tier_flag}{compatibility}.L{level}.{constraints}
187
+ return `hev1.${profileIdc}.6.L${levelIdc}.B0`;
188
+ }
189
+ }
190
+ }
191
+ } catch {
192
+ // Ignore parsing errors
193
+ }
194
+
195
+ return null;
196
+ }
197
+
198
+ /**
199
+ * Convert byte to 2-digit hex string
200
+ */
201
+ function toHex(byte: number): string {
202
+ return byte.toString(16).padStart(2, '0').toUpperCase();
203
+ }
204
+
205
+ /**
206
+ * Decode base64 string to Uint8Array
207
+ */
208
+ function base64ToBytes(base64: string): Uint8Array {
209
+ const binaryString = atob(base64);
210
+ const bytes = new Uint8Array(binaryString.length);
211
+ for (let i = 0; i < binaryString.length; i++) {
212
+ bytes[i] = binaryString.charCodeAt(i);
213
+ }
214
+ return bytes;
215
+ }
216
+
217
+ /**
218
+ * Check if a codec is supported by the browser via MediaSource
219
+ *
220
+ * @param codecString - Codec string to check
221
+ * @param containerType - Container type (default: 'video/mp4')
222
+ * @returns true if supported
223
+ */
224
+ export function isCodecSupported(codecString: string, containerType = 'video/mp4'): boolean {
225
+ if (typeof MediaSource === 'undefined' || !MediaSource.isTypeSupported) {
226
+ return false;
227
+ }
228
+
229
+ const mimeType = `${containerType}; codecs="${codecString}"`;
230
+ return MediaSource.isTypeSupported(mimeType);
231
+ }
232
+
233
+ /**
234
+ * Get the best supported codec from a list of tracks
235
+ *
236
+ * @param tracks - Array of track info
237
+ * @param type - Track type to filter ('video' or 'audio')
238
+ * @returns Best supported track or null
239
+ */
240
+ export function getBestSupportedTrack(tracks: TrackInfo[], type: 'video' | 'audio'): TrackInfo | null {
241
+ const filteredTracks = tracks.filter(t => t.type === type);
242
+
243
+ for (const track of filteredTracks) {
244
+ const codecString = translateCodec(track);
245
+ if (isCodecSupported(codecString)) {
246
+ return track;
247
+ }
248
+ }
249
+
250
+ return null;
251
+ }
252
+
253
+ export default {
254
+ translateCodec,
255
+ isCodecSupported,
256
+ getBestSupportedTrack,
257
+ };
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Disposable interface for consistent cleanup across all core classes.
3
+ *
4
+ * All classes that manage resources (timers, event listeners, WebSockets, etc.)
5
+ * should implement this interface to ensure proper cleanup.
6
+ */
7
+
8
+ /**
9
+ * Interface for objects that need cleanup
10
+ */
11
+ export interface Disposable {
12
+ /**
13
+ * Clean up all resources held by this object.
14
+ * Safe to call multiple times - subsequent calls should be no-ops.
15
+ */
16
+ dispose(): void;
17
+
18
+ /**
19
+ * Whether this object has been disposed
20
+ */
21
+ readonly disposed: boolean;
22
+ }
23
+
24
+ /**
25
+ * Base class for disposable objects that provides:
26
+ * - disposed flag tracking
27
+ * - Double-dispose protection
28
+ * - Template method for subclass cleanup
29
+ */
30
+ export abstract class BaseDisposable implements Disposable {
31
+ private _disposed = false;
32
+
33
+ /**
34
+ * Whether this object has been disposed
35
+ */
36
+ get disposed(): boolean {
37
+ return this._disposed;
38
+ }
39
+
40
+ /**
41
+ * Dispose of this object, releasing all resources.
42
+ * Safe to call multiple times.
43
+ */
44
+ dispose(): void {
45
+ if (this._disposed) return;
46
+ this._disposed = true;
47
+ this.onDispose();
48
+ }
49
+
50
+ /**
51
+ * Subclasses implement this to clean up their resources.
52
+ * Called exactly once when dispose() is first called.
53
+ */
54
+ protected abstract onDispose(): void;
55
+
56
+ /**
57
+ * Throw if this object has been disposed.
58
+ * Use at the start of methods that shouldn't run after disposal.
59
+ */
60
+ protected throwIfDisposed(operation: string = 'operation'): void {
61
+ if (this._disposed) {
62
+ throw new Error(`Cannot perform ${operation} on disposed object`);
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Check if disposed without throwing - useful for conditional guards
68
+ */
69
+ protected guardDisposed(): boolean {
70
+ return this._disposed;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Utility to dispose multiple disposables at once
76
+ */
77
+ export function disposeAll(...disposables: (Disposable | null | undefined)[]): void {
78
+ for (const d of disposables) {
79
+ if (d && !d.disposed) {
80
+ try {
81
+ d.dispose();
82
+ } catch (err) {
83
+ console.warn('[Disposable] Error during disposal:', err);
84
+ }
85
+ }
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Create a composite disposable that disposes multiple items
91
+ */
92
+ export function createCompositeDisposable(
93
+ ...disposables: (Disposable | (() => void))[]
94
+ ): Disposable {
95
+ let disposed = false;
96
+
97
+ return {
98
+ get disposed() {
99
+ return disposed;
100
+ },
101
+ dispose() {
102
+ if (disposed) return;
103
+ disposed = true;
104
+
105
+ for (const d of disposables) {
106
+ try {
107
+ if (typeof d === 'function') {
108
+ d();
109
+ } else if (d && !d.disposed) {
110
+ d.dispose();
111
+ }
112
+ } catch (err) {
113
+ console.warn('[CompositeDisposable] Error during disposal:', err);
114
+ }
115
+ }
116
+ },
117
+ };
118
+ }
119
+
120
+ export default BaseDisposable;
@@ -0,0 +1,113 @@
1
+ /**
2
+ * EventEmitter.ts
3
+ *
4
+ * A lightweight, typed event emitter for framework-agnostic components.
5
+ * Used by GatewayClient, StreamStateClient, and PlayerController.
6
+ */
7
+
8
+ type Listener<T> = (data: T) => void;
9
+
10
+ /**
11
+ * Typed event emitter that provides type-safe event handling.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * interface MyEvents {
16
+ * stateChange: { state: string };
17
+ * error: { message: string };
18
+ * }
19
+ *
20
+ * class MyClass extends TypedEventEmitter<MyEvents> {
21
+ * doSomething() {
22
+ * this.emit('stateChange', { state: 'ready' });
23
+ * }
24
+ * }
25
+ *
26
+ * const instance = new MyClass();
27
+ * const unsub = instance.on('stateChange', ({ state }) => console.log(state));
28
+ * unsub(); // unsubscribe
29
+ * ```
30
+ */
31
+ export class TypedEventEmitter<Events extends Record<string, any>> {
32
+ private listeners = new Map<keyof Events, Set<Function>>();
33
+
34
+ /**
35
+ * Subscribe to an event.
36
+ * @param event - The event name to listen for
37
+ * @param listener - Callback function invoked when the event is emitted
38
+ * @returns Unsubscribe function
39
+ */
40
+ on<K extends keyof Events>(event: K, listener: Listener<Events[K]>): () => void {
41
+ if (!this.listeners.has(event)) {
42
+ this.listeners.set(event, new Set());
43
+ }
44
+ this.listeners.get(event)!.add(listener);
45
+
46
+ // Return unsubscribe function
47
+ return () => this.off(event, listener);
48
+ }
49
+
50
+ /**
51
+ * Subscribe to an event only once.
52
+ * The listener is automatically removed after the first invocation.
53
+ * @param event - The event name to listen for
54
+ * @param listener - Callback function invoked when the event is emitted
55
+ * @returns Unsubscribe function
56
+ */
57
+ once<K extends keyof Events>(event: K, listener: Listener<Events[K]>): () => void {
58
+ const onceListener = (data: Events[K]) => {
59
+ this.off(event, onceListener);
60
+ listener(data);
61
+ };
62
+ return this.on(event, onceListener);
63
+ }
64
+
65
+ /**
66
+ * Unsubscribe from an event.
67
+ * @param event - The event name
68
+ * @param listener - The callback to remove
69
+ */
70
+ off<K extends keyof Events>(event: K, listener: Listener<Events[K]>): void {
71
+ this.listeners.get(event)?.delete(listener);
72
+ }
73
+
74
+ /**
75
+ * Emit an event to all subscribers.
76
+ * @param event - The event name
77
+ * @param data - The event payload
78
+ */
79
+ protected emit<K extends keyof Events>(event: K, data: Events[K]): void {
80
+ this.listeners.get(event)?.forEach(listener => {
81
+ try {
82
+ listener(data);
83
+ } catch (e) {
84
+ console.error(`[EventEmitter] Error in ${String(event)} listener:`, e);
85
+ }
86
+ });
87
+ }
88
+
89
+ /**
90
+ * Remove all listeners for all events.
91
+ */
92
+ removeAllListeners(): void {
93
+ this.listeners.clear();
94
+ }
95
+
96
+ /**
97
+ * Remove all listeners for a specific event.
98
+ * @param event - The event name
99
+ */
100
+ removeListeners<K extends keyof Events>(event: K): void {
101
+ this.listeners.delete(event);
102
+ }
103
+
104
+ /**
105
+ * Check if there are any listeners for an event.
106
+ * @param event - The event name
107
+ */
108
+ hasListeners<K extends keyof Events>(event: K): boolean {
109
+ return (this.listeners.get(event)?.size ?? 0) > 0;
110
+ }
111
+ }
112
+
113
+ export default TypedEventEmitter;