diff --git a/.deepsource.toml b/.deepsource.toml new file mode 100644 index 0000000..0c1cb01 --- /dev/null +++ b/.deepsource.toml @@ -0,0 +1,14 @@ +version = 1 + +[[analyzers]] +name = "javascript" + + [analyzers.meta] + plugins = ["react"] + +[[analyzers]] +name = "docker" + +[[analyzers]] +name = "test-coverage" +enabled = true \ No newline at end of file diff --git a/.github/workflows/deepsource-tests.yml b/.github/workflows/deepsource-tests.yml new file mode 100644 index 0000000..7b8b523 --- /dev/null +++ b/.github/workflows/deepsource-tests.yml @@ -0,0 +1,58 @@ +name: Deepsource report test coverage +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. +on: + push: + branches: [dev,master] + paths-ignore: + - '.github/**' + - 'images/' + - '*.md' + pull_request: + branches: + - master + - dev + + workflow_dispatch: + +env: + # Use docker.io for Docker Hub if empty + REGISTRY: ghcr.io + # github.repository as / + IMAGE_NAME: ${{ github.repository }} + +jobs: + report-test-coverage: + runs-on: ubuntu-latest + permissions: + packages: write + contents: read + steps: + - name: Setup + uses: actions/setup-node@v3 + - name: Checkout + uses: actions/checkout@v3 + - name: Get yarn cache directory path + id: yarn-cache-dir-path + run: echo "::set-output name=dir::$(yarn config get cacheFolder)" + - uses: actions/cache@v3 + id: yarn-cache + with: + path: ${{ steps.yarn-cache-dir-path.outputs.dir }} + key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} + restore-keys: | + ${{ runner.os }}-yarn- + - run: yarn install --immutable + - run: yarn test:cov + - run: curl https://deepsource.io/cli | sh + - name: Report test-coverage to DeepSource + run: | + # Install the CLI + curl https://deepsource.io/cli | sh + + # Send the report to DeepSource + ./bin/deepsource report --analyzer test-coverage --key javascript --value-file ./coverage/lcov.info + env: + DEEPSOURCE_DSN: ${{ secrets.DEEPSOURCE_DSN }} \ No newline at end of file diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml deleted file mode 100644 index efaed3a..0000000 --- a/.github/workflows/sonarcloud.yml +++ /dev/null @@ -1,85 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. -# They are provided by a third-party and are governed by -# separate terms of service, privacy policy, and support -# documentation. - -# This workflow helps you trigger a SonarCloud analysis of your code and populates -# GitHub Code Scanning alerts with the vulnerabilities found. -# Free for open source project. - -# 1. Login to SonarCloud.io using your GitHub account - -# 2. Import your project on SonarCloud -# * Add your GitHub organization first, then add your repository as a new project. -# * Please note that many languages are eligible for automatic analysis, -# which means that the analysis will start automatically without the need to set up GitHub Actions. -# * This behavior can be changed in Administration > Analysis Method. -# -# 3. Follow the SonarCloud in-product tutorial -# * a. Copy/paste the Project Key and the Organization Key into the args parameter below -# (You'll find this information in SonarCloud. Click on "Information" at the bottom left) -# -# * b. Generate a new token and add it to your Github repository's secrets using the name SONAR_TOKEN -# (On SonarCloud, click on your avatar on top-right > My account > Security -# or go directly to https://sonarcloud.io/account/security/) - -# Feel free to take a look at our documentation (https://docs.sonarcloud.io/getting-started/github/) -# or reach out to our community forum if you need some help (https://community.sonarsource.com/c/help/sc/9) - -name: SonarCloud analysis - -on: - push: - branches: [ "master", "dev" ] - pull_request: - branches: [ "master", "dev" ] - workflow_dispatch: - -permissions: - pull-requests: read # allows SonarCloud to decorate PRs with analysis results - -jobs: - Analysis: - runs-on: ubuntu-latest - - steps: - - name: Setup - uses: actions/setup-node@v3 - - name: Checkout - uses: actions/checkout@v3 - - name: Get yarn cache directory path - id: yarn-cache-dir-path - run: echo "::set-output name=dir::$(yarn config get cacheFolder)" - - uses: actions/cache@v3 - id: yarn-cache - with: - path: ${{ steps.yarn-cache-dir-path.outputs.dir }} - key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }} - restore-keys: | - ${{ runner.os }}-yarn- - - run: yarn install --immutable - - run: yarn test:cov - - name: Analyze with SonarCloud - - # You can pin the exact commit or the version. - # uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049 - uses: SonarSource/sonarcloud-github-action@de2e56b42aa84d0b1c5b622644ac17e505c9a049 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information - SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} # Generate a token on Sonarcloud.io, add it to the secrets of this repo with the name SONAR_TOKEN (Settings > Secrets > Actions > add new repository secret) - with: - # Additional arguments for the sonarcloud scanner - args: - # Unique keys of your project and organization. You can find them in SonarCloud > Information (bottom-left menu) - # mandatory - -Dsonar.projectKey=manuel-rw_jellyfin-discord-music-bot - -Dsonar.organization=manuel-rw - -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info - # Comma-separated paths to directories containing main source files. - #-Dsonar.sources= # optional, default is project base directory - # When you need the analysis to take place in a directory other than the one from which it was launched - #-Dsonar.projectBaseDir= # optional, default is . - # Comma-separated paths to directories containing test source files. - #-Dsonar.tests= # optional. For more info about Code Coverage, please refer to https://docs.sonarcloud.io/enriching/test-coverage/overview/ - # Adds more detail to both client and server-side analysis logs, activating DEBUG mode for the scanner, and adding client-side environment variables and system properties to the server-side log of analysis report processing. - #-Dsonar.verbose= # optional, default is false diff --git a/README.md b/README.md index a6316aa..2a04b8b 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@

+


