diff --git a/.gitignore b/.gitignore index 326bcde..c2fb61f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,5 +4,9 @@ node_modules .idea/ +upload +docker-compose.yml .env + +coverage/ \ No newline at end of file diff --git a/lib/7dc6da2fbc3e960f6bf7aa15fa68a16b.png b/lib/7dc6da2fbc3e960f6bf7aa15fa68a16b.png new file mode 100644 index 0000000..09af143 Binary files /dev/null and b/lib/7dc6da2fbc3e960f6bf7aa15fa68a16b.png differ diff --git a/lib/application/use_cases/artist/getArtist.js b/lib/application/use_cases/artist/getArtist.js new file mode 100644 index 0000000..31eba13 --- /dev/null +++ b/lib/application/use_cases/artist/getArtist.js @@ -0,0 +1,145 @@ +const serializeArtiste = require("../../../interfaces/serializers/ArtistSerializer"); +const throwStatusCode = require("../utils/throwStatusCode"); +const UserPublic = require("../../../domain/model/UserPublic"); +const ArtistPage = require("../../../domain/model/ArtistPage.js"); +const serializeAlbum = require("../../../interfaces/serializers/AlbumSerializer"); +const reviewSerializer = require("../../../interfaces/serializers/ReviewSerializer.js"); +module.exports = async ( + artistId, + userToken, + { + accessTokenManager, + userRepository, + spotifyRepository, + reviewRepository, + followRepository, + friendRepository, + } +) => { + const id_utilisateur = accessTokenManager.decode(userToken)?.value; + const user = await userRepository.getByUser(id_utilisateur); + if (!user) + throwStatusCode(401, "votre token d'authentification n'est pas le bon"); + const artist = await spotifyRepository.getSpotifyArtist(artistId); + if (artist.error) throwStatusCode(artist.error.status, artist.error.message); + const doesUserFollow = await followRepository.doesFollows( + id_utilisateur, + artistId + ); + const albums = await spotifyRepository.getSpotifyArtistSongs( + artistId, + "album,single" + ); + + const album_ids = albums.items.map((album) => album.id); + const [ + follower_count, + friends_followers, + friend_follower_count, + reviewsByLike, + reviewsByTime, + reviewsByFriends, + ] = await Promise.all([ + followRepository.getFollowersCount(artistId), + followRepository.getFriendsFollowing(artistId, user.id_utilisateur, 3), + followRepository.getFriendsFollowingCount(artistId, user.id_utilisateur), + reviewRepository.getOeuvreReviews( + 1, + 3, + true, + false, + false, + album_ids, + user.id_utilisateur + ), + reviewRepository.getOeuvreReviews( + 1, + 3, + false, + false, + false, + album_ids, + user.id_utilisateur + ), + reviewRepository.getOeuvreReviews( + 1, + 3, + false, + false, + true, + album_ids, + user.id_utilisateur + ), + ]); + return await artistSerizilizer( + user.id_utilisateur, + doesUserFollow, + artist, + albums, + follower_count, + friends_followers, + friend_follower_count, + reviewsByLike, + reviewsByTime, + reviewsByFriends, + { reviewRepository, spotifyRepository, friendRepository } + ); +}; + +const artistSerizilizer = async ( + id_utilisateur, + doesUserFollow, + artist, + albums, + followers_count, + friends_followers, + friend_follower_count, + reviewsByLike, + reviewsByTime, + reviewsByFriends, + { reviewRepository, spotifyRepository, friendRepository } +) => { + artist.follower_count = followers_count; + artist = serializeArtiste(artist); + + albums = await Promise.all( + albums.items.map(async (item) => { + item.rating = await reviewRepository.getOeuvreRating(item.id); + item.reviewCount = await reviewRepository.getReviewCount(item.id); + return serializeAlbum(item); + }) + ); + + friends_followers = { + count: friend_follower_count, + users: friends_followers.map((item) => { + return new UserPublic(item); + }), + }; + const serializeReviews = (reviews) => { + return reviews.map(async (review) => { + return reviewSerializer( + review, + id_utilisateur, + undefined, + spotifyRepository, + reviewRepository, + friendRepository + ); + }); + }; + reviewsByLike = await Promise.all(serializeReviews(reviewsByLike)); + + reviewsByTime = await Promise.all(serializeReviews(reviewsByTime)); + + reviewsByFriends = await Promise.all(serializeReviews(reviewsByFriends)); + return new ArtistPage( + artist, + albums, + friends_followers, + reviewsByLike, + reviewsByTime, + reviewsByFriends, + doesUserFollow + ); +}; diff --git a/lib/application/use_cases/artist/getArtistFollowers.js b/lib/application/use_cases/artist/getArtistFollowers.js new file mode 100644 index 0000000..a38380a --- /dev/null +++ b/lib/application/use_cases/artist/getArtistFollowers.js @@ -0,0 +1,41 @@ +const throwStatusCode = require("../utils/throwStatusCode.js"); +const ArtistFollowersPage = require("../../../domain/model/ArtistFollowersPage.js"); +const UserPublic = require("../../../domain/model/UserPublic.js"); + +module.exports = async (artistId, userToken, {accessTokenManager,userRepository, spotifyRepository, followRepository, friendRepository}) => { + const id_utilisateur = accessTokenManager.decode(userToken)?.value + const user = await userRepository.getByUser(id_utilisateur) + if(!user) throwStatusCode(401,"votre token d'authentification n'est pas le bon") + + const artist = await spotifyRepository.getSpotifyArtist(artistId) + if(artist.error) + throwStatusCode(artist.error.status,artist.error.message) + + const followersFriendsList = await followRepository.getFriendsFollowing(artistId,id_utilisateur) + const followersWithoutFriends = await followRepository.getArtistFollowersWithoutFriends(artistId, id_utilisateur) + + const followersFriendsList2 = await Promise.all(followersFriendsList.map(async (item) => { + const areFriends = await friendRepository.areFriends(id_utilisateur, item.id_utilisateur); + const userPublicFriends = new UserPublic(item) + return { + ...userPublicFriends, + areFriends: areFriends + }; + })); + + const followersWithoutFriendst2 = await Promise.all(followersWithoutFriends.map(async (item) => { + let areFriends = await friendRepository.areFriends(id_utilisateur, item.id_utilisateur); + const userPublicNoFriends = new UserPublic(item) + if(id_utilisateur == item.id_utilisateur) { + areFriends = false + } + return { + ...userPublicNoFriends, + areFriends: areFriends + }; + })); + + const allFollowers = [...followersFriendsList2, ...followersWithoutFriendst2]; + + return new ArtistFollowersPage(artist, allFollowers) +} \ No newline at end of file diff --git a/lib/application/use_cases/comment/deleteComment.js b/lib/application/use_cases/comment/deleteComment.js new file mode 100644 index 0000000..5696644 --- /dev/null +++ b/lib/application/use_cases/comment/deleteComment.js @@ -0,0 +1,14 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +module.exports = async ( + idComment, + userToken, + { accessTokenManager, userRepository, commentRepository } +) => { + const id_utilisateur = accessTokenManager.decode(userToken)?.value; + if (!(await userRepository.getByUser(id_utilisateur))) + throwStatusCode(401, "votre token d'authentification n'est pas le bon"); + if (!(await commentRepository.canDelete(idComment, id_utilisateur))) + throwStatusCode(403, "ce n'est pas votre post"); + + await commentRepository.delete(idComment, id_utilisateur); +}; diff --git a/lib/application/use_cases/comment/getComment.js b/lib/application/use_cases/comment/getComment.js new file mode 100644 index 0000000..3080458 --- /dev/null +++ b/lib/application/use_cases/comment/getComment.js @@ -0,0 +1,142 @@ +const reviewSerializer = require("../../../interfaces/serializers/ReviewSerializer"); +const PublicComment = require("../../../domain/model/PublicComment"); +const UserPublic = require("../../../domain/model/UserPublic"); +const throwStatusCode = require("../utils/throwStatusCode"); + + +module.exports = async ( + id_com, + page, + pageSize, + orderByLike, + userToken, + { + reviewRepository, + commentRepository, + accessTokenManager, + userRepository, + friendRepository, + spotifyRepository, + } +) => { + const id_utilisateur = accessTokenManager.decode(userToken)?.value; + if (!(await userRepository.getByUser(id_utilisateur))) + throwStatusCode(401, "votre token d'authentification n'est pas le bon"); + const mainComment = await commentRepository.getById(id_com); + if (!mainComment) throwStatusCode(404, "commentaire introuvable"); + const commentsRaw = await commentRepository.getCommentsComments( + id_com, + id_utilisateur, + true, + page, + pageSize, + orderByLike + ); + const review = await reviewRepository.getById(mainComment.id_review); + let previousComments = []; + let nextCommentsId = mainComment.id_reponse; + while (nextCommentsId) { + previousComments.push(await commentRepository.getById(nextCommentsId)); + nextCommentsId = previousComments.at(-1).id_reponse; + } + return await serialize( + id_utilisateur, + mainComment, + previousComments, + commentsRaw, + review, + { spotifyRepository, friendRepository, commentRepository, reviewRepository } + ); +}; + +const serialize = async ( + id_utilisateur, + comment, + previousComments, + comments, + review, + { spotifyRepository, friendRepository, commentRepository, reviewRepository } +) => { + const serializeReview = async () => { + return !review.utilisateur.is_private || + friendRepository.areFriends( + id_utilisateur, + review.utilisateur.id_utilisateur + ) + ? reviewSerializer( + review, + id_utilisateur, + comments, + spotifyRepository, + reviewRepository, + friendRepository + ) + : { + private: true, + }; + }; + const serializeComment = async () => { + const doesUserLike = await commentRepository.doesUserLike( + id_utilisateur, + comment.id_com + ); + comment.doesUserLike = doesUserLike; + return !comment.utilisateur.is_private || + friendRepository.areFriends( + id_utilisateur, + comment.utilisateur.id_utilisateur + ) + ? new PublicComment(comment, new UserPublic(comment.utilisateur)) + : { + private: true, + }; + }; + const serializedPreviousComments = Promise.all( + previousComments.map(async (comment) => { + const doesUserLike = await commentRepository.doesUserLike( + id_utilisateur, + comment.id_com + ); + comment.doesUserLike = doesUserLike; + return !comment.utilisateur.is_private || + friendRepository.areFriends( + id_utilisateur, + comment.utilisateur.id_utilisateur + ) + ? new PublicComment(comment) + : { + private: true, + }; + }) + ); + const serializedComments = Promise.all( + comments.map(async (comment) => { + const doesUserLike = await commentRepository.doesUserLike( + id_utilisateur, + comment.id_com + ); + comment.doesUserLike = doesUserLike; + return !comment.utilisateur.is_private || + friendRepository.areFriends( + id_utilisateur, + comment.utilisateur.id_utilisateur + ) + ? new PublicComment(comment, new UserPublic(comment.utilisateur)) + : { + private: true, + }; + }) + ); + const promise = await Promise.all([ + serializeReview(), + serializeComment(), + serializedPreviousComments, + serializedComments, + ]); + return { + review: promise[0], + comment: promise[1], + previousComments: promise[2], + comments: promise[3], + }; +}; diff --git a/lib/application/use_cases/comment/likeComment.js b/lib/application/use_cases/comment/likeComment.js new file mode 100644 index 0000000..188bc7f --- /dev/null +++ b/lib/application/use_cases/comment/likeComment.js @@ -0,0 +1,11 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +module.exports = async (commentId,userToken,{accessTokenManager,userRepository,commentRepository}) =>{ + const id_utilisateur = accessTokenManager.decode(userToken)?.value + if(! await commentRepository.getById(commentId)) throwStatusCode(404,"commentaire introuvable") + if(! await userRepository.getByUser(id_utilisateur)) throwStatusCode(401,"votre token d'authentification n'est pas le bon") + if(! await commentRepository.doesUserLike(commentId,id_utilisateur)){ + await commentRepository.like(commentId,id_utilisateur,) + return true + } + await commentRepository.unlike(commentId,id_utilisateur) +} \ No newline at end of file diff --git a/lib/application/use_cases/comment/putComment.js b/lib/application/use_cases/comment/putComment.js new file mode 100644 index 0000000..838d54b --- /dev/null +++ b/lib/application/use_cases/comment/putComment.js @@ -0,0 +1,16 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +const PublicComment = require("../../../domain/model/PublicComment") +const UserPublic = require("../../../domain/model/UserPublic") +module.exports = async ( + commentId, + description, + userToken, + {friendRepository,accessTokenManager,commentRepository,userRepository}) => { + const id_utilisateur = accessTokenManager.decode(userToken)?.value + if(! await userRepository.getByUser(id_utilisateur)) throwStatusCode(401,"votre token d'authentification n'est pas le bon") + const comment = await commentRepository.getById(commentId) + if(comment.utilisateur.is_private && !(await friendRepository.areFriends(id_utilisateur,comment.utilisateur.id_utilisateur))) + throwStatusCode(403, "l'utilisateur est en privé") + const commentRaw = await commentRepository.persist(comment.id_review,description,id_utilisateur,commentId) + return new PublicComment(commentRaw,new UserPublic(comment.utilisateur)) +} \ No newline at end of file diff --git a/lib/application/use_cases/friend/acceptRequestUser.js b/lib/application/use_cases/friend/acceptRequestUser.js new file mode 100644 index 0000000..31ba4e2 --- /dev/null +++ b/lib/application/use_cases/friend/acceptRequestUser.js @@ -0,0 +1,23 @@ +'use strict'; + +const throwStatusCode = require("../utils/throwStatusCode") + +module.exports = async (token, amiIdUtilisateur, {accessTokenManager, userRepository, friendRepository}) => { + const id = accessTokenManager.decode(token)?.value + const user = await userRepository.getByUser(id); + if (!user) { + throwStatusCode(400,"Votre token d'authentification n'est pas le bon"); + } + const ami = await userRepository.getByUser(amiIdUtilisateur) + if(!ami) { + throwStatusCode(400,'id ami invalide') + } + + const friend = await friendRepository.accept(id, amiIdUtilisateur) + if(!friend) { + throwStatusCode(403,'Il existe aucune relation entre ces deux utilisateurs') + } + + return friend +} + diff --git a/lib/application/use_cases/friend/followUser.js b/lib/application/use_cases/friend/followUser.js new file mode 100644 index 0000000..bb569ed --- /dev/null +++ b/lib/application/use_cases/friend/followUser.js @@ -0,0 +1,100 @@ +"use strict"; + +const Friend = require("../../../domain/model/Friend"); +const throwStatusCode = require("../utils/throwStatusCode"); + +module.exports = async ( + token, + amiIdUtilisateur, + { accessTokenManager, friendRepository, userRepository, mailRepository } +) => { + const id = accessTokenManager.decode(token)?.value; + const user = await userRepository.getByUser(id); + if (!user) { + throwStatusCode(400, "Votre token d'authentification n'est pas le bon"); + } + const ami = await userRepository.getByUser(amiIdUtilisateur); + if (!ami) { + throwStatusCode(400, "id ami invalide"); + } + const followInfo = await friendRepository.getFollowInfo(id, amiIdUtilisateur); + console.log(followInfo); + if (followInfo.doesFollows) { + friendRepository.removeFriendById(id, amiIdUtilisateur); + return; + } + const userRaw = { + id_utilisateur: id, + amiIdUtilisateur: amiIdUtilisateur, + en_attente: ami.is_private, + createdAt: undefined, + updatedAt: undefined, + }; + + let friend = new Friend(userRaw); + friend = await friendRepository.persist(friend); + if (ami.is_private) { + const mailOptions = { + from: process.env.MAILER_EMAIL, + to: ami.email, + subject: "Nouvelle demande d'ami sur Solimbo", + html: ` + + + + + + Nouvelle demande d'ami sur Solimbo + + + +
+ + + +

Nouvelle demande d'ami sur Solimbo

+
+

Bonjour,

+

Vous avez reçu une nouvelle demande d'ami sur Solimbo de la part de ${user.pseudo}.

+

Connectez-vous à Solimbo pour accepter ou rejeter la demande.

+
+ Accepter la demande +
+ + + `, + }; + await mailRepository.send(mailOptions); + } + return friend; +}; diff --git a/lib/application/use_cases/friend/getListFriends.js b/lib/application/use_cases/friend/getListFriends.js new file mode 100644 index 0000000..6838eef --- /dev/null +++ b/lib/application/use_cases/friend/getListFriends.js @@ -0,0 +1,15 @@ +'use strict'; + +const throwStatusCode = require("../utils/throwStatusCode") + +module.exports = async (token, {accessTokenManager, userRepository, friendRepository}) => { + const id = accessTokenManager.decode(token)?.value + const user = await userRepository.getByUser(id); + if (!user) { + throwStatusCode(400,"Votre token d'authentification n'est pas le bon"); + } + + const friends = await friendRepository.getListFriendsById(id) + + return friends +} \ No newline at end of file diff --git a/lib/application/use_cases/friend/getListFriendsRequest.js b/lib/application/use_cases/friend/getListFriendsRequest.js new file mode 100644 index 0000000..9f6cd16 --- /dev/null +++ b/lib/application/use_cases/friend/getListFriendsRequest.js @@ -0,0 +1,22 @@ +'use strict'; + +const throwStatusCode = require("../utils/throwStatusCode") +const UserPublic = require("../../../domain/model/UserPublic.js"); +const ListsFriendsRequestsPage = require("../../../domain/model/ListsFriendsRequestsPage.js"); + + +module.exports = async (token, {accessTokenManager, userRepository, friendRepository}) => { + const id = accessTokenManager.decode(token)?.value + const user = await userRepository.getByUser(id); + if (!user) { + throwStatusCode(400,"Votre token d'authentification n'est pas le bon"); + } + + const usersRequestsReceived = await friendRepository.getRequestFriendsById(id) + const usersRequestsSend = await friendRepository.getSendRequestFriendsById(id) + + const usersPrivateRequestsReceived = await Promise.all(usersRequestsReceived.map(async (userPublic) => new UserPublic(userPublic))); + const usersPrivateRequestsSend = await Promise.all(usersRequestsSend.map(async (userPublic) => new UserPublic(userPublic))); + + return new ListsFriendsRequestsPage(usersPrivateRequestsReceived, usersPrivateRequestsSend) +} \ No newline at end of file diff --git a/lib/application/use_cases/friend/getProfilFriend.js b/lib/application/use_cases/friend/getProfilFriend.js new file mode 100644 index 0000000..7af357c --- /dev/null +++ b/lib/application/use_cases/friend/getProfilFriend.js @@ -0,0 +1,22 @@ +'use strict'; + +const throwStatusCode = require("../utils/throwStatusCode") + +module.exports = async (token, amiIdUtilisateur, {accessTokenManager, userRepository, friendRepository}) => { + const id = accessTokenManager.decode(token)?.value + const user = await userRepository.getByUser(id); + if (!user) { + throwStatusCode(400,"Votre token d'authentification n'est pas le bon"); + } + const ami = await userRepository.getByUser(amiIdUtilisateur) + if(!ami) { + throwStatusCode(400,'id ami invalide') + } + + const profil = await friendRepository.getById(id, amiIdUtilisateur) + if(!profil) { + throwStatusCode(403,'Il existe aucune relation entre ces deux utilisateurs') + } + + return ami +} \ No newline at end of file diff --git a/lib/application/use_cases/friend/unfollowUser.js b/lib/application/use_cases/friend/unfollowUser.js new file mode 100644 index 0000000..6ac7e66 --- /dev/null +++ b/lib/application/use_cases/friend/unfollowUser.js @@ -0,0 +1,20 @@ +'use strict'; + +const throwStatusCode = require("../utils/throwStatusCode") + +module.exports = async (token, amiIdUtilisateur, {accessTokenManager, userRepository, friendRepository}) => { + const id = accessTokenManager.decode(token)?.value + const user = await userRepository.getByUser(id); + if (!user) { + throwStatusCode(400,"Votre token d'authentification n'est pas le bon"); + } + const ami = await userRepository.getByUser(amiIdUtilisateur) + if(!ami) { + throwStatusCode(400,'id ami invalide') + } + const friend = await friendRepository.removeFriendById(id, amiIdUtilisateur) + if(!friend) { + throwStatusCode(403,'Il existe aucune relation entre ces deux utilisateurs') + } + return friend +} \ No newline at end of file diff --git a/lib/application/use_cases/oeuvre/getOeuvre.js b/lib/application/use_cases/oeuvre/getOeuvre.js new file mode 100644 index 0000000..2a1c07d --- /dev/null +++ b/lib/application/use_cases/oeuvre/getOeuvre.js @@ -0,0 +1,129 @@ +const throwStatusCode = require("../utils/throwStatusCode.js"); +const OeuvrePage = require("../../../domain/model/OeuvrePage.js"); +const reviewSerializer = require("../../../interfaces/serializers/ReviewSerializer.js"); +const fetchArtist = require("../spotify/FetchArtist.js"); +const getAlbum = require("../spotify/getAlbum.js"); +const getTrack = require("../spotify/getTrack.js"); + +module.exports = async ( + idOeuvre, + userToken, + { + accessTokenManager, + userRepository, + spotifyRepository, + reviewRepository, + likeOeuvreRepository, + oeuvreFavRepository, + friendRepository, + followRepository + } +) => { + const idUtilisateur = accessTokenManager.decode(userToken)?.value; + const user = await userRepository.getByUser(idUtilisateur); + if (!user) + throwStatusCode(401, "votre token d'authentification n'est pas le bon"); + + let oeuvre; + + try { + oeuvre = await getAlbum(idOeuvre, { spotifyRepository }); + } catch (errorAlbum) { + try { + oeuvre = await getTrack(idOeuvre, { spotifyRepository }); + } catch (errorTrack) { + throwStatusCode(404, "L'ID de l'oeuvre est introuvable"); + } + } + + const artistIds = oeuvre.artists.map((artist) => artist.id); + + const artists = await Promise.all( + artistIds.map(async (id) => { + const artist = await fetchArtist(id, { spotifyRepository }); + artist.doesUserFollow = await followRepository.doesFollows( + idUtilisateur, + id + ); + return artist; + }) + ); + + const doesUserLikes = await likeOeuvreRepository.doesUserLikes( + idUtilisateur, + idOeuvre + ); + const doesUserFav = await oeuvreFavRepository.oeuvreFavExists( + idUtilisateur, + idOeuvre + ); + + oeuvre.likeCount = await likeOeuvreRepository.getLikeCount(idOeuvre); + oeuvre.reviewCount = await reviewRepository.getReviewCount(idOeuvre); + oeuvre.rating = await reviewRepository.getOeuvreRating(idOeuvre); + + oeuvre?.tracks?.map(async (track) => { + track.likeCount = await likeOeuvreRepository.getLikeCount( track.id); + track.reviewCount = await reviewRepository.getReviewCount( track.id); + track.rating = await reviewRepository.getOeuvreRating( track.id); + track.doesUserLike = await likeOeuvreRepository.doesUserLikes( + idUtilisateur, + track.id + ); + return track; + }); + + const reviewsByLike = await reviewRepository.getOeuvreReviews( + 1, + 3, + true, + false, + false, + [idOeuvre], + idUtilisateur + ); + const reviewsByTime = await reviewRepository.getOeuvreReviews( + 1, + 3, + false, + false, + false, + [idOeuvre], + idUtilisateur + ); + + const reviewsByLikeSeri = await Promise.all( + reviewsByLike.map(async (review) => { + return reviewSerializer( + review, + idUtilisateur, + undefined, + spotifyRepository, + reviewRepository, + friendRepository + ); + }) + ); + + const reviewsByTimeSeri = await Promise.all( + reviewsByTime.map(async (review) => { + return reviewSerializer( + review, + idUtilisateur, + undefined, + spotifyRepository, + reviewRepository, + friendRepository + ); + }) + ); + + return new OeuvrePage( + oeuvre, + artists, + reviewsByLikeSeri, + reviewsByTimeSeri, + doesUserLikes, + doesUserFav + ); +}; diff --git a/lib/application/use_cases/oeuvre/likeOeuvre.js b/lib/application/use_cases/oeuvre/likeOeuvre.js new file mode 100644 index 0000000..2e12fed --- /dev/null +++ b/lib/application/use_cases/oeuvre/likeOeuvre.js @@ -0,0 +1,13 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +module.exports = async (userToken, artistId,type, {userRepository,likeOeuvreRepository,accessTokenManager,spotifyRepository}) =>{ + const id_utilisateur = accessTokenManager.decode(userToken)?.value + if(! await userRepository.getByUser(id_utilisateur)) throwStatusCode(401,"votre token d'authentification n'est pas le bon") + const oeuvre = spotifyRepository.getOeuvre(artistId,type) + if(oeuvre.error) throwStatusCode(oeuvre.error.status,oeuvre.error.message) + if(! await likeOeuvreRepository.doesUserLikes(id_utilisateur,artistId)) { + await likeOeuvreRepository.like(id_utilisateur,artistId) + return true + } + await likeOeuvreRepository.unlike(id_utilisateur,artistId) + return false +} \ No newline at end of file diff --git a/lib/application/use_cases/review/deleteReview.js b/lib/application/use_cases/review/deleteReview.js new file mode 100644 index 0000000..680b649 --- /dev/null +++ b/lib/application/use_cases/review/deleteReview.js @@ -0,0 +1,6 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +module.exports = async (idReview, userToken, {accessTokenManager, userRepository,reviewRepository})=> { + const id_utilisateur = accessTokenManager.decode(userToken)?.value + if(! await userRepository.getByUser(id_utilisateur)) throwStatusCode(401,"votre token d'authentification n'est pas le bon") + if(!await reviewRepository.delete(idReview,id_utilisateur)) throwStatusCode(403,"ce n'est pas votre post") +} \ No newline at end of file diff --git a/lib/application/use_cases/review/getArtistReviews.js b/lib/application/use_cases/review/getArtistReviews.js new file mode 100644 index 0000000..038bccb --- /dev/null +++ b/lib/application/use_cases/review/getArtistReviews.js @@ -0,0 +1,46 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +const reviewSerializer = require("../../../interfaces/serializers/ReviewSerializer"); +module.exports = async ( + artistId, + userToken, + page, + pageSize, + orderByLike, + friendsOnly, + { reviewRepository, spotifyRepository, accessTokenManager, friendRepository } +) => { + let id = null; + if (userToken) { + id = accessTokenManager.decode(userToken)?.value; + } + const artist = await spotifyRepository.getSpotifyArtist(artistId); + if (artist.error) throwStatusCode(artist.error.status, artist.error.message); + const albums = await spotifyRepository.getSpotifyArtistSongs( + artistId, + "album,single" + ); + const album_ids = albums.items.map((album) => album.id); + const reviews = await reviewRepository.getOeuvreReviews( + page, + pageSize, + orderByLike, + false, + friendsOnly, + album_ids, + id + ); + const serializedReviews = []; + reviews.forEach((element) => { + serializedReviews.push( + reviewSerializer( + element, + id, + undefined, + spotifyRepository, + reviewRepository, + friendRepository + ) + ); + }); + return await Promise.all(serializedReviews); +}; diff --git a/lib/application/use_cases/review/getCountOeuvreReviews.js b/lib/application/use_cases/review/getCountOeuvreReviews.js new file mode 100644 index 0000000..e5b15d1 --- /dev/null +++ b/lib/application/use_cases/review/getCountOeuvreReviews.js @@ -0,0 +1,12 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +module.exports = async (OeuvreId, userToken, {reviewRepository, userRepository, accessTokenManager}) => { + let id = null + if (userToken) { + id = accessTokenManager.decode(userToken)?.value + } + if(! await userRepository.getByUser(id)) throwStatusCode(401,"votre token d'authentification n'est pas le bon") + + const count = await reviewRepository.getReviewCount(OeuvreId) + + return count +} diff --git a/lib/application/use_cases/review/getOeuvreReviews.js b/lib/application/use_cases/review/getOeuvreReviews.js new file mode 100644 index 0000000..ee1db08 --- /dev/null +++ b/lib/application/use_cases/review/getOeuvreReviews.js @@ -0,0 +1,39 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +const reviewSerializer = require("../../../interfaces/serializers/ReviewSerializer"); +module.exports = async ( + OeuvreId, + userToken, + page, + pageSize, + orderByLike, + friendsOnly, + { reviewRepository, spotifyRepository, accessTokenManager, friendRepository } +) => { + let id = null; + if (userToken) { + id = accessTokenManager.decode(userToken)?.value; + } + const reviews = await reviewRepository.getOeuvreReviews( + page, + pageSize, + orderByLike, + false, + friendsOnly, + [OeuvreId], + id + ); + const serializedReviews = []; + reviews.forEach((element) => { + serializedReviews.push( + reviewSerializer( + element, + id, + undefined, + spotifyRepository, + reviewRepository, + friendRepository + ) + ); + }); + return await Promise.all(serializedReviews); +}; diff --git a/lib/application/use_cases/review/getReview.js b/lib/application/use_cases/review/getReview.js new file mode 100644 index 0000000..0f6b47d --- /dev/null +++ b/lib/application/use_cases/review/getReview.js @@ -0,0 +1,51 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +const reviewSerializer = require("../../../interfaces/serializers/ReviewSerializer"); +const getReview = require("./util/getReview"); +module.exports = async ( + idReview, + userToken, + page, + pageSize, + orderByLike, + { + reviewRepository, + spotifyRepository, + accessTokenManager, + friendRepository, + commentRepository, + } +) => { + const rawReview = await getReview(idReview, userToken, { + accessTokenManager, + friendRepository, + reviewRepository, + }); + console.log(rawReview); + const rawOeuvre = await spotifyRepository.getOeuvre( + rawReview.id_oeuvre, + rawReview.type + ); + if (rawOeuvre.error) + throwStatusCode(rawOeuvre.error.status, rawOeuvre.error.message); + + let id_utilisateur = userToken + ? accessTokenManager.decode(userToken)?.value + : null; + const comments = await commentRepository.getReviewComments( + rawReview.id_review, + id_utilisateur, + false, + page, + pageSize, + orderByLike + ); + + return reviewSerializer( + rawReview, + id_utilisateur, + comments, + spotifyRepository, + reviewRepository, + friendRepository + ); +}; diff --git a/lib/application/use_cases/review/getReviewLikes.js b/lib/application/use_cases/review/getReviewLikes.js new file mode 100644 index 0000000..1c32011 --- /dev/null +++ b/lib/application/use_cases/review/getReviewLikes.js @@ -0,0 +1,12 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +const UserPublic = require("../../../domain/model/UserPublic") +const getReview = require("./util/getReview") + +module.exports = async (reviewId,userToken,page,pageSize,{accessTokenManager,reviewRepository,friendRepository}) =>{ + const reviewTest = await getReview(reviewId,userToken, {accessTokenManager,friendRepository,reviewRepository}) + if(userToken) { + userToken = accessTokenManager.decode(userToken)?.value + } + const users = (await reviewRepository.getLikes(userToken,reviewId,page,pageSize)).map(item => new UserPublic(item)) + return users +} \ No newline at end of file diff --git a/lib/application/use_cases/review/getReviews.js b/lib/application/use_cases/review/getReviews.js new file mode 100644 index 0000000..a3e1a14 --- /dev/null +++ b/lib/application/use_cases/review/getReviews.js @@ -0,0 +1,33 @@ +const reviewSerializer = require("../../../interfaces/serializers/ReviewSerializer"); +module.exports = async ( + page, + pageSize, + orderByLike, + friendsOnly, + userToken, + { reviewRepository, spotifyRepository, accessTokenManager, friendRepository } +) => { + if (userToken) userToken = accessTokenManager.decode(userToken)?.value; + const rawReviews = await reviewRepository.getReviews( + page, + pageSize, + orderByLike, + false, + friendsOnly, + userToken + ); + const serializedReviews = []; + rawReviews.forEach((element) => { + serializedReviews.push( + reviewSerializer( + element, + userToken, + undefined, + spotifyRepository, + reviewRepository, + friendRepository + ) + ); + }); + return await Promise.all(serializedReviews); +}; diff --git a/lib/application/use_cases/review/getUserReviews.js b/lib/application/use_cases/review/getUserReviews.js new file mode 100644 index 0000000..6487bd2 --- /dev/null +++ b/lib/application/use_cases/review/getUserReviews.js @@ -0,0 +1,50 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +const reviewSerializer = require("../../../interfaces/serializers/ReviewSerializer"); +const { identity } = require("underscore"); +module.exports = async ( + pseudo, + userToken, + page, + pageSize, + orderByLike, + { + reviewRepository, + spotifyRepository, + accessTokenManager, + friendRepository, + userRepository, + } +) => { + const testUsers = await userRepository.getByEmailOrPseudo(pseudo, pseudo); + if (!testUsers) throwStatusCode(404, "l'utilisateur n'existe pas"); + let id = null; + if (testUsers?.is_private) { + let valid = false; + if (userToken) { + id = accessTokenManager.decode(userToken)?.value; + if (await friendRepository.areFriends(id, testUsers.id_utilisateur)) + valid = true; + } + if (!valid) throwStatusCode(403, "l'utilisateur est en privé"); + } + const reviews = await reviewRepository.getReviewByUserId( + testUsers.id_utilisateur, + page, + pageSize, + orderByLike + ); + const serializedReviews = []; + reviews.forEach((element) => { + serializedReviews.push( + reviewSerializer( + element, + id, + undefined, + spotifyRepository, + reviewRepository, + friendRepository + ) + ); + }); + return await Promise.all(serializedReviews); +}; diff --git a/lib/application/use_cases/review/likeReview.js b/lib/application/use_cases/review/likeReview.js new file mode 100644 index 0000000..5aed82e --- /dev/null +++ b/lib/application/use_cases/review/likeReview.js @@ -0,0 +1,10 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +module.exports = async (reviewId,userToken,{accessTokenManager,userRepository,reviewRepository}) =>{ + const id_utilisateur = accessTokenManager.decode(userToken)?.value + if(! await userRepository.getByUser(id_utilisateur)) throwStatusCode(401,"votre token d'authentification n'est pas le bon") + if(! await reviewRepository.doesUserLikes(id_utilisateur,reviewId)){ + await reviewRepository.likeReview(id_utilisateur,reviewId) + return true + } + await reviewRepository.unlikeReview(id_utilisateur,reviewId) +} \ No newline at end of file diff --git a/lib/application/use_cases/review/putComment.js b/lib/application/use_cases/review/putComment.js new file mode 100644 index 0000000..bae5c72 --- /dev/null +++ b/lib/application/use_cases/review/putComment.js @@ -0,0 +1,29 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +const getReview = require("./util/getReview"); +module.exports = async ( + userToken, + idReview, + description, + { + userRepository, + accessTokenManager, + friendRepository, + reviewRepository, + commentRepository, + } +) => { + const id_utilisateur = accessTokenManager.decode(userToken)?.value; + if (!(await userRepository.getByUser(id_utilisateur))) + throwStatusCode(401, "votre token d'authentification n'est pas le bon"); + await getReview(idReview, userToken, { + accessTokenManager, + friendRepository, + reviewRepository, + }); + return await commentRepository.persist( + idReview, + description, + id_utilisateur, + null + ); +}; diff --git a/lib/application/use_cases/review/putReview.js b/lib/application/use_cases/review/putReview.js new file mode 100644 index 0000000..19f9ecd --- /dev/null +++ b/lib/application/use_cases/review/putReview.js @@ -0,0 +1,48 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +const reviewSerializer = require("../../../interfaces/serializers/ReviewSerializer"); +module.exports = async ( + idOeuvre, + userToken, + description, + note, + type, + { + accessTokenManager, + userRepository, + reviewRepository, + spotifyRepository, + friendRepository, + oeuvreRepository, + } +) => { + const id_utilisateur = accessTokenManager.decode(userToken)?.value; + + if (!(await userRepository.getByUser(id_utilisateur))) + throwStatusCode(401, "votre token d'authentification n'est pas le bon"); + if (await reviewRepository.getByUserAndId(idOeuvre, id_utilisateur)) + throwStatusCode(403, "vous avez déjà posté une review"); + const id_type_review = await reviewRepository.getTypeReviewID(type); + if (!id_type_review) throwStatusCode(404, "ce type de review n'existe pas"); + const rawOeuvre = await spotifyRepository.getOeuvre(idOeuvre, type); + if (rawOeuvre.error) + throwStatusCode(rawOeuvre.error.status, rawOeuvre.error.message); + if (oeuvreRepository.is_oeuvre_relation(idOeuvre)) { + console.log(rawOeuvre); + } + const ReviewRaw = { + id_oeuvre: idOeuvre, + id_utilisateur, + description, + note, + id_type_review, + }; + const review = await reviewRepository.persist(ReviewRaw); + return reviewSerializer( + review, + id_utilisateur, + undefined, + spotifyRepository, + reviewRepository, + friendRepository + ); +}; diff --git a/lib/application/use_cases/review/util/getReview.js b/lib/application/use_cases/review/util/getReview.js new file mode 100644 index 0000000..8998515 --- /dev/null +++ b/lib/application/use_cases/review/util/getReview.js @@ -0,0 +1,26 @@ +const throwStatusCode = require("../../utils/throwStatusCode"); + +module.exports = async ( + idReview, + userToken, + { accessTokenManager, friendRepository, reviewRepository } +) => { + const rawReview = await reviewRepository.getById(idReview); + if (!rawReview) throwStatusCode(404, "la review n'existe pas"); + + if (userToken) { + const id = accessTokenManager.decode(userToken)?.value; + if ( + await friendRepository.areFriends( + id, + rawReview.utilisateur.id_utilisateur + ) + ) { + return rawReview; + } + } + if (!rawReview.utilisateur.is_private) { + return rawReview; + } + throwStatusCode(403, "l'utilisateur est en privé"); +}; diff --git a/lib/application/use_cases/security/GetAccessToken.js b/lib/application/use_cases/security/GetAccessToken.js index 09fdc08..9a5f9f4 100644 --- a/lib/application/use_cases/security/GetAccessToken.js +++ b/lib/application/use_cases/security/GetAccessToken.js @@ -1,15 +1,15 @@ 'use strict'; const throwStatusCode = require("../utils/throwStatusCode") const bcrypt = require("bcrypt"); +const userPublic = require("../../../domain/model/UserPublic") module.exports = async (email, password, { userRepository, accessTokenManager }) => { const user = await userRepository.getByIdent(email); if (!user || !await bcrypt.compare(password,user.password)) { throwStatusCode(401,'Bad credentials') } - return accessTokenManager.generate({ - sub: 'my-sub', // needs to match definition above - value: user.id, // this is a custom key I used, it could be named anything. Value should be a way to authenticate the user - aud: 'urn:audience:test', // needs to match definition above - iss: 'urn:issuer:test' // needs to match definition above - }); + return { + user : new userPublic(user), + token: accessTokenManager.generate(user) + } + }; diff --git a/lib/application/use_cases/security/VerifyAccessToken.js b/lib/application/use_cases/security/VerifyAccessToken.js deleted file mode 100644 index c33f20b..0000000 --- a/lib/application/use_cases/security/VerifyAccessToken.js +++ /dev/null @@ -1,9 +0,0 @@ -'use strict'; - -module.exports = (accessToken, { accessTokenManager }) => { - const decoded = accessTokenManager.decode(accessToken); - if (!decoded) { - throw new Error('Invalid access token'); - } - return { uid: decoded.uid }; -}; diff --git a/lib/application/use_cases/spotify/FetchArtist.js b/lib/application/use_cases/spotify/FetchArtist.js new file mode 100644 index 0000000..33c786f --- /dev/null +++ b/lib/application/use_cases/spotify/FetchArtist.js @@ -0,0 +1,15 @@ +const SerializeArtist = require("../../../interfaces/serializers/ArtistSerializer") +const throwStatusCode = require("../utils/throwStatusCode") + +module.exports = async (id, {spotifyRepository}) =>{ + let artistInfo = {} + try{ + artistInfo = await spotifyRepository.getSpotifyArtist(id) + } + catch(error){ + throwStatusCode(400, `l'id : ${id} n'existe pas`) + } + + const artist = SerializeArtist(artistInfo) // Formatage de l'objet + return artist +} \ No newline at end of file diff --git a/lib/application/use_cases/spotify/FetchArtistSongs.js b/lib/application/use_cases/spotify/FetchArtistSongs.js new file mode 100644 index 0000000..b766109 --- /dev/null +++ b/lib/application/use_cases/spotify/FetchArtistSongs.js @@ -0,0 +1,44 @@ +const SerializeAlbum = require("../../../interfaces/serializers/AlbumSerializer") +const throwStatusCode = require("../utils/throwStatusCode") + +module.exports = async (id, filter, limit, { spotifyRepository }) => { + const album = "album" + const single = "single" + const compilation = "compilation" + const appearsOn = "appears_on" + + let returnValue = [] + let artistSongs = {} + + try{ + artistSongs = await spotifyRepository.getSpotifyArtistSongs(id, filter, limit) + } + catch(error){ + throwStatusCode(400, `l'id : ${id} n'existe pas`) + } + + const filterTab = filter.split(',') + + // Recuperation selon le(s) filtre(s) et serialisation + + //recupere par defaut le include_groups : appears_on avec album + const albumRaw = filterTab.includes(album) ? artistSongs?.items?.filter(item => item?.album_group === album) || [] : []; + const albumSerialized = albumRaw.length > 0 ? albumRaw.map(item => SerializeAlbum(item)) : []; + + const singleRaw = filterTab.includes(single) ? artistSongs?.items?.filter(item => item?.album_group === single) || [] : []; + const singleSerialized = singleRaw.length > 0 ? singleRaw.map(item => SerializeAlbum(item)) : []; + + const compilationRaw = filterTab.includes(compilation) ? artistSongs?.items?.filter(item => item?.album_group === compilation) || [] : []; + const compilationSerialized = compilationRaw.length > 0 ? compilationRaw.map(item => SerializeAlbum(item)) : []; + + // autre champ utilise pour appearsOn : album_group + const appearsOnRaw = filterTab.includes(appearsOn) ? artistSongs?.items?.filter(item => item?.album_group === appearsOn) || [] : []; + const appearsOnialized = appearsOnRaw.length > 0 ? appearsOnRaw.map(item => SerializeAlbum(item)) : []; + + returnValue.push(...albumSerialized) + returnValue.push(...singleSerialized) + returnValue.push(...compilationSerialized) + returnValue.push(...appearsOnialized) + + return returnValue +} \ No newline at end of file diff --git a/lib/application/use_cases/spotify/GetToken.js b/lib/application/use_cases/spotify/GetToken.js new file mode 100644 index 0000000..57962f7 --- /dev/null +++ b/lib/application/use_cases/spotify/GetToken.js @@ -0,0 +1,10 @@ +const throwStatusCode = require("../utils/throwStatusCode") +module.exports = async (code,{spotifyRepository}) => { + const {access_token, refresh_token,error} = await spotifyRepository.getToken(code) + if(error) + throwStatusCode(400,error) + return { + token:access_token, + refresh_token + } +} \ No newline at end of file diff --git a/lib/application/use_cases/spotify/RefreshToken.js b/lib/application/use_cases/spotify/RefreshToken.js new file mode 100644 index 0000000..f7d2558 --- /dev/null +++ b/lib/application/use_cases/spotify/RefreshToken.js @@ -0,0 +1,19 @@ +module.exports = async (user,instant_refresh,{spotifyRepository,userRepository}) => { + const action = async ()=>{ + const {access_token} = await spotifyRepository.refreshToken(user.refresh_token) + user.token = access_token + userRepository.updateUser(user) + } + if(instant_refresh){ + try{ + await action() + }catch(e){ + user.refresh_token = null + user.token = null + await userRepository.updateUser(user) + } + } + setInterval(async ()=>{ + action() + },3500*1000) +} \ No newline at end of file diff --git a/lib/application/use_cases/spotify/Search.js b/lib/application/use_cases/spotify/Search.js index e1e8b70..ccea3cd 100644 --- a/lib/application/use_cases/spotify/Search.js +++ b/lib/application/use_cases/spotify/Search.js @@ -1,27 +1,42 @@ const SerializeTrack = require("../../../interfaces/serializers/TrackSerializer") const SerializeAlbum = require("../../../interfaces/serializers/AlbumSerializer") const SerializeArtist = require("../../../interfaces/serializers/ArtistSerializer") +const SerializeSearchItem = require("../../../interfaces/serializers/SerializeSearchItem") const MAX_USER = 3 -module.exports = async (query,filter, limit,allow_user, {spotifyRepository, userRepository}) =>{ +module.exports = async (query,filter, limit, {spotifyRepository, userRepository}) =>{ let limitSize = limit let users = [] - if(allow_user){ - + filter = filter.split(",") + if(filter.includes("user")){ + filter = filter.filter(item => item !== "user"); users = await userRepository.getUsersByPseudo(query,MAX_USER) + console.log(users) limitSize -= users.length } - const searchListRaw = await spotifyRepository.getSpotifySearchList(query, filter, limitSize) + + const searchListRaw = filter.length>0 + ? await spotifyRepository.getSpotifySearchList(query, filter.join(","), limitSize) + : {} let returnValue = [] + + // Formatage des objets const tracks = searchListRaw?.tracks ? searchListRaw?.tracks?.items.map(item => SerializeTrack(item)) : [] + const albums = searchListRaw?.albums ? searchListRaw?.albums?.items.map(item => SerializeAlbum(item)) : [] const artists = searchListRaw?.artists ? searchListRaw?.artists?.items.map(item => SerializeArtist(item)) : [] + if(artists[0]) + artists[0].popularity = 101 + returnValue.push(...tracks) returnValue.push(...albums) returnValue.push(...artists) returnValue.sort((item1,item2) => (item2.popularity - item1.popularity)) + if(returnValue.length> limitSize) + returnValue = returnValue.splice(0,limitSize) returnValue.push(...users) - return returnValue + + return returnValue.map(item => SerializeSearchItem(item)) } \ No newline at end of file diff --git a/lib/application/use_cases/spotify/getAlbum.js b/lib/application/use_cases/spotify/getAlbum.js new file mode 100644 index 0000000..224778b --- /dev/null +++ b/lib/application/use_cases/spotify/getAlbum.js @@ -0,0 +1,9 @@ +const SerializeAlbum = require("../../../interfaces/serializers/AlbumSerializer") +const throwStatusCode = require("../utils/throwStatusCode"); + +module.exports = async (id, {spotifyRepository}) =>{ + const albumInfo = await spotifyRepository.getSpotifyAlbums(id) + if(albumInfo.error) + throwStatusCode(albumInfo.error.status,albumInfo.error.message) + return SerializeAlbum(albumInfo) +} \ No newline at end of file diff --git a/lib/application/use_cases/spotify/getSearchFilters.js b/lib/application/use_cases/spotify/getSearchFilters.js new file mode 100644 index 0000000..eca493b --- /dev/null +++ b/lib/application/use_cases/spotify/getSearchFilters.js @@ -0,0 +1,20 @@ +module.exports = () => { + return [ + { + label: 'Musique', + id: 'track' + }, + { + label: 'Artiste', + id: 'artist' + }, + { + label: 'Album', + id: 'album' + }, + { + label: 'Utilisateur', + id: 'user' + }, + ] +} \ No newline at end of file diff --git a/lib/application/use_cases/spotify/getTrack.js b/lib/application/use_cases/spotify/getTrack.js new file mode 100644 index 0000000..e8d73bc --- /dev/null +++ b/lib/application/use_cases/spotify/getTrack.js @@ -0,0 +1,8 @@ +const SerializeTrack = require("../../../interfaces/serializers/TrackSerializer") +const throwStatusCode = require("../utils/throwStatusCode") +module.exports = async (id, {spotifyRepository}) =>{ + const trackInfo = await spotifyRepository.getSpotifyTracks(id) + if(trackInfo.error) + throwStatusCode(trackInfo.error.status,trackInfo.error.message) + return SerializeTrack(trackInfo) +} \ No newline at end of file diff --git a/lib/application/use_cases/user/AuthWithSpotify.js b/lib/application/use_cases/user/AuthWithSpotify.js new file mode 100644 index 0000000..ee3c53e --- /dev/null +++ b/lib/application/use_cases/user/AuthWithSpotify.js @@ -0,0 +1,52 @@ +'use strict'; + +const User = require('../../../domain/model/User'); +const throwStatusCode = require("../utils/throwStatusCode") +const crypto = require("crypto"); +const bcrypt = require("bcrypt"); +module.exports = async (spotify_code, callback, { userRepository,spotifyRepository,accessTokenManager}) => { + const {access_token, refresh_token,error} = await spotifyRepository.getToken(spotify_code,callback) + if(error){ + throwStatusCode(400,error.message) + } + const {email,display_name,images} = await spotifyRepository.getAccountData(access_token) + const image = images?.at(-1)?.url + const userTest = await userRepository.getByEmailOrPseudo(email,email) + if(userTest?.confirmed && userTest?.refresh_token){ + userTest.token = refresh_token + userTest.refresh_token = refresh_token + await userRepository.updateUser(userTest) + return { + email : email, + token: accessTokenManager.generate(userTest) + } + } + if(!userTest){ + const confirm_token = crypto.randomBytes(16).toString('hex') + const userRaw = { + email, + alias: display_name ? display_name : null, + photo: image ? image : null, + confirmed:false, + token: access_token, + auth_with_spotify: true, + refresh_token, + confirm_token + } + const user = new User( + userRaw); + await userRepository.persist(user) + setTimeout(()=>{ + userRepository.removeUserByConfirmToken(confirm_token) + },3600*1000*24) + return { + confirmToken: confirm_token, + } + } + if(!userTest.auth_with_spotify){ + throwStatusCode(403, 'un compte existe déjà avec ce mail') + } + return { + confirmToken: userTest.confirm_token + } +}; diff --git a/lib/application/use_cases/user/CompleteAccount.js b/lib/application/use_cases/user/CompleteAccount.js new file mode 100644 index 0000000..5a5cdc6 --- /dev/null +++ b/lib/application/use_cases/user/CompleteAccount.js @@ -0,0 +1,34 @@ +'use strict'; + + +const throwStatusCode = require("../utils/throwStatusCode") +const UserPublic = require("../../../domain/model/UserPublic") + +module.exports = async (pseudo,alias, bio, photo,confirmToken, { userRepository,documentRepository, accessTokenManager }) => { + const userTest = await userRepository.getByEmailOrPseudo(pseudo,pseudo) + if(userTest){ + throwStatusCode(403,'Pseudo déjà existant') + } + alias = alias ? alias : pseudo + const user = await userRepository.getByConfirmToken(confirmToken); + if(!user) { + throwStatusCode(400,'Token invalide') + } + user.alias = alias + user.pseudo = pseudo + user.bio = bio + user.confirm_token = null + user.confirmed = true + if(user.photo_temporaire && photo){ + documentRepository.deleteFile(user.photo_temporaire) + user.photo_temporaire = photo + user.photo = photo + } + await userRepository.updateUser(user) + return { + user: new UserPublic(user), + token: accessTokenManager.generate(user) + } + + +}; diff --git a/lib/application/use_cases/user/CreateUser.js b/lib/application/use_cases/user/CreateUser.js index afccae1..17bfd67 100644 --- a/lib/application/use_cases/user/CreateUser.js +++ b/lib/application/use_cases/user/CreateUser.js @@ -1,31 +1,106 @@ 'use strict'; const User = require('../../../domain/model/User'); -const bcrypt = require("bcrypt"); -const etatsEnum = require('../../../domain/model/utils/EtatsEnum') -const rolesEnum = require('../../../domain/model/utils/RolesEnum') const throwStatusCode = require("../utils/throwStatusCode") -module.exports = async (pseudo, email,alias, bio, password,spotifyToken, { userRepository }) => { - password = await bcrypt.hash(password,10) - if(!password) { - throwStatusCode('500','Internal server error') - } - const userTest = await userRepository.getByEmailOrPseudo(email,pseudo) - if(userTest){ - throwStatusCode('403','Email ou Pseudo déjà existant') - } - const user = new User( - null, - pseudo, - email, - alias, - bio, - password, - spotifyToken, - rolesEnum.UTILISATEUR, - etatsEnum.LIBRE); - return userRepository.persist(user) +const crypto = require("crypto"); +const bcrypt = require("bcrypt"); +module.exports = async (email,password, { userRepository,mailRepository}) => { + const userTest = await userRepository.getByEmailOrPseudo(email,email) + if(userTest){ + throwStatusCode(403,'Email déjà existant') + } + password = await bcrypt.hash(password,10) + if(!password) { + throwStatusCode('500','Internal server error') + } + const confirm_token = Math.floor(Math.random() * (99999 - 10000) + 10000) + const userRaw = { + email, + confirmed:false, + password, + confirm_token + } + let user = new User( + userRaw); + user = await userRepository.persist(user) + if (!user) { + throwStatusCode(400, 'Ce compte n\'a pas pu être créé. Veuillez réessayer plus tard.') + } + const mailOptions = { + from: process.env.MAILER_EMAIL, + to: email, + subject: 'Votre token de confirmation pour votre connexion', + html: ` + + + + + + Votre token de confirmation + + + +
+ + + +

Votre token de confirmation


+

Voici votre token de confirmation pour votre prochaine connexion sur Solimbo :

+
+

${confirm_token}

+
+

Utilisez ce token pour compléter votre inscription sur notre site.

+ Se connecter +
+ + + ` + }; + await mailRepository.send(mailOptions) + setTimeout(()=>{ + userRepository.removeUserByConfirmToken(confirm_token) + },60*5*1000) + return user }; diff --git a/lib/application/use_cases/user/changePrivateStatus.js b/lib/application/use_cases/user/changePrivateStatus.js new file mode 100644 index 0000000..fd262ec --- /dev/null +++ b/lib/application/use_cases/user/changePrivateStatus.js @@ -0,0 +1,12 @@ +'use strict'; + +const throwStatusCode = require("../utils/throwStatusCode") + +module.exports = async (token, {accessTokenManager, userRepository}) => { + const id = accessTokenManager.decode(token)?.value + const user = await userRepository.changePrivateStatus(id) + if (!user) { + throwStatusCode(400,"Votre token d'authentification n'est pas le bon"); + } + return user +} \ No newline at end of file diff --git a/lib/application/use_cases/user/follow.js b/lib/application/use_cases/user/follow.js new file mode 100644 index 0000000..823ac37 --- /dev/null +++ b/lib/application/use_cases/user/follow.js @@ -0,0 +1,14 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +module.exports = async (userToken, artistId, {userRepository,followRepository,accessTokenManager,spotifyRepository}) =>{ + const id = accessTokenManager.decode(userToken)?.value + if(! await userRepository.getByUser(id)) throwStatusCode(401,"votre token d'authentification n'est pas le bon") + const artist = await spotifyRepository.getSpotifyArtist(artistId) + if(artist.error) throwStatusCode(artist.error.status,artist.error.message) + if(! await followRepository.doesFollows(id,artistId)) { + followRepository.follow(id,artistId) + return true + } + followRepository.unfollow(id,artistId) + return false + +} \ No newline at end of file diff --git a/lib/application/use_cases/user/getOeuvresFav.js b/lib/application/use_cases/user/getOeuvresFav.js new file mode 100644 index 0000000..9c0d9b8 --- /dev/null +++ b/lib/application/use_cases/user/getOeuvresFav.js @@ -0,0 +1,10 @@ +const throwStatusCode = require("../utils/throwStatusCode"); + +module.exports = async (userToken, {userRepository, oeuvreFavRepository, accessTokenManager}) =>{ + const idUtilisateur = accessTokenManager.decode(userToken)?.value + if(! await userRepository.getByUser(idUtilisateur)) throwStatusCode(401,"votre token d'authentification n'est pas le bon") + + let oeuvresFav = [] + oeuvresFav = await oeuvreFavRepository.getOeuvresFav(idUtilisateur) + return oeuvresFav +} \ No newline at end of file diff --git a/lib/application/use_cases/user/getPage.js b/lib/application/use_cases/user/getPage.js new file mode 100644 index 0000000..285e790 --- /dev/null +++ b/lib/application/use_cases/user/getPage.js @@ -0,0 +1,91 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +const oeuvreSerializer = require("../../../interfaces/serializers/OeuvreSerializer"); +const UserPublic = require("../../../domain/model/UserPublic"); +const reviewSerializer = require("../../../interfaces/serializers/ReviewSerializer"); +module.exports = async ( + id_utilisateur, + page, + pageSize, + orderByLike, + user_token, + { + userRepository, + friendRepository, + oeuvreFavRepository, + reviewRepository, + accessTokenManager, + spotifyRepository, + } +) => { + const current_user = await userRepository.getByUser( + accessTokenManager.decode(user_token)?.value + ); + + if (!current_user) + throwStatusCode(401, "votre token d'authentification n'est pas le bon"); + const selected_user = await userRepository.getByUser(id_utilisateur); + if (!selected_user) throwStatusCode(404, "l'utilisateur n'existe pas"); + const isCurrent = id_utilisateur == current_user.id_utilisateur; + const doesFollows = await friendRepository.doesFollows( + id_utilisateur, + current_user.id_utilisateur + ); + const relation = await friendRepository.getFollowInfo( + id_utilisateur, + current_user.id_utilisateur + ); + if (selected_user.is_private && !doesFollows) { + return { + user: new UserPublic(selected_user), + forbidden: true, + relation, + }; + } + + const idOeuvres = await oeuvreFavRepository.getOeuvresFav(id_utilisateur); + const reviewsRaw = await reviewRepository.getReviewByUserId( + id_utilisateur, + page, + pageSize, + orderByLike + ); + + const oeuvresPromise = Promise.all( + idOeuvres.map(async (oeuvre) => { + const SpotifyOeuvre = await spotifyRepository.getOeuvre( + oeuvre.id_oeuvre, + oeuvre.type + ); + SpotifyOeuvre.rating = await reviewRepository.getOeuvreRating( + SpotifyOeuvre.id + ); + return oeuvreSerializer(SpotifyOeuvre, oeuvre.type); + }) + ); + + const reviewsPromise = Promise.all( + reviewsRaw.map(async (review) => { + return reviewSerializer( + review, + id_utilisateur, + undefined, + spotifyRepository, + reviewRepository, + friendRepository + ); + }) + ); + + const [oeuvres, reviews] = await Promise.all([ + oeuvresPromise, + reviewsPromise, + ]); + return { + user: new UserPublic(selected_user), + forbidden: false, + isCurrent, + relation, + oeuvres, + reviews, + }; +}; diff --git a/lib/application/use_cases/user/getUserByConfirmToken.js b/lib/application/use_cases/user/getUserByConfirmToken.js new file mode 100644 index 0000000..070e217 --- /dev/null +++ b/lib/application/use_cases/user/getUserByConfirmToken.js @@ -0,0 +1,5 @@ +const UserPublic = require("../../../domain/model/UserPublic") +module.exports = async (confirmToken,{userRepository}) =>{ + const user = await userRepository.getByConfirmToken(confirmToken) + return user ? new UserPublic(user) : null +} \ No newline at end of file diff --git a/lib/application/use_cases/user/getUserByPseudo.js b/lib/application/use_cases/user/getUserByPseudo.js new file mode 100644 index 0000000..9f34bb3 --- /dev/null +++ b/lib/application/use_cases/user/getUserByPseudo.js @@ -0,0 +1,7 @@ +const UserPublic = require("../../../domain/model/UserPublic") +module.exports = async (pseudo,{userRepository}) =>{ + const user = await userRepository.getByEmailOrPseudo(pseudo,pseudo) + console.log(!!user) + console.log(user) + return user ? new UserPublic(user) : null +} \ No newline at end of file diff --git a/lib/application/use_cases/user/modifyProfile.js b/lib/application/use_cases/user/modifyProfile.js new file mode 100644 index 0000000..afd6232 --- /dev/null +++ b/lib/application/use_cases/user/modifyProfile.js @@ -0,0 +1,36 @@ +"use strict"; +const isImage = require("../utils/isImage"); +const throwStatusCode = require("../utils/throwStatusCode"); +const UserPublic = require("../../../domain/model/UserPublic"); +module.exports = async ( + file, + pseudo, + bio, + alias, + isPrivate, + token, + { accessTokenManager, userRepository, documentRepository } +) => { + const id = accessTokenManager.decode(token)?.value; + const user = await userRepository.getByUser(id); + if (!user) + throwStatusCode(401, "votre token d'authentification n'est pas le bon"); + if (file) { + if (!isImage(file)) + throwStatusCode(415, "le fichier fourni n'est pas une image"); + const previewPath = user.photo; + if (previewPath) { + await documentRepository.deleteFile(previewPath); + } + const path = await documentRepository.uploadFile("upload", file); + if (!path) throwStatusCode(500, "internal server error"); + user.photo = path; + } + if (pseudo) user.pseudo = pseudo; + if (bio) user.bio = bio; + if (alias) user.alias = alias; + if (isPrivate !== undefined) user.is_private = isPrivate; + + await userRepository.updateUser(user); + return new UserPublic(user); +}; diff --git a/lib/application/use_cases/user/oeuvreFav.js b/lib/application/use_cases/user/oeuvreFav.js new file mode 100644 index 0000000..99887e5 --- /dev/null +++ b/lib/application/use_cases/user/oeuvreFav.js @@ -0,0 +1,23 @@ +const throwStatusCode = require("../utils/throwStatusCode"); +const getAlbum = require("../spotify/getAlbum"); +const getTrack = require("../spotify/getTrack"); +const { error } = require("@hapi/joi/lib/base"); + +module.exports = async (userToken, idOeuvre, type, {userRepository, oeuvreFavRepository, accessTokenManager,spotifyRepository}) =>{ + const idUtilisateur = accessTokenManager.decode(userToken)?.value + if(! await userRepository.getByUser(idUtilisateur)) throwStatusCode(401,"votre token d'authentification n'est pas le bon") + + const oeuvre = spotifyRepository.getOeuvre(idOeuvre, type) + if(oeuvre.error) + throwStatusCode(oeuvre.error.status,oeuvre.error.message) + + if (!(await oeuvreFavRepository.oeuvreFavExists(idUtilisateur, idOeuvre))){ + + if (!(await oeuvreFavRepository.ajoutPossible(idUtilisateur))) throwStatusCode(403,"Vous avez atteints le nombre maximal d'oeuvres favorites") + await oeuvreFavRepository.addOeuvrefav(idUtilisateur,idOeuvre,type) + return true + } + + await oeuvreFavRepository.deleteOeuvrefav(idUtilisateur,idOeuvre) + return false +} \ No newline at end of file diff --git a/lib/application/use_cases/user/resetPassword.js b/lib/application/use_cases/user/resetPassword.js new file mode 100644 index 0000000..7fb6c64 --- /dev/null +++ b/lib/application/use_cases/user/resetPassword.js @@ -0,0 +1,21 @@ +'use strict'; + +const User = require('../../../domain/model/User'); +const bcrypt = require("bcrypt"); +const rolesEnum = require('../../../domain/model/utils/RolesEnum') +const throwStatusCode = require("../utils/throwStatusCode") +const crypto = require("crypto"); +module.exports = async (password,resetToken,{userRepository}) => { + const user = await userRepository.getByResetToken(resetToken) + if(!user) { + throwStatusCode(400,'Token invalide') + } + password = await bcrypt.hash(password,10) + if(!password) { + throwStatusCode('500','Internal server error') + } + user.reset_token = null + user.password = password + await userRepository.updateUser(user) + return user +}; diff --git a/lib/application/use_cases/user/sendResetEmail.js b/lib/application/use_cases/user/sendResetEmail.js new file mode 100644 index 0000000..09166ce --- /dev/null +++ b/lib/application/use_cases/user/sendResetEmail.js @@ -0,0 +1,22 @@ +'use strict'; +const crypto = require("crypto"); +module.exports = async (email,{userRepository ,mailRepository}) => { + const user = await userRepository.getByEmailOrPseudo(email,email) + if(!user || !user.confirmed) return false + const reset_token = Math.floor(Math.random() * (99999 - 10000) + 10000) + user.reset_token = reset_token + await userRepository.updateUser(user) + const mailOptions = { + from: process.env.MAILER_EMAIL, + to: "réinitialisation de votre compte", + subject: 'reinitialisation de votre mot de passe', + text: reset_token + }; + await mailRepository.send(mailOptions) + + setTimeout(()=>{ + delete user.reset_token + userRepository.updateUser(user) + },60*5*1000) + return true +}; diff --git a/lib/application/use_cases/user/uploadPreview.js b/lib/application/use_cases/user/uploadPreview.js new file mode 100644 index 0000000..13dfa18 --- /dev/null +++ b/lib/application/use_cases/user/uploadPreview.js @@ -0,0 +1,16 @@ +'use strict'; +const isImage = require("../utils/isImage") +const throwStatusCode = require("../utils/throwStatusCode") +module.exports = async (file,token,{accessTokenManager, userRepository,documentRepository}) => { + if(!isImage(file)) throwStatusCode(415,"le fichier fourni n'est pas une image") + const id = accessTokenManager.decode(token)?.value + if(! await userRepository.getByUser(id)) throwStatusCode(401,"votre token d'authentification n'est pas le bon") + const previewPath = await userRepository.getPreviewPath(id) + if(previewPath) { + documentRepository.deleteFile(previewPath) + } + const path = await documentRepository.uploadFile('upload',file) + if(! path) throwStatusCode(500, "internal server error") + userRepository.addPreviewPath(id,path) + return path +}; \ No newline at end of file diff --git a/lib/application/use_cases/utils/isImage.js b/lib/application/use_cases/utils/isImage.js new file mode 100644 index 0000000..3df302c --- /dev/null +++ b/lib/application/use_cases/utils/isImage.js @@ -0,0 +1,5 @@ +const acceptedHeaders = ['image/png','image/jpeg'] +module.exports = (file) => { + const header = file?.hapi?.headers['content-type'] + return acceptedHeaders.includes(header) && file?.hapi?.filename && file._data +} \ No newline at end of file diff --git a/lib/application/use_cases/utils/throwStatusCode.js b/lib/application/use_cases/utils/throwStatusCode.js index c732135..e2eb250 100644 --- a/lib/application/use_cases/utils/throwStatusCode.js +++ b/lib/application/use_cases/utils/throwStatusCode.js @@ -1,5 +1,6 @@ module.exports = (code, message) => { - const error = new Error(message) + const error = new Error(message || 'error') + console.log(error) error.code = code throw error } \ No newline at end of file diff --git a/lib/domain/entity/ArtistEntity.js b/lib/domain/entity/ArtistEntity.js new file mode 100644 index 0000000..be8dffb --- /dev/null +++ b/lib/domain/entity/ArtistEntity.js @@ -0,0 +1,9 @@ +const Joi = require('joi') + +const getArtist = Joi.object().keys({ + id: Joi.string().max(50).required() +}) + +module.exports = { + getArtist +} \ No newline at end of file diff --git a/lib/domain/entity/CommentEntity.js b/lib/domain/entity/CommentEntity.js new file mode 100644 index 0000000..9504280 --- /dev/null +++ b/lib/domain/entity/CommentEntity.js @@ -0,0 +1,29 @@ +const Joi = require('joi') +const getCommentParams = Joi.object().keys({ + id: Joi.string().max(50).required() +}) +const deleteCommentParams = Joi.object().keys({ + id: Joi.string().max(50).required() +}) +const likeCommentParams = Joi.object().keys({ + id: Joi.string().max(50).required() +}) +const putCommentParams = Joi.object().keys({ + id: Joi.string().max(50).required() +}) +const putCommentPayload = Joi.object().keys({ + description: Joi.string().max(1500).required() +}) +const getCommentQuery = Joi.object().keys({ + page: Joi.number().integer().min(1).required(), + pageSize: Joi.number().integer().min(1).required(), + orderByLike: Joi.boolean() +}) +module.exports = { + getCommentParams, + deleteCommentParams, + likeCommentParams, + putCommentPayload, + putCommentParams, + getCommentQuery +} \ No newline at end of file diff --git a/lib/domain/entity/OeuvreEntity.js b/lib/domain/entity/OeuvreEntity.js new file mode 100644 index 0000000..4c50b07 --- /dev/null +++ b/lib/domain/entity/OeuvreEntity.js @@ -0,0 +1,21 @@ +const Joi = require('joi') + +const likeOeuvre = Joi.object().keys({ + type: Joi + .string() + .required() + .custom((value, helpers) => { + const allowedValues = ["track", "album", "artist","single","compilation"] + return allowedValues.includes(value) ? value : helpers.error('any.invalid') + }), + id: Joi.string().max(50).required() +}) + +const getOeuvre = Joi.object().keys({ + id: Joi.string().min(1).required() +}) + +module.exports = { + likeOeuvre, + getOeuvre +} \ No newline at end of file diff --git a/lib/domain/entity/ReviewEntity.js b/lib/domain/entity/ReviewEntity.js new file mode 100644 index 0000000..7373509 --- /dev/null +++ b/lib/domain/entity/ReviewEntity.js @@ -0,0 +1,103 @@ +const Joi = require("joi"); +const putReview = Joi.object().keys({ + idOeuvre: Joi.string().min(1).max(50).required(), + description: Joi.string().min(1).max(1500).required(), + note: Joi.number().greater(-1).less(6).required(), + type: Joi.string() + .required() + .custom((value, helpers) => { + const allowedValues = [ + "track", + "album", + "artist", + "single", + "compilation", + ]; + return allowedValues.includes(value) + ? value + : helpers.error("any.invalid"); + }), +}); +const deleteReview = Joi.object().keys({ + idReview: Joi.string().min(1).max(50).required(), +}); + +const getReviewParams = Joi.object().keys({ + id: Joi.string().min(1).max(50).required(), +}); +const getReviewQuery = Joi.object().keys({ + page: Joi.number().integer().min(1).required(), + pageSize: Joi.number().integer().min(1).required(), + orderByLike: Joi.boolean(), +}); +const getReviews = Joi.object().keys({ + page: Joi.number().integer().min(1).required(), + pageSize: Joi.number().integer().min(1).required(), + orderByLike: Joi.boolean(), + friendsOnly: Joi.boolean(), +}); + +const likeReviewParams = Joi.object().keys({ + id: Joi.number().required(), +}); + +const likeReviewQuery = Joi.object().keys({ + page: Joi.number().integer().min(1).required(), + pageSize: Joi.number().integer().min(1).required(), +}); + +const userReviewParams = Joi.object().keys({ + id: Joi.string().max(50).required(), +}); + +const userReviewQuery = Joi.object().keys({ + page: Joi.number().integer().min(1).required(), + pageSize: Joi.number().integer().min(1).required(), + orderByLike: Joi.boolean(), +}); + +const oeuvreReviewParams = Joi.object().keys({ + id: Joi.string().max(50).required(), +}); + +const oeuvreReviewQuery = Joi.object().keys({ + page: Joi.number().integer().min(1).required(), + pageSize: Joi.number().integer().min(1).required(), + orderByLike: Joi.boolean(), + friendsOnly: Joi.boolean(), +}); + +const artistReviewParams = Joi.object().keys({ + id: Joi.string().max(50).required(), +}); + +const artistReviewQuery = Joi.object().keys({ + page: Joi.number().integer().min(1).required(), + pageSize: Joi.number().integer().min(1).required(), + orderByLike: Joi.boolean(), + friendsOnly: Joi.boolean(), +}); +const putCommentParams = Joi.object().keys({ + id: Joi.string().max(50).required(), +}); + +const putCommentPayload = Joi.object().keys({ + description: Joi.string().max(1500).required(), +}); +module.exports = { + putReview, + deleteReview, + getReviewParams, + getReviews, + likeReviewParams, + likeReviewQuery, + userReviewParams, + userReviewQuery, + putCommentParams, + oeuvreReviewQuery, + putCommentPayload, + getReviewQuery, + oeuvreReviewParams, + artistReviewParams, + artistReviewQuery, +}; diff --git a/lib/domain/entity/SpotifyEntity.js b/lib/domain/entity/SpotifyEntity.js index cab88d2..071f957 100644 --- a/lib/domain/entity/SpotifyEntity.js +++ b/lib/domain/entity/SpotifyEntity.js @@ -1,5 +1,6 @@ -const Joi = require('@hapi/joi') -const trackBody = Joi.object().keys({ +// Verifie le bon format de la query / payload +const Joi = require('joi') +const search = Joi.object().keys({ query: Joi .string() .min(1) @@ -7,11 +8,10 @@ const trackBody = Joi.object().keys({ .required(), spotify_filter: Joi .string() - .required() .max(50) .required() .custom((value, helpers) =>{ - const allowedValues = ["track","artist","album"] + const allowedValues = ["track","artist","album","user"] const tabValue = value.split(",") let correct = true tabValue.forEach((value) => { @@ -21,9 +21,48 @@ const trackBody = Joi.object().keys({ }) return correct ? value : helpers.error('any.invalid') }), - allow_user: Joi.boolean().required(), limit: Joi.number().integer().required() }) +const album = Joi.object().keys({ + id: Joi + .string() + .min(1) + .required(), +}) + +const track = Joi.object().keys({ + id: Joi + .string() + .min(1) + .required(), +}) + + +const fetchArtist = Joi.object().keys({ + query: Joi.string().min(1).required(), //correspond au ID Artist +}) + +const fetchArtistSongs = Joi.object().keys({ + id: Joi.string().min(1).required(), + filter: Joi + .string() + .optional() + .default("album,single") + .custom((value, helpers) =>{ + const allowedValues = ["album","single","appears_on","compilation"] + const tabValue = value.replace(/\s/g, '').split(",") // retire les espaces + + // Renvoie une erreur des qu'un filtre n'est pas valide + for (const x of tabValue) { + if (!allowedValues.includes(x)) { + return helpers.error('any.invalid'); + } + } + return tabValue.join(",") + }), + + limit: Joi.number().integer().optional().min(1).max(50) +}) -module.exports = {trackBody} \ No newline at end of file +module.exports = {album,search,track,fetchArtist,fetchArtistSongs} \ No newline at end of file diff --git a/lib/domain/entity/UserEntity.js b/lib/domain/entity/UserEntity.js index 56746ae..03763ed 100644 --- a/lib/domain/entity/UserEntity.js +++ b/lib/domain/entity/UserEntity.js @@ -1,22 +1,128 @@ -const Joi = require('@hapi/joi') -const validationErrror = require("./utils/validationError") +const Joi = require("joi"); +const validationErrror = require("./utils/validationError"); const userSignIn = Joi.object().keys({ - email: Joi.string().max(40).required(), - password: Joi.string().max(30).required(), -}) + email: Joi.string().max(40).required(), + password: Joi.string().max(30).required(), +}); const userSignUp = Joi.object().keys({ - email: Joi.string().email().min(10).max(40).error(validationErrror("email","l'email est invalide")), - pseudo: Joi.string().min(3).max(15).custom((value, helpers) => { - if (value.includes('@')) { - return helpers.error('any.invalid'); - } - return value; - }, 'pseudo validation').error(validationErrror("pseudo","le pseudo doit être compris entre 3 et 15 caractère")), - alias: Joi.string().min(3).max(15).error(validationErrror("alias","l'alias doit être compris entre 3 et 15 caractère")), - password: Joi.string().min(8).max(30).error(validationErrror("password","le mot de passe doit être compris entre 8 et 30 caractères")), - spotifyToken: Joi.string().max(40), - bio:Joi.string().max(1500).error(validationErrror("bio","le pseudo doit faire moins de 1500 caractères")), -}) + pseudo: Joi.string() + .min(3) + .max(15) + .required() + .custom((value, helpers) => { + if (value.includes("@")) { + return helpers.error("any.invalid"); + } + return value; + }, "pseudo validation") + .error( + validationErrror( + "pseudo", + "le pseudo doit être compris entre 3 et 15 caractère" + ) + ), + alias: Joi.string() + .min(3) + .max(15) + .error( + validationErrror( + "alias", + "l'alias doit être compris entre 3 et 15 caractère" + ) + ), + photo: Joi.string().max(500), + confirmToken: Joi.string().max(50), + bio: Joi.string() + .min(0) + .max(1500) + .error( + validationErrror("bio", "la bio doit faire moins de 1500 caractères") + ), +}); +const uploadPreview = Joi.object().keys({ + file: Joi.any(), +}); +const createUser = Joi.object().keys({ + email: Joi.string().email().min(10).max(40).required(), + password: Joi.string().max(30).required(), +}); +const authWithSpotify = Joi.object().keys({ + spotify_code: Joi.string().max(1000).required(), + callback: Joi.string().max(100).required(), +}); +const isUser = Joi.object().keys({ + pseudo: Joi.string().max(15).required(), +}); +const getUserByConfirmToken = Joi.object().keys({ + confirmToken: Joi.string().max(50).required(), +}); +const sendResetEmail = Joi.object().keys({ + email: Joi.string().email().min(10).max(40), +}); +const resetPassword = Joi.object().keys({ + resetToken: Joi.string().max(50).required(), + password: Joi.string() + .min(8) + .max(30) + .required() + .error( + validationErrror( + "password", + "le mot de passe doit être compris entre 8 et 30 caractères" + ) + ), +}); +const follow = Joi.object().keys({ + artistId: Joi.string().min(1).required(), +}); +const oeuvreFav = Joi.object().keys({ + idOeuvre: Joi.string().min(1).required(), + type: Joi.string() + .required() + .custom((value, helpers) => { + const allowedValues = ["track", "album", "single"]; + return allowedValues.includes(value) + ? value + : helpers.error("any.invalid"); + }), +}); -module.exports = {userSignIn, userSignUp} \ No newline at end of file +const getPageQuery = Joi.object().keys({ + page: Joi.number().integer().min(1).required(), + pageSize: Joi.number().integer().min(1).required(), + orderByLike: Joi.boolean(), +}); + +const getPageParams = Joi.object().keys({ + id: Joi.string().min(1).max(50).required(), +}); + +const modifyPayload = Joi.object().keys({ + photo: Joi.any(), + bio: Joi.string() + .min(0) + .max(200) + .error( + validationErrror("bio", "la bio doit faire moins de 200 caractères") + ), + pseudo: Joi.string().min(3).max(15), + alias: Joi.string().min(3).max(15), + isPrivate: Joi.boolean(), +}); +module.exports = { + userSignIn, + userSignUp, + uploadPreview, + createUser, + isUser, + getUserByConfirmToken, + sendResetEmail, + resetPassword, + follow, + authWithSpotify, + oeuvreFav, + getPageQuery, + getPageParams, + modifyPayload, +}; diff --git a/lib/domain/model/Album.js b/lib/domain/model/Album.js index 3780df8..94ea97a 100644 --- a/lib/domain/model/Album.js +++ b/lib/domain/model/Album.js @@ -3,12 +3,17 @@ module.exports = class { this.id = album.id this.name = album.name this.popularity = album.popularity + this.rating = album.rating + this.reviewCount = album.reviewCount this.release_date = album.release_date this.total_tracks = album.total_tracks - this.images = album.images + this.image = album.image this.spotify_url = album.spotify_url this.artists = album.artists + this.tracks = album.tracks this.genres = album.genres - this.type = "album" + this.type = album.type + this.likeCount = album.likeCount + } } \ No newline at end of file diff --git a/lib/domain/model/Artist.js b/lib/domain/model/Artist.js index c8c3a03..ed67f9f 100644 --- a/lib/domain/model/Artist.js +++ b/lib/domain/model/Artist.js @@ -1,10 +1,10 @@ -const {spotify_url} = require("./Track"); module.exports = class { constructor(artist) { this.id = artist?.id; this.name = artist.name; - this.images = artist?.images; + this.image = artist?.image; this.popularity = artist?.popularity + this.follower_count = artist?.follower_count this.genres = artist?.genres this.spotify_url = artist?.spotify_url this.type = "artist" diff --git a/lib/domain/model/ArtistFollowersPage.js b/lib/domain/model/ArtistFollowersPage.js new file mode 100644 index 0000000..503d224 --- /dev/null +++ b/lib/domain/model/ArtistFollowersPage.js @@ -0,0 +1,6 @@ +module.exports = class { + constructor(artist,allFollowers){ + this.artist = artist + this.allFollowers = allFollowers + } +} \ No newline at end of file diff --git a/lib/domain/model/ArtistPage.js b/lib/domain/model/ArtistPage.js new file mode 100644 index 0000000..f8c0445 --- /dev/null +++ b/lib/domain/model/ArtistPage.js @@ -0,0 +1,19 @@ +module.exports = class { + constructor( + artist, + albums, + friends_followers, + reviewsByLike, + reviewsByTime, + reviewsByFriends, + doesUserFollow + ) { + this.artist = artist; + this.albums = albums; + this.friends_followers = friends_followers; + this.reviewsByLike = reviewsByLike; + this.reviewsByTime = reviewsByTime; + this.reviewsByFriends = reviewsByFriends; + this.doesUserFollow = doesUserFollow; + } +}; diff --git a/lib/domain/model/Comment.js b/lib/domain/model/Comment.js new file mode 100644 index 0000000..74f2f95 --- /dev/null +++ b/lib/domain/model/Comment.js @@ -0,0 +1,12 @@ +module.exports = class { + constructor(comment,utilisateur) { + this.id_com = comment.id_com + this.id_review = comment.id_review + this.id_reponse = comment.id_reponse + this.description = comment.description + this.countLike = comment.countLike + this.countComment = comment.countComment + this.createdAt = comment.createdAt + this.utilisateur = utilisateur + } +} \ No newline at end of file diff --git a/lib/domain/model/Friend.js b/lib/domain/model/Friend.js new file mode 100644 index 0000000..d430343 --- /dev/null +++ b/lib/domain/model/Friend.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = class { + + constructor(userRaw) { + this.id_utilisateur = userRaw?.id_utilisateur + this.amiIdUtilisateur = userRaw?.amiIdUtilisateur + this.en_attente = userRaw?.en_attente + this.createdAt = userRaw?.createdAt + this.updatedAt = userRaw?.updatedAt + this.type = 'amis' + } +} \ No newline at end of file diff --git a/lib/domain/model/ListsFriendsRequestsPage.js b/lib/domain/model/ListsFriendsRequestsPage.js new file mode 100644 index 0000000..6b718b4 --- /dev/null +++ b/lib/domain/model/ListsFriendsRequestsPage.js @@ -0,0 +1,6 @@ +module.exports = class { + constructor(usersPrivateRequestsReceived,usersPrivateRequestsSend){ + this.requestsReceived = usersPrivateRequestsReceived + this.requestsSend = usersPrivateRequestsSend + } +} \ No newline at end of file diff --git a/lib/domain/model/OeuvreFav.js b/lib/domain/model/OeuvreFav.js new file mode 100644 index 0000000..2c8f438 --- /dev/null +++ b/lib/domain/model/OeuvreFav.js @@ -0,0 +1,10 @@ +'use strict'; + +module.exports = class { + + constructor(oeuvreFavRaw) { + this.id_oeuvre = oeuvreFavRaw?.id_oeuvre + this.id_utilisateur = oeuvreFavRaw?.id_utilisateur + this.type = oeuvreFavRaw?.type + } +} \ No newline at end of file diff --git a/lib/domain/model/OeuvrePage.js b/lib/domain/model/OeuvrePage.js new file mode 100644 index 0000000..6c67011 --- /dev/null +++ b/lib/domain/model/OeuvrePage.js @@ -0,0 +1,10 @@ +module.exports = class { + constructor(oeuvre, artist, reviewsByLike, reviewsByTime, doesUserLikes, doesUserFav){ + this.oeuvre = oeuvre + this.artist = artist + this.reviewsByLike = reviewsByLike + this.reviewsByTime = reviewsByTime + this.doesUserLikes = doesUserLikes + this.doesUserFav = doesUserFav + } +} \ No newline at end of file diff --git a/lib/domain/model/PublicComment.js b/lib/domain/model/PublicComment.js new file mode 100644 index 0000000..d1eb4bc --- /dev/null +++ b/lib/domain/model/PublicComment.js @@ -0,0 +1,11 @@ +module.exports = class { + constructor(comment,utilisateur) { + this.id_com = comment.id_com + this.countLike = comment.countLike + this.countComment = comment.countComment + this.description = comment.description + this.doesUserLike = !!comment.doesUserLike + this.createdAt = comment.createdAt + this.utilisateur = utilisateur + } +} \ No newline at end of file diff --git a/lib/domain/model/Review.js b/lib/domain/model/Review.js new file mode 100644 index 0000000..a856510 --- /dev/null +++ b/lib/domain/model/Review.js @@ -0,0 +1,15 @@ +module.exports = class { + constructor(rawReview, utilisateur,type) { + this.id_review = rawReview.id_review + this.id_oeuvre = rawReview.id_oeuvre + this.countlike = rawReview.dataValues.countLike + this.countComment = rawReview.dataValues.countComment + this.description = rawReview.description + this.note = rawReview.note + this.createdAt = rawReview.createdAt + this.updated_at = rawReview.updatedAt + this.type = type + + this.utilisateur = utilisateur + } +} \ No newline at end of file diff --git a/lib/domain/model/ReviewPublic.js b/lib/domain/model/ReviewPublic.js new file mode 100644 index 0000000..9fda81d --- /dev/null +++ b/lib/domain/model/ReviewPublic.js @@ -0,0 +1,16 @@ +module.exports = class { + constructor(rawReview, oeuvre, utilisateur, doesUserLike, comments) { + this.id_review = rawReview.id_review; + this.description = rawReview.description; + this.countlike = rawReview.countlike; + this.countComment = rawReview.countComment; + this.doesUserLike = doesUserLike; + this.note = rawReview.note; + this.createdAt = rawReview.createdAt; + this.oeuvre = oeuvre; + this.utilisateur = utilisateur; + this.comments = comments; + this.made_by_friend = rawReview.made_by_friend; + this.type = rawReview.type; + } +}; diff --git a/lib/domain/model/Track.js b/lib/domain/model/Track.js index 4cecd66..722833b 100644 --- a/lib/domain/model/Track.js +++ b/lib/domain/model/Track.js @@ -1,12 +1,17 @@ module.exports = class { - constructor(track) { - this.id = track.id - this.name = track.name - this.album = track.album - this.artists = track.artists - this.duration_ms = track.duration_ms - this.popularity = track.popularity - this.spotify_url = track.spotify_url - this.type = "track" - } -} \ No newline at end of file + constructor(track) { + this.id = track.id; + this.name = track.name; + this.album = track.album; + this.artists = track.artists; + this.duration_ms = track.duration_ms; + this.popularity = track.popularity; + this.spotify_url = track.spotify_url; + this.rating = track.rating; + this.reviewCount = track.reviewCount; + this.likeCount = track.likeCount; + this.release_date = track.release_date; + this.image = track.album?.image; + this.type = "track"; + } +}; diff --git a/lib/domain/model/User.js b/lib/domain/model/User.js index d5c2153..b21948c 100644 --- a/lib/domain/model/User.js +++ b/lib/domain/model/User.js @@ -2,17 +2,28 @@ module.exports = class { - constructor(id = null, pseudo, email,alias, bio, password,spotifyToken,id_role, id_etat) { - this.id = id; - this.pseudo = pseudo; - this.email = email; - this.alias = alias - this.bio = bio - this.spotifyToken = spotifyToken - this.password = password; - this.id_role = id_role; - this.id_etat = id_etat - this.type = 'user' + constructor(userRaw) { + this.id_utilisateur = userRaw?.id_utilisateur + this.pseudo = userRaw?.pseudo + this.bio = userRaw?.bio + this.email = userRaw?.email + this.alias = userRaw?.alias + this.following_count = userRaw?.following_count + this.follower_count = userRaw?.follower_count + this.review_count = userRaw?.review_count + this.photo = userRaw?.photo + this.photo_temporaire = userRaw?.photo_temporaire + this.token = userRaw?.token + this.refresh_token = userRaw?.refresh_token + this.reset_token = userRaw?.reset_token + this.password = userRaw?.password; + this.id_role = userRaw?.id_role; + this.ban_until = userRaw?.ban_until + this.confirmed= userRaw?.confirmed + this.confirm_token = userRaw?.confirm_token + this.auth_with_spotify = userRaw?.auth_with_spotify + this.is_private = userRaw?.is_private + this.type = 'user' } }; \ No newline at end of file diff --git a/lib/domain/model/UserPublic.js b/lib/domain/model/UserPublic.js new file mode 100644 index 0000000..796d81b --- /dev/null +++ b/lib/domain/model/UserPublic.js @@ -0,0 +1,29 @@ +"use strict"; +const formatPhoto = (photo) => { + if ( + !photo || + "https://" === photo.substring(0, 8) || + "http://" === photo.substring(0, 7) + ) + return photo; + return `${process.env.API_URL}/${photo}`; +}; +module.exports = class { + constructor(userRaw) { + this.id_utilisateur = userRaw?.id_utilisateur; + this.pseudo = userRaw?.pseudo; + this.email = userRaw?.email; + this.alias = userRaw?.alias; + this.photo = formatPhoto(userRaw?.photo); + this.bio = userRaw?.bio; + this.following_count = userRaw?.following_count; + this.follower_count = userRaw?.follower_count; + this.review_count = userRaw?.review_count; + this.photo_temporaire = formatPhoto(userRaw?.photo_temporaire); + this.id_role = userRaw?.id_role; + this.ban_until = userRaw?.ban_until; + this.is_private = userRaw?.is_private; + this.auth_with_spotify = userRaw?.auth_with_spotify; + this.type = "user"; + } +}; diff --git a/lib/domain/model/utils/EtatsEnum.js b/lib/domain/model/utils/EtatsEnum.js deleted file mode 100644 index 20f5d9c..0000000 --- a/lib/domain/model/utils/EtatsEnum.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - LIBRE : 1, - BAN : 2, -} \ No newline at end of file diff --git a/lib/infrastructure/config/service-locator.js b/lib/infrastructure/config/service-locator.js index de5db8a..ff71ea1 100644 --- a/lib/infrastructure/config/service-locator.js +++ b/lib/infrastructure/config/service-locator.js @@ -1,16 +1,31 @@ 'use strict'; -const constants = require('./constants'); -const environment = require('./environment'); + const UserRepository= require('../repositories/UserRepository'); const spotifyRepository= require('../repositories/SpotifyRepository'); const JwtAccessTokenManager = require('../security/JwtAccessTokenManager'); +const documentRepository= require('../repositories/DocumentRepository'); +const NodemailerRepository= require('../repositories/NodemailerRepository'); +const FollowRepository= require('../repositories/FollowRepository'); +const FriendRepository= require('../repositories/FriendRepository'); +const ReviewRepository= require('../repositories/ReviewRepository'); +const LikeOeuvreRepository= require('../repositories/LikeOeuvreRepository'); +const CommentRepository= require('../repositories/CommentRepository'); +const OeuvreFavRepository= require('../repositories/OeuvreFavRepository'); function buildBeans() { return { accessTokenManager: new JwtAccessTokenManager(), userRepository: new UserRepository(), spotifyRepository: new spotifyRepository(process.env.CLIENT_ID,process.env.CLIENT_SECRET), + documentRepository: new documentRepository(), + mailRepository: new NodemailerRepository(process.env.MAILER_SERVICE,process.env.MAILER_EMAIL,process.env.MAILER_PASS), + followRepository: new FollowRepository(), + friendRepository: new FriendRepository(), + oeuvreFavRepository: new OeuvreFavRepository(), + reviewRepository: new ReviewRepository(), + likeOeuvreRepository: new LikeOeuvreRepository(), + commentRepository: new CommentRepository() }; } diff --git a/lib/infrastructure/config/strategy.js b/lib/infrastructure/config/strategy.js new file mode 100644 index 0000000..ea0ea4d --- /dev/null +++ b/lib/infrastructure/config/strategy.js @@ -0,0 +1,18 @@ +module.exports = ({userRepository}) =>{ + return { + keys: process.env.SECRET_ENCODER, // replace with your secret key for signing and verifying JWTs + verify: { + aud: 'urn:audience:test', + iss: 'urn:issuer:test', + sub: false, + nbf: true, + timeSkewSec: 15 + }, + validate: async (artifacts, request, h) => { + const isValid = !!await userRepository.getByUser(artifacts.decoded.payload.value) + return { + isValid, + }; + }, + } +} \ No newline at end of file diff --git a/lib/infrastructure/orm/sequelize/models/Amis.js b/lib/infrastructure/orm/sequelize/models/Amis.js index 6a380e7..584d1b6 100644 --- a/lib/infrastructure/orm/sequelize/models/Amis.js +++ b/lib/infrastructure/orm/sequelize/models/Amis.js @@ -2,13 +2,11 @@ const { DataTypes } = require('sequelize'); module.exports = (sequelize) => { return sequelize.define('amis', { - - // attributes en_attente: { type: DataTypes.BOOLEAN, allowNull: false, }, }, - { freezeTableName: true,}); + { freezeTableName: true,}); }; diff --git a/lib/infrastructure/orm/sequelize/models/Commentaire.js b/lib/infrastructure/orm/sequelize/models/Commentaire.js index 7c5b24e..2b74d08 100644 --- a/lib/infrastructure/orm/sequelize/models/Commentaire.js +++ b/lib/infrastructure/orm/sequelize/models/Commentaire.js @@ -13,6 +13,10 @@ module.exports = (sequelize) => { type: DataTypes.STRING(1500), allowNull: false, }, + deleted: { + type: DataTypes.BOOLEAN, + defaultValue: false + } }, { freezeTableName: true,}); diff --git a/lib/infrastructure/orm/sequelize/models/Etat.js b/lib/infrastructure/orm/sequelize/models/Etat.js deleted file mode 100644 index 0241add..0000000 --- a/lib/infrastructure/orm/sequelize/models/Etat.js +++ /dev/null @@ -1,19 +0,0 @@ -const { DataTypes } = require('sequelize'); -module.exports = (sequelize) => { - - return sequelize.define('etat', { - - // attributes - id_etat: { - type: DataTypes.INTEGER, - primaryKey: true, - autoIncrement: true, - }, - libelle: { - type: DataTypes.STRING(20), - allowNull: false, - }, - }, - { freezeTableName: true,}); - -}; diff --git a/lib/infrastructure/orm/sequelize/models/OeuvreFavorite.js b/lib/infrastructure/orm/sequelize/models/OeuvreFavorite.js index 50cfde8..7d4f8dd 100644 --- a/lib/infrastructure/orm/sequelize/models/OeuvreFavorite.js +++ b/lib/infrastructure/orm/sequelize/models/OeuvreFavorite.js @@ -17,7 +17,11 @@ module.exports = (sequelize) => { model: 'utilisateur', key: 'id_utilisateur', }, - } + }, + type: { + type: DataTypes.STRING(50), + allowNull: false, + }, }, { freezeTableName: true,}); diff --git a/lib/infrastructure/orm/sequelize/models/Review.js b/lib/infrastructure/orm/sequelize/models/Review.js index 8d9a18d..b101ca3 100644 --- a/lib/infrastructure/orm/sequelize/models/Review.js +++ b/lib/infrastructure/orm/sequelize/models/Review.js @@ -19,6 +19,10 @@ module.exports = (sequelize) => { note: { type: DataTypes.INTEGER, }, + deleted: { + type: DataTypes.BOOLEAN, + defaultValue: false + } }, { freezeTableName: true,}); diff --git a/lib/infrastructure/orm/sequelize/models/TypeReview.js b/lib/infrastructure/orm/sequelize/models/TypeReview.js index ff3e1ba..00e8285 100644 --- a/lib/infrastructure/orm/sequelize/models/TypeReview.js +++ b/lib/infrastructure/orm/sequelize/models/TypeReview.js @@ -4,7 +4,7 @@ module.exports = (sequelize) => { return sequelize.define('type_review', { // attributes - id_type_teview: { + id_type_review: { type: DataTypes.INTEGER, primaryKey: true, autoIncrement: true, diff --git a/lib/infrastructure/orm/sequelize/models/Utilisateur.js b/lib/infrastructure/orm/sequelize/models/Utilisateur.js index 84fac16..92143ab 100644 --- a/lib/infrastructure/orm/sequelize/models/Utilisateur.js +++ b/lib/infrastructure/orm/sequelize/models/Utilisateur.js @@ -12,37 +12,61 @@ module.exports = (sequelize) => { }, pseudo: { type: DataTypes.STRING(15), - allowNull: false, unique: true, }, alias: { type: DataTypes.STRING(15), - allowNull: false, }, password: { type: DataTypes.STRING(200 ), - allowNull: false, }, email: { type: DataTypes.STRING(30), unique: true, allowNull: false, }, - token_spotify: { - type: DataTypes.STRING(50), + token: { + type: DataTypes.STRING(400), unique: true, }, + refresh_token: { + type: DataTypes.STRING(400), + unique: true, + }, + auth_with_spotify: { + type: DataTypes.BOOLEAN, + defaultValue: false + }, id_spotify: { type: DataTypes.STRING(50), }, photo: { type: DataTypes.STRING(250), }, + photo_temporaire: { + type: DataTypes.STRING(250), + }, + ban_until: { + type: DataTypes.DATE + }, bio: { type: DataTypes.STRING(1500), }, + confirmed: { + type: DataTypes.BOOLEAN + }, + confirm_token: { + type: DataTypes.STRING(50), + }, + reset_token: { + type: DataTypes.STRING(50), + }, + is_private : { + type: DataTypes.BOOLEAN, + defaultValue: false + }, }, - { freezeTableName: true,} + { freezeTableName: true,} ); }; diff --git a/lib/infrastructure/orm/sequelize/sequelize.js b/lib/infrastructure/orm/sequelize/sequelize.js index 35a6951..8268c61 100644 --- a/lib/infrastructure/orm/sequelize/sequelize.js +++ b/lib/infrastructure/orm/sequelize/sequelize.js @@ -1,131 +1,121 @@ -'use strict'; -const { Sequelize } = require('sequelize'); -const environment = require('../../config/environment'); +"use strict"; +const { Sequelize } = require("sequelize"); +const environment = require("../../config/environment"); const sequelize = new Sequelize(environment.database.url); -const UserModel = require('./models/Utilisateur')(sequelize) -const RoleModel = require('./models/Role')(sequelize) -const AmisModel = require('./models/Amis')(sequelize) -const ArtisteModel = require('./models/Artiste')(sequelize) -const ReviewModel = require('./models/Review')(sequelize) -const TypeReviewModel = require('./models/TypeReview')(sequelize) -const CommentaireModel = require('./models/Commentaire')(sequelize) -const EtatModel = require('./models/Etat')(sequelize) - -require('./models/LikeOeuvre')(sequelize) -require('./models/OeuvreFavorite')(sequelize) - - +const UserModel = require("./models/Utilisateur")(sequelize); +const RoleModel = require("./models/Role")(sequelize); +const AmisModel = require("./models/Amis")(sequelize); +const ArtisteModel = require("./models/Artiste")(sequelize); +const ReviewModel = require("./models/Review")(sequelize); +const TypeReviewModel = require("./models/TypeReview")(sequelize); +const CommentaireModel = require("./models/Commentaire")(sequelize); +require("./models/LikeOeuvre")(sequelize); +require("./models/OeuvreFavorite")(sequelize); //un utilisateur a un role -UserModel.belongsTo(RoleModel, - {foreignKey: 'id_role'}) +UserModel.belongsTo(RoleModel, { foreignKey: "id_role" }); //un utilisateur a un etat -UserModel.belongsTo(EtatModel, - {foreignKey: 'id_etat'}) - -//un utilisateur peut avoir plusieurs amis -UserModel.belongsToMany(UserModel, - { - as: 'amis_1', - foreignKey: 'amis_1_id', - through : AmisModel - } -) -UserModel.belongsToMany(UserModel, - { - as: 'amis_2', - foreignKey: 'amis_2_id', - through : AmisModel - } -) - -UserModel.belongsToMany(CommentaireModel, - { - as: 'user_like_comment', - foreignKey: 'id_user', - through : 'like_commentaire' - } -) - -CommentaireModel.belongsToMany(UserModel, - { - as: 'comment_like', - foreignKey: 'id_com', - through : 'like_commentaire' - } -) -UserModel.belongsToMany(ArtisteModel, - { - as: 'utilisateur', - foreignKey: 'id_utilisateur', - through : 'follow' - } -) -ArtisteModel.belongsToMany(UserModel, - { - as: 'artiste', - foreignKey: 'id_artiste', - through : 'follow' - } -) -UserModel.belongsToMany(ReviewModel,{ - as: 'user_like_review', - foreignKey: 'id_artiste', - through : 'like_review' -}) -ReviewModel.belongsToMany(UserModel,{ - as: 'review_like', - foreignKey: 'id_review', - through : 'like_review' -}) +UserModel.belongsToMany(UserModel, { + as: "ami", + foreignKey: { + name: "id_utilisateur", + primaryKey: true, + }, + through: { + model: AmisModel, + primaryKey: true, + }, +}); + +UserModel.belongsToMany(CommentaireModel, { + as: "user_like_comment", + foreignKey: "id_utilisateur", + through: "like_commentaire", +}); + +CommentaireModel.belongsToMany(UserModel, { + as: "comment_like", + foreignKey: "id_com", + through: "like_commentaire", +}); + +UserModel.belongsToMany(ArtisteModel, { + as: "utilisateur_m_n", + foreignKey: "id_utilisateur", + through: "follow", +}); +ArtisteModel.belongsToMany(UserModel, { + as: "artiste", + foreignKey: "id_artiste", + through: "follow", +}); +UserModel.belongsToMany(ReviewModel, { + as: "user_like_review", + foreignKey: "id_utilisateur", + through: "like_review", +}); +ReviewModel.belongsToMany(UserModel, { + as: "review_like", + foreignKey: "id_review", + through: "like_review", +}); ReviewModel.belongsTo(UserModel, { - as:'utilisateur', - foreignKey:'id_utilisateur' -}) - + as: "utilisateur", + foreignKey: "id_utilisateur", +}); ReviewModel.belongsTo(TypeReviewModel, { - as:'type_review', - foreignKey:'id_type_review' -}) - + as: "type_review", + foreignKey: "id_type_review", +}); ReviewModel.hasMany(CommentaireModel, { - as:'comment_review', - foreignKey:'id_review' -}) -CommentaireModel.belongsTo(ReviewModel,{ - as:'review_comment', - foreignKey:'id_review' -}) - -CommentaireModel.hasMany(CommentaireModel,{ as: 'reponse', foreignKey: 'id_reponse' }) - + as: "comment_review", + foreignKey: "id_review", + onDelete: "cascade", +}); +CommentaireModel.belongsTo(ReviewModel, { + as: "review_comment", + foreignKey: "id_review", +}); + +CommentaireModel.belongsTo(UserModel, { + as: "user_review", + foreignKey: "id_utilisateur", +}); +CommentaireModel.hasMany(CommentaireModel, { + as: "reponse", + foreignKey: "id_reponse", + onDelete: "cascade", +}); RoleModel.sync() - .then(()=>{ + .then(() => { return RoleModel.bulkCreate([ - {id_role: 1, libelle : 'administrateur'}, - {id_role: 2, libelle : 'utlisateur'}, - ]) - }) - .then(() => console.log('Creation des roles réussi')) - .catch((error) => { - console.error('Erreur dans la création des roles : '+error) - }) - -EtatModel.sync() - .then(()=>{ - return EtatModel.bulkCreate([ - {id_etat: 1, libelle : 'libre'}, - {id_etat: 2, libelle : 'ban'}, - ]) - }) - .then(() => console.log('Creation des etats réussi')) - .catch((error) => { - console.error('Erreur dans la création des etats : '+error) - }) -module.exports = sequelize; \ No newline at end of file + { id_role: 1, libelle: "administrateur" }, + { id_role: 2, libelle: "utlisateur" }, + ]); + }) + .then(() => console.log("Creation des roles réussi")) + .catch((error) => { + console.error("Erreur dans la création des roles : " + error); + }); + +TypeReviewModel.sync() + .then(() => { + return TypeReviewModel.bulkCreate([ + { id_type_review: 1, libelle: "track" }, + { id_type_review: 2, libelle: "album" }, + { id_type_review: 3, libelle: "artist" }, + { id_type_review: 4, libelle: "single" }, + { id_type_review: 5, libelle: "compilation" }, + ]); + }) + .then(() => console.log("Creation des types de reviews réussi")) + .catch((error) => { + console.error("Erreur dans la création des types de reviews : " + error); + }); +module.exports = sequelize; diff --git a/lib/infrastructure/repositories/CommentRepository.js b/lib/infrastructure/repositories/CommentRepository.js new file mode 100644 index 0000000..aab2798 --- /dev/null +++ b/lib/infrastructure/repositories/CommentRepository.js @@ -0,0 +1,253 @@ +"use strict"; +const { Op } = require("sequelize"); +const sequelize = require("../orm/sequelize/sequelize"); +const CommentRepository = require("./interfaces/CommentRepositoryAbstract"); +const Comment = require("../../domain/model/Comment"); +const Utilisateur = require("../../domain/model/UserPublic"); +module.exports = class extends CommentRepository { + constructor() { + super(); + this.db = sequelize; + this.comment = this.db.model("commentaire"); + this.user = this.db.model("utilisateur"); + this.likeCommentaire = this.db.model("like_commentaire"); + } + + createComment(commentRaw) { + return new Comment(commentRaw, new Utilisateur(commentRaw.user_review)); + } + async persist(id_review, description, id_utilisateur, id_reponse = null) { + const seqComment = await this.comment.create({ + id_review, + description, + id_utilisateur, + id_reponse, + }); + return await this.getById(seqComment.id_com); + } + + async getCommentsComments( + id_reponse, + id_utilisateur, + fetchPrivate, + page, + pageSize, + orderByLike + ) { + let whereClause = { + id_reponse, + deleted: false, + }; + const orderColumn = orderByLike ? "countLike" : "createdAt"; + const order = [[sequelize.literal(orderColumn), "DESC"]]; + const offset = (page - 1) * pageSize; + if (!fetchPrivate) { + whereClause.id_utilisateur = id_utilisateur + ? { + [Op.in]: sequelize.literal( + `(SELECT id_utilisateur FROM utilisateur WHERE is_private = false OR id_utilisateur=${id} OR id_utilisateur IN (SELECT id_utilisateur FROM amis WHERE amiIdUtilisateur = ${id} and en_attente = false) OR id_utilisateur IN (SELECT amiIdUtilisateur FROM amis WHERE id_utilisateur = ${id} and en_attente = false))` + ), + } + : { + [Op.in]: sequelize.literal( + `(SELECT id_utilisateur FROM utilisateur WHERE is_private = false` + ), + }; + } + const comments = await this.comment.findAll({ + offset: offset, + attributes: { + include: [ + [ + sequelize.literal( + "(SELECT COUNT(*) FROM like_commentaire WHERE like_commentaire.id_com = commentaire.id_com and deleted=false)" + ), + "countLike", + ], + [ + sequelize.literal( + "(SELECT COUNT(*) FROM commentaire AS comm2 WHERE comm2.id_reponse = commentaire.id_com and deleted = false)" + ), + "countComment", + ], + ], + }, + include: [ + { + model: this.user, + as: "user_review", + }, + // { + // model: this.review, + // include: [ + // { + // model: this.user, + // as: "utilisateur", + // } + // ], + // } + ], + where: whereClause, + order, + limit: pageSize, + }); + return comments.map((comment) => this.createComment(comment.dataValues)); + } + async delete(id_com, id_utilisateur) { + const seqComment = await this.comment.findOne({ + where: { + id_com, + id_utilisateur, + }, + }); + if (!seqComment) return false; + seqComment.deleted = true; + await seqComment.save(); + return true; + } + + async getById(id_com, deleted = false) { + const comment = await this.comment.findOne({ + attributes: { + include: [ + [ + sequelize.literal( + "(SELECT COUNT(*) FROM like_commentaire WHERE like_commentaire.id_com = commentaire.id_com and deleted=false)" + ), + "countLike", + ], + [ + sequelize.literal( + "(SELECT COUNT(*) FROM commentaire AS comm2 WHERE comm2.id_reponse = commentaire.id_com and deleted = false)" + ), + "countComment", + ], + ], + }, + include: [ + { + model: this.user, + as: "user_review", + }, + // { + // model: this.review, + // include: [ + // { + // model: this.user, + // as: "utilisateur", + // } + // ], + // } + ], + where: { + id_com, + deleted, + }, + }); + return comment ? this.createComment(comment.dataValues) : null; + } + + async doesUserLike(id_com, id_utilisateur) { + const count = await this.likeCommentaire.count({ + where: { + id_utilisateur, + id_com, + }, + }); + return count > 0; + } + async like(id_com, id_utilisateur) { + const seqLike = await this.likeCommentaire.create({ + id_com, + id_utilisateur, + }); + await seqLike.save(); + } + unlike(id_com, id_utilisateur) { + this.likeCommentaire.destroy({ + where: { + id_utilisateur, + id_com, + }, + }); + } + + async getReviewComments( + id_review, + id_utilisateur, + fetchPrivate, + page, + pageSize, + orderByLike + ) { + let whereClause = { + id_review, + id_reponse: null, + deleted: false, + }; + const orderColumn = orderByLike ? "countLike" : "createdAt"; + const order = [[sequelize.literal(orderColumn), "DESC"]]; + const offset = (page - 1) * pageSize; + if (!fetchPrivate) { + whereClause.id_utilisateur = id_utilisateur + ? { + [Op.in]: sequelize.literal( + `(SELECT id_utilisateur FROM utilisateur WHERE is_private = false OR id_utilisateur=${id_utilisateur} OR id_utilisateur IN (SELECT id_utilisateur FROM amis WHERE amiIdUtilisateur = ${id_utilisateur} and en_attente = false) OR id_utilisateur IN (SELECT amiIdUtilisateur FROM amis WHERE id_utilisateur = ${id_utilisateur} and en_attente = false))` + ), + } + : { + [Op.in]: sequelize.literal( + `(SELECT id_utilisateur FROM utilisateur WHERE is_private = false` + ), + }; + } + + const comments = await this.comment.findAll({ + offset: offset, + attributes: { + include: [ + [ + sequelize.literal( + "(SELECT COUNT(*) FROM like_commentaire WHERE like_commentaire.id_com = commentaire.id_com and deleted=false)" + ), + "countLike", + ], + [ + sequelize.literal( + "(SELECT COUNT(*) FROM commentaire AS comm2 WHERE comm2.id_reponse = commentaire.id_com and deleted = false)" + ), + "countComment", + ], + ], + }, + include: [ + { + model: this.user, + as: "user_review", + }, + // { + // model: this.review, + // include: [ + // { + // model: this.user, + // as: "utilisateur", + // } + // ], + // } + ], + where: whereClause, + order, + limit: pageSize, + }); + return comments.map((comment) => this.createComment(comment.dataValues)); + } + async canDelete(id_com, id_utilisateur) { + const count = await this.comment.count({ + where: { + id_com, + id_utilisateur, + }, + }); + return count !== 0; + } +}; diff --git a/lib/infrastructure/repositories/DocumentRepository.js b/lib/infrastructure/repositories/DocumentRepository.js new file mode 100644 index 0000000..ae1b953 --- /dev/null +++ b/lib/infrastructure/repositories/DocumentRepository.js @@ -0,0 +1,33 @@ +'use strict'; +const documentRepositoryAbstract = require('./interfaces/DoucumentRepositoryAbstract') +const {unlink, writeFile} = require("fs"); +const crypto = require("crypto"); + +module.exports = class extends documentRepositoryAbstract{ + + uploadFile(folderPath,file) { + return new Promise((resolve, reject) => { + const fileLabel = crypto.randomBytes(16).toString('hex') + const fileExt = file.hapi.filename.split(".")[1] + const path = `${folderPath}/${fileLabel}.${fileExt}` + writeFile(`./${path}`, file._data, err => { + if (err) { + reject(err) + } + resolve(path) + }) + }) + } + deleteFile(path){ + unlink(`./${path}`,err => { + if(err){ + throw err + } + }) + } + + + + + +}; diff --git a/lib/infrastructure/repositories/FollowRepository.js b/lib/infrastructure/repositories/FollowRepository.js new file mode 100644 index 0000000..cfcbf85 --- /dev/null +++ b/lib/infrastructure/repositories/FollowRepository.js @@ -0,0 +1,156 @@ +'use strict'; + +const sequelize = require('../orm/sequelize/sequelize'); +const FollowRepository = require('./interfaces/FollowRepositoryAbstract'); +const User = require("../../domain/model/User"); +const {Op} = require('sequelize'); + +module.exports = class extends FollowRepository { + + constructor() { + super(); + this.db = sequelize; + this.followModel = this.db.model('follow'); + this.artistModel = this.db.model('artiste'); + this.userModel = this.db.model('utilisateur'); + } + + async follow(userId, artistId) { + let artist = await this.artistModel.findOne({ + where: { + id_artiste: artistId + } + }) + if(!artist){ + artist = await this.artistModel.create({ + id_artiste: artistId, + nb_suivis: 0 + }) + } + artist.nb_suivis = artist.nb_suivis < 0 ? 0 : artist.nb_suivis + artist.save() + const follow = await this.followModel.create({ + id_utilisateur: userId, + id_artiste: artistId + }); + artist.nb_suivis += 1 + artist.save() + await follow.save(); + } + + async unfollow(userId, artistId) { + const artist = await this.artistModel.findOne({ + where: { + id_artiste: artistId + } + }) + await this.followModel.destroy({ + where: { + id_utilisateur: userId, + id_artiste: artistId + } + }) + artist.nb_suivis -= 1 + artist.save() + } + async doesFollows(userId, artistId) { + const follow = await this.followModel.findOne({ + where: { + id_utilisateur: userId, + id_artiste: artistId + } + }) + return !!follow + } + + async getFollowersCount(idArtist) { + const artist = await this.artistModel.findOne({ + where: { + id_artiste: idArtist + } + }) + return artist?.nb_suivis || 0 + } + + async getFriendsFollowing(idArtist,idUser,limit) { + const users = await this.userModel.findAll({ + limit, + include: [ + { + model: this.artistModel, + as: 'utilisateur_m_n', + required: true, + through: { + where: { + id_artiste: idArtist + } + } + } + ], + where: { + + [Op.or]: [ + sequelize.literal(` utilisateur.id_utilisateur IN (SELECT amiIdUtilisateur FROM amis WHERE id_utilisateur = ${idUser} AND en_attente = false )`), + sequelize.literal(`utilisateur.id_utilisateur IN (SELECT id_utilisateur FROM amis WHERE amiIdUtilisateur = ${idUser} AND en_attente = false )`) + ] + + } + }) + + return users.map(item => new User(item)) + } + async getFriendsFollowingCount(idArtist,idUser) { + const countUsers = await this.userModel.count({ + include: [ + { + model: this.artistModel, + as: 'utilisateur_m_n', + required: true, + through: { + where: { + id_artiste: idArtist + } + } + } + ], + where: { + //id_utilisateur: {[Op.in]: sequelize.literal(`(SELECT id_utilisateur FROM utilisateur WHERE id_utilisateur IN (SELECT id_utilisateur FROM amis WHERE amiIdUtilisateur = :idUser))`)} + [Op.or]: [ + sequelize.literal(` utilisateur.id_utilisateur IN (SELECT amiIdUtilisateur FROM amis WHERE id_utilisateur = ${idUser} AND en_attente = false )`), + sequelize.literal(`utilisateur.id_utilisateur IN (SELECT id_utilisateur FROM amis WHERE amiIdUtilisateur = ${idUser} AND en_attente = false )`) + ] + }, + replacements: { idUser }, + }) + return countUsers + } + async getArtistFollowersWithoutFriends(idArtist,idUser,limit) { + //affiche idUser s'il follow l'artiste + const users = await this.userModel.findAll({ + limit, + include: [ + { + model: this.artistModel, + as: 'utilisateur_m_n', + required: true, + through: { + where: { + id_artiste: idArtist + } + } + } + ], + where: { + //id_utilisateur: {[Op.notIn]: sequelize.literal(`(SELECT id_utilisateur FROM utilisateur WHERE id_utilisateur IN (SELECT id_utilisateur FROM amis WHERE amiIdUtilisateur = ${idUser}))`)} + [Op.and]: [ + sequelize.literal(`utilisateur.id_utilisateur not IN (SELECT id_utilisateur FROM amis WHERE amiIdUtilisateur = ${idUser} AND en_attente = false)`), + sequelize.literal(`utilisateur.id_utilisateur not IN (SELECT amiIdUtilisateur FROM amis WHERE id_utilisateur = ${idUser} AND en_attente = false)`), + + ] + } + }) + + return users.map(item => new User(item)) + } +}; + diff --git a/lib/infrastructure/repositories/FriendRepository.js b/lib/infrastructure/repositories/FriendRepository.js new file mode 100644 index 0000000..05fc0bf --- /dev/null +++ b/lib/infrastructure/repositories/FriendRepository.js @@ -0,0 +1,189 @@ +"use strict"; + +const sequelize = require("../orm/sequelize/sequelize"); +const Friend = require("../../domain/model/Friend"); +const User = require("../../domain/model/User"); +const FriendRepositoryAbstract = require("./interfaces/FriendRepositoryAbstract"); +const { Op } = require("sequelize"); + +module.exports = class extends FriendRepositoryAbstract { + constructor() { + super(); + this.db = sequelize; + this.model = this.db.model("amis"); + this.UserModel = this.db.model("utilisateur"); + } + + async persist(friendEntity) { + friendEntity.createdAt = sequelize.literal("CURRENT_TIMESTAMP"); + friendEntity.updatedAt = sequelize.literal("CURRENT_TIMESTAMP"); + const user = await this.model.create(friendEntity); + user.save(); + return this.createAmis(user); + } + async getById(id_utilisateur, amiIdUtilisateur) { + const user = await this.model.findOne({ + where: { + id_utilisateur: id_utilisateur, + amiIdUtilisateur: amiIdUtilisateur, + }, + }); + return this.createAmis(user); + } + + createAmis(seq) { + return seq ? new Friend(seq) : null; + } + + async getListFriendsById(id) { + const idFriends = await this.model.findAll({ + attributes: ["amiIdUtilisateur"], + where: { + id_utilisateur: id, + en_attente: false, + }, + }); + + let friends = await this.UserModel.findAll({ + where: { + id_utilisateur: { + [Op.in]: idFriends.map((f) => f.getDataValue("amiIdUtilisateur")), + }, + }, + }); + return friends ? friends.map((f) => new User(f)) : null; + } + + async removeFriendById(id_utilisateur, amiIdUtilisateur) { + const user = await this.model.destroy({ + where: { + id_utilisateur: id_utilisateur, + amiIdUtilisateur: amiIdUtilisateur, + }, + }); + return user; + } + + async getRequestFriendsById(id) { + const idFriends = await this.model.findAll({ + where: { + en_attente: true, + amiIdUtilisateur: id, + }, + }); + const friends = await this.UserModel.findAll({ + where: { + id_utilisateur: { + [Op.in]: idFriends.map((f) => f.getDataValue("id_utilisateur")), + }, + }, + }); + return friends ? friends.map((f) => new User(f)) : null; + } + + async getSendRequestFriendsById(id) { + const idFriends = await this.model.findAll({ + where: { + en_attente: true, + id_utilisateur: id, + }, + }); + const friends = await this.UserModel.findAll({ + where: { + id_utilisateur: { + [Op.in]: idFriends.map((f) => f.getDataValue("amiIdUtilisateur")), + }, + }, + }); + return friends ? friends.map((f) => new User(f)) : null; + } + + async accept(id, amiIdUtilisateur) { + const user = await this.model.update( + { + en_attente: false, + updatedAt: sequelize.literal("CURRENT_TIMESTAMP"), + }, + { + where: { + id_utilisateur: id, + amiIdUtilisateur: amiIdUtilisateur, + }, + } + ); + return this.createAmis(user); + } + + async areFriends(id, amiIdUtilisateur) { + if (id == amiIdUtilisateur) return true; + const count = await this.model.count({ + where: { + [Op.or]: [ + { + id_utilisateur: id, + amiIdUtilisateur: amiIdUtilisateur, + en_attente: false, + }, + { + id_utilisateur: amiIdUtilisateur, + amiIdUtilisateur: id, + en_attente: false, + }, + ], + }, + }); + return count > 0; + } + async doesFollows(id, amiIdUtilisateur) { + if (id == amiIdUtilisateur) return true; + const count = await this.model.count({ + where: { + [Op.or]: [ + { + id_utilisateur: id, + amiIdUtilisateur: amiIdUtilisateur, + en_attente: false, + }, + { + id_utilisateur: amiIdUtilisateur, + amiIdUtilisateur: id, + en_attente: false, + }, + ], + }, + }); + return count > 0; + } + async getFollowInfo(id_utilisateur, amiIdUtilisateur) { + const areFriends = await this.areFriends(id_utilisateur, amiIdUtilisateur); + const relation1 = await this.model.findOne({ + where: { + id_utilisateur: id_utilisateur, + amiIdUtilisateur: amiIdUtilisateur, + }, + }); + + const relation2 = await this.model.findOne({ + where: { + //if the other follows the user + id_utilisateur: amiIdUtilisateur, + amiIdUtilisateur: id_utilisateur, + }, + }); + return areFriends + ? { + areFriends: true, + doesFollows: true, + isWaiting: false, + isWaited: true, + isFollowed: true, + } + : { + areFriends: false, + doesFollows: !!relation1, + isWaiting: relation1 ? !!relation1.en_attente : false, + isWaited: relation2 ? !!relation2.en_attente : false, + isFollowed: !!relation2, + }; + } +}; diff --git a/lib/infrastructure/repositories/LikeOeuvreRepository.js b/lib/infrastructure/repositories/LikeOeuvreRepository.js new file mode 100644 index 0000000..e9c4167 --- /dev/null +++ b/lib/infrastructure/repositories/LikeOeuvreRepository.js @@ -0,0 +1,44 @@ +'use strict'; +const sequelize = require('../orm/sequelize/sequelize'); +const LikeOeuvreRepositoryAbstract = require('./interfaces/LikeOeuvreRepositoryAbstract') +module.exports = class extends LikeOeuvreRepositoryAbstract { + constructor() { + super(); + this.db = sequelize; + this.likeOeuvre = this.db.model('like_oeuvre'); + } + + like(userId, oeuvreId) { + this.likeOeuvre.create({ + id_oeuvre: oeuvreId, + id_user: userId + }) + } + + unlike(userId, oeuvreId) { + this.likeOeuvre.destroy({ + where: { + id_oeuvre: oeuvreId, + id_user: userId + } + }) + } + doesUserLikes(userId, oeuvreId) { + return this.likeOeuvre.findOne({ + where: { + id_oeuvre: oeuvreId, + id_user: userId + } + }).then(like => { + return !!like + }) + } + getLikeCount(oeuvreId) { + return this.likeOeuvre.count({ + where: { + id_oeuvre: oeuvreId, + } + }).then(count => count) + } + +}; diff --git a/lib/infrastructure/repositories/NodemailerRepository.js b/lib/infrastructure/repositories/NodemailerRepository.js new file mode 100644 index 0000000..3f68eec --- /dev/null +++ b/lib/infrastructure/repositories/NodemailerRepository.js @@ -0,0 +1,23 @@ +'use strict'; +const MailRepositoryAbstract = require('./interfaces/MailRepositoryAbstract') +const nodemailer = require('nodemailer') +module.exports = class extends MailRepositoryAbstract{ + constructor(service, user, pass) { + super(); + this.transporter = nodemailer.createTransport({ + service, + auth: { + user, + pass + } + }) + } + send(config) { + this.transporter.sendMail(config) + } + + + + + +}; diff --git a/lib/infrastructure/repositories/OeuvreFavRepository.js b/lib/infrastructure/repositories/OeuvreFavRepository.js new file mode 100644 index 0000000..6b8ad83 --- /dev/null +++ b/lib/infrastructure/repositories/OeuvreFavRepository.js @@ -0,0 +1,72 @@ +'use strict'; + + +const sequelize = require('../orm/sequelize/sequelize'); +const { Op } = require('sequelize'); +const OeuvreFav = require('../../domain/model/OeuvreFav.js'); +const OeuvreFavRepositoryAbstract = require('./interfaces/OeuvreFavRepositoryAbstract'); + +module.exports = class extends OeuvreFavRepositoryAbstract { // creer abstract + + constructor() { + super(); + this.db = sequelize; + this.oeuvreFav = this.db.model('oeuvre_favorite'); + this.oeuvresFavMax = 3; + } + createOeuvre(oeuvre) { + return oeuvre ? new OeuvreFav(oeuvre.dataValues) : null + } + async ajoutPossible(idUtilisateur){ + let nbOeuvresFav = await this.oeuvreFav.count({ + where: { + id_utilisateur: idUtilisateur + }, + }); + return nbOeuvresFav < this.oeuvresFavMax; + } + + async addOeuvrefav(idUtilisateur, idOeuvre,type) { + if(!await this.ajoutPossible(idUtilisateur)) return false + const seqUser = await this.oeuvreFav.create({ + id_utilisateur: idUtilisateur, + id_oeuvre: idOeuvre, + createdAt: sequelize.literal('CURRENT_TIMESTAMP'), + updatedAt: sequelize.literal('CURRENT_TIMESTAMP'), + type + }) + await seqUser.save() + } + + async deleteOeuvrefav(idUtilisateur, idOeuvre) { + const seqUser = await this.oeuvreFav.destroy({ + where: { + id_utilisateur: idUtilisateur, + id_oeuvre: idOeuvre, + } + }) + } + + async oeuvreFavExists(idUtilisateur, idOeuvre) { + let oeuvre = await this.oeuvreFav.findOne({ + where: { + [Op.and]: { + id_utilisateur: idUtilisateur, + id_oeuvre: idOeuvre, + } + } + }) + return !!oeuvre + } + + async getOeuvresFav(idUtilisateur) { + let oeuvresFav = await this.oeuvreFav.findAll({ + where: { + id_utilisateur: idUtilisateur + }, + }); + return oeuvresFav ? oeuvresFav.map(oeuvre => this.createOeuvre(oeuvre)) : null; + } + +}; + diff --git a/lib/infrastructure/repositories/ReviewRepository.js b/lib/infrastructure/repositories/ReviewRepository.js new file mode 100644 index 0000000..d15caa7 --- /dev/null +++ b/lib/infrastructure/repositories/ReviewRepository.js @@ -0,0 +1,354 @@ +"use strict"; + +const sequelize = require("../orm/sequelize/sequelize"); +const ReviewRepository = require("./interfaces/ReviewRepositoryAbstract"); +const { Op } = require("sequelize"); +const User = require("../../domain/model/User"); +const Review = require("../../domain/model/Review"); +const { get } = require("underscore"); +const getPreWhere = (fetchPrivate, id, friendsOnly) => { + let whereClause = { + deleted: false, + }; + if (!fetchPrivate) { + if (friendsOnly && id) { + whereClause.id_utilisateur = { + [Op.in]: sequelize.literal( + `(SELECT id_utilisateur FROM utilisateur WHERE id_utilisateur IN (SELECT id_utilisateur FROM amis WHERE amiIdUtilisateur = ${id} and en_attente = false) OR id_utilisateur IN (SELECT amiIdUtilisateur FROM amis WHERE id_utilisateur = ${id} and en_attente = false))` + ), + }; + } else { + whereClause.id_utilisateur = id + ? { + [Op.in]: sequelize.literal( + `(SELECT id_utilisateur FROM utilisateur WHERE is_private = false OR id_utilisateur=${id} OR id_utilisateur IN (SELECT id_utilisateur FROM amis WHERE amiIdUtilisateur = ${id} and en_attente = false) OR id_utilisateur IN (SELECT amiIdUtilisateur FROM amis WHERE id_utilisateur = ${id} and en_attente = false))` + ), + } + : { + [Op.in]: sequelize.literal( + `(SELECT id_utilisateur FROM utilisateur WHERE is_private = false` + ), + }; + } + } + return whereClause; +}; +module.exports = class extends ReviewRepository { + constructor() { + super(); + this.db = sequelize; + this.review = this.db.model("review"); + this.TypeReview = this.db.model("type_review"); + this.user = this.db.model("utilisateur"); + this.likeReviewModel = this.db.model("like_review"); + } + createReview(seqReview) { + if (!seqReview) return null; + const type = seqReview.type_review.dataValues.libelle; + return new Review(seqReview, new User(seqReview.utilisateur), type); + } + async persist(ReviewEntity) { + let seqReview = await this.review.create(ReviewEntity, { + include: [ + { + model: this.user, + as: "utilisateur", + attributes: ["alias"], + }, + ], + }); + return this.getById(seqReview.id_review); + } + async getByUserAndId(id_oeuvre, id_utilisateur) { + return await this.review.findOne({ + where: { + id_oeuvre, + id_utilisateur, + deleted: false, + }, + }); + } + async getTypeReviewID(label) { + const seqTypeReview = await this.TypeReview.findOne({ + where: { + libelle: label, + }, + }); + return seqTypeReview?.id_type_review; + } + + async delete(id_review, id_utilisateur) { + const seqReview = await this.review.findOne({ + where: { + id_review, + id_utilisateur, + }, + }); + if (!seqReview) return false; + seqReview.deleted = true; + seqReview.save(); + return true; + } + async getById(id_review, deleted = false) { + const seqReview = await this.review.findOne({ + where: { + id_review: id_review, + deleted, + }, + attributes: { + include: [ + [ + sequelize.literal( + "(SELECT COUNT(DISTINCT like_review.id_utilisateur ) FROM like_review WHERE like_review.id_review = review.id_review)" + ), + "countLike", + ], + [ + sequelize.literal( + "(SELECT COUNT(*) FROM commentaire WHERE commentaire.id_review = review.id_review and commentaire.deleted = false and commentaire.id_reponse = null)" + ), + "countComment", + ], + ], + }, + include: [ + { + model: this.user, + as: "utilisateur", + }, + { + model: this.TypeReview, + attributes: ["libelle"], + as: "type_review", + }, + ], + }); + return this.createReview(seqReview); + } + + async getReviews(page, pageSize, orderByLike, fetchPrivate, friendsOnly, id) { + const orderColumn = orderByLike ? "countLike" : "createdAt"; + const order = [[sequelize.literal(orderColumn), "DESC"]]; + const offset = (page - 1) * pageSize; + let whereClause = getPreWhere(fetchPrivate, id, friendsOnly); + const reviews = await this.review.findAll({ + limit: pageSize, + offset: offset, + attributes: { + include: [ + [ + sequelize.literal( + "(SELECT COUNT(*) FROM like_review WHERE like_review.id_review = review.id_review)" + ), + "countLike", + ], + [ + sequelize.literal( + "(SELECT COUNT(*) FROM commentaire WHERE commentaire.id_review = review.id_review and commentaire.deleted = false and commentaire.id_reponse = null)" + ), + "countComment", + ], + ], + }, + include: [ + { + model: this.user, + as: "utilisateur", + }, + { + model: this.TypeReview, + attributes: ["libelle"], + as: "type_review", + }, + ], + order, + where: whereClause, + }); + return reviews.map((item) => this.createReview(item)); + } + + async getOeuvreReviews( + page, + pageSize, + orderByLike, + fetchPrivate, + friendsOnly, + ids_oeuvre, + id + ) { + const orderColumn = orderByLike ? "countLike" : "createdAt"; + const order = [[sequelize.literal(orderColumn), "DESC"]]; + const offset = (page - 1) * pageSize; + let whereClause = getPreWhere(fetchPrivate, id, friendsOnly); + if (Array.isArray(ids_oeuvre) && ids_oeuvre.length > 0) { + whereClause.id_oeuvre = { [Op.in]: ids_oeuvre }; + } else { + whereClause.id_oeuvre = ids_oeuvre; + } + + const reviews = await this.review.findAll({ + limit: pageSize, + offset: offset, + attributes: { + include: [ + [ + sequelize.literal( + "(SELECT COUNT(*) FROM like_review WHERE like_review.id_review = review.id_review)" + ), + "countLike", + ], + [ + sequelize.literal( + "(SELECT COUNT(*) FROM commentaire WHERE commentaire.id_review = review.id_review and commentaire.deleted = false and commentaire.id_reponse = null)" + ), + "countComment", + ], + ], + }, + include: [ + { + model: this.user, + as: "utilisateur", + }, + { + model: this.TypeReview, + attributes: ["libelle"], + as: "type_review", + }, + ], + order, + where: whereClause, + }); + return reviews.map((item) => this.createReview(item)); + } + async doesUserLikes(id_utilisateur, id_review) { + const count = await this.likeReviewModel.count({ + where: { + id_utilisateur, + id_review, + }, + }); + return count > 0; + } + async likeReview(id_utilisateur, id_review) { + const seqLike = await this.likeReviewModel.create({ + id_review, + id_utilisateur, + }); + await seqLike.save(); + } + async unlikeReview(id_utilisateur, id_review) { + await this.likeReviewModel.destroy({ + where: { + id_utilisateur, + id_review, + }, + }); + } + async getLikes(id_utilisateur, id_review, page, pageSize) { + const offset = (page - 1) * pageSize; + const whereClause = {}; + whereClause["$review_like->like_review.id_review$"] = id_review; + whereClause.id_utilisateur = id_utilisateur + ? { + [Op.in]: sequelize.literal( + `(SELECT id_utilisateur FROM utilisateur WHERE is_private = false OR id_utilisateur=${id_utilisateur} OR id_utilisateur IN (SELECT id_utilisateur FROM amis WHERE amiIdUtilisateur = ${id_utilisateur}))` + ), + } + : { + [Op.in]: sequelize.literal( + "(SELECT id_utilisateur FROM utilisateur WHERE is_private = false)" + ), + }; + const reviews = await this.review.findAll({ + offset: offset, + include: [ + { + model: this.user, + as: "review_like", + where: whereClause, + }, + ], + }); + const limit = + reviews[0]?.review_like.length > pageSize + ? pageSize + : reviews[0]?.review_like.length; + return reviews[0]?.review_like + ? reviews[0]?.review_like.slice(0, limit) + : []; + } + + async getReviewByUserId(id_utilisateur, page, pageSize, orderByLike) { + const offset = (page - 1) * pageSize; + const orderColumn = orderByLike ? "countLike" : "createdAt"; + const order = [[sequelize.literal(orderColumn), "DESC"]]; + + const reviews = await this.review.findAll({ + offset: offset, + limit: pageSize, + attributes: { + include: [ + [ + sequelize.literal( + "(SELECT COUNT(*) FROM like_review WHERE like_review.id_review = review.id_review)" + ), + "countLike", + ], + [ + sequelize.literal( + "(SELECT COUNT(*) FROM commentaire WHERE commentaire.id_review = review.id_review and commentaire.deleted = false and commentaire.id_reponse = null)" + ), + "countComment", + ], + ], + }, + include: [ + { + model: this.user, + as: "utilisateur", + }, + { + model: this.TypeReview, + attributes: ["libelle"], + as: "type_review", + }, + ], + where: { + id_utilisateur, + deleted: false, + }, + order, + }); + return reviews.map((item) => this.createReview(item)); + } + + async getOeuvreRating(id_oeuvre) { + const seqReview = await this.review.findOne({ + where: { + id_oeuvre, + deleted: false, + }, + attributes: [[sequelize.fn("AVG", sequelize.col("note")), "rating"]], + }); + return seqReview.dataValues.rating; + } + + async doesUserLike(id_utilisateur, id_review) { + return !!(await this.likeReviewModel.findOne({ + where: { + id_utilisateur, + id_review, + }, + })); + } + + async getReviewCount(id_oeuvre) { + return await this.review.count({ + where: { + id_oeuvre, + deleted: false, + }, + }); + } +}; diff --git a/lib/infrastructure/repositories/SpotifyRepository.js b/lib/infrastructure/repositories/SpotifyRepository.js index 5789ba4..239b4e1 100644 --- a/lib/infrastructure/repositories/SpotifyRepository.js +++ b/lib/infrastructure/repositories/SpotifyRepository.js @@ -4,41 +4,170 @@ const axios = require("axios"); const spotifyRepositoryAbstract = require('./interfaces/SpotifyRepositoryAbstract') module.exports = class extends spotifyRepositoryAbstract{ - constructor(client_id,client_secret) { - super(); - this.client_id = client_id - this.client_secret = client_secret - } + constructor(client_id,client_secret) { + super(); + this.client_id = client_id + this.client_secret = client_secret + } - getSpotifyAccessToken() { + getSpotifyAccessToken() { + return axios.post('https://accounts.spotify.com/api/token', null, { + params: { + grant_type: 'client_credentials', + }, + auth: { + username: this.client_id, + password: this.client_secret, + }, + }).then((response) => { + return response.data.access_token + }) + } - return axios.post('https://accounts.spotify.com/api/token', null, { - params: { - grant_type: 'client_credentials', - }, - auth: { - username: this.client_id, - password: this.client_secret, - }, - }).then((response) => { - return response.data.access_token - }) - } + async getSpotifySearchList(query,filter,limit){ - return axios.get('https://api.spotify.com/v1/search', { - headers: { - 'Authorization': `Bearer ${await this.getSpotifyAccessToken()}`, - }, - params: { - q: query, - type: filter, - limit : limit - }, - }) + return axios.get('https://api.spotify.com/v1/search', { + headers: { + 'Authorization': `Bearer ${await this.getSpotifyAccessToken()}`, + }, + params: { + q: query, + type: filter, + limit : limit + }, + }) .then((response) => { return response.data }) } + async getSpotifyArtist(id){ + return axios.get(`https://api.spotify.com/v1/artists/${id}`, { + validateStatus: function (status) { + return true; + }, + headers: { + 'Authorization': `Bearer ${await this.getSpotifyAccessToken()}`, + }, + }) + .then((response) => { + return response.data + }) + } + + async getSpotifyAlbums(id){ + return axios.get('https://api.spotify.com/v1/albums/'+ id, { + validateStatus: function (status) { + return true; + }, + headers: { + 'Authorization': `Bearer ${await this.getSpotifyAccessToken()}`, + } + }) + .then((response) => { + return response.data + }) + } + async getSpotifyArtist(id){ + return axios.get(`https://api.spotify.com/v1/artists/${id}`, { + validateStatus: function (status) { + return true; + }, + headers: { + 'Authorization': `Bearer ${await this.getSpotifyAccessToken()}`, + }, + }) + .then((response) => { + return response.data + }) + } + + async getSpotifyTracks(id){ + return axios.get('https://api.spotify.com/v1/tracks/'+ id, { + validateStatus: function (status) { + return true; + }, + headers: { + 'Authorization': `Bearer ${await this.getSpotifyAccessToken()}`, + } + }) + .then((response) => { + return response.data + }) + } + + refreshToken(token) { + const payload = new URLSearchParams() + payload.append('grant_type', 'refresh_token'); + payload.append('refresh_token', token); + return axios.post('https://accounts.spotify.com/api/token',payload,{ + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + }, + auth: { + username: this.client_id, + password: this.client_secret, + }, + }) + .then((response) => { + return response.data + }) + } + getToken(code,redirectURI) { + const payload = new URLSearchParams() + payload.append('grant_type','authorization_code') + payload.append('redirect_uri',redirectURI) + payload.append('code',code) + return axios.post('https://accounts.spotify.com/api/token',payload, + { + validateStatus: function (status) { + return true; + }, + headers: { + 'content-type': 'application/x-www-form-urlencoded', + 'Authorization': 'Basic ' + (new Buffer.from(this.client_id + ':' + this.client_secret).toString('base64')) + }, + }) + .then((response) => { + return response.data + }) + } + getAccountData(accessToken) { + return axios.get('https://api.spotify.com/v1/me', { + headers: { + 'Authorization': `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + }, + }) + .then(response => { + return response.data + }) + } + async getSpotifyArtistSongs(id, filter, limit) { + return axios.get(`https://api.spotify.com/v1/artists/${id}/albums`, { + validateStatus: function (status) { + return true; // axios traite les reponses tjs en reussites + }, + headers: { + 'Authorization': `Bearer ${await this.getSpotifyAccessToken()}`, + }, + params: { + include_groups: filter, // N'APPARAIT PAS DANS LES PARAMS DE LA REQUETE https://developer.spotify.com/documentation/web-api/reference/get-an-artists-albums + limit: limit + }, + }) + .then((response) => { + return response.data + }) + } + getOeuvre(id,type) { + switch (type) { + case 'artist': return this.getSpotifyArtist(id) + case 'album': return this.getSpotifyAlbums(id) + case 'track': return this.getSpotifyTracks(id) + default: + return {error: {status: 404, message: 'ressource not found'}} + } + } }; diff --git a/lib/infrastructure/repositories/UserRepository.js b/lib/infrastructure/repositories/UserRepository.js index e8e94ab..1178ac9 100644 --- a/lib/infrastructure/repositories/UserRepository.js +++ b/lib/infrastructure/repositories/UserRepository.js @@ -3,9 +3,7 @@ const sequelize = require('../orm/sequelize/sequelize'); const user = require('../../domain/model/User'); const userRepository = require('./interfaces/UserRepositoryAbstract'); -const { Op } = require('sequelize'); -const {id} = require("../../domain/model/User"); -const {create} = require("underscore"); +const { Op} = require('sequelize'); module.exports = class extends userRepository { constructor() { @@ -15,17 +13,7 @@ module.exports = class extends userRepository { } async persist(userEntity) { - const { pseudo, email, password,alias, bio, id_role,id_etat,spotifyToken } = userEntity; - const seqUser = await this.model.create({ - pseudo : pseudo, - email : email, - alias : alias, - bio : bio, - password : password, - token_spotify : spotifyToken, - id_role : id_role, - id_etat : id_etat }); - + const seqUser = await this.model.create(userEntity); await seqUser.save(); return this.createUser(seqUser) } @@ -38,7 +26,7 @@ module.exports = class extends userRepository { { pseudo : ident}, { email : ident} ] - } + }, }); return this.createUser(seqUser) } @@ -48,23 +36,15 @@ module.exports = class extends userRepository { createUser(seqUser){ if(!seqUser) return null - return new user(seqUser.id_utilisateur, - seqUser.pseudo, - seqUser.email, - seqUser.alias, - seqUser.bio, - seqUser.password, - seqUser.token_spotify, - seqUser.id_role, - seqUser.id_etat); + return new user(seqUser.dataValues); } async getByEmailOrPseudo(email, pseudo){ const seqUser = await this.model.findOne({ where: { - [Op.or]: [ - { pseudo : email}, - { email : pseudo} - ] + [Op.or]: { + pseudo: pseudo, + email: email, + } } }); return this.createUser(seqUser) @@ -74,15 +54,147 @@ module.exports = class extends userRepository { let request = {where: {pseudo : {[Op.like]: `${pseudo}%`}}} if(limit) request.limit = limit let seqUsers = await this.model.findAll(request); - seqUsers = seqUsers.map(item => this.createUser(item.dataValues)) + seqUsers = seqUsers.map(item => this.createUser(item)) return Object.values(seqUsers); } async getByUser(id){ - const seqUser = await this.model.findOne({ + const seqUser = await this.model.findOne( + { + attributes: { + include: [ + [ + sequelize.literal('(SELECT COUNT(*) FROM amis WHERE amis.id_utilisateur = utilisateur.id_utilisateur AND en_attente=false)'), + 'following_count' + ], + [ + sequelize.literal('(SELECT COUNT(*) FROM amis WHERE amis.amiIdUtilisateur = utilisateur.id_utilisateur AND en_attente=false)'), + 'follower_count' + ], + [ + sequelize.literal('(SELECT COUNT(*) FROM review WHERE review.id_utilisateur = utilisateur.id_utilisateur)'), + 'review_count' + ], + ] + }, where: { id_utilisateur : id } }); return this.createUser(seqUser) } + async getPreviewPath(id){ + const url = await this.model.findOne({ + where: { + [Op.and]: [ + { id_utilisateur: id }, + sequelize.literal('NOT `photo_temporaire` <=> `photo`'), + ] + }, + attributes: ['photo_temporaire'], + raw:true + }); + return url?.photo_temporaire + } + async addPreviewPath(id,path){ + const [updatedRowsCount, updatedRows] = await this.model.update( + {photo_temporaire: path}, + {where: {id_utilisateur: id}} + ) + return updatedRowsCount + } + async updateUser(user){ + const { pseudo, email, password,alias, bio, id_role,ban_until,token,refresh_token,confirmed,confirm_token,photo, photo_temporaire, reset_token} = user; + const seqUser = await this.model.findOne({ + where: { + id_utilisateur : user.id_utilisateur + } + }); + for (let attribut in user) { + if (user.hasOwnProperty(attribut)) { + seqUser[attribut] = user[attribut]; + } + } + seqUser.save() + } + async getSpotifyAuthUser(){ + let seqUsers = await this.model.findAll({ + where: { + token: {[Op.not]: null} + } + }); + seqUsers = seqUsers.map(item => this.createUser(item.dataValues)) + return seqUsers; + } + async removeUserByConfirmToken(confirmToken){ + const seqUsers = await this.model.findOne({ + where: { + confirm_token: confirmToken + } + }); + if(seqUsers) { + await seqUsers.destroy() + } + } + removeUncheckedAccounts() { + const twentyFourHoursAgo = new Date(Date.now() - 24 * 60 * 60 * 1000); + this.model.destroy({ + where: { + [Op.and]: [ + {updatedAt: { + [Op.lt]: twentyFourHoursAgo + }}, + {confirmed: false} + ] + + } + }) + } + destroy(user) { + this.model.destroy({ + where: { + id_utilisateur: user.id_utilisateur + } + }) + } + async getByConfirmToken(token) { + const seqUsers = await this.model.findOne({ + where: { + confirm_token: token + } + }); + return this.createUser(seqUsers) + } + clearResetTokens() { + this.model.update({ + reset_token: null + },{ where: {reset_token: {[Op.not]: null}} }) + } + async getByResetToken(resetToken) { + const seqUsers = await this.model.findOne({ + where: { + reset_token: resetToken + } + }); + return this.createUser(seqUsers) + } + + async changePrivateStatus(id){ + const user = await this.model.findOne({ + where: {id_utilisateur: id} + }); + + if (user) { + const nouveauStatut = user.is_private ? false : true; + + await this.model.update( + { is_private: nouveauStatut }, + { where: { id_utilisateur: id } } + ); + + } + + return this.createUser(user); +} + }; + diff --git a/lib/infrastructure/repositories/interfaces/CommentRepositoryAbstract.js b/lib/infrastructure/repositories/interfaces/CommentRepositoryAbstract.js new file mode 100644 index 0000000..45bf5c3 --- /dev/null +++ b/lib/infrastructure/repositories/interfaces/CommentRepositoryAbstract.js @@ -0,0 +1,44 @@ +"use strict"; + +module.exports = class { + persist(id_review, description, id_utilisateur) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } + + getReviewComments( + id_review, + id_utilisateur, + fetchPrivate, + page, + pageSize, + orderByLike + ) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } + getCommentsComments( + id_reponse, + id_utilisateur, + fetchPrivate, + page, + pageSize, + orderByLike + ) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } + delete(id_comment, id_utilisateur) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } + doesUserLikes(id_comment, id_utilisateur) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } + like(id_comment, id_utilisateur) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } + unlike(id_comment, id_utilisateur) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } + + canDelete(id_comment, id_utilisateur) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } +}; diff --git a/lib/infrastructure/repositories/interfaces/DoucumentRepositoryAbstract.js b/lib/infrastructure/repositories/interfaces/DoucumentRepositoryAbstract.js new file mode 100644 index 0000000..3ff027f --- /dev/null +++ b/lib/infrastructure/repositories/interfaces/DoucumentRepositoryAbstract.js @@ -0,0 +1,16 @@ +'use strict'; + +module.exports = class { + + uploadFile(path,file) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + deleteFile(path){ + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + + + + + +}; diff --git a/lib/infrastructure/repositories/interfaces/FollowRepositoryAbstract.js b/lib/infrastructure/repositories/interfaces/FollowRepositoryAbstract.js new file mode 100644 index 0000000..7ad4b98 --- /dev/null +++ b/lib/infrastructure/repositories/interfaces/FollowRepositoryAbstract.js @@ -0,0 +1,25 @@ +'use strict'; + +module.exports = class { + + follow(userId, artistId) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + + unfollow(userId, artistId) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + doesFollows(userId, artistId) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getFollowersCount(idArtist) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getFriendsFollowing(idArtist,idUser,limit) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + + async getFriendsFollowingCount(idArtist,idUser) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } +}; diff --git a/lib/infrastructure/repositories/interfaces/FriendRepositoryAbstract.js b/lib/infrastructure/repositories/interfaces/FriendRepositoryAbstract.js new file mode 100644 index 0000000..59d5e4e --- /dev/null +++ b/lib/infrastructure/repositories/interfaces/FriendRepositoryAbstract.js @@ -0,0 +1,28 @@ +"use strict"; + +module.exports = class { + persist(friendEntity) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } + + getById(id_utilisateur, amiIdUtilisateur) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } + getListFriendsById(id) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } + removeFriendById(id_utilisateur, amiIdUtilisateur) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } + getRequestFriendsById(id) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } + + accept(id, amiIdUtilisateur) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } + + areFriends(id, amiIdUtilisateur) { + throw new Error("ERR_METHOD_NOT_IMPLEMENTED"); + } +}; diff --git a/lib/infrastructure/repositories/interfaces/LikeOeuvreRepositoryAbstract.js b/lib/infrastructure/repositories/interfaces/LikeOeuvreRepositoryAbstract.js new file mode 100644 index 0000000..7629927 --- /dev/null +++ b/lib/infrastructure/repositories/interfaces/LikeOeuvreRepositoryAbstract.js @@ -0,0 +1,19 @@ +'use strict'; + +module.exports = class { + + like(userId, oeuvreId) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + + unlike(userId, oeuvreId) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + doesUserLikes(userId, oeuvreId) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getLikeCount(oeuvreId) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + +}; diff --git a/lib/infrastructure/repositories/interfaces/MailRepositoryAbstract.js b/lib/infrastructure/repositories/interfaces/MailRepositoryAbstract.js new file mode 100644 index 0000000..7bf6f6c --- /dev/null +++ b/lib/infrastructure/repositories/interfaces/MailRepositoryAbstract.js @@ -0,0 +1,14 @@ +'use strict'; + +module.exports = class { + + send(config) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + + + + + + +}; diff --git a/lib/infrastructure/repositories/interfaces/OeuvreFavRepositoryAbstract.js b/lib/infrastructure/repositories/interfaces/OeuvreFavRepositoryAbstract.js new file mode 100644 index 0000000..47a268b --- /dev/null +++ b/lib/infrastructure/repositories/interfaces/OeuvreFavRepositoryAbstract.js @@ -0,0 +1,20 @@ +'use strict'; + +module.exports = class { + + addOeuvrefav(idUtilisateur, idOeuvre) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + deleteOeuvrefav(idUtilisateur, idOeuvre) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + oeuvreFavExists(idUtilisateur, idOeuvre) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getOeuvresFav(idUtilisateur) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + ajoutPossible(idUtilisateur) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } +}; diff --git a/lib/infrastructure/repositories/interfaces/ReviewRepositoryAbstract.js b/lib/infrastructure/repositories/interfaces/ReviewRepositoryAbstract.js new file mode 100644 index 0000000..ec03b20 --- /dev/null +++ b/lib/infrastructure/repositories/interfaces/ReviewRepositoryAbstract.js @@ -0,0 +1,55 @@ +'use strict'; + +module.exports = class { + + persist(review) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + + getByUserAndId(id_oeuvre,id_utilisateur) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + delete(id_oeuvre,id_utilisateur) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getTypeReviewID(label){ + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + + getById(id_review) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + + getReviews(page,pageSize,orderByLike) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + doesUserLikes(id_utilisateur,id_review) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + likeReview(id_utilisateur,id_review) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + unlikeReview(id_utilisateur,id_review) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getLikes(id_utilisateur,id_review,page,pageSize) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getReviewByUserId(id_utilisateur,pageSize,orderByLike) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + + getOeuvreReviews(page,pageSize,orderByLike, fetchPrivate, ids_oeuvre,id) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + doesUserLike(id_utilisateur,id_review) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getOeuvreRating(id_oeuvre) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + + getReviewCount(id_oeuvre) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } +}; diff --git a/lib/infrastructure/repositories/interfaces/SpotifyRepositoryAbstract.js b/lib/infrastructure/repositories/interfaces/SpotifyRepositoryAbstract.js index 12059c5..bc78173 100644 --- a/lib/infrastructure/repositories/interfaces/SpotifyRepositoryAbstract.js +++ b/lib/infrastructure/repositories/interfaces/SpotifyRepositoryAbstract.js @@ -10,7 +10,24 @@ module.exports = class { throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); } + async getSpotifyArtist(id){ + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + refreshToken(token) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getToken(code,redirectURI) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getAccountData(accessToken){ + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } - + async getSpotifyArtist(id){ + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getOeuvre(id) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } }; diff --git a/lib/infrastructure/repositories/interfaces/UserRepositoryAbstract.js b/lib/infrastructure/repositories/interfaces/UserRepositoryAbstract.js index 22f2528..4ff7da4 100644 --- a/lib/infrastructure/repositories/interfaces/UserRepositoryAbstract.js +++ b/lib/infrastructure/repositories/interfaces/UserRepositoryAbstract.js @@ -19,5 +19,38 @@ module.exports = class { async getByUser(id){ throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); } + async getPreviewPath(id){ + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + async addPreviewPath(id,path){ + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + updateUser(user){ + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getSpotifyAuthUser(){ + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + removeUserByConfirmToken(confirmToken){ + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + removeUncheckedAccounts() { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getByConfirmToken(token) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + clearResetTokens() { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + getByResetToken(resetToken) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + destroy(user) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } + setPrivateStatus(id) { + throw new Error('ERR_METHOD_NOT_IMPLEMENTED'); + } }; diff --git a/lib/infrastructure/routine/refreshTokens.js b/lib/infrastructure/routine/refreshTokens.js new file mode 100644 index 0000000..f7a7341 --- /dev/null +++ b/lib/infrastructure/routine/refreshTokens.js @@ -0,0 +1,7 @@ +const refreshToken = require("../../application/use_cases/spotify/RefreshToken") +module.exports = async ({spotifyRepository, userRepository}) => { + const users = await userRepository.getSpotifyAuthUser() + for(let i = 0; i { + userRepository.removeUncheckedAccounts() + userRepository.clearResetTokens() +} \ No newline at end of file diff --git a/lib/infrastructure/security/JwtAccessTokenManager.js b/lib/infrastructure/security/JwtAccessTokenManager.js index 6ae9ff9..fd297f5 100644 --- a/lib/infrastructure/security/JwtAccessTokenManager.js +++ b/lib/infrastructure/security/JwtAccessTokenManager.js @@ -4,11 +4,15 @@ const jwt = require('jsonwebtoken'); const AccessTokenManager = require('./AccessTokenManager'); const JWT_SECRET_KEY = process.env.SECRET_ENCODER; - module.exports = class extends AccessTokenManager { - generate(payload) { - return jwt.sign(payload, JWT_SECRET_KEY); + generate(user) { + return jwt.sign({ + sub: 'my-sub', // needs to match definition above + value: user.id_utilisateur, // this is a custom key I used, it could be named anything. Value should be a way to authenticate the user + aud: 'urn:audience:test', // needs to match definition above + iss: 'urn:issuer:test', // needs to match definition above, + }, JWT_SECRET_KEY,{ expiresIn: '999y' }); } decode(accessToken) { diff --git a/lib/infrastructure/webserver/server.js b/lib/infrastructure/webserver/server.js index 9317bf7..0a7a19b 100644 --- a/lib/infrastructure/webserver/server.js +++ b/lib/infrastructure/webserver/server.js @@ -1,22 +1,29 @@ 'use strict'; const Hapi = require('@hapi/hapi'); -const Good = require('@hapi/good'); const Inert = require('@hapi/inert'); const Vision = require('@hapi/vision'); const Blipp = require('blipp'); const HapiSwagger = require('hapi-swagger'); -const Package = require('../../../package'); -const routes = require('../../interfaces/routes'); const serviceLocator = require('../../infrastructure/config/service-locator') const Jwt = require('@hapi/jwt'); +const strategy = require("../config/strategy") +const Path = require("path"); +const refreshTokens = require("../routine/refreshTokens") +const removeExpiredConfirmTokens = require("../routine/removeExpiredConfirmTokens") +const HapiCors = require('hapi-cors'); const createServer = async () => { - // Create a server with a host and port const server = Hapi.server({ - port: process.env.PORT || 3000 + port: process.env.PORT || 3000, + routes:{ + files: { + relativeTo: Path.join(__dirname.split("\\").slice(0,-3).join("/")+'/upload') + } + } }); - + await server.register(Inert); + await server.register(Vision); // Register vendors plugins await server.register([ Blipp, @@ -26,34 +33,20 @@ const createServer = async () => { plugin: HapiSwagger, options: { info: { - title: 'Test API Documentation', - version: Package.version, + title: 'Solimbo', + version: '0.1' }, } }, { - plugin: Good, - options: { - ops: { - interval: 1000 * 60 - }, - reporters: { - myConsoleReporter: [ - { - module: '@hapi/good-squeeze', - name: 'Squeeze', - args: [{ ops: '*', log: '*', error: '*', response: '*' }] - }, - { - module: '@hapi/good-console' - }, - 'stdout' - ] - } - }, + plugin: Jwt }, { - plugin: Jwt + plugin: HapiCors, + options: { + origins: ["http://"+process.env.FRONT_URL], // Specify the allowed origins + methods: ['GET', 'POST', 'PUT', 'DELETE'], // Specify the allowed HTTP methods + }, } ]); // for (const route in routes) { @@ -63,32 +56,20 @@ const createServer = async () => { server.app.serviceLocator = serviceLocator; - - server.auth.strategy('jwt', 'jwt', { - keys: process.env.SECRET_ENCODER, // replace with your secret key for signing and verifying JWTs - verify: { - aud: 'urn:audience:test', - iss: 'urn:issuer:test', - sub: false, - nbf: true, - exp: true, - maxAgeSec: 14400, // 4 hours - timeSkewSec: 15 - }, - validate: async (artifacts, request, h) => { - const {userRepository} = server.app.serviceLocator - const isValid = !!await userRepository.getByUser(artifacts.decoded.payload.value) - return { - isValid, - }; - }, - }); + server.auth.strategy('jwt', 'jwt', strategy(serviceLocator)); await server.register([ require('../../interfaces/routes/users'), - require('../../interfaces/routes/spotify') + require('../../interfaces/routes/spotify'), + require('../../interfaces/routes/review'), + require('../../interfaces/routes/upload'), + require('../../interfaces/routes/friends'), + require('../../interfaces/routes/artist'), + require('../../interfaces/routes/oeuvre'), + require('../../interfaces/routes/comment') ]); - + refreshTokens(serviceLocator) + removeExpiredConfirmTokens(serviceLocator) return server; }; diff --git a/lib/interfaces/controllers/ArtistController.js b/lib/interfaces/controllers/ArtistController.js new file mode 100644 index 0000000..17c0765 --- /dev/null +++ b/lib/interfaces/controllers/ArtistController.js @@ -0,0 +1,34 @@ +'use strict'; + +const getArtist = require('../../application/use_cases/artist/getArtist'); +const getArtistFollowers = require('../../application/use_cases/artist/getArtistFollowers'); +const handleError = require("./utils/handleError"); + +module.exports = { + + async get(request, handler){ + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(' '); + const {id} = request.params + try{ + return handler.response(await getArtist(id,token, serviceLocator)).code(200) + } + catch (e){ + return handleError(e) + } + }, + async getFollowers(request, handler){ + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(' '); + const {id} = request.params + try{ + return handler.response(await getArtistFollowers(id,token, serviceLocator)).code(200) + } + catch (e){ + return handleError(e) + } + } + +} diff --git a/lib/interfaces/controllers/CommentController.js b/lib/interfaces/controllers/CommentController.js new file mode 100644 index 0000000..6c07a82 --- /dev/null +++ b/lib/interfaces/controllers/CommentController.js @@ -0,0 +1,61 @@ +const getComment = require("../../application/use_cases/comment/getComment"); +const deleteComment = require("../../application/use_cases/comment/deleteComment"); +const putComment = require("../../application/use_cases/comment/putComment"); +const likeComment = require("../../application/use_cases/comment/likeComment"); +const handleError = require("./utils/handleError"); +module.exports = { + + async get(request,handler){ + try{ + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + const authorizationHeader = request.headers.authorization; + const [, token ] = authorizationHeader.split(' '); + const {id} = request.params + const {page, pageSize, orderByLike} = request.query + return handler.response(await getComment(id,page, pageSize, orderByLike, token, serviceLocator)).code(200) + }catch(error){ + return handleError(error) + } + }, + async delete(request,handler){ + try{ + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + const authorizationHeader = request.headers.authorization; + const [, token ] = authorizationHeader.split(' '); + const {id} = request.params + + await deleteComment(id, token, serviceLocator) + return handler.response('').code(200) + }catch(error){ + return handleError(error) + } + }, + async put(request,handler){ + try{ + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + const authorizationHeader = request.headers.authorization; + const [, token ] = authorizationHeader.split(' '); + const {id} = request.params + const {description} = request.payload + return handler.response(await putComment(id,description,token, serviceLocator)).code(200) + }catch(error){ + return handleError(error) + } + }, + async like(request,handler){ + try{ + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + const authorizationHeader = request.headers.authorization; + const [, token ] = authorizationHeader.split(' '); + const {id} = request.params + await likeComment(id,token, serviceLocator) + return handler.response('').code(200) + }catch(error){ + return handleError(error) + } + }, +} \ No newline at end of file diff --git a/lib/interfaces/controllers/FriendsController.js b/lib/interfaces/controllers/FriendsController.js new file mode 100644 index 0000000..88b760d --- /dev/null +++ b/lib/interfaces/controllers/FriendsController.js @@ -0,0 +1,95 @@ +'use strict'; + +const followUser = require('../../application/use_cases/friend/followUser') +const acceptRequestUser = require('../../application/use_cases/friend/acceptRequestUser') +const unfollowUser = require('../../application/use_cases/friend/unfollowUser') +const getListFriends = require('../../application/use_cases/friend/getListFriends') +const getListFriendsRequest = require('../../application/use_cases/friend/getListFriendsRequest') +const getProfilFriend = require('../../application/use_cases/friend/getProfilFriend') +const handleError = require("./utils/handleError") + +module.exports = { + + async followUser(request, handler){ + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(' '); + const {amiIdUtilisateur} = request.payload + try{ + await followUser(token, amiIdUtilisateur, serviceLocator); + return handler.response("Demande d'ami envoyée / Ajout d'ami bien effectué").code(200) + } + catch (e){ + return handleError(e) + } + }, + + async unfollowUser(request, handler){ + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(' '); + const {amiIdUtilisateur} = request.payload + try{ + await unfollowUser(token, amiIdUtilisateur, serviceLocator); + return handler.response("Suppression d'un ami bien effectuée").code(200) + } + catch (e){ + return handleError(e) + } + }, + + async acceptRequestUser(request, handler){ + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(' '); + const {amiIdUtilisateur} = request.payload; + try{ + const user = await acceptRequestUser(token, amiIdUtilisateur, serviceLocator); + return handler.response("Demande d'ami bien acceptée").code(200) + } + catch (e){ + return handleError(e) + } + }, + + async getListFriends(request, handler){ + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(' '); + try{ + const friends = await getListFriends(token, serviceLocator); + return handler.response(friends).code(200) + } + catch (e){ + return handleError(e) + } + }, + + async getListFriendsRequest(request, handler){ + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(' '); + try{ + const requests = await getListFriendsRequest(token, serviceLocator); + return handler.response(requests).code(200) + } + catch (e){ + return handleError(e) + } + }, + + async getProfilFriend(request, handler){ + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(' '); + const {amiIdUtilisateur} = request.payload + try{ + const user = await getProfilFriend(token, amiIdUtilisateur, serviceLocator); + return handler.response(user).code(200) + } + catch (e){ + return handleError(e) + } + } + +} diff --git a/lib/interfaces/controllers/OeuvreController.js b/lib/interfaces/controllers/OeuvreController.js new file mode 100644 index 0000000..7cde952 --- /dev/null +++ b/lib/interfaces/controllers/OeuvreController.js @@ -0,0 +1,37 @@ +'use strict'; + +const likeOeuvre = require('../../application/use_cases/oeuvre/likeOeuvre'); +const getOeuvre = require('../../application/use_cases/oeuvre/getOeuvre'); +const handleError = require("./utils/handleError"); + + +module.exports = { + + async like(request, handler){ + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(' '); + const {id,type} = request.params + try{ + await likeOeuvre(token,id,type, serviceLocator) + return handler.response("").code(200) + } + catch (e){ + return handleError(e) + } + }, + async get(request, handler){ + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(' '); + const {id} = request.params + try{ + return handler.response(await getOeuvre(id,token, serviceLocator)).code(200) + } + catch (e){ + return handleError(e) + } + } + + +} diff --git a/lib/interfaces/controllers/ReviewController.js b/lib/interfaces/controllers/ReviewController.js new file mode 100644 index 0000000..9ef0644 --- /dev/null +++ b/lib/interfaces/controllers/ReviewController.js @@ -0,0 +1,242 @@ +const putReview = require("../../application/use_cases/review/putReview"); +const deleteReview = require("../../application/use_cases/review/deleteReview"); +const getReview = require("../../application/use_cases/review/getReview"); +const getReviews = require("../../application/use_cases/review/getReviews"); +const likeReview = require("../../application/use_cases/review/likeReview"); +const getReviewLikes = require("../../application/use_cases/review/getReviewLikes"); +const getUserReviews = require("../../application/use_cases/review/getUserReviews"); +const getOeuvreReviews = require("../../application/use_cases/review/getOeuvreReviews"); +const getArtistReviews = require("../../application/use_cases/review/getArtistReviews"); +const putComment = require("../../application/use_cases/review/putComment"); +const getCountOeuvreReviews = require("../../application/use_cases/review/getCountOeuvreReviews"); +const handleError = require("./utils/handleError"); + +module.exports = { + async putReview(request, handler) { + try { + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(" "); + const { idOeuvre, note, description, type } = request.payload; + + return handler + .response( + await putReview( + idOeuvre, + token, + description, + note, + type, + serviceLocator + ) + ) + .code(200); + } catch (error) { + return handleError(error); + } + }, + async deleteReview(request, handler) { + try { + // Context + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(" "); + const { idReview } = request.payload; + await deleteReview(idReview, token, serviceLocator); + return handler.response("").code(200); + } catch (error) { + return handleError(error); + } + }, + async getReview(request, handler) { + try { + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + const { id } = request.params; + const { page, pageSize, orderByLike } = request.query; + const authorizationHeader = request.headers.authorization; + const token = authorizationHeader?.split(" ")[1]; + return handler + .response( + await getReview( + id, + token, + page, + pageSize, + orderByLike, + serviceLocator + ) + ) + .code(200); + } catch (error) { + return handleError(error); + } + }, + async getReviews(request, handler) { + try { + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + const { page, pageSize, orderByLike, friendsOnly } = request.query; + const authorizationHeader = request.headers.authorization; + const token = authorizationHeader?.split(" ")[1]; + return handler + .response( + await getReviews( + page, + pageSize, + orderByLike, + friendsOnly, + token, + serviceLocator + ) + ) + .code(200); + } catch (error) { + return handleError(error); + } + }, + async likeReview(request, handler) { + try { + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + const { id } = request.params; + const authorizationHeader = request.headers.authorization; + const token = authorizationHeader?.split(" ")[1]; + await likeReview(id, token, serviceLocator); + return handler.response("").code(200); + } catch (error) { + return handleError(error); + } + }, + async getLikes(request, handler) { + try { + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + const authorizationHeader = request.headers.authorization; + const token = authorizationHeader?.split(" ")[1]; + const { id } = request.params; + const { page, pageSize } = request.query; + return handler + .response( + await getReviewLikes(id, token, page, pageSize, serviceLocator) + ) + .code(200); + } catch (error) { + return handleError(error); + } + }, + async getUserReviews(request, handler) { + try { + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + const authorizationHeader = request.headers.authorization; + const token = authorizationHeader?.split(" ")[1]; + const { id } = request.params; + const { page, pageSize, orderByLike } = request.query; + return handler + .response( + await getUserReviews( + id, + token, + page, + pageSize, + orderByLike, + serviceLocator + ) + ) + .code(200); + } catch (error) { + return handleError(error); + } + }, + async getOeuvreReviews(request, handler) { + try { + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + const authorizationHeader = request.headers.authorization; + const token = authorizationHeader?.split(" ")[1]; + const { id } = request.params; + const { page, pageSize, orderByLike } = request.query; + const response = { + data: await getOeuvreReviews( + id, + token, + page, + pageSize, + orderByLike, + serviceLocator + ), + count: await getCountOeuvreReviews(id, token, serviceLocator), + }; + return handler.response(response).code(200); + } catch (error) { + return handleError(error); + } + }, + async getArtistReviews(request, handler) { + try { + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + const authorizationHeader = request.headers.authorization; + const token = authorizationHeader?.split(" ")[1]; + const { id } = request.params; + const { page, pageSize, orderByLike, friendsOnly } = request.query; + return handler + .response( + await getArtistReviews( + id, + token, + page, + pageSize, + orderByLike, + friendsOnly, + serviceLocator + ) + ) + .code(200); + } catch (error) { + return handleError(error); + } + }, + + async putComment(request, handler) { + try { + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const token = authorizationHeader?.split(" ")[1]; + const { id } = request.params; + const { description } = request.payload; + return handler + .response(await putComment(token, id, description, serviceLocator)) + .code(200); + } catch (error) { + return handleError(error); + } + }, + async getOeuvreReviews(request, handler) { + try { + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + const authorizationHeader = request.headers.authorization; + const token = authorizationHeader?.split(" ")[1]; + const { id } = request.params; + const { page, pageSize, orderByLike, friendsOnly } = request.query; + const response = { + data: await getOeuvreReviews( + id, + token, + page, + pageSize, + orderByLike, + friendsOnly, + serviceLocator + ), + count: await getCountOeuvreReviews(id, token, serviceLocator), + }; + return handler.response(response).code(200); + } catch (error) { + return handleError(error); + } + }, +}; diff --git a/lib/interfaces/controllers/SpotifyController.js b/lib/interfaces/controllers/SpotifyController.js index b5d08fd..dcbebff 100644 --- a/lib/interfaces/controllers/SpotifyController.js +++ b/lib/interfaces/controllers/SpotifyController.js @@ -1,18 +1,80 @@ const search = require("../../application/use_cases/spotify/Search"); +const getAlbum = require("../../application/use_cases/spotify/getAlbum"); +const fetchArtist = require("../../application/use_cases/spotify/FetchArtist"); +const getTrack = require("../../application/use_cases/spotify/getTrack"); +const getSearchFilters = require("../../application/use_cases/spotify/getSearchFilters"); const handleError = require("./utils/handleError"); +const fetchArtistSongs = require("../../application/use_cases/spotify/FetchArtistSongs"); + + module.exports = { async search(request,handler){ + try{ + // Context + const serviceLocator = request.server.app.serviceLocator; // a tous les repo + // Input + // spotify_filter = type de l'API Spotify + let { query, spotify_filter, limit} = request.query + const searchResult = await search(query,spotify_filter, limit, serviceLocator) + return handler.response(searchResult).code(200) + }catch(error){ + return handleError(error) + } + }, + async getSearchFilters(request,handler){ + try{ + + const searchFilters = await getSearchFilters() + return handler.response(searchFilters).code(200) + }catch(error){ + return handleError(error) + } + }, + async getAlbums(request,handler){ try{ // Context const serviceLocator = request.server.app.serviceLocator; // Input + let {id} = request.query + const result = await getAlbum(id, serviceLocator) + return handler.response(result).code(200) + }catch(error){ + return handleError(error) + } + }, - let { query, spotify_filter, limit, allow_user} = request.query - const searchResult = await search(query,spotify_filter, limit,allow_user, serviceLocator) - return handler.response(searchResult).code(200) + async getTracks(request,handler){ + try{ + // Context + const serviceLocator = request.server.app.serviceLocator; + // Input + let {id} = request.query + const result = await getTrack(id, serviceLocator) + return handler.response(result).code(200) + }catch(error){ + return handleError(error) + } + }, + + async fetchArtist(request,handler){ + try { + const serviceLocator = request.server.app.serviceLocator; + const id = request.query.query + const fetchArtistResult = await fetchArtist(id, serviceLocator) + return handler.response(fetchArtistResult).code(200) // tout s est bien passe + }catch(error){ + return handleError(error) + } + }, + async fetchArtistSongs(request,handler){ + try { + const serviceLocator = request.server.app.serviceLocator; + let {id, filter, limit} = request.query + const fetchArtistSongsResult = await fetchArtistSongs(id, filter, limit, serviceLocator) + return handler.response(fetchArtistSongsResult).code(200) }catch(error){ return handleError(error) } - } + }, } \ No newline at end of file diff --git a/lib/interfaces/controllers/UploadController.js b/lib/interfaces/controllers/UploadController.js new file mode 100644 index 0000000..198e41e --- /dev/null +++ b/lib/interfaces/controllers/UploadController.js @@ -0,0 +1,8 @@ +const Path = require('path'); +module.exports = { + async getImage(request,handler){ + const {filename} = request.params + + return handler.file(filename) + } +} \ No newline at end of file diff --git a/lib/interfaces/controllers/UsersController.js b/lib/interfaces/controllers/UsersController.js index e572d16..158a13d 100644 --- a/lib/interfaces/controllers/UsersController.js +++ b/lib/interfaces/controllers/UsersController.js @@ -1,37 +1,249 @@ -'use strict'; +"use strict"; + +const CreateUser = require("../../application/use_cases/user/CreateUser"); +const AuthWithSpotify = require("../../application/use_cases/user/AuthWithSpotify"); +const CompleteAccount = require("../../application/use_cases/user/CompleteAccount"); +const GetAccessToken = require("../../application/use_cases/security/GetAccessToken"); +const handleError = require("./utils/handleError"); +const uploadPreview = require("../../application/use_cases/user/uploadPreview"); +const getUserByPseudo = require("../../application/use_cases/user/getUserByPseudo"); +const getUserByConfirmToken = require("../../application/use_cases/user/getUserByConfirmToken"); +const sendResetEmail = require("../../application/use_cases/user/sendResetEmail"); +const resetPassword = require("../../application/use_cases/user/resetPassword"); +const changePrivateStatus = require("../../application/use_cases/user/changePrivateStatus"); +const refreshToken = require("../../application/use_cases/spotify/RefreshToken"); +const throwStatusCode = require("../../application/use_cases/utils/throwStatusCode"); +const follow = require("../../application/use_cases/user/follow"); +const oeuvreFav = require("../../application/use_cases/user/oeuvreFav"); +const getOeuvresFav = require("../../application/use_cases/user/getOeuvresFav"); +const getPage = require("../../application/use_cases/user/getPage"); +const modifyProfile = require("../../application/use_cases/user/modifyProfile"); -const CreateUser = require('../../application/use_cases/user/CreateUser'); -const GetAccessToken = require('../../application/use_cases/security/GetAccessToken'); -const handleError = require("./utils/handleError") module.exports = { + async confirmUser(request, handler) { + try { + // Context + const serviceLocator = request.server.app.serviceLocator; + // Input + + let { pseudo, photo, alias, bio, confirmToken } = request.payload; + return handler + .response( + await CompleteAccount( + pseudo, + alias, + bio, + photo, + confirmToken, + serviceLocator + ) + ) + .code(200); + } catch (error) { + return handleError(error); + } + }, - async createUser(request,handler) { - try{ - // Context - const serviceLocator = request.server.app.serviceLocator; - // Input + async signIn(request, handler) { + console.log("tesssssssssssssssssst"); + const serviceLocator = request.server.app.serviceLocator; + const { email, password } = request.payload; + try { + return handler + .response(await GetAccessToken(email, password, serviceLocator)) + .code(200); + } catch (error) { + return handleError(error); + } + }, + async uploadPreviewProfile(request, handler) { + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(" "); + const { file } = request.payload; + try { + return handler + .response({ + path: `${request.server.info.uri}/${await uploadPreview( + file, + token, + serviceLocator + )}`, + }) + .code(200); + } catch (e) { + return handleError(e); + } + }, + async authWithSpotify(request, handler) { + const serviceLocator = request.server.app.serviceLocator; + const { spotify_code, callback } = request.payload; + try { + const returnValue = await AuthWithSpotify( + spotify_code, + callback, + serviceLocator + ); + if (returnValue.idUtilisateur) { + refreshToken(returnValue.idUtilisateur, false, serviceLocator); + delete returnValue.idUtilisateur; + } + return handler.response(returnValue).code(200); + } catch (e) { + return handleError(e); + } + }, + async createUser(request, handler) { + const serviceLocator = request.server.app.serviceLocator; + const { email, password } = request.payload; + try { + console.log(email, password); + const user = await CreateUser(email, password, serviceLocator); - let { pseudo, email, alias, bio, password,spotifyToken } = request.payload - await CreateUser(pseudo, email, alias, bio, password,spotifyToken, serviceLocator); - return handler.response('Votre compte a bien été crée').code(200) - }catch(error){ - return handleError(error) - } - }, + return handler.response("compte créé").code(200); + } catch (e) { + return handleError(e); + } + }, + async isUser(request, handler) { + const serviceLocator = request.server.app.serviceLocator; + const { pseudo } = request.query; + try { + const user = await getUserByPseudo(pseudo, serviceLocator); + return handler.response({ isUser: !!user }).code(200); + } catch (e) { + return handleError(e); + } + }, + async getUserByConfirmToken(request, handler) { + const serviceLocator = request.server.app.serviceLocator; + const { confirmToken } = request.query; + try { + const user = await getUserByConfirmToken(confirmToken, serviceLocator); + if (!user) throwStatusCode(403, "no user"); + return handler.response(user).code(200); + } catch (e) { + return handleError(e); + } + }, + async sendResetEmail(request, handler) { + const serviceLocator = request.server.app.serviceLocator; + const { email } = request.payload; + try { + await sendResetEmail(email, serviceLocator); + return handler.response("un email a été envoyé").code(200); + } catch (e) { + return handleError(e); + } + }, + async resetPassword(request, handler) { + const serviceLocator = request.server.app.serviceLocator; + const { password, resetToken } = request.payload; + try { + await resetPassword(password, resetToken, serviceLocator); + return handler.response("un email a été envoyé").code(200); + } catch (e) { + return handleError(e); + } + }, + async follow(request, handler) { + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(" "); + const { artistId } = request.payload; + try { + const returnValue = (await follow(token, artistId, serviceLocator)) + ? "artiste suivi" + : "vous avez arrêté de suivre l'artiste"; + return handler.response(returnValue).code(200); + } catch (error) { + return handleError(error); + } + }, + async changePrivateStatus(request, handler) { + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(" "); + try { + await changePrivateStatus(token, serviceLocator); + return handler + .response("Le statut du compte a bien été mis à jour") + .code(200); + } catch (e) { + return handleError(e); + } + }, + async oeuvreFav(request, handler) { + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(" "); + const { idOeuvre, type } = request.payload; + console.log(request.payload); + try { + // await oeuvreFav(idOeuvre,serviceLocator); + const returnValue = (await oeuvreFav( + token, + idOeuvre, + type, + serviceLocator + )) + ? "L'oeuvre a été rajoutée en favori" + : "L'oeuvre a été retirée de vos favori"; + return handler.response(returnValue).code(200); + } catch (e) { + return handleError(e); + } + }, - async signIn(request,handler){ + async getOeuvresFav(request, handler) { + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(" "); + try { + const arrayOeuvresFav = await getOeuvresFav(token, serviceLocator); - const serviceLocator = request.server.app.serviceLocator; - const {email, password} = request.payload - try{ - return handler - .response({ - email : email, - token: await GetAccessToken(email,password,serviceLocator) - }) - } - catch (error){ - return handleError(error) - } + return handler.response(arrayOeuvresFav).code(200); + } catch (e) { + return handleError(e); + } + }, + async getPage(request, handler) { + try { + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(" "); + const { page, pageSize, orderByLike } = request.query; + const { id } = request.params; + return handler + .response( + await getPage(id, page, pageSize, orderByLike, token, serviceLocator) + ) + .code(200); + } catch (e) { + return handleError(e); + } + }, + async modify(request, handler) { + try { + const serviceLocator = request.server.app.serviceLocator; + const authorizationHeader = request.headers.authorization; + const [, token] = authorizationHeader.split(" "); + const { photo, pseudo, bio, alias, isPrivate } = request.payload; + return handler + .response( + await modifyProfile( + photo, + pseudo, + bio, + alias, + isPrivate, + token, + serviceLocator + ) + ) + .code(200); + } catch (e) { + return handleError(e); } + }, }; diff --git a/lib/interfaces/controllers/utils/handleError.js b/lib/interfaces/controllers/utils/handleError.js index 2b19f3d..caa37d7 100644 --- a/lib/interfaces/controllers/utils/handleError.js +++ b/lib/interfaces/controllers/utils/handleError.js @@ -1,7 +1,7 @@ const Boom = require("@hapi/boom") module.exports = (error) => { - const statusCode = error.code ? error.code : 500 + const statusCode = error?.code ? error.code : 500 console.log(error) return Boom.boomify(error, {statusCode: statusCode}) } \ No newline at end of file diff --git a/lib/interfaces/routes/artist.js b/lib/interfaces/routes/artist.js new file mode 100644 index 0000000..7d71258 --- /dev/null +++ b/lib/interfaces/routes/artist.js @@ -0,0 +1,66 @@ +const ArtistController = require("../controllers/ArtistController"); +const { + getArtist +} = require("../../domain/entity/ArtistEntity"); +module.exports = { + name: 'artist', + version: '1.0.0', + register: async (server) => { + server.route([ + { + method: 'GET', + path: '/artist/{id}', + handler: ArtistController.get, + options: { + auth: 'jwt', + description: 'get artist information', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 204: {description : 'No content'}, + 401: {description : 'Unauthorized'}, + 403: {description : 'forbidden'}, + 404: {description : 'Ressource not found'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + 503: {description : 'Service unavailable'}, + } + } + }, + validate: { + params: getArtist + } + }, + }, + { + method: 'GET', + path: '/artist/{id}/followers', + handler: ArtistController.getFollowers, + options: { + auth: 'jwt', + description: 'get artist followers', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 204: {description : 'No content'}, + 401: {description : 'Unauthorized'}, + 403: {description : 'forbidden'}, + 404: {description : 'Ressource not found'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + 503: {description : 'Service unavailable'}, + } + } + }, + validate: { + params: getArtist + } + }, + }, + ]); + } +}; \ No newline at end of file diff --git a/lib/interfaces/routes/comment.js b/lib/interfaces/routes/comment.js new file mode 100644 index 0000000..ba103ec --- /dev/null +++ b/lib/interfaces/routes/comment.js @@ -0,0 +1,126 @@ +const CommentController = require("../controllers/CommentController"); +const { + getCommentParams, + deleteCommentParams, + likeCommentParams, + putCommentPayload, + putCommentParams, + getCommentQuery +} = require("../../domain/entity/CommentEntity"); +module.exports = { + name: 'comment', + version: '1.0.0', + register: async (server) => { + server.route([ + { + method: 'GET', + path: '/comment/{id}', + handler: CommentController.get, + options: { + description: 'get a comment', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 204: {description : 'No content'}, + 401: {description : 'Unauthorized'}, + 403: {description : 'forbidden'}, + 404: {description : 'Ressource not found'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + 503: {description : 'Service unavailable'}, + } + } + }, + validate: { + params: getCommentParams, + query: getCommentQuery + } + }, + }, + { + method: 'DELETE', + path: '/comment/{id}', + handler: CommentController.delete, + options: { + auth: 'jwt', + description: 'delete a comment', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 204: {description : 'No content'}, + 401: {description : 'Unauthorized'}, + 403: {description : 'forbidden'}, + 404: {description : 'Ressource not found'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + 503: {description : 'Service unavailable'}, + } + } + }, + validate: { + params: deleteCommentParams + } + }, + }, + { + method: 'POST', + path: '/comment/{id}/like', + handler: CommentController.like, + options: { + auth: 'jwt', + description: 'like a comment', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description: 'Success'}, + 204: {description: 'No content'}, + 401: {description: 'Unauthorized'}, + 403: {description: 'forbidden'}, + 404: {description: 'Ressource not found'}, + 500: {description: 'Internal server error'}, + 502: {description: 'bad gateway'}, + 503: {description: 'Service unavailable'}, + } + } + }, + validate: { + params: likeCommentParams, + } + } + }, + { + method: 'PUT', + path: '/comment/{id}', + handler: CommentController.put, + options: { + auth: 'jwt', + description: 'get review list', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description: 'Success'}, + 204: {description: 'No content'}, + 401: {description: 'Unauthorized'}, + 403: {description: 'forbidden'}, + 404: {description: 'Ressource not found'}, + 500: {description: 'Internal server error'}, + 502: {description: 'bad gateway'}, + 503: {description: 'Service unavailable'}, + } + } + }, + validate: { + payload: putCommentPayload, + params: putCommentParams, + } + } + }, + ]) + } +}; \ No newline at end of file diff --git a/lib/interfaces/routes/friends.js b/lib/interfaces/routes/friends.js new file mode 100644 index 0000000..b54d548 --- /dev/null +++ b/lib/interfaces/routes/friends.js @@ -0,0 +1,155 @@ +'use strict'; + +const FriendsController = require('../controllers/FriendsController') +const Joi = require('joi') + +module.exports = { + name: 'amis', + version: '1.0.0', + register: async (server) => { + + server.route([ + { + method: 'POST', + path: '/amis/follow', + handler: FriendsController.followUser, + options: { + description: 'Follow a user', + tags: ['api'], + auth: 'jwt', + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 403: {description : 'forbidden'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + } + } + }, + validate: { + payload: Joi.object().keys({ + amiIdUtilisateur: Joi.number().required(), + }) + } + }, + }, + { + method: 'POST', + path: '/amis/unfollow', + handler: FriendsController.unfollowUser, + options: { + description: 'Unfollow a user', + tags: ['api'], + auth: 'jwt', + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 403: {description : 'forbidden'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + } + } + }, + validate: { + payload: Joi.object().keys({ + amiIdUtilisateur: Joi.number().required(), + }) + } + }, + }, + { + method: 'POST', + path: '/amis/accept', + handler: FriendsController.acceptRequestUser, + options: { + description: 'Accept a friend request', + tags: ['api'], + auth: 'jwt', + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 403: {description : 'forbidden'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + } + } + }, + validate: { + payload: Joi.object().keys({ + amiIdUtilisateur: Joi.number().required(), + }) + } + }, + }, + { + method: 'GET', + path: '/amis/request', + handler: FriendsController.getListFriendsRequest, + options: { + description: 'Get a user\'s request friends list', + tags: ['api'], + auth: 'jwt', + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 403: {description : 'forbidden'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + } + } + }, + }, + }, + { + method: 'GET', + path: '/amis/list', + handler: FriendsController.getListFriends, + options: { + description: 'Get a user\'s friends list', + tags: ['api'], + auth: 'jwt', + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 403: {description : 'forbidden'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + } + } + }, + } + }, + { + method: 'POST', + path: '/amis/profil', + handler: FriendsController.getProfilFriend, + options: { + description: 'Get a friend profil', + tags: ['api'], + auth: 'jwt', + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 403: {description : 'forbidden'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + } + } + }, + validate: { + payload: Joi.object().keys({ + amiIdUtilisateur: Joi.number().required(), + }) + } + } + }, + + ]); + } +}; \ No newline at end of file diff --git a/lib/interfaces/routes/oeuvre.js b/lib/interfaces/routes/oeuvre.js new file mode 100644 index 0000000..ff39926 --- /dev/null +++ b/lib/interfaces/routes/oeuvre.js @@ -0,0 +1,68 @@ +const OeuvreController = require("../controllers/OeuvreController.js"); + +const { + likeOeuvre, + getOeuvre +} = require("../../domain/entity/OeuvreEntity.js"); +module.exports = { + name: 'oeuvre', + version: '1.0.0', + register: async (server) => { + server.route([ + { + method: 'POST', + path: '/oeuvre/{type}/{id}/like', + handler: OeuvreController.like, + options: { + auth: 'jwt', + description: 'like/unlike an oeuvre', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 204: {description : 'No content'}, + 401: {description : 'Unauthorized'}, + 403: {description : 'forbidden'}, + 404: {description : 'Ressource not found'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + 503: {description : 'Service unavailable'}, + } + } + }, + validate: { + params: likeOeuvre + } + }, + }, + { + method: 'GET', + path: '/oeuvre/{id}', + handler: OeuvreController.get, + options: { + auth: 'jwt', + description: 'get oeuvre information', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 204: {description : 'No content'}, + 401: {description : 'Unauthorized'}, + 403: {description : 'forbidden'}, + 404: {description : 'Ressource not found'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + 503: {description : 'Service unavailable'}, + } + } + }, + validate: { + params: getOeuvre + } + }, + } + ]); + } +}; \ No newline at end of file diff --git a/lib/interfaces/routes/review.js b/lib/interfaces/routes/review.js new file mode 100644 index 0000000..aa0341a --- /dev/null +++ b/lib/interfaces/routes/review.js @@ -0,0 +1,296 @@ +const ReviewController = require("../controllers/ReviewController"); +const { + putReview, + deleteReview, + getReviews, + getReviewParams, + getReviewQuery, + likeReviewParams, + likeReviewQuery, + userReviewParams, + putCommentParams, + putCommentPayload, + userReviewQuery, + oeuvreReviewParams, + oeuvreReviewQuery, + artistReviewParams, + artistReviewQuery, +} = require("../../domain/entity/ReviewEntity"); +module.exports = { + name: "review", + version: "1.0.0", + register: async (server) => { + server.route([ + { + method: "PUT", + path: "/review", + handler: ReviewController.putReview, + options: { + auth: "jwt", + description: "create a review", + tags: ["api"], + plugins: { + "hapi-swagger": { + responses: { + 200: { description: "Success" }, + 204: { description: "No content" }, + 401: { description: "Unauthorized" }, + 403: { description: "forbidden" }, + 404: { description: "Ressource not found" }, + 500: { description: "Internal server error" }, + 502: { description: "bad gateway" }, + 503: { description: "Service unavailable" }, + }, + }, + }, + validate: { + payload: putReview, + }, + }, + }, + { + method: "DELETE", + path: "/review", + handler: ReviewController.deleteReview, + options: { + auth: "jwt", + description: "delete a review", + tags: ["api"], + plugins: { + "hapi-swagger": { + responses: { + 200: { description: "Success" }, + 204: { description: "No content" }, + 401: { description: "Unauthorized" }, + 403: { description: "forbidden" }, + 404: { description: "Ressource not found" }, + 500: { description: "Internal server error" }, + 502: { description: "bad gateway" }, + 503: { description: "Service unavailable" }, + }, + }, + }, + validate: { + payload: deleteReview, + }, + }, + }, + { + method: "GET", + path: "/review/{id}", + handler: ReviewController.getReview, + options: { + description: "get a review", + tags: ["api"], + plugins: { + "hapi-swagger": { + responses: { + 200: { description: "Success" }, + 204: { description: "No content" }, + 401: { description: "Unauthorized" }, + 403: { description: "forbidden" }, + 404: { description: "Ressource not found" }, + 500: { description: "Internal server error" }, + 502: { description: "bad gateway" }, + 503: { description: "Service unavailable" }, + }, + }, + }, + validate: { + params: getReviewParams, + query: getReviewQuery, + }, + }, + }, + { + method: "GET", + path: "/reviews", + handler: ReviewController.getReviews, + options: { + description: "get review list", + tags: ["api"], + plugins: { + "hapi-swagger": { + responses: { + 200: { description: "Success" }, + 204: { description: "No content" }, + 401: { description: "Unauthorized" }, + 403: { description: "forbidden" }, + 404: { description: "Ressource not found" }, + 500: { description: "Internal server error" }, + 502: { description: "bad gateway" }, + 503: { description: "Service unavailable" }, + }, + }, + }, + validate: { + query: getReviews, + }, + }, + }, + { + method: "POST", + path: "/review/{id}/like", + handler: ReviewController.likeReview, + options: { + auth: "jwt", + description: "like a review", + tags: ["api"], + plugins: { + "hapi-swagger": { + responses: { + 200: { description: "Success" }, + 204: { description: "No content" }, + 401: { description: "Unauthorized" }, + 403: { description: "forbidden" }, + 404: { description: "Ressource not found" }, + 500: { description: "Internal server error" }, + 502: { description: "bad gateway" }, + 503: { description: "Service unavailable" }, + }, + }, + }, + validate: { + params: likeReviewParams, + }, + }, + }, + { + method: "GET", + path: "/review/{id}/likes", + handler: ReviewController.getLikes, + options: { + description: "get review' likes", + tags: ["api"], + plugins: { + "hapi-swagger": { + responses: { + 200: { description: "Success" }, + 204: { description: "No content" }, + 401: { description: "Unauthorized" }, + 403: { description: "forbidden" }, + 404: { description: "Ressource not found" }, + 500: { description: "Internal server error" }, + 502: { description: "bad gateway" }, + 503: { description: "Service unavailable" }, + }, + }, + }, + validate: { + params: likeReviewParams, + query: likeReviewQuery, + }, + }, + }, + { + method: "GET", + path: "/reviews/user/{id}", + handler: ReviewController.getUserReviews, + options: { + description: "get review' likes", + tags: ["api"], + plugins: { + "hapi-swagger": { + responses: { + 200: { description: "Success" }, + 204: { description: "No content" }, + 401: { description: "Unauthorized" }, + 403: { description: "forbidden" }, + 404: { description: "Ressource not found" }, + 500: { description: "Internal server error" }, + 502: { description: "bad gateway" }, + 503: { description: "Service unavailable" }, + }, + }, + }, + validate: { + params: userReviewParams, + query: userReviewQuery, + }, + }, + }, + { + method: "GET", + path: "/reviews/oeuvre/{id}", + handler: ReviewController.getOeuvreReviews, + options: { + description: "get oeuvre reviews", + tags: ["api"], + plugins: { + "hapi-swagger": { + responses: { + 200: { description: "Success" }, + 204: { description: "No content" }, + 401: { description: "Unauthorized" }, + 403: { description: "forbidden" }, + 404: { description: "Ressource not found" }, + 500: { description: "Internal server error" }, + 502: { description: "bad gateway" }, + 503: { description: "Service unavailable" }, + }, + }, + }, + validate: { + params: oeuvreReviewParams, + query: oeuvreReviewQuery, + }, + }, + }, + { + method: "GET", + path: "/reviews/artist/{id}", + handler: ReviewController.getArtistReviews, + options: { + description: "get artitst' reviews", + tags: ["api"], + plugins: { + "hapi-swagger": { + responses: { + 200: { description: "Success" }, + 204: { description: "No content" }, + 401: { description: "Unauthorized" }, + 403: { description: "forbidden" }, + 404: { description: "Ressource not found" }, + 500: { description: "Internal server error" }, + 502: { description: "bad gateway" }, + 503: { description: "Service unavailable" }, + }, + }, + }, + validate: { + params: artistReviewParams, + query: artistReviewQuery, + }, + }, + }, + { + method: "PUT", + path: "/review/{id}/comment", + handler: ReviewController.putComment, + options: { + auth: "jwt", + description: "put a comment", + tags: ["api"], + plugins: { + "hapi-swagger": { + responses: { + 200: { description: "Success" }, + 204: { description: "No content" }, + 401: { description: "Unauthorized" }, + 403: { description: "forbidden" }, + 404: { description: "Ressource not found" }, + 500: { description: "Internal server error" }, + 502: { description: "bad gateway" }, + 503: { description: "Service unavailable" }, + }, + }, + }, + validate: { + params: putCommentParams, + payload: putCommentPayload, + }, + }, + }, + ]); + }, +}; diff --git a/lib/interfaces/routes/spotify.js b/lib/interfaces/routes/spotify.js index ea35393..0070202 100644 --- a/lib/interfaces/routes/spotify.js +++ b/lib/interfaces/routes/spotify.js @@ -1,4 +1,4 @@ -const {trackBody} = require("../../domain/entity/SpotifyEntity") +const {album, search,track,fetchArtist, fetchArtistSongs } = require("../../domain/entity/SpotifyEntity") const spotify = require("../controllers/SpotifyController") module.exports = { name: 'spotify', @@ -10,6 +10,133 @@ module.exports = { method: 'GET', path: '/spotify/search', handler: spotify.search, + options: { + description: 'get a spotify search', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: { description: 'Success' }, + 204: { description: 'No content' }, + 401: { description: 'Unauthorized' }, + 403: { description: 'forbidden' }, + 404: { description: 'Ressource not found' }, + 500: { description: 'Internal server error' }, + 502: { description: 'bad gateway' }, + 503: { description: 'Service unavailable' }, + } + } + }, + validate: { + query: search + } + }, + }, + { + method: 'GET', + path: '/spotify/Searchfilters', + handler: spotify.getSearchFilters, + options: { + description: 'get spotify search filters', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: { description: 'Success' }, + 204: { description: 'No content' }, + 401: { description: 'Unauthorized' }, + 403: { description: 'forbidden' }, + 404: { description: 'Ressource not found' }, + 500: { description: 'Internal server error' }, + 502: { description: 'bad gateway' }, + 503: { description: 'Service unavailable' }, + } + } + }, + }, + }, + { + method: 'GET', + path: '/spotify/fetchArtist', + handler: spotify.fetchArtist, + options: { + description: 'get Artist informations', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: { description: 'Success' }, + 204: { description: 'No content' }, + 401: { description: 'Unauthorized' }, + 403: { description: 'forbidden' }, + 404: { description: 'Ressource not found' }, + 500: { description: 'Internal server error' }, + 502: { description: 'bad gateway' }, + 503: { description: 'Service unavailable' }, + } + } + }, + validate: { + query: fetchArtist + } + }, + }, + { + method: 'GET', + path: '/spotify/fetchArtistSongs', + handler: spotify.fetchArtistSongs, + options: { + description: 'get Artist Songs informations', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: { description: 'Success' }, + 204: { description: 'No content' }, + 401: { description: 'Unauthorized' }, + 403: { description: 'forbidden' }, + 404: { description: 'Ressource not found' }, + 500: { description: 'Internal server error' }, + 502: { description: 'bad gateway' }, + 503: { description: 'Service unavailable' }, + } + } + }, + validate: { + query: fetchArtistSongs + } + }, + }, + { + method: 'GET', + path: '/spotify/album', + handler: spotify.getAlbums, + options: { + description: 'get a spotify album', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 204: {description : 'No content'}, + 401: {description : 'Unauthorized'}, + 403: {description : 'forbidden'}, + 404: {description : 'Ressource not found'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + 503: {description : 'Service unavailable'}, + } + } + }, + validate: { + query: album + } + }, + }, + { + method: 'GET', + path: '/spotify/track', + handler: spotify.getTracks, options: { description: 'get a spotify track', tags: ['api'], @@ -28,10 +155,11 @@ module.exports = { } }, validate: { - query: trackBody + query: track } }, - } + }, + ]); } }; \ No newline at end of file diff --git a/lib/interfaces/routes/upload.js b/lib/interfaces/routes/upload.js new file mode 100644 index 0000000..f0d085e --- /dev/null +++ b/lib/interfaces/routes/upload.js @@ -0,0 +1,30 @@ +'use strict'; +const {userSignUp, userSignIn} = require('../../domain/entity/UserEntity') +const UploadController = require('../controllers/UploadController'); + +module.exports = { + name: 'upload', + version: '1.0.0', + register: async (server) => { + + server.route([ + { + method: 'GET', + path: '/upload/{filename}', + handler: UploadController.getImage, + options: { + description: 'Create a static image', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 204: {description : 'No content'}, + } + } + }, + }, + }, + ]); + } +}; \ No newline at end of file diff --git a/lib/interfaces/routes/users.js b/lib/interfaces/routes/users.js index cd0415e..dc66e97 100644 --- a/lib/interfaces/routes/users.js +++ b/lib/interfaces/routes/users.js @@ -1,6 +1,22 @@ 'use strict'; -const {userSignUp, userSignIn} = require('../../domain/entity/UserEntity') +const { + userSignUp, + userSignIn, + uploadPreview, + createUser, + isUser, + getUserByConfirmToken, + sendResetEmail, + resetPassword, + follow, + authWithSpotify, + oeuvreFav, + getPageQuery, + getPageParams, + modifyPayload +} = require('../../domain/entity/UserEntity') const UsersController = require('../controllers/UsersController'); +const MAX_BYTE_SIZE =20971520 module.exports = { name: 'users', version: '1.0.0', @@ -9,10 +25,10 @@ module.exports = { server.route([ { method: 'POST', - path: '/users/signup', - handler: UsersController.createUser, + path: '/users/confirmUser', + handler: UsersController.confirmUser, options: { - description: 'Create a user', + description: 'Confirm a user', tags: ['api'], plugins: { 'hapi-swagger': { @@ -64,6 +80,315 @@ module.exports = { } }, }, + { + method: 'POST', + path: '/users/uploadPreviewProfile', + handler: UsersController.uploadPreviewProfile, + options: { + payload: { + maxBytes: MAX_BYTE_SIZE, // Set your desired maximum payload size in bytes + output: 'stream', + parse: true, + allow: 'multipart/form-data', + multipart: true, // Set multipart to true for handling file uploads + }, + auth: 'jwt', + description: 'allow to upload preview profile picture', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 204: {description : 'No content'}, + 401: {description : 'Unauthorized'}, + 403: {description : 'forbidden'}, + 404: {description : 'Ressource not found'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + 503: {description : 'Service unavailable'}, + } + } + }, + validate: { + payload: uploadPreview + } + }, + }, + { + method: 'POST', + path: '/users/createUser', + handler: UsersController.createUser, + options: { + description: 'send Verification email to create account', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 403: {description : 'forbidden'}, + 500: {description : 'Internal server error'}, + } + } + }, + validate: { + payload: createUser + } + }, + }, + { + method: 'POST', + path: '/users/authWithSpotify', + handler: UsersController.authWithSpotify, + options: { + description: 'send Verification email to create account', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 403: {description : 'forbidden'}, + 500: {description : 'Internal server error'}, + } + } + }, + validate: { + payload: authWithSpotify + } + }, + }, + { + method: 'GET', + path: '/users/isUser', + handler: UsersController.isUser, + options: { + description: 'check if a user exists', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 500: {description : 'Internal server error'}, + } + } + }, + validate: { + query: isUser + } + } + }, + { + method: 'GET', + path: '/users/getUserByConfirmToken', + handler: UsersController.getUserByConfirmToken, + options: { + description: 'get user info', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description: 'Success'}, + 403: {description : 'forbidden'}, + 500: {description: 'Internal server error'}, + } + } + }, + validate: { + query: getUserByConfirmToken + } + } + }, + { + method: 'POST', + path: '/users/sendResetEmail', + handler: UsersController.sendResetEmail, + options: { + description: 'send reset email', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description: 'Success'}, + 500: {description: 'Internal server error'}, + } + } + }, + validate: { + payload: sendResetEmail + } + } + }, + { + method: 'POST', + path: '/users/resetPassword', + handler: UsersController.resetPassword, + options: { + description: 'reset user password', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description: 'Success'}, + 403: {description : 'forbidden'}, + 500: {description: 'Internal server error'}, + } + } + }, + validate: { + payload: resetPassword + } + } + }, + { + method: 'POST', + path: '/users/follow', + handler: UsersController.follow, + options: { + auth: 'jwt', + description: 'follow artist', + tags: ['api'], + plugins: { + 'hapi-swagger': { + responses: { + 200: {description : 'Success'}, + 204: {description : 'No content'}, + 401: {description : 'Unauthorized'}, + 403: {description : 'forbidden'}, + 404: {description : 'Ressource not found'}, + 500: {description : 'Internal server error'}, + 502: {description : 'bad gateway'}, + 503: {description : 'Service unavailable'}, + } + } + }, + validate: { + payload: follow + } + }, + }, + { + method: 'POST', + path: '/users/status', + handler: UsersController.changePrivateStatus, + options: { + description: 'set a user in statut private', + tags: ['api'], + auth: 'jwt', + plugins: { + 'hapi-swagger': { + responses: { + 200: {description: 'Success'}, + 403: {description : 'forbidden'}, + 500: {description: 'Internal server error'}, + } + } + }, + } + }, + { + method: 'POST', + path: '/users/oeuvreFav', + handler: UsersController.oeuvreFav, + options: { + description: 'add favorite music', + tags: ['api'], + auth: 'jwt', + plugins: { + 'hapi-swagger': { + responses: { + 200: {description: 'Success'}, + 401: {description : 'Unauthorized'}, + 403: {description : 'forbidden'}, + 404: {description : 'Ressource not found'}, + 500: {description: 'Internal server error'}, + 503: {description : 'Service unavailable'}, + } + } + }, + validate: { + payload: oeuvreFav + } + } + }, + { + method: 'GET', + path: '/users/getOeuvresFav', + handler: UsersController.getOeuvresFav, + options: { + description: 'get favorites music', + tags: ['api'], + auth: 'jwt', + plugins: { + 'hapi-swagger': { + responses: { + 200: {description: 'Success'}, + 401: {description : 'Unauthorized'}, + 403: {description : 'forbidden'}, + 404: {description : 'Ressource not found'}, + 500: {description: 'Internal server error'}, + 503: {description : 'Service unavailable'}, + } + } + } + } + }, + { + method: 'GET', + path: '/users/{id}/page', + handler: UsersController.getPage, + options: { + description: 'get artist page', + tags: ['api'], + auth: 'jwt', + plugins: { + 'hapi-swagger': { + responses: { + 200: {description: 'Success'}, + 401: {description : 'Unauthorized'}, + 403: {description : 'forbidden'}, + 404: {description : 'Ressource not found'}, + 500: {description: 'Internal server error'}, + 503: {description : 'Service unavailable'}, + } + } + }, + validate: { + query: getPageQuery, + params: getPageParams + } + } + }, + { + method: 'POST', + path: '/users/modify', + handler: UsersController.modify, + options: { + description: 'modify user info', + tags: ['api'], + auth: 'jwt', + payload: { + maxBytes: MAX_BYTE_SIZE, // Set your desired maximum payload size in bytes + output: 'stream', + parse: true, + allow: 'multipart/form-data', + multipart: true, // Set multipart to true for handling file uploads + }, + plugins: { + 'hapi-swagger': { + responses: { + 200: {description: 'Success'}, + 401: {description : 'Unauthorized'}, + 403: {description : 'forbidden'}, + 404: {description : 'Ressource not found'}, + 500: {description: 'Internal server error'}, + 503: {description : 'Service unavailable'}, + } + } + }, + validate: { + payload: modifyPayload, + } + } + } ]); } }; \ No newline at end of file diff --git a/lib/interfaces/serializers/AlbumSerializer.js b/lib/interfaces/serializers/AlbumSerializer.js index 508d554..277161e 100644 --- a/lib/interfaces/serializers/AlbumSerializer.js +++ b/lib/interfaces/serializers/AlbumSerializer.js @@ -1,17 +1,26 @@ -const Album = require("../../domain/model/Album") -const ArtistSerializer = require("./ArtistSerializer") +const Album = require("../../domain/model/Album"); +const SerializeArtist = require("./ArtistSerializer"); +const SerializeTrack = require("./AlbumTrackSerializer"); + const serializeAlbum = (albumRaw) => { + const tracks = albumRaw.tracks?.items ? albumRaw.tracks?.items.map(item => SerializeTrack(item)) : undefined const album = { id: albumRaw.id, total_tracks: albumRaw.total_tracks, spotify_url : albumRaw.external_urls.spotify, name: albumRaw.name, - images: albumRaw.images, + image: albumRaw?.images ? albumRaw.images[0].url : null, release_date : albumRaw.release_date, - artists : albumRaw?.artists?.map(item => ArtistSerializer(item)), + artists : albumRaw?.artists?.map(item => SerializeArtist(item)), + tracks: tracks, genres : albumRaw?.genres, - popularity: albumRaw.popularity ? albumRaw.popularity : Math.floor(Math.random() * 100) - } + rating: albumRaw?.rating, + reviewCount: albumRaw?.reviewCount, + type : albumRaw?.album_type, // utilise album_group (plus precis pour fetchArtistSongs pour recuperer appears_on) sinon album_type (present dans fetchAlbum et fetchArtistSongs, n'a pas appears_on) + likeCount: albumRaw?.likeCount, + popularity: albumRaw.popularity ? albumRaw.popularity : Math.floor(Math.random() * 100), + }; return new Album(album) } -module.exports = serializeAlbum \ No newline at end of file + +module.exports = serializeAlbum diff --git a/lib/interfaces/serializers/AlbumTrackSerializer.js b/lib/interfaces/serializers/AlbumTrackSerializer.js new file mode 100644 index 0000000..26b4d5c --- /dev/null +++ b/lib/interfaces/serializers/AlbumTrackSerializer.js @@ -0,0 +1,18 @@ +// TrackSerializer.js +const Track = require("../../domain/model/Track"); +const SerializeArtist = require("./ArtistSerializer"); + +const serializeTrack = (trackRaw) => { + const track = { + id: trackRaw.id, + name: trackRaw.name, + spotify_url : trackRaw?.external_urls.spotify, + duration_ms: trackRaw.duration_ms, + rating: trackRaw?.rating, + likeCount: trackRaw?.likeCount, + reviewCount: trackRaw?.reviewCount, + }; + return new Track(track) +}; + +module.exports = serializeTrack \ No newline at end of file diff --git a/lib/interfaces/serializers/ArtistSerializer.js b/lib/interfaces/serializers/ArtistSerializer.js index 2bd457b..24f1358 100644 --- a/lib/interfaces/serializers/ArtistSerializer.js +++ b/lib/interfaces/serializers/ArtistSerializer.js @@ -1,13 +1,17 @@ -const Artist = require("../../domain/model/Artist") +const Artist = require("../../domain/model/Artist"); const serializeArtiste = (artisteRaw) => { - const artist = { - id: artisteRaw.id, - name: artisteRaw.name, - images: artisteRaw?.images, - spotify_url : artisteRaw?.external_urls?.spotify, - popularity: artisteRaw?.popularity, - genres : artisteRaw?.genres - } - return new Artist(artist) -} -module.exports=serializeArtiste \ No newline at end of file + const artist = { + id: artisteRaw.id, + name: artisteRaw.name, + follower_count: artisteRaw?.follower_count, + image: + artisteRaw?.images && artisteRaw.images.length > 0 + ? artisteRaw.images[0].url + : null, + spotify_url: artisteRaw?.external_urls?.spotify, + popularity: artisteRaw?.popularity, + genres: artisteRaw?.genres, + }; + return new Artist(artist); +}; +module.exports = serializeArtiste; diff --git a/lib/interfaces/serializers/OeuvreSerializer.js b/lib/interfaces/serializers/OeuvreSerializer.js new file mode 100644 index 0000000..9899f3d --- /dev/null +++ b/lib/interfaces/serializers/OeuvreSerializer.js @@ -0,0 +1,20 @@ +const artistSerializer = require("./ArtistSerializer"); +const trackSerializer = require("./TrackSerializer"); +const albumSerializer = require("./AlbumSerializer"); +const oeuvreSerializer = (rawOeuvre, type) => { + console.log(type); + switch (type) { + case "artist": + return artistSerializer(rawOeuvre); + case "track": + return trackSerializer(rawOeuvre); + case "album": + return albumSerializer(rawOeuvre); + case "single": + return albumSerializer(rawOeuvre); + default: + return null; + } +}; + +module.exports = oeuvreSerializer; diff --git a/lib/interfaces/serializers/ReviewSerializer.js b/lib/interfaces/serializers/ReviewSerializer.js new file mode 100644 index 0000000..8ed09d2 --- /dev/null +++ b/lib/interfaces/serializers/ReviewSerializer.js @@ -0,0 +1,56 @@ +const artistSerializer = require("./ArtistSerializer"); +const trackSerializer = require("./TrackSerializer"); +const albumSerializer = require("./AlbumSerializer"); +const UserPublic = require("../../domain/model/UserPublic"); +const ReviewPublic = require("../../domain/model/ReviewPublic"); +const serializeReview = async ( + rawReview, + id_utilisateur, + comments, + spotifyRepository, + reviewRepository, + friendRepository +) => { + let doesUserLike = false; + if (id_utilisateur) { + doesUserLike = await reviewRepository.doesUserLikes( + id_utilisateur, + rawReview.id_review + ); + } + let rawOeuvre = await spotifyRepository.getOeuvre( + rawReview.id_oeuvre, + rawReview.type + ); + if (rawOeuvre.error) + throwStatusCode(rawOeuvre.error.status, rawOeuvre.error.message); + rawReview.made_by_friend = await friendRepository.areFriends( + id_utilisateur, + rawReview.utilisateur.id_utilisateur + ); + switch (rawReview.type) { + case "artist": + rawOeuvre = artistSerializer(rawOeuvre); + break; + case "track": + rawOeuvre = trackSerializer(rawOeuvre); + break; + case "album": + rawOeuvre = albumSerializer(rawOeuvre); + break; + case "single": + rawOeuvre = albumSerializer(rawOeuvre); + break; + default: + rawOeuvre = null; + } + return new ReviewPublic( + rawReview, + rawOeuvre, + new UserPublic(rawReview.utilisateur), + doesUserLike, + comments + ); +}; + +module.exports = serializeReview; diff --git a/lib/interfaces/serializers/SerializeSearchItem.js b/lib/interfaces/serializers/SerializeSearchItem.js new file mode 100644 index 0000000..96349f1 --- /dev/null +++ b/lib/interfaces/serializers/SerializeSearchItem.js @@ -0,0 +1,57 @@ +const seralizer = { + 'artist': (item) => { + return { + id: item.id, + imageURL: item.image, + title: item.name, + subtitle: '' + } + + }, + 'album': (item) => { + return { + id: item.id, + imageURL: item.image, + title: item.name, + subtitle: item?.artists[0].name + } + }, + 'track': (item) => { + return { + id: item.id, + imageURL: item.album.image, + title: item.name, + subtitle: item?.artists[0].name + } + }, + 'single': (item) => { + return { + id: item.id, + + imageURL: item.image, + title: item.name, + subtitle: item?.artists[0].name + } + }, + 'compilation': (item) => { + return { + id: item.id, + imageURL: item.image, + title: item.name, + subtitle: item?.artists[0].name + } + }, + 'user': (item) => { + return { + id: item.id_utilisateur, + imageURL: item.photo, + title: item.alias, + subtitle: item.pseudo + } + } +} +module.exports = (item) => { + const value = seralizer[item.type](item) + value.type = item.type + return value +} \ No newline at end of file diff --git a/lib/interfaces/serializers/TrackSerializer.js b/lib/interfaces/serializers/TrackSerializer.js index 66600e1..41f6af9 100644 --- a/lib/interfaces/serializers/TrackSerializer.js +++ b/lib/interfaces/serializers/TrackSerializer.js @@ -1,18 +1,27 @@ -const Track = require("../../domain/model/Track") -const SerializeAlbum = require("./AlbumSerializer") -const SerializeArtist = require("./ArtistSerializer") +// TrackSerializer.js +const Track = require("../../domain/model/Track"); +const SerializeAlbum = require("./AlbumSerializer"); +const SerializeArtist = require("./ArtistSerializer"); + const serializeTrack = (trackRaw) => { - const album = trackRaw.album ? SerializeAlbum(trackRaw?.album) : undefined - const track = { - id: trackRaw.id, - name: trackRaw.name, - album: album, - artists: trackRaw?.artists?.map(item => SerializeArtist(item)), - spotify_url : trackRaw.external_urls.spotify, - duration_ms: trackRaw.duration_ms, - popularity:trackRaw.popularity, - } - return new Track(track) -} + const album = trackRaw?.album ? SerializeAlbum(trackRaw.album) : undefined; + const track = { + id: trackRaw.id, + name: trackRaw.name, + album: album, + artists: trackRaw?.artists + ? trackRaw?.artists?.map((item) => SerializeArtist(item)) + : undefined, + spotify_url: trackRaw.external_urls.spotify, + duration_ms: trackRaw?.duration_ms, + popularity: trackRaw?.popularity ? trackRaw?.popularity : undefined, + rating: trackRaw?.rating, + likeCount: trackRaw?.likeCount, + reviewCount: trackRaw?.reviewCount, + release_date: trackRaw?.album?.release_date, + }; + + return new Track(track); +}; -module.exports = serializeTrack \ No newline at end of file +module.exports = serializeTrack; diff --git a/package-lock.json b/package-lock.json index bd5d4df..fc743dd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,861 +1,1025 @@ { "name": "nodejs-clean-architecture-app", "version": "1.0.0", - "lockfileVersion": 1, + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.1.tgz", - "integrity": "sha512-IGhtTmpjGbYzcEDOw7DcQtbQSXcG9ftmAXtWTu9V936vDye4xjjekktFAtgZsWpzTj/X01jocB46mTywm/4SZw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.1" - } - }, - "@babel/core": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.10.1.tgz", - "integrity": "sha512-u8XiZ6sMXW/gPmoP5ijonSUln4unazG291X0XAQ5h0s8qnAFr6BRRZGUEK+jtRWdmB0NTJQt7Uga25q8GetIIg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.10.1", - "@babel/generator": "^7.10.1", - "@babel/helper-module-transforms": "^7.10.1", - "@babel/helpers": "^7.10.1", - "@babel/parser": "^7.10.1", - "@babel/template": "^7.10.1", - "@babel/traverse": "^7.10.1", - "@babel/types": "^7.10.1", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" + "packages": { + "": { + "name": "nodejs-clean-architecture-app", + "version": "1.0.0", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/hapi": "^21.3.2", + "@hapi/inert": "^7.1.0", + "@hapi/joi": "^17.1.1", + "@hapi/jwt": "^3.2.0", + "@hapi/vision": "^7.0.3", + "axios": "^1.5.1", + "bcrypt": "^5.1.1", + "blipp": "^4.0.2", + "dotenv": "^8.6.0", + "hapi-cors": "^1.0.3", + "hapi-swagger": "^17.2.1", + "install": "^0.13.0", + "joi": "^17.10.0", + "jsonwebtoken": "^8.5.1", + "mysql2": "^3.6.0", + "nodemailer": "^6.9.7", + "npm": "^10.2.5", + "request": "^2.88.2", + "sequelize": "^5.21.11", + "underscore": "^1.13.6" + }, + "devDependencies": { + "husky": "^8.0.3", + "jest": "^28.0.0", + "nodemon": "^2.0.4", + "sequelize-mock": "^0.10.2" }, + "engines": { + "node": ">=12", + "npm": ">=6.12" + } + }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@apidevtools/json-schema-ref-parser": { + "version": "11.5.4", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-11.5.4.tgz", + "integrity": "sha512-o2fsypTGU0WxRxbax8zQoHiIB4dyrkwYfcm8TxZ+bx9pCzcWZbQtiMqpgBvWA/nJ2TrGjK5adCLfTH8wUeU/Wg==", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.15", + "js-yaml": "^4.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/philsturgeon" + } + }, + "node_modules/@apidevtools/openapi-schemas": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@apidevtools/openapi-schemas/-/openapi-schemas-2.1.0.tgz", + "integrity": "sha512-Zc1AlqrJlX3SlpupFGpiLi2EbteyP7fXmUOGup6/DnkRgjP9bgMM/ag+n91rsv0U1Gpz0H3VILA/o3bW7Ua6BQ==", + "engines": { + "node": ">=10" + } + }, + "node_modules/@apidevtools/swagger-methods": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-methods/-/swagger-methods-3.0.2.tgz", + "integrity": "sha512-QAkD5kK2b1WfjDS/UQn/qQkbwF31uqRjPTrsCs5ZG9BQGAkjwvqGFjjPqAuzac/IYzpPtRzjCP1WrTuAIjMrXg==" + }, + "node_modules/@apidevtools/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/@apidevtools/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-sNiLY51vZOmSPFZA5TF35KZ2HbgYklQnTSDnkghamzLb3EkNtcQnrBQEj5AOCxHpTtXpqMCRM1CrmV2rG6nw4g==", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^9.0.6", + "@apidevtools/openapi-schemas": "^2.0.4", + "@apidevtools/swagger-methods": "^3.0.2", + "@jsdevtools/ono": "^7.1.3", + "call-me-maybe": "^1.0.1", + "z-schema": "^5.0.1" + }, + "peerDependencies": { + "openapi-types": ">=7" + } + }, + "node_modules/@apidevtools/swagger-parser/node_modules/@apidevtools/json-schema-ref-parser": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@apidevtools/json-schema-ref-parser/-/json-schema-ref-parser-9.1.2.tgz", + "integrity": "sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==", + "dependencies": { + "@jsdevtools/ono": "^7.1.3", + "@types/json-schema": "^7.0.6", + "call-me-maybe": "^1.0.1", + "js-yaml": "^4.1.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", + "integrity": "sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.23.4", + "chalk": "^2.4.2" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/generator": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.10.1.tgz", - "integrity": "sha512-AT0YPLQw9DI21tliuJIdplVfLHya6mcGa8ctkv7n4Qv+hYacJrKmNWIteAK1P9iyLikFIAkwqJ7HAOqIDLFfgA==", + "node_modules/@babel/code-frame/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "requires": { - "@babel/types": "^7.10.1", - "jsesc": "^2.5.1", - "lodash": "^4.17.13", - "source-map": "^0.5.0" + "dependencies": { + "color-convert": "^1.9.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/code-frame/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" } }, - "@babel/helper-function-name": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.1.tgz", - "integrity": "sha512-fcpumwhs3YyZ/ttd5Rz0xn0TpIwVkN7X0V38B9TWNfVF42KEkhkAAuPCQ3oXmtTRtiPJrmZ0TrfS0GKF0eMaRQ==", + "node_modules/@babel/code-frame/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.1", - "@babel/template": "^7.10.1", - "@babel/types": "^7.10.1" + "dependencies": { + "color-name": "1.1.3" } }, - "@babel/helper-get-function-arity": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.1.tgz", - "integrity": "sha512-F5qdXkYGOQUb0hpRaPoetF9AnsXknKjWMZ+wmsIRsp5ge5sFh4c3h1eH2pRTTuy9KKAA2+TTYomGXAtEL2fQEw==", + "node_modules/@babel/code-frame/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true + }, + "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, - "requires": { - "@babel/types": "^7.10.1" + "engines": { + "node": ">=0.8.0" } }, - "@babel/helper-member-expression-to-functions": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.10.1.tgz", - "integrity": "sha512-u7XLXeM2n50gb6PWJ9hoO5oO7JFPaZtrh35t8RqKLT1jFKj9IWeD1zrcrYp1q1qiZTdEarfDWfTIP8nGsu0h5g==", + "node_modules/@babel/code-frame/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "requires": { - "@babel/types": "^7.10.1" + "engines": { + "node": ">=4" } }, - "@babel/helper-module-imports": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.1.tgz", - "integrity": "sha512-SFxgwYmZ3HZPyZwJRiVNLRHWuW2OgE5k2nrVs6D9Iv4PPnXVffuEHy83Sfx/l4SqF+5kyJXjAyUmrG7tNm+qVg==", + "node_modules/@babel/code-frame/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, - "requires": { - "@babel/types": "^7.10.1" + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "@babel/helper-module-transforms": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.10.1.tgz", - "integrity": "sha512-RLHRCAzyJe7Q7sF4oy2cB+kRnU4wDZY/H2xJFGof+M+SJEGhZsb+GFj5j1AD8NiSaVBJ+Pf0/WObiXu/zxWpFg==", + "node_modules/@babel/compat-data": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.23.5.tgz", + "integrity": "sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==", "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.10.1", - "@babel/helper-replace-supers": "^7.10.1", - "@babel/helper-simple-access": "^7.10.1", - "@babel/helper-split-export-declaration": "^7.10.1", - "@babel/template": "^7.10.1", - "@babel/types": "^7.10.1", - "lodash": "^4.17.13" + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.1.tgz", - "integrity": "sha512-a0DjNS1prnBsoKx83dP2falChcs7p3i8VMzdrSbfLhuQra/2ENC4sbri34dz/rWmDADsmF1q5GbfaXydh0Jbjg==", + "node_modules/@babel/core": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.24.0.tgz", + "integrity": "sha512-fQfkg0Gjkza3nf0c7/w6Xf34BW4YvzNfACRLmmb7XRLa6XHdR+K9AlJlxneFfWYf6uhOzuzZVTjF/8KfndZANw==", "dev": true, - "requires": { - "@babel/types": "^7.10.1" + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-compilation-targets": "^7.23.6", + "@babel/helper-module-transforms": "^7.23.3", + "@babel/helpers": "^7.24.0", + "@babel/parser": "^7.24.0", + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "@babel/helper-plugin-utils": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.1.tgz", - "integrity": "sha512-fvoGeXt0bJc7VMWZGCAEBEMo/HAjW2mP8apF5eXK0wSqwLAVHAISCWRoLMBMUs2kqeaG77jltVqu4Hn8Egl3nA==", + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, - "@babel/helper-replace-supers": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.1.tgz", - "integrity": "sha512-SOwJzEfpuQwInzzQJGjGaiG578UYmyi2Xw668klPWV5n07B73S0a9btjLk/52Mlcxa+5AdIYqws1KyXRfMoB7A==", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.6.tgz", + "integrity": "sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.23.6", + "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.23.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.23.6.tgz", + "integrity": "sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==", "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.1", - "@babel/helper-optimise-call-expression": "^7.10.1", - "@babel/traverse": "^7.10.1", - "@babel/types": "^7.10.1" + "dependencies": { + "@babel/compat-data": "^7.23.5", + "@babel/helper-validator-option": "^7.23.5", + "browserslist": "^4.22.2", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-simple-access": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.1.tgz", - "integrity": "sha512-VSWpWzRzn9VtgMJBIWTZ+GP107kZdQ4YplJlCmIrjoLVSi/0upixezHCDG8kpPVTBJpKfxTH01wDhh+jS2zKbw==", + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, - "requires": { - "@babel/template": "^7.10.1", - "@babel/types": "^7.10.1" + "dependencies": { + "yallist": "^3.0.2" } }, - "@babel/helper-split-export-declaration": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.10.1.tgz", - "integrity": "sha512-UQ1LVBPrYdbchNhLwj6fetj46BcFwfS4NllJo/1aJsT+1dLTEnXJL0qHqtY7gPzF8S2fXBJamf1biAXV3X077g==", + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "requires": { - "@babel/types": "^7.10.1" + "bin": { + "semver": "bin/semver.js" } }, - "@babel/helper-validator-identifier": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.1.tgz", - "integrity": "sha512-5vW/JXLALhczRCWP0PnFDMCJAchlBvM7f4uk/jXritBnIa6E1KmqmtrS3yn1LAnxFBypQ3eneLuXjsnfQsgILw==", + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, - "@babel/helpers": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.1.tgz", - "integrity": "sha512-muQNHF+IdU6wGgkaJyhhEmI54MOZBKsFfsXFhboz1ybwJ1Kl7IHlbm2a++4jwrmY5UYsgitt5lfqo1wMFcHmyw==", + "node_modules/@babel/helper-environment-visitor": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-function-name": { + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "dev": true, + "dependencies": { + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-hoist-variables": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz", + "integrity": "sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.15" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.23.3.tgz", + "integrity": "sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==", + "dev": true, + "dependencies": { + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-module-imports": "^7.22.15", + "@babel/helper-simple-access": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/helper-validator-identifier": "^7.22.20" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.0.tgz", + "integrity": "sha512-9cUznXMG0+FxRuJfvL82QlTqIzhVW9sL0KjMPHhAOOvpQGL8QtdxnBKILjBqxlHyliz0yCa1G903ZXI/FuHy2w==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-simple-access": { + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz", + "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-split-export-declaration": { + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", + "dev": true, + "dependencies": { + "@babel/types": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.23.4.tgz", + "integrity": "sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.23.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.23.5.tgz", + "integrity": "sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==", "dev": true, - "requires": { - "@babel/template": "^7.10.1", - "@babel/traverse": "^7.10.1", - "@babel/types": "^7.10.1" + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.24.0.tgz", + "integrity": "sha512-ulDZdc0Aj5uLc5nETsa7EPx2L7rM0YJM8r7ck7U73AXi7qOV44IHHRAYZHY6iU1rr3C5N4NtTmMRUJP6kwCWeA==", + "dev": true, + "dependencies": { + "@babel/template": "^7.24.0", + "@babel/traverse": "^7.24.0", + "@babel/types": "^7.24.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/highlight": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.1.tgz", - "integrity": "sha512-8rMof+gVP8mxYZApLF/JgNDAkdKa+aJt3ZYxF8z6+j/hpeXL7iMsKCPHa2jNMHu/qqBwzQF4OHNoYi8dMA/rYg==", + "node_modules/@babel/highlight": { + "version": "7.23.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.23.4.tgz", + "integrity": "sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==", "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.1", - "chalk": "^2.0.0", + "dependencies": { + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" } }, - "@babel/parser": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.10.1.tgz", - "integrity": "sha512-AUTksaz3FqugBkbTZ1i+lDLG5qy8hIzCaAxEtttU6C0BtZZU9pkNZtWSVAht4EW9kl46YBiyTGMp9xTTGqViNg==", + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true }, - "@babel/plugin-syntax-async-generators": { + "node_modules/@babel/highlight/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/parser": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.0.tgz", + "integrity": "sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==", + "dev": true, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-bigint": { + "node_modules/@babel/plugin-syntax-bigint": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-class-properties": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.1.tgz", - "integrity": "sha512-Gf2Yx/iRs1JREDtVZ56OrjjgFHCaldpTnuy9BHla10qyVT3YkIIGEtoDWhyop0ksu1GvNjHIoYRBqm3zoR1jyQ==", + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-json-strings": { + "node_modules/@babel/plugin-syntax-json-strings": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.1.tgz", - "integrity": "sha512-XyHIFa9kdrgJS91CUH+ccPVTnJShr8nLGc5bG2IhGXv5p1Rd+8BleGE5yzIg2Nc1QZAdHDa0Qp4m6066OL96Iw==", + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-nullish-coalescing-operator": { + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.1.tgz", - "integrity": "sha512-uTd0OsHrpe3tH5gRPTxG8Voh99/WCU78vIm5NMRYPAqC8lR4vajt6KkCAknCHrx24vkPdd/05yfdGSB4EIY2mg==", + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.1" + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-object-rest-spread": { + "node_modules/@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-optional-catch-binding": { + "node_modules/@babel/plugin-syntax-optional-catch-binding": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/plugin-syntax-optional-chaining": { + "node_modules/@babel/plugin-syntax-optional-chaining": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/template": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.1.tgz", - "integrity": "sha512-OQDg6SqvFSsc9A0ej6SKINWrpJiNonRIniYondK2ViKhB06i3c0s+76XUft71iqBEe9S1OKsHwPAjfHnuvnCig==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, - "requires": { - "@babel/code-frame": "^7.10.1", - "@babel/parser": "^7.10.1", - "@babel/types": "^7.10.1" + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@babel/traverse": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.10.1.tgz", - "integrity": "sha512-C/cTuXeKt85K+p08jN6vMDz8vSV0vZcI0wmQ36o6mjbuo++kPMdpOYw23W2XH04dbRt9/nMEfA4W3eR21CD+TQ==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.23.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.23.3.tgz", + "integrity": "sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==", "dev": true, - "requires": { - "@babel/code-frame": "^7.10.1", - "@babel/generator": "^7.10.1", - "@babel/helper-function-name": "^7.10.1", - "@babel/helper-split-export-declaration": "^7.10.1", - "@babel/parser": "^7.10.1", - "@babel/types": "^7.10.1", - "debug": "^4.1.0", - "globals": "^11.1.0", - "lodash": "^4.17.13" + "dependencies": { + "@babel/helper-plugin-utils": "^7.22.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.24.0.tgz", + "integrity": "sha512-Bkf2q8lMB0AFpX0NFEqSbx1OkTHf0f+0j82mkw+ZpzBnkk7e9Ql0891vlfgi+kHwOk8tQjiQHpqh4LaSa0fKEA==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.23.5", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0" }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.24.0.tgz", + "integrity": "sha512-HfuJlI8qq3dEDmNU5ChzzpZRWq+oxCZQyMzIMEqLho+AQnhMnKQUzH6ydo3RBl/YjPCuk68Y6s0Gx0AeyULiWw==", + "dev": true, "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } + "@babel/code-frame": "^7.23.5", + "@babel/generator": "^7.23.6", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.24.0", + "@babel/types": "^7.24.0", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/types": { - "version": "7.10.1", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.10.1.tgz", - "integrity": "sha512-L2yqUOpf3tzlW9GVuipgLEcZxnO+96SzR6fjXMuxxNkIgFJ5+07mHCZ+HkHqaeZu8+3LKnNJJ1bKbjBETQAsrA==", + "node_modules/@babel/types": { + "version": "7.24.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.24.0.tgz", + "integrity": "sha512-+j7a5c253RfKh8iABBhywc8NSfP5LURe7Uh4qpsh6jc+aLJguvmIUBdjSdEMQv2bENrCR5MfRdjGo7vzS/ob7w==", "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.1", - "lodash": "^4.17.13", + "dependencies": { + "@babel/helper-string-parser": "^7.23.4", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" + }, + "engines": { + "node": ">=6.9.0" } }, - "@bcoe/v8-coverage": { + "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, - "@cnakazawa/watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", - "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" + "node_modules/@hapi/accept": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@hapi/accept/-/accept-6.0.3.tgz", + "integrity": "sha512-p72f9k56EuF0n3MwlBNThyVE5PXX40g+aQh+C/xbKrfzahM2Oispv3AXmOIU51t3j77zay1qrX7IIziZXspMlw==", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2" } }, - "@hapi/accept": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@hapi/accept/-/accept-5.0.2.tgz", - "integrity": "sha512-CmzBx/bXUR8451fnZRuZAJRlzgm0Jgu5dltTX/bszmR2lheb9BpyN47Q1RbaGTsvFzn0PXAEs+lXDKfshccYZw==", - "requires": { - "@hapi/boom": "9.x.x", - "@hapi/hoek": "9.x.x" - }, + "node_modules/@hapi/address": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@hapi/address/-/address-4.1.0.tgz", + "integrity": "sha512-SkszZf13HVgGmChdHo/PxchnSaCJ6cetVqLzyciudzZRT0jcOouIF/Q93mgjw8cce+D+4F4C1Z/WrfFN+O3VHQ==", + "deprecated": "Moved to 'npm install @sideway/address'", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/hoek": "^9.0.0" } }, - "@hapi/address": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.2.tgz", - "integrity": "sha512-O4QDrx+JoGKZc6aN64L04vqa7e41tIiLU+OvKdcYaEMP97UttL0f9GIi9/0A4WAMx0uBd6SidDIhktZhgOcN8Q==" + "node_modules/@hapi/address/node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" }, - "@hapi/ammo": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@hapi/ammo/-/ammo-5.0.1.tgz", - "integrity": "sha512-FbCNwcTbnQP4VYYhLNGZmA76xb2aHg9AMPiy18NZyWMG310P5KdFGyA9v2rm5ujrIny77dEEIkMOwl0Xv+fSSA==", - "requires": { - "@hapi/hoek": "9.x.x" - }, + "node_modules/@hapi/ammo": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/ammo/-/ammo-6.0.1.tgz", + "integrity": "sha512-pmL+nPod4g58kXrMcsGLp05O2jF4P2Q3GiL8qYV7nKYEh3cGf+rV4P5Jyi2Uq0agGhVU63GtaSAfBEZOlrJn9w==", "dependencies": { - "@hapi/hoek": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz", - "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==" - } + "@hapi/hoek": "^11.0.2" } }, - "@hapi/b64": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@hapi/b64/-/b64-5.0.0.tgz", - "integrity": "sha512-ngu0tSEmrezoiIaNGG6rRvKOUkUuDdf4XTPnONHGYfSGRmDqPZX5oJL6HAdKTo1UQHECbdB4OzhWrfgVppjHUw==", - "requires": { - "@hapi/hoek": "9.x.x" - }, + "node_modules/@hapi/b64": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/b64/-/b64-6.0.1.tgz", + "integrity": "sha512-ZvjX4JQReUmBheeCq+S9YavcnMMHWqx3S0jHNXWIM1kQDxB9cyfSycpVvjfrKcIS8Mh5N3hmu/YKo4Iag9g2Kw==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } - } - }, - "@hapi/basic": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@hapi/basic/-/basic-5.1.1.tgz", - "integrity": "sha512-fHyVvf2xurgGBJJaLpRMtDF4AaPSs679nGk8/FjBw3p8/Kj5fuPv5kD1+fJ4ZSdPt1rHHGvKIDK0aVysbbUDMg==", - "requires": { - "@hapi/boom": "7.x.x", - "@hapi/hoek": "8.x.x" - }, - "dependencies": { - "@hapi/boom": { - "version": "7.4.11", - "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-7.4.11.tgz", - "integrity": "sha512-VSU/Cnj1DXouukYxxkes4nNJonCnlogHvIff1v1RVoN4xzkKhMXX+GRmb3NyH1iar10I9WFPDv2JPwfH3GaV0A==", - "requires": { - "@hapi/hoek": "8.x.x" - } - } + "@hapi/hoek": "^11.0.2" } }, - "@hapi/boom": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.0.tgz", - "integrity": "sha512-4nZmpp4tXbm162LaZT45P7F7sgiem8dwAh2vHWT6XX24dozNjGMg6BvKCRvtCUcmcXqeMIUqWN8Rc5X8yKuROQ==", - "requires": { - "@hapi/hoek": "9.x.x" - }, + "node_modules/@hapi/boom": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-10.0.1.tgz", + "integrity": "sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==", "dependencies": { - "@hapi/hoek": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz", - "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==" - } + "@hapi/hoek": "^11.0.2" } }, - "@hapi/bounce": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/bounce/-/bounce-2.0.0.tgz", - "integrity": "sha512-JesW92uyzOOyuzJKjoLHM1ThiOvHPOLDHw01YV8yh5nCso7sDwJho1h0Ad2N+E62bZyz46TG3xhAi/78Gsct6A==", - "requires": { - "@hapi/boom": "9.x.x", - "@hapi/hoek": "9.x.x" - }, + "node_modules/@hapi/bounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@hapi/bounce/-/bounce-3.0.1.tgz", + "integrity": "sha512-G+/Pp9c1Ha4FDP+3Sy/Xwg2O4Ahaw3lIZFSX+BL4uWi64CmiETuZPxhKDUD4xBMOUZbBlzvO8HjiK8ePnhBadA==", "dependencies": { - "@hapi/hoek": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz", - "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==" - } + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2" } }, - "@hapi/bourne": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-1.3.2.tgz", - "integrity": "sha512-1dVNHT76Uu5N3eJNTYcvxee+jzX4Z9lfciqRRHCU27ihbUcYi+iSc2iml5Ke1LXe1SyJCLA0+14Jh4tXJgOppA==" + "node_modules/@hapi/bourne": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz", + "integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==" }, - "@hapi/call": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@hapi/call/-/call-8.0.1.tgz", - "integrity": "sha512-bOff6GTdOnoe5b8oXRV3lwkQSb/LAWylvDMae6RgEWWntd0SHtkYbQukDHKlfaYtVnSAgIavJ0kqszF/AIBb6g==", - "requires": { - "@hapi/boom": "9.x.x", - "@hapi/hoek": "9.x.x" - }, + "node_modules/@hapi/call": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/@hapi/call/-/call-9.0.1.tgz", + "integrity": "sha512-uPojQRqEL1GRZR4xXPqcLMujQGaEpyVPRyBlD8Pp5rqgIwLhtveF9PkixiKru2THXvuN8mUrLeet5fqxKAAMGg==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2" } }, - "@hapi/catbox": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@hapi/catbox/-/catbox-11.1.1.tgz", - "integrity": "sha512-u/8HvB7dD/6X8hsZIpskSDo4yMKpHxFd7NluoylhGrL6cUfYxdQPnvUp9YU2C6F9hsyBVLGulBd9vBN1ebfXOQ==", - "requires": { - "@hapi/boom": "9.x.x", - "@hapi/hoek": "9.x.x", - "@hapi/podium": "4.x.x", - "@hapi/validate": "1.x.x" - }, + "node_modules/@hapi/catbox": { + "version": "12.1.1", + "resolved": "https://registry.npmjs.org/@hapi/catbox/-/catbox-12.1.1.tgz", + "integrity": "sha512-hDqYB1J+R0HtZg4iPH3LEnldoaBsar6bYp0EonBmNQ9t5CO+1CqgCul2ZtFveW1ReA5SQuze9GPSU7/aecERhw==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/podium": "^5.0.0", + "@hapi/validate": "^2.0.1" } }, - "@hapi/catbox-memory": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@hapi/catbox-memory/-/catbox-memory-5.0.1.tgz", - "integrity": "sha512-QWw9nOYJq5PlvChLWV8i6hQHJYfvdqiXdvTupJFh0eqLZ64Xir7mKNi96d5/ZMUAqXPursfNDIDxjFgoEDUqeQ==", - "requires": { - "@hapi/boom": "9.x.x", - "@hapi/hoek": "9.x.x" - }, + "node_modules/@hapi/catbox-memory": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/catbox-memory/-/catbox-memory-6.0.1.tgz", + "integrity": "sha512-sVb+/ZxbZIvaMtJfAbdyY+QJUQg9oKTwamXpEg/5xnfG5WbJLTjvEn4kIGKz9pN3ENNbIL/bIdctmHmqi/AdGA==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2" } }, - "@hapi/catbox-object": { + "node_modules/@hapi/catbox-object": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@hapi/catbox-object/-/catbox-object-3.0.1.tgz", "integrity": "sha512-3w6E2DXtjWbmLYi4WcFUOor5jgrXN4PWhDrMrXKP/cEsFSfVSRJ0FhY2PXrhrUHwcllfKezYafWU3tQ5+8RO1w==", - "requires": { + "dependencies": { "@hapi/boom": "^10.0.1", "@hapi/hoek": "^11.0.2" - }, + } + }, + "node_modules/@hapi/catbox/node_modules/@hapi/validate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/validate/-/validate-2.0.1.tgz", + "integrity": "sha512-NZmXRnrSLK8MQ9y/CMqE9WSspgB9xA41/LlYR0k967aSZebWr4yNrpxIbov12ICwKy4APSlWXZga9jN5p6puPA==", "dependencies": { - "@hapi/boom": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-10.0.1.tgz", - "integrity": "sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==", - "requires": { - "@hapi/hoek": "^11.0.2" - } - }, - "@hapi/hoek": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.2.tgz", - "integrity": "sha512-aKmlCO57XFZ26wso4rJsW4oTUnrgTFw2jh3io7CAtO9w4UltBNwRXvXIVzzyfkaaLRo3nluP/19msA8vDUUuKw==" - } + "@hapi/hoek": "^11.0.2", + "@hapi/topo": "^6.0.1" } }, - "@hapi/content": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/@hapi/content/-/content-5.0.2.tgz", - "integrity": "sha512-mre4dl1ygd4ZyOH3tiYBrOUBzV7Pu/EOs8VLGf58vtOEECWed8Uuw6B4iR9AN/8uQt42tB04qpVaMyoMQh0oMw==", - "requires": { - "@hapi/boom": "9.x.x" + "node_modules/@hapi/content": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@hapi/content/-/content-6.0.0.tgz", + "integrity": "sha512-CEhs7j+H0iQffKfe5Htdak5LBOz/Qc8TRh51cF+BFv0qnuph3Em4pjGVzJMkI2gfTDdlJKWJISGWS1rK34POGA==", + "dependencies": { + "@hapi/boom": "^10.0.0" } }, - "@hapi/cryptiles": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/cryptiles/-/cryptiles-5.1.0.tgz", - "integrity": "sha512-fo9+d1Ba5/FIoMySfMqPBR/7Pa29J2RsiPrl7bkwo5W5o+AN1dAYQRi4SPrPwwVxVGKjgLOEWrsvt1BonJSfLA==", - "requires": { - "@hapi/boom": "9.x.x" + "node_modules/@hapi/cryptiles": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/cryptiles/-/cryptiles-6.0.1.tgz", + "integrity": "sha512-9GM9ECEHfR8lk5ASOKG4+4ZsEzFqLfhiryIJ2ISePVB92OHLp/yne4m+zn7z9dgvM98TLpiFebjDFQ0UHcqxXQ==", + "dependencies": { + "@hapi/boom": "^10.0.1" + }, + "engines": { + "node": ">=14.0.0" } }, - "@hapi/file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/file/-/file-2.0.0.tgz", - "integrity": "sha512-WSrlgpvEqgPWkI18kkGELEZfXr0bYLtr16iIN4Krh9sRnzBZN6nnWxHFxtsnP684wueEySBbXPDg/WfA9xJdBQ==" + "node_modules/@hapi/file": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@hapi/file/-/file-3.0.0.tgz", + "integrity": "sha512-w+lKW+yRrLhJu620jT3y+5g2mHqnKfepreykvdOcl9/6up8GrQQn+l3FRTsjHTKbkbfQFkuksHpdv2EcpKcJ4Q==" }, - "@hapi/formula": { + "node_modules/@hapi/formula": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-2.0.0.tgz", - "integrity": "sha512-V87P8fv7PI0LH7LiVi8Lkf3x+KCO7pQozXRssAHNXXL9L1K+uyu4XypLXwxqVDKgyQai6qj3/KteNlrqDx4W5A==" + "integrity": "sha512-V87P8fv7PI0LH7LiVi8Lkf3x+KCO7pQozXRssAHNXXL9L1K+uyu4XypLXwxqVDKgyQai6qj3/KteNlrqDx4W5A==", + "deprecated": "Moved to 'npm install @sideway/formula'" }, - "@hapi/good": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@hapi/good/-/good-9.0.0.tgz", - "integrity": "sha512-jt6mEzFfY+jzE/IbvNVTTHcqKE9RP609MXKff1Pj4VPCnCSG8UVUtTdr1nM6UFN02NvntqShlpeZi4o+RgN35g==", - "requires": { - "@hapi/hoek": "9.x.x", - "@hapi/joi": "17.x.x", - "@hapi/oppsy": "3.x.x", - "pumpify": "1.x.x" - }, - "dependencies": { - "@hapi/address": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@hapi/address/-/address-4.0.1.tgz", - "integrity": "sha512-0oEP5UiyV4f3d6cBL8F3Z5S7iWSX39Knnl0lY8i+6gfmmIBj44JCBNtcMgwyS+5v7j3VYavNay0NFHDS+UGQcw==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@hapi/formula": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-2.0.0.tgz", - "integrity": "sha512-V87P8fv7PI0LH7LiVi8Lkf3x+KCO7pQozXRssAHNXXL9L1K+uyu4XypLXwxqVDKgyQai6qj3/KteNlrqDx4W5A==" - }, - "@hapi/hoek": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz", - "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==" - }, - "@hapi/joi": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-17.1.1.tgz", - "integrity": "sha512-p4DKeZAoeZW4g3u7ZeRo+vCDuSDgSvtsB/NpfjXEHTUjSeINAi/RrVOWiVQ1isaoLzMvFEhe8n5065mQq1AdQg==", - "requires": { - "@hapi/address": "^4.0.1", - "@hapi/formula": "^2.0.0", - "@hapi/hoek": "^9.0.0", - "@hapi/pinpoint": "^2.0.0", - "@hapi/topo": "^5.0.0" - } - }, - "@hapi/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-vzXR5MY7n4XeIvLpfl3HtE3coZYO4raKXW766R6DZw/6aLqR26iuZ109K7a0NtF2Db0jxqh7xz2AxkUwpUFybw==" - }, - "@hapi/topo": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz", - "integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - } + "node_modules/@hapi/hapi": { + "version": "21.3.3", + "resolved": "https://registry.npmjs.org/@hapi/hapi/-/hapi-21.3.3.tgz", + "integrity": "sha512-6pgwWVl/aSKSNVn86n+mWa06jRqCAKi2adZp/Hti19A0u5x3/6eiKz8UTBPMzfrdGf9WcrYbFBYzWr/qd2s28g==", + "dependencies": { + "@hapi/accept": "^6.0.1", + "@hapi/ammo": "^6.0.1", + "@hapi/boom": "^10.0.1", + "@hapi/bounce": "^3.0.1", + "@hapi/call": "^9.0.1", + "@hapi/catbox": "^12.1.1", + "@hapi/catbox-memory": "^6.0.1", + "@hapi/heavy": "^8.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/mimos": "^7.0.1", + "@hapi/podium": "^5.0.1", + "@hapi/shot": "^6.0.1", + "@hapi/somever": "^4.1.1", + "@hapi/statehood": "^8.1.1", + "@hapi/subtext": "^8.1.0", + "@hapi/teamwork": "^6.0.0", + "@hapi/topo": "^6.0.1", + "@hapi/validate": "^2.0.1" + }, + "engines": { + "node": ">=14.15.0" + } + }, + "node_modules/@hapi/hapi/node_modules/@hapi/validate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/validate/-/validate-2.0.1.tgz", + "integrity": "sha512-NZmXRnrSLK8MQ9y/CMqE9WSspgB9xA41/LlYR0k967aSZebWr4yNrpxIbov12ICwKy4APSlWXZga9jN5p6puPA==", + "dependencies": { + "@hapi/hoek": "^11.0.2", + "@hapi/topo": "^6.0.1" } }, - "@hapi/good-console": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/@hapi/good-console/-/good-console-9.0.0.tgz", - "integrity": "sha512-f0+eFyKGfagfCvyjFm3C2vFmL+4/fsSJ6bgf6LbBf3GV918hZAl40RUCRyiPKdYmEoTAKcq++Ti9xe1oOVDKdA==", - "requires": { - "@hapi/hoek": "9.x.x", - "@hapi/joi": "17.x.x", - "json-stringify-safe": "5.x.x", - "moment": "2.x.x" - }, - "dependencies": { - "@hapi/address": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@hapi/address/-/address-4.0.1.tgz", - "integrity": "sha512-0oEP5UiyV4f3d6cBL8F3Z5S7iWSX39Knnl0lY8i+6gfmmIBj44JCBNtcMgwyS+5v7j3VYavNay0NFHDS+UGQcw==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@hapi/formula": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-2.0.0.tgz", - "integrity": "sha512-V87P8fv7PI0LH7LiVi8Lkf3x+KCO7pQozXRssAHNXXL9L1K+uyu4XypLXwxqVDKgyQai6qj3/KteNlrqDx4W5A==" - }, - "@hapi/hoek": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz", - "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==" - }, - "@hapi/joi": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-17.1.1.tgz", - "integrity": "sha512-p4DKeZAoeZW4g3u7ZeRo+vCDuSDgSvtsB/NpfjXEHTUjSeINAi/RrVOWiVQ1isaoLzMvFEhe8n5065mQq1AdQg==", - "requires": { - "@hapi/address": "^4.0.1", - "@hapi/formula": "^2.0.0", - "@hapi/hoek": "^9.0.0", - "@hapi/pinpoint": "^2.0.0", - "@hapi/topo": "^5.0.0" - } - }, - "@hapi/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-vzXR5MY7n4XeIvLpfl3HtE3coZYO4raKXW766R6DZw/6aLqR26iuZ109K7a0NtF2Db0jxqh7xz2AxkUwpUFybw==" - }, - "@hapi/topo": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz", - "integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - } + "node_modules/@hapi/heavy": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/@hapi/heavy/-/heavy-8.0.1.tgz", + "integrity": "sha512-gBD/NANosNCOp6RsYTsjo2vhr5eYA3BEuogk6cxY0QdhllkkTaJFYtTXv46xd6qhBVMbMMqcSdtqey+UQU3//w==", + "dependencies": { + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/validate": "^2.0.1" } }, - "@hapi/good-squeeze": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@hapi/good-squeeze/-/good-squeeze-6.0.0.tgz", - "integrity": "sha512-UgHAF9Lm8fJPzgf2HymtowOwNc1+IL+p08YTVR+XA4d8nmyE1t9x3RLA4riqldnOKHkVqGakJ1jGqUG7jk77Cg==", - "requires": { - "@hapi/hoek": "9.x.x", - "fast-safe-stringify": "2.x.x" - }, + "node_modules/@hapi/heavy/node_modules/@hapi/validate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/validate/-/validate-2.0.1.tgz", + "integrity": "sha512-NZmXRnrSLK8MQ9y/CMqE9WSspgB9xA41/LlYR0k967aSZebWr4yNrpxIbov12ICwKy4APSlWXZga9jN5p6puPA==", "dependencies": { - "@hapi/hoek": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz", - "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==" - } + "@hapi/hoek": "^11.0.2", + "@hapi/topo": "^6.0.1" } }, - "@hapi/hapi": { - "version": "20.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hapi/-/hapi-20.3.0.tgz", - "integrity": "sha512-zvPSRvaQyF3S6Nev9aiAzko2/hIFZmNSJNcs07qdVaVAvj8dGJSV4fVUuQSnufYJAGiSau+U5LxMLhx79se5WA==", - "requires": { - "@hapi/accept": "^5.0.1", - "@hapi/ammo": "^5.0.1", - "@hapi/boom": "^9.1.0", - "@hapi/bounce": "^2.0.0", - "@hapi/call": "^8.0.0", - "@hapi/catbox": "^11.1.1", - "@hapi/catbox-memory": "^5.0.0", - "@hapi/heavy": "^7.0.1", - "@hapi/hoek": "^9.0.4", - "@hapi/mimos": "^6.0.0", - "@hapi/podium": "^4.1.1", - "@hapi/shot": "^5.0.5", - "@hapi/somever": "^3.0.0", - "@hapi/statehood": "^7.0.3", - "@hapi/subtext": "^7.1.0", - "@hapi/teamwork": "^5.1.0", - "@hapi/topo": "^5.0.0", - "@hapi/validate": "^1.1.1" - }, - "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - }, - "@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - } - } + "node_modules/@hapi/hoek": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.4.tgz", + "integrity": "sha512-PnsP5d4q7289pS2T2EgGz147BFJ2Jpb4yrEdkpz2IhgEUzos1S7HTl7ezWh1yfYzYlj89KzLdCRkqsP6SIryeQ==" }, - "@hapi/heavy": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/@hapi/heavy/-/heavy-7.0.1.tgz", - "integrity": "sha512-vJ/vzRQ13MtRzz6Qd4zRHWS3FaUc/5uivV2TIuExGTM9Qk+7Zzqj0e2G7EpE6KztO9SalTbiIkTh7qFKj/33cA==", - "requires": { - "@hapi/boom": "9.x.x", - "@hapi/hoek": "9.x.x", - "@hapi/validate": "1.x.x" - }, + "node_modules/@hapi/inert": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@hapi/inert/-/inert-7.1.0.tgz", + "integrity": "sha512-5X+cl/Ozm0U9uPGGX1dSKhnhTQIf161bH/kkTN9OBVAZKFG+nrj8j/NMj6S1zBBZWmQrkVRNPfCUGrXzB4fCFQ==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/ammo": "^6.0.1", + "@hapi/boom": "^10.0.1", + "@hapi/bounce": "^3.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/validate": "^2.0.1", + "lru-cache": "^7.14.1" } }, - "@hapi/hoek": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.3.2.tgz", - "integrity": "sha512-NP5SG4bzix+EtSMtcudp8TvI0lB46mXNo8uFpTDw6tqxGx4z5yx+giIunEFA0Z7oUO4DuWrOJV9xqR2tJVEdyA==" - }, - "@hapi/inert": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@hapi/inert/-/inert-6.0.1.tgz", - "integrity": "sha512-oLxAmtWni3nH4INU2gcXFnHBw0GhHYF3HR71hAWrPc91dq+iFYGfawfaMbonwGr5DkzFiGe8Ir5sZAt2AqeINA==", - "requires": { - "@hapi/ammo": "5.x.x", - "@hapi/boom": "9.x.x", - "@hapi/bounce": "2.x.x", - "@hapi/hoek": "9.x.x", - "@hapi/joi": "17.x.x", - "lru-cache": "5.x.x" - }, + "node_modules/@hapi/inert/node_modules/@hapi/validate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/validate/-/validate-2.0.1.tgz", + "integrity": "sha512-NZmXRnrSLK8MQ9y/CMqE9WSspgB9xA41/LlYR0k967aSZebWr4yNrpxIbov12ICwKy4APSlWXZga9jN5p6puPA==", "dependencies": { - "@hapi/hoek": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz", - "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==" - }, - "lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "requires": { - "yallist": "^3.0.2" - } - }, - "yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" - } + "@hapi/hoek": "^11.0.2", + "@hapi/topo": "^6.0.1" } }, - "@hapi/iron": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@hapi/iron/-/iron-6.0.0.tgz", - "integrity": "sha512-zvGvWDufiTGpTJPG1Y/McN8UqWBu0k/xs/7l++HVU535NLHXsHhy54cfEMdW7EjwKfbBfM9Xy25FmTiobb7Hvw==", - "requires": { - "@hapi/b64": "5.x.x", - "@hapi/boom": "9.x.x", - "@hapi/bourne": "2.x.x", - "@hapi/cryptiles": "5.x.x", - "@hapi/hoek": "9.x.x" - }, - "dependencies": { - "@hapi/bourne": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-2.1.0.tgz", - "integrity": "sha512-i1BpaNDVLJdRBEKeJWkVO6tYX6DMFBuwMhSuWqLsY4ufeTKGVuV5rBsUhxPayXqnnWHgXUAmWK16H/ykO5Wj4Q==" - }, - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "node_modules/@hapi/iron": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@hapi/iron/-/iron-7.0.1.tgz", + "integrity": "sha512-tEZnrOujKpS6jLKliyWBl3A9PaE+ppuL/+gkbyPPDb/l2KSKQyH4lhMkVb+sBhwN+qaxxlig01JRqB8dk/mPxQ==", + "dependencies": { + "@hapi/b64": "^6.0.1", + "@hapi/boom": "^10.0.1", + "@hapi/bourne": "^3.0.0", + "@hapi/cryptiles": "^6.0.1", + "@hapi/hoek": "^11.0.2" } }, - "@hapi/joi": { + "node_modules/@hapi/joi": { "version": "17.1.1", "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-17.1.1.tgz", "integrity": "sha512-p4DKeZAoeZW4g3u7ZeRo+vCDuSDgSvtsB/NpfjXEHTUjSeINAi/RrVOWiVQ1isaoLzMvFEhe8n5065mQq1AdQg==", - "requires": { + "deprecated": "Switch to 'npm install joi'", + "dependencies": { "@hapi/address": "^4.0.1", "@hapi/formula": "^2.0.0", "@hapi/hoek": "^9.0.0", "@hapi/pinpoint": "^2.0.0", "@hapi/topo": "^5.0.0" - }, + } + }, + "node_modules/@hapi/joi/node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/@hapi/joi/node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", "dependencies": { - "@hapi/address": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@hapi/address/-/address-4.0.1.tgz", - "integrity": "sha512-0oEP5UiyV4f3d6cBL8F3Z5S7iWSX39Knnl0lY8i+6gfmmIBj44JCBNtcMgwyS+5v7j3VYavNay0NFHDS+UGQcw==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@hapi/hoek": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz", - "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==" - }, - "@hapi/topo": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz", - "integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - } + "@hapi/hoek": "^9.0.0" } }, - "@hapi/jwt": { + "node_modules/@hapi/jwt": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/@hapi/jwt/-/jwt-3.2.0.tgz", "integrity": "sha512-2EU5zdr0petG63J2RHiagGByA1Qr6qzxMmrJJ3/0cm78be1Lq4vi038YgKJvbBSxSboIp5SWGZdYB6+CJlqEoQ==", - "requires": { + "dependencies": { "@hapi/b64": "^6.0.0", "@hapi/boom": "^10.0.0", "@hapi/bounce": "^3.0.0", @@ -866,1014 +1030,572 @@ "@hapi/wreck": "^18.0.0", "ecdsa-sig-formatter": "^1.0.0", "joi": "^17.2.1" - }, + } + }, + "node_modules/@hapi/jwt/node_modules/@hapi/hoek": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-10.0.1.tgz", + "integrity": "sha512-CvlW7jmOhWzuqOqiJQ3rQVLMcREh0eel4IBnxDx2FAcK8g7qoJRQK4L1CPBASoCY6y8e6zuCy3f2g+HWdkzcMw==" + }, + "node_modules/@hapi/mimos": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/@hapi/mimos/-/mimos-7.0.1.tgz", + "integrity": "sha512-b79V+BrG0gJ9zcRx1VGcCI6r6GEzzZUgiGEJVoq5gwzuB2Ig9Cax8dUuBauQCFKvl2YWSWyOc8mZ8HDaJOtkew==", "dependencies": { - "@hapi/b64": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@hapi/b64/-/b64-6.0.1.tgz", - "integrity": "sha512-ZvjX4JQReUmBheeCq+S9YavcnMMHWqx3S0jHNXWIM1kQDxB9cyfSycpVvjfrKcIS8Mh5N3hmu/YKo4Iag9g2Kw==", - "requires": { - "@hapi/hoek": "^11.0.2" - }, - "dependencies": { - "@hapi/hoek": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.2.tgz", - "integrity": "sha512-aKmlCO57XFZ26wso4rJsW4oTUnrgTFw2jh3io7CAtO9w4UltBNwRXvXIVzzyfkaaLRo3nluP/19msA8vDUUuKw==" - } - } - }, - "@hapi/boom": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-10.0.1.tgz", - "integrity": "sha512-ERcCZaEjdH3OgSJlyjVk8pHIFeus91CjKP3v+MpgBNp5IvGzP2l/bRiD78nqYcKPaZdbKkK5vDBVPd2ohHBlsA==", - "requires": { - "@hapi/hoek": "^11.0.2" - }, - "dependencies": { - "@hapi/hoek": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.2.tgz", - "integrity": "sha512-aKmlCO57XFZ26wso4rJsW4oTUnrgTFw2jh3io7CAtO9w4UltBNwRXvXIVzzyfkaaLRo3nluP/19msA8vDUUuKw==" - } - } - }, - "@hapi/bounce": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@hapi/bounce/-/bounce-3.0.1.tgz", - "integrity": "sha512-G+/Pp9c1Ha4FDP+3Sy/Xwg2O4Ahaw3lIZFSX+BL4uWi64CmiETuZPxhKDUD4xBMOUZbBlzvO8HjiK8ePnhBadA==", - "requires": { - "@hapi/boom": "^10.0.1", - "@hapi/hoek": "^11.0.2" - }, - "dependencies": { - "@hapi/hoek": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.2.tgz", - "integrity": "sha512-aKmlCO57XFZ26wso4rJsW4oTUnrgTFw2jh3io7CAtO9w4UltBNwRXvXIVzzyfkaaLRo3nluP/19msA8vDUUuKw==" - } - } - }, - "@hapi/bourne": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-3.0.0.tgz", - "integrity": "sha512-Waj1cwPXJDucOib4a3bAISsKJVb15MKi9IvmTI/7ssVEm6sywXGjVJDhl6/umt1pK1ZS7PacXU3A1PmFKHEZ2w==" - }, - "@hapi/cryptiles": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@hapi/cryptiles/-/cryptiles-6.0.1.tgz", - "integrity": "sha512-9GM9ECEHfR8lk5ASOKG4+4ZsEzFqLfhiryIJ2ISePVB92OHLp/yne4m+zn7z9dgvM98TLpiFebjDFQ0UHcqxXQ==", - "requires": { - "@hapi/boom": "^10.0.1" - } - }, - "@hapi/hoek": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-10.0.1.tgz", - "integrity": "sha512-CvlW7jmOhWzuqOqiJQ3rQVLMcREh0eel4IBnxDx2FAcK8g7qoJRQK4L1CPBASoCY6y8e6zuCy3f2g+HWdkzcMw==" - }, - "@hapi/wreck": { - "version": "18.0.1", - "resolved": "https://registry.npmjs.org/@hapi/wreck/-/wreck-18.0.1.tgz", - "integrity": "sha512-OLHER70+rZxvDl75xq3xXOfd3e8XIvz8fWY0dqg92UvhZ29zo24vQgfqgHSYhB5ZiuFpSLeriOisAlxAo/1jWg==", - "requires": { - "@hapi/boom": "^10.0.1", - "@hapi/bourne": "^3.0.0", - "@hapi/hoek": "^11.0.2" - }, - "dependencies": { - "@hapi/hoek": { - "version": "11.0.2", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-11.0.2.tgz", - "integrity": "sha512-aKmlCO57XFZ26wso4rJsW4oTUnrgTFw2jh3io7CAtO9w4UltBNwRXvXIVzzyfkaaLRo3nluP/19msA8vDUUuKw==" - } - } - } + "@hapi/hoek": "^11.0.2", + "mime-db": "^1.52.0" } }, - "@hapi/mimos": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@hapi/mimos/-/mimos-6.0.0.tgz", - "integrity": "sha512-Op/67tr1I+JafN3R3XN5DucVSxKRT/Tc+tUszDwENoNpolxeXkhrJ2Czt6B6AAqrespHoivhgZBWYSuANN9QXg==", - "requires": { - "@hapi/hoek": "9.x.x", - "mime-db": "1.x.x" + "node_modules/@hapi/nigel": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hapi/nigel/-/nigel-5.0.1.tgz", + "integrity": "sha512-uv3dtYuB4IsNaha+tigWmN8mQw/O9Qzl5U26Gm4ZcJVtDdB1AVJOwX3X5wOX+A07qzpEZnOMBAm8jjSqGsU6Nw==", + "dependencies": { + "@hapi/hoek": "^11.0.2", + "@hapi/vise": "^5.0.1" }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@hapi/pez": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@hapi/pez/-/pez-6.1.0.tgz", + "integrity": "sha512-+FE3sFPYuXCpuVeHQ/Qag1b45clR2o54QoonE/gKHv9gukxQ8oJJZPR7o3/ydDTK6racnCJXxOyT1T93FCJMIg==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/b64": "^6.0.1", + "@hapi/boom": "^10.0.1", + "@hapi/content": "^6.0.0", + "@hapi/hoek": "^11.0.2", + "@hapi/nigel": "^5.0.1" } }, - "@hapi/nigel": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/@hapi/nigel/-/nigel-4.0.2.tgz", - "integrity": "sha512-ht2KoEsDW22BxQOEkLEJaqfpoKPXxi7tvabXy7B/77eFtOyG5ZEstfZwxHQcqAiZhp58Ae5vkhEqI03kawkYNw==", - "requires": { - "@hapi/hoek": "^9.0.4", - "@hapi/vise": "^4.0.0" - }, + "node_modules/@hapi/pinpoint": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.1.tgz", + "integrity": "sha512-EKQmr16tM8s16vTT3cA5L0kZZcTMU5DUOZTuvpnY738m+jyP3JIUj+Mm1xc1rsLkGBQ/gVnfKYPwOmPg1tUR4Q==" + }, + "node_modules/@hapi/podium": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hapi/podium/-/podium-5.0.1.tgz", + "integrity": "sha512-eznFTw6rdBhAijXFIlBOMJJd+lXTvqbrBIS4Iu80r2KTVIo4g+7fLy4NKp/8+UnSt5Ox6mJtAlKBU/Sf5080TQ==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/hoek": "^11.0.2", + "@hapi/teamwork": "^6.0.0", + "@hapi/validate": "^2.0.1" } }, - "@hapi/oppsy": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@hapi/oppsy/-/oppsy-3.0.0.tgz", - "integrity": "sha512-0kfUEAqIi21GzFVK2snMO07znMEBiXb+/pOx1dmgOO9TuvFstcfmHU5i56aDfiFP2DM5WzQCU2UWc2gK1lMDhQ==", - "requires": { - "@hapi/hoek": "9.x.x" - }, + "node_modules/@hapi/podium/node_modules/@hapi/validate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/validate/-/validate-2.0.1.tgz", + "integrity": "sha512-NZmXRnrSLK8MQ9y/CMqE9WSspgB9xA41/LlYR0k967aSZebWr4yNrpxIbov12ICwKy4APSlWXZga9jN5p6puPA==", "dependencies": { - "@hapi/hoek": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz", - "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==" - } + "@hapi/hoek": "^11.0.2", + "@hapi/topo": "^6.0.1" } }, - "@hapi/pez": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/pez/-/pez-5.1.0.tgz", - "integrity": "sha512-YfB0btnkLB3lb6Ry/1KifnMPBm5ZPfaAHWFskzOMAgDgXgcBgA+zjpIynyEiBfWEz22DBT8o1e2tAaBdlt8zbw==", - "requires": { - "@hapi/b64": "5.x.x", - "@hapi/boom": "9.x.x", - "@hapi/content": "^5.0.2", - "@hapi/hoek": "9.x.x", - "@hapi/nigel": "4.x.x" - }, + "node_modules/@hapi/shot": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@hapi/shot/-/shot-6.0.1.tgz", + "integrity": "sha512-s5ynMKZXYoDd3dqPw5YTvOR/vjHvMTxc388+0qL0jZZP1+uwXuUD32o9DuuuLsmTlyXCWi02BJl1pBpwRuUrNA==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/hoek": "^11.0.2", + "@hapi/validate": "^2.0.1" } }, - "@hapi/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-vzXR5MY7n4XeIvLpfl3HtE3coZYO4raKXW766R6DZw/6aLqR26iuZ109K7a0NtF2Db0jxqh7xz2AxkUwpUFybw==" - }, - "@hapi/podium": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@hapi/podium/-/podium-4.1.3.tgz", - "integrity": "sha512-ljsKGQzLkFqnQxE7qeanvgGj4dejnciErYd30dbrYzUOF/FyS/DOF97qcrT3bhoVwCYmxa6PEMhxfCPlnUcD2g==", - "requires": { - "@hapi/hoek": "9.x.x", - "@hapi/teamwork": "5.x.x", - "@hapi/validate": "1.x.x" - }, + "node_modules/@hapi/shot/node_modules/@hapi/validate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/validate/-/validate-2.0.1.tgz", + "integrity": "sha512-NZmXRnrSLK8MQ9y/CMqE9WSspgB9xA41/LlYR0k967aSZebWr4yNrpxIbov12ICwKy4APSlWXZga9jN5p6puPA==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/hoek": "^11.0.2", + "@hapi/topo": "^6.0.1" } }, - "@hapi/shot": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@hapi/shot/-/shot-5.0.5.tgz", - "integrity": "sha512-x5AMSZ5+j+Paa8KdfCoKh+klB78otxF+vcJR/IoN91Vo2e5ulXIW6HUsFTCU+4W6P/Etaip9nmdAx2zWDimB2A==", - "requires": { - "@hapi/hoek": "9.x.x", - "@hapi/validate": "1.x.x" - }, + "node_modules/@hapi/somever": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@hapi/somever/-/somever-4.1.1.tgz", + "integrity": "sha512-lt3QQiDDOVRatS0ionFDNrDIv4eXz58IibQaZQDOg4DqqdNme8oa0iPWcE0+hkq/KTeBCPtEOjDOBKBKwDumVg==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/bounce": "^3.0.1", + "@hapi/hoek": "^11.0.2" } }, - "@hapi/somever": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@hapi/somever/-/somever-3.0.1.tgz", - "integrity": "sha512-4ZTSN3YAHtgpY/M4GOtHUXgi6uZtG9nEZfNI6QrArhK0XN/RDVgijlb9kOmXwCR5VclDSkBul9FBvhSuKXx9+w==", - "requires": { - "@hapi/bounce": "2.x.x", - "@hapi/hoek": "9.x.x" - }, + "node_modules/@hapi/statehood": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/@hapi/statehood/-/statehood-8.1.1.tgz", + "integrity": "sha512-YbK7PSVUA59NArAW5Np0tKRoIZ5VNYUicOk7uJmWZF6XyH5gGL+k62w77SIJb0AoAJ0QdGQMCQ/WOGL1S3Ydow==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/boom": "^10.0.1", + "@hapi/bounce": "^3.0.1", + "@hapi/bourne": "^3.0.0", + "@hapi/cryptiles": "^6.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/iron": "^7.0.1", + "@hapi/validate": "^2.0.1" } }, - "@hapi/statehood": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/@hapi/statehood/-/statehood-7.0.4.tgz", - "integrity": "sha512-Fia6atroOVmc5+2bNOxF6Zv9vpbNAjEXNcUbWXavDqhnJDlchwUUwKS5LCi5mGtCTxRhUKKHwuxuBZJkmLZ7fw==", - "requires": { - "@hapi/boom": "9.x.x", - "@hapi/bounce": "2.x.x", - "@hapi/bourne": "2.x.x", - "@hapi/cryptiles": "5.x.x", - "@hapi/hoek": "9.x.x", - "@hapi/iron": "6.x.x", - "@hapi/validate": "1.x.x" - }, + "node_modules/@hapi/statehood/node_modules/@hapi/validate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/validate/-/validate-2.0.1.tgz", + "integrity": "sha512-NZmXRnrSLK8MQ9y/CMqE9WSspgB9xA41/LlYR0k967aSZebWr4yNrpxIbov12ICwKy4APSlWXZga9jN5p6puPA==", "dependencies": { - "@hapi/bourne": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-2.1.0.tgz", - "integrity": "sha512-i1BpaNDVLJdRBEKeJWkVO6tYX6DMFBuwMhSuWqLsY4ufeTKGVuV5rBsUhxPayXqnnWHgXUAmWK16H/ykO5Wj4Q==" - }, - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/hoek": "^11.0.2", + "@hapi/topo": "^6.0.1" } }, - "@hapi/subtext": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@hapi/subtext/-/subtext-7.1.0.tgz", - "integrity": "sha512-n94cU6hlvsNRIpXaROzBNEJGwxC+HA69q769pChzej84On8vsU14guHDub7Pphr/pqn5b93zV3IkMPDU5AUiXA==", - "requires": { - "@hapi/boom": "9.x.x", - "@hapi/bourne": "2.x.x", - "@hapi/content": "^5.0.2", - "@hapi/file": "2.x.x", - "@hapi/hoek": "9.x.x", - "@hapi/pez": "^5.1.0", - "@hapi/wreck": "17.x.x" - }, + "node_modules/@hapi/subtext": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@hapi/subtext/-/subtext-8.1.0.tgz", + "integrity": "sha512-PyaN4oSMtqPjjVxLny1k0iYg4+fwGusIhaom9B2StinBclHs7v46mIW706Y+Wo21lcgulGyXbQrmT/w4dus6ww==", "dependencies": { - "@hapi/bourne": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-2.1.0.tgz", - "integrity": "sha512-i1BpaNDVLJdRBEKeJWkVO6tYX6DMFBuwMhSuWqLsY4ufeTKGVuV5rBsUhxPayXqnnWHgXUAmWK16H/ykO5Wj4Q==" - }, - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/boom": "^10.0.1", + "@hapi/bourne": "^3.0.0", + "@hapi/content": "^6.0.0", + "@hapi/file": "^3.0.0", + "@hapi/hoek": "^11.0.2", + "@hapi/pez": "^6.1.0", + "@hapi/wreck": "^18.0.1" } }, - "@hapi/teamwork": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/@hapi/teamwork/-/teamwork-5.1.1.tgz", - "integrity": "sha512-1oPx9AE5TIv+V6Ih54RP9lTZBso3rP8j4Xhb6iSVwPXtAM+sDopl5TFMv5Paw73UnpZJ9gjcrTE1BXrWt9eQrg==" + "node_modules/@hapi/teamwork": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@hapi/teamwork/-/teamwork-6.0.0.tgz", + "integrity": "sha512-05HumSy3LWfXpmJ9cr6HzwhAavrHkJ1ZRCmNE2qJMihdM5YcWreWPfyN0yKT2ZjCM92au3ZkuodjBxOibxM67A==", + "engines": { + "node": ">=14.0.0" + } }, - "@hapi/topo": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-3.1.6.tgz", - "integrity": "sha512-tAag0jEcjwH+P2quUfipd7liWCNX2F8NvYjQp2wtInsZxnMlypdw0FtAOLxtvvkO+GSRRbmNi8m/5y42PQJYCQ==", - "requires": { - "@hapi/hoek": "^8.3.0" + "node_modules/@hapi/topo": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-6.0.2.tgz", + "integrity": "sha512-KR3rD5inZbGMrHmgPxsJ9dbi6zEK+C3ZwUwTa+eMwWLz7oijWUTWD2pMSNNYJAU6Qq+65NkxXjqHr/7LM2Xkqg==", + "dependencies": { + "@hapi/hoek": "^11.0.2" } }, - "@hapi/validate": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@hapi/validate/-/validate-1.1.3.tgz", - "integrity": "sha512-/XMR0N0wjw0Twzq2pQOzPBZlDzkekGcoCtzO314BpIEsbXdYGthQUbxgkGDf4nhk1+IPDAsXqWjMohRQYO06UA==", - "requires": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0" - }, + "node_modules/@hapi/vise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hapi/vise/-/vise-5.0.1.tgz", + "integrity": "sha512-XZYWzzRtINQLedPYlIkSkUr7m5Ddwlu99V9elh8CSygXstfv3UnWIXT0QD+wmR0VAG34d2Vx3olqcEhRRoTu9A==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - }, - "@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - } + "@hapi/hoek": "^11.0.2" } }, - "@hapi/vise": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@hapi/vise/-/vise-4.0.0.tgz", - "integrity": "sha512-eYyLkuUiFZTer59h+SGy7hUm+qE9p+UemePTHLlIWppEd+wExn3Df5jO04bFQTm7nleF5V8CtuYQYb+VFpZ6Sg==", - "requires": { - "@hapi/hoek": "9.x.x" - }, + "node_modules/@hapi/vision": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@hapi/vision/-/vision-7.0.3.tgz", + "integrity": "sha512-1UM3Xej7HZQPaxzWkefvMfcuXoF9R8kIiDTl+Pfdv8f5mJwAv0zIB4R/UvNoQP1+JYgQT+QeUDxcGD8QdIUDyg==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/boom": "^10.0.1", + "@hapi/bounce": "^3.0.1", + "@hapi/hoek": "^11.0.2", + "@hapi/validate": "^2.0.1" } }, - "@hapi/vision": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@hapi/vision/-/vision-6.0.0.tgz", - "integrity": "sha512-NRG87SLS8evCTwAGlVHykmlk1i9z+7TD2pUwoDHRpjwqX/syt0ecrEKMLV/VdMijE9tCAtKIIXt+Myb16tT7Sw==", - "requires": { - "@hapi/boom": "9.x.x", - "@hapi/bounce": "2.x.x", - "@hapi/hoek": "9.x.x", - "@hapi/joi": "17.x.x" - }, + "node_modules/@hapi/vision/node_modules/@hapi/validate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@hapi/validate/-/validate-2.0.1.tgz", + "integrity": "sha512-NZmXRnrSLK8MQ9y/CMqE9WSspgB9xA41/LlYR0k967aSZebWr4yNrpxIbov12ICwKy4APSlWXZga9jN5p6puPA==", "dependencies": { - "@hapi/address": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@hapi/address/-/address-4.0.1.tgz", - "integrity": "sha512-0oEP5UiyV4f3d6cBL8F3Z5S7iWSX39Knnl0lY8i+6gfmmIBj44JCBNtcMgwyS+5v7j3VYavNay0NFHDS+UGQcw==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - }, - "@hapi/bounce": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/bounce/-/bounce-2.0.0.tgz", - "integrity": "sha512-JesW92uyzOOyuzJKjoLHM1ThiOvHPOLDHw01YV8yh5nCso7sDwJho1h0Ad2N+E62bZyz46TG3xhAi/78Gsct6A==", - "requires": { - "@hapi/boom": "9.x.x", - "@hapi/hoek": "9.x.x" - } - }, - "@hapi/formula": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/formula/-/formula-2.0.0.tgz", - "integrity": "sha512-V87P8fv7PI0LH7LiVi8Lkf3x+KCO7pQozXRssAHNXXL9L1K+uyu4XypLXwxqVDKgyQai6qj3/KteNlrqDx4W5A==" - }, - "@hapi/hoek": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz", - "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==" - }, - "@hapi/joi": { - "version": "17.1.1", - "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-17.1.1.tgz", - "integrity": "sha512-p4DKeZAoeZW4g3u7ZeRo+vCDuSDgSvtsB/NpfjXEHTUjSeINAi/RrVOWiVQ1isaoLzMvFEhe8n5065mQq1AdQg==", - "requires": { - "@hapi/address": "^4.0.1", - "@hapi/formula": "^2.0.0", - "@hapi/hoek": "^9.0.0", - "@hapi/pinpoint": "^2.0.0", - "@hapi/topo": "^5.0.0" - } - }, - "@hapi/pinpoint": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@hapi/pinpoint/-/pinpoint-2.0.0.tgz", - "integrity": "sha512-vzXR5MY7n4XeIvLpfl3HtE3coZYO4raKXW766R6DZw/6aLqR26iuZ109K7a0NtF2Db0jxqh7xz2AxkUwpUFybw==" - }, - "@hapi/topo": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.0.0.tgz", - "integrity": "sha512-tFJlT47db0kMqVm3H4nQYgn6Pwg10GTZHb1pwmSiv1K4ks6drQOtfEF5ZnPjkvC+y4/bUPHK+bc87QvLcL+WMw==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - } + "@hapi/hoek": "^11.0.2", + "@hapi/topo": "^6.0.1" } }, - "@hapi/wreck": { - "version": "17.2.0", - "resolved": "https://registry.npmjs.org/@hapi/wreck/-/wreck-17.2.0.tgz", - "integrity": "sha512-pJ5kjYoRPYDv+eIuiLQqhGon341fr2bNIYZjuotuPJG/3Ilzr/XtI+JAp0A86E2bYfsS3zBPABuS2ICkaXFT8g==", - "requires": { - "@hapi/boom": "9.x.x", - "@hapi/bourne": "2.x.x", - "@hapi/hoek": "9.x.x" - }, + "node_modules/@hapi/wreck": { + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@hapi/wreck/-/wreck-18.0.1.tgz", + "integrity": "sha512-OLHER70+rZxvDl75xq3xXOfd3e8XIvz8fWY0dqg92UvhZ29zo24vQgfqgHSYhB5ZiuFpSLeriOisAlxAo/1jWg==", "dependencies": { - "@hapi/bourne": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@hapi/bourne/-/bourne-2.1.0.tgz", - "integrity": "sha512-i1BpaNDVLJdRBEKeJWkVO6tYX6DMFBuwMhSuWqLsY4ufeTKGVuV5rBsUhxPayXqnnWHgXUAmWK16H/ykO5Wj4Q==" - }, - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/boom": "^10.0.1", + "@hapi/bourne": "^3.0.0", + "@hapi/hoek": "^11.0.2" } }, - "@istanbuljs/load-nyc-config": { + "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, - "requires": { + "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } }, - "@jest/console": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.0.1.tgz", - "integrity": "sha512-9t1KUe/93coV1rBSxMmBAOIK3/HVpwxArCA1CxskKyRiv6o8J70V8C/V3OJminVCTa2M0hQI9AWRd5wxu2dAHw==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/console": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-28.1.3.tgz", + "integrity": "sha512-QPAkP5EwKdK/bxIr6C1I4Vs0rm2nHiANzj/Z5X2JQkrZo6IqvC4ldZ9K95tF0HdidhA8Bo6egxSzUFPYKcEXLw==", "dev": true, - "requires": { - "@jest/types": "^26.0.1", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", "chalk": "^4.0.0", - "jest-message-util": "^26.0.1", - "jest-util": "^26.0.1", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", "slash": "^3.0.0" }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "@jest/core": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.0.1.tgz", - "integrity": "sha512-Xq3eqYnxsG9SjDC+WLeIgf7/8KU6rddBxH+SCt18gEpOhAGYC/Mq+YbtlNcIdwjnnT+wDseXSbU0e5X84Y4jTQ==", + "node_modules/@jest/core": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-28.1.3.tgz", + "integrity": "sha512-CIKBrlaKOzA7YG19BEqCw3SLIsEwjZkeJzf5bdooVnW4bH5cktqe3JX+G2YV1aK5vP8N9na1IGWFzYaTp6k6NA==", "dev": true, - "requires": { - "@jest/console": "^26.0.1", - "@jest/reporters": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/reporters": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", + "ci-info": "^3.2.0", "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.0.1", - "jest-config": "^26.0.1", - "jest-haste-map": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-resolve-dependencies": "^26.0.1", - "jest-runner": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "jest-watcher": "^26.0.1", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^28.1.3", + "jest-config": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-resolve-dependencies": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "jest-watcher": "^28.1.3", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", "rimraf": "^3.0.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/environment": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-28.1.3.tgz", + "integrity": "sha512-1bf40cMFTEkKyEf585R9Iz1WayDjHoHqvts0XFYEqyKM3cFWDpeMoqKKTAF9LSYQModPUlh8FKptoM2YcMWAXA==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-lzc8CpUbSoE4dqT0U+g1qODQjBRHPpCPXissXD4mS9+sWQdmmpeJ9zSH1rS1HEkrsMN0fb7nKrJ9giAR1d3wBw==", + "dev": true, + "dependencies": { + "expect": "^28.1.3", + "jest-snapshot": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "@jest/environment": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.0.1.tgz", - "integrity": "sha512-xBDxPe8/nx251u0VJ2dFAFz2H23Y98qdIaNwnMK6dFQr05jc+Ne/2np73lOAx+5mSBO/yuQldRrQOf6hP1h92g==", + "node_modules/@jest/expect-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-28.1.3.tgz", + "integrity": "sha512-wvbi9LUrHJLn3NlDW6wF2hvIMtd4JUl2QNVrjq+IBSHirgfrR3o9RnVtxzdEGO2n9JyIWwHnLfby5KzqBGg2YA==", "dev": true, - "requires": { - "@jest/fake-timers": "^26.0.1", - "@jest/types": "^26.0.1", - "jest-mock": "^26.0.1" + "dependencies": { + "jest-get-type": "^28.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "@jest/fake-timers": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.0.1.tgz", - "integrity": "sha512-Oj/kCBnTKhm7CR+OJSjZty6N1bRDr9pgiYQr4wY221azLz5PHi08x/U+9+QpceAYOWheauLP8MhtSVFrqXQfhg==", + "node_modules/@jest/fake-timers": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-28.1.3.tgz", + "integrity": "sha512-D/wOkL2POHv52h+ok5Oj/1gOG9HSywdoPtFsRCUmlCILXNn5eIWmcnd3DIiWlJnpGvQtmajqBP95Ei0EimxfLw==", "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "@sinonjs/fake-timers": "^6.0.1", - "jest-message-util": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-util": "^26.0.1" + "dependencies": { + "@jest/types": "^28.1.3", + "@sinonjs/fake-timers": "^9.1.2", + "@types/node": "*", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "@jest/globals": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.0.1.tgz", - "integrity": "sha512-iuucxOYB7BRCvT+TYBzUqUNuxFX1hqaR6G6IcGgEqkJ5x4htNKo1r7jk1ji9Zj8ZMiMw0oB5NaA7k5Tx6MVssA==", + "node_modules/@jest/globals": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-28.1.3.tgz", + "integrity": "sha512-XFU4P4phyryCXu1pbcqMO0GSQcYe1IsalYCDzRNyhetyeyxMcIxa11qPNDpVNLeretItNqEmYYQn1UYz/5x1NA==", "dev": true, - "requires": { - "@jest/environment": "^26.0.1", - "@jest/types": "^26.0.1", - "expect": "^26.0.1" + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/types": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "@jest/reporters": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.0.1.tgz", - "integrity": "sha512-NWWy9KwRtE1iyG/m7huiFVF9YsYv/e+mbflKRV84WDoJfBqUrNRyDbL/vFxQcYLl8IRqI4P3MgPn386x76Gf2g==", + "node_modules/@jest/reporters": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-28.1.3.tgz", + "integrity": "sha512-JuAy7wkxQZVNU/V6g9xKzCGC5LVXx9FDcABKsSXp5MiKPEE2144a/vXTEDoyzjUpZKfVwp08Wqg5A4WfTMAzjg==", "dev": true, - "requires": { + "dependencies": { "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", + "@jest/console": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "@types/node": "*", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-instrument": "^5.1.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.0.1", - "jest-resolve": "^26.0.1", - "jest-util": "^26.0.1", - "jest-worker": "^26.0.0", - "node-notifier": "^7.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", "slash": "^3.0.0", - "source-map": "^0.6.0", "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", "terminal-link": "^2.0.0", - "v8-to-istanbul": "^4.1.3" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + "v8-to-istanbul": "^9.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "@jest/source-map": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.0.0.tgz", - "integrity": "sha512-S2Z+Aj/7KOSU2TfW0dyzBze7xr95bkm5YXNUqqCek+HE0VbNNSNzrRwfIi5lf7wvzDTSS0/ib8XQ1krFNyYgbQ==", + "node_modules/@jest/schemas": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-28.1.3.tgz", + "integrity": "sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg==", "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" + "dependencies": { + "@sinclair/typebox": "^0.24.1" }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jest/source-map": { + "version": "28.1.2", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-28.1.2.tgz", + "integrity": "sha512-cV8Lx3BeStJb8ipPHnqVw/IM2VCMWO3crWZzYodSIkxXnRcXJipCdx1JCK0K5MsJJouZQTH73mzf4vgxRaH9ww==", + "dev": true, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - } + "@jridgewell/trace-mapping": "^0.3.13", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "@jest/test-result": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.0.1.tgz", - "integrity": "sha512-oKwHvOI73ICSYRPe8WwyYPTtiuOAkLSbY8/MfWF3qDEd/sa8EDyZzin3BaXTqufir/O/Gzea4E8Zl14XU4Mlyg==", + "node_modules/@jest/test-result": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-28.1.3.tgz", + "integrity": "sha512-kZAkxnSE+FqE8YjW8gNuoVkkC9I7S1qmenl8sGcDOLropASP+BkcGKwhXoyqQuGOGeYY0y/ixjrd/iERpEXHNg==", "dev": true, - "requires": { - "@jest/console": "^26.0.1", - "@jest/types": "^26.0.1", + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/types": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "@jest/test-sequencer": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.0.1.tgz", - "integrity": "sha512-ssga8XlwfP8YjbDcmVhwNlrmblddMfgUeAkWIXts1V22equp2GMIHxm7cyeD5Q/B0ZgKPK/tngt45sH99yLLGg==", + "node_modules/@jest/test-sequencer": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-28.1.3.tgz", + "integrity": "sha512-NIMPEqqa59MWnDi1kvXXpYbqsfQmSJsIbnd85mdVGkiDfQ9WQQTXOLsvISUfonmnBT+w85WEgneCigEEdHDFxw==", "dev": true, - "requires": { - "@jest/test-result": "^26.0.1", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.0.1", - "jest-runner": "^26.0.1", - "jest-runtime": "^26.0.1" - }, "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - } + "@jest/test-result": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "slash": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "@jest/transform": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.0.1.tgz", - "integrity": "sha512-pPRkVkAQ91drKGbzCfDOoHN838+FSbYaEAvBXvKuWeeRRUD8FjwXkqfUNUZL6Ke48aA/1cqq/Ni7kVMCoqagWA==", + "node_modules/@jest/transform": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-28.1.3.tgz", + "integrity": "sha512-u5dT5di+oFI6hfcLOHGTAfmUxFRrjK+vnaP0kkVow9Md/M7V/MxqQMOz/VV25UZO8pzeA9PjfTpOu6BDuwSPQA==", "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^26.0.1", - "babel-plugin-istanbul": "^6.0.0", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^28.1.3", + "@jridgewell/trace-mapping": "^0.3.13", + "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^1.4.0", "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-util": "^26.0.1", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "requires": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - } + "write-file-atomic": "^4.0.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "@jest/types": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.0.1.tgz", - "integrity": "sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==", + "node_modules/@jest/types": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-28.1.3.tgz", + "integrity": "sha512-RyjiyMUZrKz/c+zlMFO1pm70DcIlST8AeWTkoUdZevew44wcNZQHsEVOiCVtgVnlFFD82FPaXycys58cf2muVQ==", "dev": true, - "requires": { + "dependencies": { + "@jest/schemas": "^28.1.3", "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", "chalk": "^4.0.0" }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "engines": { + "node": ">=6.0.0" } }, - "@mapbox/node-pre-gyp": { + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@jsdevtools/ono": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/@jsdevtools/ono/-/ono-7.1.3.tgz", + "integrity": "sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==" + }, + "node_modules/@mapbox/node-pre-gyp": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz", "integrity": "sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==", - "requires": { + "dependencies": { "detect-libc": "^2.0.0", "https-proxy-agent": "^5.0.0", "make-dir": "^3.1.0", @@ -1884,1917 +1606,1200 @@ "semver": "^7.3.5", "tar": "^6.1.11" }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "requires": { - "yallist": "^4.0.0" - } - }, - "nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", - "requires": { - "abbrev": "1" - } - }, - "semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "requires": { - "lru-cache": "^6.0.0" - } - } + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" } }, - "@sideway/address": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.4.tgz", - "integrity": "sha512-7vwq+rOHVWjyXxVlR76Agnvhy8I9rpzjosTESvmhNeXOXdZZB15Fl+TI9x1SiHZH5Jv2wTGduSxFDIaq0m3DUw==", - "requires": { - "@hapi/hoek": "^9.0.0" - }, + "node_modules/@sideway/address": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/@sideway/address/-/address-4.1.5.tgz", + "integrity": "sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - } + "@hapi/hoek": "^9.0.0" } }, - "@sideway/formula": { + "node_modules/@sideway/address/node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/@sideway/formula": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sideway/formula/-/formula-3.0.1.tgz", "integrity": "sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==" }, - "@sideway/pinpoint": { + "node_modules/@sideway/pinpoint": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@sideway/pinpoint/-/pinpoint-2.0.0.tgz", "integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==" }, - "@sindresorhus/is": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", - "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==", + "node_modules/@sinclair/typebox": { + "version": "0.24.51", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.24.51.tgz", + "integrity": "sha512-1P1OROm/rdubP5aFDSZQILU0vrLCJ4fvHt6EoqHEM+2D/G5MK3bIaymUKLit8Js9gbns5UyJnkP/TZROLw4tUA==", "dev": true }, - "@sinonjs/commons": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.0.tgz", - "integrity": "sha512-wEj54PfsZ5jGSwMX68G8ZXFawcSglQSXqCftWX3ec8MDUzQdHgcKvw97awHbY0efQEL5iKUOAmmVtoYgmrSG4Q==", + "node_modules/@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", "dev": true, - "requires": { + "dependencies": { "type-detect": "4.0.8" } }, - "@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "node_modules/@sinonjs/fake-timers": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz", + "integrity": "sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw==", "dev": true, - "requires": { + "dependencies": { "@sinonjs/commons": "^1.7.0" } }, - "@szmarczak/http-timer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", - "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", - "dev": true, - "requires": { - "defer-to-connect": "^1.0.1" - } - }, - "@types/babel__core": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz", - "integrity": "sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw==", + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, - "@types/babel__generator": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", - "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, - "requires": { + "dependencies": { "@babel/types": "^7.0.0" } }, - "@types/babel__template": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", - "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, - "requires": { + "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, - "@types/babel__traverse": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.11.tgz", - "integrity": "sha512-ddHK5icION5U6q11+tV2f9Mo6CZVuT8GJKld2q9LqHSZbvLbH34Kcu2yFGckZut453+eQU6btIA3RihmnRgI+Q==", + "node_modules/@types/babel__traverse": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.5.tgz", + "integrity": "sha512-WXCyOcRtH37HAUkpXhUduaxdm82b4GSlyTqajXviN4EfiuPgNYR109xMCKvpl6zPIpua0DGlMEDCq+g8EdoheQ==", "dev": true, - "requires": { - "@babel/types": "^7.3.0" + "dependencies": { + "@babel/types": "^7.20.7" } }, - "@types/color-name": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", - "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==", - "dev": true - }, - "@types/graceful-fs": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.3.tgz", - "integrity": "sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ==", + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, - "requires": { + "dependencies": { "@types/node": "*" } }, - "@types/istanbul-lib-coverage": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.2.tgz", - "integrity": "sha512-rsZg7eL+Xcxsxk2XlBt9KcG8nOp9iYdKCOikY9x2RFJCyOdNj4MKPQty0e8oZr29vVAzKXr1BmR+kZauti3o1w==", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, - "requires": { + "dependencies": { "@types/istanbul-lib-coverage": "*" } }, - "@types/istanbul-reports": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*", + "dependencies": { "@types/istanbul-lib-report": "*" } }, - "@types/node": { - "version": "14.0.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.6.tgz", - "integrity": "sha512-FbNmu4F67d3oZMWBV6Y4MaPER+0EpE9eIYf2yaHhCWovc1dlXCZkqGX4NLHfVVr6umt20TNBdRzrNJIzIKfdbw==" + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==" }, - "@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true + "node_modules/@types/node": { + "version": "20.11.25", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.25.tgz", + "integrity": "sha512-TBHyJxk2b7HceLVGFcpAUjsa5zIdsPWlR6XHfyGzd0SFu+/NFgQgMAl96MSDZgQDvJAvV6BKsFOrt6zIL09JDw==", + "dependencies": { + "undici-types": "~5.26.4" + } }, - "@types/prettier": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.0.1.tgz", - "integrity": "sha512-boy4xPNEtiw6N3abRhBi/e7hNvy3Tt8E9ZRAQrwAGzoCGZS/1wjo9KY7JHhnfnEsG5wSjDbymCozUM9a3ea7OQ==", + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", "dev": true }, - "@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, - "@types/yargs": { - "version": "15.0.5", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz", - "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==", + "node_modules/@types/yargs": { + "version": "17.0.32", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", + "integrity": "sha512-xQ67Yc/laOG5uMfX/093MRlGGCIBzZMarVa+gfNKJxWAIgykYpVGkBdbqEzGDDfCrVUj6Hiff4mTZ5BA6TmAog==", "dev": true, - "requires": { + "dependencies": { "@types/yargs-parser": "*" } }, - "@types/yargs-parser": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", - "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", - "dev": true - }, - "abab": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", - "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, - "abbrev": { + "node_modules/abbrev": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" }, - "acorn": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.2.0.tgz", - "integrity": "sha512-apwXVmYVpQ34m/i71vrApRrRKCWQnZZF1+npOD0WV5xZFfwWOmKGQ2RWlfdy9vWITsenisM8M0Qeq8agcFHNiQ==", - "dev": true - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "acorn-walk": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", - "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==", - "dev": true - }, - "agent-base": { + "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "requires": { + "dependencies": { "debug": "4" }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - } + "engines": { + "node": ">= 6.0.0" } }, - "ajv": { - "version": "6.10.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz", - "integrity": "sha512-TXtUUEYHuaTEbLZWIKUr5pmBuhDLy+8KYtPYdcV8qC+pOZL+NKqYwvWSRrVXHn+ZmRRAu8vJTAznH7Oag6RVRw==", - "requires": { - "fast-deep-equal": "^2.0.1", + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dependencies": { + "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" - } - }, - "ansi-align": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.0.tgz", - "integrity": "sha512-ZpClVKqXN3RGBmKibdfWzqCY4lnjEuoNzU5T0oEFpfd/z5qJHVarukridD4juLO2FXMiwUQxr9WqQtaYa8XRYw==", - "dev": true, - "requires": { - "string-width": "^3.0.0" }, - "dependencies": { - "ansi-regex": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", - "integrity": "sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==", - "dev": true - }, - "emoji-regex": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", - "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", - "dev": true - }, - "string-width": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", - "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", - "dev": true, - "requires": { - "emoji-regex": "^7.0.1", - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^5.1.0" - } - }, - "strip-ansi": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", - "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", - "dev": true, - "requires": { - "ansi-regex": "^4.1.0" - } - } + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "ansi-escapes": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz", - "integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, - "requires": { - "type-fest": "^0.11.0" - }, "dependencies": { - "type-fest": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz", - "integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==", - "dev": true - } + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "requires": { - "color-convert": "^1.9.0" + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "any-promise": { + "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==" }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "aproba": { + "node_modules/aproba": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==" }, - "are-we-there-yet": { + "node_modules/are-we-there-yet": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", - "requires": { + "dependencies": { "delegates": "^1.0.0", "readable-stream": "^3.6.0" }, - "dependencies": { - "readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "requires": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - } - } - } - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" + "engines": { + "node": ">=10" } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "requires": { + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dependencies": { "safer-buffer": "~2.1.0" } }, - "assert-plus": { + "node_modules/assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "engines": { + "node": ">=0.8" + } }, - "asynckit": { + "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, - "aws-sign2": { + "node_modules/aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" - }, - "aws4": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" - }, - "axios": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.5.1.tgz", - "integrity": "sha512-Q28iYCWzNHjAm+yEAot5QaAMxhMghWLFVf7rRdwhUI+c2jix2DUXjAHXVi+s1ibs3mjPO/cCgbA++3BjD0vP/A==", - "requires": { - "follow-redirects": "^1.15.0", + "integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==", + "engines": { + "node": "*" + } + }, + "node_modules/aws4": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.12.0.tgz", + "integrity": "sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==" + }, + "node_modules/axios": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.7.tgz", + "integrity": "sha512-/hDJGff6/c7u0hDkvkGxR/oy6CbCs8ziCsC7SqmhjfozqiJGc8Z11wrv9z9lYfY4K8l+H9TpjcMDX0xOZmx+RA==", + "dependencies": { + "follow-redirects": "^1.15.4", "form-data": "^4.0.0", "proxy-from-env": "^1.1.0" - }, - "dependencies": { - "form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - } - } } }, - "babel-jest": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.0.1.tgz", - "integrity": "sha512-Z4GGmSNQ8pX3WS1O+6v3fo41YItJJZsVxG5gIQ+HuB/iuAQBJxMTHTwz292vuYws1LnHfwSRgoqI+nxdy/pcvw==", + "node_modules/babel-jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-28.1.3.tgz", + "integrity": "sha512-epUaPOEWMk3cWX0M/sPvCHHCe9fMFAa/9hXEgKP8nFfNl/jlGkE9ucq9NqkZGXLDduCJYS0UvSlPUwC0S+rH6Q==", "dev": true, - "requires": { - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "@types/babel__core": "^7.1.7", - "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.0.0", + "dependencies": { + "@jest/transform": "^28.1.3", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^28.1.3", "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", + "graceful-fs": "^4.2.9", "slash": "^3.0.0" }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" } }, - "babel-plugin-istanbul": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", - "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, - "requires": { + "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^4.0.0", + "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, - "babel-plugin-jest-hoist": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.0.0.tgz", - "integrity": "sha512-+AuoehOrjt9irZL7DOt2+4ZaTM6dlu1s5TTS46JBa0/qem4dy7VNW3tMb96qeEqcIh20LD73TVNtmVEeymTG7w==", + "node_modules/babel-plugin-jest-hoist": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-28.1.3.tgz", + "integrity": "sha512-Ys3tUKAmfnkRUpPdpa98eYrAR0nV+sSFUZZEGuQ2EbFd1y4SOLtD5QDNHAq+bb9a+bbXvYQC4b+ID/THIMcU6Q==", "dev": true, - "requires": { + "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "babel-preset-current-node-syntax": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz", - "integrity": "sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw==", + "node_modules/babel-preset-current-node-syntax": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", + "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", "dev": true, - "requires": { + "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-class-properties": "^7.8.3", + "@babel/plugin-syntax-import-meta": "^7.8.3", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-numeric-separator": "^7.8.3", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-top-level-await": "^7.8.3" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "babel-preset-jest": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.0.0.tgz", - "integrity": "sha512-9ce+DatAa31DpR4Uir8g4Ahxs5K4W4L8refzt+qHWQANb6LhGcAEfIFgLUwk67oya2cCUd6t4eUMtO/z64ocNw==", + "node_modules/babel-preset-jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-28.1.3.tgz", + "integrity": "sha512-L+fupJvlWAHbQfn74coNX3zf60LXMJsezNvvx8eIh7iOR1luJ1poxYgQk1F8PYtNq/6QODDHCqsSnTFSWC491A==", "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^26.0.0", - "babel-preset-current-node-syntax": "^0.1.2" + "dependencies": { + "babel-plugin-jest-hoist": "^28.1.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, - "bcrypt": { + "node_modules/bcrypt": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-5.1.1.tgz", "integrity": "sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==", - "requires": { + "hasInstallScript": true, + "dependencies": { "@mapbox/node-pre-gyp": "^1.0.11", "node-addon-api": "^5.0.0" + }, + "engines": { + "node": ">= 10.0.0" } }, - "bcrypt-pbkdf": { + "node_modules/bcrypt-pbkdf": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "requires": { + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dependencies": { "tweetnacl": "^0.14.3" } }, - "binary-extensions": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.0.0.tgz", - "integrity": "sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow==", - "dev": true - }, - "bl": { + "node_modules/binary-extensions": { "version": "2.2.0", - "resolved": "https://registry.npmjs.org/bl/-/bl-2.2.0.tgz", - "integrity": "sha512-wbgvOpqopSr7uq6fJrLH8EsvYMJf9gzfo2jCsL2eTy75qXPukA4pCgHamOQkZtY5vmfVtjB+P3LNlMHW5CEZXA==", - "requires": { - "readable-stream": "^2.3.5", - "safe-buffer": "^5.1.1" + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" } }, - "blipp": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/blipp/-/blipp-4.0.1.tgz", - "integrity": "sha512-nmtErzngVgJF6HlpnEymOil23m5U82oTYhbU8m619kQzj8yJ2q1ZFbL45i+dBcO92XTocyyj3QtC3GMxRujv8w==", - "requires": { - "@hapi/hoek": "8.x.x", - "@hapi/joi": "15.x.x", - "chalk": "2.x.x", - "easy-table": "1.x.x" - }, - "dependencies": { - "@hapi/joi": { - "version": "15.1.1", - "resolved": "https://registry.npmjs.org/@hapi/joi/-/joi-15.1.1.tgz", - "integrity": "sha512-entf8ZMOK8sc+8YfeOlM8pCfg3b5+WZIKBfUaaJT8UsjAAPjartzxIYm3TIbjvA4u+u++KbcXD38k682nVHDAQ==", - "requires": { - "@hapi/address": "2.x.x", - "@hapi/bourne": "1.x.x", - "@hapi/hoek": "8.x.x", - "@hapi/topo": "3.x.x" - } - } + "node_modules/blipp": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/blipp/-/blipp-4.0.2.tgz", + "integrity": "sha512-QA5amT0IFJgCFgJeWw2udD2zZLui60NgqXTyvbSq+qpVbS6jfqELTRlC8PWW0yD4+chdZ2a+svnN6WE9zqfK5Q==", + "dependencies": { + "@hapi/hoek": "9.x.x", + "chalk": "4.x.x", + "easy-table": "1.x.x", + "joi": "17.x.x" } }, - "bluebird": { + "node_modules/blipp/node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/bluebird": { "version": "3.7.2", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, - "boxen": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/boxen/-/boxen-4.2.0.tgz", - "integrity": "sha512-eB4uT9RGzg2odpER62bBwSLvUeGC+WbRjjyyFhGsKnc8wp/m0+hQsMUvUe3H2V0D5vw0nBdO1hCJoZo5mKeuIQ==", - "dev": true, - "requires": { - "ansi-align": "^3.0.0", - "camelcase": "^5.3.1", - "chalk": "^3.0.0", - "cli-boxes": "^2.2.0", - "string-width": "^4.1.0", - "term-size": "^2.1.0", - "type-fest": "^0.8.1", - "widest-line": "^3.1.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" } }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true + "node_modules/browserslist": { + "version": "4.23.0", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.0.tgz", + "integrity": "sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "caniuse-lite": "^1.0.30001587", + "electron-to-chromium": "^1.4.668", + "node-releases": "^2.0.14", + "update-browserslist-db": "^1.0.13" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } }, - "bser": { + "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, - "requires": { + "dependencies": { "node-int64": "^0.4.0" } }, - "bson": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/bson/-/bson-1.1.4.tgz", - "integrity": "sha512-S/yKGU1syOMzO86+dGpg2qGoDL0zvzcb262G+gqEy6TgP6rt6z6qxSFX/8X6vLC91P7G7C3nLs0+bvDzmvBA3Q==" - }, - "buffer-equal-constant-time": { + "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" }, - "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "cacheable-request": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", - "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", - "dev": true, - "requires": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^3.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^4.1.0", - "responselike": "^1.0.2" - }, - "dependencies": { - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } - } - }, - "call-me-maybe": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.1.tgz", - "integrity": "sha1-JtII6onje1y95gJQoV8DHBak1ms=" + "node_modules/call-me-maybe": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-me-maybe/-/call-me-maybe-1.0.2.tgz", + "integrity": "sha512-HpX65o1Hnr9HH25ojC1YGs7HCQLq0GCOibSaWER0eNpgJ/Z1MZv2mTc7+xh6WOPxbRVcmgbv4hGU+uSQ/2xFZQ==" }, - "callsites": { + "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "camelcase": { + "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", "dev": true, - "requires": { - "rsvp": "^4.8.4" + "engines": { + "node": ">=6" } }, - "caseless": { + "node_modules/caniuse-lite": { + "version": "1.0.30001596", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001596.tgz", + "integrity": "sha512-zpkZ+kEr6We7w63ORkoJ2pOfBwBkY/bJrG/UZ90qNb45Isblu8wzDgevEOrRL1r9dWayHjYiiyCMEXPn4DweGQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ] + }, + "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==" }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "char-regex": { + "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "chokidar": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.4.0.tgz", - "integrity": "sha512-aXAaho2VJtisB/1fg1+3nlLJqGOuewTzQpd/Tz0yTg2R0e4IGtshYvtjowyEumcBv2z+y4+kc75Mz7j5xJskcQ==", + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "requires": { - "anymatch": "~3.1.1", + "dependencies": { + "anymatch": "~3.1.2", "braces": "~3.0.2", - "fsevents": "~2.1.2", - "glob-parent": "~5.1.0", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.4.0" - }, - "dependencies": { - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" } }, - "chownr": { + "node_modules/chownr": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "engines": { + "node": ">=10" + } }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" } + ], + "engines": { + "node": ">=8" } }, - "cli-boxes": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-2.2.0.tgz", - "integrity": "sha512-gpaBrMAizVEANOpfZp/EEUixTXDyGt7DFzdK5hU+UbWt/J0lB0w20ncZj59Z9a93xHb9u12zF5BS6i9RKbtg4w==", + "node_modules/cjs-module-lexer": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", + "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", "dev": true }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, - "requires": { + "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "clone": { + "node_modules/clone": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", - "integrity": "sha1-2jCcwmPfFZlMaIypAheco8fNfH4=", - "optional": true - }, - "clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", - "dev": true, - "requires": { - "mimic-response": "^1.0.0" + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "optional": true, + "engines": { + "node": ">=0.8" } }, - "cls-bluebird": { + "node_modules/cls-bluebird": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cls-bluebird/-/cls-bluebird-2.1.0.tgz", - "integrity": "sha1-N+8eCAqP+1XC9BZPU28ZGeeWiu4=", - "requires": { + "integrity": "sha512-XVb0RPmHQyy35Tz9z34gvtUcBKUK8A/1xkGCyeFc9B0C7Zr5SysgFaswRVdwI5NEMcO+3JKlIDGIOgERSn9NdA==", + "dependencies": { "is-bluebird": "^1.0.2", "shimmer": "^1.1.0" } }, - "co": { + "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" + "engines": { + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "requires": { - "color-name": "1.1.3" + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=" + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "color-support": { + "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==" + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "bin": { + "color-support": "bin.js" + } }, - "combined-stream": { + "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "requires": { + "dependencies": { "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" } }, - "commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "optional": true - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true + "node_modules/commander": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.5.0.tgz", + "integrity": "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==", + "optional": true, + "engines": { + "node": "^12.20.0 || >=14" + } }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" - }, - "configstore": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/configstore/-/configstore-5.0.1.tgz", - "integrity": "sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==", - "dev": true, - "requires": { - "dot-prop": "^5.2.0", - "graceful-fs": "^4.1.2", - "make-dir": "^3.0.0", - "unique-string": "^2.0.0", - "write-file-atomic": "^3.0.0", - "xdg-basedir": "^4.0.0" - } + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, - "console-control-strings": { + "node_modules/console-control-strings": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==" }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", "dev": true }, - "core-js": { - "version": "2.6.11", - "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", - "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" - }, - "core-util-is": { + "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "crypto-random-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/crypto-random-string/-/crypto-random-string-2.0.0.tgz", - "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==", - "dev": true - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==" }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "requires": { - "cssom": "~0.3.6" - }, "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "dashdash": { + "node_modules/dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "dependencies": { "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" } }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "debug": { - "version": "3.2.6", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", - "integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==", - "requires": { - "ms": "^2.1.1" + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decimal.js": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", - "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "node_modules/dedent": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz", + "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, - "decompress-response": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", - "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, - "requires": { - "mimic-response": "^1.0.0" + "engines": { + "node": ">=0.10.0" } }, - "deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "defaults": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.3.tgz", - "integrity": "sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=", + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "optional": true, - "requires": { + "dependencies": { "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "defer-to-connect": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", - "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", - "dev": true - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "delayed-stream": { + "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } }, - "delegates": { + "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==" }, - "denque": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/denque/-/denque-1.4.1.tgz", - "integrity": "sha512-OfzPuSZKGcgr96rf1oODnfjqBFmr1DVoc/TrItj3Ohe0Ah1C5WX5Baquw/9U9KovnQ88EqmJbD66rKYUQYN1tQ==" + "node_modules/denque": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", + "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==", + "engines": { + "node": ">=0.10" + } }, - "detect-libc": { + "node_modules/detect-libc": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.2.tgz", - "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==" + "integrity": "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==", + "engines": { + "node": ">=8" + } }, - "detect-newline": { + "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff-sequences": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz", - "integrity": "sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg==", - "dev": true - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } + "engines": { + "node": ">=8" } }, - "dot-prop": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz", - "integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==", + "node_modules/diff-sequences": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-28.1.1.tgz", + "integrity": "sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw==", "dev": true, - "requires": { - "is-obj": "^2.0.0" + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "dotenv": { + "node_modules/dotenv": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.6.0.tgz", - "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==" - }, - "dottie": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", - "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==" - }, - "duplexer3": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", - "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=", - "dev": true - }, - "duplexify": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz", - "integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==", - "requires": { - "end-of-stream": "^1.0.0", - "inherits": "^2.0.1", - "readable-stream": "^2.0.0", - "stream-shift": "^1.0.0" + "integrity": "sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==", + "engines": { + "node": ">=10" } }, - "easy-table": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.1.1.tgz", - "integrity": "sha512-C9Lvm0WFcn2RgxbMnTbXZenMIWcBtkzMr+dWqq/JsVoGFSVUVlPqeOa5LP5kM0I3zoOazFpckOEb2/0LDFfToQ==", - "requires": { - "ansi-regex": "^3.0.0", - "wcwidth": ">=1.0.1" + "node_modules/dottie": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.6.tgz", + "integrity": "sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA==" + }, + "node_modules/easy-table": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/easy-table/-/easy-table-1.2.0.tgz", + "integrity": "sha512-OFzVOv03YpvtcWGe5AayU5G2hgybsg3iqA6drU8UaoZyB9jLGMTrz9+asnLp/E+6qPh88yEI1gvyZFZ41dmgww==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "optionalDependencies": { + "wcwidth": "^1.0.1" } }, - "ecc-jsbn": { + "node_modules/ecc-jsbn": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "requires": { + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "dependencies": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" } }, - "ecdsa-sig-formatter": { + "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", - "requires": { + "dependencies": { "safe-buffer": "^5.0.1" } }, - "emoji-regex": { + "node_modules/electron-to-chromium": { + "version": "1.4.695", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.695.tgz", + "integrity": "sha512-eMijZmeqPtm774pCZIOrfUHMs/7ls++W1sLhxwqgu8KQ8E2WmMtzwyqOMt0XXUJ3HTIPfuwlfwF+I5cwnfItBA==", + "dev": true + }, + "node_modules/emittery": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.10.2.tgz", + "integrity": "sha512-aITqOwnLanpHLNXZJENbOgjUBeHocD+xsSJmNrjovKBW5HbSpW3d1pEls7GFQPUWXiwG9+0P4GtHfEqC/4M0Iw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sindresorhus/emittery?sponsor=1" + } + }, + "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "requires": { - "once": "^1.4.0" - } - }, - "error-ex": { + "node_modules/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, - "requires": { + "dependencies": { "is-arrayish": "^0.2.1" } }, - "escape-goat": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/escape-goat/-/escape-goat-2.1.1.tgz", - "integrity": "sha512-8/uIhbG12Csjy2JEW7D9pHbreaVaS/OpN3ycnyvElTdwM5n6GY6W6e2IPemfvGZeUMqZ9A/3GqIZMgKnBhAw/Q==", - "dev": true - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + "node_modules/escalade": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz", + "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==", + "dev": true, + "engines": { + "node": ">=6" + } }, - "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" + "engines": { + "node": ">=8" } }, - "esprima": { + "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "exec-sh": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", - "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" } }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "expect": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.0.1.tgz", - "integrity": "sha512-QcCy4nygHeqmbw564YxNbHTJlXh47dVID2BUP52cZFpLU9zHViMFK6h07cC1wf7GYCTIigTdAXhVua8Yl1FkKg==", + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.0.0", - "jest-matcher-utils": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-regex-util": "^26.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/expect": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/expect/-/expect-28.1.3.tgz", + "integrity": "sha512-eEh0xn8HlsuOBxFgIss+2mX85VAS4Qy3OSkjV7rlBWljtA4oWH37glVGyOZSZvErDT/yBywZdPGwCXuTvSG85g==", + "dev": true, + "dependencies": { + "@jest/expect-utils": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "extend": { + "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "extsprintf": { + "node_modules/extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "fast-deep-equal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" - }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + "integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==", + "engines": [ + "node >=0.6.0" + ] }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, - "fast-safe-stringify": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", - "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" }, - "fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, - "requires": { + "dependencies": { "bser": "2.1.1" } }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "find-up": { + "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, - "requires": { + "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "follow-redirects": { - "version": "1.15.3", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", - "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==" - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true + "node_modules/follow-redirects": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.5.tgz", + "integrity": "sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } }, - "forever-agent": { + "node_modules/forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==", + "engines": { + "node": "*" + } }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "requires": { + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", + "combined-stream": "^1.0.8", "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" } }, - "format-util": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/format-util/-/format-util-1.0.5.tgz", - "integrity": "sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg==" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fs-minipass": { + "node_modules/fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "requires": { + "dependencies": { "minipass": "^3.0.0" }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - } + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "optional": true + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, - "gauge": { + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", - "requires": { + "dependencies": { "aproba": "^1.0.3 || ^2.0.0", "color-support": "^1.1.2", "console-control-strings": "^1.0.0", @@ -3805,2439 +2810,1238 @@ "strip-ansi": "^6.0.1", "wide-align": "^1.1.2" }, - "dependencies": { - "ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - }, - "string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - } - }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "requires": { - "ansi-regex": "^5.0.1" - } - } + "engines": { + "node": ">=10" } }, - "generate-function": { + "node_modules/generate-function": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", "integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==", - "requires": { + "dependencies": { "is-property": "^1.0.2" } }, - "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "dev": true + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } }, - "get-caller-file": { + "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } }, - "get-package-type": { + "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - }, - "dependencies": { - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - } + "dev": true, + "engines": { + "node": ">=8.0.0" } }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "getpass": { + "node_modules/getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "dependencies": { "assert-plus": "^1.0.0" } }, - "glob": { - "version": "7.1.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.5.tgz", - "integrity": "sha512-J9dlskqUXK1OeTOYBEn5s8aMukWMwWfs+rPTn/jn50Ux4MNXVhubL1wu/j2t+H4NVI+cXEcCaYellqaPVGXNqQ==", - "requires": { + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, - "requires": { + "dependencies": { "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "global-dirs": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/global-dirs/-/global-dirs-2.0.1.tgz", - "integrity": "sha512-5HqUqdhkEovj2Of/ms3IeS/EekcO54ytHRLV4PEY2rhRwrHXLQjeVEES0Lhka0xwNDtGYn58wyC4s5+MHsOO6A==", - "dev": true, - "requires": { - "ini": "^1.3.5" - } - }, - "globals": { + "node_modules/globals": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "dev": true, + "engines": { + "node": ">=4" + } }, - "got": { - "version": "9.6.0", - "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", - "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", - "dev": true, - "requires": { - "@sindresorhus/is": "^0.14.0", - "@szmarczak/http-timer": "^1.1.2", - "cacheable-request": "^6.0.0", - "decompress-response": "^3.3.0", - "duplexer3": "^0.1.4", - "get-stream": "^4.1.0", - "lowercase-keys": "^1.0.1", - "mimic-response": "^1.0.1", - "p-cancelable": "^1.0.0", - "to-readable-stream": "^1.0.0", - "url-parse-lax": "^3.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "dev": true }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true, - "optional": true + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } }, - "hapi-swagger": { - "version": "13.0.2", - "resolved": "https://registry.npmjs.org/hapi-swagger/-/hapi-swagger-13.0.2.tgz", - "integrity": "sha512-fPUSVPLh1tpCS1q1ECUhmxL3sZCo6MmrQArwZIoh7QrwN7Tmw2zprz5nGS96KjSQE/MDJOfq82pdEq6FHD7yIQ==", - "requires": { - "@hapi/boom": "^8.0.1", - "@hapi/hoek": "^9.0.2", - "handlebars": "^4.5.3", - "http-status": "^1.0.1", - "json-schema-ref-parser": "^6.1.0", - "swagger-parser": "4.0.2", - "swagger-ui-dist": "^3.22.1" - }, - "dependencies": { - "@hapi/boom": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-8.0.1.tgz", - "integrity": "sha512-SnBM2GzEYEA6AGFKXBqNLWXR3uNBui0bkmklYXX1gYtevVhDTy2uakwkSauxvIWMtlANGRhzChYg95If3FWCwA==", - "requires": { - "@hapi/hoek": "8.x.x" - }, - "dependencies": { - "@hapi/hoek": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-8.5.1.tgz", - "integrity": "sha512-yN7kbciD87WzLGc5539Tn0sApjyiGHAJgKvG9W8C7O+6c7qmoQMfVs0W4bX17eqz6C78QJqqFrtgdK5EWf6Qow==" - } - } - }, - "@hapi/hoek": { - "version": "9.0.4", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.0.4.tgz", - "integrity": "sha512-EwaJS7RjoXUZ2cXXKZZxZqieGtc7RbvQhUy8FwDoMQtxWVi14tFjeFCYPZAM1mBCpOpiBpyaZbb9NeHc7eGKgw==" - }, - "handlebars": { - "version": "4.7.6", - "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.6.tgz", - "integrity": "sha512-1f2BACcBfiwAfStCKZNrUCgqNZkGsAT7UM3kkYtXuLo0KnaVfjKOyf7PRzB6++aK9STyT1Pd2ZCPe3EGOXleXA==", - "requires": { - "minimist": "^1.2.5", - "neo-async": "^2.6.0", - "source-map": "^0.6.1", - "uglify-js": "^3.1.4", - "wordwrap": "^1.0.0" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" - }, - "wordwrap": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" - } + "node_modules/hapi-cors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/hapi-cors/-/hapi-cors-1.0.3.tgz", + "integrity": "sha512-45fkvy13d+Awp25OXuMj8imQSoD3x5SJ99D+P/WBEwruHArpoHdj+zMlrXOoixgo/O281/lls6rAZBnkBtNOpg==", + "deprecated": "This package is no longer being supported. Please migrate to the CORS functionality built into hapi.", + "dependencies": { + "joi": "^7.0.1" + } + }, + "node_modules/hapi-cors/node_modules/joi": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/joi/-/joi-7.3.0.tgz", + "integrity": "sha512-7ysLFfGtSg5L1MWnIkJGvJLAsdZPLZK2+qAi+0D9QdsnlPVSk+dBZxEl1ezveJuhEcvZKLK+AZSkmnXeATcB0A==", + "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", + "dependencies": { + "hoek": "3.x.x", + "isemail": "2.x.x", + "moment": "2.x.x", + "topo": "2.x.x" + }, + "engines": { + "node": ">=4.0.0" } }, - "har-schema": { + "node_modules/hapi-swagger": { + "version": "17.2.1", + "resolved": "https://registry.npmjs.org/hapi-swagger/-/hapi-swagger-17.2.1.tgz", + "integrity": "sha512-IaF3OHfYjzDuyi5EQgS0j0xB7sbAAD4DaTwexdhPYqEBI/J7GWMXFbftGObCIOeMVDufjoSBZWeaarEkNn6/ww==", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^11.1.0", + "@hapi/boom": "^10.0.1", + "@hapi/hoek": "^11.0.2", + "handlebars": "^4.7.8", + "http-status": "^1.7.3", + "swagger-parser": "^10.0.3", + "swagger-ui-dist": "^5.9.1" + }, + "engines": { + "node": ">=16.0.0" + }, + "peerDependencies": { + "@hapi/hapi": ">=20.x.x", + "joi": "17.x" + } + }, + "node_modules/har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "requires": { - "ajv": "^6.5.5", + "integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==", + "engines": { + "node": ">=4" + } + }, + "node_modules/har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "deprecated": "this library is no longer supported", + "dependencies": { + "ajv": "^6.12.3", "har-schema": "^2.0.0" + }, + "engines": { + "node": ">=6" } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } }, - "has-unicode": { + "node_modules/has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==" }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "node_modules/hasown": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", + "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "has-yarn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-2.1.0.tgz", - "integrity": "sha512-UqBRqi4ju7T+TqGNdqAO0PaSVGsDGJUBQvk9eUWNGRY1CFGDzYhLWoM7JQEemnlvVcv/YEmc2wNW8BC24EnUsw==", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.5" + "node_modules/hoek": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-3.0.4.tgz", + "integrity": "sha512-VIMFzySNWnvVqBZIWJSHzun/dvtgYYxv0DypA8Mr9ue+kjXyf1mkq4/EOU/a33cIoW+fFyk9+t8W6ZSqucKYpA==", + "deprecated": "This version has been deprecated in accordance with the hapi support policy (hapi.im/support). Please upgrade to the latest version to get the best features, bug fixes, and security patches. If you are unable to upgrade at this time, paid support is available for older versions (hapi.im/commercial).", + "engines": { + "node": ">=4.0.0" } }, - "html-escaper": { + "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, - "http-cache-semantics": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", - "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==", - "dev": true - }, - "http-signature": { + "node_modules/http-signature": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { + "integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==", + "dependencies": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", "sshpk": "^1.7.0" + }, + "engines": { + "node": ">=0.8", + "npm": ">=1.3.7" } }, - "http-status": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/http-status/-/http-status-1.4.2.tgz", - "integrity": "sha512-mBnIohUwRw9NyXMEMMv8/GANnzEYUj0Y8d3uL01zDWFkxUjYyZ6rgCaAI2zZ1Wb34Oqtbx/nFZolPRDc8Xlm5A==" + "node_modules/http-status": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/http-status/-/http-status-1.7.4.tgz", + "integrity": "sha512-c2qSwNtTlHVYAhMj9JpGdyo0No/+DiKXCJ9pHtZ2Yf3QmPnBIytKSRT7BuyIiQ7icXLynavGmxUqkOjSrAuMuA==", + "engines": { + "node": ">= 0.4.0" + } }, - "https-proxy-agent": { + "node_modules/https-proxy-agent": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "requires": { + "dependencies": { "agent-base": "6", "debug": "4" }, - "dependencies": { - "debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", - "requires": { - "ms": "2.1.2" - } - } + "engines": { + "node": ">= 6" } }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "engines": { + "node": ">=10.17.0" + } }, - "husky": { + "node_modules/husky": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz", "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" + "bin": { + "husky": "lib/bin.js" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "ignore-by-default": { + "node_modules/ignore-by-default": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", - "integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk=", - "dev": true - }, - "import-lazy": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz", - "integrity": "sha1-BWmOPUXIjo1+nZLLBYTnfwlvPkM=", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, - "import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", + "node_modules/import-local": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", + "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", "dev": true, - "requires": { + "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "imurmurhash": { + "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } }, - "inflection": { + "node_modules/inflection": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.12.0.tgz", - "integrity": "sha1-ogCTVlbW9fa8TcdQLhrstwMihBY=" + "integrity": "sha512-lRy4DxuIFWXlJU7ed8UiTJOSTqStqYdEb4CEbtXfNbkdj3nH1L+reUWiE10VWcJS2yR7tge8Z74pJjtBjNwj0w==", + "engines": [ + "node >= 0.4.0" + ] }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "requires": { + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, - "ini": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", - "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", - "dev": true - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "node_modules/install": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/install/-/install-0.13.0.tgz", + "integrity": "sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==", + "engines": { + "node": ">= 0.10" } }, - "is-arrayish": { + "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, - "is-binary-path": { + "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "requires": { + "dependencies": { "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "is-bluebird": { + "node_modules/is-bluebird": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-bluebird/-/is-bluebird-1.0.2.tgz", - "integrity": "sha1-CWQ5Bg9KpBGr7hkUOoTWpVNG1uI=" + "integrity": "sha512-PDRu1vVip5dGQg5tfn2qVCCyxbBYu5MhYUJwSfL/RoGBI97n1fxvilVazxzptZW0gcmsMH17H4EVZZI5E/RSeA==", + "engines": { + "node": ">=0.10.0" + } }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "requires": { - "ci-info": "^2.0.0" + "engines": { + "node": ">=0.10.0" } }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "engines": { + "node": ">=6" } }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-docker": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", - "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, - "optional": true - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-installed-globally": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/is-installed-globally/-/is-installed-globally-0.3.2.tgz", - "integrity": "sha512-wZ8x1js7Ia0kecP/CHM/3ABkAmujX7WPvQk6uu3Fly/Mk44pySulQpnHG46OMjHGXApINnV4QhY3SWnECO2z5g==", - "dev": true, - "requires": { - "global-dirs": "^2.0.1", - "is-path-inside": "^3.0.1" - } - }, - "is-npm": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-npm/-/is-npm-4.0.0.tgz", - "integrity": "sha512-96ECIfh9xtDDlPylNPXhzjsykHsMJZ18ASpaWzQyBr4YRTcVjUvzaHayDAES2oU/3KpljhHUjtSRNiDwi0F0ig==", - "dev": true - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "engines": { + "node": ">=0.12.0" } }, - "is-obj": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", - "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==", - "dev": true - }, - "is-path-inside": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.2.tgz", - "integrity": "sha512-/2UGPSgmtqwo1ktx8NDHjuPwZWmHhO+gj0f93EkhLB5RgW9RZevWYYlIkS6zePc6U2WpOdQYIwHe9YC4DWEBVg==", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-potential-custom-element-name": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", - "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", - "dev": true - }, - "is-property": { + "node_modules/is-property": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", "integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g==" }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "is-typedarray": { + "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true + "integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==" }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "optional": true, - "requires": { - "is-docker": "^2.0.0" + "node_modules/isemail": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isemail/-/isemail-2.2.1.tgz", + "integrity": "sha512-LPjFxaTatluwGAJlGe4FtRdzg0a9KlXrahHoHAR4HwRNf90Ttwi6sOQ9zj+EoCPmk9yyK+WFUqkm0imUo8UJbw==", + "engines": { + "node": ">=4.0.0" } }, - "is-yarn-global": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is-yarn-global/-/is-yarn-global-0.3.0.tgz", - "integrity": "sha512-VjSeb/lHmkoyd8ryPVIKvOCn4D1koMqY+vqyjjUfc3xyKtP4dYOxM44sZrnqQSzSds3xyOrUTLTC9LVCVgLngw==", - "dev": true - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, - "isexe": { + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, - "isstream": { + "node_modules/isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==" }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "engines": { + "node": ">=8" + } }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, - "requires": { - "@babel/core": "^7.7.5", + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "engines": { + "node": ">=8" } }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, - "requires": { + "dependencies": { "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", + "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": ">=10" } }, - "istanbul-lib-source-maps": { + "node_modules/istanbul-lib-report/node_modules/make-dir": { "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, - "requires": { + "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "dev": true, - "requires": { - "ms": "^2.1.1" - } - } + "engines": { + "node": ">=10" } }, - "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, - "requires": { + "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "jest": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.0.1.tgz", - "integrity": "sha512-29Q54kn5Bm7ZGKIuH2JRmnKl85YRigp0o0asTc6Sb6l2ch1DCXIeZTLLFy9ultJvhkTqbswF5DEx4+RlkmCxWg==", + "node_modules/jest": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest/-/jest-28.1.3.tgz", + "integrity": "sha512-N4GT5on8UkZgH0O5LUavMRV1EDEhNTL0KEfRmDIeZHSV7p2XgLoY9t9VDUgL6o+yfdgYHVxuz81G8oB9VG5uyA==", "dev": true, - "requires": { - "@jest/core": "^26.0.1", + "dependencies": { + "@jest/core": "^28.1.3", + "@jest/types": "^28.1.3", "import-local": "^3.0.2", - "jest-cli": "^26.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "jest-cli": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.0.1.tgz", - "integrity": "sha512-pFLfSOBcbG9iOZWaMK4Een+tTxi/Wcm34geqZEqrst9cZDkTQ1LZ2CnBrTlHWuYAiTMFr0EQeK52ScyFU8wK+w==", - "dev": true, - "requires": { - "@jest/core": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "is-ci": "^2.0.0", - "jest-config": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "prompts": "^2.0.1", - "yargs": "^15.3.1" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + "jest-cli": "^28.1.3" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "jest-changed-files": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.0.1.tgz", - "integrity": "sha512-q8LP9Sint17HaE2LjxQXL+oYWW/WeeXMPE2+Op9X3mY8IEGFVc14xRxFjUuXUbcPAlDLhtWdIEt59GdQbn76Hw==", + "node_modules/jest-changed-files": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-28.1.3.tgz", + "integrity": "sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==", "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "execa": "^4.0.0", - "throat": "^5.0.0" + "dependencies": { + "execa": "^5.0.0", + "p-limit": "^3.1.0" }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-circus": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-28.1.3.tgz", + "integrity": "sha512-cZ+eS5zc79MBwt+IhQhiEp0OeBddpc1n8MBo1nMB8A7oPMKEO+Sre+wHaLJexQUj9Ya/8NOBY0RESUgYjB6fow==", + "dev": true, "dependencies": { - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "execa": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.2.tgz", - "integrity": "sha512-QI2zLa6CjGWdiQsmSkZoGtDx2N+cQIGb3yNolGTdjSQzydzLgYYf8LRuagp7S7fPimjcrzUDSUFd/MgzELMi4Q==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.1.0.tgz", - "integrity": "sha512-EXr1FOzrzTfGeL0gQdeFEvOMm2mzMOglyiOXSTpPC+iAjAKftbr3jpCMWynogwYnM+eSj9sHGc6wjIcDvYiygw==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } + "@jest/environment": "^28.1.3", + "@jest/expect": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^0.7.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", + "p-limit": "^3.1.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-cli": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-28.1.3.tgz", + "integrity": "sha512-roY3kvrv57Azn1yPgdTebPAXvdR2xfezaKKYzVxZ6It/5NCxzJym6tUI5P1zkdWhfUYkxEI9uZWcQdaFLo8mJQ==", + "dev": true, + "dependencies": { + "@jest/core": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "import-local": "^3.0.2", + "jest-config": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "prompts": "^2.0.1", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true } } }, - "jest-config": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.0.1.tgz", - "integrity": "sha512-9mWKx2L1LFgOXlDsC4YSeavnblN6A4CPfXFiobq+YYLaBMymA/SczN7xYTSmLaEYHZOcB98UdoN4m5uNt6tztg==", + "node_modules/jest-config": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-28.1.3.tgz", + "integrity": "sha512-MG3INjByJ0J4AsNBm7T3hsuxKQqFIiRo/AUqb1q9LRKI5UU6Aar9JHbr9Ivn1TVwfUD9KirRoM/T6u8XlcQPHQ==", "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.0.1", - "@jest/types": "^26.0.1", - "babel-jest": "^26.0.1", + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^28.1.3", + "@jest/types": "^28.1.3", + "babel-jest": "^28.1.3", "chalk": "^4.0.0", + "ci-info": "^3.2.0", "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.0.1", - "jest-environment-node": "^26.0.1", - "jest-get-type": "^26.0.0", - "jest-jasmine2": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "micromatch": "^4.0.2", - "pretty-format": "^26.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^28.1.3", + "jest-environment-node": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-runner": "^28.1.3", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^28.1.3", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } + "ts-node": { + "optional": true } } }, - "jest-diff": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.0.1.tgz", - "integrity": "sha512-odTcHyl5X+U+QsczJmOjWw5tPvww+y9Yim5xzqxVl/R1j4z71+fHW4g8qu1ugMmKdFdxw+AtQgs5mupPnzcIBQ==", + "node_modules/jest-diff": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-28.1.3.tgz", + "integrity": "sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.0.0", - "diff-sequences": "^26.0.0", - "jest-get-type": "^26.0.0", - "pretty-format": "^26.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "diff-sequences": "^28.1.1", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-docblock": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", - "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", + "node_modules/jest-docblock": { + "version": "28.1.1", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-28.1.1.tgz", + "integrity": "sha512-3wayBVNiOYx0cwAbl9rwm5kKFP8yHH3d/fkEaL02NPTkDojPtheGB7HZSFY4wzX+DxyrvhXz0KSCVksmCknCuA==", "dev": true, - "requires": { + "dependencies": { "detect-newline": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-each": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.0.1.tgz", - "integrity": "sha512-OTgJlwXCAR8NIWaXFL5DBbeS4QIYPuNASkzSwMCJO+ywo9BEa6TqkaSWsfR7VdbMLdgYJqSfQcIyjJCNwl5n4Q==", + "node_modules/jest-each": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-28.1.3.tgz", + "integrity": "sha512-arT1z4sg2yABU5uogObVPvSlSMQlDA48owx07BDPAiasW0yYpYHYOo4HHLz9q0BVzDVU4hILFjzJw0So9aCL/g==", "dev": true, - "requires": { - "@jest/types": "^26.0.1", + "dependencies": { + "@jest/types": "^28.1.3", "chalk": "^4.0.0", - "jest-get-type": "^26.0.0", - "jest-util": "^26.0.1", - "pretty-format": "^26.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "jest-get-type": "^28.0.2", + "jest-util": "^28.1.3", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-environment-jsdom": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.0.1.tgz", - "integrity": "sha512-u88NJa3aptz2Xix2pFhihRBAatwZHWwSiRLBDBQE1cdJvDjPvv7ZGA0NQBxWwDDn7D0g1uHqxM8aGgfA9Bx49g==", + "node_modules/jest-environment-node": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-28.1.3.tgz", + "integrity": "sha512-ugP6XOhEpjAEhGYvp5Xj989ns5cB1K6ZdjBYuS30umT4CQEETaxSiPcZ/E1kFktX4GkrcM4qu07IIlDYX1gp+A==", "dev": true, - "requires": { - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/types": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-util": "^26.0.1", - "jsdom": "^16.2.2" + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", + "jest-mock": "^28.1.3", + "jest-util": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-environment-node": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.0.1.tgz", - "integrity": "sha512-4FRBWcSn5yVo0KtNav7+5NH5Z/tEgDLp7VRQVS5tCouWORxj+nI+1tOLutM07Zb2Qi7ja+HEDoOUkjBSWZg/IQ==", + "node_modules/jest-get-type": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-28.0.2.tgz", + "integrity": "sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA==", "dev": true, - "requires": { - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/types": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-util": "^26.0.1" + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-get-type": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz", - "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==", - "dev": true - }, - "jest-haste-map": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.0.1.tgz", - "integrity": "sha512-J9kBl/EdjmDsvyv7CiyKY5+DsTvVOScenprz/fGqfLg/pm1gdjbwwQ98nW0t+OIt+f+5nAVaElvn/6wP5KO7KA==", + "node_modules/jest-haste-map": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-28.1.3.tgz", + "integrity": "sha512-3S+RQWDXccXDKSWnkHa/dPwt+2qwA8CJzR61w3FoYCvoo3Pn8tvGcysmMF0Bj0EX5RYvAI2EIvC57OmotfdtKA==", "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "@types/graceful-fs": "^4.1.2", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", - "graceful-fs": "^4.2.4", - "jest-serializer": "^26.0.0", - "jest-util": "^26.0.1", - "jest-worker": "^26.0.0", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7", - "which": "^2.0.2" - }, - "dependencies": { - "anymatch": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.1.tgz", - "integrity": "sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "jest-jasmine2": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.0.1.tgz", - "integrity": "sha512-ILaRyiWxiXOJ+RWTKupzQWwnPaeXPIoLS5uW41h18varJzd9/7I0QJGqg69fhTT1ev9JpSSo9QtalriUN0oqOg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.0.1", - "@jest/source-map": "^26.0.0", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^26.0.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^26.0.1", - "jest-matcher-utils": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "pretty-format": "^26.0.1", - "throat": "^5.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "graceful-fs": "^4.2.9", + "jest-regex-util": "^28.0.2", + "jest-util": "^28.1.3", + "jest-worker": "^28.1.3", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "jest-leak-detector": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.0.1.tgz", - "integrity": "sha512-93FR8tJhaYIWrWsbmVN1pQ9ZNlbgRpfvrnw5LmgLRX0ckOJ8ut/I35CL7awi2ecq6Ca4lL59bEK9hr7nqoHWPA==", + "node_modules/jest-leak-detector": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-28.1.3.tgz", + "integrity": "sha512-WFVJhnQsiKtDEo5lG2mM0v40QWnBM+zMdHHyJs8AWZ7J0QZJS59MsyKeJHWhpBZBH32S48FOVvGyOFT1h0DlqA==", "dev": true, - "requires": { - "jest-get-type": "^26.0.0", - "pretty-format": "^26.0.1" + "dependencies": { + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-matcher-utils": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.0.1.tgz", - "integrity": "sha512-PUMlsLth0Azen8Q2WFTwnSkGh2JZ8FYuwijC8NR47vXKpsrKmA1wWvgcj1CquuVfcYiDEdj985u5Wmg7COEARw==", + "node_modules/jest-matcher-utils": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz", + "integrity": "sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.0.0", - "jest-diff": "^26.0.1", - "jest-get-type": "^26.0.0", - "pretty-format": "^26.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-message-util": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.0.1.tgz", - "integrity": "sha512-CbK8uQREZ8umUfo8+zgIfEt+W7HAHjQCoRaNs4WxKGhAYBGwEyvxuK81FXa7VeB9pwDEXeeKOB2qcsNVCAvB7Q==", + "node_modules/jest-message-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-28.1.3.tgz", + "integrity": "sha512-PFdn9Iewbt575zKPf1286Ht9EPoJmYT7P0kY+RibeYZ2XtOr53pDLEFoTWXbd1h4JiGiWpTBC84fc8xMXQMb7g==", "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.0.1", - "@types/stack-utils": "^1.0.1", + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^28.1.3", + "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^28.1.3", "slash": "^3.0.0", - "stack-utils": "^2.0.2" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "micromatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.2.tgz", - "integrity": "sha512-y7FpHSbMUMoyPbYUSzO6PaZ6FyRnQOpHuKwbo1G+Knck95XVU4QAiKdGEnj5wwoS7PlOgthX/09u5iFJ+aYf5Q==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.0.5" - } - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - }, - "to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "requires": { - "is-number": "^7.0.0" - } - } + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-mock": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.0.1.tgz", - "integrity": "sha512-MpYTBqycuPYSY6xKJognV7Ja46/TeRbAZept987Zp+tuJvMN0YBWyyhG9mXyYQaU3SBI0TUlSaO5L3p49agw7Q==", + "node_modules/jest-mock": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-28.1.3.tgz", + "integrity": "sha512-o3J2jr6dMMWYVH4Lh/NKmDXdosrsJgi4AviS8oXLujcjpCMBb1FMsblDnOXKZKfSiHLxYub1eS0IHuRXsio9eA==", "dev": true, - "requires": { - "@jest/types": "^26.0.1" + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-pnp-resolver": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.1.tgz", - "integrity": "sha512-pgFw2tm54fzgYvc/OHrnysABEObZCUNFnhjoRjaVOCN8NYc032/gVjPaHD4Aq6ApkSieWtfKAFQtmDKAmhupnQ==", - "dev": true + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "dev": true, + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true + "node_modules/jest-regex-util": { + "version": "28.0.2", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-28.0.2.tgz", + "integrity": "sha512-4s0IgyNIy0y9FK+cjoVYoxamT7Zeo7MhzqRGx7YDYmaQn1wucY9rotiGkBzzcMXTtjrCAP/f7f+E0F7+fxPNdw==", + "dev": true, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } }, - "jest-resolve": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.0.1.tgz", - "integrity": "sha512-6jWxk0IKZkPIVTvq6s72RH735P8f9eCJW3IM5CX/SJFeKq1p2cZx0U49wf/SdMlhaB/anann5J2nCJj6HrbezQ==", + "node_modules/jest-resolve": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-28.1.3.tgz", + "integrity": "sha512-Z1W3tTjE6QaNI90qo/BJpfnvpxtaFTFw5CDgwpyE/Kz8U/06N1Hjf4ia9quUhCh39qIGWF1ZuxFiBiJQwSEYKQ==", "dev": true, - "requires": { - "@jest/types": "^26.0.1", + "dependencies": { "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.1", - "jest-util": "^26.0.1", - "read-pkg-up": "^7.0.1", - "resolve": "^1.17.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^28.1.3", + "jest-validate": "^28.1.3", + "resolve": "^1.20.0", + "resolve.exports": "^1.1.0", "slash": "^3.0.0" }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-resolve-dependencies": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.0.1.tgz", - "integrity": "sha512-9d5/RS/ft0vB/qy7jct/qAhzJsr6fRQJyGAFigK3XD4hf9kIbEH5gks4t4Z7kyMRhowU6HWm/o8ILqhaHdSqLw==", + "node_modules/jest-resolve-dependencies": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-28.1.3.tgz", + "integrity": "sha512-qa0QO2Q0XzQoNPouMbCc7Bvtsem8eQgVPNkwn9LnS+R2n8DaVDPL/U1gngC0LTl1RYXJU0uJa2BMC2DbTfFrHA==", "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.0.1" + "dependencies": { + "jest-regex-util": "^28.0.2", + "jest-snapshot": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-runner": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.0.1.tgz", - "integrity": "sha512-CApm0g81b49Znm4cZekYQK67zY7kkB4umOlI2Dx5CwKAzdgw75EN+ozBHRvxBzwo1ZLYZ07TFxkaPm+1t4d8jA==", + "node_modules/jest-runner": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-28.1.3.tgz", + "integrity": "sha512-GkMw4D/0USd62OVO0oEgjn23TM+YJa2U2Wu5zz9xsQB1MxWKDOlrnykPxnMsN0tnJllfLPinHTka61u0QhaxBA==", "dev": true, - "requires": { - "@jest/console": "^26.0.1", - "@jest/environment": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", + "dependencies": { + "@jest/console": "^28.1.3", + "@jest/environment": "^28.1.3", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-config": "^26.0.1", - "jest-docblock": "^26.0.0", - "jest-haste-map": "^26.0.1", - "jest-jasmine2": "^26.0.1", - "jest-leak-detector": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-resolve": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-util": "^26.0.1", - "jest-worker": "^26.0.0", - "source-map-support": "^0.5.6", - "throat": "^5.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-runtime": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.0.1.tgz", - "integrity": "sha512-Ci2QhYFmANg5qaXWf78T2Pfo6GtmIBn2rRaLnklRyEucmPccmCKvS9JPljcmtVamsdMmkyNkVFb9pBTD6si9Lw==", - "dev": true, - "requires": { - "@jest/console": "^26.0.1", - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/globals": "^26.0.1", - "@jest/source-map": "^26.0.0", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "@types/yargs": "^15.0.0", + "emittery": "^0.10.2", + "graceful-fs": "^4.2.9", + "jest-docblock": "^28.1.1", + "jest-environment-node": "^28.1.3", + "jest-haste-map": "^28.1.3", + "jest-leak-detector": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-resolve": "^28.1.3", + "jest-runtime": "^28.1.3", + "jest-util": "^28.1.3", + "jest-watcher": "^28.1.3", + "jest-worker": "^28.1.3", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-runtime": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-28.1.3.tgz", + "integrity": "sha512-NU+881ScBQQLc1JHG5eJGU7Ui3kLKrmwCPPtYsJtBykixrM2OhVQlpMmFWJjMyDfdkGgBMNjXCGB/ebzsgNGQw==", + "dev": true, + "dependencies": { + "@jest/environment": "^28.1.3", + "@jest/fake-timers": "^28.1.3", + "@jest/globals": "^28.1.3", + "@jest/source-map": "^28.1.2", + "@jest/test-result": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", + "execa": "^5.0.0", "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-config": "^26.0.1", - "jest-haste-map": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-mock": "^28.1.3", + "jest-regex-util": "^28.0.2", + "jest-resolve": "^28.1.3", + "jest-snapshot": "^28.1.3", + "jest-util": "^28.1.3", "slash": "^3.0.0", - "strip-bom": "^4.0.0", - "yargs": "^15.3.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "jest-serializer": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.0.0.tgz", - "integrity": "sha512-sQGXLdEGWFAE4wIJ2ZaIDb+ikETlUirEOBsLXdoBbeLhTHkZUJwgk3+M8eyFizhM6le43PDCCKPA1hzkSDo4cQ==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.4" + "strip-bom": "^4.0.0" }, - "dependencies": { - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - } + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-snapshot": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.0.1.tgz", - "integrity": "sha512-jxd+cF7+LL+a80qh6TAnTLUZHyQoWwEHSUFJjkw35u3Gx+BZUNuXhYvDqHXr62UQPnWo2P6fvQlLjsU93UKyxA==", + "node_modules/jest-snapshot": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-28.1.3.tgz", + "integrity": "sha512-4lzMgtiNlc3DU/8lZfmqxN3AYD6GGLbl+72rdBpXvcV+whX7mDrREzkPdp2RnmfIiWBg1YbuFSkXduF2JcafJg==", "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.0.1", - "@types/prettier": "^2.0.0", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/traverse": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^28.1.3", + "@jest/transform": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/babel__traverse": "^7.0.6", + "@types/prettier": "^2.1.5", + "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", - "expect": "^26.0.1", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.0.1", - "jest-get-type": "^26.0.0", - "jest-matcher-utils": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-resolve": "^26.0.1", - "make-dir": "^3.0.0", + "expect": "^28.1.3", + "graceful-fs": "^4.2.9", + "jest-diff": "^28.1.3", + "jest-get-type": "^28.0.2", + "jest-haste-map": "^28.1.3", + "jest-matcher-utils": "^28.1.3", + "jest-message-util": "^28.1.3", + "jest-util": "^28.1.3", "natural-compare": "^1.4.0", - "pretty-format": "^26.0.1", - "semver": "^7.3.2" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "pretty-format": "^28.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-util": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.0.1.tgz", - "integrity": "sha512-byQ3n7ad1BO/WyFkYvlWQHTsomB6GIewBh8tlGtusiylAlaxQ1UpS0XYH0ngOyhZuHVLN79Qvl6/pMiDMSSG1g==", + "node_modules/jest-util": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-28.1.3.tgz", + "integrity": "sha512-XdqfpHwpcSRko/C35uLYFM2emRAltIIKZiJ9eAmhjsj0CqZMa0p1ib0R5fWIqGhn1a103DebTbpqIaP1qCQ6tQ==", "dev": true, - "requires": { - "@jest/types": "^26.0.1", + "dependencies": { + "@jest/types": "^28.1.3", + "@types/node": "*", "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-validate": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.0.1.tgz", - "integrity": "sha512-u0xRc+rbmov/VqXnX3DlkxD74rHI/CfS5xaV2VpeaVySjbb1JioNVOyly5b56q2l9ZKe7bVG5qWmjfctkQb0bA==", + "node_modules/jest-validate": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-28.1.3.tgz", + "integrity": "sha512-SZbOGBWEsaTxBGCOpsRWlXlvNkvTkY0XxRfh7zYmvd8uL5Qzyg0CHAXiXKROflh801quA6+/DsT4ODDthOC/OA==", "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "camelcase": "^6.0.0", + "dependencies": { + "@jest/types": "^28.1.3", + "camelcase": "^6.2.0", "chalk": "^4.0.0", - "jest-get-type": "^26.0.0", + "jest-get-type": "^28.0.2", "leven": "^3.1.0", - "pretty-format": "^26.0.1" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "camelcase": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.0.0.tgz", - "integrity": "sha512-8KMDF1Vz2gzOq54ONPJS65IvTUaB1cHJ2DMM7MbPmLZljDH1qpzzLsWdiN9pHh6qvkRVDTi/07+eNGch/oLU4w==", - "dev": true - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "pretty-format": "^28.1.3" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "jest-watcher": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.0.1.tgz", - "integrity": "sha512-pdZPydsS8475f89kGswaNsN3rhP6lnC3/QDCppP7bg1L9JQz7oU9Mb/5xPETk1RHDCWeqmVC47M4K5RR7ejxFw==", + "node_modules/jest-watcher": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-28.1.3.tgz", + "integrity": "sha512-t4qcqj9hze+jviFPUN3YAtAEeFnr/azITXQEMARf5cMwKY2SMBRnCQTXLixTl20OR6mLh9KLMrgVJgJISym+1g==", "dev": true, - "requires": { - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", + "dependencies": { + "@jest/test-result": "^28.1.3", + "@jest/types": "^28.1.3", + "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", - "jest-util": "^26.0.1", + "emittery": "^0.10.2", + "jest-util": "^28.1.3", "string-length": "^4.0.1" }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.0.0.tgz", - "integrity": "sha512-N9oWFcegS0sFr9oh1oz2d7Npos6vNoWW9HvtCg5N1KRFpUhaAhvTv5Y58g880fZaEYSNm3qDz8SU1UrGvp+n7A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "jest-worker": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.0.0.tgz", - "integrity": "sha512-pPaYa2+JnwmiZjK9x7p9BoZht+47ecFCDFA/CJxspHzeDvQcfVBLWzCiWyo+EGrSiQMWZtCFo9iSvMZnAAo8vw==", + "node_modules/jest-worker": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-28.1.3.tgz", + "integrity": "sha512-CqRA220YV/6jCo8VWvAt1KKx6eek1VIHMPeLEbpcfSfkEeWyBNppynM/o6q+Wmw+sOhos2ml34wZbSX3G13//g==", "dev": true, - "requires": { + "dependencies": { + "@types/node": "*", "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" + "supports-color": "^8.0.0" }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "joi": { - "version": "17.10.1", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.10.1.tgz", - "integrity": "sha512-vIiDxQKmRidUVp8KngT8MZSOcmRVm2zV7jbMjNYWuHcJWI0bUck3nRTGQjhpPlQenIQIBC5Vp9AhcnHbWQqafw==", - "requires": { - "@hapi/hoek": "^9.0.0", - "@hapi/topo": "^5.0.0", - "@sideway/address": "^4.1.3", + "node_modules/joi": { + "version": "17.12.2", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.12.2.tgz", + "integrity": "sha512-RonXAIzCiHLc8ss3Ibuz45u28GOsWE1UpfDXLbN/9NKbL4tCJf8TWYVKsoYuuh+sAUt7fsSNpA+r2+TBA6Wjmw==", + "dependencies": { + "@hapi/hoek": "^9.3.0", + "@hapi/topo": "^5.1.0", + "@sideway/address": "^4.1.5", "@sideway/formula": "^3.0.1", "@sideway/pinpoint": "^2.0.0" - }, + } + }, + "node_modules/joi/node_modules/@hapi/hoek": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", + "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" + }, + "node_modules/joi/node_modules/@hapi/topo": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", + "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", "dependencies": { - "@hapi/hoek": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", - "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==" - }, - "@hapi/topo": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@hapi/topo/-/topo-5.1.0.tgz", - "integrity": "sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==", - "requires": { - "@hapi/hoek": "^9.0.0" - } - } + "@hapi/hoek": "^9.0.0" } }, - "js-tokens": { + "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true }, - "js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "jsbn": { + "node_modules/jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" - }, - "jsdom": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.2.2.tgz", - "integrity": "sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "acorn": "^7.1.1", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.2.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.0", - "domexception": "^2.0.1", - "escodegen": "^1.14.1", - "html-encoding-sniffer": "^2.0.1", - "is-potential-custom-element-name": "^1.0.0", - "nwsapi": "^2.2.0", - "parse5": "5.1.1", - "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "saxes": "^5.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^3.0.1", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.0.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0", - "ws": "^7.2.3", - "xml-name-validator": "^3.0.0" - }, - "dependencies": { - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } - } - } - } + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==" }, - "jsesc": { + "node_modules/jsesc": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-buffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", - "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=", - "dev": true + "dev": true, + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=4" + } }, - "json-parse-better-errors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", - "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-ref-parser": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-6.1.0.tgz", - "integrity": "sha512-pXe9H1m6IgIpXmE5JSb8epilNTGsmTb2iPohAXpOdhqGFbQjNeHHsZxU+C8w6T81GZxSPFLeUoqDJmzxx5IGuw==", - "requires": { - "call-me-maybe": "^1.0.1", - "js-yaml": "^3.12.1", - "ono": "^4.0.11" - } + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==" }, - "json-schema-traverse": { + "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" }, - "json-stringify-safe": { + "node_modules/json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" }, - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, - "requires": { - "minimist": "^1.2.5" + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" } }, - "jsonwebtoken": { + "node_modules/jsonwebtoken": { "version": "8.5.1", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz", "integrity": "sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==", - "requires": { + "dependencies": { "jws": "^3.2.2", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", @@ -6248,440 +4052,320 @@ "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^5.6.0" + }, + "engines": { + "node": ">=4", + "npm": ">=1.4.28" } }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { + "node_modules/jsonwebtoken/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/jsprim": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz", + "integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==", + "dependencies": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", - "json-schema": "0.2.3", + "json-schema": "0.4.0", "verror": "1.10.0" + }, + "engines": { + "node": ">=0.6.0" } }, - "jwa": { + "node_modules/jwa": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", - "requires": { + "dependencies": { "buffer-equal-constant-time": "1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, - "jws": { + "node_modules/jws": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", - "requires": { + "dependencies": { "jwa": "^1.4.1", "safe-buffer": "^5.0.1" } }, - "kareem": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/kareem/-/kareem-2.3.1.tgz", - "integrity": "sha512-l3hLhffs9zqoDe8zjmb/mAN4B8VT3L56EUvKNqLFVs9YlFA+zx7ke1DO8STAdDyYNkeSo1nKmjuvQeI12So8Xw==" - }, - "keyv": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", - "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", - "dev": true, - "requires": { - "json-buffer": "3.0.0" - } - }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==", - "dev": true - }, - "kleur": { + "node_modules/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "latest-version": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/latest-version/-/latest-version-5.1.0.tgz", - "integrity": "sha512-weT+r0kTkRQdCdYCNtkMwWXQTMEswKrFBkm4ckQOMVhhqhIMI1UT2hMj+1iigIhgSZm5gTmrRXBNoGUgaTY1xA==", "dev": true, - "requires": { - "package-json": "^6.3.0" + "engines": { + "node": ">=6" } }, - "leven": { + "node_modules/leven": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" + "engines": { + "node": ">=6" } }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, - "locate-path": { + "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, - "requires": { + "dependencies": { "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "lodash": { - "version": "4.17.15", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz", - "integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==" + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, - "lodash.get": { + "node_modules/lodash.get": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk=" + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" }, - "lodash.includes": { + "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==" }, - "lodash.isboolean": { + "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==" }, - "lodash.isequal": { + "node_modules/lodash.isequal": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", - "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA=" + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" }, - "lodash.isinteger": { + "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==" }, - "lodash.isnumber": { + "node_modules/lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==" }, - "lodash.isplainobject": { + "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==" }, - "lodash.isstring": { + "node_modules/lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==" }, - "lodash.once": { + "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, - "long": { + "node_modules/long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==" }, - "lowercase-keys": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", - "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==", - "dev": true - }, - "lru-cache": { - "version": "8.0.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", - "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==" + "node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "engines": { + "node": ">=12" + } }, - "make-dir": { + "node_modules/make-dir": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "requires": { + "dependencies": { "semver": "^6.0.0" }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true, - "requires": { - "tmpl": "1.0.x" + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" } }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, - "requires": { - "object-visit": "^1.0.0" + "dependencies": { + "tmpl": "1.0.5" } }, - "memory-pager": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", - "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", - "optional": true - }, - "merge-stream": { + "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "mime-db": { + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } }, - "mime-types": { - "version": "2.1.24", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz", - "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==", - "requires": { - "mime-db": "1.40.0" - }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dependencies": { - "mime-db": { - "version": "1.40.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz", - "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==" - } + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "mimic-fn": { + "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "requires": { + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "minipass": { + "node_modules/minipass": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==" + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "engines": { + "node": ">=8" + } }, - "minizlib": { + "node_modules/minizlib": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "requires": { + "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" }, - "dependencies": { - "minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "requires": { - "yallist": "^4.0.0" - } - } + "engines": { + "node": ">= 8" } }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } + "node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "mkdirp": { + "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" - }, - "moment": { - "version": "2.24.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz", - "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg==" - }, - "moment-timezone": { - "version": "0.5.31", - "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.31.tgz", - "integrity": "sha512-+GgHNg8xRhMXfEbv81iDtrVeTcWt0kWmTEY1XQK14dICTXnWJnT0dxdlPspwqF3keKMVPXwayEsk1DI0AA/jdA==", - "requires": { - "moment": ">= 2.9.0" - } - }, - "mongodb": { - "version": "3.5.7", - "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.5.7.tgz", - "integrity": "sha512-lMtleRT+vIgY/JhhTn1nyGwnSMmJkJELp+4ZbrjctrnBxuLbj6rmLuJFz8W2xUzUqWmqoyVxJLYuC58ZKpcTYQ==", - "requires": { - "bl": "^2.2.0", - "bson": "^1.1.4", - "denque": "^1.4.1", - "require_optional": "^1.0.1", - "safe-buffer": "^5.1.2", - "saslprep": "^1.0.0" - } - }, - "mongoose": { - "version": "5.9.16", - "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-5.9.16.tgz", - "integrity": "sha512-b4HNndgh+dacoLE/2SBF3iBBofeaKL+aGVZH7jnPRc2RXRCplX4sfH5sgoz03ryCSXJ+RQNIfqKAADt/ZBzPDA==", - "requires": { - "bson": "^1.1.4", - "kareem": "2.3.1", - "mongodb": "3.5.7", - "mongoose-legacy-pluralize": "1.0.2", - "mpath": "0.7.0", - "mquery": "3.2.2", - "ms": "2.1.2", - "regexp-clone": "1.0.0", - "safe-buffer": "5.1.2", - "sift": "7.0.1", - "sliced": "1.0.1" - } - }, - "mongoose-legacy-pluralize": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/mongoose-legacy-pluralize/-/mongoose-legacy-pluralize-1.0.2.tgz", - "integrity": "sha512-Yo/7qQU4/EyIS8YDFSeenIvXxZN+ld7YdV9LqFVQJzTLye8unujAWPZ4NWKfFA+RNjh+wvTWKY9Z3E5XM6ZZiQ==" + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } }, - "mpath": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.7.0.tgz", - "integrity": "sha512-Aiq04hILxhz1L+f7sjGyn7IxYzWm1zLNNXcfhDtx04kZ2Gk7uvFdgZ8ts1cWa/6d0TQmag2yR8zSGZUmp0tFNg==" + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "engines": { + "node": "*" + } }, - "mquery": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/mquery/-/mquery-3.2.2.tgz", - "integrity": "sha512-XB52992COp0KP230I3qloVUbkLUxJIu328HBP2t2EsxSFtf4W1HPSOBWOXf1bqxK4Xbb66lfMJ+Bpfd9/yZE1Q==", - "requires": { - "bluebird": "3.5.1", - "debug": "3.1.0", - "regexp-clone": "^1.0.0", - "safe-buffer": "5.1.2", - "sliced": "1.0.1" - }, - "dependencies": { - "bluebird": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz", - "integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA==" - }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - } + "node_modules/moment-timezone": { + "version": "0.5.45", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.45.tgz", + "integrity": "sha512-HIWmqA86KcmCAhnMAN0wuDOARV/525R2+lOLotuGFzn4HO+FH+/645z2wx0Dt3iDv6/p61SIvKnDstISainhLQ==", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" } }, - "ms": { + "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, - "mysql2": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.6.0.tgz", - "integrity": "sha512-EWUGAhv6SphezurlfI2Fpt0uJEWLmirrtQR7SkbTHFC+4/mJBrPiSzHESHKAWKG7ALVD6xaG/NBjjd1DGJGQQQ==", - "requires": { + "node_modules/mysql2": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.9.2.tgz", + "integrity": "sha512-3Cwg/UuRkAv/wm6RhtPE5L7JlPB877vwSF6gfLAS68H+zhH+u5oa3AieqEd0D0/kC3W7qIhYbH419f7O9i/5nw==", + "dependencies": { "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.6.3", @@ -6691,744 +4375,3041 @@ "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" }, - "dependencies": { - "denque": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", - "integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==" - }, - "iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "requires": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - } - } + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/mysql2/node_modules/lru-cache": { + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-8.0.5.tgz", + "integrity": "sha512-MhWWlVnuab1RG5/zMRRcVGXZLCXrZTgfwMikgzCegsPnG62yDQo5JnqKkrK4jO5iKqDAZGItAqN5CtKBCBWRUA==", + "engines": { + "node": ">=16.14" } }, - "named-placeholders": { + "node_modules/named-placeholders": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.3.tgz", "integrity": "sha512-eLoBxg6wE/rZkJPhU/xRX1WTpkFEwDJEN96oxFrTsqBdbT5ec295Q+CoHrL9IT0DipqKhmGcaZmwOt8OON5x1w==", - "requires": { + "dependencies": { "lru-cache": "^7.14.1" }, - "dependencies": { - "lru-cache": { - "version": "7.18.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", - "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==" - } - } - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "engines": { + "node": ">=12.0.0" } }, - "natural-compare": { + "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, - "neo-async": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.1.tgz", - "integrity": "sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw==" - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==" }, - "node-addon-api": { + "node_modules/node-addon-api": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-5.1.0.tgz", "integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==" }, - "node-fetch": { + "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", - "requires": { + "dependencies": { "whatwg-url": "^5.0.0" }, - "dependencies": { - "tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, - "webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "requires": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" - } + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true } } }, - "node-int64": { + "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "node_modules/node-releases": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.14.tgz", + "integrity": "sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==", "dev": true }, - "node-notifier": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-7.0.1.tgz", - "integrity": "sha512-VkzhierE7DBmQEElhTGJIoiZa1oqRijOtgOlsXg32KrJRXsPy0NXFBqWGW/wTswnJlDCs5viRYaqWguqzsKcmg==", - "dev": true, - "optional": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^2.1.1", - "semver": "^7.2.1", - "shellwords": "^0.1.1", - "uuid": "^7.0.3", - "which": "^2.0.2" - }, - "dependencies": { - "semver": { - "version": "7.3.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.2.tgz", - "integrity": "sha512-OrOb32TeeambH6UrhtShmF7CRDqhL6/5XpPNp2DuRH6+9QLw/orhp72j87v8Qa1ScDkvrrBNpZcDejAirJmfXQ==", - "dev": true, - "optional": true - }, - "uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "dev": true, - "optional": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "optional": true, - "requires": { - "isexe": "^2.0.0" - } - } + "node_modules/nodemailer": { + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.9.11.tgz", + "integrity": "sha512-UiAkgiERuG94kl/3bKfE8o10epvDnl0vokNEtZDPTq9BWzIl6EFT9336SbIT4oaTBD8NmmUTLsQyXHV82eXSWg==", + "engines": { + "node": ">=6.0.0" } }, - "nodemon": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.4.tgz", - "integrity": "sha512-Ltced+hIfTmaS28Zjv1BM552oQ3dbwPqI4+zI0SLgq+wpJhSyqgYude/aZa/3i31VCQWMfXJVxvu86abcam3uQ==", + "node_modules/nodemon": { + "version": "2.0.22", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.22.tgz", + "integrity": "sha512-B8YqaKMmyuCO7BowF1Z1/mkPqLk6cs/l63Ojtd6otKjMx47Dq1utxfRxcavH1I7VSaL8n5BUaoutadnsX3AAVQ==", "dev": true, - "requires": { - "chokidar": "^3.2.2", - "debug": "^3.2.6", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^3.2.7", "ignore-by-default": "^1.0.1", - "minimatch": "^3.0.4", - "pstree.remy": "^1.1.7", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", "semver": "^5.7.1", + "simple-update-notifier": "^1.0.7", "supports-color": "^5.5.0", "touch": "^3.1.0", - "undefsafe": "^2.0.2", - "update-notifier": "^4.0.0" - } - }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "dev": true, - "requires": { - "abbrev": "1" + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=8.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" } }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "node_modules/nodemon/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" + "dependencies": { + "ms": "^2.1.1" } }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "node_modules/nodemon/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" + "engines": { + "node": ">=4" } }, - "normalize-url": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", - "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "node_modules/nodemon/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "dev": true, - "requires": { - "path-key": "^2.0.0" + "bin": { + "semver": "bin/semver" } }, - "npmlog": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", - "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", - "requires": { - "are-we-there-yet": "^2.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^3.0.0", - "set-blocking": "^2.0.0" + "node_modules/nodemon/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" - }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" } }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/npm/-/npm-10.5.0.tgz", + "integrity": "sha512-Ejxwvfh9YnWVU2yA5FzoYLTW52vxHCz+MHrOFg9Cc8IFgF/6f5AGPAvb5WTay5DIUP1NIfN3VBZ0cLlGO0Ys+A==", + "bundleDependencies": [ + "@isaacs/string-locale-compare", + "@npmcli/arborist", + "@npmcli/config", + "@npmcli/fs", + "@npmcli/map-workspaces", + "@npmcli/package-json", + "@npmcli/promise-spawn", + "@npmcli/run-script", + "@sigstore/tuf", + "abbrev", + "archy", + "cacache", + "chalk", + "ci-info", + "cli-columns", + "cli-table3", + "columnify", + "fastest-levenshtein", + "fs-minipass", + "glob", + "graceful-fs", + "hosted-git-info", + "ini", + "init-package-json", + "is-cidr", + "json-parse-even-better-errors", + "libnpmaccess", + "libnpmdiff", + "libnpmexec", + "libnpmfund", + "libnpmhook", + "libnpmorg", + "libnpmpack", + "libnpmpublish", + "libnpmsearch", + "libnpmteam", + "libnpmversion", + "make-fetch-happen", + "minimatch", + "minipass", + "minipass-pipeline", + "ms", + "node-gyp", + "nopt", + "normalize-package-data", + "npm-audit-report", + "npm-install-checks", + "npm-package-arg", + "npm-pick-manifest", + "npm-profile", + "npm-registry-fetch", + "npm-user-validate", + "npmlog", + "p-map", + "pacote", + "parse-conflict-json", + "proc-log", + "qrcode-terminal", + "read", + "semver", + "spdx-expression-parse", + "ssri", + "supports-color", + "tar", + "text-table", + "tiny-relative-date", + "treeverse", + "validate-npm-package-name", + "which", + "write-file-atomic" + ], + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/arborist": "^7.2.1", + "@npmcli/config": "^8.0.2", + "@npmcli/fs": "^3.1.0", + "@npmcli/map-workspaces": "^3.0.4", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.1", + "@npmcli/run-script": "^7.0.4", + "@sigstore/tuf": "^2.3.1", + "abbrev": "^2.0.0", + "archy": "~1.0.0", + "cacache": "^18.0.2", + "chalk": "^5.3.0", + "ci-info": "^4.0.0", + "cli-columns": "^4.0.0", + "cli-table3": "^0.6.3", + "columnify": "^1.6.0", + "fastest-levenshtein": "^1.0.16", + "fs-minipass": "^3.0.3", + "glob": "^10.3.10", + "graceful-fs": "^4.2.11", + "hosted-git-info": "^7.0.1", + "ini": "^4.1.1", + "init-package-json": "^6.0.0", + "is-cidr": "^5.0.3", + "json-parse-even-better-errors": "^3.0.1", + "libnpmaccess": "^8.0.1", + "libnpmdiff": "^6.0.3", + "libnpmexec": "^7.0.4", + "libnpmfund": "^5.0.1", + "libnpmhook": "^10.0.0", + "libnpmorg": "^6.0.1", + "libnpmpack": "^6.0.3", + "libnpmpublish": "^9.0.2", + "libnpmsearch": "^7.0.0", + "libnpmteam": "^6.0.0", + "libnpmversion": "^5.0.1", + "make-fetch-happen": "^13.0.0", + "minimatch": "^9.0.3", + "minipass": "^7.0.4", + "minipass-pipeline": "^1.2.4", + "ms": "^2.1.2", + "node-gyp": "^10.0.1", + "nopt": "^7.2.0", + "normalize-package-data": "^6.0.0", + "npm-audit-report": "^5.0.0", + "npm-install-checks": "^6.3.0", + "npm-package-arg": "^11.0.1", + "npm-pick-manifest": "^9.0.0", + "npm-profile": "^9.0.0", + "npm-registry-fetch": "^16.1.0", + "npm-user-validate": "^2.0.0", + "npmlog": "^7.0.1", + "p-map": "^4.0.0", + "pacote": "^17.0.6", + "parse-conflict-json": "^3.0.1", + "proc-log": "^3.0.0", + "qrcode-terminal": "^0.12.0", + "read": "^2.1.0", + "semver": "^7.6.0", + "spdx-expression-parse": "^3.0.1", + "ssri": "^10.0.5", + "supports-color": "^9.4.0", + "tar": "^6.2.0", + "text-table": "~0.2.0", + "tiny-relative-date": "^1.3.0", + "treeverse": "^3.0.0", + "validate-npm-package-name": "^5.0.0", + "which": "^4.0.0", + "write-file-atomic": "^5.0.1" + }, + "bin": { + "npm": "bin/npm-cli.js", + "npx": "bin/npx-cli.js" + }, + "engines": { + "node": "^18.17.0 || >=20.5.0" + } + }, + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, - "requires": { - "isobject": "^3.0.0" + "dependencies": { + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" + "node_modules/npm/node_modules/@colors/colors": { + "version": "1.5.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.1.90" } }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "requires": { - "wrappy": "1" + "node_modules/npm/node_modules/@isaacs/cliui": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" } }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, - "ono": { - "version": "4.0.11", - "resolved": "https://registry.npmjs.org/ono/-/ono-4.0.11.tgz", - "integrity": "sha512-jQ31cORBFE6td25deYeD80wxKBMj+zBmHTrVxnc6CKhx8gho6ipmWM5zj/oeoqioZ99yqBls9Z/9Nss7J26G2g==", - "requires": { - "format-util": "^1.0.3" + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" + "node_modules/npm/node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, - "p-cancelable": { + "node_modules/npm/node_modules/@isaacs/string-locale-compare": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", - "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==", - "dev": true + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/@npmcli/agent": { + "version": "2.2.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "agent-base": "^7.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.1", + "lru-cache": "^10.0.1", + "socks-proxy-agent": "^8.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/arborist": { + "version": "7.4.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@isaacs/string-locale-compare": "^1.1.0", + "@npmcli/fs": "^3.1.0", + "@npmcli/installed-package-contents": "^2.0.2", + "@npmcli/map-workspaces": "^3.0.2", + "@npmcli/metavuln-calculator": "^7.0.0", + "@npmcli/name-from-folder": "^2.0.0", + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/query": "^3.1.0", + "@npmcli/run-script": "^7.0.2", + "bin-links": "^4.0.1", + "cacache": "^18.0.0", + "common-ancestor-path": "^1.0.1", + "hosted-git-info": "^7.0.1", + "json-parse-even-better-errors": "^3.0.0", + "json-stringify-nice": "^1.1.4", + "minimatch": "^9.0.0", + "nopt": "^7.0.0", + "npm-install-checks": "^6.2.0", + "npm-package-arg": "^11.0.1", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^16.0.0", + "npmlog": "^7.0.1", + "pacote": "^17.0.4", + "parse-conflict-json": "^3.0.0", + "proc-log": "^3.0.0", + "promise-all-reject-late": "^1.0.0", + "promise-call-limit": "^3.0.1", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.7", + "ssri": "^10.0.5", + "treeverse": "^3.0.0", + "walk-up-path": "^3.0.1" + }, + "bin": { + "arborist": "bin/index.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/config": { + "version": "8.2.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/map-workspaces": "^3.0.2", + "ci-info": "^4.0.0", + "ini": "^4.1.0", + "nopt": "^7.0.0", + "proc-log": "^3.0.0", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.5", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "p-each-series": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", - "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", - "dev": true + "node_modules/npm/node_modules/@npmcli/disparity-colors": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ansi-styles": "^4.3.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true + "node_modules/npm/node_modules/@npmcli/disparity-colors/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" + "node_modules/npm/node_modules/@npmcli/fs": { + "version": "3.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" + "node_modules/npm/node_modules/@npmcli/git": { + "version": "5.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/promise-spawn": "^7.0.0", + "lru-cache": "^10.0.1", + "npm-pick-manifest": "^9.0.0", + "proc-log": "^3.0.0", + "promise-inflight": "^1.0.1", + "promise-retry": "^2.0.1", + "semver": "^7.3.5", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true + "node_modules/npm/node_modules/@npmcli/installed-package-contents": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-bundled": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "bin": { + "installed-package-contents": "lib/index.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, - "package-json": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/package-json/-/package-json-6.5.0.tgz", - "integrity": "sha512-k3bdm2n25tkyxcjSKzB5x8kfVxlMdgsbPr0GkZcwHsLpba6cBjqCt1KlcChKEvxHIcTB1FVMuwoijZ26xex5MQ==", - "dev": true, - "requires": { - "got": "^9.6.0", - "registry-auth-token": "^4.0.0", - "registry-url": "^5.0.0", - "semver": "^6.2.0" + "node_modules/npm/node_modules/@npmcli/map-workspaces": { + "version": "3.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/name-from-folder": "^2.0.0", + "glob": "^10.2.2", + "minimatch": "^9.0.0", + "read-package-json-fast": "^3.0.0" }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/metavuln-calculator": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "cacache": "^18.0.0", + "json-parse-even-better-errors": "^3.0.0", + "pacote": "^17.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/name-from-folder": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/@npmcli/node-gyp": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "parse-json": { + "node_modules/npm/node_modules/@npmcli/package-json": { "version": "5.0.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.0.tgz", - "integrity": "sha512-OOY5b7PAEFV0E2Fir1KOkxchnZNCdowAJgQ5NuxjpBKTRP3pQhwkrkxqQjeoKJ+fO7bCpmIZaogI4eZGDMEGOw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-better-errors": "^1.0.1", - "lines-and-columns": "^1.1.6" + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "glob": "^10.2.2", + "hosted-git-info": "^7.0.0", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "proc-log": "^3.0.0", + "semver": "^7.5.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "dev": true + "node_modules/npm/node_modules/@npmcli/promise-spawn": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true + "node_modules/npm/node_modules/@npmcli/query": { + "version": "3.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "postcss-selector-parser": "^6.0.10" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "node_modules/npm/node_modules/@npmcli/run-script": { + "version": "7.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/node-gyp": "^3.0.0", + "@npmcli/package-json": "^5.0.0", + "@npmcli/promise-spawn": "^7.0.0", + "node-gyp": "^10.0.0", + "which": "^4.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + "node_modules/npm/node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "inBundle": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true + "node_modules/npm/node_modules/@sigstore/bundle": { + "version": "2.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true + "node_modules/npm/node_modules/@sigstore/core": { + "version": "1.0.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + "node_modules/npm/node_modules/@sigstore/protobuf-specs": { + "version": "0.3.0", + "inBundle": true, + "license": "Apache-2.0", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, - "picomatch": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", - "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", - "dev": true + "node_modules/npm/node_modules/@sigstore/sign": { + "version": "2.2.3", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.2.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.0", + "make-fetch-happen": "^13.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" + "node_modules/npm/node_modules/@sigstore/tuf": { + "version": "2.3.1", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/protobuf-specs": "^0.3.0", + "tuf-js": "^2.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" + "node_modules/npm/node_modules/@sigstore/verify": { + "version": "1.1.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.2.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true + "node_modules/npm/node_modules/@tufjs/canonical-json": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true + "node_modules/npm/node_modules/@tufjs/models": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/canonical-json": "2.0.0", + "minimatch": "^9.0.3" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "prepend-http": { + "node_modules/npm/node_modules/abbrev": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", - "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", - "dev": true + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, - "pretty-format": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.0.1.tgz", - "integrity": "sha512-SWxz6MbupT3ZSlL0Po4WF/KujhQaVehijR2blyRDCzk9e45EaYMVhMBn49fnRuHxtkSpXTes1GxNpVmH86Bxfw==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" + "node_modules/npm/node_modules/agent-base": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/aggregate-error": { + "version": "3.1.0", + "inBundle": true, + "license": "MIT", "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "process-nextick-args": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", - "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + "node_modules/npm/node_modules/ansi-regex": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "prompts": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz", - "integrity": "sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.4" + "node_modules/npm/node_modules/ansi-styles": { + "version": "6.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + "node_modules/npm/node_modules/aproba": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" }, - "psl": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", - "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==" + "node_modules/npm/node_modules/archy": { + "version": "1.0.0", + "inBundle": true, + "license": "MIT" }, - "pstree.remy": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", - "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", - "dev": true + "node_modules/npm/node_modules/are-we-there-yet": { + "version": "4.0.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, - "pump": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pump/-/pump-2.0.1.tgz", - "integrity": "sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==", - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "pumpify": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/pumpify/-/pumpify-1.5.1.tgz", - "integrity": "sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==", - "requires": { - "duplexify": "^3.6.0", - "inherits": "^2.0.3", - "pump": "^2.0.0" + "node_modules/npm/node_modules/balanced-match": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/bin-links": { + "version": "4.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cmd-shim": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "read-cmd-shim": "^4.0.0", + "write-file-atomic": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "node_modules/npm/node_modules/binary-extensions": { + "version": "2.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "pupa": { + "node_modules/npm/node_modules/brace-expansion": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pupa/-/pupa-2.0.1.tgz", - "integrity": "sha512-hEJH0s8PXLY/cdXh66tNEQGndDrIKNqNC5xmrysZy3i5C3oEoLna7YAOad+7u125+zH1HNXUmGEkrhb3c2VriA==", - "dev": true, - "requires": { - "escape-goat": "^2.0.0" + "inBundle": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "node_modules/npm/node_modules/builtins": { + "version": "5.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "semver": "^7.0.0" + } }, - "rc": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", - "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "dev": true, - "requires": { - "deep-extend": "^0.6.0", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, + "node_modules/npm/node_modules/cacache": { + "version": "18.0.2", + "inBundle": true, + "license": "ISC", "dependencies": { - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - } + "@npmcli/fs": "^3.1.0", + "fs-minipass": "^3.0.0", + "glob": "^10.2.2", + "lru-cache": "^10.0.1", + "minipass": "^7.0.3", + "minipass-collect": "^2.0.1", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "p-map": "^4.0.0", + "ssri": "^10.0.0", + "tar": "^6.1.11", + "unique-filename": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" } }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "node_modules/npm/node_modules/chalk": { + "version": "5.3.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/npm/node_modules/chownr": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ci-info": { + "version": "4.0.0", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/cidr-regex": { + "version": "4.0.3", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "ip-regex": "^5.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/clean-stack": { + "version": "2.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/cli-columns": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/npm/node_modules/cli-table3": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/npm/node_modules/clone": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/npm/node_modules/cmd-shim": { + "version": "6.0.2", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/color-convert": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/npm/node_modules/color-name": { + "version": "1.1.4", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/color-support": { + "version": "1.1.3", + "inBundle": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/npm/node_modules/columnify": { + "version": "1.6.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "strip-ansi": "^6.0.1", + "wcwidth": "^1.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/npm/node_modules/common-ancestor-path": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/console-control-strings": { + "version": "1.1.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/cross-spawn": { + "version": "7.0.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/cssesc": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/debug": { + "version": "4.3.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/npm/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/defaults": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/diff": { + "version": "5.2.0", + "inBundle": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/npm/node_modules/eastasianwidth": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/emoji-regex": { + "version": "8.0.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/encoding": { + "version": "0.1.13", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/npm/node_modules/env-paths": { + "version": "2.2.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/npm/node_modules/err-code": { + "version": "2.0.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/exponential-backoff": { + "version": "3.1.1", + "inBundle": true, + "license": "Apache-2.0" + }, + "node_modules/npm/node_modules/fastest-levenshtein": { + "version": "1.0.16", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4.9.1" + } + }, + "node_modules/npm/node_modules/foreground-child": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/fs-minipass": { + "version": "3.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/function-bind": { + "version": "1.1.2", + "inBundle": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/npm/node_modules/gauge": { + "version": "5.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^4.0.1", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/glob": { + "version": "10.3.10", + "inBundle": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/graceful-fs": { + "version": "4.2.11", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/has-unicode": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/hasown": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/npm/node_modules/hosted-git-info": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/http-cache-semantics": { + "version": "4.1.1", + "inBundle": true, + "license": "BSD-2-Clause" + }, + "node_modules/npm/node_modules/http-proxy-agent": { + "version": "7.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/https-proxy-agent": { + "version": "7.0.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/iconv-lite": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm/node_modules/ignore-walk": { + "version": "6.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minimatch": "^9.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/imurmurhash": { + "version": "0.1.4", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/npm/node_modules/indent-string": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/ini": { + "version": "4.1.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/init-package-json": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^11.0.0", + "promzard": "^1.0.0", + "read": "^2.0.0", + "read-package-json": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/ip-address": { + "version": "9.0.5", + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsbn": "1.1.0", + "sprintf-js": "^1.1.3" + }, + "engines": { + "node": ">= 12" + } + }, + "node_modules/npm/node_modules/ip-address/node_modules/sprintf-js": { + "version": "1.1.3", + "inBundle": true, + "license": "BSD-3-Clause" + }, + "node_modules/npm/node_modules/ip-regex": { + "version": "5.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/is-cidr": { + "version": "5.0.3", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "cidr-regex": "4.0.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/npm/node_modules/is-core-module": { + "version": "2.13.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/npm/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/is-lambda": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/isexe": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/jackspeak": { + "version": "2.3.6", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/npm/node_modules/jsbn": { + "version": "1.1.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/json-parse-even-better-errors": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/json-stringify-nice": { + "version": "1.1.4", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/jsonparse": { + "version": "1.3.1", + "engines": [ + "node >= 0.2.0" + ], + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff": { + "version": "6.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/just-diff-apply": { + "version": "5.5.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/libnpmaccess": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-package-arg": "^11.0.1", + "npm-registry-fetch": "^16.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmdiff": { + "version": "6.0.7", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.2.1", + "@npmcli/disparity-colors": "^3.0.0", + "@npmcli/installed-package-contents": "^2.0.2", + "binary-extensions": "^2.2.0", + "diff": "^5.1.0", + "minimatch": "^9.0.0", + "npm-package-arg": "^11.0.1", + "pacote": "^17.0.4", + "tar": "^6.2.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmexec": { + "version": "7.0.8", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.2.1", + "@npmcli/run-script": "^7.0.2", + "ci-info": "^4.0.0", + "npm-package-arg": "^11.0.1", + "npmlog": "^7.0.1", + "pacote": "^17.0.4", + "proc-log": "^3.0.0", + "read": "^2.0.0", + "read-package-json-fast": "^3.0.2", + "semver": "^7.3.7", + "walk-up-path": "^3.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmfund": { + "version": "5.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.2.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmhook": { + "version": "10.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^16.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmorg": { + "version": "6.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^16.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpack": { + "version": "6.0.7", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/arborist": "^7.2.1", + "@npmcli/run-script": "^7.0.2", + "npm-package-arg": "^11.0.1", + "pacote": "^17.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmpublish": { + "version": "9.0.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ci-info": "^4.0.0", + "normalize-package-data": "^6.0.0", + "npm-package-arg": "^11.0.1", + "npm-registry-fetch": "^16.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.7", + "sigstore": "^2.2.0", + "ssri": "^10.0.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmsearch": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^16.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmteam": { + "version": "6.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "aproba": "^2.0.0", + "npm-registry-fetch": "^16.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/libnpmversion": { + "version": "5.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.3", + "@npmcli/run-script": "^7.0.2", + "json-parse-even-better-errors": "^3.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/lru-cache": { + "version": "10.2.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/npm/node_modules/make-fetch-happen": { + "version": "13.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/agent": "^2.0.0", + "cacache": "^18.0.0", + "http-cache-semantics": "^4.1.1", + "is-lambda": "^1.0.1", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "ssri": "^10.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/minimatch": { + "version": "9.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/minipass": { + "version": "7.0.4", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-collect": { + "version": "2.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/npm/node_modules/minipass-fetch": { + "version": "3.0.4", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^7.0.3", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/npm/node_modules/minipass-flush": { + "version": "1.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minipass-flush/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-json-stream": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "jsonparse": "^1.3.1", + "minipass": "^3.0.0" + } + }, + "node_modules/npm/node_modules/minipass-json-stream/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline": { + "version": "1.2.4", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-pipeline/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized": { + "version": "1.0.3", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minipass-sized/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/minizlib": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/minizlib/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/mkdirp": { + "version": "1.0.4", + "inBundle": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/ms": { + "version": "2.1.3", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/mute-stream": { + "version": "1.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/negotiator": { + "version": "0.6.3", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/npm/node_modules/node-gyp": { + "version": "10.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^10.3.10", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^13.0.0", + "nopt": "^7.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^4.0.0" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/nopt": { + "version": "7.2.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "abbrev": "^2.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/normalize-package-data": { + "version": "6.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "is-core-module": "^2.8.1", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-audit-report": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-bundled": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-install-checks": { + "version": "6.3.0", + "inBundle": true, + "license": "BSD-2-Clause", + "dependencies": { + "semver": "^7.1.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-normalize-package-bin": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-package-arg": { + "version": "11.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "hosted-git-info": "^7.0.0", + "proc-log": "^3.0.0", + "semver": "^7.3.5", + "validate-npm-package-name": "^5.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-packlist": { + "version": "8.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "ignore-walk": "^6.0.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-pick-manifest": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-install-checks": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0", + "npm-package-arg": "^11.0.0", + "semver": "^7.3.5" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-profile": { + "version": "9.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "npm-registry-fetch": "^16.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-registry-fetch": { + "version": "16.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "make-fetch-happen": "^13.0.0", + "minipass": "^7.0.2", + "minipass-fetch": "^3.0.0", + "minipass-json-stream": "^1.0.1", + "minizlib": "^2.1.2", + "npm-package-arg": "^11.0.0", + "proc-log": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npm-user-validate": { + "version": "2.0.0", + "inBundle": true, + "license": "BSD-2-Clause", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/npmlog": { + "version": "7.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^4.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^5.0.0", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/p-map": { + "version": "4.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/pacote": { + "version": "17.0.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "@npmcli/git": "^5.0.0", + "@npmcli/installed-package-contents": "^2.0.1", + "@npmcli/promise-spawn": "^7.0.0", + "@npmcli/run-script": "^7.0.0", + "cacache": "^18.0.0", + "fs-minipass": "^3.0.0", + "minipass": "^7.0.2", + "npm-package-arg": "^11.0.0", + "npm-packlist": "^8.0.0", + "npm-pick-manifest": "^9.0.0", + "npm-registry-fetch": "^16.0.0", + "proc-log": "^3.0.0", + "promise-retry": "^2.0.1", + "read-package-json": "^7.0.0", + "read-package-json-fast": "^3.0.0", + "sigstore": "^2.2.0", + "ssri": "^10.0.0", + "tar": "^6.1.11" + }, + "bin": { + "pacote": "lib/bin.js" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/parse-conflict-json": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "just-diff": "^6.0.0", + "just-diff-apply": "^5.2.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/path-key": { + "version": "3.1.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/path-scurry": { + "version": "1.10.1", + "inBundle": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/postcss-selector-parser": { + "version": "6.0.15", + "inBundle": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm/node_modules/proc-log": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/promise-all-reject-late": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-call-limit": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC", + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/promise-inflight": { + "version": "1.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/promise-retry": { + "version": "2.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/promzard": { + "version": "1.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "read": "^2.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/qrcode-terminal": { + "version": "0.12.0", + "inBundle": true, + "bin": { + "qrcode-terminal": "bin/qrcode-terminal.js" + } + }, + "node_modules/npm/node_modules/read": { + "version": "2.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~1.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/read-cmd-shim": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/read-package-json": { + "version": "7.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "glob": "^10.2.2", + "json-parse-even-better-errors": "^3.0.0", + "normalize-package-data": "^6.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/read-package-json-fast": { + "version": "3.0.2", + "inBundle": true, + "license": "ISC", + "dependencies": { + "json-parse-even-better-errors": "^3.0.0", + "npm-normalize-package-bin": "^3.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/retry": { + "version": "0.12.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm/node_modules/safer-buffer": { + "version": "2.1.2", + "inBundle": true, + "license": "MIT", + "optional": true + }, + "node_modules/npm/node_modules/semver": { + "version": "7.6.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/set-blocking": { + "version": "2.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/shebang-command": { + "version": "2.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/shebang-regex": { + "version": "3.0.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/signal-exit": { + "version": "4.1.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/npm/node_modules/sigstore": { + "version": "2.2.2", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "@sigstore/bundle": "^2.2.0", + "@sigstore/core": "^1.0.0", + "@sigstore/protobuf-specs": "^0.3.0", + "@sigstore/sign": "^2.2.3", + "@sigstore/tuf": "^2.3.1", + "@sigstore/verify": "^1.1.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/smart-buffer": { + "version": "4.2.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks": { + "version": "2.8.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ip-address": "^9.0.5", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 16.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/npm/node_modules/socks-proxy-agent": { + "version": "8.0.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.0.2", + "debug": "^4.3.4", + "socks": "^2.7.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/npm/node_modules/spdx-correct": { + "version": "3.2.0", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-exceptions": { + "version": "2.5.0", + "inBundle": true, + "license": "CC-BY-3.0" + }, + "node_modules/npm/node_modules/spdx-expression-parse": { + "version": "3.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/npm/node_modules/spdx-license-ids": { + "version": "3.0.17", + "inBundle": true, + "license": "CC0-1.0" + }, + "node_modules/npm/node_modules/ssri": { + "version": "10.0.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.3" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/string-width": { + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "inBundle": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/supports-color": { + "version": "9.4.0", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/npm/node_modules/tar": { + "version": "6.2.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass": { + "version": "2.1.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { + "version": "3.3.6", + "inBundle": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/npm/node_modules/text-table": { + "version": "0.2.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/tiny-relative-date": { + "version": "1.3.0", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/treeverse": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/tuf-js": { + "version": "2.2.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "@tufjs/models": "2.0.0", + "debug": "^4.3.4", + "make-fetch-happen": "^13.0.0" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/unique-filename": { + "version": "3.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^4.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/unique-slug": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/util-deprecate": { + "version": "1.0.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/validate-npm-package-license": { + "version": "3.0.4", + "inBundle": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/npm/node_modules/validate-npm-package-name": { + "version": "5.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "builtins": "^5.0.0" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/walk-up-path": { + "version": "3.0.1", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npm/node_modules/wcwidth": { + "version": "1.0.1", + "inBundle": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/npm/node_modules/which": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC", + "dependencies": { + "isexe": "^3.1.1" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/which/node_modules/isexe": { + "version": "3.1.1", + "inBundle": true, + "license": "ISC", + "engines": { + "node": ">=16" + } + }, + "node_modules/npm/node_modules/wide-align": { + "version": "1.1.5", + "inBundle": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/npm/node_modules/wrap-ansi": { + "version": "8.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/ansi-regex": { + "version": "6.0.1", + "inBundle": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "inBundle": true, + "license": "MIT" + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "inBundle": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm/node_modules/wrap-ansi/node_modules/strip-ansi": { + "version": "7.1.0", + "inBundle": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/npm/node_modules/write-file-atomic": { + "version": "5.0.1", + "inBundle": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/npm/node_modules/yallist": { + "version": "4.0.0", + "inBundle": true, + "license": "ISC" + }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, + "node_modules/oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "engines": { + "node": "*" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/openapi-types": { + "version": "12.1.3", + "resolved": "https://registry.npmjs.org/openapi-types/-/openapi-types-12.1.3.tgz", + "integrity": "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==", + "peer": true + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dev": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/p-locate/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dev": true, + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", "dev": true }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" + "engines": { + "node": ">=8.6" }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" + "engines": { + "node": ">= 6" } }, - "readable-stream": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.4.0.tgz", - "integrity": "sha512-0xe001vZBnJEK+uKcj8qOhyAKPzIT+gStxWr3LCB0DwcXR5NZJ3IaC+yGnHCYzB/S7ov3m3EEbZI2zeNvX+hGQ==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, - "requires": { - "picomatch": "^2.2.1" + "dependencies": { + "find-up": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "node_modules/pretty-format": { + "version": "28.1.3", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-28.1.3.tgz", + "integrity": "sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q==", "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "dependencies": { + "@jest/schemas": "^28.1.3", + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || ^16.10.0 || >=17.0.0" } }, - "regexp-clone": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/regexp-clone/-/regexp-clone-1.0.0.tgz", - "integrity": "sha512-TuAasHQNamyyJ2hb97IuBEif4qBHGjPHBS64sZwytpLEqtBQ1gPJTnOaQ6qmpET16cK14kkjbazl6+p0RRv0yw==" - }, - "registry-auth-token": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz", - "integrity": "sha512-9bKS7nTl9+/A1s7tnPeGrUpRcVY+LUh7bfFgzpndALdPfXQBfQV77rQVtqgUV3ti4vc/Ik81Ex8UJDWDQ12zQA==", + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "requires": { - "rc": "^1.2.8" + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "registry-url": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/registry-url/-/registry-url-5.1.0.tgz", - "integrity": "sha512-8acYXXTI0AkQv6RAOjE3vOaIXZkT9wo4LOFbBKYQEEnnMNBpKqdUrI6S4NT0KPIo/WVvJ5tE/X5LF/TQUf0ekw==", + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, - "requires": { - "rc": "^1.2.8" + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" } }, - "remove-trailing-separator": { + "node_modules/proxy-from-env": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==" + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==", "dev": true }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", + "integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/react-is": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", + "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==", "dev": true }, - "request": { + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/request": { "version": "2.88.2", "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "requires": { + "deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142", + "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", "caseless": "~0.12.0", @@ -7450,230 +7431,161 @@ "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" }, - "dependencies": { - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } - } - }, - "request-promise-core": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", - "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", - "dev": true, - "requires": { - "lodash": "^4.17.15" + "engines": { + "node": ">= 6" } }, - "request-promise-native": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", - "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", - "dev": true, - "requires": { - "request-promise-core": "1.1.3", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, - "dependencies": { - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } + "node_modules/request/node_modules/form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" } }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "require_optional": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require_optional/-/require_optional-1.0.1.tgz", - "integrity": "sha512-qhM/y57enGWHAe3v/NcwML6a3/vfESLe/sGM2dII+gEO0BpKRUkWZow/tyloNqJyN6kXSl3RyyM8Ll5D/sJP8g==", - "requires": { - "resolve-from": "^2.0.0", - "semver": "^5.1.0" - }, - "dependencies": { - "resolve-from": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-2.0.0.tgz", - "integrity": "sha1-lICrIOlP+h2egKgEx+oUdhGWa1c=" - } + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" } }, - "resolve": { - "version": "1.17.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", - "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", "dev": true, - "requires": { - "path-parse": "^1.0.6" + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "resolve-cwd": { + "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, - "requires": { + "dependencies": { "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" } }, - "resolve-from": { + "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "responselike": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", - "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", "dev": true, - "requires": { - "lowercase-keys": "^1.0.0" + "engines": { + "node": ">=8" } }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true + "node_modules/resolve.exports": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-1.1.1.tgz", + "integrity": "sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==", + "dev": true, + "engines": { + "node": ">=10" + } }, - "retry-as-promised": { + "node_modules/retry-as-promised": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-3.2.0.tgz", "integrity": "sha512-CybGs60B7oYU/qSQ6kuaFmRd9sTZ6oXSc0toqePvV74Ac6/IFZSI1ReFQmtCN+uvW1Mtqdwpvt/LGOiCBAY2Mg==", - "requires": { + "dependencies": { "any-promise": "^1.3.0" } }, - "rimraf": { + "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "requires": { + "dependencies": { "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", - "dev": true - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "safer-buffer": { + "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - } - }, - "saslprep": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz", - "integrity": "sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==", - "optional": true, - "requires": { - "sparse-bitfield": "^3.0.3" - } - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" + "node_modules/semver": { + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" - }, - "semver-diff": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/semver-diff/-/semver-diff-3.1.1.tgz", - "integrity": "sha512-GX0Ix/CJcHyB8c4ykpHGIAvLyOwOobtM/8d+TQkAd81/bEjgPHrfba41Vpesr7jX/t8Uh+R3EX9eAS5be+jQYg==", - "dev": true, - "requires": { - "semver": "^6.3.0" - }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "seq-queue": { + "node_modules/seq-queue": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/seq-queue/-/seq-queue-0.0.5.tgz", "integrity": "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q==" }, - "sequelize": { - "version": "5.21.11", - "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-5.21.11.tgz", - "integrity": "sha512-ZJw3Hp+NS7iHcTz4fHlKvIBm4I7xYibYRCP4HhSyMB26xgqFYFOXTaeWbHD2UUwAFaksTLw5ntzfpA9kE33SVA==", - "requires": { + "node_modules/sequelize": { + "version": "5.22.5", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-5.22.5.tgz", + "integrity": "sha512-ySIHof18sJbeVG4zjEvsDL490cd9S14/IhkCrZR/g0C/FPlZq1AzEJVeSAo++9/sgJH2eERltAIGqYQNgVqX/A==", + "deprecated": "Please update to v6 or higher! A migration guide can be found here: https://sequelize.org/v6/manual/upgrade-to-v6.html", + "dependencies": { "bluebird": "^3.5.0", "cls-bluebird": "^2.1.0", "debug": "^4.1.1", @@ -7686,351 +7598,158 @@ "semver": "^6.3.0", "sequelize-pool": "^2.3.0", "toposort-class": "^1.0.1", - "uuid": "^3.3.3", - "validator": "^10.11.0", + "uuid": "^8.3.2", + "validator": "^13.7.0", "wkx": "^0.4.8" }, - "dependencies": { - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "requires": { - "ms": "^2.1.1" - } - }, - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" - } + "engines": { + "node": ">=6.0.0" } }, - "sequelize-mock": { + "node_modules/sequelize-mock": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/sequelize-mock/-/sequelize-mock-0.10.2.tgz", "integrity": "sha512-Vu95by/Bmhcx9PHKlZe+w7/7zw1AycV/SeevxQ5lDokAb50H7Kaf2SkjK5mqKxHWX6y/ICZ8JEfyMOg0nd1M2w==", "dev": true, - "requires": { + "dependencies": { "bluebird": "^3.4.6", "inflection": "^1.10.0", "lodash": "^4.16.4" } }, - "sequelize-pool": { + "node_modules/sequelize-pool": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-2.3.0.tgz", - "integrity": "sha512-Ibz08vnXvkZ8LJTiUOxRcj1Ckdn7qafNZ2t59jYHMX1VIebTAOYefWdRYFt6z6+hy52WGthAHAoLc9hvk3onqA==" + "integrity": "sha512-Ibz08vnXvkZ8LJTiUOxRcj1Ckdn7qafNZ2t59jYHMX1VIebTAOYefWdRYFt6z6+hy52WGthAHAoLc9hvk3onqA==", + "engines": { + "node": ">= 6.0.0" + } }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" + "node_modules/sequelize/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } + "node_modules/sequelize/node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" } }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { - "shebang-regex": "^1.0.0" + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "optional": true + "engines": { + "node": ">=8" + } }, - "shimmer": { + "node_modules/shimmer": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/shimmer/-/shimmer-1.2.1.tgz", "integrity": "sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==" }, - "sift": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/sift/-/sift-7.0.1.tgz", - "integrity": "sha512-oqD7PMJ+uO6jV9EQCl0LrRw1OwsiPsiFQR5AR30heR+4Dl7jBBbDLnNvWiak20tzZlSE1H7RB30SX/1j/YYT7g==" + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" + "node_modules/simple-update-notifier": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", + "integrity": "sha512-VpsrsJSUcJEseSbMHkrsrAVSdvVS5I96Qo1QAQ4FxQ9wXFcB+pjj7FB7/us9+GcgfW4ziHtYMc1J0PLczb55mg==", + "dev": true, + "dependencies": { + "semver": "~7.0.0" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.0.0.tgz", + "integrity": "sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } }, - "sisteransi": { + "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, - "slash": { + "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "sliced": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz", - "integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E=" - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "engines": { + "node": ">=8" } }, - "source-map": { + "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" - }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "dev": true, - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" } }, - "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, - "requires": { + "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "sparse-bitfield": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", - "integrity": "sha1-/0rm5oZWBWuks+eSqzM004JzyhE=", - "optional": true, - "requires": { - "memory-pager": "^1.0.2" - } - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.0" - } - }, - "sprintf-js": { + "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true }, - "sqlstring": { + "node_modules/sqlstring": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/sqlstring/-/sqlstring-2.3.3.tgz", - "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==" + "integrity": "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==", + "engines": { + "node": ">= 0.6" + } }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "requires": { + "node_modules/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "dependencies": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", "bcrypt-pbkdf": "^1.0.0", @@ -8040,906 +7759,629 @@ "jsbn": "~0.1.0", "safer-buffer": "^2.0.2", "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" } }, - "stack-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.2.tgz", - "integrity": "sha512-0H7QK2ECz3fyZMzQ8rH0j2ykpfbnd20BFtfg/SqVC2+sCTtcw0aDTGB7dk+de4U4uUeuz6nOtJcrkFFLG1B0Rg==", + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, - "requires": { + "dependencies": { "escape-string-regexp": "^2.0.0" }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } + "engines": { + "node": ">=10" } }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" } }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true - }, - "stream-shift": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.1.tgz", - "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==" - }, - "string-length": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", - "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, - "requires": { + "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" } }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "requires": { + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==" - } - } - }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "requires": { - "safe-buffer": "~5.1.0" + "engines": { + "node": ">=8" } }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "requires": { - "ansi-regex": "^5.0.0" - }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==" - } + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-bom": { + "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "strip-final-newline": { + "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "strip-json-comments": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", - "dev": true + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "requires": { - "has-flag": "^3.0.0" + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "supports-hyperlinks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", - "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", + "node_modules/supports-hyperlinks": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.3.0.tgz", + "integrity": "sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==", "dev": true, - "requires": { + "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" }, - "dependencies": { - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "engines": { + "node": ">=8" } }, - "swagger-methods": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/swagger-methods/-/swagger-methods-1.0.8.tgz", - "integrity": "sha512-G6baCwuHA+C5jf4FNOrosE4XlmGsdjbOjdBK4yuiDDj/ro9uR4Srj3OR84oQMT8F3qKp00tYNv0YN730oTHPZA==" - }, - "swagger-parser": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-4.0.2.tgz", - "integrity": "sha512-hKslog8LhsXICJ1sMLsA8b8hQ3oUEX0457aLCFJc4zz6m8drmnCtyjbVqS5HycaKFOKVolJc2wFoe8KDPWfp4g==", - "requires": { - "call-me-maybe": "^1.0.1", - "debug": "^3.1.0", - "json-schema-ref-parser": "^4.1.0", - "ono": "^4.0.3", - "swagger-methods": "^1.0.4", - "swagger-schema-official": "2.0.0-bab6bed", - "z-schema": "^3.19.0" - }, - "dependencies": { - "json-schema-ref-parser": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/json-schema-ref-parser/-/json-schema-ref-parser-4.1.1.tgz", - "integrity": "sha512-lByoCHZ6H2zgb6NtsXIqtzQ+6Ji7iVqnrhWxsXLhF+gXmgu6E8+ErpDxCMR439MUG1nfMjWI2HAoM8l0XgSNhw==", - "requires": { - "call-me-maybe": "^1.0.1", - "debug": "^3.1.0", - "js-yaml": "^3.10.0", - "ono": "^4.0.3" - } - } + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "swagger-schema-official": { - "version": "2.0.0-bab6bed", - "resolved": "https://registry.npmjs.org/swagger-schema-official/-/swagger-schema-official-2.0.0-bab6bed.tgz", - "integrity": "sha1-cAcEaNbSl3ylI3suUZyn0Gouo/0=" - }, - "swagger-ui-dist": { - "version": "3.25.5", - "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.25.5.tgz", - "integrity": "sha512-JZ6dVQS2nPgVYW0+JIIxm84vvFbR/ole6xYJG2DcSdejDLt8ARqIhbZ4InL7RVsLdXpPirUMb7hf2z4Fzqesyw==" + "node_modules/swagger-parser": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/swagger-parser/-/swagger-parser-10.0.3.tgz", + "integrity": "sha512-nF7oMeL4KypldrQhac8RyHerJeGPD1p2xDh900GPvc+Nk7nWP6jX2FcC7WmkinMoAmoO774+AFXcWsW8gMWEIg==", + "dependencies": { + "@apidevtools/swagger-parser": "10.0.3" + }, + "engines": { + "node": ">=10" + } }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true + "node_modules/swagger-ui-dist": { + "version": "5.11.10", + "resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.11.10.tgz", + "integrity": "sha512-wAHf32iFqJCBkdQRBYB1pR8kJuliJbgCXcdgkU7GkDvrOfD2gVmyEwdTi9rERCur/OrufifnH5UecOzlQ07CYg==" }, - "tar": { + "node_modules/tar": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.0.tgz", "integrity": "sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==", - "requires": { + "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "term-size": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.0.tgz", - "integrity": "sha512-a6sumDlzyHVJWb8+YofY4TW112G6p2FCPEAFk+59gIYHv3XHRhm9ltVQ9kli4hNWeQBwSpe8cRN25x0ROunMOw==", - "dev": true - }, - "terminal-link": { + "node_modules/terminal-link": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", "dev": true, - "requires": { + "dependencies": { "ansi-escapes": "^4.2.1", "supports-hyperlinks": "^2.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "test-exclude": { + "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, - "requires": { + "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" } }, - "throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", - "dev": true - }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, - "to-fast-properties": { + "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true - }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "engines": { + "node": ">=4" } }, - "to-readable-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", - "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==", - "dev": true - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "to-regex-range": { + "node_modules/topo": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "resolved": "https://registry.npmjs.org/topo/-/topo-2.1.1.tgz", + "integrity": "sha512-ZPrPP5nwzZy1fw9abHQH2k+YarTgp9UMAztcB3MmlcZSif63Eg+az05p6wTDaZmnqpS3Mk7K+2W60iHarlz8Ug==", + "deprecated": "This module has moved and is now available at @hapi/topo. Please update your dependencies as this version is no longer maintained and may contain bugs and security issues.", + "dependencies": { + "hoek": "4.x.x" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/topo/node_modules/hoek": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.3.1.tgz", + "integrity": "sha512-v7E+yIjcHECn973i0xHm4kJkEpv3C8sbYS4344WXbzYqRyiDD7rjnnKo4hsJkejQBAFdRMUGNHySeSPKSH9Rqw==", + "deprecated": "This module has moved and is now available at @hapi/hoek. Please update your dependencies as this version is no longer maintained an may contain bugs and security issues.", + "engines": { + "node": ">=6.0.0" } }, - "toposort-class": { + "node_modules/toposort-class": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", - "integrity": "sha1-f/0feMi+KMO6Rc1OGj9e4ZO9mYg=" + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" }, - "touch": { + "node_modules/touch": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz", "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==", "dev": true, - "requires": { + "dependencies": { "nopt": "~1.0.10" + }, + "bin": { + "nodetouch": "bin/nodetouch.js" } }, - "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "node_modules/touch/node_modules/nopt": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", + "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==", "dev": true, - "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "*" } }, - "tr46": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", - "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", - "dev": true, - "requires": { + "node_modules/tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dependencies": { + "psl": "^1.1.28", "punycode": "^2.1.1" + }, + "engines": { + "node": ">=0.8" } }, - "tunnel-agent": { + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dependencies": { "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" } }, - "tweetnacl": { + "node_modules/tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2" - } + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==" }, - "type-detect": { + "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, - "requires": { - "is-typedarray": "^1.0.0" + "engines": { + "node": ">=4" } }, - "uglify-js": { - "version": "3.6.3", - "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.3.tgz", - "integrity": "sha512-KfQUgOqTkLp2aZxrMbCuKCDGW9slFYu2A23A36Gs7sGzTLcRBDORdOi5E21KWHFIfkY8kzgi/Pr1cXCh0yIp5g==", - "optional": true, - "requires": { - "commander": "~2.20.3", - "source-map": "~0.6.1" + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "undefsafe": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz", - "integrity": "sha512-nrXZwwXrD/T/JXeygJqdCO6NZZ1L66HrxM/Z7mIq2oPanoN0F1nLx3lwJMu6AwJY69hdixaFQOuoYsMjE5/C2A==", - "dev": true, - "requires": { - "debug": "^2.2.0" + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } + "engines": { + "node": ">=0.8.0" } }, - "underscore": { + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==", + "dev": true + }, + "node_modules/underscore": { "version": "1.13.6", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.6.tgz", "integrity": "sha512-+A5Sja4HP1M08MaXya7p5LvjuM7K6q/2EaC0+iovj/wOcMsTzMvDFbasi/oSapiwOlt252IqsKqPjCl7huKS0A==" }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - } + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" }, - "unique-string": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/unique-string/-/unique-string-2.0.0.tgz", - "integrity": "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==", + "node_modules/update-browserslist-db": { + "version": "1.0.13", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz", + "integrity": "sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==", "dev": true, - "requires": { - "crypto-random-string": "^2.0.0" - } - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } - } - }, - "update-notifier": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/update-notifier/-/update-notifier-4.1.0.tgz", - "integrity": "sha512-w3doE1qtI0/ZmgeoDoARmI5fjDoT93IfKgEGqm26dGUOh8oNpaSTsGNdYRN/SjOuo10jcJGwkEL3mroKzktkew==", - "dev": true, - "requires": { - "boxen": "^4.2.0", - "chalk": "^3.0.0", - "configstore": "^5.0.1", - "has-yarn": "^2.1.0", - "import-lazy": "^2.1.0", - "is-ci": "^2.0.0", - "is-installed-globally": "^0.3.1", - "is-npm": "^4.0.0", - "is-yarn-global": "^0.3.0", - "latest-version": "^5.0.0", - "pupa": "^2.0.1", - "semver-diff": "^3.1.1", - "xdg-basedir": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" }, - "supports-color": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.1.0.tgz", - "integrity": "sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } + { + "type": "github", + "url": "https://github.com/sponsors/ai" } + ], + "dependencies": { + "escalade": "^3.1.1", + "picocolors": "^1.0.0" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "requires": { + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dependencies": { "punycode": "^2.1.0" } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "url-parse-lax": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", - "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", - "dev": true, - "requires": { - "prepend-http": "^2.0.0" - } - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, - "uuid": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" + "node_modules/uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", + "deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.", + "bin": { + "uuid": "bin/uuid" + } }, - "v8-to-istanbul": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz", - "integrity": "sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ==", + "node_modules/v8-to-istanbul": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", + "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", "dev": true, - "requires": { + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" + "convert-source-map": "^2.0.0" }, - "dependencies": { - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "dev": true - } + "engines": { + "node": ">=10.12.0" } }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } + "node_modules/v8-to-istanbul/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true }, - "validator": { - "version": "10.11.0", - "resolved": "https://registry.npmjs.org/validator/-/validator-10.11.0.tgz", - "integrity": "sha512-X/p3UZerAIsbBfN/IwahhYaBbY68EN/UQBWHtsbXGT5bfrH/p4NQzUCG1kF/rtKaNpnJ7jAu6NGTdSNtyNIXMw==" + "node_modules/validator": { + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.11.0.tgz", + "integrity": "sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==", + "engines": { + "node": ">= 0.10" + } }, - "verror": { + "node_modules/verror": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { + "integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==", + "engines": [ + "node >=0.6.0" + ], + "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" - } - }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "requires": { - "xml-name-validator": "^3.0.0" - } - }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, - "requires": { - "makeerror": "1.0.x" + "dependencies": { + "makeerror": "1.0.12" } }, - "wcwidth": { + "node_modules/wcwidth": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", - "integrity": "sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "optional": true, - "requires": { + "dependencies": { "defaults": "^1.0.3" } }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "requires": { - "iconv-lite": "0.4.24" - } - }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" }, - "whatwg-url": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.1.0.tgz", - "integrity": "sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^2.0.2", - "webidl-conversions": "^5.0.0" - }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" } }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "wide-align": { + "node_modules/wide-align": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "requires": { + "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" } }, - "widest-line": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-3.1.0.tgz", - "integrity": "sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==", - "dev": true, - "requires": { - "string-width": "^4.0.0" - } - }, - "wkx": { + "node_modules/wkx": { "version": "0.4.8", "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.4.8.tgz", "integrity": "sha512-ikPXMM9IR/gy/LwiOSqWlSL3X/J5uk9EO2hHNRXS41eTLXaUFEVw9fn/593jW/tE5tedNg8YjT5HkCa4FqQZyQ==", - "requires": { + "dependencies": { "@types/node": "*" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==" }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, - "dependencies": { - "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", - "dev": true, - "requires": { - "@types/color-name": "^1.1.1", - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, - "requires": { + "dependencies": { "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "ws": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.3.0.tgz", - "integrity": "sha512-iFtXzngZVXPGgpTlP1rBqsUK82p9tKqsWRPg5L56egiljujJT3vGAYnHANvFxBieXrTFavhzhxW52jnaWV+w2w==", - "dev": true - }, - "xdg-basedir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz", - "integrity": "sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==", - "dev": true - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==", - "dev": true + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } }, - "yallist": { + "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, - "yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" - } - }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - } - }, - "z-schema": { - "version": "3.25.1", - "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-3.25.1.tgz", - "integrity": "sha512-7tDlwhrBG+oYFdXNOjILSurpfQyuVgkRe3hB2q8TEssamDHB7BbLWYkYO98nTn0FibfdFroFKDjndbgufAgS/Q==", - "requires": { - "commander": "^2.7.1", - "core-js": "^2.5.7", - "lodash.get": "^4.0.0", - "lodash.isequal": "^4.0.0", - "validator": "^10.0.0" + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/z-schema": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/z-schema/-/z-schema-5.0.5.tgz", + "integrity": "sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==", + "dependencies": { + "lodash.get": "^4.4.2", + "lodash.isequal": "^4.5.0", + "validator": "^13.7.0" + }, + "bin": { + "z-schema": "bin/z-schema" + }, + "engines": { + "node": ">=8.0.0" + }, + "optionalDependencies": { + "commander": "^9.4.1" } } } diff --git a/package.json b/package.json index 5606170..5ab0ccf 100644 --- a/package.json +++ b/package.json @@ -12,31 +12,31 @@ "husky": "node ./node_modules/husky/lib/bin.js" }, "dependencies": { - "@hapi/boom": "^9.1.0", - "@hapi/good": "^9.0.0", - "@hapi/good-console": "^9.0.0", - "@hapi/good-squeeze": "^6.0.0", - "@hapi/hapi": "^20.0.0", - "@hapi/inert": "^6.0.1", + "@hapi/boom": "^10.0.1", + "@hapi/hapi": "^21.3.2", + "@hapi/inert": "^7.1.0", + "@hapi/joi": "^17.1.1", "@hapi/jwt": "^3.2.0", - "@hapi/vision": "^6.0.0", + "@hapi/vision": "^7.0.3", "axios": "^1.5.1", "bcrypt": "^5.1.1", - "blipp": "^4.0.1", + "blipp": "^4.0.2", "dotenv": "^8.6.0", - "hapi-swagger": "^13.0.2", + "hapi-cors": "^1.0.3", + "mysql2": "^3.6.0", + "hapi-swagger": "^17.2.1", + "install": "^0.13.0", "joi": "^17.10.0", "jsonwebtoken": "^8.5.1", - "mongoose": "^5.9.16", - "mysql2": "^3.6.0", + "nodemailer": "^6.9.7", + "npm": "^10.2.5", "request": "^2.88.2", - "require-directory": "^2.1.1", "sequelize": "^5.21.11", "underscore": "^1.13.6" }, "devDependencies": { "husky": "^8.0.3", - "jest": "^26.0.1", + "jest": "^28.0.0", "nodemon": "^2.0.4", "sequelize-mock": "^0.10.2" }, @@ -45,12 +45,10 @@ "npm": ">=6.12" }, "jest": { + "collectCoverage": true, "collectCoverageFrom": [ - "**/*.{js}", - "!**/node_modules/**", - "!**/vendor/**", - "!**/coverage/**" + "lib/application/**/*.js" ], - "testURL": "http://localhost/" + "coverageDirectory": "coverage" } } diff --git a/scripts/insert_test_data.py b/scripts/insert_test_data.py new file mode 100644 index 0000000..4c1f88f --- /dev/null +++ b/scripts/insert_test_data.py @@ -0,0 +1,322 @@ +import random +import requests +import mysql.connector +import faker + + +def getRandomImageURL(): + baseUrl = 'https://ozgrozer.github.io/100k-faces' + folder = random.randint(0, 9) + val = random.randint(0,999) + file = f"00{folder}{'000'[len(str(val)):] + str(val)}" + + return f"{baseUrl}/0/{folder}/{file}.jpg" + + + +def insertUser(cursor,pseudo,password,bio): + + try: + photo = getRandomImageURL() + user_id = None + date = faker.Faker().date_between(start_date='-1y', end_date='today') + data_to_insert = { + "pseudo": pseudo, + "alias": pseudo, + "password": password, + "email": f"{pseudo}.{pseudo}@gmail.com", + "photo": photo, + "bio": bio, + "confirmed": 1, + "id_role": 1, + "createdAt": date, + "updatedAt": date, + "is_private": 1 if random.random() <0.5 else 0 + } + insert_query = """ + INSERT INTO utilisateur + (pseudo, alias, password, email, photo, bio, confirmed, id_role,createdAt,updatedAt,is_private) + VALUES (%(pseudo)s, %(alias)s, %(password)s, %(email)s, %(photo)s, %(bio)s,%(confirmed)s, %(id_role)s,%(createdAt)s, %(updatedAt)s,%(is_private)s) + """ + # Exécuter la requête d'insertion avec les données + cursor.execute(insert_query, data_to_insert) + # Récupérer l'ID de l'utilisateur créé + user_id = cursor.lastrowid + print("insert") + except Exception as err: + print(f"Erreur lors de l'insertion dans la base de données : {err}") + + return user_id + +def insert_follow(cursor,ids,artists): + date = faker.Faker().date_between(start_date='-1y', end_date='today') + for artist in artists: + data_to_insert = { + "createdAt": date, + "updatedAt": date, + "nb_suivis": 0, + "id_artiste": artist, + } + try: + insert_reponse = """ + INSERT INTO `artiste`(`nb_suivis`,`id_artiste`, `createdAt`, `updatedAt` ) + VALUES (%(nb_suivis)s,%(id_artiste)s,%(createdAt)s,%(updatedAt)s) + """ + cursor.execute(insert_reponse, data_to_insert) + except Exception as err: + print(f"Artist already exist") + for artist in artists: + for id_utilisateur in ids: + if(random.random() > 0.85): continue + data_to_insert = { + "createdAt": date, + "updatedAt": date, + "id_utilisateur": id_utilisateur, + "id_artiste": artist, + } + insert_reponse = """ + INSERT INTO `follow`(`id_utilisateur`,`id_artiste`, `createdAt`, `updatedAt` ) + VALUES (%(id_utilisateur)s,%(id_artiste)s,%(createdAt)s,%(updatedAt)s) + """ + cursor.execute(insert_reponse, data_to_insert) +def putReponse(cursor,reponse,ids, review_ids): + reponse_ids = [] + for i in range(len(review_ids)): + rid = None + for y in range(random.randint(0,5)): + date = faker.Faker().date_between(start_date='-1y', end_date='today') + data_to_insert = { + "description": random.choice(reponse), + "createdAt": date, + "updatedAt": date, + "id_utilisateur": random.choice(ids), + "id_review": review_ids[i], + "id_reponse": rid, + } + insert_reponse = """ + INSERT INTO `commentaire`(`description`, `id_review`,`id_utilisateur`,`id_reponse`, `createdAt`, `updatedAt` ) + VALUES (%(description)s,%(id_review)s,%(id_utilisateur)s,%(id_reponse)s,%(createdAt)s,%(updatedAt)s) + """ + cursor.execute(insert_reponse, data_to_insert) + rid = cursor.lastrowid + reponse_ids.append(rid) + return reponse_ids +def putReview(cursor,review, ids,track_ids): + review_ids = [] + for i in range(len(ids)): + if(random.random() < 0.5): + date = faker.Faker().date_between(start_date='-1y', end_date='today') + data_to_insert = { + "description": random.choice(review), + "note": random.randint(0,5), + "createdAt": date, + "updatedAt": date, + "id_utilisateur": ids[i], + "id_oeuvre": random.choice(track_ids), + "id_type_review": 1 + } + insert_review = """ + INSERT INTO `review`(`description`, `note`, `createdAt`, `updatedAt`, `id_utilisateur`,`id_oeuvre`,`id_type_review`) + VALUES (%(description)s,%(note)s,%(createdAt)s,%(updatedAt)s,%(id_utilisateur)s,%(id_oeuvre)s,%(id_type_review)s) + """ + cursor.execute(insert_review, data_to_insert) + # Récupérer l'ID de l'utilisateur créé + review_ids.append(cursor.lastrowid) + return review_ids +def putLikesReview(cursor,ids,review_ids): + for i in range(len(review_ids)): + for y in range(len(ids)): + if(random.random() <0.2): continue + date = faker.Faker().date_between(start_date='-1y', end_date='today') + data_to_insert = { + "createdAt": date, + "updatedAt": date, + "id_utilisateur": ids[y], + "id_review": review_ids[i], + + } + insert_query = """ + INSERT INTO `like_review`(`id_utilisateur`, `id_review`, `createdAt`, `updatedAt`) + VALUES (%(id_utilisateur)s,%(id_review)s,%(createdAt)s,%(updatedAt)s) + """ + cursor.execute(insert_query, data_to_insert) + + +def putLikesReponse(cursor,ids,reponse_ids): + for i in range(len(reponse_ids)): + for y in range(len(ids)): + if(random.random() <0.2): continue + date = faker.Faker().date_between(start_date='-1y', end_date='today') + data_to_insert = { + "createdAt": date, + "updatedAt": date, + "id_utilisateur": ids[y], + "id_com": reponse_ids[i], + + } + insert_query = """ + INSERT INTO `like_commentaire`(`id_utilisateur`, `id_com`, `createdAt`, `updatedAt`) + VALUES (%(id_utilisateur)s,%(id_com)s,%(createdAt)s,%(updatedAt)s) + """ + cursor.execute(insert_query, data_to_insert) + +def insert_relation(cursor,ids): + validation = {} + date = faker.Faker().date_between(start_date='-1y', end_date='today') + for i in range(len(ids)): + for y in range(len(ids)): + if(i == y or random.random() < 0.4): + continue + en_attente = 1 + if(f"key-{y}-{i}" in validation): + en_attente = 0 + validation[f"key-{y}-{i}"][0]['en_attente'] = 0 + data_to_insert = { + "en_attente": en_attente, + "id_utilisateur": ids[i], + "amiIdUtilisateur": ids[y], + "createdAt": date, + "updatedAt": date, + } + insert_query = """ + INSERT INTO amis + (en_attente,id_utilisateur, amiIdUtilisateur, createdAt,updatedAt) + VALUES (%(en_attente)s, %(id_utilisateur)s, %(amiIdUtilisateur)s, %(createdAt)s, %(updatedAt)s) + """ + validation[f"key-{i}-{y}"] = [data_to_insert,insert_query] + # Récupérer l'ID de l'utilisateur créé + + for value in validation.values(): + try: + cursor.execute(value[1], value[0]) + except Exception as err: + print(f"Erreur lors de l'insertion dans la base de données : {err}") + +def obtenir_prenoms_aleatoires(nombre): + # Remplacez l'URL de l'API par celle que vous souhaitez utiliser + api_url = f"https://opendata.paris.fr/api/explore/v2.1/catalog/datasets/liste_des_prenoms/records?select=prenoms&limit=100&offset={random.randint(0,10000)}" + try: + # Effectuer la requête à l'API + reponse = requests.get(api_url) + # Vérifier si la requête a réussi (code de statut 200) + if reponse.status_code == 200: + # Extraire les prénoms de la réponse JSON + prenoms_api = reponse.json() + # Sélectionner un échantillon aléatoire de prénoms + sample = [x["prenoms"] for x in prenoms_api["results"]] + prenoms_aleatoires = random.sample(sample, min(nombre, len(sample))) + return prenoms_aleatoires + else: + print(f"Erreur lors de la requête à l'API. Code de statut : {reponse.status_code}") + except Exception as e: + print(f"Erreur lors de la requête à l'API : {e}") + +n = 50 +biographies = [ + "Explorateur du monde en 150 caractères. Passionné de voyages, de cultures et de moments uniques. #LifeExplorer", + "Artiste en herbe, créant des chefs-d'œuvre avec chaque coup de pinceau. #ArtLife #PassionCréative", + "Architecte de la vie, construisant des rêves un jour à la fois. #DreamBuilder #LifeArchitect", + "Mordu de fitness, repoussant mes limites chaque jour. #FitLife #ChallengeAccepted", + "Globe-trotteur intrépide, capturant des souvenirs partout où je vais. #Wanderlust #AdventureSeeker", + "Amateur de mots, jonglant avec les lettres pour créer des univers magiques. #WordWizard #Storyteller", + "Explorateur culinaire, découvrant de nouveaux plaisirs gustatifs. #FoodieAdventures #TasteExplorer", + "Passionné de tech, naviguant à travers le monde numérique. #TechEnthusiast #DigitalNomad", + "Danseur de la vie, rythmant chaque moment avec grâce et énergie. #DanceLife #JoyfulRhythms", + "Aventurier des étoiles, explorant l'univers avec curiosité. #Stargazer #CosmicExplorer", + "Éco-warrior, défenseur de notre planète. #GreenLiving #EarthDefender", + "Exploratrice du bien-être, cultivant l'harmonie intérieure. #WellnessJourney #MindfulLiving", + "Ingénieur du bonheur, construisant des ponts vers la joie. #HappinessEngineer #PositiveVibesOnly", + "Mélomane moderne, explorant les notes du passé et du présent. #MusicExplorer #HarmonySeeker", + "Maître des codes, tissant des histoires à travers le langage binaire. #CodeArtisan #DigitalStoryteller" +] + +# Afficher la liste complète +reviews_musicales = [ + "Une symphonie transcendante qui emporte l'auditeur dans un voyage émotionnel profond. #ChefDOeuvre #MusiqueClassique", + "Des rythmes hypnotiques qui font vibrer l'âme, une expérience musicale inoubliable. #GrooveIntense #MusiqueDuMonde", + "Des paroles poignantes qui capturent l'esprit de toute une génération. #TextesProfonds #ChansonEngagée", + "Un mélange parfait de mélodies envoûtantes et de beats entraînants. #VibesPositives #PopPerfection", + "Une performance vocale à couper le souffle, une véritable démonstration de talent. #VoixExceptionnelle #SoulfulSounds", + "Des accords de guitare envoûtants qui créent une atmosphère intime et captivante. #AcousticMagic #FolkVibes", + "Innovation sonore à son apogée, repoussant les limites de l'expérimentation musicale. #AvantGarde #SoundExploration", + "Un album qui transcende les genres, une fusion audacieuse de styles musicaux. #GenreBlending #Eclectique", + "Des harmonies célestes qui transportent l'auditeur dans un état de béatitude. #HarmonieParfaite #AmbianceCéleste", + "Des beats percutants et des paroles incisives, un manifeste musical de rébellion. #RythmesEngagés #RebelAttitude", + "Une expérience auditive immersive, chaque morceau est une aventure sensorielle unique. #SonEn3D #AudioExpérience", + "Un crescendo émotionnel qui éveille les passions les plus profondes. #ÉmotionsIntenses #CrescendoEmotionnel", + "Des arrangements subtils qui capturent l'essence de la nostalgie d'une époque révolue. #VintageVibes #NostalgieMusicale", + "Des beats entraînants qui font vibrer le corps, une invitation à la piste de danse. #RythmesContagieux #DanceFloorAnthems", + "Un tour de force musical qui transcende les générations, une œuvre intemporelle. #MusiqueÉternelle #ClassicForever" +] + +reponses_reviews = [ + "Merci pour cette superbe critique! Tellement d'accord sur l'impact émotionnel de cette symphonie. #PartageonsNosÉmotions", + "Ces rythmes m'ont aussi transporté! On devrait tous explorer davantage la diversité musicale du monde. #MusiqueUniverselle", + "Les paroles de cette chanson m'ont vraiment touché. La musique peut être une force puissante pour le changement. #PowerOfLyrics", + "D'accord à 100%! Les vibes positives de cet album sont exactement ce dont on a besoin. #SpreadJoy #MusicLover", + "Sa voix est une merveille! Tellement d'émotion derrière chaque note. #VoixInoubliable #MusicalSoulmate", + "J'aime ces accords de guitare! Ils créent une ambiance si chaleureuse et authentique. #AcousticMagic #FolkVibesForever", + "Absolument, l'innovation sonore est si rafraîchissante. On a besoin de plus d'expérimentation dans la musique! #PushingBoundaries", + "Je suis totalement d'accord avec cette analyse! La fusion de genres est une véritable prouesse artistique. #EclectiqueForever", + "Ces harmonies sont vraiment transcendantes. Chaque écoute est une expérience méditative. #MusiqueMystique #AudioPhile", + "Les paroles engagées résonnent vraiment avec moi. La musique peut être un moyen puissant de transmettre un message. #MusicActivism", + "Tu as décrit parfaitement cette expérience immersive! On se sent transporté à chaque écoute. #AudioVoyage #SonicDreams", + "Le crescendo émotionnel est époustouflant! On ressent chaque montée et descente de l'émotion. #CrescendoMagique #EmotionalJourney", + "Tellement vrai! Les arrangements subtils ajoutent une couche de nostalgie et d'authenticité. #VintageVibes #TimelessTunes", + "J'ai dansé toute la nuit sur ces beats entraînants! La musique a le pouvoir de rassembler les gens sur la piste de danse. #MusicUnity", + "L'œuvre intemporelle transcende vraiment les générations. Un chef-d'œuvre qui restera gravé dans l'histoire musicale. #ClassicMasterpiece", + "Merci pour cette critique passionnée! Il est génial de voir tant d'enthousiasme pour la musique. #MusicEnthusiast #KeepListening" +] + +track_ids = [ + "0wJoRiX5K5BxlqZTolB2LD", + "2AxCeJ6PSsBYiTckM0HLY7", + "0NWPxcsf5vdjdiFUI8NgkP", + "1Eolhana7nKHYpcYpdVcT5", + "1ntxpzIUbSsizvuAy6lTYY", + "23MrkN7g6Q5U7GLIxNHN1B", + "0auKlivXpm76wR63mMJ3pR", + "5uDpwSGjljhIgDB1ZYdp9c", + "5LI7PoHEolR8plrf3I16sq", + "5H6Jp0syB5yEPk7SWYdlmk", +] + +artists = [ + '6mEQK9m2krja6X1cfsAjfl', + '7yeFMUrYTY5cAZx0GKXnti', + '7Ln80lUS6He07XvHI8qqHH', + '13ubrt8QOOCPljQ2FL1Kca', + '711MCceyCBcFnzjGY4Q7Un', + '3QVolfxko2UyCOtexhVTli' +] +pseudo = obtenir_prenoms_aleatoires(n) +conn = mysql.connector.connect( + host="localhost", + user="root", + password="root", + database="spotify", + port=3306 +) + +print("-------------INSERT USERS----------------------") +password = "$2b$10$feqJS.qjWmoYovS805Mmhu.7VjOncR68em0Bu4LSxS/4tDSP8HWK2" +ids = [] +cursor = conn.cursor() +for data in pseudo: + id = insertUser(cursor,data,password,random.choice(biographies)) + if(id is not None): + ids.append(id) + +print("-------------INSERT FOLLOW----------------------") +insert_follow(cursor,ids,artists) +print("-------------INSERT RELATIONS----------------------") +insert_relation(cursor,ids) + +print("-------------INSERT REVIEWS----------------------") +review_ids = putReview(cursor,reviews_musicales,ids,track_ids) +putLikesReview(cursor,ids,review_ids) +print("-------------INSERT COMMENT----------------------") +reponse_ids = putReponse(cursor, reponses_reviews,ids,review_ids) +putLikesReponse(cursor,ids,reponse_ids) +conn.commit() + diff --git a/test/integration/artist.test.js b/test/integration/artist.test.js new file mode 100644 index 0000000..f022f71 --- /dev/null +++ b/test/integration/artist.test.js @@ -0,0 +1,123 @@ +'use strict'; +const Hapi = require('@hapi/hapi'); +const strategy = require("../../lib/infrastructure/config/strategy"); +const Jwt = require("@hapi/jwt"); +const jwt = require('jsonwebtoken'); + +require('dotenv').config() +let server +const mockReviewRepository = {} +const mockAccesTokenManager = {} +const mockUserRepository = {} +const mockSpotifyRepository = {} +const mockFollowRepository = {} + +mockAccesTokenManager.generate = ((test) =>{return ''}) +const mockToken = jwt.sign({ + sub: 'my-sub', + value: 1, + aud: 'urn:audience:test', + iss: 'urn:issuer:test', + expiresIn: '365d' +}, process.env.SECRET_ENCODER) + +describe('artist route', () => { + + beforeEach(async () => { + + server = Hapi.server({ + port: process.env.PORT || 3000 + }); + server.app.serviceLocator = { + reviewRepository: mockReviewRepository, + accessTokenManager: mockAccesTokenManager, + userRepository: mockUserRepository, + spotifyRepository: mockSpotifyRepository, + followRepository: mockFollowRepository, + } + server.register(Jwt) + server.auth.strategy('jwt', 'jwt', strategy({userRepository: mockUserRepository})); + await server.register([ + require('../../lib/interfaces/routes/artist'), + ]); + }); + + afterEach(async () => { + jest.clearAllMocks(); + await server.stop(); + }); + const { + mockUser, + mockArtist, + mockUserPrivate, + mockAlbumRaw, + mockLikedReview, + mockCommentedReview, + mockOeuvreReviewSpotify, + } = require('./fixture/artist/getArtistFixture') + describe("GET review route", ()=>{ + + it("should return status code 200", async () => { + + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => mockUser) + mockFollowRepository.getFollowersCount = jest.fn((artistId) => 100) + mockFollowRepository.getFriendsFollowing = jest.fn((artistId,userId,limit) => [mockUserPrivate]) + mockFollowRepository.getFriendsFollowingCount = jest.fn((artistId,userId) => 1) + mockSpotifyRepository.getSpotifyArtist = jest.fn((artistId) => mockArtist) + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((artistId,filter,limit) => mockAlbumRaw) + mockReviewRepository.getOeuvreRating = jest.fn((id) => 1) + mockReviewRepository.getReviewCount = jest.fn((id) => 2) + mockReviewRepository.getOeuvreReviews = jest.fn().mockReturnValueOnce([mockLikedReview]).mockReturnValueOnce([mockCommentedReview]) + mockreviewRepository.doesUserLikes = jest.fn().mockReturnValue(true) + mockFollowRepository.doesFollows = jest.fn().mockReturnValue(true) + mockSpotifyRepository.getOeuvre = jest.fn().mockReturnValue(mockOeuvreReviewSpotify) + const res1 = await server.inject({ + method: 'GET', + url: `/artist/test`, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(200); + + }) + it("should return status code 401", async () => { + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => null) + const res1 = await server.inject({ + method: 'GET', + url: `/artist/test`, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(401); + + }) + it("should return status code 400", async () => { + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => mockUser) + mockSpotifyRepository.getSpotifyArtist = jest.fn((artistId) => { + return { + error: { + status: 400, + message: "message", + } + } + }) + const res1 = await server.inject({ + method: 'GET', + url: `/artist/test`, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(400); + + }) + + }) + + +}); diff --git a/test/integration/fixture/artist/getArtistFixture.js b/test/integration/fixture/artist/getArtistFixture.js new file mode 100644 index 0000000..abf8c1b --- /dev/null +++ b/test/integration/fixture/artist/getArtistFixture.js @@ -0,0 +1,217 @@ +const mockArtist = { + external_urls: { + spotify: "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + }, + genres: ["french hip hop", "old school rap francais", "rap conscient"], + id: "4FpJcNgOvIpSBeJgRg3OfN", + images: [ + { + height: 640, + url: "https://i.scdn.co/image/ab6761610000e5eb32086a424e6f1e499e347cde", + width: 640, + }, + ], + name: "Orelsan", + popularity: 64, + type: "artist", +}; +const mockAlbumRaw = { + href: "https://api.spotify.com/v1/artists/4FpJcNgOvIpSBeJgRg3OfN/albums?include_groups=album,single&offset=0&limit=10", + items: [ + { + album_group: "album", + album_type: "album", + artists: [ + { + external_urls: { + spotify: "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + }, + id: "4FpJcNgOvIpSBeJgRg3OfN", + name: "Orelsan", + type: "artist", + }, + ], + external_urls: { + spotify: "https://open.spotify.com/album/68YP0pEgwhnfRqQAzu71gP", + }, + id: "68YP0pEgwhnfRqQAzu71gP", + images: [ + { + height: 640, + url: "https://i.scdn.co/image/ab67616d0000b2732724364cd86bb791926b6cc8", + width: 640, + }, + ], + name: "Civilisation Edition Ultime", + release_date: "2022-10-28", + total_tracks: 25, + type: "album", + }, + ], +}; +const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false, +}; + +const mockUserPrivate = { + pseudo: "John Doe2", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + id_utilisateur: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: true, +}; +const actualDate = new Date(); + +const mockLikedReview = { + id_review: 25, + id_oeuvre: "68YP0pEgwhnfRqQAzu71gP", + countlikes: 32, + countComment: 0, + description: + "Innovation sonore à son apogée, repoussant les limites de l'expérimentation musicale. #AvantGarde #SoundExploration", + note: 5, + createdAt: "2024-01-03T00:00:00.000Z", + updated_at: "2024-01-03T00:00:00.000Z", + type: "album", + utilisateur: { + id_utilisateur: 54, + pseudo: "Constance", + email: "Constance.Constance@gmail.com", + auth_with_spotify: false, + alias: "Constance", + photo: "https://ozgrozer.github.io/100k-faces/0/6/006026.jpg", + photo_temporaire: null, + token: null, + refresh_token: null, + reset_token: null, + password: "$2b$10$feqJS.qjWmoYovS805Mmhu.7VjOncR68em0Bu4LSxS/4tDSP8HWK2", + id_role: 1, + ban_until: null, + confirmed: true, + confirm_token: null, + auth_with_spotify: false, + is_private: true, + type: "user", + }, +}; + +const mockCommentedReview = { + id_review: 25, + id_oeuvre: "68YP0pEgwhnfRqQAzu71gP", + countlikes: 32, + countComment: 0, + description: + "Innovation sonore à son apogée, repoussant les limites de l'expérimentation musicale. #AvantGarde #SoundExploration", + note: 5, + createdAt: "2024-01-03T00:00:00.000Z", + updated_at: "2024-01-03T00:00:00.000Z", + type: "album", + utilisateur: { + id_utilisateur: 54, + pseudo: "Constance", + email: "Constance.Constance@gmail.com", + auth_with_spotify: false, + alias: "Constance", + photo: "https://ozgrozer.github.io/100k-faces/0/6/006026.jpg", + photo_temporaire: null, + token: null, + refresh_token: null, + reset_token: null, + password: "$2b$10$feqJS.qjWmoYovS805Mmhu.7VjOncR68em0Bu4LSxS/4tDSP8HWK2", + id_role: 1, + ban_until: null, + confirmed: true, + confirm_token: null, + auth_with_spotify: false, + is_private: true, + type: "user", + }, +}; + +const mockOeuvreReviewSpotify = { + album_type: "album", + artists: [ + { + external_urls: { + spotify: "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + }, + id: "4FpJcNgOvIpSBeJgRg3OfN", + name: "Orelsan", + type: "artist", + }, + ], + + external_urls: { + spotify: "https://open.spotify.com/album/68YP0pEgwhnfRqQAzu71gP", + }, + genres: [], + id: "68YP0pEgwhnfRqQAzu71gP", + images: [ + { + height: 640, + url: "https://i.scdn.co/image/ab67616d0000b2732724364cd86bb791926b6cc8", + width: 640, + }, + ], + name: "Civilisation Edition Ultime", + popularity: 60, + release_date: "2022-10-28", + total_tracks: 1, + tracks: { + href: "https://api.spotify.com/v1/albums/68YP0pEgwhnfRqQAzu71gP/tracks?offset=0&limit=50", + items: [ + { + artists: [ + { + external_urls: { + spotify: "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + }, + href: "https://api.spotify.com/v1/artists/4FpJcNgOvIpSBeJgRg3OfN", + id: "4FpJcNgOvIpSBeJgRg3OfN", + name: "Orelsan", + type: "artist", + uri: "spotify:artist:4FpJcNgOvIpSBeJgRg3OfN", + }, + ], + + disc_number: 1, + duration_ms: 161732, + external_urls: { + spotify: "https://open.spotify.com/track/7b3YQboXo3kau9YVUyx3r4", + }, + id: "7b3YQboXo3kau9YVUyx3r4", + name: "CP_001_ Intro Civilisation Perdue", + preview_url: + "https://p.scdn.co/mp3-preview/b7f3ef4f7ee54bcba700a821952539b7e4e21d2f?cid=24b7c8fbb7d146edbce14e1743cea479", + track_number: 1, + type: "track", + }, + ], + total: 25, + }, + type: "album", +}; + +module.exports = { + mockUser, + mockArtist, + mockUserPrivate, + mockAlbumRaw, + mockLikedReview, + mockCommentedReview, + mockOeuvreReviewSpotify, +}; diff --git a/test/integration/fixture/imageTest.jpg b/test/integration/fixture/imageTest.jpg new file mode 100644 index 0000000..a723e03 Binary files /dev/null and b/test/integration/fixture/imageTest.jpg differ diff --git a/test/integration/fixture/review/getOeuvreReviewFixture.js b/test/integration/fixture/review/getOeuvreReviewFixture.js new file mode 100644 index 0000000..ae20643 --- /dev/null +++ b/test/integration/fixture/review/getOeuvreReviewFixture.js @@ -0,0 +1,60 @@ +const mockArtist = { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + images: [{ + height: 640, + url: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + width: 640 + }], + external_urls: {spotify: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii"}, + popularity: 79, + genres: ["Reggae", "Roots"] +} +const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} +const mockPublicUser = { + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_utilisateur: 1, + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} + +const actualDate = new Date() +const rawReview = { + id_review: 1, + id_oeuvre: 1, + countlikes: 2, + countComment: 4, + description: "C'est top", + note: 5, + createdAt: actualDate, + updated_at: actualDate, + type: 'artist', + utilisateur: mockUser +} + + + + + + +module.exports = { + mockArtist, + rawReview, +} \ No newline at end of file diff --git a/test/integration/fixture/review/getOeuvreReviewsFixture.js b/test/integration/fixture/review/getOeuvreReviewsFixture.js new file mode 100644 index 0000000..16508d7 --- /dev/null +++ b/test/integration/fixture/review/getOeuvreReviewsFixture.js @@ -0,0 +1,47 @@ +const mockArtist = { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + images: [{ + height: 640, + url: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + width: 640 + }], + external_urls: {spotify: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii"}, + popularity: 79, + genres: ["Reggae", "Roots"] +} +const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} + + +const actualDate = new Date() +const rawReview = { + id_review: 1, + id_oeuvre: 1, + countlike: 2, + countComment: 4, + description: "C'est top", + note: 5, + createdAt: actualDate, + updated_at: actualDate, + type: 'artist', + utilisateur: mockUser +} + + + + +module.exports = { + mockArtist, + rawReview, +} \ No newline at end of file diff --git a/test/integration/fixture/review/getReviewFixture.js b/test/integration/fixture/review/getReviewFixture.js new file mode 100644 index 0000000..b933e54 --- /dev/null +++ b/test/integration/fixture/review/getReviewFixture.js @@ -0,0 +1,55 @@ +const mockArtist = { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + images: [{ + height: 640, + url: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + width: 640 + }], + external_urls: {spotify: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii"}, + popularity: 79, + genres: ["Reggae", "Roots"] +} +const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false + +} + +const actualDate = new Date() +const rawReview = { + id_review: 1, + id_oeuvre: 1, + countlike: 2, + countComment: 4, + description: "C'est top", + note: 5, + createdAt: actualDate, + updatedAt: actualDate, + type: 'artist', + utilisateur: mockUser +} + +const mockComments = [ + { + id: 298, + description: 'test4', + utilisateur: mockUser + } +] + + + +module.exports = { + rawReview, + mockArtist, + mockComments, +} \ No newline at end of file diff --git a/test/integration/fixture/review/getReviewLikesFixture.js b/test/integration/fixture/review/getReviewLikesFixture.js new file mode 100644 index 0000000..c5289b5 --- /dev/null +++ b/test/integration/fixture/review/getReviewLikesFixture.js @@ -0,0 +1,28 @@ +const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false + +} +const mockPublicUser = { + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} +module.exports = { + mockUser, + mockPublicUser +} \ No newline at end of file diff --git a/test/integration/friend.test.js b/test/integration/friend.test.js new file mode 100644 index 0000000..51d19f8 --- /dev/null +++ b/test/integration/friend.test.js @@ -0,0 +1,398 @@ +'use strict'; +const Hapi = require('@hapi/hapi'); +const Friend = require("../../lib/domain/model/Friend") +const User = require("../../lib/domain/model/User") +const strategy = require("../../lib/infrastructure/config/strategy"); +const Jwt = require("@hapi/jwt"); +const jwt = require('jsonwebtoken'); +require('dotenv').config() +let server +const mockUserRepository = {} +const mockFriendRepository = {} +const mockMailRepository = {} +const mockAccesTokenManager = {} + +mockAccesTokenManager.generate = ((test) =>{return ''}) +const mockToken = jwt.sign({ + sub: 'my-sub', + value: 1, + aud: 'urn:audience:test', + iss: 'urn:issuer:test', + expiresIn: '365d' +}, process.env.SECRET_ENCODER) + +describe('friend route', () => { + + beforeEach(async () => { + + server = Hapi.server({ + port: process.env.PORT || 3000 + }); + server.app.serviceLocator = { + userRepository: mockUserRepository, + friendRepository: mockFriendRepository, + mailRepository: mockMailRepository, + accessTokenManager:mockAccesTokenManager + } + server.register(Jwt) + server.auth.strategy('jwt', 'jwt', strategy({userRepository: mockUserRepository})); + await server.register([ + require('../../lib/interfaces/routes/friends'), + ]); + }); + + afterEach(async () => { + jest.clearAllMocks(); + await server.stop(); + }); + + describe("/amis/follow", ()=>{ + afterEach(()=>{ + jest.clearAllMocks(); + }) + beforeEach(()=>{ + mockUserRepository.getByUser = jest.fn((id)=> { + return new User({pseudo:"test", id_utilisateur:id, email: "test@gmail.com", alias: "testt", is_private: true}) + }) + mockFriendRepository.persist = jest.fn((test) => { + return test + }) + mockMailRepository.send = jest.fn(option => null) + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: 1}}) + + }) + it('should respond code 200 with token user', async () => { + const res = await server.inject({ + method: 'POST', + url: '/amis/follow', + payload: { + amiIdUtilisateur: 2 + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + + expect(res.statusCode).toBe(200); + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.persist).toHaveBeenCalledTimes(1) + expect(mockMailRepository.send).toHaveBeenCalledTimes(1) + }); + it('should respond code 400 with invalid id friend', async () => { + mockUserRepository.getByUser = jest.fn((id)=> { + if(id > 0) return new User({pseudo:"test", id_utilisateur:id, email: "test@gmail.com", alias: "testt", is_private: false}) + return null + }) + const res = await server.inject({ + method: 'POST', + url: '/amis/follow', + payload: { + amiIdUtilisateur: -2 + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res.statusCode).toBe(400); + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.persist).toHaveBeenCalledTimes(0) + expect(mockMailRepository.send).toHaveBeenCalledTimes(0) + }); + it('should respond code 403 with already existing frindship', async () => { + mockUserRepository.getByUser = jest.fn((id)=> { + return new User({pseudo:"test", id_utilisateur:id, email: "test@gmail.com", alias: "testt", is_private: true}) + }) + mockFriendRepository.persist = jest.fn((f)=> null) + const res = await server.inject({ + method: 'POST', + url: '/amis/follow', + payload: { + amiIdUtilisateur: 2 + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res.statusCode).toBe(403); + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.persist).toHaveBeenCalledTimes(1) + expect(mockMailRepository.send).toHaveBeenCalledTimes(0) + }); + + }) + + describe("/amis/unfollow", ()=>{ + afterEach(()=>{ + jest.clearAllMocks(); + }) + beforeEach(()=>{ + mockUserRepository.getByUser = jest.fn((id)=> { + return new User({pseudo:"test", id_utilisateur:id, email: "test@gmail.com", alias: "testt", is_private: true}) + }) + mockFriendRepository.removeFriendById = jest.fn((id, id_ami) => { + return new Friend({id_utilisateur:id, amiIdUtilisateur:id_ami, en_attente:true}) + }) + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: 1}}) + + }) + + it('should respond code 200 with token user', async () => { + + const res = await server.inject({ + method: 'POST', + url: '/amis/unfollow', + payload: { + amiIdUtilisateur: 2 + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + + expect(res.statusCode).toBe(200); + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1); + expect(mockFriendRepository.removeFriendById).toHaveBeenCalledTimes(1) + }); + it('should respond code 400 with invalid id friend', async () => { + mockUserRepository.getByUser= jest.fn((id)=> { + if(id > 0) return new User({pseudo:"test", id_utilisateur:id, email: "test@gmail.com", alias: "testt", is_private: false}) + return null + }) + const res = await server.inject({ + method: 'POST', + url: '/amis/unfollow', + payload: { + amiIdUtilisateur: -2 + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res.statusCode).toBe(400); + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.removeFriendById).toHaveBeenCalledTimes(0) + }); + it('should respond code 403 with frindship doesn\'t exist', async () => { + mockFriendRepository.removeFriendById = jest.fn((f)=> null) + const res = await server.inject({ + method: 'POST', + url: '/amis/unfollow', + payload: { + amiIdUtilisateur: 3 + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res.statusCode).toBe(403); + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.removeFriendById).toHaveBeenCalledTimes(1) + }); + + }) + + describe("/amis/accept", ()=>{ + afterEach(()=>{ + jest.clearAllMocks(); + }) + beforeEach(()=>{ + mockUserRepository.getByUser = jest.fn((id)=> { + return new User({pseudo:"test", id_utilisateur:id, email: "test@gmail.com", alias: "testt", is_private: true}) + }) + mockFriendRepository.accept = jest.fn((id, id_ami) => { + return new Friend({id_utilisateur:id, amiIdUtilisateur:id_ami, en_attente:true}) + }) + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: 1}}) + }) + + it('should respond code 200 with token user and friend', async () => { + const res = await server.inject({ + method: 'POST', + url: '/amis/accept', + payload: { + amiIdUtilisateur: 2 + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + + expect(res.statusCode).toBe(200); + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.accept).toHaveBeenCalledTimes(1) + }); + it('should respond code 400 with invalid id friend', async () => { + mockUserRepository.getByUser= jest.fn((id)=> { + if(id > 0) return new User({pseudo:"test", id_utilisateur:id, email: "test@gmail.com", alias: "testt", is_private: false}) + return null + }) + const res = await server.inject({ + method: 'POST', + url: '/amis/accept', + payload: { + amiIdUtilisateur: -2 + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res.statusCode).toBe(400); + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.accept).toHaveBeenCalledTimes(0) + }); + it('should respond code 403 with frindship doesn\'t exist', async () => { + mockFriendRepository.accept = jest.fn((f)=> null) + const res = await server.inject({ + method: 'POST', + url: '/amis/accept', + payload: { + amiIdUtilisateur: 2 + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res.statusCode).toBe(403); + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.accept).toHaveBeenCalledTimes(1) + }); + + }) + + describe("/amis/profil", ()=>{ + afterEach(()=>{ + jest.clearAllMocks(); + }) + beforeEach(()=>{ + mockUserRepository.getByUser = jest.fn((id)=> { + return new User({pseudo:"test", id_utilisateur:id, email: "test@gmail.com", alias: "testt", is_private: true}) + }) + mockFriendRepository.getById = jest.fn((id, id_ami) => { + return new Friend({amiIdUtilisateur:id_ami, id_utilisateur:id, en_attente: false}) + }) + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: 1}}) + }); + + it('should respond code 400 with invalid id friend', async () => { + mockUserRepository.getByUser= jest.fn((id)=> { + if(id > 0) return new User({pseudo:"test", id_utilisateur:id, email: "test@gmail.com", alias: "testt", is_private: false}) + return null + }) + const res = await server.inject({ + method: 'POST', + url: '/amis/profil', + payload: { + amiIdUtilisateur: -1 + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res.statusCode).toBe(400); + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(3) + expect(mockFriendRepository.getById).toHaveBeenCalledTimes(0) + }); + it('should respond code 200', async () => { + mockUserRepository.getByUser= jest.fn((id)=> { + if(id > 0) return new User({pseudo:"test", id_utilisateur:id, email: "test@gmail.com", alias: "testt", is_private: false}) + return null + }) + const res = await server.inject({ + method: 'POST', + url: '/amis/profil', + payload: { + amiIdUtilisateur: 3 + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res.statusCode).toBe(200); + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(3) + expect(mockFriendRepository.getById).toHaveBeenCalledTimes(1) + }); + it('should respond code 403 with frindship doesn\'t exist', async () => { + mockFriendRepository.getById = jest.fn((id, id_ami)=> { + return null + }) + const res = await server.inject({ + method: 'POST', + url: '/amis/profil', + payload: { + amiIdUtilisateur: 3 + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res.statusCode).toBe(403); + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(3) + expect(mockFriendRepository.getById).toHaveBeenCalledTimes(1) + }); + + }) + + describe("/amis/list", ()=>{ + afterEach(()=>{ + jest.clearAllMocks(); + }) + + mockUserRepository.getByUser = jest.fn((id)=> { + return new User({pseudo:"test", id_utilisateur:id, email: "test@gmail.com", alias: "testt", is_private: true}) + }) + mockFriendRepository.getListFriendsById = jest.fn((id) => { + return [ + new User({pseudo:"test", id_utilisateur:1, email: "test@gmail.com", alias: "testt", is_private: true}), + new User({pseudo:"test2", id_utilisateur:2, email: "test2@gmail.com", alias: "testt2", is_private: false}) + ] + }) + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: 1}}) + + it('should respond code 200 with token user', async () => { + const res = await server.inject({ + method: 'GET', + url: '/amis/list', + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + + expect(res.statusCode).toBe(200); + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.getListFriendsById).toHaveBeenCalledTimes(1) + }); + }) + + describe("/amis/request", ()=>{ + afterEach(()=>{ + jest.clearAllMocks(); + }) + beforeEach( async ()=>{ + mockUserRepository.getByUser = jest.fn((id)=> { + return new User({pseudo:"test", id_utilisateur:id, email: "test@gmail.com", alias: "testt", is_private: true}) + }) + mockFriendRepository.getRequestFriendsById = jest.fn((id) => { + return [ + new User({pseudo:"test", id_utilisateur:2, email: "test@gmail.com", alias: "testt", is_private: true}), + new User({pseudo:"test2", id_utilisateur:3, email: "test2@gmail.com", alias: "testt2", is_private: true}) + ] + }) + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: 1}}) + }) + + it('should respond code 200 with token user', async () => { + const res = await server.inject({ + method: 'GET', + url: '/amis/request', + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + + expect(res.statusCode).toBe(200); + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.getRequestFriendsById).toHaveBeenCalledTimes(1) + }); + }) +}); diff --git a/test/integration/oeuvre.test.js b/test/integration/oeuvre.test.js new file mode 100644 index 0000000..5db7eae --- /dev/null +++ b/test/integration/oeuvre.test.js @@ -0,0 +1,129 @@ +'use strict'; +const Hapi = require('@hapi/hapi'); +const strategy = require("../../lib/infrastructure/config/strategy"); +const Jwt = require("@hapi/jwt"); +const jwt = require('jsonwebtoken'); + +require('dotenv').config() +let server +const mockUserRepository = {} +const mockLikeOeuvreRepository = {} +const mockAccessTokenManager = {} +const mockSpotifyRepository = {} + +mockAccessTokenManager.generate = ((test) =>{return ''}) +const mockToken = jwt.sign({ + sub: 'my-sub', + value: 1, + aud: 'urn:audience:test', + iss: 'urn:issuer:test', + expiresIn: '365d' +}, process.env.SECRET_ENCODER) + +describe('artist route', () => { + + beforeEach(async () => { + + server = Hapi.server({ + port: process.env.PORT || 3000 + }); + server.app.serviceLocator = { + userRepository: mockUserRepository, + likeOeuvreRepository: mockLikeOeuvreRepository, + accessTokenManager: mockAccessTokenManager, + spotifyRepository: mockSpotifyRepository + } + server.register(Jwt) + server.auth.strategy('jwt', 'jwt', strategy({userRepository: mockUserRepository})); + await server.register([ + require('../../lib/interfaces/routes/oeuvre'), + ]); + }); + + afterEach(async () => { + jest.clearAllMocks(); + await server.stop(); + }); + describe("POST oeuvre/{type}/{id}/like route", ()=>{ + const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false + } + const mockArtist = { + external_urls: { spotify: 'https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN' }, + genres: [ 'french hip hop', 'old school rap francais', 'rap conscient' ], + id: '4FpJcNgOvIpSBeJgRg3OfN', + images: [ + { + height: 640, + url: 'https://i.scdn.co/image/ab6761610000e5eb32086a424e6f1e499e347cde', + width: 640 + }, + ], + name: 'Orelsan', + popularity: 64, + type: 'artist', + } + const errror = { + error: { + status: 400, + message: "message", + } + } + it("should return status code 200", async () => { + + mockAccessTokenManager.decode = jest.fn(()=>{return {value:1}}) + mockUserRepository.getByUser = jest.fn().mockReturnValue(mockUser) + mockSpotifyRepository.getOeuvre = jest.fn().mockReturnValue(mockArtist) + mockLikeOeuvreRepository.doesUserLike = jest.fn().mockReturnValue(false) + mockLikeOeuvreRepository.like = jest.fn() + const res1 = await server.inject({ + method: 'POST', + url: `/oeuvre/artist/4FpJcNgOvIpSBeJgRg3OfN/like`, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(200); + + }) + it("should return status code 401", async () => { + mockAccessTokenManager.decode = jest.fn(()=>{return {value:1}}) + mockUserRepository.getByUser = jest.fn().mockReturnValue(null) + const res1 = await server.inject({ + method: 'POST', + url: `/oeuvre/artist/4FpJcNgOvIpSBeJgRg3OfN/like`, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(401); + + }) + it("should return status code 400", async () => { + mockAccessTokenManager.decode = jest.fn(()=>{return {value:1}}) + mockUserRepository.getByUser = jest.fn().mockReturnValue(mockUser) + mockSpotifyRepository.getOeuvre = jest.fn().mockReturnValue(errror) + const res1 = await server.inject({ + method: 'POST', + url: `/oeuvre/artist/4FpJcNgOvIpSBeJgRg3OfN/like`, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(400); + + }) + + }) + + +}); diff --git a/test/integration/review.test.js b/test/integration/review.test.js new file mode 100644 index 0000000..52232cc --- /dev/null +++ b/test/integration/review.test.js @@ -0,0 +1,533 @@ +'use strict'; +const Hapi = require('@hapi/hapi'); +const strategy = require("../../lib/infrastructure/config/strategy"); +const Jwt = require("@hapi/jwt"); +const jwt = require('jsonwebtoken'); + +require('dotenv').config() +let server +const mockReviewRepository = {} +const mockFriendRepository = {} +const mockAccesTokenManager = {} +const mockSpotifyRepository = {} +const mockUserRepository = {} +const mockCommentRepository = {} + +mockAccesTokenManager.generate = ((test) =>{return ''}) +const mockToken = jwt.sign({ + sub: 'my-sub', + value: 1, + aud: 'urn:audience:test', + iss: 'urn:issuer:test', + expiresIn: '365d' +}, process.env.SECRET_ENCODER) + +describe('review route', () => { + + beforeEach(async () => { + + server = Hapi.server({ + port: process.env.PORT || 3000 + }); + server.app.serviceLocator = { + reviewRepository: mockReviewRepository, + friendRepository: mockFriendRepository, + accessTokenManager: mockAccesTokenManager, + spotifyRepository: mockSpotifyRepository, + userRepository: mockUserRepository, + commentRepository: mockCommentRepository + } + server.register(Jwt) + server.auth.strategy('jwt', 'jwt', strategy({userRepository: mockUserRepository})); + await server.register([ + require('../../lib/interfaces/routes/review'), + ]); + }); + + afterEach(async () => { + jest.clearAllMocks(); + await server.stop(); + }); + const { + mockArtist, + rawReview, + mockComments + } = require("./fixture/review/getReviewFixture") + describe("GET review route", ()=>{ + + it("should return status code 200", async () => { + mockCommentRepository.getReviewComments = jest.fn().mockReturnValue(mockComments) + mockReviewRepository.getById = jest.fn((id) => rawReview) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + mockCommentRepository.getReviewComments = jest.fn().mockReturnValue(mockComments) + const res1 = await server.inject({ + method: 'GET', + url: `/review/1?page=1&pageSize=10&orderByLike=true`, + }); + expect(res1.statusCode).toBe(200); + + }) + it("should return status code 404", async () => { + mockReviewRepository.getById = jest.fn((id) => null) + mockCommentRepository.getReviewComments = jest.fn().mockReturnValue(mockComments) + const res1 = await server.inject({ + method: 'GET', + url: `/review/1?page=1&pageSize=10&orderByLike=true`, + }); + expect(res1.statusCode).toBe(404); + + }) + it("should return status code 403", async () => { + const mockReview = { + utilisateur: { + id_utilisateur: 1, + pseudo: "John Doe", + is_private: true + } + } + mockCommentRepository.getReviewComments = jest.fn().mockReturnValue(mockComments) + mockReviewRepository.getById = jest.fn((id) => mockReview) + mockFriendRepository.areFriends = jest.fn((id, id_ami) => false) + mockAccesTokenManager.decode = jest.fn((token) => {return {value: 1}}) + const res1 = await server.inject({ + method: 'GET', + url: `/review/1?page=1&pageSize=10&orderByLike=true`, + headers: { + Authorization: `Bearer ${mockToken}` + } + + }); + expect(res1.statusCode).toBe(403); + + }) + it("should return status code 400", async () => { + mockCommentRepository.getReviewComments = jest.fn().mockReturnValue(mockComments) + mockReviewRepository.getById = jest.fn((id) => rawReview) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => { + return { + error: { + status: 400, + message: "message", + } + } + }) + const res1 = await server.inject({ + method: 'GET', + url: `/review/1?page=1&pageSize=10&orderByLike=true`, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(400); + + }) + }) + describe("GET reviews route", ()=>{ + it("should return status code 200", async ()=>{ + + mockReviewRepository.getReviews = jest.fn((page,pageSize,orderByLike, isPrivate,userToken) => [rawReview]) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + const res1 = await server.inject({ + method: 'GET', + url: `/reviews?page=1&pageSize=10&orderByLike=true`, + + }); + expect(res1.statusCode).toBe(200); + }) + + it("should return status code 200 with user login", async ()=>{ + mockAccesTokenManager.decode = jest.fn((token) => {return {value: 1}}) + mockReviewRepository.getReviews = jest.fn((page,pageSize,orderByLike, isPrivate,userToken) => [rawReview]) + mockreviewRepository.doesUserLikes = jest.fn((id_utilisateur,reviewId) => false) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + const res1 = await server.inject({ + method: 'GET', + url: `/reviews?page=1&pageSize=10&orderByLike=true`, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(200); + }) + }) + + describe("DELETE review route", ()=>{ + it("should return status code 200", async ()=>{ + mockReviewRepository.delete = jest.fn((id) => true) + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => { + return { + id_utilisateur: 1 + } + }) + const res1 = await server.inject({ + method: 'DELETE', + url: `/review`, + payload: { + idReview: 'idReview' + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + + }); + expect(res1.statusCode).toBe(200); + }) + + it("should return status code 401 no header", async ()=>{ + mockReviewRepository.delete = jest.fn((id) => true) + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => { + return { + id_utilisateur: 1 + } + }) + const res1 = await server.inject({ + method: 'DELETE', + url: `/review`, + payload: { + idReview: 'idReview' + }, + }); + expect(res1.statusCode).toBe(401); + }) + + it("should return status code 401 bad auth token", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => null) + const res1 = await server.inject({ + method: 'DELETE', + url: `/review`, + payload: { + idReview: 'idReview' + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(401); + }) + it("should return status code 401 not post owner", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => { + return { + id_utilisateur: 1 + } + }) + mockReviewRepository.delete = jest.fn((id) => false) + const res1 = await server.inject({ + method: 'DELETE', + url: `/review`, + payload: { + idReview: 'idReview' + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(403); + }) + }) + + describe("GET reviewLikes route", ()=>{ + const { + mockUser, + mockPublicUser + } = require("./fixture/review/getReviewLikesFixture") + it("should return status code 404", async ()=>{ + mockReviewRepository.getById = jest.fn((id) => null) + const res1 = await server.inject({ + method: 'GET', + url: `/review/1/likes?page=1&pageSize=10`, + // headers: { + // Authorization: `Bearer ${mockToken}` + // } + }); + expect(res1.statusCode).toBe(404) + }) + it("should return status code 403", async ()=>{ + const mockReview = { + utilisateur: { + id_utilisateur: 1, + pseudo: "John Doe", + is_private: true + } + } + mockReviewRepository.getById = jest.fn((id) => mockReview) + mockAccesTokenManager.decode = jest.fn((token) => {return {value: 1}}) + mockFriendRepository.areFriends = jest.fn((id, id_ami) => false) + const res1 = await server.inject({ + method: 'GET', + url: `/review/1/likes?page=1&pageSize=10`, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(403) + }) + it("should return status code 200", async ()=>{ + const mockReview = { + utilisateur: { + id_utilisateur: 1, + pseudo: "John Doe", + is_private: false + } + } + mockReviewRepository.getById = jest.fn((id) => mockReview) + mockReviewRepository.getLikes = jest.fn((id) => [mockUser]) + const res1 = await server.inject({ + method: 'GET', + url: `/review/1/likes?page=1&pageSize=10`, + }); + expect(res1.statusCode).toBe(200) + }) + }) + + describe("GET userReviews route", ()=>{ + it("should return status code 200", async ()=>{ + mockUserRepository.getByEmailOrPseudo = jest.fn((pseudo,email) => { + return { + id_utilisateur: 1, + is_private: false + } + }) + mockReviewRepository.getReviewByUserId = jest.fn((id_utilisateur,page,pageSize,orderByLike) => [rawReview]) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + const res1 = await server.inject({ + method: 'GET', + url: `/reviews/user/{id}?page=1&pageSize=10&orderByLike=true`, + }); + expect(res1.statusCode).toBe(200); + }) + + it("should return status code 403", async ()=>{ + mockUserRepository.getByEmailOrPseudo = jest.fn((pseudo,email) => { + return { + id_utilisateur: 1, + is_private: true + } + }) + const res1 = await server.inject({ + method: 'GET', + url: `/reviews/user/{id}?page=1&pageSize=10&orderByLike=true`, + }); + expect(res1.statusCode).toBe(403); + }) + + it("should return status code 404", async ()=>{ + mockUserRepository.getByEmailOrPseudo = jest.fn().mockReturnValue(null) + const res1 = await server.inject({ + method: 'GET', + url: `/reviews/user/{id}?page=1&pageSize=10&orderByLike=true`, + }); + expect(res1.statusCode).toBe(404); + }) + }) + + describe("GET reviewLike route",()=>{ + it("should return status code 200", async ()=>{ + const mockReview = { + utilisateur: { + id_utilisateur: 1, + pseudo: "John Doe", + is_private: false + } + } + const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false + + } + mockReviewRepository.getById = jest.fn((id) => mockReview) + mockReviewRepository.getLikes = jest.fn((id) => [mockUser]) + + const res1 = await server.inject({ + method: 'GET', + url: `/review/1/likes?page=1&pageSize=10`, + }); + expect(res1.statusCode).toBe(200); + }) + + it("should return status code 403", async ()=>{ + + const mockReview = { + utilisateur: { + id_utilisateur: 1, + pseudo: "John Doe", + is_private: true + } + } + mockReviewRepository.getById = jest.fn((id) => mockReview) + mockAccesTokenManager.decode = jest.fn((token) => {return {value: 1}}) + mockFriendRepository.areFriends = jest.fn((id, id_ami) => false) + const res1 = await server.inject({ + method: 'GET', + url: `/review/1/likes?page=1&pageSize=10`, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(403); + + }) + it("should return status code 404", async ()=>{ + + mockReviewRepository.getById = jest.fn((id) => null) + const res1 = await server.inject({ + method: 'GET', + url: `/review/1/likes?page=1&pageSize=10`, + }); + expect(res1.statusCode).toBe(404); + + }) + }) + + describe("PUT review route",()=>{ + it("should return status code 200", async ()=>{ + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1 + } + }) + mockReviewRepository.getByUserAndId = jest.fn((idOeuvre, id_utilisateur) => null) + mockReviewRepository.getTypeReviewID = jest.fn((type) => 1) + mockReviewRepository.persist = jest.fn((reviewRaw) => rawReview) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + const res1 = await server.inject({ + method: 'PUT', + url: `/review`, + payload: { + idOeuvre: 'idOeuvre', + description: 'description', + note: 5, + type: 'artist' + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(200); + }) + + it("should return status code 400", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1 + } + }) + mockReviewRepository.getByUserAndId = jest.fn((idOeuvre, id_utilisateur) => null) + mockReviewRepository.getTypeReviewID = jest.fn((type) => 1) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => { + return { + error: { + status: 400, + message: 'error' + } + } + }) + const res1 = await server.inject({ + method: 'PUT', + url: `/review`, + payload: { + idOeuvre: 'idOeuvre', + description: 'description', + note: 5, + type: 'artist' + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(400); + }) + it("should return status code 401", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((userToken) => null) + const res1 = await server.inject({ + method: 'PUT', + url: `/review`, + payload: { + idOeuvre: 'idOeuvre', + description: 'description', + note: 5, + type: 'artist' + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(401); + }) + it("should return status code 403", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1 + } + }) + mockReviewRepository.getByUserAndId = jest.fn((idOeuvre, id_utilisateur) => { + return { + id_review: 1 + } + }) + const res1 = await server.inject({ + method: 'PUT', + url: `/review`, + payload: { + idOeuvre: 'idOeuvre', + description: 'description', + note: 5, + type: 'artist' + }, + headers: { + Authorization: `Bearer ${mockToken}` + } + }); + expect(res1.statusCode).toBe(403); + }) + }) + + describe("GET oeuvreReviews",()=>{ + const { + mockArtist, + rawReview, + } = require("./fixture/review/getOeuvreReviewFixture.js") + it("should return status code 200", async ()=>{ + mockReviewRepository.getOeuvreReviews = jest.fn((id_utilisateur,page,pageSize,orderByLike) => [rawReview]) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + const res1 = await server.inject({ + method: 'GET', + url: `/reviews/oeuvre/{id}?page=1&pageSize=10&orderByLike=true`, + }); + expect(res1.statusCode).toBe(200); + }) + + it("should return status code 400", async ()=>{ + mockReviewRepository.getOeuvreReviews = jest.fn((id_utilisateur,page,pageSize,orderByLike) => [rawReview]) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => { + return { + error: { + status: 400, + message: "message", + } + } + }) + const res1 = await server.inject({ + method: 'GET', + url: `/reviews/oeuvre/{id}?page=1&pageSize=10&orderByLike=true`, + }); + expect(res1.statusCode).toBe(400); + }) + + }) +}); diff --git a/test/integration/spotify.test.js b/test/integration/spotify.test.js index 53394cf..c77aa1c 100644 --- a/test/integration/spotify.test.js +++ b/test/integration/spotify.test.js @@ -2,11 +2,22 @@ const Hapi = require('@hapi/hapi'); const User = require("../../lib/domain/model/User") const bcrypt = require("bcrypt"); +const { + rawTrackWithServeralArtists, +} = require("../unit/interfaces/serializers/fixtures/trackFixture"); +const { + albumRawOneArtist, +} = require("../unit/interfaces/serializers/fixtures/albumFixture"); + +const getTrack = require("../../lib/application/use_cases/spotify/getTrack"); +const catchError = require("../unit/application/usecase/utils/catchError"); let server const mockUserRepository = {} const mockSpotifyRepository = {} mockUserRepository.getUsersByPseudo = jest.fn((pseudo) =>{return []}) mockSpotifyRepository.getSpotifySearchList = jest.fn((query, filter, limitSize) =>{return {}}) +mockSpotifyRepository.getSpotifyArtist = jest.fn((id) =>{return {}}) // mock la fct de repo +mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((id, filter, limit) =>{return {}}) describe('spotify route', () => { @@ -29,22 +40,14 @@ describe('spotify route', () => { it('should respond code 400 invalid query', async () => { const res1 = await server.inject({ method: 'GET', - url: `/spotify/search?query=${"a".repeat(51)}&spotify_filter=trddzack&allow_user=true&limit=10`, + url: `/spotify/search?query=${"a".repeat(51)}&spotify_filter=trddzack&limit=10`, }); expect(res1.statusCode).toBe(400); }); it('should respond code 400 invalid spotify_filter', async () => { const res1 = await server.inject({ method: 'GET', - url: `/spotify/search?query=query&spotify_filter=trddzack&allow_user=true&limit=10`, - }); - - expect(res1.statusCode).toBe(400); - }); - it('should respond code 400 invalid allow_user', async () => { - const res1 = await server.inject({ - method: 'GET', - url: `/spotify/search?query=query&spotify_filter=track&allow_user=dsd&limit=10`, + url: `/spotify/search?query=query&spotify_filter=trddzack&limit=10`, }); expect(res1.statusCode).toBe(400); @@ -52,13 +55,13 @@ describe('spotify route', () => { it('should respond code 400 invalid limit', async () => { const res1 = await server.inject({ method: 'GET', - url: `/spotify/search?query=query&spotify_filter=track&allow_user=true&limit=cez`, + url: `/spotify/search?query=query&spotify_filter=track&limit=cez`, }); expect(res1.statusCode).toBe(400); }); it('should respond code 200', async () => { - const allowedValues = ["track","artist","album"] + const allowedValues = ["track","artist","album","user"] const getAllSubset = (array) =>{ const n = array.length; const allSubsets = []; @@ -79,11 +82,147 @@ describe('spotify route', () => { for(let i =1; i{ + it('should respond code 200', async () => { + const res1 = await server.inject({ + method: 'GET', + url: `/spotify/Searchfilters`, + }); + expect(res1.statusCode).toBe(200); + }); + }) + describe("/spotify/FetchArtist", ()=>{ + it('should respond code 400 invalid query/id', async () => { + const res1 = await server.inject({ + method: 'GET', + url: `/spotify/fetchArtist?query=`, + }); + expect(res1.statusCode).toBe(400); + }); + it('should respond code 200', async () => { + const res1 = await server.inject({ + method: 'GET', + url: `/spotify/fetchArtist?query=query`, + }); + expect(res1.statusCode).toBe(200); + }); + it('should respond code 400 invalidID', async () => { + mockSpotifyRepository.getSpotifyArtist = jest.fn((id) =>{ + throw new Error('test error') + }) + const res1 = await server.inject({ + method: 'GET', + url: `/spotify/fetchArtist?query=query`, + }); + expect(res1.statusCode).toBe(400); + }); + }) + describe('/spotify/track', () => { + it("should invalid return code 400", async ()=>{ + mockSpotifyRepository.getSpotifyTracks = jest.fn((id) =>{ + return {error : {status: 400, message: "msg"}} + }) + const res1 = await server.inject({ + method: 'GET', + url: `/spotify/track?id=29092`, + }); + expect(res1.statusCode).toBe(400); + }) + it("should invalid return code 200", async ()=>{ + mockSpotifyRepository.getSpotifyTracks = jest.fn((id) =>{ + return rawTrackWithServeralArtists + }) + const res1 = await server.inject({ + method: 'GET', + url: `/spotify/track?id=29092`, + }); + expect(res1.statusCode).toBe(200); + }) + }); + describe('/spotify/album', () => { + it("should invalid return code 400", async ()=>{ + mockSpotifyRepository.getSpotifyAlbums = jest.fn((id) =>{ + return {error : {status: 400, message: "msg"}} + }) + const res1 = await server.inject({ + method: 'GET', + url: `/spotify/album?id=29092`, + }); + expect(res1.statusCode).toBe(400); + }) + it("should invalid return code 200", async ()=>{ + mockSpotifyRepository.getSpotifyAlbums = jest.fn((id) =>{ + return albumRawOneArtist + }) + const res1 = await server.inject({ + method: 'GET', + url: `/spotify/album?id=29092`, + }); + expect(res1.statusCode).toBe(200); + }) + + }); + + + describe("/spotify/FetchArtistSongs", ()=>{ + + it('should respond code 200', async () => { + const res1 = await server.inject({ + method: 'GET', + url: `/spotify/fetchArtistSongs?id=azerty1234`, // juste id, filter, limit optional + }); + expect(res1.statusCode).toBe(200); + }); + + it('should respond code 200', async () => { + const res1 = await server.inject({ + method: 'GET', + url: `/spotify/fetchArtistSongs?id=azerty1234&limit=10`, // juste id et limit + }); + expect(res1.statusCode).toBe(200); + }); + + it('should respond code 200', async () => { + const res1 = await server.inject({ + method: 'GET', + url: '/spotify/fetchArtistSongs?id=azerty1234&filter= album , compilation , appears_on ' // id et filtre avec espaces + }); + expect(res1.statusCode).toBe(200); + }); + + it('should respond code 400', async () => { + const res1 = await server.inject({ + method: 'GET', + url: '/spotify/fetchArtistSongs?id=azerty1234&filter= albu , compilation , appears_on ' // mauvaise orthographe sur un filtre + }); + expect(res1.statusCode).toBe(400); + }); + + + it('should respond code 400 invalid id', async () => { + const res1 = await server.inject({ + method: 'GET', + url: `/spotify/fetchArtistSongs?id=`, + }); + expect(res1.statusCode).toBe(400); + }); + + it('should respond code 400 invalidID', async () => { + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((id) =>{ + throw new Error('test error') + }) + const res1 = await server.inject({ + method: 'GET', + url: `/spotify/fetchArtistSongs?id=1234`, + }); + expect(res1.statusCode).toBe(400); + }); + }) }); diff --git a/test/integration/users.test.js b/test/integration/users.test.js index 9d25b15..5f66178 100644 --- a/test/integration/users.test.js +++ b/test/integration/users.test.js @@ -1,297 +1,971 @@ -'use strict'; -const Hapi = require('@hapi/hapi'); -const User = require("../../lib/domain/model/User") +"use strict"; +const Hapi = require("@hapi/hapi"); +const User = require("../../lib/domain/model/User"); const bcrypt = require("bcrypt"); -let server -const mockUserRepository = {} -const mockAccesTokenManager = {} -mockUserRepository.getByEmailOrPseudo = jest.fn((email,pseudo) => { - return null -}) -mockUserRepository.persist = jest.fn((test) =>{ - return test -}) - -mockAccesTokenManager.generate = ((test) =>{return ''}) -describe('user route', () => { - - beforeEach(async () => { - server = Hapi.server({ - port: process.env.PORT || 3000 - }); - await server.register([ - require('../../lib/interfaces/routes/users'), - ]); - server.app.serviceLocator = { - userRepository: mockUserRepository, - accessTokenManager:mockAccesTokenManager - } - }); - - afterEach(async () => { - await server.stop(); - }); - describe("/users/signup", ()=>{ - it('should respond code 200', async () => { - const res = await server.inject({ - method: 'POST', - url: '/users/signup', - payload: { - email: "tesddesqt@gmaiL.com", - pseudo: "testsss", - alias: "testsss", - password: "pdazdazassworrdddd", - spotifyToken: "tokeeeeen", - bio: "dzada" - } - }); - expect(res.statusCode).toBe(200); - }); - - - it('should respond code 403', async () => { - mockUserRepository.getByEmailOrPseudo = jest.fn((email,pseudo) => { - return {} - }) - const res = await server.inject({ - method: 'POST', - url: '/users/signup', - payload: { - email: "tesddesqt@gmaiL.com", - pseudo: "testsss", - alias: "testsss", - password: "pdazdazassworrdddd", - spotifyToken: "tokeeeeen", - bio: "dzada" - } - }); - expect(res.statusCode).toBe(403); - }); - - it('should respond code 400 with invalid email', async () => { - const res = await server.inject({ - method: 'POST', - url: '/users/signup', - payload: { - email: "te", - pseudo: "testsss", - alias: "testsss", - password: "pdazdazassworrdddd", - spotifyToken: "tokeeeeen", - bio: "dzada" - } - }); - expect(res.statusCode).toBe(400); - expect(JSON.parse(res.payload).validation.keys).toBe("email") - }); - - it('should respond code 400 with invalid pseudo', async () => { - const res1 = await server.inject({ - method: 'POST', - url: '/users/signup', - payload: { - email: "tesddesqt@gmaiL.com", - pseudo: "test@sss", - alias: "testsss", - password: "pdazdazassworrdddd", - spotifyToken: "tokeeeeen", - bio: "dzada" - } - }); - const res2 = await server.inject({ - method: 'POST', - url: '/users/signup', - payload: { - email: "tesddesqt@gmaiL.com", - pseudo: "te", - alias: "testsss", - password: "pdazdazassworrdddd", - spotifyToken: "tokeeeeen", - bio: "dzada" - } - }); - const res3 = await server.inject({ - method: 'POST', - url: '/users/signup', - payload: { - email: "tesddesqt@gmaiL.com", - pseudo: 'a'.repeat(16), - alias: "testsss", - password: "pdazdazassworrdddd", - spotifyToken: "tokeeeeen", - bio: "dzada" - } - }); - expect(res1.statusCode).toBe(400); - expect(JSON.parse(res1.payload).validation.keys).toBe("pseudo") - expect(res2.statusCode).toBe(400); - expect(JSON.parse(res2.payload).validation.keys).toBe("pseudo") - expect(res3.statusCode).toBe(400); - expect(JSON.parse(res3.payload).validation.keys).toBe("pseudo") - - }); - - it('should respond code 400 with invalid alias', async () => { - const res1 = await server.inject({ - method: 'POST', - url: '/users/signup', - payload: { - email: "tesddesqt@gmaiL.com", - pseudo: "testsss", - alias: "te", - password: "pdazdazassworrdddd", - spotifyToken: "tokeeeeen", - bio: "dzada" - } - }); - const res2 = await server.inject({ - method: 'POST', - url: '/users/signup', - payload: { - email: "tesddesqt@gmaiL.com", - pseudo: "testsss", - alias: 'a'.repeat(16), - password: "pdazdazassworrdddd", - spotifyToken: "tokeeeeen", - bio: "dzada" - } - }); - - expect(res1.statusCode).toBe(400); - expect(JSON.parse(res1.payload).validation.keys).toBe("alias") - expect(res2.statusCode).toBe(400); - expect(JSON.parse(res2.payload).validation.keys).toBe("alias") - - }); - - it('should respond code 400 with invalid password', async () => { - const res1 = await server.inject({ - method: 'POST', - url: '/users/signup', - payload: { - email: "tesddesqt@gmaiL.com", - pseudo: "testsss", - alias: "testsss", - password: "aa", - spotifyToken: "tokeeeeen", - bio: "dzada" - } - }); - const res2 = await server.inject({ - method: 'POST', - url: '/users/signup', - payload: { - email: "tesddesqt@gmaiL.com", - pseudo: "testsss", - alias: "testsss", - password: "a".repeat(31), - spotifyToken: "tokeeeeen", - bio: "dzada" - } - }); - - expect(res1.statusCode).toBe(400); - expect(JSON.parse(res1.payload).validation.keys).toBe("password") - expect(res2.statusCode).toBe(400); - expect(JSON.parse(res2.payload).validation.keys).toBe("password") - }); - - it('should respond code 400 with invalid bio', async () => { - const res1 = await server.inject({ - method: 'POST', - url: '/users/signup', - payload: { - email: "tesddesqt@gmaiL.com", - pseudo: "testsss", - alias: "testsss", - password: "aaaaaaaaaaa", - spotifyToken: "tokeeeeen", - bio: "a".repeat(1501) - } - }); - - - expect(res1.statusCode).toBe(400); - expect(JSON.parse(res1.payload).validation.keys).toBe("bio") - }); - }) - describe("/users/signin", ()=> { - - - it('should respond code 200', async () => { - const password = 'password' - let fetchedUser = new User("id","pseudo","email","alias","bio",await bcrypt.hash(password,10),"token",1,1) - - mockUserRepository.getByIdent = jest.fn((ident) =>{ - return fetchedUser - }) - - const res = await server.inject({ - method: 'POST', - url: '/users/signin', - payload: { - email:"tesddesqt@gmaiL.com", - password: password, - }} - ) - - expect(res.statusCode).toBe(200); - - }) - it('should respond code 401 bad password', async () => { - const password = 'password' - let fetchedUser = new User("id","pseudo","email","alias","bio",await bcrypt.hash(password,10),"token",1,1) - - mockUserRepository.getByIdent = jest.fn((ident) =>{ - return fetchedUser - }) - const res = await server.inject({ - method: 'POST', - url: '/users/signin', - payload: { - email:"tesddesqt@gmaiL.com", - password: "aaaaadaaaaaa", - }} - ) - expect(res.statusCode).toBe(401); - }) - it('should respond code 400 user not found', async () => { - mockUserRepository.getByIdent = jest.fn((ident) =>{ - return null - }) - const res = await server.inject({ - method: 'POST', - url: '/users/signin', - payload: { - email:"fezfez", - password: "aaaaadaaaaaa", - }} - ) - expect(res.statusCode).toBe(401); - }) - it('should respond code 400 invalid email', async () => { - - const res = await server.inject({ - method: 'POST', - url: '/users/signin', - payload: { - email:"fezfez", - password: "a".repeat(31), - }} - ) - expect(res.statusCode).toBe(400); - }) - it('should respond code 400 invalid password', async () => { - - const res = await server.inject({ - method: 'POST', - url: '/users/signin', - payload: { - email:"a".repeat(41), - password: "dzadzaa", - }} - ) - expect(res.statusCode).toBe(400); - }) - }) +const strategy = require("../../lib/infrastructure/config/strategy"); +const Jwt = require("@hapi/jwt"); +const jwt = require("jsonwebtoken"); +const { type } = require("@hapi/joi/lib/extend.js"); +require("dotenv").config(); +let server; +const mockUserRepository = {}; +const mockAccesTokenManager = {}; +const mockSpotifyRepository = {}; +const mockMailRepository = {}; +const mockDocumentRepository = {}; +const mockFollowRepository = {}; +const mockOeuvreFavRepository = {}; +const mockToken = jwt.sign( + { + sub: "my-sub", // needs to match definition above + value: 1, // this is a custom key I used, it could be named anything. Value should be a way to authenticate the user + aud: "urn:audience:test", // needs to match definition above + iss: "urn:issuer:test", // needs to match definition above, + expiresIn: "365d", + }, + process.env.SECRET_ENCODER +); + +mockAccesTokenManager.generate = (test) => { + return ""; +}; +describe("user route", () => { + beforeEach(async () => { + server = Hapi.server({ + port: process.env.PORT || 3000, + }); + server.app.serviceLocator = { + userRepository: mockUserRepository, + accessTokenManager: mockAccesTokenManager, + spotifyRepository: mockSpotifyRepository, + mailRepository: mockMailRepository, + documentRepository: mockDocumentRepository, + followRepository: mockFollowRepository, + oeuvreFavRepository: mockOeuvreFavRepository, + }; + server.register(Jwt); + server.auth.strategy( + "jwt", + "jwt", + strategy({ userRepository: mockUserRepository }) + ); + await server.register([require("../../lib/interfaces/routes/users")]); + }); + + afterEach(async () => { + jest.clearAllMocks(); + await server.stop(); + }); + describe("/users/createUser", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + mockUserRepository.getByEmailOrPseudo = jest.fn((email, pseudo) => null); + mockUserRepository.persist = jest.fn((test) => { + return { id_utilisateur: 1 }; + }); + mockMailRepository.send = jest.fn((option) => null); + mockSpotifyRepository.getToken = jest.fn(() => { + return { access_token: 1, refresh_token: 1 }; + }); + it("should respond code 200 with email inscription", async () => { + const res = await server.inject({ + method: "POST", + url: "/users/createUser", + payload: { + email: "tesddesqt@gmaiL.com", + password: "somepassword", + }, + }); + expect(res.statusCode).toBe(200); + }); + + it("should respond code 403 with already existing email", async () => { + mockUserRepository.getByEmailOrPseudo = jest.fn( + (email, pseudo) => "something" + ); + const res = await server.inject({ + method: "POST", + url: "/users/createUser", + payload: { + email: "tesddesqt@gmaiL.com", + password: "somepassword", + }, + }); + expect(res.statusCode).toBe(403); + }); + }); + describe("/users/confirmUser", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + mockUserRepository.getByConfirmToken = jest.fn(() => { + return { + id_utilisateur: 1, + photo_temporaire: "path", + }; + }); + mockUserRepository.updateUser = jest.fn((user) => user); + mockUserRepository.persist = jest.fn((test) => { + return { id_utilisateur: 1 }; + }); + mockDocumentRepository.deleteFile = jest.fn(() => null); + it("should respond code 200 with confirmation and photo", async () => { + mockUserRepository.getByEmailOrPseudo = jest.fn((email, pseudo) => null); + const payload = { + pseudo: "testPseudo", + alias: "testAlias", + confirmToken: "token", + photo: "path", + bio: "bio", + }; + const res = await server.inject({ + method: "POST", + url: "/users/confirmUser", + payload: payload, + }); + expect(res.statusCode).toBe(200); + expect(mockDocumentRepository.deleteFile).toHaveBeenCalledTimes(1); + }); + it("should respond code 200 with confirmation", async () => { + mockUserRepository.getByEmailOrPseudo = jest.fn((email, pseudo) => null); + const payload = { + pseudo: "testPseudo", + alias: "testAlias", + confirmToken: "token", + bio: "bio", + }; + const res = await server.inject({ + method: "POST", + url: "/users/confirmUser", + payload: payload, + }); + expect(res.statusCode).toBe(200); + expect(mockDocumentRepository.deleteFile).toHaveBeenCalledTimes(0); + }); + it("should respond code 400", async () => { + mockUserRepository.getByEmailOrPseudo = jest.fn((email, pseudo) => null); + mockUserRepository.getByConfirmToken = jest.fn(() => null); + const payload = { + pseudo: "testPseudo", + alias: "testAlias", + confirmToken: "token", + bio: "bio", + }; + const res = await server.inject({ + method: "POST", + url: "/users/confirmUser", + payload: payload, + }); + expect(res.statusCode).toBe(400); + }); + it("should respond code 403", async () => { + mockUserRepository.getByEmailOrPseudo = jest.fn(() => "someting"); + const payload = { + pseudo: "testPseudo", + alias: "testAlias", + confirmToken: "token", + bio: "bio", + }; + const res = await server.inject({ + method: "POST", + url: "/users/confirmUser", + payload: payload, + }); + expect(res.statusCode).toBe(403); + }); + }); + describe("/users/signin", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + mockUserRepository.getByUser = jest.fn((test) => { + return 1; + }); + mockAccesTokenManager.generate = (test) => { + return ""; + }; + it("should respond code 200", async () => { + const password = "password"; + const mockUserRaw = { + id_utilisateur: "id", + pseudo: "pseudo", + email: "email", + alias: "alias", + bio: "bio", + photo: "path/to/file", + photo_temporaire: "path/to/file", + password: await bcrypt.hash(password, 10), + token: "token", + id_role: 1, + ban_until: new Date("10-06-2003"), + }; + const fetchedUser = new User(mockUserRaw); + + mockUserRepository.getByIdent = jest.fn((ident) => { + return fetchedUser; + }); + + const res = await server.inject({ + method: "POST", + url: "/users/signin", + payload: { + email: "tesddesqt@gmaiL.com", + password: password, + }, + }); + + expect(res.statusCode).toBe(200); + }); + it("should respond code 401 bad password", async () => { + const password = "password"; + const mockUserRaw = { + id_utilisateur: "id", + pseudo: "pseudo", + email: "email", + alias: "alias", + bio: "bio", + photo: "path/to/file", + photo_temporaire: "path/to/file", + password: await bcrypt.hash(password, 10), + token: "token", + id_role: 1, + ban_until: new Date("10-06-2003"), + }; + const fetchedUser = new User(mockUserRaw); + + mockUserRepository.getByIdent = jest.fn((ident) => { + return fetchedUser; + }); + const res = await server.inject({ + method: "POST", + url: "/users/signin", + payload: { + email: "tesddesqt@gmaiL.com", + password: "aaaaadaaaaaa", + }, + }); + expect(res.statusCode).toBe(401); + }); + it("should respond code 400 user not found", async () => { + mockUserRepository.getByIdent = jest.fn((ident) => { + return null; + }); + const res = await server.inject({ + method: "POST", + url: "/users/signin", + payload: { + email: "fezfez", + password: "aaaaadaaaaaa", + }, + }); + expect(res.statusCode).toBe(401); + }); + it("should respond code 400 invalid email", async () => { + const res = await server.inject({ + method: "POST", + url: "/users/signin", + payload: { + email: "fezfez", + password: "a".repeat(31), + }, + }); + expect(res.statusCode).toBe(400); + }); + it("should respond code 400 invalid password", async () => { + const res = await server.inject({ + method: "POST", + url: "/users/signin", + payload: { + email: "a".repeat(41), + password: "dzadzaa", + }, + }); + expect(res.statusCode).toBe(400); + }); + }); + describe("/users/isUser", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + it("should return true", async () => { + mockUserRepository.getByEmailOrPseudo = jest.fn(() => { + return { id_utilisateur: 1 }; + }); + const res = await server.inject({ + method: "GET", + url: "/users/isUser?pseudo=test", + }); + expect(res.statusCode).toBe(200); + expect(res.result.isUser).toBe(true); + }); + it("should return false", async () => { + mockUserRepository.getByEmailOrPseudo = jest.fn(() => { + return null; + }); + const res = await server.inject({ + method: "GET", + url: "/users/isUser?pseudo=test", + }); + expect(res.statusCode).toBe(200); + expect(res.result.isUser).toBe(false); + }); + }); + describe("/users/getUserByConfirmToken", () => { + it("should return valid code 200", async () => { + mockUserRepository.getByConfirmToken = jest.fn(() => { + return { id_utilisateur: 1 }; + }); + const res = await server.inject({ + method: "GET", + url: "/users/getUserByConfirmToken?confirmToken=test", + }); + expect(res.statusCode).toBe(200); + }); + it("should return invalid code 403", async () => { + mockUserRepository.getByConfirmToken = jest.fn(() => { + return null; + }); + const res = await server.inject({ + method: "GET", + url: "/users/getUserByConfirmToken?confirmToken=test", + }); + expect(res.statusCode).toBe(403); + }); + }); + describe("/users/sendResetEmail", () => { + it("should return valid code 200", async () => { + mockMailRepository.send = jest.fn(() => {}); + mockUserRepository.getByEmailOrPseudo = jest.fn(() => { + return { reset_token: 1, confirmed: true }; + }); + mockUserRepository.updateUser = jest.fn(() => {}); + const res = await server.inject({ + method: "POST", + url: "/users/sendResetEmail", + payload: { + email: "chanon.mael@gmail.com", + }, + }); + expect(res.statusCode).toBe(200); + expect(mockMailRepository.send).toHaveBeenCalledTimes(1); + }); + it("should return valid code 200 even though the user is not confirmed", async () => { + mockMailRepository.send = jest.fn(() => {}); + mockUserRepository.getByEmailOrPseudo = jest.fn(() => { + return { reset_token: 1, confirmed: false }; + }); + mockUserRepository.updateUser = jest.fn(() => {}); + const res = await server.inject({ + method: "POST", + url: "/users/sendResetEmail", + payload: { + email: "chanon.mael@gmail.com", + }, + }); + expect(res.statusCode).toBe(200); + expect(mockMailRepository.send).toHaveBeenCalledTimes(0); + }); + it("should return valid code 200 even though the user doesn't exists", async () => { + mockMailRepository.send = jest.fn(() => {}); + mockUserRepository.getByEmailOrPseudo = jest.fn(() => { + return null; + }); + mockUserRepository.updateUser = jest.fn(() => {}); + const res = await server.inject({ + method: "POST", + url: "/users/sendResetEmail", + payload: { + email: "chanon.mael@gmail.com", + }, + }); + expect(res.statusCode).toBe(200); + expect(mockMailRepository.send).toHaveBeenCalledTimes(0); + }); + }); + describe("/users/resetPassword", () => { + it("should return valid code 200", async () => { + mockUserRepository.getByResetToken = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.updateUser = jest.fn(() => {}); + const res = await server.inject({ + method: "POST", + url: "/users/resetPassword", + payload: { + resetToken: "eztgergrehre", + password: "TestPassword", + }, + }); + expect(res.statusCode).toBe(200); + }); + it("should return valid code 400 error on token", async () => { + mockUserRepository.getByResetToken = jest.fn(() => null); + mockUserRepository.updateUser = jest.fn(() => {}); + const res = await server.inject({ + method: "POST", + url: "/users/resetPassword", + payload: { + resetToken: "eztgergrehre", + password: "TestPassword", + }, + }); + expect(res.statusCode).toBe(400); + }); + }); + describe("/users/follow", () => { + it("should return valid code 200", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => "something"); + mockSpotifyRepository.getSpotifyArtist = jest.fn(() => "something"); + mockFollowRepository.doesFollows = jest.fn(() => true); + mockFollowRepository.follow = jest.fn(() => {}); + mockFollowRepository.unfollow = jest.fn(() => {}); + const res = await server.inject({ + method: "POST", + url: "/users/follow", + payload: { + artistId: "eztgergrehre", + }, + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + expect(res.statusCode).toBe(200); + }); + it("should return error code 401", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => null); + + const res = await server.inject({ + method: "POST", + url: "/users/follow", + payload: { + artistId: "eztgergrehre", + }, + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + expect(res.statusCode).toBe(401); + }); + it("should return return invalid code 415", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => "something"); + mockSpotifyRepository.getSpotifyArtist = jest.fn(() => { + return { + error: { + status: 415, + message: "message", + }, + }; + }); + const res = await server.inject({ + method: "POST", + url: "/users/follow", + payload: { + artistId: "eztgergrehre", + }, + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + expect(res.statusCode).toBe(415); + }); + }); + describe("AuthWithSpotifyTest", () => { + const mockSpotifyCode = "code"; + const email = "some@mail"; + const display_name = "name"; + const access_token = "access_token"; + const refresh_token = "refresh_token"; + const images = [ + "https://i.ytimg.com/vi/uLHdmBf1lvs/hq720.jpg?sqp=-oaymwEXCNAFEJQDSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAmH-kUIb43CviOetK-ZjGl0AnSog", + ]; + beforeEach(() => { + mockUserRepository.updateUser = jest.fn(() => "ok"); + }); + it("should throw error 400", async () => { + mockSpotifyRepository.getToken = jest.fn(() => { + return { error: "some error" }; + }); + const res = await server.inject({ + method: "POST", + url: "/users/authWithSpotify", + payload: { + spotify_code: "eztgergrehre", + }, + }); + expect(res.statusCode).toBe(400); + }); + it("should throw error 403 1", async () => { + mockSpotifyRepository.getToken = jest.fn(() => { + return { + access_token, + refresh_token, + }; + }); + mockSpotifyRepository.getAccountData = jest.fn(() => { + return { email, display_name, images }; + }); + mockUserRepository.getByEmailOrPseudo = jest.fn(() => { + return { + confirmed: false, + }; + }); + const res = await server.inject({ + method: "POST", + url: "/users/authWithSpotify", + payload: { + spotify_code: "eztgergrehre", + callback: "callback", + }, + }); + expect(res.statusCode).toBe(403); + }); + it("should throw error 403 2", async () => { + mockSpotifyRepository.getToken = jest.fn(() => { + return { + access_token, + refresh_token, + }; + }); + mockSpotifyRepository.getAccountData = jest.fn(() => { + return { email, display_name, images }; + }); + mockUserRepository.getByEmailOrPseudo = jest.fn(() => { + return { + confirmed: true, + }; + }); + mockAccesTokenManager.generate = jest.fn(() => "expected_token"); + const res = await server.inject({ + method: "POST", + url: "/users/authWithSpotify", + payload: { + spotify_code: "eztgergrehre", + callback: "callback", + }, + }); + expect(res.statusCode).toBe(403); + }); + it("should return auth token", async () => { + mockSpotifyRepository.getToken = jest.fn(() => { + return { + access_token, + refresh_token, + }; + }); + mockSpotifyRepository.getAccountData = jest.fn(() => { + return { email, display_name, images }; + }); + mockUserRepository.getByEmailOrPseudo = jest.fn(() => { + return { + confirmed: true, + refresh_token: "someting", + }; + }); + mockAccesTokenManager.generate = jest.fn(() => "expected_token"); + const res = await server.inject({ + method: "POST", + url: "/users/authWithSpotify", + payload: { + spotify_code: "eztgergrehre", + callback: "callback", + }, + }); + expect(res.statusCode).toBe(200); + }); + }); + describe("/users/status", () => { + mockAccesTokenManager.decode = jest.fn((token) => { + return { value: 1 }; + }); + mockUserRepository.changePrivateStatus = jest.fn((id) => { + return new User({ + id_utilisateur: 1, + pseudo: "pseudo", + email: "test@test.fr", + password: "hjkklllllm", + id_role: 1, + }); + }); + it("should return valid code 200", async () => { + const res = await server.inject({ + method: "POST", + url: "/users/status", + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + expect(res.statusCode).toBe(200); + expect(mockUserRepository.changePrivateStatus).toHaveBeenCalledTimes(1); + }); + }); + + describe("/users/oeuvreFav", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + const { + albumRawOneArtist, + } = require("../unit/interfaces/serializers/fixtures/albumFixture.js"); + const { + rawTrackWithOneArtist, + } = require("../unit/interfaces/serializers/fixtures/albumTrackFixture.js"); + + // login erreur + it("should return error code 401", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => null); + + const res = await server.inject({ + method: "POST", + url: "/users/oeuvreFav", + payload: { + idOeuvre: "test", + }, + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + expect(res.statusCode).toBe(401); + }); + + // payload incorrect avec entity + it("should return error code 400", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => 1); + + const res = await server.inject({ + method: "POST", + url: "/users/oeuvreFav", + payload: { + idOeuvre: null, + }, + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + expect(res.statusCode).toBe(400); + }); + + // payload incorrect avec entity + it("should return error code 400", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => 1); + + const res = await server.inject({ + method: "POST", + url: "/users/oeuvreFav", + payload: { + idOeuvre: 123, // doit être un string + }, + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + expect(res.statusCode).toBe(400); + }); + // aucune oeuvre de trouve + it("should return error code 404", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => 1); + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + return { error: { status: 404, message: "invalid id" } }; + }); + + const res = await server.inject({ + method: "POST", + url: "/users/oeuvreFav", + payload: { + idOeuvre: "test1323", + type: "track", + }, + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + expect(res.statusCode).toBe(404); + expect(mockSpotifyRepository.getOeuvre).toHaveBeenCalledTimes(1); + }); + + // plus de 3 oeuvres favorites avec album + it("should return error code 403 1", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => 1); + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + return Promise.resolve(albumRawOneArtist); + }); + mockOeuvreFavRepository.oeuvreFavExists = jest.fn( + (id_utilisateur, idOeuvre) => false + ); + mockOeuvreFavRepository.ajoutPossible = jest.fn( + (id_utilisateur, idOeuvre) => false + ); + + const res = await server.inject({ + method: "POST", + url: "/users/oeuvreFav", + payload: { + idOeuvre: "test123", // doit être un string + type: "album", + }, + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + + expect(mockOeuvreFavRepository.ajoutPossible).toHaveBeenCalledTimes(1); + expect(res.statusCode).toBe(403); + }); + + // plus de 3 oeuvres favorites avec track + it("should return error code 403 2", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => 1); + + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + return Promise.resolve(rawTrackWithOneArtist); + }); + mockOeuvreFavRepository.oeuvreFavExists = jest.fn( + (id_utilisateur, idOeuvre) => false + ); + mockOeuvreFavRepository.ajoutPossible = jest.fn( + (id_utilisateur, idOeuvre) => false + ); + + const res = await server.inject({ + method: "POST", + url: "/users/oeuvreFav", + payload: { + idOeuvre: "test123", + type: "track", + }, + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + + // expect(mockSpotifyRepository.getSpotifyTracks).toHaveBeenCalledTimes(1); + // expect(mockOeuvreFavRepository.ajoutPossible).toHaveBeenCalledTimes(1); + expect(res.statusCode).toBe(403); + }); + + // ajout réussie avec album + it("should return code 200 1", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => 1); + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + return Promise.resolve(albumRawOneArtist); + }); + mockSpotifyRepository.getSpotifyTracks = jest.fn(); + mockOeuvreFavRepository.oeuvreFavExists = jest.fn( + (id_utilisateur, idOeuvre) => false + ); + mockOeuvreFavRepository.ajoutPossible = jest.fn( + (id_utilisateur, idOeuvre) => true + ); + mockOeuvreFavRepository.addOeuvrefav = jest.fn(); + + const res = await server.inject({ + method: "POST", + url: "/users/oeuvreFav", + payload: { + idOeuvre: "test123", + type: "track", + }, + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + + expect(mockSpotifyRepository.getSpotifyTracks).not.toHaveBeenCalled(); + + expect(mockOeuvreFavRepository.ajoutPossible).toHaveBeenCalledTimes(1); + expect(mockOeuvreFavRepository.addOeuvrefav).toHaveBeenCalledTimes(1); + expect(res.statusCode).toBe(200); + }); + + // ajout réussie avec track + it("should return code 200 2", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => 1); + + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + return Promise.resolve(rawTrackWithOneArtist); + }); + mockOeuvreFavRepository.oeuvreFavExists = jest.fn( + (id_utilisateur, idOeuvre) => false + ); + mockOeuvreFavRepository.ajoutPossible = jest.fn( + (id_utilisateur, idOeuvre) => true + ); + mockOeuvreFavRepository.addOeuvrefav = jest.fn(); + + const res = await server.inject({ + method: "POST", + url: "/users/oeuvreFav", + payload: { + idOeuvre: "test123", + type: "track", + }, + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + + expect(mockOeuvreFavRepository.ajoutPossible).toHaveBeenCalledTimes(1); + expect(mockOeuvreFavRepository.addOeuvrefav).toHaveBeenCalledTimes(1); + expect(res.statusCode).toBe(200); + }); + + // supression reussite avec album + it("should return code 200 3 ", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => 1); + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + return Promise.resolve(albumRawOneArtist); + }); + + mockSpotifyRepository.getSpotifyTracks = jest.fn(); + mockOeuvreFavRepository.oeuvreFavExists = jest.fn( + (id_utilisateur, idOeuvre) => true + ); + mockOeuvreFavRepository.ajoutPossible = jest.fn(); + mockOeuvreFavRepository.addOeuvrefav = jest.fn(); + mockOeuvreFavRepository.deleteOeuvrefav = jest.fn(); + + const res = await server.inject({ + method: "POST", + url: "/users/oeuvreFav", + payload: { + idOeuvre: "test123", + type: "track", + }, + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + + expect(mockSpotifyRepository.getSpotifyTracks).not.toHaveBeenCalled(); + + expect(mockOeuvreFavRepository.ajoutPossible).not.toHaveBeenCalled(); + expect(res.statusCode).toBe(200); + }); + + // supression reussite avec track + it("should return code 200 4", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => 1); + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + return Promise.resolve(rawTrackWithOneArtist); + }); + mockOeuvreFavRepository.oeuvreFavExists = jest.fn( + (id_utilisateur, idOeuvre) => true + ); + mockOeuvreFavRepository.ajoutPossible = jest.fn(); + mockOeuvreFavRepository.addOeuvrefav = jest.fn(); + mockOeuvreFavRepository.deleteOeuvrefav = jest.fn(); + + const res = await server.inject({ + method: "POST", + url: "/users/oeuvreFav", + payload: { + idOeuvre: "test123", + type: "track", + }, + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + + expect(mockOeuvreFavRepository.ajoutPossible).not.toHaveBeenCalled(); + expect(res.statusCode).toBe(200); + }); + }); + + describe("/users/getOeuvresFav", () => { + afterEach(() => { + jest.clearAllMocks(); + }); + it("should return error code 401", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => null); + + const res = await server.inject({ + method: "GET", + url: "/users/getOeuvresFav", + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + expect(res.statusCode).toBe(401); + }); + }); + it("should return code 200", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => 1); + mockOeuvreFavRepository.getOeuvresFav = jest.fn((id) => { + return [1, 2, 3]; + }); + const res = await server.inject({ + method: "GET", + url: "/users/getOeuvresFav", + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + expect(res.statusCode).toBe(200); + }); + + it("should return code 200 ", async () => { + mockAccesTokenManager.decode = jest.fn(() => { + return { id: 1 }; + }); + mockUserRepository.getByUser = jest.fn(() => 1); + mockOeuvreFavRepository.getOeuvresFav = jest.fn((id) => { + return []; + }); + const res = await server.inject({ + method: "GET", + url: "/users/getOeuvresFav", + headers: { + Authorization: `Bearer ${mockToken}`, + }, + }); + expect(res.statusCode).toBe(200); + }); }); diff --git a/test/unit/application/usecase/artist/getArtist.test.js b/test/unit/application/usecase/artist/getArtist.test.js new file mode 100644 index 0000000..14a86aa --- /dev/null +++ b/test/unit/application/usecase/artist/getArtist.test.js @@ -0,0 +1,126 @@ +const getArtist = require("./../../../../../lib/application/use_cases/artist/getArtist") +const catchError = require("../utils/catchError") +const { + mockUser, + mockArtist, + mockUserPrivate, + mockAlbumRaw, + mockLikedReview, + mockCommentedReview, + mockOeuvreReviewSpotify, + expectedArtist +} = require('./getArtistFixture') +describe("getArtist Test", ()=>{ + const mockReviewRepository = {} + const mockAccesTokenManager = {} + const mockUserRepository = {} + const mockSpotifyRepository = {} + const mockFollowRepository = {} + const serviceLocator = { + reviewRepository: mockReviewRepository, + accessTokenManager: mockAccesTokenManager, + userRepository: mockUserRepository, + spotifyRepository: mockSpotifyRepository, + followRepository: mockFollowRepository, + } + describe("valid cases", ()=>{ + it("should return valid artist", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => mockUser) + mockFollowRepository.getFollowersCount = jest.fn((artistId) => 100) + mockFollowRepository.getFriendsFollowing = jest.fn((artistId,userId,limit) => [mockUserPrivate]) + mockFollowRepository.getFriendsFollowingCount = jest.fn((artistId,userId) => 1) + mockSpotifyRepository.getSpotifyArtist = jest.fn((artistId) => mockArtist) + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((artistId,filter,limit) => mockAlbumRaw) + mockReviewRepository.getOeuvreRating = jest.fn((id) => 1) + mockReviewRepository.getReviewCount = jest.fn((id) => 2) + mockReviewRepository.getOeuvreReviews = jest.fn().mockReturnValueOnce([mockLikedReview]).mockReturnValueOnce([mockCommentedReview]) + mockreviewRepository.doesUserLikes = jest.fn().mockReturnValue(true) + mockFollowRepository.doesFollows = jest.fn().mockReturnValue(true) + mockSpotifyRepository.getOeuvre = jest.fn().mockReturnValue(mockOeuvreReviewSpotify) + const result = await getArtist(1,'token', serviceLocator) + result.albums[0].popularity = 64 + expect(result).toEqual(expectedArtist) + }) + }) + describe("invalid cases", ()=>{ + it("should throw error bad auth token", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => null) + const error = await catchError(async ()=>{ + await getArtist(1,'token', serviceLocator) + }) + expect(error.code).toBe(401) + }) + it("should throw error artist not found", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => mockUser) + mockSpotifyRepository.getSpotifyArtist = jest.fn((artistId) => { + return { + error: { + status: 400, + message: "message", + } + } + }) + const error = await catchError(async ()=>{ + await getArtist(1,'token', serviceLocator) + }) + expect(error.code).toBe(400) + }) + + it("should throw error oeuvre not found 1", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => mockUser) + mockFollowRepository.getFollowersCount = jest.fn((artistId) => 100) + mockFollowRepository.getFriendsFollowing = jest.fn((artistId,userId,limit) => [mockUserPrivate]) + mockFollowRepository.getFriendsFollowingCount = jest.fn((artistId,userId) => 1) + mockSpotifyRepository.getSpotifyArtist = jest.fn((artistId) => mockArtist) + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((artistId,filter,limit) => mockAlbumRaw) + mockReviewRepository.getOeuvreRating = jest.fn((id) => 1) + mockReviewRepository.getReviewCount = jest.fn((id) => 2) + mockReviewRepository.getOeuvreReviews = jest.fn().mockReturnValueOnce([mockLikedReview]).mockReturnValueOnce([mockCommentedReview]) + mockreviewRepository.doesUserLikes = jest.fn().mockReturnValue(true) + mockFollowRepository.doesFollows = jest.fn().mockReturnValue(true) + mockSpotifyRepository.getOeuvre = jest.fn().mockReturnValue({ + error: { + status: 400, + message: "message", + } + }) + + const error = await catchError(async ()=>{ + await getArtist(1,'token', serviceLocator) + }) + expect(error.code).toBe(400) + }) + + it("should throw error oeuvre not found 2", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => mockUser) + mockFollowRepository.getFollowersCount = jest.fn((artistId) => 100) + mockFollowRepository.getFriendsFollowing = jest.fn((artistId,userId,limit) => [mockUserPrivate]) + mockFollowRepository.getFriendsFollowingCount = jest.fn((artistId,userId) => 1) + mockSpotifyRepository.getSpotifyArtist = jest.fn((artistId) => mockArtist) + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((artistId,filter,limit) => mockAlbumRaw) + mockReviewRepository.getOeuvreRating = jest.fn((id) => 1) + mockReviewRepository.getReviewCount = jest.fn((id) => 2) + mockReviewRepository.getOeuvreReviews = jest.fn().mockReturnValueOnce([mockLikedReview]).mockReturnValueOnce([mockCommentedReview]) + mockreviewRepository.doesUserLikes = jest.fn().mockReturnValue(true) + mockFollowRepository.doesFollows = jest.fn().mockReturnValue(true) + mockSpotifyRepository.getOeuvre = jest.fn().mockReturnValueOnce(mockOeuvreReviewSpotify).mockReturnValue({ + error: { + status: 400, + message: "message", + } + }) + + const error = await catchError(async ()=>{ + await getArtist(1,'token', serviceLocator) + }) + expect(error.code).toBe(400) + }) + + }) + +}) \ No newline at end of file diff --git a/test/unit/application/usecase/artist/getArtistFixture.js b/test/unit/application/usecase/artist/getArtistFixture.js new file mode 100644 index 0000000..4f2c7b2 --- /dev/null +++ b/test/unit/application/usecase/artist/getArtistFixture.js @@ -0,0 +1,405 @@ +const mockArtist = { + external_urls: { + spotify: "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + }, + genres: ["french hip hop", "old school rap francais", "rap conscient"], + id: "4FpJcNgOvIpSBeJgRg3OfN", + images: [ + { + height: 640, + url: "https://i.scdn.co/image/ab6761610000e5eb32086a424e6f1e499e347cde", + width: 640, + }, + ], + name: "Orelsan", + popularity: 64, + type: "artist", +}; +const mockAlbumRaw = { + href: "https://api.spotify.com/v1/artists/4FpJcNgOvIpSBeJgRg3OfN/albums?include_groups=album,single&offset=0&limit=10", + items: [ + { + album_group: "album", + album_type: "album", + artists: [ + { + external_urls: { + spotify: "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + }, + id: "4FpJcNgOvIpSBeJgRg3OfN", + name: "Orelsan", + type: "artist", + }, + ], + external_urls: { + spotify: "https://open.spotify.com/album/68YP0pEgwhnfRqQAzu71gP", + }, + id: "68YP0pEgwhnfRqQAzu71gP", + images: [ + { + height: 640, + url: "https://i.scdn.co/image/ab67616d0000b2732724364cd86bb791926b6cc8", + width: 640, + }, + ], + name: "Civilisation Edition Ultime", + release_date: "2022-10-28", + total_tracks: 25, + type: "album", + }, + ], +}; +const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false, +}; + +const mockUserPrivate = { + pseudo: "John Doe2", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + id_utilisateur: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: true, +}; +const actualDate = new Date(); + +const mockLikedReview = { + id_review: 25, + id_oeuvre: "68YP0pEgwhnfRqQAzu71gP", + countlikes: 32, + countComment: 0, + description: + "Innovation sonore à son apogée, repoussant les limites de l'expérimentation musicale. #AvantGarde #SoundExploration", + note: 5, + createdAt: "2024-01-03T00:00:00.000Z", + updated_at: "2024-01-03T00:00:00.000Z", + type: "album", + utilisateur: { + id_utilisateur: 54, + pseudo: "Constance", + email: "Constance.Constance@gmail.com", + alias: "Constance", + photo: "https://ozgrozer.github.io/100k-faces/0/6/006026.jpg", + photo_temporaire: null, + token: null, + refresh_token: null, + reset_token: null, + password: "$2b$10$feqJS.qjWmoYovS805Mmhu.7VjOncR68em0Bu4LSxS/4tDSP8HWK2", + id_role: 1, + ban_until: null, + confirmed: true, + confirm_token: null, + auth_with_spotify: false, + is_private: true, + type: "user", + }, +}; + +const mockCommentedReview = { + id_review: 25, + id_oeuvre: "68YP0pEgwhnfRqQAzu71gP", + countlikes: 32, + countComment: 0, + description: + "Innovation sonore à son apogée, repoussant les limites de l'expérimentation musicale. #AvantGarde #SoundExploration", + note: 5, + createdAt: "2024-01-03T00:00:00.000Z", + updated_at: "2024-01-03T00:00:00.000Z", + type: "album", + utilisateur: { + id_utilisateur: 54, + pseudo: "Constance", + email: "Constance.Constance@gmail.com", + alias: "Constance", + photo: "https://ozgrozer.github.io/100k-faces/0/6/006026.jpg", + photo_temporaire: null, + token: null, + refresh_token: null, + reset_token: null, + password: "$2b$10$feqJS.qjWmoYovS805Mmhu.7VjOncR68em0Bu4LSxS/4tDSP8HWK2", + id_role: 1, + ban_until: null, + confirmed: true, + confirm_token: null, + auth_with_spotify: false, + is_private: true, + type: "user", + }, +}; + +const mockOeuvreReviewSpotify = { + album_type: "album", + artists: [ + { + external_urls: { + spotify: "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + }, + id: "4FpJcNgOvIpSBeJgRg3OfN", + name: "Orelsan", + type: "artist", + }, + ], + + external_urls: { + spotify: "https://open.spotify.com/album/68YP0pEgwhnfRqQAzu71gP", + }, + genres: [], + id: "68YP0pEgwhnfRqQAzu71gP", + images: [ + { + height: 640, + url: "https://i.scdn.co/image/ab67616d0000b2732724364cd86bb791926b6cc8", + width: 640, + }, + ], + name: "Civilisation Edition Ultime", + popularity: 60, + release_date: "2022-10-28", + total_tracks: 1, + tracks: { + href: "https://api.spotify.com/v1/albums/68YP0pEgwhnfRqQAzu71gP/tracks?offset=0&limit=50", + items: [ + { + artists: [ + { + external_urls: { + spotify: "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + }, + href: "https://api.spotify.com/v1/artists/4FpJcNgOvIpSBeJgRg3OfN", + id: "4FpJcNgOvIpSBeJgRg3OfN", + name: "Orelsan", + type: "artist", + uri: "spotify:artist:4FpJcNgOvIpSBeJgRg3OfN", + }, + ], + + disc_number: 1, + duration_ms: 161732, + external_urls: { + spotify: "https://open.spotify.com/track/7b3YQboXo3kau9YVUyx3r4", + }, + id: "7b3YQboXo3kau9YVUyx3r4", + name: "CP_001_ Intro Civilisation Perdue", + preview_url: + "https://p.scdn.co/mp3-preview/b7f3ef4f7ee54bcba700a821952539b7e4e21d2f?cid=24b7c8fbb7d146edbce14e1743cea479", + track_number: 1, + type: "track", + }, + ], + total: 25, + }, + type: "album", +}; + +const expectedArtist = { + artist: { + id: "4FpJcNgOvIpSBeJgRg3OfN", + name: "Orelsan", + image: "https://i.scdn.co/image/ab6761610000e5eb32086a424e6f1e499e347cde", + popularity: 64, + follower_count: 100, + genres: ["french hip hop", "old school rap francais", "rap conscient"], + spotify_url: "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + type: "artist", + }, + albums: [ + { + id: "68YP0pEgwhnfRqQAzu71gP", + name: "Civilisation Edition Ultime", + popularity: 64, + rating: 1, + reviewCount: 2, + release_date: "2022-10-28", + total_tracks: 25, + image: "https://i.scdn.co/image/ab67616d0000b2732724364cd86bb791926b6cc8", + spotify_url: "https://open.spotify.com/album/68YP0pEgwhnfRqQAzu71gP", + artists: [ + { + id: "4FpJcNgOvIpSBeJgRg3OfN", + name: "Orelsan", + image: null, + spotify_url: "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + type: "artist", + }, + ], + type: "album", + }, + ], + friends_followers: { + count: 1, + users: [ + { + id_utilisateur: 1, + pseudo: "John Doe2", + email: "testemail@gmail", + alias: "John", + photo: null, + photo_temporaire: null, + id_role: 1, + ban_until: null, + is_private: true, + type: "user", + }, + ], + }, + reviewsByLike: [ + { + id_review: 25, + description: + "Innovation sonore à son apogée, repoussant les limites de l'expérimentation musicale. #AvantGarde #SoundExploration", + countlikes: 32, + countComment: 0, + doesUserLike: true, + createdAt: "2024-01-03T00:00:00.000Z", + note: 5, + oeuvre: { + id: "68YP0pEgwhnfRqQAzu71gP", + name: "Civilisation Edition Ultime", + popularity: 60, + release_date: "2022-10-28", + total_tracks: 1, + image: + "https://i.scdn.co/image/ab67616d0000b2732724364cd86bb791926b6cc8", + spotify_url: "https://open.spotify.com/album/68YP0pEgwhnfRqQAzu71gP", + artists: [ + { + id: "4FpJcNgOvIpSBeJgRg3OfN", + name: "Orelsan", + image: null, + spotify_url: + "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + type: "artist", + }, + ], + tracks: [ + { + id: "7b3YQboXo3kau9YVUyx3r4", + name: "CP_001_ Intro Civilisation Perdue", + artists: [ + { + id: "4FpJcNgOvIpSBeJgRg3OfN", + name: "Orelsan", + image: null, + spotify_url: + "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + type: "artist", + }, + ], + duration_ms: 161732, + spotify_url: + "https://open.spotify.com/track/7b3YQboXo3kau9YVUyx3r4", + type: "track", + }, + ], + genres: [], + type: "album", + }, + utilisateur: { + id_utilisateur: 54, + pseudo: "Constance", + auth_with_spotify: false, + email: "Constance.Constance@gmail.com", + alias: "Constance", + photo: "https://ozgrozer.github.io/100k-faces/0/6/006026.jpg", + photo_temporaire: null, + id_role: 1, + ban_until: null, + is_private: true, + type: "user", + }, + type: "album", + }, + ], + reviewsByTime: [ + { + id_review: 25, + description: + "Innovation sonore à son apogée, repoussant les limites de l'expérimentation musicale. #AvantGarde #SoundExploration", + countlikes: 32, + createdAt: "2024-01-03T00:00:00.000Z", + countComment: 0, + doesUserLike: true, + note: 5, + oeuvre: { + id: "68YP0pEgwhnfRqQAzu71gP", + name: "Civilisation Edition Ultime", + popularity: 60, + release_date: "2022-10-28", + total_tracks: 1, + image: + "https://i.scdn.co/image/ab67616d0000b2732724364cd86bb791926b6cc8", + spotify_url: "https://open.spotify.com/album/68YP0pEgwhnfRqQAzu71gP", + artists: [ + { + id: "4FpJcNgOvIpSBeJgRg3OfN", + name: "Orelsan", + image: null, + spotify_url: + "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + type: "artist", + }, + ], + tracks: [ + { + id: "7b3YQboXo3kau9YVUyx3r4", + name: "CP_001_ Intro Civilisation Perdue", + artists: [ + { + id: "4FpJcNgOvIpSBeJgRg3OfN", + name: "Orelsan", + image: null, + spotify_url: + "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + type: "artist", + }, + ], + duration_ms: 161732, + spotify_url: + "https://open.spotify.com/track/7b3YQboXo3kau9YVUyx3r4", + type: "track", + }, + ], + genres: [], + type: "album", + }, + utilisateur: { + id_utilisateur: 54, + pseudo: "Constance", + auth_with_spotify: false, + email: "Constance.Constance@gmail.com", + alias: "Constance", + photo: "https://ozgrozer.github.io/100k-faces/0/6/006026.jpg", + photo_temporaire: null, + id_role: 1, + ban_until: null, + is_private: true, + type: "user", + }, + type: "album", + }, + ], + doesUserFollow: true, +}; +module.exports = { + mockUser, + mockArtist, + mockUserPrivate, + mockAlbumRaw, + mockLikedReview, + mockCommentedReview, + mockOeuvreReviewSpotify, + expectedArtist, +}; diff --git a/test/unit/application/usecase/fixtures/fetchArtist.js b/test/unit/application/usecase/fixtures/fetchArtist.js new file mode 100644 index 0000000..e5a2891 --- /dev/null +++ b/test/unit/application/usecase/fixtures/fetchArtist.js @@ -0,0 +1,12 @@ +const { + artistFixture, + expectedFixture +} = require("../../../interfaces/serializers/fixtures/artistFixture") + +artistFixture.popularity = 2 +expectedFixture.popularity = 2 + +module.exports = { + artistFixture, + expectedFixture, +} \ No newline at end of file diff --git a/test/unit/application/usecase/fixtures/getOeuvreFixture.js b/test/unit/application/usecase/fixtures/getOeuvreFixture.js new file mode 100644 index 0000000..bbda437 --- /dev/null +++ b/test/unit/application/usecase/fixtures/getOeuvreFixture.js @@ -0,0 +1,394 @@ +const mockArtist = { + external_urls: { spotify: 'https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN' }, + genres: [ 'french hip hop', 'old school rap francais', 'rap conscient' ], + id: '4FpJcNgOvIpSBeJgRg3OfN', + images: [ + { + height: 640, + url: 'https://i.scdn.co/image/ab6761610000e5eb32086a424e6f1e499e347cde', + width: 640 + }, + ], + name: 'Orelsan', + popularity: 64, + type: 'artist', + } +const mockAlbumRaw = { + "href": "https://api.spotify.com/v1/artists/4FpJcNgOvIpSBeJgRg3OfN/albums?include_groups=album,single&offset=0&limit=10", + "items": [ + { + "album_group": "album", + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN" + }, + "id": "4FpJcNgOvIpSBeJgRg3OfN", + "name": "Orelsan", + "type": "artist" + } + ], + "external_urls": { + "spotify": "https://open.spotify.com/album/68YP0pEgwhnfRqQAzu71gP" + }, + "id": "68YP0pEgwhnfRqQAzu71gP", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/ab67616d0000b2732724364cd86bb791926b6cc8", + "width": 640 + } + ], + "name": "Civilisation Edition Ultime", + "release_date": "2022-10-28", + "total_tracks": 25, + "type": "album" + } + ] +} +const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} + +const mockUserPrivate = { + pseudo: "John Doe2", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + id_utilisateur: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: true +} +const actualDate = new Date() + + +const mockLikedReview = { + "id_review": 25, + "id_oeuvre": "68YP0pEgwhnfRqQAzu71gP", + "countlikes": 32, + "countComment": 0, + "description": "Innovation sonore à son apogée, repoussant les limites de l'expérimentation musicale. #AvantGarde #SoundExploration", + "note": 5, + "createdAt": "2024-01-03T00:00:00.000Z", + "updated_at": "2024-01-03T00:00:00.000Z", + "type": "album", + "utilisateur": { + "id_utilisateur": 54, + "pseudo": "Constance", + "email": "Constance.Constance@gmail.com", + "alias": "Constance", + "photo": "https://ozgrozer.github.io/100k-faces/0/6/006026.jpg", + "photo_temporaire": null, + "token": null, + "refresh_token": null, + "reset_token": null, + "password": "$2b$10$feqJS.qjWmoYovS805Mmhu.7VjOncR68em0Bu4LSxS/4tDSP8HWK2", + "id_role": 1, + "ban_until": null, + "confirmed": true, + "confirm_token": null, + "auth_with_spotify": false, + "is_private": true, + "type": "user" + } +} + +const mockCommentedReview = { + "id_review": 25, + "id_oeuvre": "68YP0pEgwhnfRqQAzu71gP", + "countlikes": 32, + "countComment": 0, + "description": "Innovation sonore à son apogée, repoussant les limites de l'expérimentation musicale. #AvantGarde #SoundExploration", + "note": 5, + "createdAt": "2024-01-03T00:00:00.000Z", + "updated_at": "2024-01-03T00:00:00.000Z", + "type": "album", + "utilisateur": { + "id_utilisateur": 54, + "pseudo": "Constance", + "email": "Constance.Constance@gmail.com", + "alias": "Constance", + "photo": "https://ozgrozer.github.io/100k-faces/0/6/006026.jpg", + "photo_temporaire": null, + "token": null, + "refresh_token": null, + "reset_token": null, + "password": "$2b$10$feqJS.qjWmoYovS805Mmhu.7VjOncR68em0Bu4LSxS/4tDSP8HWK2", + "id_role": 1, + "ban_until": null, + "confirmed": true, + "confirm_token": null, + "auth_with_spotify": false, + "is_private": true, + "type": "user" + } +} + +const mockOeuvreReviewSpotify = { + "album_type": "album", + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN" + }, + "id": "4FpJcNgOvIpSBeJgRg3OfN", + "name": "Orelsan", + "type": "artist" + } + ], + + "external_urls": { + "spotify": "https://open.spotify.com/album/68YP0pEgwhnfRqQAzu71gP" + }, + "genres": [], + "id": "68YP0pEgwhnfRqQAzu71gP", + "images": [ + { + "height": 640, + "url": "https://i.scdn.co/image/ab67616d0000b2732724364cd86bb791926b6cc8", + "width": 640 + } + ], + "name": "Civilisation Edition Ultime", + "popularity": 60, + "release_date": "2022-10-28", + "total_tracks": 1, + "tracks": { + "href": "https://api.spotify.com/v1/albums/68YP0pEgwhnfRqQAzu71gP/tracks?offset=0&limit=50", + "items": [ + { + "artists": [ + { + "external_urls": { + "spotify": "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN" + }, + "href": "https://api.spotify.com/v1/artists/4FpJcNgOvIpSBeJgRg3OfN", + "id": "4FpJcNgOvIpSBeJgRg3OfN", + "name": "Orelsan", + "type": "artist", + "uri": "spotify:artist:4FpJcNgOvIpSBeJgRg3OfN" + } + ], + + "disc_number": 1, + "duration_ms": 161732, + "external_urls": { + "spotify": "https://open.spotify.com/track/7b3YQboXo3kau9YVUyx3r4" + }, + "id": "7b3YQboXo3kau9YVUyx3r4", + "name": "CP_001_ Intro Civilisation Perdue", + "preview_url": "https://p.scdn.co/mp3-preview/b7f3ef4f7ee54bcba700a821952539b7e4e21d2f?cid=24b7c8fbb7d146edbce14e1743cea479", + "track_number": 1, + "type": "track" + } + ], + "total": 25 + }, + "type": "album" +} + + +const expectedArtist = { + "artist": { + "id": "4FpJcNgOvIpSBeJgRg3OfN", + "name": "Orelsan", + "image": "https://i.scdn.co/image/ab6761610000e5eb32086a424e6f1e499e347cde", + "popularity": 64, + "follower_count": 100, + "genres": [ + "french hip hop", + "old school rap francais", + "rap conscient" + ], + "spotify_url": "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + "type": "artist" + }, + "albums": [ + { + "id": "68YP0pEgwhnfRqQAzu71gP", + "name": "Civilisation Edition Ultime", + "popularity": 64, + "rating": 1, + "reviewCount": 2, + "release_date": "2022-10-28", + "total_tracks": 25, + "image": "https://i.scdn.co/image/ab67616d0000b2732724364cd86bb791926b6cc8", + "spotify_url": "https://open.spotify.com/album/68YP0pEgwhnfRqQAzu71gP", + "artists": [ + { + "id": "4FpJcNgOvIpSBeJgRg3OfN", + "name": "Orelsan", + "image": null, + "spotify_url": "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + "type": "artist" + } + ], + "type": "album" + } + ], + "friends_followers": { + "count": 1, + "users": [ + { + "id_utilisateur": 1, + "pseudo": "John Doe2", + "email": "testemail@gmail", + "alias": "John", + "photo": null, + "photo_temporaire": null, + "id_role": 1, + "ban_until": null, + "is_private": true, + "type": "user" + } + ] + }, + "reviewsByLike": [ + { + "id_review": 25, + "description": "Innovation sonore à son apogée, repoussant les limites de l'expérimentation musicale. #AvantGarde #SoundExploration", + "countlikes": 32, + "countComment": 0, + "doesUserLike": true, + "createdAt": "2024-01-03T00:00:00.000Z", + "note": 5, + "oeuvre": { + "id": "68YP0pEgwhnfRqQAzu71gP", + "name": "Civilisation Edition Ultime", + "popularity": 60, + "release_date": "2022-10-28", + "total_tracks": 1, + "image": "https://i.scdn.co/image/ab67616d0000b2732724364cd86bb791926b6cc8", + "spotify_url": "https://open.spotify.com/album/68YP0pEgwhnfRqQAzu71gP", + "artists": [ + { + "id": "4FpJcNgOvIpSBeJgRg3OfN", + "name": "Orelsan", + "image": null, + "spotify_url": "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + "type": "artist" + } + ], + "tracks": [ + { + "id": "7b3YQboXo3kau9YVUyx3r4", + "name": "CP_001_ Intro Civilisation Perdue", + "artists": [ + { + "id": "4FpJcNgOvIpSBeJgRg3OfN", + "name": "Orelsan", + "image": null, + "spotify_url": "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + "type": "artist" + } + ], + "duration_ms": 161732, + "spotify_url": "https://open.spotify.com/track/7b3YQboXo3kau9YVUyx3r4", + "type": "track" + } + ], + "genres": [], + "type": "album" + }, + "utilisateur": { + "id_utilisateur": 54, + "pseudo": "Constance", + "email": "Constance.Constance@gmail.com", + "alias": "Constance", + "photo": "https://ozgrozer.github.io/100k-faces/0/6/006026.jpg", + "photo_temporaire": null, + "id_role": 1, + "ban_until": null, + "is_private": true, + "type": "user" + }, + "type": "album" + } + ], + "reviewsByTime": [ + { + "id_review": 25, + "description": "Innovation sonore à son apogée, repoussant les limites de l'expérimentation musicale. #AvantGarde #SoundExploration", + "countlikes": 32, + "createdAt": "2024-01-03T00:00:00.000Z", + "countComment": 0, + "doesUserLike": true, + "note": 5, + "oeuvre": { + "id": "68YP0pEgwhnfRqQAzu71gP", + "name": "Civilisation Edition Ultime", + "popularity": 60, + "release_date": "2022-10-28", + "total_tracks": 1, + "image": "https://i.scdn.co/image/ab67616d0000b2732724364cd86bb791926b6cc8", + "spotify_url": "https://open.spotify.com/album/68YP0pEgwhnfRqQAzu71gP", + "artists": [ + { + "id": "4FpJcNgOvIpSBeJgRg3OfN", + "name": "Orelsan", + "image": null, + "spotify_url": "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + "type": "artist" + } + ], + "tracks": [ + { + "id": "7b3YQboXo3kau9YVUyx3r4", + "name": "CP_001_ Intro Civilisation Perdue", + "artists": [ + { + "id": "4FpJcNgOvIpSBeJgRg3OfN", + "name": "Orelsan", + "image": null, + "spotify_url": "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN", + "type": "artist" + } + ], + "duration_ms": 161732, + "spotify_url": "https://open.spotify.com/track/7b3YQboXo3kau9YVUyx3r4", + "type": "track" + } + ], + "genres": [], + "type": "album" + }, + "utilisateur": { + "id_utilisateur": 54, + "pseudo": "Constance", + "email": "Constance.Constance@gmail.com", + "alias": "Constance", + "photo": "https://ozgrozer.github.io/100k-faces/0/6/006026.jpg", + "photo_temporaire": null, + "id_role": 1, + "ban_until": null, + "is_private": true, + "type": "user" + }, + "type": "album" + } + ], + "doesUserFollow": true +} +module.exports = { + mockUser, + mockArtist, + mockUserPrivate, + mockAlbumRaw, + mockLikedReview, + mockCommentedReview, + mockOeuvreReviewSpotify, + expectedArtist +} \ No newline at end of file diff --git a/test/unit/application/usecase/fixtures/searchFixture.js b/test/unit/application/usecase/fixtures/searchFixture.js index 049588e..4e0a72f 100644 --- a/test/unit/application/usecase/fixtures/searchFixture.js +++ b/test/unit/application/usecase/fixtures/searchFixture.js @@ -11,6 +11,7 @@ const { rawTrackWithOneArtistOneAlbum, expectedRawTrackWithOneArtistOneAlbum } = require("../../../interfaces/serializers/fixtures/trackFixture") +const {tracks} = require("../../../../../lib/domain/model/Album"); albumRawOneArtist.popularity = 3 artistFixture.popularity = 2 @@ -19,33 +20,84 @@ rawTrackWithOneArtistOneAlbum.popularity = 1 expectedAlbumOneArtist.popularity = 3 expectedFixture.popularity = 2 expectedRawTrackWithOneArtistOneAlbum.popularity = 1 -const mockUser = new User( - 1, - 'testPeudo', - 'testEmail@gmail.com', - 'test_alias', - 'testbio', - 'passwordtest', - 'spotifyToken', - 2, - 1 -) + const SpotifyRepositoryFixture = { tracks : { items : [rawTrackWithOneArtistOneAlbum]}, albums : {items : [albumRawOneArtist]}, artists : {items : [artistFixture]} } const expectedSearchResult = [ - expectedAlbumOneArtist, - expectedFixture, - expectedRawTrackWithOneArtistOneAlbum, + { + id: "4FpJcNgOvIpSBeJgRg3OfN", + imageURL: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", + subtitle: "", + title: "Orelsan", + type: "artist" + }, + { + id: "17UiqpQyl8T8vVxz2Towjy", + imageURL: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", + subtitle: "Orelsan", + title: "Perdu D'Avance", + type: "album" + }, + + { + id: "test_id", + imageURL: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", + subtitle: "Orelsan", + title: "test_name", + type: "track" + }, ] const expectedSearchResultWithUsers = [ - expectedAlbumOneArtist, - expectedFixture, - expectedRawTrackWithOneArtistOneAlbum, - mockUser + { + id: "4FpJcNgOvIpSBeJgRg3OfN", + imageURL: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", + subtitle: "", + title: "Orelsan", + type: "artist" + }, + { + id: "17UiqpQyl8T8vVxz2Towjy", + imageURL: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", + subtitle: "Orelsan", + title: "Perdu D'Avance", + type: "album" + }, + + { + id: "test_id", + imageURL: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", + subtitle: "Orelsan", + title: "test_name", + type: "track" + }, + { + id: "id_utilisateur", + imageURL: "photo", + subtitle: "pseudo", + title: "alias", + type: "user" + } ] +const mockUser = { + id_utilisateur: "id_utilisateur", + pseudo: "pseudo", + email: "email", + alias: "alias", + photo: "photo", + photo_temporaire: "photo_temporaire", + token: "token", + refresh_token: "refresh_token", + reset_token: "reset_token", + password: "password", + id_role: "id_role", + ban_until: "ban_until", + confirmed: "confirmed", + confirm_token: "confirm_token", + type: "user" +} module.exports = { mockUser, SpotifyRepositoryFixture, diff --git a/test/unit/application/usecase/friend/acceptResquestUser.test.js b/test/unit/application/usecase/friend/acceptResquestUser.test.js new file mode 100644 index 0000000..97311b6 --- /dev/null +++ b/test/unit/application/usecase/friend/acceptResquestUser.test.js @@ -0,0 +1,86 @@ +const acceptRequestUser = require('../../../../../lib/application/use_cases/friend/acceptRequestUser') +const catchError = require("../utils/catchError") +const mockFriendRepository = {} +const mockUserRepository = {} +const mockAccesTokenManager = {} + +mockAccesTokenManager.generate = ((test) =>{return ''}) +const relation = { + id_utilisateur: 1, + amiIdUtilisateur: 2, + en_attente: false, + createdAt: undefined, + updatedAt: undefined, + type : 'amis' +} +const user = { + id_utilisateur : 1, + pseudo : "pseudo", + email : "test@test.fr", + alias : undefined, + photo : undefined, + photo_temporaire : undefined, + token : undefined, + refresh_token : undefined, + reset_token : undefined, + password : "hjkklllllm", + id_role : 1, + ban_until : undefined, + confirmed: undefined, + confirm_token : undefined, + is_private : true , + type : 'user' +} + +describe("acceptRequestUser", ()=>{ + afterEach(()=>{ + jest.clearAllMocks(); + }) + beforeEach(()=>{ + mockUserRepository.getByUser = jest.fn((id)=> { + user.id_utilisateur = id + return user + }) + mockFriendRepository.accept = jest.fn((id, id_ami) => { + return relation + }) + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: 1}}) + }) + + const serviceLocator = { + userRepository: mockUserRepository, + friendRepository: mockFriendRepository, + accessTokenManager:mockAccesTokenManager + } + it('should accept a request friend of a user', async () => { + + const user = await acceptRequestUser("testtoken",2,serviceLocator) + expect(user).toBe(relation) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(2) + expect(mockFriendRepository.accept).toHaveBeenCalledTimes(1) + }); + + it('should throw error 400 invalid id friend', async () => { + mockUserRepository.getByUser = jest.fn((id)=> { + return null + }) + const error = await catchError(async ()=>{ + await acceptRequestUser("testtoken", -2,serviceLocator) + }) + expect(error.code).toBe(400) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(1) + }) + + it('should throw error 403 friendship doesn\'t exist', async () => { + mockFriendRepository.accept = jest.fn((id, id_ami)=> { + return null + }) + const error = await catchError(async ()=>{ + await acceptRequestUser("testtoken", 3,serviceLocator) + }) + expect(error.code).toBe(403) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(2) + expect(mockFriendRepository.accept).toHaveBeenCalledTimes(1) + }) + +}) \ No newline at end of file diff --git a/test/unit/application/usecase/friend/followUser.test.js b/test/unit/application/usecase/friend/followUser.test.js new file mode 100644 index 0000000..85847fe --- /dev/null +++ b/test/unit/application/usecase/friend/followUser.test.js @@ -0,0 +1,92 @@ +const followUser = require('../../../../../lib/application/use_cases/friend/followUser') +const catchError = require("../utils/catchError") +const mockFriendRepository = {} +const mockUserRepository = {} +const mockMailRepository = {} +const mockAccesTokenManager = {} + +mockAccesTokenManager.generate = ((test) =>{return ''}) +const relation = { + id_utilisateur: 1, + amiIdUtilisateur: 2, + en_attente: true, + createdAt: undefined, + updatedAt: undefined, + type : 'amis' +} +const user = { + id_utilisateur : 1, + pseudo : "pseudo", + email : "test@test.fr", + alias : undefined, + photo : undefined, + photo_temporaire : undefined, + token : undefined, + refresh_token : undefined, + reset_token : undefined, + password : "hjkklllllm", + id_role : 1, + ban_until : undefined, + confirmed: undefined, + confirm_token : undefined, + is_private : true , + type : 'user' +} +describe("followUser", ()=>{ + afterEach(()=>{ + jest.clearAllMocks(); + }) + beforeEach(()=>{ + mockUserRepository.getByUser = jest.fn((id) => { + user.id_utilisateur = id + return user + }) + mockFriendRepository.persist = jest.fn((friend) => { + return friend + }) + mockMailRepository.send = jest.fn(option => null) + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: 1}}) + }) + + const serviceLocator = { + userRepository: mockUserRepository, + mailRepository: mockMailRepository, + friendRepository: mockFriendRepository, + accessTokenManager:mockAccesTokenManager + } + it('should return friendship', async () => { + + const res = await followUser("testtoken",2,serviceLocator) + expect(res).toEqual(relation) + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(2) + expect(mockFriendRepository.persist).toHaveBeenCalledTimes(1) + expect(mockMailRepository.send).toHaveBeenCalledTimes(1) + }); + + it('should throw error 400 invalid id friend', async () => { + mockUserRepository.getByUser = jest.fn((id)=> { + if (id != -1) return user + return null + }) + const error = await catchError(async ()=>{ + await followUser("testtoken",-1,serviceLocator) + }) + expect(error.code).toBe(400) + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(2) + }); + + it('should throw error 403 friendship exist', async () => { + mockFriendRepository.persist = jest.fn((user)=> { + return null + }) + const error = await catchError(async ()=>{ + await followUser("testtoken",2,serviceLocator) + }) + expect(error.code).toBe(403) + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(2) + expect(mockFriendRepository.persist).toHaveBeenCalledTimes(1) + }); +}) \ No newline at end of file diff --git a/test/unit/application/usecase/friend/getListFriends.test.js b/test/unit/application/usecase/friend/getListFriends.test.js new file mode 100644 index 0000000..13f2128 --- /dev/null +++ b/test/unit/application/usecase/friend/getListFriends.test.js @@ -0,0 +1,102 @@ +const getListFriends = require('../../../../../lib/application/use_cases/friend/getListFriends') +const catchError = require("../utils/catchError") +const mockFriendRepository = {} +const mockUserRepository = {} +const mockAccesTokenManager = {} + +mockAccesTokenManager.generate = ((test) =>{return ''}) +const friends = [ + { + id_utilisateur : 1, + pseudo : "pseudo", + email : "test@test.fr", + alias : undefined, + photo : undefined, + photo_temporaire : undefined, + token : undefined, + refresh_token : undefined, + reset_token : undefined, + password : "hjkklllllm", + id_role : 1, + ban_until : undefined, + confirmed: undefined, + confirm_token : undefined, + is_private : true , + type : 'user' + },{ + id_utilisateur : 2, + pseudo : "pseudotest", + email : "test@test.fr", + alias : undefined, + photo : undefined, + photo_temporaire : undefined, + token : undefined, + refresh_token : undefined, + reset_token : undefined, + password : "hjkklllllm", + id_role : 1, + ban_until : undefined, + confirmed: undefined, + confirm_token : undefined, + is_private : true , + type : 'user' + } +] +const user = { + id_utilisateur : 3, + pseudo : "pseudo", + email : "test@test.fr", + alias : undefined, + photo : undefined, + photo_temporaire : undefined, + token : undefined, + refresh_token : undefined, + reset_token : undefined, + password : "hjkklllllm", + id_role : 1, + ban_until : undefined, + confirmed: undefined, + confirm_token : undefined, + is_private : true , + type : 'user' +} +describe("getListFriends", ()=>{ + afterEach(()=>{ + jest.clearAllMocks(); + }) + beforeEach(()=>{ + mockUserRepository.getByUser = jest.fn((id => user)) + mockFriendRepository.getListFriendsById = jest.fn((user) => { + return friends + }) + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: 1}}) + }) + + const serviceLocator = { + userRepository: mockUserRepository, + friendRepository: mockFriendRepository, + accessTokenManager:mockAccesTokenManager + } + it('should return list friends of a user', async () => { + + const user = await getListFriends("testtoken",serviceLocator) + expect(user).toBe(friends) + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.getListFriendsById).toHaveBeenCalledTimes(1) + }); + + it('should throw error 400 invalid token user', async () => { + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: -1}}) + mockUserRepository.getByUser = jest.fn((id)=> { + return null + }) + const error = await catchError(async ()=>{ + await getListFriends("testtoken", serviceLocator) + }) + expect(error.code).toBe(400) + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(1) + }); + +}) \ No newline at end of file diff --git a/test/unit/application/usecase/friend/getListFriendsRequest.test.js b/test/unit/application/usecase/friend/getListFriendsRequest.test.js new file mode 100644 index 0000000..9622cb6 --- /dev/null +++ b/test/unit/application/usecase/friend/getListFriendsRequest.test.js @@ -0,0 +1,86 @@ +const getListFriendsRequest = require('../../../../../lib/application/use_cases/friend/getListFriendsRequest') +const catchError = require("../utils/catchError") +const mockFriendRepository = {} +const mockUserRepository = {} +const mockAccesTokenManager = {} + +mockAccesTokenManager.generate = ((test) =>{return ''}) +const friends = [ + { + id_utilisateur : 4, + pseudo : "pseudo", + email : "test@test.fr", + alias : undefined, + photo : undefined, + photo_temporaire : undefined, + token : undefined, + refresh_token : undefined, + reset_token : undefined, + password : "hjkklllllm", + id_role : 1, + ban_until : undefined, + confirmed: undefined, + confirm_token : undefined, + is_private : true , + type : 'user' + } +] +const user = { + id_utilisateur : 3, + pseudo : "pseudo", + email : "test@test.fr", + alias : undefined, + photo : undefined, + photo_temporaire : undefined, + token : undefined, + refresh_token : undefined, + reset_token : undefined, + password : "hjkklllllm", + id_role : 1, + ban_until : undefined, + confirmed: undefined, + confirm_token : undefined, + is_private : true , + type : 'user' +} +describe("getListFriendsRequest", ()=>{ + afterEach(()=>{ + jest.clearAllMocks(); + }) + beforeEach(()=>{ + mockUserRepository.getByUser = jest.fn((id => user)) + mockFriendRepository.getRequestFriendsById = jest.fn((id) => { + return friends + }) + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: 1}}) + + }) + + const serviceLocator = { + userRepository: mockUserRepository, + friendRepository: mockFriendRepository, + accessTokenManager: mockAccesTokenManager + } + it('should return request friends of a user', async () => { + const users = await getListFriendsRequest("testtoken",serviceLocator) + expect(users).toBe(friends) + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.getRequestFriendsById).toHaveBeenCalledTimes(1) + }); + + it('should throw error 400 invalid token user', async () => { + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: -1}}) + mockUserRepository.getByUser = jest.fn((id)=> { + return null + }) + const error = await catchError(async ()=>{ + await getListFriendsRequest("testtoken", serviceLocator) + }) + expect(error.code).toBe(400) + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.getRequestFriendsById).toHaveBeenCalledTimes(0) + }); + +}) \ No newline at end of file diff --git a/test/unit/application/usecase/friend/getProfilFriend.test.js b/test/unit/application/usecase/friend/getProfilFriend.test.js new file mode 100644 index 0000000..88eb50a --- /dev/null +++ b/test/unit/application/usecase/friend/getProfilFriend.test.js @@ -0,0 +1,108 @@ +const getProfilFriend = require('../../../../../lib/application/use_cases/friend/getProfilFriend') +const catchError = require("../utils/catchError") +const mockFriendRepository = {} +const mockUserRepository = {} +const mockAccesTokenManager = {} + +mockAccesTokenManager.generate = ((test) =>{return ''}) +const relation = { + id_utilisateur: 1, + amiIdUtilisateur: 2, + en_attente: true, + createdAt: undefined, + updatedAt: undefined, + type : 'amis' +} +const user = { + id_utilisateur : 1, + pseudo : "pseudo", + email : "test@test.fr", + alias : undefined, + photo : undefined, + photo_temporaire : undefined, + token : undefined, + refresh_token : undefined, + reset_token : undefined, + password : "hjkklllllm", + id_role : 1, + ban_until : undefined, + confirmed: undefined, + confirm_token : undefined, + is_private : true , + type : 'user' +} + +const friend = { + id_utilisateur : 2, + pseudo : "pseudo22", + email : "test@test.fr", + alias : undefined, + photo : undefined, + photo_temporaire : undefined, + token : undefined, + refresh_token : undefined, + reset_token : undefined, + password : "hjkklllllm", + id_role : 1, + ban_until : undefined, + confirmed: undefined, + confirm_token : undefined, + is_private : true , + type : 'user' +} + +describe("getProfilFriend", ()=>{ + afterEach(()=>{ + jest.clearAllMocks(); + }) + + beforeEach(()=>{ + mockUserRepository.getByUser = jest.fn((id)=> { + return user.id_utilisateur == id ? user : friend + }) + mockFriendRepository.getById = jest.fn((id, id_ami) => { + return relation + }) + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: 1}}) + }) + const serviceLocator = { + userRepository: mockUserRepository, + friendRepository: mockFriendRepository, + accessTokenManager: mockAccesTokenManager + } + it('should accept a request friend of a user', async () => { + + const res = await getProfilFriend("testtoken",2,serviceLocator) + expect(res).toBe(friend) + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(2) + expect(mockFriendRepository.getById).toHaveBeenCalledTimes(1) + }); + + it('should throw error 400 invalid id friend', async () => { + mockUserRepository.getByUser = jest.fn((id)=> { + return null + }) + const error = await catchError(async ()=>{ + await getProfilFriend("testtoken", -2,serviceLocator) + }) + expect(error.code).toBe(400) + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.getById).toHaveBeenCalledTimes(0) + }) + + it('should throw error 403 friendship doesn\'t exist', async () => { + mockFriendRepository.getById = jest.fn((id, id_ami)=> { + return null + }) + const error = await catchError(async ()=>{ + await getProfilFriend("testtoken", 3,serviceLocator) + }) + expect(error.code).toBe(403) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(2) + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockFriendRepository.getById).toHaveBeenCalledTimes(1) + }) + +}) \ No newline at end of file diff --git a/test/unit/application/usecase/friend/unfollowUser.test.js b/test/unit/application/usecase/friend/unfollowUser.test.js new file mode 100644 index 0000000..3d2fd0d --- /dev/null +++ b/test/unit/application/usecase/friend/unfollowUser.test.js @@ -0,0 +1,78 @@ +const unfollowUser = require('../../../../../lib/application/use_cases/friend/unfollowUser') +const catchError = require("../utils/catchError") +const mockFriendRepository = {} +const mockUserRepository = {} +const mockAccesTokenManager = {} + +mockAccesTokenManager.generate = ((test) =>{return ''}) +const relation = { + id_utilisateur: undefined, + amiIdUtilisateur: undefined, + en_attente: false, + createdAt: undefined, + updatedAt: undefined, + type : 'amis' +} +const user = { + id_utilisateur : 1, + pseudo : "pseudo", + email : "test@test.fr", + alias : undefined, + photo : undefined, + photo_temporaire : undefined, + token : undefined, + refresh_token : undefined, + reset_token : undefined, + password : "hjkklllllm", + id_role : 1, + ban_until : undefined, + confirmed: undefined, + confirm_token : undefined, + is_private : true , + type : 'user' +} +describe("unfollowUser", ()=>{ + afterEach(()=>{ + jest.clearAllMocks(); + }) + beforeEach(()=>{ + mockUserRepository.getByUser = jest.fn((id)=> { + user.id_utilisateur=id + return user + }) + mockFriendRepository.removeFriendById = jest.fn((id, id_ami) => { + relation.id_utilisateur = id + relation.amiIdUtilisateur = id_ami + return relation + }) + mockAccesTokenManager.decode = jest.fn((token)=> {return {value: 1}}) + }) + + const serviceLocator = { + userRepository: mockUserRepository, + friendRepository: mockFriendRepository, + accessTokenManager:mockAccesTokenManager + } + it('should remove a friend of a user', async () => { + const user = await unfollowUser("testtoken",2,serviceLocator) + expect(user).toBe(relation) + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(2) + expect(mockFriendRepository.removeFriendById).toHaveBeenCalledTimes(1) + }); + + it('should throw error 400 invalid id friend', async () => { + mockUserRepository.getByUser = jest.fn((id)=> { + if (id < 0) return user + return null + }) + const error = await catchError(async ()=>{ + await unfollowUser("testtoken",-2,serviceLocator) + }) + expect(error.code).toBe(400) + expect(mockAccesTokenManager.decode).toHaveBeenCalledTimes(1) + expect(mockUserRepository.getByUser).toHaveBeenCalledTimes(1) + }); + + +}) \ No newline at end of file diff --git a/test/unit/application/usecase/oeuvre/getOeuvre.test.js b/test/unit/application/usecase/oeuvre/getOeuvre.test.js new file mode 100644 index 0000000..a29be6d --- /dev/null +++ b/test/unit/application/usecase/oeuvre/getOeuvre.test.js @@ -0,0 +1,148 @@ +const getOeuvre = require("./../../../../../lib/application/use_cases/oeuvre/getOeuvre") +const catchError = require("../utils/catchError") +const { + mockUser, + mockArtist, + mockUserPrivate, + mockAlbumRaw, + mockLikedReview, + mockCommentedReview, + mockOeuvreReviewSpotify, + expectedArtist +} = require('../fixtures/getOeuvreFixture') + +describe("getOeuvre Test", ()=>{ + const mockReviewRepository = {} + const mockAccesTokenManager = {} + const mockUserRepository = {} + const mockSpotifyRepository = {} + //const mockFollowRepository = {} + const mocklikeOeuvreRepository = {} + const mockoeuvreFavRepository = {} + + const serviceLocator = { + reviewRepository: mockReviewRepository, + accessTokenManager: mockAccesTokenManager, + userRepository: mockUserRepository, + spotifyRepository: mockSpotifyRepository, + //followRepository: mockFollowRepository, + likeOeuvreRepository : mocklikeOeuvreRepository, + oeuvreFavRepository : mockoeuvreFavRepository + } + describe("valid cases", ()=>{ + it("should return valid oeuvre with 1 artist", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((idUtilisateur) => mockUser) + // mockFollowRepository.getFollowersCount = jest.fn((artistId) => 100) + // mockFollowRepository.getFriendsFollowing = jest.fn((artistId,userId,limit) => [mockUserPrivate]) + // mockFollowRepository.getFriendsFollowingCount = jest.fn((artistId,userId) => 1) + mockSpotifyRepository.getSpotifyAlbums = jest.fn((idOeuvre) => { + return Promise.resolve(mockAlbumRaw); + }); + mockSpotifyRepository.getSpotifyTracks = jest.fn((idOeuvre)); + mockSpotifyRepository.getSpotifyArtist = jest.fn((idArtist) => mockArtist); + + mocklikeOeuvreRepository.doesUserLikes = jest.fn((idUtilisateur,idOeuvre)).mockReturnValue(false) + mockoeuvreFavRepository.oeuvreFavExists = jest.fn((idUtilisateur,idOeuvre)).mockReturnValue(false) + + mocklikeOeuvreRepository.getLikeCount = jest.fn((idOeuvre) => 9) + mockReviewRepository.getReviewCount = jest.fn((idOeuvre) => 2) + mockReviewRepository.getOeuvreRating = jest.fn(() => 1) + mockReviewRepository.getOeuvreReviews = jest.fn().mockReturnValueOnce([mockLikedReview]).mockReturnValueOnce([mockCommentedReview]) + mockreviewRepository.doesUserLikes = jest.fn().mockReturnValue(false) + mockSpotifyRepository.getOeuvre = jest.fn().mockReturnValue(mockOeuvreReviewSpotify) + + //mockSpotifyRepository.getSpotifyArtist = jest.fn((artistId) => mockArtist) + // mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((artistId,filter,limit) => mockAlbumRaw) + // mockReviewRepository.getOeuvreRating = jest.fn((id) => 1) + //mockReviewRepository.getReviewCount = jest.fn((id) => 2) + //mockReviewRepository.getOeuvreReviews = jest.fn().mockReturnValueOnce([mockLikedReview]).mockReturnValueOnce([mockCommentedReview]) + // mockreviewRepository.doesUserLikes = jest.fn().mockReturnValue(true) + // mockFollowRepository.doesFollows = jest.fn().mockReturnValue(true) + // mockSpotifyRepository.getOeuvre = jest.fn().mockReturnValue(mockOeuvreReviewSpotify) + const result = await getOeuvre(1,'token', serviceLocator) + result.albums[0].popularity = 64 + expect(result).toEqual(expectedOeuvre) + }) + }) + describe("invalid cases", ()=>{ + it("should throw error bad auth token", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => null) + const error = await catchError(async ()=>{ + await getArtist(1,'token', serviceLocator) + }) + expect(error.code).toBe(401) + }) + it("should throw error artist not found", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => mockUser) + mockSpotifyRepository.getSpotifyArtist = jest.fn((artistId) => { + return { + error: { + status: 400, + message: "message", + } + } + }) + const error = await catchError(async ()=>{ + await getArtist(1,'token', serviceLocator) + }) + expect(error.code).toBe(400) + }) + + it("should throw error oeuvre not found 1", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => mockUser) + mockFollowRepository.getFollowersCount = jest.fn((artistId) => 100) + mockFollowRepository.getFriendsFollowing = jest.fn((artistId,userId,limit) => [mockUserPrivate]) + mockFollowRepository.getFriendsFollowingCount = jest.fn((artistId,userId) => 1) + mockSpotifyRepository.getSpotifyArtist = jest.fn((artistId) => mockArtist) + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((artistId,filter,limit) => mockAlbumRaw) + mockReviewRepository.getOeuvreRating = jest.fn((id) => 1) + mockReviewRepository.getReviewCount = jest.fn((id) => 2) + mockReviewRepository.getOeuvreReviews = jest.fn().mockReturnValueOnce([mockLikedReview]).mockReturnValueOnce([mockCommentedReview]) + mockreviewRepository.doesUserLikes = jest.fn().mockReturnValue(true) + mockFollowRepository.doesFollows = jest.fn().mockReturnValue(true) + mockSpotifyRepository.getOeuvre = jest.fn().mockReturnValue({ + error: { + status: 400, + message: "message", + } + }) + + const error = await catchError(async ()=>{ + await getArtist(1,'token', serviceLocator) + }) + expect(error.code).toBe(400) + }) + + it("should throw error oeuvre not found 2", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => mockUser) + mockFollowRepository.getFollowersCount = jest.fn((artistId) => 100) + mockFollowRepository.getFriendsFollowing = jest.fn((artistId,userId,limit) => [mockUserPrivate]) + mockFollowRepository.getFriendsFollowingCount = jest.fn((artistId,userId) => 1) + mockSpotifyRepository.getSpotifyArtist = jest.fn((artistId) => mockArtist) + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((artistId,filter,limit) => mockAlbumRaw) + mockReviewRepository.getOeuvreRating = jest.fn((id) => 1) + mockReviewRepository.getReviewCount = jest.fn((id) => 2) + mockReviewRepository.getOeuvreReviews = jest.fn().mockReturnValueOnce([mockLikedReview]).mockReturnValueOnce([mockCommentedReview]) + mockreviewRepository.doesUserLikes = jest.fn().mockReturnValue(true) + mockFollowRepository.doesFollows = jest.fn().mockReturnValue(true) + mockSpotifyRepository.getOeuvre = jest.fn().mockReturnValueOnce(mockOeuvreReviewSpotify).mockReturnValue({ + error: { + status: 400, + message: "message", + } + }) + + const error = await catchError(async ()=>{ + await getArtist(1,'token', serviceLocator) + }) + expect(error.code).toBe(400) + }) + + }) + +}) \ No newline at end of file diff --git a/test/unit/application/usecase/oeuvre/likeOeuvre.test.js b/test/unit/application/usecase/oeuvre/likeOeuvre.test.js new file mode 100644 index 0000000..70980ee --- /dev/null +++ b/test/unit/application/usecase/oeuvre/likeOeuvre.test.js @@ -0,0 +1,91 @@ +const catchError = require("../utils/catchError") +const likeOeuvre = require("../../../../../lib/application/use_cases/oeuvre/likeOeuvre") +const mockUserRepository = {} +const mockLikeOeuvreRepository = {} +const mockAccessTokenManager = {} +const mockSpotifyRepository = {} + +const serviceLocator = { + userRepository: mockUserRepository, + likeOeuvreRepository: mockLikeOeuvreRepository, + accessTokenManager: mockAccessTokenManager, + spotifyRepository: mockSpotifyRepository +} + +describe("like oeuvre use case ", ()=>{ + const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false + } + const mockArtist = { + external_urls: { spotify: 'https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN' }, + genres: [ 'french hip hop', 'old school rap francais', 'rap conscient' ], + id: '4FpJcNgOvIpSBeJgRg3OfN', + images: [ + { + height: 640, + url: 'https://i.scdn.co/image/ab6761610000e5eb32086a424e6f1e499e347cde', + width: 640 + }, + ], + name: 'Orelsan', + popularity: 64, + type: 'artist', + } + const errror = { + error: { + status: 400, + message: "message", + } + } + describe("valid usecase", ()=>{ + + it("should return true", async ()=>{ + mockAccessTokenManager.decode = jest.fn(()=>{return {value:1}}) + mockUserRepository.getByUser = jest.fn().mockReturnValue(mockUser) + mockSpotifyRepository.getOeuvre = jest.fn().mockReturnValue(mockArtist) + mockLikeOeuvreRepository.doesUserLike = jest.fn().mockReturnValue(false) + mockLikeOeuvreRepository.like = jest.fn() + const result = await likeOeuvre(1,1,"artist",serviceLocator) + expect(result).toBe(true) + }) + + it("should return false", async ()=>{ + mockAccessTokenManager.decode = jest.fn(()=>{return {value:1}}) + mockUserRepository.getByUser = jest.fn().mockReturnValue(mockUser) + mockSpotifyRepository.getOeuvre = jest.fn().mockReturnValue(mockArtist) + mockLikeOeuvreRepository.doesUserLike = jest.fn().mockReturnValue(true) + mockLikeOeuvreRepository.unlike = jest.fn() + const result = await likeOeuvre(1,1,"artist",serviceLocator) + expect(result).toBe(false) + }) + }) + + describe("invalid usecase", ()=>{ + it("should return error 401", async ()=>{ + mockAccessTokenManager.decode = jest.fn(()=>{return {value:1}}) + mockUserRepository.getByUser = jest.fn().mockReturnValue(null) + const error = await catchError(async ()=>{ + await likeOeuvre(1,1,"artist", serviceLocator) + }) + expect(error.code).toBe(401) + }) + it("should return error 400", async ()=>{ + mockAccessTokenManager.decode = jest.fn(()=>{return {value:1}}) + mockUserRepository.getByUser = jest.fn().mockReturnValue(mockUser) + mockSpotifyRepository.getOeuvre = jest.fn().mockReturnValue(errror) + const error = await catchError(async ()=>{ + await likeOeuvre(1,1,"artist", serviceLocator) + }) + expect(error.code).toBe(400) + }) + }) +}) \ No newline at end of file diff --git a/test/unit/application/usecase/review/deleteReview.test.js b/test/unit/application/usecase/review/deleteReview.test.js new file mode 100644 index 0000000..dbf513e --- /dev/null +++ b/test/unit/application/usecase/review/deleteReview.test.js @@ -0,0 +1,50 @@ +const deleteReview = require("./../../../../../lib/application/use_cases/review/deleteReview") +const catchError = require("../utils/catchError") +describe("deleteReview Test", ()=>{ + const mockReviewRepository = {} + const mockAccesTokenManager = {} + const mockUserRepository = {} + const serviceLocator = { + reviewRepository: mockReviewRepository, + accessTokenManager: mockAccesTokenManager, + userRepository: mockUserRepository, + } + describe("valid cases", ()=>{ + it("should delete review", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => { + return { + id_utilisateur: 1 + } + }) + mockReviewRepository.delete = jest.fn((id) => true) + await deleteReview(1,'token', serviceLocator) + expect(mockReviewRepository.delete).toHaveBeenCalledTimes(1) + }) + }) + describe("invalid cases", ()=>{ + it("should throw error bad auth token", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => null) + const error = await catchError(async ()=>{ + await deleteReview(1,'token', serviceLocator) + }) + expect(error.code).toBe(401) + }) + it("should throw error not post owner", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((id) => { + return { + id_utilisateur: 1 + } + }) + mockReviewRepository.delete = jest.fn((id) => false) + const error = await catchError(async ()=>{ + await deleteReview(1,'token', serviceLocator) + }) + expect(error.code).toBe(403) + }) + + }) + +}) \ No newline at end of file diff --git a/test/unit/application/usecase/review/fixture/getOeuvreReviewsFixture.js b/test/unit/application/usecase/review/fixture/getOeuvreReviewsFixture.js new file mode 100644 index 0000000..bc561c6 --- /dev/null +++ b/test/unit/application/usecase/review/fixture/getOeuvreReviewsFixture.js @@ -0,0 +1,80 @@ +const mockArtist = { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + images: [{ + height: 640, + url: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + width: 640 + }], + external_urls: {spotify: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii"}, + popularity: 79, + genres: ["Reggae", "Roots"] +} +const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} +const mockPublicUser = { + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_utilisateur: 1, + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} + +const actualDate = new Date() +const rawReview = { + id_review: 1, + id_oeuvre: 1, + countlike: 2, + countComment: 4, + description: "C'est top", + note: 5, + createdAt: actualDate, + updated_at: actualDate, + type: 'artist', + utilisateur: mockUser +} +const expectedReview = { + id_review:1, + description: "C'est top", + countlike: 2, + countComment: 4, + note: 5, + createdAt: actualDate, + doesUserLike: false, + oeuvre: { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + image: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + spotify_url: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii", + popularity: 79, + genres: ["Reggae", "Roots"], + type: "artist" + }, + utilisateur: mockPublicUser, + type: 'artist', +} + + + + + +module.exports = { + mockArtist, + rawReview, + expectedReview, +} \ No newline at end of file diff --git a/test/unit/application/usecase/review/fixture/getReviewFixture.js b/test/unit/application/usecase/review/fixture/getReviewFixture.js new file mode 100644 index 0000000..74dccab --- /dev/null +++ b/test/unit/application/usecase/review/fixture/getReviewFixture.js @@ -0,0 +1,138 @@ +const mockArtist = { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + images: [{ + height: 640, + url: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + width: 640 + }], + external_urls: {spotify: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii"}, + popularity: 79, + genres: ["Reggae", "Roots"] +} +const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} + + +const mockPublicUser = { + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_utilisateur: 1, + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} +const mockComments = [ + { + id: 298, + description: 'test4', + utilisateur: mockPublicUser + } + ] +const mockUserPrivate = { + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + id_utilisateur: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: true +} +const actualDate = new Date() +const rawReview = { + id_review: 1, + id_oeuvre: 1, + countlike: 2, + countComment: 4, + description: "C'est top", + note: 5, + createdAt: actualDate, + updated_at: actualDate, + type: 'artist', + utilisateur: mockUser +} +const expectedReview = { + id_review:1, + description: "C'est top", + countlike: 2, + countComment: 4, + note: 5, + createdAt: actualDate, + doesUserLike: false, + oeuvre: { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + image: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + spotify_url: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii", + popularity: 79, + genres: ["Reggae", "Roots"], + type: "artist" + }, + comments: mockComments, + utilisateur: mockPublicUser, + type: 'artist', +} + + + +const rawReviewPrivate = { + id_review: 1, + id_oeuvre: 1, + countlike: 2, + countComment: 4, + description: "C'est top", + note: 5, + createdAt: actualDate, + updated_at: actualDate, + type: 'artist', + utilisateur: mockUserPrivate +} +const expectedPrivate = { + id_review:1, + description: "C'est top", + countlike: 2, + countComment: 4, + note: 5, + createdAt: actualDate, + doesUserLike: false, + oeuvre: { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + image: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + spotify_url: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii", + popularity: 79, + genres: ["Reggae", "Roots"], + type: "artist" + }, + comments: mockComments, + utilisateur: mockUserPrivate, + type: 'artist', +} + + +module.exports = { + rawReview, + mockArtist, + expectedReview, + rawReviewPrivate, + expectedPrivate, + mockComments, + +} \ No newline at end of file diff --git a/test/unit/application/usecase/review/fixture/getReviewLikesFixture.js b/test/unit/application/usecase/review/fixture/getReviewLikesFixture.js new file mode 100644 index 0000000..a3566da --- /dev/null +++ b/test/unit/application/usecase/review/fixture/getReviewLikesFixture.js @@ -0,0 +1,29 @@ +const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false + +} +const mockPublicUser = { + pseudo: "John Doe", + alias: "John", + ban_until: null, + id_utilisateur: 1, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} +module.exports = { + mockUser, + mockPublicUser +} \ No newline at end of file diff --git a/test/unit/application/usecase/review/fixture/getReviewsFixture.js b/test/unit/application/usecase/review/fixture/getReviewsFixture.js new file mode 100644 index 0000000..ea1e698 --- /dev/null +++ b/test/unit/application/usecase/review/fixture/getReviewsFixture.js @@ -0,0 +1,76 @@ +const mockArtist = { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + images: [{ + height: 640, + url: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + width: 640 + }], + external_urls: {spotify: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii"}, + popularity: 79, + genres: ["Reggae", "Roots"] +} +const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} +const actualDate = new Date() +const rawReview = { + id_review: 1, + id_oeuvre: 1, + countlike: 2, + countComment: 4, + description: "C'est top", + note: 5, + createdAt: actualDate, + updatedAt: actualDate, + type: 'artist', + utilisateur: mockUser +} +const mockPublicUser = { + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_utilisateur: 1, + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} +const expectedReview = { + id_review:1, + description: "C'est top", + countlike: 2, + countComment: 4, + note: 5, + createdAt: actualDate, + doesUserLike: false, + oeuvre: { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + image: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + spotify_url: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii", + popularity: 79, + genres: ["Reggae", "Roots"], + type: "artist" + }, + utilisateur: mockPublicUser, + type: 'artist', +} + + +module.exports = { + rawReview, + mockArtist, + expectedReview, +} \ No newline at end of file diff --git a/test/unit/application/usecase/review/fixture/getUserReviewFixture.js b/test/unit/application/usecase/review/fixture/getUserReviewFixture.js new file mode 100644 index 0000000..8cb6d92 --- /dev/null +++ b/test/unit/application/usecase/review/fixture/getUserReviewFixture.js @@ -0,0 +1,95 @@ +const mockArtist = { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + images: [{ + height: 640, + url: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + width: 640 + }], + external_urls: {spotify: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii"}, + popularity: 79, + genres: ["Reggae", "Roots"] +} +const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} + + +const mockPublicUser = { + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_utilisateur: 1, + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} + +const mockUserPrivate = { + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + id_utilisateur: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: true +} +const actualDate = new Date() +const rawReview = { + id_review: 1, + id_oeuvre: 1, + countlike: 2, + countComment: 4, + description: "C'est top", + note: 5, + createdAt: actualDate, + updatedAt: actualDate, + type: 'artist', + utilisateur: mockUser +} +const expectedReview = { + id_review:1, + description: "C'est top", + countlike: 2, + countComment: 4, + note: 5, + createdAt: actualDate, + doesUserLike: false, + oeuvre: { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + image: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + spotify_url: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii", + popularity: 79, + genres: ["Reggae", "Roots"], + type: "artist" + }, + utilisateur: mockPublicUser, + type: 'artist', +} + + + + + +module.exports = { + rawReview, + mockArtist, + expectedReview, + +} \ No newline at end of file diff --git a/test/unit/application/usecase/review/fixture/putReviewFixture.js b/test/unit/application/usecase/review/fixture/putReviewFixture.js new file mode 100644 index 0000000..a2da09b --- /dev/null +++ b/test/unit/application/usecase/review/fixture/putReviewFixture.js @@ -0,0 +1,90 @@ +const mockArtist = { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + images: [{ + height: 640, + url: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + width: 640 + }], + external_urls: {spotify: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii"}, + popularity: 79, + genres: ["Reggae", "Roots"] +} +const mockUser = { + id_utilisateur: 1, + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false + +} +const mockPublicUser = { + pseudo: "John Doe", + id_utilisateur:1, + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: false +} +const mockUserPrivate = { + pseudo: "John Doe", + alias: "John", + ban_until: null, + email: "testemail@gmail", + id_role: 1, + photo: null, + photo_temporaire: null, + type: "user", + is_private: true +} +const actualDate = new Date() +const rawReview = { + id_review: 1, + id_oeuvre: 1, + countlike: 2, + countComment: 4, + description: "C'est top", + note: 5, + createdAt: actualDate, + updated_at: actualDate, + type: 'artist', + utilisateur: mockUser +} +const expectedReview = { + id_review:1, + description: "C'est top", + countlike: 2, + countComment: 4, + note: 5, + createdAt: actualDate, + doesUserLike: false, + oeuvre: { + id: "32kWZXLpwGm5Y2B0lKb6Ii", + name: "Bob Marley", + image: "https://i.scdn.co/image/ab67616d0000b273c9adfbd773852e286faed040", + spotify_url: "https://open.spotify.com/artist/32kWZXLpwGm5Y2B0lKb6Ii", + popularity: 79, + genres: ["Reggae", "Roots"], + type: "artist" + }, + utilisateur: mockPublicUser, + type: 'artist', +} + + + + +module.exports = { + rawReview, + mockArtist, + expectedReview +} \ No newline at end of file diff --git a/test/unit/application/usecase/review/getOeuvreReviews.test.js b/test/unit/application/usecase/review/getOeuvreReviews.test.js new file mode 100644 index 0000000..3346f7c --- /dev/null +++ b/test/unit/application/usecase/review/getOeuvreReviews.test.js @@ -0,0 +1,59 @@ +const getOeuvreReviews = require("../../../../../lib/application/use_cases/review/getOeuvreReviews") +const catchError = require("../utils/catchError") +const { + mockArtist, + rawReview, + expectedReview, +} = require("./fixture/getOeuvreReviewsFixture") + +describe("getReviews Test", ()=>{ + const mockReviewRepository = {} + const mockAccesTokenManager = {} + const mockSpotifyRepository = {} + const serviceLocator = { + reviewRepository: mockReviewRepository, + accessTokenManager: mockAccesTokenManager, + spotifyRepository: mockSpotifyRepository, + } + describe("valid cases", ()=>{ + it("should return serialized review with public user", async ()=>{ + mockReviewRepository.getOeuvreReviews = jest.fn((id_utilisateur,page,pageSize,orderByLike) => [rawReview]) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + const expectedReviews = [expectedReview] + const result = await getOeuvreReviews(1,undefined,1,10,true, serviceLocator) + expect(result).toEqual(expectedReviews) + }) + it("should return serialized review with private user", async ()=>{ + + mockAccesTokenManager.decode = jest.fn((userToken) => { + return {value: 1} + }) + mockReviewRepository.getOeuvreReviews = jest.fn((id_utilisateur,page,pageSize,orderByLike) => [rawReview]) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + mockreviewRepository.doesUserLikes = jest.fn((id_utilisateur,reviewId) => false) + const expectedReviews = [expectedReview] + const result = await getOeuvreReviews(1,'token',1,10,true, serviceLocator) + expect(result).toEqual(expectedReviews) + }) + }) + + describe("invalide cases", ()=>{ + it("should throw error 400", async ()=>{ + mockReviewRepository.getOeuvreReviews = jest.fn((id_utilisateur,page,pageSize,orderByLike) => [rawReview]) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => { + return { + error: { + status: 400, + message: "message", + } + } + }) + const error = await catchError(async () => { + await getOeuvreReviews(1,undefined,1,10,true, serviceLocator) + }) + expect(error.code).toBe(400) + }) + + }) + +}) \ No newline at end of file diff --git a/test/unit/application/usecase/review/getReview.test.js b/test/unit/application/usecase/review/getReview.test.js new file mode 100644 index 0000000..54ef6f9 --- /dev/null +++ b/test/unit/application/usecase/review/getReview.test.js @@ -0,0 +1,107 @@ +const getReview = require("../../../../../lib/application/use_cases/review/getReview") +const catchError = require("../utils/catchError") + +const { + mockArtist, + rawReview, + expectedReview, + rawReviewPrivate, + expectedPrivate, + mockComments +} = require("./fixture/getReviewFixture") + + +describe("getReview Test", ()=>{ + const mockReviewRepository = {} + const mockFriendRepository = {} + const mockAccesTokenManager = {} + const mockSpotifyRepository = {} + const mockCommentRepository = {} + const serviceLocator = { + reviewRepository: mockReviewRepository, + friendRepository: mockFriendRepository, + accessTokenManager: mockAccesTokenManager, + spotifyRepository: mockSpotifyRepository, + commentRepository: mockCommentRepository + } + + describe("successful cases", () => { + it("should return review with artist from repository", async () => { + mockReviewRepository.getById = jest.fn((id) => rawReview) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + mockCommentRepository.getReviewComments = jest.fn().mockReturnValue(mockComments) + const result = await getReview(1,undefined,1,10,true, serviceLocator) + console.log(result) + expect(result).toEqual(expectedReview) + }) + it("should return review with artist private from repository", async () => { + mockReviewRepository.getById = jest.fn((id) => rawReviewPrivate) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + mockFriendRepository.areFriends = jest.fn((id, id_ami) => true) + mockAccesTokenManager.decode = jest.fn((token) => {return {value: 1}}) + mockreviewRepository.doesUserLikes = jest.fn((id_utilisateur,reviewId) => false) + const result = await getReview(1,'something',1,10,true, serviceLocator) + expect(result).toEqual(expectedPrivate) + }) + + }) + describe("invalid cases", () => { + it("should throw review not found error", async () => { + mockReviewRepository.getById = jest.fn((id) => null) + const error = await catchError(async () => { + await getReview(1,undefined,1,10,true, serviceLocator) + }) + console.log(error) + expect(error.code).toBe(404) + }) + it("should throw user private 1", async () => { + const mockReview = { + utilisateur: { + id_utilisateur: 1, + pseudo: "John Doe", + is_private: true + } + } + mockReviewRepository.getById = jest.fn((id) => mockReview) + const error = await catchError(async () => { + await getReview(1,undefined,1,10,true, serviceLocator) + }) + expect(error.code).toBe(403) + }) + + it("should throw user private 2", async () => { + const mockReview = { + utilisateur: { + id_utilisateur: 1, + pseudo: "John Doe", + is_private: true + } + } + mockReviewRepository.getById = jest.fn((id) => mockReview) + mockFriendRepository.areFriends = jest.fn((id, id_ami) => false) + mockAccesTokenManager.decode = jest.fn((token) => {return {value: 1}}) + const error = await catchError(async () => { + await getReview(1,'something',1,10,true, serviceLocator) + }) + expect(error.code).toBe(403) + expect(mockFriendRepository.areFriends).toHaveBeenCalledTimes(1) + expect().hasBee + }) + it("should throw error review not found ", async () => { + mockReviewRepository.getById = jest.fn((id) => rawReview) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => { + return { + error: { + status: 400, + message: "message", + } + } + }) + const error = await catchError(async () => { + await getReview(1,undefined,1,10,true, serviceLocator) + }) + expect(error.code).toBe(400) + + }) + }) +}) diff --git a/test/unit/application/usecase/review/getReviewLikes.test.js b/test/unit/application/usecase/review/getReviewLikes.test.js new file mode 100644 index 0000000..8804049 --- /dev/null +++ b/test/unit/application/usecase/review/getReviewLikes.test.js @@ -0,0 +1,88 @@ +const getReviewLikes = require("./../../../../../lib/application/use_cases/review/getReviewLikes") +const catchError = require("../utils/catchError") +const { + mockUser, + mockPublicUser +} = require("./fixture/getReviewLikesFixture") +describe("getReviewLikes Test", ()=>{ + const mockReviewRepository = {} + const mockAccesTokenManager = {} + const mockUserRepository = {} + const mockFriendRepository = {} + const serviceLocator = { + reviewRepository: mockReviewRepository, + accessTokenManager: mockAccesTokenManager, + userRepository: mockUserRepository, + friendRepository: mockFriendRepository + } + describe("invalid cases", ()=>{ + + it("should throw review not found error", async ()=>{ + mockReviewRepository.getById = jest.fn((id) => null) + const error = await catchError(async ()=>{ + await getReviewLikes(1,'token',1,10, serviceLocator) + }) + expect(error.code).toBe(404) + }) + it("should throw user is private error 1", async ()=>{ + const mockReview = { + utilisateur: { + id_utilisateur: 1, + pseudo: "John Doe", + is_private: true + } + } + mockReviewRepository.getById = jest.fn((id) => mockReview) + mockAccesTokenManager.decode = jest.fn((token) => {return {value: 1}}) + mockFriendRepository.areFriends = jest.fn((id, id_ami) => false) + const error = await catchError(async ()=>{ + await getReviewLikes(1,'token',1,10, serviceLocator) + }) + expect(error.code).toBe(403) + }) + it("should throw user is private error 2", async ()=>{ + const mockReview = { + utilisateur: { + id_utilisateur: 1, + pseudo: "John Doe", + is_private: true + } + } + mockReviewRepository.getById = jest.fn((id) => mockReview) + const error = await catchError(async ()=>{ + await getReviewLikes(1,undefined,1,10, serviceLocator) + }) + expect(error.code).toBe(403) + }) + }) + describe("valid cases", ()=>{ + it("should get review private like", async ()=>{ + const mockReview = { + utilisateur: { + id_utilisateur: 1, + pseudo: "John Doe", + is_private: true + } + } + mockReviewRepository.getById = jest.fn((id) => mockReview) + mockAccesTokenManager.decode = jest.fn((token) => {return {value: 1}}) + mockFriendRepository.areFriends = jest.fn((id, id_ami) => true) + mockReviewRepository.getLikes = jest.fn((id) => [mockUser]) + const result = await getReviewLikes(1,'token',1,10, serviceLocator) + expect(result).toEqual([mockPublicUser]) + }) + it("should get review public", async ()=>{ + const mockReview = { + utilisateur: { + id_utilisateur: 1, + pseudo: "John Doe", + is_private: false + } + } + mockReviewRepository.getById = jest.fn((id) => mockReview) + mockReviewRepository.getLikes = jest.fn((id) => [mockUser]) + const result = await getReviewLikes(1,undefined,1,10, serviceLocator) + expect(result).toEqual([mockPublicUser]) + }) + }) +}) \ No newline at end of file diff --git a/test/unit/application/usecase/review/getReviews.test.js b/test/unit/application/usecase/review/getReviews.test.js new file mode 100644 index 0000000..32c9cdb --- /dev/null +++ b/test/unit/application/usecase/review/getReviews.test.js @@ -0,0 +1,37 @@ +const getReviews = require("../../../../../lib/application/use_cases/review/getReviews") + +const { + mockArtist, + rawReview, + expectedReview, +} = require("./fixture/getReviewsFixture") + +describe("getReviews Test", ()=>{ + const mockReviewRepository = {} + const mockAccesTokenManager = {} + const mockSpotifyRepository = {} + const serviceLocator = { + reviewRepository: mockReviewRepository, + accessTokenManager: mockAccesTokenManager, + spotifyRepository: mockSpotifyRepository, + } + it("should return serialized review", async ()=>{ + + mockReviewRepository.getReviews = jest.fn((page,pageSize,orderByLike, isPrivate,userToken) => [rawReview]) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + mockreviewRepository.doesUserLikes = jest.fn((id_utilisateur,reviewId) => false) + const expectedReviews = [expectedReview] + const result = await getReviews(1,10,true,undefined, serviceLocator) + expect(result).toEqual(expectedReviews) + }) + + it("should return serialized review with user login", async ()=>{ + mockAccesTokenManager.decode = jest.fn((token) => {return {value: 1}}) + mockReviewRepository.getReviews = jest.fn((page,pageSize,orderByLike, isPrivate,userToken) => [rawReview]) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + mockreviewRepository.doesUserLikes = jest.fn((id_utilisateur,reviewId) => false) + const expectedReviews = [expectedReview] + const result = await getReviews(1,10,true,'something', serviceLocator) + expect(result).toEqual(expectedReviews) + }) +}) \ No newline at end of file diff --git a/test/unit/application/usecase/review/getUserReviews.test.js b/test/unit/application/usecase/review/getUserReviews.test.js new file mode 100644 index 0000000..2bf786a --- /dev/null +++ b/test/unit/application/usecase/review/getUserReviews.test.js @@ -0,0 +1,95 @@ +const getUserReviews = require("../../../../../lib/application/use_cases/review/getUserReviews") +const catchError = require("../utils/catchError") +const { + mockArtist, + rawReview, + expectedReview, +} = require("./fixture/getUserReviewFixture") + +describe("getReviews Test", ()=>{ + const mockReviewRepository = {} + const mockAccesTokenManager = {} + const mockSpotifyRepository = {} + const mockUserRepository = {} + const mockFriendRepository = {} + const serviceLocator = { + reviewRepository: mockReviewRepository, + accessTokenManager: mockAccesTokenManager, + spotifyRepository: mockSpotifyRepository, + userRepository: mockUserRepository, + friendRepository: mockFriendRepository, + } + describe("successful cases", ()=>{ + it("should return serialized review with public user", async ()=>{ + mockUserRepository.getByEmailOrPseudo = jest.fn((pseudo,email) => { + return { + id_utilisateur: 1, + is_private: false + } + }) + mockReviewRepository.getReviewByUserId = jest.fn((id_utilisateur,page,pageSize,orderByLike) => [rawReview]) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + const expectedReviews = [expectedReview] + const result = await getUserReviews(1,'token',1,10,true, serviceLocator) + expect(result).toEqual(expectedReviews) + }) + it("should return serialized review with private user", async ()=>{ + mockUserRepository.getByEmailOrPseudo = jest.fn((pseudo,email) => { + return { + id_utilisateur: 1, + is_private: true + } + }) + mockAccesTokenManager.decode = jest.fn((userToken) => { + return {value: 1} + }) + mockFriendRepository.areFriends = jest.fn((id, id_ami) => true) + mockReviewRepository.getReviewByUserId = jest.fn((id_utilisateur,page,pageSize,orderByLike) => [rawReview]) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + mockreviewRepository.doesUserLikes = jest.fn((id_utilisateur,reviewId) => false) + const expectedReviews = [expectedReview] + const result = await getUserReviews(1,'token',1,10,true, serviceLocator) + expect(result).toEqual(expectedReviews) + }) + }) + describe("invalid cases", ()=>{ + + it("should throw error user private 1", async ()=>{ + mockUserRepository.getByEmailOrPseudo = jest.fn((pseudo,email) => { + return { + id_utilisateur: 1, + is_private: true + } + }) + const error = await catchError(async ()=>{ + await getUserReviews(1,undefined,1,10,true, serviceLocator) + }) + expect(error.code).toBe(403) + }) + + it("should throw error user dont exist", async ()=>{ + mockUserRepository.getByEmailOrPseudo = jest.fn().mockReturnValue(null) + const error = await catchError(async ()=>{ + await getUserReviews(1,undefined,1,10,true, serviceLocator) + }) + expect(error.code).toBe(404) + }) + it("should throw error user private 2", async ()=>{ + mockUserRepository.getByEmailOrPseudo = jest.fn((pseudo,email) => { + return { + id_utilisateur: 1, + is_private: true + } + }) + mockAccesTokenManager.decode = jest.fn((userToken) => { + return {value: 1} + }) + mockFriendRepository.areFriends = jest.fn((id, id_ami) => false) + const error = await catchError(async ()=>{ + await getUserReviews(1,'token',1,10,true, serviceLocator) + }) + expect(error.code).toBe(403) + expect(mockFriendRepository.areFriends).toHaveBeenCalledTimes(1) + }) + }) +}) \ No newline at end of file diff --git a/test/unit/application/usecase/review/likeReview.test.js b/test/unit/application/usecase/review/likeReview.test.js new file mode 100644 index 0000000..ce3f64e --- /dev/null +++ b/test/unit/application/usecase/review/likeReview.test.js @@ -0,0 +1,50 @@ +const likeReview = require("../../../../../lib/application/use_cases/review/likeReview") +const catchError = require("../utils/catchError") +describe("likeReview Test", ()=>{ + const mockReviewRepository = {} + const mockAccesTokenManager = {} + const mockUserRepository = {} + + + const serviceLocator = { + reviewRepository: mockReviewRepository, + accessTokenManager: mockAccesTokenManager, + userRepository: mockUserRepository, + } + describe("valid cases", ()=>{ + it("should like review", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => {return {value: 1}}) + mockUserRepository.getByUser = jest.fn((id) => { + return { + id_utilisateur: 1 + } + }) + mockreviewRepository.doesUserLikes = jest.fn((id) => true) + mockReviewRepository.unlikeReview = jest.fn((id) => true) + await likeReview(1,'token', serviceLocator) + expect(mockReviewRepository.unlikeReview).toHaveBeenCalledTimes(1) + }) + + it("should unlike review", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => {return {value: 1}}) + mockUserRepository.getByUser = jest.fn((id) => { + return { + id_utilisateur: 1 + } + }) + mockreviewRepository.doesUserLikes = jest.fn((id) => false) + mockReviewRepository.likeReview = jest.fn((id) => true) + await likeReview(1,'token', serviceLocator) + expect(mockReviewRepository.likeReview).toHaveBeenCalledTimes(1) + }) + }) + describe("invalid cases", ()=>{ + it("should throw error bad auth token", async ()=>{ + mockUserRepository.getByUser = jest.fn((id) => null) + const error = await catchError(async ()=>{ + await await likeReview(1,'token', serviceLocator) + }) + expect(error.code).toBe(401) + }) + }) +}) \ No newline at end of file diff --git a/test/unit/application/usecase/review/putReview.test.js b/test/unit/application/usecase/review/putReview.test.js new file mode 100644 index 0000000..a24fd26 --- /dev/null +++ b/test/unit/application/usecase/review/putReview.test.js @@ -0,0 +1,108 @@ +const putReview = require("../../../../../lib/application/use_cases/review/putReview") +const catchError = require("../utils/catchError") +describe("putReview Test", ()=>{ + const idOeuvre = 'idOeuvre' + const userToken = 'token' + const description = 'description' + const note = 5 + const type = 'artist' + const { + rawReview, + mockArtist, + expectedReview, + } = require("./fixture/putReviewFixture") + const mockReviewRepository = {} + const mockAccesTokenManager = {} + const mockSpotifyRepository = {} + const mockUserRepository = {} + const serviceLocator = { + reviewRepository: mockReviewRepository, + accessTokenManager: mockAccesTokenManager, + spotifyRepository: mockSpotifyRepository, + userRepository: mockUserRepository, + } + describe("invalid cases", ()=>{ + + it("should throw error bad auth token", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((userToken) => null) + const error = await catchError(async ()=>{ + await putReview(idOeuvre, userToken, description,note, type, serviceLocator) + }) + expect(error.code).toBe(401) + }) + it("should throw error review already posted", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1 + } + }) + mockReviewRepository.getByUserAndId = jest.fn((idOeuvre, id_utilisateur) => { + return { + id_review: 1 + } + }) + + const error = await catchError(async ()=>{ + await putReview(idOeuvre, userToken, description,note, type, serviceLocator) + }) + expect(error.code).toBe(403) + }) + it("should throw error type review doesn't exist", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1 + } + }) + mockReviewRepository.getByUserAndId = jest.fn((idOeuvre, id_utilisateur) => null) + mockReviewRepository.getTypeReviewID = jest.fn((type) => null) + const error = await catchError(async ()=>{ + await putReview(idOeuvre, userToken, description,note, type, serviceLocator) + }) + expect(error.code).toBe(404) + expect(mockReviewRepository.getTypeReviewID).toHaveBeenCalledTimes(1) + }) + it("should throw error rawOeuvre ", async ()=>{ + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1 + } + }) + mockReviewRepository.getByUserAndId = jest.fn((idOeuvre, id_utilisateur) => null) + mockReviewRepository.getTypeReviewID = jest.fn((type) => 1) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => { + return { + error: { + status: 400, + message: 'error' + } + } + }) + const error = await catchError(async ()=>{ + await putReview(idOeuvre, userToken, description,note, type, serviceLocator) + }) + expect(error.code).toBe(400) + }) + }) + describe("valid cases", ()=>{ + + it("should put review", async ()=>{ + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1 + } + }) + mockReviewRepository.getByUserAndId = jest.fn((idOeuvre, id_utilisateur) => null) + mockReviewRepository.getTypeReviewID = jest.fn((type) => 1) + mockReviewRepository.persist = jest.fn((reviewRaw) => rawReview) + mockSpotifyRepository.getOeuvre = jest.fn((id,type) => mockArtist) + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + const result = await putReview(idOeuvre, userToken, description,note, type, serviceLocator) + expect(result).toEqual(expectedReview) + }) + + }) +}) \ No newline at end of file diff --git a/test/unit/application/usecase/spotify/FetchArtist.test.js b/test/unit/application/usecase/spotify/FetchArtist.test.js new file mode 100644 index 0000000..6099c08 --- /dev/null +++ b/test/unit/application/usecase/spotify/FetchArtist.test.js @@ -0,0 +1,39 @@ +const fetchArtist = require("../../../../../lib/application/use_cases/spotify/FetchArtist") +const catchError = require("../utils/catchError") +const { + artistFixture, // reponse pas serialise + expectedFixture, // reponse serialise +} = require("../fixtures/fetchArtist") + +const mockSpotifyRepository = {} + + +const idOrelsan = "4FpJcNgOvIpSBeJgRg3OfN" + +describe('FetchArtist usecase', () => { + it("should return a serialized artist item", async ()=>{ + mockSpotifyRepository.getSpotifyArtist = jest.fn((id) =>{ + return artistFixture + }) + const result = await fetchArtist( // fetchArtist metier, serialize est fait a la fin + idOrelsan, + {spotifyRepository : mockSpotifyRepository}) // va utiliser la fct getSpotifyArtist de mockSpotifyRepository + console.log("result", result) + console.log("expectedFixture", expectedFixture) + + expect(result).toEqual(expectedFixture) + expect(mockSpotifyRepository.getSpotifyArtist).toHaveBeenCalledWith(idOrelsan) + }) + it("should return throw an error 400", async ()=>{ + mockSpotifyRepository.getSpotifyArtist = jest.fn((id) =>{ + throw new Error('test error') + }) + const error = await catchError(async ()=> { + await fetchArtist( // fetchArtist metier, serialize est fait a la fin + idOrelsan, + {spotifyRepository : mockSpotifyRepository}) // va utiliser la fct getSpotifyArtist de mockSpotifyRepository + + }) + expect(error.code).toBe(400) + }) +}); \ No newline at end of file diff --git a/test/unit/application/usecase/spotify/FetchArtistSongs.test.js b/test/unit/application/usecase/spotify/FetchArtistSongs.test.js new file mode 100644 index 0000000..f642d82 --- /dev/null +++ b/test/unit/application/usecase/spotify/FetchArtistSongs.test.js @@ -0,0 +1,146 @@ +const { VERSION } = require("underscore"); +const catchError = require("../utils/catchError") + +const FetchArtistSongs = require("../../../../../lib/application/use_cases/spotify/FetchArtistSongs") +const { + songsRawAlbumArtist, + expectedAlbumArtist, + songsRawSingleArtist, + expectedSingleArtist, + songsRawCompilationArtist, + expectedCompilationArtist, + songsRawAppearsOnArtist, + expectedAppearsOnArtist +} = require("../../../interfaces/serializers/fixtures/artistAlbumsFixture") + +const songsRawAll = { + items: [ + songsRawAlbumArtist.items[0], + songsRawSingleArtist.items[0], + songsRawCompilationArtist.items[0], + songsRawAppearsOnArtist.items[0] + ], + } + + const expectedFixturesAll = [ + expectedAlbumArtist[0], + expectedSingleArtist[0], + expectedCompilationArtist[0], + expectedAppearsOnArtist[0] + ]; + + const songsRawAlbumSingle = { + items: [ + songsRawAlbumArtist.items[0], + songsRawSingleArtist.items[0] + ] + } + + const expectedFixturesAlbumSingle = [ + expectedAlbumArtist[0], + expectedSingleArtist[0] + ]; + + +const mockSpotifyRepository = {} + +const id = "57TzZhbqvYoUBzJSVKFVlG" +const album ="album" +const single ="single" +const compilation ="compilation" +const appearsOn = "appears_on" + + +describe('FetchArtistSongs usecase', () => { + it("should return a serialized album item", async ()=>{ + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((id, filter, limit) =>{ + return songsRawAlbumArtist + }) + const result = await FetchArtistSongs( // fetchArtist metier, serialize est fait a la fin + id, + album, + 1, + {spotifyRepository : mockSpotifyRepository}) // va utiliser la fct getSpotifyArtistSongs de mockSpotifyRepository + expect(result).toEqual(expectedAlbumArtist) + expect(mockSpotifyRepository.getSpotifyArtistSongs).toHaveBeenCalledWith(id, album, 1) + + }) + + it("should return a serialized album type single item", async ()=>{ + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((id, filter, limit) =>{ + return songsRawSingleArtist + }) + const result = await FetchArtistSongs( // fetchArtist metier, serialize est fait a la fin + id, + single, + 1, + {spotifyRepository : mockSpotifyRepository}) // va utiliser la fct getSpotifyArtistSongs de mockSpotifyRepository + expect(result).toEqual(expectedSingleArtist) + expect(mockSpotifyRepository.getSpotifyArtistSongs).toHaveBeenCalledWith(id, single, 1) + }) + + + + it("should return a serialized album type compilation item", async ()=>{ + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((id, filter, limit) =>{ + return songsRawCompilationArtist + }) + const result = await FetchArtistSongs( // fetchArtist metier, serialize est fait a la fin + id, + compilation, + 1, + {spotifyRepository : mockSpotifyRepository}) // va utiliser la fct getSpotifyArtistSongs de mockSpotifyRepository + expect(result).toEqual(expectedCompilationArtist) + expect(mockSpotifyRepository.getSpotifyArtistSongs).toHaveBeenCalledWith(id, compilation, 1) + }) + + it("should return a serialized album type appears_on item", async ()=>{ + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((id, filter, limit) =>{ + return songsRawAppearsOnArtist + }) + const result = await FetchArtistSongs( // fetchArtist metier, serialize est fait a la fin + id, + appearsOn, + 1, + {spotifyRepository : mockSpotifyRepository}) // va utiliser la fct getSpotifyArtistSongs de mockSpotifyRepository + expect(result).toEqual(expectedAppearsOnArtist) + expect(mockSpotifyRepository.getSpotifyArtistSongs).toHaveBeenCalledWith(id, appearsOn, 1) + }) + + it("should return 4 serialized album's type album, single, compilation and appears_on items", async ()=>{ + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((id, filter, limit) =>{ + return songsRawAll + }) + const result = await FetchArtistSongs( // fetchArtist metier, serialize est fait a la fin + id, + "album,single,compilation,appears_on", + 4, + {spotifyRepository : mockSpotifyRepository}) // va utiliser la fct getSpotifyArtistSongs de mockSpotifyRepository + expect(result).toEqual(expectedFixturesAll) + expect(mockSpotifyRepository.getSpotifyArtistSongs).toHaveBeenCalledWith(id, "album,single,compilation,appears_on" , 4) + }) + + it("should return 2 serialized album's type album and single", async ()=>{ + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((id, filter, limit) =>{ + return songsRawAlbumSingle + }) + const result = await FetchArtistSongs( // fetchArtist metier, serialize est fait a la fin + id, + "album,single", + 2, + {spotifyRepository : mockSpotifyRepository}) // va utiliser la fct getSpotifyArtistSongs de mockSpotifyRepository + expect(result).toEqual(expectedFixturesAlbumSingle) + expect(mockSpotifyRepository.getSpotifyArtistSongs).toHaveBeenCalledWith(id,"album,single" , 2) + }) + + it("should return throw an error 400", async ()=>{ + mockSpotifyRepository.getSpotifyArtistSongs = jest.fn((id, filter, limit) =>{ + throw new Error('test error') + }) + + const error = await catchError(async ()=> { + await FetchArtistSongs(id, "single", 1, {spotifyRepository : mockSpotifyRepository}) + }) + expect(error.code).toBe(400) + }) +}); \ No newline at end of file diff --git a/test/unit/application/usecase/spotify/GetToken.test.js b/test/unit/application/usecase/spotify/GetToken.test.js new file mode 100644 index 0000000..16bf5d3 --- /dev/null +++ b/test/unit/application/usecase/spotify/GetToken.test.js @@ -0,0 +1,29 @@ +const getToken = require("../../../../../lib/application/use_cases/spotify/GetToken") +const catchError = require("../utils/catchError") +const mockSpotifyRepository = {} + +describe("GetToken", () =>{ + describe("GetToken valid ", ()=>{ + + it("should return valid json", async ()=>{ + const expectedSpotifyCode = { + access_token: "access_token", + refresh_token: "refresh_token", + } + mockSpotifyRepository.getToken = jest.fn((code )=> {return expectedSpotifyCode}) + const result = await getToken("code",{spotifyRepository: mockSpotifyRepository}) + }) + }) + describe("GetToken invalid ", ()=>{ + it("should return error", async ()=>{ + const expectedSpotifyCode = { + error: "something" + } + mockSpotifyRepository.getToken = jest.fn((code )=> {return expectedSpotifyCode}) + const error = await catchError(async ()=>{ + await getToken("code",{spotifyRepository: mockSpotifyRepository}) + }) + expect(error.code).toBe(400) + }) + }) +}) diff --git a/test/unit/application/usecase/spotify/Search.test.js b/test/unit/application/usecase/spotify/Search.test.js index 3e6a78c..06b7810 100644 --- a/test/unit/application/usecase/spotify/Search.test.js +++ b/test/unit/application/usecase/spotify/Search.test.js @@ -21,7 +21,6 @@ describe('Search usecase', () => { "test", "filter", 4, - false, {spotifyRepository : mockSpotifyRepository,userRepository: mockUserRepository}) expect(result).toEqual(expectedSearchResult) expect(mockSpotifyRepository.getSpotifySearchList).toHaveBeenCalledWith("test","filter",4) @@ -29,12 +28,11 @@ describe('Search usecase', () => { it("should return item list sorted by popularity with user", async ()=>{ const result = await Search( "test", - "filter", + "user,track", 4, - true, {spotifyRepository : mockSpotifyRepository,userRepository: mockUserRepository}) expect(result).toEqual(expectedSearchResultWithUsers) - expect(mockSpotifyRepository.getSpotifySearchList).toHaveBeenCalledWith("test","filter",3) + expect(mockSpotifyRepository.getSpotifySearchList).toHaveBeenCalledWith("test","track",3) }) }); //see \ No newline at end of file diff --git a/test/unit/application/usecase/spotify/follow.test.js b/test/unit/application/usecase/spotify/follow.test.js new file mode 100644 index 0000000..f74b00a --- /dev/null +++ b/test/unit/application/usecase/spotify/follow.test.js @@ -0,0 +1,59 @@ +const follow = require("../../../../../lib/application/use_cases/user/follow") +const mockUserRepository = {} +const mockAccesTokenManager = {} +const mockSpotifyRepository = {} +const mockFollowRepository = {} +const catchError = require("../utils/catchError") +const serviceLocator = { + userRepository: mockUserRepository, + accessTokenManager:mockAccesTokenManager, + spotifyRepository: mockSpotifyRepository, + followRepository: mockFollowRepository +} +describe("follow use case", ()=>{ + it("should return false",async ()=>{ + mockAccesTokenManager.decode = jest.fn(()=>{return {id:1}}) + mockUserRepository.getByUser = jest.fn(() => "something") + mockSpotifyRepository.getSpotifyArtist = jest.fn(()=>"something") + mockFollowRepository.doesFollows = jest.fn(()=> true) + mockFollowRepository.follow = jest.fn(()=> {}) + mockFollowRepository.unfollow = jest.fn(()=> {}) + const result = await follow("something","something",serviceLocator) + expect(result).toBe(false); + }) + it("should return true",async ()=>{ + mockAccesTokenManager.decode = jest.fn(()=>{return {id:1}}) + mockUserRepository.getByUser = jest.fn(() => "something") + mockSpotifyRepository.getSpotifyArtist = jest.fn(()=>"something") + mockFollowRepository.doesFollows = jest.fn(()=> false) + mockFollowRepository.follow = jest.fn(()=> {}) + mockFollowRepository.unfollow = jest.fn(()=> {}) + const result = await follow("something","something",serviceLocator) + expect(result).toBe(true); + }) + it("should return error code 401",async ()=>{ + mockAccesTokenManager.decode = jest.fn(()=>{return {id:1}}) + mockUserRepository.getByUser = jest.fn(() => null) + + const result = await catchError(async ()=>{ + await follow("something","something",serviceLocator) + }) + expect(result.code).toBe(401); + }) + it("should return return invalid code 415",async ()=>{ + mockAccesTokenManager.decode = jest.fn(()=>{return {id:1}}) + mockUserRepository.getByUser = jest.fn(() => "something") + mockSpotifyRepository.getSpotifyArtist = jest.fn(()=>{ + return { + error: { + status:415, + message: "message" + } + } + }) + const result = await catchError(async ()=>{ + await follow("something","something",serviceLocator) + }) + expect(result.code).toBe(415); + }) +}) \ No newline at end of file diff --git a/test/unit/application/usecase/spotify/getAlbum.test.js b/test/unit/application/usecase/spotify/getAlbum.test.js new file mode 100644 index 0000000..0516400 --- /dev/null +++ b/test/unit/application/usecase/spotify/getAlbum.test.js @@ -0,0 +1,63 @@ +const getAlbum = require("../../../../../lib/application/use_cases/spotify/getAlbum") + +const { + albumRawOneArtist, + expectedAlbumOneArtist, + albumRawSeveralArtist, + expectedAlbumSeveralArtist, + albumRawNoArtist, + expectedAlbumNoArtist +} = require("../../../interfaces/serializers/fixtures/albumTrackFixture") +const catchError = require("../utils/catchError"); +const mockSpotifyRepository = {} + + +describe('get an album usecase', () => { + it("should return an album with one artist 1", async ()=>{ + mockSpotifyRepository.getSpotifyAlbums = jest.fn((id) =>{ + return albumRawOneArtist + }) + const result = await getAlbum( + "45i3tB9z0dgJ33olyrsLUz", + {spotifyRepository : mockSpotifyRepository} + ) + result.popularity = 0 + expect(result).toEqual(expectedAlbumOneArtist) + }) + it("should return an album with one artist 2", async ()=>{ + mockSpotifyRepository.getSpotifyAlbums = jest.fn((id) =>{ + return albumRawSeveralArtist + }) + const result = await getAlbum( + "45i3tB9z0dgJ33olyrsLUz", + {spotifyRepository : mockSpotifyRepository} + ) + result.popularity = 0 + expect(result).toEqual(expectedAlbumSeveralArtist) + }) + it("should return an album with one artist 3", async ()=>{ + mockSpotifyRepository.getSpotifyAlbums = jest.fn((id) =>{ + return albumRawNoArtist + }) + const result = await getAlbum( + "45i3tB9z0dgJ33olyrsLUz", + {spotifyRepository : mockSpotifyRepository} + ) + result.popularity = 0 + expect(result).toEqual(expectedAlbumNoArtist) + }) + it("should throw error", async ()=>{ + mockSpotifyRepository.getSpotifyAlbums = jest.fn((id) =>{ + return {error : {status: 400, message: "msg"}} + }) + + const error = await catchError(async ()=>{ + await getAlbum( + "45i3tB9z0dgJ33olyrsLUz", + {spotifyRepository : mockSpotifyRepository} + ) + }) + expect(error.code).toBe(400) + }) +}); +//see \ No newline at end of file diff --git a/test/unit/application/usecase/spotify/getSearchFilters.test.js b/test/unit/application/usecase/spotify/getSearchFilters.test.js new file mode 100644 index 0000000..f537ffa --- /dev/null +++ b/test/unit/application/usecase/spotify/getSearchFilters.test.js @@ -0,0 +1,27 @@ +const getSearchFilters = require("../../../../../lib/application/use_cases/spotify/getSearchFilters") + + +describe('getSearchFilters usecase', () => { + it("should return list of filters", async ()=>{ + const expectSearchFixture = [ + { + label: 'Musique', + id: 'track' + }, + { + label: 'Artiste', + id: 'artist' + }, + { + label: 'Album', + id: 'album' + }, + { + label: 'Utilisateur', + id: 'user' + }, + ] + const result = getSearchFilters() + expect(result).toEqual(expectSearchFixture) + }) +}); \ No newline at end of file diff --git a/test/unit/application/usecase/spotify/getTrack.test.js b/test/unit/application/usecase/spotify/getTrack.test.js new file mode 100644 index 0000000..63d35cf --- /dev/null +++ b/test/unit/application/usecase/spotify/getTrack.test.js @@ -0,0 +1,80 @@ +const getTrack = require("../../../../../lib/application/use_cases/spotify/getTrack") +const { + expectedRawTrackWithOneArtistOneAlbum, + rawTrackWithOneArtistOneAlbum, + expectedRawTrackWithServeralArtists, + rawTrackWithServeralArtists, + expectedRawTrackWithNoArtist, + rawTrackWithNoArtist, + rawTrackWithNoAlbum, + expectedRawTrackWithNoAlbum +} = require("../../../interfaces/serializers/fixtures/trackFixture") +const catchError = require("../utils/catchError"); + +const mockSpotifyRepository = {} + + +describe('get a track usecase', () => { + it("should return a track with one artist", async ()=>{ + mockSpotifyRepository.getSpotifyTracks = jest.fn((id) =>{ + return rawTrackWithOneArtistOneAlbum + }) + + const result = await getTrack( + "3YP99J8wTzG55t1cFmd6iq", + {spotifyRepository : mockSpotifyRepository} + ) + expectedRawTrackWithOneArtistOneAlbum.album.popularity = result.album.popularity + expect(result).toEqual(expectedRawTrackWithOneArtistOneAlbum) + }) + it("should return a track with several artists", async ()=>{ + mockSpotifyRepository.getSpotifyTracks = jest.fn((id) =>{ + return rawTrackWithServeralArtists + }) + + const result = await getTrack( + "3YP99J8wTzG55t1cFmd6iq", + {spotifyRepository : mockSpotifyRepository} + ) + expectedRawTrackWithOneArtistOneAlbum.album.popularity = result.album.popularity + expect(result).toEqual(expectedRawTrackWithServeralArtists) + }) + it("should return a track with no artist", async ()=>{ + mockSpotifyRepository.getSpotifyTracks = jest.fn((id) =>{ + return rawTrackWithNoArtist + }) + + const result = await getTrack( + "3YP99J8wTzG55t1cFmd6iq", + {spotifyRepository : mockSpotifyRepository} + ) + expectedRawTrackWithOneArtistOneAlbum.album.popularity = result.album.popularity + expect(result).toEqual(expectedRawTrackWithNoArtist) + }) + it("should return a track with no album", async ()=>{ + mockSpotifyRepository.getSpotifyTracks = jest.fn((id) =>{ + return rawTrackWithNoAlbum + }) + + const result = await getTrack( + "3YP99J8wTzG55t1cFmd6iq", + {spotifyRepository : mockSpotifyRepository} + ) + expect(result).toEqual(expectedRawTrackWithNoAlbum) + }) + it("should throw error", async ()=>{ + mockSpotifyRepository.getSpotifyTracks = jest.fn((id) =>{ + return {error : {status: 400, message: "msg"}} + }) + + const error = await catchError(async ()=>{ + await getTrack( + "3YP99J8wTzG55t1cFmd6iq", + {spotifyRepository : mockSpotifyRepository} + ) + }) + expect(error.code).toBe(400) + }) +}); + + diff --git a/test/unit/application/usecase/user/authWithSpotify.test.js b/test/unit/application/usecase/user/authWithSpotify.test.js new file mode 100644 index 0000000..ba6e673 --- /dev/null +++ b/test/unit/application/usecase/user/authWithSpotify.test.js @@ -0,0 +1,138 @@ +const AuthWithSpotify = require('../../../../../lib/application/use_cases/user/AuthWithSpotify') +const catchError = require("../utils/catchError") +const mockUserRepository = {} +const mockSpotifyRepository = {} +const mockAccessTokenManager = {} +const serviceLocator = { + userRepository: mockUserRepository, + spotifyRepository: mockSpotifyRepository, + accessTokenManager: mockAccessTokenManager +} +describe('AuthWithSpotifyTest', () =>{ + const mockSpotifyCode = 'code' + const email = "some@mail" + const display_name = "name" + const access_token = 'access_token' + const refresh_token = 'refresh_token' + const images = ["https://i.ytimg.com/vi/uLHdmBf1lvs/hq720.jpg?sqp=-oaymwEXCNAFEJQDSFryq4qpAwkIARUAAIhCGAE=&rs=AOn4CLAmH-kUIb43CviOetK-ZjGl0AnSog"] + afterEach(()=>{ + jest.clearAllMocks(); + }) + beforeEach(() => { + mockUserRepository.updateUser = jest.fn(() => "ok") + }) + it("should throw error 400", async ()=>{ + mockSpotifyRepository.getToken = jest.fn(()=> { + return {error: 'some error'} + }) + const error = await catchError(async () =>{ + await AuthWithSpotify(mockSpotifyCode, 'callback',serviceLocator) + }) + expect(error.code).toBe(400) + + }) + it("should throw error 403 1", async ()=>{ + mockSpotifyRepository.getToken = jest.fn(()=> { + return { + access_token, + refresh_token + } + }) + mockSpotifyRepository.getAccountData = jest.fn(()=> { + return {email,display_name,images} + }) + mockUserRepository.getByEmailOrPseudo = jest.fn(()=> { + return { + confirmed: false + } + }) + const error = await catchError(async () =>{ + await AuthWithSpotify(mockSpotifyCode, 'callback',serviceLocator) + }) + expect(error.code).toBe(403) + }) + it("should throw error 403 2", async ()=>{ + mockSpotifyRepository.getToken = jest.fn(()=> { + return { + access_token, + refresh_token + } + }) + mockSpotifyRepository.getAccountData = jest.fn(()=> { + return {email,display_name,images} + }) + mockUserRepository.getByEmailOrPseudo = jest.fn(()=> { + return { + confirmed: true, + } + }) + mockAccessTokenManager.generate = jest.fn(() => 'expected_token') + const error = await catchError(async () =>{ + await AuthWithSpotify(mockSpotifyCode, 'callback',serviceLocator) + }) + expect(error.code).toBe(403) + }) + it("should return auth token", async ()=>{ + mockSpotifyRepository.getToken = jest.fn(()=> { + return { + access_token, + refresh_token + } + }) + mockSpotifyRepository.getAccountData = jest.fn(()=> { + return {email,display_name,images} + }) + mockUserRepository.getByEmailOrPseudo = jest.fn(()=> { + return { + confirmed: true, + refresh_token: 'something' + } + }) + mockAccessTokenManager.generate = jest.fn(() => 'expected_token') + const result = await AuthWithSpotify(mockSpotifyCode, 'callback',serviceLocator) + expect(result.email).toBe(email) + expect(result.token).toBe("expected_token") + expect(mockAccessTokenManager.generate).toHaveBeenCalled() + }) + it("should return confirm token 1", async ()=>{ + mockSpotifyRepository.getToken = jest.fn(()=> { + return { + access_token, + refresh_token + } + }) + mockSpotifyRepository.getAccountData = jest.fn(()=> { + return {email,display_name,images} + }) + mockUserRepository.getByEmailOrPseudo = jest.fn(()=> { + return null + }) + mockUserRepository.persist = jest.fn(() => 'ok') + const result = await AuthWithSpotify(mockSpotifyCode, 'callback',serviceLocator) + expect(result.confirmToken).not.toBeNull() + expect(mockUserRepository.persist).toHaveBeenCalled() + }) + it("should return confirm token 2", async ()=>{ + mockSpotifyRepository.getToken = jest.fn(()=> { + return { + access_token, + refresh_token + } + }) + mockSpotifyRepository.getAccountData = jest.fn(()=> { + return {email,display_name,images} + }) + mockUserRepository.getByEmailOrPseudo = jest.fn(()=> { + return { + auth_with_spotify: true, + confirm_token: "expected_token", + refresh_token: "something" + } + }) + mockUserRepository.persist = jest.fn(() => 'ok') + const result = await AuthWithSpotify(mockSpotifyCode, 'callback',serviceLocator) + expect(result.confirmToken).toBe("expected_token") + expect(mockUserRepository.persist).not.toHaveBeenCalled() + }) + +}) \ No newline at end of file diff --git a/test/unit/application/usecase/user/completeAccount.test.js b/test/unit/application/usecase/user/completeAccount.test.js new file mode 100644 index 0000000..ea5bcff --- /dev/null +++ b/test/unit/application/usecase/user/completeAccount.test.js @@ -0,0 +1,81 @@ +const completeAccount = require('../../../../../lib/application/use_cases/user/CompleteAccount') +const catchError = require("../utils/catchError") +const mockUserRepository = {} +const mockDocumentRepository = {} +const mockAccessTokenManager = {} +const serviceLocator = { + userRepository: mockUserRepository, + documentRepository: mockDocumentRepository, + accessTokenManager: mockAccessTokenManager +} +describe('CompleteAccount', () =>{ + const pseudo= 'testPseudo' + const alias= 'testAlias' + const bio= 'testBio' + const confirm_token= 'confirmToken' + const photo = "otherpath/to/file" + const email = "testemail@gmail.com" + const photo_temporaire = "path/to/file" + const confirmed = false + const id_utilisateur = 1 + const mockToken = 'token' + const mockUser = { + id_utilisateur,email,photo_temporaire,confirmed,confirm_token + } + mockAccessTokenManager.generate = jest.fn(() => mockToken) + mockDocumentRepository.deleteFile = jest.fn(()=>{}) + mockUserRepository.updateUser = jest.fn((user)=>user) + afterEach(()=>{ + jest.clearAllMocks(); + }) + it("should return user with replaced photo ", async ()=>{ + mockUserRepository.getByEmailOrPseudo = jest.fn(()=>null) + mockUserRepository.getByConfirmToken = jest.fn(()=>{ + return {...mockUser} + }) + const user = await completeAccount(pseudo,alias,bio,photo,confirm_token,serviceLocator) + expect(user.token).toBe(mockToken) + expect(user.email).toBe(email) + + expect(mockDocumentRepository.deleteFile).toHaveBeenCalledTimes(1) + }) + it("should return user without replaced photo ", async ()=>{ + mockUserRepository.getByEmailOrPseudo = jest.fn(()=>null) + mockUserRepository.getByConfirmToken = jest.fn(()=>{ + return {...mockUser} + }) + const user = await completeAccount(pseudo,alias,bio,null,confirm_token,serviceLocator) + expect(user.token).toBe(mockToken) + expect(user.email).toBe(email) + + expect(mockDocumentRepository.deleteFile).toHaveBeenCalledTimes(0) + }) + it("should return user without alias", async ()=>{ + mockUserRepository.getByEmailOrPseudo = jest.fn(()=>null) + mockUserRepository.getByConfirmToken = jest.fn(()=>{ + return {...mockUser} + }) + const user = await completeAccount(pseudo,null,bio,null,confirm_token,serviceLocator) + expect(user.token).toBe(mockToken) + expect(user.email).toBe(email) + + }) + it("should throw error 403 user already exists", async ()=>{ + mockUserRepository.getByConfirmToken = jest.fn(()=>{ + return {...mockUser} + }) + mockUserRepository.getByEmailOrPseudo = jest.fn(()=>'something') + const error = await catchError(async ()=>{ + await completeAccount(pseudo,null,bio,null,confirm_token,serviceLocator) + }) + expect(error.code).toBe(403) + }) + it("should throw error 400 invalid token", async ()=>{ + mockUserRepository.getByConfirmToken = jest.fn(()=>null) + mockUserRepository.getByEmailOrPseudo = jest.fn(()=>null) + const error = await catchError(async ()=>{ + await completeAccount(pseudo,null,bio,null,confirm_token,serviceLocator) + }) + expect(error.code).toBe(400) + }) +}) \ No newline at end of file diff --git a/test/unit/application/usecase/user/createUser.test.js b/test/unit/application/usecase/user/createUser.test.js new file mode 100644 index 0000000..a7c0ff9 --- /dev/null +++ b/test/unit/application/usecase/user/createUser.test.js @@ -0,0 +1,37 @@ +const createUser = require('../../../../../lib/application/use_cases/user/CreateUser') +const catchError = require("../utils/catchError") +const mockUserRepository = {} +const mockMailRepository = {} +const email = "testemail@gmail.com" +const display_name = "display_name" +const image = [{url:"testurl"}] +describe("createUser", ()=>{ + afterEach(()=>{ + jest.clearAllMocks(); + }) + mockUserRepository.getByEmailOrPseudo = jest.fn((email,pseudo)=> null) + mockUserRepository.persist = jest.fn((user) => { + user.id_utilisateur = 1 + return user + }) + mockMailRepository.send = jest.fn(option => null) + + const serviceLocator = { + userRepository: mockUserRepository, + mailRepository: mockMailRepository + } + it('should return user with email inscription', async () => { + + const user = await createUser(email,"password",serviceLocator) + expect(user.email).toBe(email) + expect(user.confirmed).toBe(false) + !expect(user.password).not.toBe(null) + }); + it('should throw error 403 email already exists', async () => { + mockUserRepository.getByEmailOrPseudo = jest.fn((email,pseudo)=> 'something') + const error = await catchError(async ()=>{ + await createUser(email,"password",serviceLocator) + }) + expect(error.code).toBe(403); + }); +}) \ No newline at end of file diff --git a/test/unit/application/usecase/user/getAccessToken.test.js b/test/unit/application/usecase/user/getAccessToken.test.js new file mode 100644 index 0000000..6fbe806 --- /dev/null +++ b/test/unit/application/usecase/user/getAccessToken.test.js @@ -0,0 +1,93 @@ +const UserRepository = require("../../../../../lib/infrastructure/repositories/interfaces/UserRepositoryAbstract"); +const getAccessToken = require("../../../../../lib/application/use_cases/security/GetAccessToken"); +const bcrypt = require("bcrypt"); +const catchError = require("../utils/catchError"); +require("dotenv").config(); + +describe("getAccessToken", () => { + it("should generate access token", async () => { + const passwordTest = "passwordTest"; + const persistedUserCrypted = { + id_utilisateur: "id", + pseudo: "pseudo", + email: "email", + alias: "alias", + bio: "bio", + photo: "path/to/file", + photo_temporaire: "path/to/file", + password: await bcrypt.hash(passwordTest, 10), + token: "token", + is_private: false, + id_role: 1, + ban_until: new Date("10-06-2003"), + }; + const expectedAccessToken = { + user: { + id_utilisateur: "id", + pseudo: "pseudo", + email: "email", + alias: "alias", + bio: "bio", + photo: `${process.env.API_URL}/path/to/file`, + photo_temporaire: `${process.env.API_URL}/path/to/file`, + type: "user", + is_private: false, + id_role: 1, + ban_until: new Date("10-06-2003"), + }, + token: 1, + }; + const mockUserRepository = new UserRepository(); + const mockAccessTokenManager = {}; + mockAccessTokenManager.generate = jest.fn((uid) => 1); + mockUserRepository.getByIdent = jest.fn((ident) => persistedUserCrypted); + expect( + await getAccessToken(persistedUserCrypted.pseudo, passwordTest, { + userRepository: mockUserRepository, + accessTokenManager: mockAccessTokenManager, + }) + ).toEqual(expectedAccessToken); + }); + + it("should throw an error because the password is incorrect", async () => { + const passwordTest = "passwordTest"; + const persistedUserCrypted = { + id_utilisateur: "id", + pseudo: "pseudo", + email: "email", + alias: "alias", + bio: "bio", + photo: "path/to/file", + photo_temporaire: "path/to/file", + password: await bcrypt.hash(passwordTest, 10), + token: "token", + id_role: 1, + ban_until: new Date("10-06-2003"), + }; + const mockUserRepository = new UserRepository(); + const mockAccessTokenManager = {}; + mockUserRepository.getByIdent = jest.fn((ident) => persistedUserCrypted); + const error = await catchError(async () => { + await getAccessToken(persistedUserCrypted.pseudo, "badPassword", { + userRepository: mockUserRepository, + accessTokenManager: mockAccessTokenManager, + }); + }); + + expect(error.code).toBe(401); + }); + + it("should throw an error because the user doesnt exists", async () => { + const mockUserRepository = new UserRepository(); + const mockAccessTokenManager = {}; + mockUserRepository.getByIdent = jest.fn((ident) => null); + const error = await catchError(async () => { + await getAccessToken("", "", { + userRepository: mockUserRepository, + accessTokenManager: mockAccessTokenManager, + }); + }); + console.log(error); + expect(error.code).toBe(401); + }); +}); diff --git a/test/unit/application/usecase/user/getOeuvreFav.test.js b/test/unit/application/usecase/user/getOeuvreFav.test.js new file mode 100644 index 0000000..46150b2 --- /dev/null +++ b/test/unit/application/usecase/user/getOeuvreFav.test.js @@ -0,0 +1,64 @@ +const getOeuvresFav = require("../../../../../lib/application/use_cases/user/getOeuvresFav.js") +const throwStatusCode = require("../../../../../lib/application/use_cases/utils/throwStatusCode.js") +const catchError = require("../utils/catchError.js") + +describe("getOeuvresFav Test", () => { + const userToken = 'token' + + const mockAccesTokenManager = {} + const mockSpotifyRepository = {} + const mockUserRepository = {} + const mockOeuvreFavRepository = {} + const serviceLocator = { + userRepository: mockUserRepository, + oeuvreFavRepository: mockOeuvreFavRepository, + accessTokenManager: mockAccesTokenManager, + spotifyRepository: mockSpotifyRepository, + } + + describe("invalid and valid cases", () => { + + it("should throw error bad auth token", async () => { + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((userToken) => null) + const error = await catchError(async () => { + await getOeuvresFav(userToken, serviceLocator) + }) + expect(error.code).toBe(401) + }) + + it("should send an array with oeuvre fav id", async () => { + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1 + } + }) + mockOeuvreFavRepository.getOeuvresFav = jest.fn((userToken) => { + return [1, 2, 3] + }) + + const result = await getOeuvresFav(userToken, serviceLocator) + + expect(result).toEqual([1, 2, 3]) + expect(mockOeuvreFavRepository.getOeuvresFav).toHaveBeenCalledTimes(1) + }) + + it("should send a empty array", async () => { + mockAccesTokenManager.decode = jest.fn((userToken) => 1) + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1 + } + }) + mockOeuvreFavRepository.getOeuvresFav = jest.fn((userToken) => { + return [] + }) + + const result = await getOeuvresFav(userToken, serviceLocator) + + expect(result).toEqual([]) + expect(mockOeuvreFavRepository.getOeuvresFav).toHaveBeenCalledTimes(1) + }) + }) +}) diff --git a/test/unit/application/usecase/user/getUserByConfirmToken.test.js b/test/unit/application/usecase/user/getUserByConfirmToken.test.js new file mode 100644 index 0000000..c0c4f4f --- /dev/null +++ b/test/unit/application/usecase/user/getUserByConfirmToken.test.js @@ -0,0 +1,49 @@ +const getUserByConfirmToken = require("../../../../../lib/application/use_cases/user/getUserByConfirmToken"); +const mockUser = { + id_utilisateur: 1, + pseudo: "pseudo", + alias: "alias", + email: "testemail@gmail.com", + photo: "path/to/file", + photo_temporaire: "path/to/file", + token: "token", + refresh_token: "refreshToken", + password: "password", + id_role: 1, + ban_until: null, + confirmed: false, + confirm_token: "fezfezgezrhgez", + type: "user", +}; +const expectedUser = { + id_utilisateur: 1, + pseudo: "pseudo", + alias: "alias", + email: "testemail@gmail.com", + photo: `${process.env.API_URL}/path/to/file`, + photo_temporaire: `${process.env.API_URL}/path/to/file`, + id_role: 1, + ban_until: null, + type: "user", +}; +const mockUserRepository = {}; +describe("getUserByPseudo", () => { + afterEach(async () => { + jest.clearAllMocks(); + }); + it("should return Public User", async () => { + console.log("test"); + mockUserRepository.getByConfirmToken = jest.fn(() => mockUser); + const result = await getUserByConfirmToken("test", { + userRepository: mockUserRepository, + }); + expect(result).toEqual(expectedUser); + }); + it("should return null", async () => { + mockUserRepository.getByConfirmToken = jest.fn(() => null); + const result = await getUserByConfirmToken("test", { + userRepository: mockUserRepository, + }); + expect(result).toBeNull(); + }); +}); diff --git a/test/unit/application/usecase/user/getUserByPseudo.test.js b/test/unit/application/usecase/user/getUserByPseudo.test.js new file mode 100644 index 0000000..fa1e35d --- /dev/null +++ b/test/unit/application/usecase/user/getUserByPseudo.test.js @@ -0,0 +1,49 @@ +require("dotenv").config(); +const getUserBuPseudo = require("../../../../../lib/application/use_cases/user/getUserByPseudo"); +const mockUser = { + id_utilisateur: 1, + pseudo: "pseudo", + alias: "alias", + email: "testemail@gmail.com", + photo: "path/to/file", + photo_temporaire: "path/to/file", + token: "token", + refresh_token: "refreshToken", + password: "password", + id_role: 1, + ban_until: null, + confirmed: false, + confirm_token: "fezfezgezrhgez", + type: "user", +}; +const expectedUser = { + id_utilisateur: 1, + pseudo: "pseudo", + alias: "alias", + email: "testemail@gmail.com", + photo: `${process.env.API_URL}/path/to/file`, + photo_temporaire: `${process.env.API_URL}/path/to/file`, + id_role: 1, + ban_until: null, + type: "user", +}; +const mockUserRepository = {}; +describe("getUserByPseudo", () => { + afterEach(async () => { + jest.clearAllMocks(); + }); + it("should return Public User", async () => { + mockUserRepository.getByEmailOrPseudo = jest.fn(() => mockUser); + const result = await getUserBuPseudo("test", { + userRepository: mockUserRepository, + }); + expect(result).toEqual(expectedUser); + }); + it("should return null", async () => { + mockUserRepository.getByEmailOrPseudo = jest.fn(() => null); + const result = await getUserBuPseudo("test", { + userRepository: mockUserRepository, + }); + expect(result).toBeNull(); + }); +}); diff --git a/test/unit/application/usecase/user/oeuvreFav.test.js b/test/unit/application/usecase/user/oeuvreFav.test.js new file mode 100644 index 0000000..30afe5e --- /dev/null +++ b/test/unit/application/usecase/user/oeuvreFav.test.js @@ -0,0 +1,239 @@ +const OeuvreFav = require("../../../../../lib/application/use_cases/user/oeuvreFav.js"); +const throwStatusCode = require("../../../../../lib/application/use_cases/utils/throwStatusCode.js"); +const catchError = require("../utils/catchError.js"); +const { + albumRawOneArtist, +} = require("../../../interfaces/serializers/fixtures/albumFixture.js"); +const { + rawTrackWithOneArtist, +} = require("../../../interfaces/serializers/fixtures/albumTrackFixture.js"); + +describe("OeuvreFav Test", () => { + const idOeuvre = "idOeuvre"; + const userToken = "token"; + + const mockAccesTokenManager = {}; + const mockSpotifyRepository = {}; + const mockUserRepository = {}; + const mockOeuvreFavRepository = {}; + const serviceLocator = { + userRepository: mockUserRepository, + oeuvreFavRepository: mockOeuvreFavRepository, + accessTokenManager: mockAccesTokenManager, + spotifyRepository: mockSpotifyRepository, + }; + + describe("invalid and valid cases", () => { + it("should throw error bad auth token", async () => { + mockAccesTokenManager.decode = jest.fn((userToken) => 1); + mockUserRepository.getByUser = jest.fn((userToken) => null); + const error = await catchError(async () => { + await OeuvreFav(userToken, "track", idOeuvre, serviceLocator); + }); + expect(error.code).toBe(401); + }); + + // aucune oeuvre de trouve + it("should throw incrorrect id oeuvre error", async () => { + mockAccesTokenManager.decode = jest.fn((userToken) => 1); + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1, + }; + }); + + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + throwStatusCode(404, "invalid id"); + }); + const error = await catchError(async () => { + await OeuvreFav(userToken, "track", idOeuvre, serviceLocator); + }); + expect(error.code).toBe(404); + + expect(mockSpotifyRepository.getOeuvre).toHaveBeenCalledTimes(1); + }); + + // plus de 3 oeuvres favorites avec album + it("should throw maximal add", async () => { + mockAccesTokenManager.decode = jest.fn((userToken) => 1); + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1, + }; + }); + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + return Promise.resolve(albumRawOneArtist); + }); + mockOeuvreFavRepository.oeuvreFavExists = jest.fn( + (id_utilisateur, idOeuvre) => false + ); + mockOeuvreFavRepository.ajoutPossible = jest.fn( + (id_utilisateur, idOeuvre) => false + ); + + const error = await catchError(async () => { + await OeuvreFav(userToken, "track", idOeuvre, serviceLocator); + }); + + expect(mockOeuvreFavRepository.ajoutPossible).toHaveBeenCalledTimes(1); + expect(error.code).toBe(403); + }); + + // plus de 3 oeuvres favorites avec track + it("should throw maximal add", async () => { + mockAccesTokenManager.decode = jest.fn((userToken) => 1); + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1, + }; + }); + + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + return Promise.resolve(rawTrackWithOneArtist); + }); + mockOeuvreFavRepository.oeuvreFavExists = jest.fn( + (id_utilisateur, idOeuvre) => false + ); + mockOeuvreFavRepository.ajoutPossible = jest.fn( + (id_utilisateur, idOeuvre) => false + ); + + const error = await catchError(async () => { + await OeuvreFav(userToken, "track", idOeuvre, serviceLocator); + }); + + expect(mockSpotifyRepository.getOeuvre).toHaveBeenCalledTimes(1); + + expect(mockOeuvreFavRepository.ajoutPossible).toHaveBeenCalledTimes(1); + expect(error.code).toBe(403); + }); + + // ajout réussie avec album + it("should put an album", async () => { + mockAccesTokenManager.decode = jest.fn((userToken) => 1); + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1, + }; + }); + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + return Promise.resolve(albumRawOneArtist); + }); + mockOeuvreFavRepository.oeuvreFavExists = jest.fn( + (id_utilisateur, idOeuvre) => false + ); + mockOeuvreFavRepository.ajoutPossible = jest.fn( + (id_utilisateur, idOeuvre) => true + ); + mockOeuvreFavRepository.addOeuvrefav = jest.fn(); + + const result = await OeuvreFav( + userToken, + "track", + idOeuvre, + serviceLocator + ); + + expect(mockOeuvreFavRepository.ajoutPossible).toHaveBeenCalledTimes(1); + expect(mockOeuvreFavRepository.addOeuvrefav).toHaveBeenCalledTimes(1); + expect(result).toEqual(true); + }); + + // ajout réussie avec track + it("should put an track", async () => { + mockAccesTokenManager.decode = jest.fn((userToken) => 1); + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1, + }; + }); + + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + return Promise.resolve(rawTrackWithOneArtist); + }); + mockOeuvreFavRepository.oeuvreFavExists = jest.fn( + (id_utilisateur, idOeuvre) => false + ); + mockOeuvreFavRepository.ajoutPossible = jest.fn( + (id_utilisateur, idOeuvre) => true + ); + mockOeuvreFavRepository.addOeuvrefav = jest.fn(); + + const result = await OeuvreFav( + userToken, + "track", + idOeuvre, + serviceLocator + ); + + expect(mockSpotifyRepository.getOeuvre).toHaveBeenCalledTimes(1); + + expect(mockOeuvreFavRepository.ajoutPossible).toHaveBeenCalledTimes(1); + expect(mockOeuvreFavRepository.addOeuvrefav).toHaveBeenCalledTimes(1); + expect(result).toEqual(true); + }); + + // supression réussite d'un album + it("should remove an album ", async () => { + mockAccesTokenManager.decode = jest.fn((userToken) => 1); + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1, + }; + }); + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + return Promise.resolve(albumRawOneArtist); + }); + mockOeuvreFavRepository.oeuvreFavExists = jest.fn( + (id_utilisateur, idOeuvre) => true + ); + mockOeuvreFavRepository.ajoutPossible = jest.fn(); + mockOeuvreFavRepository.addOeuvrefav = jest.fn(); + mockOeuvreFavRepository.deleteOeuvrefav = jest.fn(); + + const result = await OeuvreFav( + userToken, + "track", + idOeuvre, + serviceLocator + ); + + expect(mockOeuvreFavRepository.ajoutPossible).not.toHaveBeenCalled(); + expect(mockOeuvreFavRepository.deleteOeuvrefav).toHaveBeenCalledTimes(1); + expect(result).toEqual(false); + }); + + // supression réussite d'une track + it("should remove a track ", async () => { + mockAccesTokenManager.decode = jest.fn((userToken) => 1); + mockUserRepository.getByUser = jest.fn((userToken) => { + return { + id_utilisateur: 1, + }; + }); + + mockSpotifyRepository.getOeuvre = jest.fn((idOeuvre) => { + return Promise.resolve(rawTrackWithOneArtist); + }); + mockOeuvreFavRepository.oeuvreFavExists = jest.fn( + (id_utilisateur, idOeuvre) => true + ); + mockOeuvreFavRepository.ajoutPossible = jest.fn(); + mockOeuvreFavRepository.addOeuvrefav = jest.fn(); + mockOeuvreFavRepository.deleteOeuvrefav = jest.fn(); + + const result = await OeuvreFav( + userToken, + "track", + idOeuvre, + serviceLocator + ); + + expect(mockSpotifyRepository.getOeuvre).toHaveBeenCalledTimes(1); + + expect(mockOeuvreFavRepository.ajoutPossible).not.toHaveBeenCalled(); + expect(mockOeuvreFavRepository.deleteOeuvrefav).toHaveBeenCalledTimes(1); + expect(result).toEqual(false); + }); + }); +}); diff --git a/test/unit/application/usecase/user/resetPassword.test.js b/test/unit/application/usecase/user/resetPassword.test.js new file mode 100644 index 0000000..552217c --- /dev/null +++ b/test/unit/application/usecase/user/resetPassword.test.js @@ -0,0 +1,26 @@ +const mockUserRepository = {} +const resetPassword = require("../../../../../lib/application/use_cases/user/resetPassword") +const serviceLocator = { + userRepository: mockUserRepository +} +require("dotenv").config() +const catchError = require("../utils/catchError") +const bcrypt = require("bcrypt"); +describe("sendResetEmail", ()=>{ + it("should return true",async ()=>{ + mockUserRepository.getByResetToken = jest.fn(()=>{return {id:"test"}}) + mockUserRepository.updateUser = jest.fn(()=>{}) + const result = await resetPassword("testPassword","token",serviceLocator) + expect(result.password).not.toBe("test"); + expect(mockUserRepository.updateUser).toHaveBeenCalledTimes(1) + }) + it("should return error",async ()=>{ + mockUserRepository.getByResetToken = jest.fn(()=>{}) + mockUserRepository.updateUser = jest.fn(()=>{}) + const error = await catchError(async () =>{ + await resetPassword("testPassword","token",serviceLocator) + }) + expect(error.code).toBe(400) + }) + +}) \ No newline at end of file diff --git a/test/unit/application/usecase/user/sendResetEmail.test.js b/test/unit/application/usecase/user/sendResetEmail.test.js new file mode 100644 index 0000000..fc36b26 --- /dev/null +++ b/test/unit/application/usecase/user/sendResetEmail.test.js @@ -0,0 +1,29 @@ +const mockMailRepository = {} +const mockUserRepository = {} +const sendResetEmail = require("../../../../../lib/application/use_cases/user/sendResetEmail") +const serviceLocator = { + mailRepository: mockMailRepository, + userRepository: mockUserRepository +} +describe("sendResetEmail", ()=>{ + it("should return true",async ()=>{ + mockMailRepository.send = jest.fn(()=>{}) + mockUserRepository.getByEmailOrPseudo = jest.fn(()=>{return {reset_token: 1,confirmed: true}}) + mockUserRepository.updateUser = jest.fn(()=>{}) + + expect(await sendResetEmail("testemail",serviceLocator)).toBe(true); + expect(mockMailRepository.send).toHaveBeenCalledTimes(1) + }) + it("should return false because the user is not confirmed",async ()=>{ + mockMailRepository.send = jest.fn(()=>{}) + mockUserRepository.getByEmailOrPseudo = jest.fn(()=>{return {reset_token: 1,confirmed:false}}) + mockUserRepository.updateUser = jest.fn(()=>{}) + expect(await sendResetEmail("testemail",serviceLocator)).toBe(false); + }) + it("should return false because the user doesn't exists",async ()=>{ + mockMailRepository.send = jest.fn(()=>{}) + mockUserRepository.getByEmailOrPseudo = jest.fn(()=>{return null}) + mockUserRepository.updateUser = jest.fn(()=>{}) + expect(await sendResetEmail("testemail",serviceLocator)).toBe(false); + }) +}) \ No newline at end of file diff --git a/test/unit/application/usecase/user/uploadPreview.test.js b/test/unit/application/usecase/user/uploadPreview.test.js new file mode 100644 index 0000000..72dae52 --- /dev/null +++ b/test/unit/application/usecase/user/uploadPreview.test.js @@ -0,0 +1,91 @@ +mockAccessTokenManager = {} +mockUserRepository = {} +mockDocumentRepository = {} +mockID = 12424 +const uploadPreview = require("../../../../../lib/application/use_cases/user/uploadPreview") +const catchError = require("../utils/catchError"); +const mockFile = { + hapi: { + filename: "test.png", + headers: { + 'content-type' : 'image/png' + } + }, + _data: "testDFata" +} +mockAccessTokenManager.decode = jest.fn((token) => mockID) +mockUserRepository.getByUser = jest.fn((id) => 'something') +mockUserRepository.addPreviewPath = jest.fn((id,path) => {}) +mockDocumentRepository.deleteFile = jest.fn((path) => path) +mockUserRepository.getPreviewPath = jest.fn((id) => null) +mockDocumentRepository.uploadFile = jest.fn((path,file) => path) +describe("uploadPreview usecase", ()=>{ + describe("valid upload preview cases", ()=>{ + it('should upload the image without deleting previous file',async() =>{ + mockUserRepository.getPreviewPath = jest.fn((id) => null) + const path = await uploadPreview(mockFile,'token', { + accessTokenManager: mockAccessTokenManager, + userRepository: mockUserRepository, + documentRepository: mockDocumentRepository + }) + expect(path).not.toBeNull() + expect(mockDocumentRepository.deleteFile).toHaveBeenCalledTimes(0) + } ) + it('should upload the image and deleting previous file',async() =>{ + mockUserRepository.getPreviewPath = jest.fn((id) => 'path') + const path = await uploadPreview(mockFile,'token', { + accessTokenManager: mockAccessTokenManager, + userRepository: mockUserRepository, + documentRepository: mockDocumentRepository + }) + expect(path).not.toBeNull() + expect(mockDocumentRepository.deleteFile).toHaveBeenCalledTimes(1) + } ) + }) + describe("invalid upload preview cases", ()=> { + + const invalidMockFile = { + hapi: { + filename: "test.rpg", + headers: { + 'content-type' : 'image/rpg' + } + }, + _data: "testDFata" + } + it('should throw 415 error',async() =>{ + const error = await catchError(async ()=>{ + await uploadPreview(invalidMockFile,'token', { + accessTokenManager: mockAccessTokenManager, + userRepository: mockUserRepository, + documentRepository: mockDocumentRepository + }) + }) + expect(error.code).toBe(415) + } ) + it('should throw 500 error',async() =>{ + mockDocumentRepository.uploadFile = jest.fn((path,file) => null) + const error = await catchError(async ()=>{ + await uploadPreview(mockFile,'token', { + accessTokenManager: mockAccessTokenManager, + userRepository: mockUserRepository, + documentRepository: mockDocumentRepository + }) + }) + expect(error.code).toBe(500) + + } ) + it('should throw 401 error',async() =>{ + mockUserRepository.getByUser = jest.fn((id) => null) + const error = await catchError(async ()=>{ + await uploadPreview(mockFile,'token', { + accessTokenManager: mockAccessTokenManager, + userRepository: mockUserRepository, + documentRepository: mockDocumentRepository + }) + }) + expect(error.code).toBe(401) + } ) + }) + +}) \ No newline at end of file diff --git a/test/unit/application/usecase/user/user.test.js b/test/unit/application/usecase/user/user.test.js deleted file mode 100644 index 39572ad..0000000 --- a/test/unit/application/usecase/user/user.test.js +++ /dev/null @@ -1,149 +0,0 @@ - -const createUser = require('../../../../../lib/application/use_cases/user/CreateUser') -const UserRepository = require('../../../../../lib/infrastructure/repositories/interfaces/UserRepositoryAbstract'); -const getAccessToken = require('../../../../../lib/application/use_cases/security/GetAccessToken') -const User = require('../../../../../lib/domain/model/User') -const bcrypt = require("bcrypt"); -const {use} = require("bcrypt/promises"); -const persistedUser = new User( - 1, - 'testPeudo', - 'testEmail@gmail.com', - 'test_alias', - 'testbio', - 'passwordtest', - 'spotifyToken', - 2, - 1) - - -describe('createUser', () =>{ - it("should create an user", async () =>{ - const mockUserRepository = new UserRepository(); - mockUserRepository.persist = jest.fn(() => persistedUser) - mockUserRepository.getByEmailOrPseudo = jest.fn((email,pseudo) => null) - const user = await createUser( - 'testPeudo', - 'testEmail@gmail.com', - 'test_alias', - 'testbio', - 'passwordtest', - 'spotifyToken', - {userRepository : mockUserRepository} - ) - const mockResult = mockUserRepository.persist.mock.calls[0][0] - expect(mockUserRepository.persist).toHaveBeenCalled() - expect(mockResult.id).toBe(null) - expect(mockResult.pseudo).toBe(persistedUser.pseudo) - expect(mockResult.email).toBe(persistedUser.email) - expect(mockResult.alias).toBe(persistedUser.alias) - expect(mockResult.bio).toBe(persistedUser.bio) - expect(mockResult.spotifyToken).toBe(persistedUser.spotifyToken) - expect(mockResult.id_role).toBe(2) - expect(mockResult.id_etat).toBe(1) - expect(mockResult.password).not.toBe(user.password) - }) - it('should throw an error when an user with the same pseudo exists', async ()=>{ - const mockUserRepository = new UserRepository(); - mockUserRepository.persist = jest.fn(() => persistedUser) - mockUserRepository.getByEmailOrPseudo = jest.fn((email,pseudo) => persistedUser) - await expect(createUser( - persistedUser.pseudo, - '', - '', - '', - '', - '', - {userRepository: mockUserRepository} - )).rejects.toThrow('Email ou Pseudo déjà existant') - }) - it('should throw an error when an user with the same email exists', async ()=>{ - const mockUserRepository = new UserRepository(); - mockUserRepository.persist = jest.fn(() => persistedUser) - mockUserRepository.getByEmailOrPseudo = jest.fn((email,pseudo) => persistedUser) - await expect(createUser( - '', - persistedUser.email, - '', - '', - '', - '', - {userRepository: mockUserRepository} - )).rejects.toThrow('Email ou Pseudo déjà existant') - }) - -}) - -describe('getAccessToken', () =>{ - it('should generate access token', async () =>{ - const passwordTest = 'passwordTest' - const persistedUserCrypted = new User( - 1, - 'testPeudo', - 'testEmail@gmail.com', - 'test_alias', - 'testbio', - await bcrypt.hash(passwordTest,10), - 'spotifyToken', - 2, - 1) - const mockUserRepository = new UserRepository(); - const mockAccessTokenManager = {}; - mockAccessTokenManager.generate = jest.fn((uid) => 1) - mockUserRepository.getByIdent = jest.fn((ident) => persistedUserCrypted) - expect( - await getAccessToken( - persistedUserCrypted.pseudo, - passwordTest, - { - userRepository : mockUserRepository, - accessTokenManager: mockAccessTokenManager - } - ) - ).toBe(1) - }) - - - it('should throw an error because the password is incorrect', async () =>{ - const passwordTest = 'passwordTest' - const persistedUserCrypted = new User( - 1, - 'testPeudo', - 'testEmail@gmail.com', - 'test_alias', - 'testbio', - await bcrypt.hash(passwordTest,10), - 'spotifyToken', - 2, - 1) - const mockUserRepository = new UserRepository(); - const mockAccessTokenManager = {}; - mockUserRepository.getByIdent = jest.fn((ident) => persistedUserCrypted) - await expect( - getAccessToken( - persistedUserCrypted.pseudo, - 'badPassword', - { - userRepository : mockUserRepository, - accessTokenManager: mockAccessTokenManager - } - ) - ).rejects.toThrow('Bad credentials') - }) - - it('should throw an error because the user doesnt exists', async () =>{ - const mockUserRepository = new UserRepository(); - const mockAccessTokenManager = {}; - mockUserRepository.getByIdent = jest.fn((ident) => null) - await expect( - getAccessToken( - '', - '', - { - userRepository : mockUserRepository, - accessTokenManager: mockAccessTokenManager - } - ) - ).rejects.toThrow('Bad credentials') - }) -}) \ No newline at end of file diff --git a/test/unit/application/usecase/utils/catchError.js b/test/unit/application/usecase/utils/catchError.js new file mode 100644 index 0000000..3f78d81 --- /dev/null +++ b/test/unit/application/usecase/utils/catchError.js @@ -0,0 +1,8 @@ +module.exports = async (action) => { + try{ + await action() + }catch (error){ + return error + } + return null +} \ No newline at end of file diff --git a/test/unit/infrastructure/repositories/DocumentRepository.test.js b/test/unit/infrastructure/repositories/DocumentRepository.test.js new file mode 100644 index 0000000..d510cb3 --- /dev/null +++ b/test/unit/infrastructure/repositories/DocumentRepository.test.js @@ -0,0 +1,25 @@ +const DocumentRepositoryTest = require("../../../../lib/infrastructure/repositories/DocumentRepository") +const createUser = require("../../../../lib/application/use_cases/user/CompleteAccount"); +let filePath +const documentRepository = new DocumentRepositoryTest(); +const mockFile = { + hapi: { + filename: "test.png" + }, + _data: "testDFata" +} +describe('document repository', ()=>{ + describe('document repository with no problem', ()=>{ + it('should create file without problem', async () =>{ + filePath= await documentRepository.uploadFile("test/unit/infrastructure/repositories/testFolder", mockFile) + }) + it('should creadzadte file without problem', async () =>{ + documentRepository.deleteFile(filePath) + }) + }) + describe('document repository with errors', () =>{ + it("shouldn't create file", async () =>{ + await expect(documentRepository.uploadFile("test/unit/infrastructure/repositories/testFolder",{})).rejects.toThrow() + }) + }) +}) \ No newline at end of file diff --git a/test/unit/infrastructure/repositories/testFolder/1593d51b7bec762dc17fbc8833c18c7c.png b/test/unit/infrastructure/repositories/testFolder/1593d51b7bec762dc17fbc8833c18c7c.png new file mode 100644 index 0000000..84decf0 --- /dev/null +++ b/test/unit/infrastructure/repositories/testFolder/1593d51b7bec762dc17fbc8833c18c7c.png @@ -0,0 +1 @@ +testDFata \ No newline at end of file diff --git a/test/unit/interfaces/serializers/AlbumSerializer.test.js b/test/unit/interfaces/serializers/AlbumSerializer.test.js index 09edb99..f835ac2 100644 --- a/test/unit/interfaces/serializers/AlbumSerializer.test.js +++ b/test/unit/interfaces/serializers/AlbumSerializer.test.js @@ -7,6 +7,7 @@ const { albumRawNoArtist, expectedAlbumNoArtist } = require("./fixtures/albumFixture") + describe('albumSerialize', () => { it("should return serialized album with one artist", ()=>{ const result = serializeAlbum(albumRawOneArtist) diff --git a/test/unit/interfaces/serializers/AlbumTrackSerializer.test.js b/test/unit/interfaces/serializers/AlbumTrackSerializer.test.js new file mode 100644 index 0000000..3bedde1 --- /dev/null +++ b/test/unit/interfaces/serializers/AlbumTrackSerializer.test.js @@ -0,0 +1,25 @@ +const serializeTrack = require("../../../../lib/interfaces/serializers/AlbumTrackSerializer") +const { + expectedRawTrackWithOneArtist, + rawTrackWithOneArtist, + expectedRawTrackWithServeralArtists, + rawTrackWithServeralArtists, + expectedRawTrackWithNoArtist, + rawTrackWithNoArtist, +} = require("./fixtures/albumTrackFixture") + +describe('AlbumTrackSerializer', () => { + it("should return serialized track with one artist", ()=>{ + const result = serializeTrack(rawTrackWithOneArtist) + expect(result).toEqual(expectedRawTrackWithOneArtist) + }) + + it("should return serialized track with several artists", ()=>{ + const result = serializeTrack(rawTrackWithServeralArtists) + expect(result).toEqual(expectedRawTrackWithServeralArtists) + }) + it("should return serialized track with no artist", ()=>{ + const result = serializeTrack(rawTrackWithNoArtist) + expect(result).toEqual(expectedRawTrackWithNoArtist) + }) +}); \ No newline at end of file diff --git a/test/unit/interfaces/serializers/fixtures/albumFixture.js b/test/unit/interfaces/serializers/fixtures/albumFixture.js index 5a35a54..55dbab0 100644 --- a/test/unit/interfaces/serializers/fixtures/albumFixture.js +++ b/test/unit/interfaces/serializers/fixtures/albumFixture.js @@ -2,6 +2,16 @@ const { artistFixture, expectedFixture, } = require("./artistFixture") + +const { + expectedRawTrackWithOneArtist, + rawTrackWithOneArtist, + expectedRawTrackWithServeralArtists, + rawTrackWithServeralArtists, + expectedRawTrackWithNoArtist, + rawTrackWithNoArtist, +} = require('./albumTrackFixture') + const albumRawOneArtist = { album_type: "album", total_tracks: 14, @@ -31,7 +41,8 @@ const albumRawOneArtist = { genres: ["genre1", "genre2"], artists: [ artistFixture - ] + ], + tracks: undefined, } const expectedAlbumOneArtist = { @@ -41,26 +52,11 @@ const expectedAlbumOneArtist = { popularity:0, release_date:"2009-02-16", spotify_url: "https://open.spotify.com/album/17UiqpQyl8T8vVxz2Towjy", - images:[ - { - url:"https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", - height:640, - width:640 - }, - { - url:"https://i.scdn.co/image/ab67616d00001e020b2e3999b189fa2a8a6a752f", - height:300, - width:300 - }, - { - url:"https://i.scdn.co/image/ab67616d000048510b2e3999b189fa2a8a6a752f", - height:64, - width:64 - } - ], + image: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", artists:[ expectedFixture ], + tracks: undefined, genres: ["genre1", "genre2"], type:"album" } @@ -95,7 +91,9 @@ const albumRawSeveralArtist = { artists: [ artistFixture, artistFixture - ] + ], + tracks: undefined, + } const expectedAlbumSeveralArtist = { @@ -105,27 +103,12 @@ const expectedAlbumSeveralArtist = { popularity:0, release_date:"2009-02-16", spotify_url: "https://open.spotify.com/album/17UiqpQyl8T8vVxz2Towjy", - images:[ - { - url:"https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", - height:640, - width:640 - }, - { - url:"https://i.scdn.co/image/ab67616d00001e020b2e3999b189fa2a8a6a752f", - height:300, - width:300 - }, - { - url:"https://i.scdn.co/image/ab67616d000048510b2e3999b189fa2a8a6a752f", - height:64, - width:64 - } - ], + image: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", artists:[ expectedFixture, expectedFixture ], + tracks: undefined, genres: ["genre1", "genre2"], type:"album" } @@ -154,6 +137,7 @@ const albumRawNoArtist = { width: 64 } ], + tracks: undefined, name: "Perdu D'Avance", release_date: "2009-02-16", genres: ["genre1", "genre2"], @@ -166,24 +150,9 @@ const expectedAlbumNoArtist = { popularity:0, release_date:"2009-02-16", spotify_url: "https://open.spotify.com/album/17UiqpQyl8T8vVxz2Towjy", - images:[ - { - url:"https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", - height:640, - width:640 - }, - { - url:"https://i.scdn.co/image/ab67616d00001e020b2e3999b189fa2a8a6a752f", - height:300, - width:300 - }, - { - url:"https://i.scdn.co/image/ab67616d000048510b2e3999b189fa2a8a6a752f", - height:64, - width:64 - } - ], + image: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", artists: undefined, + tracks: undefined, genres: ["genre1", "genre2"], type:"album" } diff --git a/test/unit/interfaces/serializers/fixtures/albumTrackFixture.js b/test/unit/interfaces/serializers/fixtures/albumTrackFixture.js new file mode 100644 index 0000000..de2b5fa --- /dev/null +++ b/test/unit/interfaces/serializers/fixtures/albumTrackFixture.js @@ -0,0 +1,251 @@ +const { + artistFixture, + expectedFixture, +} = require("./artistFixture") + + +const rawTrackWithOneArtist = { + id: "test_id", + name: "test_name", + external_urls: { + spotify: "https://open.spotify.com/track/4UVDKEPTgMQXv9UlIqVTcA" + }, + artists: [ + artistFixture + ], + duration_ms: 12121231, +} + +const expectedRawTrackWithOneArtist = { + id:"test_id", + name:"test_name", + album: undefined, + artists:[ + expectedFixture + ], + duration_ms:12121231, + popularity: undefined, + spotify_url:"https://open.spotify.com/track/4UVDKEPTgMQXv9UlIqVTcA", + type:"track" +} + +const rawTrackWithServeralArtists = { + id: "test_id", + name: "test_name", + external_urls: { + spotify: "https://open.spotify.com/track/4UVDKEPTgMQXv9UlIqVTcA" + }, + artists: [ + artistFixture + ], + duration_ms: 12121231, +} + +const expectedRawTrackWithServeralArtists = { + id:"test_id", + name:"test_name", + album: undefined, + artists:[ + expectedFixture + ], + duration_ms:12121231, + popularity: undefined, + spotify_url:"https://open.spotify.com/track/4UVDKEPTgMQXv9UlIqVTcA", + type:"track" +} + + +const rawTrackWithNoArtist = { + id: "test_id", + name: "test_name", + external_urls: { + spotify: "https://open.spotify.com/track/4UVDKEPTgMQXv9UlIqVTcA" + }, + artists: undefined, + duration_ms: 12121231, +} + +const expectedRawTrackWithNoArtist = { + id:"test_id", + name:"test_name", + artists: undefined, + album: undefined, + duration_ms:12121231, + popularity: undefined, + spotify_url:"https://open.spotify.com/track/4UVDKEPTgMQXv9UlIqVTcA", + type:"track" +} + +const albumRawOneArtist = { + album_type: "album", + total_tracks: 14, + external_urls: { + spotify: "https://open.spotify.com/album/17UiqpQyl8T8vVxz2Towjy" + }, + id: "17UiqpQyl8T8vVxz2Towjy", + images: [ + { + url: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", + height: 640, + width: 640 + }, + { + url: "https://i.scdn.co/image/ab67616d00001e020b2e3999b189fa2a8a6a752f", + height: 300, + width: 300 + }, + { + url: "https://i.scdn.co/image/ab67616d000048510b2e3999b189fa2a8a6a752f", + height: 64, + width: 64 + } + ], + name: "Perdu D'Avance", + release_date: "2009-02-16", + genres: ["genre1", "genre2"], + artists: [ + artistFixture + ], + tracks: {items : [ + rawTrackWithOneArtist + ]}, +} + +const expectedAlbumOneArtist = { + id:"17UiqpQyl8T8vVxz2Towjy", + total_tracks:14, + name:"Perdu D'Avance", + popularity:0, + release_date:"2009-02-16", + spotify_url: "https://open.spotify.com/album/17UiqpQyl8T8vVxz2Towjy", + image: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", + artists:[ + expectedFixture + ], + tracks: [ + expectedRawTrackWithOneArtist + + ], + genres: ["genre1", "genre2"], + type:"album" +} + +const albumRawSeveralArtist = { + album_type: "album", + total_tracks: 14, + external_urls: { + spotify: "https://open.spotify.com/album/17UiqpQyl8T8vVxz2Towjy" + }, + id: "17UiqpQyl8T8vVxz2Towjy", + images: [ + { + url: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", + height: 640, + width: 640 + }, + { + url: "https://i.scdn.co/image/ab67616d00001e020b2e3999b189fa2a8a6a752f", + height: 300, + width: 300 + }, + { + url: "https://i.scdn.co/image/ab67616d000048510b2e3999b189fa2a8a6a752f", + height: 64, + width: 64 + } + ], + name: "Perdu D'Avance", + release_date: "2009-02-16", + genres: ["genre1", "genre2"], + artists: [ + artistFixture, + artistFixture + ], + tracks: { + items: [ + rawTrackWithServeralArtists + ], + } + +} + +const expectedAlbumSeveralArtist = { + id:"17UiqpQyl8T8vVxz2Towjy", + total_tracks:14, + name:"Perdu D'Avance", + popularity:0, + release_date:"2009-02-16", + spotify_url: "https://open.spotify.com/album/17UiqpQyl8T8vVxz2Towjy", + image: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", + artists:[ + expectedFixture, + expectedFixture + ], + tracks: [ + expectedRawTrackWithServeralArtists + ], + genres: ["genre1", "genre2"], + type:"album" +} + +const albumRawNoArtist = { + album_type: "album", + total_tracks: 14, + external_urls: { + spotify: "https://open.spotify.com/album/17UiqpQyl8T8vVxz2Towjy" + }, + id: "17UiqpQyl8T8vVxz2Towjy", + images: [ + { + url: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", + height: 640, + width: 640 + }, + { + url: "https://i.scdn.co/image/ab67616d00001e020b2e3999b189fa2a8a6a752f", + height: 300, + width: 300 + }, + { + url: "https://i.scdn.co/image/ab67616d000048510b2e3999b189fa2a8a6a752f", + height: 64, + width: 64 + } + ], + tracks: {items: [ + rawTrackWithNoArtist + ]}, + name: "Perdu D'Avance", + release_date: "2009-02-16", + genres: ["genre1", "genre2"], +} + +const expectedAlbumNoArtist = { + id:"17UiqpQyl8T8vVxz2Towjy", + total_tracks:14, + name:"Perdu D'Avance", + popularity:0, + release_date:"2009-02-16", + spotify_url: "https://open.spotify.com/album/17UiqpQyl8T8vVxz2Towjy", + image: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", + artists: undefined, + tracks: [ + expectedRawTrackWithNoArtist + ], + genres: ["genre1", "genre2"], + type:"album" +} +module.exports = { + expectedRawTrackWithOneArtist, + rawTrackWithOneArtist, + expectedRawTrackWithServeralArtists, + rawTrackWithServeralArtists, + expectedRawTrackWithNoArtist, + rawTrackWithNoArtist, + albumRawOneArtist, + expectedAlbumOneArtist, + albumRawSeveralArtist, + expectedAlbumSeveralArtist, + albumRawNoArtist, + expectedAlbumNoArtist +} \ No newline at end of file diff --git a/test/unit/interfaces/serializers/fixtures/artistAlbumsFixture.js b/test/unit/interfaces/serializers/fixtures/artistAlbumsFixture.js new file mode 100644 index 0000000..1cb81eb --- /dev/null +++ b/test/unit/interfaces/serializers/fixtures/artistAlbumsFixture.js @@ -0,0 +1,336 @@ + +/* Artist */ + +const artistFixture = { + external_urls: { + spotify: "https://open.spotify.com/artist/3fMbdgg4jU18AjLCKBhRSm" + }, + id: "3fMbdgg4jU18AjLCKBhRSm", + name: "Michael Jackson", + popularity: 81, + genres: ["r&b", "soul"], + "images": [ + { + "url": "https://i.scdn.co/image/ab6761610000e5eb0e08ea2c4d6789fbf5cbe0aa", + "height": 640, + "width": 640 + }, + { + "url": "https://i.scdn.co/image/ab676161000051740e08ea2c4d6789fbf5cbe0aa", + "height": 320, + "width": 320 + }, + { + "url": "https://i.scdn.co/image/ab6761610000f1780e08ea2c4d6789fbf5cbe0aa", + "height": 160, + "width": 160 + } + ] +} + +const artistExpectedFixture = { + id: "3fMbdgg4jU18AjLCKBhRSm", + name: "Michael Jackson", + image: "https://i.scdn.co/image/ab6761610000e5eb0e08ea2c4d6789fbf5cbe0aa", + popularity: 81, + genres: ["r&b", "soul"], + spotify_url: "https://open.spotify.com/artist/3fMbdgg4jU18AjLCKBhRSm", + type: "artist", +} + + + + +/* Pour appearsOn */ +const artistFixtureAppearsOn = { + external_urls: { + spotify: "https://open.spotify.com/artist/3TVXtAsR1Inumwj472S9r4" + }, + id: "3TVXtAsR1Inumwj472S9r4", + name: "Drake", + popularity: 97, + genres: ["canadian hip hop","canadian pop","hip hop","pop rap","rap"], + "images": [ + { + "url": "https://i.scdn.co/image/ab6761610000e5eb4293385d324db8558179afd9", + "height": 640, + "width": 640 + }, + { + "url": "https://i.scdn.co/image/ab676161000051744293385d324db8558179afd9", + "height": 320, + "width": 320 + }, + { + "url": "https://i.scdn.co/image/ab6761610000f1784293385d324db8558179afd9", + "height": 160, + "width": 160 + } + ] +} + +const artistExpectedFixtureAppearsOn = { + id: "3TVXtAsR1Inumwj472S9r4", + name: "Drake", + image: "https://i.scdn.co/image/ab6761610000e5eb4293385d324db8558179afd9", + popularity: 97, + genres: ["canadian hip hop","canadian pop","hip hop","pop rap","rap"], + spotify_url: "https://open.spotify.com/artist/3TVXtAsR1Inumwj472S9r4", + type: "artist", +} + + +/* Album Raw */ + +const songsRawAlbumArtist = { + items: [ + { + album_type: 'album', + total_tracks: 34, + popularity: 50, // rentre a la main + external_urls: { + spotify: "https://open.spotify.com/album/57TzZhbqvYoUBzJSVKFVlG" + }, + id: "57TzZhbqvYoUBzJSVKFVlG", + images: [ + { + "height": 640, + "url": "https://i.scdn.co/image/ab67616d0000b273822e06488f98e53e8743ff6b", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/ab67616d00001e02822e06488f98e53e8743ff6b", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/ab67616d00004851822e06488f98e53e8743ff6b", + "width": 64 + } + ], + name: "Thriller 40", + release_date: "2022-11-18", + artists: [ + artistFixture + ], + album_group: "album" + } + ] +} + +/* Expected Album Raw */ + +const expectedAlbumArtist = [ + { + id: "57TzZhbqvYoUBzJSVKFVlG", + total_tracks: 34, + name: "Thriller 40", + popularity: 50, // rentre a la main + release_date: "2022-11-18", + spotify_url: "https://open.spotify.com/album/57TzZhbqvYoUBzJSVKFVlG", + image: "https://i.scdn.co/image/ab67616d0000b273822e06488f98e53e8743ff6b", + artists: [ + artistExpectedFixture + ], + tracks: undefined, + genres: undefined, + type: "album" + } +] + + + +/* Single Raw */ + +const songsRawSingleArtist = { + items: [ + { + album_type: 'single', + total_tracks: 1, + popularity: 50, // rentre a la main + external_urls: { + spotify: "https://open.spotify.com/album/6ST7naJFCe9iBeOleU5Ccu" + }, + id: "6ST7naJFCe9iBeOleU5Ccu", + images: [ + { + "height": 640, + "url": "https://i.scdn.co/image/ab67616d0000b2739e9ceac980e4b6b59a766f8b", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/ab67616d00001e029e9ceac980e4b6b59a766f8b", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/ab67616d000048519e9ceac980e4b6b59a766f8b", + "width": 64 + } + ], + name: "Michael Jackson x Mark Ronson: Diamonds are Invincible", + release_date: "2018-08-29", + artists: [ + artistFixture + ], + album_group: "single" + } + ] +} + +/* Expected Single Raw */ + +const expectedSingleArtist = [ + { + id: "6ST7naJFCe9iBeOleU5Ccu", + total_tracks: 1, + name: "Michael Jackson x Mark Ronson: Diamonds are Invincible", + popularity: 50, // rentre a la main + release_date: "2018-08-29", + spotify_url: "https://open.spotify.com/album/6ST7naJFCe9iBeOleU5Ccu", + image: "https://i.scdn.co/image/ab67616d0000b2739e9ceac980e4b6b59a766f8b", + artists: [ + artistExpectedFixture + ], + tracks: undefined, + genres: undefined, + type: "single" + } +] + + +/* Compilation Raw */ + +const songsRawCompilationArtist = { + items: [ + { + album_type: 'compilation', + total_tracks: 14, + popularity: 50, // rentre a la main + external_urls: { + spotify: "https://open.spotify.com/album/2X8UOIkZQdcz2Hi5Ynt2uk" + }, + id: "2X8UOIkZQdcz2Hi5Ynt2uk", + images: [ + { + "height": 640, + "url": "https://i.scdn.co/image/ab67616d0000b273cde37cfdee48dc0eae1e2ab8", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/ab67616d00001e02cde37cfdee48dc0eae1e2ab8", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/ab67616d00004851cde37cfdee48dc0eae1e2ab8", + "width": 64 + } + ], + name: "Scream", + release_date: "2017-09-27", + artists: [ + artistFixture + ], + album_group: "compilation" + } + ] +} + +/* Expected Compilation Raw */ + +const expectedCompilationArtist = [ + { + id: "2X8UOIkZQdcz2Hi5Ynt2uk", + total_tracks: 14, + name: "Scream", + popularity: 50, // rentre a la main + release_date: "2017-09-27", //fait + spotify_url: "https://open.spotify.com/album/2X8UOIkZQdcz2Hi5Ynt2uk", + image: "https://i.scdn.co/image/ab67616d0000b273cde37cfdee48dc0eae1e2ab8", + artists: [ + artistExpectedFixture + ], + tracks: undefined, + genres: undefined, + type: "compilation" + } +] + + + +/* appears_on Raw */ + +const songsRawAppearsOnArtist = { + items: [ + { + album_type: 'album', + total_tracks: 25, + popularity: 50, // rentre a la main + external_urls: { + spotify: "https://open.spotify.com/album/1ATL5GLyefJaxhQzSPVrLX" + }, + id: "1ATL5GLyefJaxhQzSPVrLX", + images: [ + { + "height": 640, + "url": "https://i.scdn.co/image/ab67616d0000b273f907de96b9a4fbc04accc0d5", + "width": 640 + }, + { + "height": 300, + "url": "https://i.scdn.co/image/ab67616d00001e02f907de96b9a4fbc04accc0d5", + "width": 300 + }, + { + "height": 64, + "url": "https://i.scdn.co/image/ab67616d00004851f907de96b9a4fbc04accc0d5", + "width": 64 + } + ], + name: "Scorpion", + release_date: "2018-06-29", + artists: [ + artistFixtureAppearsOn + ], + album_group: "appears_on" + } + ] +} + +/* Expected appears_on Raw */ + +const expectedAppearsOnArtist = [ + { + id: "1ATL5GLyefJaxhQzSPVrLX", + total_tracks: 25, + name: "Scorpion", + popularity: 50, // rentre a la main + release_date: "2018-06-29", //fait + spotify_url: "https://open.spotify.com/album/1ATL5GLyefJaxhQzSPVrLX", + image: "https://i.scdn.co/image/ab67616d0000b273f907de96b9a4fbc04accc0d5", + artists: [ + artistExpectedFixtureAppearsOn + ], + tracks: undefined, + genres: undefined, + type: "appears_on" + } +] + + + + +module.exports = { + songsRawAlbumArtist, + expectedAlbumArtist, + songsRawSingleArtist, + expectedSingleArtist, + songsRawCompilationArtist, + expectedCompilationArtist, + songsRawAppearsOnArtist, + expectedAppearsOnArtist +} \ No newline at end of file diff --git a/test/unit/interfaces/serializers/fixtures/artistFixture.js b/test/unit/interfaces/serializers/fixtures/artistFixture.js index ec49a4a..c2532f2 100644 --- a/test/unit/interfaces/serializers/fixtures/artistFixture.js +++ b/test/unit/interfaces/serializers/fixtures/artistFixture.js @@ -28,23 +28,7 @@ const expectedFixture = { id: "4FpJcNgOvIpSBeJgRg3OfN", name: "Orelsan", - images: [ - { - url: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", - height: 640, - width: 640 - }, - { - url: "https://i.scdn.co/image/ab67616d00001e020b2e3999b189fa2a8a6a752f", - height: 300, - width: 300 - }, - { - url: "https://i.scdn.co/image/ab67616d000048510b2e3999b189fa2a8a6a752f", - height: 64, - width: 64 - } - ], + image: "https://i.scdn.co/image/ab67616d0000b2730b2e3999b189fa2a8a6a752f", popularity: 10, genres:['rock','funk'], spotify_url: "https://open.spotify.com/artist/4FpJcNgOvIpSBeJgRg3OfN",