audio-channel-queue 1.8.0 → 1.10.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.
@@ -0,0 +1,104 @@
1
+ /**
2
+ * @fileoverview Queue manipulation functions for the audio-channel-queue package
3
+ * Provides advanced queue management including item removal, reordering, and clearing
4
+ */
5
+ import { QueueManipulationResult, QueueItem } from './types';
6
+ /**
7
+ * Removes a specific item from the queue by its slot number (0-based index)
8
+ * Cannot remove the currently playing item (index 0) - use stopCurrentAudioInChannel instead
9
+ * @param queuedSlotNumber - Zero-based index of the item to remove (must be > 0)
10
+ * @param channelNumber - The channel number (defaults to 0)
11
+ * @returns Promise resolving to operation result with success status and updated queue
12
+ * @throws Error if trying to remove currently playing item or invalid slot number
13
+ * @example
14
+ * ```typescript
15
+ * // Remove the second item in queue (index 1)
16
+ * const result = await removeQueuedItem(1, 0);
17
+ * if (result.success) {
18
+ * console.log(`Removed item, queue now has ${result.updatedQueue.totalItems} items`);
19
+ * }
20
+ *
21
+ * // Remove the third item from channel 1
22
+ * await removeQueuedItem(2, 1);
23
+ * ```
24
+ */
25
+ export declare const removeQueuedItem: (queuedSlotNumber: number, channelNumber?: number) => Promise<QueueManipulationResult>;
26
+ /**
27
+ * Reorders a queue item by moving it from one position to another
28
+ * Cannot reorder the currently playing item (index 0)
29
+ * @param currentQueuedSlotNumber - Current zero-based index of the item to move (must be > 0)
30
+ * @param newQueuedSlotNumber - New zero-based index where the item should be placed (must be > 0)
31
+ * @param channelNumber - The channel number (defaults to 0)
32
+ * @returns Promise resolving to operation result with success status and updated queue
33
+ * @throws Error if trying to reorder currently playing item or invalid slot numbers
34
+ * @example
35
+ * ```typescript
36
+ * // Move item from position 2 to position 1 (make it play next)
37
+ * const result = await reorderQueue(2, 1, 0);
38
+ * if (result.success) {
39
+ * console.log('Item moved successfully');
40
+ * }
41
+ *
42
+ * // Move item from position 1 to end of queue
43
+ * await reorderQueue(1, 4, 0); // Assuming queue has 5+ items
44
+ * ```
45
+ */
46
+ export declare const reorderQueue: (currentQueuedSlotNumber: number, newQueuedSlotNumber: number, channelNumber?: number) => Promise<QueueManipulationResult>;
47
+ /**
48
+ * Clears all queued audio items after the currently playing item
49
+ * The current audio will continue playing but nothing will follow it
50
+ * @param channelNumber - The channel number (defaults to 0)
51
+ * @returns Promise resolving to operation result with success status and updated queue
52
+ * @example
53
+ * ```typescript
54
+ * // Let current song finish but clear everything after it
55
+ * const result = await clearQueueAfterCurrent(0);
56
+ * if (result.success) {
57
+ * console.log(`Cleared queue, current audio will be the last to play`);
58
+ * }
59
+ * ```
60
+ */
61
+ export declare const clearQueueAfterCurrent: (channelNumber?: number) => Promise<QueueManipulationResult>;
62
+ /**
63
+ * Gets information about a specific queue item by its slot number
64
+ * @param queueSlotNumber - Zero-based index of the queue item
65
+ * @param channelNumber - The channel number (defaults to 0)
66
+ * @returns QueueItem information or null if slot doesn't exist
67
+ * @example
68
+ * ```typescript
69
+ * const itemInfo = getQueueItemInfo(1, 0);
70
+ * if (itemInfo) {
71
+ * console.log(`Next to play: ${itemInfo.fileName}`);
72
+ * console.log(`Duration: ${itemInfo.duration}ms`);
73
+ * }
74
+ * ```
75
+ */
76
+ export declare const getQueueItemInfo: (queueSlotNumber: number, channelNumber?: number) => QueueItem | null;
77
+ /**
78
+ * Gets the current queue length for a specific channel
79
+ * @param channelNumber - The channel number (defaults to 0)
80
+ * @returns Number of items in the queue, or 0 if channel doesn't exist
81
+ * @example
82
+ * ```typescript
83
+ * const queueSize = getQueueLength(0);
84
+ * console.log(`Channel 0 has ${queueSize} items in queue`);
85
+ * ```
86
+ */
87
+ export declare const getQueueLength: (channelNumber?: number) => number;
88
+ /**
89
+ * Swaps the positions of two queue items
90
+ * Cannot swap with the currently playing item (index 0)
91
+ * @param slotA - Zero-based index of first item to swap (must be > 0)
92
+ * @param slotB - Zero-based index of second item to swap (must be > 0)
93
+ * @param channelNumber - The channel number (defaults to 0)
94
+ * @returns Promise resolving to operation result with success status and updated queue
95
+ * @example
96
+ * ```typescript
97
+ * // Swap the second and third items in queue
98
+ * const result = await swapQueueItems(1, 2, 0);
99
+ * if (result.success) {
100
+ * console.log('Items swapped successfully');
101
+ * }
102
+ * ```
103
+ */
104
+ export declare const swapQueueItems: (slotA: number, slotB: number, channelNumber?: number) => Promise<QueueManipulationResult>;
@@ -0,0 +1,319 @@
1
+ "use strict";
2
+ /**
3
+ * @fileoverview Queue manipulation functions for the audio-channel-queue package
4
+ * Provides advanced queue management including item removal, reordering, and clearing
5
+ */
6
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
7
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
8
+ return new (P || (P = Promise))(function (resolve, reject) {
9
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
10
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
11
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
12
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
13
+ });
14
+ };
15
+ Object.defineProperty(exports, "__esModule", { value: true });
16
+ exports.swapQueueItems = exports.getQueueLength = exports.getQueueItemInfo = exports.clearQueueAfterCurrent = exports.reorderQueue = exports.removeQueuedItem = void 0;
17
+ const info_1 = require("./info");
18
+ const events_1 = require("./events");
19
+ const events_2 = require("./events");
20
+ const utils_1 = require("./utils");
21
+ /**
22
+ * Removes a specific item from the queue by its slot number (0-based index)
23
+ * Cannot remove the currently playing item (index 0) - use stopCurrentAudioInChannel instead
24
+ * @param queuedSlotNumber - Zero-based index of the item to remove (must be > 0)
25
+ * @param channelNumber - The channel number (defaults to 0)
26
+ * @returns Promise resolving to operation result with success status and updated queue
27
+ * @throws Error if trying to remove currently playing item or invalid slot number
28
+ * @example
29
+ * ```typescript
30
+ * // Remove the second item in queue (index 1)
31
+ * const result = await removeQueuedItem(1, 0);
32
+ * if (result.success) {
33
+ * console.log(`Removed item, queue now has ${result.updatedQueue.totalItems} items`);
34
+ * }
35
+ *
36
+ * // Remove the third item from channel 1
37
+ * await removeQueuedItem(2, 1);
38
+ * ```
39
+ */
40
+ const removeQueuedItem = (queuedSlotNumber_1, ...args_1) => __awaiter(void 0, [queuedSlotNumber_1, ...args_1], void 0, function* (queuedSlotNumber, channelNumber = 0) {
41
+ const channel = info_1.audioChannels[channelNumber];
42
+ if (!channel) {
43
+ return {
44
+ error: `Channel ${channelNumber} does not exist`,
45
+ success: false
46
+ };
47
+ }
48
+ if (queuedSlotNumber < 0 || queuedSlotNumber >= channel.queue.length) {
49
+ return {
50
+ error: `Invalid slot number ${queuedSlotNumber}. Queue has ${channel.queue.length} items ` +
51
+ `(indices 0-${channel.queue.length - 1})`,
52
+ success: false
53
+ };
54
+ }
55
+ if (queuedSlotNumber === 0) {
56
+ return {
57
+ error: 'Cannot remove currently playing item (index 0). ' +
58
+ 'Use stopCurrentAudioInChannel() instead',
59
+ success: false
60
+ };
61
+ }
62
+ // Remove the audio element from the queue
63
+ const removedAudio = channel.queue.splice(queuedSlotNumber, 1)[0];
64
+ // Clean up any progress tracking for the removed audio
65
+ (0, events_2.cleanupProgressTracking)(removedAudio, channelNumber, info_1.audioChannels);
66
+ // Emit queue change event
67
+ (0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
68
+ const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
69
+ return {
70
+ success: true,
71
+ updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
72
+ };
73
+ });
74
+ exports.removeQueuedItem = removeQueuedItem;
75
+ /**
76
+ * Reorders a queue item by moving it from one position to another
77
+ * Cannot reorder the currently playing item (index 0)
78
+ * @param currentQueuedSlotNumber - Current zero-based index of the item to move (must be > 0)
79
+ * @param newQueuedSlotNumber - New zero-based index where the item should be placed (must be > 0)
80
+ * @param channelNumber - The channel number (defaults to 0)
81
+ * @returns Promise resolving to operation result with success status and updated queue
82
+ * @throws Error if trying to reorder currently playing item or invalid slot numbers
83
+ * @example
84
+ * ```typescript
85
+ * // Move item from position 2 to position 1 (make it play next)
86
+ * const result = await reorderQueue(2, 1, 0);
87
+ * if (result.success) {
88
+ * console.log('Item moved successfully');
89
+ * }
90
+ *
91
+ * // Move item from position 1 to end of queue
92
+ * await reorderQueue(1, 4, 0); // Assuming queue has 5+ items
93
+ * ```
94
+ */
95
+ const reorderQueue = (currentQueuedSlotNumber_1, newQueuedSlotNumber_1, ...args_1) => __awaiter(void 0, [currentQueuedSlotNumber_1, newQueuedSlotNumber_1, ...args_1], void 0, function* (currentQueuedSlotNumber, newQueuedSlotNumber, channelNumber = 0) {
96
+ const channel = info_1.audioChannels[channelNumber];
97
+ if (!channel) {
98
+ return {
99
+ error: `Channel ${channelNumber} does not exist`,
100
+ success: false
101
+ };
102
+ }
103
+ if (currentQueuedSlotNumber < 0 || currentQueuedSlotNumber >= channel.queue.length) {
104
+ return {
105
+ error: `Invalid current slot number ${currentQueuedSlotNumber}. Queue has ` +
106
+ `${channel.queue.length} items (indices 0-${channel.queue.length - 1})`,
107
+ success: false
108
+ };
109
+ }
110
+ if (newQueuedSlotNumber < 0 || newQueuedSlotNumber >= channel.queue.length) {
111
+ return {
112
+ error: `Invalid new slot number ${newQueuedSlotNumber}. Queue has ` +
113
+ `${channel.queue.length} items (indices 0-${channel.queue.length - 1})`,
114
+ success: false
115
+ };
116
+ }
117
+ if (currentQueuedSlotNumber === 0) {
118
+ return {
119
+ error: 'Cannot reorder currently playing item (index 0). ' + 'Stop current audio first if needed',
120
+ success: false
121
+ };
122
+ }
123
+ if (newQueuedSlotNumber === 0) {
124
+ return {
125
+ error: 'Cannot move item to currently playing position (index 0). ' +
126
+ 'Use queueAudioPriority() to add items to front of queue',
127
+ success: false
128
+ };
129
+ }
130
+ if (currentQueuedSlotNumber === newQueuedSlotNumber) {
131
+ // No change needed, but return success
132
+ const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
133
+ return {
134
+ success: true,
135
+ updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
136
+ };
137
+ }
138
+ // Remove the item from its current position
139
+ const audioToMove = channel.queue.splice(currentQueuedSlotNumber, 1)[0];
140
+ // Insert it at the new position
141
+ channel.queue.splice(newQueuedSlotNumber, 0, audioToMove);
142
+ // Emit queue change event
143
+ (0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
144
+ const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
145
+ return {
146
+ success: true,
147
+ updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
148
+ };
149
+ });
150
+ exports.reorderQueue = reorderQueue;
151
+ /**
152
+ * Clears all queued audio items after the currently playing item
153
+ * The current audio will continue playing but nothing will follow it
154
+ * @param channelNumber - The channel number (defaults to 0)
155
+ * @returns Promise resolving to operation result with success status and updated queue
156
+ * @example
157
+ * ```typescript
158
+ * // Let current song finish but clear everything after it
159
+ * const result = await clearQueueAfterCurrent(0);
160
+ * if (result.success) {
161
+ * console.log(`Cleared queue, current audio will be the last to play`);
162
+ * }
163
+ * ```
164
+ */
165
+ const clearQueueAfterCurrent = (...args_1) => __awaiter(void 0, [...args_1], void 0, function* (channelNumber = 0) {
166
+ const channel = info_1.audioChannels[channelNumber];
167
+ if (!channel) {
168
+ // For empty/non-existent channels, we can consider this a successful no-op
169
+ // since there's nothing to clear anyway
170
+ return {
171
+ success: true,
172
+ updatedQueue: {
173
+ channelNumber,
174
+ currentIndex: -1,
175
+ isPaused: false,
176
+ items: [],
177
+ totalItems: 0,
178
+ volume: 1.0
179
+ }
180
+ };
181
+ }
182
+ if (channel.queue.length <= 1) {
183
+ // Nothing to clear - either empty queue or only current audio
184
+ const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
185
+ return {
186
+ success: true,
187
+ updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
188
+ };
189
+ }
190
+ // Clean up progress tracking for all items except the current one
191
+ for (let i = 1; i < channel.queue.length; i++) {
192
+ (0, events_2.cleanupProgressTracking)(channel.queue[i], channelNumber, info_1.audioChannels);
193
+ }
194
+ // Keep only the currently playing audio (index 0)
195
+ channel.queue = channel.queue.slice(0, 1);
196
+ // Emit queue change event
197
+ (0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
198
+ const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
199
+ return {
200
+ success: true,
201
+ updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
202
+ };
203
+ });
204
+ exports.clearQueueAfterCurrent = clearQueueAfterCurrent;
205
+ /**
206
+ * Gets information about a specific queue item by its slot number
207
+ * @param queueSlotNumber - Zero-based index of the queue item
208
+ * @param channelNumber - The channel number (defaults to 0)
209
+ * @returns QueueItem information or null if slot doesn't exist
210
+ * @example
211
+ * ```typescript
212
+ * const itemInfo = getQueueItemInfo(1, 0);
213
+ * if (itemInfo) {
214
+ * console.log(`Next to play: ${itemInfo.fileName}`);
215
+ * console.log(`Duration: ${itemInfo.duration}ms`);
216
+ * }
217
+ * ```
218
+ */
219
+ const getQueueItemInfo = (queueSlotNumber, channelNumber = 0) => {
220
+ const channel = info_1.audioChannels[channelNumber];
221
+ if (!channel || queueSlotNumber < 0 || queueSlotNumber >= channel.queue.length) {
222
+ return null;
223
+ }
224
+ const audio = channel.queue[queueSlotNumber];
225
+ const audioInfo = (0, utils_1.getAudioInfoFromElement)(audio, channelNumber, info_1.audioChannels);
226
+ if (!audioInfo) {
227
+ return null;
228
+ }
229
+ const { duration, fileName, isLooping, isPlaying, src, volume } = audioInfo;
230
+ return {
231
+ duration,
232
+ fileName,
233
+ isCurrentlyPlaying: queueSlotNumber === 0 && isPlaying,
234
+ isLooping,
235
+ src,
236
+ volume
237
+ };
238
+ };
239
+ exports.getQueueItemInfo = getQueueItemInfo;
240
+ /**
241
+ * Gets the current queue length for a specific channel
242
+ * @param channelNumber - The channel number (defaults to 0)
243
+ * @returns Number of items in the queue, or 0 if channel doesn't exist
244
+ * @example
245
+ * ```typescript
246
+ * const queueSize = getQueueLength(0);
247
+ * console.log(`Channel 0 has ${queueSize} items in queue`);
248
+ * ```
249
+ */
250
+ const getQueueLength = (channelNumber = 0) => {
251
+ const channel = info_1.audioChannels[channelNumber];
252
+ return channel ? channel.queue.length : 0;
253
+ };
254
+ exports.getQueueLength = getQueueLength;
255
+ /**
256
+ * Swaps the positions of two queue items
257
+ * Cannot swap with the currently playing item (index 0)
258
+ * @param slotA - Zero-based index of first item to swap (must be > 0)
259
+ * @param slotB - Zero-based index of second item to swap (must be > 0)
260
+ * @param channelNumber - The channel number (defaults to 0)
261
+ * @returns Promise resolving to operation result with success status and updated queue
262
+ * @example
263
+ * ```typescript
264
+ * // Swap the second and third items in queue
265
+ * const result = await swapQueueItems(1, 2, 0);
266
+ * if (result.success) {
267
+ * console.log('Items swapped successfully');
268
+ * }
269
+ * ```
270
+ */
271
+ const swapQueueItems = (slotA_1, slotB_1, ...args_1) => __awaiter(void 0, [slotA_1, slotB_1, ...args_1], void 0, function* (slotA, slotB, channelNumber = 0) {
272
+ const channel = info_1.audioChannels[channelNumber];
273
+ if (!channel) {
274
+ return {
275
+ error: `Channel ${channelNumber} does not exist`,
276
+ success: false
277
+ };
278
+ }
279
+ if (slotA < 0 || slotA >= channel.queue.length) {
280
+ return {
281
+ error: `Invalid slot A ${slotA}. Queue has ${channel.queue.length} items ` +
282
+ `(indices 0-${channel.queue.length - 1})`,
283
+ success: false
284
+ };
285
+ }
286
+ if (slotB < 0 || slotB >= channel.queue.length) {
287
+ return {
288
+ error: `Invalid slot B ${slotB}. Queue has ${channel.queue.length} items ` +
289
+ `(indices 0-${channel.queue.length - 1})`,
290
+ success: false
291
+ };
292
+ }
293
+ if (slotA === 0 || slotB === 0) {
294
+ return {
295
+ error: 'Cannot swap with currently playing item (index 0)',
296
+ success: false
297
+ };
298
+ }
299
+ if (slotA === slotB) {
300
+ // No change needed, but return success
301
+ const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
302
+ return {
303
+ success: true,
304
+ updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
305
+ };
306
+ }
307
+ // Swap the audio elements
308
+ const temp = channel.queue[slotA];
309
+ channel.queue[slotA] = channel.queue[slotB];
310
+ channel.queue[slotB] = temp;
311
+ // Emit queue change event
312
+ (0, events_1.emitQueueChange)(channelNumber, info_1.audioChannels);
313
+ const updatedQueue = (0, utils_1.createQueueSnapshot)(channelNumber, info_1.audioChannels);
314
+ return {
315
+ success: true,
316
+ updatedQueue: updatedQueue !== null && updatedQueue !== void 0 ? updatedQueue : undefined
317
+ };
318
+ });
319
+ exports.swapQueueItems = swapQueueItems;
package/dist/types.d.ts CHANGED
@@ -1,6 +1,15 @@
1
1
  /**
2
2
  * @fileoverview Type definitions for the audio-channel-queue package
3
3
  */
4
+ /**
5
+ * Maximum number of audio channels allowed to prevent memory exhaustion
6
+ */
7
+ export declare const MAX_CHANNELS: number;
8
+ /**
9
+ * Symbol used as a key for global (channel-wide) progress callbacks
10
+ * This avoids the need for `null as any` type assertions
11
+ */
12
+ export declare const GLOBAL_PROGRESS_KEY: unique symbol;
4
13
  /**
5
14
  * Array of HTMLAudioElement objects representing an audio queue
6
15
  */
@@ -8,9 +17,9 @@ export type AudioQueue = HTMLAudioElement[];
8
17
  /**
9
18
  * Basic audio queue channel structure
10
19
  */
11
- export type AudioQueueChannel = {
20
+ export interface AudioQueueChannel {
12
21
  queue: AudioQueue;
13
- };
22
+ }
14
23
  /**
15
24
  * Volume ducking configuration for channels
16
25
  */
@@ -29,18 +38,31 @@ export interface VolumeConfig {
29
38
  transitionEasing?: EasingType;
30
39
  }
31
40
  /**
32
- * Audio file configuration for queueing
41
+ * Configuration options for queuing audio
33
42
  */
34
43
  export interface AudioQueueOptions {
35
- /** Whether to add the audio to the front of the queue (defaults to false) */
44
+ /** Whether to add this audio to the front of the queue (after currently playing) */
36
45
  addToFront?: boolean;
37
46
  /** Whether the audio should loop when it finishes */
38
47
  loop?: boolean;
39
- /** Whether to add the audio with priority (same as addToFront) */
48
+ /** Maximum number of items allowed in the queue (defaults to unlimited) */
49
+ maxQueueSize?: number;
50
+ /** @deprecated Use addToFront instead. Legacy support for priority queuing */
40
51
  priority?: boolean;
41
- /** Volume level for this specific audio file (0-1, defaults to channel volume) */
52
+ /** Volume level for this specific audio (0-1) */
42
53
  volume?: number;
43
54
  }
55
+ /**
56
+ * Global queue configuration options
57
+ */
58
+ export interface QueueConfig {
59
+ /** Default maximum queue size across all channels (defaults to unlimited) */
60
+ defaultMaxQueueSize?: number;
61
+ /** Whether to drop oldest items when queue is full (defaults to false - reject new items) */
62
+ dropOldestWhenFull?: boolean;
63
+ /** Whether to show warnings when queue limits are reached (defaults to true) */
64
+ showQueueWarnings?: boolean;
65
+ }
44
66
  /**
45
67
  * Comprehensive audio information interface providing metadata about currently playing audio
46
68
  */
@@ -126,6 +148,17 @@ export interface QueueSnapshot {
126
148
  /** Current volume level for the channel (0-1) */
127
149
  volume: number;
128
150
  }
151
+ /**
152
+ * Information about a queue manipulation operation result
153
+ */
154
+ export interface QueueManipulationResult {
155
+ /** Error message if operation failed */
156
+ error?: string;
157
+ /** Whether the operation was successful */
158
+ success: boolean;
159
+ /** The queue snapshot after the operation (if successful) */
160
+ updatedQueue?: QueueSnapshot;
161
+ }
129
162
  /**
130
163
  * Callback function type for audio progress updates
131
164
  * @param info Current audio information
@@ -198,7 +231,7 @@ export interface ErrorRecoveryOptions {
198
231
  */
199
232
  export type AudioErrorCallback = (errorInfo: AudioErrorInfo) => void;
200
233
  /**
201
- * Extended audio queue channel with error handling capabilities
234
+ * Extended audio channel with queue management and callback support
202
235
  */
203
236
  export interface ExtendedAudioQueueChannel {
204
237
  audioCompleteCallbacks: Set<AudioCompleteCallback>;
@@ -207,13 +240,16 @@ export interface ExtendedAudioQueueChannel {
207
240
  audioResumeCallbacks: Set<AudioResumeCallback>;
208
241
  audioStartCallbacks: Set<AudioStartCallback>;
209
242
  fadeState?: ChannelFadeState;
210
- isPaused?: boolean;
211
- progressCallbacks: Map<HTMLAudioElement | null, Set<ProgressCallback>>;
243
+ isPaused: boolean;
244
+ /** Active operation lock to prevent race conditions */
245
+ isLocked?: boolean;
246
+ /** Maximum allowed queue size for this channel */
247
+ maxQueueSize?: number;
248
+ progressCallbacks: Map<HTMLAudioElement | typeof GLOBAL_PROGRESS_KEY, Set<ProgressCallback>>;
212
249
  queue: HTMLAudioElement[];
213
250
  queueChangeCallbacks: Set<QueueChangeCallback>;
214
251
  retryConfig?: RetryConfig;
215
- volume?: number;
216
- volumeConfig?: VolumeConfig;
252
+ volume: number;
217
253
  }
218
254
  /**
219
255
  * Easing function types for volume transitions
@@ -232,6 +268,13 @@ export declare enum FadeType {
232
268
  Gentle = "gentle",
233
269
  Dramatic = "dramatic"
234
270
  }
271
+ /**
272
+ * Timer types for volume transitions to ensure proper cleanup
273
+ */
274
+ export declare enum TimerType {
275
+ RequestAnimationFrame = "raf",
276
+ Timeout = "timeout"
277
+ }
235
278
  /**
236
279
  * Configuration for fade transitions
237
280
  */
@@ -253,4 +296,8 @@ export interface ChannelFadeState {
253
296
  fadeType: FadeType;
254
297
  /** Whether the channel is currently paused due to fade */
255
298
  isPaused: boolean;
299
+ /** Custom duration in milliseconds if specified (overrides fade type default) */
300
+ customDuration?: number;
301
+ /** Whether the channel is currently transitioning (during any fade operation) to prevent capturing intermediate volumes during rapid pause/resume toggles */
302
+ isTransitioning?: boolean;
256
303
  }
package/dist/types.js CHANGED
@@ -3,7 +3,16 @@
3
3
  * @fileoverview Type definitions for the audio-channel-queue package
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.FadeType = exports.EasingType = void 0;
6
+ exports.TimerType = exports.FadeType = exports.EasingType = exports.GLOBAL_PROGRESS_KEY = exports.MAX_CHANNELS = void 0;
7
+ /**
8
+ * Maximum number of audio channels allowed to prevent memory exhaustion
9
+ */
10
+ exports.MAX_CHANNELS = 64;
11
+ /**
12
+ * Symbol used as a key for global (channel-wide) progress callbacks
13
+ * This avoids the need for `null as any` type assertions
14
+ */
15
+ exports.GLOBAL_PROGRESS_KEY = Symbol('global-progress-callbacks');
7
16
  /**
8
17
  * Easing function types for volume transitions
9
18
  */
@@ -23,3 +32,11 @@ var FadeType;
23
32
  FadeType["Gentle"] = "gentle";
24
33
  FadeType["Dramatic"] = "dramatic";
25
34
  })(FadeType || (exports.FadeType = FadeType = {}));
35
+ /**
36
+ * Timer types for volume transitions to ensure proper cleanup
37
+ */
38
+ var TimerType;
39
+ (function (TimerType) {
40
+ TimerType["RequestAnimationFrame"] = "raf";
41
+ TimerType["Timeout"] = "timeout";
42
+ })(TimerType || (exports.TimerType = TimerType = {}));
package/dist/utils.d.ts CHANGED
@@ -2,6 +2,31 @@
2
2
  * @fileoverview Utility functions for the audio-channel-queue package
3
3
  */
4
4
  import { AudioInfo, QueueSnapshot, ExtendedAudioQueueChannel } from './types';
5
+ /**
6
+ * Validates an audio URL for security and correctness
7
+ * @param url - The URL to validate
8
+ * @returns The validated URL
9
+ * @throws Error if the URL is invalid or potentially malicious
10
+ * @example
11
+ * ```typescript
12
+ * validateAudioUrl('https://example.com/audio.mp3'); // Valid
13
+ * validateAudioUrl('./sounds/local.wav'); // Valid relative path
14
+ * validateAudioUrl('javascript:alert("XSS")'); // Throws error
15
+ * validateAudioUrl('data:text/html,<script>alert("XSS")</script>'); // Throws error
16
+ * ```
17
+ */
18
+ export declare const validateAudioUrl: (url: string) => string;
19
+ /**
20
+ * Sanitizes a string for safe display in HTML contexts
21
+ * @param text - The text to sanitize
22
+ * @returns The sanitized text safe for display
23
+ * @example
24
+ * ```typescript
25
+ * sanitizeForDisplay('<script>alert("XSS")</script>'); // Returns: '&lt;script&gt;alert("XSS")&lt;/script&gt;'
26
+ * sanitizeForDisplay('normal-file.mp3'); // Returns: 'normal-file.mp3'
27
+ * ```
28
+ */
29
+ export declare const sanitizeForDisplay: (text: string) => string;
5
30
  /**
6
31
  * Extracts the filename from a URL string
7
32
  * @param url - The URL to extract the filename from