diff --git a/package.json b/package.json index 275bca8..a6b3e97 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "jellyfin-discord-music-bot", - "version": "0.0.6", - "description": "", + "version": "0.0.7", + "description": "A simple and leightweight Discord Bot, that integrates with your Jellyfin Media server and enables you to listen to your favourite music directly from discord.", "author": "manuel-rw", "private": true, "license": "MIT", @@ -24,49 +24,50 @@ "@discord-nestjs/common": "^5.2.3", "@discord-nestjs/core": "^5.3.5", "@discordjs/opus": "^0.9.0", - "@discordjs/voice": "^0.15.0", - "@jellyfin/sdk": "^0.7.0", - "@nestjs/common": "^9.3.12", + "@discordjs/voice": "^0.16.0", + "@jellyfin/sdk": "^0.8.1", + "@nestjs/common": "^9.4.0", "@nestjs/config": "^2.2.0", - "@nestjs/core": "^9.3.12", + "@nestjs/core": "^9.4.0", "@nestjs/event-emitter": "^1.3.1", "@nestjs/platform-express": "^9.3.12", "@nestjs/schedule": "^2.1.0", "@nestjs/serve-static": "^3.0.1", - "@nestjs/terminus": "^9.1.4", + "@nestjs/terminus": "^9.2.2", "date-fns": "^2.29.3", - "discord.js": "^14.8.0", + "discord.js": "^14.9.0", "joi": "^17.9.1", "libsodium-wrappers": "^0.7.10", "opusscript": "^0.0.8", "reflect-metadata": "^0.1.13", - "rimraf": "^4.4.1", + "rimraf": "^5.0.0", "rxjs": "^7.2.0", "uuid": "^9.0.0", - "ws": "^8.13.0" + "ws": "^8.13.0", + "zod": "^3.21.4" }, "devDependencies": { "@nestjs/cli": "^9.3.0", - "@nestjs/schematics": "^9.0.0", - "@nestjs/testing": "^9.3.12", + "@nestjs/schematics": "^9.1.0", + "@nestjs/testing": "^9.4.0", "@types/cron": "^2.0.1", "@types/express": "^4.17.13", "@types/jest": "28.1.8", - "@types/node": "^18.15.10", + "@types/node": "^18.15.11", "@types/supertest": "^2.0.11", - "@typescript-eslint/eslint-plugin": "^5.56.0", - "@typescript-eslint/parser": "^5.56.0", - "eslint": "^8.35.0", + "@typescript-eslint/eslint-plugin": "^5.57.0", + "@typescript-eslint/parser": "^5.57.0", + "eslint": "^8.38.0", "eslint-config-prettier": "^8.8.0", "eslint-plugin-prettier": "^4.0.0", "jest": "28.1.3", - "prettier": "^2.8.4", + "prettier": "^2.8.7", "source-map-support": "^0.5.20", "supertest": "^6.1.3", "ts-jest": "28.0.8", "ts-loader": "^9.2.3", "ts-node": "^10.0.0", - "tsconfig-paths": "4.1.2", + "tsconfig-paths": "4.2.0", "typescript": "^4.7.4" }, "jest": { diff --git a/src/app.module.ts b/src/app.module.ts index f860303..8773310 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -28,6 +28,7 @@ import { UpdatesModule } from './updates/updates.module'; UPDATER_DISABLE_NOTIFICATIONS: Joi.boolean(), LOG_LEVEL: Joi.string() .valid('error', 'warn', 'log', 'debug', 'verbose') + .insensitive() .default('log'), PORT: Joi.number().min(1), }), diff --git a/src/clients/discord/discord.config.service.ts b/src/clients/discord/discord.config.service.ts index 870d5ee..b73cf52 100644 --- a/src/clients/discord/discord.config.service.ts +++ b/src/clients/discord/discord.config.service.ts @@ -9,7 +9,7 @@ import { GatewayIntentBits } from 'discord.js'; export class DiscordConfigService implements DiscordOptionsFactory { createDiscordOptions(): DiscordModuleOption { return { - token: process.env.DISCORD_CLIENT_TOKEN, + token: process.env.DISCORD_CLIENT_TOKEN ?? '', discordClientOptions: { intents: [ GatewayIntentBits.Guilds, diff --git a/src/clients/discord/discord.voice.service.ts b/src/clients/discord/discord.voice.service.ts index 615fbc9..db81906 100644 --- a/src/clients/discord/discord.voice.service.ts +++ b/src/clients/discord/discord.voice.service.ts @@ -9,20 +9,20 @@ import { joinVoiceChannel, NoSubscriberBehavior, VoiceConnection, - VoiceConnectionStatus, } from '@discordjs/voice'; import { Injectable } from '@nestjs/common'; import { Logger } from '@nestjs/common/services'; import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'; +import { Interval } from '@nestjs/schedule'; import { GuildMember } from 'discord.js'; +import { GenericTryHandler } from '../../models/generic-try-handler'; +import { Track } from '../../models/shared/Track'; +import { PlaybackService } from '../../playback/playback.service'; import { JellyfinStreamBuilderService } from '../jellyfin/jellyfin.stream.builder.service'; import { JellyfinWebSocketService } from '../jellyfin/jellyfin.websocket.service'; -import { GenericTryHandler } from '../../models/generic-try-handler'; -import { PlaybackService } from '../../playback/playback.service'; -import { Track } from '../../models/shared/Track'; import { DiscordMessageService } from './discord.message.service'; @@ -41,7 +41,7 @@ export class DiscordVoiceService { private readonly eventEmitter: EventEmitter2, ) {} - @OnEvent('internal.audio.announce') + @OnEvent('internal.audio.track.announce') handleOnNewTrack(track: Track) { const resource = createAudioResource( track.getStreamUrl(this.jellyfinStreamBuilder), @@ -93,7 +93,7 @@ export class DiscordVoiceService { this.jellyfinWebSocketService.initializeAndConnect(); - if (this.voiceConnection == undefined) { + if (this.voiceConnection === undefined) { this.voiceConnection = getVoiceConnection(member.guild.id); } @@ -104,6 +104,12 @@ export class DiscordVoiceService { } changeVolume(volume: number) { + if (!this.audioResource || !this.audioResource.volume) { + this.logger.error( + `Failed to change audio volume, AudioResource or volume was undefined`, + ); + return; + } this.audioResource.volume.setVolume(volume); } @@ -116,6 +122,7 @@ export class DiscordVoiceService { /** * Pauses the current audio player */ + @OnEvent('internal.voice.controls.pause') pause() { this.createAndReturnOrGetAudioPlayer().pause(); this.eventEmitter.emit('playback.state.pause', true); @@ -124,6 +131,7 @@ export class DiscordVoiceService { /** * Stops the audio player */ + @OnEvent('internal.voice.controls.stop') stop(force: boolean): boolean { const stopped = this.createAndReturnOrGetAudioPlayer().stop(force); this.eventEmitter.emit('playback.state.stop'); @@ -161,6 +169,7 @@ export class DiscordVoiceService { * Checks if the current state is paused or not and toggles the states to the opposite. * @returns The new paused state - true: paused, false: unpaused */ + @OnEvent('internal.voice.controls.togglePause') togglePaused(): boolean { if (this.isPaused()) { this.unpause(); @@ -216,7 +225,7 @@ export class DiscordVoiceService { if (this.audioPlayer === undefined) { this.logger.debug( - `Initialized new instance of AudioPlayer because it has not been defined yet`, + 'Initialized new instance of AudioPlayer because it has not been defined yet', ); this.audioPlayer = createAudioPlayer({ debug: process.env.DEBUG?.toLowerCase() === 'true', @@ -233,6 +242,20 @@ export class DiscordVoiceService { } private attachEventListenersToAudioPlayer() { + if (!this.voiceConnection) { + this.logger.error( + `Unable to attach listener events, because the VoiceConnection was undefined`, + ); + return; + } + + if (!this.audioPlayer) { + this.logger.error( + `Unable to attach listener events, because the AudioPlayer was undefined`, + ); + return; + } + this.voiceConnection.on('debug', (message) => { if (process.env.DEBUG?.toLowerCase() !== 'true') { return; @@ -250,6 +273,13 @@ export class DiscordVoiceService { this.logger.error(message); }); this.audioPlayer.on('stateChange', (previousState) => { + if (!this.audioPlayer) { + this.logger.error( + `Unable to process state change from audio player, because the current audio player in the callback was undefined`, + ); + return; + } + this.logger.debug( `Audio player changed state from ${previousState.status} to ${this.audioPlayer.state.status}`, ); @@ -262,22 +292,60 @@ export class DiscordVoiceService { return; } - this.logger.debug(`Audio player finished playing old resource`); + this.logger.debug('Audio player finished playing old resource'); - const hasNextTrack = this.playbackService - .getPlaylistOrDefault() - .hasNextTrackInPlaylist(); + const playlist = this.playbackService.getPlaylistOrDefault(); + const finishedTrack = playlist.getActiveTrack(); + + if (finishedTrack) { + finishedTrack.playing = false; + this.eventEmitter.emit('internal.audio.track.finish', finishedTrack); + } + + const hasNextTrack = playlist.hasNextTrackInPlaylist(); this.logger.debug( `Playlist has next track: ${hasNextTrack ? 'yes' : 'no'}`, ); if (!hasNextTrack) { - this.logger.debug(`Reached the end of the playlist`); + this.logger.debug('Reached the end of the playlist'); return; } this.playbackService.getPlaylistOrDefault().setNextTrackAsActiveTrack(); }); } + + @Interval(500) + private checkAudioResourcePlayback() { + if (!this.audioResource) { + return; + } + + const progress = this.audioResource.playbackDuration; + + const playlist = this.playbackService.getPlaylistOrDefault(); + + if (!playlist) { + this.logger.error( + `Failed to update ellapsed audio time because playlist was unexpectitly undefined`, + ); + return; + } + + const activeTrack = playlist.getActiveTrack(); + + if (!activeTrack) { + this.logger.error( + `Failed to update ellapsed audio time because active track was unexpectitly undefined`, + ); + return; + } + + activeTrack.updatePlaybackProgress(progress); + this.logger.verbose( + `Reporting progress: ${progress} on track ${activeTrack.id}`, + ); + } } diff --git a/src/clients/jellyfin/jellyfin.playstate.service.ts b/src/clients/jellyfin/jellyfin.playstate.service.ts index 57b1483..ae56dc2 100644 --- a/src/clients/jellyfin/jellyfin.playstate.service.ts +++ b/src/clients/jellyfin/jellyfin.playstate.service.ts @@ -10,9 +10,10 @@ import { getSessionApi } from '@jellyfin/sdk/lib/utils/api/session-api'; import { Injectable, Logger } from '@nestjs/common'; import { OnEvent } from '@nestjs/event-emitter'; +import { Interval } from '@nestjs/schedule'; +import { Track } from '../../models/shared/Track'; import { PlaybackService } from '../../playback/playback.service'; -import { Track } from '../../types/track'; @Injectable() export class JellyinPlaystateService { @@ -46,12 +47,71 @@ export class JellyinPlaystateService { this.logger.debug('Reported playback capabilities sucessfully'); } - @OnEvent('playback.newTrack') + @OnEvent('internal.audio.track.announce') private async onPlaybackNewTrack(track: Track) { + this.logger.debug(`Reporting playback start on track '${track.id}'`); await this.playstateApi.reportPlaybackStart({ playbackStartInfo: { - ItemId: track.jellyfinId, + ItemId: track.id, + PositionTicks: 0, }, }); } + + @OnEvent('internal.audio.track.finish') + private async onPlaybackFinished(track: Track) { + if (!track) { + this.logger.error( + 'Unable to report playback because finished track was undefined', + ); + return; + } + this.logger.debug(`Reporting playback finish on track '${track.id}'`); + await this.playstateApi.reportPlaybackStopped({ + playbackStopInfo: { + ItemId: track.id, + PositionTicks: track.playbackProgress * 10000, + }, + }); + } + + @OnEvent('playback.state.pause') + private async onPlaybackPause(paused: boolean) { + const track = this.playbackService.getPlaylistOrDefault().getActiveTrack(); + + if (!track) { + this.logger.error( + 'Unable to report changed playstate to Jellyfin because no track was active', + ); + return; + } + + this.playstateApi.reportPlaybackProgress({ + playbackProgressInfo: { + IsPaused: paused, + ItemId: track.id, + PositionTicks: track.playbackProgress * 10000, + }, + }); + } + + @Interval(1000) + private async onPlaybackProgress() { + const track = this.playbackService.getPlaylistOrDefault().getActiveTrack(); + + if (!track) { + return; + } + + await this.playstateApi.reportPlaybackProgress({ + playbackProgressInfo: { + ItemId: track.id, + PositionTicks: track.playbackProgress * 10000, + }, + }); + + this.logger.verbose( + `Reported playback progress ${track.playbackProgress} to Jellyfin for item ${track.id}`, + ); + } } diff --git a/src/clients/jellyfin/jellyfin.search.service.ts b/src/clients/jellyfin/jellyfin.search.service.ts index 626d73f..318585c 100644 --- a/src/clients/jellyfin/jellyfin.search.service.ts +++ b/src/clients/jellyfin/jellyfin.search.service.ts @@ -1,4 +1,5 @@ import { + BaseItemDto, BaseItemKind, RemoteImageResult, SearchHint as JellyfinSearchHint, @@ -37,7 +38,7 @@ export class JellyfinSearchService { if (includeItemTypes.length === 0) { this.logger.warn( - `Included item types are empty. This may lead to unwanted results`, + "Included item types are empty. This may lead to unwanted results", ); } @@ -57,9 +58,13 @@ export class JellyfinSearchService { const { SearchHints } = data; - return SearchHints.map((hint) => this.transformToSearchHint(hint)).filter( - (x) => x !== null, - ); + if (!SearchHints) { + throw new Error('SearchHints were undefined'); + } + + return SearchHints.map((hint) => + this.transformToSearchHintFromHint(hint), + ).filter((x) => x !== null) as SearchHint[]; } catch (err) { this.logger.error(`Failed to search on Jellyfin: ${err}`); return []; @@ -82,8 +87,15 @@ export class JellyfinSearchService { return []; } + if (!axiosResponse.data.Items) { + this.logger.error( + `Jellyfin search returned no items: ${axiosResponse.data}`, + ); + return []; + } + return axiosResponse.data.Items.map((hint) => - SearchHint.constructFromHint(hint), + SearchHint.constructFromBaseItem(hint), ); } @@ -104,15 +116,22 @@ export class JellyfinSearchService { return []; } - return axiosResponse.data.SearchHints.map((hint) => - SearchHint.constructFromHint(hint), - ); + if (!axiosResponse.data.SearchHints) { + this.logger.error( + `Received an unexpected empty list but expected a list of tracks of the album`, + ); + return []; + } + + return [...axiosResponse.data.SearchHints] + .reverse() + .map((hint) => SearchHint.constructFromHint(hint)); } async getById( id: string, includeItemTypes: BaseItemKind[], - ): Promise | undefined { + ): Promise { const api = this.jellyfinService.getApi(); const searchApi = getItemsApi(api); @@ -122,12 +141,35 @@ export class JellyfinSearchService { includeItemTypes: includeItemTypes, }); - if (data.Items.length !== 1) { + if (!data.Items || data.Items.length !== 1) { this.logger.warn(`Failed to retrieve item via id '${id}'`); - return null; + return undefined; } - return this.transformToSearchHint(data.Items[0]); + return this.transformToSearchHintFromBaseItemDto(data.Items[0]); + } + + async getAllById( + ids: string[], + includeItemTypes: BaseItemKind[] = [BaseItemKind.Audio], + ): Promise { + const api = this.jellyfinService.getApi(); + + const searchApi = getItemsApi(api); + const { data } = await searchApi.getItems({ + ids: ids, + userId: this.jellyfinService.getUserId(), + includeItemTypes: includeItemTypes, + }); + + if (!data.Items || data.Items.length !== 1) { + this.logger.warn(`Failed to retrieve item via id '${ids}'`); + return []; + } + + return data.Items.map((item) => + this.transformToSearchHintFromBaseItemDto(item), + ).filter((searchHint) => searchHint !== undefined) as SearchHint[]; } async getRemoteImageById(id: string, limit = 20): Promise { @@ -183,18 +225,25 @@ export class JellyfinSearchService { recursive: true, }); + if (!response.data.Items) { + this.logger.error( + `Received empty list of items but expected a random list of tracks`, + ); + return []; + } + return response.data.Items.map((item) => { return SearchHint.constructFromBaseItem(item); }); } catch (err) { this.logger.error( - `Unabele to retrieve random items from Jellyfin: ${err}`, + `Unable to retrieve random items from Jellyfin: ${err}`, ); return []; } } - private transformToSearchHint(jellyifnHint: JellyfinSearchHint) { + private transformToSearchHintFromHint(jellyifnHint: JellyfinSearchHint) { switch (jellyifnHint.Type) { case BaseItemKind[BaseItemKind.Audio]: return SearchHint.constructFromHint(jellyifnHint); @@ -206,7 +255,23 @@ export class JellyfinSearchService { this.logger.warn( `Received unexpected item type from Jellyfin search: ${jellyifnHint.Type}`, ); - return null; + return undefined; + } + } + + private transformToSearchHintFromBaseItemDto(baseItemDto: BaseItemDto) { + switch (baseItemDto.Type) { + case BaseItemKind[BaseItemKind.Audio]: + return SearchHint.constructFromBaseItem(baseItemDto); + case BaseItemKind[BaseItemKind.MusicAlbum]: + return AlbumSearchHint.constructFromBaseItem(baseItemDto); + case BaseItemKind[BaseItemKind.Playlist]: + return PlaylistSearchHint.constructFromBaseItem(baseItemDto); + default: + this.logger.warn( + `Received unexpected item type from Jellyfin search: ${baseItemDto.Type}`, + ); + return undefined; } } } diff --git a/src/clients/jellyfin/jellyfin.service.ts b/src/clients/jellyfin/jellyfin.service.ts index 3051f12..b049f79 100644 --- a/src/clients/jellyfin/jellyfin.service.ts +++ b/src/clients/jellyfin/jellyfin.service.ts @@ -33,18 +33,20 @@ export class JellyfinService { }, }); - this.api = this.jellyfin.createApi(process.env.JELLYFIN_SERVER_ADDRESS); + this.api = this.jellyfin.createApi( + process.env.JELLYFIN_SERVER_ADDRESS ?? '', + ); this.logger.debug('Created Jellyfin Client and Api'); } authenticate() { this.api .authenticateUserByName( - process.env.JELLYFIN_AUTHENTICATION_USERNAME, + process.env.JELLYFIN_AUTHENTICATION_USERNAME ?? '', process.env.JELLYFIN_AUTHENTICATION_PASSWORD, ) .then(async (response) => { - if (response.data.SessionInfo === undefined) { + if (response.data.SessionInfo?.UserId === undefined) { this.logger.error( `Failed to authenticate with response code ${response.status}: '${response.data}'`, ); diff --git a/src/clients/jellyfin/jellyfin.websocket.service.ts b/src/clients/jellyfin/jellyfin.websocket.service.ts index 21bb8d6..136cc69 100644 --- a/src/clients/jellyfin/jellyfin.websocket.service.ts +++ b/src/clients/jellyfin/jellyfin.websocket.service.ts @@ -4,8 +4,9 @@ import { } from '@jellyfin/sdk/lib/generated-client/models'; import { Injectable, Logger, OnModuleDestroy } from '@nestjs/common'; -import { Cron } from '@nestjs/schedule'; import { EventEmitter2 } from '@nestjs/event-emitter'; +import { Cron } from '@nestjs/schedule'; +import { convertToTracks } from 'src/utils/trackConverter'; import { WebSocket } from 'ws'; @@ -14,11 +15,9 @@ import { PlayNowCommand, SessionApiSendPlaystateCommandRequest, } from '../../types/websocket'; -import { Track } from '../../models/shared/Track'; import { JellyfinSearchService } from './jellyfin.search.service'; import { JellyfinService } from './jellyfin.service'; -import { JellyfinStreamBuilderService } from './jellyfin.stream.builder.service'; @Injectable() export class JellyfinWebSocketService implements OnModuleDestroy { @@ -28,9 +27,8 @@ export class JellyfinWebSocketService implements OnModuleDestroy { constructor( private readonly jellyfinService: JellyfinService, - private readonly jellyfinSearchService: JellyfinSearchService, private readonly playbackService: PlaybackService, - private readonly jellyfinStreamBuilderService: JellyfinStreamBuilderService, + private readonly jellyfinSearchService: JellyfinSearchService, private readonly eventEmitter: EventEmitter2, ) {} @@ -103,8 +101,12 @@ export class JellyfinWebSocketService implements OnModuleDestroy { data.hasSelection = PlayNowCommand.prototype.hasSelection; data.getSelection = PlayNowCommand.prototype.getSelection; const ids = data.getSelection(); - - // TODO: Implement this again + this.logger.log( + `Processing ${ids.length} ids received via websocket and adding them to the queue`, + ); + const searchHints = await this.jellyfinSearchService.getAllById(ids); + const tracks = convertToTracks(searchHints, this.jellyfinSearchService); + this.playbackService.getPlaylistOrDefault().enqueueTracks(tracks); break; case SessionMessageType[SessionMessageType.Playstate]: const sendPlaystateCommandRequest = @@ -124,13 +126,19 @@ export class JellyfinWebSocketService implements OnModuleDestroy { ) { switch (request.Command) { case PlaystateCommand.PlayPause: - this.eventEmitter.emitAsync('playback.control.togglePause'); + this.eventEmitter.emit('internal.voice.controls.togglePause'); break; case PlaystateCommand.Pause: - this.eventEmitter.emitAsync('playback.control.pause'); + this.eventEmitter.emit('internal.voice.controls.pause'); break; case PlaystateCommand.Stop: - this.eventEmitter.emitAsync('playback.control.stop'); + this.eventEmitter.emit('internal.voice.controls.stop'); + break; + case PlaystateCommand.NextTrack: + this.eventEmitter.emit('internal.audio.track.next'); + break; + case PlaystateCommand.PreviousTrack: + this.eventEmitter.emit('internal.audio.track.previous'); break; default: this.logger.warn( diff --git a/src/commands/play/play.comands.ts b/src/commands/play/play.comands.ts index 0e72eb4..d1d3b2a 100644 --- a/src/commands/play/play.comands.ts +++ b/src/commands/play/play.comands.ts @@ -27,7 +27,7 @@ import { DiscordVoiceService } from '../../clients/discord/discord.voice.service import { JellyfinSearchService } from '../../clients/jellyfin/jellyfin.search.service'; import { SearchHint } from '../../models/search/SearchHint'; -import { SearchType, PlayCommandParams } from './play.params.ts'; +import { PlayCommandParams, SearchType } from './play.params.ts'; @Injectable() @Command({ @@ -48,12 +48,12 @@ export class PlayItemCommand { async handler( @InteractionEvent(SlashCommandPipe) dto: PlayCommandParams, @IA() interaction: CommandInteraction, - ): Promise { + ) { await interaction.deferReply({ ephemeral: true }); const baseItems = PlayCommandParams.getBaseItemKinds(dto.type); - let item: SearchHint; + let item: SearchHint | undefined; if (dto.name.startsWith('native-')) { item = await this.jellyfinSearchService.getById( dto.name.replace('native-', ''), @@ -70,7 +70,8 @@ export class PlayItemCommand { embeds: [ this.discordMessageService.buildMessage({ title: 'No results found', - description: `- Check for any misspellings\n- Grant me access to your desired libraries\n- Avoid special characters`, + description: + '- Check for any misspellings\n- Grant me access to your desired libraries\n- Avoid special characters', }), ], ephemeral: true, @@ -104,9 +105,9 @@ export class PlayItemCommand { ); this.playbackService.getPlaylistOrDefault().enqueueTracks(tracks); - const remoteImage: RemoteImageInfo | undefined = tracks - .flatMap((x) => x.getRemoteImages()) - .find((x) => true); + const remoteImages = tracks.flatMap((track) => track.getRemoteImages()); + const remoteImage: RemoteImageInfo | undefined = + remoteImages.length > 0 ? remoteImages[0] : undefined; await interaction.followUp({ embeds: [ @@ -117,7 +118,7 @@ export class PlayItemCommand { reducedDuration, )})`, mixin(embedBuilder) { - if (!remoteImage) { + if (!remoteImage?.Url) { return embedBuilder; } return embedBuilder.setThumbnail(remoteImage.Url); @@ -135,8 +136,9 @@ export class PlayItemCommand { } const focusedAutoCompleteAction = interaction.options.getFocused(true); - const typeIndex: number | null = interaction.options.getInteger('type'); - const type = Object.values(SearchType)[typeIndex] as SearchType; + const typeIndex = interaction.options.getInteger('type'); + const type = + typeIndex !== null ? Object.values(SearchType)[typeIndex] : undefined; const searchQuery = focusedAutoCompleteAction.value; if (!searchQuery || searchQuery.length < 1) { @@ -154,7 +156,7 @@ export class PlayItemCommand { const hints = await this.jellyfinSearchService.searchItem( searchQuery, 20, - PlayCommandParams.getBaseItemKinds(type), + PlayCommandParams.getBaseItemKinds(type as SearchType), ); if (hints.length === 0) { diff --git a/src/commands/playlist/playlist.command.ts b/src/commands/playlist/playlist.command.ts index d244041..2cd41cf 100644 --- a/src/commands/playlist/playlist.command.ts +++ b/src/commands/playlist/playlist.command.ts @@ -22,16 +22,19 @@ import { InteractionUpdateOptions, } from 'discord.js'; -import { PlaybackService } from '../../playback/playback.service'; -import { chunkArray } from '../../utils/arrayUtils'; -import { Constants } from '../../utils/constants'; -import { formatMillisecondsAsHumanReadable } from '../../utils/timeUtils'; import { DiscordMessageService } from '../../clients/discord/discord.message.service'; import { Track } from '../../models/shared/Track'; -import { trimStringToFixedLength } from '../../utils/stringUtils/stringUtils'; +import { PlaybackService } from '../../playback/playback.service'; +import { chunkArray } from '../../utils/arrayUtils'; +import { trimStringToFixedLength, zeroPad } from '../../utils/stringUtils/stringUtils'; +import { Interval } from '@nestjs/schedule'; +import { lightFormat } from 'date-fns'; import { PlaylistInteractionCollector } from './playlist.interaction-collector'; import { PlaylistCommandParams } from './playlist.params'; +import { PlaylistTempCommandData } from './playlist.types'; +import { tr } from 'date-fns/locale'; +import { takeCoverage } from 'v8'; @Injectable() @Command({ @@ -41,7 +44,7 @@ import { PlaylistCommandParams } from './playlist.params'; @UseInterceptors(CollectorInterceptor) @UseCollectors(PlaylistInteractionCollector) export class PlaylistCommand { - public pageData: Map = new Map(); + public pageData: Map = new Map(); private readonly logger = new Logger(PlaylistCommand.name); constructor( @@ -61,7 +64,10 @@ export class PlaylistCommand { this.getReplyForPage(page) as InteractionReplyOptions, ); - this.pageData.set(interaction.id, page); + this.pageData.set(interaction.id, { + page, + interaction, + }); this.logger.debug( `Added '${interaction.id}' as a message id for page storage`, ); @@ -82,6 +88,36 @@ export class PlaylistCommand { return chunkArray(playlist.tracks, 10); } + private createInterval(interaction: CommandInteraction) { + return setInterval(async () => { + const tempData = this.pageData.get(interaction.id); + + if (!tempData) { + this.logger.warn( + `Failed to update from interval, because temp data was not found`, + ); + return; + } + + await interaction.editReply(this.getReplyForPage(tempData.page)); + }, 2000); + } + + @Interval(2 * 1000) + private async updatePlaylists() { + if (this.pageData.size === 0) { + return; + } + + this.logger.verbose( + `Updating playlist for ${this.pageData.size} playlist datas`, + ); + + this.pageData.forEach(async (value) => { + await value.interaction.editReply(this.getReplyForPage(value.page)); + }); + } + public getReplyForPage( page: number, ): InteractionReplyOptions | InteractionUpdateOptions { @@ -176,26 +212,34 @@ export class PlaylistCommand { ); } + const paddingNumber = playlist.getLength() >= 100 ? 3 : 2; + const content = chunk .map((track, index) => { const isCurrent = track === playlist.getActiveTrack(); - // use the offset for the page, add the current index and offset by one because the array index is used - let point = `${offset + index + 1}. `; - point += `**${trimStringToFixedLength(track.name, 30)}**`; - + let line = `\`\`${zeroPad(offset + index + 1, paddingNumber)}.\`\` `; + line += this.getTrackName(track, isCurrent) + ' • '; if (isCurrent) { - point += ' :loud_sound:'; + line += lightFormat(track.getPlaybackProgress(), 'mm:ss') + ' / '; } - - point += '\n'; - point += Constants.Design.InvisibleSpace.repeat(2); - point += formatMillisecondsAsHumanReadable(track.getDuration()); - - return point; + line += lightFormat(track.getDuration(), 'mm:ss'); + if (isCurrent) { + line += ' • (:play_pause:)'; + } + return line; }) .join('\n'); return new EmbedBuilder().setTitle('Your playlist').setDescription(content); } + + private getTrackName(track: Track, active: boolean) { + const trimmedTitle = trimStringToFixedLength(track.name, 30); + if (active) { + return `**${trimmedTitle}**`; + } + + return trimmedTitle; + } } diff --git a/src/commands/playlist/playlist.interaction-collector.ts b/src/commands/playlist/playlist.interaction-collector.ts index 462ad16..49e3ca8 100644 --- a/src/commands/playlist/playlist.interaction-collector.ts +++ b/src/commands/playlist/playlist.interaction-collector.ts @@ -15,6 +15,7 @@ import { } from 'discord.js'; import { PlaylistCommand } from './playlist.command'; +import { PlaylistTempCommandData } from './playlist.types'; @Injectable({ scope: Scope.REQUEST }) @InteractionEventCollector({ time: 60 * 1000 }) @@ -30,14 +31,17 @@ export class PlaylistInteractionCollector { @Filter() filter(interaction: ButtonInteraction): boolean { - return this.causeInteraction.id === interaction.message.interaction.id; + return ( + interaction.message.interaction !== null && + this.causeInteraction.id === interaction.message.interaction.id + ); } @On('collect') async onCollect(interaction: ButtonInteraction): Promise { const targetPage = this.getInteraction(interaction); this.logger.verbose( - `Extracted the target page ${targetPage} from the button interaction`, + `Extracted the target page '${targetPage?.page}' from the button interaction`, ); if (targetPage === undefined) { @@ -48,14 +52,16 @@ export class PlaylistInteractionCollector { } this.logger.debug( - `Updating current page for interaction ${this.causeInteraction.id} to ${targetPage}`, + `Updating current page for interaction ${this.causeInteraction.id} to ${targetPage.page}`, ); this.playlistCommand.pageData.set(this.causeInteraction.id, targetPage); - const reply = this.playlistCommand.getReplyForPage(targetPage); + const reply = this.playlistCommand.getReplyForPage(targetPage.page); await interaction.update(reply as InteractionUpdateOptions); } - private getInteraction(interaction: ButtonInteraction): number | null { + private getInteraction( + interaction: ButtonInteraction, + ): PlaylistTempCommandData | undefined { const current = this.playlistCommand.pageData.get(this.causeInteraction.id); if (current === undefined) { @@ -75,12 +81,18 @@ export class PlaylistInteractionCollector { switch (interaction.customId) { case 'playlist-controls-next': - return current + 1; + return { + ...current, + page: current.page + 1, + }; case 'playlist-controls-previous': - return current - 1; + return { + ...current, + page: current.page - 1, + }; default: this.logger.error( - `Unable to map button interaction from collector to target page`, + 'Unable to map button interaction from collector to target page', ); return undefined; } diff --git a/src/commands/playlist/playlist.types.ts b/src/commands/playlist/playlist.types.ts new file mode 100644 index 0000000..1858151 --- /dev/null +++ b/src/commands/playlist/playlist.types.ts @@ -0,0 +1,6 @@ +import { CommandInteraction } from 'discord.js'; + +export type PlaylistTempCommandData = { + page: number; + interaction: CommandInteraction; +}; diff --git a/src/commands/status.command.ts b/src/commands/status.command.ts index a868546..5412dc2 100644 --- a/src/commands/status.command.ts +++ b/src/commands/status.command.ts @@ -39,7 +39,7 @@ export class StatusCommand { const status = Status[this.client.ws.status]; const interval = intervalToDuration({ - start: this.client.uptime, + start: this.client.uptime ?? 0, end: 0, }); const formattedDuration = formatDuration(interval); diff --git a/src/commands/stop.command.ts b/src/commands/stop.command.ts index d9d1579..b57b129 100644 --- a/src/commands/stop.command.ts +++ b/src/commands/stop.command.ts @@ -30,8 +30,8 @@ export class StopPlaybackCommand { ? 'In addition, your playlist has been cleared' : 'There is no active track in the queue'; if (hasActiveTrack) { - this.playbackService.getPlaylistOrDefault().clear(); this.discordVoiceService.stop(false); + // this.playbackService.getPlaylistOrDefault().clear(); } await interaction.reply({ diff --git a/src/commands/volume/volume.command.ts b/src/commands/volume/volume.command.ts index 3bbe4ab..a777d9c 100644 --- a/src/commands/volume/volume.command.ts +++ b/src/commands/volume/volume.command.ts @@ -36,7 +36,7 @@ export class VolumeCommand { await interaction.editReply({ embeds: [ this.discordMessageService.buildMessage({ - title: `Unable to change your volume`, + title: "Unable to change your volume", description: 'The bot is not playing any music or is not straming to a channel', }), diff --git a/src/health/health.controller.spec.ts b/src/health/health.controller.spec.ts index 7de55d4..7d1cf84 100644 --- a/src/health/health.controller.spec.ts +++ b/src/health/health.controller.spec.ts @@ -1,3 +1,4 @@ +import { InjectionToken } from '@nestjs/common'; import { HealthCheckResult, HealthCheckService, @@ -42,10 +43,14 @@ describe('HealthController', () => { } if (token === HealthCheckService) { - return new HealthCheckService(new HealthCheckExecutor(), null, null); + return new HealthCheckService( + new HealthCheckExecutor(), + { getErrorMessage: jest.fn() }, + { log: jest.fn(), error: jest.fn(), warn: jest.fn() }, + ); } - return useDefaultMockerToken(token); + return useDefaultMockerToken(token as InjectionToken); }) .compile(); diff --git a/src/health/indicators/jeyllfin.indicator.spec.ts b/src/health/indicators/jeyllfin.indicator.spec.ts index 38b12c7..dcebe2d 100644 --- a/src/health/indicators/jeyllfin.indicator.spec.ts +++ b/src/health/indicators/jeyllfin.indicator.spec.ts @@ -1,3 +1,4 @@ +import { InjectionToken } from '@nestjs/common'; import { HealthIndicatorResult } from '@nestjs/terminus'; import { Test } from '@nestjs/testing'; import { JellyfinService } from '../../clients/jellyfin/jellyfin.service'; @@ -16,7 +17,7 @@ describe('JellyfinHealthIndicator', () => { if (token === JellyfinService) { return { isConnected: jest.fn() }; } - return useDefaultMockerToken(token); + return useDefaultMockerToken(token as InjectionToken); }) .compile(); diff --git a/src/main.ts b/src/main.ts index 088755b..4fc280c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,10 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; function getLoggingLevels(): LogLevel[] { + if (!process.env.LOG_LEVEL) { + return ['error', 'warn', 'log']; + } + switch (process.env.LOG_LEVEL.toLowerCase()) { case 'error': return ['error']; diff --git a/src/models/search/AlbumSearchHint.ts b/src/models/search/AlbumSearchHint.ts index dd4b440..b51a57e 100644 --- a/src/models/search/AlbumSearchHint.ts +++ b/src/models/search/AlbumSearchHint.ts @@ -11,6 +11,12 @@ export class AlbumSearchHint extends SearchHint { } static constructFromHint(hint: JellyfinSearchHint) { + if (hint.Id === undefined || !hint.Name || !hint.RunTimeTicks) { + throw new Error( + 'Unable to construct playlist search hint, required properties were undefined', + ); + } + return new AlbumSearchHint(hint.Id, hint.Name, hint.RunTimeTicks / 10000); } @@ -18,14 +24,13 @@ export class AlbumSearchHint extends SearchHint { searchService: JellyfinSearchService, ): Promise { const remoteImages = await searchService.getRemoteImageById(this.id); - const albumItems = await searchService.getAlbumItems(this.id); const tracks = await Promise.all( albumItems.map(async (x) => (await x.toTracks(searchService)).find((x) => x !== null), ), ); - return tracks.map((track): Track => { + return tracks.map((track: Track): Track => { track.remoteImages = remoteImages; return track; }); diff --git a/src/models/search/PlaylistSearchHint.ts b/src/models/search/PlaylistSearchHint.ts index dd308c4..b1099f9 100644 --- a/src/models/search/PlaylistSearchHint.ts +++ b/src/models/search/PlaylistSearchHint.ts @@ -4,6 +4,7 @@ import { Track } from '../shared/Track'; import { JellyfinSearchService } from '../../clients/jellyfin/jellyfin.search.service'; import { SearchHint } from './SearchHint'; +import { convertToTracks } from 'src/utils/trackConverter'; export class PlaylistSearchHint extends SearchHint { override toString(): string { @@ -11,6 +12,12 @@ export class PlaylistSearchHint extends SearchHint { } static constructFromHint(hint: JellyfinSearchHint) { + if (hint.Id === undefined || !hint.Name || !hint.RunTimeTicks) { + throw new Error( + 'Unable to construct playlist search hint, required properties were undefined', + ); + } + return new PlaylistSearchHint( hint.Id, hint.Name, @@ -22,9 +29,6 @@ export class PlaylistSearchHint extends SearchHint { searchService: JellyfinSearchService, ): Promise { const playlistItems = await searchService.getPlaylistitems(this.id); - const tracks = playlistItems.map(async (x) => - (await x.toTracks(searchService)).find((x) => x !== null), - ); - return await Promise.all(tracks); + return convertToTracks(playlistItems, searchService); } } diff --git a/src/models/search/SearchHint.ts b/src/models/search/SearchHint.ts index 016acc4..09a382e 100644 --- a/src/models/search/SearchHint.ts +++ b/src/models/search/SearchHint.ts @@ -2,6 +2,7 @@ import { BaseItemDto, SearchHint as JellyfinSearchHint, } from '@jellyfin/sdk/lib/generated-client/models'; +import { z } from 'zod'; import { JellyfinSearchService } from '../../clients/jellyfin/jellyfin.search.service'; import { Track } from '../shared/Track'; @@ -26,10 +27,35 @@ export class SearchHint { } static constructFromHint(hint: JellyfinSearchHint) { - return new SearchHint(hint.Id, hint.Name, hint.RunTimeTicks / 10000); + const schema = z.object({ + Id: z.string(), + Name: z.string(), + RunTimeTicks: z.number(), + }); + + const result = schema.safeParse(hint); + + if (!result.success) { + throw new Error( + `Unable to construct search hint, required properties were undefined: ${JSON.stringify( + hint, + )}`, + ); + } + + return new SearchHint( + result.data.Id, + result.data.Name, + result.data.RunTimeTicks / 10000, + ); } static constructFromBaseItem(baseItem: BaseItemDto) { + if (baseItem.Id === undefined || !baseItem.Name || !baseItem.RunTimeTicks) { + throw new Error( + 'Unable to construct search hint from base item, required properties were undefined', + ); + } return new SearchHint( baseItem.Id, baseItem.Name, diff --git a/src/models/shared/Playlist.ts b/src/models/shared/Playlist.ts index f96ebea..c93bf5d 100644 --- a/src/models/shared/Playlist.ts +++ b/src/models/shared/Playlist.ts @@ -1,4 +1,4 @@ -import { EventEmitter2 } from '@nestjs/event-emitter'; +import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'; import { Track } from './Track'; @@ -24,7 +24,7 @@ export class Playlist { * @returns active track or undefined if there's none */ getActiveTrack(): Track | undefined { - if (this.isActiveTrackOutOfSync()) { + if (this.isActiveTrackOutOfSync() || this.activeTrackIndex === undefined) { return undefined; } return this.tracks[this.activeTrackIndex]; @@ -49,7 +49,12 @@ export class Playlist { * @returns if the track has been changed successfully */ setNextTrackAsActiveTrack(): boolean { - if (this.activeTrackIndex >= this.tracks.length) { + this.announceTrackFinishIfSet(); + + if ( + this.activeTrackIndex === undefined || + this.activeTrackIndex >= this.tracks.length + ) { return false; } @@ -66,7 +71,9 @@ export class Playlist { * @returns if the track has been changed successfully */ setPreviousTrackAsActiveTrack(): boolean { - if (this.activeTrackIndex <= 0) { + this.announceTrackFinishIfSet(); + + if (this.activeTrackIndex === undefined || this.activeTrackIndex <= 0) { return false; } @@ -88,12 +95,21 @@ export class Playlist { return 0; } + const previousTrackLength = this.tracks.length; + this.eventEmitter.emit('controls.playlist.tracks.enqueued', { count: tracks.length, activeTrack: this.activeTrackIndex, }); const length = this.tracks.push(...tracks); + // existing tracks are in the playlist, but none are playing. play the first track out of the new tracks + if (!this.hasAnyPlaying() && tracks.length > 0) { + this.activeTrackIndex = previousTrackLength; + this.announceTrackChange(); + return length; + } + // emit a track change if there is no item if (this.activeTrackIndex === undefined) { this.announceTrackChange(); @@ -107,7 +123,7 @@ export class Playlist { * @returns if there is a track next in the playlist */ hasNextTrackInPlaylist() { - return this.activeTrackIndex + 1 < this.tracks.length; + return (this.activeTrackIndex ?? 0) + 1 < this.tracks.length; } /** @@ -115,7 +131,7 @@ export class Playlist { * @returns if there is a previous track in the playlist */ hasPreviousTrackInPlaylist() { - return this.activeTrackIndex > 0; + return this.activeTrackIndex !== undefined && this.activeTrackIndex > 0; } clear() { @@ -124,17 +140,39 @@ export class Playlist { this.activeTrackIndex = undefined; } + private hasAnyPlaying() { + return this.tracks.some((track) => track.playing); + } + + private announceTrackFinishIfSet() { + if (this.activeTrackIndex === undefined) { + return; + } + + const currentTrack = this.getActiveTrack(); + this.eventEmitter.emit('internal.audio.track.finish', currentTrack); + } + private announceTrackChange() { if (!this.activeTrackIndex) { this.activeTrackIndex = 0; } - this.eventEmitter.emit('internal.audio.announce', this.getActiveTrack()); + const activeTrack = this.getActiveTrack(); + + if (!activeTrack) { + return; + } + + activeTrack.playing = true; + this.eventEmitter.emit('internal.audio.track.announce', activeTrack); } private isActiveTrackOutOfSync(): boolean { return ( - this.activeTrackIndex < 0 || this.activeTrackIndex >= this.tracks.length + this.activeTrackIndex === undefined || + this.activeTrackIndex < 0 || + this.activeTrackIndex >= this.tracks.length ); } } diff --git a/src/models/shared/Track.ts b/src/models/shared/Track.ts index b93875f..c3cfd8d 100644 --- a/src/models/shared/Track.ts +++ b/src/models/shared/Track.ts @@ -27,6 +27,10 @@ export class Track { */ remoteImages?: RemoteImageResult; + playing: boolean; + + playbackProgress: number; + constructor( id: string, name: string, @@ -37,6 +41,8 @@ export class Track { this.name = name; this.duration = duration; this.remoteImages = remoteImages; + this.playing = false; + this.playbackProgress = 0; } getDuration() { @@ -48,6 +54,14 @@ export class Track { } getRemoteImages(): RemoteImageInfo[] { - return this.remoteImages.Images; + return this.remoteImages?.Images ?? []; + } + + getPlaybackProgress() { + return this.playbackProgress; + } + + updatePlaybackProgress(progress: number) { + this.playbackProgress = progress; } } diff --git a/src/playback/playback.service.ts b/src/playback/playback.service.ts index 067ecf9..df96f2d 100644 --- a/src/playback/playback.service.ts +++ b/src/playback/playback.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger } from '@nestjs/common'; -import { EventEmitter2 } from '@nestjs/event-emitter'; +import { EventEmitter2, OnEvent } from '@nestjs/event-emitter'; import { Playlist } from '../models/shared/Playlist'; @@ -18,4 +18,14 @@ export class PlaybackService { this.playlist = new Playlist(this.eventEmitter); return this.playlist; } + + @OnEvent('internal.audio.track.previous') + private handlePreviousTrackEvent() { + this.getPlaylistOrDefault().setPreviousTrackAsActiveTrack(); + } + + @OnEvent('internal.audio.track.next') + private handleNextTrackEvent() { + this.getPlaylistOrDefault().setNextTrackAsActiveTrack(); + } } diff --git a/src/types/websocket.ts b/src/types/websocket.ts index ee4aa8f..43c4edf 100644 --- a/src/types/websocket.ts +++ b/src/types/websocket.ts @@ -30,7 +30,7 @@ export class PlayNowCommand { } getSelection(): string[] { - if (this.hasSelection()) { + if (this.hasSelection() && this.StartIndex !== undefined) { return [this.ItemIds[this.StartIndex]]; } diff --git a/src/updates/updates.service.spec.ts b/src/updates/updates.service.spec.ts index 911aa04..a72d899 100644 --- a/src/updates/updates.service.spec.ts +++ b/src/updates/updates.service.spec.ts @@ -6,6 +6,7 @@ import { DiscordMessageService } from '../clients/discord/discord.message.servic import { GithubRelease } from '../models/github-release'; import { useDefaultMockerToken } from '../utils/tests/defaultMockerToken'; import { UpdatesService } from './updates.service'; +import { InjectionToken } from '@nestjs/common'; // mock axios: https://stackoverflow.com/questions/51275434/type-of-axios-mock-using-jest-typescript/55351900#55351900 jest.mock('axios'); @@ -33,7 +34,7 @@ describe('UpdatesService', () => { } as DiscordMessageService; } - if (token === Client || token == '__inject_discord_client__') { + if (token === Client || token === '__inject_discord_client__') { return { guilds: { cache: [ @@ -49,7 +50,7 @@ describe('UpdatesService', () => { }; } - return useDefaultMockerToken(token); + return useDefaultMockerToken(token as InjectionToken); }) .compile(); diff --git a/src/updates/updates.service.ts b/src/updates/updates.service.ts index cdc3a88..6c90b3e 100644 --- a/src/updates/updates.service.ts +++ b/src/updates/updates.service.ts @@ -30,6 +30,14 @@ export class UpdatesService { this.logger.debug('Checking for available updates...'); const latestGitHubRelease = await this.fetchLatestGithubRelease(); + + if (!latestGitHubRelease) { + this.logger.warn( + `Aborting update check because api request failed. Please check your internet connection or disable the check`, + ); + return; + } + const currentVersion = Constants.Metadata.Version.All(); if (latestGitHubRelease.tag_name <= currentVersion) { @@ -95,21 +103,21 @@ export class UpdatesService { }); } - private async fetchLatestGithubRelease(): Promise { + private async fetchLatestGithubRelease(): Promise { return axios({ method: 'GET', url: Constants.Links.Api.GetLatestRelease, }) .then((response) => { if (response.status !== 200) { - return null; + return undefined; } return response.data as GithubRelease; }) .catch((err) => { this.logger.error('Error while checking for updates', err); - return null; + return undefined; }); } } diff --git a/src/utils/stringUtils/stringUtils.ts b/src/utils/stringUtils/stringUtils.ts index b9637a3..18fb29e 100644 --- a/src/utils/stringUtils/stringUtils.ts +++ b/src/utils/stringUtils/stringUtils.ts @@ -11,3 +11,6 @@ export const trimStringToFixedLength = (value: string, maxLength: number) => { return value.substring(0, upperBound) + '...'; }; + +export const zeroPad = (num: number, places: number) => + String(num).padStart(places, '0'); diff --git a/src/utils/trackConverter.ts b/src/utils/trackConverter.ts new file mode 100644 index 0000000..4754a96 --- /dev/null +++ b/src/utils/trackConverter.ts @@ -0,0 +1,15 @@ +import { JellyfinSearchService } from 'src/clients/jellyfin/jellyfin.search.service'; +import { SearchHint } from 'src/models/search/SearchHint'; +import { Track } from 'src/models/shared/Track'; + +export const convertToTracks = ( + hints: SearchHint[], + jellyfinSearchService: JellyfinSearchService, +): Track[] => { + let tracks: Track[] = []; + hints.forEach(async (hint) => { + const searchedTracks = await hint.toTracks(jellyfinSearchService); + tracks = [...tracks, ...searchedTracks]; + }); + return tracks; +}; diff --git a/tsconfig.json b/tsconfig.json index efc026f..1e9efef 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,7 @@ "baseUrl": "./", "incremental": true, "skipLibCheck": true, - "strictNullChecks": false, + "strictNullChecks": true, "noImplicitAny": false, "strictBindCallApply": false, "forceConsistentCasingInFileNames": false, diff --git a/yarn.lock b/yarn.lock index c7826fd..823b661 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,17 +10,6 @@ "@jridgewell/gen-mapping" "^0.1.0" "@jridgewell/trace-mapping" "^0.3.9" -"@angular-devkit/core@15.0.4": - version "15.0.4" - resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-15.0.4.tgz#257ba1d76cd106216d0150f480d0062e726af996" - integrity sha512-4ITpRAevd652SxB+qNesIQ9qfbm7wT5UBU5kJOPPwGL77I21g8CQpkmV1n5VSacPvC9Zbz90feOWexf7w7JzcA== - dependencies: - ajv "8.11.0" - ajv-formats "2.1.1" - jsonc-parser "3.2.0" - rxjs "6.6.7" - source-map "0.7.4" - "@angular-devkit/core@15.2.4": version "15.2.4" resolved "https://registry.yarnpkg.com/@angular-devkit/core/-/core-15.2.4.tgz#f7696f09c66d01568a07f0e71672e887fdf57280" @@ -44,17 +33,6 @@ symbol-observable "4.0.0" yargs-parser "21.1.1" -"@angular-devkit/schematics@15.0.4": - version "15.0.4" - resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-15.0.4.tgz#64de42f9100d7080bc3c59bb06d1e4f6f15a088e" - integrity sha512-/gXiLFS0+xFdx6wPoBpe/c6/K9I5edMpaASqPf4XheKtrsSvL+qTlIi3nsbfItzOiDXbaBmlbxGfkMHz/yg0Ig== - dependencies: - "@angular-devkit/core" "15.0.4" - jsonc-parser "3.2.0" - magic-string "0.26.7" - ora "5.4.1" - rxjs "6.6.7" - "@angular-devkit/schematics@15.2.4": version "15.2.4" resolved "https://registry.yarnpkg.com/@angular-devkit/schematics/-/schematics-15.2.4.tgz#85129ebabcdb362f4b65a6e290bb2ae846f3d64c" @@ -66,56 +44,56 @@ ora "5.4.1" rxjs "6.6.7" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.18.6", "@babel/code-frame@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.21.4.tgz#d0fa9e4413aca81f2b23b9442797bda1826edb39" + integrity sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g== dependencies: "@babel/highlight" "^7.18.6" -"@babel/compat-data@^7.20.5": - version "7.21.0" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.0.tgz#c241dc454e5b5917e40d37e525e2f4530c399298" - integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== +"@babel/compat-data@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.21.4.tgz#457ffe647c480dff59c2be092fc3acf71195c87f" + integrity sha512-/DYyDpeCfaVinT40FPGdkkb+lYSKvsVuMjDAG7jPOWWiM1ibOaB9CXJAlc4d1QpP/U2q2P9jbrSlClKSErd55g== "@babel/core@^7.11.6", "@babel/core@^7.12.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.3.tgz#cf1c877284a469da5d1ce1d1e53665253fae712e" - integrity sha512-qIJONzoa/qiHghnm0l1n4i/6IIziDpzqc36FBs4pzMhDUraHqponwJLiAKm1hGLP3OSB/TVNz6rMwVGpwxxySw== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.21.4.tgz#c6dc73242507b8e2a27fd13a9c1814f9fa34a659" + integrity sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.21.3" - "@babel/helper-compilation-targets" "^7.20.7" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" + "@babel/helper-compilation-targets" "^7.21.4" "@babel/helper-module-transforms" "^7.21.2" "@babel/helpers" "^7.21.0" - "@babel/parser" "^7.21.3" + "@babel/parser" "^7.21.4" "@babel/template" "^7.20.7" - "@babel/traverse" "^7.21.3" - "@babel/types" "^7.21.3" + "@babel/traverse" "^7.21.4" + "@babel/types" "^7.21.4" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" json5 "^2.2.2" semver "^6.3.0" -"@babel/generator@^7.21.3", "@babel/generator@^7.7.2": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.3.tgz#232359d0874b392df04045d72ce2fd9bb5045fce" - integrity sha512-QS3iR1GYC/YGUnW7IdggFeN5c1poPUurnGttOV/bZgPGV+izC/D8HnD6DLwod0fsatNyVn1G3EVWMYIF0nHbeA== +"@babel/generator@^7.21.4", "@babel/generator@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.21.4.tgz#64a94b7448989f421f919d5239ef553b37bb26bc" + integrity sha512-NieM3pVIYW2SwGzKoqfPrQsf4xGs9M9AIG3ThppsSRmO+m7eQhmI6amajKMUeIO37wFfsvnvcxQFx6x6iqxDnA== dependencies: - "@babel/types" "^7.21.3" + "@babel/types" "^7.21.4" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" -"@babel/helper-compilation-targets@^7.20.7": - version "7.20.7" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.20.7.tgz#a6cd33e93629f5eb473b021aac05df62c4cd09bb" - integrity sha512-4tGORmfQcrc+bvrjb5y3dG9Mx1IOZjsHqQVUz7XCNHO+iTmqxWnVg3KRygjGmpRLJGdQSKuvFinbIb0CnZwHAQ== +"@babel/helper-compilation-targets@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.4.tgz#770cd1ce0889097ceacb99418ee6934ef0572656" + integrity sha512-Fa0tTuOXZ1iL8IeDFUWCzjZcn+sJGd9RZdH9esYVjEejGmzf+FFYQpMi/kZUk2kPy/q1H3/GPw7np8qar/stfg== dependencies: - "@babel/compat-data" "^7.20.5" - "@babel/helper-validator-option" "^7.18.6" + "@babel/compat-data" "^7.21.4" + "@babel/helper-validator-option" "^7.21.0" browserslist "^4.21.3" lru-cache "^5.1.1" semver "^6.3.0" @@ -141,11 +119,11 @@ "@babel/types" "^7.18.6" "@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz#ac88b2f76093637489e718a90cec6cf8a9b029af" + integrity sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg== dependencies: - "@babel/types" "^7.18.6" + "@babel/types" "^7.21.4" "@babel/helper-module-transforms@^7.21.2": version "7.21.2" @@ -161,7 +139,7 @@ "@babel/traverse" "^7.21.2" "@babel/types" "^7.21.2" -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.19.0", "@babel/helper-plugin-utils@^7.8.0": +"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.20.2", "@babel/helper-plugin-utils@^7.8.0": version "7.20.2" resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.20.2.tgz#d1b9000752b18d0877cff85a5c376ce5c3121629" integrity sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ== @@ -190,7 +168,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz#7eea834cf32901ffdc1a7ee555e2f9c27e249ca2" integrity sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w== -"@babel/helper-validator-option@^7.18.6": +"@babel/helper-validator-option@^7.21.0": version "7.21.0" resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz#8224c7e13ace4bafdc4004da2cf064ef42673180" integrity sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ== @@ -213,10 +191,10 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.3.tgz#1d285d67a19162ff9daa358d4cb41d50c06220b3" - integrity sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.21.4": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.21.4.tgz#94003fdfc520bbe2875d4ae557b43ddb6d880f17" + integrity sha512-alVJj7k7zIxqBZ7BTRhz0IqJFxW1VJbm6N8JbcYhQ186df9ZBPbZBmWSqAMXwHGsCJdYks7z/voa3ibiS5bCIw== "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" @@ -303,11 +281,11 @@ "@babel/helper-plugin-utils" "^7.14.5" "@babel/plugin-syntax-typescript@^7.7.2": - version "7.20.0" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.20.0.tgz#4e9a0cfc769c85689b77a2e642d24e9f697fc8c7" - integrity sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ== + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.21.4.tgz#2751948e9b7c6d771a8efa59340c15d4a2891ff8" + integrity sha512-xz0D39NvhQn4t4RNsHmDnnsaQizIlUkdtYvLs8La1BlfjQ6JEwxkJGeqJMW2tAXx+q6H+WFuUTXNdYVpEya0YA== dependencies: - "@babel/helper-plugin-utils" "^7.19.0" + "@babel/helper-plugin-utils" "^7.20.2" "@babel/template@^7.20.7", "@babel/template@^7.3.3": version "7.20.7" @@ -318,26 +296,26 @@ "@babel/parser" "^7.20.7" "@babel/types" "^7.20.7" -"@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.3", "@babel/traverse@^7.7.2": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.3.tgz#4747c5e7903d224be71f90788b06798331896f67" - integrity sha512-XLyopNeaTancVitYZe2MlUEvgKb6YVVPXzofHgqHijCImG33b/uTurMS488ht/Hbsb2XK3U2BnSTxKVNGV3nGQ== +"@babel/traverse@^7.21.0", "@babel/traverse@^7.21.2", "@babel/traverse@^7.21.4", "@babel/traverse@^7.7.2": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.21.4.tgz#a836aca7b116634e97a6ed99976236b3282c9d36" + integrity sha512-eyKrRHKdyZxqDm+fV1iqL9UAHMoIg0nDaGqfIOd8rKH17m5snv7Gn4qgjBoFfLz9APvjFU/ICT00NVCv1Epp8Q== dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.21.3" + "@babel/code-frame" "^7.21.4" + "@babel/generator" "^7.21.4" "@babel/helper-environment-visitor" "^7.18.9" "@babel/helper-function-name" "^7.21.0" "@babel/helper-hoist-variables" "^7.18.6" "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.21.3" - "@babel/types" "^7.21.3" + "@babel/parser" "^7.21.4" + "@babel/types" "^7.21.4" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.3", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.21.3" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.3.tgz#4865a5357ce40f64e3400b0f3b737dc6d4f64d05" - integrity sha512-sBGdETxC+/M4o/zKC0sl6sjWv62WFR/uzxrJ6uYyMLZOUlPnwzw0tKgVHOXxaAd5l2g8pEDM5RZ495GPQI77kg== +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.20.2", "@babel/types@^7.20.7", "@babel/types@^7.21.0", "@babel/types@^7.21.2", "@babel/types@^7.21.4", "@babel/types@^7.3.0", "@babel/types@^7.3.3": + version "7.21.4" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.21.4.tgz#2d5d6bb7908699b3b416409ffd3b5daa25b030d4" + integrity sha512-rU2oY501qDxE8Pyo7i/Orqma4ziCOrby0/9mvbDUGEfvZjb279Nk9k19e2fiCxHbRRpY2ZyrgW1eq22mvmOIzA== dependencies: "@babel/helper-string-parser" "^7.19.4" "@babel/helper-validator-identifier" "^7.19.1" @@ -376,30 +354,30 @@ dependencies: class-transformer "0.5.1" -"@discordjs/builders@^1.5.0": - version "1.5.0" - resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.5.0.tgz#f6dd4684e46707eb600eabdfdacd3b44c9e924cd" - integrity sha512-7XxT78mnNBPigHn2y6KAXkicxIBFtZREGWaRZ249EC1l6gBUEP8IyVY5JTciIjJArxkF+tg675aZvsTNTKBpmA== +"@discordjs/builders@^1.6.0": + version "1.6.1" + resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.6.1.tgz#5b1447cfa493bc1306671ef18ce3aae13c0af0ba" + integrity sha512-CCcLwn/8ANhlAbhlE18fcaN0hfXTen53/JiwZs1t9oE/Cqa9maA8ZRarkCIsXF4J7J/MYnd0J6IsxeKsq+f6mw== dependencies: - "@discordjs/formatters" "^0.2.0" + "@discordjs/formatters" "^0.3.0" "@discordjs/util" "^0.2.0" "@sapphire/shapeshift" "^3.8.1" - discord-api-types "^0.37.35" + discord-api-types "^0.37.37" fast-deep-equal "^3.1.3" ts-mixer "^6.0.3" tslib "^2.5.0" -"@discordjs/collection@^1.4.0": - version "1.4.0" - resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.4.0.tgz#6b5d5429db0691a3f5a962c21f6bc7859eef3333" - integrity sha512-hiOJyk2CPFf1+FL3a4VKCuu1f448LlROVuu8nLz1+jCOAPokUcdFAV+l4pd3B3h6uJlJQSASoZzrdyNdjdtfzQ== +"@discordjs/collection@^1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.5.0.tgz#478acd5d510cb5996c5101f47b24959ac7499cc2" + integrity sha512-suyVndkEAAWrGxyw/CPGdtXoRRU6AUNkibtnbJevQzpelkJh3Q1gQqWDpqf5i39CnAn5+LrN0YS+cULeEjq2Yw== -"@discordjs/formatters@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@discordjs/formatters/-/formatters-0.2.0.tgz#a861d9c385dfc6c7294e44c5441beee933820a4f" - integrity sha512-vn4oMSXuMZUm8ITqVOtvE7/fMMISj4cI5oLsR09PEQXHKeKDAMLltG/DWeeIs7Idfy6V8Fk3rn1e69h7NfzuNA== +"@discordjs/formatters@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@discordjs/formatters/-/formatters-0.3.0.tgz#8313d158c5e974597eec43b1f381d870a507d133" + integrity sha512-Fc4MomalbP8HMKEMor3qUiboAKDtR7PSBoPjwm7WYghVRwgJlj5WYvUsriLsxeKk8+Qq2oy+HJlGTUkGvX0YnA== dependencies: - discord-api-types "^0.37.35" + discord-api-types "^0.37.37" "@discordjs/node-pre-gyp@^0.4.5": version "0.4.5" @@ -424,56 +402,56 @@ "@discordjs/node-pre-gyp" "^0.4.5" node-addon-api "^5.0.0" -"@discordjs/rest@^1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-1.6.0.tgz#d77c9b5533f6d2079468d4fd497d3fabeb529c20" - integrity sha512-HGvqNCZ5Z5j0tQHjmT1lFvE5ETO4hvomJ1r0cbnpC1zM23XhCpZ9wgTCiEmaxKz05cyf2CI9p39+9LL+6Yz1bA== +"@discordjs/rest@^1.7.0": + version "1.7.0" + resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-1.7.0.tgz#c61fcd14e810b44e4821df5dfb5e74fa5fcb6e5d" + integrity sha512-r2HzmznRIo8IDGYBWqQfkEaGN1LrFfWQd3dSyC4tOpMU8nuVvFUEw6V/lwnG44jyOq+vgyDny2fxeUDMt9I4aQ== dependencies: - "@discordjs/collection" "^1.4.0" + "@discordjs/collection" "^1.5.0" "@discordjs/util" "^0.2.0" "@sapphire/async-queue" "^1.5.0" "@sapphire/snowflake" "^3.4.0" - discord-api-types "^0.37.35" + discord-api-types "^0.37.37" file-type "^18.2.1" tslib "^2.5.0" - undici "^5.20.0" + undici "^5.21.0" "@discordjs/util@^0.2.0": version "0.2.0" resolved "https://registry.yarnpkg.com/@discordjs/util/-/util-0.2.0.tgz#91b590dae3934ffa5fe34530afc5212c569d6751" integrity sha512-/8qNbebFzLWKOOg+UV+RB8itp4SmU5jw0tBUD3ifElW6rYNOj1Ku5JaSW7lLl/WgjjxF01l/1uQPCzkwr110vg== -"@discordjs/voice@^0.15.0": - version "0.15.0" - resolved "https://registry.yarnpkg.com/@discordjs/voice/-/voice-0.15.0.tgz#774febb2a1623b23e234744fefb60544858db77d" - integrity sha512-YEvrRchDhjB0QbI9QYOF/qgDwvGb9sNGUyks5d3Srl+VRoMoKkMzWY+wcEfVbAgdMIAdLi5vyrTKP/gLND26jA== +"@discordjs/voice@^0.16.0": + version "0.16.0" + resolved "https://registry.yarnpkg.com/@discordjs/voice/-/voice-0.16.0.tgz#9df1e492c8fea95113236a3de3ac52702c587729" + integrity sha512-ToGCvHD1cBscuW3p+C7zOF5+L7MJmU4GjdOARfNk9mkHyFFZq4grK+Sxr3QXKbp27DtfDBc9uqD4GUOYgxngfA== dependencies: "@types/ws" "^8.5.4" - discord-api-types "^0.37.35" + discord-api-types "^0.37.37" prism-media "^1.3.5" tslib "^2.5.0" - ws "^8.12.1" + ws "^8.13.0" "@eslint-community/eslint-utils@^4.2.0": - version "4.2.0" - resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.2.0.tgz#a831e6e468b4b2b5ae42bf658bea015bf10bc518" - integrity sha512-gB8T4H4DEfX2IV9zGDJPOBgP1e/DbfCPDTtEqUMckpvzS1OYtva8JdFYBqMwYk7xAQ429WGF/UPqn8uQ//h2vQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz#a23514e8fb9af1269d5f7788aa556798d61c6b59" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== dependencies: eslint-visitor-keys "^3.3.0" "@eslint-community/regexpp@^4.4.0": - version "4.4.0" - resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.4.0.tgz#3e61c564fcd6b921cb789838631c5ee44df09403" - integrity sha512-A9983Q0LnDGdLPjxyXQ00sbV+K+O+ko2Dr+CZigbHWtX9pNfxlaBkMR8X1CztI73zuEyEBXTVjx7CE+/VSwDiQ== + version "4.5.0" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.5.0.tgz#f6f729b02feee2c749f57e334b7a1b5f40a81724" + integrity sha512-vITaYzIcNmjn5tF5uxcZ/ft7/RXGrMUIS9HalWckEOF6ESiwXKoMzAQf2UW0aVd6rnOeExTJVd5hmWXucBKGXQ== -"@eslint/eslintrc@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.1.tgz#7888fe7ec8f21bc26d646dbd2c11cd776e21192d" - integrity sha512-eFRmABvW2E5Ho6f5fHLqgena46rOj7r7OKHYfLElqcBfGFHHpjBhivyi5+jOEQuSpdc/1phIZJlbC2te+tZNIw== +"@eslint/eslintrc@^2.0.2": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.0.2.tgz#01575e38707add677cf73ca1589abba8da899a02" + integrity sha512-3W4f5tDUra+pA+FzgugqL2pRimUTDJWKr7BINqOpkZrC0uYI0NIc0/JFgBROCU07HR6GieA5m3/rsPIhDmCXTQ== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.5.0" + espree "^9.5.1" globals "^13.19.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -481,10 +459,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.36.0": - version "8.36.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.36.0.tgz#9837f768c03a1e4a30bd304a64fb8844f0e72efe" - integrity sha512-lxJ9R5ygVm8ZWgYdUweoq5ownDlJ4upvoWmO4eLxBYHdMo+vZ/Rx0EN6MbKWDJOSUGrqJy2Gt+Dyv/VKml0fjg== +"@eslint/js@8.38.0": + version "8.38.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.38.0.tgz#73a8a0d8aa8a8e6fe270431c5e72ae91b5337892" + integrity sha512-IoD2MfUnOV58ghIHCiil01PcohxjbYR/qCxsoC+xNgUwh1EY8jOOrYmu3d3a71+tJJ23uscEV4X2HJWMsPJu4g== "@hapi/hoek@^9.0.0": version "9.3.0" @@ -533,13 +511,13 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@jellyfin/sdk@^0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@jellyfin/sdk/-/sdk-0.7.0.tgz#34bcbe53acc089caa9f458e866ed5745726f5397" - integrity sha512-GNoGv+2qY+xK7WpO7sUUNpZvzgN7RwXMyOhIy9mE/LdDSr6bqZHwrzT1Pv0+vUW7Epw67bwIMWuYivyBYejEHw== +"@jellyfin/sdk@^0.8.1": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@jellyfin/sdk/-/sdk-0.8.1.tgz#56491d671219bddf37d1d86cee7d7c7843d17386" + integrity sha512-DiyaUax4i2upDz4JS/PpWKyBnFcOoUyRc63GysUvqPWeOT9yPfnRsNFkmTzUV2oRTFIeO++od/INq5DfeUbs8g== dependencies: - axios "0.27.2" - compare-versions "5.0.1" + axios "1.3.4" + compare-versions "5.0.3" "@jest/console@^28.1.3": version "28.1.3" @@ -791,9 +769,9 @@ "@jridgewell/sourcemap-codec" "1.4.14" "@lukeed/csprng@^1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@lukeed/csprng/-/csprng-1.0.1.tgz#625e93a0edb2c830e3c52ce2d67b9d53377c6a66" - integrity sha512-uSvJdwQU5nK+Vdf6zxcWAY2A8r7uqe+gePwLWzJ+fsQehq18pc0I2hJKwypZ2aLM90+Er9u1xn4iLJPZ+xlL4g== + version "1.1.0" + resolved "https://registry.yarnpkg.com/@lukeed/csprng/-/csprng-1.1.0.tgz#1e3e4bd05c1cc7a0b2ddbd8a03f39f6e4b5e6cfe" + integrity sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA== "@nestjs/cli@^9.3.0": version "9.3.0" @@ -823,12 +801,12 @@ webpack "5.76.2" webpack-node-externals "3.0.0" -"@nestjs/common@^9.3.12": - version "9.3.12" - resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-9.3.12.tgz#9b3a9e162fe73c830ffa588fd3d921fd8a9f174a" - integrity sha512-NtrUG2VgCbhmZEO1yRt/Utq16uFRV+xeHAOtdYIsfHGG0ssAV2lVLlvFFAQYh0SQ+KuYY1Gsxd3GK2JFoJCNqQ== +"@nestjs/common@^9.4.0": + version "9.4.0" + resolved "https://registry.yarnpkg.com/@nestjs/common/-/common-9.4.0.tgz#3597e4f3a1278486fc2e015c94e58bcbbb4f72ca" + integrity sha512-RUcVAQsEF4WPrmzFXEOUfZnPwrLTe1UVlzXTlSyfqfqbdWDPKDGlIPVelBLfc5/+RRUQ0I5iE4+CQvpCmkqldw== dependencies: - uid "2.0.1" + uid "2.0.2" iterare "1.2.1" tslib "2.5.0" @@ -842,12 +820,12 @@ lodash "4.17.21" uuid "9.0.0" -"@nestjs/core@^9.3.12": - version "9.3.12" - resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-9.3.12.tgz#b1c06b56ff5c8ad41e841688c6763aa9748a2395" - integrity sha512-Qe0ZjJo7bOlfudn7KHLppYrt5i4k1nR1+9d5ppYat2bb5knCIT4kIqblj666n+22/2zvsHRiTo015cLyLKsLRQ== +"@nestjs/core@^9.4.0": + version "9.4.0" + resolved "https://registry.yarnpkg.com/@nestjs/core/-/core-9.4.0.tgz#bca5128138fcf9b4668bc524b578f3805a325183" + integrity sha512-yTLryCgFD0462wPe4HIzhyTcDgibt8Stfwb5YzcX7Ma0NM4m8uBIpcPG109KBubp8ZmV85e5mw4rl20qLQQVsQ== dependencies: - uid "2.0.1" + uid "2.0.2" "@nuxtjs/opencollective" "0.3.2" fast-safe-stringify "2.1.1" iterare "1.2.1" @@ -878,21 +856,20 @@ tslib "2.5.0" "@nestjs/schedule@^2.1.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@nestjs/schedule/-/schedule-2.2.0.tgz#6e6e55648d9fa03dfc861354d00da2985161753b" - integrity sha512-wrDnUONTxBkD6lTWh9ecYk/kvJTbA3PylotjBoRsECmcS1SNvgInFXuL38UnHiFnXM3CHSFnzRLB259Bc1mOdQ== + version "2.2.1" + resolved "https://registry.yarnpkg.com/@nestjs/schedule/-/schedule-2.2.1.tgz#404e79133e50e4b70dec3b7c194d6b796485cb13" + integrity sha512-7jev9Q3aFnRajKAi/At+9rzwflZNN10SA5PcdCvxc35GFfTdM2a6O5GA7tiIbLuOOzdjPYPbC3RxP4tpXOHVWw== dependencies: - cron "2.2.0" + cron "2.3.0" uuid "9.0.0" -"@nestjs/schematics@^9.0.0", "@nestjs/schematics@^9.0.4": - version "9.0.4" - resolved "https://registry.yarnpkg.com/@nestjs/schematics/-/schematics-9.0.4.tgz#ab612f5a8e006ca1d617eddc8143ee00b766312b" - integrity sha512-egurCfAc4e5i1r2TmeAF0UrOKejFmT5oTdv4b7HcOVPupc3QGU7CbEfGleL3mkM5AjrixTQeMxU9bJ00ttAbGg== +"@nestjs/schematics@^9.0.4", "@nestjs/schematics@^9.1.0": + version "9.1.0" + resolved "https://registry.yarnpkg.com/@nestjs/schematics/-/schematics-9.1.0.tgz#8afc4b1e7c7988c18d3ab44cffe56773b7507272" + integrity sha512-/7CyMTnPJSK9/xD9CkCqwuHPOlHVlLC2RDnbdCJ7mIO07SdbBbY14msTqtYW9VRQtsjZPLh1GTChf7ryJUImwA== dependencies: - "@angular-devkit/core" "15.0.4" - "@angular-devkit/schematics" "15.0.4" - fs-extra "11.1.0" + "@angular-devkit/core" "15.2.4" + "@angular-devkit/schematics" "15.2.4" jsonc-parser "3.2.0" pluralize "8.0.0" @@ -903,18 +880,18 @@ dependencies: path-to-regexp "0.2.5" -"@nestjs/terminus@^9.1.4": - version "9.2.1" - resolved "https://registry.yarnpkg.com/@nestjs/terminus/-/terminus-9.2.1.tgz#f173ba807bbab6ed2ee892e859455553274a725e" - integrity sha512-bPJsxKzqLl1BIs1YFIji20h42VG4ElGqc+lyw7nW+as0DkfjpRYUdyEBQJo6dTAcqRrVxSN2m3wKweBknK3Nxw== +"@nestjs/terminus@^9.2.2": + version "9.2.2" + resolved "https://registry.yarnpkg.com/@nestjs/terminus/-/terminus-9.2.2.tgz#88c78061307eaf19308b1f6a6055aa6c33750195" + integrity sha512-AWUA8XLcgxWUjUFYHDqi42M7CZn2e+DEWxP+MqNAbMzz4ybB5jGcFK5Fy8qwaNBoWg6KMF1JiXOOygGXgk9ydg== dependencies: boxen "5.1.2" check-disk-space "3.3.1" -"@nestjs/testing@^9.3.12": - version "9.3.12" - resolved "https://registry.yarnpkg.com/@nestjs/testing/-/testing-9.3.12.tgz#e02ed34c9a267ba9495ba7f5e3d83d4956e3414f" - integrity sha512-nH274IXEqU4hr4bcb71POe58hYLONt9RcfKKM5ZvOS7wYMnybMpKKR8DkC1WcfE1P2k2GQmQoHeSH5emPtYrBA== +"@nestjs/testing@^9.4.0": + version "9.4.0" + resolved "https://registry.yarnpkg.com/@nestjs/testing/-/testing-9.4.0.tgz#1e5d1e799413e996c9c2da02a89dfefa62c3b70e" + integrity sha512-xZWp363P4otcebg++gSjUcdCfTK0RorORzyFq3aLaSAQOlq8kxfFDRIKzEATR4aOUfqTMMsAA8lhnMJWf35N6A== dependencies: tslib "2.5.0" @@ -954,9 +931,9 @@ integrity sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA== "@sapphire/shapeshift@^3.8.1": - version "3.8.1" - resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.8.1.tgz#b98dc6a7180f9b38219267917b2e6fa33f9ec656" - integrity sha512-xG1oXXBhCjPKbxrRTlox9ddaZTvVpOhYLmKmApD/vIWOV1xEYXnpoFs68zHIZBGbqztq6FrUPNPerIrO1Hqeaw== + version "3.8.2" + resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.8.2.tgz#f9f25cba74c710b56f8790de76a9642a9635e7db" + integrity sha512-NXpnJAsxN3/h9TqQPntOeVWZrpIuucqXI3IWF6tj2fWCoRLCuVK5wx7Dtg7pRrtkYfsMUbDqgKoX26vrC5iYfA== dependencies: fast-deep-equal "^3.1.3" lodash "^4.17.21" @@ -1097,9 +1074,9 @@ "@types/estree" "*" "@types/eslint@*": - version "8.21.2" - resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.21.2.tgz#2b61b43a8b0e66006856a2a4c8e51f6f773ead27" - integrity sha512-EMpxUyystd3uZVByZap1DACsMXvb82ypQnGn89e1Y0a+LYu3JJscUd/gqhRsVFDkaD2MIiWo0MT8EfXr3DGRKw== + version "8.37.0" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.37.0.tgz#29cebc6c2a3ac7fea7113207bf5a828fdf4d7ef1" + integrity sha512-Piet7dG2JBuDIfohBngQ3rCt7MgO9xCO4xIMKxBThCq5PNRB91IjlJ10eJVwfoNtvTErmxLzwBZ7rHZtbOMmFQ== dependencies: "@types/estree" "*" "@types/json-schema" "*" @@ -1173,24 +1150,19 @@ integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== "@types/luxon@*": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.2.0.tgz#99901b4ab29a5fdffc88fff59b3b47fbfbe0557b" - integrity sha512-lGmaGFoaXHuOLXFvuju2bfvZRqxAqkHPx9Y9IQdQABrinJJshJwfNCKV+u7rR3kJbiqfTF/NhOkcxxAFrObyaA== + version "3.3.0" + resolved "https://registry.yarnpkg.com/@types/luxon/-/luxon-3.3.0.tgz#a61043a62c0a72696c73a0a305c544c96501e006" + integrity sha512-uKRI5QORDnrGFYgcdAVnHvEIvEZ8noTpP/Bg+HeUzZghwinDlIS87DEenV5r1YoOF9G4x600YsUXLWZ19rmTmg== "@types/mime@*": version "3.0.1" resolved "https://registry.yarnpkg.com/@types/mime/-/mime-3.0.1.tgz#5f8f2bca0a5863cb69bc0b0acd88c96cb1d4ae10" integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== -"@types/node@*": - version "18.15.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.3.tgz#f0b991c32cfc6a4e7f3399d6cb4b8cf9a0315014" - integrity sha512-p6ua9zBxz5otCmbpb5D3U4B5Nanw6Pk3PPyX05xnxbB/fRv71N7CPmORg7uAD5P70T0xmx1pzAx/FUfa5X+3cw== - -"@types/node@^18.15.10": - version "18.15.10" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.10.tgz#4ee2171c3306a185d1208dad5f44dae3dee4cfe3" - integrity sha512-9avDaQJczATcXgfmMAW3MIWArOO7A+m90vuCFLr8AotWf8igO/mRoYukrk2cqZVtv38tHs33retzHEilM7FpeQ== +"@types/node@*", "@types/node@^18.15.11": + version "18.15.11" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" + integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== "@types/parse-json@^4.0.0": version "4.0.0" @@ -1263,21 +1235,21 @@ integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== "@types/yargs@^17.0.8": - version "17.0.22" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.22.tgz#7dd37697691b5f17d020f3c63e7a45971ff71e9a" - integrity sha512-pet5WJ9U8yPVRhkwuEIp5ktAeAqRZOq4UdAyWLWzxbtpyXnzbtLdKiXAjJzi/KLmPGS9wk86lUFWZFN6sISo4g== + version "17.0.24" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.24.tgz#b3ef8d50ad4aa6aecf6ddc97c580a00f5aa11902" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.56.0.tgz#e4fbb4d6dd8dab3e733485c1a44a02189ae75364" - integrity sha512-ZNW37Ccl3oMZkzxrYDUX4o7cnuPgU+YrcaYXzsRtLB16I1FR5SHMqga3zGsaSliZADCWo2v8qHWqAYIj8nWCCg== +"@typescript-eslint/eslint-plugin@^5.57.0": + version "5.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.57.1.tgz#d1ab162a3cd2671b8a1c9ddf6e2db73b14439735" + integrity sha512-1MeobQkQ9tztuleT3v72XmY0XuKXVXusAhryoLuU5YZ+mXoYKZP9SQ7Flulh1NX4DTjpGTc2b/eMu4u7M7dhnQ== dependencies: "@eslint-community/regexpp" "^4.4.0" - "@typescript-eslint/scope-manager" "5.56.0" - "@typescript-eslint/type-utils" "5.56.0" - "@typescript-eslint/utils" "5.56.0" + "@typescript-eslint/scope-manager" "5.57.1" + "@typescript-eslint/type-utils" "5.57.1" + "@typescript-eslint/utils" "5.57.1" debug "^4.3.4" grapheme-splitter "^1.0.4" ignore "^5.2.0" @@ -1285,72 +1257,72 @@ semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/parser@^5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.56.0.tgz#42eafb44b639ef1dbd54a3dbe628c446ca753ea6" - integrity sha512-sn1OZmBxUsgxMmR8a8U5QM/Wl+tyqlH//jTqCg8daTAmhAk26L2PFhcqPLlYBhYUJMZJK276qLXlHN3a83o2cg== +"@typescript-eslint/parser@^5.57.0": + version "5.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.57.1.tgz#af911234bd4401d09668c5faf708a0570a17a748" + integrity sha512-hlA0BLeVSA/wBPKdPGxoVr9Pp6GutGoY380FEhbVi0Ph4WNe8kLvqIRx76RSQt1lynZKfrXKs0/XeEk4zZycuA== dependencies: - "@typescript-eslint/scope-manager" "5.56.0" - "@typescript-eslint/types" "5.56.0" - "@typescript-eslint/typescript-estree" "5.56.0" + "@typescript-eslint/scope-manager" "5.57.1" + "@typescript-eslint/types" "5.57.1" + "@typescript-eslint/typescript-estree" "5.57.1" debug "^4.3.4" -"@typescript-eslint/scope-manager@5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.56.0.tgz#62b4055088903b5254fa20403010e1c16d6ab725" - integrity sha512-jGYKyt+iBakD0SA5Ww8vFqGpoV2asSjwt60Gl6YcO8ksQ8s2HlUEyHBMSa38bdLopYqGf7EYQMUIGdT/Luw+sw== +"@typescript-eslint/scope-manager@5.57.1": + version "5.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.57.1.tgz#5d28799c0fc8b501a29ba1749d827800ef22d710" + integrity sha512-N/RrBwEUKMIYxSKl0oDK5sFVHd6VI7p9K5MyUlVYAY6dyNb/wHUqndkTd3XhpGlXgnQsBkRZuu4f9kAHghvgPw== dependencies: - "@typescript-eslint/types" "5.56.0" - "@typescript-eslint/visitor-keys" "5.56.0" + "@typescript-eslint/types" "5.57.1" + "@typescript-eslint/visitor-keys" "5.57.1" -"@typescript-eslint/type-utils@5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.56.0.tgz#e6f004a072f09c42e263dc50e98c70b41a509685" - integrity sha512-8WxgOgJjWRy6m4xg9KoSHPzBNZeQbGlQOH7l2QEhQID/+YseaFxg5J/DLwWSsi9Axj4e/cCiKx7PVzOq38tY4A== +"@typescript-eslint/type-utils@5.57.1": + version "5.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.57.1.tgz#235daba621d3f882b8488040597b33777c74bbe9" + integrity sha512-/RIPQyx60Pt6ga86hKXesXkJ2WOS4UemFrmmq/7eOyiYjYv/MUSHPlkhU6k9T9W1ytnTJueqASW+wOmW4KrViw== dependencies: - "@typescript-eslint/typescript-estree" "5.56.0" - "@typescript-eslint/utils" "5.56.0" + "@typescript-eslint/typescript-estree" "5.57.1" + "@typescript-eslint/utils" "5.57.1" debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.56.0.tgz#b03f0bfd6fa2afff4e67c5795930aff398cbd834" - integrity sha512-JyAzbTJcIyhuUhogmiu+t79AkdnqgPUEsxMTMc/dCZczGMJQh1MK2wgrju++yMN6AWroVAy2jxyPcPr3SWCq5w== +"@typescript-eslint/types@5.57.1": + version "5.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.57.1.tgz#d9989c7a9025897ea6f0550b7036027f69e8a603" + integrity sha512-bSs4LOgyV3bJ08F5RDqO2KXqg3WAdwHCu06zOqcQ6vqbTJizyBhuh1o1ImC69X4bV2g1OJxbH71PJqiO7Y1RuA== -"@typescript-eslint/typescript-estree@5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.56.0.tgz#48342aa2344649a03321e74cab9ccecb9af086c3" - integrity sha512-41CH/GncsLXOJi0jb74SnC7jVPWeVJ0pxQj8bOjH1h2O26jXN3YHKDT1ejkVz5YeTEQPeLCCRY0U2r68tfNOcg== +"@typescript-eslint/typescript-estree@5.57.1": + version "5.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.57.1.tgz#10d9643e503afc1ca4f5553d9bbe672ea4050b71" + integrity sha512-A2MZqD8gNT0qHKbk2wRspg7cHbCDCk2tcqt6ScCFLr5Ru8cn+TCfM786DjPhqwseiS+PrYwcXht5ztpEQ6TFTw== dependencies: - "@typescript-eslint/types" "5.56.0" - "@typescript-eslint/visitor-keys" "5.56.0" + "@typescript-eslint/types" "5.57.1" + "@typescript-eslint/visitor-keys" "5.57.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/utils@5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.56.0.tgz#db64705409b9a15546053fb4deb2888b37df1f41" - integrity sha512-XhZDVdLnUJNtbzaJeDSCIYaM+Tgr59gZGbFuELgF7m0IY03PlciidS7UQNKLE0+WpUTn1GlycEr6Ivb/afjbhA== +"@typescript-eslint/utils@5.57.1": + version "5.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.57.1.tgz#0f97b0bbd88c2d5e2036869f26466be5f4c69475" + integrity sha512-kN6vzzf9NkEtawECqze6v99LtmDiUJCVpvieTFA1uL7/jDghiJGubGZ5csicYHU1Xoqb3oH/R5cN5df6W41Nfg== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@types/json-schema" "^7.0.9" "@types/semver" "^7.3.12" - "@typescript-eslint/scope-manager" "5.56.0" - "@typescript-eslint/types" "5.56.0" - "@typescript-eslint/typescript-estree" "5.56.0" + "@typescript-eslint/scope-manager" "5.57.1" + "@typescript-eslint/types" "5.57.1" + "@typescript-eslint/typescript-estree" "5.57.1" eslint-scope "^5.1.1" semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.56.0": - version "5.56.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.56.0.tgz#f19eb297d972417eb13cb69b35b3213e13cc214f" - integrity sha512-1mFdED7u5bZpX6Xxf5N9U2c18sb+8EvU3tyOIj6LQZ5OOvnmj8BVeNNP603OFPm5KkS1a7IvCIcwrdHXaEMG/Q== +"@typescript-eslint/visitor-keys@5.57.1": + version "5.57.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.57.1.tgz#585e5fa42a9bbcd9065f334fd7c8a4ddfa7d905e" + integrity sha512-RjQrAniDU0CEk5r7iphkm731zKlFiUjvcBS2yHAg8WWqFMCaCrD0rKEVOMUyMMcbGPZ0bPp56srkGWrgfZqLRA== dependencies: - "@typescript-eslint/types" "5.56.0" + "@typescript-eslint/types" "5.57.1" eslint-visitor-keys "^3.3.0" "@webassemblyjs/ast@1.11.1": @@ -1536,16 +1508,6 @@ ajv-keywords@^3.5.2: resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== -ajv@8.11.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f" - integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg== - dependencies: - fast-deep-equal "^3.1.1" - json-schema-traverse "^1.0.0" - require-from-string "^2.0.2" - uri-js "^4.2.2" - ajv@8.12.0, ajv@^8.0.0: version "8.12.0" resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.12.0.tgz#d1a0527323e22f53562c567c00991577dfbe19d1" @@ -1672,13 +1634,14 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -axios@0.27.2: - version "0.27.2" - resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" - integrity sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ== +axios@1.3.4: + version "1.3.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.3.4.tgz#f5760cefd9cfb51fd2481acf88c05f67c4523024" + integrity sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ== dependencies: - follow-redirects "^1.14.9" + follow-redirects "^1.15.0" form-data "^4.0.0" + proxy-from-env "^1.1.0" babel-jest@^28.1.3: version "28.1.3" @@ -1909,9 +1872,9 @@ camelcase@^6.2.0: integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== caniuse-lite@^1.0.30001449: - version "1.0.30001466" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001466.tgz#c1e6197c540392e09709ecaa9e3e403428c53375" - integrity sha512-ewtFBSfWjEmxUgNBSZItFSmVtvk9zkwkl1OfRZlKA8slltRN+/C/tuGVrF9styXkN36Yu3+SeJ1qkXxDEyNZ5w== + version "1.0.30001474" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001474.tgz#13b6fe301a831fe666cce8ca4ef89352334133d5" + integrity sha512-iaIZ8gVrWfemh5DG3T9/YqarVZoYf0r188IjaGwx68j4Pf0SGY6CQkmJUIE+NZHkkecQGohzXmBGEwWDr9aM3Q== chalk@4.1.2, chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" @@ -2095,10 +2058,10 @@ commander@^2.20.0: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -compare-versions@5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-5.0.1.tgz#14c6008436d994c3787aba38d4087fabe858555e" - integrity sha512-v8Au3l0b+Nwkp4G142JcgJFh1/TUhdxut7wzD1Nq1dyp5oa3tXaqb03EXOAB6jS4gMlalkjAUPZBMiAfKUixHQ== +compare-versions@5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/compare-versions/-/compare-versions-5.0.3.tgz#a9b34fea217472650ef4a2651d905f42c28ebfd7" + integrity sha512-4UZlZP8Z99MGEY+Ovg/uJxJuvoXuN4M6B3hKaiackiHrgzQFEe3diJi1mf1PNHbFujM7FvLrK2bpgIaImbtZ1A== component-emitter@^1.3.0: version "1.3.0" @@ -2191,10 +2154,10 @@ create-require@^1.1.0: resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== -cron@2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/cron/-/cron-2.2.0.tgz#605ae5048e9715a22db12e9fe2563a92740b9fed" - integrity sha512-GPiI3OgMv83XRtEUc2gUdaLvJhO3XbLN288layOBkDTupg0RK5IECNGpkykIMHg+muVR2bxt29b0xvCAcBrjYQ== +cron@2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/cron/-/cron-2.3.0.tgz#20df6da18d4f7d2f8937def2eb5fc0d1a320c526" + integrity sha512-ZN5HP8zDY41sJolMsbc+GksRATcbvkPKF5wR/qc8FrV4NBVi9ORQa1HmYa5GydaysUB80X9XpRlRkooa5uEtTA== dependencies: luxon "^3.2.1" @@ -2237,9 +2200,9 @@ deep-is@^0.1.3: integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== deepmerge@^4.2.2: - version "4.3.0" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.0.tgz#65491893ec47756d44719ae520e0e2609233b59b" - integrity sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og== + version "4.3.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.3.1.tgz#44b5f2147cd3b00d4b56137685966f26fd25dd4a" + integrity sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A== defaults@^1.0.3: version "1.0.4" @@ -2303,29 +2266,29 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -discord-api-types@^0.37.35: - version "0.37.36" - resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.36.tgz#650a8f66dce2c5e54a8c2275db74a0bb7936430d" - integrity sha512-Nlxmp10UpVr/utgZ9uODQvG2Or+5w7LFrvFMswyeKC9l/+UaqGT6H0OVgEFhu9GEO4U6K7NNO5W8Carv7irnCA== +discord-api-types@^0.37.37: + version "0.37.37" + resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.37.tgz#49bc42a36124c85f06297f1548f130329ed5aeb0" + integrity sha512-LDMBKzl/zbvHO/yCzno5hevuA6lFIXJwdKSJZQrB+1ToDpFfN9thK+xxgZNR4aVkI7GHRDja0p4Sl2oYVPnHYg== -discord.js@^14.8.0: - version "14.8.0" - resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.8.0.tgz#0e5def8a95a22018844cdfc0f63b9806392da79b" - integrity sha512-UOxYtc/YnV7jAJ2gISluJyYeBw4e+j8gWn+IoqG8unaHAVuvZ13DdYN0M1f9fbUgUvSarV798inIrYFtDNDjwQ== +discord.js@^14.9.0: + version "14.9.0" + resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.9.0.tgz#61e26c4a7a27f91fd669b4c46892868420a5be43" + integrity sha512-ygGms5xP4hG+QrrY9k7d/OYCzMltSMtdl/2Snzq/nLCiZo+Sna91Ulv9l0+B5Jd/Czcq37B7wJAnmja7GOa+bg== dependencies: - "@discordjs/builders" "^1.5.0" - "@discordjs/collection" "^1.4.0" - "@discordjs/formatters" "^0.2.0" - "@discordjs/rest" "^1.6.0" + "@discordjs/builders" "^1.6.0" + "@discordjs/collection" "^1.5.0" + "@discordjs/formatters" "^0.3.0" + "@discordjs/rest" "^1.7.0" "@discordjs/util" "^0.2.0" "@sapphire/snowflake" "^3.4.0" "@types/ws" "^8.5.4" - discord-api-types "^0.37.35" + discord-api-types "^0.37.37" fast-deep-equal "^3.1.3" lodash.snakecase "^4.1.1" tslib "^2.5.0" - undici "^5.20.0" - ws "^8.12.1" + undici "^5.21.0" + ws "^8.13.0" doctrine@^3.0.0: version "3.0.0" @@ -2350,9 +2313,9 @@ ee-first@1.1.1: integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== electron-to-chromium@^1.4.284: - version "1.4.329" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.329.tgz#ea54b67c981c814f16917b585acf3a667d4c997f" - integrity sha512-dcwPzNUG4+reo5z+wHnrl2eZMu4kz+nLQEeepxLEDTLDC7Mi7AVTM4NXWct1TZyu3G4oQgygaAfbByaBtPqw2Q== + version "1.4.351" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.351.tgz#949144993f3ed5c18601e37c786bb72540dbf9e9" + integrity sha512-W35n4jAsyj6OZGxeWe+gA6+2Md4jDO19fzfsRKEt3DBwIdlVTT8O9Uv8ojgUAoQeXASdgG9zMU+8n8Xg/W6dRQ== emittery@^0.10.2: version "0.10.2" @@ -2449,20 +2412,20 @@ eslint-scope@^7.1.1: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.0.tgz#c7f0f956124ce677047ddbc192a68f999454dedc" + integrity sha512-HPpKPUBQcAsZOsHAFwTtIKcYlCje62XB7SEAcxjtmW6TD1WVpkS6i6/hOVtTZIl4zGj/mBqpFVGvaDneik+VoQ== -eslint@^8.35.0: - version "8.36.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.36.0.tgz#1bd72202200a5492f91803b113fb8a83b11285cf" - integrity sha512-Y956lmS7vDqomxlaaQAHVmeb4tNMp2FWIvU/RnU5BD3IKMD/MJPr76xdyr68P8tV1iNMvN2mRK0yy3c+UjL+bw== +eslint@^8.38.0: + version "8.38.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.38.0.tgz#a62c6f36e548a5574dd35728ac3c6209bd1e2f1a" + integrity sha512-pIdsD2jwlUGf/U38Jv97t8lq6HpaU/G9NKbYmpWpZGw3LdTNhZLbJePqxOXGB5+JEKfOPU/XLxYxFh03nr1KTg== dependencies: "@eslint-community/eslint-utils" "^4.2.0" "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.0.1" - "@eslint/js" "8.36.0" + "@eslint/eslintrc" "^2.0.2" + "@eslint/js" "8.38.0" "@humanwhocodes/config-array" "^0.11.8" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" @@ -2473,8 +2436,8 @@ eslint@^8.35.0: doctrine "^3.0.0" escape-string-regexp "^4.0.0" eslint-scope "^7.1.1" - eslint-visitor-keys "^3.3.0" - espree "^9.5.0" + eslint-visitor-keys "^3.4.0" + espree "^9.5.1" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -2500,14 +2463,14 @@ eslint@^8.35.0: strip-json-comments "^3.1.0" text-table "^0.2.0" -espree@^9.5.0: - version "9.5.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.0.tgz#3646d4e3f58907464edba852fa047e6a27bdf113" - integrity sha512-JPbJGhKc47++oo4JkEoTe2wjy4fmMwvFpgJT9cQzmfXKp22Dr6Hf1tdCteLz1h0P3t+mGvWZ+4Uankvh8+c6zw== +espree@^9.5.1: + version "9.5.1" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.5.1.tgz#4f26a4d5f18905bf4f2e0bd99002aab807e96dd4" + integrity sha512-5yxtHSZXRSW5pvv3hAlXM5+/Oswi1AUFqBmbibKb5s6bp3rGIDkyXU6xCoyuuLhijr4SFwPrXRoZjz0AZDN9tg== dependencies: acorn "^8.8.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" + eslint-visitor-keys "^3.4.0" esprima@^4.0.0: version "4.0.1" @@ -2772,7 +2735,7 @@ flatted@^3.1.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787" integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ== -follow-redirects@^1.14.9: +follow-redirects@^1.15.0: version "1.15.2" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13" integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA== @@ -2824,15 +2787,6 @@ fresh@0.5.2: resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q== -fs-extra@11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.1.0.tgz#5784b102104433bb0e090f48bfc4a30742c357ed" - integrity sha512-0rcTq621PD5jM/e0a3EJoGC/1TC5ZBCERW82LQuwfGnCa1V8w7dpYH1yNu+SLb6E5dkeCBzKEyLGlFrnr+dUyw== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs-extra@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.1.0.tgz#02873cfbc4084dde127eaa5f9905eef2325d1abf" @@ -2939,6 +2893,16 @@ glob-to-regexp@^0.4.1: resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== +glob@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.0.0.tgz#034ab2e93644ba702e769c3e0558143d3fbd1612" + integrity sha512-zmp9ZDC6NpDNLujV2W2n+3lH+BafIVZ4/ct+Yj3BMZTH/+bgm/eVjHzeFLwxJrrIGgjjS2eiQLlpurHsNlEAtQ== + dependencies: + fs.realpath "^1.0.0" + minimatch "^9.0.0" + minipass "^5.0.0" + path-scurry "^1.6.4" + glob@^7.0.0, glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -2952,12 +2916,12 @@ glob@^7.0.0, glob@^7.1.3, glob@^7.1.4: path-is-absolute "^1.0.0" glob@^9.2.0: - version "9.3.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.0.tgz#be6e50d172d025c3fcf87903ae25b36b787c0bb0" - integrity sha512-EAZejC7JvnQINayvB/7BJbpZpNOJ8Lrw2OZNEvQxe0vaLn1SuwMcfV7/MNaX8L/T0wmptBFI4YMtDvSBxYDc7w== + version "9.3.4" + resolved "https://registry.yarnpkg.com/glob/-/glob-9.3.4.tgz#e75dee24891a80c25cc7ee1dd327e126b98679af" + integrity sha512-qaSc49hojMOv1EPM4EuyITjDSgSKI0rthoHnvE81tcOi1SCVndHko7auqxdQ14eiQG2NDBJBE86+2xIrbIvrbA== dependencies: fs.realpath "^1.0.0" - minimatch "^7.4.1" + minimatch "^8.0.2" minipass "^4.2.4" path-scurry "^1.6.1" @@ -2986,9 +2950,9 @@ globby@^11.1.0: slash "^3.0.0" graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4, graceful-fs@^4.2.9: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== grapheme-splitter@^1.0.4: version "1.0.4" @@ -3666,9 +3630,9 @@ joi@^17.9.1: "@sideway/pinpoint" "^2.0.0" js-sdsl@^4.1.4: - version "4.3.0" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" - integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== + version "4.4.0" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.4.0.tgz#8b437dbe642daa95760400b602378ed8ffea8430" + integrity sha512-FfVSdx6pJ41Oa+CF7RDaFmTnCaFhua+SNYQX74riGOpl96x+2jQCqEfQ2bnXu/5DPCqlRuiqyvTJM0Qjz26IVg== js-tokens@^4.0.0: version "4.0.0" @@ -3753,9 +3717,9 @@ levn@^0.4.1: type-check "~0.4.0" libphonenumber-js@^1.10.14: - version "1.10.21" - resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.21.tgz#860312cbec1389e36e28389161025c7817a3ae32" - integrity sha512-/udZhx49av2r2gZR/+xXSrwcR8smX/sDNrVpOFrvW+CA26TfYTVZfwb3MIDvmwAYMLs7pXuJjZX0VxxGpqPhsA== + version "1.10.26" + resolved "https://registry.yarnpkg.com/libphonenumber-js/-/libphonenumber-js-1.10.26.tgz#3e6604357b3434b0005f85778b44153f4fadeecd" + integrity sha512-oB3l4J5gEhMV+ymmlIjWedsbCpsNRqbEZ/E/MpN2QVyinKNra6DcuXywxSk/72M3DZDoH/6kzurOq1erznBMwQ== libsodium-wrappers@^0.7.10: version "0.7.11" @@ -3840,6 +3804,11 @@ lru-cache@^7.14.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== +lru-cache@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.0.0.tgz#daece36a9fc332e93f8e75f3fcfd17900253567c" + integrity sha512-9AEKXzvOZc4BMacFnYiTOlDH/197LNnQIK9wZ6iMB5NXPzuv4bWR/Msv7iUMplkiMQ1qQL+KSv/JF1mZAB5Lrg== + luxon@^3.2.1: version "3.3.0" resolved "https://registry.yarnpkg.com/luxon/-/luxon-3.3.0.tgz#d73ab5b5d2b49a461c47cedbc7e73309b4805b48" @@ -3850,13 +3819,6 @@ macos-release@^2.5.0: resolved "https://registry.yarnpkg.com/macos-release/-/macos-release-2.5.1.tgz#bccac4a8f7b93163a8d163b8ebf385b3c5f55bf9" integrity sha512-DXqXhEM7gW59OjZO8NIjBCz9AQ1BEMrfiOAl4AYByHCtVHRF4KoGNO8mqQeM8lRCtQe/UnJ4imO/d2HdkKsd+A== -magic-string@0.26.7: - version "0.26.7" - resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.7.tgz#caf7daf61b34e9982f8228c4527474dac8981d6f" - integrity sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow== - dependencies: - sourcemap-codec "^1.4.8" - magic-string@0.29.0: version "0.29.0" resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.29.0.tgz#f034f79f8c43dba4ae1730ffb5e8c4e084b16cf3" @@ -3957,10 +3919,17 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimatch@^7.4.1: - version "7.4.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-7.4.2.tgz#157e847d79ca671054253b840656720cb733f10f" - integrity sha512-xy4q7wou3vUoC9k1xGTXc+awNdGaGVHtFUaey8tiX4H1QRc04DZ/rmDFwNm2EBsuYEhAZ6SgMmYf3InGY6OauA== +minimatch@^8.0.2: + version "8.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-8.0.3.tgz#0415cb9bb0c1d8ac758c8a673eb1d288e13f5e75" + integrity sha512-tEEvU9TkZgnFDCtpnrEYnPsjT7iUx42aXfs4bzmQ5sMA09/6hZY0jeZcGkXyDagiBOvkUjNo8Viom+Me6+2x7g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.0.tgz#bfc8e88a1c40ffd40c172ddac3decb8451503b56" + integrity sha512-0jJj8AvgKqWN05mrwuqi8QYKx1WmYSUoKSxu5Qhs9prezTz10sxAHGNZe9J9cqIJzta8DWsleh2KaVaLl6Ru2w== dependencies: brace-expansion "^2.0.1" @@ -3981,6 +3950,11 @@ minipass@^4.0.0, minipass@^4.0.2, minipass@^4.2.4: resolved "https://registry.yarnpkg.com/minipass/-/minipass-4.2.5.tgz#9e0e5256f1e3513f8c34691dd68549e85b2c8ceb" integrity sha512-+yQl7SX3bIT83Lhb4BVorMAHVuqsskxRdlmO9kTpyukp8vsm2Sn/fUOV9xlnG8/a5JsypJzap21lz/y3FBMJ8Q== +minipass@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" + integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== + minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -4269,13 +4243,21 @@ path-parse@^1.0.7: integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== path-scurry@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.1.tgz#dab45f7bb1d3f45a0e271ab258999f4ab7e23132" - integrity sha512-OW+5s+7cw6253Q4E+8qQ/u1fVvcJQCJo/VFD8pje+dbJCF1n5ZRMV2AEHbGp+5Q7jxQIYJxkHopnj6nzdGeZLA== + version "1.6.3" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.3.tgz#4eba7183d64ef88b63c7d330bddc3ba279dc6c40" + integrity sha512-RAmB+n30SlN+HnNx6EbcpoDy9nwdpcGPnEKrJnu6GZoDWBdIjo1UQMVtW2ybtC7LC2oKLcMq8y5g8WnKLiod9g== dependencies: lru-cache "^7.14.1" minipass "^4.0.2" +path-scurry@^1.6.4: + version "1.6.4" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.6.4.tgz#020a9449e5382a4acb684f9c7e1283bc5695de66" + integrity sha512-Qp/9IHkdNiXJ3/Kon++At2nVpnhRiPq/aSvQN+H3U1WZbvNRK0RIQK/o4HMqPoXjpuGJUEWpHSs6Mnjxqh3TQg== + dependencies: + lru-cache "^9.0.0" + minipass "^5.0.0" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -4340,10 +4322,10 @@ prettier-linter-helpers@^1.0.0: dependencies: fast-diff "^1.1.2" -prettier@^2.8.4: - version "2.8.4" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.4.tgz#34dd2595629bfbb79d344ac4a91ff948694463c3" - integrity sha512-vIS4Rlc2FNh0BySk3Wkd6xmwxB0FpOndW5fisM5H8hsZSxU2VWVB5CWIkIjWvrHjIhxk2g3bfMKM87zNTrZddw== +prettier@^2.8.7: + version "2.8.7" + resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.8.7.tgz#bb79fc8729308549d28fe3a98fce73d2c0656450" + integrity sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw== pretty-format@^28.0.0, pretty-format@^28.1.3: version "28.1.3" @@ -4381,6 +4363,11 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + pump@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" @@ -4566,12 +4553,12 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" -rimraf@^4.4.1: - version "4.4.1" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-4.4.1.tgz#bd33364f67021c5b79e93d7f4fa0568c7c21b755" - integrity sha512-Gk8NlF062+T9CqNGn6h4tls3k6T1+/nXdOcSZVikNVtlRdYpA7wRJJMoXmuvOnLW844rPjdQ7JgXCYM6PPC/og== +rimraf@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-5.0.0.tgz#5bda14e410d7e4dd522154891395802ce032c2cb" + integrity sha512-Jf9llaP+RvaEVS5nPShYFhtXIrb3LRKP281ib3So0KkeZKo2wIKyq0Re7TOSwanasA423PSr6CCIL4bP6T040g== dependencies: - glob "^9.2.0" + glob "^10.0.0" run-async@^2.4.0: version "2.4.1" @@ -4752,11 +4739,6 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== -sourcemap-codec@^1.4.8: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -4945,9 +4927,9 @@ terser-webpack-plugin@^5.1.3: terser "^5.16.5" terser@^5.16.5: - version "5.16.6" - resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.6.tgz#f6c7a14a378ee0630fbe3ac8d1f41b4681109533" - integrity sha512-IBZ+ZQIA9sMaXmRZCUMDjNH0D5AQQfdn4WUjHL0+1lF4TP1IHRJbrhb6fNaXWikrYQTSkb7SLxkeXAiy1p7mbg== + version "5.16.8" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.16.8.tgz#ccde583dabe71df3f4ed02b65eb6532e0fae15d5" + integrity sha512-QI5g1E/ef7d+PsDifb+a6nnVgC4F22Bg6T0xrBrz6iloVB4PUkkunp6V8nzoOOZJIzjWVdAGqCdlKlhLq/TbIA== dependencies: "@jridgewell/source-map" "^0.3.2" acorn "^8.5.0" @@ -5077,7 +5059,7 @@ tsconfig-paths-webpack-plugin@4.0.1: enhanced-resolve "^5.7.0" tsconfig-paths "^4.1.2" -tsconfig-paths@4.1.2, tsconfig-paths@^4.1.2: +tsconfig-paths@4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.1.2.tgz#4819f861eef82e6da52fb4af1e8c930a39ed979a" integrity sha512-uhxiMgnXQp1IR622dUXI+9Ehnws7i/y6xvpZB9IbUVOPy0muvdvgXeZOn88UcGPiT98Vp3rJPTa8bFoalZ3Qhw== @@ -5086,6 +5068,15 @@ tsconfig-paths@4.1.2, tsconfig-paths@^4.1.2: minimist "^1.2.6" strip-bom "^3.0.0" +tsconfig-paths@4.2.0, tsconfig-paths@^4.1.2: + version "4.2.0" + resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz#ef78e19039133446d244beac0fd6a1632e2d107c" + integrity sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg== + dependencies: + json5 "^2.2.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + tslib@2.5.0, tslib@^2.1.0, tslib@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" @@ -5150,7 +5141,14 @@ uid@2.0.1: dependencies: "@lukeed/csprng" "^1.0.0" -undici@^5.20.0: +uid@2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/uid/-/uid-2.0.2.tgz#4b5782abf0f2feeefc00fa88006b2b3b7af3e3b9" + integrity sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g== + dependencies: + "@lukeed/csprng" "^1.0.0" + +undici@^5.21.0: version "5.21.0" resolved "https://registry.yarnpkg.com/undici/-/undici-5.21.0.tgz#b00dfc381f202565ab7f52023222ab862bb2494f" integrity sha512-HOjK8l6a57b2ZGXOcUsI5NLfoTrfmbOl90ixJDl0AEFG4wgHNDQxtZy15/ZQp7HhjkpaGlp/eneMgtsu1dIlUA== @@ -5351,7 +5349,7 @@ write-file-atomic@^4.0.1: imurmurhash "^0.1.4" signal-exit "^3.0.7" -ws@^8.12.1, ws@^8.13.0: +ws@^8.13.0: version "8.13.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== @@ -5408,3 +5406,8 @@ yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== + +zod@^3.21.4: + version "3.21.4" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.21.4.tgz#10882231d992519f0a10b5dd58a38c9dabbb64db" + integrity sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==