audio-channel-queue 1.9.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.
- package/README.md +197 -313
- package/dist/core.d.ts +59 -1
- package/dist/core.js +331 -40
- package/dist/errors.d.ts +1 -0
- package/dist/errors.js +10 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +21 -2
- package/dist/info.d.ts +17 -6
- package/dist/info.js +81 -10
- package/dist/pause.js +1 -1
- package/dist/queue-manipulation.d.ts +104 -0
- package/dist/queue-manipulation.js +319 -0
- package/dist/types.d.ts +46 -8
- package/dist/types.js +13 -1
- package/dist/utils.d.ts +25 -0
- package/dist/utils.js +98 -10
- package/dist/volume.d.ts +14 -1
- package/dist/volume.js +173 -54
- package/package.json +12 -2
- package/src/core.ts +389 -50
- package/src/errors.ts +14 -2
- package/src/index.ts +26 -5
- package/src/info.ts +107 -9
- package/src/pause.ts +4 -6
- package/src/queue-manipulation.ts +378 -0
- package/src/types.ts +51 -9
- package/src/utils.ts +110 -9
- package/src/volume.ts +214 -62
package/dist/volume.js
CHANGED
|
@@ -12,11 +12,18 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
12
12
|
});
|
|
13
13
|
};
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
-
exports.restoreVolumeLevels = exports.fadeVolume = exports.applyVolumeDucking = exports.clearVolumeDucking = exports.setVolumeDucking = exports.setAllChannelsVolume = exports.getAllChannelsVolume = exports.getChannelVolume = exports.setChannelVolume = exports.transitionVolume = exports.getFadeConfig = void 0;
|
|
15
|
+
exports.cancelAllVolumeTransitions = exports.cancelVolumeTransition = exports.restoreVolumeLevels = exports.fadeVolume = exports.applyVolumeDucking = exports.clearVolumeDucking = exports.setVolumeDucking = exports.setAllChannelsVolume = exports.getAllChannelsVolume = exports.getChannelVolume = exports.setChannelVolume = exports.transitionVolume = exports.getFadeConfig = void 0;
|
|
16
16
|
const types_1 = require("./types");
|
|
17
17
|
const info_1 = require("./info");
|
|
18
18
|
// Store active volume transitions to handle interruptions
|
|
19
19
|
const activeTransitions = new Map();
|
|
20
|
+
// Track which timer type was used for each channel
|
|
21
|
+
const timerTypes = new Map();
|
|
22
|
+
/**
|
|
23
|
+
* Global volume ducking configuration
|
|
24
|
+
* Stores the volume ducking settings that apply to all channels
|
|
25
|
+
*/
|
|
26
|
+
let globalVolumeConfig = null;
|
|
20
27
|
/**
|
|
21
28
|
* Predefined fade configurations for different transition types
|
|
22
29
|
*/
|
|
@@ -82,22 +89,27 @@ const transitionVolume = (channelNumber_1, targetVolume_1, ...args_1) => __await
|
|
|
82
89
|
// Cancel any existing transition for this channel
|
|
83
90
|
if (activeTransitions.has(channelNumber)) {
|
|
84
91
|
const transitionId = activeTransitions.get(channelNumber);
|
|
92
|
+
const timerType = timerTypes.get(channelNumber);
|
|
85
93
|
if (transitionId) {
|
|
86
|
-
//
|
|
87
|
-
if (
|
|
94
|
+
// Cancel based on the timer type that was actually used
|
|
95
|
+
if (timerType === types_1.TimerType.RequestAnimationFrame &&
|
|
96
|
+
typeof cancelAnimationFrame !== 'undefined') {
|
|
88
97
|
cancelAnimationFrame(transitionId);
|
|
89
98
|
}
|
|
90
|
-
|
|
99
|
+
else if (timerType === types_1.TimerType.Timeout) {
|
|
100
|
+
clearTimeout(transitionId);
|
|
101
|
+
}
|
|
91
102
|
}
|
|
92
103
|
activeTransitions.delete(channelNumber);
|
|
104
|
+
timerTypes.delete(channelNumber);
|
|
93
105
|
}
|
|
94
106
|
// If no change needed, resolve immediately
|
|
95
107
|
if (Math.abs(volumeDelta) < 0.001) {
|
|
96
108
|
channel.volume = targetVolume;
|
|
97
109
|
return Promise.resolve();
|
|
98
110
|
}
|
|
99
|
-
// Handle zero duration - instant change
|
|
100
|
-
if (duration
|
|
111
|
+
// Handle zero or negative duration - instant change
|
|
112
|
+
if (duration <= 0) {
|
|
101
113
|
channel.volume = targetVolume;
|
|
102
114
|
if (channel.queue.length > 0) {
|
|
103
115
|
channel.queue[0].volume = targetVolume;
|
|
@@ -121,6 +133,7 @@ const transitionVolume = (channelNumber_1, targetVolume_1, ...args_1) => __await
|
|
|
121
133
|
if (progress >= 1) {
|
|
122
134
|
// Transition complete
|
|
123
135
|
activeTransitions.delete(channelNumber);
|
|
136
|
+
timerTypes.delete(channelNumber);
|
|
124
137
|
resolve();
|
|
125
138
|
}
|
|
126
139
|
else {
|
|
@@ -128,11 +141,13 @@ const transitionVolume = (channelNumber_1, targetVolume_1, ...args_1) => __await
|
|
|
128
141
|
if (typeof requestAnimationFrame !== 'undefined') {
|
|
129
142
|
const rafId = requestAnimationFrame(updateVolume);
|
|
130
143
|
activeTransitions.set(channelNumber, rafId);
|
|
144
|
+
timerTypes.set(channelNumber, types_1.TimerType.RequestAnimationFrame);
|
|
131
145
|
}
|
|
132
146
|
else {
|
|
133
147
|
// In test environment, use shorter intervals
|
|
134
148
|
const timeoutId = setTimeout(updateVolume, 1);
|
|
135
149
|
activeTransitions.set(channelNumber, timeoutId);
|
|
150
|
+
timerTypes.set(channelNumber, types_1.TimerType.Timeout);
|
|
136
151
|
}
|
|
137
152
|
}
|
|
138
153
|
};
|
|
@@ -146,6 +161,7 @@ exports.transitionVolume = transitionVolume;
|
|
|
146
161
|
* @param volume - Volume level (0-1)
|
|
147
162
|
* @param transitionDuration - Optional transition duration in milliseconds
|
|
148
163
|
* @param easing - Optional easing function
|
|
164
|
+
* @throws Error if the channel number exceeds the maximum allowed channels
|
|
149
165
|
* @example
|
|
150
166
|
* ```typescript
|
|
151
167
|
* setChannelVolume(0, 0.5); // Set channel 0 to 50%
|
|
@@ -154,6 +170,13 @@ exports.transitionVolume = transitionVolume;
|
|
|
154
170
|
*/
|
|
155
171
|
const setChannelVolume = (channelNumber, volume, transitionDuration, easing) => __awaiter(void 0, void 0, void 0, function* () {
|
|
156
172
|
const clampedVolume = Math.max(0, Math.min(1, volume));
|
|
173
|
+
// Check channel number limits
|
|
174
|
+
if (channelNumber < 0) {
|
|
175
|
+
throw new Error('Channel number must be non-negative');
|
|
176
|
+
}
|
|
177
|
+
if (channelNumber >= types_1.MAX_CHANNELS) {
|
|
178
|
+
throw new Error(`Channel number ${channelNumber} exceeds maximum allowed channels (${types_1.MAX_CHANNELS})`);
|
|
179
|
+
}
|
|
157
180
|
if (!info_1.audioChannels[channelNumber]) {
|
|
158
181
|
info_1.audioChannels[channelNumber] = {
|
|
159
182
|
audioCompleteCallbacks: new Set(),
|
|
@@ -236,6 +259,7 @@ exports.setAllChannelsVolume = setAllChannelsVolume;
|
|
|
236
259
|
* Configures volume ducking for channels. When the priority channel plays audio,
|
|
237
260
|
* all other channels will be automatically reduced to the ducking volume level
|
|
238
261
|
* @param config - Volume ducking configuration
|
|
262
|
+
* @throws Error if the priority channel number exceeds the maximum allowed channels
|
|
239
263
|
* @example
|
|
240
264
|
* ```typescript
|
|
241
265
|
* // When channel 1 plays, reduce all other channels to 20% volume
|
|
@@ -247,8 +271,18 @@ exports.setAllChannelsVolume = setAllChannelsVolume;
|
|
|
247
271
|
* ```
|
|
248
272
|
*/
|
|
249
273
|
const setVolumeDucking = (config) => {
|
|
250
|
-
|
|
251
|
-
|
|
274
|
+
const { priorityChannel } = config;
|
|
275
|
+
// Check priority channel limits
|
|
276
|
+
if (priorityChannel < 0) {
|
|
277
|
+
throw new Error('Priority channel number must be non-negative');
|
|
278
|
+
}
|
|
279
|
+
if (priorityChannel >= types_1.MAX_CHANNELS) {
|
|
280
|
+
throw new Error(`Priority channel ${priorityChannel} exceeds maximum allowed channels (${types_1.MAX_CHANNELS})`);
|
|
281
|
+
}
|
|
282
|
+
// Store the configuration globally
|
|
283
|
+
globalVolumeConfig = config;
|
|
284
|
+
// Ensure we have enough channels for the priority channel
|
|
285
|
+
while (info_1.audioChannels.length <= priorityChannel) {
|
|
252
286
|
info_1.audioChannels.push({
|
|
253
287
|
audioCompleteCallbacks: new Set(),
|
|
254
288
|
audioErrorCallbacks: new Set(),
|
|
@@ -262,24 +296,6 @@ const setVolumeDucking = (config) => {
|
|
|
262
296
|
volume: 1.0
|
|
263
297
|
});
|
|
264
298
|
}
|
|
265
|
-
// Apply the config to all existing channels
|
|
266
|
-
info_1.audioChannels.forEach((channel, index) => {
|
|
267
|
-
if (!info_1.audioChannels[index]) {
|
|
268
|
-
info_1.audioChannels[index] = {
|
|
269
|
-
audioCompleteCallbacks: new Set(),
|
|
270
|
-
audioErrorCallbacks: new Set(),
|
|
271
|
-
audioPauseCallbacks: new Set(),
|
|
272
|
-
audioResumeCallbacks: new Set(),
|
|
273
|
-
audioStartCallbacks: new Set(),
|
|
274
|
-
isPaused: false,
|
|
275
|
-
progressCallbacks: new Map(),
|
|
276
|
-
queue: [],
|
|
277
|
-
queueChangeCallbacks: new Set(),
|
|
278
|
-
volume: 1.0
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
info_1.audioChannels[index].volumeConfig = config;
|
|
282
|
-
});
|
|
283
299
|
};
|
|
284
300
|
exports.setVolumeDucking = setVolumeDucking;
|
|
285
301
|
/**
|
|
@@ -290,11 +306,7 @@ exports.setVolumeDucking = setVolumeDucking;
|
|
|
290
306
|
* ```
|
|
291
307
|
*/
|
|
292
308
|
const clearVolumeDucking = () => {
|
|
293
|
-
|
|
294
|
-
if (channel) {
|
|
295
|
-
delete channel.volumeConfig;
|
|
296
|
-
}
|
|
297
|
-
});
|
|
309
|
+
globalVolumeConfig = null;
|
|
298
310
|
};
|
|
299
311
|
exports.clearVolumeDucking = clearVolumeDucking;
|
|
300
312
|
/**
|
|
@@ -303,22 +315,31 @@ exports.clearVolumeDucking = clearVolumeDucking;
|
|
|
303
315
|
* @internal
|
|
304
316
|
*/
|
|
305
317
|
const applyVolumeDucking = (activeChannelNumber) => __awaiter(void 0, void 0, void 0, function* () {
|
|
318
|
+
var _a, _b;
|
|
319
|
+
// Check if ducking is configured and this channel is the priority channel
|
|
320
|
+
if (!globalVolumeConfig || globalVolumeConfig.priorityChannel !== activeChannelNumber) {
|
|
321
|
+
return; // No ducking configured for this channel
|
|
322
|
+
}
|
|
323
|
+
const config = globalVolumeConfig;
|
|
306
324
|
const transitionPromises = [];
|
|
325
|
+
const duration = (_a = config.duckTransitionDuration) !== null && _a !== void 0 ? _a : 250;
|
|
326
|
+
const easing = (_b = config.transitionEasing) !== null && _b !== void 0 ? _b : types_1.EasingType.EaseOut;
|
|
327
|
+
// Duck all channels except the priority channel
|
|
307
328
|
info_1.audioChannels.forEach((channel, channelNumber) => {
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
329
|
+
if (!channel || channel.queue.length === 0) {
|
|
330
|
+
return; // Skip channels without audio
|
|
331
|
+
}
|
|
332
|
+
if (channelNumber === activeChannelNumber) {
|
|
333
|
+
// This is the priority channel - set to priority volume
|
|
334
|
+
// Only change audio volume, preserve channel.volume as desired volume
|
|
335
|
+
const currentAudio = channel.queue[0];
|
|
336
|
+
transitionPromises.push(transitionAudioVolume(currentAudio, config.priorityVolume, duration, easing));
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
// This is a background channel - duck it
|
|
340
|
+
// Only change audio volume, preserve channel.volume as desired volume
|
|
341
|
+
const currentAudio = channel.queue[0];
|
|
342
|
+
transitionPromises.push(transitionAudioVolume(currentAudio, config.duckingVolume, duration, easing));
|
|
322
343
|
}
|
|
323
344
|
});
|
|
324
345
|
// Wait for all transitions to complete
|
|
@@ -343,25 +364,123 @@ const fadeVolume = (channelNumber_1, targetVolume_1, ...args_1) => __awaiter(voi
|
|
|
343
364
|
});
|
|
344
365
|
exports.fadeVolume = fadeVolume;
|
|
345
366
|
/**
|
|
346
|
-
* Restores normal volume levels when priority channel
|
|
367
|
+
* Restores normal volume levels when priority channel queue becomes empty
|
|
347
368
|
* @param stoppedChannelNumber - The channel that just stopped playing
|
|
348
369
|
* @internal
|
|
349
370
|
*/
|
|
350
371
|
const restoreVolumeLevels = (stoppedChannelNumber) => __awaiter(void 0, void 0, void 0, function* () {
|
|
372
|
+
// Check if ducking is configured and this channel is the priority channel
|
|
373
|
+
if (!globalVolumeConfig || globalVolumeConfig.priorityChannel !== stoppedChannelNumber) {
|
|
374
|
+
return; // No ducking configured for this channel
|
|
375
|
+
}
|
|
376
|
+
// Check if the priority channel queue is now empty
|
|
377
|
+
const priorityChannel = info_1.audioChannels[stoppedChannelNumber];
|
|
378
|
+
if (priorityChannel && priorityChannel.queue.length > 0) {
|
|
379
|
+
return; // Priority channel still has audio queued, don't restore yet
|
|
380
|
+
}
|
|
381
|
+
const config = globalVolumeConfig;
|
|
351
382
|
const transitionPromises = [];
|
|
383
|
+
// Restore volume for all channels EXCEPT the priority channel
|
|
352
384
|
info_1.audioChannels.forEach((channel, channelNumber) => {
|
|
353
385
|
var _a, _b, _c;
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
const duration = (_a = config.restoreTransitionDuration) !== null && _a !== void 0 ? _a : 500;
|
|
358
|
-
const easing = (_b = config.transitionEasing) !== null && _b !== void 0 ? _b : types_1.EasingType.EaseOut;
|
|
359
|
-
// Priority channel stopped, restore normal volumes
|
|
360
|
-
transitionPromises.push((0, exports.transitionVolume)(channelNumber, (_c = channel.volume) !== null && _c !== void 0 ? _c : 1.0, duration, easing));
|
|
361
|
-
}
|
|
386
|
+
// Skip the priority channel itself and channels without audio
|
|
387
|
+
if (channelNumber === stoppedChannelNumber || !channel || channel.queue.length === 0) {
|
|
388
|
+
return;
|
|
362
389
|
}
|
|
390
|
+
// Restore this channel to its desired volume
|
|
391
|
+
const duration = (_a = config.restoreTransitionDuration) !== null && _a !== void 0 ? _a : 500;
|
|
392
|
+
const easing = (_b = config.transitionEasing) !== null && _b !== void 0 ? _b : types_1.EasingType.EaseOut;
|
|
393
|
+
const targetVolume = (_c = channel.volume) !== null && _c !== void 0 ? _c : 1.0;
|
|
394
|
+
// Only transition the audio element volume, keep channel.volume as the desired volume
|
|
395
|
+
const currentAudio = channel.queue[0];
|
|
396
|
+
transitionPromises.push(transitionAudioVolume(currentAudio, targetVolume, duration, easing));
|
|
363
397
|
});
|
|
364
398
|
// Wait for all transitions to complete
|
|
365
399
|
yield Promise.all(transitionPromises);
|
|
366
400
|
});
|
|
367
401
|
exports.restoreVolumeLevels = restoreVolumeLevels;
|
|
402
|
+
/**
|
|
403
|
+
* Transitions only the audio element volume without affecting channel.volume
|
|
404
|
+
* This is used for ducking/restoration where channel.volume represents desired volume
|
|
405
|
+
* @param audio - The audio element to transition
|
|
406
|
+
* @param targetVolume - Target volume level (0-1)
|
|
407
|
+
* @param duration - Transition duration in milliseconds
|
|
408
|
+
* @param easing - Easing function type
|
|
409
|
+
* @returns Promise that resolves when transition completes
|
|
410
|
+
* @internal
|
|
411
|
+
*/
|
|
412
|
+
const transitionAudioVolume = (audio_1, targetVolume_1, ...args_1) => __awaiter(void 0, [audio_1, targetVolume_1, ...args_1], void 0, function* (audio, targetVolume, duration = 250, easing = types_1.EasingType.EaseOut) {
|
|
413
|
+
const startVolume = audio.volume;
|
|
414
|
+
const volumeDelta = targetVolume - startVolume;
|
|
415
|
+
// If no change needed, resolve immediately
|
|
416
|
+
if (Math.abs(volumeDelta) < 0.001) {
|
|
417
|
+
return Promise.resolve();
|
|
418
|
+
}
|
|
419
|
+
// Handle zero or negative duration - instant change
|
|
420
|
+
if (duration <= 0) {
|
|
421
|
+
audio.volume = Math.max(0, Math.min(1, targetVolume));
|
|
422
|
+
return Promise.resolve();
|
|
423
|
+
}
|
|
424
|
+
const startTime = performance.now();
|
|
425
|
+
const easingFn = easingFunctions[easing];
|
|
426
|
+
return new Promise((resolve) => {
|
|
427
|
+
const updateVolume = () => {
|
|
428
|
+
const elapsed = performance.now() - startTime;
|
|
429
|
+
const progress = Math.min(elapsed / duration, 1);
|
|
430
|
+
const easedProgress = easingFn(progress);
|
|
431
|
+
const currentVolume = startVolume + volumeDelta * easedProgress;
|
|
432
|
+
const clampedVolume = Math.max(0, Math.min(1, currentVolume));
|
|
433
|
+
// Only apply volume to audio element, not channel.volume
|
|
434
|
+
audio.volume = clampedVolume;
|
|
435
|
+
if (progress >= 1) {
|
|
436
|
+
resolve();
|
|
437
|
+
}
|
|
438
|
+
else {
|
|
439
|
+
// Use requestAnimationFrame in browser, setTimeout in tests
|
|
440
|
+
if (typeof requestAnimationFrame !== 'undefined') {
|
|
441
|
+
requestAnimationFrame(updateVolume);
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
444
|
+
setTimeout(updateVolume, 1);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
updateVolume();
|
|
449
|
+
});
|
|
450
|
+
});
|
|
451
|
+
/**
|
|
452
|
+
* Cancels any active volume transition for a specific channel
|
|
453
|
+
* @param channelNumber - The channel number to cancel transitions for
|
|
454
|
+
* @internal
|
|
455
|
+
*/
|
|
456
|
+
const cancelVolumeTransition = (channelNumber) => {
|
|
457
|
+
if (activeTransitions.has(channelNumber)) {
|
|
458
|
+
const transitionId = activeTransitions.get(channelNumber);
|
|
459
|
+
const timerType = timerTypes.get(channelNumber);
|
|
460
|
+
if (transitionId) {
|
|
461
|
+
// Cancel based on the timer type that was actually used
|
|
462
|
+
if (timerType === types_1.TimerType.RequestAnimationFrame &&
|
|
463
|
+
typeof cancelAnimationFrame !== 'undefined') {
|
|
464
|
+
cancelAnimationFrame(transitionId);
|
|
465
|
+
}
|
|
466
|
+
else if (timerType === types_1.TimerType.Timeout) {
|
|
467
|
+
clearTimeout(transitionId);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
activeTransitions.delete(channelNumber);
|
|
471
|
+
timerTypes.delete(channelNumber);
|
|
472
|
+
}
|
|
473
|
+
};
|
|
474
|
+
exports.cancelVolumeTransition = cancelVolumeTransition;
|
|
475
|
+
/**
|
|
476
|
+
* Cancels all active volume transitions across all channels
|
|
477
|
+
* @internal
|
|
478
|
+
*/
|
|
479
|
+
const cancelAllVolumeTransitions = () => {
|
|
480
|
+
// Get all active channel numbers to avoid modifying Map while iterating
|
|
481
|
+
const activeChannels = Array.from(activeTransitions.keys());
|
|
482
|
+
activeChannels.forEach((channelNumber) => {
|
|
483
|
+
(0, exports.cancelVolumeTransition)(channelNumber);
|
|
484
|
+
});
|
|
485
|
+
};
|
|
486
|
+
exports.cancelAllVolumeTransitions = cancelAllVolumeTransitions;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "audio-channel-queue",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0",
|
|
4
4
|
"description": "Allows you to queue audio files to different playback channels.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -17,7 +17,16 @@
|
|
|
17
17
|
"lint": "npx eslint src/**/*.ts __tests__/**/*.ts",
|
|
18
18
|
"lint:fix": "npx eslint src/**/*.ts __tests__/**/*.ts --fix",
|
|
19
19
|
"format": "npx prettier --write src/**/*.ts __tests__/**/*.ts",
|
|
20
|
-
"format:check": "npx prettier --check src/**/*.ts __tests__/**/*.ts"
|
|
20
|
+
"format:check": "npx prettier --check src/**/*.ts __tests__/**/*.ts",
|
|
21
|
+
"clean": "rimraf dist",
|
|
22
|
+
"prebuild": "npm run clean",
|
|
23
|
+
"prepack": "npm run validate && npm run build",
|
|
24
|
+
"validate": "npm run format:check && npm run lint && npm run test",
|
|
25
|
+
"publish:patch": "npm version patch && npm run publish:release",
|
|
26
|
+
"publish:minor": "npm version minor && npm run publish:release",
|
|
27
|
+
"publish:major": "npm version major && npm run publish:release",
|
|
28
|
+
"publish:release": "npm run validate && npm run build && npm publish",
|
|
29
|
+
"publish:dry": "npm run validate && npm run build && npm publish --dry-run"
|
|
21
30
|
},
|
|
22
31
|
"repository": {
|
|
23
32
|
"type": "git",
|
|
@@ -42,6 +51,7 @@
|
|
|
42
51
|
"@types/jest": "^29.5.13",
|
|
43
52
|
"jest": "^29.7.0",
|
|
44
53
|
"jest-environment-jsdom": "^29.7.0",
|
|
54
|
+
"rimraf": "^6.0.1",
|
|
45
55
|
"ts-jest": "^29.2.5",
|
|
46
56
|
"typescript": "^5.6.2",
|
|
47
57
|
"typescript-eslint": "^8.34.1"
|