feat(test): add Playback service tests

This commit is contained in:
Phan An 2022-05-14 16:45:48 +02:00
parent cc36f49796
commit 9e1e708782
No known key found for this signature in database
GPG key ID: A81E4477F0BB6FDC
9 changed files with 805 additions and 703 deletions

View file

@ -66,9 +66,9 @@
"factoria": "^4.0.0",
"file-loader": "^1.1.6",
"font-awesome": "^4.7.0",
"happy-dom": "^3.1.0",
"husky": "^4.2.5",
"jest-serializer-vue": "^2.0.2",
"jsdom": "^19.0.0",
"kill-port": "^1.6.1",
"laravel-mix": "^6.0.43",
"lint-staged": "^10.3.0",

View file

@ -7,8 +7,6 @@ import { defineComponent, nextTick } from 'vue'
import { commonStore, userStore } from '@/stores'
import factory from '@/__tests__/factory'
declare type Methods<T> = { [K in keyof T]: T[K] extends Closure ? K : never; }[keyof T] & (string | symbol);
export default abstract class UnitTestCase {
private backupMethods = new Map()
@ -44,7 +42,7 @@ export default abstract class UnitTestCase {
return this.actingAs(factory.states('admin')<User>('user'))
}
protected mock<T, M extends Methods<Required<T>>> (obj: T, methodName: M, implementation?: any) {
protected mock<T, M extends MethodOf<Required<T>>> (obj: T, methodName: M, implementation?: any) {
const mock = vi.fn()
if (implementation !== undefined) {
@ -88,5 +86,11 @@ export default abstract class UnitTestCase {
}
}
protected setReadOnlyProperty<T> (obj: T, prop: keyof T, value: any) {
return Object.defineProperty(obj, prop, {
get: () => value
})
}
protected abstract test ()
}

View file

@ -1,14 +0,0 @@
/* eslint @typescript-eslint/no-unused-vars: 0 */
import _ from 'lodash'
_.orderBy = jest.fn(<T> (collection: T[]): T[] => collection)
_.shuffle = jest.fn(<T> (collection: T[]): T[] => collection)
_.throttle = jest.fn((fn: Function, wait: number): any => fn)
_.sample = jest.fn(<T> (collection: T[]): T | undefined => {
return collection.length ? collection[0] : undefined
})
module.exports = _

View file

@ -1,496 +0,0 @@
import plyr from 'plyr'
import { orderBy, shuffle } from 'lodash'
import { playbackService, socketService } from '@/services'
import { eventBus, noop } from '@/utils'
import { mock } from '@/__tests__/__helpers__'
import {
commonStore,
preferenceStore as preferences,
queueStore,
recentlyPlayedStore,
songStore,
userStore
} from '@/stores'
import router from '@/router'
import factory from '@/__tests__/factory'
import Vue from 'vue'
import FunctionPropertyNames = jest.FunctionPropertyNames
const prepareForTests = () => {
document.body.innerHTML = `
<div class="plyr">
<audio crossorigin="anonymous" controls></audio>
</div>
<input
class="plyr__volume"
id="volumeRange"
max="10"
step="0.1"
title="Volume"
type="range"
>
`
window.AudioContext = jest.fn().mockImplementation(() => {
return {
createMediaElementSource: jest.fn(noop)
}
})
}
describe('services/playback', () => {
beforeEach(() => prepareForTests())
afterEach(() => {
jest.resetModules()
jest.restoreAllMocks()
jest.clearAllMocks()
})
it('only initializes once', () => {
const plyrSetupSpy = jest.spyOn(plyr, 'setup')
playbackService.init()
expect(plyrSetupSpy).toHaveBeenCalled()
playbackService.init()
expect(plyrSetupSpy).toHaveBeenCalledTimes(1)
})
describe('listens to media events', () => {
it.each<[boolean, boolean, number, number, number]>([
/* playCountRegistered, isTranscoding, current media time, media duration, number of registerPlay()'s calls */
[false, false, 100, 400, 1],
[true, false, 100, 400, 0],
[false, true, 100, 400, 0],
[false, false, 100, 500, 0]
])(
'when playCountRegistered is %s, isTranscoding is %s, current media time is %d, media duration is %d, then registerPlay() should be call %d times',
(playCountRegistered, isTranscoding, currentTime, duration, numberOfCalls) => {
queueStore.current = factory<Song>('song', { playCountRegistered })
Object.defineProperty(playbackService, 'isTranscoding', { get: () => isTranscoding })
playbackService.init()
const mediaElement = playbackService.player!.media
// we can't set mediaElement.currentTime|duration directly because they're read-only
Object.defineProperties(mediaElement, {
currentTime: {
value: currentTime,
configurable: true
},
duration: {
value: duration,
configurable: true
}
})
const registerPlayMock = mock(playbackService, 'registerPlay')
mediaElement.dispatchEvent(new Event('timeupdate'))
expect(registerPlayMock).toHaveBeenCalledTimes(numberOfCalls)
})
it('plays next song if current song is errored', () => {
playbackService.init()
const playNextMock = mock(playbackService, 'playNext')
playbackService.player!.media.dispatchEvent(new Event('error'))
expect(playNextMock).toHaveBeenCalled()
})
it('scrobbles if current song ends', () => {
queueStore.current = factory<Song>('song')
commonStore.state.useLastfm = true
userStore.current = factory<User>('user', {
preferences: {
lastfm_session_key: 'foo'
}
})
playbackService.init()
const scrobbleMock = mock(songStore, 'scrobble')
playbackService.player!.media.dispatchEvent(new Event('ended'))
expect(scrobbleMock).toHaveBeenCalled()
})
it.each<[RepeatMode, number, number]>([['REPEAT_ONE', 1, 0], ['NO_REPEAT', 0, 1], ['REPEAT_ALL', 0, 1]])(
'when song ends, if repeat mode is %s then restart() is called %d times and playNext() is called %d times',
(repeatMode, restartCalls, playNextCalls) => {
commonStore.state.useLastfm = false // so that no scrobbling is made unnecessarily
preferences.repeatMode = repeatMode
playbackService.init()
const restartMock = mock(playbackService, 'restart')
const playNextMock = mock(playbackService, 'playNext')
playbackService.player!.media.dispatchEvent(new Event('ended'))
expect(restartMock).toHaveBeenCalledTimes(restartCalls)
expect(playNextMock).toHaveBeenCalledTimes(playNextCalls)
})
it.each([
[false, true, 300, 310, 0],
[true, false, 300, 310, 0],
[false, false, 300, 400, 0],
[false, false, 300, 310, 1]
])(
'when next song preloaded is %s, isTrancoding is %s, current media time is %d, media duration is %d, then preload() should be called %d times',
(preloaded, isTranscoding, currentTime, duration, numberOfCalls) => {
queueStore.current = factory<Song>('song', { playCountRegistered: true }) // avoid triggering play count logic
Object.defineProperty(queueStore, 'next', {
get: () => factory('song', { preloaded })
})
Object.defineProperty(playbackService, 'isTranscoding', { get: () => isTranscoding })
playbackService.init()
const mediaElement = playbackService.player!.media
Object.defineProperties(mediaElement, {
currentTime: {
value: currentTime,
configurable: true
},
duration: {
value: duration,
configurable: true
}
})
const preloadMock = mock(playbackService, 'preload')
mediaElement.dispatchEvent(new Event('timeupdate'))
expect(preloadMock).toHaveBeenCalledTimes(numberOfCalls)
}
)
})
it('registers play', () => {
const recentlyPlayedStoreAddMock = mock(recentlyPlayedStore, 'add')
const recentlyPlayedStoreFetchAllMock = mock(recentlyPlayedStore, 'fetchAll')
const registerPlayMock = mock(songStore, 'registerPlay')
const song = factory<Song>('song')
playbackService.registerPlay(song)
expect(recentlyPlayedStoreAddMock).toHaveBeenCalledWith(song)
expect(recentlyPlayedStoreFetchAllMock).toHaveBeenCalled()
expect(registerPlayMock).toHaveBeenCalledWith(song)
expect(song.playCountRegistered).toBe(true)
})
it('preloads a song', () => {
const setAttributeMock = jest.fn()
const loadMock = jest.fn()
const audioElement = {
setAttribute: setAttributeMock,
load: loadMock
}
const createElementMock = mock(document, 'createElement', audioElement)
mock(songStore, 'getSourceUrl').mockReturnValue('/foo?token=o5afd')
const song = factory<Song>('song')
playbackService.preload(song)
expect(createElementMock).toHaveBeenCalledWith('audio')
expect(setAttributeMock).toHaveBeenNthCalledWith(1, 'src', '/foo?token=o5afd')
expect(setAttributeMock).toHaveBeenNthCalledWith(2, 'preload', 'auto')
expect(loadMock).toHaveBeenCalled()
expect(song.preloaded).toBe(true)
})
it('restarts a song', async () => {
const song = factory<Song>('song')
Object.defineProperty(queueStore, 'current', {
get: () => song
})
mock(Math, 'floor', 1000)
const emitMock = mock(eventBus, 'emit')
const broadcastMock = mock(socketService, 'broadcast')
const showNotificationMock = mock(playbackService, 'showNotification')
const dataToBroadcast = {}
mock(songStore, 'generateDataToBroadcast', dataToBroadcast)
const restartMock = mock(playbackService.player!, 'restart')
const playMock = mock(window.HTMLMediaElement.prototype, 'play')
await playbackService.restart()
expect(song.playStartTime).toEqual(1000)
expect(song.playCountRegistered).toBe(false)
expect(emitMock).toHaveBeenCalledWith('SONG_STARTED', song)
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_SONG', dataToBroadcast)
expect(showNotificationMock).toHaveBeenCalled()
expect(restartMock).toHaveBeenCalled()
expect(playMock).toHaveBeenCalled()
})
it.each<[RepeatMode, RepeatMode]>([
['NO_REPEAT', 'REPEAT_ALL'],
['REPEAT_ALL', 'REPEAT_ONE'],
['REPEAT_ONE', 'NO_REPEAT']
])(
'it switches from repeat mode %s to repeat mode %s',
(fromMode, toMode) => {
preferences.repeatMode = fromMode
playbackService.changeRepeatMode()
expect(preferences.repeatMode).toEqual(toMode)
})
it('restarts song if playPrev is triggered after 5 seconds', async () => {
const restartMock = mock(playbackService.player!, 'restart')
Object.defineProperty(playbackService.player!.media, 'currentTime', {
get: () => 6
})
Object.defineProperty(queueStore, 'current', {
get: () => factory('song', { length: 120 })
})
await playbackService.playPrev()
expect(restartMock).toHaveBeenCalled()
})
it('stops if playPrev is triggered when there is no prev song and repeat mode is NO_REPEAT', async () => {
const stopMock = mock(playbackService, 'stop')
Object.defineProperty(playbackService.player!.media, 'currentTime', {
get: () => 4
})
Object.defineProperty(queueStore, 'current', {
get: () => factory('song', { length: 120 })
})
Object.defineProperty(playbackService, 'previous', {
get: () => null
})
preferences.repeatMode = 'NO_REPEAT'
await playbackService.playPrev()
expect(stopMock).toHaveBeenCalled()
})
it('plays the previous song', async () => {
const previousSong = factory('song')
Object.defineProperty(playbackService, 'previous', {
get: () => previousSong
})
Object.defineProperty(playbackService.player!.media, 'currentTime', {
get: () => 4
})
Object.defineProperty(queueStore, 'current', {
get: () => factory('song', { length: 120 })
})
const playMock = mock(playbackService, 'play')
await playbackService.playPrev()
expect(playMock).toHaveBeenCalledWith(previousSong)
})
it('stops if playNext is triggered when there is no next song and repeat mode is NO_REPEAT', async () => {
Object.defineProperty(playbackService, 'next', {
get: () => null
})
preferences.repeatMode = 'NO_REPEAT'
const stopMock = mock(playbackService, 'stop')
await playbackService.playNext()
expect(stopMock).toHaveBeenCalled()
})
it('plays the next song', async () => {
const nextSong = factory('song')
Object.defineProperty(playbackService, 'next', {
get: () => nextSong
})
const playMock = mock(playbackService, 'play')
await playbackService.playNext()
expect(playMock).toHaveBeenCalledWith(nextSong)
})
it('stops playback', () => {
const currentSong = factory<Song>('song')
const pauseMock = mock(playbackService.player!, 'pause')
const seekMock = mock(playbackService.player!, 'seek')
Object.defineProperty(queueStore, 'current', {
get: () => currentSong
})
const broadcastMock = mock(socketService, 'broadcast')
playbackService.stop()
expect(currentSong.playbackState).toEqual('Stopped')
expect(pauseMock).toHaveBeenCalled()
expect(seekMock).toHaveBeenCalledWith(0)
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_PLAYBACK_STOPPED')
expect(document.title).toEqual('Koel')
})
it('pauses playback', () => {
const currentSong = factory<Song>('song')
Object.defineProperty(queueStore, 'current', {
get: () => currentSong
})
const dataToBroadcast = {}
mock(songStore, 'generateDataToBroadcast', dataToBroadcast)
const pauseMock = mock(playbackService.player!, 'pause')
const broadcastMock = mock(socketService, 'broadcast')
playbackService.pause()
expect(currentSong.playbackState).toEqual('Paused')
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_SONG', dataToBroadcast)
expect(pauseMock).toHaveBeenCalled()
})
it('resumes playback', async () => {
const currentSong = factory<Song>('song')
Object.defineProperty(queueStore, 'current', {
get: () => currentSong
})
const dataToBroadcast = {}
mock(songStore, 'generateDataToBroadcast', dataToBroadcast)
const playMock = mock(window.HTMLMediaElement.prototype, 'play')
const broadcastMock = mock(socketService, 'broadcast')
const emitMock = mock(eventBus, 'emit')
playbackService.init()
await playbackService.resume()
expect(queueStore.current?.playbackState).toEqual('Playing')
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_SONG', dataToBroadcast)
expect(playMock).toHaveBeenCalled()
expect(emitMock).toHaveBeenCalledWith('SONG_STARTED', currentSong)
})
it('plays first in queue if toggled when there is no current song', async () => {
const playFirstInQueueMock = mock(playbackService, 'playFirstInQueue')
Object.defineProperty(queueStore, 'current', {
get: () => null
})
await playbackService.toggle()
expect(playFirstInQueueMock).toHaveBeenCalled()
})
it.each<[FunctionPropertyNames<typeof playbackService>, PlaybackState]>([
['resume', 'Stopped'],
['resume', 'Paused'],
['pause', 'Playing']
])('%ss playback if toggled when current song playback state is %s', async (action, playbackState) => {
const actionMock = mock(playbackService, action)
Object.defineProperty(queueStore, 'current', {
get: () => factory('song', { playbackState })
})
await playbackService.toggle()
expect(actionMock).toHaveBeenCalled()
})
it('queues and plays all songs shuffled by default', async () => {
const allSongs = factory<Song>('song', 5)
const shuffledSongs = factory<Song>('song', 5)
Object.defineProperty(songStore, 'all', {
get: () => allSongs
})
const firstSongInQueue = factory('song')
Object.defineProperty(queueStore, 'first', {
get: () => firstSongInQueue
})
const replaceQueueMock = mock(queueStore, 'replaceQueueWith')
const goMock = mock(router, 'go')
const playMock = mock(playbackService, 'play')
;(shuffle as jest.Mock).mockReturnValue(shuffledSongs)
await playbackService.queueAndPlay()
await Vue.nextTick()
expect(shuffle).toHaveBeenCalledWith(allSongs)
expect(replaceQueueMock).toHaveBeenCalledWith(shuffledSongs)
expect(goMock).toHaveBeenCalledWith('queue')
expect(playMock).toHaveBeenCalledWith(firstSongInQueue)
})
it('queues and plays songs without shuffling', async () => {
const songs = factory<Song>('song', 5)
const replaceQueueMock = mock(queueStore, 'replaceQueueWith')
const goMock = mock(router, 'go')
const playMock = mock(playbackService, 'play')
const firstSongInQueue = songs[0]
Object.defineProperty(queueStore, 'first', {
get: () => firstSongInQueue
})
await playbackService.queueAndPlay(songs)
await Vue.nextTick()
expect(shuffle).not.toHaveBeenCalled()
expect(replaceQueueMock).toHaveBeenCalledWith(songs)
expect(goMock).toHaveBeenCalledWith('queue')
expect(playMock).toHaveBeenCalledWith(firstSongInQueue)
})
it('queues and plays songs with shuffling', async () => {
const songs = factory<Song>('song', 5)
const shuffledSongs = factory<Song>('song', 5)
const replaceQueueMock = mock(queueStore, 'replaceQueueWith')
const goMock = mock(router, 'go')
const playMock = mock(playbackService, 'play')
const firstSongInQueue = songs[0]
Object.defineProperty(queueStore, 'first', {
get: () => firstSongInQueue
})
;(shuffle as jest.Mock).mockReturnValue(shuffledSongs)
await playbackService.queueAndPlay(songs, true)
await Vue.nextTick()
expect(shuffle).toHaveBeenCalledWith(songs)
expect(replaceQueueMock).toHaveBeenCalledWith(shuffledSongs)
expect(goMock).toHaveBeenCalledWith('queue')
expect(playMock).toHaveBeenCalledWith(firstSongInQueue)
})
it('plays first song in queue', async () => {
const songs = factory<Song>('song', 5)
queueStore.all = songs
Object.defineProperty(queueStore, 'first', {
get: () => songs[0]
})
const playMock = mock(playbackService, 'play')
await playbackService.playFirstInQueue()
expect(playMock).toHaveBeenCalledWith(songs[0])
})
it('playFirstInQueue triggers queueAndPlay if queue is empty', async () => {
queueStore.all = []
const queueAndPlayMock = mock(playbackService, 'queueAndPlay')
await playbackService.playFirstInQueue()
expect(queueAndPlayMock).toHaveBeenCalled()
})
it('plays all songs by an artist, shuffled', async () => {
const artist = factory<Artist>('artist', {
songs: factory<Song>('song', 5)
})
const queueAndPlayMock = mock(playbackService, 'queueAndPlay')
await playbackService.playAllByArtist(artist)
expect(queueAndPlayMock).toHaveBeenCalledWith(artist.songs, true)
})
it('plays all songs by an artist in proper order', async () => {
const artist = factory<Artist>('artist', {
songs: factory<Song>('song', 5)
})
const orderedSongs = factory('song', 5)
;(orderBy as jest.Mock).mockReturnValue(orderedSongs)
const queueAndPlayMock = mock(playbackService, 'queueAndPlay')
await playbackService.playAllByArtist(artist, false)
expect(orderBy).toHaveBeenCalledWith(artist.songs, ['album_id', 'disc', 'track'])
expect(queueAndPlayMock).toHaveBeenCalledWith(orderedSongs)
})
it('plays all songs in an album, shuffled', async () => {
const album = factory<Album>('album', {
songs: factory<Song>('song', 5)
})
const queueAndPlayMock = mock(playbackService, 'queueAndPlay')
await playbackService.playAllInAlbum(album)
expect(queueAndPlayMock).toHaveBeenCalledWith(album.songs, true)
})
it('plays all songs in an album in proper order', async () => {
const album = factory<Album>('album', {
songs: factory<Song>('song', 5)
})
const orderedSongs = factory('song', 5)
;(orderBy as jest.Mock).mockReturnValue(orderedSongs)
const queueAndPlayMock = mock(playbackService, 'queueAndPlay')
await playbackService.playAllInAlbum(album, false)
expect(orderBy).toHaveBeenCalledWith(album.songs, ['disc', 'track'])
expect(queueAndPlayMock).toHaveBeenCalledWith(orderedSongs)
})
})

View file

@ -0,0 +1,475 @@
import plyr from 'plyr'
import lodash from 'lodash'
import { expect, it, vi } from 'vitest'
import { eventBus, noop } from '@/utils'
import router from '@/router'
import factory from '@/__tests__/factory'
import UnitTestCase from '@/__tests__/UnitTestCase'
import { nextTick } from 'vue'
import { socketService } from '@/services'
import { playbackService } from './playbackService'
import {
commonStore,
preferenceStore as preferences,
queueStore,
recentlyPlayedStore,
songStore,
userStore
} from '@/stores'
new class extends UnitTestCase {
private setupEnvironment () {
document.body.innerHTML = `
<div class="plyr">
<audio crossorigin="anonymous" controls/>
</div>
<input
class="plyr__volume"
id="volumeRange"
max="10"
step="0.1"
title="Volume"
type="range"
>
`
window.AudioContext = vi.fn().mockImplementation(() => ({
createMediaElementSource: vi.fn(noop)
}))
}
protected beforeEach () {
super.beforeEach(() => this.setupEnvironment())
}
protected test () {
it('only initializes once', () => {
const spy = vi.spyOn(plyr, 'setup')
playbackService.init()
expect(spy).toHaveBeenCalled()
playbackService.init()
expect(spy).toHaveBeenCalledTimes(1)
})
it.each<[boolean, boolean, number, number, number]>([
[false, false, 100, 400, 1],
[true, false, 100, 400, 0],
[false, true, 100, 400, 0],
[false, false, 100, 500, 0]
])(
'when playCountRegistered is %s, isTranscoding is %s, current media time is %d, media duration is %d, then registerPlay() should be call %d times',
(playCountRegistered, isTranscoding, currentTime, duration, numberOfCalls) => {
queueStore.current = factory<Song>('song', { playCountRegistered })
this.setReadOnlyProperty(playbackService, 'isTranscoding', isTranscoding)
playbackService.init()
const mediaElement = playbackService.player!.media
// we can't set mediaElement.currentTime|duration directly because they're read-only
Object.defineProperties(mediaElement, {
currentTime: {
value: currentTime,
configurable: true
},
duration: {
value: duration,
configurable: true
}
})
const registerPlayMock = this.mock(playbackService, 'registerPlay')
mediaElement.dispatchEvent(new Event('timeupdate'))
expect(registerPlayMock).toHaveBeenCalledTimes(numberOfCalls)
})
it('plays next song if current song is errored', () => {
playbackService.init()
const playNextMock = this.mock(playbackService, 'playNext')
playbackService.player!.media.dispatchEvent(new Event('error'))
expect(playNextMock).toHaveBeenCalled()
})
it('scrobbles if current song ends', () => {
queueStore.current = factory<Song>('song')
commonStore.state.useLastfm = true
userStore.current = factory<User>('user', {
preferences: {
lastfm_session_key: 'foo'
}
})
playbackService.init()
const scrobbleMock = this.mock(songStore, 'scrobble')
playbackService.player!.media.dispatchEvent(new Event('ended'))
expect(scrobbleMock).toHaveBeenCalled()
})
it.each<[RepeatMode, number, number]>([['REPEAT_ONE', 1, 0], ['NO_REPEAT', 0, 1], ['REPEAT_ALL', 0, 1]])(
'when song ends, if repeat mode is %s then restart() is called %d times and playNext() is called %d times',
(repeatMode, restartCalls, playNextCalls) => {
commonStore.state.useLastfm = false // so that no scrobbling is made unnecessarily
preferences.repeatMode = repeatMode
playbackService.init()
const restartMock = this.mock(playbackService, 'restart')
const playNextMock = this.mock(playbackService, 'playNext')
playbackService.player!.media.dispatchEvent(new Event('ended'))
expect(restartMock).toHaveBeenCalledTimes(restartCalls)
expect(playNextMock).toHaveBeenCalledTimes(playNextCalls)
})
it.each([
[false, true, 300, 310, 0],
[true, false, 300, 310, 0],
[false, false, 300, 400, 0],
[false, false, 300, 310, 1]
])(
'when next song preloaded is %s, isTranscoding is %s, current media time is %d, media duration is %d, then preload() should be called %d times',
(preloaded, isTranscoding, currentTime, duration, numberOfCalls) => {
queueStore.current = factory<Song>('song', { playCountRegistered: true }) // avoid triggering play count logic
this.setReadOnlyProperty(queueStore, 'next', factory<Song>('song', { preloaded }))
this.setReadOnlyProperty(playbackService, 'isTranscoding', isTranscoding)
playbackService.init()
const mediaElement = playbackService.player!.media
Object.defineProperties(mediaElement, {
currentTime: {
value: currentTime,
configurable: true
},
duration: {
value: duration,
configurable: true
}
})
const preloadMock = this.mock(playbackService, 'preload')
mediaElement.dispatchEvent(new Event('timeupdate'))
expect(preloadMock).toHaveBeenCalledTimes(numberOfCalls)
}
)
it('registers play', () => {
const recentlyPlayedStoreAddMock = this.mock(recentlyPlayedStore, 'add')
const recentlyPlayedStoreFetchAllMock = this.mock(recentlyPlayedStore, 'fetchAll')
const registerPlayMock = this.mock(songStore, 'registerPlay')
const song = factory<Song>('song')
playbackService.registerPlay(song)
expect(recentlyPlayedStoreAddMock).toHaveBeenCalledWith(song)
expect(recentlyPlayedStoreFetchAllMock).toHaveBeenCalled()
expect(registerPlayMock).toHaveBeenCalledWith(song)
expect(song.playCountRegistered).toBe(true)
})
it('preloads a song', () => {
const audioElement = {
setAttribute: vi.fn(),
load: vi.fn()
}
const createElementMock = this.mock(document, 'createElement', audioElement)
this.mock(songStore, 'getSourceUrl').mockReturnValue('/foo?token=o5afd')
const song = factory<Song>('song')
playbackService.preload(song)
expect(createElementMock).toHaveBeenCalledWith('audio')
expect(audioElement.setAttribute).toHaveBeenNthCalledWith(1, 'src', '/foo?token=o5afd')
expect(audioElement.setAttribute).toHaveBeenNthCalledWith(2, 'preload', 'auto')
expect(audioElement.load).toHaveBeenCalled()
expect(song.preloaded).toBe(true)
})
it('restarts a song', async () => {
const song = factory<Song>('song')
queueStore.current = song
this.mock(Math, 'floor', 1000)
const emitMock = this.mock(eventBus, 'emit')
const broadcastMock = this.mock(socketService, 'broadcast')
const showNotificationMock = this.mock(playbackService, 'showNotification')
const dataToBroadcast = {}
this.mock(songStore, 'generateDataToBroadcast', dataToBroadcast)
const restartMock = this.mock(playbackService.player!, 'restart')
const playMock = this.mock(window.HTMLMediaElement.prototype, 'play')
await playbackService.restart()
expect(song.playStartTime).toEqual(1000)
expect(song.playCountRegistered).toBe(false)
expect(emitMock).toHaveBeenCalledWith('SONG_STARTED', song)
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_SONG', dataToBroadcast)
expect(showNotificationMock).toHaveBeenCalled()
expect(restartMock).toHaveBeenCalled()
expect(playMock).toHaveBeenCalled()
})
it.each<[RepeatMode, RepeatMode]>([
['NO_REPEAT', 'REPEAT_ALL'],
['REPEAT_ALL', 'REPEAT_ONE'],
['REPEAT_ONE', 'NO_REPEAT']
])('it switches from repeat mode %s to repeat mode %s', (fromMode, toMode) => {
preferences.repeatMode = fromMode
playbackService.changeRepeatMode()
expect(preferences.repeatMode).toEqual(toMode)
})
it('restarts song if playPrev is triggered after 5 seconds', async () => {
const mock = this.mock(playbackService.player!, 'restart')
this.setReadOnlyProperty(playbackService.player!.media, 'currentTime', 6)
queueStore.current = factory<Song>('song', { length: 120 })
await playbackService.playPrev()
expect(mock).toHaveBeenCalled()
})
it('stops if playPrev is triggered when there is no prev song and repeat mode is NO_REPEAT', async () => {
const stopMock = this.mock(playbackService, 'stop')
this.setReadOnlyProperty(playbackService.player!.media, 'currentTime', 4)
this.setReadOnlyProperty(playbackService, 'previous', undefined)
queueStore.current = factory<Song>('song')
preferences.repeatMode = 'NO_REPEAT'
await playbackService.playPrev()
expect(stopMock).toHaveBeenCalled()
})
it('plays the previous song', async () => {
const previousSong = factory('song')
this.setReadOnlyProperty(playbackService.player!.media, 'currentTime', 4)
this.setReadOnlyProperty(playbackService, 'previous', previousSong)
queueStore.current = factory<Song>('song')
const playMock = this.mock(playbackService, 'play')
await playbackService.playPrev()
expect(playMock).toHaveBeenCalledWith(previousSong)
})
it('stops if playNext is triggered when there is no next song and repeat mode is NO_REPEAT', async () => {
this.setReadOnlyProperty(playbackService, 'next', undefined)
preferences.repeatMode = 'NO_REPEAT'
const stopMock = this.mock(playbackService, 'stop')
await playbackService.playNext()
expect(stopMock).toHaveBeenCalled()
})
it('plays the next song', async () => {
const nextSong = factory('song')
this.setReadOnlyProperty(playbackService, 'next', nextSong)
const playMock = this.mock(playbackService, 'play')
await playbackService.playNext()
expect(playMock).toHaveBeenCalledWith(nextSong)
})
it('stops playback', () => {
const currentSong = factory<Song>('song')
queueStore.current = currentSong
const pauseMock = this.mock(playbackService.player!, 'pause')
const seekMock = this.mock(playbackService.player!, 'seek')
const broadcastMock = this.mock(socketService, 'broadcast')
playbackService.stop()
expect(currentSong.playbackState).toEqual('Stopped')
expect(pauseMock).toHaveBeenCalled()
expect(seekMock).toHaveBeenCalledWith(0)
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_PLAYBACK_STOPPED')
expect(document.title).toEqual('Koel')
})
it('pauses playback', () => {
const currentSong = factory<Song>('song')
queueStore.current = currentSong
const dataToBroadcast = {}
this.mock(songStore, 'generateDataToBroadcast', dataToBroadcast)
const pauseMock = this.mock(playbackService.player!, 'pause')
const broadcastMock = this.mock(socketService, 'broadcast')
playbackService.pause()
expect(currentSong.playbackState).toEqual('Paused')
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_SONG', dataToBroadcast)
expect(pauseMock).toHaveBeenCalled()
})
it('resumes playback', async () => {
const currentSong = factory<Song>('song')
queueStore.current = currentSong
const dataToBroadcast = {}
this.mock(songStore, 'generateDataToBroadcast', dataToBroadcast)
const playMock = this.mock(window.HTMLMediaElement.prototype, 'play')
const broadcastMock = this.mock(socketService, 'broadcast')
const emitMock = this.mock(eventBus, 'emit')
playbackService.init()
await playbackService.resume()
expect(queueStore.current?.playbackState).toEqual('Playing')
expect(broadcastMock).toHaveBeenCalledWith('SOCKET_SONG', dataToBroadcast)
expect(playMock).toHaveBeenCalled()
expect(emitMock).toHaveBeenCalledWith('SONG_STARTED', currentSong)
})
it('plays first in queue if toggled when there is no current song', async () => {
const playFirstInQueueMock = this.mock(playbackService, 'playFirstInQueue')
queueStore.current = undefined
await playbackService.toggle()
expect(playFirstInQueueMock).toHaveBeenCalled()
})
it.each<[MethodOf<typeof playbackService>, PlaybackState]>([
['resume', 'Stopped'],
['resume', 'Paused'],
['pause', 'Playing']
])('%ss playback if toggled when current song playback state is %s', async (action, playbackState) => {
const actionMock = this.mock(playbackService, action)
queueStore.current = factory<Song>('song', { playbackState })
await playbackService.toggle()
expect(actionMock).toHaveBeenCalled()
})
it('queues and plays all songs shuffled by default', async () => {
const allSongs = factory<Song>('song', 5)
const shuffledSongs = factory<Song>('song', 5)
songStore.all = allSongs
const firstSongInQueue = factory('song')
this.setReadOnlyProperty(queueStore, 'first', firstSongInQueue)
const replaceQueueMock = this.mock(queueStore, 'replaceQueueWith')
const goMock = this.mock(router, 'go')
const playMock = this.mock(playbackService, 'play')
const shuffleMock = this.mock(lodash, 'shuffle', shuffledSongs)
await playbackService.queueAndPlay()
await nextTick()
expect(shuffleMock).toHaveBeenCalledWith(allSongs)
expect(replaceQueueMock).toHaveBeenCalledWith(shuffledSongs)
expect(goMock).toHaveBeenCalledWith('queue')
expect(playMock).toHaveBeenCalledWith(firstSongInQueue)
})
it('queues and plays songs without shuffling', async () => {
const songs = factory<Song>('song', 5)
const replaceQueueMock = this.mock(queueStore, 'replaceQueueWith')
const goMock = this.mock(router, 'go')
const playMock = this.mock(playbackService, 'play')
const firstSongInQueue = songs[0]
const shuffleMock = this.mock(lodash, 'shuffle')
this.setReadOnlyProperty(queueStore, 'first', firstSongInQueue)
await playbackService.queueAndPlay(songs)
await nextTick()
expect(shuffleMock).not.toHaveBeenCalled()
expect(replaceQueueMock).toHaveBeenCalledWith(songs)
expect(goMock).toHaveBeenCalledWith('queue')
expect(playMock).toHaveBeenCalledWith(firstSongInQueue)
})
it('queues and plays songs with shuffling', async () => {
const songs = factory<Song>('song', 5)
const shuffledSongs = factory<Song>('song', 5)
const replaceQueueMock = this.mock(queueStore, 'replaceQueueWith')
const goMock = this.mock(router, 'go')
const playMock = this.mock(playbackService, 'play')
const firstSongInQueue = songs[0]
this.setReadOnlyProperty(queueStore, 'first', firstSongInQueue)
const shuffleMock = this.mock(lodash, 'shuffle', shuffledSongs)
await playbackService.queueAndPlay(songs, true)
await nextTick()
expect(shuffleMock).toHaveBeenCalledWith(songs)
expect(replaceQueueMock).toHaveBeenCalledWith(shuffledSongs)
expect(goMock).toHaveBeenCalledWith('queue')
expect(playMock).toHaveBeenCalledWith(firstSongInQueue)
})
it('plays first song in queue', async () => {
const songs = factory<Song>('song', 5)
queueStore.all = songs
this.setReadOnlyProperty(queueStore, 'first', songs[0])
const playMock = this.mock(playbackService, 'play')
await playbackService.playFirstInQueue()
expect(playMock).toHaveBeenCalledWith(songs[0])
})
it('playFirstInQueue triggers queueAndPlay if queue is empty', async () => {
queueStore.all = []
const queueAndPlayMock = this.mock(playbackService, 'queueAndPlay')
await playbackService.playFirstInQueue()
expect(queueAndPlayMock).toHaveBeenCalled()
})
it('plays all songs by an artist, shuffled', async () => {
const artist = factory<Artist>('artist', {
songs: factory<Song>('song', 5)
})
const queueAndPlayMock = this.mock(playbackService, 'queueAndPlay')
await playbackService.playAllByArtist(artist)
expect(queueAndPlayMock).toHaveBeenCalledWith(artist.songs, true)
})
it('plays all songs by an artist in proper order', async () => {
const artist = factory<Artist>('artist', {
songs: factory<Song>('song', 5)
})
const orderedSongs = factory('song', 5)
const orderByMock = this.mock(lodash, 'orderBy', orderedSongs)
const queueAndPlayMock = this.mock(playbackService, 'queueAndPlay')
await playbackService.playAllByArtist(artist, false)
expect(orderByMock).toHaveBeenCalledWith(artist.songs, ['album_id', 'disc', 'track'])
expect(queueAndPlayMock).toHaveBeenCalledWith(orderedSongs)
})
it('plays all songs in an album, shuffled', async () => {
const album = factory<Album>('album', {
songs: factory<Song>('song', 5)
})
const queueAndPlayMock = this.mock(playbackService, 'queueAndPlay')
await playbackService.playAllInAlbum(album)
expect(queueAndPlayMock).toHaveBeenCalledWith(album.songs, true)
})
it('plays all songs in an album in proper order', async () => {
const album = factory<Album>('album', {
songs: factory<Song>('song', 5)
})
const orderedSongs = factory('song', 5)
const orderByMock = this.mock(lodash, 'orderBy', orderedSongs)
const queueAndPlayMock = this.mock(playbackService, 'queueAndPlay')
await playbackService.playAllInAlbum(album, false)
expect(orderByMock).toHaveBeenCalledWith(album.songs, ['disc', 'track'])
expect(queueAndPlayMock).toHaveBeenCalledWith(orderedSongs)
})
}
}

View file

@ -30,7 +30,6 @@ export const playbackService = {
volumeInput: null as unknown as HTMLInputElement,
repeatModes: REPEAT_MODES,
initialized: false,
mainWin: null as any,
init () {
// We don't need to init this service twice, or the media events will be duplicated.
@ -38,7 +37,7 @@ export const playbackService = {
return
}
this.player = plyr.setup(document.querySelector<HTMLMediaElement>('.plyr')!, {
this.player = plyr.setup('.plyr', {
controls: []
})[0]
@ -83,6 +82,10 @@ export const playbackService = {
},
setMediaSessionActionHandlers () {
if (!navigator.mediaSession) {
return
}
navigator.mediaSession.setActionHandler('play', () => this.resume())
navigator.mediaSession.setActionHandler('pause', () => this.pause())
navigator.mediaSession.setActionHandler('previoustrack', () => this.playPrev())
@ -100,7 +103,7 @@ export const playbackService = {
preferences.repeatMode === 'REPEAT_ONE' ? this.restart() : this.playNext()
})
mediaElement.addEventListener('timeupdate', throttle(() => {
let timeUpdateHandler = () => {
const currentSong = queueStore.current!
if (!currentSong.playCountRegistered && !this.isTranscoding) {
@ -120,7 +123,13 @@ export const playbackService = {
if (mediaElement.duration && mediaElement.currentTime + PRELOAD_BUFFER > mediaElement.duration) {
this.preload(nextSong)
}
}, 3000))
}
if (process.env.NODE_ENV !== 'test') {
timeUpdateHandler = throttle(timeUpdateHandler, 3000)
}
mediaElement.addEventListener('timeupdate', timeUpdateHandler)
},
get isTranscoding () {
@ -150,7 +159,7 @@ export const playbackService = {
* So many dreams swinging out of the blue
* We'll let them come true
*/
async play (song: Song | undefined) {
async play (song?: Song) {
if (!song) {
return
}
@ -184,14 +193,14 @@ export const playbackService = {
}
try {
const notif = new window.Notification(`${song.title}`, {
const notification = new window.Notification(`${song.title}`, {
icon: song.album.cover,
body: `${song.album.name} ${song.artist.name}`
})
notif.onclick = () => window.focus()
notification.onclick = () => window.focus()
window.setTimeout(() => notif.close(), 5000)
window.setTimeout(() => notification.close(), 5000)
} catch (e) {
// Notification fails.
// @link https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration/showNotification
@ -203,7 +212,11 @@ export const playbackService = {
artist: song.artist.name,
album: song.album.name,
artwork: [
{ src: song.album.cover, sizes: '256x256', type: 'image/png' }
{
src: song.album.cover,
sizes: '256x256',
type: 'image/png'
}
]
})
},
@ -250,7 +263,7 @@ export const playbackService = {
* The previous song in the queue.
* If we're in REPEAT_ALL mode and there's no prev song, get the last song.
*/
get previous (): Song | undefined {
get previous () {
if (queueStore.previous) {
return queueStore.previous
}
@ -279,7 +292,6 @@ export const playbackService = {
* If the prev song is not found and the current mode is NO_REPEAT, we stop completely.
*/
async playPrev () {
console.log('called')
// If the song's duration is greater than 5 seconds and we've passed 5 seconds into it,
// restart playing instead.
if (this.getPlayer().media.currentTime > 5 && queueStore.current!.length > 5) {

View file

@ -62,7 +62,7 @@ interface Plyr {
}
declare module 'plyr' {
function setup (el: HTMLMediaElement | HTMLMediaElement[], options: Record<string, any>): Plyr[]
function setup (el: string | HTMLMediaElement | HTMLMediaElement[], options: Record<string, any>): Plyr[]
}
declare module 'ismobilejs' {

View file

@ -25,7 +25,7 @@ export default defineConfig({
KOEL_ENV: '""'
},
test: {
environment: 'happy-dom',
environment: 'jsdom',
setupFiles: path.resolve(__dirname, './resources/assets/js/__tests__/setup.ts')
},
})

473
yarn.lock
View file

@ -1108,6 +1108,11 @@
"@testing-library/dom" "^8.5.0"
"@vue/test-utils" "^2.0.0-rc.18"
"@tootallnate/once@2":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf"
integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==
"@trysound/sax@0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
@ -1203,13 +1208,6 @@
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
"@types/concat-stream@^1.6.0":
version "1.6.1"
resolved "https://registry.yarnpkg.com/@types/concat-stream/-/concat-stream-1.6.1.tgz#24bcfc101ecf68e886aaedce60dfd74b632a1b74"
integrity sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==
dependencies:
"@types/node" "*"
"@types/connect-history-api-fallback@^1.3.5":
version "1.3.5"
resolved "https://registry.yarnpkg.com/@types/connect-history-api-fallback/-/connect-history-api-fallback-1.3.5.tgz#d1f7a8a09d0ed5a57aee5ae9c18ab9b803205dae"
@ -1265,13 +1263,6 @@
"@types/qs" "*"
"@types/serve-static" "*"
"@types/form-data@0.0.33":
version "0.0.33"
resolved "https://registry.yarnpkg.com/@types/form-data/-/form-data-0.0.33.tgz#c9ac85b2a5fd18435b8c85d9ecb50e6d6c893ff8"
integrity sha1-yayFsqX9GENbjIXZ7LUObWyJP/g=
dependencies:
"@types/node" "*"
"@types/glob@^7.1.1":
version "7.2.0"
resolved "https://registry.yarnpkg.com/@types/glob/-/glob-7.2.0.tgz#bc1b5bf3aa92f25bd5dd39f35c57361bdce5b2eb"
@ -1358,21 +1349,11 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.13.4.tgz#1581d6c16e3d4803eb079c87d4ac893ee7501c2c"
integrity sha512-x26ur3dSXgv5AwKS0lNfbjpCakGIduWU1DU91Zz58ONRWrIKGunmZBNv4P7N+e27sJkiGDsw/3fT4AtsqQBrBA==
"@types/node@^10.0.3":
version "10.17.60"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.17.60.tgz#35f3d6213daed95da7f0f73e75bcc6980e90597b"
integrity sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==
"@types/node@^14.14.31":
version "14.18.13"
resolved "https://registry.yarnpkg.com/@types/node/-/node-14.18.13.tgz#6ad4d9db59e6b3faf98dcfe4ca9d2aec84443277"
integrity sha512-Z6/KzgyWOga3pJNS42A+zayjhPbf2zM3hegRQaOPnLOzEi86VV++6FLDWgR1LGrVCRufP/ph2daa3tEa5br1zA==
"@types/node@^8.0.0":
version "8.10.66"
resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.66.tgz#dd035d409df322acc83dff62a602f12a5783bbb3"
integrity sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==
"@types/nprogress@^0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@types/nprogress/-/nprogress-0.2.0.tgz#86c593682d4199212a0509cc3c4d562bbbd6e45f"
@ -1388,7 +1369,7 @@
resolved "https://registry.yarnpkg.com/@types/pusher-js/-/pusher-js-4.2.2.tgz#129fae1854255c5883e874137cd045c48d0a422a"
integrity sha512-LP9isBRAFlNzQohQtySJxJjzmy4zQCcv5xGZD2G3rsDnTWfpEkFKyLw3x9711pFAXwwUl9ZivxKkcnFr8umSAQ==
"@types/qs@*", "@types/qs@^6.2.31":
"@types/qs@*":
version "6.9.7"
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
@ -1822,6 +1803,11 @@
resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d"
integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
abab@^2.0.5, abab@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291"
integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@ -1835,6 +1821,14 @@ accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8:
mime-types "~2.1.34"
negotiator "0.6.3"
acorn-globals@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45"
integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==
dependencies:
acorn "^7.1.1"
acorn-walk "^7.1.1"
acorn-import-assertions@^1.7.6:
version "1.8.0"
resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz#ba2b5939ce62c238db6d93d81c9b111b29b855e9"
@ -1845,6 +1839,16 @@ acorn-jsx@^5.3.1:
resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.1.tgz#fc8661e11b7ac1539c47dbfea2e72b3af34d267b"
integrity sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==
acorn-walk@^7.1.1:
version "7.2.0"
resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc"
integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==
acorn@^7.1.1:
version "7.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
acorn@^8.4.1, acorn@^8.5.0:
version "8.7.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
@ -1866,6 +1870,13 @@ adjust-sourcemap-loader@2.0.0:
object-path "0.11.4"
regex-parser "2.2.10"
agent-base@6:
version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
dependencies:
debug "4"
aggregate-error@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0"
@ -2053,11 +2064,6 @@ array.prototype.flat@^1.2.5:
es-abstract "^1.19.2"
es-shim-unscopables "^1.0.0"
asap@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
asn1.js@^5.2.0:
version "5.4.1"
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
@ -2343,6 +2349,11 @@ brorand@^1.0.1, brorand@^1.1.0:
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
browser-process-hrtime@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626"
integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
@ -2536,7 +2547,7 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001317:
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001332.tgz#39476d3aa8d83ea76359c70302eafdd4a1d727dd"
integrity sha512-10T30NYOEQtN6C11YGg411yebhvpnC6Z102+B95eAsN0oB6KUs01ivE8u+G6FMIRtIrVlYXhL+LUwQ3/hXwDWw==
caseless@^0.12.0, caseless@~0.12.0:
caseless@~0.12.0:
version "0.12.0"
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
@ -2792,7 +2803,7 @@ colors@~1.1.2:
resolved "https://registry.yarnpkg.com/colors/-/colors-1.1.2.tgz#168a4701756b6a7f51a12ce0c97bfa28c084ed63"
integrity sha1-FopHAXVran9RoSzgyXv6KMCE7WM=
combined-stream@^1.0.6, combined-stream@~1.0.6:
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
@ -2871,16 +2882,6 @@ concat-map@0.0.1:
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
concat-stream@^1.6.0, concat-stream@^1.6.2:
version "1.6.2"
resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34"
integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==
dependencies:
buffer-from "^1.0.0"
inherits "^2.0.3"
readable-stream "^2.2.2"
typedarray "^0.0.6"
concat@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/concat/-/concat-1.0.3.tgz#40f3353089d65467695cb1886b45edd637d8cca8"
@ -3163,11 +3164,6 @@ css-what@^6.0.1:
resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4"
integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==
css.escape@^1.5.1:
version "1.5.1"
resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb"
integrity sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=
css@^2.0.0:
version "2.2.4"
resolved "https://registry.yarnpkg.com/css/-/css-2.2.4.tgz#c646755c73971f2bba6a601e2cf2fd71b1298929"
@ -3285,6 +3281,23 @@ csso@~2.3.1:
clap "^1.0.9"
source-map "^0.5.3"
cssom@^0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36"
integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==
cssom@~0.3.6:
version "0.3.8"
resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a"
integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==
cssstyle@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852"
integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==
dependencies:
cssom "~0.3.6"
csstype@^2.6.8:
version "2.6.20"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda"
@ -3353,6 +3366,15 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"
data-urls@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143"
integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==
dependencies:
abab "^2.0.6"
whatwg-mimetype "^3.0.0"
whatwg-url "^11.0.0"
dayjs@^1.10.4:
version "1.11.0"
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.0.tgz#009bf7ef2e2ea2d5db2e6583d2d39a4b5061e805"
@ -3365,6 +3387,13 @@ debug@2.6.9, debug@^2.6.9:
dependencies:
ms "2.0.0"
debug@4, debug@^4.1.0, debug@^4.3.2:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
debug@4.3.2:
version "4.3.2"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.2.tgz#f0a49c18ac8779e31d4a0c6029dfb76873c7428b"
@ -3379,13 +3408,6 @@ debug@^3.1.0, debug@^3.1.1, debug@^3.2.7:
dependencies:
ms "^2.1.1"
debug@^4.1.0, debug@^4.3.2:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
debug@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791"
@ -3398,6 +3420,11 @@ decamelize@^1.1.2:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
decimal.js@^10.3.1:
version "10.3.1"
resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783"
integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ==
decode-uri-component@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
@ -3420,6 +3447,11 @@ deep-is@^0.1.3:
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34"
integrity sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=
deep-is@~0.1.3:
version "0.1.4"
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
deepmerge@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955"
@ -3543,6 +3575,13 @@ domelementtype@^2.0.1, domelementtype@^2.2.0:
resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d"
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
domexception@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673"
integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==
dependencies:
webidl-conversions "^7.0.0"
domhandler@^3.0.0:
version "3.3.0"
resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a"
@ -3908,6 +3947,18 @@ escape-string-regexp@^4.0.0:
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
escodegen@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd"
integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==
dependencies:
esprima "^4.0.1"
estraverse "^5.2.0"
esutils "^2.0.2"
optionator "^0.8.1"
optionalDependencies:
source-map "~0.6.1"
eslint-import-resolver-node@^0.3.6:
version "0.3.6"
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd"
@ -4085,6 +4136,11 @@ esprima@^2.6.0:
resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581"
integrity sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=
esprima@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71"
integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==
esquery@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
@ -4324,7 +4380,7 @@ fast-json-stable-stringify@^2.0.0:
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2"
integrity sha1-1RQsDK7msRifh9OnYREGT4bIu/I=
fast-levenshtein@^2.0.6:
fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
@ -4491,13 +4547,13 @@ forever-agent@~0.6.1:
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=
form-data@^2.2.0:
version "2.5.1"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.5.1.tgz#f2cbec57b5e59e23716e128fe44d4e5dd23895f4"
integrity sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.6"
combined-stream "^1.0.8"
mime-types "^2.1.12"
form-data@~2.3.2:
@ -4602,11 +4658,6 @@ get-own-enumerable-property-symbols@^3.0.0:
resolved "https://registry.yarnpkg.com/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz#b5fde77f22cbe35f390b4e089922c50bce6ef664"
integrity sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==
get-port@^3.1.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/get-port/-/get-port-3.2.0.tgz#dd7ce7de187c06c8bf353796ac71e099f0980ebc"
integrity sha1-3Xzn3hh8Bsi/NTeWrHHgmfCYDrw=
get-stream@^5.0.0, get-stream@^5.1.0:
version "5.2.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-5.2.0.tgz#4966a1795ee5ace65e706c4b7beb71257d6e22d3"
@ -4768,19 +4819,6 @@ handle-thing@^2.0.0:
resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-2.0.1.tgz#857f79ce359580c340d43081cc648970d0bb234e"
integrity sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==
happy-dom@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/happy-dom/-/happy-dom-3.1.0.tgz#dad86056ac2ee3d5b4172fba41fe3b4a4e1cea4b"
integrity sha512-BewZQwLdu6JS9HYT7enB2toju80OjSjl44+3HXMB3hT+2skC9Mja+/N/b+SbtnwJCMbQqiZVzy/RXevPPuBIXQ==
dependencies:
css.escape "^1.5.1"
he "^1.2.0"
node-fetch "^2.x.x"
sync-request "^6.1.0"
webidl-conversions "^7.0.0"
whatwg-encoding "^2.0.0"
whatwg-mimetype "^3.0.0"
has-ansi@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
@ -4883,6 +4921,13 @@ html-comment-regex@^1.1.0:
resolved "https://registry.yarnpkg.com/html-comment-regex/-/html-comment-regex-1.1.2.tgz#97d4688aeb5c81886a364faa0cad1dda14d433a7"
integrity sha512-P+M65QY2JQ5Y0G9KKdlDpo0zK+/OHptU5AaBwUfAIDJZk1MYf32Frm84EcOytfJE0t5JvkAnKlmjsXDnWzCJmQ==
html-encoding-sniffer@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==
dependencies:
whatwg-encoding "^2.0.0"
html-entities@^2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/html-entities/-/html-entities-2.3.3.tgz#117d7626bece327fc8baace8868fa6f5ef856e46"
@ -4921,16 +4966,6 @@ htmlparser2@^4.1.0:
domutils "^2.0.0"
entities "^2.0.0"
http-basic@^8.1.1:
version "8.1.3"
resolved "https://registry.yarnpkg.com/http-basic/-/http-basic-8.1.3.tgz#a7cabee7526869b9b710136970805b1004261bbf"
integrity sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==
dependencies:
caseless "^0.12.0"
concat-stream "^1.6.2"
http-response-object "^3.0.1"
parse-cache-control "^1.0.1"
http-deceiver@^1.2.7:
version "1.2.7"
resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87"
@ -4962,6 +4997,15 @@ http-parser-js@>=0.5.1:
resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.5.6.tgz#2e02406ab2df8af8a7abfba62e0da01c62b95afd"
integrity sha512-vDlkRPDJn93swjcjqMSaGSPABbIarsr1TLAui/gLDXzV5VsJNdXNzMYDyNBLQkjWQCJ1uizu8T2oDMhmGt0PRA==
http-proxy-agent@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43"
integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==
dependencies:
"@tootallnate/once" "2"
agent-base "6"
debug "4"
http-proxy-middleware@^2.0.3:
version "2.0.4"
resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-2.0.4.tgz#03af0f4676d172ae775cb5c33f592f40e1a4e07a"
@ -4982,13 +5026,6 @@ http-proxy@^1.18.1:
follow-redirects "^1.0.0"
requires-port "^1.0.0"
http-response-object@^3.0.1:
version "3.0.2"
resolved "https://registry.yarnpkg.com/http-response-object/-/http-response-object-3.0.2.tgz#7f435bb210454e4360d074ef1f989d5ea8aa9810"
integrity sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==
dependencies:
"@types/node" "^10.0.3"
http-signature@~1.3.6:
version "1.3.6"
resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.3.6.tgz#cb6fbfdf86d1c974f343be94e87f7fc128662cf9"
@ -5003,6 +5040,14 @@ https-browserify@^1.0.0:
resolved "https://registry.yarnpkg.com/https-browserify/-/https-browserify-1.0.0.tgz#ec06c10e0a34c0f2faf199f7fd7fc78fffd03c73"
integrity sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=
https-proxy-agent@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
dependencies:
agent-base "6"
debug "4"
human-signals@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3"
@ -5345,6 +5390,11 @@ is-plain-object@^2.0.4:
dependencies:
isobject "^3.0.1"
is-potential-custom-element-name@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5"
integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==
is-regex@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
@ -5512,6 +5562,39 @@ jsbn@~0.1.0:
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513"
integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM=
jsdom@^19.0.0:
version "19.0.0"
resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-19.0.0.tgz#93e67c149fe26816d38a849ea30ac93677e16b6a"
integrity sha512-RYAyjCbxy/vri/CfnjUWJQQtZ3LKlLnDqj+9XLNnJPgEGeirZs3hllKR20re8LUZ6o1b1X4Jat+Qd26zmP41+A==
dependencies:
abab "^2.0.5"
acorn "^8.5.0"
acorn-globals "^6.0.0"
cssom "^0.5.0"
cssstyle "^2.3.0"
data-urls "^3.0.1"
decimal.js "^10.3.1"
domexception "^4.0.0"
escodegen "^2.0.0"
form-data "^4.0.0"
html-encoding-sniffer "^3.0.0"
http-proxy-agent "^5.0.0"
https-proxy-agent "^5.0.0"
is-potential-custom-element-name "^1.0.1"
nwsapi "^2.2.0"
parse5 "6.0.1"
saxes "^5.0.1"
symbol-tree "^3.2.4"
tough-cookie "^4.0.0"
w3c-hr-time "^1.0.2"
w3c-xmlserializer "^3.0.0"
webidl-conversions "^7.0.0"
whatwg-encoding "^2.0.0"
whatwg-mimetype "^3.0.0"
whatwg-url "^10.0.0"
ws "^8.2.3"
xml-name-validator "^4.0.0"
jsesc@^2.5.1:
version "2.5.2"
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4"
@ -5687,6 +5770,14 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"
levn@~0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee"
integrity sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=
dependencies:
prelude-ls "~1.1.2"
type-check "~0.3.2"
lilconfig@^2.0.3, lilconfig@^2.0.5:
version "2.0.5"
resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.5.tgz#19e57fd06ccc3848fd1891655b5a447092225b25"
@ -6135,13 +6226,6 @@ no-case@^3.0.4:
lower-case "^2.0.2"
tslib "^2.0.3"
node-fetch@^2.x.x:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
node-forge@^1:
version "1.3.1"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3"
@ -6254,6 +6338,11 @@ num2fraction@^1.2.2:
resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede"
integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
nwsapi@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
integrity sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==
object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
@ -6338,6 +6427,18 @@ opencollective-postinstall@^2.0.2:
resolved "https://registry.yarnpkg.com/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz#7a0fff978f6dbfa4d006238fbac98ed4198c3259"
integrity sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==
optionator@^0.8.1:
version "0.8.3"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495"
integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==
dependencies:
deep-is "~0.1.3"
fast-levenshtein "~2.0.6"
levn "~0.3.0"
prelude-ls "~1.1.2"
type-check "~0.3.2"
word-wrap "~1.2.3"
optionator@^0.9.1:
version "0.9.1"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
@ -6449,11 +6550,6 @@ parse-asn1@^5.0.0, parse-asn1@^5.1.5:
pbkdf2 "^3.0.3"
safe-buffer "^5.1.1"
parse-cache-control@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/parse-cache-control/-/parse-cache-control-1.0.1.tgz#8eeab3e54fa56920fe16ba38f77fa21aacc2d74e"
integrity sha1-juqz5U+laSD+Fro493+iGqzC104=
parse-json@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646"
@ -6464,6 +6560,11 @@ parse-json@^5.0.0:
json-parse-even-better-errors "^2.3.0"
lines-and-columns "^1.1.6"
parse5@6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b"
integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==
parseurl@~1.3.2, parseurl@~1.3.3:
version "1.3.3"
resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
@ -7154,6 +7255,11 @@ prelude-ls@^1.2.1:
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
prelude-ls@~1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54"
integrity sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=
prepend-http@^1.0.0:
version "1.0.4"
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
@ -7197,13 +7303,6 @@ process@^0.11.10:
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
promise@^8.0.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/promise/-/promise-8.1.0.tgz#697c25c3dfe7435dd79fcd58c38a135888eaf05e"
integrity sha512-W04AqnILOL/sPRXziNicCjSNRruLAuIHEOVBazepu0545DDNGYHz7ar9ZgZ1fMU8/MA4mVxp5rkBWRi6OXIy3Q==
dependencies:
asap "~2.0.6"
proto-list@~1.2.1:
version "1.2.4"
resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849"
@ -7234,7 +7333,7 @@ pseudomap@^1.0.2:
resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM=
psl@^1.1.28:
psl@^1.1.28, psl@^1.1.33:
version "1.8.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24"
integrity sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==
@ -7294,13 +7393,6 @@ qs@6.9.7:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe"
integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==
qs@^6.4.0:
version "6.10.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.10.3.tgz#d6cde1b2ffca87b5aa57889816c5f81535e22e8e"
integrity sha512-wr7M2E0OFRfIfJZjKGieI8lBKb7fRCH4Fv5KNPEs7gJ8jadvotdsS08PzOKR7opXhZ/Xkjtt3WF9g38drmyRqQ==
dependencies:
side-channel "^1.0.4"
qs@~6.5.2:
version "6.5.3"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.3.tgz#3aeeffc91967ef6e35c0e488ef46fb296ab76aad"
@ -7359,7 +7451,7 @@ react-is@^17.0.1:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.1.tgz#5b3531bd76a645a4c9fb6e693ed36419e3301339"
integrity sha512-NAnt2iGDXohE5LI7uBnLnqvLQMtzhkiAOLXTmv+qnF9Ky7xAPcX8Up/xWIhxvLVGJvuLiNc4xQLtuqDRzb4fSA==
readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.2.2, readable-stream@^2.3.3, readable-stream@^2.3.6:
readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.3.3, readable-stream@^2.3.6:
version "2.3.7"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
@ -7667,6 +7759,13 @@ sax@~1.2.1:
resolved "https://registry.yarnpkg.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9"
integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==
saxes@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/saxes/-/saxes-5.0.1.tgz#eebab953fa3b7608dbe94e5dadb15c888fa6696d"
integrity sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==
dependencies:
xmlchars "^2.2.0"
schema-utils@^0.4.5:
version "0.4.7"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-0.4.7.tgz#ba74f597d2be2ea880131746ee17d0a093c68187"
@ -8291,21 +8390,10 @@ svgo@^2.7.0:
picocolors "^1.0.0"
stable "^0.1.8"
sync-request@^6.1.0:
version "6.1.0"
resolved "https://registry.yarnpkg.com/sync-request/-/sync-request-6.1.0.tgz#e96217565b5e50bbffe179868ba75532fb597e68"
integrity sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==
dependencies:
http-response-object "^3.0.1"
sync-rpc "^1.2.1"
then-request "^6.0.0"
sync-rpc@^1.2.1:
version "1.3.6"
resolved "https://registry.yarnpkg.com/sync-rpc/-/sync-rpc-1.3.6.tgz#b2e8b2550a12ccbc71df8644810529deb68665a7"
integrity sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==
dependencies:
get-port "^3.1.0"
symbol-tree@^3.2.4:
version "3.2.4"
resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==
tapable@^2.1.1, tapable@^2.2.0:
version "2.2.1"
@ -8347,23 +8435,6 @@ text-table@^0.2.0:
resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
then-request@^6.0.0:
version "6.0.2"
resolved "https://registry.yarnpkg.com/then-request/-/then-request-6.0.2.tgz#ec18dd8b5ca43aaee5cb92f7e4c1630e950d4f0c"
integrity sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==
dependencies:
"@types/concat-stream" "^1.6.0"
"@types/form-data" "0.0.33"
"@types/node" "^8.0.0"
"@types/qs" "^6.2.31"
caseless "~0.12.0"
concat-stream "^1.6.0"
form-data "^2.2.0"
http-basic "^8.1.1"
http-response-object "^3.0.1"
promise "^8.0.0"
qs "^6.4.0"
throttleit@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/throttleit/-/throttleit-1.0.0.tgz#9e785836daf46743145a5984b6268d828528ac6c"
@ -8425,6 +8496,15 @@ toidentifier@1.0.1:
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
tough-cookie@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4"
integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg==
dependencies:
psl "^1.1.33"
punycode "^2.1.1"
universalify "^0.1.2"
tough-cookie@~2.5.0:
version "2.5.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.5.0.tgz#cd9fb2a0aa1d5a12b473bd9fb96fa3dcff65ade2"
@ -8433,10 +8513,12 @@ tough-cookie@~2.5.0:
psl "^1.1.28"
punycode "^2.1.1"
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
tr46@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9"
integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==
dependencies:
punycode "^2.1.1"
ts-loader@^9.3.0:
version "9.3.0"
@ -8521,6 +8603,13 @@ type-check@^0.4.0, type-check@~0.4.0:
dependencies:
prelude-ls "^1.2.1"
type-check@~0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
integrity sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=
dependencies:
prelude-ls "~1.1.2"
type-detect@^4.0.0, type-detect@^4.0.5:
version "4.0.8"
resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
@ -8549,11 +8638,6 @@ type@^1.0.1:
resolved "https://registry.yarnpkg.com/type/-/type-1.0.3.tgz#16f5d39f27a2d28d86e48f8981859e9d3296c179"
integrity sha512-51IMtNfVcee8+9GJvj0spSuFcZHe9vSib6Xtgsny1Km9ugyz2mbS08I3rsUIRYgJohFRFU1160sgRodYz378Hg==
typedarray@^0.0.6:
version "0.0.6"
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^4.6.3:
version "4.6.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c"
@ -8602,6 +8686,11 @@ uniqs@^2.0.0:
resolved "https://registry.yarnpkg.com/uniqs/-/uniqs-2.0.0.tgz#ffede4b36b25290696e6e165d4a59edb998e6b02"
integrity sha1-/+3ks2slKQaW5uFl1KWe25mOawI=
universalify@^0.1.2:
version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
universalify@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
@ -8766,6 +8855,20 @@ vue@^3.2.32:
"@vue/server-renderer" "3.2.32"
"@vue/shared" "3.2.32"
w3c-hr-time@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd"
integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==
dependencies:
browser-process-hrtime "^1.0.0"
w3c-xmlserializer@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923"
integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg==
dependencies:
xml-name-validator "^4.0.0"
wait-on@6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/wait-on/-/wait-on-6.0.0.tgz#7e9bf8e3d7fe2daecbb7a570ac8ca41e9311c7e7"
@ -8792,11 +8895,6 @@ wbuf@^1.1.0, wbuf@^1.7.3:
dependencies:
minimalistic-assert "^1.0.0"
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
webidl-conversions@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a"
@ -8966,13 +9064,21 @@ whatwg-mimetype@^3.0.0:
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7"
integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
whatwg-url@^10.0.0:
version "10.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-10.0.0.tgz#37264f720b575b4a311bd4094ed8c760caaa05da"
integrity sha512-CLxxCmdUby142H5FZzn4D8ikO1cmypvXVQktsgosNy4a4BHrDHeciBBGZhb0bNoR5/MltoCatso+vFjjGx8t0w==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
tr46 "^3.0.0"
webidl-conversions "^7.0.0"
whatwg-url@^11.0.0:
version "11.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018"
integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==
dependencies:
tr46 "^3.0.0"
webidl-conversions "^7.0.0"
whet.extend@~0.9.9:
version "0.9.9"
@ -9007,7 +9113,7 @@ wildcard@^2.0.0:
resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
word-wrap@^1.2.3:
word-wrap@^1.2.3, word-wrap@~1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
@ -9035,11 +9141,26 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
ws@^8.2.3:
version "8.6.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.6.0.tgz#e5e9f1d9e7ff88083d0c0dd8281ea662a42c9c23"
integrity sha512-AzmM3aH3gk0aX7/rZLYvjdvZooofDu3fFOzGqcSnQ1tOcTWwhM/o+q++E8mAyVVIyUdajrkzWUGftaVSDLn1bw==
ws@^8.4.2:
version "8.5.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f"
integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==
xml-name-validator@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835"
integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==
xmlchars@^2.2.0:
version "2.2.0"
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb"
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==
xmlhttprequest@^1.8.0:
version "1.8.0"
resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc"